Code

ac7a1a8ae31da6e67d4ebf73ddeeb5cb15038f17
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2007 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 /**
25  * To use this file, compile with:
26  * <pre>
27  * g++ -O3 buildtool.cpp -o btool.exe
28  * (or whatever your compiler might be) 
29  * Then
30  * btool
31  * or 
32  * btool {target}
33  * 
34  * Note: if you are using MinGW, and a not very recent version of it,
35  * gettimeofday() might be missing.  If so, just build this file with
36  * this command:
37  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38  *     
39  */  
41 #define BUILDTOOL_VERSION  "BuildTool v0.6.13, 2007 Bob Jamison"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73       int tz_minuteswest; /* minutes west of Greenwich */
74       int tz_dsttime;     /* type of dst correction */
75     };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79    struct _timeb tb;
81    if (!tv)
82       return (-1);
84     _ftime (&tb);
85     tv->tv_sec  = tb.time;
86     tv->tv_usec = tb.millitm * 1000 + 500;
87     if (tz)
88         {
89         tz->tz_minuteswest = -60 * _timezone;
90         tz->tz_dsttime = _daylight;
91         }
92     return 0;
93 }
95 #endif
103 namespace buildtool
109 //########################################################################
110 //########################################################################
111 //##  R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116  * This is the T-Rex regular expression library, which we
117  * gratefully acknowledge.  It's clean code and small size allow
118  * us to embed it in BuildTool without adding a dependency
119  *
120  */    
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127     T-Rex a tiny regular expression library
129     Copyright (C) 2003-2006 Alberto Demichelis
131     This software is provided 'as-is', without any express 
132     or implied warranty. In no event will the authors be held 
133     liable for any damages arising from the use of this software.
135     Permission is granted to anyone to use this software for 
136     any purpose, including commercial applications, and to alter
137     it and redistribute it freely, subject to the following restrictions:
139         1. The origin of this software must not be misrepresented;
140         you must not claim that you wrote the original software.
141         If you use this software in a product, an acknowledgment
142         in the product documentation would be appreciated but
143         is not required.
145         2. Altered source versions must be plainly marked as such,
146         and must not be misrepresented as being the original software.
148         3. This notice may not be removed or altered from any
149         source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c 
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c) 
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178     const TRexChar *begin;
179     int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
224     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
225     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
226     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR            (MAX_CHAR+2)
233 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT            (MAX_CHAR+5)
236 #define OP_CLASS        (MAX_CHAR+6)
237 #define OP_CCLASS        (MAX_CHAR+7)
238 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE        (MAX_CHAR+9)
240 #define OP_CHAR            (MAX_CHAR+10)
241 #define OP_EOL            (MAX_CHAR+11)
242 #define OP_BOL            (MAX_CHAR+12)
243 #define OP_WB            (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258     TRexNodeType type;
259     int left;
260     int right;
261     int next;
262 }TRexNode;
264 struct TRex{
265     const TRexChar *_eol;
266     const TRexChar *_bol;
267     const TRexChar *_p;
268     int _first;
269     int _op;
270     TRexNode *_nodes;
271     int _nallocated;
272     int _nsize;
273     int _nsubexpr;
274     TRexMatch *_matches;
275     int _currsubexp;
276     void *_jmpbuf;
277     const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
284     TRexNode n;
285     int newid;
286     n.type = type;
287     n.next = n.right = n.left = -1;
288     if(type == OP_EXPR)
289         n.right = exp->_nsubexpr++;
290     if(exp->_nallocated < (exp->_nsize + 1)) {
291         //int oldsize = exp->_nallocated;
292         exp->_nallocated *= 2;
293         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294     }
295     exp->_nodes[exp->_nsize++] = n;
296     newid = exp->_nsize - 1;
297     return (int)newid;
300 static void trex_error(TRex *exp,const TRexChar *error)
302     if(exp->_error) *exp->_error = error;
303     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
306 static void trex_expect(TRex *exp, int n){
307     if((*exp->_p) != n) 
308         trex_error(exp, _SC("expected paren"));
309     exp->_p++;
312 static TRexChar trex_escapechar(TRex *exp)
314     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315         exp->_p++;
316         switch(*exp->_p) {
317         case 'v': exp->_p++; return '\v';
318         case 'n': exp->_p++; return '\n';
319         case 't': exp->_p++; return '\t';
320         case 'r': exp->_p++; return '\r';
321         case 'f': exp->_p++; return '\f';
322         default: return (*exp->_p++);
323         }
324     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325     return (*exp->_p++);
328 static int trex_charclass(TRex *exp,int classid)
330     int n = trex_newnode(exp,OP_CCLASS);
331     exp->_nodes[n].left = classid;
332     return n;
335 static int trex_charnode(TRex *exp,TRexBool isclass)
337     TRexChar t;
338     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339         exp->_p++;
340         switch(*exp->_p) {
341             case 'n': exp->_p++; return trex_newnode(exp,'\n');
342             case 't': exp->_p++; return trex_newnode(exp,'\t');
343             case 'r': exp->_p++; return trex_newnode(exp,'\r');
344             case 'f': exp->_p++; return trex_newnode(exp,'\f');
345             case 'v': exp->_p++; return trex_newnode(exp,'\v');
346             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
347             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
348             case 'p': case 'P': case 'l': case 'u': 
349                 {
350                 t = *exp->_p; exp->_p++; 
351                 return trex_charclass(exp,t);
352                 }
353             case 'b': 
354             case 'B':
355                 if(!isclass) {
356                     int node = trex_newnode(exp,OP_WB);
357                     exp->_nodes[node].left = *exp->_p;
358                     exp->_p++; 
359                     return node;
360                 } //else default
361             default: 
362                 t = *exp->_p; exp->_p++; 
363                 return trex_newnode(exp,t);
364         }
365     }
366     else if(!scisprint(*exp->_p)) {
367         
368         trex_error(exp,_SC("letter expected"));
369     }
370     t = *exp->_p; exp->_p++; 
371     return trex_newnode(exp,t);
373 static int trex_class(TRex *exp)
375     int ret = -1;
376     int first = -1,chain;
377     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378         ret = trex_newnode(exp,OP_NCLASS);
379         exp->_p++;
380     }else ret = trex_newnode(exp,OP_CLASS);
381     
382     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383     chain = ret;
384     while(*exp->_p != ']' && exp->_p != exp->_eol) {
385         if(*exp->_p == '-' && first != -1){ 
386             int r,t;
387             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388             r = trex_newnode(exp,OP_RANGE);
389             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391             exp->_nodes[r].left = exp->_nodes[first].type;
392             t = trex_escapechar(exp);
393             exp->_nodes[r].right = t;
394             exp->_nodes[chain].next = r;
395             chain = r;
396             first = -1;
397         }
398         else{
399             if(first!=-1){
400                 int c = first;
401                 exp->_nodes[chain].next = c;
402                 chain = c;
403                 first = trex_charnode(exp,TRex_True);
404             }
405             else{
406                 first = trex_charnode(exp,TRex_True);
407             }
408         }
409     }
410     if(first!=-1){
411         int c = first;
412         exp->_nodes[chain].next = c;
413         chain = c;
414         first = -1;
415     }
416     /* hack? */
417     exp->_nodes[ret].left = exp->_nodes[ret].next;
418     exp->_nodes[ret].next = -1;
419     return ret;
422 static int trex_parsenumber(TRex *exp)
424     int ret = *exp->_p-'0';
425     int positions = 10;
426     exp->_p++;
427     while(isdigit(*exp->_p)) {
428         ret = ret*10+(*exp->_p++-'0');
429         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430         positions *= 10;
431     };
432     return ret;
435 static int trex_element(TRex *exp)
437     int ret = -1;
438     switch(*exp->_p)
439     {
440     case '(': {
441         int expr,newn;
442         exp->_p++;
445         if(*exp->_p =='?') {
446             exp->_p++;
447             trex_expect(exp,':');
448             expr = trex_newnode(exp,OP_NOCAPEXPR);
449         }
450         else
451             expr = trex_newnode(exp,OP_EXPR);
452         newn = trex_list(exp);
453         exp->_nodes[expr].left = newn;
454         ret = expr;
455         trex_expect(exp,')');
456               }
457               break;
458     case '[':
459         exp->_p++;
460         ret = trex_class(exp);
461         trex_expect(exp,']');
462         break;
463     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465     default:
466         ret = trex_charnode(exp,TRex_False);
467         break;
468     }
470     {
471         int op;
472         TRexBool isgreedy = TRex_False;
473         unsigned short p0 = 0, p1 = 0;
474         switch(*exp->_p){
475             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478             case '{':
479                 exp->_p++;
480                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481                 p0 = (unsigned short)trex_parsenumber(exp);
482                 /*******************************/
483                 switch(*exp->_p) {
484             case '}':
485                 p1 = p0; exp->_p++;
486                 break;
487             case ',':
488                 exp->_p++;
489                 p1 = 0xFFFF;
490                 if(isdigit(*exp->_p)){
491                     p1 = (unsigned short)trex_parsenumber(exp);
492                 }
493                 trex_expect(exp,'}');
494                 break;
495             default:
496                 trex_error(exp,_SC(", or } expected"));
497         }
498         /*******************************/
499         isgreedy = TRex_True; 
500         break;
502         }
503         if(isgreedy) {
504             int nnode = trex_newnode(exp,OP_GREEDY);
505             op = OP_GREEDY;
506             exp->_nodes[nnode].left = ret;
507             exp->_nodes[nnode].right = ((p0)<<16)|p1;
508             ret = nnode;
509         }
510     }
511     if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
512         int nnode = trex_element(exp);
513         exp->_nodes[ret].next = nnode;
514     }
516     return ret;
519 static int trex_list(TRex *exp)
521     int ret=-1,e;
522     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523         exp->_p++;
524         ret = trex_newnode(exp,OP_BOL);
525     }
526     e = trex_element(exp);
527     if(ret != -1) {
528         exp->_nodes[ret].next = e;
529     }
530     else ret = e;
532     if(*exp->_p == TREX_SYMBOL_BRANCH) {
533         int temp,tright;
534         exp->_p++;
535         temp = trex_newnode(exp,OP_OR);
536         exp->_nodes[temp].left = ret;
537         tright = trex_list(exp);
538         exp->_nodes[temp].right = tright;
539         ret = temp;
540     }
541     return ret;
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
546     switch(cclass) {
547     case 'a': return isalpha(c)?TRex_True:TRex_False;
548     case 'A': return !isalpha(c)?TRex_True:TRex_False;
549     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551     case 's': return isspace(c)?TRex_True:TRex_False;
552     case 'S': return !isspace(c)?TRex_True:TRex_False;
553     case 'd': return isdigit(c)?TRex_True:TRex_False;
554     case 'D': return !isdigit(c)?TRex_True:TRex_False;
555     case 'x': return isxdigit(c)?TRex_True:TRex_False;
556     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557     case 'c': return iscntrl(c)?TRex_True:TRex_False;
558     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559     case 'p': return ispunct(c)?TRex_True:TRex_False;
560     case 'P': return !ispunct(c)?TRex_True:TRex_False;
561     case 'l': return islower(c)?TRex_True:TRex_False;
562     case 'u': return isupper(c)?TRex_True:TRex_False;
563     }
564     return TRex_False; /*cannot happen*/
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
569     do {
570         switch(node->type) {
571             case OP_RANGE:
572                 if(c >= node->left && c <= node->right) return TRex_True;
573                 break;
574             case OP_CCLASS:
575                 if(trex_matchcclass(node->left,c)) return TRex_True;
576                 break;
577             default:
578                 if(c == node->type)return TRex_True;
579         }
580     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581     return TRex_False;
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
586     
587     TRexNodeType type = node->type;
588     switch(type) {
589     case OP_GREEDY: {
590         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591         TRexNode *greedystop = NULL;
592         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593         const TRexChar *s=str, *good = str;
595         if(node->next != -1) {
596             greedystop = &exp->_nodes[node->next];
597         }
598         else {
599             greedystop = next;
600         }
602         while((nmaches == 0xFFFF || nmaches < p1)) {
604             const TRexChar *stop;
605             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606                 break;
607             nmaches++;
608             good=s;
609             if(greedystop) {
610                 //checks that 0 matches satisfy the expression(if so skips)
611                 //if not would always stop(for instance if is a '?')
612                 if(greedystop->type != OP_GREEDY ||
613                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614                 {
615                     TRexNode *gnext = NULL;
616                     if(greedystop->next != -1) {
617                         gnext = &exp->_nodes[greedystop->next];
618                     }else if(next && next->next != -1){
619                         gnext = &exp->_nodes[next->next];
620                     }
621                     stop = trex_matchnode(exp,greedystop,s,gnext);
622                     if(stop) {
623                         //if satisfied stop it
624                         if(p0 == p1 && p0 == nmaches) break;
625                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
626                         else if(nmaches >= p0 && nmaches <= p1) break;
627                     }
628                 }
629             }
630             
631             if(s >= exp->_eol)
632                 break;
633         }
634         if(p0 == p1 && p0 == nmaches) return good;
635         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636         else if(nmaches >= p0 && nmaches <= p1) return good;
637         return NULL;
638     }
639     case OP_OR: {
640             const TRexChar *asd = str;
641             TRexNode *temp=&exp->_nodes[node->left];
642             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643                 if(temp->next != -1)
644                     temp = &exp->_nodes[temp->next];
645                 else
646                     return asd;
647             }
648             asd = str;
649             temp = &exp->_nodes[node->right];
650             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651                 if(temp->next != -1)
652                     temp = &exp->_nodes[temp->next];
653                 else
654                     return asd;
655             }
656             return NULL;
657             break;
658     }
659     case OP_EXPR:
660     case OP_NOCAPEXPR:{
661             TRexNode *n = &exp->_nodes[node->left];
662             const TRexChar *cur = str;
663             int capture = -1;
664             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665                 capture = exp->_currsubexp;
666                 exp->_matches[capture].begin = cur;
667                 exp->_currsubexp++;
668             }
669             
670             do {
671                 TRexNode *subnext = NULL;
672                 if(n->next != -1) {
673                     subnext = &exp->_nodes[n->next];
674                 }else {
675                     subnext = next;
676                 }
677                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678                     if(capture != -1){
679                         exp->_matches[capture].begin = 0;
680                         exp->_matches[capture].len = 0;
681                     }
682                     return NULL;
683                 }
684             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686             if(capture != -1) 
687                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688             return cur;
689     }                 
690     case OP_WB:
691         if(str == exp->_bol && !isspace(*str)
692          || (str == exp->_eol && !isspace(*(str-1)))
693          || (!isspace(*str) && isspace(*(str+1)))
694          || (isspace(*str) && !isspace(*(str+1))) ) {
695             return (node->left == 'b')?str:NULL;
696         }
697         return (node->left == 'b')?NULL:str;
698     case OP_BOL:
699         if(str == exp->_bol) return str;
700         return NULL;
701     case OP_EOL:
702         if(str == exp->_eol) return str;
703         return NULL;
704     case OP_DOT:{
705         *str++;
706                 }
707         return str;
708     case OP_NCLASS:
709     case OP_CLASS:
710         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711             *str++;
712             return str;
713         }
714         return NULL;
715     case OP_CCLASS:
716         if(trex_matchcclass(node->left,*str)) {
717             *str++;
718             return str;
719         }
720         return NULL;
721     default: /* char */
722         if(*str != node->type) return NULL;
723         *str++;
724         return str;
725     }
726     return NULL;
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
732     TRex *exp = (TRex *)malloc(sizeof(TRex));
733     exp->_eol = exp->_bol = NULL;
734     exp->_p = pattern;
735     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737     exp->_nsize = 0;
738     exp->_matches = 0;
739     exp->_nsubexpr = 0;
740     exp->_first = trex_newnode(exp,OP_EXPR);
741     exp->_error = error;
742     exp->_jmpbuf = malloc(sizeof(jmp_buf));
743     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744         int res = trex_list(exp);
745         exp->_nodes[exp->_first].left = res;
746         if(*exp->_p!='\0')
747             trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749         {
750             int nsize,i;
751             TRexNode *t;
752             nsize = exp->_nsize;
753             t = &exp->_nodes[0];
754             scprintf(_SC("\n"));
755             for(i = 0;i < nsize; i++) {
756                 if(exp->_nodes[i].type>MAX_CHAR)
757                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758                 else
759                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761             }
762             scprintf(_SC("\n"));
763         }
764 #endif
765         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767     }
768     else{
769         trex_free(exp);
770         return NULL;
771     }
772     return exp;
775 void trex_free(TRex *exp)
777     if(exp)    {
778         if(exp->_nodes) free(exp->_nodes);
779         if(exp->_jmpbuf) free(exp->_jmpbuf);
780         if(exp->_matches) free(exp->_matches);
781         free(exp);
782     }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
787     const TRexChar* res = NULL;
788     exp->_bol = text;
789     exp->_eol = text + scstrlen(text);
790     exp->_currsubexp = 0;
791     res = trex_matchnode(exp,exp->_nodes,text,NULL);
792     if(res == NULL || res != exp->_eol)
793         return TRex_False;
794     return TRex_True;
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
799     const TRexChar *cur = NULL;
800     int node = exp->_first;
801     if(text_begin >= text_end) return TRex_False;
802     exp->_bol = text_begin;
803     exp->_eol = text_end;
804     do {
805         cur = text_begin;
806         while(node != -1) {
807             exp->_currsubexp = 0;
808             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809             if(!cur)
810                 break;
811             node = exp->_nodes[node].next;
812         }
813         *text_begin++;
814     } while(cur == NULL && text_begin != text_end);
816     if(cur == NULL)
817         return TRex_False;
819     --text_begin;
821     if(out_begin) *out_begin = text_begin;
822     if(out_end) *out_end = cur;
823     return TRex_True;
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
828     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
831 int trex_getsubexpcount(TRex* exp)
833     return exp->_nsubexpr;
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
838     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839     *subexp = exp->_matches[n];
840     return TRex_True;
844 //########################################################################
845 //########################################################################
846 //##  E N D    R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //##  X M L
857 //########################################################################
858 //########################################################################
860 // Note:  This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
869 public:
870     Namespace()
871         {}
873     Namespace(const String &prefixArg, const String &namespaceURIArg)
874         {
875         prefix       = prefixArg;
876         namespaceURI = namespaceURIArg;
877         }
879     Namespace(const Namespace &other)
880         {
881         assign(other);
882         }
884     Namespace &operator=(const Namespace &other)
885         {
886         assign(other);
887         return *this;
888         }
890     virtual ~Namespace()
891         {}
893     virtual String getPrefix()
894         { return prefix; }
896     virtual String getNamespaceURI()
897         { return namespaceURI; }
899 protected:
901     void assign(const Namespace &other)
902         {
903         prefix       = other.prefix;
904         namespaceURI = other.namespaceURI;
905         }
907     String prefix;
908     String namespaceURI;
910 };
912 class Attribute
914 public:
915     Attribute()
916         {}
918     Attribute(const String &nameArg, const String &valueArg)
919         {
920         name  = nameArg;
921         value = valueArg;
922         }
924     Attribute(const Attribute &other)
925         {
926         assign(other);
927         }
929     Attribute &operator=(const Attribute &other)
930         {
931         assign(other);
932         return *this;
933         }
935     virtual ~Attribute()
936         {}
938     virtual String getName()
939         { return name; }
941     virtual String getValue()
942         { return value; }
944 protected:
946     void assign(const Attribute &other)
947         {
948         name  = other.name;
949         value = other.value;
950         }
952     String name;
953     String value;
955 };
958 class Element
960 friend class Parser;
962 public:
963     Element()
964         {
965         init();
966         }
968     Element(const String &nameArg)
969         {
970         init();
971         name   = nameArg;
972         }
974     Element(const String &nameArg, const String &valueArg)
975         {
976         init();
977         name   = nameArg;
978         value  = valueArg;
979         }
981     Element(const Element &other)
982         {
983         assign(other);
984         }
986     Element &operator=(const Element &other)
987         {
988         assign(other);
989         return *this;
990         }
992     virtual Element *clone();
994     virtual ~Element()
995         {
996         for (unsigned int i=0 ; i<children.size() ; i++)
997             delete children[i];
998         }
1000     virtual String getName()
1001         { return name; }
1003     virtual String getValue()
1004         { return value; }
1006     Element *getParent()
1007         { return parent; }
1009     std::vector<Element *> getChildren()
1010         { return children; }
1012     std::vector<Element *> findElements(const String &name);
1014     String getAttribute(const String &name);
1016     std::vector<Attribute> &getAttributes()
1017         { return attributes; } 
1019     String getTagAttribute(const String &tagName, const String &attrName);
1021     String getTagValue(const String &tagName);
1023     void addChild(Element *child);
1025     void addAttribute(const String &name, const String &value);
1027     void addNamespace(const String &prefix, const String &namespaceURI);
1030     /**
1031      * Prettyprint an XML tree to an output stream.  Elements are indented
1032      * according to element hierarchy.
1033      * @param f a stream to receive the output
1034      * @param elem the element to output
1035      */
1036     void writeIndented(FILE *f);
1038     /**
1039      * Prettyprint an XML tree to standard output.  This is the equivalent of
1040      * writeIndented(stdout).
1041      * @param elem the element to output
1042      */
1043     void print();
1044     
1045     int getLine()
1046         { return line; }
1048 protected:
1050     void init()
1051         {
1052         parent = NULL;
1053         line   = 0;
1054         }
1056     void assign(const Element &other)
1057         {
1058         parent     = other.parent;
1059         children   = other.children;
1060         attributes = other.attributes;
1061         namespaces = other.namespaces;
1062         name       = other.name;
1063         value      = other.value;
1064         line       = other.line;
1065         }
1067     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069     void writeIndentedRecursive(FILE *f, int indent);
1071     Element *parent;
1073     std::vector<Element *>children;
1075     std::vector<Attribute> attributes;
1076     std::vector<Namespace> namespaces;
1078     String name;
1079     String value;
1080     
1081     int line;
1082 };
1088 class Parser
1090 public:
1091     /**
1092      * Constructor
1093      */
1094     Parser()
1095         { init(); }
1097     virtual ~Parser()
1098         {}
1100     /**
1101      * Parse XML in a char buffer.
1102      * @param buf a character buffer to parse
1103      * @param pos position to start parsing
1104      * @param len number of chars, from pos, to parse.
1105      * @return a pointer to the root of the XML document;
1106      */
1107     Element *parse(const char *buf,int pos,int len);
1109     /**
1110      * Parse XML in a char buffer.
1111      * @param buf a character buffer to parse
1112      * @param pos position to start parsing
1113      * @param len number of chars, from pos, to parse.
1114      * @return a pointer to the root of the XML document;
1115      */
1116     Element *parse(const String &buf);
1118     /**
1119      * Parse a named XML file.  The file is loaded like a data file;
1120      * the original format is not preserved.
1121      * @param fileName the name of the file to read
1122      * @return a pointer to the root of the XML document;
1123      */
1124     Element *parseFile(const String &fileName);
1126     /**
1127      * Utility method to preprocess a string for XML
1128      * output, escaping its entities.
1129      * @param str the string to encode
1130      */
1131     static String encode(const String &str);
1133     /**
1134      *  Removes whitespace from beginning and end of a string
1135      */
1136     String trim(const String &s);
1138 private:
1140     void init()
1141         {
1142         keepGoing       = true;
1143         currentNode     = NULL;
1144         parselen        = 0;
1145         parsebuf        = NULL;
1146         currentPosition = 0;
1147         }
1149     int countLines(int begin, int end);
1151     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153     void error(const char *fmt, ...);
1155     int peek(int pos);
1157     int match(int pos, const char *text);
1159     int skipwhite(int p);
1161     int getWord(int p0, String &buf);
1163     int getQuoted(int p0, String &buf, int do_i_parse);
1165     int parseVersion(int p0);
1167     int parseDoctype(int p0);
1169     int parseElement(int p0, Element *par,int depth);
1171     Element *parse(XMLCh *buf,int pos,int len);
1173     bool       keepGoing;
1174     Element    *currentNode;
1175     int        parselen;
1176     XMLCh      *parsebuf;
1177     String     cdatabuf;
1178     int        currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1190     Element *elem = new Element(name, value);
1191     elem->parent     = parent;
1192     elem->attributes = attributes;
1193     elem->namespaces = namespaces;
1194     elem->line       = line;
1196     std::vector<Element *>::iterator iter;
1197     for (iter = children.begin(); iter != children.end() ; iter++)
1198         {
1199         elem->addChild((*iter)->clone());
1200         }
1201     return elem;
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1207     if (getName() == name)
1208         {
1209         res.push_back(this);
1210         }
1211     for (unsigned int i=0; i<children.size() ; i++)
1212         children[i]->findElementsRecursive(res, name);
1215 std::vector<Element *> Element::findElements(const String &name)
1217     std::vector<Element *> res;
1218     findElementsRecursive(res, name);
1219     return res;
1222 String Element::getAttribute(const String &name)
1224     for (unsigned int i=0 ; i<attributes.size() ; i++)
1225         if (attributes[i].getName() ==name)
1226             return attributes[i].getValue();
1227     return "";
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1232     std::vector<Element *>elems = findElements(tagName);
1233     if (elems.size() <1)
1234         return "";
1235     String res = elems[0]->getAttribute(attrName);
1236     return res;
1239 String Element::getTagValue(const String &tagName)
1241     std::vector<Element *>elems = findElements(tagName);
1242     if (elems.size() <1)
1243         return "";
1244     String res = elems[0]->getValue();
1245     return res;
1248 void Element::addChild(Element *child)
1250     if (!child)
1251         return;
1252     child->parent = this;
1253     children.push_back(child);
1257 void Element::addAttribute(const String &name, const String &value)
1259     Attribute attr(name, value);
1260     attributes.push_back(attr);
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1265     Namespace ns(prefix, namespaceURI);
1266     namespaces.push_back(ns);
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1271     int i;
1272     if (!f)
1273         return;
1274     //Opening tag, and attributes
1275     for (i=0;i<indent;i++)
1276         fputc(' ',f);
1277     fprintf(f,"<%s",name.c_str());
1278     for (unsigned int i=0 ; i<attributes.size() ; i++)
1279         {
1280         fprintf(f," %s=\"%s\"",
1281               attributes[i].getName().c_str(),
1282               attributes[i].getValue().c_str());
1283         }
1284     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285         {
1286         fprintf(f," xmlns:%s=\"%s\"",
1287               namespaces[i].getPrefix().c_str(),
1288               namespaces[i].getNamespaceURI().c_str());
1289         }
1290     fprintf(f,">\n");
1292     //Between the tags
1293     if (value.size() > 0)
1294         {
1295         for (int i=0;i<indent;i++)
1296             fputc(' ', f);
1297         fprintf(f," %s\n", value.c_str());
1298         }
1300     for (unsigned int i=0 ; i<children.size() ; i++)
1301         children[i]->writeIndentedRecursive(f, indent+2);
1303     //Closing tag
1304     for (int i=0; i<indent; i++)
1305         fputc(' ',f);
1306     fprintf(f,"</%s>\n", name.c_str());
1309 void Element::writeIndented(FILE *f)
1311     writeIndentedRecursive(f, 0);
1314 void Element::print()
1316     writeIndented(stdout);
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327     {
1328     const char *escaped;
1329     char value;
1330     } EntityEntry;
1332 static EntityEntry entities[] =
1334     { "&amp;" , '&'  },
1335     { "&lt;"  , '<'  },
1336     { "&gt;"  , '>'  },
1337     { "&apos;", '\'' },
1338     { "&quot;", '"'  },
1339     { NULL    , '\0' }
1340 };
1344 /**
1345  *  Removes whitespace from beginning and end of a string
1346  */
1347 String Parser::trim(const String &s)
1349     if (s.size() < 1)
1350         return s;
1351     
1352     //Find first non-ws char
1353     unsigned int begin = 0;
1354     for ( ; begin < s.size() ; begin++)
1355         {
1356         if (!isspace(s[begin]))
1357             break;
1358         }
1360     //Find first non-ws char, going in reverse
1361     unsigned int end = s.size() - 1;
1362     for ( ; end > begin ; end--)
1363         {
1364         if (!isspace(s[end]))
1365             break;
1366         }
1367     //trace("begin:%d  end:%d", begin, end);
1369     String res = s.substr(begin, end-begin+1);
1370     return res;
1374 int Parser::countLines(int begin, int end)
1376     int count = 0;
1377     for (int i=begin ; i<end ; i++)
1378         {
1379         XMLCh ch = parsebuf[i];
1380         if (ch == '\n' || ch == '\r')
1381             count++;
1382         }
1383     return count;
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1389     int line = 1;
1390     int col  = 1;
1391     for (long i=0 ; i<pos ; i++)
1392         {
1393         XMLCh ch = parsebuf[i];
1394         if (ch == '\n' || ch == '\r')
1395             {
1396             col = 0;
1397             line ++;
1398             }
1399         else
1400             col++;
1401         }
1402     *lineNr = line;
1403     *colNr  = col;
1408 void Parser::error(const char *fmt, ...)
1410     int lineNr;
1411     int colNr;
1412     getLineAndColumn(currentPosition, &lineNr, &colNr);
1413     va_list args;
1414     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415     va_start(args,fmt);
1416     vfprintf(stderr,fmt,args);
1417     va_end(args) ;
1418     fprintf(stderr, "\n");
1423 int Parser::peek(int pos)
1425     if (pos >= parselen)
1426         return -1;
1427     currentPosition = pos;
1428     int ch = parsebuf[pos];
1429     //printf("ch:%c\n", ch);
1430     return ch;
1435 String Parser::encode(const String &str)
1437     String ret;
1438     for (unsigned int i=0 ; i<str.size() ; i++)
1439         {
1440         XMLCh ch = (XMLCh)str[i];
1441         if (ch == '&')
1442             ret.append("&amp;");
1443         else if (ch == '<')
1444             ret.append("&lt;");
1445         else if (ch == '>')
1446             ret.append("&gt;");
1447         else if (ch == '\'')
1448             ret.append("&apos;");
1449         else if (ch == '"')
1450             ret.append("&quot;");
1451         else
1452             ret.push_back(ch);
1454         }
1455     return ret;
1459 int Parser::match(int p0, const char *text)
1461     int p = p0;
1462     while (*text)
1463         {
1464         if (peek(p) != *text)
1465             return p0;
1466         p++; text++;
1467         }
1468     return p;
1473 int Parser::skipwhite(int p)
1476     while (p<parselen)
1477         {
1478         int p2 = match(p, "<!--");
1479         if (p2 > p)
1480             {
1481             p = p2;
1482             while (p<parselen)
1483               {
1484               p2 = match(p, "-->");
1485               if (p2 > p)
1486                   {
1487                   p = p2;
1488                   break;
1489                   }
1490               p++;
1491               }
1492           }
1493       XMLCh b = peek(p);
1494       if (!isspace(b))
1495           break;
1496       p++;
1497       }
1498   return p;
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1504     int p = p0;
1505     while (p<parselen)
1506         {
1507         XMLCh b = peek(p);
1508         if (b<=' ' || b=='/' || b=='>' || b=='=')
1509             break;
1510         buf.push_back(b);
1511         p++;
1512         }
1513     return p;
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1519     int p = p0;
1520     if (peek(p) != '"' && peek(p) != '\'')
1521         return p0;
1522     p++;
1524     while ( p<parselen )
1525         {
1526         XMLCh b = peek(p);
1527         if (b=='"' || b=='\'')
1528             break;
1529         if (b=='&' && do_i_parse)
1530             {
1531             bool found = false;
1532             for (EntityEntry *ee = entities ; ee->value ; ee++)
1533                 {
1534                 int p2 = match(p, ee->escaped);
1535                 if (p2>p)
1536                     {
1537                     buf.push_back(ee->value);
1538                     p = p2;
1539                     found = true;
1540                     break;
1541                     }
1542                 }
1543             if (!found)
1544                 {
1545                 error("unterminated entity");
1546                 return false;
1547                 }
1548             }
1549         else
1550             {
1551             buf.push_back(b);
1552             p++;
1553             }
1554         }
1555     return p;
1558 int Parser::parseVersion(int p0)
1560     //printf("### parseVersion: %d\n", p0);
1562     int p = p0;
1564     p = skipwhite(p0);
1566     if (peek(p) != '<')
1567         return p0;
1569     p++;
1570     if (p>=parselen || peek(p)!='?')
1571         return p0;
1573     p++;
1575     String buf;
1577     while (p<parselen)
1578         {
1579         XMLCh ch = peek(p);
1580         if (ch=='?')
1581             {
1582             p++;
1583             break;
1584             }
1585         buf.push_back(ch);
1586         p++;
1587         }
1589     if (peek(p) != '>')
1590         return p0;
1591     p++;
1593     //printf("Got version:%s\n",buf.c_str());
1594     return p;
1597 int Parser::parseDoctype(int p0)
1599     //printf("### parseDoctype: %d\n", p0);
1601     int p = p0;
1602     p = skipwhite(p);
1604     if (p>=parselen || peek(p)!='<')
1605         return p0;
1607     p++;
1609     if (peek(p)!='!' || peek(p+1)=='-')
1610         return p0;
1611     p++;
1613     String buf;
1614     while (p<parselen)
1615         {
1616         XMLCh ch = peek(p);
1617         if (ch=='>')
1618             {
1619             p++;
1620             break;
1621             }
1622         buf.push_back(ch);
1623         p++;
1624         }
1626     //printf("Got doctype:%s\n",buf.c_str());
1627     return p;
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1635     int p = p0;
1637     int p2 = p;
1639     p = skipwhite(p);
1641     //## Get open tag
1642     XMLCh ch = peek(p);
1643     if (ch!='<')
1644         return p0;
1646     //int line, col;
1647     //getLineAndColumn(p, &line, &col);
1649     p++;
1651     String openTagName;
1652     p = skipwhite(p);
1653     p = getWord(p, openTagName);
1654     //printf("####tag :%s\n", openTagName.c_str());
1655     p = skipwhite(p);
1657     //Add element to tree
1658     Element *n = new Element(openTagName);
1659     n->line = lineNr + countLines(p0, p);
1660     n->parent = par;
1661     par->addChild(n);
1663     // Get attributes
1664     if (peek(p) != '>')
1665         {
1666         while (p<parselen)
1667             {
1668             p = skipwhite(p);
1669             ch = peek(p);
1670             //printf("ch:%c\n",ch);
1671             if (ch=='>')
1672                 break;
1673             else if (ch=='/' && p<parselen+1)
1674                 {
1675                 p++;
1676                 p = skipwhite(p);
1677                 ch = peek(p);
1678                 if (ch=='>')
1679                     {
1680                     p++;
1681                     //printf("quick close\n");
1682                     return p;
1683                     }
1684                 }
1685             String attrName;
1686             p2 = getWord(p, attrName);
1687             if (p2==p)
1688                 break;
1689             //printf("name:%s",buf);
1690             p=p2;
1691             p = skipwhite(p);
1692             ch = peek(p);
1693             //printf("ch:%c\n",ch);
1694             if (ch!='=')
1695                 break;
1696             p++;
1697             p = skipwhite(p);
1698             // ch = parsebuf[p];
1699             // printf("ch:%c\n",ch);
1700             String attrVal;
1701             p2 = getQuoted(p, attrVal, true);
1702             p=p2+1;
1703             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704             char *namestr = (char *)attrName.c_str();
1705             if (strncmp(namestr, "xmlns:", 6)==0)
1706                 n->addNamespace(attrName, attrVal);
1707             else
1708                 n->addAttribute(attrName, attrVal);
1709             }
1710         }
1712     bool cdata = false;
1714     p++;
1715     // ### Get intervening data ### */
1716     String data;
1717     while (p<parselen)
1718         {
1719         //# COMMENT
1720         p2 = match(p, "<!--");
1721         if (!cdata && p2>p)
1722             {
1723             p = p2;
1724             while (p<parselen)
1725                 {
1726                 p2 = match(p, "-->");
1727                 if (p2 > p)
1728                     {
1729                     p = p2;
1730                     break;
1731                     }
1732                 p++;
1733                 }
1734             }
1736         ch = peek(p);
1737         //# END TAG
1738         if (ch=='<' && !cdata && peek(p+1)=='/')
1739             {
1740             break;
1741             }
1742         //# CDATA
1743         p2 = match(p, "<![CDATA[");
1744         if (p2 > p)
1745             {
1746             cdata = true;
1747             p = p2;
1748             continue;
1749             }
1751         //# CHILD ELEMENT
1752         if (ch == '<')
1753             {
1754             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755             if (p2 == p)
1756                 {
1757                 /*
1758                 printf("problem on element:%s.  p2:%d p:%d\n",
1759                       openTagName.c_str(), p2, p);
1760                 */
1761                 return p0;
1762                 }
1763             p = p2;
1764             continue;
1765             }
1766         //# ENTITY
1767         if (ch=='&' && !cdata)
1768             {
1769             bool found = false;
1770             for (EntityEntry *ee = entities ; ee->value ; ee++)
1771                 {
1772                 int p2 = match(p, ee->escaped);
1773                 if (p2>p)
1774                     {
1775                     data.push_back(ee->value);
1776                     p = p2;
1777                     found = true;
1778                     break;
1779                     }
1780                 }
1781             if (!found)
1782                 {
1783                 error("unterminated entity");
1784                 return -1;
1785                 }
1786             continue;
1787             }
1789         //# NONE OF THE ABOVE
1790         data.push_back(ch);
1791         p++;
1792         }/*while*/
1795     n->value = data;
1796     //printf("%d : data:%s\n",p,data.c_str());
1798     //## Get close tag
1799     p = skipwhite(p);
1800     ch = peek(p);
1801     if (ch != '<')
1802         {
1803         error("no < for end tag\n");
1804         return p0;
1805         }
1806     p++;
1807     ch = peek(p);
1808     if (ch != '/')
1809         {
1810         error("no / on end tag");
1811         return p0;
1812         }
1813     p++;
1814     ch = peek(p);
1815     p = skipwhite(p);
1816     String closeTagName;
1817     p = getWord(p, closeTagName);
1818     if (openTagName != closeTagName)
1819         {
1820         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1821                 openTagName.c_str(), closeTagName.c_str());
1822         return p0;
1823         }
1824     p = skipwhite(p);
1825     if (peek(p) != '>')
1826         {
1827         error("no > on end tag for '%s'", closeTagName.c_str());
1828         return p0;
1829         }
1830     p++;
1831     // printf("close element:%s\n",closeTagName.c_str());
1832     p = skipwhite(p);
1833     return p;
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1841     parselen = len;
1842     parsebuf = buf;
1843     Element *rootNode = new Element("root");
1844     pos = parseVersion(pos);
1845     pos = parseDoctype(pos);
1846     pos = parseElement(pos, rootNode, 1);
1847     return rootNode;
1851 Element *Parser::parse(const char *buf, int pos, int len)
1853     XMLCh *charbuf = new XMLCh[len + 1];
1854     long i = 0;
1855     for ( ; i < len ; i++)
1856         charbuf[i] = (XMLCh)buf[i];
1857     charbuf[i] = '\0';
1859     Element *n = parse(charbuf, pos, len);
1860     delete[] charbuf;
1861     return n;
1864 Element *Parser::parse(const String &buf)
1866     long len = (long)buf.size();
1867     XMLCh *charbuf = new XMLCh[len + 1];
1868     long i = 0;
1869     for ( ; i < len ; i++)
1870         charbuf[i] = (XMLCh)buf[i];
1871     charbuf[i] = '\0';
1873     Element *n = parse(charbuf, 0, len);
1874     delete[] charbuf;
1875     return n;
1878 Element *Parser::parseFile(const String &fileName)
1881     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882     FILE *f = fopen(fileName.c_str(), "rb");
1883     if (!f)
1884         return NULL;
1886     struct stat  statBuf;
1887     if (fstat(fileno(f),&statBuf)<0)
1888         {
1889         fclose(f);
1890         return NULL;
1891         }
1892     long filelen = statBuf.st_size;
1894     //printf("length:%d\n",filelen);
1895     XMLCh *charbuf = new XMLCh[filelen + 1];
1896     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897         {
1898         *p = (XMLCh)fgetc(f);
1899         }
1900     fclose(f);
1901     charbuf[filelen] = '\0';
1904     /*
1905     printf("nrbytes:%d\n",wc_count);
1906     printf("buf:%ls\n======\n",charbuf);
1907     */
1908     Element *n = parse(charbuf, 0, filelen);
1909     delete[] charbuf;
1910     return n;
1913 //########################################################################
1914 //########################################################################
1915 //##  E N D    X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //##  U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934  *  A class that implements the W3C URI resource reference.
1935  */
1936 class URI
1938 public:
1940     typedef enum
1941         {
1942         SCHEME_NONE =0,
1943         SCHEME_DATA,
1944         SCHEME_HTTP,
1945         SCHEME_HTTPS,
1946         SCHEME_FTP,
1947         SCHEME_FILE,
1948         SCHEME_LDAP,
1949         SCHEME_MAILTO,
1950         SCHEME_NEWS,
1951         SCHEME_TELNET
1952         } SchemeTypes;
1954     /**
1955      *
1956      */
1957     URI()
1958         {
1959         init();
1960         }
1962     /**
1963      *
1964      */
1965     URI(const String &str)
1966         {
1967         init();
1968         parse(str);
1969         }
1972     /**
1973      *
1974      */
1975     URI(const char *str)
1976         {
1977         init();
1978         String domStr = str;
1979         parse(domStr);
1980         }
1983     /**
1984      *
1985      */
1986     URI(const URI &other)
1987         {
1988         init();
1989         assign(other);
1990         }
1993     /**
1994      *
1995      */
1996     URI &operator=(const URI &other)
1997         {
1998         init();
1999         assign(other);
2000         return *this;
2001         }
2004     /**
2005      *
2006      */
2007     virtual ~URI()
2008         {}
2012     /**
2013      *
2014      */
2015     virtual bool parse(const String &str);
2017     /**
2018      *
2019      */
2020     virtual String toString() const;
2022     /**
2023      *
2024      */
2025     virtual int getScheme() const;
2027     /**
2028      *
2029      */
2030     virtual String getSchemeStr() const;
2032     /**
2033      *
2034      */
2035     virtual String getAuthority() const;
2037     /**
2038      *  Same as getAuthority, but if the port has been specified
2039      *  as host:port , the port will not be included
2040      */
2041     virtual String getHost() const;
2043     /**
2044      *
2045      */
2046     virtual int getPort() const;
2048     /**
2049      *
2050      */
2051     virtual String getPath() const;
2053     /**
2054      *
2055      */
2056     virtual String getNativePath() const;
2058     /**
2059      *
2060      */
2061     virtual bool isAbsolute() const;
2063     /**
2064      *
2065      */
2066     virtual bool isOpaque() const;
2068     /**
2069      *
2070      */
2071     virtual String getQuery() const;
2073     /**
2074      *
2075      */
2076     virtual String getFragment() const;
2078     /**
2079      *
2080      */
2081     virtual URI resolve(const URI &other) const;
2083     /**
2084      *
2085      */
2086     virtual void normalize();
2088 private:
2090     /**
2091      *
2092      */
2093     void init()
2094         {
2095         parsebuf  = NULL;
2096         parselen  = 0;
2097         scheme    = SCHEME_NONE;
2098         schemeStr = "";
2099         port      = 0;
2100         authority = "";
2101         path      = "";
2102         absolute  = false;
2103         opaque    = false;
2104         query     = "";
2105         fragment  = "";
2106         }
2109     /**
2110      *
2111      */
2112     void assign(const URI &other)
2113         {
2114         scheme    = other.scheme;
2115         schemeStr = other.schemeStr;
2116         authority = other.authority;
2117         port      = other.port;
2118         path      = other.path;
2119         absolute  = other.absolute;
2120         opaque    = other.opaque;
2121         query     = other.query;
2122         fragment  = other.fragment;
2123         }
2125     int scheme;
2127     String schemeStr;
2129     String authority;
2131     bool portSpecified;
2133     int port;
2135     String path;
2137     bool absolute;
2139     bool opaque;
2141     String query;
2143     String fragment;
2145     void error(const char *fmt, ...);
2147     void trace(const char *fmt, ...);
2150     int peek(int p);
2152     int match(int p, const char *key);
2154     int parseScheme(int p);
2156     int parseHierarchicalPart(int p0);
2158     int parseQuery(int p0);
2160     int parseFragment(int p0);
2162     int parse(int p);
2164     char *parsebuf;
2166     int parselen;
2168 };
2172 typedef struct
2174     int         ival;
2175     const char *sval;
2176     int         port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2181     { URI::SCHEME_DATA,   "data:",    0 },
2182     { URI::SCHEME_HTTP,   "http:",   80 },
2183     { URI::SCHEME_HTTPS,  "https:", 443 },
2184     { URI::SCHEME_FTP,    "ftp",     12 },
2185     { URI::SCHEME_FILE,   "file:",    0 },
2186     { URI::SCHEME_LDAP,   "ldap:",  123 },
2187     { URI::SCHEME_MAILTO, "mailto:", 25 },
2188     { URI::SCHEME_NEWS,   "news:",  117 },
2189     { URI::SCHEME_TELNET, "telnet:", 23 },
2190     { 0,                  NULL,       0 }
2191 };
2194 String URI::toString() const
2196     String str = schemeStr;
2197     if (authority.size() > 0)
2198         {
2199         str.append("//");
2200         str.append(authority);
2201         }
2202     str.append(path);
2203     if (query.size() > 0)
2204         {
2205         str.append("?");
2206         str.append(query);
2207         }
2208     if (fragment.size() > 0)
2209         {
2210         str.append("#");
2211         str.append(fragment);
2212         }
2213     return str;
2217 int URI::getScheme() const
2219     return scheme;
2222 String URI::getSchemeStr() const
2224     return schemeStr;
2228 String URI::getAuthority() const
2230     String ret = authority;
2231     if (portSpecified && port>=0)
2232         {
2233         char buf[7];
2234         snprintf(buf, 6, ":%6d", port);
2235         ret.append(buf);
2236         }
2237     return ret;
2240 String URI::getHost() const
2242     return authority;
2245 int URI::getPort() const
2247     return port;
2251 String URI::getPath() const
2253     return path;
2256 String URI::getNativePath() const
2258     String npath;
2259 #ifdef __WIN32__
2260     unsigned int firstChar = 0;
2261     if (path.size() >= 3)
2262         {
2263         if (path[0] == '/' &&
2264             isLetter(path[1]) &&
2265             path[2] == ':')
2266             firstChar++;
2267          }
2268     for (unsigned int i=firstChar ; i<path.size() ; i++)
2269         {
2270         XMLCh ch = (XMLCh) path[i];
2271         if (ch == '/')
2272             npath.push_back((XMLCh)'\\');
2273         else
2274             npath.push_back(ch);
2275         }
2276 #else
2277     npath = path;
2278 #endif
2279     return npath;
2283 bool URI::isAbsolute() const
2285     return absolute;
2288 bool URI::isOpaque() const
2290     return opaque;
2294 String URI::getQuery() const
2296     return query;
2300 String URI::getFragment() const
2302     return fragment;
2306 URI URI::resolve(const URI &other) const
2308     //### According to w3c, this is handled in 3 cases
2310     //## 1
2311     if (opaque || other.isAbsolute())
2312         return other;
2314     //## 2
2315     if (other.fragment.size()  >  0 &&
2316         other.path.size()      == 0 &&
2317         other.scheme           == SCHEME_NONE &&
2318         other.authority.size() == 0 &&
2319         other.query.size()     == 0 )
2320         {
2321         URI fragUri = *this;
2322         fragUri.fragment = other.fragment;
2323         return fragUri;
2324         }
2326     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327     URI newUri;
2328     //# 3.1
2329     newUri.scheme    = scheme;
2330     newUri.schemeStr = schemeStr;
2331     newUri.query     = other.query;
2332     newUri.fragment  = other.fragment;
2333     if (other.authority.size() > 0)
2334         {
2335         //# 3.2
2336         if (absolute || other.absolute)
2337             newUri.absolute = true;
2338         newUri.authority = other.authority;
2339         newUri.port      = other.port;//part of authority
2340         newUri.path      = other.path;
2341         }
2342     else
2343         {
2344         //# 3.3
2345         if (other.absolute)
2346             {
2347             newUri.absolute = true;
2348             newUri.path     = other.path;
2349             }
2350         else
2351             {
2352             unsigned int pos = path.find_last_of('/');
2353             if (pos != path.npos)
2354                 {
2355                 String tpath = path.substr(0, pos+1);
2356                 tpath.append(other.path);
2357                 newUri.path = tpath;
2358                 }
2359             else
2360                 newUri.path = other.path;
2361             }
2362         }
2364     newUri.normalize();
2365     return newUri;
2370 /**
2371  *  This follows the Java URI algorithm:
2372  *   1. All "." segments are removed.
2373  *   2. If a ".." segment is preceded by a non-".." segment
2374  *          then both of these segments are removed. This step
2375  *          is repeated until it is no longer applicable.
2376  *   3. If the path is relative, and if its first segment
2377  *          contains a colon character (':'), then a "." segment
2378  *          is prepended. This prevents a relative URI with a path
2379  *          such as "a:b/c/d" from later being re-parsed as an
2380  *          opaque URI with a scheme of "a" and a scheme-specific
2381  *          part of "b/c/d". (Deviation from RFC 2396)
2382  */
2383 void URI::normalize()
2385     std::vector<String> segments;
2387     //## Collect segments
2388     if (path.size()<2)
2389         return;
2390     bool abs = false;
2391     unsigned int pos=0;
2392     if (path[0]=='/')
2393         {
2394         abs = true;
2395         pos++;
2396         }
2397     while (pos < path.size())
2398         {
2399         unsigned int pos2 = path.find('/', pos);
2400         if (pos2==path.npos)
2401             {
2402             String seg = path.substr(pos);
2403             //printf("last segment:%s\n", seg.c_str());
2404             segments.push_back(seg);
2405             break;
2406             }
2407         if (pos2>pos)
2408             {
2409             String seg = path.substr(pos, pos2-pos);
2410             //printf("segment:%s\n", seg.c_str());
2411             segments.push_back(seg);
2412             }
2413         pos = pos2;
2414         pos++;
2415         }
2417     //## Clean up (normalize) segments
2418     bool edited = false;
2419     std::vector<String>::iterator iter;
2420     for (iter=segments.begin() ; iter!=segments.end() ; )
2421         {
2422         String s = *iter;
2423         if (s == ".")
2424             {
2425             iter = segments.erase(iter);
2426             edited = true;
2427             }
2428         else if (s == ".." &&
2429                  iter != segments.begin() &&
2430                  *(iter-1) != "..")
2431             {
2432             iter--; //back up, then erase two entries
2433             iter = segments.erase(iter);
2434             iter = segments.erase(iter);
2435             edited = true;
2436             }
2437         else
2438             iter++;
2439         }
2441     //## Rebuild path, if necessary
2442     if (edited)
2443         {
2444         path.clear();
2445         if (abs)
2446             {
2447             path.append("/");
2448             }
2449         std::vector<String>::iterator iter;
2450         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451             {
2452             if (iter != segments.begin())
2453                 path.append("/");
2454             path.append(*iter);
2455             }
2456         }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2468     va_list args;
2469     fprintf(stderr, "URI error: ");
2470     va_start(args, fmt);
2471     vfprintf(stderr, fmt, args);
2472     va_end(args);
2473     fprintf(stderr, "\n");
2476 void URI::trace(const char *fmt, ...)
2478     va_list args;
2479     fprintf(stdout, "URI: ");
2480     va_start(args, fmt);
2481     vfprintf(stdout, fmt, args);
2482     va_end(args);
2483     fprintf(stdout, "\n");
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2497     if (p<0 || p>=parselen)
2498         return -1;
2499     return parsebuf[p];
2504 int URI::match(int p0, const char *key)
2506     int p = p0;
2507     while (p < parselen)
2508         {
2509         if (*key == '\0')
2510             return p;
2511         else if (*key != parsebuf[p])
2512             break;
2513         p++; key++;
2514         }
2515     return p0;
2518 //#########################################################################
2519 //#  Parsing is performed according to:
2520 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2525     int p = p0;
2526     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527         {
2528         int p2 = match(p, entry->sval);
2529         if (p2 > p)
2530             {
2531             schemeStr = entry->sval;
2532             scheme    = entry->ival;
2533             port      = entry->port;
2534             p = p2;
2535             return p;
2536             }
2537         }
2539     return p;
2543 int URI::parseHierarchicalPart(int p0)
2545     int p = p0;
2546     int ch;
2548     //# Authority field (host and port, for example)
2549     int p2 = match(p, "//");
2550     if (p2 > p)
2551         {
2552         p = p2;
2553         portSpecified = false;
2554         String portStr;
2555         while (p < parselen)
2556             {
2557             ch = peek(p);
2558             if (ch == '/')
2559                 break;
2560             else if (ch == ':')
2561                 portSpecified = true;
2562             else if (portSpecified)
2563                 portStr.push_back((XMLCh)ch);
2564             else
2565                 authority.push_back((XMLCh)ch);
2566             p++;
2567             }
2568         if (portStr.size() > 0)
2569             {
2570             char *pstr = (char *)portStr.c_str();
2571             char *endStr;
2572             long val = strtol(pstr, &endStr, 10);
2573             if (endStr > pstr) //successful parse?
2574                 port = val;
2575             }
2576         }
2578     //# Are we absolute?
2579     ch = peek(p);
2580     if (isLetter(ch) && peek(p+1)==':')
2581         {
2582         absolute = true;
2583         path.push_back((XMLCh)'/');
2584         }
2585     else if (ch == '/')
2586         {
2587         absolute = true;
2588         if (p>p0) //in other words, if '/' is not the first char
2589             opaque = true;
2590         path.push_back((XMLCh)ch);
2591         p++;
2592         }
2594     while (p < parselen)
2595         {
2596         ch = peek(p);
2597         if (ch == '?' || ch == '#')
2598             break;
2599         path.push_back((XMLCh)ch);
2600         p++;
2601         }
2603     return p;
2606 int URI::parseQuery(int p0)
2608     int p = p0;
2609     int ch = peek(p);
2610     if (ch != '?')
2611         return p0;
2613     p++;
2614     while (p < parselen)
2615         {
2616         ch = peek(p);
2617         if (ch == '#')
2618             break;
2619         query.push_back((XMLCh)ch);
2620         p++;
2621         }
2624     return p;
2627 int URI::parseFragment(int p0)
2630     int p = p0;
2631     int ch = peek(p);
2632     if (ch != '#')
2633         return p0;
2635     p++;
2636     while (p < parselen)
2637         {
2638         ch = peek(p);
2639         if (ch == '?')
2640             break;
2641         fragment.push_back((XMLCh)ch);
2642         p++;
2643         }
2646     return p;
2650 int URI::parse(int p0)
2653     int p = p0;
2655     int p2 = parseScheme(p);
2656     if (p2 < 0)
2657         {
2658         error("Scheme");
2659         return -1;
2660         }
2661     p = p2;
2664     p2 = parseHierarchicalPart(p);
2665     if (p2 < 0)
2666         {
2667         error("Hierarchical part");
2668         return -1;
2669         }
2670     p = p2;
2672     p2 = parseQuery(p);
2673     if (p2 < 0)
2674         {
2675         error("Query");
2676         return -1;
2677         }
2678     p = p2;
2681     p2 = parseFragment(p);
2682     if (p2 < 0)
2683         {
2684         error("Fragment");
2685         return -1;
2686         }
2687     p = p2;
2689     return p;
2695 bool URI::parse(const String &str)
2697     init();
2698     
2699     parselen = str.size();
2701     String tmp;
2702     for (unsigned int i=0 ; i<str.size() ; i++)
2703         {
2704         XMLCh ch = (XMLCh) str[i];
2705         if (ch == '\\')
2706             tmp.push_back((XMLCh)'/');
2707         else
2708             tmp.push_back(ch);
2709         }
2710     parsebuf = (char *) tmp.c_str();
2713     int p = parse(0);
2714     normalize();
2716     if (p < 0)
2717         {
2718         error("Syntax error");
2719         return false;
2720         }
2722     //printf("uri:%s\n", toString().c_str());
2723     //printf("path:%s\n", path.c_str());
2725     return true;
2736 //########################################################################
2737 //########################################################################
2738 //##  M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746  * This is the descriptor for a <fileset> item
2747  */
2748 class FileSet
2750 public:
2752     /**
2753      *
2754      */
2755     FileSet()
2756         {}
2758     /**
2759      *
2760      */
2761     FileSet(const FileSet &other)
2762         { assign(other); }
2764     /**
2765      *
2766      */
2767     FileSet &operator=(const FileSet &other)
2768         { assign(other); return *this; }
2770     /**
2771      *
2772      */
2773     virtual ~FileSet()
2774         {}
2776     /**
2777      *
2778      */
2779     String getDirectory()
2780         { return directory; }
2781         
2782     /**
2783      *
2784      */
2785     void setDirectory(const String &val)
2786         { directory = val; }
2788     /**
2789      *
2790      */
2791     void setFiles(const std::vector<String> &val)
2792         { files = val; }
2794     /**
2795      *
2796      */
2797     std::vector<String> getFiles()
2798         { return files; }
2799         
2800     /**
2801      *
2802      */
2803     void setIncludes(const std::vector<String> &val)
2804         { includes = val; }
2806     /**
2807      *
2808      */
2809     std::vector<String> getIncludes()
2810         { return includes; }
2811         
2812     /**
2813      *
2814      */
2815     void setExcludes(const std::vector<String> &val)
2816         { excludes = val; }
2818     /**
2819      *
2820      */
2821     std::vector<String> getExcludes()
2822         { return excludes; }
2823         
2824     /**
2825      *
2826      */
2827     unsigned int size()
2828         { return files.size(); }
2829         
2830     /**
2831      *
2832      */
2833     String operator[](int index)
2834         { return files[index]; }
2835         
2836     /**
2837      *
2838      */
2839     void clear()
2840         {
2841         directory = "";
2842         files.clear();
2843         includes.clear();
2844         excludes.clear();
2845         }
2846         
2848 private:
2850     void assign(const FileSet &other)
2851         {
2852         directory = other.directory;
2853         files     = other.files;
2854         includes  = other.includes;
2855         excludes  = other.excludes;
2856         }
2858     String directory;
2859     std::vector<String> files;
2860     std::vector<String> includes;
2861     std::vector<String> excludes;
2862 };
2867 //########################################################################
2868 //# M A K E    B A S E
2869 //########################################################################
2870 /**
2871  * Base class for all classes in this file
2872  */
2873 class MakeBase
2875 public:
2877     MakeBase()
2878         { line = 0; }
2879     virtual ~MakeBase()
2880         {}
2882     /**
2883      *     Return the URI of the file associated with this object 
2884      */     
2885     URI getURI()
2886         { return uri; }
2888     /**
2889      * Set the uri to the given string
2890      */
2891     void setURI(const String &uristr)
2892         { uri.parse(uristr); }
2894     /**
2895      *  Resolve another path relative to this one
2896      */
2897     String resolve(const String &otherPath);
2899     /**
2900      *  Get an element attribute, performing substitutions if necessary
2901      */
2902     bool getAttribute(Element *elem, const String &name, String &result);
2904     /**
2905      * Get an element value, performing substitutions if necessary
2906      */
2907     bool getValue(Element *elem, String &result);
2908     
2909     /**
2910      * Set the current line number in the file
2911      */         
2912     void setLine(int val)
2913         { line = val; }
2914         
2915     /**
2916      * Get the current line number in the file
2917      */         
2918     int getLine()
2919         { return line; }
2921 protected:
2923     /**
2924      *    The path to the file associated with this object
2925      */     
2926     URI uri;
2929     /**
2930      *  Print a printf()-like formatted error message
2931      */
2932     void error(const char *fmt, ...);
2934     /**
2935      *  Print a printf()-like formatted trace message
2936      */
2937     void status(const char *fmt, ...);
2939     /**
2940      *  Print a printf()-like formatted trace message
2941      */
2942     void trace(const char *fmt, ...);
2944     /**
2945      *  Check if a given string matches a given regex pattern
2946      */
2947     bool regexMatch(const String &str, const String &pattern);
2949     /**
2950      *
2951      */
2952     String getSuffix(const String &fname);
2954     /**
2955      * Break up a string into substrings delimited the characters
2956      * in delimiters.  Null-length substrings are ignored
2957      */  
2958     std::vector<String> tokenize(const String &val,
2959                           const String &delimiters);
2961     /**
2962      *  replace runs of whitespace with a space
2963      */
2964     String strip(const String &s);
2966     /**
2967      *  remove leading whitespace from each line
2968      */
2969     String leftJustify(const String &s);
2971     /**
2972      *  remove leading and trailing whitespace from string
2973      */
2974     String trim(const String &s);
2976     /**
2977      * Return the native format of the canonical
2978      * path which we store
2979      */
2980     String getNativePath(const String &path);
2982     /**
2983      * Execute a shell command.  Outbuf is a ref to a string
2984      * to catch the result.     
2985      */         
2986     bool executeCommand(const String &call,
2987                         const String &inbuf,
2988                         String &outbuf,
2989                         String &errbuf);
2990     /**
2991      * List all directories in a given base and starting directory
2992      * It is usually called like:
2993      *        bool ret = listDirectories("src", "", result);    
2994      */         
2995     bool listDirectories(const String &baseName,
2996                          const String &dirname,
2997                          std::vector<String> &res);
2999     /**
3000      * Find all files in the named directory 
3001      */         
3002     bool listFiles(const String &baseName,
3003                    const String &dirname,
3004                    std::vector<String> &result);
3006     /**
3007      * Perform a listing for a fileset 
3008      */         
3009     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3011     /**
3012      * Parse a <patternset>
3013      */  
3014     bool parsePatternSet(Element *elem,
3015                        MakeBase &propRef,
3016                        std::vector<String> &includes,
3017                        std::vector<String> &excludes);
3019     /**
3020      * Parse a <fileset> entry, and determine which files
3021      * should be included
3022      */  
3023     bool parseFileSet(Element *elem,
3024                     MakeBase &propRef,
3025                     FileSet &fileSet);
3027     /**
3028      * Return this object's property list
3029      */
3030     virtual std::map<String, String> &getProperties()
3031         { return properties; }
3033     /**
3034      * Return a named property if found, else a null string
3035      */
3036     virtual String getProperty(const String &name)
3037         {
3038         String val;
3039         std::map<String, String>::iterator iter;
3040         iter = properties.find(name);
3041         if (iter != properties.end())
3042             val = iter->second;
3043         return val;
3044         }
3047     std::map<String, String> properties;
3049     /**
3050      * Turn 'true' and 'false' into boolean values
3051      */             
3052     bool getBool(const String &str, bool &val);
3054     /**
3055      * Create a directory, making intermediate dirs
3056      * if necessary
3057      */                  
3058     bool createDirectory(const String &dirname);
3060     /**
3061      * Delete a directory and its children if desired
3062      */
3063     bool removeDirectory(const String &dirName);
3065     /**
3066      * Copy a file from one name to another. Perform only if needed
3067      */ 
3068     bool copyFile(const String &srcFile, const String &destFile);
3070     /**
3071      * Tests if the file exists and is a regular file
3072      */ 
3073     bool isRegularFile(const String &fileName);
3075     /**
3076      * Tests if the file exists and is a directory
3077      */ 
3078     bool isDirectory(const String &fileName);
3080     /**
3081      * Tests is the modification date of fileA is newer than fileB
3082      */ 
3083     bool isNewerThan(const String &fileA, const String &fileB);
3085 private:
3087     /**
3088      * replace variable refs like ${a} with their values
3089      */         
3090     bool getSubstitutions(const String &s, String &result);
3092     int line;
3095 };
3100 /**
3101  *  Print a printf()-like formatted error message
3102  */
3103 void MakeBase::error(const char *fmt, ...)
3105     va_list args;
3106     va_start(args,fmt);
3107     fprintf(stderr, "Make error line %d: ", line);
3108     vfprintf(stderr, fmt, args);
3109     fprintf(stderr, "\n");
3110     va_end(args) ;
3115 /**
3116  *  Print a printf()-like formatted trace message
3117  */
3118 void MakeBase::status(const char *fmt, ...)
3120     va_list args;
3121     va_start(args,fmt);
3122     //fprintf(stdout, " ");
3123     vfprintf(stdout, fmt, args);
3124     fprintf(stdout, "\n");
3125     va_end(args) ;
3130 /**
3131  *  Resolve another path relative to this one
3132  */
3133 String MakeBase::resolve(const String &otherPath)
3135     URI otherURI(otherPath);
3136     URI fullURI = uri.resolve(otherURI);
3137     String ret = fullURI.toString();
3138     return ret;
3142 /**
3143  *  Print a printf()-like formatted trace message
3144  */
3145 void MakeBase::trace(const char *fmt, ...)
3147     va_list args;
3148     va_start(args,fmt);
3149     fprintf(stdout, "Make: ");
3150     vfprintf(stdout, fmt, args);
3151     fprintf(stdout, "\n");
3152     va_end(args) ;
3157 /**
3158  *  Check if a given string matches a given regex pattern
3159  */
3160 bool MakeBase::regexMatch(const String &str, const String &pattern)
3162     const TRexChar *terror = NULL;
3163     const TRexChar *cpat = pattern.c_str();
3164     TRex *expr = trex_compile(cpat, &terror);
3165     if (!expr)
3166         {
3167         if (!terror)
3168             terror = "undefined";
3169         error("compilation error [%s]!\n", terror);
3170         return false;
3171         } 
3173     bool ret = true;
3175     const TRexChar *cstr = str.c_str();
3176     if (trex_match(expr, cstr))
3177         {
3178         ret = true;
3179         }
3180     else
3181         {
3182         ret = false;
3183         }
3185     trex_free(expr);
3187     return ret;
3190 /**
3191  *  Return the suffix, if any, of a file name
3192  */
3193 String MakeBase::getSuffix(const String &fname)
3195     if (fname.size() < 2)
3196         return "";
3197     unsigned int pos = fname.find_last_of('.');
3198     if (pos == fname.npos)
3199         return "";
3200     pos++;
3201     String res = fname.substr(pos, fname.size()-pos);
3202     //trace("suffix:%s", res.c_str()); 
3203     return res;
3208 /**
3209  * Break up a string into substrings delimited the characters
3210  * in delimiters.  Null-length substrings are ignored
3211  */  
3212 std::vector<String> MakeBase::tokenize(const String &str,
3213                                 const String &delimiters)
3216     std::vector<String> res;
3217     char *del = (char *)delimiters.c_str();
3218     String dmp;
3219     for (unsigned int i=0 ; i<str.size() ; i++)
3220         {
3221         char ch = str[i];
3222         char *p = (char *)0;
3223         for (p=del ; *p ; p++)
3224             if (*p == ch)
3225                 break;
3226         if (*p)
3227             {
3228             if (dmp.size() > 0)
3229                 {
3230                 res.push_back(dmp);
3231                 dmp.clear();
3232                 }
3233             }
3234         else
3235             {
3236             dmp.push_back(ch);
3237             }
3238         }
3239     //Add tail
3240     if (dmp.size() > 0)
3241         {
3242         res.push_back(dmp);
3243         dmp.clear();
3244         }
3246     return res;
3251 /**
3252  *  replace runs of whitespace with a single space
3253  */
3254 String MakeBase::strip(const String &s)
3256     int len = s.size();
3257     String stripped;
3258     for (int i = 0 ; i<len ; i++)
3259         {
3260         char ch = s[i];
3261         if (isspace(ch))
3262             {
3263             stripped.push_back(' ');
3264             for ( ; i<len ; i++)
3265                 {
3266                 ch = s[i];
3267                 if (!isspace(ch))
3268                     {
3269                     stripped.push_back(ch);
3270                     break;
3271                     }
3272                 }
3273             }
3274         else
3275             {
3276             stripped.push_back(ch);
3277             }
3278         }
3279     return stripped;
3282 /**
3283  *  remove leading whitespace from each line
3284  */
3285 String MakeBase::leftJustify(const String &s)
3287     String out;
3288     int len = s.size();
3289     for (int i = 0 ; i<len ; )
3290         {
3291         char ch;
3292         //Skip to first visible character
3293         while (i<len)
3294             {
3295             ch = s[i];
3296             if (ch == '\n' || ch == '\r'
3297               || !isspace(ch))
3298                   break;
3299             i++;
3300             }
3301         //Copy the rest of the line
3302         while (i<len)
3303             {
3304             ch = s[i];
3305             if (ch == '\n' || ch == '\r')
3306                 {
3307                 if (ch != '\r')
3308                     out.push_back('\n');
3309                 i++;
3310                 break;
3311                 }
3312             else
3313                 {
3314                 out.push_back(ch);
3315                 }
3316             i++;
3317             }
3318         }
3319     return out;
3323 /**
3324  *  Removes whitespace from beginning and end of a string
3325  */
3326 String MakeBase::trim(const String &s)
3328     if (s.size() < 1)
3329         return s;
3330     
3331     //Find first non-ws char
3332     unsigned int begin = 0;
3333     for ( ; begin < s.size() ; begin++)
3334         {
3335         if (!isspace(s[begin]))
3336             break;
3337         }
3339     //Find first non-ws char, going in reverse
3340     unsigned int end = s.size() - 1;
3341     for ( ; end > begin ; end--)
3342         {
3343         if (!isspace(s[end]))
3344             break;
3345         }
3346     //trace("begin:%d  end:%d", begin, end);
3348     String res = s.substr(begin, end-begin+1);
3349     return res;
3352 /**
3353  * Return the native format of the canonical
3354  * path which we store
3355  */
3356 String MakeBase::getNativePath(const String &path)
3358 #ifdef __WIN32__
3359     String npath;
3360     unsigned int firstChar = 0;
3361     if (path.size() >= 3)
3362         {
3363         if (path[0] == '/' &&
3364             isalpha(path[1]) &&
3365             path[2] == ':')
3366             firstChar++;
3367         }
3368     for (unsigned int i=firstChar ; i<path.size() ; i++)
3369         {
3370         char ch = path[i];
3371         if (ch == '/')
3372             npath.push_back('\\');
3373         else
3374             npath.push_back(ch);
3375         }
3376     return npath;
3377 #else
3378     return path;
3379 #endif
3383 #ifdef __WIN32__
3384 #include <tchar.h>
3386 static String win32LastError()
3389     DWORD dw = GetLastError(); 
3391     LPVOID str;
3392     FormatMessage(
3393         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3394         FORMAT_MESSAGE_FROM_SYSTEM,
3395         NULL,
3396         dw,
3397         0,
3398         (LPTSTR) &str,
3399         0, NULL );
3400     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3401     if(p != NULL)
3402         { // lose CRLF
3403         *p = _T('\0');
3404         }
3405     String ret = (char *)str;
3406     LocalFree(str);
3408     return ret;
3410 #endif
3414 /**
3415  * Execute a system call, using pipes to send data to the
3416  * program's stdin,  and reading stdout and stderr.
3417  */
3418 bool MakeBase::executeCommand(const String &command,
3419                               const String &inbuf,
3420                               String &outbuf,
3421                               String &errbuf)
3424     status("============ cmd ============\n%s\n=============================",
3425                 command.c_str());
3427     outbuf.clear();
3428     errbuf.clear();
3429     
3430 #ifdef __WIN32__
3432     /*
3433     I really hate having win32 code in this program, but the
3434     read buffer in command.com and cmd.exe are just too small
3435     for the large commands we need for compiling and linking.
3436     */
3438     bool ret = true;
3440     //# Allocate a separate buffer for safety
3441     char *paramBuf = new char[command.size() + 1];
3442     if (!paramBuf)
3443        {
3444        error("executeCommand cannot allocate command buffer");
3445        return false;
3446        }
3447     strcpy(paramBuf, (char *)command.c_str());
3449     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3450     //# to see how Win32 pipes work
3452     //# Create pipes
3453     SECURITY_ATTRIBUTES saAttr; 
3454     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3455     saAttr.bInheritHandle = TRUE; 
3456     saAttr.lpSecurityDescriptor = NULL; 
3457     HANDLE stdinRead,  stdinWrite;
3458     HANDLE stdoutRead, stdoutWrite;
3459     HANDLE stderrRead, stderrWrite;
3460     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3461         {
3462         error("executeProgram: could not create pipe");
3463         delete[] paramBuf;
3464         return false;
3465         } 
3466     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3467     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3468         {
3469         error("executeProgram: could not create pipe");
3470         delete[] paramBuf;
3471         return false;
3472         } 
3473     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3474     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3475         {
3476         error("executeProgram: could not create pipe");
3477         delete[] paramBuf;
3478         return false;
3479         } 
3480     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3482     // Create the process
3483     STARTUPINFO siStartupInfo;
3484     PROCESS_INFORMATION piProcessInfo;
3485     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3486     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3487     siStartupInfo.cb = sizeof(siStartupInfo);
3488     siStartupInfo.hStdError   =  stderrWrite;
3489     siStartupInfo.hStdOutput  =  stdoutWrite;
3490     siStartupInfo.hStdInput   =  stdinRead;
3491     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3492    
3493     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3494                 0, NULL, NULL, &siStartupInfo,
3495                 &piProcessInfo))
3496         {
3497         error("executeCommand : could not create process : %s",
3498                     win32LastError().c_str());
3499         ret = false;
3500         }
3502     delete[] paramBuf;
3504     DWORD bytesWritten;
3505     if (inbuf.size()>0 &&
3506         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3507                &bytesWritten, NULL))
3508         {
3509         error("executeCommand: could not write to pipe");
3510         return false;
3511         }    
3512     if (!CloseHandle(stdinWrite))
3513         {          
3514         error("executeCommand: could not close write pipe");
3515         return false;
3516         }
3517     if (!CloseHandle(stdoutWrite))
3518         {
3519         error("executeCommand: could not close read pipe");
3520         return false;
3521         }
3522     if (!CloseHandle(stderrWrite))
3523         {
3524         error("executeCommand: could not close read pipe");
3525         return false;
3526         }
3528     bool lastLoop = false;
3529     while (true)
3530         {
3531         DWORD avail;
3532         DWORD bytesRead;
3533         char readBuf[4096];
3535         //trace("## stderr");
3536         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3537         if (avail > 0)
3538             {
3539             bytesRead = 0;
3540             if (avail>4096) avail = 4096;
3541             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3542             if (bytesRead > 0)
3543                 {
3544                 for (unsigned int i=0 ; i<bytesRead ; i++)
3545                     errbuf.push_back(readBuf[i]);
3546                 }
3547             }
3549         //trace("## stdout");
3550         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3551         if (avail > 0)
3552             {
3553             bytesRead = 0;
3554             if (avail>4096) avail = 4096;
3555             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3556             if (bytesRead > 0)
3557                 {
3558                 for (unsigned int i=0 ; i<bytesRead ; i++)
3559                     outbuf.push_back(readBuf[i]);
3560                 }
3561             }
3562             
3563         //Was this the final check after program done?
3564         if (lastLoop)
3565             break;
3567         DWORD exitCode;
3568         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3569         if (exitCode != STILL_ACTIVE)
3570             lastLoop = true;
3572         Sleep(10);
3573         }    
3574     //trace("outbuf:%s", outbuf.c_str());
3575     if (!CloseHandle(stdoutRead))
3576         {
3577         error("executeCommand: could not close read pipe");
3578         return false;
3579         }
3580     if (!CloseHandle(stderrRead))
3581         {
3582         error("executeCommand: could not close read pipe");
3583         return false;
3584         }
3586     DWORD exitCode;
3587     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3588     //trace("exit code:%d", exitCode);
3589     if (exitCode != 0)
3590         {
3591         ret = false;
3592         }
3593     
3594     CloseHandle(piProcessInfo.hProcess);
3595     CloseHandle(piProcessInfo.hThread);
3597     return ret;
3599 #else //do it unix-style
3601     String s;
3602     FILE *f = popen(command.c_str(), "r");
3603     int errnum = 0;
3604     if (f)
3605         {
3606         while (true)
3607             {
3608             int ch = fgetc(f);
3609             if (ch < 0)
3610                 break;
3611             s.push_back((char)ch);
3612             }
3613         errnum = pclose(f);
3614         }
3615     outbuf = s;
3616     if (errnum != 0)
3617         {
3618         error("exec of command '%s' failed : %s",
3619              command.c_str(), strerror(errno));
3620         return false;
3621         }
3622     else
3623         return true;
3625 #endif
3626
3631 bool MakeBase::listDirectories(const String &baseName,
3632                               const String &dirName,
3633                               std::vector<String> &res)
3635     res.push_back(dirName);
3636     String fullPath = baseName;
3637     if (dirName.size()>0)
3638         {
3639         fullPath.append("/");
3640         fullPath.append(dirName);
3641         }
3642     DIR *dir = opendir(fullPath.c_str());
3643     while (true)
3644         {
3645         struct dirent *de = readdir(dir);
3646         if (!de)
3647             break;
3649         //Get the directory member name
3650         String s = de->d_name;
3651         if (s.size() == 0 || s[0] == '.')
3652             continue;
3653         String childName = dirName;
3654         childName.append("/");
3655         childName.append(s);
3657         String fullChildPath = baseName;
3658         fullChildPath.append("/");
3659         fullChildPath.append(childName);
3660         struct stat finfo;
3661         String childNative = getNativePath(fullChildPath);
3662         if (stat(childNative.c_str(), &finfo)<0)
3663             {
3664             error("cannot stat file:%s", childNative.c_str());
3665             }
3666         else if (S_ISDIR(finfo.st_mode))
3667             {
3668             //trace("directory: %s", childName.c_str());
3669             if (!listDirectories(baseName, childName, res))
3670                 return false;
3671             }
3672         }
3673     closedir(dir);
3675     return true;
3679 bool MakeBase::listFiles(const String &baseDir,
3680                          const String &dirName,
3681                          std::vector<String> &res)
3683     String fullDir = baseDir;
3684     if (dirName.size()>0)
3685         {
3686         fullDir.append("/");
3687         fullDir.append(dirName);
3688         }
3689     String dirNative = getNativePath(fullDir);
3691     std::vector<String> subdirs;
3692     DIR *dir = opendir(dirNative.c_str());
3693     if (!dir)
3694         {
3695         error("Could not open directory %s : %s",
3696               dirNative.c_str(), strerror(errno));
3697         return false;
3698         }
3699     while (true)
3700         {
3701         struct dirent *de = readdir(dir);
3702         if (!de)
3703             break;
3705         //Get the directory member name
3706         String s = de->d_name;
3707         if (s.size() == 0 || s[0] == '.')
3708             continue;
3709         String childName;
3710         if (dirName.size()>0)
3711             {
3712             childName.append(dirName);
3713             childName.append("/");
3714             }
3715         childName.append(s);
3716         String fullChild = baseDir;
3717         fullChild.append("/");
3718         fullChild.append(childName);
3719         
3720         if (isDirectory(fullChild))
3721             {
3722             //trace("directory: %s", childName.c_str());
3723             if (!listFiles(baseDir, childName, res))
3724                 return false;
3725             continue;
3726             }
3727         else if (!isRegularFile(fullChild))
3728             {
3729             error("unknown file:%s", childName.c_str());
3730             return false;
3731             }
3733        //all done!
3734         res.push_back(childName);
3736         }
3737     closedir(dir);
3739     return true;
3743 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3745     String baseDir = propRef.resolve(fileSet.getDirectory());
3746     std::vector<String> fileList;
3747     if (!listFiles(baseDir, "", fileList))
3748         return false;
3750     std::vector<String> includes = fileSet.getIncludes();
3751     std::vector<String> excludes = fileSet.getExcludes();
3753     std::vector<String> incs;
3754     std::vector<String>::iterator iter;
3756     std::sort(fileList.begin(), fileList.end());
3758     //If there are <includes>, then add files to the output
3759     //in the order of the include list
3760     if (includes.size()==0)
3761         incs = fileList;
3762     else
3763         {
3764         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3765             {
3766             String pattern = *iter;
3767             std::vector<String>::iterator siter;
3768             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3769                 {
3770                 String s = *siter;
3771                 if (regexMatch(s, pattern))
3772                     {
3773                     //trace("INCLUDED:%s", s.c_str());
3774                     incs.push_back(s);
3775                     }
3776                 }
3777             }
3778         }
3780     //Now trim off the <excludes>
3781     std::vector<String> res;
3782     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3783         {
3784         String s = *iter;
3785         bool skipme = false;
3786         std::vector<String>::iterator siter;
3787         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3788             {
3789             String pattern = *siter;
3790             if (regexMatch(s, pattern))
3791                 {
3792                 //trace("EXCLUDED:%s", s.c_str());
3793                 skipme = true;
3794                 break;
3795                 }
3796             }
3797         if (!skipme)
3798             res.push_back(s);
3799         }
3800         
3801     fileSet.setFiles(res);
3803     return true;
3810 bool MakeBase::getSubstitutions(const String &str, String &result)
3812     String s = trim(str);
3813     int len = (int)s.size();
3814     String val;
3815     for (int i=0 ; i<len ; i++)
3816         {
3817         char ch = s[i];
3818         if (ch == '$' && s[i+1] == '{')
3819             {
3820             String varname;
3821             int j = i+2;
3822             for ( ; j<len ; j++)
3823                 {
3824                 ch = s[j];
3825                 if (ch == '$' && s[j+1] == '{')
3826                     {
3827                     error("attribute %s cannot have nested variable references",
3828                            s.c_str());
3829                     return false;
3830                     }
3831                 else if (ch == '}')
3832                     {
3833                     std::map<String, String>::iterator iter;
3834                     iter = properties.find(trim(varname));
3835                     if (iter != properties.end())
3836                         {
3837                         val.append(iter->second);
3838                         }
3839                     else
3840                         {
3841                         error("property ${%s} not found", varname.c_str());
3842                         return false;
3843                         }
3844                     break;
3845                     }
3846                 else
3847                     {
3848                     varname.push_back(ch);
3849                     }
3850                 }
3851             i = j;
3852             }
3853         else
3854             {
3855             val.push_back(ch);
3856             }
3857         }
3858     result = val;
3859     return true;
3863 bool MakeBase::getAttribute(Element *elem, const String &name,
3864                                     String &result)
3866     String s = elem->getAttribute(name);
3867     return getSubstitutions(s, result);
3871 bool MakeBase::getValue(Element *elem, String &result)
3873     String s = elem->getValue();
3874     //Replace all runs of whitespace with a single space
3875     return getSubstitutions(s, result);
3879 /**
3880  * Turn 'true' and 'false' into boolean values
3881  */             
3882 bool MakeBase::getBool(const String &str, bool &val)
3884     if (str == "true")
3885         val = true;
3886     else if (str == "false")
3887         val = false;
3888     else
3889         {
3890         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3891         return false;
3892         }
3893     return true;
3899 /**
3900  * Parse a <patternset> entry
3901  */  
3902 bool MakeBase::parsePatternSet(Element *elem,
3903                           MakeBase &propRef,
3904                           std::vector<String> &includes,
3905                           std::vector<String> &excludes
3906                           )
3908     std::vector<Element *> children  = elem->getChildren();
3909     for (unsigned int i=0 ; i<children.size() ; i++)
3910         {
3911         Element *child = children[i];
3912         String tagName = child->getName();
3913         if (tagName == "exclude")
3914             {
3915             String fname;
3916             if (!propRef.getAttribute(child, "name", fname))
3917                 return false;
3918             //trace("EXCLUDE: %s", fname.c_str());
3919             excludes.push_back(fname);
3920             }
3921         else if (tagName == "include")
3922             {
3923             String fname;
3924             if (!propRef.getAttribute(child, "name", fname))
3925                 return false;
3926             //trace("INCLUDE: %s", fname.c_str());
3927             includes.push_back(fname);
3928             }
3929         }
3931     return true;
3937 /**
3938  * Parse a <fileset> entry, and determine which files
3939  * should be included
3940  */  
3941 bool MakeBase::parseFileSet(Element *elem,
3942                           MakeBase &propRef,
3943                           FileSet &fileSet)
3945     String name = elem->getName();
3946     if (name != "fileset")
3947         {
3948         error("expected <fileset>");
3949         return false;
3950         }
3953     std::vector<String> includes;
3954     std::vector<String> excludes;
3956     //A fileset has one implied patternset
3957     if (!parsePatternSet(elem, propRef, includes, excludes))
3958         {
3959         return false;
3960         }
3961     //Look for child tags, including more patternsets
3962     std::vector<Element *> children  = elem->getChildren();
3963     for (unsigned int i=0 ; i<children.size() ; i++)
3964         {
3965         Element *child = children[i];
3966         String tagName = child->getName();
3967         if (tagName == "patternset")
3968             {
3969             if (!parsePatternSet(child, propRef, includes, excludes))
3970                 {
3971                 return false;
3972                 }
3973             }
3974         }
3976     String dir;
3977     //Now do the stuff
3978     //Get the base directory for reading file names
3979     if (!propRef.getAttribute(elem, "dir", dir))
3980         return false;
3982     fileSet.setDirectory(dir);
3983     fileSet.setIncludes(includes);
3984     fileSet.setExcludes(excludes);
3985     
3986     /*
3987     std::vector<String> fileList;
3988     if (dir.size() > 0)
3989         {
3990         String baseDir = propRef.resolve(dir);
3991         if (!listFiles(baseDir, "", includes, excludes, fileList))
3992             return false;
3993         }
3994     std::sort(fileList.begin(), fileList.end());
3995     result = fileList;
3996     */
3998     
3999     /*
4000     for (unsigned int i=0 ; i<result.size() ; i++)
4001         {
4002         trace("RES:%s", result[i].c_str());
4003         }
4004     */
4006     
4007     return true;
4012 /**
4013  * Create a directory, making intermediate dirs
4014  * if necessary
4015  */                  
4016 bool MakeBase::createDirectory(const String &dirname)
4018     //trace("## createDirectory: %s", dirname.c_str());
4019     //## first check if it exists
4020     struct stat finfo;
4021     String nativeDir = getNativePath(dirname);
4022     char *cnative = (char *) nativeDir.c_str();
4023 #ifdef __WIN32__
4024     if (strlen(cnative)==2 && cnative[1]==':')
4025         return true;
4026 #endif
4027     if (stat(cnative, &finfo)==0)
4028         {
4029         if (!S_ISDIR(finfo.st_mode))
4030             {
4031             error("mkdir: file %s exists but is not a directory",
4032                   cnative);
4033             return false;
4034             }
4035         else //exists
4036             {
4037             return true;
4038             }
4039         }
4041     //## 2: pull off the last path segment, if any,
4042     //## to make the dir 'above' this one, if necessary
4043     unsigned int pos = dirname.find_last_of('/');
4044     if (pos>0 && pos != dirname.npos)
4045         {
4046         String subpath = dirname.substr(0, pos);
4047         //A letter root (c:) ?
4048         if (!createDirectory(subpath))
4049             return false;
4050         }
4051         
4052     //## 3: now make
4053 #ifdef __WIN32__
4054     if (mkdir(cnative)<0)
4055 #else
4056     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4057 #endif
4058         {
4059         error("cannot make directory '%s' : %s",
4060                  cnative, strerror(errno));
4061         return false;
4062         }
4063         
4064     return true;
4068 /**
4069  * Remove a directory recursively
4070  */ 
4071 bool MakeBase::removeDirectory(const String &dirName)
4073     char *dname = (char *)dirName.c_str();
4075     DIR *dir = opendir(dname);
4076     if (!dir)
4077         {
4078         //# Let this fail nicely.
4079         return true;
4080         //error("error opening directory %s : %s", dname, strerror(errno));
4081         //return false;
4082         }
4083     
4084     while (true)
4085         {
4086         struct dirent *de = readdir(dir);
4087         if (!de)
4088             break;
4090         //Get the directory member name
4091         String s = de->d_name;
4092         if (s.size() == 0 || s[0] == '.')
4093             continue;
4094         String childName;
4095         if (dirName.size() > 0)
4096             {
4097             childName.append(dirName);
4098             childName.append("/");
4099             }
4100         childName.append(s);
4103         struct stat finfo;
4104         String childNative = getNativePath(childName);
4105         char *cnative = (char *)childNative.c_str();
4106         if (stat(cnative, &finfo)<0)
4107             {
4108             error("cannot stat file:%s", cnative);
4109             }
4110         else if (S_ISDIR(finfo.st_mode))
4111             {
4112             //trace("DEL dir: %s", childName.c_str());
4113             if (!removeDirectory(childName))
4114                 {
4115                 return false;
4116                 }
4117             }
4118         else if (!S_ISREG(finfo.st_mode))
4119             {
4120             //trace("not regular: %s", cnative);
4121             }
4122         else
4123             {
4124             //trace("DEL file: %s", childName.c_str());
4125             if (remove(cnative)<0)
4126                 {
4127                 error("error deleting %s : %s",
4128                      cnative, strerror(errno));
4129                 return false;
4130                 }
4131             }
4132         }
4133     closedir(dir);
4135     //Now delete the directory
4136     String native = getNativePath(dirName);
4137     if (rmdir(native.c_str())<0)
4138         {
4139         error("could not delete directory %s : %s",
4140             native.c_str() , strerror(errno));
4141         return false;
4142         }
4144     return true;
4145     
4149 /**
4150  * Copy a file from one name to another. Perform only if needed
4151  */ 
4152 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4154     //# 1 Check up-to-date times
4155     String srcNative = getNativePath(srcFile);
4156     struct stat srcinfo;
4157     if (stat(srcNative.c_str(), &srcinfo)<0)
4158         {
4159         error("source file %s for copy does not exist",
4160                  srcNative.c_str());
4161         return false;
4162         }
4164     String destNative = getNativePath(destFile);
4165     struct stat destinfo;
4166     if (stat(destNative.c_str(), &destinfo)==0)
4167         {
4168         if (destinfo.st_mtime >= srcinfo.st_mtime)
4169             return true;
4170         }
4171         
4172     //# 2 prepare a destination directory if necessary
4173     unsigned int pos = destFile.find_last_of('/');
4174     if (pos != destFile.npos)
4175         {
4176         String subpath = destFile.substr(0, pos);
4177         if (!createDirectory(subpath))
4178             return false;
4179         }
4181     //# 3 do the data copy
4182 #ifndef __WIN32__
4184     FILE *srcf = fopen(srcNative.c_str(), "rb");
4185     if (!srcf)
4186         {
4187         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4188         return false;
4189         }
4190     FILE *destf = fopen(destNative.c_str(), "wb");
4191     if (!destf)
4192         {
4193         error("copyFile cannot open %s for writing", srcNative.c_str());
4194         return false;
4195         }
4197     while (!feof(srcf))
4198         {
4199         int ch = fgetc(srcf);
4200         if (ch<0)
4201             break;
4202         fputc(ch, destf);
4203         }
4205     fclose(destf);
4206     fclose(srcf);
4208 #else
4209     
4210     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4211         {
4212         error("copyFile from %s to %s failed",
4213              srcNative.c_str(), destNative.c_str());
4214         return false;
4215         }
4216         
4217 #endif /* __WIN32__ */
4220     return true;
4225 /**
4226  * Tests if the file exists and is a regular file
4227  */ 
4228 bool MakeBase::isRegularFile(const String &fileName)
4230     String native = getNativePath(fileName);
4231     struct stat finfo;
4232     
4233     //Exists?
4234     if (stat(native.c_str(), &finfo)<0)
4235         return false;
4238     //check the file mode
4239     if (!S_ISREG(finfo.st_mode))
4240         return false;
4242     return true;
4245 /**
4246  * Tests if the file exists and is a directory
4247  */ 
4248 bool MakeBase::isDirectory(const String &fileName)
4250     String native = getNativePath(fileName);
4251     struct stat finfo;
4252     
4253     //Exists?
4254     if (stat(native.c_str(), &finfo)<0)
4255         return false;
4258     //check the file mode
4259     if (!S_ISDIR(finfo.st_mode))
4260         return false;
4262     return true;
4267 /**
4268  * Tests is the modification of fileA is newer than fileB
4269  */ 
4270 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4272     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4273     String nativeA = getNativePath(fileA);
4274     struct stat infoA;
4275     //IF source does not exist, NOT newer
4276     if (stat(nativeA.c_str(), &infoA)<0)
4277         {
4278         return false;
4279         }
4281     String nativeB = getNativePath(fileB);
4282     struct stat infoB;
4283     //IF dest does not exist, YES, newer
4284     if (stat(nativeB.c_str(), &infoB)<0)
4285         {
4286         return true;
4287         }
4289     //check the actual times
4290     if (infoA.st_mtime > infoB.st_mtime)
4291         {
4292         return true;
4293         }
4295     return false;
4299 //########################################################################
4300 //# P K G    C O N F I G
4301 //########################################################################
4303 /**
4304  *
4305  */
4306 class PkgConfig : public MakeBase
4309 public:
4311     /**
4312      *
4313      */
4314     PkgConfig()
4315         { init(); }
4317     /**
4318      *
4319      */
4320     PkgConfig(const String &namearg)
4321         { init(); name = namearg; }
4323     /**
4324      *
4325      */
4326     PkgConfig(const PkgConfig &other)
4327         { assign(other); }
4329     /**
4330      *
4331      */
4332     PkgConfig &operator=(const PkgConfig &other)
4333         { assign(other); return *this; }
4335     /**
4336      *
4337      */
4338     virtual ~PkgConfig()
4339         { }
4341     /**
4342      *
4343      */
4344     virtual String getName()
4345         { return name; }
4347     /**
4348      *
4349      */
4350     virtual String getDescription()
4351         { return description; }
4353     /**
4354      *
4355      */
4356     virtual String getCflags()
4357         { return cflags; }
4359     /**
4360      *
4361      */
4362     virtual String getLibs()
4363         { return libs; }
4365     /**
4366      *
4367      */
4368     virtual String getVersion()
4369         { return version; }
4371     /**
4372      *
4373      */
4374     virtual int getMajorVersion()
4375         { return majorVersion; }
4377     /**
4378      *
4379      */
4380     virtual int getMinorVersion()
4381         { return minorVersion; }
4383     /**
4384      *
4385      */
4386     virtual int getMicroVersion()
4387         { return microVersion; }
4389     /**
4390      *
4391      */
4392     virtual std::map<String, String> &getAttributes()
4393         { return attrs; }
4395     /**
4396      *
4397      */
4398     virtual std::vector<String> &getRequireList()
4399         { return requireList; }
4401     virtual bool readFile(const String &fileName);
4403 private:
4405     void init()
4406         {
4407         name         = "";
4408         description  = "";
4409         cflags       = "";
4410         libs         = "";
4411         requires     = "";
4412         version      = "";
4413         majorVersion = 0;
4414         minorVersion = 0;
4415         microVersion = 0;
4416         fileName     = "";
4417         attrs.clear();
4418         requireList.clear();
4419         }
4421     void assign(const PkgConfig &other)
4422         {
4423         name         = other.name;
4424         description  = other.description;
4425         cflags       = other.cflags;
4426         libs         = other.libs;
4427         requires     = other.requires;
4428         version      = other.version;
4429         majorVersion = other.majorVersion;
4430         minorVersion = other.minorVersion;
4431         microVersion = other.microVersion;
4432         fileName     = other.fileName;
4433         attrs        = other.attrs;
4434         requireList  = other.requireList;
4435         }
4439     int get(int pos);
4441     int skipwhite(int pos);
4443     int getword(int pos, String &ret);
4445     void parseRequires();
4447     void parseVersion();
4449     bool parse(const String &buf);
4451     void dumpAttrs();
4453     String name;
4455     String description;
4457     String cflags;
4459     String libs;
4461     String requires;
4463     String version;
4465     int majorVersion;
4467     int minorVersion;
4469     int microVersion;
4471     String fileName;
4473     std::map<String, String> attrs;
4475     std::vector<String> requireList;
4477     char *parsebuf;
4478     int parselen;
4479 };
4482 /**
4483  * Get a character from the buffer at pos.  If out of range,
4484  * return -1 for safety
4485  */
4486 int PkgConfig::get(int pos)
4488     if (pos>parselen)
4489         return -1;
4490     return parsebuf[pos];
4495 /**
4496  *  Skip over all whitespace characters beginning at pos.  Return
4497  *  the position of the first non-whitespace character.
4498  */
4499 int PkgConfig::skipwhite(int pos)
4501     while (pos < parselen)
4502         {
4503         int ch = get(pos);
4504         if (ch < 0)
4505             break;
4506         if (!isspace(ch))
4507             break;
4508         pos++;
4509         }
4510     return pos;
4514 /**
4515  *  Parse the buffer beginning at pos, for a word.  Fill
4516  *  'ret' with the result.  Return the position after the
4517  *  word.
4518  */
4519 int PkgConfig::getword(int pos, String &ret)
4521     while (pos < parselen)
4522         {
4523         int ch = get(pos);
4524         if (ch < 0)
4525             break;
4526         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4527             break;
4528         ret.push_back((char)ch);
4529         pos++;
4530         }
4531     return pos;
4534 void PkgConfig::parseRequires()
4536     if (requires.size() == 0)
4537         return;
4538     parsebuf = (char *)requires.c_str();
4539     parselen = requires.size();
4540     int pos = 0;
4541     while (pos < parselen)
4542         {
4543         pos = skipwhite(pos);
4544         String val;
4545         int pos2 = getword(pos, val);
4546         if (pos2 == pos)
4547             break;
4548         pos = pos2;
4549         //trace("val %s", val.c_str());
4550         requireList.push_back(val);
4551         }
4554 static int getint(const String str)
4556     char *s = (char *)str.c_str();
4557     char *ends = NULL;
4558     long val = strtol(s, &ends, 10);
4559     if (ends == s)
4560         return 0L;
4561     else
4562         return val;
4565 void PkgConfig::parseVersion()
4567     if (version.size() == 0)
4568         return;
4569     String s1, s2, s3;
4570     unsigned int pos = 0;
4571     unsigned int pos2 = version.find('.', pos);
4572     if (pos2 == version.npos)
4573         {
4574         s1 = version;
4575         }
4576     else
4577         {
4578         s1 = version.substr(pos, pos2-pos);
4579         pos = pos2;
4580         pos++;
4581         if (pos < version.size())
4582             {
4583             pos2 = version.find('.', pos);
4584             if (pos2 == version.npos)
4585                 {
4586                 s2 = version.substr(pos, version.size()-pos);
4587                 }
4588             else
4589                 {
4590                 s2 = version.substr(pos, pos2-pos);
4591                 pos = pos2;
4592                 pos++;
4593                 if (pos < version.size())
4594                     s3 = version.substr(pos, pos2-pos);
4595                 }
4596             }
4597         }
4599     majorVersion = getint(s1);
4600     minorVersion = getint(s2);
4601     microVersion = getint(s3);
4602     //trace("version:%d.%d.%d", majorVersion,
4603     //          minorVersion, microVersion );
4607 bool PkgConfig::parse(const String &buf)
4609     init();
4611     parsebuf = (char *)buf.c_str();
4612     parselen = buf.size();
4613     int pos = 0;
4616     while (pos < parselen)
4617         {
4618         String attrName;
4619         pos = skipwhite(pos);
4620         int ch = get(pos);
4621         if (ch == '#')
4622             {
4623             //comment.  eat the rest of the line
4624             while (pos < parselen)
4625                 {
4626                 ch = get(pos);
4627                 if (ch == '\n' || ch < 0)
4628                     break;
4629                 pos++;
4630                 }
4631             continue;
4632             }
4633         pos = getword(pos, attrName);
4634         if (attrName.size() == 0)
4635             continue;
4636         pos = skipwhite(pos);
4637         ch = get(pos);
4638         if (ch != ':' && ch != '=')
4639             {
4640             error("expected ':' or '='");
4641             return false;
4642             }
4643         pos++;
4644         pos = skipwhite(pos);
4645         String attrVal;
4646         while (pos < parselen)
4647             {
4648             ch = get(pos);
4649             if (ch == '\n' || ch < 0)
4650                 break;
4651             else if (ch == '$' && get(pos+1) == '{')
4652                 {
4653                 //#  this is a ${substitution}
4654                 pos += 2;
4655                 String subName;
4656                 while (pos < parselen)
4657                     {
4658                     ch = get(pos);
4659                     if (ch < 0)
4660                         {
4661                         error("unterminated substitution");
4662                         return false;
4663                         }
4664                     else if (ch == '}')
4665                         break;
4666                     else
4667                         subName.push_back((char)ch);
4668                     pos++;
4669                     }
4670                 //trace("subName:%s", subName.c_str());
4671                 String subVal = attrs[subName];
4672                 //trace("subVal:%s", subVal.c_str());
4673                 attrVal.append(subVal);
4674                 }
4675             else
4676                 attrVal.push_back((char)ch);
4677             pos++;
4678             }
4680         attrVal = trim(attrVal);
4681         attrs[attrName] = attrVal;
4683         if (attrName == "Name")
4684             name = attrVal;
4685         else if (attrName == "Description")
4686             description = attrVal;
4687         else if (attrName == "Cflags")
4688             cflags = attrVal;
4689         else if (attrName == "Libs")
4690             libs = attrVal;
4691         else if (attrName == "Requires")
4692             requires = attrVal;
4693         else if (attrName == "Version")
4694             version = attrVal;
4696         //trace("name:'%s'  value:'%s'",
4697         //      attrName.c_str(), attrVal.c_str());
4698         }
4701     parseRequires();
4702     parseVersion();
4704     return true;
4707 void PkgConfig::dumpAttrs()
4709     //trace("### PkgConfig attributes for %s", fileName.c_str());
4710     std::map<String, String>::iterator iter;
4711     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4712         {
4713         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4714         }
4718 bool PkgConfig::readFile(const String &fileNameArg)
4720     fileName = fileNameArg;
4722     FILE *f = fopen(fileName.c_str(), "r");
4723     if (!f)
4724         {
4725         error("cannot open file '%s' for reading", fileName.c_str());
4726         return false;
4727         }
4728     String buf;
4729     while (true)
4730         {
4731         int ch = fgetc(f);
4732         if (ch < 0)
4733             break;
4734         buf.push_back((char)ch);
4735         }
4736     fclose(f);
4738     //trace("####### File:\n%s", buf.c_str());
4739     if (!parse(buf))
4740         {
4741         return false;
4742         }
4744     dumpAttrs();
4746     return true;
4753 //########################################################################
4754 //# D E P T O O L
4755 //########################################################################
4759 /**
4760  *  Class which holds information for each file.
4761  */
4762 class FileRec
4764 public:
4766     typedef enum
4767         {
4768         UNKNOWN,
4769         CFILE,
4770         HFILE,
4771         OFILE
4772         } FileType;
4774     /**
4775      *  Constructor
4776      */
4777     FileRec()
4778         { init(); type = UNKNOWN; }
4780     /**
4781      *  Copy constructor
4782      */
4783     FileRec(const FileRec &other)
4784         { init(); assign(other); }
4785     /**
4786      *  Constructor
4787      */
4788     FileRec(int typeVal)
4789         { init(); type = typeVal; }
4790     /**
4791      *  Assignment operator
4792      */
4793     FileRec &operator=(const FileRec &other)
4794         { init(); assign(other); return *this; }
4797     /**
4798      *  Destructor
4799      */
4800     ~FileRec()
4801         {}
4803     /**
4804      *  Directory part of the file name
4805      */
4806     String path;
4808     /**
4809      *  Base name, sans directory and suffix
4810      */
4811     String baseName;
4813     /**
4814      *  File extension, such as cpp or h
4815      */
4816     String suffix;
4818     /**
4819      *  Type of file: CFILE, HFILE, OFILE
4820      */
4821     int type;
4823     /**
4824      * Used to list files ref'd by this one
4825      */
4826     std::map<String, FileRec *> files;
4829 private:
4831     void init()
4832         {
4833         }
4835     void assign(const FileRec &other)
4836         {
4837         type     = other.type;
4838         baseName = other.baseName;
4839         suffix   = other.suffix;
4840         files    = other.files;
4841         }
4843 };
4847 /**
4848  *  Simpler dependency record
4849  */
4850 class DepRec
4852 public:
4854     /**
4855      *  Constructor
4856      */
4857     DepRec()
4858         {init();}
4860     /**
4861      *  Copy constructor
4862      */
4863     DepRec(const DepRec &other)
4864         {init(); assign(other);}
4865     /**
4866      *  Constructor
4867      */
4868     DepRec(const String &fname)
4869         {init(); name = fname; }
4870     /**
4871      *  Assignment operator
4872      */
4873     DepRec &operator=(const DepRec &other)
4874         {init(); assign(other); return *this;}
4877     /**
4878      *  Destructor
4879      */
4880     ~DepRec()
4881         {}
4883     /**
4884      *  Directory part of the file name
4885      */
4886     String path;
4888     /**
4889      *  Base name, without the path and suffix
4890      */
4891     String name;
4893     /**
4894      *  Suffix of the source
4895      */
4896     String suffix;
4899     /**
4900      * Used to list files ref'd by this one
4901      */
4902     std::vector<String> files;
4905 private:
4907     void init()
4908         {
4909         }
4911     void assign(const DepRec &other)
4912         {
4913         path     = other.path;
4914         name     = other.name;
4915         suffix   = other.suffix;
4916         files    = other.files; //avoid recursion
4917         }
4919 };
4922 class DepTool : public MakeBase
4924 public:
4926     /**
4927      *  Constructor
4928      */
4929     DepTool()
4930         { init(); }
4932     /**
4933      *  Copy constructor
4934      */
4935     DepTool(const DepTool &other)
4936         { init(); assign(other); }
4938     /**
4939      *  Assignment operator
4940      */
4941     DepTool &operator=(const DepTool &other)
4942         { init(); assign(other); return *this; }
4945     /**
4946      *  Destructor
4947      */
4948     ~DepTool()
4949         {}
4952     /**
4953      *  Reset this section of code
4954      */
4955     virtual void init();
4956     
4957     /**
4958      *  Reset this section of code
4959      */
4960     virtual void assign(const DepTool &other)
4961         {
4962         }
4963     
4964     /**
4965      *  Sets the source directory which will be scanned
4966      */
4967     virtual void setSourceDirectory(const String &val)
4968         { sourceDir = val; }
4970     /**
4971      *  Returns the source directory which will be scanned
4972      */
4973     virtual String getSourceDirectory()
4974         { return sourceDir; }
4976     /**
4977      *  Sets the list of files within the directory to analyze
4978      */
4979     virtual void setFileList(const std::vector<String> &list)
4980         { fileList = list; }
4982     /**
4983      * Creates the list of all file names which will be
4984      * candidates for further processing.  Reads make.exclude
4985      * to see which files for directories to leave out.
4986      */
4987     virtual bool createFileList();
4990     /**
4991      *  Generates the forward dependency list
4992      */
4993     virtual bool generateDependencies();
4996     /**
4997      *  Generates the forward dependency list, saving the file
4998      */
4999     virtual bool generateDependencies(const String &);
5002     /**
5003      *  Load a dependency file
5004      */
5005     std::vector<DepRec> loadDepFile(const String &fileName);
5007     /**
5008      *  Load a dependency file, generating one if necessary
5009      */
5010     std::vector<DepRec> getDepFile(const String &fileName,
5011               bool forceRefresh);
5013     /**
5014      *  Save a dependency file
5015      */
5016     bool saveDepFile(const String &fileName);
5019 private:
5022     /**
5023      *
5024      */
5025     void parseName(const String &fullname,
5026                    String &path,
5027                    String &basename,
5028                    String &suffix);
5030     /**
5031      *
5032      */
5033     int get(int pos);
5035     /**
5036      *
5037      */
5038     int skipwhite(int pos);
5040     /**
5041      *
5042      */
5043     int getword(int pos, String &ret);
5045     /**
5046      *
5047      */
5048     bool sequ(int pos, const char *key);
5050     /**
5051      *
5052      */
5053     bool addIncludeFile(FileRec *frec, const String &fname);
5055     /**
5056      *
5057      */
5058     bool scanFile(const String &fname, FileRec *frec);
5060     /**
5061      *
5062      */
5063     bool processDependency(FileRec *ofile, FileRec *include);
5065     /**
5066      *
5067      */
5068     String sourceDir;
5070     /**
5071      *
5072      */
5073     std::vector<String> fileList;
5075     /**
5076      *
5077      */
5078     std::vector<String> directories;
5080     /**
5081      * A list of all files which will be processed for
5082      * dependencies.
5083      */
5084     std::map<String, FileRec *> allFiles;
5086     /**
5087      * The list of .o files, and the
5088      * dependencies upon them.
5089      */
5090     std::map<String, FileRec *> oFiles;
5092     int depFileSize;
5093     char *depFileBuf;
5095     static const int readBufSize = 8192;
5096     char readBuf[8193];//byte larger
5098 };
5104 /**
5105  *  Clean up after processing.  Called by the destructor, but should
5106  *  also be called before the object is reused.
5107  */
5108 void DepTool::init()
5110     sourceDir = ".";
5112     fileList.clear();
5113     directories.clear();
5114     
5115     //clear output file list
5116     std::map<String, FileRec *>::iterator iter;
5117     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5118         delete iter->second;
5119     oFiles.clear();
5121     //allFiles actually contains the master copies. delete them
5122     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5123         delete iter->second;
5124     allFiles.clear(); 
5131 /**
5132  *  Parse a full path name into path, base name, and suffix
5133  */
5134 void DepTool::parseName(const String &fullname,
5135                         String &path,
5136                         String &basename,
5137                         String &suffix)
5139     if (fullname.size() < 2)
5140         return;
5142     unsigned int pos = fullname.find_last_of('/');
5143     if (pos != fullname.npos && pos<fullname.size()-1)
5144         {
5145         path = fullname.substr(0, pos);
5146         pos++;
5147         basename = fullname.substr(pos, fullname.size()-pos);
5148         }
5149     else
5150         {
5151         path = "";
5152         basename = fullname;
5153         }
5155     pos = basename.find_last_of('.');
5156     if (pos != basename.npos && pos<basename.size()-1)
5157         {
5158         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5159         basename = basename.substr(0, pos);
5160         }
5162     //trace("parsename:%s %s %s", path.c_str(),
5163     //        basename.c_str(), suffix.c_str()); 
5168 /**
5169  *  Generate our internal file list.
5170  */
5171 bool DepTool::createFileList()
5174     for (unsigned int i=0 ; i<fileList.size() ; i++)
5175         {
5176         String fileName = fileList[i];
5177         //trace("## FileName:%s", fileName.c_str());
5178         String path;
5179         String basename;
5180         String sfx;
5181         parseName(fileName, path, basename, sfx);
5182         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5183             sfx == "cc" || sfx == "CC")
5184             {
5185             FileRec *fe         = new FileRec(FileRec::CFILE);
5186             fe->path            = path;
5187             fe->baseName        = basename;
5188             fe->suffix          = sfx;
5189             allFiles[fileName]  = fe;
5190             }
5191         else if (sfx == "h"   ||  sfx == "hh"  ||
5192                  sfx == "hpp" ||  sfx == "hxx")
5193             {
5194             FileRec *fe         = new FileRec(FileRec::HFILE);
5195             fe->path            = path;
5196             fe->baseName        = basename;
5197             fe->suffix          = sfx;
5198             allFiles[fileName]  = fe;
5199             }
5200         }
5202     if (!listDirectories(sourceDir, "", directories))
5203         return false;
5204         
5205     return true;
5212 /**
5213  * Get a character from the buffer at pos.  If out of range,
5214  * return -1 for safety
5215  */
5216 int DepTool::get(int pos)
5218     if (pos>depFileSize)
5219         return -1;
5220     return depFileBuf[pos];
5225 /**
5226  *  Skip over all whitespace characters beginning at pos.  Return
5227  *  the position of the first non-whitespace character.
5228  */
5229 int DepTool::skipwhite(int pos)
5231     while (pos < depFileSize)
5232         {
5233         int ch = get(pos);
5234         if (ch < 0)
5235             break;
5236         if (!isspace(ch))
5237             break;
5238         pos++;
5239         }
5240     return pos;
5244 /**
5245  *  Parse the buffer beginning at pos, for a word.  Fill
5246  *  'ret' with the result.  Return the position after the
5247  *  word.
5248  */
5249 int DepTool::getword(int pos, String &ret)
5251     while (pos < depFileSize)
5252         {
5253         int ch = get(pos);
5254         if (ch < 0)
5255             break;
5256         if (isspace(ch))
5257             break;
5258         ret.push_back((char)ch);
5259         pos++;
5260         }
5261     return pos;
5264 /**
5265  * Return whether the sequence of characters in the buffer
5266  * beginning at pos match the key,  for the length of the key
5267  */
5268 bool DepTool::sequ(int pos, const char *key)
5270     while (*key)
5271         {
5272         if (*key != get(pos))
5273             return false;
5274         key++; pos++;
5275         }
5276     return true;
5281 /**
5282  *  Add an include file name to a file record.  If the name
5283  *  is not found in allFiles explicitly, try prepending include
5284  *  directory names to it and try again.
5285  */
5286 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5288     //# if the name is an exact match to a path name
5289     //# in allFiles, like "myinc.h"
5290     std::map<String, FileRec *>::iterator iter =
5291            allFiles.find(iname);
5292     if (iter != allFiles.end()) //already exists
5293         {
5294          //h file in same dir
5295         FileRec *other = iter->second;
5296         //trace("local: '%s'", iname.c_str());
5297         frec->files[iname] = other;
5298         return true;
5299         }
5300     else 
5301         {
5302         //## Ok, it was not found directly
5303         //look in other dirs
5304         std::vector<String>::iterator diter;
5305         for (diter=directories.begin() ;
5306              diter!=directories.end() ; diter++)
5307             {
5308             String dfname = *diter;
5309             dfname.append("/");
5310             dfname.append(iname);
5311             URI fullPathURI(dfname);  //normalize path name
5312             String fullPath = fullPathURI.getPath();
5313             if (fullPath[0] == '/')
5314                 fullPath = fullPath.substr(1);
5315             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5316             iter = allFiles.find(fullPath);
5317             if (iter != allFiles.end())
5318                 {
5319                 FileRec *other = iter->second;
5320                 //trace("other: '%s'", iname.c_str());
5321                 frec->files[fullPath] = other;
5322                 return true;
5323                 }
5324             }
5325         }
5326     return true;
5331 /**
5332  *  Lightly parse a file to find the #include directives.  Do
5333  *  a bit of state machine stuff to make sure that the directive
5334  *  is valid.  (Like not in a comment).
5335  */
5336 bool DepTool::scanFile(const String &fname, FileRec *frec)
5338     String fileName;
5339     if (sourceDir.size() > 0)
5340         {
5341         fileName.append(sourceDir);
5342         fileName.append("/");
5343         }
5344     fileName.append(fname);
5345     String nativeName = getNativePath(fileName);
5346     FILE *f = fopen(nativeName.c_str(), "r");
5347     if (!f)
5348         {
5349         error("Could not open '%s' for reading", fname.c_str());
5350         return false;
5351         }
5352     String buf;
5353     while (!feof(f))
5354         {
5355         int len = fread(readBuf, 1, readBufSize, f);
5356         readBuf[len] = '\0';
5357         buf.append(readBuf);
5358         }
5359     fclose(f);
5361     depFileSize = buf.size();
5362     depFileBuf  = (char *)buf.c_str();
5363     int pos = 0;
5366     while (pos < depFileSize)
5367         {
5368         //trace("p:%c", get(pos));
5370         //# Block comment
5371         if (get(pos) == '/' && get(pos+1) == '*')
5372             {
5373             pos += 2;
5374             while (pos < depFileSize)
5375                 {
5376                 if (get(pos) == '*' && get(pos+1) == '/')
5377                     {
5378                     pos += 2;
5379                     break;
5380                     }
5381                 else
5382                     pos++;
5383                 }
5384             }
5385         //# Line comment
5386         else if (get(pos) == '/' && get(pos+1) == '/')
5387             {
5388             pos += 2;
5389             while (pos < depFileSize)
5390                 {
5391                 if (get(pos) == '\n')
5392                     {
5393                     pos++;
5394                     break;
5395                     }
5396                 else
5397                     pos++;
5398                 }
5399             }
5400         //# #include! yaay
5401         else if (sequ(pos, "#include"))
5402             {
5403             pos += 8;
5404             pos = skipwhite(pos);
5405             String iname;
5406             pos = getword(pos, iname);
5407             if (iname.size()>2)
5408                 {
5409                 iname = iname.substr(1, iname.size()-2);
5410                 addIncludeFile(frec, iname);
5411                 }
5412             }
5413         else
5414             {
5415             pos++;
5416             }
5417         }
5419     return true;
5424 /**
5425  *  Recursively check include lists to find all files in allFiles to which
5426  *  a given file is dependent.
5427  */
5428 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5430     std::map<String, FileRec *>::iterator iter;
5431     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5432         {
5433         String fname  = iter->first;
5434         if (ofile->files.find(fname) != ofile->files.end())
5435             {
5436             //trace("file '%s' already seen", fname.c_str());
5437             continue;
5438             }
5439         FileRec *child  = iter->second;
5440         ofile->files[fname] = child;
5441       
5442         processDependency(ofile, child);
5443         }
5446     return true;
5453 /**
5454  *  Generate the file dependency list.
5455  */
5456 bool DepTool::generateDependencies()
5458     std::map<String, FileRec *>::iterator iter;
5459     //# First pass.  Scan for all includes
5460     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5461         {
5462         FileRec *frec = iter->second;
5463         if (!scanFile(iter->first, frec))
5464             {
5465             //quit?
5466             }
5467         }
5469     //# Second pass.  Scan for all includes
5470     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5471         {
5472         FileRec *include = iter->second;
5473         if (include->type == FileRec::CFILE)
5474             {
5475             String cFileName   = iter->first;
5476             FileRec *ofile     = new FileRec(FileRec::OFILE);
5477             ofile->path        = include->path;
5478             ofile->baseName    = include->baseName;
5479             ofile->suffix      = include->suffix;
5480             String fname       = include->path;
5481             if (fname.size()>0)
5482                 fname.append("/");
5483             fname.append(include->baseName);
5484             fname.append(".o");
5485             oFiles[fname]    = ofile;
5486             //add the .c file first?   no, don't
5487             //ofile->files[cFileName] = include;
5488             
5489             //trace("ofile:%s", fname.c_str());
5491             processDependency(ofile, include);
5492             }
5493         }
5495       
5496     return true;
5501 /**
5502  *  High-level call to generate deps and optionally save them
5503  */
5504 bool DepTool::generateDependencies(const String &fileName)
5506     if (!createFileList())
5507         return false;
5508     if (!generateDependencies())
5509         return false;
5510     if (!saveDepFile(fileName))
5511         return false;
5512     return true;
5516 /**
5517  *   This saves the dependency cache.
5518  */
5519 bool DepTool::saveDepFile(const String &fileName)
5521     time_t tim;
5522     time(&tim);
5524     FILE *f = fopen(fileName.c_str(), "w");
5525     if (!f)
5526         {
5527         trace("cannot open '%s' for writing", fileName.c_str());
5528         }
5529     fprintf(f, "<?xml version='1.0'?>\n");
5530     fprintf(f, "<!--\n");
5531     fprintf(f, "########################################################\n");
5532     fprintf(f, "## File: build.dep\n");
5533     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5534     fprintf(f, "########################################################\n");
5535     fprintf(f, "-->\n");
5537     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5538     std::map<String, FileRec *>::iterator iter;
5539     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5540         {
5541         FileRec *frec = iter->second;
5542         if (frec->type == FileRec::OFILE)
5543             {
5544             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5545                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5546             std::map<String, FileRec *>::iterator citer;
5547             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5548                 {
5549                 String cfname = citer->first;
5550                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5551                 }
5552             fprintf(f, "</object>\n\n");
5553             }
5554         }
5556     fprintf(f, "</dependencies>\n");
5557     fprintf(f, "\n");
5558     fprintf(f, "<!--\n");
5559     fprintf(f, "########################################################\n");
5560     fprintf(f, "## E N D\n");
5561     fprintf(f, "########################################################\n");
5562     fprintf(f, "-->\n");
5564     fclose(f);
5566     return true;
5572 /**
5573  *   This loads the dependency cache.
5574  */
5575 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5577     std::vector<DepRec> result;
5578     
5579     Parser parser;
5580     Element *root = parser.parseFile(depFile.c_str());
5581     if (!root)
5582         {
5583         //error("Could not open %s for reading", depFile.c_str());
5584         return result;
5585         }
5587     if (root->getChildren().size()==0 ||
5588         root->getChildren()[0]->getName()!="dependencies")
5589         {
5590         error("loadDepFile: main xml element should be <dependencies>");
5591         delete root;
5592         return result;
5593         }
5595     //########## Start parsing
5596     Element *depList = root->getChildren()[0];
5598     std::vector<Element *> objects = depList->getChildren();
5599     for (unsigned int i=0 ; i<objects.size() ; i++)
5600         {
5601         Element *objectElem = objects[i];
5602         String tagName = objectElem->getName();
5603         if (tagName != "object")
5604             {
5605             error("loadDepFile: <dependencies> should have only <object> children");
5606             return result;
5607             }
5609         String objName   = objectElem->getAttribute("name");
5610          //trace("object:%s", objName.c_str());
5611         DepRec depObject(objName);
5612         depObject.path   = objectElem->getAttribute("path");
5613         depObject.suffix = objectElem->getAttribute("suffix");
5614         //########## DESCRIPTION
5615         std::vector<Element *> depElems = objectElem->getChildren();
5616         for (unsigned int i=0 ; i<depElems.size() ; i++)
5617             {
5618             Element *depElem = depElems[i];
5619             tagName = depElem->getName();
5620             if (tagName != "dep")
5621                 {
5622                 error("loadDepFile: <object> should have only <dep> children");
5623                 return result;
5624                 }
5625             String depName = depElem->getAttribute("name");
5626             //trace("    dep:%s", depName.c_str());
5627             depObject.files.push_back(depName);
5628             }
5630         //Insert into the result list, in a sorted manner
5631         bool inserted = false;
5632         std::vector<DepRec>::iterator iter;
5633         for (iter = result.begin() ; iter != result.end() ; iter++)
5634             {
5635             String vpath = iter->path;
5636             vpath.append("/");
5637             vpath.append(iter->name);
5638             String opath = depObject.path;
5639             opath.append("/");
5640             opath.append(depObject.name);
5641             if (vpath > opath)
5642                 {
5643                 inserted = true;
5644                 iter = result.insert(iter, depObject);
5645                 break;
5646                 }
5647             }
5648         if (!inserted)
5649             result.push_back(depObject);
5650         }
5652     delete root;
5654     return result;
5658 /**
5659  *   This loads the dependency cache.
5660  */
5661 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5662                    bool forceRefresh)
5664     std::vector<DepRec> result;
5665     if (forceRefresh)
5666         {
5667         generateDependencies(depFile);
5668         result = loadDepFile(depFile);
5669         }
5670     else
5671         {
5672         //try once
5673         result = loadDepFile(depFile);
5674         if (result.size() == 0)
5675             {
5676             //fail? try again
5677             generateDependencies(depFile);
5678             result = loadDepFile(depFile);
5679             }
5680         }
5681     return result;
5687 //########################################################################
5688 //# T A S K
5689 //########################################################################
5690 //forward decl
5691 class Target;
5692 class Make;
5694 /**
5695  *
5696  */
5697 class Task : public MakeBase
5700 public:
5702     typedef enum
5703         {
5704         TASK_NONE,
5705         TASK_CC,
5706         TASK_COPY,
5707         TASK_DELETE,
5708         TASK_JAR,
5709         TASK_JAVAC,
5710         TASK_LINK,
5711         TASK_MAKEFILE,
5712         TASK_MKDIR,
5713         TASK_MSGFMT,
5714         TASK_PKG_CONFIG,
5715         TASK_RANLIB,
5716         TASK_RC,
5717         TASK_SHAREDLIB,
5718         TASK_STATICLIB,
5719         TASK_STRIP,
5720         TASK_TOUCH,
5721         TASK_TSTAMP
5722         } TaskType;
5723         
5725     /**
5726      *
5727      */
5728     Task(MakeBase &par) : parent(par)
5729         { init(); }
5731     /**
5732      *
5733      */
5734     Task(const Task &other) : parent(other.parent)
5735         { init(); assign(other); }
5737     /**
5738      *
5739      */
5740     Task &operator=(const Task &other)
5741         { assign(other); return *this; }
5743     /**
5744      *
5745      */
5746     virtual ~Task()
5747         { }
5750     /**
5751      *
5752      */
5753     virtual MakeBase &getParent()
5754         { return parent; }
5756      /**
5757      *
5758      */
5759     virtual int  getType()
5760         { return type; }
5762     /**
5763      *
5764      */
5765     virtual void setType(int val)
5766         { type = val; }
5768     /**
5769      *
5770      */
5771     virtual String getName()
5772         { return name; }
5774     /**
5775      *
5776      */
5777     virtual bool execute()
5778         { return true; }
5780     /**
5781      *
5782      */
5783     virtual bool parse(Element *elem)
5784         { return true; }
5786     /**
5787      *
5788      */
5789     Task *createTask(Element *elem, int lineNr);
5792 protected:
5794     void init()
5795         {
5796         type = TASK_NONE;
5797         name = "none";
5798         }
5800     void assign(const Task &other)
5801         {
5802         type = other.type;
5803         name = other.name;
5804         }
5805         
5806     String getAttribute(Element *elem, const String &attrName)
5807         {
5808         String str;
5809         return str;
5810         }
5812     MakeBase &parent;
5814     int type;
5816     String name;
5817 };
5821 /**
5822  * This task runs the C/C++ compiler.  The compiler is invoked
5823  * for all .c or .cpp files which are newer than their correcsponding
5824  * .o files.  
5825  */
5826 class TaskCC : public Task
5828 public:
5830     TaskCC(MakeBase &par) : Task(par)
5831         {
5832         type = TASK_CC; name = "cc";
5833         ccCommand   = "gcc";
5834         cxxCommand  = "g++";
5835         source      = ".";
5836         dest        = ".";
5837         flags       = "";
5838         defines     = "";
5839         includes    = "";
5840         fileSet.clear();
5841         }
5843     virtual ~TaskCC()
5844         {}
5846     virtual bool needsCompiling(const FileRec &depRec,
5847               const String &src, const String &dest)
5848         {
5849         return false;
5850         }
5852     virtual bool execute()
5853         {
5854         if (!listFiles(parent, fileSet))
5855             return false;
5856             
5857         FILE *f = NULL;
5858         f = fopen("compile.lst", "w");
5860         bool refreshCache = false;
5861         String fullName = parent.resolve("build.dep");
5862         if (isNewerThan(parent.getURI().getPath(), fullName))
5863             {
5864             status("          : regenerating C/C++ dependency cache");
5865             refreshCache = true;
5866             }
5868         DepTool depTool;
5869         depTool.setSourceDirectory(source);
5870         depTool.setFileList(fileSet.getFiles());
5871         std::vector<DepRec> deps =
5872              depTool.getDepFile("build.dep", refreshCache);
5873         
5874         String incs;
5875         incs.append("-I");
5876         incs.append(parent.resolve("."));
5877         incs.append(" ");
5878         if (includes.size()>0)
5879             {
5880             incs.append(includes);
5881             incs.append(" ");
5882             }
5883         std::set<String> paths;
5884         std::vector<DepRec>::iterator viter;
5885         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5886             {
5887             DepRec dep = *viter;
5888             if (dep.path.size()>0)
5889                 paths.insert(dep.path);
5890             }
5891         if (source.size()>0)
5892             {
5893             incs.append(" -I");
5894             incs.append(parent.resolve(source));
5895             incs.append(" ");
5896             }
5897         std::set<String>::iterator setIter;
5898         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5899             {
5900             incs.append(" -I");
5901             String dname;
5902             if (source.size()>0)
5903                 {
5904                 dname.append(source);
5905                 dname.append("/");
5906                 }
5907             dname.append(*setIter);
5908             incs.append(parent.resolve(dname));
5909             }
5910         std::vector<String> cfiles;
5911         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5912             {
5913             DepRec dep = *viter;
5915             //## Select command
5916             String sfx = dep.suffix;
5917             String command = ccCommand;
5918             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
5919                  sfx == "cc" || sfx == "CC")
5920                 command = cxxCommand;
5921  
5922             //## Make paths
5923             String destPath = dest;
5924             String srcPath  = source;
5925             if (dep.path.size()>0)
5926                 {
5927                 destPath.append("/");
5928                 destPath.append(dep.path);
5929                 srcPath.append("/");
5930                 srcPath.append(dep.path);
5931                 }
5932             //## Make sure destination directory exists
5933             if (!createDirectory(destPath))
5934                 return false;
5935                 
5936             //## Check whether it needs to be done
5937             String destName;
5938             if (destPath.size()>0)
5939                 {
5940                 destName.append(destPath);
5941                 destName.append("/");
5942                 }
5943             destName.append(dep.name);
5944             destName.append(".o");
5945             String destFullName = parent.resolve(destName);
5946             String srcName;
5947             if (srcPath.size()>0)
5948                 {
5949                 srcName.append(srcPath);
5950                 srcName.append("/");
5951                 }
5952             srcName.append(dep.name);
5953             srcName.append(".");
5954             srcName.append(dep.suffix);
5955             String srcFullName = parent.resolve(srcName);
5956             bool compileMe = false;
5957             //# First we check if the source is newer than the .o
5958             if (isNewerThan(srcFullName, destFullName))
5959                 {
5960                 status("          : compile of %s required by %s",
5961                         destFullName.c_str(), srcFullName.c_str());
5962                 compileMe = true;
5963                 }
5964             else
5965                 {
5966                 //# secondly, we check if any of the included dependencies
5967                 //# of the .c/.cpp is newer than the .o
5968                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5969                     {
5970                     String depName;
5971                     if (source.size()>0)
5972                         {
5973                         depName.append(source);
5974                         depName.append("/");
5975                         }
5976                     depName.append(dep.files[i]);
5977                     String depFullName = parent.resolve(depName);
5978                     bool depRequires = isNewerThan(depFullName, destFullName);
5979                     //trace("%d %s %s\n", depRequires,
5980                     //        destFullName.c_str(), depFullName.c_str());
5981                     if (depRequires)
5982                         {
5983                         status("          : compile of %s required by %s",
5984                                 destFullName.c_str(), depFullName.c_str());
5985                         compileMe = true;
5986                         break;
5987                         }
5988                     }
5989                 }
5990             if (!compileMe)
5991                 {
5992                 continue;
5993                 }
5995             //## Assemble the command
5996             String cmd = command;
5997             cmd.append(" -c ");
5998             cmd.append(flags);
5999             cmd.append(" ");
6000             cmd.append(defines);
6001             cmd.append(" ");
6002             cmd.append(incs);
6003             cmd.append(" ");
6004             cmd.append(srcFullName);
6005             cmd.append(" -o ");
6006             cmd.append(destFullName);
6008             //## Execute the command
6010             String outString, errString;
6011             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6013             if (f)
6014                 {
6015                 fprintf(f, "########################### File : %s\n",
6016                              srcFullName.c_str());
6017                 fprintf(f, "#### COMMAND ###\n");
6018                 int col = 0;
6019                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6020                     {
6021                     char ch = cmd[i];
6022                     if (isspace(ch)  && col > 63)
6023                         {
6024                         fputc('\n', f);
6025                         col = 0;
6026                         }
6027                     else
6028                         {
6029                         fputc(ch, f);
6030                         col++;
6031                         }
6032                     if (col > 76)
6033                         {
6034                         fputc('\n', f);
6035                         col = 0;
6036                         }
6037                     }
6038                 fprintf(f, "\n");
6039                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6040                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6041                 }
6042             if (!ret)
6043                 {
6044                 error("problem compiling: %s", errString.c_str());
6045                 return false;
6046                 }
6047                 
6048             }
6050         if (f)
6051             {
6052             fclose(f);
6053             }
6054         
6055         return true;
6056         }
6058     virtual bool parse(Element *elem)
6059         {
6060         String s;
6061         if (!parent.getAttribute(elem, "command", s))
6062             return false;
6063         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6064         if (!parent.getAttribute(elem, "cc", s))
6065             return false;
6066         if (s.size()>0) ccCommand = s;
6067         if (!parent.getAttribute(elem, "cxx", s))
6068             return false;
6069         if (s.size()>0) cxxCommand = s;
6070         if (!parent.getAttribute(elem, "destdir", s))
6071             return false;
6072         if (s.size()>0) dest = s;
6074         std::vector<Element *> children = elem->getChildren();
6075         for (unsigned int i=0 ; i<children.size() ; i++)
6076             {
6077             Element *child = children[i];
6078             String tagName = child->getName();
6079             if (tagName == "flags")
6080                 {
6081                 if (!parent.getValue(child, flags))
6082                     return false;
6083                 flags = strip(flags);
6084                 }
6085             else if (tagName == "includes")
6086                 {
6087                 if (!parent.getValue(child, includes))
6088                     return false;
6089                 includes = strip(includes);
6090                 }
6091             else if (tagName == "defines")
6092                 {
6093                 if (!parent.getValue(child, defines))
6094                     return false;
6095                 defines = strip(defines);
6096                 }
6097             else if (tagName == "fileset")
6098                 {
6099                 if (!parseFileSet(child, parent, fileSet))
6100                     return false;
6101                 source = fileSet.getDirectory();
6102                 }
6103             }
6105         return true;
6106         }
6107         
6108 protected:
6110     String ccCommand;
6111     String cxxCommand;
6112     String source;
6113     String dest;
6114     String flags;
6115     String defines;
6116     String includes;
6117     FileSet fileSet;
6118     
6119 };
6123 /**
6124  *
6125  */
6126 class TaskCopy : public Task
6128 public:
6130     typedef enum
6131         {
6132         CP_NONE,
6133         CP_TOFILE,
6134         CP_TODIR
6135         } CopyType;
6137     TaskCopy(MakeBase &par) : Task(par)
6138         {
6139         type = TASK_COPY; name = "copy";
6140         cptype = CP_NONE;
6141         verbose = false;
6142         haveFileSet = false;
6143         }
6145     virtual ~TaskCopy()
6146         {}
6148     virtual bool execute()
6149         {
6150         switch (cptype)
6151            {
6152            case CP_TOFILE:
6153                {
6154                if (fileName.size()>0)
6155                    {
6156                    status("          : %s to %s",
6157                         fileName.c_str(), toFileName.c_str());
6158                    String fullSource = parent.resolve(fileName);
6159                    String fullDest = parent.resolve(toFileName);
6160                    //trace("copy %s to file %s", fullSource.c_str(),
6161                    //                       fullDest.c_str());
6162                    if (!isRegularFile(fullSource))
6163                        {
6164                        error("copy : file %s does not exist", fullSource.c_str());
6165                        return false;
6166                        }
6167                    if (!isNewerThan(fullSource, fullDest))
6168                        {
6169                        status("          : skipped");
6170                        return true;
6171                        }
6172                    if (!copyFile(fullSource, fullDest))
6173                        return false;
6174                    status("          : 1 file copied");
6175                    }
6176                return true;
6177                }
6178            case CP_TODIR:
6179                {
6180                if (haveFileSet)
6181                    {
6182                    if (!listFiles(parent, fileSet))
6183                        return false;
6184                    String fileSetDir = fileSet.getDirectory();
6186                    status("          : %s to %s",
6187                        fileSetDir.c_str(), toDirName.c_str());
6189                    int nrFiles = 0;
6190                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6191                        {
6192                        String fileName = fileSet[i];
6194                        String sourcePath;
6195                        if (fileSetDir.size()>0)
6196                            {
6197                            sourcePath.append(fileSetDir);
6198                            sourcePath.append("/");
6199                            }
6200                        sourcePath.append(fileName);
6201                        String fullSource = parent.resolve(sourcePath);
6202                        
6203                        //Get the immediate parent directory's base name
6204                        String baseFileSetDir = fileSetDir;
6205                        unsigned int pos = baseFileSetDir.find_last_of('/');
6206                        if (pos!=baseFileSetDir.npos &&
6207                                   pos < baseFileSetDir.size()-1)
6208                            baseFileSetDir =
6209                               baseFileSetDir.substr(pos+1,
6210                                    baseFileSetDir.size());
6211                        //Now make the new path
6212                        String destPath;
6213                        if (toDirName.size()>0)
6214                            {
6215                            destPath.append(toDirName);
6216                            destPath.append("/");
6217                            }
6218                        if (baseFileSetDir.size()>0)
6219                            {
6220                            destPath.append(baseFileSetDir);
6221                            destPath.append("/");
6222                            }
6223                        destPath.append(fileName);
6224                        String fullDest = parent.resolve(destPath);
6225                        //trace("fileName:%s", fileName.c_str());
6226                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6227                        //                   fullDest.c_str());
6228                        if (!isNewerThan(fullSource, fullDest))
6229                            {
6230                            //trace("copy skipping %s", fullSource.c_str());
6231                            continue;
6232                            }
6233                        if (!copyFile(fullSource, fullDest))
6234                            return false;
6235                        nrFiles++;
6236                        }
6237                    status("          : %d file(s) copied", nrFiles);
6238                    }
6239                else //file source
6240                    {
6241                    //For file->dir we want only the basename of
6242                    //the source appended to the dest dir
6243                    status("          : %s to %s", 
6244                        fileName.c_str(), toDirName.c_str());
6245                    String baseName = fileName;
6246                    unsigned int pos = baseName.find_last_of('/');
6247                    if (pos!=baseName.npos && pos<baseName.size()-1)
6248                        baseName = baseName.substr(pos+1, baseName.size());
6249                    String fullSource = parent.resolve(fileName);
6250                    String destPath;
6251                    if (toDirName.size()>0)
6252                        {
6253                        destPath.append(toDirName);
6254                        destPath.append("/");
6255                        }
6256                    destPath.append(baseName);
6257                    String fullDest = parent.resolve(destPath);
6258                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6259                    //                       fullDest.c_str());
6260                    if (!isRegularFile(fullSource))
6261                        {
6262                        error("copy : file %s does not exist", fullSource.c_str());
6263                        return false;
6264                        }
6265                    if (!isNewerThan(fullSource, fullDest))
6266                        {
6267                        status("          : skipped");
6268                        return true;
6269                        }
6270                    if (!copyFile(fullSource, fullDest))
6271                        return false;
6272                    status("          : 1 file copied");
6273                    }
6274                return true;
6275                }
6276            }
6277         return true;
6278         }
6281     virtual bool parse(Element *elem)
6282         {
6283         if (!parent.getAttribute(elem, "file", fileName))
6284             return false;
6285         if (!parent.getAttribute(elem, "tofile", toFileName))
6286             return false;
6287         if (toFileName.size() > 0)
6288             cptype = CP_TOFILE;
6289         if (!parent.getAttribute(elem, "todir", toDirName))
6290             return false;
6291         if (toDirName.size() > 0)
6292             cptype = CP_TODIR;
6293         String ret;
6294         if (!parent.getAttribute(elem, "verbose", ret))
6295             return false;
6296         if (ret.size()>0 && !getBool(ret, verbose))
6297             return false;
6298             
6299         haveFileSet = false;
6300         
6301         std::vector<Element *> children = elem->getChildren();
6302         for (unsigned int i=0 ; i<children.size() ; i++)
6303             {
6304             Element *child = children[i];
6305             String tagName = child->getName();
6306             if (tagName == "fileset")
6307                 {
6308                 if (!parseFileSet(child, parent, fileSet))
6309                     {
6310                     error("problem getting fileset");
6311                     return false;
6312                     }
6313                 haveFileSet = true;
6314                 }
6315             }
6317         //Perform validity checks
6318         if (fileName.size()>0 && fileSet.size()>0)
6319             {
6320             error("<copy> can only have one of : file= and <fileset>");
6321             return false;
6322             }
6323         if (toFileName.size()>0 && toDirName.size()>0)
6324             {
6325             error("<copy> can only have one of : tofile= or todir=");
6326             return false;
6327             }
6328         if (haveFileSet && toDirName.size()==0)
6329             {
6330             error("a <copy> task with a <fileset> must have : todir=");
6331             return false;
6332             }
6333         if (cptype == CP_TOFILE && fileName.size()==0)
6334             {
6335             error("<copy> tofile= must be associated with : file=");
6336             return false;
6337             }
6338         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6339             {
6340             error("<copy> todir= must be associated with : file= or <fileset>");
6341             return false;
6342             }
6344         return true;
6345         }
6346         
6347 private:
6349     int cptype;
6350     String fileName;
6351     FileSet fileSet;
6352     String toFileName;
6353     String toDirName;
6354     bool verbose;
6355     bool haveFileSet;
6356 };
6359 /**
6360  *
6361  */
6362 class TaskDelete : public Task
6364 public:
6366     typedef enum
6367         {
6368         DEL_FILE,
6369         DEL_DIR,
6370         DEL_FILESET
6371         } DeleteType;
6373     TaskDelete(MakeBase &par) : Task(par)
6374         { 
6375           type        = TASK_DELETE;
6376           name        = "delete";
6377           delType     = DEL_FILE;
6378           verbose     = false;
6379           quiet       = false;
6380           failOnError = true;
6381         }
6383     virtual ~TaskDelete()
6384         {}
6386     virtual bool execute()
6387         {
6388         struct stat finfo;
6389         switch (delType)
6390             {
6391             case DEL_FILE:
6392                 {
6393                 status("          : %s", fileName.c_str());
6394                 String fullName = parent.resolve(fileName);
6395                 char *fname = (char *)fullName.c_str();
6396                 //does not exist
6397                 if (stat(fname, &finfo)<0)
6398                     return true;
6399                 //exists but is not a regular file
6400                 if (!S_ISREG(finfo.st_mode))
6401                     {
6402                     error("<delete> failed. '%s' exists and is not a regular file",
6403                           fname);
6404                     return false;
6405                     }
6406                 if (remove(fname)<0)
6407                     {
6408                     error("<delete> failed: %s", strerror(errno));
6409                     return false;
6410                     }
6411                 return true;
6412                 }
6413             case DEL_DIR:
6414                 {
6415                 status("          : %s", dirName.c_str());
6416                 String fullDir = parent.resolve(dirName);
6417                 if (!removeDirectory(fullDir))
6418                     return false;
6419                 return true;
6420                 }
6421             }
6422         return true;
6423         }
6425     virtual bool parse(Element *elem)
6426         {
6427         if (!parent.getAttribute(elem, "file", fileName))
6428             return false;
6429         if (fileName.size() > 0)
6430             delType = DEL_FILE;
6431         if (!parent.getAttribute(elem, "dir", dirName))
6432             return false;
6433         if (dirName.size() > 0)
6434             delType = DEL_DIR;
6435         if (fileName.size()>0 && dirName.size()>0)
6436             {
6437             error("<delete> can have one attribute of file= or dir=");
6438             return false;
6439             }
6440         if (fileName.size()==0 && dirName.size()==0)
6441             {
6442             error("<delete> must have one attribute of file= or dir=");
6443             return false;
6444             }
6445         String ret;
6446         if (!parent.getAttribute(elem, "verbose", ret))
6447             return false;
6448         if (ret.size()>0 && !getBool(ret, verbose))
6449             return false;
6450         if (!parent.getAttribute(elem, "quiet", ret))
6451             return false;
6452         if (ret.size()>0 && !getBool(ret, quiet))
6453             return false;
6454         if (!parent.getAttribute(elem, "failonerror", ret))
6455             return false;
6456         if (ret.size()>0 && !getBool(ret, failOnError))
6457             return false;
6458         return true;
6459         }
6461 private:
6463     int delType;
6464     String dirName;
6465     String fileName;
6466     bool verbose;
6467     bool quiet;
6468     bool failOnError;
6469 };
6472 /**
6473  *
6474  */
6475 class TaskJar : public Task
6477 public:
6479     TaskJar(MakeBase &par) : Task(par)
6480         { type = TASK_JAR; name = "jar"; }
6482     virtual ~TaskJar()
6483         {}
6485     virtual bool execute()
6486         {
6487         return true;
6488         }
6490     virtual bool parse(Element *elem)
6491         {
6492         return true;
6493         }
6494 };
6497 /**
6498  *
6499  */
6500 class TaskJavac : public Task
6502 public:
6504     TaskJavac(MakeBase &par) : Task(par)
6505         { type = TASK_JAVAC; name = "javac"; }
6507     virtual ~TaskJavac()
6508         {}
6510     virtual bool execute()
6511         {
6512         return true;
6513         }
6515     virtual bool parse(Element *elem)
6516         {
6517         return true;
6518         }
6519 };
6522 /**
6523  *
6524  */
6525 class TaskLink : public Task
6527 public:
6529     TaskLink(MakeBase &par) : Task(par)
6530         {
6531         type = TASK_LINK; name = "link";
6532         command = "g++";
6533         doStrip = false;
6534                 stripCommand = "strip";
6535                 objcopyCommand = "objcopy";
6536         }
6538     virtual ~TaskLink()
6539         {}
6541     virtual bool execute()
6542         {
6543         if (!listFiles(parent, fileSet))
6544             return false;
6545         String fileSetDir = fileSet.getDirectory();
6546         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6547         bool doit = false;
6548         String fullTarget = parent.resolve(fileName);
6549         String cmd = command;
6550         cmd.append(" -o ");
6551         cmd.append(fullTarget);
6552         cmd.append(" ");
6553         cmd.append(flags);
6554         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6555             {
6556             cmd.append(" ");
6557             String obj;
6558             if (fileSetDir.size()>0)
6559                 {
6560                 obj.append(fileSetDir);
6561                 obj.append("/");
6562                 }
6563             obj.append(fileSet[i]);
6564             String fullObj = parent.resolve(obj);
6565             String nativeFullObj = getNativePath(fullObj);
6566             cmd.append(nativeFullObj);
6567             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6568             //          fullObj.c_str());
6569             if (isNewerThan(fullObj, fullTarget))
6570                 doit = true;
6571             }
6572         cmd.append(" ");
6573         cmd.append(libs);
6574         if (!doit)
6575             {
6576             //trace("link not needed");
6577             return true;
6578             }
6579         //trace("LINK cmd:%s", cmd.c_str());
6582         String outbuf, errbuf;
6583         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6584             {
6585             error("LINK problem: %s", errbuf.c_str());
6586             return false;
6587             }
6589         if (symFileName.size()>0)
6590             {
6591             String symFullName = parent.resolve(symFileName);
6592             cmd = objcopyCommand;
6593             cmd.append(" --only-keep-debug ");
6594             cmd.append(getNativePath(fullTarget));
6595             cmd.append(" ");
6596             cmd.append(getNativePath(symFullName));
6597             if (!executeCommand(cmd, "", outbuf, errbuf))
6598                 {
6599                 error("<strip> symbol file failed : %s", errbuf.c_str());
6600                 return false;
6601                 }
6602             }
6603             
6604         if (doStrip)
6605             {
6606             cmd = stripCommand;
6607             cmd.append(" ");
6608             cmd.append(getNativePath(fullTarget));
6609             if (!executeCommand(cmd, "", outbuf, errbuf))
6610                {
6611                error("<strip> failed : %s", errbuf.c_str());
6612                return false;
6613                }
6614             }
6616         return true;
6617         }
6619     virtual bool parse(Element *elem)
6620         {
6621         String s;
6622         if (!parent.getAttribute(elem, "command", s))
6623             return false;
6624         if (s.size()>0)
6625             command = s;
6626         if (!parent.getAttribute(elem, "objcopycommand", s))
6627             return false;
6628         if (s.size()>0)
6629             objcopyCommand = s;
6630         if (!parent.getAttribute(elem, "stripcommand", s))
6631             return false;
6632         if (s.size()>0)
6633             stripCommand = s;
6634         if (!parent.getAttribute(elem, "out", fileName))
6635             return false;
6636         if (!parent.getAttribute(elem, "strip", s))
6637             return false;
6638         if (s.size()>0 && !getBool(s, doStrip))
6639             return false;
6640         if (!parent.getAttribute(elem, "symfile", symFileName))
6641             return false;
6642             
6643         std::vector<Element *> children = elem->getChildren();
6644         for (unsigned int i=0 ; i<children.size() ; i++)
6645             {
6646             Element *child = children[i];
6647             String tagName = child->getName();
6648             if (tagName == "fileset")
6649                 {
6650                 if (!parseFileSet(child, parent, fileSet))
6651                     return false;
6652                 }
6653             else if (tagName == "flags")
6654                 {
6655                 if (!parent.getValue(child, flags))
6656                     return false;
6657                 flags = strip(flags);
6658                 }
6659             else if (tagName == "libs")
6660                 {
6661                 if (!parent.getValue(child, libs))
6662                     return false;
6663                 libs = strip(libs);
6664                 }
6665             }
6666         return true;
6667         }
6669 private:
6671     String  command;
6672     String  fileName;
6673     String  flags;
6674     String  libs;
6675     FileSet fileSet;
6676     bool    doStrip;
6677     String  symFileName;
6678     String  stripCommand;
6679     String  objcopyCommand;
6681 };
6685 /**
6686  * Create a named directory
6687  */
6688 class TaskMakeFile : public Task
6690 public:
6692     TaskMakeFile(MakeBase &par) : Task(par)
6693         { type = TASK_MAKEFILE; name = "makefile"; }
6695     virtual ~TaskMakeFile()
6696         {}
6698     virtual bool execute()
6699         {
6700         status("          : %s", fileName.c_str());
6701         String fullName = parent.resolve(fileName);
6702         if (!isNewerThan(parent.getURI().getPath(), fullName))
6703             {
6704             //trace("skipped <makefile>");
6705             return true;
6706             }
6707         //trace("fullName:%s", fullName.c_str());
6708         FILE *f = fopen(fullName.c_str(), "w");
6709         if (!f)
6710             {
6711             error("<makefile> could not open %s for writing : %s",
6712                 fullName.c_str(), strerror(errno));
6713             return false;
6714             }
6715         for (unsigned int i=0 ; i<text.size() ; i++)
6716             fputc(text[i], f);
6717         fputc('\n', f);
6718         fclose(f);
6719         return true;
6720         }
6722     virtual bool parse(Element *elem)
6723         {
6724         if (!parent.getAttribute(elem, "file", fileName))
6725             return false;
6726         if (fileName.size() == 0)
6727             {
6728             error("<makefile> requires 'file=\"filename\"' attribute");
6729             return false;
6730             }
6731         if (!parent.getValue(elem, text))
6732             return false;
6733         text = leftJustify(text);
6734         //trace("dirname:%s", dirName.c_str());
6735         return true;
6736         }
6738 private:
6740     String fileName;
6741     String text;
6742 };
6746 /**
6747  * Create a named directory
6748  */
6749 class TaskMkDir : public Task
6751 public:
6753     TaskMkDir(MakeBase &par) : Task(par)
6754         { type = TASK_MKDIR; name = "mkdir"; }
6756     virtual ~TaskMkDir()
6757         {}
6759     virtual bool execute()
6760         {
6761         status("          : %s", dirName.c_str());
6762         String fullDir = parent.resolve(dirName);
6763         //trace("fullDir:%s", fullDir.c_str());
6764         if (!createDirectory(fullDir))
6765             return false;
6766         return true;
6767         }
6769     virtual bool parse(Element *elem)
6770         {
6771         if (!parent.getAttribute(elem, "dir", dirName))
6772             return false;
6773         if (dirName.size() == 0)
6774             {
6775             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6776             return false;
6777             }
6778         return true;
6779         }
6781 private:
6783     String dirName;
6784 };
6788 /**
6789  * Create a named directory
6790  */
6791 class TaskMsgFmt: public Task
6793 public:
6795     TaskMsgFmt(MakeBase &par) : Task(par)
6796          {
6797          type    = TASK_MSGFMT;
6798          name    = "msgfmt";
6799          command = "msgfmt";
6800          owndir  = false;
6801          outName = "";
6802          }
6804     virtual ~TaskMsgFmt()
6805         {}
6807     virtual bool execute()
6808         {
6809         if (!listFiles(parent, fileSet))
6810             return false;
6811         String fileSetDir = fileSet.getDirectory();
6813         //trace("msgfmt: %d", fileSet.size());
6814         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6815             {
6816             String fileName = fileSet[i];
6817             if (getSuffix(fileName) != "po")
6818                 continue;
6819             String sourcePath;
6820             if (fileSetDir.size()>0)
6821                 {
6822                 sourcePath.append(fileSetDir);
6823                 sourcePath.append("/");
6824                 }
6825             sourcePath.append(fileName);
6826             String fullSource = parent.resolve(sourcePath);
6828             String destPath;
6829             if (toDirName.size()>0)
6830                 {
6831                 destPath.append(toDirName);
6832                 destPath.append("/");
6833                 }
6834             if (owndir)
6835                 {
6836                 String subdir = fileName;
6837                 unsigned int pos = subdir.find_last_of('.');
6838                 if (pos != subdir.npos)
6839                     subdir = subdir.substr(0, pos);
6840                 destPath.append(subdir);
6841                 destPath.append("/");
6842                 }
6843             //Pick the output file name
6844             if (outName.size() > 0)
6845                 {
6846                 destPath.append(outName);
6847                 }
6848             else
6849                 {
6850                 destPath.append(fileName);
6851                 destPath[destPath.size()-2] = 'm';
6852                 }
6854             String fullDest = parent.resolve(destPath);
6856             if (!isNewerThan(fullSource, fullDest))
6857                 {
6858                 //trace("skip %s", fullSource.c_str());
6859                 continue;
6860                 }
6861                 
6862             String cmd = command;
6863             cmd.append(" ");
6864             cmd.append(fullSource);
6865             cmd.append(" -o ");
6866             cmd.append(fullDest);
6867             
6868             int pos = fullDest.find_last_of('/');
6869             if (pos>0)
6870                 {
6871                 String fullDestPath = fullDest.substr(0, pos);
6872                 if (!createDirectory(fullDestPath))
6873                     return false;
6874                 }
6878             String outString, errString;
6879             if (!executeCommand(cmd.c_str(), "", outString, errString))
6880                 {
6881                 error("<msgfmt> problem: %s", errString.c_str());
6882                 return false;
6883                 }
6884             }
6886         return true;
6887         }
6889     virtual bool parse(Element *elem)
6890         {
6891         String s;
6892         if (!parent.getAttribute(elem, "command", s))
6893             return false;
6894         if (s.size()>0)
6895             command = s;
6896         if (!parent.getAttribute(elem, "todir", toDirName))
6897             return false;
6898         if (!parent.getAttribute(elem, "out", outName))
6899             return false;
6900         if (!parent.getAttribute(elem, "owndir", s))
6901             return false;
6902         if (s.size()>0 && !getBool(s, owndir))
6903             return false;
6904             
6905         std::vector<Element *> children = elem->getChildren();
6906         for (unsigned int i=0 ; i<children.size() ; i++)
6907             {
6908             Element *child = children[i];
6909             String tagName = child->getName();
6910             if (tagName == "fileset")
6911                 {
6912                 if (!parseFileSet(child, parent, fileSet))
6913                     return false;
6914                 }
6915             }
6916         return true;
6917         }
6919 private:
6921     String  command;
6922     String  toDirName;
6923     String  outName;
6924     FileSet fileSet;
6925     bool    owndir;
6927 };
6931 /**
6932  *  Perform a Package-Config query similar to pkg-config
6933  */
6934 class TaskPkgConfig : public Task
6936 public:
6938     typedef enum
6939         {
6940         PKG_CONFIG_QUERY_CFLAGS,
6941         PKG_CONFIG_QUERY_LIBS,
6942         PKG_CONFIG_QUERY_BOTH
6943         } QueryTypes;
6945     TaskPkgConfig(MakeBase &par) : Task(par)
6946         {
6947         type = TASK_PKG_CONFIG; name = "pkg-config";
6948         }
6950     virtual ~TaskPkgConfig()
6951         {}
6953     virtual bool execute()
6954         {
6955         String path = parent.resolve(pkg_config_path);
6956         //fill this in
6957         return true;
6958         }
6960     virtual bool parse(Element *elem)
6961         {
6962         String s;
6963         if (!parent.getAttribute(elem, "path", s))
6964             return false;
6965         if (s.size()>0)
6966            pkg_config_path = s;
6967         if (!parent.getAttribute(elem, "query", s))
6968             return false;
6969         if (s == "cflags")
6970             query = PKG_CONFIG_QUERY_CFLAGS;
6971         else if (s == "libs")
6972             query = PKG_CONFIG_QUERY_LIBS;
6973         else if (s == "both")
6974             query = PKG_CONFIG_QUERY_BOTH;
6975         else
6976             {
6977             error("<pkg-config> requires 'query=\"type\"' attribute");
6978             error("where type = cflags, libs, or both");
6979             return false;
6980             }
6981         return true;
6982         }
6984 private:
6986     String pkg_config_path;
6987     int query;
6989 };
6996 /**
6997  *  Process an archive to allow random access
6998  */
6999 class TaskRanlib : public Task
7001 public:
7003     TaskRanlib(MakeBase &par) : Task(par)
7004         {
7005         type = TASK_RANLIB; name = "ranlib";
7006         command = "ranlib";
7007         }
7009     virtual ~TaskRanlib()
7010         {}
7012     virtual bool execute()
7013         {
7014         String fullName = parent.resolve(fileName);
7015         //trace("fullDir:%s", fullDir.c_str());
7016         String cmd = command;
7017         cmd.append(" ");
7018         cmd.append(fullName);
7019         String outbuf, errbuf;
7020         if (!executeCommand(cmd, "", outbuf, errbuf))
7021             return false;
7022         return true;
7023         }
7025     virtual bool parse(Element *elem)
7026         {
7027         String s;
7028         if (!parent.getAttribute(elem, "command", s))
7029             return false;
7030         if (s.size()>0)
7031            command = s;
7032         if (!parent.getAttribute(elem, "file", fileName))
7033             return false;
7034         if (fileName.size() == 0)
7035             {
7036             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7037             return false;
7038             }
7039         return true;
7040         }
7042 private:
7044     String fileName;
7045     String command;
7046 };
7050 /**
7051  * Run the "ar" command to archive .o's into a .a
7052  */
7053 class TaskRC : public Task
7055 public:
7057     TaskRC(MakeBase &par) : Task(par)
7058         {
7059         type = TASK_RC; name = "rc";
7060         command = "windres";
7061         }
7063     virtual ~TaskRC()
7064         {}
7066     virtual bool execute()
7067         {
7068         String fullFile = parent.resolve(fileName);
7069         String fullOut  = parent.resolve(outName);
7070         if (!isNewerThan(fullFile, fullOut))
7071             return true;
7072         String cmd = command;
7073         cmd.append(" -o ");
7074         cmd.append(fullOut);
7075         cmd.append(" ");
7076         cmd.append(flags);
7077         cmd.append(" ");
7078         cmd.append(fullFile);
7080         String outString, errString;
7081         if (!executeCommand(cmd.c_str(), "", outString, errString))
7082             {
7083             error("RC problem: %s", errString.c_str());
7084             return false;
7085             }
7086         return true;
7087         }
7089     virtual bool parse(Element *elem)
7090         {
7091         if (!parent.getAttribute(elem, "command", command))
7092             return false;
7093         if (!parent.getAttribute(elem, "file", fileName))
7094             return false;
7095         if (!parent.getAttribute(elem, "out", outName))
7096             return false;
7097         std::vector<Element *> children = elem->getChildren();
7098         for (unsigned int i=0 ; i<children.size() ; i++)
7099             {
7100             Element *child = children[i];
7101             String tagName = child->getName();
7102             if (tagName == "flags")
7103                 {
7104                 if (!parent.getValue(child, flags))
7105                     return false;
7106                 }
7107             }
7108         return true;
7109         }
7111 private:
7113     String command;
7114     String flags;
7115     String fileName;
7116     String outName;
7118 };
7122 /**
7123  *  Collect .o's into a .so or DLL
7124  */
7125 class TaskSharedLib : public Task
7127 public:
7129     TaskSharedLib(MakeBase &par) : Task(par)
7130         {
7131         type = TASK_SHAREDLIB; name = "dll";
7132         command = "dllwrap";
7133         }
7135     virtual ~TaskSharedLib()
7136         {}
7138     virtual bool execute()
7139         {
7140         //trace("###########HERE %d", fileSet.size());
7141         bool doit = false;
7142         
7143         String fullOut = parent.resolve(fileName);
7144         //trace("ar fullout: %s", fullOut.c_str());
7145         
7146         if (!listFiles(parent, fileSet))
7147             return false;
7148         String fileSetDir = fileSet.getDirectory();
7150         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7151             {
7152             String fname;
7153             if (fileSetDir.size()>0)
7154                 {
7155                 fname.append(fileSetDir);
7156                 fname.append("/");
7157                 }
7158             fname.append(fileSet[i]);
7159             String fullName = parent.resolve(fname);
7160             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7161             if (isNewerThan(fullName, fullOut))
7162                 doit = true;
7163             }
7164         //trace("Needs it:%d", doit);
7165         if (!doit)
7166             {
7167             return true;
7168             }
7170         String cmd = "dllwrap";
7171         cmd.append(" -o ");
7172         cmd.append(fullOut);
7173         if (defFileName.size()>0)
7174             {
7175             cmd.append(" --def ");
7176             cmd.append(defFileName);
7177             cmd.append(" ");
7178             }
7179         if (impFileName.size()>0)
7180             {
7181             cmd.append(" --implib ");
7182             cmd.append(impFileName);
7183             cmd.append(" ");
7184             }
7185         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7186             {
7187             String fname;
7188             if (fileSetDir.size()>0)
7189                 {
7190                 fname.append(fileSetDir);
7191                 fname.append("/");
7192                 }
7193             fname.append(fileSet[i]);
7194             String fullName = parent.resolve(fname);
7196             cmd.append(" ");
7197             cmd.append(fullName);
7198             }
7199         cmd.append(" ");
7200         cmd.append(libs);
7202         String outString, errString;
7203         if (!executeCommand(cmd.c_str(), "", outString, errString))
7204             {
7205             error("<sharedlib> problem: %s", errString.c_str());
7206             return false;
7207             }
7209         return true;
7210         }
7212     virtual bool parse(Element *elem)
7213         {
7214         if (!parent.getAttribute(elem, "file", fileName))
7215             return false;
7216         if (!parent.getAttribute(elem, "import", impFileName))
7217             return false;
7218         if (!parent.getAttribute(elem, "def", defFileName))
7219             return false;
7220             
7221         std::vector<Element *> children = elem->getChildren();
7222         for (unsigned int i=0 ; i<children.size() ; i++)
7223             {
7224             Element *child = children[i];
7225             String tagName = child->getName();
7226             if (tagName == "fileset")
7227                 {
7228                 if (!parseFileSet(child, parent, fileSet))
7229                     return false;
7230                 }
7231             else if (tagName == "libs")
7232                 {
7233                 if (!parent.getValue(child, libs))
7234                     return false;
7235                 libs = strip(libs);
7236                 }
7237             }
7238         return true;
7239         }
7241 private:
7243     String command;
7244     String fileName;
7245     String defFileName;
7246     String impFileName;
7247     FileSet fileSet;
7248     String libs;
7250 };
7254 /**
7255  * Run the "ar" command to archive .o's into a .a
7256  */
7257 class TaskStaticLib : public Task
7259 public:
7261     TaskStaticLib(MakeBase &par) : Task(par)
7262         {
7263         type = TASK_STATICLIB; name = "staticlib";
7264         command = "ar crv";
7265         }
7267     virtual ~TaskStaticLib()
7268         {}
7270     virtual bool execute()
7271         {
7272         //trace("###########HERE %d", fileSet.size());
7273         bool doit = false;
7274         
7275         String fullOut = parent.resolve(fileName);
7276         //trace("ar fullout: %s", fullOut.c_str());
7277         
7278         if (!listFiles(parent, fileSet))
7279             return false;
7280         String fileSetDir = fileSet.getDirectory();
7282         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7283             {
7284             String fname;
7285             if (fileSetDir.size()>0)
7286                 {
7287                 fname.append(fileSetDir);
7288                 fname.append("/");
7289                 }
7290             fname.append(fileSet[i]);
7291             String fullName = parent.resolve(fname);
7292             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7293             if (isNewerThan(fullName, fullOut))
7294                 doit = true;
7295             }
7296         //trace("Needs it:%d", doit);
7297         if (!doit)
7298             {
7299             return true;
7300             }
7302         String cmd = command;
7303         cmd.append(" ");
7304         cmd.append(fullOut);
7305         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7306             {
7307             String fname;
7308             if (fileSetDir.size()>0)
7309                 {
7310                 fname.append(fileSetDir);
7311                 fname.append("/");
7312                 }
7313             fname.append(fileSet[i]);
7314             String fullName = parent.resolve(fname);
7316             cmd.append(" ");
7317             cmd.append(fullName);
7318             }
7320         String outString, errString;
7321         if (!executeCommand(cmd.c_str(), "", outString, errString))
7322             {
7323             error("<staticlib> problem: %s", errString.c_str());
7324             return false;
7325             }
7327         return true;
7328         }
7331     virtual bool parse(Element *elem)
7332         {
7333         String s;
7334         if (!parent.getAttribute(elem, "command", s))
7335             return false;
7336         if (s.size()>0)
7337             command = s;
7338         if (!parent.getAttribute(elem, "file", fileName))
7339             return false;
7340             
7341         std::vector<Element *> children = elem->getChildren();
7342         for (unsigned int i=0 ; i<children.size() ; i++)
7343             {
7344             Element *child = children[i];
7345             String tagName = child->getName();
7346             if (tagName == "fileset")
7347                 {
7348                 if (!parseFileSet(child, parent, fileSet))
7349                     return false;
7350                 }
7351             }
7352         return true;
7353         }
7355 private:
7357     String command;
7358     String fileName;
7359     FileSet fileSet;
7361 };
7366 /**
7367  * Strip an executable
7368  */
7369 class TaskStrip : public Task
7371 public:
7373     TaskStrip(MakeBase &par) : Task(par)
7374         { type = TASK_STRIP; name = "strip"; }
7376     virtual ~TaskStrip()
7377         {}
7379     virtual bool execute()
7380         {
7381         String fullName = parent.resolve(fileName);
7382         //trace("fullDir:%s", fullDir.c_str());
7383         String cmd;
7384         String outbuf, errbuf;
7386         if (symFileName.size()>0)
7387             {
7388             String symFullName = parent.resolve(symFileName);
7389             cmd = "objcopy --only-keep-debug ";
7390             cmd.append(getNativePath(fullName));
7391             cmd.append(" ");
7392             cmd.append(getNativePath(symFullName));
7393             if (!executeCommand(cmd, "", outbuf, errbuf))
7394                 {
7395                 error("<strip> symbol file failed : %s", errbuf.c_str());
7396                 return false;
7397                 }
7398             }
7399             
7400         cmd = "strip ";
7401         cmd.append(getNativePath(fullName));
7402         if (!executeCommand(cmd, "", outbuf, errbuf))
7403             {
7404             error("<strip> failed : %s", errbuf.c_str());
7405             return false;
7406             }
7407         return true;
7408         }
7410     virtual bool parse(Element *elem)
7411         {
7412         if (!parent.getAttribute(elem, "file", fileName))
7413             return false;
7414         if (!parent.getAttribute(elem, "symfile", symFileName))
7415             return false;
7416         if (fileName.size() == 0)
7417             {
7418             error("<strip> requires 'file=\"fileName\"' attribute");
7419             return false;
7420             }
7421         return true;
7422         }
7424 private:
7426     String fileName;
7427     String symFileName;
7428 };
7431 /**
7432  *
7433  */
7434 class TaskTouch : public Task
7436 public:
7438     TaskTouch(MakeBase &par) : Task(par)
7439         { type = TASK_TOUCH; name = "touch"; }
7441     virtual ~TaskTouch()
7442         {}
7444     virtual bool execute()
7445         {
7446         String fullName = parent.resolve(fileName);
7447         String nativeFile = getNativePath(fullName);
7448         if (!isRegularFile(fullName) && !isDirectory(fullName))
7449             {            
7450             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7451             int ret = creat(nativeFile.c_str(), 0666);
7452             if (ret != 0) 
7453                 {
7454                 error("<touch> could not create '%s' : %s",
7455                     nativeFile.c_str(), strerror(ret));
7456                 return false;
7457                 }
7458             return true;
7459             }
7460         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7461         if (ret != 0)
7462             {
7463             error("<touch> could not update the modification time for '%s' : %s",
7464                 nativeFile.c_str(), strerror(ret));
7465             return false;
7466             }
7467         return true;
7468         }
7470     virtual bool parse(Element *elem)
7471         {
7472         //trace("touch parse");
7473         if (!parent.getAttribute(elem, "file", fileName))
7474             return false;
7475         if (fileName.size() == 0)
7476             {
7477             error("<touch> requires 'file=\"fileName\"' attribute");
7478             return false;
7479             }
7480         return true;
7481         }
7483     String fileName;
7484 };
7487 /**
7488  *
7489  */
7490 class TaskTstamp : public Task
7492 public:
7494     TaskTstamp(MakeBase &par) : Task(par)
7495         { type = TASK_TSTAMP; name = "tstamp"; }
7497     virtual ~TaskTstamp()
7498         {}
7500     virtual bool execute()
7501         {
7502         return true;
7503         }
7505     virtual bool parse(Element *elem)
7506         {
7507         //trace("tstamp parse");
7508         return true;
7509         }
7510 };
7514 /**
7515  *
7516  */
7517 Task *Task::createTask(Element *elem, int lineNr)
7519     String tagName = elem->getName();
7520     //trace("task:%s", tagName.c_str());
7521     Task *task = NULL;
7522     if (tagName == "cc")
7523         task = new TaskCC(parent);
7524     else if (tagName == "copy")
7525         task = new TaskCopy(parent);
7526     else if (tagName == "delete")
7527         task = new TaskDelete(parent);
7528     else if (tagName == "jar")
7529         task = new TaskJar(parent);
7530     else if (tagName == "javac")
7531         task = new TaskJavac(parent);
7532     else if (tagName == "link")
7533         task = new TaskLink(parent);
7534     else if (tagName == "makefile")
7535         task = new TaskMakeFile(parent);
7536     else if (tagName == "mkdir")
7537         task = new TaskMkDir(parent);
7538     else if (tagName == "msgfmt")
7539         task = new TaskMsgFmt(parent);
7540     else if (tagName == "ranlib")
7541         task = new TaskRanlib(parent);
7542     else if (tagName == "rc")
7543         task = new TaskRC(parent);
7544     else if (tagName == "sharedlib")
7545         task = new TaskSharedLib(parent);
7546     else if (tagName == "staticlib")
7547         task = new TaskStaticLib(parent);
7548     else if (tagName == "strip")
7549         task = new TaskStrip(parent);
7550     else if (tagName == "touch")
7551         task = new TaskTouch(parent);
7552     else if (tagName == "tstamp")
7553         task = new TaskTstamp(parent);
7554     else
7555         {
7556         error("Unknown task '%s'", tagName.c_str());
7557         return NULL;
7558         }
7560     task->setLine(lineNr);
7562     if (!task->parse(elem))
7563         {
7564         delete task;
7565         return NULL;
7566         }
7567     return task;
7572 //########################################################################
7573 //# T A R G E T
7574 //########################################################################
7576 /**
7577  *
7578  */
7579 class Target : public MakeBase
7582 public:
7584     /**
7585      *
7586      */
7587     Target(Make &par) : parent(par)
7588         { init(); }
7590     /**
7591      *
7592      */
7593     Target(const Target &other) : parent(other.parent)
7594         { init(); assign(other); }
7596     /**
7597      *
7598      */
7599     Target &operator=(const Target &other)
7600         { init(); assign(other); return *this; }
7602     /**
7603      *
7604      */
7605     virtual ~Target()
7606         { cleanup() ; }
7609     /**
7610      *
7611      */
7612     virtual Make &getParent()
7613         { return parent; }
7615     /**
7616      *
7617      */
7618     virtual String getName()
7619         { return name; }
7621     /**
7622      *
7623      */
7624     virtual void setName(const String &val)
7625         { name = val; }
7627     /**
7628      *
7629      */
7630     virtual String getDescription()
7631         { return description; }
7633     /**
7634      *
7635      */
7636     virtual void setDescription(const String &val)
7637         { description = val; }
7639     /**
7640      *
7641      */
7642     virtual void addDependency(const String &val)
7643         { deps.push_back(val); }
7645     /**
7646      *
7647      */
7648     virtual void parseDependencies(const String &val)
7649         { deps = tokenize(val, ", "); }
7651     /**
7652      *
7653      */
7654     virtual std::vector<String> &getDependencies()
7655         { return deps; }
7657     /**
7658      *
7659      */
7660     virtual String getIf()
7661         { return ifVar; }
7663     /**
7664      *
7665      */
7666     virtual void setIf(const String &val)
7667         { ifVar = val; }
7669     /**
7670      *
7671      */
7672     virtual String getUnless()
7673         { return unlessVar; }
7675     /**
7676      *
7677      */
7678     virtual void setUnless(const String &val)
7679         { unlessVar = val; }
7681     /**
7682      *
7683      */
7684     virtual void addTask(Task *val)
7685         { tasks.push_back(val); }
7687     /**
7688      *
7689      */
7690     virtual std::vector<Task *> &getTasks()
7691         { return tasks; }
7693 private:
7695     void init()
7696         {
7697         }
7699     void cleanup()
7700         {
7701         tasks.clear();
7702         }
7704     void assign(const Target &other)
7705         {
7706         //parent      = other.parent;
7707         name        = other.name;
7708         description = other.description;
7709         ifVar       = other.ifVar;
7710         unlessVar   = other.unlessVar;
7711         deps        = other.deps;
7712         tasks       = other.tasks;
7713         }
7715     Make &parent;
7717     String name;
7719     String description;
7721     String ifVar;
7723     String unlessVar;
7725     std::vector<String> deps;
7727     std::vector<Task *> tasks;
7729 };
7738 //########################################################################
7739 //# M A K E
7740 //########################################################################
7743 /**
7744  *
7745  */
7746 class Make : public MakeBase
7749 public:
7751     /**
7752      *
7753      */
7754     Make()
7755         { init(); }
7757     /**
7758      *
7759      */
7760     Make(const Make &other)
7761         { assign(other); }
7763     /**
7764      *
7765      */
7766     Make &operator=(const Make &other)
7767         { assign(other); return *this; }
7769     /**
7770      *
7771      */
7772     virtual ~Make()
7773         { cleanup(); }
7775     /**
7776      *
7777      */
7778     virtual std::map<String, Target> &getTargets()
7779         { return targets; }
7782     /**
7783      *
7784      */
7785     virtual String version()
7786         { return BUILDTOOL_VERSION; }
7788     /**
7789      * Overload a <property>
7790      */
7791     virtual bool specifyProperty(const String &name,
7792                                  const String &value);
7794     /**
7795      *
7796      */
7797     virtual bool run();
7799     /**
7800      *
7801      */
7802     virtual bool run(const String &target);
7806 private:
7808     /**
7809      *
7810      */
7811     void init();
7813     /**
7814      *
7815      */
7816     void cleanup();
7818     /**
7819      *
7820      */
7821     void assign(const Make &other);
7823     /**
7824      *
7825      */
7826     bool executeTask(Task &task);
7829     /**
7830      *
7831      */
7832     bool executeTarget(Target &target,
7833              std::set<String> &targetsCompleted);
7836     /**
7837      *
7838      */
7839     bool execute();
7841     /**
7842      *
7843      */
7844     bool checkTargetDependencies(Target &prop,
7845                     std::vector<String> &depList);
7847     /**
7848      *
7849      */
7850     bool parsePropertyFile(const String &fileName,
7851                            const String &prefix);
7853     /**
7854      *
7855      */
7856     bool parseProperty(Element *elem);
7858     /**
7859      *
7860      */
7861     bool parseFile();
7863     /**
7864      *
7865      */
7866     std::vector<String> glob(const String &pattern);
7869     //###############
7870     //# Fields
7871     //###############
7873     String projectName;
7875     String currentTarget;
7877     String defaultTarget;
7879     String specifiedTarget;
7881     String baseDir;
7883     String description;
7884     
7885     String envAlias;
7887     //std::vector<Property> properties;
7888     
7889     std::map<String, Target> targets;
7891     std::vector<Task *> allTasks;
7892     
7893     std::map<String, String> specifiedProperties;
7895 };
7898 //########################################################################
7899 //# C L A S S  M A I N T E N A N C E
7900 //########################################################################
7902 /**
7903  *
7904  */
7905 void Make::init()
7907     uri             = "build.xml";
7908     projectName     = "";
7909     currentTarget   = "";
7910     defaultTarget   = "";
7911     specifiedTarget = "";
7912     baseDir         = "";
7913     description     = "";
7914     envAlias        = "";
7915     properties.clear();
7916     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7917         delete allTasks[i];
7918     allTasks.clear();
7923 /**
7924  *
7925  */
7926 void Make::cleanup()
7928     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7929         delete allTasks[i];
7930     allTasks.clear();
7935 /**
7936  *
7937  */
7938 void Make::assign(const Make &other)
7940     uri              = other.uri;
7941     projectName      = other.projectName;
7942     currentTarget    = other.currentTarget;
7943     defaultTarget    = other.defaultTarget;
7944     specifiedTarget  = other.specifiedTarget;
7945     baseDir          = other.baseDir;
7946     description      = other.description;
7947     properties       = other.properties;
7952 //########################################################################
7953 //# U T I L I T Y    T A S K S
7954 //########################################################################
7956 /**
7957  *  Perform a file globbing
7958  */
7959 std::vector<String> Make::glob(const String &pattern)
7961     std::vector<String> res;
7962     return res;
7966 //########################################################################
7967 //# P U B L I C    A P I
7968 //########################################################################
7972 /**
7973  *
7974  */
7975 bool Make::executeTarget(Target &target,
7976              std::set<String> &targetsCompleted)
7979     String name = target.getName();
7981     //First get any dependencies for this target
7982     std::vector<String> deps = target.getDependencies();
7983     for (unsigned int i=0 ; i<deps.size() ; i++)
7984         {
7985         String dep = deps[i];
7986         //Did we do it already?  Skip
7987         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7988             continue;
7989             
7990         std::map<String, Target> &tgts =
7991                target.getParent().getTargets();
7992         std::map<String, Target>::iterator iter =
7993                tgts.find(dep);
7994         if (iter == tgts.end())
7995             {
7996             error("Target '%s' dependency '%s' not found",
7997                       name.c_str(),  dep.c_str());
7998             return false;
7999             }
8000         Target depTarget = iter->second;
8001         if (!executeTarget(depTarget, targetsCompleted))
8002             {
8003             return false;
8004             }
8005         }
8007     status("## Target : %s", name.c_str());
8009     //Now let's do the tasks
8010     std::vector<Task *> &tasks = target.getTasks();
8011     for (unsigned int i=0 ; i<tasks.size() ; i++)
8012         {
8013         Task *task = tasks[i];
8014         status("---- task : %s", task->getName().c_str());
8015         if (!task->execute())
8016             {
8017             return false;
8018             }
8019         }
8020         
8021     targetsCompleted.insert(name);
8022     
8023     return true;
8028 /**
8029  *  Main execute() method.  Start here and work
8030  *  up the dependency tree 
8031  */
8032 bool Make::execute()
8034     status("######## EXECUTE");
8036     //Determine initial target
8037     if (specifiedTarget.size()>0)
8038         {
8039         currentTarget = specifiedTarget;
8040         }
8041     else if (defaultTarget.size()>0)
8042         {
8043         currentTarget = defaultTarget;
8044         }
8045     else
8046         {
8047         error("execute: no specified or default target requested");
8048         return false;
8049         }
8051     std::map<String, Target>::iterator iter =
8052                targets.find(currentTarget);
8053     if (iter == targets.end())
8054         {
8055         error("Initial target '%s' not found",
8056                  currentTarget.c_str());
8057         return false;
8058         }
8059         
8060     //Now run
8061     Target target = iter->second;
8062     std::set<String> targetsCompleted;
8063     if (!executeTarget(target, targetsCompleted))
8064         {
8065         return false;
8066         }
8068     status("######## EXECUTE COMPLETE");
8069     return true;
8075 /**
8076  *
8077  */
8078 bool Make::checkTargetDependencies(Target &target, 
8079                             std::vector<String> &depList)
8081     String tgtName = target.getName().c_str();
8082     depList.push_back(tgtName);
8084     std::vector<String> deps = target.getDependencies();
8085     for (unsigned int i=0 ; i<deps.size() ; i++)
8086         {
8087         String dep = deps[i];
8088         //First thing entered was the starting Target
8089         if (dep == depList[0])
8090             {
8091             error("Circular dependency '%s' found at '%s'",
8092                       dep.c_str(), tgtName.c_str());
8093             std::vector<String>::iterator diter;
8094             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8095                 {
8096                 error("  %s", diter->c_str());
8097                 }
8098             return false;
8099             }
8101         std::map<String, Target> &tgts =
8102                   target.getParent().getTargets();
8103         std::map<String, Target>::iterator titer = tgts.find(dep);
8104         if (titer == tgts.end())
8105             {
8106             error("Target '%s' dependency '%s' not found",
8107                       tgtName.c_str(), dep.c_str());
8108             return false;
8109             }
8110         if (!checkTargetDependencies(titer->second, depList))
8111             {
8112             return false;
8113             }
8114         }
8115     return true;
8122 static int getword(int pos, const String &inbuf, String &result)
8124     int p = pos;
8125     int len = (int)inbuf.size();
8126     String val;
8127     while (p < len)
8128         {
8129         char ch = inbuf[p];
8130         if (!isalnum(ch) && ch!='.' && ch!='_')
8131             break;
8132         val.push_back(ch);
8133         p++;
8134         }
8135     result = val;
8136     return p;
8142 /**
8143  *
8144  */
8145 bool Make::parsePropertyFile(const String &fileName,
8146                              const String &prefix)
8148     FILE *f = fopen(fileName.c_str(), "r");
8149     if (!f)
8150         {
8151         error("could not open property file %s", fileName.c_str());
8152         return false;
8153         }
8154     int linenr = 0;
8155     while (!feof(f))
8156         {
8157         char buf[256];
8158         if (!fgets(buf, 255, f))
8159             break;
8160         linenr++;
8161         String s = buf;
8162         s = trim(s);
8163         int len = s.size();
8164         if (len == 0)
8165             continue;
8166         if (s[0] == '#')
8167             continue;
8168         String key;
8169         String val;
8170         int p = 0;
8171         int p2 = getword(p, s, key);
8172         if (p2 <= p)
8173             {
8174             error("property file %s, line %d: expected keyword",
8175                     fileName.c_str(), linenr);
8176             return false;
8177             }
8178         if (prefix.size() > 0)
8179             {
8180             key.insert(0, prefix);
8181             }
8183         //skip whitespace
8184         for (p=p2 ; p<len ; p++)
8185             if (!isspace(s[p]))
8186                 break;
8188         if (p>=len || s[p]!='=')
8189             {
8190             error("property file %s, line %d: expected '='",
8191                     fileName.c_str(), linenr);
8192             return false;
8193             }
8194         p++;
8196         //skip whitespace
8197         for ( ; p<len ; p++)
8198             if (!isspace(s[p]))
8199                 break;
8201         /* This way expects a word after the =
8202         p2 = getword(p, s, val);
8203         if (p2 <= p)
8204             {
8205             error("property file %s, line %d: expected value",
8206                     fileName.c_str(), linenr);
8207             return false;
8208             }
8209         */
8210         // This way gets the rest of the line after the =
8211         if (p>=len)
8212             {
8213             error("property file %s, line %d: expected value",
8214                     fileName.c_str(), linenr);
8215             return false;
8216             }
8217         val = s.substr(p);
8218         if (key.size()==0)
8219             continue;
8220         //allow property to be set, even if val=""
8222         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8223         //See if we wanted to overload this property
8224         std::map<String, String>::iterator iter =
8225             specifiedProperties.find(key);
8226         if (iter!=specifiedProperties.end())
8227             {
8228             val = iter->second;
8229             status("overloading property '%s' = '%s'",
8230                    key.c_str(), val.c_str());
8231             }
8232         properties[key] = val;
8233         }
8234     fclose(f);
8235     return true;
8241 /**
8242  *
8243  */
8244 bool Make::parseProperty(Element *elem)
8246     std::vector<Attribute> &attrs = elem->getAttributes();
8247     for (unsigned int i=0 ; i<attrs.size() ; i++)
8248         {
8249         String attrName = attrs[i].getName();
8250         String attrVal  = attrs[i].getValue();
8252         if (attrName == "name")
8253             {
8254             String val;
8255             if (!getAttribute(elem, "value", val))
8256                 return false;
8257             if (val.size() > 0)
8258                 {
8259                 properties[attrVal] = val;
8260                 }
8261             else
8262                 {
8263                 if (!getAttribute(elem, "location", val))
8264                     return false;
8265                 //let the property exist, even if not defined
8266                 properties[attrVal] = val;
8267                 }
8268             //See if we wanted to overload this property
8269             std::map<String, String>::iterator iter =
8270                 specifiedProperties.find(attrVal);
8271             if (iter != specifiedProperties.end())
8272                 {
8273                 val = iter->second;
8274                 status("overloading property '%s' = '%s'",
8275                     attrVal.c_str(), val.c_str());
8276                 properties[attrVal] = val;
8277                 }
8278             }
8279         else if (attrName == "file")
8280             {
8281             String prefix;
8282             if (!getAttribute(elem, "prefix", prefix))
8283                 return false;
8284             if (prefix.size() > 0)
8285                 {
8286                 if (prefix[prefix.size()-1] != '.')
8287                     prefix.push_back('.');
8288                 }
8289             if (!parsePropertyFile(attrName, prefix))
8290                 return false;
8291             }
8292         else if (attrName == "environment")
8293             {
8294             if (envAlias.size() > 0)
8295                 {
8296                 error("environment property can only be set once");
8297                 return false;
8298                 }
8299             envAlias = attrVal;
8300             }
8301         }
8303     return true;
8309 /**
8310  *
8311  */
8312 bool Make::parseFile()
8314     status("######## PARSE : %s", uri.getPath().c_str());
8316     setLine(0);
8318     Parser parser;
8319     Element *root = parser.parseFile(uri.getNativePath());
8320     if (!root)
8321         {
8322         error("Could not open %s for reading",
8323               uri.getNativePath().c_str());
8324         return false;
8325         }
8326     
8327     setLine(root->getLine());
8329     if (root->getChildren().size()==0 ||
8330         root->getChildren()[0]->getName()!="project")
8331         {
8332         error("Main xml element should be <project>");
8333         delete root;
8334         return false;
8335         }
8337     //########## Project attributes
8338     Element *project = root->getChildren()[0];
8339     String s = project->getAttribute("name");
8340     if (s.size() > 0)
8341         projectName = s;
8342     s = project->getAttribute("default");
8343     if (s.size() > 0)
8344         defaultTarget = s;
8345     s = project->getAttribute("basedir");
8346     if (s.size() > 0)
8347         baseDir = s;
8349     //######### PARSE MEMBERS
8350     std::vector<Element *> children = project->getChildren();
8351     for (unsigned int i=0 ; i<children.size() ; i++)
8352         {
8353         Element *elem = children[i];
8354         setLine(elem->getLine());
8355         String tagName = elem->getName();
8357         //########## DESCRIPTION
8358         if (tagName == "description")
8359             {
8360             description = parser.trim(elem->getValue());
8361             }
8363         //######### PROPERTY
8364         else if (tagName == "property")
8365             {
8366             if (!parseProperty(elem))
8367                 return false;
8368             }
8370         //######### TARGET
8371         else if (tagName == "target")
8372             {
8373             String tname   = elem->getAttribute("name");
8374             String tdesc   = elem->getAttribute("description");
8375             String tdeps   = elem->getAttribute("depends");
8376             String tif     = elem->getAttribute("if");
8377             String tunless = elem->getAttribute("unless");
8378             Target target(*this);
8379             target.setName(tname);
8380             target.setDescription(tdesc);
8381             target.parseDependencies(tdeps);
8382             target.setIf(tif);
8383             target.setUnless(tunless);
8384             std::vector<Element *> telems = elem->getChildren();
8385             for (unsigned int i=0 ; i<telems.size() ; i++)
8386                 {
8387                 Element *telem = telems[i];
8388                 Task breeder(*this);
8389                 Task *task = breeder.createTask(telem, telem->getLine());
8390                 if (!task)
8391                     return false;
8392                 allTasks.push_back(task);
8393                 target.addTask(task);
8394                 }
8396             //Check name
8397             if (tname.size() == 0)
8398                 {
8399                 error("no name for target");
8400                 return false;
8401                 }
8402             //Check for duplicate name
8403             if (targets.find(tname) != targets.end())
8404                 {
8405                 error("target '%s' already defined", tname.c_str());
8406                 return false;
8407                 }
8408             //more work than targets[tname]=target, but avoids default allocator
8409             targets.insert(std::make_pair<String, Target>(tname, target));
8410             }
8411         //######### none of the above
8412         else
8413             {
8414             error("unknown toplevel tag: <%s>", tagName.c_str());
8415             return false;
8416             }
8418         }
8420     std::map<String, Target>::iterator iter;
8421     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8422         {
8423         Target tgt = iter->second;
8424         std::vector<String> depList;
8425         if (!checkTargetDependencies(tgt, depList))
8426             {
8427             return false;
8428             }
8429         }
8432     delete root;
8433     status("######## PARSE COMPLETE");
8434     return true;
8438 /**
8439  * Overload a <property>
8440  */
8441 bool Make::specifyProperty(const String &name, const String &value)
8443     if (specifiedProperties.find(name) != specifiedProperties.end())
8444         {
8445         error("Property %s already specified", name.c_str());
8446         return false;
8447         }
8448     specifiedProperties[name] = value;
8449     return true;
8454 /**
8455  *
8456  */
8457 bool Make::run()
8459     if (!parseFile())
8460         return false;
8461         
8462     if (!execute())
8463         return false;
8465     return true;
8471 /**
8472  * Get a formatted MM:SS.sss time elapsed string
8473  */ 
8474 static String
8475 timeDiffString(struct timeval &x, struct timeval &y)
8477     long microsX  = x.tv_usec;
8478     long secondsX = x.tv_sec;
8479     long microsY  = y.tv_usec;
8480     long secondsY = y.tv_sec;
8481     if (microsX < microsY)
8482         {
8483         microsX += 1000000;
8484         secondsX -= 1;
8485         }
8487     int seconds = (int)(secondsX - secondsY);
8488     int millis  = (int)((microsX - microsY)/1000);
8490     int minutes = seconds/60;
8491     seconds -= minutes*60;
8492     char buf[80];
8493     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8494     String ret = buf;
8495     return ret;
8496     
8499 /**
8500  *
8501  */
8502 bool Make::run(const String &target)
8504     status("####################################################");
8505     status("#   %s", version().c_str());
8506     status("####################################################");
8507     struct timeval timeStart, timeEnd;
8508     ::gettimeofday(&timeStart, NULL);
8509     specifiedTarget = target;
8510     if (!run())
8511         return false;
8512     ::gettimeofday(&timeEnd, NULL);
8513     String timeStr = timeDiffString(timeEnd, timeStart);
8514     status("####################################################");
8515     status("#   BuildTool Completed : %s", timeStr.c_str());
8516     status("####################################################");
8517     return true;
8526 }// namespace buildtool
8527 //########################################################################
8528 //# M A I N
8529 //########################################################################
8531 typedef buildtool::String String;
8533 /**
8534  *  Format an error message in printf() style
8535  */
8536 static void error(const char *fmt, ...)
8538     va_list ap;
8539     va_start(ap, fmt);
8540     fprintf(stderr, "BuildTool error: ");
8541     vfprintf(stderr, fmt, ap);
8542     fprintf(stderr, "\n");
8543     va_end(ap);
8547 static bool parseProperty(const String &s, String &name, String &val)
8549     int len = s.size();
8550     int i;
8551     for (i=0 ; i<len ; i++)
8552         {
8553         char ch = s[i];
8554         if (ch == '=')
8555             break;
8556         name.push_back(ch);
8557         }
8558     if (i>=len || s[i]!='=')
8559         {
8560         error("property requires -Dname=value");
8561         return false;
8562         }
8563     i++;
8564     for ( ; i<len ; i++)
8565         {
8566         char ch = s[i];
8567         val.push_back(ch);
8568         }
8569     return true;
8573 /**
8574  * Compare a buffer with a key, for the length of the key
8575  */
8576 static bool sequ(const String &buf, const char *key)
8578     int len = buf.size();
8579     for (int i=0 ; key[i] && i<len ; i++)
8580         {
8581         if (key[i] != buf[i])
8582             return false;
8583         }        
8584     return true;
8587 static void usage(int argc, char **argv)
8589     printf("usage:\n");
8590     printf("   %s [options] [target]\n", argv[0]);
8591     printf("Options:\n");
8592     printf("  -help, -h              print this message\n");
8593     printf("  -version               print the version information and exit\n");
8594     printf("  -file <file>           use given buildfile\n");
8595     printf("  -f <file>                 ''\n");
8596     printf("  -D<property>=<value>   use value for given property\n");
8602 /**
8603  * Parse the command-line args, get our options,
8604  * and run this thing
8605  */   
8606 static bool parseOptions(int argc, char **argv)
8608     if (argc < 1)
8609         {
8610         error("Cannot parse arguments");
8611         return false;
8612         }
8614     buildtool::Make make;
8616     String target;
8618     //char *progName = argv[0];
8619     for (int i=1 ; i<argc ; i++)
8620         {
8621         String arg = argv[i];
8622         if (arg.size()>1 && arg[0]=='-')
8623             {
8624             if (arg == "-h" || arg == "-help")
8625                 {
8626                 usage(argc,argv);
8627                 return true;
8628                 }
8629             else if (arg == "-version")
8630                 {
8631                 printf("%s", make.version().c_str());
8632                 return true;
8633                 }
8634             else if (arg == "-f" || arg == "-file")
8635                 {
8636                 if (i>=argc)
8637                    {
8638                    usage(argc, argv);
8639                    return false;
8640                    }
8641                 i++; //eat option
8642                 make.setURI(argv[i]);
8643                 }
8644             else if (arg.size()>2 && sequ(arg, "-D"))
8645                 {
8646                 String s = arg.substr(2, s.size());
8647                 String name, value;
8648                 if (!parseProperty(s, name, value))
8649                    {
8650                    usage(argc, argv);
8651                    return false;
8652                    }
8653                 if (!make.specifyProperty(name, value))
8654                     return false;
8655                 }
8656             else
8657                 {
8658                 error("Unknown option:%s", arg.c_str());
8659                 return false;
8660                 }
8661             }
8662         else
8663             {
8664             if (target.size()>0)
8665                 {
8666                 error("only one initial target");
8667                 usage(argc, argv);
8668                 return false;
8669                 }
8670             target = arg;
8671             }
8672         }
8674     //We have the options.  Now execute them
8675     if (!make.run(target))
8676         return false;
8678     return true;
8684 /*
8685 static bool runMake()
8687     buildtool::Make make;
8688     if (!make.run())
8689         return false;
8690     return true;
8694 static bool pkgConfigTest()
8696     buildtool::PkgConfig pkgConfig;
8697     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8698         return false;
8699     return true;
8704 static bool depTest()
8706     buildtool::DepTool deptool;
8707     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8708     if (!deptool.generateDependencies("build.dep"))
8709         return false;
8710     std::vector<buildtool::FileRec> res =
8711            deptool.loadDepFile("build.dep");
8712     if (res.size() == 0)
8713         return false;
8714     return true;
8717 static bool popenTest()
8719     buildtool::Make make;
8720     buildtool::String out, err;
8721     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8722     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8723     return true;
8727 static bool propFileTest()
8729     buildtool::Make make;
8730     make.parsePropertyFile("test.prop", "test.");
8731     return true;
8733 */
8735 int main(int argc, char **argv)
8738     if (!parseOptions(argc, argv))
8739         return 1;
8740     /*
8741     if (!popenTest())
8742         return 1;
8744     if (!depTest())
8745         return 1;
8746     if (!propFileTest())
8747         return 1;
8748     if (runMake())
8749         return 1;
8750     */
8751     return 0;
8755 //########################################################################
8756 //# E N D 
8757 //########################################################################