Code

8d05b6bcdb26b30d980fbc2d67b17dace23f750d
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006 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 build.exe
28  * (or whatever your compiler might be) 
29  * Then
30  * build
31  * or 
32  * build {target}
33  */  
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <dirent.h>
45 #include <string>
46 #include <map>
47 #include <set>
48 #include <vector>
50 #ifdef __WIN32__
51 #include <windows.h>
52 #endif
56 namespace buildtool
57 {
61 //########################################################################
62 //########################################################################
63 //##  R E G E X P
64 //########################################################################
65 //########################################################################
67 /**
68  * This is the T-Rex regular expression library, which we
69  * gratefully acknowledge.  It's clean code and small size allow
70  * us to embed it in BuildTool without adding a dependency
71  *
72  */    
74 //begin trex.h
76 #ifndef _TREX_H_
77 #define _TREX_H_
78 /***************************************************************
79         T-Rex a tiny regular expression library
81         Copyright (C) 2003-2006 Alberto Demichelis
83         This software is provided 'as-is', without any express 
84         or implied warranty. In no event will the authors be held 
85         liable for any damages arising from the use of this software.
87         Permission is granted to anyone to use this software for 
88         any purpose, including commercial applications, and to alter
89         it and redistribute it freely, subject to the following restrictions:
91                 1. The origin of this software must not be misrepresented;
92                 you must not claim that you wrote the original software.
93                 If you use this software in a product, an acknowledgment
94                 in the product documentation would be appreciated but
95                 is not required.
97                 2. Altered source versions must be plainly marked as such,
98                 and must not be misrepresented as being the original software.
100                 3. This notice may not be removed or altered from any
101                 source distribution.
103 ****************************************************************/
105 #ifdef _UNICODE
106 #define TRexChar unsigned short
107 #define MAX_CHAR 0xFFFF
108 #define _TREXC(c) L##c 
109 #define trex_strlen wcslen
110 #define trex_printf wprintf
111 #else
112 #define TRexChar char
113 #define MAX_CHAR 0xFF
114 #define _TREXC(c) (c) 
115 #define trex_strlen strlen
116 #define trex_printf printf
117 #endif
119 #ifndef TREX_API
120 #define TREX_API extern
121 #endif
123 #define TRex_True 1
124 #define TRex_False 0
126 typedef unsigned int TRexBool;
127 typedef struct TRex TRex;
129 typedef struct {
130         const TRexChar *begin;
131         int len;
132 } TRexMatch;
134 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
135 TREX_API void trex_free(TRex *exp);
136 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
137 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
138 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
139 TREX_API int trex_getsubexpcount(TRex* exp);
140 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
142 #endif
144 //end trex.h
146 //start trex.c
149 #include <stdio.h>
150 #include <string>
152 /* see copyright notice in trex.h */
153 #include <string.h>
154 #include <stdlib.h>
155 #include <ctype.h>
156 #include <setjmp.h>
157 //#include "trex.h"
159 #ifdef _UINCODE
160 #define scisprint iswprint
161 #define scstrlen wcslen
162 #define scprintf wprintf
163 #define _SC(x) L(x)
164 #else
165 #define scisprint isprint
166 #define scstrlen strlen
167 #define scprintf printf
168 #define _SC(x) (x)
169 #endif
171 #ifdef _DEBUG
172 #include <stdio.h>
174 static const TRexChar *g_nnames[] =
176         _SC("NONE"),_SC("OP_GREEDY"),   _SC("OP_OR"),
177         _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),       _SC("OP_CLASS"),
178         _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
179         _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
180 };
182 #endif
183 #define OP_GREEDY               (MAX_CHAR+1) // * + ? {n}
184 #define OP_OR                   (MAX_CHAR+2)
185 #define OP_EXPR                 (MAX_CHAR+3) //parentesis ()
186 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
187 #define OP_DOT                  (MAX_CHAR+5)
188 #define OP_CLASS                (MAX_CHAR+6)
189 #define OP_CCLASS               (MAX_CHAR+7)
190 #define OP_NCLASS               (MAX_CHAR+8) //negates class the [^
191 #define OP_RANGE                (MAX_CHAR+9)
192 #define OP_CHAR                 (MAX_CHAR+10)
193 #define OP_EOL                  (MAX_CHAR+11)
194 #define OP_BOL                  (MAX_CHAR+12)
195 #define OP_WB                   (MAX_CHAR+13)
197 #define TREX_SYMBOL_ANY_CHAR ('.')
198 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
199 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
200 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
201 #define TREX_SYMBOL_BRANCH ('|')
202 #define TREX_SYMBOL_END_OF_STRING ('$')
203 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
204 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
207 typedef int TRexNodeType;
209 typedef struct tagTRexNode{
210         TRexNodeType type;
211         int left;
212         int right;
213         int next;
214 }TRexNode;
216 struct TRex{
217         const TRexChar *_eol;
218         const TRexChar *_bol;
219         const TRexChar *_p;
220         int _first;
221         int _op;
222         TRexNode *_nodes;
223         int _nallocated;
224         int _nsize;
225         int _nsubexpr;
226         TRexMatch *_matches;
227         int _currsubexp;
228         void *_jmpbuf;
229         const TRexChar **_error;
230 };
232 static int trex_list(TRex *exp);
234 static int trex_newnode(TRex *exp, TRexNodeType type)
236         TRexNode n;
237         int newid;
238         n.type = type;
239         n.next = n.right = n.left = -1;
240         if(type == OP_EXPR)
241                 n.right = exp->_nsubexpr++;
242         if(exp->_nallocated < (exp->_nsize + 1)) {
243                 //int oldsize = exp->_nallocated;
244                 exp->_nallocated *= 2;
245                 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
246         }
247         exp->_nodes[exp->_nsize++] = n;
248         newid = exp->_nsize - 1;
249         return (int)newid;
252 static void trex_error(TRex *exp,const TRexChar *error)
254         if(exp->_error) *exp->_error = error;
255         longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
258 static void trex_expect(TRex *exp, int n){
259         if((*exp->_p) != n) 
260                 trex_error(exp, _SC("expected paren"));
261         exp->_p++;
264 static TRexChar trex_escapechar(TRex *exp)
266         if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
267                 exp->_p++;
268                 switch(*exp->_p) {
269                 case 'v': exp->_p++; return '\v';
270                 case 'n': exp->_p++; return '\n';
271                 case 't': exp->_p++; return '\t';
272                 case 'r': exp->_p++; return '\r';
273                 case 'f': exp->_p++; return '\f';
274                 default: return (*exp->_p++);
275                 }
276         } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
277         return (*exp->_p++);
280 static int trex_charclass(TRex *exp,int classid)
282         int n = trex_newnode(exp,OP_CCLASS);
283         exp->_nodes[n].left = classid;
284         return n;
287 static int trex_charnode(TRex *exp,TRexBool isclass)
289         TRexChar t;
290         if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
291                 exp->_p++;
292                 switch(*exp->_p) {
293                         case 'n': exp->_p++; return trex_newnode(exp,'\n');
294                         case 't': exp->_p++; return trex_newnode(exp,'\t');
295                         case 'r': exp->_p++; return trex_newnode(exp,'\r');
296                         case 'f': exp->_p++; return trex_newnode(exp,'\f');
297                         case 'v': exp->_p++; return trex_newnode(exp,'\v');
298                         case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
299                         case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
300                         case 'p': case 'P': case 'l': case 'u': 
301                                 {
302                                 t = *exp->_p; exp->_p++; 
303                                 return trex_charclass(exp,t);
304                                 }
305                         case 'b': 
306                         case 'B':
307                                 if(!isclass) {
308                                         int node = trex_newnode(exp,OP_WB);
309                                         exp->_nodes[node].left = *exp->_p;
310                                         exp->_p++; 
311                                         return node;
312                                 } //else default
313                         default: 
314                                 t = *exp->_p; exp->_p++; 
315                                 return trex_newnode(exp,t);
316                 }
317         }
318         else if(!scisprint(*exp->_p)) {
319                 
320                 trex_error(exp,_SC("letter expected"));
321         }
322         t = *exp->_p; exp->_p++; 
323         return trex_newnode(exp,t);
325 static int trex_class(TRex *exp)
327         int ret = -1;
328         int first = -1,chain;
329         if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
330                 ret = trex_newnode(exp,OP_NCLASS);
331                 exp->_p++;
332         }else ret = trex_newnode(exp,OP_CLASS);
333         
334         if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
335         chain = ret;
336         while(*exp->_p != ']' && exp->_p != exp->_eol) {
337                 if(*exp->_p == '-' && first != -1){ 
338                         int r,t;
339                         if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
340                         r = trex_newnode(exp,OP_RANGE);
341                         if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
342                         if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
343                         exp->_nodes[r].left = exp->_nodes[first].type;
344                         t = trex_escapechar(exp);
345                         exp->_nodes[r].right = t;
346             exp->_nodes[chain].next = r;
347                         chain = r;
348                         first = -1;
349                 }
350                 else{
351                         if(first!=-1){
352                                 int c = first;
353                                 exp->_nodes[chain].next = c;
354                                 chain = c;
355                                 first = trex_charnode(exp,TRex_True);
356                         }
357                         else{
358                                 first = trex_charnode(exp,TRex_True);
359                         }
360                 }
361         }
362         if(first!=-1){
363                 int c = first;
364                 exp->_nodes[chain].next = c;
365                 chain = c;
366                 first = -1;
367         }
368         /* hack? */
369         exp->_nodes[ret].left = exp->_nodes[ret].next;
370         exp->_nodes[ret].next = -1;
371         return ret;
374 static int trex_parsenumber(TRex *exp)
376         int ret = *exp->_p-'0';
377         int positions = 10;
378         exp->_p++;
379         while(isdigit(*exp->_p)) {
380                 ret = ret*10+(*exp->_p++-'0');
381                 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
382                 positions *= 10;
383         };
384         return ret;
387 static int trex_element(TRex *exp)
389         int ret = -1;
390         switch(*exp->_p)
391         {
392         case '(': {
393                 int expr,newn;
394                 exp->_p++;
397                 if(*exp->_p =='?') {
398                         exp->_p++;
399                         trex_expect(exp,':');
400                         expr = trex_newnode(exp,OP_NOCAPEXPR);
401                 }
402                 else
403                         expr = trex_newnode(exp,OP_EXPR);
404                 newn = trex_list(exp);
405                 exp->_nodes[expr].left = newn;
406                 ret = expr;
407                 trex_expect(exp,')');
408                           }
409                           break;
410         case '[':
411                 exp->_p++;
412                 ret = trex_class(exp);
413                 trex_expect(exp,']');
414                 break;
415         case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
416         case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
417         default:
418                 ret = trex_charnode(exp,TRex_False);
419                 break;
420         }
422         {
423                 int op;
424                 TRexBool isgreedy = TRex_False;
425                 unsigned short p0 = 0, p1 = 0;
426                 switch(*exp->_p){
427                         case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
428                         case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
429                         case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
430                         case '{':
431                                 exp->_p++;
432                                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
433                                 p0 = (unsigned short)trex_parsenumber(exp);
434                                 /*******************************/
435                                 switch(*exp->_p) {
436                         case '}':
437                                 p1 = p0; exp->_p++;
438                                 break;
439                         case ',':
440                                 exp->_p++;
441                                 p1 = 0xFFFF;
442                                 if(isdigit(*exp->_p)){
443                                         p1 = (unsigned short)trex_parsenumber(exp);
444                                 }
445                                 trex_expect(exp,'}');
446                                 break;
447                         default:
448                                 trex_error(exp,_SC(", or } expected"));
449                 }
450                 /*******************************/
451                 isgreedy = TRex_True; 
452                 break;
454                 }
455                 if(isgreedy) {
456                         int nnode = trex_newnode(exp,OP_GREEDY);
457                         op = OP_GREEDY;
458                         exp->_nodes[nnode].left = ret;
459                         exp->_nodes[nnode].right = ((p0)<<16)|p1;
460                         ret = nnode;
461                 }
462         }
463         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')) {
464                 int nnode = trex_element(exp);
465                 exp->_nodes[ret].next = nnode;
466         }
468         return ret;
471 static int trex_list(TRex *exp)
473         int ret=-1,e;
474         if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
475                 exp->_p++;
476                 ret = trex_newnode(exp,OP_BOL);
477         }
478         e = trex_element(exp);
479         if(ret != -1) {
480                 exp->_nodes[ret].next = e;
481         }
482         else ret = e;
484         if(*exp->_p == TREX_SYMBOL_BRANCH) {
485                 int temp,tright;
486                 exp->_p++;
487                 temp = trex_newnode(exp,OP_OR);
488                 exp->_nodes[temp].left = ret;
489                 tright = trex_list(exp);
490                 exp->_nodes[temp].right = tright;
491                 ret = temp;
492         }
493         return ret;
496 static TRexBool trex_matchcclass(int cclass,TRexChar c)
498         switch(cclass) {
499         case 'a': return isalpha(c)?TRex_True:TRex_False;
500         case 'A': return !isalpha(c)?TRex_True:TRex_False;
501         case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
502         case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
503         case 's': return isspace(c)?TRex_True:TRex_False;
504         case 'S': return !isspace(c)?TRex_True:TRex_False;
505         case 'd': return isdigit(c)?TRex_True:TRex_False;
506         case 'D': return !isdigit(c)?TRex_True:TRex_False;
507         case 'x': return isxdigit(c)?TRex_True:TRex_False;
508         case 'X': return !isxdigit(c)?TRex_True:TRex_False;
509         case 'c': return iscntrl(c)?TRex_True:TRex_False;
510         case 'C': return !iscntrl(c)?TRex_True:TRex_False;
511         case 'p': return ispunct(c)?TRex_True:TRex_False;
512         case 'P': return !ispunct(c)?TRex_True:TRex_False;
513         case 'l': return islower(c)?TRex_True:TRex_False;
514         case 'u': return isupper(c)?TRex_True:TRex_False;
515         }
516         return TRex_False; /*cannot happen*/
519 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
521         do {
522                 switch(node->type) {
523                         case OP_RANGE:
524                                 if(c >= node->left && c <= node->right) return TRex_True;
525                                 break;
526                         case OP_CCLASS:
527                                 if(trex_matchcclass(node->left,c)) return TRex_True;
528                                 break;
529                         default:
530                                 if(c == node->type)return TRex_True;
531                 }
532         } while((node->next != -1) && (node = &exp->_nodes[node->next]));
533         return TRex_False;
536 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
538         
539         TRexNodeType type = node->type;
540         switch(type) {
541         case OP_GREEDY: {
542                 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
543                 TRexNode *greedystop = NULL;
544                 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
545                 const TRexChar *s=str, *good = str;
547                 if(node->next != -1) {
548                         greedystop = &exp->_nodes[node->next];
549                 }
550                 else {
551                         greedystop = next;
552                 }
554                 while((nmaches == 0xFFFF || nmaches < p1)) {
556                         const TRexChar *stop;
557                         if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
558                                 break;
559                         nmaches++;
560                         good=s;
561                         if(greedystop) {
562                                 //checks that 0 matches satisfy the expression(if so skips)
563                                 //if not would always stop(for instance if is a '?')
564                                 if(greedystop->type != OP_GREEDY ||
565                                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
566                                 {
567                                         TRexNode *gnext = NULL;
568                                         if(greedystop->next != -1) {
569                                                 gnext = &exp->_nodes[greedystop->next];
570                                         }else if(next && next->next != -1){
571                                                 gnext = &exp->_nodes[next->next];
572                                         }
573                                         stop = trex_matchnode(exp,greedystop,s,gnext);
574                                         if(stop) {
575                                                 //if satisfied stop it
576                                                 if(p0 == p1 && p0 == nmaches) break;
577                                                 else if(nmaches >= p0 && p1 == 0xFFFF) break;
578                                                 else if(nmaches >= p0 && nmaches <= p1) break;
579                                         }
580                                 }
581                         }
582                         
583                         if(s >= exp->_eol)
584                                 break;
585                 }
586                 if(p0 == p1 && p0 == nmaches) return good;
587                 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
588                 else if(nmaches >= p0 && nmaches <= p1) return good;
589                 return NULL;
590         }
591         case OP_OR: {
592                         const TRexChar *asd = str;
593                         TRexNode *temp=&exp->_nodes[node->left];
594                         while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
595                                 if(temp->next != -1)
596                                         temp = &exp->_nodes[temp->next];
597                                 else
598                                         return asd;
599                         }
600                         asd = str;
601                         temp = &exp->_nodes[node->right];
602                         while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
603                                 if(temp->next != -1)
604                                         temp = &exp->_nodes[temp->next];
605                                 else
606                                         return asd;
607                         }
608                         return NULL;
609                         break;
610         }
611         case OP_EXPR:
612         case OP_NOCAPEXPR:{
613                         TRexNode *n = &exp->_nodes[node->left];
614                         const TRexChar *cur = str;
615                         int capture = -1;
616                         if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
617                                 capture = exp->_currsubexp;
618                                 exp->_matches[capture].begin = cur;
619                                 exp->_currsubexp++;
620                         }
621                         
622                         do {
623                                 TRexNode *subnext = NULL;
624                                 if(n->next != -1) {
625                                         subnext = &exp->_nodes[n->next];
626                                 }else {
627                                         subnext = next;
628                                 }
629                                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
630                                         if(capture != -1){
631                                                 exp->_matches[capture].begin = 0;
632                                                 exp->_matches[capture].len = 0;
633                                         }
634                                         return NULL;
635                                 }
636                         } while((n->next != -1) && (n = &exp->_nodes[n->next]));
638                         if(capture != -1) 
639                                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
640                         return cur;
641         }                                
642         case OP_WB:
643                 if(str == exp->_bol && !isspace(*str)
644                  || (str == exp->_eol && !isspace(*(str-1)))
645                  || (!isspace(*str) && isspace(*(str+1)))
646                  || (isspace(*str) && !isspace(*(str+1))) ) {
647                         return (node->left == 'b')?str:NULL;
648                 }
649                 return (node->left == 'b')?NULL:str;
650         case OP_BOL:
651                 if(str == exp->_bol) return str;
652                 return NULL;
653         case OP_EOL:
654                 if(str == exp->_eol) return str;
655                 return NULL;
656         case OP_DOT:{
657                 *str++;
658                                 }
659                 return str;
660         case OP_NCLASS:
661         case OP_CLASS:
662                 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
663                         *str++;
664                         return str;
665                 }
666                 return NULL;
667         case OP_CCLASS:
668                 if(trex_matchcclass(node->left,*str)) {
669                         *str++;
670                         return str;
671                 }
672                 return NULL;
673         default: /* char */
674                 if(*str != node->type) return NULL;
675                 *str++;
676                 return str;
677         }
678         return NULL;
681 /* public api */
682 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
684         TRex *exp = (TRex *)malloc(sizeof(TRex));
685         exp->_eol = exp->_bol = NULL;
686         exp->_p = pattern;
687         exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
688         exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
689         exp->_nsize = 0;
690         exp->_matches = 0;
691         exp->_nsubexpr = 0;
692         exp->_first = trex_newnode(exp,OP_EXPR);
693         exp->_error = error;
694         exp->_jmpbuf = malloc(sizeof(jmp_buf));
695         if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
696                 int res = trex_list(exp);
697                 exp->_nodes[exp->_first].left = res;
698                 if(*exp->_p!='\0')
699                         trex_error(exp,_SC("unexpected character"));
700 #ifdef _DEBUG
701                 {
702                         int nsize,i;
703                         TRexNode *t;
704                         nsize = exp->_nsize;
705                         t = &exp->_nodes[0];
706                         scprintf(_SC("\n"));
707                         for(i = 0;i < nsize; i++) {
708                                 if(exp->_nodes[i].type>MAX_CHAR)
709                                         scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
710                                 else
711                                         scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
712                                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
713                         }
714                         scprintf(_SC("\n"));
715                 }
716 #endif
717                 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
718                 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
719         }
720         else{
721                 trex_free(exp);
722                 return NULL;
723         }
724         return exp;
727 void trex_free(TRex *exp)
729         if(exp) {
730                 if(exp->_nodes) free(exp->_nodes);
731                 if(exp->_jmpbuf) free(exp->_jmpbuf);
732                 if(exp->_matches) free(exp->_matches);
733                 free(exp);
734         }
737 TRexBool trex_match(TRex* exp,const TRexChar* text)
739         const TRexChar* res = NULL;
740         exp->_bol = text;
741         exp->_eol = text + scstrlen(text);
742         exp->_currsubexp = 0;
743         res = trex_matchnode(exp,exp->_nodes,text,NULL);
744         if(res == NULL || res != exp->_eol)
745                 return TRex_False;
746         return TRex_True;
749 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
751         const TRexChar *cur = NULL;
752         int node = exp->_first;
753         if(text_begin >= text_end) return TRex_False;
754         exp->_bol = text_begin;
755         exp->_eol = text_end;
756         do {
757                 cur = text_begin;
758                 while(node != -1) {
759                         exp->_currsubexp = 0;
760                         cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
761                         if(!cur)
762                                 break;
763                         node = exp->_nodes[node].next;
764                 }
765                 *text_begin++;
766         } while(cur == NULL && text_begin != text_end);
768         if(cur == NULL)
769                 return TRex_False;
771         --text_begin;
773         if(out_begin) *out_begin = text_begin;
774         if(out_end) *out_end = cur;
775         return TRex_True;
778 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
780         return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
783 int trex_getsubexpcount(TRex* exp)
785         return exp->_nsubexpr;
788 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
790         if( n<0 || n >= exp->_nsubexpr) return TRex_False;
791         *subexp = exp->_matches[n];
792         return TRex_True;
796 //########################################################################
797 //########################################################################
798 //##  E N D    R E G E X P
799 //########################################################################
800 //########################################################################
806 //########################################################################
807 //########################################################################
808 //##  X M L
809 //########################################################################
810 //########################################################################
812 // Note:  This mini-dom library comes from Pedro, another little project
813 // of mine.
815 typedef std::string String;
816 typedef unsigned int XMLCh;
819 class Namespace
821 public:
822     Namespace()
823         {}
825     Namespace(const String &prefixArg, const String &namespaceURIArg)
826         {
827         prefix       = prefixArg;
828         namespaceURI = namespaceURIArg;
829         }
831     Namespace(const Namespace &other)
832         {
833         assign(other);
834         }
836     Namespace &operator=(const Namespace &other)
837         {
838         assign(other);
839         return *this;
840         }
842     virtual ~Namespace()
843         {}
845     virtual String getPrefix()
846         { return prefix; }
848     virtual String getNamespaceURI()
849         { return namespaceURI; }
851 protected:
853     void assign(const Namespace &other)
854         {
855         prefix       = other.prefix;
856         namespaceURI = other.namespaceURI;
857         }
859     String prefix;
860     String namespaceURI;
862 };
864 class Attribute
866 public:
867     Attribute()
868         {}
870     Attribute(const String &nameArg, const String &valueArg)
871         {
872         name  = nameArg;
873         value = valueArg;
874         }
876     Attribute(const Attribute &other)
877         {
878         assign(other);
879         }
881     Attribute &operator=(const Attribute &other)
882         {
883         assign(other);
884         return *this;
885         }
887     virtual ~Attribute()
888         {}
890     virtual String getName()
891         { return name; }
893     virtual String getValue()
894         { return value; }
896 protected:
898     void assign(const Attribute &other)
899         {
900         name  = other.name;
901         value = other.value;
902         }
904     String name;
905     String value;
907 };
910 class Element
912 friend class Parser;
914 public:
915     Element()
916         {
917         parent = NULL;
918         }
920     Element(const String &nameArg)
921         {
922         parent = NULL;
923         name   = nameArg;
924         }
926     Element(const String &nameArg, const String &valueArg)
927         {
928         parent = NULL;
929         name   = nameArg;
930         value  = valueArg;
931         }
933     Element(const Element &other)
934         {
935         assign(other);
936         }
938     Element &operator=(const Element &other)
939         {
940         assign(other);
941         return *this;
942         }
944     virtual Element *clone();
946     virtual ~Element()
947         {
948         for (unsigned int i=0 ; i<children.size() ; i++)
949             delete children[i];
950         }
952     virtual String getName()
953         { return name; }
955     virtual String getValue()
956         { return value; }
958     Element *getParent()
959         { return parent; }
961     std::vector<Element *> getChildren()
962         { return children; }
964     std::vector<Element *> findElements(const String &name);
966     String getAttribute(const String &name);
968     std::vector<Attribute> &getAttributes()
969         { return attributes; } 
971     String getTagAttribute(const String &tagName, const String &attrName);
973     String getTagValue(const String &tagName);
975     void addChild(Element *child);
977     void addAttribute(const String &name, const String &value);
979     void addNamespace(const String &prefix, const String &namespaceURI);
982     /**
983      * Prettyprint an XML tree to an output stream.  Elements are indented
984      * according to element hierarchy.
985      * @param f a stream to receive the output
986      * @param elem the element to output
987      */
988     void writeIndented(FILE *f);
990     /**
991      * Prettyprint an XML tree to standard output.  This is the equivalent of
992      * writeIndented(stdout).
993      * @param elem the element to output
994      */
995     void print();
997 protected:
999     void assign(const Element &other)
1000         {
1001         parent     = other.parent;
1002         children   = other.children;
1003         attributes = other.attributes;
1004         namespaces = other.namespaces;
1005         name       = other.name;
1006         value      = other.value;
1007         }
1009     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1011     void writeIndentedRecursive(FILE *f, int indent);
1013     Element *parent;
1015     std::vector<Element *>children;
1017     std::vector<Attribute> attributes;
1018     std::vector<Namespace> namespaces;
1020     String name;
1021     String value;
1023 };
1029 class Parser
1031 public:
1032     /**
1033      * Constructor
1034      */
1035     Parser()
1036         { init(); }
1038     virtual ~Parser()
1039         {}
1041     /**
1042      * Parse XML in a char buffer.
1043      * @param buf a character buffer to parse
1044      * @param pos position to start parsing
1045      * @param len number of chars, from pos, to parse.
1046      * @return a pointer to the root of the XML document;
1047      */
1048     Element *parse(const char *buf,int pos,int len);
1050     /**
1051      * Parse XML in a char buffer.
1052      * @param buf a character buffer to parse
1053      * @param pos position to start parsing
1054      * @param len number of chars, from pos, to parse.
1055      * @return a pointer to the root of the XML document;
1056      */
1057     Element *parse(const String &buf);
1059     /**
1060      * Parse a named XML file.  The file is loaded like a data file;
1061      * the original format is not preserved.
1062      * @param fileName the name of the file to read
1063      * @return a pointer to the root of the XML document;
1064      */
1065     Element *parseFile(const String &fileName);
1067     /**
1068      * Utility method to preprocess a string for XML
1069      * output, escaping its entities.
1070      * @param str the string to encode
1071      */
1072     static String encode(const String &str);
1074     /**
1075      *  Removes whitespace from beginning and end of a string
1076      */
1077     String trim(const String &s);
1079 private:
1081     void init()
1082         {
1083         keepGoing       = true;
1084         currentNode     = NULL;
1085         parselen        = 0;
1086         parsebuf        = NULL;
1087         currentPosition = 0;
1088         }
1090     void getLineAndColumn(long pos, long *lineNr, long *colNr);
1092     void error(char *fmt, ...);
1094     int peek(long pos);
1096     int match(long pos, const char *text);
1098     int skipwhite(long p);
1100     int getWord(int p0, String &buf);
1102     int getQuoted(int p0, String &buf, int do_i_parse);
1104     int parseVersion(int p0);
1106     int parseDoctype(int p0);
1108     int parseElement(int p0, Element *par,int depth);
1110     Element *parse(XMLCh *buf,int pos,int len);
1112     bool       keepGoing;
1113     Element    *currentNode;
1114     long       parselen;
1115     XMLCh      *parsebuf;
1116     String  cdatabuf;
1117     long       currentPosition;
1118     int        colNr;
1120 };
1125 //########################################################################
1126 //# E L E M E N T
1127 //########################################################################
1129 Element *Element::clone()
1131     Element *elem = new Element(name, value);
1132     elem->parent     = parent;
1133     elem->attributes = attributes;
1134     elem->namespaces = namespaces;
1136     std::vector<Element *>::iterator iter;
1137     for (iter = children.begin(); iter != children.end() ; iter++)
1138         {
1139         elem->addChild((*iter)->clone());
1140         }
1141     return elem;
1145 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1147     if (getName() == name)
1148         {
1149         res.push_back(this);
1150         }
1151     for (unsigned int i=0; i<children.size() ; i++)
1152         children[i]->findElementsRecursive(res, name);
1155 std::vector<Element *> Element::findElements(const String &name)
1157     std::vector<Element *> res;
1158     findElementsRecursive(res, name);
1159     return res;
1162 String Element::getAttribute(const String &name)
1164     for (unsigned int i=0 ; i<attributes.size() ; i++)
1165         if (attributes[i].getName() ==name)
1166             return attributes[i].getValue();
1167     return "";
1170 String Element::getTagAttribute(const String &tagName, const String &attrName)
1172     std::vector<Element *>elems = findElements(tagName);
1173     if (elems.size() <1)
1174         return "";
1175     String res = elems[0]->getAttribute(attrName);
1176     return res;
1179 String Element::getTagValue(const String &tagName)
1181     std::vector<Element *>elems = findElements(tagName);
1182     if (elems.size() <1)
1183         return "";
1184     String res = elems[0]->getValue();
1185     return res;
1188 void Element::addChild(Element *child)
1190     if (!child)
1191         return;
1192     child->parent = this;
1193     children.push_back(child);
1197 void Element::addAttribute(const String &name, const String &value)
1199     Attribute attr(name, value);
1200     attributes.push_back(attr);
1203 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1205     Namespace ns(prefix, namespaceURI);
1206     namespaces.push_back(ns);
1209 void Element::writeIndentedRecursive(FILE *f, int indent)
1211     int i;
1212     if (!f)
1213         return;
1214     //Opening tag, and attributes
1215     for (i=0;i<indent;i++)
1216         fputc(' ',f);
1217     fprintf(f,"<%s",name.c_str());
1218     for (unsigned int i=0 ; i<attributes.size() ; i++)
1219         {
1220         fprintf(f," %s=\"%s\"",
1221               attributes[i].getName().c_str(),
1222               attributes[i].getValue().c_str());
1223         }
1224     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1225         {
1226         fprintf(f," xmlns:%s=\"%s\"",
1227               namespaces[i].getPrefix().c_str(),
1228               namespaces[i].getNamespaceURI().c_str());
1229         }
1230     fprintf(f,">\n");
1232     //Between the tags
1233     if (value.size() > 0)
1234         {
1235         for (int i=0;i<indent;i++)
1236             fputc(' ', f);
1237         fprintf(f," %s\n", value.c_str());
1238         }
1240     for (unsigned int i=0 ; i<children.size() ; i++)
1241         children[i]->writeIndentedRecursive(f, indent+2);
1243     //Closing tag
1244     for (int i=0; i<indent; i++)
1245         fputc(' ',f);
1246     fprintf(f,"</%s>\n", name.c_str());
1249 void Element::writeIndented(FILE *f)
1251     writeIndentedRecursive(f, 0);
1254 void Element::print()
1256     writeIndented(stdout);
1260 //########################################################################
1261 //# P A R S E R
1262 //########################################################################
1266 typedef struct
1267     {
1268     char *escaped;
1269     char value;
1270     } EntityEntry;
1272 static EntityEntry entities[] =
1274     { "&amp;" , '&'  },
1275     { "&lt;"  , '<'  },
1276     { "&gt;"  , '>'  },
1277     { "&apos;", '\'' },
1278     { "&quot;", '"'  },
1279     { NULL    , '\0' }
1280 };
1284 /**
1285  *  Removes whitespace from beginning and end of a string
1286  */
1287 String Parser::trim(const String &s)
1289     if (s.size() < 1)
1290         return s;
1291     
1292     //Find first non-ws char
1293     unsigned int begin = 0;
1294     for ( ; begin < s.size() ; begin++)
1295         {
1296         if (!isspace(s[begin]))
1297             break;
1298         }
1300     //Find first non-ws char, going in reverse
1301     unsigned int end = s.size() - 1;
1302     for ( ; end > begin ; end--)
1303         {
1304         if (!isspace(s[end]))
1305             break;
1306         }
1307     //trace("begin:%d  end:%d", begin, end);
1309     String res = s.substr(begin, end-begin+1);
1310     return res;
1313 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1315     long line = 1;
1316     long col  = 1;
1317     for (long i=0 ; i<pos ; i++)
1318         {
1319         XMLCh ch = parsebuf[i];
1320         if (ch == '\n' || ch == '\r')
1321             {
1322             col = 0;
1323             line ++;
1324             }
1325         else
1326             col++;
1327         }
1328     *lineNr = line;
1329     *colNr  = col;
1334 void Parser::error(char *fmt, ...)
1336     long lineNr;
1337     long colNr;
1338     getLineAndColumn(currentPosition, &lineNr, &colNr);
1339     va_list args;
1340     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1341     va_start(args,fmt);
1342     vfprintf(stderr,fmt,args);
1343     va_end(args) ;
1344     fprintf(stderr, "\n");
1349 int Parser::peek(long pos)
1351     if (pos >= parselen)
1352         return -1;
1353     currentPosition = pos;
1354     int ch = parsebuf[pos];
1355     //printf("ch:%c\n", ch);
1356     return ch;
1361 String Parser::encode(const String &str)
1363     String ret;
1364     for (unsigned int i=0 ; i<str.size() ; i++)
1365         {
1366         XMLCh ch = (XMLCh)str[i];
1367         if (ch == '&')
1368             ret.append("&amp;");
1369         else if (ch == '<')
1370             ret.append("&lt;");
1371         else if (ch == '>')
1372             ret.append("&gt;");
1373         else if (ch == '\'')
1374             ret.append("&apos;");
1375         else if (ch == '"')
1376             ret.append("&quot;");
1377         else
1378             ret.push_back(ch);
1380         }
1381     return ret;
1385 int Parser::match(long p0, const char *text)
1387     int p = p0;
1388     while (*text)
1389         {
1390         if (peek(p) != *text)
1391             return p0;
1392         p++; text++;
1393         }
1394     return p;
1399 int Parser::skipwhite(long p)
1402     while (p<parselen)
1403         {
1404         int p2 = match(p, "<!--");
1405         if (p2 > p)
1406             {
1407             p = p2;
1408             while (p<parselen)
1409               {
1410               p2 = match(p, "-->");
1411               if (p2 > p)
1412                   {
1413                   p = p2;
1414                   break;
1415                   }
1416               p++;
1417               }
1418           }
1419       XMLCh b = peek(p);
1420       if (!isspace(b))
1421           break;
1422       p++;
1423       }
1424   return p;
1427 /* modify this to allow all chars for an element or attribute name*/
1428 int Parser::getWord(int p0, String &buf)
1430     int p = p0;
1431     while (p<parselen)
1432         {
1433         XMLCh b = peek(p);
1434         if (b<=' ' || b=='/' || b=='>' || b=='=')
1435             break;
1436         buf.push_back(b);
1437         p++;
1438         }
1439     return p;
1442 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1445     int p = p0;
1446     if (peek(p) != '"' && peek(p) != '\'')
1447         return p0;
1448     p++;
1450     while ( p<parselen )
1451         {
1452         XMLCh b = peek(p);
1453         if (b=='"' || b=='\'')
1454             break;
1455         if (b=='&' && do_i_parse)
1456             {
1457             bool found = false;
1458             for (EntityEntry *ee = entities ; ee->value ; ee++)
1459                 {
1460                 int p2 = match(p, ee->escaped);
1461                 if (p2>p)
1462                     {
1463                     buf.push_back(ee->value);
1464                     p = p2;
1465                     found = true;
1466                     break;
1467                     }
1468                 }
1469             if (!found)
1470                 {
1471                 error("unterminated entity");
1472                 return false;
1473                 }
1474             }
1475         else
1476             {
1477             buf.push_back(b);
1478             p++;
1479             }
1480         }
1481     return p;
1484 int Parser::parseVersion(int p0)
1486     //printf("### parseVersion: %d\n", p0);
1488     int p = p0;
1490     p = skipwhite(p0);
1492     if (peek(p) != '<')
1493         return p0;
1495     p++;
1496     if (p>=parselen || peek(p)!='?')
1497         return p0;
1499     p++;
1501     String buf;
1503     while (p<parselen)
1504         {
1505         XMLCh ch = peek(p);
1506         if (ch=='?')
1507             {
1508             p++;
1509             break;
1510             }
1511         buf.push_back(ch);
1512         p++;
1513         }
1515     if (peek(p) != '>')
1516         return p0;
1517     p++;
1519     //printf("Got version:%s\n",buf.c_str());
1520     return p;
1523 int Parser::parseDoctype(int p0)
1525     //printf("### parseDoctype: %d\n", p0);
1527     int p = p0;
1528     p = skipwhite(p);
1530     if (p>=parselen || peek(p)!='<')
1531         return p0;
1533     p++;
1535     if (peek(p)!='!' || peek(p+1)=='-')
1536         return p0;
1537     p++;
1539     String buf;
1540     while (p<parselen)
1541         {
1542         XMLCh ch = peek(p);
1543         if (ch=='>')
1544             {
1545             p++;
1546             break;
1547             }
1548         buf.push_back(ch);
1549         p++;
1550         }
1552     //printf("Got doctype:%s\n",buf.c_str());
1553     return p;
1556 int Parser::parseElement(int p0, Element *par,int depth)
1559     int p = p0;
1561     int p2 = p;
1563     p = skipwhite(p);
1565     //## Get open tag
1566     XMLCh ch = peek(p);
1567     if (ch!='<')
1568         return p0;
1570     p++;
1572     String openTagName;
1573     p = skipwhite(p);
1574     p = getWord(p, openTagName);
1575     //printf("####tag :%s\n", openTagName.c_str());
1576     p = skipwhite(p);
1578     //Add element to tree
1579     Element *n = new Element(openTagName);
1580     n->parent = par;
1581     par->addChild(n);
1583     // Get attributes
1584     if (peek(p) != '>')
1585         {
1586         while (p<parselen)
1587             {
1588             p = skipwhite(p);
1589             ch = peek(p);
1590             //printf("ch:%c\n",ch);
1591             if (ch=='>')
1592                 break;
1593             else if (ch=='/' && p<parselen+1)
1594                 {
1595                 p++;
1596                 p = skipwhite(p);
1597                 ch = peek(p);
1598                 if (ch=='>')
1599                     {
1600                     p++;
1601                     //printf("quick close\n");
1602                     return p;
1603                     }
1604                 }
1605             String attrName;
1606             p2 = getWord(p, attrName);
1607             if (p2==p)
1608                 break;
1609             //printf("name:%s",buf);
1610             p=p2;
1611             p = skipwhite(p);
1612             ch = peek(p);
1613             //printf("ch:%c\n",ch);
1614             if (ch!='=')
1615                 break;
1616             p++;
1617             p = skipwhite(p);
1618             // ch = parsebuf[p];
1619             // printf("ch:%c\n",ch);
1620             String attrVal;
1621             p2 = getQuoted(p, attrVal, true);
1622             p=p2+1;
1623             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1624             char *namestr = (char *)attrName.c_str();
1625             if (strncmp(namestr, "xmlns:", 6)==0)
1626                 n->addNamespace(attrName, attrVal);
1627             else
1628                 n->addAttribute(attrName, attrVal);
1629             }
1630         }
1632     bool cdata = false;
1634     p++;
1635     // ### Get intervening data ### */
1636     String data;
1637     while (p<parselen)
1638         {
1639         //# COMMENT
1640         p2 = match(p, "<!--");
1641         if (!cdata && p2>p)
1642             {
1643             p = p2;
1644             while (p<parselen)
1645                 {
1646                 p2 = match(p, "-->");
1647                 if (p2 > p)
1648                     {
1649                     p = p2;
1650                     break;
1651                     }
1652                 p++;
1653                 }
1654             }
1656         ch = peek(p);
1657         //# END TAG
1658         if (ch=='<' && !cdata && peek(p+1)=='/')
1659             {
1660             break;
1661             }
1662         //# CDATA
1663         p2 = match(p, "<![CDATA[");
1664         if (p2 > p)
1665             {
1666             cdata = true;
1667             p = p2;
1668             continue;
1669             }
1671         //# CHILD ELEMENT
1672         if (ch == '<')
1673             {
1674             p2 = parseElement(p, n, depth+1);
1675             if (p2 == p)
1676                 {
1677                 /*
1678                 printf("problem on element:%s.  p2:%d p:%d\n",
1679                       openTagName.c_str(), p2, p);
1680                 */
1681                 return p0;
1682                 }
1683             p = p2;
1684             continue;
1685             }
1686         //# ENTITY
1687         if (ch=='&' && !cdata)
1688             {
1689             bool found = false;
1690             for (EntityEntry *ee = entities ; ee->value ; ee++)
1691                 {
1692                 int p2 = match(p, ee->escaped);
1693                 if (p2>p)
1694                     {
1695                     data.push_back(ee->value);
1696                     p = p2;
1697                     found = true;
1698                     break;
1699                     }
1700                 }
1701             if (!found)
1702                 {
1703                 error("unterminated entity");
1704                 return -1;
1705                 }
1706             continue;
1707             }
1709         //# NONE OF THE ABOVE
1710         data.push_back(ch);
1711         p++;
1712         }/*while*/
1715     n->value = data;
1716     //printf("%d : data:%s\n",p,data.c_str());
1718     //## Get close tag
1719     p = skipwhite(p);
1720     ch = peek(p);
1721     if (ch != '<')
1722         {
1723         error("no < for end tag\n");
1724         return p0;
1725         }
1726     p++;
1727     ch = peek(p);
1728     if (ch != '/')
1729         {
1730         error("no / on end tag");
1731         return p0;
1732         }
1733     p++;
1734     ch = peek(p);
1735     p = skipwhite(p);
1736     String closeTagName;
1737     p = getWord(p, closeTagName);
1738     if (openTagName != closeTagName)
1739         {
1740         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1741                 openTagName.c_str(), closeTagName.c_str());
1742         return p0;
1743         }
1744     p = skipwhite(p);
1745     if (peek(p) != '>')
1746         {
1747         error("no > on end tag for '%s'", closeTagName.c_str());
1748         return p0;
1749         }
1750     p++;
1751     // printf("close element:%s\n",closeTagName.c_str());
1752     p = skipwhite(p);
1753     return p;
1759 Element *Parser::parse(XMLCh *buf,int pos,int len)
1761     parselen = len;
1762     parsebuf = buf;
1763     Element *rootNode = new Element("root");
1764     pos = parseVersion(pos);
1765     pos = parseDoctype(pos);
1766     pos = parseElement(pos, rootNode, 0);
1767     return rootNode;
1771 Element *Parser::parse(const char *buf, int pos, int len)
1773     XMLCh *charbuf = new XMLCh[len + 1];
1774     long i = 0;
1775     for ( ; i < len ; i++)
1776         charbuf[i] = (XMLCh)buf[i];
1777     charbuf[i] = '\0';
1779     Element *n = parse(charbuf, pos, len);
1780     delete[] charbuf;
1781     return n;
1784 Element *Parser::parse(const String &buf)
1786     long len = (long)buf.size();
1787     XMLCh *charbuf = new XMLCh[len + 1];
1788     long i = 0;
1789     for ( ; i < len ; i++)
1790         charbuf[i] = (XMLCh)buf[i];
1791     charbuf[i] = '\0';
1793     Element *n = parse(charbuf, 0, len);
1794     delete[] charbuf;
1795     return n;
1798 Element *Parser::parseFile(const String &fileName)
1801     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1802     FILE *f = fopen(fileName.c_str(), "rb");
1803     if (!f)
1804         return NULL;
1806     struct stat  statBuf;
1807     if (fstat(fileno(f),&statBuf)<0)
1808         {
1809         fclose(f);
1810         return NULL;
1811         }
1812     long filelen = statBuf.st_size;
1814     //printf("length:%d\n",filelen);
1815     XMLCh *charbuf = new XMLCh[filelen + 1];
1816     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1817         {
1818         *p = (XMLCh)fgetc(f);
1819         }
1820     fclose(f);
1821     charbuf[filelen] = '\0';
1824     /*
1825     printf("nrbytes:%d\n",wc_count);
1826     printf("buf:%ls\n======\n",charbuf);
1827     */
1828     Element *n = parse(charbuf, 0, filelen);
1829     delete[] charbuf;
1830     return n;
1835 //########################################################################
1836 //########################################################################
1837 //##  E N D    X M L
1838 //########################################################################
1839 //########################################################################
1842 //########################################################################
1843 //########################################################################
1844 //##  U R I
1845 //########################################################################
1846 //########################################################################
1848 //This would normally be a call to a UNICODE function
1849 #define isLetter(x) isalpha(x)
1851 /**
1852  *  A class that implements the W3C URI resource reference.
1853  */
1854 class URI
1856 public:
1858     typedef enum
1859         {
1860         SCHEME_NONE =0,
1861         SCHEME_DATA,
1862         SCHEME_HTTP,
1863         SCHEME_HTTPS,
1864         SCHEME_FTP,
1865         SCHEME_FILE,
1866         SCHEME_LDAP,
1867         SCHEME_MAILTO,
1868         SCHEME_NEWS,
1869         SCHEME_TELNET
1870         } SchemeTypes;
1872     /**
1873      *
1874      */
1875     URI()
1876         {
1877         init();
1878         }
1880     /**
1881      *
1882      */
1883     URI(const String &str)
1884         {
1885         init();
1886         parse(str);
1887         }
1890     /**
1891      *
1892      */
1893     URI(const char *str)
1894         {
1895         init();
1896         String domStr = str;
1897         parse(domStr);
1898         }
1901     /**
1902      *
1903      */
1904     URI(const URI &other)
1905         {
1906         init();
1907         assign(other);
1908         }
1911     /**
1912      *
1913      */
1914     URI &operator=(const URI &other)
1915         {
1916         init();
1917         assign(other);
1918         return *this;
1919         }
1922     /**
1923      *
1924      */
1925     virtual ~URI()
1926         {}
1930     /**
1931      *
1932      */
1933     virtual bool parse(const String &str);
1935     /**
1936      *
1937      */
1938     virtual String toString() const;
1940     /**
1941      *
1942      */
1943     virtual int getScheme() const;
1945     /**
1946      *
1947      */
1948     virtual String getSchemeStr() const;
1950     /**
1951      *
1952      */
1953     virtual String getAuthority() const;
1955     /**
1956      *  Same as getAuthority, but if the port has been specified
1957      *  as host:port , the port will not be included
1958      */
1959     virtual String getHost() const;
1961     /**
1962      *
1963      */
1964     virtual int getPort() const;
1966     /**
1967      *
1968      */
1969     virtual String getPath() const;
1971     /**
1972      *
1973      */
1974     virtual String getNativePath() const;
1976     /**
1977      *
1978      */
1979     virtual bool isAbsolute() const;
1981     /**
1982      *
1983      */
1984     virtual bool isOpaque() const;
1986     /**
1987      *
1988      */
1989     virtual String getQuery() const;
1991     /**
1992      *
1993      */
1994     virtual String getFragment() const;
1996     /**
1997      *
1998      */
1999     virtual URI resolve(const URI &other) const;
2001     /**
2002      *
2003      */
2004     virtual void normalize();
2006 private:
2008     /**
2009      *
2010      */
2011     void init()
2012         {
2013         parsebuf  = NULL;
2014         parselen  = 0;
2015         scheme    = SCHEME_NONE;
2016         schemeStr = "";
2017         port      = 0;
2018         authority = "";
2019         path      = "";
2020         absolute  = false;
2021         opaque    = false;
2022         query     = "";
2023         fragment  = "";
2024         }
2027     /**
2028      *
2029      */
2030     void assign(const URI &other)
2031         {
2032         scheme    = other.scheme;
2033         schemeStr = other.schemeStr;
2034         authority = other.authority;
2035         port      = other.port;
2036         path      = other.path;
2037         absolute  = other.absolute;
2038         opaque    = other.opaque;
2039         query     = other.query;
2040         fragment  = other.fragment;
2041         }
2043     int scheme;
2045     String schemeStr;
2047     String authority;
2049     bool portSpecified;
2051     int port;
2053     String path;
2055     bool absolute;
2057     bool opaque;
2059     String query;
2061     String fragment;
2063     void error(const char *fmt, ...);
2065     void trace(const char *fmt, ...);
2068     int peek(int p);
2070     int match(int p, char *key);
2072     int parseScheme(int p);
2074     int parseHierarchicalPart(int p0);
2076     int parseQuery(int p0);
2078     int parseFragment(int p0);
2080     int parse(int p);
2082     char *parsebuf;
2084     int parselen;
2086 };
2090 typedef struct
2092     int  ival;
2093     char *sval;
2094     int  port;
2095 } LookupEntry;
2097 LookupEntry schemes[] =
2099     { URI::SCHEME_DATA,   "data:",    0 },
2100     { URI::SCHEME_HTTP,   "http:",   80 },
2101     { URI::SCHEME_HTTPS,  "https:", 443 },
2102     { URI::SCHEME_FTP,    "ftp",     12 },
2103     { URI::SCHEME_FILE,   "file:",    0 },
2104     { URI::SCHEME_LDAP,   "ldap:",  123 },
2105     { URI::SCHEME_MAILTO, "mailto:", 25 },
2106     { URI::SCHEME_NEWS,   "news:",  117 },
2107     { URI::SCHEME_TELNET, "telnet:", 23 },
2108     { 0,                  NULL,       0 }
2109 };
2112 String URI::toString() const
2114     String str = schemeStr;
2115     if (authority.size() > 0)
2116         {
2117         str.append("//");
2118         str.append(authority);
2119         }
2120     str.append(path);
2121     if (query.size() > 0)
2122         {
2123         str.append("?");
2124         str.append(query);
2125         }
2126     if (fragment.size() > 0)
2127         {
2128         str.append("#");
2129         str.append(fragment);
2130         }
2131     return str;
2135 int URI::getScheme() const
2137     return scheme;
2140 String URI::getSchemeStr() const
2142     return schemeStr;
2146 String URI::getAuthority() const
2148     String ret = authority;
2149     if (portSpecified && port>=0)
2150         {
2151         char buf[7];
2152         snprintf(buf, 6, ":%6d", port);
2153         ret.append(buf);
2154         }
2155     return ret;
2158 String URI::getHost() const
2160     return authority;
2163 int URI::getPort() const
2165     return port;
2169 String URI::getPath() const
2171     return path;
2174 String URI::getNativePath() const
2176     String npath;
2177 #ifdef __WIN32__
2178     unsigned int firstChar = 0;
2179     if (path.size() >= 3)
2180         {
2181         if (path[0] == '/' &&
2182             isLetter(path[1]) &&
2183             path[2] == ':')
2184             firstChar++;
2185          }
2186     for (unsigned int i=firstChar ; i<path.size() ; i++)
2187         {
2188         XMLCh ch = (XMLCh) path[i];
2189         if (ch == '/')
2190             npath.push_back((XMLCh)'\\');
2191         else
2192             npath.push_back(ch);
2193         }
2194 #else
2195     npath = path;
2196 #endif
2197     return npath;
2201 bool URI::isAbsolute() const
2203     return absolute;
2206 bool URI::isOpaque() const
2208     return opaque;
2212 String URI::getQuery() const
2214     return query;
2218 String URI::getFragment() const
2220     return fragment;
2224 URI URI::resolve(const URI &other) const
2226     //### According to w3c, this is handled in 3 cases
2228     //## 1
2229     if (opaque || other.isAbsolute())
2230         return other;
2232     //## 2
2233     if (other.fragment.size()  >  0 &&
2234         other.path.size()      == 0 &&
2235         other.scheme           == SCHEME_NONE &&
2236         other.authority.size() == 0 &&
2237         other.query.size()     == 0 )
2238         {
2239         URI fragUri = *this;
2240         fragUri.fragment = other.fragment;
2241         return fragUri;
2242         }
2244     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2245     URI newUri;
2246     //# 3.1
2247     newUri.scheme    = scheme;
2248     newUri.schemeStr = schemeStr;
2249     newUri.query     = other.query;
2250     newUri.fragment  = other.fragment;
2251     if (other.authority.size() > 0)
2252         {
2253         //# 3.2
2254         if (absolute || other.absolute)
2255             newUri.absolute = true;
2256         newUri.authority = other.authority;
2257         newUri.port      = other.port;//part of authority
2258         newUri.path      = other.path;
2259         }
2260     else
2261         {
2262         //# 3.3
2263         if (other.absolute)
2264             {
2265             newUri.absolute = true;
2266             newUri.path     = other.path;
2267             }
2268         else
2269             {
2270             unsigned int pos = path.find_last_of('/');
2271             if (pos != path.npos)
2272                 {
2273                 String tpath = path.substr(0, pos+1);
2274                 tpath.append(other.path);
2275                 newUri.path = tpath;
2276                 }
2277             else
2278                 newUri.path = other.path;
2279             }
2280         }
2282     newUri.normalize();
2283     return newUri;
2287 /**
2288  *  This follows the Java URI algorithm:
2289  *   1. All "." segments are removed.
2290  *   2. If a ".." segment is preceded by a non-".." segment
2291  *          then both of these segments are removed. This step
2292  *          is repeated until it is no longer applicable.
2293  *   3. If the path is relative, and if its first segment
2294  *          contains a colon character (':'), then a "." segment
2295  *          is prepended. This prevents a relative URI with a path
2296  *          such as "a:b/c/d" from later being re-parsed as an
2297  *          opaque URI with a scheme of "a" and a scheme-specific
2298  *          part of "b/c/d". (Deviation from RFC 2396)
2299  */
2300 void URI::normalize()
2302     std::vector<String> segments;
2304     //## Collect segments
2305     if (path.size()<2)
2306         return;
2307     bool abs = false;
2308     unsigned int pos=0;
2309     if (path[0]=='/')
2310         {
2311         abs = true;
2312         pos++;
2313         }
2314     while (pos < path.size())
2315         {
2316         unsigned int pos2 = path.find('/', pos);
2317         if (pos2==path.npos)
2318             {
2319             String seg = path.substr(pos);
2320             //printf("last segment:%s\n", seg.c_str());
2321             segments.push_back(seg);
2322             break;
2323             }
2324         if (pos2>pos)
2325             {
2326             String seg = path.substr(pos, pos2-pos);
2327             //printf("segment:%s\n", seg.c_str());
2328             segments.push_back(seg);
2329             }
2330         pos = pos2;
2331         pos++;
2332         }
2334     //## Clean up (normalize) segments
2335     bool edited = false;
2336     std::vector<String>::iterator iter;
2337     for (iter=segments.begin() ; iter!=segments.end() ; )
2338         {
2339         String s = *iter;
2340         if (s == ".")
2341             {
2342             iter = segments.erase(iter);
2343             edited = true;
2344             }
2345         else if (s == ".." &&
2346                  iter != segments.begin() &&
2347                  *(iter-1) != "..")
2348             {
2349             iter--; //back up, then erase two entries
2350             iter = segments.erase(iter);
2351             iter = segments.erase(iter);
2352             edited = true;
2353             }
2354         else
2355             iter++;
2356         }
2358     //## Rebuild path, if necessary
2359     if (edited)
2360         {
2361         path.clear();
2362         if (abs)
2363             {
2364             path.append("/");
2365             }
2366         std::vector<String>::iterator iter;
2367         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2368             {
2369             if (iter != segments.begin())
2370                 path.append("/");
2371             path.append(*iter);
2372             }
2373         }
2379 //#########################################################################
2380 //# M E S S A G E S
2381 //#########################################################################
2383 void URI::error(const char *fmt, ...)
2385     va_list args;
2386     fprintf(stderr, "URI error: ");
2387     va_start(args, fmt);
2388     vfprintf(stderr, fmt, args);
2389     va_end(args);
2390     fprintf(stderr, "\n");
2393 void URI::trace(const char *fmt, ...)
2395     va_list args;
2396     fprintf(stdout, "URI: ");
2397     va_start(args, fmt);
2398     vfprintf(stdout, fmt, args);
2399     va_end(args);
2400     fprintf(stdout, "\n");
2405 //#########################################################################
2406 //# P A R S I N G
2407 //#########################################################################
2411 int URI::peek(int p)
2413     if (p<0 || p>=parselen)
2414         return -1;
2415     return parsebuf[p];
2420 int URI::match(int p0, char *key)
2422     int p = p0;
2423     while (p < parselen)
2424         {
2425         if (*key == '\0')
2426             return p;
2427         else if (*key != parsebuf[p])
2428             break;
2429         p++; key++;
2430         }
2431     return p0;
2434 //#########################################################################
2435 //#  Parsing is performed according to:
2436 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2437 //#########################################################################
2439 int URI::parseScheme(int p0)
2441     int p = p0;
2442     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2443         {
2444         int p2 = match(p, entry->sval);
2445         if (p2 > p)
2446             {
2447             schemeStr = entry->sval;
2448             scheme    = entry->ival;
2449             port      = entry->port;
2450             p = p2;
2451             return p;
2452             }
2453         }
2455     return p;
2459 int URI::parseHierarchicalPart(int p0)
2461     int p = p0;
2462     int ch;
2464     //# Authority field (host and port, for example)
2465     int p2 = match(p, "//");
2466     if (p2 > p)
2467         {
2468         p = p2;
2469         portSpecified = false;
2470         String portStr;
2471         while (p < parselen)
2472             {
2473             ch = peek(p);
2474             if (ch == '/')
2475                 break;
2476             else if (ch == ':')
2477                 portSpecified = true;
2478             else if (portSpecified)
2479                 portStr.push_back((XMLCh)ch);
2480             else
2481                 authority.push_back((XMLCh)ch);
2482             p++;
2483             }
2484         if (portStr.size() > 0)
2485             {
2486             char *pstr = (char *)portStr.c_str();
2487             char *endStr;
2488             long val = strtol(pstr, &endStr, 10);
2489             if (endStr > pstr) //successful parse?
2490                 port = val;
2491             }
2492         }
2494     //# Are we absolute?
2495     ch = peek(p);
2496     if (isLetter(ch) && peek(p+1)==':')
2497         {
2498         absolute = true;
2499         path.push_back((XMLCh)'/');
2500         }
2501     else if (ch == '/')
2502         {
2503         absolute = true;
2504         if (p>p0) //in other words, if '/' is not the first char
2505             opaque = true;
2506         path.push_back((XMLCh)ch);
2507         p++;
2508         }
2510     while (p < parselen)
2511         {
2512         ch = peek(p);
2513         if (ch == '?' || ch == '#')
2514             break;
2515         path.push_back((XMLCh)ch);
2516         p++;
2517         }
2519     return p;
2522 int URI::parseQuery(int p0)
2524     int p = p0;
2525     int ch = peek(p);
2526     if (ch != '?')
2527         return p0;
2529     p++;
2530     while (p < parselen)
2531         {
2532         ch = peek(p);
2533         if (ch == '#')
2534             break;
2535         query.push_back((XMLCh)ch);
2536         p++;
2537         }
2540     return p;
2543 int URI::parseFragment(int p0)
2546     int p = p0;
2547     int ch = peek(p);
2548     if (ch != '#')
2549         return p0;
2551     p++;
2552     while (p < parselen)
2553         {
2554         ch = peek(p);
2555         if (ch == '?')
2556             break;
2557         fragment.push_back((XMLCh)ch);
2558         p++;
2559         }
2562     return p;
2566 int URI::parse(int p0)
2569     int p = p0;
2571     int p2 = parseScheme(p);
2572     if (p2 < 0)
2573         {
2574         error("Scheme");
2575         return -1;
2576         }
2577     p = p2;
2580     p2 = parseHierarchicalPart(p);
2581     if (p2 < 0)
2582         {
2583         error("Hierarchical part");
2584         return -1;
2585         }
2586     p = p2;
2588     p2 = parseQuery(p);
2589     if (p2 < 0)
2590         {
2591         error("Query");
2592         return -1;
2593         }
2594     p = p2;
2597     p2 = parseFragment(p);
2598     if (p2 < 0)
2599         {
2600         error("Fragment");
2601         return -1;
2602         }
2603     p = p2;
2605     return p;
2611 bool URI::parse(const String &str)
2613     init();
2614     
2615     parselen = str.size();
2617     String tmp;
2618     for (unsigned int i=0 ; i<str.size() ; i++)
2619         {
2620         XMLCh ch = (XMLCh) str[i];
2621         if (ch == '\\')
2622             tmp.push_back((XMLCh)'/');
2623         else
2624             tmp.push_back(ch);
2625         }
2626     parsebuf = (char *) tmp.c_str();
2629     int p = parse(0);
2630     normalize();
2632     if (p < 0)
2633         {
2634         error("Syntax error");
2635         return false;
2636         }
2638     //printf("uri:%s\n", toString().c_str());
2639     //printf("path:%s\n", path.c_str());
2641     return true;
2652 //########################################################################
2653 //########################################################################
2654 //##  M A K E
2655 //########################################################################
2656 //########################################################################
2658 //########################################################################
2659 //# F I L E S E T
2660 //########################################################################
2661 /**
2662  * This is the descriptor for a <fileset> item
2663  */
2664 class FileSet
2666 public:
2668     /**
2669      *
2670      */
2671     FileSet()
2672         {}
2674     /**
2675      *
2676      */
2677     FileSet(const FileSet &other)
2678         { assign(other); }
2680     /**
2681      *
2682      */
2683     FileSet &operator=(const FileSet &other)
2684         { assign(other); return *this; }
2686     /**
2687      *
2688      */
2689     virtual ~FileSet()
2690         {}
2692     /**
2693      *
2694      */
2695     String getDirectory()
2696         { return directory; }
2697         
2698     /**
2699      *
2700      */
2701     void setDirectory(const String &val)
2702         { directory = val; }
2704     /**
2705      *
2706      */
2707     void setFiles(const std::vector<String> &val)
2708         { files = val; }
2710     /**
2711      *
2712      */
2713     std::vector<String> getFiles()
2714         { return files; }
2715         
2716     /**
2717      *
2718      */
2719     void setIncludes(const std::vector<String> &val)
2720         { includes = val; }
2722     /**
2723      *
2724      */
2725     std::vector<String> getIncludes()
2726         { return includes; }
2727         
2728     /**
2729      *
2730      */
2731     void setExcludes(const std::vector<String> &val)
2732         { excludes = val; }
2734     /**
2735      *
2736      */
2737     std::vector<String> getExcludes()
2738         { return excludes; }
2739         
2740     /**
2741      *
2742      */
2743     unsigned int size()
2744         { return files.size(); }
2745         
2746     /**
2747      *
2748      */
2749     String operator[](int index)
2750         { return files[index]; }
2751         
2752     /**
2753      *
2754      */
2755     void clear()
2756         {
2757         directory = "";
2758         files.clear();
2759         includes.clear();
2760         excludes.clear();
2761         }
2762         
2764 private:
2766     void assign(const FileSet &other)
2767         {
2768         directory = other.directory;
2769         files     = other.files;
2770         includes  = other.includes;
2771         excludes  = other.excludes;
2772         }
2774     String directory;
2775     std::vector<String> files;
2776     std::vector<String> includes;
2777     std::vector<String> excludes;
2778 };
2783 //########################################################################
2784 //# M A K E    B A S E
2785 //########################################################################
2786 /**
2787  * Base class for all classes in this file
2788  */
2789 class MakeBase
2791 public:
2792     MakeBase()
2793         {}
2794     virtual ~MakeBase()
2795         {}
2797     /**
2798      *  Return the URI of the file associated with this object 
2799      */     
2800     URI getURI()
2801         { return uri; }
2803     /**
2804      * Set the uri to the given string
2805      */
2806     void setURI(const String &uristr)
2807         { uri.parse(uristr); }
2809     /**
2810      *  Resolve another path relative to this one
2811      */
2812     String resolve(const String &otherPath);
2814     /**
2815      *  Get an element attribute, performing substitutions if necessary
2816      */
2817     bool getAttribute(Element *elem, const String &name, String &result);
2819     /**
2820      * Get an element value, performing substitutions if necessary
2821      */
2822     bool getValue(Element *elem, String &result);
2824 protected:
2826     /**
2827      *  The path to the file associated with this object
2828      */     
2829     URI uri;
2832     /**
2833      *  Print a printf()-like formatted error message
2834      */
2835     void error(char *fmt, ...);
2837     /**
2838      *  Print a printf()-like formatted trace message
2839      */
2840     void status(char *fmt, ...);
2842     /**
2843      *  Print a printf()-like formatted trace message
2844      */
2845     void trace(char *fmt, ...);
2847     /**
2848      *  Check if a given string matches a given regex pattern
2849      */
2850     bool regexMatch(const String &str, const String &pattern);
2852     /**
2853      *
2854      */
2855     String getSuffix(const String &fname);
2857     /**
2858      * Break up a string into substrings delimited the characters
2859      * in delimiters.  Null-length substrings are ignored
2860      */  
2861     std::vector<String> tokenize(const String &val,
2862                               const String &delimiters);
2864     /**
2865      *  replace runs of whitespace with a space
2866      */
2867     String strip(const String &s);
2869     /**
2870      *  remove leading whitespace from each line
2871      */
2872     String leftJustify(const String &s);
2874     /**
2875      *  remove leading and trailing whitespace from string
2876      */
2877     String trim(const String &s);
2879     /**
2880      * Return the native format of the canonical
2881      * path which we store
2882      */
2883     String getNativePath(const String &path);
2885     /**
2886      * Execute a shell command.  Outbuf is a ref to a string
2887      * to catch the result.     
2888      */      
2889     bool executeCommand(const String &call,
2890                             const String &inbuf,
2891                                                 String &outbuf,
2892                                                 String &errbuf);
2893     /**
2894      * List all directories in a given base and starting directory
2895      * It is usually called like:
2896      *           bool ret = listDirectories("src", "", result);    
2897      */      
2898     bool listDirectories(const String &baseName,
2899                          const String &dirname,
2900                          std::vector<String> &res);
2902     /**
2903      * Find all files in the named directory 
2904      */      
2905     bool listFiles(const String &baseName,
2906                    const String &dirname,
2907                    std::vector<String> &result);
2909     /**
2910      * Perform a listing for a fileset 
2911      */      
2912     bool listFiles(MakeBase &propRef, FileSet &fileSet);
2914     /**
2915      * Parse a <patternset>
2916      */  
2917     bool parsePatternSet(Element *elem,
2918                        MakeBase &propRef,
2919                                            std::vector<String> &includes,
2920                                            std::vector<String> &excludes);
2922     /**
2923      * Parse a <fileset> entry, and determine which files
2924      * should be included
2925      */  
2926     bool parseFileSet(Element *elem,
2927                     MakeBase &propRef,
2928                                         FileSet &fileSet);
2930     /**
2931      * Return this object's property list
2932      */
2933     virtual std::map<String, String> &getProperties()
2934         { return properties; }
2936     /**
2937      * Return a named property if found, else a null string
2938      */
2939     virtual String getProperty(const String &name)
2940         {
2941         String val;
2942         std::map<String, String>::iterator iter;
2943         iter = properties.find(name);
2944         if (iter != properties.end())
2945             val = iter->second;
2946         return val;
2947         }
2950     std::map<String, String> properties;
2952     /**
2953      * Turn 'true' and 'false' into boolean values
2954      */             
2955     bool getBool(const String &str, bool &val);
2957     /**
2958      * Create a directory, making intermediate dirs
2959      * if necessary
2960      */                     
2961     bool createDirectory(const String &dirname);
2963     /**
2964      * Delete a directory and its children if desired
2965      */
2966     bool removeDirectory(const String &dirName);
2968     /**
2969      * Copy a file from one name to another. Perform only if needed
2970      */ 
2971     bool copyFile(const String &srcFile, const String &destFile);
2973     /**
2974      * Tests if the file exists and is a regular file
2975      */ 
2976     bool isRegularFile(const String &fileName);
2978     /**
2979      * Tests if the file exists and is a directory
2980      */ 
2981     bool isDirectory(const String &fileName);
2983     /**
2984      * Tests is the modification date of fileA is newer than fileB
2985      */ 
2986     bool isNewerThan(const String &fileA, const String &fileB);
2988 private:
2990     /**
2991      * replace variable refs like ${a} with their values
2992      */      
2993     bool getSubstitutions(const String &s, String &result);
2997 };
3002 /**
3003  *  Print a printf()-like formatted error message
3004  */
3005 void MakeBase::error(char *fmt, ...)
3007     va_list args;
3008     va_start(args,fmt);
3009     fprintf(stderr, "Make error: ");
3010     vfprintf(stderr, fmt, args);
3011     fprintf(stderr, "\n");
3012     va_end(args) ;
3017 /**
3018  *  Print a printf()-like formatted trace message
3019  */
3020 void MakeBase::status(char *fmt, ...)
3022     va_list args;
3023     va_start(args,fmt);
3024     //fprintf(stdout, " ");
3025     vfprintf(stdout, fmt, args);
3026     fprintf(stdout, "\n");
3027     va_end(args) ;
3032 /**
3033  *  Resolve another path relative to this one
3034  */
3035 String MakeBase::resolve(const String &otherPath)
3037     URI otherURI(otherPath);
3038     URI fullURI = uri.resolve(otherURI);
3039     String ret = fullURI.toString();
3040     return ret;
3044 /**
3045  *  Print a printf()-like formatted trace message
3046  */
3047 void MakeBase::trace(char *fmt, ...)
3049     va_list args;
3050     va_start(args,fmt);
3051     fprintf(stdout, "Make: ");
3052     vfprintf(stdout, fmt, args);
3053     fprintf(stdout, "\n");
3054     va_end(args) ;
3059 /**
3060  *  Check if a given string matches a given regex pattern
3061  */
3062 bool MakeBase::regexMatch(const String &str, const String &pattern)
3064         const TRexChar *terror = NULL;
3065         const TRexChar *cpat = pattern.c_str();
3066         TRex *expr = trex_compile(cpat, &terror);
3067         if (!expr)
3068             {
3069             if (!terror)
3070                 terror = "undefined";
3071                 error("compilation error [%s]!\n", terror);
3072             return false;
3073                 } 
3075     bool ret = true;
3077         const TRexChar *cstr = str.c_str();
3078         if (trex_match(expr, cstr))
3079                 {
3080                 ret = true;
3081                 }
3082         else
3083             {
3084                 ret = false;
3085                 }
3087         trex_free(expr);
3089     return ret;
3092 /**
3093  *  Return the suffix, if any, of a file name
3094  */
3095 String MakeBase::getSuffix(const String &fname)
3097     if (fname.size() < 2)
3098         return "";
3099     unsigned int pos = fname.find_last_of('.');
3100     if (pos == fname.npos)
3101         return "";
3102     pos++;
3103     String res = fname.substr(pos, fname.size()-pos);
3104     //trace("suffix:%s", res.c_str()); 
3105     return res;
3110 /**
3111  * Break up a string into substrings delimited the characters
3112  * in delimiters.  Null-length substrings are ignored
3113  */  
3114 std::vector<String> MakeBase::tokenize(const String &str,
3115                                 const String &delimiters)
3118     std::vector<String> res;
3119     char *del = (char *)delimiters.c_str();
3120     String dmp;
3121     for (unsigned int i=0 ; i<str.size() ; i++)
3122         {
3123         char ch = str[i];
3124         char *p = (char *)0;
3125         for (p=del ; *p ; p++)
3126             if (*p == ch)
3127                 break;
3128         if (*p)
3129             {
3130             if (dmp.size() > 0)
3131                 {
3132                 res.push_back(dmp);
3133                 dmp.clear();
3134                 }
3135             }
3136         else
3137             {
3138             dmp.push_back(ch);
3139             }
3140         }
3141     //Add tail
3142     if (dmp.size() > 0)
3143         {
3144         res.push_back(dmp);
3145         dmp.clear();
3146         }
3148     return res;
3153 /**
3154  *  replace runs of whitespace with a single space
3155  */
3156 String MakeBase::strip(const String &s)
3158     int len = s.size();
3159     String stripped;
3160     for (int i = 0 ; i<len ; i++)
3161         {
3162         char ch = s[i];
3163         if (isspace(ch))
3164             {
3165             stripped.push_back(' ');
3166             for ( ; i<len ; i++)
3167                 {
3168                 ch = s[i];
3169                 if (!isspace(ch))
3170                     {
3171                     stripped.push_back(ch);
3172                     break;
3173                     }
3174                 }
3175             }
3176         else
3177             {
3178             stripped.push_back(ch);
3179             }
3180         }
3181     return stripped;
3184 /**
3185  *  remove leading whitespace from each line
3186  */
3187 String MakeBase::leftJustify(const String &s)
3189     String out;
3190     int len = s.size();
3191     for (int i = 0 ; i<len ; )
3192         {
3193         char ch;
3194         //Skip to first visible character
3195         while (i<len)
3196             {
3197                         ch = s[i];
3198                         if (ch == '\n' || ch == '\r'
3199                           || !isspace(ch))
3200                               break;
3201                         i++;
3202                         }
3203         //Copy the rest of the line
3204                 while (i<len)
3205                     {
3206                     ch = s[i];
3207             if (ch == '\n' || ch == '\r')
3208                 {
3209                 if (ch != '\r')
3210                     out.push_back('\n');
3211                 i++;
3212                 break;
3213                 }
3214             else
3215                 {
3216                 out.push_back(ch);
3217                 }
3218             i++;
3219             }
3220         }
3221     return out;
3225 /**
3226  *  Removes whitespace from beginning and end of a string
3227  */
3228 String MakeBase::trim(const String &s)
3230     if (s.size() < 1)
3231         return s;
3232     
3233     //Find first non-ws char
3234     unsigned int begin = 0;
3235     for ( ; begin < s.size() ; begin++)
3236         {
3237         if (!isspace(s[begin]))
3238             break;
3239         }
3241     //Find first non-ws char, going in reverse
3242     unsigned int end = s.size() - 1;
3243     for ( ; end > begin ; end--)
3244         {
3245         if (!isspace(s[end]))
3246             break;
3247         }
3248     //trace("begin:%d  end:%d", begin, end);
3250     String res = s.substr(begin, end-begin+1);
3251     return res;
3254 /**
3255  * Return the native format of the canonical
3256  * path which we store
3257  */
3258 String MakeBase::getNativePath(const String &path)
3260 #ifdef __WIN32__
3261     String npath;
3262     unsigned int firstChar = 0;
3263     if (path.size() >= 3)
3264         {
3265         if (path[0] == '/' &&
3266             isalpha(path[1]) &&
3267             path[2] == ':')
3268             firstChar++;
3269         }
3270     for (unsigned int i=firstChar ; i<path.size() ; i++)
3271         {
3272         char ch = path[i];
3273         if (ch == '/')
3274             npath.push_back('\\');
3275         else
3276             npath.push_back(ch);
3277         }
3278     return npath;
3279 #else
3280     return path;
3281 #endif
3285 #ifdef __WIN32__
3286 #include <tchar.h>
3288 static String win32LastError()
3291     DWORD dw = GetLastError(); 
3293     LPVOID str;
3294     FormatMessage(
3295         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3296         FORMAT_MESSAGE_FROM_SYSTEM,
3297         NULL,
3298         dw,
3299         0,
3300         (LPTSTR) &str,
3301         0, NULL );
3302     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3303     if(p != NULL)
3304         { // lose CRLF
3305         *p = _T('\0');
3306         }
3307     String ret = (char *)str;
3308     LocalFree(str);
3310     return ret;
3312 #endif
3316 /**
3317  * Execute a system call, using pipes to send data to the
3318  * program's stdin,  and reading stdout and stderr.
3319  */
3320 bool MakeBase::executeCommand(const String &command,
3321                               const String &inbuf,
3322                                                           String &outbuf,
3323                                                           String &errbuf)
3326     status("============ cmd ============\n%s\n=============================",
3327                     command.c_str());
3329 #ifdef __WIN32__
3331     /*
3332     I really hate having win32 code in this program, but the
3333     read buffer in command.com and cmd.exe are just too small
3334     for the large commands we need for compiling and linking.
3335     */
3337     bool ret = true;
3339     //# Allocate a separate buffer for safety
3340     char *paramBuf = new char[command.size() + 1];
3341     if (!paramBuf)
3342        {
3343        error("executeCommand cannot allocate command buffer");
3344            return false;
3345        }
3346     strcpy(paramBuf, (char *)command.c_str());
3348     //# Create pipes
3349     SECURITY_ATTRIBUTES saAttr; 
3350     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3351     saAttr.bInheritHandle = TRUE; 
3352     saAttr.lpSecurityDescriptor = NULL; 
3353     HANDLE stdinRead,  stdinWrite;
3354     HANDLE stdoutRead, stdoutWrite;
3355     HANDLE stderrRead, stderrWrite;
3356     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3357             {
3358                 error("executeProgram: could not create pipe");
3359         delete[] paramBuf;
3360                 return false;
3361                 } 
3362     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3363         if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3364             {
3365                 error("executeProgram: could not create pipe");
3366         delete[] paramBuf;
3367                 return false;
3368                 } 
3369     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3370         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3371             {
3372                 error("executeProgram: could not create pipe");
3373         delete[] paramBuf;
3374                 return false;
3375                 } 
3376     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3378     // Create the process
3379     STARTUPINFO siStartupInfo;
3380     PROCESS_INFORMATION piProcessInfo;
3381     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3382     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3383     siStartupInfo.cb = sizeof(siStartupInfo);
3384     siStartupInfo.hStdError   =  stderrWrite;
3385     siStartupInfo.hStdOutput  =  stdoutWrite;
3386     siStartupInfo.hStdInput   =  stdinRead;
3387     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3388    
3389     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3390                 0, NULL, NULL, &siStartupInfo,
3391                 &piProcessInfo))
3392         {
3393         error("executeCommand : could not create process : %s",
3394                             win32LastError().c_str());
3395         ret = false;
3396         }
3398     DWORD bytesWritten;
3399     if (inbuf.size()>0 &&
3400         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3401                &bytesWritten, NULL))
3402         {
3403         error("executeCommand: could not write to pipe");
3404                 return false;
3405                 }       
3406     if (!CloseHandle(stdinWrite))
3407             {           
3408         error("executeCommand: could not close write pipe");
3409                 return false;
3410                 }
3411     if (!CloseHandle(stdoutWrite))
3412             {
3413         error("executeCommand: could not close read pipe");
3414                 return false;
3415                 }
3416     if (!CloseHandle(stderrWrite))
3417             {
3418         error("executeCommand: could not close read pipe");
3419                 return false;
3420                 }
3421         while (true)
3422         {
3423         //trace("## stderr");
3424         DWORD avail;
3425         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3426             break;
3427         if (avail > 0)
3428             {
3429             DWORD bytesRead = 0;
3430             char readBuf[1025];
3431             if (avail>1024) avail = 1024;
3432             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3433                 || bytesRead == 0)
3434                 {
3435                 break;
3436                 }
3437             for (unsigned int i=0 ; i<bytesRead ; i++)
3438                 errbuf.push_back(readBuf[i]);
3439             }
3440         //trace("## stdout");
3441         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3442             break;
3443         if (avail > 0)
3444             {
3445             DWORD bytesRead = 0;
3446             char readBuf[1025];
3447             if (avail>1024) avail = 1024;
3448             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3449                 || bytesRead==0)
3450                 {
3451                 break;
3452                 }
3453             for (unsigned int i=0 ; i<bytesRead ; i++)
3454                 outbuf.push_back(readBuf[i]);
3455             }
3456                 DWORD exitCode;
3457         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3458         if (exitCode != STILL_ACTIVE)
3459             break;
3460         Sleep(100);
3461         }       
3462     //trace("outbuf:%s", outbuf.c_str());
3463     if (!CloseHandle(stdoutRead))
3464         {
3465         error("executeCommand: could not close read pipe");
3466         return false;
3467         }
3468     if (!CloseHandle(stderrRead))
3469         {
3470         error("executeCommand: could not close read pipe");
3471         return false;
3472         }
3474     DWORD exitCode;
3475     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3476     //trace("exit code:%d", exitCode);
3477     if (exitCode != 0)
3478         {
3479         ret = false;
3480         }
3481     
3482     // Clean up
3483     CloseHandle(piProcessInfo.hProcess);
3484     CloseHandle(piProcessInfo.hThread);
3487     return ret;
3489 #else //do it unix-style
3491     String s;
3492     FILE *f = popen(command.c_str(), "r");
3493     int errnum = 0;
3494     if (f)
3495         {
3496         while (true)
3497             {
3498             int ch = fgetc(f);
3499             if (ch < 0)
3500                 break;
3501             s.push_back((char)ch);
3502             }
3503         errnum = pclose(f);
3504         }
3505         outbuf = s;
3506         if (errnum < 0)
3507             {
3508             error("exec of command '%s' failed : %s",
3509                      command.c_str(), strerror(errno));
3510             return false;
3511             }
3512         else
3513             return true;
3515 #endif
3516
3521 bool MakeBase::listDirectories(const String &baseName,
3522                               const String &dirName,
3523                               std::vector<String> &res)
3525     res.push_back(dirName);
3526     String fullPath = baseName;
3527     if (dirName.size()>0)
3528         {
3529         fullPath.append("/");
3530         fullPath.append(dirName);
3531         }
3532     DIR *dir = opendir(fullPath.c_str());
3533     while (true)
3534         {
3535         struct dirent *de = readdir(dir);
3536         if (!de)
3537             break;
3539         //Get the directory member name
3540         String s = de->d_name;
3541         if (s.size() == 0 || s[0] == '.')
3542             continue;
3543         String childName = dirName;
3544         childName.append("/");
3545         childName.append(s);
3547         String fullChildPath = baseName;
3548         fullChildPath.append("/");
3549         fullChildPath.append(childName);
3550         struct stat finfo;
3551         String childNative = getNativePath(fullChildPath);
3552         if (stat(childNative.c_str(), &finfo)<0)
3553             {
3554             error("cannot stat file:%s", childNative.c_str());
3555             }
3556         else if (S_ISDIR(finfo.st_mode))
3557             {
3558             //trace("directory: %s", childName.c_str());
3559             if (!listDirectories(baseName, childName, res))
3560                 return false;
3561             }
3562         }
3563     closedir(dir);
3565     return true;
3569 bool MakeBase::listFiles(const String &baseDir,
3570                          const String &dirName,
3571                          std::vector<String> &res)
3573     String fullDir = baseDir;
3574     if (dirName.size()>0)
3575         {
3576         fullDir.append("/");
3577         fullDir.append(dirName);
3578         }
3579     String dirNative = getNativePath(fullDir);
3581     std::vector<String> subdirs;
3582     DIR *dir = opendir(dirNative.c_str());
3583     while (true)
3584         {
3585         struct dirent *de = readdir(dir);
3586         if (!de)
3587             break;
3589         //Get the directory member name
3590         String s = de->d_name;
3591         if (s.size() == 0 || s[0] == '.')
3592             continue;
3593         String childName;
3594         if (dirName.size()>0)
3595             {
3596             childName.append(dirName);
3597             childName.append("/");
3598             }
3599         childName.append(s);
3600         String fullChild = baseDir;
3601         fullChild.append("/");
3602         fullChild.append(childName);
3603         
3604         if (isDirectory(fullChild))
3605             {
3606             //trace("directory: %s", childName.c_str());
3607             if (!listFiles(baseDir, childName, res))
3608                 return false;
3609             continue;
3610             }
3611         else if (!isRegularFile(fullChild))
3612             {
3613             error("unknown file:%s", childName.c_str());
3614             return false;
3615             }
3617        //all done!
3618         res.push_back(childName);
3620         }
3621     closedir(dir);
3623     return true;
3627 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3629     String baseDir = propRef.resolve(fileSet.getDirectory());
3630     std::vector<String> fileList;
3631     if (!listFiles(baseDir, "", fileList))
3632             return false;
3634     std::vector<String> includes = fileSet.getIncludes();
3635     std::vector<String> excludes = fileSet.getExcludes();
3637     std::vector<String> incs;
3638     std::vector<String>::iterator iter;
3640     std::sort(fileList.begin(), fileList.end());
3642     //If there are <includes>, then add files to the output
3643     //in the order of the include list
3644     if (includes.size()==0)
3645             incs = fileList;
3646         else
3647             {
3648         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3649             {
3650             String pattern = *iter;
3651             std::vector<String>::iterator siter;
3652             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3653                 {
3654                 String s = *siter;
3655                 if (regexMatch(s, pattern))
3656                     {
3657                     //trace("INCLUDED:%s", s.c_str());
3658                     incs.push_back(s);
3659                     }
3660                 }
3661             }
3662         }
3664     //Now trim off the <excludes>
3665     std::vector<String> res;
3666     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3667         {
3668         String s = *iter;
3669         bool skipme = false;
3670         std::vector<String>::iterator siter;
3671         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3672             {
3673             String pattern = *siter;
3674             if (regexMatch(s, pattern))
3675                 {
3676                 //trace("EXCLUDED:%s", s.c_str());
3677                 skipme = true;
3678                 break;
3679                 }
3680             }
3681         if (!skipme)
3682             res.push_back(s);
3683         }
3684         
3685         fileSet.setFiles(res);
3687         return true;
3694 bool MakeBase::getSubstitutions(const String &str, String &result)
3696     String s = trim(str);
3697     int len = (int)s.size();
3698     String val;
3699     for (int i=0 ; i<len ; i++)
3700         {
3701         char ch = s[i];
3702         if (ch == '$' && s[i+1] == '{')
3703                     {
3704             String varname;
3705                     int j = i+2;
3706                     for ( ; j<len ; j++)
3707                         {
3708                         ch = s[j];
3709                         if (ch == '$' && s[j+1] == '{')
3710                             {
3711                             error("attribute %s cannot have nested variable references",
3712                                    s.c_str());
3713                             return false;
3714                             }
3715                         else if (ch == '}')
3716                             {
3717                             std::map<String, String>::iterator iter;
3718                             iter = properties.find(trim(varname));
3719                             if (iter != properties.end())
3720                                 {
3721                                 val.append(iter->second);
3722                                 }
3723                             else
3724                                 {
3725                                 error("property ${%s} not found", varname.c_str());
3726                                 return false;
3727                                 }
3728                             break;
3729                             }
3730                         else
3731                             {
3732                             varname.push_back(ch);
3733                             }
3734                         }
3735                     i = j;
3736                         }
3737                 else
3738                     {
3739                     val.push_back(ch);
3740                     }
3741         }
3742     result = val;
3743     return true;
3747 bool MakeBase::getAttribute(Element *elem, const String &name,
3748                                     String &result)
3750     String s = elem->getAttribute(name);
3751     return getSubstitutions(s, result);
3755 bool MakeBase::getValue(Element *elem, String &result)
3757     String s = elem->getValue();
3758     //Replace all runs of whitespace with a single space
3759     return getSubstitutions(s, result);
3763 /**
3764  * Turn 'true' and 'false' into boolean values
3765  */                 
3766 bool MakeBase::getBool(const String &str, bool &val)
3768     if (str == "true")
3769         val = true;
3770     else if (str == "false")
3771         val = false;
3772     else
3773         {
3774         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3775         return false;
3776         }
3777     return true;
3783 /**
3784  * Parse a <patternset> entry
3785  */  
3786 bool MakeBase::parsePatternSet(Element *elem,
3787                           MakeBase &propRef,
3788                                                   std::vector<String> &includes,
3789                                                   std::vector<String> &excludes
3790                                                   )
3792     std::vector<Element *> children  = elem->getChildren();
3793     for (unsigned int i=0 ; i<children.size() ; i++)
3794         {
3795         Element *child = children[i];
3796         String tagName = child->getName();
3797         if (tagName == "exclude")
3798             {
3799             String fname;
3800                         if (!propRef.getAttribute(child, "name", fname))
3801                             return false;
3802             //trace("EXCLUDE: %s", fname.c_str());
3803             excludes.push_back(fname);
3804             }
3805         else if (tagName == "include")
3806             {
3807             String fname;
3808                         if (!propRef.getAttribute(child, "name", fname))
3809                             return false;
3810             //trace("INCLUDE: %s", fname.c_str());
3811             includes.push_back(fname);
3812             }
3813         }
3815     return true;
3821 /**
3822  * Parse a <fileset> entry, and determine which files
3823  * should be included
3824  */  
3825 bool MakeBase::parseFileSet(Element *elem,
3826                           MakeBase &propRef,
3827                                                   FileSet &fileSet)
3829     String name = elem->getName();
3830     if (name != "fileset")
3831         {
3832         error("expected <fileset>");
3833         return false;
3834         }
3837     std::vector<String> includes;
3838     std::vector<String> excludes;
3840     //A fileset has one implied patternset
3841     if (!parsePatternSet(elem, propRef, includes, excludes))
3842         {
3843         return false;
3844         }
3845     //Look for child tags, including more patternsets
3846     std::vector<Element *> children  = elem->getChildren();
3847     for (unsigned int i=0 ; i<children.size() ; i++)
3848         {
3849         Element *child = children[i];
3850         String tagName = child->getName();
3851         if (tagName == "patternset")
3852             {
3853             if (!parsePatternSet(child, propRef, includes, excludes))
3854                 {
3855                 return false;
3856                 }
3857             }
3858         }
3860     String dir;
3861     //Now do the stuff
3862     //Get the base directory for reading file names
3863     if (!propRef.getAttribute(elem, "dir", dir))
3864         return false;
3866     fileSet.setDirectory(dir);
3867     fileSet.setIncludes(includes);
3868     fileSet.setExcludes(excludes);
3869     
3870     /*
3871     std::vector<String> fileList;
3872     if (dir.size() > 0)
3873         {
3874         String baseDir = propRef.resolve(dir);
3875             if (!listFiles(baseDir, "", includes, excludes, fileList))
3876                 return false;
3877             }
3878     std::sort(fileList.begin(), fileList.end());
3879         result = fileList;
3880         */
3882         
3883         /*
3884         for (unsigned int i=0 ; i<result.size() ; i++)
3885             {
3886             trace("RES:%s", result[i].c_str());
3887             }
3888     */
3890     
3891     return true;
3896 /**
3897  * Create a directory, making intermediate dirs
3898  * if necessary
3899  */                         
3900 bool MakeBase::createDirectory(const String &dirname)
3902     //trace("## createDirectory: %s", dirname.c_str());
3903     //## first check if it exists
3904     struct stat finfo;
3905     String nativeDir = getNativePath(dirname);
3906     char *cnative = (char *) nativeDir.c_str();
3907 #ifdef __WIN32__
3908     if (strlen(cnative)==2 && cnative[1]==':')
3909         return true;
3910 #endif
3911     if (stat(cnative, &finfo)==0)
3912         {
3913         if (!S_ISDIR(finfo.st_mode))
3914             {
3915             error("mkdir: file %s exists but is not a directory",
3916                               cnative);
3917             return false;
3918             }
3919         else //exists
3920             {
3921             return true;
3922             }
3923         }
3925     //## 2: pull off the last path segment, if any,
3926     //## to make the dir 'above' this one, if necessary
3927     unsigned int pos = dirname.find_last_of('/');
3928     if (pos>0 && pos != dirname.npos)
3929         {
3930         String subpath = dirname.substr(0, pos);
3931         //A letter root (c:) ?
3932         if (!createDirectory(subpath))
3933             return false;
3934         }
3935         
3936     //## 3: now make
3937     if (mkdir(cnative)<0)
3938         {
3939         error("cannot make directory '%s'", cnative);
3940         return false;
3941         }
3942         
3943     return true;
3947 /**
3948  * Remove a directory recursively
3949  */ 
3950 bool MakeBase::removeDirectory(const String &dirName)
3952     char *dname = (char *)dirName.c_str();
3954     DIR *dir = opendir(dname);
3955     if (!dir)
3956         {
3957         //# Let this fail nicely.
3958         return true;
3959         //error("error opening directory %s : %s", dname, strerror(errno));
3960         //return false;
3961         }
3962     
3963     while (true)
3964         {
3965         struct dirent *de = readdir(dir);
3966         if (!de)
3967             break;
3969         //Get the directory member name
3970         String s = de->d_name;
3971         if (s.size() == 0 || s[0] == '.')
3972             continue;
3973         String childName;
3974         if (dirName.size() > 0)
3975             {
3976             childName.append(dirName);
3977             childName.append("/");
3978             }
3979         childName.append(s);
3982         struct stat finfo;
3983         String childNative = getNativePath(childName);
3984         char *cnative = (char *)childNative.c_str();
3985         if (stat(cnative, &finfo)<0)
3986             {
3987             error("cannot stat file:%s", cnative);
3988             }
3989         else if (S_ISDIR(finfo.st_mode))
3990             {
3991             //trace("DEL dir: %s", childName.c_str());
3992                         if (!removeDirectory(childName))
3993                     {
3994                             return false;
3995                             }
3996             }
3997         else if (!S_ISREG(finfo.st_mode))
3998             {
3999             //trace("not regular: %s", cnative);
4000             }
4001         else
4002             {
4003             //trace("DEL file: %s", childName.c_str());
4004             if (remove(cnative)<0)
4005                 {
4006                 error("error deleting %s : %s",
4007                                      cnative, strerror(errno));
4008                                 return false;
4009                                 }
4010             }
4011         }
4012     closedir(dir);
4014     //Now delete the directory
4015     String native = getNativePath(dirName);
4016     if (rmdir(native.c_str())<0)
4017         {
4018         error("could not delete directory %s : %s",
4019             native.c_str() , strerror(errno));
4020         return false;
4021         }
4023     return true;
4024     
4028 /**
4029  * Copy a file from one name to another. Perform only if needed
4030  */ 
4031 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4033     //# 1 Check up-to-date times
4034     String srcNative = getNativePath(srcFile);
4035     struct stat srcinfo;
4036     if (stat(srcNative.c_str(), &srcinfo)<0)
4037         {
4038         error("source file %s for copy does not exist",
4039                          srcNative.c_str());
4040         return false;
4041         }
4043     String destNative = getNativePath(destFile);
4044     struct stat destinfo;
4045     if (stat(destNative.c_str(), &destinfo)==0)
4046         {
4047         if (destinfo.st_mtime >= srcinfo.st_mtime)
4048             return true;
4049         }
4050         
4051     //# 2 prepare a destination directory if necessary
4052     unsigned int pos = destFile.find_last_of('/');
4053     if (pos != destFile.npos)
4054         {
4055         String subpath = destFile.substr(0, pos);
4056         if (!createDirectory(subpath))
4057             return false;
4058         }
4060     //# 3 do the data copy
4061 #ifndef __WIN32__
4063     FILE *srcf = fopen(srcNative.c_str(), "rb");
4064     if (!srcf)
4065         {
4066         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4067         return false;
4068         }
4069     FILE *destf = fopen(destNative.c_str(), "wb");
4070     if (!destf)
4071         {
4072         error("copyFile cannot open %s for writing", srcNative.c_str());
4073         return false;
4074         }
4076     while (!feof(srcf))
4077         {
4078         int ch = fgetc(srcf);
4079         if (ch<0)
4080             break;
4081         fputc(ch, destf);
4082         }
4084     fclose(destf);
4085     fclose(srcf);
4087 #else
4088     
4089     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4090         {
4091         error("copyFile from %s to %s failed",
4092                      srcNative.c_str(), destNative.c_str());
4093         return false;
4094         }
4095         
4096 #endif /* __WIN32__ */
4099     return true;
4104 /**
4105  * Tests if the file exists and is a regular file
4106  */ 
4107 bool MakeBase::isRegularFile(const String &fileName)
4109     String native = getNativePath(fileName);
4110     struct stat finfo;
4111     
4112     //Exists?
4113     if (stat(native.c_str(), &finfo)<0)
4114                 return false;
4117     //check the file mode
4118     if (!S_ISREG(finfo.st_mode))
4119                 return false;
4121     return true;
4124 /**
4125  * Tests if the file exists and is a directory
4126  */ 
4127 bool MakeBase::isDirectory(const String &fileName)
4129     String native = getNativePath(fileName);
4130     struct stat finfo;
4131     
4132     //Exists?
4133     if (stat(native.c_str(), &finfo)<0)
4134                 return false;
4137     //check the file mode
4138     if (!S_ISDIR(finfo.st_mode))
4139                 return false;
4141     return true;
4146 /**
4147  * Tests is the modification of fileA is newer than fileB
4148  */ 
4149 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4151     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4152     String nativeA = getNativePath(fileA);
4153     struct stat infoA;
4154     //IF source does not exist, NOT newer
4155     if (stat(nativeA.c_str(), &infoA)<0)
4156         {
4157                 return false;
4158                 }
4160     String nativeB = getNativePath(fileB);
4161     struct stat infoB;
4162     //IF dest does not exist, YES, newer
4163     if (stat(nativeB.c_str(), &infoB)<0)
4164         {
4165                 return true;
4166                 }
4168     //check the actual times
4169     if (infoA.st_mtime > infoB.st_mtime)
4170         {
4171                 return true;
4172                 }
4174     return false;
4178 //########################################################################
4179 //# P K G    C O N F I G
4180 //########################################################################
4182 /**
4183  *
4184  */
4185 class PkgConfig : public MakeBase
4188 public:
4190     /**
4191      *
4192      */
4193     PkgConfig()
4194         { init(); }
4196     /**
4197      *
4198      */
4199     PkgConfig(const String &namearg)
4200         { init(); name = namearg; }
4202     /**
4203      *
4204      */
4205     PkgConfig(const PkgConfig &other)
4206         { assign(other); }
4208     /**
4209      *
4210      */
4211     PkgConfig &operator=(const PkgConfig &other)
4212         { assign(other); return *this; }
4214     /**
4215      *
4216      */
4217     virtual ~PkgConfig()
4218         { }
4220     /**
4221      *
4222      */
4223     virtual String getName()
4224         { return name; }
4226     /**
4227      *
4228      */
4229     virtual String getDescription()
4230         { return description; }
4232     /**
4233      *
4234      */
4235     virtual String getCflags()
4236         { return cflags; }
4238     /**
4239      *
4240      */
4241     virtual String getLibs()
4242         { return libs; }
4244     /**
4245      *
4246      */
4247     virtual String getVersion()
4248         { return version; }
4250     /**
4251      *
4252      */
4253     virtual int getMajorVersion()
4254         { return majorVersion; }
4256     /**
4257      *
4258      */
4259     virtual int getMinorVersion()
4260         { return minorVersion; }
4262     /**
4263      *
4264      */
4265     virtual int getMicroVersion()
4266         { return microVersion; }
4268     /**
4269      *
4270      */
4271     virtual std::map<String, String> &getAttributes()
4272         { return attrs; }
4274     /**
4275      *
4276      */
4277     virtual std::vector<String> &getRequireList()
4278         { return requireList; }
4280     virtual bool readFile(const String &fileName);
4282 private:
4284     void init()
4285         {
4286         name         = "";
4287         description  = "";
4288         cflags       = "";
4289         libs         = "";
4290         requires     = "";
4291         version      = "";
4292         majorVersion = 0;
4293         minorVersion = 0;
4294         microVersion = 0;
4295         fileName     = "";
4296         attrs.clear();
4297         requireList.clear();
4298         }
4300     void assign(const PkgConfig &other)
4301         {
4302         name         = other.name;
4303         description  = other.description;
4304         cflags       = other.cflags;
4305         libs         = other.libs;
4306         requires     = other.requires;
4307         version      = other.version;
4308         majorVersion = other.majorVersion;
4309         minorVersion = other.minorVersion;
4310         microVersion = other.microVersion;
4311         fileName     = other.fileName;
4312         attrs        = other.attrs;
4313         requireList  = other.requireList;
4314         }
4318     int get(int pos);
4320     int skipwhite(int pos);
4322     int getword(int pos, String &ret);
4324     void parseRequires();
4326     void parseVersion();
4328     bool parse(const String &buf);
4330     void dumpAttrs();
4332     String name;
4334     String description;
4336     String cflags;
4338     String libs;
4340     String requires;
4342     String version;
4344     int majorVersion;
4346     int minorVersion;
4348     int microVersion;
4350     String fileName;
4352     std::map<String, String> attrs;
4354     std::vector<String> requireList;
4356     char *parsebuf;
4357     int parselen;
4358 };
4361 /**
4362  * Get a character from the buffer at pos.  If out of range,
4363  * return -1 for safety
4364  */
4365 int PkgConfig::get(int pos)
4367     if (pos>parselen)
4368         return -1;
4369     return parsebuf[pos];
4374 /**
4375  *  Skip over all whitespace characters beginning at pos.  Return
4376  *  the position of the first non-whitespace character.
4377  */
4378 int PkgConfig::skipwhite(int pos)
4380     while (pos < parselen)
4381         {
4382         int ch = get(pos);
4383         if (ch < 0)
4384             break;
4385         if (!isspace(ch))
4386             break;
4387         pos++;
4388         }
4389     return pos;
4393 /**
4394  *  Parse the buffer beginning at pos, for a word.  Fill
4395  *  'ret' with the result.  Return the position after the
4396  *  word.
4397  */
4398 int PkgConfig::getword(int pos, String &ret)
4400     while (pos < parselen)
4401         {
4402         int ch = get(pos);
4403         if (ch < 0)
4404             break;
4405         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4406             break;
4407         ret.push_back((char)ch);
4408         pos++;
4409         }
4410     return pos;
4413 void PkgConfig::parseRequires()
4415     if (requires.size() == 0)
4416         return;
4417     parsebuf = (char *)requires.c_str();
4418     parselen = requires.size();
4419     int pos = 0;
4420     while (pos < parselen)
4421         {
4422         pos = skipwhite(pos);
4423         String val;
4424         int pos2 = getword(pos, val);
4425         if (pos2 == pos)
4426             break;
4427         pos = pos2;
4428         //trace("val %s", val.c_str());
4429         requireList.push_back(val);
4430         }
4433 static int getint(const String str)
4435     char *s = (char *)str.c_str();
4436     char *ends = NULL;
4437     long val = strtol(s, &ends, 10);
4438     if (ends == s)
4439         return 0L;
4440     else
4441         return val;
4444 void PkgConfig::parseVersion()
4446     if (version.size() == 0)
4447         return;
4448     String s1, s2, s3;
4449     unsigned int pos = 0;
4450     unsigned int pos2 = version.find('.', pos);
4451     if (pos2 == version.npos)
4452         {
4453         s1 = version;
4454         }
4455     else
4456         {
4457         s1 = version.substr(pos, pos2-pos);
4458         pos = pos2;
4459         pos++;
4460         if (pos < version.size())
4461             {
4462             pos2 = version.find('.', pos);
4463             if (pos2 == version.npos)
4464                 {
4465                 s2 = version.substr(pos, version.size()-pos);
4466                 }
4467             else
4468                 {
4469                 s2 = version.substr(pos, pos2-pos);
4470                 pos = pos2;
4471                 pos++;
4472                 if (pos < version.size())
4473                     s3 = version.substr(pos, pos2-pos);
4474                 }
4475             }
4476         }
4478     majorVersion = getint(s1);
4479     minorVersion = getint(s2);
4480     microVersion = getint(s3);
4481     //trace("version:%d.%d.%d", majorVersion,
4482     //          minorVersion, microVersion );
4486 bool PkgConfig::parse(const String &buf)
4488     init();
4490     parsebuf = (char *)buf.c_str();
4491     parselen = buf.size();
4492     int pos = 0;
4495     while (pos < parselen)
4496         {
4497         String attrName;
4498         pos = skipwhite(pos);
4499         int ch = get(pos);
4500         if (ch == '#')
4501             {
4502             //comment.  eat the rest of the line
4503             while (pos < parselen)
4504                 {
4505                 ch = get(pos);
4506                 if (ch == '\n' || ch < 0)
4507                     break;
4508                 pos++;
4509                 }
4510             continue;
4511             }
4512         pos = getword(pos, attrName);
4513         if (attrName.size() == 0)
4514             continue;
4515         pos = skipwhite(pos);
4516         ch = get(pos);
4517         if (ch != ':' && ch != '=')
4518             {
4519             error("expected ':' or '='");
4520             return false;
4521             }
4522         pos++;
4523         pos = skipwhite(pos);
4524         String attrVal;
4525         while (pos < parselen)
4526             {
4527             ch = get(pos);
4528             if (ch == '\n' || ch < 0)
4529                 break;
4530             else if (ch == '$' && get(pos+1) == '{')
4531                 {
4532                 //#  this is a ${substitution}
4533                 pos += 2;
4534                 String subName;
4535                 while (pos < parselen)
4536                     {
4537                     ch = get(pos);
4538                     if (ch < 0)
4539                         {
4540                         error("unterminated substitution");
4541                         return false;
4542                         }
4543                     else if (ch == '}')
4544                         break;
4545                     else
4546                         subName.push_back((char)ch);
4547                     pos++;
4548                     }
4549                 //trace("subName:%s", subName.c_str());
4550                 String subVal = attrs[subName];
4551                 //trace("subVal:%s", subVal.c_str());
4552                 attrVal.append(subVal);
4553                 }
4554             else
4555                 attrVal.push_back((char)ch);
4556             pos++;
4557             }
4559         attrVal = trim(attrVal);
4560         attrs[attrName] = attrVal;
4562         if (attrName == "Name")
4563             name = attrVal;
4564         else if (attrName == "Description")
4565             description = attrVal;
4566         else if (attrName == "Cflags")
4567             cflags = attrVal;
4568         else if (attrName == "Libs")
4569             libs = attrVal;
4570         else if (attrName == "Requires")
4571             requires = attrVal;
4572         else if (attrName == "Version")
4573             version = attrVal;
4575         //trace("name:'%s'  value:'%s'",
4576         //      attrName.c_str(), attrVal.c_str());
4577         }
4580     parseRequires();
4581     parseVersion();
4583     return true;
4586 void PkgConfig::dumpAttrs()
4588     //trace("### PkgConfig attributes for %s", fileName.c_str());
4589     std::map<String, String>::iterator iter;
4590     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4591         {
4592         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4593         }
4597 bool PkgConfig::readFile(const String &fileNameArg)
4599     fileName = fileNameArg;
4601     FILE *f = fopen(fileName.c_str(), "r");
4602     if (!f)
4603         {
4604         error("cannot open file '%s' for reading", fileName.c_str());
4605         return false;
4606         }
4607     String buf;
4608     while (true)
4609         {
4610         int ch = fgetc(f);
4611         if (ch < 0)
4612             break;
4613         buf.push_back((char)ch);
4614         }
4615     fclose(f);
4617     //trace("####### File:\n%s", buf.c_str());
4618     if (!parse(buf))
4619         {
4620         return false;
4621         }
4623     dumpAttrs();
4625     return true;
4632 //########################################################################
4633 //# D E P T O O L
4634 //########################################################################
4638 /**
4639  *  Class which holds information for each file.
4640  */
4641 class FileRec
4643 public:
4645     typedef enum
4646         {
4647         UNKNOWN,
4648         CFILE,
4649         HFILE,
4650         OFILE
4651         } FileType;
4653     /**
4654      *  Constructor
4655      */
4656     FileRec()
4657         {init(); type = UNKNOWN;}
4659     /**
4660      *  Copy constructor
4661      */
4662     FileRec(const FileRec &other)
4663         {init(); assign(other);}
4664     /**
4665      *  Constructor
4666      */
4667     FileRec(int typeVal)
4668         {init(); type = typeVal;}
4669     /**
4670      *  Assignment operator
4671      */
4672     FileRec &operator=(const FileRec &other)
4673         {init(); assign(other); return *this;}
4676     /**
4677      *  Destructor
4678      */
4679     ~FileRec()
4680         {}
4682     /**
4683      *  Directory part of the file name
4684      */
4685     String path;
4687     /**
4688      *  Base name, sans directory and suffix
4689      */
4690     String baseName;
4692     /**
4693      *  File extension, such as cpp or h
4694      */
4695     String suffix;
4697     /**
4698      *  Type of file: CFILE, HFILE, OFILE
4699      */
4700     int type;
4702     /**
4703      * Used to list files ref'd by this one
4704      */
4705     std::map<String, FileRec *> files;
4708 private:
4710     void init()
4711         {
4712         }
4714     void assign(const FileRec &other)
4715         {
4716         type     = other.type;
4717         baseName = other.baseName;
4718         suffix   = other.suffix;
4719         files    = other.files;
4720         }
4722 };
4726 /**
4727  *  Simpler dependency record
4728  */
4729 class DepRec
4731 public:
4733     /**
4734      *  Constructor
4735      */
4736     DepRec()
4737         {init();}
4739     /**
4740      *  Copy constructor
4741      */
4742     DepRec(const DepRec &other)
4743         {init(); assign(other);}
4744     /**
4745      *  Constructor
4746      */
4747     DepRec(const String &fname)
4748         {init(); name = fname; }
4749     /**
4750      *  Assignment operator
4751      */
4752     DepRec &operator=(const DepRec &other)
4753         {init(); assign(other); return *this;}
4756     /**
4757      *  Destructor
4758      */
4759     ~DepRec()
4760         {}
4762     /**
4763      *  Directory part of the file name
4764      */
4765     String path;
4767     /**
4768      *  Base name, without the path and suffix
4769      */
4770     String name;
4772     /**
4773      *  Suffix of the source
4774      */
4775     String suffix;
4778     /**
4779      * Used to list files ref'd by this one
4780      */
4781     std::vector<String> files;
4784 private:
4786     void init()
4787         {
4788         }
4790     void assign(const DepRec &other)
4791         {
4792         path     = other.path;
4793         name     = other.name;
4794         suffix   = other.suffix;
4795         files    = other.files;
4796         }
4798 };
4801 class DepTool : public MakeBase
4803 public:
4805     /**
4806      *  Constructor
4807      */
4808     DepTool()
4809         {init();}
4811     /**
4812      *  Copy constructor
4813      */
4814     DepTool(const DepTool &other)
4815         {init(); assign(other);}
4817     /**
4818      *  Assignment operator
4819      */
4820     DepTool &operator=(const DepTool &other)
4821         {init(); assign(other); return *this;}
4824     /**
4825      *  Destructor
4826      */
4827     ~DepTool()
4828         {}
4831     /**
4832      *  Reset this section of code
4833      */
4834     virtual void init();
4835     
4836     /**
4837      *  Reset this section of code
4838      */
4839     virtual void assign(const DepTool &other)
4840         {
4841         }
4842     
4843     /**
4844      *  Sets the source directory which will be scanned
4845      */
4846     virtual void setSourceDirectory(const String &val)
4847         { sourceDir = val; }
4849     /**
4850      *  Returns the source directory which will be scanned
4851      */
4852     virtual String getSourceDirectory()
4853         { return sourceDir; }
4855     /**
4856      *  Sets the list of files within the directory to analyze
4857      */
4858     virtual void setFileList(const std::vector<String> &list)
4859         { fileList = list; }
4861     /**
4862      * Creates the list of all file names which will be
4863      * candidates for further processing.  Reads make.exclude
4864      * to see which files for directories to leave out.
4865      */
4866     virtual bool createFileList();
4869     /**
4870      *  Generates the forward dependency list
4871      */
4872     virtual bool generateDependencies();
4875     /**
4876      *  Generates the forward dependency list, saving the file
4877      */
4878     virtual bool generateDependencies(const String &);
4881     /**
4882      *  Load a dependency file
4883      */
4884     std::vector<DepRec> loadDepFile(const String &fileName);
4886     /**
4887      *  Load a dependency file, generating one if necessary
4888      */
4889     std::vector<DepRec> getDepFile(const String &fileName,
4890               bool forceRefresh);
4892     /**
4893      *  Save a dependency file
4894      */
4895     bool saveDepFile(const String &fileName);
4898 private:
4901     /**
4902      *
4903      */
4904     void parseName(const String &fullname,
4905                    String &path,
4906                    String &basename,
4907                    String &suffix);
4909     /**
4910      *
4911      */
4912     int get(int pos);
4914     /**
4915      *
4916      */
4917     int skipwhite(int pos);
4919     /**
4920      *
4921      */
4922     int getword(int pos, String &ret);
4924     /**
4925      *
4926      */
4927     bool sequ(int pos, char *key);
4929     /**
4930      *
4931      */
4932     bool addIncludeFile(FileRec *frec, const String &fname);
4934     /**
4935      *
4936      */
4937     bool scanFile(const String &fname, FileRec *frec);
4939     /**
4940      *
4941      */
4942     bool processDependency(FileRec *ofile,
4943                            FileRec *include,
4944                            int depth);
4946     /**
4947      *
4948      */
4949     String sourceDir;
4951     /**
4952      *
4953      */
4954     std::vector<String> fileList;
4956     /**
4957      *
4958      */
4959     std::vector<String> directories;
4961     /**
4962      * A list of all files which will be processed for
4963      * dependencies.  This is the only list that has the actual
4964      * records.  All other lists have pointers to these records.     
4965      */
4966     std::map<String, FileRec *> allFiles;
4968     /**
4969      * The list of .o files, and the
4970      * dependencies upon them.
4971      */
4972     std::map<String, FileRec *> depFiles;
4974     int depFileSize;
4975     char *depFileBuf;
4977     static const int readBufSize = 8192;
4978     char readBuf[8193];//byte larger
4980 };
4986 /**
4987  *  Clean up after processing.  Called by the destructor, but should
4988  *  also be called before the object is reused.
4989  */
4990 void DepTool::init()
4992     sourceDir = ".";
4994     fileList.clear();
4995     directories.clear();
4996     
4997     //clear refs
4998     depFiles.clear();
4999     //clear records
5000     std::map<String, FileRec *>::iterator iter;
5001     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5002          delete iter->second;
5004     allFiles.clear(); 
5011 /**
5012  *  Parse a full path name into path, base name, and suffix
5013  */
5014 void DepTool::parseName(const String &fullname,
5015                         String &path,
5016                         String &basename,
5017                         String &suffix)
5019     if (fullname.size() < 2)
5020         return;
5022     unsigned int pos = fullname.find_last_of('/');
5023     if (pos != fullname.npos && pos<fullname.size()-1)
5024         {
5025         path = fullname.substr(0, pos);
5026         pos++;
5027         basename = fullname.substr(pos, fullname.size()-pos);
5028         }
5029     else
5030         {
5031         path = "";
5032         basename = fullname;
5033         }
5035     pos = basename.find_last_of('.');
5036     if (pos != basename.npos && pos<basename.size()-1)
5037         {
5038         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5039         basename = basename.substr(0, pos);
5040         }
5042     //trace("parsename:%s %s %s", path.c_str(),
5043     //        basename.c_str(), suffix.c_str()); 
5048 /**
5049  *  Generate our internal file list.
5050  */
5051 bool DepTool::createFileList()
5054     for (unsigned int i=0 ; i<fileList.size() ; i++)
5055         {
5056         String fileName = fileList[i];
5057         //trace("## FileName:%s", fileName.c_str());
5058         String path;
5059         String basename;
5060         String sfx;
5061         parseName(fileName, path, basename, sfx);
5062         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5063                     sfx == "cc" || sfx == "CC")
5064             {
5065             FileRec *fe         = new FileRec(FileRec::CFILE);
5066             fe->path            = path;
5067             fe->baseName        = basename;
5068             fe->suffix          = sfx;
5069             allFiles[fileName]  = fe;
5070             }
5071         else if (sfx == "h"   ||  sfx == "hh"  ||
5072                  sfx == "hpp" ||  sfx == "hxx")
5073             {
5074             FileRec *fe         = new FileRec(FileRec::HFILE);
5075             fe->path            = path;
5076             fe->baseName        = basename;
5077             fe->suffix          = sfx;
5078             allFiles[fileName]  = fe;
5079             }
5080         }
5082     if (!listDirectories(sourceDir, "", directories))
5083         return false;
5084         
5085     return true;
5092 /**
5093  * Get a character from the buffer at pos.  If out of range,
5094  * return -1 for safety
5095  */
5096 int DepTool::get(int pos)
5098     if (pos>depFileSize)
5099         return -1;
5100     return depFileBuf[pos];
5105 /**
5106  *  Skip over all whitespace characters beginning at pos.  Return
5107  *  the position of the first non-whitespace character.
5108  */
5109 int DepTool::skipwhite(int pos)
5111     while (pos < depFileSize)
5112         {
5113         int ch = get(pos);
5114         if (ch < 0)
5115             break;
5116         if (!isspace(ch))
5117             break;
5118         pos++;
5119         }
5120     return pos;
5124 /**
5125  *  Parse the buffer beginning at pos, for a word.  Fill
5126  *  'ret' with the result.  Return the position after the
5127  *  word.
5128  */
5129 int DepTool::getword(int pos, String &ret)
5131     while (pos < depFileSize)
5132         {
5133         int ch = get(pos);
5134         if (ch < 0)
5135             break;
5136         if (isspace(ch))
5137             break;
5138         ret.push_back((char)ch);
5139         pos++;
5140         }
5141     return pos;
5144 /**
5145  * Return whether the sequence of characters in the buffer
5146  * beginning at pos match the key,  for the length of the key
5147  */
5148 bool DepTool::sequ(int pos, char *key)
5150     while (*key)
5151         {
5152         if (*key != get(pos))
5153             return false;
5154         key++; pos++;
5155         }
5156     return true;
5161 /**
5162  *  Add an include file name to a file record.  If the name
5163  *  is not found in allFiles explicitly, try prepending include
5164  *  directory names to it and try again.
5165  */
5166 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5169     std::map<String, FileRec *>::iterator iter =
5170            allFiles.find(iname);
5171     if (iter != allFiles.end()) //already exists
5172         {
5173          //h file in same dir
5174         FileRec *other = iter->second;
5175         //trace("local: '%s'", iname.c_str());
5176         frec->files[iname] = other;
5177         return true;
5178         }
5179     else 
5180         {
5181         //look in other dirs
5182         std::vector<String>::iterator diter;
5183         for (diter=directories.begin() ;
5184              diter!=directories.end() ; diter++)
5185             {
5186             String dfname = *diter;
5187             dfname.append("/");
5188             dfname.append(iname);
5189             iter = allFiles.find(dfname);
5190             if (iter != allFiles.end())
5191                 {
5192                 FileRec *other = iter->second;
5193                 //trace("other: '%s'", iname.c_str());
5194                 frec->files[dfname] = other;
5195                 return true;
5196                 }
5197             }
5198         }
5199     return true;
5204 /**
5205  *  Lightly parse a file to find the #include directives.  Do
5206  *  a bit of state machine stuff to make sure that the directive
5207  *  is valid.  (Like not in a comment).
5208  */
5209 bool DepTool::scanFile(const String &fname, FileRec *frec)
5211     String fileName;
5212     if (sourceDir.size() > 0)
5213         {
5214         fileName.append(sourceDir);
5215         fileName.append("/");
5216         }
5217     fileName.append(fname);
5218     String nativeName = getNativePath(fileName);
5219     FILE *f = fopen(nativeName.c_str(), "r");
5220     if (!f)
5221         {
5222         error("Could not open '%s' for reading", fname.c_str());
5223         return false;
5224         }
5225     String buf;
5226     while (!feof(f))
5227         {
5228         int len = fread(readBuf, 1, readBufSize, f);
5229         readBuf[len] = '\0';
5230         buf.append(readBuf);
5231         }
5232     fclose(f);
5234     depFileSize = buf.size();
5235     depFileBuf  = (char *)buf.c_str();
5236     int pos = 0;
5239     while (pos < depFileSize)
5240         {
5241         //trace("p:%c", get(pos));
5243         //# Block comment
5244         if (get(pos) == '/' && get(pos+1) == '*')
5245             {
5246             pos += 2;
5247             while (pos < depFileSize)
5248                 {
5249                 if (get(pos) == '*' && get(pos+1) == '/')
5250                     {
5251                     pos += 2;
5252                     break;
5253                     }
5254                 else
5255                     pos++;
5256                 }
5257             }
5258         //# Line comment
5259         else if (get(pos) == '/' && get(pos+1) == '/')
5260             {
5261             pos += 2;
5262             while (pos < depFileSize)
5263                 {
5264                 if (get(pos) == '\n')
5265                     {
5266                     pos++;
5267                     break;
5268                     }
5269                 else
5270                     pos++;
5271                 }
5272             }
5273         //# #include! yaay
5274         else if (sequ(pos, "#include"))
5275             {
5276             pos += 8;
5277             pos = skipwhite(pos);
5278             String iname;
5279             pos = getword(pos, iname);
5280             if (iname.size()>2)
5281                 {
5282                 iname = iname.substr(1, iname.size()-2);
5283                 addIncludeFile(frec, iname);
5284                 }
5285             }
5286         else
5287             {
5288             pos++;
5289             }
5290         }
5292     return true;
5297 /**
5298  *  Recursively check include lists to find all files in allFiles to which
5299  *  a given file is dependent.
5300  */
5301 bool DepTool::processDependency(FileRec *ofile,
5302                              FileRec *include,
5303                              int depth)
5305     std::map<String, FileRec *>::iterator iter;
5306     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5307         {
5308         String fname  = iter->first;
5309         if (ofile->files.find(fname) != ofile->files.end())
5310             {
5311             //trace("file '%s' already seen", fname.c_str());
5312             continue;
5313             }
5314         FileRec *child  = iter->second;
5315         ofile->files[fname] = child;
5316       
5317         processDependency(ofile, child, depth+1);
5318         }
5321     return true;
5328 /**
5329  *  Generate the file dependency list.
5330  */
5331 bool DepTool::generateDependencies()
5333     std::map<String, FileRec *>::iterator iter;
5334     //# First pass.  Scan for all includes
5335     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5336         {
5337         FileRec *frec = iter->second;
5338         if (!scanFile(iter->first, frec))
5339             {
5340             //quit?
5341             }
5342         }
5344     //# Second pass.  Scan for all includes
5345     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5346         {
5347         FileRec *include = iter->second;
5348         if (include->type == FileRec::CFILE)
5349             {
5350             String cFileName = iter->first;
5351             FileRec *ofile      = new FileRec(FileRec::OFILE);
5352             ofile->path         = include->path;
5353             ofile->baseName     = include->baseName;
5354             ofile->suffix       = include->suffix;
5355             String fname     = include->path;
5356             if (fname.size()>0)
5357                 fname.append("/");
5358             fname.append(include->baseName);
5359             fname.append(".o");
5360             depFiles[fname]    = ofile;
5361             //add the .c file first?   no, don't
5362             //ofile->files[cFileName] = include;
5363             
5364             //trace("ofile:%s", fname.c_str());
5366             processDependency(ofile, include, 0);
5367             }
5368         }
5370       
5371     return true;
5376 /**
5377  *  High-level call to generate deps and optionally save them
5378  */
5379 bool DepTool::generateDependencies(const String &fileName)
5381     if (!createFileList())
5382         return false;
5383     if (!generateDependencies())
5384         return false;
5385     if (!saveDepFile(fileName))
5386         return false;
5387     return true;
5391 /**
5392  *   This saves the dependency cache.
5393  */
5394 bool DepTool::saveDepFile(const String &fileName)
5396     time_t tim;
5397     time(&tim);
5399     FILE *f = fopen(fileName.c_str(), "w");
5400     if (!f)
5401         {
5402         trace("cannot open '%s' for writing", fileName.c_str());
5403         }
5404     fprintf(f, "<?xml version='1.0'?>\n");
5405     fprintf(f, "<!--\n");
5406     fprintf(f, "########################################################\n");
5407     fprintf(f, "## File: build.dep\n");
5408     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5409     fprintf(f, "########################################################\n");
5410     fprintf(f, "-->\n");
5412     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5413     std::map<String, FileRec *>::iterator iter;
5414     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5415         {
5416         FileRec *frec = iter->second;
5417         if (frec->type == FileRec::OFILE)
5418             {
5419             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5420                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5421             std::map<String, FileRec *>::iterator citer;
5422             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5423                 {
5424                 String cfname = citer->first;
5425                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5426                 }
5427             fprintf(f, "</object>\n\n");
5428             }
5429         }
5431     fprintf(f, "</dependencies>\n");
5432     fprintf(f, "\n");
5433     fprintf(f, "<!--\n");
5434     fprintf(f, "########################################################\n");
5435     fprintf(f, "## E N D\n");
5436     fprintf(f, "########################################################\n");
5437     fprintf(f, "-->\n");
5439     fclose(f);
5441     return true;
5447 /**
5448  *   This loads the dependency cache.
5449  */
5450 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5452     std::vector<DepRec> result;
5453     
5454     Parser parser;
5455     Element *root = parser.parseFile(depFile.c_str());
5456     if (!root)
5457         {
5458         //error("Could not open %s for reading", depFile.c_str());
5459         return result;
5460         }
5462     if (root->getChildren().size()==0 ||
5463         root->getChildren()[0]->getName()!="dependencies")
5464         {
5465         error("Main xml element should be <dependencies>");
5466         delete root;
5467         return result;
5468         }
5470     //########## Start parsing
5471     Element *depList = root->getChildren()[0];
5473     std::vector<Element *> objects = depList->getChildren();
5474     for (unsigned int i=0 ; i<objects.size() ; i++)
5475         {
5476         Element *objectElem = objects[i];
5477         String tagName = objectElem->getName();
5478         if (tagName == "object")
5479             {
5480             String objName   = objectElem->getAttribute("name");
5481              //trace("object:%s", objName.c_str());
5482             DepRec depObject(objName);
5483             depObject.path   = objectElem->getAttribute("path");
5484             depObject.suffix = objectElem->getAttribute("suffix");
5485             //########## DESCRIPTION
5486             std::vector<Element *> depElems = objectElem->getChildren();
5487             for (unsigned int i=0 ; i<depElems.size() ; i++)
5488                 {
5489                 Element *depElem = depElems[i];
5490                 tagName = depElem->getName();
5491                 if (tagName == "dep")
5492                     {
5493                     String depName = depElem->getAttribute("name");
5494                     //trace("    dep:%s", depName.c_str());
5495                     depObject.files.push_back(depName);
5496                     }
5497                 }
5498             //Insert into the result list, in a sorted manner
5499             bool inserted = false;
5500             std::vector<DepRec>::iterator iter;
5501             for (iter = result.begin() ; iter != result.end() ; iter++)
5502                 {
5503                 String vpath = iter->path;
5504                 vpath.append("/");
5505                 vpath.append(iter->name);
5506                 String opath = depObject.path;
5507                 opath.append("/");
5508                 opath.append(depObject.name);
5509                 if (vpath > opath)
5510                     {
5511                     inserted = true;
5512                     iter = result.insert(iter, depObject);
5513                     break;
5514                     }
5515                 }
5516             if (!inserted)
5517                 result.push_back(depObject);
5518             }
5519         }
5521     delete root;
5523     return result;
5527 /**
5528  *   This loads the dependency cache.
5529  */
5530 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5531                    bool forceRefresh)
5533     std::vector<DepRec> result;
5534     if (forceRefresh)
5535         {
5536         generateDependencies(depFile);
5537         result = loadDepFile(depFile);
5538         }
5539     else
5540         {
5541         //try once
5542         result = loadDepFile(depFile);
5543         if (result.size() == 0)
5544             {
5545             //fail? try again
5546             generateDependencies(depFile);
5547             result = loadDepFile(depFile);
5548             }
5549         }
5550     return result;
5556 //########################################################################
5557 //# T A S K
5558 //########################################################################
5559 //forward decl
5560 class Target;
5561 class Make;
5563 /**
5564  *
5565  */
5566 class Task : public MakeBase
5569 public:
5571     typedef enum
5572         {
5573         TASK_NONE,
5574         TASK_CC,
5575         TASK_COPY,
5576         TASK_DELETE,
5577         TASK_JAR,
5578         TASK_JAVAC,
5579         TASK_LINK,
5580         TASK_MAKEFILE,
5581         TASK_MKDIR,
5582         TASK_MSGFMT,
5583         TASK_RANLIB,
5584         TASK_RC,
5585         TASK_SHAREDLIB,
5586         TASK_STATICLIB,
5587         TASK_STRIP,
5588         TASK_TSTAMP
5589         } TaskType;
5590         
5592     /**
5593      *
5594      */
5595     Task(MakeBase &par) : parent(par)
5596         { init(); }
5598     /**
5599      *
5600      */
5601     Task(const Task &other) : parent(other.parent)
5602         { init(); assign(other); }
5604     /**
5605      *
5606      */
5607     Task &operator=(const Task &other)
5608         { assign(other); return *this; }
5610     /**
5611      *
5612      */
5613     virtual ~Task()
5614         { }
5617     /**
5618      *
5619      */
5620     virtual MakeBase &getParent()
5621         { return parent; }
5623      /**
5624      *
5625      */
5626     virtual int  getType()
5627         { return type; }
5629     /**
5630      *
5631      */
5632     virtual void setType(int val)
5633         { type = val; }
5635     /**
5636      *
5637      */
5638     virtual String getName()
5639         { return name; }
5641     /**
5642      *
5643      */
5644     virtual bool execute()
5645         { return true; }
5647     /**
5648      *
5649      */
5650     virtual bool parse(Element *elem)
5651         { return true; }
5653     /**
5654      *
5655      */
5656     Task *createTask(Element *elem);
5659 protected:
5661     void init()
5662         {
5663         type = TASK_NONE;
5664         name = "none";
5665         }
5667     void assign(const Task &other)
5668         {
5669         type = other.type;
5670         name = other.name;
5671         }
5672         
5673     String getAttribute(Element *elem, const String &attrName)
5674         {
5675         String str;
5676         return str;
5677         }
5679     MakeBase &parent;
5681     int type;
5683     String name;
5684 };
5688 /**
5689  * This task runs the C/C++ compiler.  The compiler is invoked
5690  * for all .c or .cpp files which are newer than their correcsponding
5691  * .o files.  
5692  */
5693 class TaskCC : public Task
5695 public:
5697     TaskCC(MakeBase &par) : Task(par)
5698         {
5699                 type = TASK_CC; name = "cc";
5700                 ccCommand   = "gcc";
5701                 cxxCommand  = "g++";
5702                 source      = ".";
5703                 dest        = ".";
5704                 flags       = "";
5705                 defines     = "";
5706                 includes    = "";
5707                 fileSet.clear();
5708         }
5710     virtual ~TaskCC()
5711         {}
5713     virtual bool needsCompiling(const DepRec &depRec,
5714               const String &src, const String &dest)
5715         {
5716         return false;
5717         }
5719     virtual bool execute()
5720         {
5721         if (!listFiles(parent, fileSet))
5722             return false;
5724         bool refreshCache = false;
5725         String fullName = parent.resolve("build.dep");
5726         if (isNewerThan(parent.getURI().getPath(), fullName))
5727             {
5728             status("          : regenerating C/C++ dependency cache");
5729             refreshCache = true;
5730             }
5732         DepTool depTool;
5733         depTool.setSourceDirectory(source);
5734         depTool.setFileList(fileSet.getFiles());
5735         std::vector<DepRec> deps =
5736              depTool.getDepFile("build.dep", refreshCache);
5737         
5738         String incs;
5739         incs.append("-I");
5740         incs.append(parent.resolve("."));
5741         incs.append(" ");
5742         if (includes.size()>0)
5743             {
5744             incs.append(includes);
5745             incs.append(" ");
5746             }
5747         std::set<String> paths;
5748         std::vector<DepRec>::iterator viter;
5749         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5750             {
5751             DepRec dep = *viter;
5752             if (dep.path.size()>0)
5753                 paths.insert(dep.path);
5754             }
5755         if (source.size()>0)
5756             {
5757             incs.append(" -I");
5758             incs.append(parent.resolve(source));
5759             incs.append(" ");
5760             }
5761         std::set<String>::iterator setIter;
5762         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5763             {
5764             incs.append(" -I");
5765             String dname;
5766             if (source.size()>0)
5767                 {
5768                 dname.append(source);
5769                 dname.append("/");
5770                 }
5771             dname.append(*setIter);
5772             incs.append(parent.resolve(dname));
5773             }
5774         std::vector<String> cfiles;
5775         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5776             {
5777             DepRec dep = *viter;
5779             //## Select command
5780             String sfx = dep.suffix;
5781             String command = ccCommand;
5782             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5783                              || sfx == "CC")
5784                             command = cxxCommand;
5785  
5786             //## Make paths
5787             String destPath = dest;
5788             String srcPath  = source;
5789             if (dep.path.size()>0)
5790                             {
5791                 destPath.append("/");
5792                                 destPath.append(dep.path);
5793                 srcPath.append("/");
5794                                 srcPath.append(dep.path);
5795                             }
5796             //## Make sure destination directory exists
5797                         if (!createDirectory(destPath))
5798                             return false;
5799                             
5800             //## Check whether it needs to be done
5801                         String destName;
5802             if (destPath.size()>0)
5803                 {
5804                 destName.append(destPath);
5805                     destName.append("/");
5806                     }
5807                         destName.append(dep.name);
5808                         destName.append(".o");
5809                         String destFullName = parent.resolve(destName);
5810                         String srcName;
5811             if (srcPath.size()>0)
5812                 {
5813                 srcName.append(srcPath);
5814                 srcName.append("/");
5815                 }
5816                         srcName.append(dep.name);
5817                         srcName.append(".");
5818                         srcName.append(dep.suffix);
5819                         String srcFullName = parent.resolve(srcName);
5820                         bool compileMe = false;
5821             if (isNewerThan(srcFullName, destFullName))
5822                 {
5823                 status("          : compile of %s required by %s",
5824                         destFullName.c_str(), srcFullName.c_str());
5825                 compileMe = true;
5826                 }
5827             else
5828                 {
5829                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5830                     {
5831                     String depName;
5832                     if (srcPath.size()>0)
5833                         {
5834                         depName.append(srcPath);
5835                         depName.append("/");
5836                         }
5837                     depName.append(dep.files[i]);
5838                     String depFullName = parent.resolve(depName);
5839                     if (isNewerThan(depFullName, destFullName))
5840                         {
5841                         status("          : compile of %s required by %s",
5842                                 destFullName.c_str(), depFullName.c_str());
5843                         compileMe = true;
5844                         break;
5845                         }
5846                     }
5847                 }
5848             if (!compileMe)
5849                 {
5850                 continue;
5851                 }
5853             //## Assemble the command
5854             String cmd = command;
5855             cmd.append(" -c ");
5856             cmd.append(flags);
5857                         cmd.append(" ");
5858             cmd.append(defines);
5859                         cmd.append(" ");
5860             cmd.append(incs);
5861                         cmd.append(" ");
5862                         cmd.append(srcFullName);
5863             cmd.append(" -o ");
5864                         cmd.append(destFullName);
5866             //## Execute the command
5868             String outString, errString;
5869             if (!executeCommand(cmd.c_str(), "", outString, errString))
5870                 {
5871                 error("problem compiling: %s", errString.c_str());
5872                 return false;
5873                 }
5874             }
5875         
5876         return true;
5877         }
5879     virtual bool parse(Element *elem)
5880         {
5881         String s;
5882         if (!parent.getAttribute(elem, "command", s))
5883             return false;
5884         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5885         if (!parent.getAttribute(elem, "cc", s))
5886             return false;
5887         if (s.size()>0) ccCommand = s;
5888         if (!parent.getAttribute(elem, "cxx", s))
5889             return false;
5890         if (s.size()>0) cxxCommand = s;
5891         if (!parent.getAttribute(elem, "destdir", s))
5892             return false;
5893         if (s.size()>0) dest = s;
5895         std::vector<Element *> children = elem->getChildren();
5896         for (unsigned int i=0 ; i<children.size() ; i++)
5897             {
5898             Element *child = children[i];
5899             String tagName = child->getName();
5900             if (tagName == "flags")
5901                 {
5902                 if (!parent.getValue(child, flags))
5903                     return false;
5904                 flags = strip(flags);
5905                 }
5906             else if (tagName == "includes")
5907                 {
5908                 if (!parent.getValue(child, includes))
5909                     return false;
5910                 includes = strip(includes);
5911                 }
5912             else if (tagName == "defines")
5913                 {
5914                 if (!parent.getValue(child, defines))
5915                     return false;
5916                 defines = strip(defines);
5917                 }
5918             else if (tagName == "fileset")
5919                 {
5920                 if (!parseFileSet(child, parent, fileSet))
5921                     return false;
5922                 source = fileSet.getDirectory();
5923                 }
5924             }
5926         return true;
5927         }
5928         
5929 protected:
5931     String ccCommand;
5932     String cxxCommand;
5933     String source;
5934     String dest;
5935     String flags;
5936     String defines;
5937     String includes;
5938     FileSet fileSet;
5939     
5940 };
5944 /**
5945  *
5946  */
5947 class TaskCopy : public Task
5949 public:
5951     typedef enum
5952         {
5953         CP_NONE,
5954         CP_TOFILE,
5955         CP_TODIR
5956         } CopyType;
5958     TaskCopy(MakeBase &par) : Task(par)
5959         {
5960                 type = TASK_COPY; name = "copy";
5961                 cptype = CP_NONE;
5962                 verbose = false;
5963                 haveFileSet = false;
5964                 }
5966     virtual ~TaskCopy()
5967         {}
5969     virtual bool execute()
5970         {
5971         switch (cptype)
5972            {
5973            case CP_TOFILE:
5974                {
5975                if (fileName.size()>0)
5976                    {
5977                    status("          : %s to %s",
5978                         fileName.c_str(), toFileName.c_str());
5979                    String fullSource = parent.resolve(fileName);
5980                    String fullDest = parent.resolve(toFileName);
5981                    //trace("copy %s to file %s", fullSource.c_str(),
5982                                    //                       fullDest.c_str());
5983                                    if (!isRegularFile(fullSource))
5984                                        {
5985                        error("copy : file %s does not exist", fullSource.c_str());
5986                                        return false;
5987                                        }
5988                    if (!isNewerThan(fullSource, fullDest))
5989                        {
5990                        return true;
5991                        }
5992                    if (!copyFile(fullSource, fullDest))
5993                        return false;
5994                    status("          : 1 file copied");
5995                    }
5996                return true;
5997                }
5998            case CP_TODIR:
5999                {
6000                if (haveFileSet)
6001                    {
6002                    if (!listFiles(parent, fileSet))
6003                        return false;
6004                    String fileSetDir = fileSet.getDirectory();
6006                    status("          : %s to %s",
6007                        fileSetDir.c_str(), toDirName.c_str());
6009                    int nrFiles = 0;
6010                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6011                        {
6012                        String fileName = fileSet[i];
6014                        String sourcePath;
6015                        if (fileSetDir.size()>0)
6016                            {
6017                            sourcePath.append(fileSetDir);
6018                            sourcePath.append("/");
6019                            }
6020                        sourcePath.append(fileName);
6021                        String fullSource = parent.resolve(sourcePath);
6022                        
6023                        //Get the immediate parent directory's base name
6024                        String baseFileSetDir = fileSetDir;
6025                        unsigned int pos = baseFileSetDir.find_last_of('/');
6026                        if (pos!=baseFileSetDir.npos &&
6027                                                       pos < baseFileSetDir.size()-1)
6028                            baseFileSetDir =
6029                                                       baseFileSetDir.substr(pos+1,
6030                                                                baseFileSetDir.size());
6031                                            //Now make the new path
6032                        String destPath;
6033                        if (toDirName.size()>0)
6034                            {
6035                            destPath.append(toDirName);
6036                            destPath.append("/");
6037                            }
6038                        if (baseFileSetDir.size()>0)
6039                            {
6040                            destPath.append(baseFileSetDir);
6041                            destPath.append("/");
6042                            }
6043                        destPath.append(fileName);
6044                        String fullDest = parent.resolve(destPath);
6045                        //trace("fileName:%s", fileName.c_str());
6046                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6047                                        //                   fullDest.c_str());
6048                        if (!isNewerThan(fullSource, fullDest))
6049                            {
6050                            //trace("copy skipping %s", fullSource.c_str());
6051                            continue;
6052                            }
6053                        if (!copyFile(fullSource, fullDest))
6054                            return false;
6055                        nrFiles++;
6056                        }
6057                    status("          : %d file(s) copied", nrFiles);
6058                    }
6059                else //file source
6060                    {
6061                    //For file->dir we want only the basename of
6062                    //the source appended to the dest dir
6063                    status("          : %s to %s", 
6064                        fileName.c_str(), toDirName.c_str());
6065                    String baseName = fileName;
6066                    unsigned int pos = baseName.find_last_of('/');
6067                    if (pos!=baseName.npos && pos<baseName.size()-1)
6068                        baseName = baseName.substr(pos+1, baseName.size());
6069                    String fullSource = parent.resolve(fileName);
6070                    String destPath;
6071                    if (toDirName.size()>0)
6072                        {
6073                        destPath.append(toDirName);
6074                        destPath.append("/");
6075                        }
6076                    destPath.append(baseName);
6077                    String fullDest = parent.resolve(destPath);
6078                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6079                                    //                       fullDest.c_str());
6080                                    if (!isRegularFile(fullSource))
6081                                        {
6082                        error("copy : file %s does not exist", fullSource.c_str());
6083                                        return false;
6084                                        }
6085                    if (!isNewerThan(fullSource, fullDest))
6086                        {
6087                        return true;
6088                        }
6089                    if (!copyFile(fullSource, fullDest))
6090                        return false;
6091                    status("          : 1 file copied");
6092                    }
6093                return true;
6094                }
6095            }
6096         return true;
6097         }
6100     virtual bool parse(Element *elem)
6101         {
6102         if (!parent.getAttribute(elem, "file", fileName))
6103             return false;
6104         if (!parent.getAttribute(elem, "tofile", toFileName))
6105             return false;
6106         if (toFileName.size() > 0)
6107             cptype = CP_TOFILE;
6108         if (!parent.getAttribute(elem, "todir", toDirName))
6109             return false;
6110         if (toDirName.size() > 0)
6111             cptype = CP_TODIR;
6112         String ret;
6113         if (!parent.getAttribute(elem, "verbose", ret))
6114             return false;
6115         if (ret.size()>0 && !getBool(ret, verbose))
6116             return false;
6117             
6118         haveFileSet = false;
6119         
6120         std::vector<Element *> children = elem->getChildren();
6121         for (unsigned int i=0 ; i<children.size() ; i++)
6122             {
6123             Element *child = children[i];
6124             String tagName = child->getName();
6125             if (tagName == "fileset")
6126                 {
6127                 if (!parseFileSet(child, parent, fileSet))
6128                     {
6129                     error("problem getting fileset");
6130                                         return false;
6131                                         }
6132                                 haveFileSet = true;
6133                 }
6134             }
6136         //Perform validity checks
6137                 if (fileName.size()>0 && fileSet.size()>0)
6138                     {
6139                     error("<copy> can only have one of : file= and <fileset>");
6140                     return false;
6141                     }
6142         if (toFileName.size()>0 && toDirName.size()>0)
6143             {
6144             error("<copy> can only have one of : tofile= or todir=");
6145             return false;
6146             }
6147         if (haveFileSet && toDirName.size()==0)
6148             {
6149             error("a <copy> task with a <fileset> must have : todir=");
6150             return false;
6151             }
6152                 if (cptype == CP_TOFILE && fileName.size()==0)
6153                     {
6154                     error("<copy> tofile= must be associated with : file=");
6155                     return false;
6156                     }
6157                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6158                     {
6159                     error("<copy> todir= must be associated with : file= or <fileset>");
6160                     return false;
6161                     }
6163         return true;
6164         }
6165         
6166 private:
6168     int cptype;
6169     String fileName;
6170     FileSet fileSet;
6171     String toFileName;
6172     String toDirName;
6173     bool verbose;
6174     bool haveFileSet;
6175 };
6178 /**
6179  *
6180  */
6181 class TaskDelete : public Task
6183 public:
6185     typedef enum
6186         {
6187         DEL_FILE,
6188         DEL_DIR,
6189         DEL_FILESET
6190         } DeleteType;
6192     TaskDelete(MakeBase &par) : Task(par)
6193         { 
6194                   type        = TASK_DELETE;
6195                   name        = "delete";
6196                   delType     = DEL_FILE;
6197           verbose     = false;
6198           quiet       = false;
6199           failOnError = true;
6200                 }
6202     virtual ~TaskDelete()
6203         {}
6205     virtual bool execute()
6206         {
6207         struct stat finfo;
6208         switch (delType)
6209             {
6210             case DEL_FILE:
6211                 {
6212                 status("          : %s", fileName.c_str());
6213                 String fullName = parent.resolve(fileName);
6214                 char *fname = (char *)fullName.c_str();
6215                 //does not exist
6216                 if (stat(fname, &finfo)<0)
6217                     return true;
6218                 //exists but is not a regular file
6219                 if (!S_ISREG(finfo.st_mode))
6220                     {
6221                     error("<delete> failed. '%s' exists and is not a regular file",
6222                           fname);
6223                     return false;
6224                     }
6225                 if (remove(fname)<0)
6226                     {
6227                     error("<delete> failed: %s", strerror(errno));
6228                     return false;
6229                     }
6230                 return true;
6231                 }
6232             case DEL_DIR:
6233                 {
6234                 status("          : %s", dirName.c_str());
6235                 String fullDir = parent.resolve(dirName);
6236                 if (!removeDirectory(fullDir))
6237                     return false;
6238                 return true;
6239                 }
6240             }
6241         return true;
6242         }
6244     virtual bool parse(Element *elem)
6245         {
6246         if (!parent.getAttribute(elem, "file", fileName))
6247             return false;
6248         if (fileName.size() > 0)
6249             delType = DEL_FILE;
6250         if (!parent.getAttribute(elem, "dir", dirName))
6251             return false;
6252         if (dirName.size() > 0)
6253             delType = DEL_DIR;
6254         if (fileName.size()>0 && dirName.size()>0)
6255             {
6256             error("<delete> can only have one attribute of file= or dir=");
6257             return false;
6258             }
6259         String ret;
6260         if (!parent.getAttribute(elem, "verbose", ret))
6261             return false;
6262         if (ret.size()>0 && !getBool(ret, verbose))
6263             return false;
6264         if (!parent.getAttribute(elem, "quiet", ret))
6265             return false;
6266         if (ret.size()>0 && !getBool(ret, quiet))
6267             return false;
6268         if (!parent.getAttribute(elem, "failonerror", ret))
6269             return false;
6270         if (ret.size()>0 && !getBool(ret, failOnError))
6271             return false;
6272         return true;
6273         }
6275 private:
6277     int delType;
6278     String dirName;
6279     String fileName;
6280     bool verbose;
6281     bool quiet;
6282     bool failOnError;
6283 };
6286 /**
6287  *
6288  */
6289 class TaskJar : public Task
6291 public:
6293     TaskJar(MakeBase &par) : Task(par)
6294         { type = TASK_JAR; name = "jar"; }
6296     virtual ~TaskJar()
6297         {}
6299     virtual bool execute()
6300         {
6301         return true;
6302         }
6304     virtual bool parse(Element *elem)
6305         {
6306         return true;
6307         }
6308 };
6311 /**
6312  *
6313  */
6314 class TaskJavac : public Task
6316 public:
6318     TaskJavac(MakeBase &par) : Task(par)
6319         { type = TASK_JAVAC; name = "javac"; }
6321     virtual ~TaskJavac()
6322         {}
6324     virtual bool execute()
6325         {
6326         return true;
6327         }
6329     virtual bool parse(Element *elem)
6330         {
6331         return true;
6332         }
6333 };
6336 /**
6337  *
6338  */
6339 class TaskLink : public Task
6341 public:
6343     TaskLink(MakeBase &par) : Task(par)
6344         {
6345                 type = TASK_LINK; name = "link";
6346                 command = "g++";
6347                 }
6349     virtual ~TaskLink()
6350         {}
6352     virtual bool execute()
6353         {
6354         if (!listFiles(parent, fileSet))
6355             return false;
6356         String fileSetDir = fileSet.getDirectory();
6357         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6358         bool doit = false;
6359         String fullTarget = parent.resolve(fileName);
6360         String cmd = command;
6361         cmd.append(" -o ");
6362         cmd.append(fullTarget);
6363         cmd.append(" ");
6364         cmd.append(flags);
6365         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6366             {
6367             cmd.append(" ");
6368             String obj;
6369             if (fileSetDir.size()>0)
6370                             {
6371                                 obj.append(fileSetDir);
6372                 obj.append("/");
6373                 }
6374             obj.append(fileSet[i]);
6375             String fullObj = parent.resolve(obj);
6376             cmd.append(fullObj);
6377             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6378             //          fullObj.c_str());
6379             if (isNewerThan(fullObj, fullTarget))
6380                 doit = true;
6381             }
6382         cmd.append(" ");
6383         cmd.append(libs);
6384         if (!doit)
6385             {
6386             //trace("link not needed");
6387             return true;
6388             }
6389         //trace("LINK cmd:%s", cmd.c_str());
6392         String outString, errString;
6393         if (!executeCommand(cmd.c_str(), "", outString, errString))
6394             {
6395             error("LINK problem: %s", errString.c_str());
6396             return false;
6397             }
6398         return true;
6399         }
6401     virtual bool parse(Element *elem)
6402         {
6403         String s;
6404         if (!parent.getAttribute(elem, "command", s))
6405             return false;
6406         if (s.size()>0)
6407             command = s;
6408         if (!parent.getAttribute(elem, "out", fileName))
6409             return false;
6410             
6411         std::vector<Element *> children = elem->getChildren();
6412         for (unsigned int i=0 ; i<children.size() ; i++)
6413             {
6414             Element *child = children[i];
6415             String tagName = child->getName();
6416             if (tagName == "fileset")
6417                 {
6418                 if (!parseFileSet(child, parent, fileSet))
6419                     return false;
6420                 }
6421             else if (tagName == "flags")
6422                 {
6423                 if (!parent.getValue(child, flags))
6424                     return false;
6425                 flags = strip(flags);
6426                 }
6427             else if (tagName == "libs")
6428                 {
6429                 if (!parent.getValue(child, libs))
6430                     return false;
6431                 libs = strip(libs);
6432                 }
6433             }
6434         return true;
6435         }
6437 private:
6439     String command;
6440     String fileName;
6441     String flags;
6442     String libs;
6443     FileSet fileSet;
6445 };
6449 /**
6450  * Create a named directory
6451  */
6452 class TaskMakeFile : public Task
6454 public:
6456     TaskMakeFile(MakeBase &par) : Task(par)
6457         { type = TASK_MAKEFILE; name = "makefile"; }
6459     virtual ~TaskMakeFile()
6460         {}
6462     virtual bool execute()
6463         {
6464         status("          : %s", fileName.c_str());
6465         String fullName = parent.resolve(fileName);
6466         if (!isNewerThan(parent.getURI().getPath(), fullName))
6467             {
6468             //trace("skipped <makefile>");
6469             return true;
6470             }
6471         //trace("fullName:%s", fullName.c_str());
6472         FILE *f = fopen(fullName.c_str(), "w");
6473         if (!f)
6474             {
6475             error("<makefile> could not open %s for writing : %s",
6476                 fullName.c_str(), strerror(errno));
6477             return false;
6478             }
6479         for (unsigned int i=0 ; i<text.size() ; i++)
6480             fputc(text[i], f);
6481         fputc('\n', f);
6482         fclose(f);
6483         return true;
6484         }
6486     virtual bool parse(Element *elem)
6487         {
6488         if (!parent.getAttribute(elem, "file", fileName))
6489             return false;
6490         if (fileName.size() == 0)
6491             {
6492             error("<makefile> requires 'file=\"filename\"' attribute");
6493             return false;
6494             }
6495         if (!parent.getValue(elem, text))
6496             return false;
6497         text = leftJustify(text);
6498         //trace("dirname:%s", dirName.c_str());
6499         return true;
6500         }
6502 private:
6504     String fileName;
6505     String text;
6506 };
6510 /**
6511  * Create a named directory
6512  */
6513 class TaskMkDir : public Task
6515 public:
6517     TaskMkDir(MakeBase &par) : Task(par)
6518         { type = TASK_MKDIR; name = "mkdir"; }
6520     virtual ~TaskMkDir()
6521         {}
6523     virtual bool execute()
6524         {
6525         status("          : %s", dirName.c_str());
6526         String fullDir = parent.resolve(dirName);
6527         //trace("fullDir:%s", fullDir.c_str());
6528         if (!createDirectory(fullDir))
6529             return false;
6530         return true;
6531         }
6533     virtual bool parse(Element *elem)
6534         {
6535         if (!parent.getAttribute(elem, "dir", dirName))
6536             return false;
6537         if (dirName.size() == 0)
6538             {
6539             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6540             return false;
6541             }
6542         return true;
6543         }
6545 private:
6547     String dirName;
6548 };
6552 /**
6553  * Create a named directory
6554  */
6555 class TaskMsgFmt: public Task
6557 public:
6559     TaskMsgFmt(MakeBase &par) : Task(par)
6560          {
6561                  type    = TASK_MSGFMT;
6562                  name    = "msgfmt";
6563                  command = "msgfmt";
6564                  owndir  = false;
6565                  }
6567     virtual ~TaskMsgFmt()
6568         {}
6570     virtual bool execute()
6571         {
6572         if (!listFiles(parent, fileSet))
6573             return false;
6574         String fileSetDir = fileSet.getDirectory();
6576         //trace("msgfmt: %d", fileSet.size());
6577         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6578             {
6579             String fileName = fileSet[i];
6580             if (getSuffix(fileName) != "po")
6581                 continue;
6582             String sourcePath;
6583                         if (fileSetDir.size()>0)
6584                             {
6585                             sourcePath.append(fileSetDir);
6586                 sourcePath.append("/");
6587                 }
6588             sourcePath.append(fileName);
6589             String fullSource = parent.resolve(sourcePath);
6591             String destPath;
6592                         if (toDirName.size()>0)
6593                             {
6594                             destPath.append(toDirName);
6595                 destPath.append("/");
6596                 }
6597             if (owndir)
6598                 {
6599                 String subdir = fileName;
6600                 unsigned int pos = subdir.find_last_of('.');
6601                 if (pos != subdir.npos)
6602                     subdir = subdir.substr(0, pos);
6603                 destPath.append(subdir);
6604                 destPath.append("/");
6605                 }
6606             destPath.append(fileName);
6607             destPath[destPath.size()-2] = 'm';
6608             String fullDest = parent.resolve(destPath);
6610             if (!isNewerThan(fullSource, fullDest))
6611                 {
6612                 //trace("skip %s", fullSource.c_str());
6613                 continue;
6614                 }
6615                 
6616             String cmd = command;
6617             cmd.append(" ");
6618             cmd.append(fullSource);
6619             cmd.append(" -o ");
6620             cmd.append(fullDest);
6621             
6622             int pos = fullDest.find_last_of('/');
6623             if (pos>0)
6624                 {
6625                 String fullDestPath = fullDest.substr(0, pos);
6626                 if (!createDirectory(fullDestPath))
6627                     return false;
6628                 }
6632             String outString, errString;
6633             if (!executeCommand(cmd.c_str(), "", outString, errString))
6634                 {
6635                 error("<msgfmt> problem: %s", errString.c_str());
6636                 return false;
6637                 }
6638             }
6640         return true;
6641         }
6643     virtual bool parse(Element *elem)
6644         {
6645         if (!parent.getAttribute(elem, "todir", toDirName))
6646             return false;
6647         String s;
6648         if (!parent.getAttribute(elem, "owndir", s))
6649             return false;
6650         if (!getBool(s, owndir))
6651             return false;
6652             
6653         std::vector<Element *> children = elem->getChildren();
6654         for (unsigned int i=0 ; i<children.size() ; i++)
6655             {
6656             Element *child = children[i];
6657             String tagName = child->getName();
6658             if (tagName == "fileset")
6659                 {
6660                 if (!parseFileSet(child, parent, fileSet))
6661                     return false;
6662                 }
6663             }
6664         return true;
6665         }
6667 private:
6669     String command;
6670     String toDirName;
6671     FileSet fileSet;
6672     bool owndir;
6674 };
6680 /**
6681  *  Process an archive to allow random access
6682  */
6683 class TaskRanlib : public Task
6685 public:
6687     TaskRanlib(MakeBase &par) : Task(par)
6688         { type = TASK_RANLIB; name = "ranlib"; }
6690     virtual ~TaskRanlib()
6691         {}
6693     virtual bool execute()
6694         {
6695         String fullName = parent.resolve(fileName);
6696         //trace("fullDir:%s", fullDir.c_str());
6697         String cmd = "ranlib ";
6698         cmd.append(fullName);
6699         String outbuf, errbuf;
6700         if (!executeCommand(cmd, "", outbuf, errbuf))
6701             return false;
6702         return true;
6703         }
6705     virtual bool parse(Element *elem)
6706         {
6707         if (!parent.getAttribute(elem, "file", fileName))
6708             return false;
6709         if (fileName.size() == 0)
6710             {
6711             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6712             return false;
6713             }
6714         return true;
6715         }
6717 private:
6719     String fileName;
6720 };
6724 /**
6725  * Run the "ar" command to archive .o's into a .a
6726  */
6727 class TaskRC : public Task
6729 public:
6731     TaskRC(MakeBase &par) : Task(par)
6732         {
6733                 type = TASK_RC; name = "rc";
6734                 command = "windres -o";
6735                 }
6737     virtual ~TaskRC()
6738         {}
6740     virtual bool execute()
6741         {
6742         String fullFile = parent.resolve(fileName);
6743         String fullOut  = parent.resolve(outName);
6744         if (!isNewerThan(fullFile, fullOut))
6745             return true;
6746         String cmd = command;
6747         cmd.append(" ");
6748         cmd.append(fullOut);
6749         cmd.append(" ");
6750         cmd.append(flags);
6751         cmd.append(" ");
6752         cmd.append(fullFile);
6754         String outString, errString;
6755         if (!executeCommand(cmd.c_str(), "", outString, errString))
6756             {
6757             error("RC problem: %s", errString.c_str());
6758             return false;
6759             }
6760         return true;
6761         }
6763     virtual bool parse(Element *elem)
6764         {
6765         if (!parent.getAttribute(elem, "command", command))
6766             return false;
6767         if (!parent.getAttribute(elem, "file", fileName))
6768             return false;
6769         if (!parent.getAttribute(elem, "out", outName))
6770             return false;
6771         std::vector<Element *> children = elem->getChildren();
6772         for (unsigned int i=0 ; i<children.size() ; i++)
6773             {
6774             Element *child = children[i];
6775             String tagName = child->getName();
6776             if (tagName == "flags")
6777                 {
6778                 if (!parent.getValue(child, flags))
6779                     return false;
6780                 }
6781             }
6782         return true;
6783         }
6785 private:
6787     String command;
6788     String flags;
6789     String fileName;
6790     String outName;
6792 };
6796 /**
6797  *  Collect .o's into a .so or DLL
6798  */
6799 class TaskSharedLib : public Task
6801 public:
6803     TaskSharedLib(MakeBase &par) : Task(par)
6804         {
6805                 type = TASK_SHAREDLIB; name = "dll";
6806                 command = "ar crv";
6807                 }
6809     virtual ~TaskSharedLib()
6810         {}
6812     virtual bool execute()
6813         {
6814         //trace("###########HERE %d", fileSet.size());
6815         bool doit = false;
6816         
6817         String fullOut = parent.resolve(fileName);
6818         //trace("ar fullout: %s", fullOut.c_str());
6819         
6820         if (!listFiles(parent, fileSet))
6821             return false;
6822         String fileSetDir = fileSet.getDirectory();
6824         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6825             {
6826             String fname;
6827                         if (fileSetDir.size()>0)
6828                             {
6829                             fname.append(fileSetDir);
6830                 fname.append("/");
6831                 }
6832             fname.append(fileSet[i]);
6833             String fullName = parent.resolve(fname);
6834             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6835             if (isNewerThan(fullName, fullOut))
6836                 doit = true;
6837             }
6838         //trace("Needs it:%d", doit);
6839         if (!doit)
6840             {
6841             return true;
6842             }
6844         String cmd = "dllwrap";
6845         cmd.append(" -o ");
6846         cmd.append(fullOut);
6847         if (defFileName.size()>0)
6848             {
6849             cmd.append(" --def ");
6850             cmd.append(defFileName);
6851             cmd.append(" ");
6852             }
6853         if (impFileName.size()>0)
6854             {
6855             cmd.append(" --implib ");
6856             cmd.append(impFileName);
6857             cmd.append(" ");
6858             }
6859         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6860             {
6861             String fname;
6862                         if (fileSetDir.size()>0)
6863                             {
6864                             fname.append(fileSetDir);
6865                 fname.append("/");
6866                 }
6867             fname.append(fileSet[i]);
6868             String fullName = parent.resolve(fname);
6870             cmd.append(" ");
6871             cmd.append(fullName);
6872             }
6873         cmd.append(" ");
6874         cmd.append(libs);
6876         String outString, errString;
6877         if (!executeCommand(cmd.c_str(), "", outString, errString))
6878             {
6879             error("<sharedlib> problem: %s", errString.c_str());
6880             return false;
6881             }
6883         return true;
6884         }
6886     virtual bool parse(Element *elem)
6887         {
6888         if (!parent.getAttribute(elem, "file", fileName))
6889             return false;
6890         if (!parent.getAttribute(elem, "import", impFileName))
6891             return false;
6892         if (!parent.getAttribute(elem, "def", defFileName))
6893             return false;
6894             
6895         std::vector<Element *> children = elem->getChildren();
6896         for (unsigned int i=0 ; i<children.size() ; i++)
6897             {
6898             Element *child = children[i];
6899             String tagName = child->getName();
6900             if (tagName == "fileset")
6901                 {
6902                 if (!parseFileSet(child, parent, fileSet))
6903                     return false;
6904                 }
6905             else if (tagName == "libs")
6906                 {
6907                 if (!parent.getValue(child, libs))
6908                     return false;
6909                 libs = strip(libs);
6910                 }
6911             }
6912         return true;
6913         }
6915 private:
6917     String command;
6918     String fileName;
6919     String defFileName;
6920     String impFileName;
6921     FileSet fileSet;
6922     String libs;
6924 };
6927 /**
6928  * Run the "ar" command to archive .o's into a .a
6929  */
6930 class TaskStaticLib : public Task
6932 public:
6934     TaskStaticLib(MakeBase &par) : Task(par)
6935         {
6936                 type = TASK_STATICLIB; name = "staticlib";
6937                 command = "ar crv";
6938                 }
6940     virtual ~TaskStaticLib()
6941         {}
6943     virtual bool execute()
6944         {
6945         //trace("###########HERE %d", fileSet.size());
6946         bool doit = false;
6947         
6948         String fullOut = parent.resolve(fileName);
6949         //trace("ar fullout: %s", fullOut.c_str());
6950         
6951         if (!listFiles(parent, fileSet))
6952             return false;
6953         String fileSetDir = fileSet.getDirectory();
6955         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6956             {
6957             String fname;
6958                         if (fileSetDir.size()>0)
6959                             {
6960                             fname.append(fileSetDir);
6961                 fname.append("/");
6962                 }
6963             fname.append(fileSet[i]);
6964             String fullName = parent.resolve(fname);
6965             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6966             if (isNewerThan(fullName, fullOut))
6967                 doit = true;
6968             }
6969         //trace("Needs it:%d", doit);
6970         if (!doit)
6971             {
6972             return true;
6973             }
6975         String cmd = command;
6976         cmd.append(" ");
6977         cmd.append(fullOut);
6978         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6979             {
6980             String fname;
6981                         if (fileSetDir.size()>0)
6982                             {
6983                             fname.append(fileSetDir);
6984                 fname.append("/");
6985                 }
6986             fname.append(fileSet[i]);
6987             String fullName = parent.resolve(fname);
6989             cmd.append(" ");
6990             cmd.append(fullName);
6991             }
6993         String outString, errString;
6994         if (!executeCommand(cmd.c_str(), "", outString, errString))
6995             {
6996             error("<staticlib> problem: %s", errString.c_str());
6997             return false;
6998             }
7000         return true;
7001         }
7003     virtual bool parse(Element *elem)
7004         {
7005         if (!parent.getAttribute(elem, "file", fileName))
7006             return false;
7007             
7008         std::vector<Element *> children = elem->getChildren();
7009         for (unsigned int i=0 ; i<children.size() ; i++)
7010             {
7011             Element *child = children[i];
7012             String tagName = child->getName();
7013             if (tagName == "fileset")
7014                 {
7015                 if (!parseFileSet(child, parent, fileSet))
7016                     return false;
7017                 }
7018             }
7019         return true;
7020         }
7022 private:
7024     String command;
7025     String fileName;
7026     FileSet fileSet;
7028 };
7031 /**
7032  * Strip an executable
7033  */
7034 class TaskStrip : public Task
7036 public:
7038     TaskStrip(MakeBase &par) : Task(par)
7039         { type = TASK_STRIP; name = "strip"; }
7041     virtual ~TaskStrip()
7042         {}
7044     virtual bool execute()
7045         {
7046         String fullName = parent.resolve(fileName);
7047         //trace("fullDir:%s", fullDir.c_str());
7048         String cmd = "strip ";
7049         cmd.append(fullName);
7051         String outbuf, errbuf;
7052         if (!executeCommand(cmd, "", outbuf, errbuf))
7053             return false;
7054         return true;
7055         }
7057     virtual bool parse(Element *elem)
7058         {
7059         if (!parent.getAttribute(elem, "file", fileName))
7060             return false;
7061         if (fileName.size() == 0)
7062             {
7063             error("<strip> requires 'file=\"fileNname\"' attribute");
7064             return false;
7065             }
7066         return true;
7067         }
7069 private:
7071     String fileName;
7072 };
7075 /**
7076  *
7077  */
7078 class TaskTstamp : public Task
7080 public:
7082     TaskTstamp(MakeBase &par) : Task(par)
7083         { type = TASK_TSTAMP; name = "tstamp"; }
7085     virtual ~TaskTstamp()
7086         {}
7088     virtual bool execute()
7089         {
7090         return true;
7091         }
7093     virtual bool parse(Element *elem)
7094         {
7095         //trace("tstamp parse");
7096         return true;
7097         }
7098 };
7102 /**
7103  *
7104  */
7105 Task *Task::createTask(Element *elem)
7107     String tagName = elem->getName();
7108     //trace("task:%s", tagName.c_str());
7109     Task *task = NULL;
7110     if (tagName == "cc")
7111         task = new TaskCC(parent);
7112     else if (tagName == "copy")
7113         task = new TaskCopy(parent);
7114     else if (tagName == "delete")
7115         task = new TaskDelete(parent);
7116     else if (tagName == "jar")
7117         task = new TaskJar(parent);
7118     else if (tagName == "javac")
7119         task = new TaskJavac(parent);
7120     else if (tagName == "link")
7121         task = new TaskLink(parent);
7122     else if (tagName == "makefile")
7123         task = new TaskMakeFile(parent);
7124     else if (tagName == "mkdir")
7125         task = new TaskMkDir(parent);
7126     else if (tagName == "msgfmt")
7127         task = new TaskMsgFmt(parent);
7128     else if (tagName == "ranlib")
7129         task = new TaskRanlib(parent);
7130     else if (tagName == "rc")
7131         task = new TaskRC(parent);
7132     else if (tagName == "sharedlib")
7133         task = new TaskSharedLib(parent);
7134     else if (tagName == "staticlib")
7135         task = new TaskStaticLib(parent);
7136     else if (tagName == "strip")
7137         task = new TaskStrip(parent);
7138     else if (tagName == "tstamp")
7139         task = new TaskTstamp(parent);
7140     else
7141         {
7142         error("Unknown task '%s'", tagName.c_str());
7143         return NULL;
7144         }
7146     if (!task->parse(elem))
7147         {
7148         delete task;
7149         return NULL;
7150         }
7151     return task;
7156 //########################################################################
7157 //# T A R G E T
7158 //########################################################################
7160 /**
7161  *
7162  */
7163 class Target : public MakeBase
7166 public:
7168     /**
7169      *
7170      */
7171     Target(Make &par) : parent(par)
7172         { init(); }
7174     /**
7175      *
7176      */
7177     Target(const Target &other) : parent(other.parent)
7178         { init(); assign(other); }
7180     /**
7181      *
7182      */
7183     Target &operator=(const Target &other)
7184         { init(); assign(other); return *this; }
7186     /**
7187      *
7188      */
7189     virtual ~Target()
7190         { cleanup() ; }
7193     /**
7194      *
7195      */
7196     virtual Make &getParent()
7197         { return parent; }
7199     /**
7200      *
7201      */
7202     virtual String getName()
7203         { return name; }
7205     /**
7206      *
7207      */
7208     virtual void setName(const String &val)
7209         { name = val; }
7211     /**
7212      *
7213      */
7214     virtual String getDescription()
7215         { return description; }
7217     /**
7218      *
7219      */
7220     virtual void setDescription(const String &val)
7221         { description = val; }
7223     /**
7224      *
7225      */
7226     virtual void addDependency(const String &val)
7227         { deps.push_back(val); }
7229     /**
7230      *
7231      */
7232     virtual void parseDependencies(const String &val)
7233         { deps = tokenize(val, ", "); }
7235     /**
7236      *
7237      */
7238     virtual std::vector<String> &getDependencies()
7239         { return deps; }
7241     /**
7242      *
7243      */
7244     virtual String getIf()
7245         { return ifVar; }
7247     /**
7248      *
7249      */
7250     virtual void setIf(const String &val)
7251         { ifVar = val; }
7253     /**
7254      *
7255      */
7256     virtual String getUnless()
7257         { return unlessVar; }
7259     /**
7260      *
7261      */
7262     virtual void setUnless(const String &val)
7263         { unlessVar = val; }
7265     /**
7266      *
7267      */
7268     virtual void addTask(Task *val)
7269         { tasks.push_back(val); }
7271     /**
7272      *
7273      */
7274     virtual std::vector<Task *> &getTasks()
7275         { return tasks; }
7277 private:
7279     void init()
7280         {
7281         }
7283     void cleanup()
7284         {
7285         tasks.clear();
7286         }
7288     void assign(const Target &other)
7289         {
7290         //parent      = other.parent;
7291         name        = other.name;
7292         description = other.description;
7293         ifVar       = other.ifVar;
7294         unlessVar   = other.unlessVar;
7295         deps        = other.deps;
7296         tasks       = other.tasks;
7297         }
7299     Make &parent;
7301     String name;
7303     String description;
7305     String ifVar;
7307     String unlessVar;
7309     std::vector<String> deps;
7311     std::vector<Task *> tasks;
7313 };
7322 //########################################################################
7323 //# M A K E
7324 //########################################################################
7327 /**
7328  *
7329  */
7330 class Make : public MakeBase
7333 public:
7335     /**
7336      *
7337      */
7338     Make()
7339         { init(); }
7341     /**
7342      *
7343      */
7344     Make(const Make &other)
7345         { assign(other); }
7347     /**
7348      *
7349      */
7350     Make &operator=(const Make &other)
7351         { assign(other); return *this; }
7353     /**
7354      *
7355      */
7356     virtual ~Make()
7357         { cleanup(); }
7359     /**
7360      *
7361      */
7362     virtual std::map<String, Target> &getTargets()
7363         { return targets; }
7366     /**
7367      *
7368      */
7369     virtual String version()
7370         { return "BuildTool v0.3, 2006 Bob Jamison"; }
7372     /**
7373      * Overload a <property>
7374      */
7375     virtual bool specifyProperty(const String &name,
7376                                      const String &value);
7378     /**
7379      *
7380      */
7381     virtual bool run();
7383     /**
7384      *
7385      */
7386     virtual bool run(const String &target);
7390 private:
7392     /**
7393      *
7394      */
7395     void init();
7397     /**
7398      *
7399      */
7400     void cleanup();
7402     /**
7403      *
7404      */
7405     void assign(const Make &other);
7407     /**
7408      *
7409      */
7410     bool executeTask(Task &task);
7413     /**
7414      *
7415      */
7416     bool executeTarget(Target &target,
7417                  std::set<String> &targetsCompleted);
7420     /**
7421      *
7422      */
7423     bool execute();
7425     /**
7426      *
7427      */
7428     bool checkTargetDependencies(Target &prop,
7429                     std::vector<String> &depList);
7431     /**
7432      *
7433      */
7434     bool parsePropertyFile(const String &fileName,
7435                                const String &prefix);
7437     /**
7438      *
7439      */
7440     bool parseProperty(Element *elem);
7442     /**
7443      *
7444      */
7445     bool parseTask(Task &task, Element *elem);
7447     /**
7448      *
7449      */
7450     bool parseFile();
7452     /**
7453      *
7454      */
7455     std::vector<String> glob(const String &pattern);
7458     //###############
7459     //# Fields
7460     //###############
7462     String projectName;
7464     String currentTarget;
7466     String defaultTarget;
7468     String specifiedTarget;
7470     String baseDir;
7472     String description;
7473     
7474     String envAlias;
7476     //std::vector<Property> properties;
7477     
7478     std::map<String, Target> targets;
7480     std::vector<Task *> allTasks;
7481     
7482     std::map<String, String> specifiedProperties;
7484 };
7487 //########################################################################
7488 //# C L A S S  M A I N T E N A N C E
7489 //########################################################################
7491 /**
7492  *
7493  */
7494 void Make::init()
7496     uri             = "build.xml";
7497     projectName     = "";
7498     currentTarget   = "";
7499     defaultTarget   = "";
7500     specifiedTarget = "";
7501     baseDir         = "";
7502     description     = "";
7503     envAlias        = "";
7504     properties.clear();
7505     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7506         delete allTasks[i];
7507     allTasks.clear();
7512 /**
7513  *
7514  */
7515 void Make::cleanup()
7517     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7518         delete allTasks[i];
7519     allTasks.clear();
7524 /**
7525  *
7526  */
7527 void Make::assign(const Make &other)
7529     uri              = other.uri;
7530     projectName      = other.projectName;
7531     currentTarget    = other.currentTarget;
7532     defaultTarget    = other.defaultTarget;
7533     specifiedTarget  = other.specifiedTarget;
7534     baseDir          = other.baseDir;
7535     description      = other.description;
7536     properties       = other.properties;
7541 //########################################################################
7542 //# U T I L I T Y    T A S K S
7543 //########################################################################
7545 /**
7546  *  Perform a file globbing
7547  */
7548 std::vector<String> Make::glob(const String &pattern)
7550     std::vector<String> res;
7551     return res;
7555 //########################################################################
7556 //# P U B L I C    A P I
7557 //########################################################################
7561 /**
7562  *
7563  */
7564 bool Make::executeTarget(Target &target,
7565              std::set<String> &targetsCompleted)
7568     String name = target.getName();
7570     //First get any dependencies for this target
7571     std::vector<String> deps = target.getDependencies();
7572     for (unsigned int i=0 ; i<deps.size() ; i++)
7573         {
7574         String dep = deps[i];
7575         //Did we do it already?  Skip
7576         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7577             continue;
7578             
7579         std::map<String, Target> &tgts =
7580                target.getParent().getTargets();
7581         std::map<String, Target>::iterator iter =
7582                tgts.find(dep);
7583         if (iter == tgts.end())
7584             {
7585             error("Target '%s' dependency '%s' not found",
7586                       name.c_str(),  dep.c_str());
7587             return false;
7588             }
7589         Target depTarget = iter->second;
7590         if (!executeTarget(depTarget, targetsCompleted))
7591             {
7592             return false;
7593             }
7594         }
7596     status("## Target : %s", name.c_str());
7598     //Now let's do the tasks
7599     std::vector<Task *> &tasks = target.getTasks();
7600     for (unsigned int i=0 ; i<tasks.size() ; i++)
7601         {
7602         Task *task = tasks[i];
7603         status("---- task : %s", task->getName().c_str());
7604         if (!task->execute())
7605             {
7606             return false;
7607             }
7608         }
7609         
7610     targetsCompleted.insert(name);
7611     
7612     return true;
7617 /**
7618  *  Main execute() method.  Start here and work
7619  *  up the dependency tree 
7620  */
7621 bool Make::execute()
7623     status("######## EXECUTE");
7625     //Determine initial target
7626     if (specifiedTarget.size()>0)
7627         {
7628         currentTarget = specifiedTarget;
7629         }
7630     else if (defaultTarget.size()>0)
7631         {
7632         currentTarget = defaultTarget;
7633         }
7634     else
7635         {
7636         error("execute: no specified or default target requested");
7637         return false;
7638         }
7640     std::map<String, Target>::iterator iter =
7641                    targets.find(currentTarget);
7642     if (iter == targets.end())
7643         {
7644         error("Initial target '%s' not found",
7645                          currentTarget.c_str());
7646         return false;
7647         }
7648         
7649     //Now run
7650     Target target = iter->second;
7651     std::set<String> targetsCompleted;
7652     if (!executeTarget(target, targetsCompleted))
7653         {
7654         return false;
7655         }
7657     status("######## EXECUTE COMPLETE");
7658     return true;
7664 /**
7665  *
7666  */
7667 bool Make::checkTargetDependencies(Target &target, 
7668                             std::vector<String> &depList)
7670     String tgtName = target.getName().c_str();
7671     depList.push_back(tgtName);
7673     std::vector<String> deps = target.getDependencies();
7674     for (unsigned int i=0 ; i<deps.size() ; i++)
7675         {
7676         String dep = deps[i];
7677         //First thing entered was the starting Target
7678         if (dep == depList[0])
7679             {
7680             error("Circular dependency '%s' found at '%s'",
7681                       dep.c_str(), tgtName.c_str());
7682             std::vector<String>::iterator diter;
7683             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7684                 {
7685                 error("  %s", diter->c_str());
7686                 }
7687             return false;
7688             }
7690         std::map<String, Target> &tgts =
7691                   target.getParent().getTargets();
7692         std::map<String, Target>::iterator titer = tgts.find(dep);
7693         if (titer == tgts.end())
7694             {
7695             error("Target '%s' dependency '%s' not found",
7696                       tgtName.c_str(), dep.c_str());
7697             return false;
7698             }
7699         if (!checkTargetDependencies(titer->second, depList))
7700             {
7701             return false;
7702             }
7703         }
7704     return true;
7711 static int getword(int pos, const String &inbuf, String &result)
7713     int p = pos;
7714     int len = (int)inbuf.size();
7715     String val;
7716     while (p < len)
7717         {
7718         char ch = inbuf[p];
7719         if (!isalnum(ch) && ch!='.' && ch!='_')
7720             break;
7721         val.push_back(ch);
7722         p++;
7723         }
7724     result = val;
7725     return p;
7731 /**
7732  *
7733  */
7734 bool Make::parsePropertyFile(const String &fileName,
7735                              const String &prefix)
7737     FILE *f = fopen(fileName.c_str(), "r");
7738     if (!f)
7739         {
7740         error("could not open property file %s", fileName.c_str());
7741         return false;
7742         }
7743     int linenr = 0;
7744     while (!feof(f))
7745         {
7746         char buf[256];
7747         if (!fgets(buf, 255, f))
7748             break;
7749         linenr++;
7750         String s = buf;
7751         s = trim(s);
7752         int len = s.size();
7753         if (len == 0)
7754             continue;
7755         if (s[0] == '#')
7756             continue;
7757         String key;
7758         String val;
7759         int p = 0;
7760         int p2 = getword(p, s, key);
7761         if (p2 <= p)
7762             {
7763             error("property file %s, line %d: expected keyword",
7764                                 fileName.c_str(), linenr);
7765                         return false;
7766                         }
7767                 if (prefix.size() > 0)
7768                     {
7769                     key.insert(0, prefix);
7770                     }
7772         //skip whitespace
7773                 for (p=p2 ; p<len ; p++)
7774                     if (!isspace(s[p]))
7775                         break;
7777         if (p>=len || s[p]!='=')
7778             {
7779             error("property file %s, line %d: expected '='",
7780                                 fileName.c_str(), linenr);
7781             return false;
7782             }
7783         p++;
7785         //skip whitespace
7786                 for ( ; p<len ; p++)
7787                     if (!isspace(s[p]))
7788                         break;
7790         /* This way expects a word after the =
7791                 p2 = getword(p, s, val);
7792         if (p2 <= p)
7793             {
7794             error("property file %s, line %d: expected value",
7795                                 fileName.c_str(), linenr);
7796                         return false;
7797                         }
7798                 */
7799         // This way gets the rest of the line after the =
7800                 if (p>=len)
7801             {
7802             error("property file %s, line %d: expected value",
7803                                 fileName.c_str(), linenr);
7804                         return false;
7805                         }
7806         val = s.substr(p);
7807                 if (key.size()==0 || val.size()==0)
7808                     continue;
7810         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7811         //See if we wanted to overload this property
7812         std::map<String, String>::iterator iter =
7813             specifiedProperties.find(key);
7814         if (iter!=specifiedProperties.end())
7815             {
7816             val = iter->second;
7817             status("overloading property '%s' = '%s'",
7818                    key.c_str(), val.c_str());
7819             }
7820             properties[key] = val;
7821         }
7822     fclose(f);
7823     return true;
7829 /**
7830  *
7831  */
7832 bool Make::parseProperty(Element *elem)
7834     std::vector<Attribute> &attrs = elem->getAttributes();
7835     for (unsigned int i=0 ; i<attrs.size() ; i++)
7836         {
7837         String attrName = attrs[i].getName();
7838         String attrVal  = attrs[i].getValue();
7840         if (attrName == "name")
7841             {
7842             String val;
7843                         if (!getAttribute(elem, "value", val))
7844                             return false;
7845             if (val.size() > 0)
7846                 {
7847                 properties[attrVal] = val;
7848                 }
7849             else
7850                 {
7851                 if (!getAttribute(elem, "location", val))
7852                     return false;
7853                 if (val.size() > 0)
7854                     {
7855                     properties[attrVal] = val;
7856                     }
7857                 }
7858             //See if we wanted to overload this property
7859             std::map<String, String>::iterator iter =
7860                 specifiedProperties.find(attrVal);
7861             if (iter != specifiedProperties.end())
7862                 {
7863                 val = iter->second;
7864                 status("overloading property '%s' = '%s'",
7865                     attrVal.c_str(), val.c_str());
7866                 properties[attrVal] = val;
7867                 }
7868             }
7869         else if (attrName == "file")
7870             {
7871             String prefix;
7872                         if (!getAttribute(elem, "prefix", prefix))
7873                             return false;
7874             if (prefix.size() > 0)
7875                 {
7876                 if (prefix[prefix.size()-1] != '.')
7877                     prefix.push_back('.');
7878                 }
7879             if (!parsePropertyFile(attrName, prefix))
7880                 return false;
7881             }
7882         else if (attrName == "environment")
7883             {
7884             if (envAlias.size() > 0)
7885                 {
7886                 error("environment property can only be set once");
7887                 return false;
7888                 }
7889             envAlias = attrVal;
7890             }
7891         }
7893     return true;
7899 /**
7900  *
7901  */
7902 bool Make::parseFile()
7904     status("######## PARSE : %s", uri.getPath().c_str());
7906     Parser parser;
7907     Element *root = parser.parseFile(uri.getNativePath());
7908     if (!root)
7909         {
7910         error("Could not open %s for reading",
7911                       uri.getNativePath().c_str());
7912         return false;
7913         }
7915     if (root->getChildren().size()==0 ||
7916         root->getChildren()[0]->getName()!="project")
7917         {
7918         error("Main xml element should be <project>");
7919         delete root;
7920         return false;
7921         }
7923     //########## Project attributes
7924     Element *project = root->getChildren()[0];
7925     String s = project->getAttribute("name");
7926     if (s.size() > 0)
7927         projectName = s;
7928     s = project->getAttribute("default");
7929     if (s.size() > 0)
7930         defaultTarget = s;
7931     s = project->getAttribute("basedir");
7932     if (s.size() > 0)
7933         baseDir = s;
7935     //######### PARSE MEMBERS
7936     std::vector<Element *> children = project->getChildren();
7937     for (unsigned int i=0 ; i<children.size() ; i++)
7938         {
7939         Element *elem = children[i];
7940         String tagName = elem->getName();
7942         //########## DESCRIPTION
7943         if (tagName == "description")
7944             {
7945             description = parser.trim(elem->getValue());
7946             }
7948         //######### PROPERTY
7949         else if (tagName == "property")
7950             {
7951             if (!parseProperty(elem))
7952                 return false;
7953             }
7955         //######### TARGET
7956         else if (tagName == "target")
7957             {
7958             String tname   = elem->getAttribute("name");
7959             String tdesc   = elem->getAttribute("description");
7960             String tdeps   = elem->getAttribute("depends");
7961             String tif     = elem->getAttribute("if");
7962             String tunless = elem->getAttribute("unless");
7963             Target target(*this);
7964             target.setName(tname);
7965             target.setDescription(tdesc);
7966             target.parseDependencies(tdeps);
7967             target.setIf(tif);
7968             target.setUnless(tunless);
7969             std::vector<Element *> telems = elem->getChildren();
7970             for (unsigned int i=0 ; i<telems.size() ; i++)
7971                 {
7972                 Element *telem = telems[i];
7973                 Task breeder(*this);
7974                 Task *task = breeder.createTask(telem);
7975                 if (!task)
7976                     return false;
7977                 allTasks.push_back(task);
7978                 target.addTask(task);
7979                 }
7981             //Check name
7982             if (tname.size() == 0)
7983                 {
7984                 error("no name for target");
7985                 return false;
7986                 }
7987             //Check for duplicate name
7988             if (targets.find(tname) != targets.end())
7989                 {
7990                 error("target '%s' already defined", tname.c_str());
7991                 return false;
7992                 }
7993             //more work than targets[tname]=target, but avoids default allocator
7994             targets.insert(std::make_pair<String, Target>(tname, target));
7995             }
7997         }
7999     std::map<String, Target>::iterator iter;
8000     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8001         {
8002         Target tgt = iter->second;
8003         std::vector<String> depList;
8004         if (!checkTargetDependencies(tgt, depList))
8005             {
8006             return false;
8007             }
8008         }
8011     delete root;
8012     status("######## PARSE COMPLETE");
8013     return true;
8017 /**
8018  * Overload a <property>
8019  */
8020 bool Make::specifyProperty(const String &name, const String &value)
8022     if (specifiedProperties.find(name) != specifiedProperties.end())
8023         {
8024         error("Property %s already specified", name.c_str());
8025         return false;
8026         }
8027     specifiedProperties[name] = value;
8028     return true;
8033 /**
8034  *
8035  */
8036 bool Make::run()
8038     if (!parseFile())
8039         return false;
8040         
8041     if (!execute())
8042         return false;
8044     return true;
8048 /**
8049  *
8050  */
8051 bool Make::run(const String &target)
8053     status("##################################");
8054     status("#   BuildTool");
8055     status("#   version 0.5");
8056     status("#   21 Nov 06");
8057     status("##################################");
8058     specifiedTarget = target;
8059     if (!run())
8060         return false;
8061     status("##################################");
8062     status("#   BuildTool Completed");
8063     status("##################################");
8064     return true;
8073 }// namespace buildtool
8074 //########################################################################
8075 //# M A I N
8076 //########################################################################
8078 typedef buildtool::String String;
8080 /**
8081  *  Format an error message in printf() style
8082  */
8083 static void error(char *fmt, ...)
8085     va_list ap;
8086     va_start(ap, fmt);
8087     fprintf(stderr, "BuildTool error: ");
8088     vfprintf(stderr, fmt, ap);
8089     fprintf(stderr, "\n");
8090     va_end(ap);
8094 static bool parseProperty(const String &s, String &name, String &val)
8096     int len = s.size();
8097     int i;
8098     for (i=0 ; i<len ; i++)
8099         {
8100         char ch = s[i];
8101         if (ch == '=')
8102             break;
8103         name.push_back(ch);
8104         }
8105     if (i>=len || s[i]!='=')
8106         {
8107         error("property requires -Dname=value");
8108         return false;
8109         }
8110     i++;
8111     for ( ; i<len ; i++)
8112         {
8113         char ch = s[i];
8114         val.push_back(ch);
8115         }
8116     return true;
8120 /**
8121  * Compare a buffer with a key, for the length of the key
8122  */
8123 static bool sequ(const String &buf, char *key)
8125     int len = buf.size();
8126     for (int i=0 ; key[i] && i<len ; i++)
8127         {
8128         if (key[i] != buf[i])
8129             return false;
8130         }        
8131     return true;
8134 static void usage(int argc, char **argv)
8136     printf("usage:\n");
8137     printf("   %s [options] [target]\n", argv[0]);
8138     printf("Options:\n");
8139     printf("  -help, -h              print this message\n");
8140     printf("  -version               print the version information and exit\n");
8141     printf("  -file <file>           use given buildfile\n");
8142     printf("  -f <file>                 ''\n");
8143     printf("  -D<property>=<value>   use value for given property\n");
8149 /**
8150  * Parse the command-line args, get our options,
8151  * and run this thing
8152  */   
8153 static bool parseOptions(int argc, char **argv)
8155     if (argc < 1)
8156         {
8157         error("Cannot parse arguments");
8158         return false;
8159         }
8161     buildtool::Make make;
8163     String target;
8165     //char *progName = argv[0];
8166     for (int i=1 ; i<argc ; i++)
8167         {
8168         String arg = argv[i];
8169         if (arg.size()>1 && arg[0]=='-')
8170             {
8171             if (arg == "-h" || arg == "-help")
8172                 {
8173                 usage(argc,argv);
8174                 return true;
8175                 }
8176             else if (arg == "-version")
8177                 {
8178                 printf("%s", make.version().c_str());
8179                 return true;
8180                 }
8181             else if (arg == "-f" || arg == "-file")
8182                 {
8183                 if (i>=argc)
8184                    {
8185                    usage(argc, argv);
8186                    return false;
8187                    }
8188                 i++; //eat option
8189                 make.setURI(argv[i]);
8190                 }
8191             else if (arg.size()>2 && sequ(arg, "-D"))
8192                 {
8193                 String s = arg.substr(2, s.size());
8194                 String name, value;
8195                 if (!parseProperty(s, name, value))
8196                    {
8197                    usage(argc, argv);
8198                    return false;
8199                    }
8200                 if (!make.specifyProperty(name, value))
8201                     return false;
8202                 }
8203             else
8204                 {
8205                 error("Unknown option:%s", arg.c_str());
8206                 return false;
8207                 }
8208             }
8209         else
8210             {
8211             if (target.size()>0)
8212                 {
8213                 error("only one initial target");
8214                 usage(argc, argv);
8215                 return false;
8216                 }
8217             target = arg;
8218             }
8219         }
8221     //We have the options.  Now execute them
8222     if (!make.run(target))
8223         return false;
8225     return true;
8231 /*
8232 static bool runMake()
8234     buildtool::Make make;
8235     if (!make.run())
8236         return false;
8237     return true;
8241 static bool pkgConfigTest()
8243     buildtool::PkgConfig pkgConfig;
8244     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8245         return false;
8246     return true;
8251 static bool depTest()
8253     buildtool::DepTool deptool;
8254     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8255     if (!deptool.generateDependencies("build.dep"))
8256         return false;
8257     std::vector<buildtool::DepRec> res =
8258                deptool.loadDepFile("build.dep");
8259         if (res.size() == 0)
8260         return false;
8261     return true;
8264 static bool popenTest()
8266     buildtool::Make make;
8267     buildtool::String out, err;
8268         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8269     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8270     return true;
8274 static bool propFileTest()
8276     buildtool::Make make;
8277     make.parsePropertyFile("test.prop", "test.");
8278     return true;
8280 */
8282 int main(int argc, char **argv)
8285     if (!parseOptions(argc, argv))
8286         return 1;
8287     /*
8288     if (!popenTest())
8289         return 1;
8291     if (!depTest())
8292         return 1;
8293     if (!propFileTest())
8294         return 1;
8295     if (runMake())
8296         return 1;
8297     */
8298     return 0;
8302 //########################################################################
8303 //# E N D 
8304 //########################################################################