Code

25636f1547c87a2f97083c6c2f720dd2c42e08f9
[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;
4976     
4978 };
4984 /**
4985  *  Clean up after processing.  Called by the destructor, but should
4986  *  also be called before the object is reused.
4987  */
4988 void DepTool::init()
4990     sourceDir = ".";
4992     fileList.clear();
4993     directories.clear();
4994     
4995     //clear refs
4996     depFiles.clear();
4997     //clear records
4998     std::map<String, FileRec *>::iterator iter;
4999     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5000          delete iter->second;
5002     allFiles.clear(); 
5009 /**
5010  *  Parse a full path name into path, base name, and suffix
5011  */
5012 void DepTool::parseName(const String &fullname,
5013                         String &path,
5014                         String &basename,
5015                         String &suffix)
5017     if (fullname.size() < 2)
5018         return;
5020     unsigned int pos = fullname.find_last_of('/');
5021     if (pos != fullname.npos && pos<fullname.size()-1)
5022         {
5023         path = fullname.substr(0, pos);
5024         pos++;
5025         basename = fullname.substr(pos, fullname.size()-pos);
5026         }
5027     else
5028         {
5029         path = "";
5030         basename = fullname;
5031         }
5033     pos = basename.find_last_of('.');
5034     if (pos != basename.npos && pos<basename.size()-1)
5035         {
5036         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5037         basename = basename.substr(0, pos);
5038         }
5040     //trace("parsename:%s %s %s", path.c_str(),
5041     //        basename.c_str(), suffix.c_str()); 
5046 /**
5047  *  Generate our internal file list.
5048  */
5049 bool DepTool::createFileList()
5052     for (unsigned int i=0 ; i<fileList.size() ; i++)
5053         {
5054         String fileName = fileList[i];
5055         //trace("## FileName:%s", fileName.c_str());
5056         String path;
5057         String basename;
5058         String sfx;
5059         parseName(fileName, path, basename, sfx);
5060         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5061                     sfx == "cc" || sfx == "CC")
5062             {
5063             FileRec *fe         = new FileRec(FileRec::CFILE);
5064             fe->path            = path;
5065             fe->baseName        = basename;
5066             fe->suffix          = sfx;
5067             allFiles[fileName]  = fe;
5068             }
5069         else if (sfx == "h"   ||  sfx == "hh"  ||
5070                  sfx == "hpp" ||  sfx == "hxx")
5071             {
5072             FileRec *fe         = new FileRec(FileRec::HFILE);
5073             fe->path            = path;
5074             fe->baseName        = basename;
5075             fe->suffix          = sfx;
5076             allFiles[fileName]  = fe;
5077             }
5078         }
5080     if (!listDirectories(sourceDir, "", directories))
5081         return false;
5082         
5083     return true;
5090 /**
5091  * Get a character from the buffer at pos.  If out of range,
5092  * return -1 for safety
5093  */
5094 int DepTool::get(int pos)
5096     if (pos>depFileSize)
5097         return -1;
5098     return depFileBuf[pos];
5103 /**
5104  *  Skip over all whitespace characters beginning at pos.  Return
5105  *  the position of the first non-whitespace character.
5106  */
5107 int DepTool::skipwhite(int pos)
5109     while (pos < depFileSize)
5110         {
5111         int ch = get(pos);
5112         if (ch < 0)
5113             break;
5114         if (!isspace(ch))
5115             break;
5116         pos++;
5117         }
5118     return pos;
5122 /**
5123  *  Parse the buffer beginning at pos, for a word.  Fill
5124  *  'ret' with the result.  Return the position after the
5125  *  word.
5126  */
5127 int DepTool::getword(int pos, String &ret)
5129     while (pos < depFileSize)
5130         {
5131         int ch = get(pos);
5132         if (ch < 0)
5133             break;
5134         if (isspace(ch))
5135             break;
5136         ret.push_back((char)ch);
5137         pos++;
5138         }
5139     return pos;
5142 /**
5143  * Return whether the sequence of characters in the buffer
5144  * beginning at pos match the key,  for the length of the key
5145  */
5146 bool DepTool::sequ(int pos, char *key)
5148     while (*key)
5149         {
5150         if (*key != get(pos))
5151             return false;
5152         key++; pos++;
5153         }
5154     return true;
5159 /**
5160  *  Add an include file name to a file record.  If the name
5161  *  is not found in allFiles explicitly, try prepending include
5162  *  directory names to it and try again.
5163  */
5164 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5167     std::map<String, FileRec *>::iterator iter =
5168            allFiles.find(iname);
5169     if (iter != allFiles.end()) //already exists
5170         {
5171          //h file in same dir
5172         FileRec *other = iter->second;
5173         //trace("local: '%s'", iname.c_str());
5174         frec->files[iname] = other;
5175         return true;
5176         }
5177     else 
5178         {
5179         //look in other dirs
5180         std::vector<String>::iterator diter;
5181         for (diter=directories.begin() ;
5182              diter!=directories.end() ; diter++)
5183             {
5184             String dfname = *diter;
5185             dfname.append("/");
5186             dfname.append(iname);
5187             iter = allFiles.find(dfname);
5188             if (iter != allFiles.end())
5189                 {
5190                 FileRec *other = iter->second;
5191                 //trace("other: '%s'", iname.c_str());
5192                 frec->files[dfname] = other;
5193                 return true;
5194                 }
5195             }
5196         }
5197     return true;
5202 /**
5203  *  Lightly parse a file to find the #include directives.  Do
5204  *  a bit of state machine stuff to make sure that the directive
5205  *  is valid.  (Like not in a comment).
5206  */
5207 bool DepTool::scanFile(const String &fname, FileRec *frec)
5209     String fileName;
5210     if (sourceDir.size() > 0)
5211         {
5212         fileName.append(sourceDir);
5213         fileName.append("/");
5214         }
5215     fileName.append(fname);
5216     String nativeName = getNativePath(fileName);
5217     FILE *f = fopen(nativeName.c_str(), "r");
5218     if (!f)
5219         {
5220         error("Could not open '%s' for reading", fname.c_str());
5221         return false;
5222         }
5223     String buf;
5224     while (true)
5225         {
5226         int ch = fgetc(f);
5227         if (ch < 0)
5228             break;
5229         buf.push_back((char)ch);
5230         }
5231     fclose(f);
5233     depFileSize = buf.size();
5234     depFileBuf  = (char *)buf.c_str();
5235     int pos = 0;
5238     while (pos < depFileSize)
5239         {
5240         //trace("p:%c", get(pos));
5242         //# Block comment
5243         if (get(pos) == '/' && get(pos+1) == '*')
5244             {
5245             pos += 2;
5246             while (pos < depFileSize)
5247                 {
5248                 if (get(pos) == '*' && get(pos+1) == '/')
5249                     {
5250                     pos += 2;
5251                     break;
5252                     }
5253                 else
5254                     pos++;
5255                 }
5256             }
5257         //# Line comment
5258         else if (get(pos) == '/' && get(pos+1) == '/')
5259             {
5260             pos += 2;
5261             while (pos < depFileSize)
5262                 {
5263                 if (get(pos) == '\n')
5264                     {
5265                     pos++;
5266                     break;
5267                     }
5268                 else
5269                     pos++;
5270                 }
5271             }
5272         //# #include! yaay
5273         else if (sequ(pos, "#include"))
5274             {
5275             pos += 8;
5276             pos = skipwhite(pos);
5277             String iname;
5278             pos = getword(pos, iname);
5279             if (iname.size()>2)
5280                 {
5281                 iname = iname.substr(1, iname.size()-2);
5282                 addIncludeFile(frec, iname);
5283                 }
5284             }
5285         else
5286             {
5287             pos++;
5288             }
5289         }
5291     return true;
5296 /**
5297  *  Recursively check include lists to find all files in allFiles to which
5298  *  a given file is dependent.
5299  */
5300 bool DepTool::processDependency(FileRec *ofile,
5301                              FileRec *include,
5302                              int depth)
5304     std::map<String, FileRec *>::iterator iter;
5305     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5306         {
5307         String fname  = iter->first;
5308         if (ofile->files.find(fname) != ofile->files.end())
5309             {
5310             //trace("file '%s' already seen", fname.c_str());
5311             continue;
5312             }
5313         FileRec *child  = iter->second;
5314         ofile->files[fname] = child;
5315       
5316         processDependency(ofile, child, depth+1);
5317         }
5320     return true;
5327 /**
5328  *  Generate the file dependency list.
5329  */
5330 bool DepTool::generateDependencies()
5332     std::map<String, FileRec *>::iterator iter;
5333     //# First pass.  Scan for all includes
5334     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5335         {
5336         FileRec *frec = iter->second;
5337         if (!scanFile(iter->first, frec))
5338             {
5339             //quit?
5340             }
5341         }
5343     //# Second pass.  Scan for all includes
5344     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5345         {
5346         FileRec *include = iter->second;
5347         if (include->type == FileRec::CFILE)
5348             {
5349             String cFileName = iter->first;
5350             FileRec *ofile      = new FileRec(FileRec::OFILE);
5351             ofile->path         = include->path;
5352             ofile->baseName     = include->baseName;
5353             ofile->suffix       = include->suffix;
5354             String fname     = include->path;
5355             if (fname.size()>0)
5356                 fname.append("/");
5357             fname.append(include->baseName);
5358             fname.append(".o");
5359             depFiles[fname]    = ofile;
5360             //add the .c file first?   no, don't
5361             //ofile->files[cFileName] = include;
5362             
5363             //trace("ofile:%s", fname.c_str());
5365             processDependency(ofile, include, 0);
5366             }
5367         }
5369       
5370     return true;
5375 /**
5376  *  High-level call to generate deps and optionally save them
5377  */
5378 bool DepTool::generateDependencies(const String &fileName)
5380     if (!createFileList())
5381         return false;
5382     if (!generateDependencies())
5383         return false;
5384     if (!saveDepFile(fileName))
5385         return false;
5386     return true;
5390 /**
5391  *   This saves the dependency cache.
5392  */
5393 bool DepTool::saveDepFile(const String &fileName)
5395     time_t tim;
5396     time(&tim);
5398     FILE *f = fopen(fileName.c_str(), "w");
5399     if (!f)
5400         {
5401         trace("cannot open '%s' for writing", fileName.c_str());
5402         }
5403     fprintf(f, "<?xml version='1.0'?>\n");
5404     fprintf(f, "<!--\n");
5405     fprintf(f, "########################################################\n");
5406     fprintf(f, "## File: build.dep\n");
5407     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5408     fprintf(f, "########################################################\n");
5409     fprintf(f, "-->\n");
5411     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5412     std::map<String, FileRec *>::iterator iter;
5413     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5414         {
5415         FileRec *frec = iter->second;
5416         if (frec->type == FileRec::OFILE)
5417             {
5418             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5419                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5420             std::map<String, FileRec *>::iterator citer;
5421             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5422                 {
5423                 String cfname = citer->first;
5424                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5425                 }
5426             fprintf(f, "</object>\n\n");
5427             }
5428         }
5430     fprintf(f, "</dependencies>\n");
5431     fprintf(f, "\n");
5432     fprintf(f, "<!--\n");
5433     fprintf(f, "########################################################\n");
5434     fprintf(f, "## E N D\n");
5435     fprintf(f, "########################################################\n");
5436     fprintf(f, "-->\n");
5438     fclose(f);
5440     return true;
5446 /**
5447  *   This loads the dependency cache.
5448  */
5449 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5451     std::vector<DepRec> result;
5452     
5453     Parser parser;
5454     Element *root = parser.parseFile(depFile.c_str());
5455     if (!root)
5456         {
5457         //error("Could not open %s for reading", depFile.c_str());
5458         return result;
5459         }
5461     if (root->getChildren().size()==0 ||
5462         root->getChildren()[0]->getName()!="dependencies")
5463         {
5464         error("Main xml element should be <dependencies>");
5465         delete root;
5466         return result;
5467         }
5469     //########## Start parsing
5470     Element *depList = root->getChildren()[0];
5472     std::vector<Element *> objects = depList->getChildren();
5473     for (unsigned int i=0 ; i<objects.size() ; i++)
5474         {
5475         Element *objectElem = objects[i];
5476         String tagName = objectElem->getName();
5477         if (tagName == "object")
5478             {
5479             String objName   = objectElem->getAttribute("name");
5480              //trace("object:%s", objName.c_str());
5481             DepRec depObject(objName);
5482             depObject.path   = objectElem->getAttribute("path");
5483             depObject.suffix = objectElem->getAttribute("suffix");
5484             //########## DESCRIPTION
5485             std::vector<Element *> depElems = objectElem->getChildren();
5486             for (unsigned int i=0 ; i<depElems.size() ; i++)
5487                 {
5488                 Element *depElem = depElems[i];
5489                 tagName = depElem->getName();
5490                 if (tagName == "dep")
5491                     {
5492                     String depName = depElem->getAttribute("name");
5493                     //trace("    dep:%s", depName.c_str());
5494                     depObject.files.push_back(depName);
5495                     }
5496                 }
5497             //Insert into the result list, in a sorted manner
5498             bool inserted = false;
5499             std::vector<DepRec>::iterator iter;
5500             for (iter = result.begin() ; iter != result.end() ; iter++)
5501                 {
5502                 String vpath = iter->path;
5503                 vpath.append("/");
5504                 vpath.append(iter->name);
5505                 String opath = depObject.path;
5506                 opath.append("/");
5507                 opath.append(depObject.name);
5508                 if (vpath > opath)
5509                     {
5510                     inserted = true;
5511                     iter = result.insert(iter, depObject);
5512                     break;
5513                     }
5514                 }
5515             if (!inserted)
5516                 result.push_back(depObject);
5517             }
5518         }
5520     delete root;
5522     return result;
5526 /**
5527  *   This loads the dependency cache.
5528  */
5529 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5530                    bool forceRefresh)
5532     std::vector<DepRec> result;
5533     if (forceRefresh)
5534         {
5535         generateDependencies(depFile);
5536         result = loadDepFile(depFile);
5537         }
5538     else
5539         {
5540         //try once
5541         result = loadDepFile(depFile);
5542         if (result.size() == 0)
5543             {
5544             //fail? try again
5545             generateDependencies(depFile);
5546             result = loadDepFile(depFile);
5547             }
5548         }
5549     return result;
5555 //########################################################################
5556 //# T A S K
5557 //########################################################################
5558 //forward decl
5559 class Target;
5560 class Make;
5562 /**
5563  *
5564  */
5565 class Task : public MakeBase
5568 public:
5570     typedef enum
5571         {
5572         TASK_NONE,
5573         TASK_CC,
5574         TASK_COPY,
5575         TASK_DELETE,
5576         TASK_JAR,
5577         TASK_JAVAC,
5578         TASK_LINK,
5579         TASK_MAKEFILE,
5580         TASK_MKDIR,
5581         TASK_MSGFMT,
5582         TASK_RANLIB,
5583         TASK_RC,
5584         TASK_SHAREDLIB,
5585         TASK_STATICLIB,
5586         TASK_STRIP,
5587         TASK_TSTAMP
5588         } TaskType;
5589         
5591     /**
5592      *
5593      */
5594     Task(MakeBase &par) : parent(par)
5595         { init(); }
5597     /**
5598      *
5599      */
5600     Task(const Task &other) : parent(other.parent)
5601         { init(); assign(other); }
5603     /**
5604      *
5605      */
5606     Task &operator=(const Task &other)
5607         { assign(other); return *this; }
5609     /**
5610      *
5611      */
5612     virtual ~Task()
5613         { }
5616     /**
5617      *
5618      */
5619     virtual MakeBase &getParent()
5620         { return parent; }
5622      /**
5623      *
5624      */
5625     virtual int  getType()
5626         { return type; }
5628     /**
5629      *
5630      */
5631     virtual void setType(int val)
5632         { type = val; }
5634     /**
5635      *
5636      */
5637     virtual String getName()
5638         { return name; }
5640     /**
5641      *
5642      */
5643     virtual bool execute()
5644         { return true; }
5646     /**
5647      *
5648      */
5649     virtual bool parse(Element *elem)
5650         { return true; }
5652     /**
5653      *
5654      */
5655     Task *createTask(Element *elem);
5658 protected:
5660     void init()
5661         {
5662         type = TASK_NONE;
5663         name = "none";
5664         }
5666     void assign(const Task &other)
5667         {
5668         type = other.type;
5669         name = other.name;
5670         }
5671         
5672     String getAttribute(Element *elem, const String &attrName)
5673         {
5674         String str;
5675         return str;
5676         }
5678     MakeBase &parent;
5680     int type;
5682     String name;
5683 };
5687 /**
5688  * This task runs the C/C++ compiler.  The compiler is invoked
5689  * for all .c or .cpp files which are newer than their correcsponding
5690  * .o files.  
5691  */
5692 class TaskCC : public Task
5694 public:
5696     TaskCC(MakeBase &par) : Task(par)
5697         {
5698                 type = TASK_CC; name = "cc";
5699                 ccCommand   = "gcc";
5700                 cxxCommand  = "g++";
5701                 source      = ".";
5702                 dest        = ".";
5703                 flags       = "";
5704                 defines     = "";
5705                 includes    = "";
5706                 fileSet.clear();
5707         }
5709     virtual ~TaskCC()
5710         {}
5712     virtual bool execute()
5713         {
5714         if (!listFiles(parent, fileSet))
5715             return false;
5717         bool refreshCache = false;
5718         String fullName = parent.resolve("build.dep");
5719         if (isNewerThan(parent.getURI().getPath(), fullName))
5720             {
5721             trace("regenerating cache");
5722             refreshCache = true;
5723             }
5725         DepTool depTool;
5726         depTool.setSourceDirectory(source);
5727         depTool.setFileList(fileSet.getFiles());
5728         std::vector<DepRec> deps =
5729              depTool.getDepFile("build.dep", refreshCache);
5730         
5731         String incs;
5732         incs.append("-I");
5733         incs.append(parent.resolve("."));
5734         incs.append(" ");
5735         if (includes.size()>0)
5736             {
5737             incs.append(includes);
5738             incs.append(" ");
5739             }
5740         std::set<String> paths;
5741         std::vector<DepRec>::iterator viter;
5742         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5743             {
5744             DepRec dep = *viter;
5745             if (dep.path.size()>0)
5746                 paths.insert(dep.path);
5747             }
5748         if (source.size()>0)
5749             {
5750             incs.append(" -I");
5751             incs.append(parent.resolve(source));
5752             incs.append(" ");
5753             }
5754         std::set<String>::iterator setIter;
5755         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5756             {
5757             incs.append(" -I");
5758             String dname;
5759             if (source.size()>0)
5760                 {
5761                 dname.append(source);
5762                 dname.append("/");
5763                 }
5764             dname.append(*setIter);
5765             incs.append(parent.resolve(dname));
5766             }
5767         std::vector<String> cfiles;
5768         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5769             {
5770             DepRec dep = *viter;
5772             //## Select command
5773             String sfx = dep.suffix;
5774             String command = ccCommand;
5775             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5776                              || sfx == "CC")
5777                             command = cxxCommand;
5778  
5779             //## Make paths
5780             String destPath = dest;
5781             String srcPath  = source;
5782             if (dep.path.size()>0)
5783                             {
5784                 destPath.append("/");
5785                                 destPath.append(dep.path);
5786                 srcPath.append("/");
5787                                 srcPath.append(dep.path);
5788                             }
5789             //## Make sure destination directory exists
5790                         if (!createDirectory(destPath))
5791                             return false;
5792                             
5793             //## Check whether it needs to be done
5794                         String destFullName = destPath;
5795                         destFullName.append("/");
5796                         destFullName.append(dep.name);
5797                         destFullName.append(".o");
5798                         String srcFullName = srcPath;
5799                         srcFullName.append("/");
5800                         srcFullName.append(dep.name);
5801                         srcFullName.append(".");
5802                         srcFullName.append(dep.suffix);
5803             if (!isNewerThan(srcFullName, destFullName))
5804                 {
5805                 //trace("%s skipped", srcFullName.c_str());
5806                 continue;
5807                 }
5809             //## Assemble the command
5810             String cmd = command;
5811             cmd.append(" -c ");
5812             cmd.append(flags);
5813                         cmd.append(" ");
5814             cmd.append(defines);
5815                         cmd.append(" ");
5816             cmd.append(incs);
5817                         cmd.append(" ");
5818                         cmd.append(srcFullName);
5819             cmd.append(" -o ");
5820                         cmd.append(destFullName);
5822             //## Execute the command
5824             String outString, errString;
5825             if (!executeCommand(cmd.c_str(), "", outString, errString))
5826                 {
5827                 error("problem compiling: %s", errString.c_str());
5828                 return false;
5829                 }
5830             }
5831         
5832         return true;
5833         }
5835     virtual bool parse(Element *elem)
5836         {
5837         String s;
5838         if (!parent.getAttribute(elem, "command", s))
5839             return false;
5840         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5841         if (!parent.getAttribute(elem, "cc", s))
5842             return false;
5843         if (s.size()>0) ccCommand = s;
5844         if (!parent.getAttribute(elem, "cxx", s))
5845             return false;
5846         if (s.size()>0) cxxCommand = s;
5847         if (!parent.getAttribute(elem, "destdir", s))
5848             return false;
5849         if (s.size()>0) dest = s;
5851         std::vector<Element *> children = elem->getChildren();
5852         for (unsigned int i=0 ; i<children.size() ; i++)
5853             {
5854             Element *child = children[i];
5855             String tagName = child->getName();
5856             if (tagName == "flags")
5857                 {
5858                 if (!parent.getValue(child, flags))
5859                     return false;
5860                 flags = strip(flags);
5861                 }
5862             else if (tagName == "includes")
5863                 {
5864                 if (!parent.getValue(child, includes))
5865                     return false;
5866                 includes = strip(includes);
5867                 }
5868             else if (tagName == "defines")
5869                 {
5870                 if (!parent.getValue(child, defines))
5871                     return false;
5872                 defines = strip(defines);
5873                 }
5874             else if (tagName == "fileset")
5875                 {
5876                 if (!parseFileSet(child, parent, fileSet))
5877                     return false;
5878                 source = fileSet.getDirectory();
5879                 }
5880             }
5882         return true;
5883         }
5884         
5885 protected:
5887     String ccCommand;
5888     String cxxCommand;
5889     String source;
5890     String dest;
5891     String flags;
5892     String defines;
5893     String includes;
5894     FileSet fileSet;
5895     
5896 };
5900 /**
5901  *
5902  */
5903 class TaskCopy : public Task
5905 public:
5907     typedef enum
5908         {
5909         CP_NONE,
5910         CP_TOFILE,
5911         CP_TODIR
5912         } CopyType;
5914     TaskCopy(MakeBase &par) : Task(par)
5915         {
5916                 type = TASK_COPY; name = "copy";
5917                 cptype = CP_NONE;
5918                 verbose = false;
5919                 haveFileSet = false;
5920                 }
5922     virtual ~TaskCopy()
5923         {}
5925     virtual bool execute()
5926         {
5927         switch (cptype)
5928            {
5929            case CP_TOFILE:
5930                {
5931                if (fileName.size()>0)
5932                    {
5933                    status("          : %s to %s",
5934                         fileName.c_str(), toFileName.c_str());
5935                    String fullSource = parent.resolve(fileName);
5936                    String fullDest = parent.resolve(toFileName);
5937                    //trace("copy %s to file %s", fullSource.c_str(),
5938                                    //                       fullDest.c_str());
5939                                    if (!isRegularFile(fullSource))
5940                                        {
5941                        error("copy : file %s does not exist", fullSource.c_str());
5942                                        return false;
5943                                        }
5944                    if (!isNewerThan(fullSource, fullDest))
5945                        {
5946                        return true;
5947                        }
5948                    if (!copyFile(fullSource, fullDest))
5949                        return false;
5950                    status("          : 1 file copied");
5951                    }
5952                return true;
5953                }
5954            case CP_TODIR:
5955                {
5956                if (haveFileSet)
5957                    {
5958                    if (!listFiles(parent, fileSet))
5959                        return false;
5960                    String fileSetDir = fileSet.getDirectory();
5962                    status("          : %s to %s",
5963                        fileSetDir.c_str(), toDirName.c_str());
5965                    int nrFiles = 0;
5966                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
5967                        {
5968                        String fileName = fileSet[i];
5970                        String sourcePath;
5971                        if (fileSetDir.size()>0)
5972                            {
5973                            sourcePath.append(fileSetDir);
5974                            sourcePath.append("/");
5975                            }
5976                        sourcePath.append(fileName);
5977                        String fullSource = parent.resolve(sourcePath);
5978                        
5979                        //Get the immediate parent directory's base name
5980                        String baseFileSetDir = fileSetDir;
5981                        unsigned int pos = baseFileSetDir.find_last_of('/');
5982                        if (pos!=baseFileSetDir.npos &&
5983                                                       pos < baseFileSetDir.size()-1)
5984                            baseFileSetDir =
5985                                                       baseFileSetDir.substr(pos+1,
5986                                                                baseFileSetDir.size());
5987                                            //Now make the new path
5988                        String destPath;
5989                        if (toDirName.size()>0)
5990                            {
5991                            destPath.append(toDirName);
5992                            destPath.append("/");
5993                            }
5994                        if (baseFileSetDir.size()>0)
5995                            {
5996                            destPath.append(baseFileSetDir);
5997                            destPath.append("/");
5998                            }
5999                        destPath.append(fileName);
6000                        String fullDest = parent.resolve(destPath);
6001                        //trace("fileName:%s", fileName.c_str());
6002                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6003                                        //                   fullDest.c_str());
6004                        if (!isNewerThan(fullSource, fullDest))
6005                            {
6006                            //trace("copy skipping %s", fullSource.c_str());
6007                            continue;
6008                            }
6009                        if (!copyFile(fullSource, fullDest))
6010                            return false;
6011                        nrFiles++;
6012                        }
6013                    status("          : %d file(s) copied", nrFiles);
6014                    }
6015                else //file source
6016                    {
6017                    //For file->dir we want only the basename of
6018                    //the source appended to the dest dir
6019                    status("          : %s to %s", 
6020                        fileName.c_str(), toDirName.c_str());
6021                    String baseName = fileName;
6022                    unsigned int pos = baseName.find_last_of('/');
6023                    if (pos!=baseName.npos && pos<baseName.size()-1)
6024                        baseName = baseName.substr(pos+1, baseName.size());
6025                    String fullSource = parent.resolve(fileName);
6026                    String destPath;
6027                    if (toDirName.size()>0)
6028                        {
6029                        destPath.append(toDirName);
6030                        destPath.append("/");
6031                        }
6032                    destPath.append(baseName);
6033                    String fullDest = parent.resolve(destPath);
6034                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6035                                    //                       fullDest.c_str());
6036                                    if (!isRegularFile(fullSource))
6037                                        {
6038                        error("copy : file %s does not exist", fullSource.c_str());
6039                                        return false;
6040                                        }
6041                    if (!isNewerThan(fullSource, fullDest))
6042                        {
6043                        return true;
6044                        }
6045                    if (!copyFile(fullSource, fullDest))
6046                        return false;
6047                    status("          : 1 file copied");
6048                    }
6049                return true;
6050                }
6051            }
6052         return true;
6053         }
6056     virtual bool parse(Element *elem)
6057         {
6058         if (!parent.getAttribute(elem, "file", fileName))
6059             return false;
6060         if (!parent.getAttribute(elem, "tofile", toFileName))
6061             return false;
6062         if (toFileName.size() > 0)
6063             cptype = CP_TOFILE;
6064         if (!parent.getAttribute(elem, "todir", toDirName))
6065             return false;
6066         if (toDirName.size() > 0)
6067             cptype = CP_TODIR;
6068         String ret;
6069         if (!parent.getAttribute(elem, "verbose", ret))
6070             return false;
6071         if (ret.size()>0 && !getBool(ret, verbose))
6072             return false;
6073             
6074         haveFileSet = false;
6075         
6076         std::vector<Element *> children = elem->getChildren();
6077         for (unsigned int i=0 ; i<children.size() ; i++)
6078             {
6079             Element *child = children[i];
6080             String tagName = child->getName();
6081             if (tagName == "fileset")
6082                 {
6083                 if (!parseFileSet(child, parent, fileSet))
6084                     {
6085                     error("problem getting fileset");
6086                                         return false;
6087                                         }
6088                                 haveFileSet = true;
6089                 }
6090             }
6092         //Perform validity checks
6093                 if (fileName.size()>0 && fileSet.size()>0)
6094                     {
6095                     error("<copy> can only have one of : file= and <fileset>");
6096                     return false;
6097                     }
6098         if (toFileName.size()>0 && toDirName.size()>0)
6099             {
6100             error("<copy> can only have one of : tofile= or todir=");
6101             return false;
6102             }
6103         if (haveFileSet && toDirName.size()==0)
6104             {
6105             error("a <copy> task with a <fileset> must have : todir=");
6106             return false;
6107             }
6108                 if (cptype == CP_TOFILE && fileName.size()==0)
6109                     {
6110                     error("<copy> tofile= must be associated with : file=");
6111                     return false;
6112                     }
6113                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6114                     {
6115                     error("<copy> todir= must be associated with : file= or <fileset>");
6116                     return false;
6117                     }
6119         return true;
6120         }
6121         
6122 private:
6124     int cptype;
6125     String fileName;
6126     FileSet fileSet;
6127     String toFileName;
6128     String toDirName;
6129     bool verbose;
6130     bool haveFileSet;
6131 };
6134 /**
6135  *
6136  */
6137 class TaskDelete : public Task
6139 public:
6141     typedef enum
6142         {
6143         DEL_FILE,
6144         DEL_DIR,
6145         DEL_FILESET
6146         } DeleteType;
6148     TaskDelete(MakeBase &par) : Task(par)
6149         { 
6150                   type        = TASK_DELETE;
6151                   name        = "delete";
6152                   delType     = DEL_FILE;
6153           verbose     = false;
6154           quiet       = false;
6155           failOnError = true;
6156                 }
6158     virtual ~TaskDelete()
6159         {}
6161     virtual bool execute()
6162         {
6163         struct stat finfo;
6164         switch (delType)
6165             {
6166             case DEL_FILE:
6167                 {
6168                 status("          : %s", fileName.c_str());
6169                 String fullName = parent.resolve(fileName);
6170                 char *fname = (char *)fullName.c_str();
6171                 //does not exist
6172                 if (stat(fname, &finfo)<0)
6173                     return true;
6174                 //exists but is not a regular file
6175                 if (!S_ISREG(finfo.st_mode))
6176                     {
6177                     error("<delete> failed. '%s' exists and is not a regular file",
6178                           fname);
6179                     return false;
6180                     }
6181                 if (remove(fname)<0)
6182                     {
6183                     error("<delete> failed: %s", strerror(errno));
6184                     return false;
6185                     }
6186                 return true;
6187                 }
6188             case DEL_DIR:
6189                 {
6190                 status("          : %s", dirName.c_str());
6191                 String fullDir = parent.resolve(dirName);
6192                 if (!removeDirectory(fullDir))
6193                     return false;
6194                 return true;
6195                 }
6196             }
6197         return true;
6198         }
6200     virtual bool parse(Element *elem)
6201         {
6202         if (!parent.getAttribute(elem, "file", fileName))
6203             return false;
6204         if (fileName.size() > 0)
6205             delType = DEL_FILE;
6206         if (!parent.getAttribute(elem, "dir", dirName))
6207             return false;
6208         if (dirName.size() > 0)
6209             delType = DEL_DIR;
6210         if (fileName.size()>0 && dirName.size()>0)
6211             {
6212             error("<delete> can only have one attribute of file= or dir=");
6213             return false;
6214             }
6215         String ret;
6216         if (!parent.getAttribute(elem, "verbose", ret))
6217             return false;
6218         if (ret.size()>0 && !getBool(ret, verbose))
6219             return false;
6220         if (!parent.getAttribute(elem, "quiet", ret))
6221             return false;
6222         if (ret.size()>0 && !getBool(ret, quiet))
6223             return false;
6224         if (!parent.getAttribute(elem, "failonerror", ret))
6225             return false;
6226         if (ret.size()>0 && !getBool(ret, failOnError))
6227             return false;
6228         return true;
6229         }
6231 private:
6233     int delType;
6234     String dirName;
6235     String fileName;
6236     bool verbose;
6237     bool quiet;
6238     bool failOnError;
6239 };
6242 /**
6243  *
6244  */
6245 class TaskJar : public Task
6247 public:
6249     TaskJar(MakeBase &par) : Task(par)
6250         { type = TASK_JAR; name = "jar"; }
6252     virtual ~TaskJar()
6253         {}
6255     virtual bool execute()
6256         {
6257         return true;
6258         }
6260     virtual bool parse(Element *elem)
6261         {
6262         return true;
6263         }
6264 };
6267 /**
6268  *
6269  */
6270 class TaskJavac : public Task
6272 public:
6274     TaskJavac(MakeBase &par) : Task(par)
6275         { type = TASK_JAVAC; name = "javac"; }
6277     virtual ~TaskJavac()
6278         {}
6280     virtual bool execute()
6281         {
6282         return true;
6283         }
6285     virtual bool parse(Element *elem)
6286         {
6287         return true;
6288         }
6289 };
6292 /**
6293  *
6294  */
6295 class TaskLink : public Task
6297 public:
6299     TaskLink(MakeBase &par) : Task(par)
6300         {
6301                 type = TASK_LINK; name = "link";
6302                 command = "g++";
6303                 }
6305     virtual ~TaskLink()
6306         {}
6308     virtual bool execute()
6309         {
6310         if (!listFiles(parent, fileSet))
6311             return false;
6312         String fileSetDir = fileSet.getDirectory();
6313         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6314         bool doit = false;
6315         String fullTarget = parent.resolve(fileName);
6316         String cmd = command;
6317         cmd.append(" -o ");
6318         cmd.append(fullTarget);
6319         cmd.append(" ");
6320         cmd.append(flags);
6321         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6322             {
6323             cmd.append(" ");
6324             String obj;
6325             if (fileSetDir.size()>0)
6326                             {
6327                                 obj.append(fileSetDir);
6328                 obj.append("/");
6329                 }
6330             obj.append(fileSet[i]);
6331             String fullObj = parent.resolve(obj);
6332             cmd.append(fullObj);
6333             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6334             //          fullObj.c_str());
6335             if (isNewerThan(fullObj, fullTarget))
6336                 doit = true;
6337             }
6338         cmd.append(" ");
6339         cmd.append(libs);
6340         if (!doit)
6341             {
6342             //trace("link not needed");
6343             return true;
6344             }
6345         //trace("LINK cmd:%s", cmd.c_str());
6348         String outString, errString;
6349         if (!executeCommand(cmd.c_str(), "", outString, errString))
6350             {
6351             error("LINK problem: %s", errString.c_str());
6352             return false;
6353             }
6354         return true;
6355         }
6357     virtual bool parse(Element *elem)
6358         {
6359         String s;
6360         if (!parent.getAttribute(elem, "command", s))
6361             return false;
6362         if (s.size()>0)
6363             command = s;
6364         if (!parent.getAttribute(elem, "out", fileName))
6365             return false;
6366             
6367         std::vector<Element *> children = elem->getChildren();
6368         for (unsigned int i=0 ; i<children.size() ; i++)
6369             {
6370             Element *child = children[i];
6371             String tagName = child->getName();
6372             if (tagName == "fileset")
6373                 {
6374                 if (!parseFileSet(child, parent, fileSet))
6375                     return false;
6376                 }
6377             else if (tagName == "flags")
6378                 {
6379                 if (!parent.getValue(child, flags))
6380                     return false;
6381                 flags = strip(flags);
6382                 }
6383             else if (tagName == "libs")
6384                 {
6385                 if (!parent.getValue(child, libs))
6386                     return false;
6387                 libs = strip(libs);
6388                 }
6389             }
6390         return true;
6391         }
6393 private:
6395     String command;
6396     String fileName;
6397     String flags;
6398     String libs;
6399     FileSet fileSet;
6401 };
6405 /**
6406  * Create a named directory
6407  */
6408 class TaskMakeFile : public Task
6410 public:
6412     TaskMakeFile(MakeBase &par) : Task(par)
6413         { type = TASK_MAKEFILE; name = "makefile"; }
6415     virtual ~TaskMakeFile()
6416         {}
6418     virtual bool execute()
6419         {
6420         status("          : %s", fileName.c_str());
6421         String fullName = parent.resolve(fileName);
6422         if (!isNewerThan(parent.getURI().getPath(), fullName))
6423             {
6424             //trace("skipped <makefile>");
6425             return true;
6426             }
6427         //trace("fullName:%s", fullName.c_str());
6428         FILE *f = fopen(fullName.c_str(), "w");
6429         if (!f)
6430             {
6431             error("<makefile> could not open %s for writing : %s",
6432                 fullName.c_str(), strerror(errno));
6433             return false;
6434             }
6435         for (unsigned int i=0 ; i<text.size() ; i++)
6436             fputc(text[i], f);
6437         fputc('\n', f);
6438         fclose(f);
6439         return true;
6440         }
6442     virtual bool parse(Element *elem)
6443         {
6444         if (!parent.getAttribute(elem, "file", fileName))
6445             return false;
6446         if (fileName.size() == 0)
6447             {
6448             error("<makefile> requires 'file=\"filename\"' attribute");
6449             return false;
6450             }
6451         if (!parent.getValue(elem, text))
6452             return false;
6453         text = leftJustify(text);
6454         //trace("dirname:%s", dirName.c_str());
6455         return true;
6456         }
6458 private:
6460     String fileName;
6461     String text;
6462 };
6466 /**
6467  * Create a named directory
6468  */
6469 class TaskMkDir : public Task
6471 public:
6473     TaskMkDir(MakeBase &par) : Task(par)
6474         { type = TASK_MKDIR; name = "mkdir"; }
6476     virtual ~TaskMkDir()
6477         {}
6479     virtual bool execute()
6480         {
6481         status("          : %s", dirName.c_str());
6482         String fullDir = parent.resolve(dirName);
6483         //trace("fullDir:%s", fullDir.c_str());
6484         if (!createDirectory(fullDir))
6485             return false;
6486         return true;
6487         }
6489     virtual bool parse(Element *elem)
6490         {
6491         if (!parent.getAttribute(elem, "dir", dirName))
6492             return false;
6493         if (dirName.size() == 0)
6494             {
6495             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6496             return false;
6497             }
6498         return true;
6499         }
6501 private:
6503     String dirName;
6504 };
6508 /**
6509  * Create a named directory
6510  */
6511 class TaskMsgFmt: public Task
6513 public:
6515     TaskMsgFmt(MakeBase &par) : Task(par)
6516          {
6517                  type = TASK_MSGFMT;
6518                  name = "msgfmt";
6519                  command = "msgfmt";
6520                  }
6522     virtual ~TaskMsgFmt()
6523         {}
6525     virtual bool execute()
6526         {
6527         if (!listFiles(parent, fileSet))
6528             return false;
6529         String fileSetDir = fileSet.getDirectory();
6531         //trace("msgfmt: %d", fileSet.size());
6532         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6533             {
6534             String fileName = fileSet[i];
6535             if (getSuffix(fileName) != "po")
6536                 continue;
6537             String sourcePath;
6538                         if (fileSetDir.size()>0)
6539                             {
6540                             sourcePath.append(fileSetDir);
6541                 sourcePath.append("/");
6542                 }
6543             sourcePath.append(fileName);
6544             String fullSource = parent.resolve(sourcePath);
6546             String destPath;
6547                         if (toDirName.size()>0)
6548                             {
6549                             destPath.append(toDirName);
6550                 destPath.append("/");
6551                 }
6552             destPath.append(fileName);
6553             destPath[destPath.size()-2] = 'm';
6554             String fullDest = parent.resolve(destPath);
6556             if (!isNewerThan(fullSource, fullDest))
6557                 {
6558                 //trace("skip %s", fullSource.c_str());
6559                 continue;
6560                 }
6561                 
6562             String cmd = command;
6563             cmd.append(" ");
6564             cmd.append(fullSource);
6565             cmd.append(" -o ");
6566             cmd.append(fullDest);
6567             
6568             int pos = fullDest.find_last_of('/');
6569             if (pos>0)
6570                 {
6571                 String fullDestPath = fullDest.substr(0, pos);
6572                 if (!createDirectory(fullDestPath))
6573                     return false;
6574                 }
6578             String outString, errString;
6579             if (!executeCommand(cmd.c_str(), "", outString, errString))
6580                 {
6581                 error("<msgfmt> problem: %s", errString.c_str());
6582                 return false;
6583                 }
6584             }
6586         return true;
6587         }
6589     virtual bool parse(Element *elem)
6590         {
6591         if (!parent.getAttribute(elem, "todir", toDirName))
6592             return false;
6593             
6594         std::vector<Element *> children = elem->getChildren();
6595         for (unsigned int i=0 ; i<children.size() ; i++)
6596             {
6597             Element *child = children[i];
6598             String tagName = child->getName();
6599             if (tagName == "fileset")
6600                 {
6601                 if (!parseFileSet(child, parent, fileSet))
6602                     return false;
6603                 }
6604             }
6605         return true;
6606         }
6608 private:
6610     String command;
6611     String toDirName;
6612     FileSet fileSet;
6614 };
6620 /**
6621  *  Process an archive to allow random access
6622  */
6623 class TaskRanlib : public Task
6625 public:
6627     TaskRanlib(MakeBase &par) : Task(par)
6628         { type = TASK_RANLIB; name = "ranlib"; }
6630     virtual ~TaskRanlib()
6631         {}
6633     virtual bool execute()
6634         {
6635         String fullName = parent.resolve(fileName);
6636         //trace("fullDir:%s", fullDir.c_str());
6637         String cmd = "ranlib ";
6638         cmd.append(fullName);
6639         String outbuf, errbuf;
6640         if (!executeCommand(cmd, "", outbuf, errbuf))
6641             return false;
6642         return true;
6643         }
6645     virtual bool parse(Element *elem)
6646         {
6647         if (!parent.getAttribute(elem, "file", fileName))
6648             return false;
6649         if (fileName.size() == 0)
6650             {
6651             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6652             return false;
6653             }
6654         return true;
6655         }
6657 private:
6659     String fileName;
6660 };
6664 /**
6665  * Run the "ar" command to archive .o's into a .a
6666  */
6667 class TaskRC : public Task
6669 public:
6671     TaskRC(MakeBase &par) : Task(par)
6672         {
6673                 type = TASK_RC; name = "rc";
6674                 command = "windres -o";
6675                 }
6677     virtual ~TaskRC()
6678         {}
6680     virtual bool execute()
6681         {
6682         String fullFile = parent.resolve(fileName);
6683         String fullOut  = parent.resolve(outName);
6684         if (!isNewerThan(fullFile, fullOut))
6685             return true;
6686         String cmd = command;
6687         cmd.append(" ");
6688         cmd.append(fullOut);
6689         cmd.append(" ");
6690         cmd.append(flags);
6691         cmd.append(" ");
6692         cmd.append(fullFile);
6694         String outString, errString;
6695         if (!executeCommand(cmd.c_str(), "", outString, errString))
6696             {
6697             error("RC problem: %s", errString.c_str());
6698             return false;
6699             }
6700         return true;
6701         }
6703     virtual bool parse(Element *elem)
6704         {
6705         if (!parent.getAttribute(elem, "command", command))
6706             return false;
6707         if (!parent.getAttribute(elem, "file", fileName))
6708             return false;
6709         if (!parent.getAttribute(elem, "out", outName))
6710             return false;
6711         std::vector<Element *> children = elem->getChildren();
6712         for (unsigned int i=0 ; i<children.size() ; i++)
6713             {
6714             Element *child = children[i];
6715             String tagName = child->getName();
6716             if (tagName == "flags")
6717                 {
6718                 if (!parent.getValue(child, flags))
6719                     return false;
6720                 }
6721             }
6722         return true;
6723         }
6725 private:
6727     String command;
6728     String flags;
6729     String fileName;
6730     String outName;
6732 };
6736 /**
6737  *  Collect .o's into a .so or DLL
6738  */
6739 class TaskSharedLib : public Task
6741 public:
6743     TaskSharedLib(MakeBase &par) : Task(par)
6744         {
6745                 type = TASK_SHAREDLIB; name = "dll";
6746                 command = "ar crv";
6747                 }
6749     virtual ~TaskSharedLib()
6750         {}
6752     virtual bool execute()
6753         {
6754         //trace("###########HERE %d", fileSet.size());
6755         bool doit = false;
6756         
6757         String fullOut = parent.resolve(fileName);
6758         //trace("ar fullout: %s", fullOut.c_str());
6759         
6760         if (!listFiles(parent, fileSet))
6761             return false;
6762         String fileSetDir = fileSet.getDirectory();
6764         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6765             {
6766             String fname;
6767                         if (fileSetDir.size()>0)
6768                             {
6769                             fname.append(fileSetDir);
6770                 fname.append("/");
6771                 }
6772             fname.append(fileSet[i]);
6773             String fullName = parent.resolve(fname);
6774             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6775             if (isNewerThan(fullName, fullOut))
6776                 doit = true;
6777             }
6778         //trace("Needs it:%d", doit);
6779         if (!doit)
6780             {
6781             return true;
6782             }
6784         String cmd = "dllwrap";
6785         cmd.append(" -o ");
6786         cmd.append(fullOut);
6787         if (defFileName.size()>0)
6788             {
6789             cmd.append(" --def ");
6790             cmd.append(defFileName);
6791             cmd.append(" ");
6792             }
6793         if (impFileName.size()>0)
6794             {
6795             cmd.append(" --implib ");
6796             cmd.append(impFileName);
6797             cmd.append(" ");
6798             }
6799         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6800             {
6801             String fname;
6802                         if (fileSetDir.size()>0)
6803                             {
6804                             fname.append(fileSetDir);
6805                 fname.append("/");
6806                 }
6807             fname.append(fileSet[i]);
6808             String fullName = parent.resolve(fname);
6810             cmd.append(" ");
6811             cmd.append(fullName);
6812             }
6813         cmd.append(" ");
6814         cmd.append(libs);
6816         String outString, errString;
6817         if (!executeCommand(cmd.c_str(), "", outString, errString))
6818             {
6819             error("<sharedlib> problem: %s", errString.c_str());
6820             return false;
6821             }
6823         return true;
6824         }
6826     virtual bool parse(Element *elem)
6827         {
6828         if (!parent.getAttribute(elem, "file", fileName))
6829             return false;
6830         if (!parent.getAttribute(elem, "import", impFileName))
6831             return false;
6832         if (!parent.getAttribute(elem, "def", defFileName))
6833             return false;
6834             
6835         std::vector<Element *> children = elem->getChildren();
6836         for (unsigned int i=0 ; i<children.size() ; i++)
6837             {
6838             Element *child = children[i];
6839             String tagName = child->getName();
6840             if (tagName == "fileset")
6841                 {
6842                 if (!parseFileSet(child, parent, fileSet))
6843                     return false;
6844                 }
6845             else if (tagName == "libs")
6846                 {
6847                 if (!parent.getValue(child, libs))
6848                     return false;
6849                 libs = strip(libs);
6850                 }
6851             }
6852         return true;
6853         }
6855 private:
6857     String command;
6858     String fileName;
6859     String defFileName;
6860     String impFileName;
6861     FileSet fileSet;
6862     String libs;
6864 };
6867 /**
6868  * Run the "ar" command to archive .o's into a .a
6869  */
6870 class TaskStaticLib : public Task
6872 public:
6874     TaskStaticLib(MakeBase &par) : Task(par)
6875         {
6876                 type = TASK_STATICLIB; name = "staticlib";
6877                 command = "ar crv";
6878                 }
6880     virtual ~TaskStaticLib()
6881         {}
6883     virtual bool execute()
6884         {
6885         //trace("###########HERE %d", fileSet.size());
6886         bool doit = false;
6887         
6888         String fullOut = parent.resolve(fileName);
6889         //trace("ar fullout: %s", fullOut.c_str());
6890         
6891         if (!listFiles(parent, fileSet))
6892             return false;
6893         String fileSetDir = fileSet.getDirectory();
6895         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6896             {
6897             String fname;
6898                         if (fileSetDir.size()>0)
6899                             {
6900                             fname.append(fileSetDir);
6901                 fname.append("/");
6902                 }
6903             fname.append(fileSet[i]);
6904             String fullName = parent.resolve(fname);
6905             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6906             if (isNewerThan(fullName, fullOut))
6907                 doit = true;
6908             }
6909         //trace("Needs it:%d", doit);
6910         if (!doit)
6911             {
6912             return true;
6913             }
6915         String cmd = command;
6916         cmd.append(" ");
6917         cmd.append(fullOut);
6918         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6919             {
6920             String fname;
6921                         if (fileSetDir.size()>0)
6922                             {
6923                             fname.append(fileSetDir);
6924                 fname.append("/");
6925                 }
6926             fname.append(fileSet[i]);
6927             String fullName = parent.resolve(fname);
6929             cmd.append(" ");
6930             cmd.append(fullName);
6931             }
6933         String outString, errString;
6934         if (!executeCommand(cmd.c_str(), "", outString, errString))
6935             {
6936             error("<staticlib> problem: %s", errString.c_str());
6937             return false;
6938             }
6940         return true;
6941         }
6943     virtual bool parse(Element *elem)
6944         {
6945         if (!parent.getAttribute(elem, "file", fileName))
6946             return false;
6947             
6948         std::vector<Element *> children = elem->getChildren();
6949         for (unsigned int i=0 ; i<children.size() ; i++)
6950             {
6951             Element *child = children[i];
6952             String tagName = child->getName();
6953             if (tagName == "fileset")
6954                 {
6955                 if (!parseFileSet(child, parent, fileSet))
6956                     return false;
6957                 }
6958             }
6959         return true;
6960         }
6962 private:
6964     String command;
6965     String fileName;
6966     FileSet fileSet;
6968 };
6971 /**
6972  * Strip an executable
6973  */
6974 class TaskStrip : public Task
6976 public:
6978     TaskStrip(MakeBase &par) : Task(par)
6979         { type = TASK_STRIP; name = "strip"; }
6981     virtual ~TaskStrip()
6982         {}
6984     virtual bool execute()
6985         {
6986         String fullName = parent.resolve(fileName);
6987         //trace("fullDir:%s", fullDir.c_str());
6988         String cmd = "strip ";
6989         cmd.append(fullName);
6991         String outbuf, errbuf;
6992         if (!executeCommand(cmd, "", outbuf, errbuf))
6993             return false;
6994         return true;
6995         }
6997     virtual bool parse(Element *elem)
6998         {
6999         if (!parent.getAttribute(elem, "file", fileName))
7000             return false;
7001         if (fileName.size() == 0)
7002             {
7003             error("<strip> requires 'file=\"fileNname\"' attribute");
7004             return false;
7005             }
7006         return true;
7007         }
7009 private:
7011     String fileName;
7012 };
7015 /**
7016  *
7017  */
7018 class TaskTstamp : public Task
7020 public:
7022     TaskTstamp(MakeBase &par) : Task(par)
7023         { type = TASK_TSTAMP; name = "tstamp"; }
7025     virtual ~TaskTstamp()
7026         {}
7028     virtual bool execute()
7029         {
7030         return true;
7031         }
7033     virtual bool parse(Element *elem)
7034         {
7035         //trace("tstamp parse");
7036         return true;
7037         }
7038 };
7042 /**
7043  *
7044  */
7045 Task *Task::createTask(Element *elem)
7047     String tagName = elem->getName();
7048     //trace("task:%s", tagName.c_str());
7049     Task *task = NULL;
7050     if (tagName == "cc")
7051         task = new TaskCC(parent);
7052     else if (tagName == "copy")
7053         task = new TaskCopy(parent);
7054     else if (tagName == "delete")
7055         task = new TaskDelete(parent);
7056     else if (tagName == "jar")
7057         task = new TaskJar(parent);
7058     else if (tagName == "javac")
7059         task = new TaskJavac(parent);
7060     else if (tagName == "link")
7061         task = new TaskLink(parent);
7062     else if (tagName == "makefile")
7063         task = new TaskMakeFile(parent);
7064     else if (tagName == "mkdir")
7065         task = new TaskMkDir(parent);
7066     else if (tagName == "msgfmt")
7067         task = new TaskMsgFmt(parent);
7068     else if (tagName == "ranlib")
7069         task = new TaskRanlib(parent);
7070     else if (tagName == "rc")
7071         task = new TaskRC(parent);
7072     else if (tagName == "sharedlib")
7073         task = new TaskSharedLib(parent);
7074     else if (tagName == "staticlib")
7075         task = new TaskStaticLib(parent);
7076     else if (tagName == "strip")
7077         task = new TaskStrip(parent);
7078     else if (tagName == "tstamp")
7079         task = new TaskTstamp(parent);
7080     else
7081         {
7082         error("Unknown task '%s'", tagName.c_str());
7083         return NULL;
7084         }
7086     if (!task->parse(elem))
7087         {
7088         delete task;
7089         return NULL;
7090         }
7091     return task;
7096 //########################################################################
7097 //# T A R G E T
7098 //########################################################################
7100 /**
7101  *
7102  */
7103 class Target : public MakeBase
7106 public:
7108     /**
7109      *
7110      */
7111     Target(Make &par) : parent(par)
7112         { init(); }
7114     /**
7115      *
7116      */
7117     Target(const Target &other) : parent(other.parent)
7118         { init(); assign(other); }
7120     /**
7121      *
7122      */
7123     Target &operator=(const Target &other)
7124         { init(); assign(other); return *this; }
7126     /**
7127      *
7128      */
7129     virtual ~Target()
7130         { cleanup() ; }
7133     /**
7134      *
7135      */
7136     virtual Make &getParent()
7137         { return parent; }
7139     /**
7140      *
7141      */
7142     virtual String getName()
7143         { return name; }
7145     /**
7146      *
7147      */
7148     virtual void setName(const String &val)
7149         { name = val; }
7151     /**
7152      *
7153      */
7154     virtual String getDescription()
7155         { return description; }
7157     /**
7158      *
7159      */
7160     virtual void setDescription(const String &val)
7161         { description = val; }
7163     /**
7164      *
7165      */
7166     virtual void addDependency(const String &val)
7167         { deps.push_back(val); }
7169     /**
7170      *
7171      */
7172     virtual void parseDependencies(const String &val)
7173         { deps = tokenize(val, ", "); }
7175     /**
7176      *
7177      */
7178     virtual std::vector<String> &getDependencies()
7179         { return deps; }
7181     /**
7182      *
7183      */
7184     virtual String getIf()
7185         { return ifVar; }
7187     /**
7188      *
7189      */
7190     virtual void setIf(const String &val)
7191         { ifVar = val; }
7193     /**
7194      *
7195      */
7196     virtual String getUnless()
7197         { return unlessVar; }
7199     /**
7200      *
7201      */
7202     virtual void setUnless(const String &val)
7203         { unlessVar = val; }
7205     /**
7206      *
7207      */
7208     virtual void addTask(Task *val)
7209         { tasks.push_back(val); }
7211     /**
7212      *
7213      */
7214     virtual std::vector<Task *> &getTasks()
7215         { return tasks; }
7217 private:
7219     void init()
7220         {
7221         }
7223     void cleanup()
7224         {
7225         tasks.clear();
7226         }
7228     void assign(const Target &other)
7229         {
7230         //parent      = other.parent;
7231         name        = other.name;
7232         description = other.description;
7233         ifVar       = other.ifVar;
7234         unlessVar   = other.unlessVar;
7235         deps        = other.deps;
7236         tasks       = other.tasks;
7237         }
7239     Make &parent;
7241     String name;
7243     String description;
7245     String ifVar;
7247     String unlessVar;
7249     std::vector<String> deps;
7251     std::vector<Task *> tasks;
7253 };
7262 //########################################################################
7263 //# M A K E
7264 //########################################################################
7267 /**
7268  *
7269  */
7270 class Make : public MakeBase
7273 public:
7275     /**
7276      *
7277      */
7278     Make()
7279         { init(); }
7281     /**
7282      *
7283      */
7284     Make(const Make &other)
7285         { assign(other); }
7287     /**
7288      *
7289      */
7290     Make &operator=(const Make &other)
7291         { assign(other); return *this; }
7293     /**
7294      *
7295      */
7296     virtual ~Make()
7297         { cleanup(); }
7299     /**
7300      *
7301      */
7302     virtual std::map<String, Target> &getTargets()
7303         { return targets; }
7306     /**
7307      *
7308      */
7309     virtual String version()
7310         { return "BuildTool v0.3, 2006 Bob Jamison"; }
7312     /**
7313      * Overload a <property>
7314      */
7315     virtual bool specifyProperty(const String &name,
7316                                      const String &value);
7318     /**
7319      *
7320      */
7321     virtual bool run();
7323     /**
7324      *
7325      */
7326     virtual bool run(const String &target);
7330 private:
7332     /**
7333      *
7334      */
7335     void init();
7337     /**
7338      *
7339      */
7340     void cleanup();
7342     /**
7343      *
7344      */
7345     void assign(const Make &other);
7347     /**
7348      *
7349      */
7350     bool executeTask(Task &task);
7353     /**
7354      *
7355      */
7356     bool executeTarget(Target &target,
7357                  std::set<String> &targetsCompleted);
7360     /**
7361      *
7362      */
7363     bool execute();
7365     /**
7366      *
7367      */
7368     bool checkTargetDependencies(Target &prop,
7369                     std::vector<String> &depList);
7371     /**
7372      *
7373      */
7374     bool parsePropertyFile(const String &fileName,
7375                                const String &prefix);
7377     /**
7378      *
7379      */
7380     bool parseProperty(Element *elem);
7382     /**
7383      *
7384      */
7385     bool parseTask(Task &task, Element *elem);
7387     /**
7388      *
7389      */
7390     bool parseFile();
7392     /**
7393      *
7394      */
7395     std::vector<String> glob(const String &pattern);
7398     //###############
7399     //# Fields
7400     //###############
7402     String projectName;
7404     String currentTarget;
7406     String defaultTarget;
7408     String specifiedTarget;
7410     String baseDir;
7412     String description;
7413     
7414     String envAlias;
7416     //std::vector<Property> properties;
7417     
7418     std::map<String, Target> targets;
7420     std::vector<Task *> allTasks;
7421     
7422     std::map<String, String> specifiedProperties;
7424 };
7427 //########################################################################
7428 //# C L A S S  M A I N T E N A N C E
7429 //########################################################################
7431 /**
7432  *
7433  */
7434 void Make::init()
7436     uri             = "build.xml";
7437     projectName     = "";
7438     currentTarget   = "";
7439     defaultTarget   = "";
7440     specifiedTarget = "";
7441     baseDir         = "";
7442     description     = "";
7443     envAlias        = "";
7444     properties.clear();
7445     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7446         delete allTasks[i];
7447     allTasks.clear();
7452 /**
7453  *
7454  */
7455 void Make::cleanup()
7457     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7458         delete allTasks[i];
7459     allTasks.clear();
7464 /**
7465  *
7466  */
7467 void Make::assign(const Make &other)
7469     uri              = other.uri;
7470     projectName      = other.projectName;
7471     currentTarget    = other.currentTarget;
7472     defaultTarget    = other.defaultTarget;
7473     specifiedTarget  = other.specifiedTarget;
7474     baseDir          = other.baseDir;
7475     description      = other.description;
7476     properties       = other.properties;
7481 //########################################################################
7482 //# U T I L I T Y    T A S K S
7483 //########################################################################
7485 /**
7486  *  Perform a file globbing
7487  */
7488 std::vector<String> Make::glob(const String &pattern)
7490     std::vector<String> res;
7491     return res;
7495 //########################################################################
7496 //# P U B L I C    A P I
7497 //########################################################################
7501 /**
7502  *
7503  */
7504 bool Make::executeTarget(Target &target,
7505              std::set<String> &targetsCompleted)
7508     String name = target.getName();
7510     //First get any dependencies for this target
7511     std::vector<String> deps = target.getDependencies();
7512     for (unsigned int i=0 ; i<deps.size() ; i++)
7513         {
7514         String dep = deps[i];
7515         //Did we do it already?  Skip
7516         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7517             continue;
7518             
7519         std::map<String, Target> &tgts =
7520                target.getParent().getTargets();
7521         std::map<String, Target>::iterator iter =
7522                tgts.find(dep);
7523         if (iter == tgts.end())
7524             {
7525             error("Target '%s' dependency '%s' not found",
7526                       name.c_str(),  dep.c_str());
7527             return false;
7528             }
7529         Target depTarget = iter->second;
7530         if (!executeTarget(depTarget, targetsCompleted))
7531             {
7532             return false;
7533             }
7534         }
7536     status("## Target : %s", name.c_str());
7538     //Now let's do the tasks
7539     std::vector<Task *> &tasks = target.getTasks();
7540     for (unsigned int i=0 ; i<tasks.size() ; i++)
7541         {
7542         Task *task = tasks[i];
7543         status("---- task : %s", task->getName().c_str());
7544         if (!task->execute())
7545             {
7546             return false;
7547             }
7548         }
7549         
7550     targetsCompleted.insert(name);
7551     
7552     return true;
7557 /**
7558  *  Main execute() method.  Start here and work
7559  *  up the dependency tree 
7560  */
7561 bool Make::execute()
7563     status("######## EXECUTE");
7565     //Determine initial target
7566     if (specifiedTarget.size()>0)
7567         {
7568         currentTarget = specifiedTarget;
7569         }
7570     else if (defaultTarget.size()>0)
7571         {
7572         currentTarget = defaultTarget;
7573         }
7574     else
7575         {
7576         error("execute: no specified or default target requested");
7577         return false;
7578         }
7580     std::map<String, Target>::iterator iter =
7581                    targets.find(currentTarget);
7582     if (iter == targets.end())
7583         {
7584         error("Initial target '%s' not found",
7585                          currentTarget.c_str());
7586         return false;
7587         }
7588         
7589     //Now run
7590     Target target = iter->second;
7591     std::set<String> targetsCompleted;
7592     if (!executeTarget(target, targetsCompleted))
7593         {
7594         return false;
7595         }
7597     status("######## EXECUTE COMPLETE");
7598     return true;
7604 /**
7605  *
7606  */
7607 bool Make::checkTargetDependencies(Target &target, 
7608                             std::vector<String> &depList)
7610     String tgtName = target.getName().c_str();
7611     depList.push_back(tgtName);
7613     std::vector<String> deps = target.getDependencies();
7614     for (unsigned int i=0 ; i<deps.size() ; i++)
7615         {
7616         String dep = deps[i];
7617         //First thing entered was the starting Target
7618         if (dep == depList[0])
7619             {
7620             error("Circular dependency '%s' found at '%s'",
7621                       dep.c_str(), tgtName.c_str());
7622             std::vector<String>::iterator diter;
7623             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7624                 {
7625                 error("  %s", diter->c_str());
7626                 }
7627             return false;
7628             }
7630         std::map<String, Target> &tgts =
7631                   target.getParent().getTargets();
7632         std::map<String, Target>::iterator titer = tgts.find(dep);
7633         if (titer == tgts.end())
7634             {
7635             error("Target '%s' dependency '%s' not found",
7636                       tgtName.c_str(), dep.c_str());
7637             return false;
7638             }
7639         if (!checkTargetDependencies(titer->second, depList))
7640             {
7641             return false;
7642             }
7643         }
7644     return true;
7651 static int getword(int pos, const String &inbuf, String &result)
7653     int p = pos;
7654     int len = (int)inbuf.size();
7655     String val;
7656     while (p < len)
7657         {
7658         char ch = inbuf[p];
7659         if (!isalnum(ch) && ch!='.' && ch!='_')
7660             break;
7661         val.push_back(ch);
7662         p++;
7663         }
7664     result = val;
7665     return p;
7671 /**
7672  *
7673  */
7674 bool Make::parsePropertyFile(const String &fileName,
7675                              const String &prefix)
7677     FILE *f = fopen(fileName.c_str(), "r");
7678     if (!f)
7679         {
7680         error("could not open property file %s", fileName.c_str());
7681         return false;
7682         }
7683     int linenr = 0;
7684     while (!feof(f))
7685         {
7686         char buf[256];
7687         if (!fgets(buf, 255, f))
7688             break;
7689         linenr++;
7690         String s = buf;
7691         s = trim(s);
7692         int len = s.size();
7693         if (len == 0)
7694             continue;
7695         if (s[0] == '#')
7696             continue;
7697         String key;
7698         String val;
7699         int p = 0;
7700         int p2 = getword(p, s, key);
7701         if (p2 <= p)
7702             {
7703             error("property file %s, line %d: expected keyword",
7704                                 fileName.c_str(), linenr);
7705                         return false;
7706                         }
7707                 if (prefix.size() > 0)
7708                     {
7709                     key.insert(0, prefix);
7710                     }
7712         //skip whitespace
7713                 for (p=p2 ; p<len ; p++)
7714                     if (!isspace(s[p]))
7715                         break;
7717         if (p>=len || s[p]!='=')
7718             {
7719             error("property file %s, line %d: expected '='",
7720                                 fileName.c_str(), linenr);
7721             return false;
7722             }
7723         p++;
7725         //skip whitespace
7726                 for ( ; p<len ; p++)
7727                     if (!isspace(s[p]))
7728                         break;
7730         /* This way expects a word after the =
7731                 p2 = getword(p, s, val);
7732         if (p2 <= p)
7733             {
7734             error("property file %s, line %d: expected value",
7735                                 fileName.c_str(), linenr);
7736                         return false;
7737                         }
7738                 */
7739         // This way gets the rest of the line after the =
7740                 if (p>=len)
7741             {
7742             error("property file %s, line %d: expected value",
7743                                 fileName.c_str(), linenr);
7744                         return false;
7745                         }
7746         val = s.substr(p);
7747                 if (key.size()==0 || val.size()==0)
7748                     continue;
7750         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7751         //See if we wanted to overload this property
7752         std::map<String, String>::iterator iter =
7753             specifiedProperties.find(key);
7754         if (iter!=specifiedProperties.end())
7755             {
7756             val = iter->second;
7757             status("overloading property '%s' = '%s'",
7758                    key.c_str(), val.c_str());
7759             }
7760             properties[key] = val;
7761         }
7762     fclose(f);
7763     return true;
7769 /**
7770  *
7771  */
7772 bool Make::parseProperty(Element *elem)
7774     std::vector<Attribute> &attrs = elem->getAttributes();
7775     for (unsigned int i=0 ; i<attrs.size() ; i++)
7776         {
7777         String attrName = attrs[i].getName();
7778         String attrVal  = attrs[i].getValue();
7780         if (attrName == "name")
7781             {
7782             String val;
7783                         if (!getAttribute(elem, "value", val))
7784                             return false;
7785             if (val.size() > 0)
7786                 {
7787                 properties[attrVal] = val;
7788                 }
7789             else
7790                 {
7791                 if (!getAttribute(elem, "location", val))
7792                     return false;
7793                 if (val.size() > 0)
7794                     {
7795                     properties[attrVal] = val;
7796                     }
7797                 }
7798             //See if we wanted to overload this property
7799             std::map<String, String>::iterator iter =
7800                 specifiedProperties.find(attrVal);
7801             if (iter != specifiedProperties.end())
7802                 {
7803                 val = iter->second;
7804                 status("overloading property '%s' = '%s'",
7805                     attrName.c_str(), val.c_str());
7806                 properties[attrVal] = val;
7807                 }
7808             }
7809         else if (attrName == "file")
7810             {
7811             String prefix;
7812                         if (!getAttribute(elem, "prefix", prefix))
7813                             return false;
7814             if (prefix.size() > 0)
7815                 {
7816                 if (prefix[prefix.size()-1] != '.')
7817                     prefix.push_back('.');
7818                 }
7819             if (!parsePropertyFile(attrName, prefix))
7820                 return false;
7821             }
7822         else if (attrName == "environment")
7823             {
7824             if (envAlias.size() > 0)
7825                 {
7826                 error("environment property can only be set once");
7827                 return false;
7828                 }
7829             envAlias = attrVal;
7830             }
7831         }
7833     return true;
7839 /**
7840  *
7841  */
7842 bool Make::parseFile()
7844     status("######## PARSE : %s", uri.getPath().c_str());
7846     Parser parser;
7847     Element *root = parser.parseFile(uri.getNativePath());
7848     if (!root)
7849         {
7850         error("Could not open %s for reading",
7851                       uri.getNativePath().c_str());
7852         return false;
7853         }
7855     if (root->getChildren().size()==0 ||
7856         root->getChildren()[0]->getName()!="project")
7857         {
7858         error("Main xml element should be <project>");
7859         delete root;
7860         return false;
7861         }
7863     //########## Project attributes
7864     Element *project = root->getChildren()[0];
7865     String s = project->getAttribute("name");
7866     if (s.size() > 0)
7867         projectName = s;
7868     s = project->getAttribute("default");
7869     if (s.size() > 0)
7870         defaultTarget = s;
7871     s = project->getAttribute("basedir");
7872     if (s.size() > 0)
7873         baseDir = s;
7875     //######### PARSE MEMBERS
7876     std::vector<Element *> children = project->getChildren();
7877     for (unsigned int i=0 ; i<children.size() ; i++)
7878         {
7879         Element *elem = children[i];
7880         String tagName = elem->getName();
7882         //########## DESCRIPTION
7883         if (tagName == "description")
7884             {
7885             description = parser.trim(elem->getValue());
7886             }
7888         //######### PROPERTY
7889         else if (tagName == "property")
7890             {
7891             if (!parseProperty(elem))
7892                 return false;
7893             }
7895         //######### TARGET
7896         else if (tagName == "target")
7897             {
7898             String tname   = elem->getAttribute("name");
7899             String tdesc   = elem->getAttribute("description");
7900             String tdeps   = elem->getAttribute("depends");
7901             String tif     = elem->getAttribute("if");
7902             String tunless = elem->getAttribute("unless");
7903             Target target(*this);
7904             target.setName(tname);
7905             target.setDescription(tdesc);
7906             target.parseDependencies(tdeps);
7907             target.setIf(tif);
7908             target.setUnless(tunless);
7909             std::vector<Element *> telems = elem->getChildren();
7910             for (unsigned int i=0 ; i<telems.size() ; i++)
7911                 {
7912                 Element *telem = telems[i];
7913                 Task breeder(*this);
7914                 Task *task = breeder.createTask(telem);
7915                 if (!task)
7916                     return false;
7917                 allTasks.push_back(task);
7918                 target.addTask(task);
7919                 }
7921             //Check name
7922             if (tname.size() == 0)
7923                 {
7924                 error("no name for target");
7925                 return false;
7926                 }
7927             //Check for duplicate name
7928             if (targets.find(tname) != targets.end())
7929                 {
7930                 error("target '%s' already defined", tname.c_str());
7931                 return false;
7932                 }
7933             //more work than targets[tname]=target, but avoids default allocator
7934             targets.insert(std::make_pair<String, Target>(tname, target));
7935             }
7937         }
7939     std::map<String, Target>::iterator iter;
7940     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
7941         {
7942         Target tgt = iter->second;
7943         std::vector<String> depList;
7944         if (!checkTargetDependencies(tgt, depList))
7945             {
7946             return false;
7947             }
7948         }
7951     delete root;
7952     status("######## PARSE COMPLETE");
7953     return true;
7957 /**
7958  * Overload a <property>
7959  */
7960 bool Make::specifyProperty(const String &name, const String &value)
7962     if (specifiedProperties.find(name) != specifiedProperties.end())
7963         {
7964         error("Property %s already specified", name.c_str());
7965         return false;
7966         }
7967     specifiedProperties[name] = value;
7968     return true;
7973 /**
7974  *
7975  */
7976 bool Make::run()
7978     if (!parseFile())
7979         return false;
7980         
7981     if (!execute())
7982         return false;
7984     return true;
7988 /**
7989  *
7990  */
7991 bool Make::run(const String &target)
7993     status("##################################");
7994     status("#   BuildTool");
7995     status("#   version 0.4");
7996     status("#   17 Nov 06");
7997     status("##################################");
7998     specifiedTarget = target;
7999     if (!run())
8000         return false;
8001     status("##################################");
8002     status("#   BuildTool Completed");
8003     status("##################################");
8004     return true;
8013 }// namespace buildtool
8014 //########################################################################
8015 //# M A I N
8016 //########################################################################
8018 typedef buildtool::String String;
8020 /**
8021  *  Format an error message in printf() style
8022  */
8023 static void error(char *fmt, ...)
8025     va_list ap;
8026     va_start(ap, fmt);
8027     fprintf(stderr, "BuildTool error: ");
8028     vfprintf(stderr, fmt, ap);
8029     fprintf(stderr, "\n");
8030     va_end(ap);
8034 static bool parseProperty(const String &s, String &name, String &val)
8036     int len = s.size();
8037     int i;
8038     for (i=0 ; i<len ; i++)
8039         {
8040         char ch = s[i];
8041         if (ch == '=')
8042             break;
8043         name.push_back(ch);
8044         }
8045     if (i>=len || s[i]!='=')
8046         {
8047         error("property requires -Dname=value");
8048         return false;
8049         }
8050     i++;
8051     for ( ; i<len ; i++)
8052         {
8053         char ch = s[i];
8054         val.push_back(ch);
8055         }
8056     return true;
8060 /**
8061  * Compare a buffer with a key, for the length of the key
8062  */
8063 static bool sequ(const String &buf, char *key)
8065     int len = buf.size();
8066     for (int i=0 ; key[i] && i<len ; i++)
8067         {
8068         if (key[i] != buf[i])
8069             return false;
8070         }        
8071     return true;
8074 static void usage(int argc, char **argv)
8076     printf("usage:\n");
8077     printf("   %s [options] [target]\n", argv[0]);
8078     printf("Options:\n");
8079     printf("  -help, -h              print this message\n");
8080     printf("  -version               print the version information and exit\n");
8081     printf("  -file <file>           use given buildfile\n");
8082     printf("  -f <file>                 ''\n");
8083     printf("  -D<property>=<value>   use value for given property\n");
8089 /**
8090  * Parse the command-line args, get our options,
8091  * and run this thing
8092  */   
8093 static bool parseOptions(int argc, char **argv)
8095     if (argc < 1)
8096         {
8097         error("Cannot parse arguments");
8098         return false;
8099         }
8101     buildtool::Make make;
8103     String target;
8105     //char *progName = argv[0];
8106     for (int i=1 ; i<argc ; i++)
8107         {
8108         String arg = argv[i];
8109         if (arg.size()>1 && arg[0]=='-')
8110             {
8111             if (arg == "-h" || arg == "-help")
8112                 {
8113                 usage(argc,argv);
8114                 return true;
8115                 }
8116             else if (arg == "-version")
8117                 {
8118                 printf("%s", make.version().c_str());
8119                 return true;
8120                 }
8121             else if (arg == "-f" || arg == "-file")
8122                 {
8123                 if (i>=argc)
8124                    {
8125                    usage(argc, argv);
8126                    return false;
8127                    }
8128                 i++; //eat option
8129                 make.setURI(argv[i]);
8130                 }
8131             else if (arg.size()>2 && sequ(arg, "-D"))
8132                 {
8133                 String s = arg.substr(2, s.size());
8134                 String name, value;
8135                 if (!parseProperty(s, name, value))
8136                    {
8137                    usage(argc, argv);
8138                    return false;
8139                    }
8140                 if (!make.specifyProperty(name, value))
8141                     return false;
8142                 }
8143             else
8144                 {
8145                 error("Unknown option:%s", arg.c_str());
8146                 return false;
8147                 }
8148             }
8149         else
8150             {
8151             if (target.size()>0)
8152                 {
8153                 error("only one initial target");
8154                 usage(argc, argv);
8155                 return false;
8156                 }
8157             target = arg;
8158             }
8159         }
8161     //We have the options.  Now execute them
8162     if (!make.run(target))
8163         return false;
8165     return true;
8171 /*
8172 static bool runMake()
8174     buildtool::Make make;
8175     if (!make.run())
8176         return false;
8177     return true;
8181 static bool pkgConfigTest()
8183     buildtool::PkgConfig pkgConfig;
8184     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8185         return false;
8186     return true;
8191 static bool depTest()
8193     buildtool::DepTool deptool;
8194     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8195     if (!deptool.generateDependencies("build.dep"))
8196         return false;
8197     std::vector<buildtool::DepRec> res =
8198                deptool.loadDepFile("build.dep");
8199         if (res.size() == 0)
8200         return false;
8201     return true;
8204 static bool popenTest()
8206     buildtool::Make make;
8207     buildtool::String out, err;
8208         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8209     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8210     return true;
8214 static bool propFileTest()
8216     buildtool::Make make;
8217     make.parsePropertyFile("test.prop", "test.");
8218     return true;
8220 */
8222 int main(int argc, char **argv)
8225     if (!parseOptions(argc, argv))
8226         return 1;
8227     /*
8228     if (!popenTest())
8229         return 1;
8231     if (!depTest())
8232         return 1;
8233     if (!propFileTest())
8234         return 1;
8235     if (runMake())
8236         return 1;
8237     */
8238     return 0;
8242 //########################################################################
8243 //# E N D 
8244 //########################################################################