Code

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