Code

ef80f872aaed951b61d18fd8621350a4832eafdf
[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 btool.exe
28  * (or whatever your compiler might be) 
29  * Then
30  * btool
31  * or 
32  * btool {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
55 #include <errno.h>
60 namespace buildtool
61 {
65 //########################################################################
66 //########################################################################
67 //##  R E G E X P
68 //########################################################################
69 //########################################################################
71 /**
72  * This is the T-Rex regular expression library, which we
73  * gratefully acknowledge.  It's clean code and small size allow
74  * us to embed it in BuildTool without adding a dependency
75  *
76  */    
78 //begin trex.h
80 #ifndef _TREX_H_
81 #define _TREX_H_
82 /***************************************************************
83         T-Rex a tiny regular expression library
85         Copyright (C) 2003-2006 Alberto Demichelis
87         This software is provided 'as-is', without any express 
88         or implied warranty. In no event will the authors be held 
89         liable for any damages arising from the use of this software.
91         Permission is granted to anyone to use this software for 
92         any purpose, including commercial applications, and to alter
93         it and redistribute it freely, subject to the following restrictions:
95                 1. The origin of this software must not be misrepresented;
96                 you must not claim that you wrote the original software.
97                 If you use this software in a product, an acknowledgment
98                 in the product documentation would be appreciated but
99                 is not required.
101                 2. Altered source versions must be plainly marked as such,
102                 and must not be misrepresented as being the original software.
104                 3. This notice may not be removed or altered from any
105                 source distribution.
107 ****************************************************************/
109 #ifdef _UNICODE
110 #define TRexChar unsigned short
111 #define MAX_CHAR 0xFFFF
112 #define _TREXC(c) L##c 
113 #define trex_strlen wcslen
114 #define trex_printf wprintf
115 #else
116 #define TRexChar char
117 #define MAX_CHAR 0xFF
118 #define _TREXC(c) (c) 
119 #define trex_strlen strlen
120 #define trex_printf printf
121 #endif
123 #ifndef TREX_API
124 #define TREX_API extern
125 #endif
127 #define TRex_True 1
128 #define TRex_False 0
130 typedef unsigned int TRexBool;
131 typedef struct TRex TRex;
133 typedef struct {
134         const TRexChar *begin;
135         int len;
136 } TRexMatch;
138 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
139 TREX_API void trex_free(TRex *exp);
140 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
141 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
142 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
143 TREX_API int trex_getsubexpcount(TRex* exp);
144 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
146 #endif
148 //end trex.h
150 //start trex.c
153 #include <stdio.h>
154 #include <string>
156 /* see copyright notice in trex.h */
157 #include <string.h>
158 #include <stdlib.h>
159 #include <ctype.h>
160 #include <setjmp.h>
161 //#include "trex.h"
163 #ifdef _UINCODE
164 #define scisprint iswprint
165 #define scstrlen wcslen
166 #define scprintf wprintf
167 #define _SC(x) L(x)
168 #else
169 #define scisprint isprint
170 #define scstrlen strlen
171 #define scprintf printf
172 #define _SC(x) (x)
173 #endif
175 #ifdef _DEBUG
176 #include <stdio.h>
178 static const TRexChar *g_nnames[] =
180         _SC("NONE"),_SC("OP_GREEDY"),   _SC("OP_OR"),
181         _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),       _SC("OP_CLASS"),
182         _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
183         _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
184 };
186 #endif
187 #define OP_GREEDY               (MAX_CHAR+1) // * + ? {n}
188 #define OP_OR                   (MAX_CHAR+2)
189 #define OP_EXPR                 (MAX_CHAR+3) //parentesis ()
190 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
191 #define OP_DOT                  (MAX_CHAR+5)
192 #define OP_CLASS                (MAX_CHAR+6)
193 #define OP_CCLASS               (MAX_CHAR+7)
194 #define OP_NCLASS               (MAX_CHAR+8) //negates class the [^
195 #define OP_RANGE                (MAX_CHAR+9)
196 #define OP_CHAR                 (MAX_CHAR+10)
197 #define OP_EOL                  (MAX_CHAR+11)
198 #define OP_BOL                  (MAX_CHAR+12)
199 #define OP_WB                   (MAX_CHAR+13)
201 #define TREX_SYMBOL_ANY_CHAR ('.')
202 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
203 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
204 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
205 #define TREX_SYMBOL_BRANCH ('|')
206 #define TREX_SYMBOL_END_OF_STRING ('$')
207 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
208 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
211 typedef int TRexNodeType;
213 typedef struct tagTRexNode{
214         TRexNodeType type;
215         int left;
216         int right;
217         int next;
218 }TRexNode;
220 struct TRex{
221         const TRexChar *_eol;
222         const TRexChar *_bol;
223         const TRexChar *_p;
224         int _first;
225         int _op;
226         TRexNode *_nodes;
227         int _nallocated;
228         int _nsize;
229         int _nsubexpr;
230         TRexMatch *_matches;
231         int _currsubexp;
232         void *_jmpbuf;
233         const TRexChar **_error;
234 };
236 static int trex_list(TRex *exp);
238 static int trex_newnode(TRex *exp, TRexNodeType type)
240         TRexNode n;
241         int newid;
242         n.type = type;
243         n.next = n.right = n.left = -1;
244         if(type == OP_EXPR)
245                 n.right = exp->_nsubexpr++;
246         if(exp->_nallocated < (exp->_nsize + 1)) {
247                 //int oldsize = exp->_nallocated;
248                 exp->_nallocated *= 2;
249                 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
250         }
251         exp->_nodes[exp->_nsize++] = n;
252         newid = exp->_nsize - 1;
253         return (int)newid;
256 static void trex_error(TRex *exp,const TRexChar *error)
258         if(exp->_error) *exp->_error = error;
259         longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
262 static void trex_expect(TRex *exp, int n){
263         if((*exp->_p) != n) 
264                 trex_error(exp, _SC("expected paren"));
265         exp->_p++;
268 static TRexChar trex_escapechar(TRex *exp)
270         if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
271                 exp->_p++;
272                 switch(*exp->_p) {
273                 case 'v': exp->_p++; return '\v';
274                 case 'n': exp->_p++; return '\n';
275                 case 't': exp->_p++; return '\t';
276                 case 'r': exp->_p++; return '\r';
277                 case 'f': exp->_p++; return '\f';
278                 default: return (*exp->_p++);
279                 }
280         } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
281         return (*exp->_p++);
284 static int trex_charclass(TRex *exp,int classid)
286         int n = trex_newnode(exp,OP_CCLASS);
287         exp->_nodes[n].left = classid;
288         return n;
291 static int trex_charnode(TRex *exp,TRexBool isclass)
293         TRexChar t;
294         if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
295                 exp->_p++;
296                 switch(*exp->_p) {
297                         case 'n': exp->_p++; return trex_newnode(exp,'\n');
298                         case 't': exp->_p++; return trex_newnode(exp,'\t');
299                         case 'r': exp->_p++; return trex_newnode(exp,'\r');
300                         case 'f': exp->_p++; return trex_newnode(exp,'\f');
301                         case 'v': exp->_p++; return trex_newnode(exp,'\v');
302                         case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
303                         case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
304                         case 'p': case 'P': case 'l': case 'u': 
305                                 {
306                                 t = *exp->_p; exp->_p++; 
307                                 return trex_charclass(exp,t);
308                                 }
309                         case 'b': 
310                         case 'B':
311                                 if(!isclass) {
312                                         int node = trex_newnode(exp,OP_WB);
313                                         exp->_nodes[node].left = *exp->_p;
314                                         exp->_p++; 
315                                         return node;
316                                 } //else default
317                         default: 
318                                 t = *exp->_p; exp->_p++; 
319                                 return trex_newnode(exp,t);
320                 }
321         }
322         else if(!scisprint(*exp->_p)) {
323                 
324                 trex_error(exp,_SC("letter expected"));
325         }
326         t = *exp->_p; exp->_p++; 
327         return trex_newnode(exp,t);
329 static int trex_class(TRex *exp)
331         int ret = -1;
332         int first = -1,chain;
333         if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
334                 ret = trex_newnode(exp,OP_NCLASS);
335                 exp->_p++;
336         }else ret = trex_newnode(exp,OP_CLASS);
337         
338         if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
339         chain = ret;
340         while(*exp->_p != ']' && exp->_p != exp->_eol) {
341                 if(*exp->_p == '-' && first != -1){ 
342                         int r,t;
343                         if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
344                         r = trex_newnode(exp,OP_RANGE);
345                         if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
346                         if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
347                         exp->_nodes[r].left = exp->_nodes[first].type;
348                         t = trex_escapechar(exp);
349                         exp->_nodes[r].right = t;
350             exp->_nodes[chain].next = r;
351                         chain = r;
352                         first = -1;
353                 }
354                 else{
355                         if(first!=-1){
356                                 int c = first;
357                                 exp->_nodes[chain].next = c;
358                                 chain = c;
359                                 first = trex_charnode(exp,TRex_True);
360                         }
361                         else{
362                                 first = trex_charnode(exp,TRex_True);
363                         }
364                 }
365         }
366         if(first!=-1){
367                 int c = first;
368                 exp->_nodes[chain].next = c;
369                 chain = c;
370                 first = -1;
371         }
372         /* hack? */
373         exp->_nodes[ret].left = exp->_nodes[ret].next;
374         exp->_nodes[ret].next = -1;
375         return ret;
378 static int trex_parsenumber(TRex *exp)
380         int ret = *exp->_p-'0';
381         int positions = 10;
382         exp->_p++;
383         while(isdigit(*exp->_p)) {
384                 ret = ret*10+(*exp->_p++-'0');
385                 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
386                 positions *= 10;
387         };
388         return ret;
391 static int trex_element(TRex *exp)
393         int ret = -1;
394         switch(*exp->_p)
395         {
396         case '(': {
397                 int expr,newn;
398                 exp->_p++;
401                 if(*exp->_p =='?') {
402                         exp->_p++;
403                         trex_expect(exp,':');
404                         expr = trex_newnode(exp,OP_NOCAPEXPR);
405                 }
406                 else
407                         expr = trex_newnode(exp,OP_EXPR);
408                 newn = trex_list(exp);
409                 exp->_nodes[expr].left = newn;
410                 ret = expr;
411                 trex_expect(exp,')');
412                           }
413                           break;
414         case '[':
415                 exp->_p++;
416                 ret = trex_class(exp);
417                 trex_expect(exp,']');
418                 break;
419         case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
420         case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
421         default:
422                 ret = trex_charnode(exp,TRex_False);
423                 break;
424         }
426         {
427                 int op;
428                 TRexBool isgreedy = TRex_False;
429                 unsigned short p0 = 0, p1 = 0;
430                 switch(*exp->_p){
431                         case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
432                         case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
433                         case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
434                         case '{':
435                                 exp->_p++;
436                                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
437                                 p0 = (unsigned short)trex_parsenumber(exp);
438                                 /*******************************/
439                                 switch(*exp->_p) {
440                         case '}':
441                                 p1 = p0; exp->_p++;
442                                 break;
443                         case ',':
444                                 exp->_p++;
445                                 p1 = 0xFFFF;
446                                 if(isdigit(*exp->_p)){
447                                         p1 = (unsigned short)trex_parsenumber(exp);
448                                 }
449                                 trex_expect(exp,'}');
450                                 break;
451                         default:
452                                 trex_error(exp,_SC(", or } expected"));
453                 }
454                 /*******************************/
455                 isgreedy = TRex_True; 
456                 break;
458                 }
459                 if(isgreedy) {
460                         int nnode = trex_newnode(exp,OP_GREEDY);
461                         op = OP_GREEDY;
462                         exp->_nodes[nnode].left = ret;
463                         exp->_nodes[nnode].right = ((p0)<<16)|p1;
464                         ret = nnode;
465                 }
466         }
467         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')) {
468                 int nnode = trex_element(exp);
469                 exp->_nodes[ret].next = nnode;
470         }
472         return ret;
475 static int trex_list(TRex *exp)
477         int ret=-1,e;
478         if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
479                 exp->_p++;
480                 ret = trex_newnode(exp,OP_BOL);
481         }
482         e = trex_element(exp);
483         if(ret != -1) {
484                 exp->_nodes[ret].next = e;
485         }
486         else ret = e;
488         if(*exp->_p == TREX_SYMBOL_BRANCH) {
489                 int temp,tright;
490                 exp->_p++;
491                 temp = trex_newnode(exp,OP_OR);
492                 exp->_nodes[temp].left = ret;
493                 tright = trex_list(exp);
494                 exp->_nodes[temp].right = tright;
495                 ret = temp;
496         }
497         return ret;
500 static TRexBool trex_matchcclass(int cclass,TRexChar c)
502         switch(cclass) {
503         case 'a': return isalpha(c)?TRex_True:TRex_False;
504         case 'A': return !isalpha(c)?TRex_True:TRex_False;
505         case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
506         case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
507         case 's': return isspace(c)?TRex_True:TRex_False;
508         case 'S': return !isspace(c)?TRex_True:TRex_False;
509         case 'd': return isdigit(c)?TRex_True:TRex_False;
510         case 'D': return !isdigit(c)?TRex_True:TRex_False;
511         case 'x': return isxdigit(c)?TRex_True:TRex_False;
512         case 'X': return !isxdigit(c)?TRex_True:TRex_False;
513         case 'c': return iscntrl(c)?TRex_True:TRex_False;
514         case 'C': return !iscntrl(c)?TRex_True:TRex_False;
515         case 'p': return ispunct(c)?TRex_True:TRex_False;
516         case 'P': return !ispunct(c)?TRex_True:TRex_False;
517         case 'l': return islower(c)?TRex_True:TRex_False;
518         case 'u': return isupper(c)?TRex_True:TRex_False;
519         }
520         return TRex_False; /*cannot happen*/
523 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
525         do {
526                 switch(node->type) {
527                         case OP_RANGE:
528                                 if(c >= node->left && c <= node->right) return TRex_True;
529                                 break;
530                         case OP_CCLASS:
531                                 if(trex_matchcclass(node->left,c)) return TRex_True;
532                                 break;
533                         default:
534                                 if(c == node->type)return TRex_True;
535                 }
536         } while((node->next != -1) && (node = &exp->_nodes[node->next]));
537         return TRex_False;
540 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
542         
543         TRexNodeType type = node->type;
544         switch(type) {
545         case OP_GREEDY: {
546                 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
547                 TRexNode *greedystop = NULL;
548                 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
549                 const TRexChar *s=str, *good = str;
551                 if(node->next != -1) {
552                         greedystop = &exp->_nodes[node->next];
553                 }
554                 else {
555                         greedystop = next;
556                 }
558                 while((nmaches == 0xFFFF || nmaches < p1)) {
560                         const TRexChar *stop;
561                         if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
562                                 break;
563                         nmaches++;
564                         good=s;
565                         if(greedystop) {
566                                 //checks that 0 matches satisfy the expression(if so skips)
567                                 //if not would always stop(for instance if is a '?')
568                                 if(greedystop->type != OP_GREEDY ||
569                                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
570                                 {
571                                         TRexNode *gnext = NULL;
572                                         if(greedystop->next != -1) {
573                                                 gnext = &exp->_nodes[greedystop->next];
574                                         }else if(next && next->next != -1){
575                                                 gnext = &exp->_nodes[next->next];
576                                         }
577                                         stop = trex_matchnode(exp,greedystop,s,gnext);
578                                         if(stop) {
579                                                 //if satisfied stop it
580                                                 if(p0 == p1 && p0 == nmaches) break;
581                                                 else if(nmaches >= p0 && p1 == 0xFFFF) break;
582                                                 else if(nmaches >= p0 && nmaches <= p1) break;
583                                         }
584                                 }
585                         }
586                         
587                         if(s >= exp->_eol)
588                                 break;
589                 }
590                 if(p0 == p1 && p0 == nmaches) return good;
591                 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
592                 else if(nmaches >= p0 && nmaches <= p1) return good;
593                 return NULL;
594         }
595         case OP_OR: {
596                         const TRexChar *asd = str;
597                         TRexNode *temp=&exp->_nodes[node->left];
598                         while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
599                                 if(temp->next != -1)
600                                         temp = &exp->_nodes[temp->next];
601                                 else
602                                         return asd;
603                         }
604                         asd = str;
605                         temp = &exp->_nodes[node->right];
606                         while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
607                                 if(temp->next != -1)
608                                         temp = &exp->_nodes[temp->next];
609                                 else
610                                         return asd;
611                         }
612                         return NULL;
613                         break;
614         }
615         case OP_EXPR:
616         case OP_NOCAPEXPR:{
617                         TRexNode *n = &exp->_nodes[node->left];
618                         const TRexChar *cur = str;
619                         int capture = -1;
620                         if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
621                                 capture = exp->_currsubexp;
622                                 exp->_matches[capture].begin = cur;
623                                 exp->_currsubexp++;
624                         }
625                         
626                         do {
627                                 TRexNode *subnext = NULL;
628                                 if(n->next != -1) {
629                                         subnext = &exp->_nodes[n->next];
630                                 }else {
631                                         subnext = next;
632                                 }
633                                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
634                                         if(capture != -1){
635                                                 exp->_matches[capture].begin = 0;
636                                                 exp->_matches[capture].len = 0;
637                                         }
638                                         return NULL;
639                                 }
640                         } while((n->next != -1) && (n = &exp->_nodes[n->next]));
642                         if(capture != -1) 
643                                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
644                         return cur;
645         }                                
646         case OP_WB:
647                 if(str == exp->_bol && !isspace(*str)
648                  || (str == exp->_eol && !isspace(*(str-1)))
649                  || (!isspace(*str) && isspace(*(str+1)))
650                  || (isspace(*str) && !isspace(*(str+1))) ) {
651                         return (node->left == 'b')?str:NULL;
652                 }
653                 return (node->left == 'b')?NULL:str;
654         case OP_BOL:
655                 if(str == exp->_bol) return str;
656                 return NULL;
657         case OP_EOL:
658                 if(str == exp->_eol) return str;
659                 return NULL;
660         case OP_DOT:{
661                 *str++;
662                                 }
663                 return str;
664         case OP_NCLASS:
665         case OP_CLASS:
666                 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
667                         *str++;
668                         return str;
669                 }
670                 return NULL;
671         case OP_CCLASS:
672                 if(trex_matchcclass(node->left,*str)) {
673                         *str++;
674                         return str;
675                 }
676                 return NULL;
677         default: /* char */
678                 if(*str != node->type) return NULL;
679                 *str++;
680                 return str;
681         }
682         return NULL;
685 /* public api */
686 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
688         TRex *exp = (TRex *)malloc(sizeof(TRex));
689         exp->_eol = exp->_bol = NULL;
690         exp->_p = pattern;
691         exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
692         exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
693         exp->_nsize = 0;
694         exp->_matches = 0;
695         exp->_nsubexpr = 0;
696         exp->_first = trex_newnode(exp,OP_EXPR);
697         exp->_error = error;
698         exp->_jmpbuf = malloc(sizeof(jmp_buf));
699         if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
700                 int res = trex_list(exp);
701                 exp->_nodes[exp->_first].left = res;
702                 if(*exp->_p!='\0')
703                         trex_error(exp,_SC("unexpected character"));
704 #ifdef _DEBUG
705                 {
706                         int nsize,i;
707                         TRexNode *t;
708                         nsize = exp->_nsize;
709                         t = &exp->_nodes[0];
710                         scprintf(_SC("\n"));
711                         for(i = 0;i < nsize; i++) {
712                                 if(exp->_nodes[i].type>MAX_CHAR)
713                                         scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
714                                 else
715                                         scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
716                                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
717                         }
718                         scprintf(_SC("\n"));
719                 }
720 #endif
721                 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
722                 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
723         }
724         else{
725                 trex_free(exp);
726                 return NULL;
727         }
728         return exp;
731 void trex_free(TRex *exp)
733         if(exp) {
734                 if(exp->_nodes) free(exp->_nodes);
735                 if(exp->_jmpbuf) free(exp->_jmpbuf);
736                 if(exp->_matches) free(exp->_matches);
737                 free(exp);
738         }
741 TRexBool trex_match(TRex* exp,const TRexChar* text)
743         const TRexChar* res = NULL;
744         exp->_bol = text;
745         exp->_eol = text + scstrlen(text);
746         exp->_currsubexp = 0;
747         res = trex_matchnode(exp,exp->_nodes,text,NULL);
748         if(res == NULL || res != exp->_eol)
749                 return TRex_False;
750         return TRex_True;
753 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
755         const TRexChar *cur = NULL;
756         int node = exp->_first;
757         if(text_begin >= text_end) return TRex_False;
758         exp->_bol = text_begin;
759         exp->_eol = text_end;
760         do {
761                 cur = text_begin;
762                 while(node != -1) {
763                         exp->_currsubexp = 0;
764                         cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
765                         if(!cur)
766                                 break;
767                         node = exp->_nodes[node].next;
768                 }
769                 *text_begin++;
770         } while(cur == NULL && text_begin != text_end);
772         if(cur == NULL)
773                 return TRex_False;
775         --text_begin;
777         if(out_begin) *out_begin = text_begin;
778         if(out_end) *out_end = cur;
779         return TRex_True;
782 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
784         return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
787 int trex_getsubexpcount(TRex* exp)
789         return exp->_nsubexpr;
792 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
794         if( n<0 || n >= exp->_nsubexpr) return TRex_False;
795         *subexp = exp->_matches[n];
796         return TRex_True;
800 //########################################################################
801 //########################################################################
802 //##  E N D    R E G E X P
803 //########################################################################
804 //########################################################################
810 //########################################################################
811 //########################################################################
812 //##  X M L
813 //########################################################################
814 //########################################################################
816 // Note:  This mini-dom library comes from Pedro, another little project
817 // of mine.
819 typedef std::string String;
820 typedef unsigned int XMLCh;
823 class Namespace
825 public:
826     Namespace()
827         {}
829     Namespace(const String &prefixArg, const String &namespaceURIArg)
830         {
831         prefix       = prefixArg;
832         namespaceURI = namespaceURIArg;
833         }
835     Namespace(const Namespace &other)
836         {
837         assign(other);
838         }
840     Namespace &operator=(const Namespace &other)
841         {
842         assign(other);
843         return *this;
844         }
846     virtual ~Namespace()
847         {}
849     virtual String getPrefix()
850         { return prefix; }
852     virtual String getNamespaceURI()
853         { return namespaceURI; }
855 protected:
857     void assign(const Namespace &other)
858         {
859         prefix       = other.prefix;
860         namespaceURI = other.namespaceURI;
861         }
863     String prefix;
864     String namespaceURI;
866 };
868 class Attribute
870 public:
871     Attribute()
872         {}
874     Attribute(const String &nameArg, const String &valueArg)
875         {
876         name  = nameArg;
877         value = valueArg;
878         }
880     Attribute(const Attribute &other)
881         {
882         assign(other);
883         }
885     Attribute &operator=(const Attribute &other)
886         {
887         assign(other);
888         return *this;
889         }
891     virtual ~Attribute()
892         {}
894     virtual String getName()
895         { return name; }
897     virtual String getValue()
898         { return value; }
900 protected:
902     void assign(const Attribute &other)
903         {
904         name  = other.name;
905         value = other.value;
906         }
908     String name;
909     String value;
911 };
914 class Element
916 friend class Parser;
918 public:
919     Element()
920         {
921         parent = NULL;
922         }
924     Element(const String &nameArg)
925         {
926         parent = NULL;
927         name   = nameArg;
928         }
930     Element(const String &nameArg, const String &valueArg)
931         {
932         parent = NULL;
933         name   = nameArg;
934         value  = valueArg;
935         }
937     Element(const Element &other)
938         {
939         assign(other);
940         }
942     Element &operator=(const Element &other)
943         {
944         assign(other);
945         return *this;
946         }
948     virtual Element *clone();
950     virtual ~Element()
951         {
952         for (unsigned int i=0 ; i<children.size() ; i++)
953             delete children[i];
954         }
956     virtual String getName()
957         { return name; }
959     virtual String getValue()
960         { return value; }
962     Element *getParent()
963         { return parent; }
965     std::vector<Element *> getChildren()
966         { return children; }
968     std::vector<Element *> findElements(const String &name);
970     String getAttribute(const String &name);
972     std::vector<Attribute> &getAttributes()
973         { return attributes; } 
975     String getTagAttribute(const String &tagName, const String &attrName);
977     String getTagValue(const String &tagName);
979     void addChild(Element *child);
981     void addAttribute(const String &name, const String &value);
983     void addNamespace(const String &prefix, const String &namespaceURI);
986     /**
987      * Prettyprint an XML tree to an output stream.  Elements are indented
988      * according to element hierarchy.
989      * @param f a stream to receive the output
990      * @param elem the element to output
991      */
992     void writeIndented(FILE *f);
994     /**
995      * Prettyprint an XML tree to standard output.  This is the equivalent of
996      * writeIndented(stdout).
997      * @param elem the element to output
998      */
999     void print();
1001 protected:
1003     void assign(const Element &other)
1004         {
1005         parent     = other.parent;
1006         children   = other.children;
1007         attributes = other.attributes;
1008         namespaces = other.namespaces;
1009         name       = other.name;
1010         value      = other.value;
1011         }
1013     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1015     void writeIndentedRecursive(FILE *f, int indent);
1017     Element *parent;
1019     std::vector<Element *>children;
1021     std::vector<Attribute> attributes;
1022     std::vector<Namespace> namespaces;
1024     String name;
1025     String value;
1027 };
1033 class Parser
1035 public:
1036     /**
1037      * Constructor
1038      */
1039     Parser()
1040         { init(); }
1042     virtual ~Parser()
1043         {}
1045     /**
1046      * Parse XML in a char buffer.
1047      * @param buf a character buffer to parse
1048      * @param pos position to start parsing
1049      * @param len number of chars, from pos, to parse.
1050      * @return a pointer to the root of the XML document;
1051      */
1052     Element *parse(const char *buf,int pos,int len);
1054     /**
1055      * Parse XML in a char buffer.
1056      * @param buf a character buffer to parse
1057      * @param pos position to start parsing
1058      * @param len number of chars, from pos, to parse.
1059      * @return a pointer to the root of the XML document;
1060      */
1061     Element *parse(const String &buf);
1063     /**
1064      * Parse a named XML file.  The file is loaded like a data file;
1065      * the original format is not preserved.
1066      * @param fileName the name of the file to read
1067      * @return a pointer to the root of the XML document;
1068      */
1069     Element *parseFile(const String &fileName);
1071     /**
1072      * Utility method to preprocess a string for XML
1073      * output, escaping its entities.
1074      * @param str the string to encode
1075      */
1076     static String encode(const String &str);
1078     /**
1079      *  Removes whitespace from beginning and end of a string
1080      */
1081     String trim(const String &s);
1083 private:
1085     void init()
1086         {
1087         keepGoing       = true;
1088         currentNode     = NULL;
1089         parselen        = 0;
1090         parsebuf        = NULL;
1091         currentPosition = 0;
1092         }
1094     void getLineAndColumn(long pos, long *lineNr, long *colNr);
1096     void error(char *fmt, ...);
1098     int peek(long pos);
1100     int match(long pos, const char *text);
1102     int skipwhite(long p);
1104     int getWord(int p0, String &buf);
1106     int getQuoted(int p0, String &buf, int do_i_parse);
1108     int parseVersion(int p0);
1110     int parseDoctype(int p0);
1112     int parseElement(int p0, Element *par,int depth);
1114     Element *parse(XMLCh *buf,int pos,int len);
1116     bool       keepGoing;
1117     Element    *currentNode;
1118     long       parselen;
1119     XMLCh      *parsebuf;
1120     String  cdatabuf;
1121     long       currentPosition;
1122     int        colNr;
1124 };
1129 //########################################################################
1130 //# E L E M E N T
1131 //########################################################################
1133 Element *Element::clone()
1135     Element *elem = new Element(name, value);
1136     elem->parent     = parent;
1137     elem->attributes = attributes;
1138     elem->namespaces = namespaces;
1140     std::vector<Element *>::iterator iter;
1141     for (iter = children.begin(); iter != children.end() ; iter++)
1142         {
1143         elem->addChild((*iter)->clone());
1144         }
1145     return elem;
1149 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1151     if (getName() == name)
1152         {
1153         res.push_back(this);
1154         }
1155     for (unsigned int i=0; i<children.size() ; i++)
1156         children[i]->findElementsRecursive(res, name);
1159 std::vector<Element *> Element::findElements(const String &name)
1161     std::vector<Element *> res;
1162     findElementsRecursive(res, name);
1163     return res;
1166 String Element::getAttribute(const String &name)
1168     for (unsigned int i=0 ; i<attributes.size() ; i++)
1169         if (attributes[i].getName() ==name)
1170             return attributes[i].getValue();
1171     return "";
1174 String Element::getTagAttribute(const String &tagName, const String &attrName)
1176     std::vector<Element *>elems = findElements(tagName);
1177     if (elems.size() <1)
1178         return "";
1179     String res = elems[0]->getAttribute(attrName);
1180     return res;
1183 String Element::getTagValue(const String &tagName)
1185     std::vector<Element *>elems = findElements(tagName);
1186     if (elems.size() <1)
1187         return "";
1188     String res = elems[0]->getValue();
1189     return res;
1192 void Element::addChild(Element *child)
1194     if (!child)
1195         return;
1196     child->parent = this;
1197     children.push_back(child);
1201 void Element::addAttribute(const String &name, const String &value)
1203     Attribute attr(name, value);
1204     attributes.push_back(attr);
1207 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1209     Namespace ns(prefix, namespaceURI);
1210     namespaces.push_back(ns);
1213 void Element::writeIndentedRecursive(FILE *f, int indent)
1215     int i;
1216     if (!f)
1217         return;
1218     //Opening tag, and attributes
1219     for (i=0;i<indent;i++)
1220         fputc(' ',f);
1221     fprintf(f,"<%s",name.c_str());
1222     for (unsigned int i=0 ; i<attributes.size() ; i++)
1223         {
1224         fprintf(f," %s=\"%s\"",
1225               attributes[i].getName().c_str(),
1226               attributes[i].getValue().c_str());
1227         }
1228     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1229         {
1230         fprintf(f," xmlns:%s=\"%s\"",
1231               namespaces[i].getPrefix().c_str(),
1232               namespaces[i].getNamespaceURI().c_str());
1233         }
1234     fprintf(f,">\n");
1236     //Between the tags
1237     if (value.size() > 0)
1238         {
1239         for (int i=0;i<indent;i++)
1240             fputc(' ', f);
1241         fprintf(f," %s\n", value.c_str());
1242         }
1244     for (unsigned int i=0 ; i<children.size() ; i++)
1245         children[i]->writeIndentedRecursive(f, indent+2);
1247     //Closing tag
1248     for (int i=0; i<indent; i++)
1249         fputc(' ',f);
1250     fprintf(f,"</%s>\n", name.c_str());
1253 void Element::writeIndented(FILE *f)
1255     writeIndentedRecursive(f, 0);
1258 void Element::print()
1260     writeIndented(stdout);
1264 //########################################################################
1265 //# P A R S E R
1266 //########################################################################
1270 typedef struct
1271     {
1272     char *escaped;
1273     char value;
1274     } EntityEntry;
1276 static EntityEntry entities[] =
1278     { "&amp;" , '&'  },
1279     { "&lt;"  , '<'  },
1280     { "&gt;"  , '>'  },
1281     { "&apos;", '\'' },
1282     { "&quot;", '"'  },
1283     { NULL    , '\0' }
1284 };
1288 /**
1289  *  Removes whitespace from beginning and end of a string
1290  */
1291 String Parser::trim(const String &s)
1293     if (s.size() < 1)
1294         return s;
1295     
1296     //Find first non-ws char
1297     unsigned int begin = 0;
1298     for ( ; begin < s.size() ; begin++)
1299         {
1300         if (!isspace(s[begin]))
1301             break;
1302         }
1304     //Find first non-ws char, going in reverse
1305     unsigned int end = s.size() - 1;
1306     for ( ; end > begin ; end--)
1307         {
1308         if (!isspace(s[end]))
1309             break;
1310         }
1311     //trace("begin:%d  end:%d", begin, end);
1313     String res = s.substr(begin, end-begin+1);
1314     return res;
1317 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1319     long line = 1;
1320     long col  = 1;
1321     for (long i=0 ; i<pos ; i++)
1322         {
1323         XMLCh ch = parsebuf[i];
1324         if (ch == '\n' || ch == '\r')
1325             {
1326             col = 0;
1327             line ++;
1328             }
1329         else
1330             col++;
1331         }
1332     *lineNr = line;
1333     *colNr  = col;
1338 void Parser::error(char *fmt, ...)
1340     long lineNr;
1341     long colNr;
1342     getLineAndColumn(currentPosition, &lineNr, &colNr);
1343     va_list args;
1344     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1345     va_start(args,fmt);
1346     vfprintf(stderr,fmt,args);
1347     va_end(args) ;
1348     fprintf(stderr, "\n");
1353 int Parser::peek(long pos)
1355     if (pos >= parselen)
1356         return -1;
1357     currentPosition = pos;
1358     int ch = parsebuf[pos];
1359     //printf("ch:%c\n", ch);
1360     return ch;
1365 String Parser::encode(const String &str)
1367     String ret;
1368     for (unsigned int i=0 ; i<str.size() ; i++)
1369         {
1370         XMLCh ch = (XMLCh)str[i];
1371         if (ch == '&')
1372             ret.append("&amp;");
1373         else if (ch == '<')
1374             ret.append("&lt;");
1375         else if (ch == '>')
1376             ret.append("&gt;");
1377         else if (ch == '\'')
1378             ret.append("&apos;");
1379         else if (ch == '"')
1380             ret.append("&quot;");
1381         else
1382             ret.push_back(ch);
1384         }
1385     return ret;
1389 int Parser::match(long p0, const char *text)
1391     int p = p0;
1392     while (*text)
1393         {
1394         if (peek(p) != *text)
1395             return p0;
1396         p++; text++;
1397         }
1398     return p;
1403 int Parser::skipwhite(long p)
1406     while (p<parselen)
1407         {
1408         int p2 = match(p, "<!--");
1409         if (p2 > p)
1410             {
1411             p = p2;
1412             while (p<parselen)
1413               {
1414               p2 = match(p, "-->");
1415               if (p2 > p)
1416                   {
1417                   p = p2;
1418                   break;
1419                   }
1420               p++;
1421               }
1422           }
1423       XMLCh b = peek(p);
1424       if (!isspace(b))
1425           break;
1426       p++;
1427       }
1428   return p;
1431 /* modify this to allow all chars for an element or attribute name*/
1432 int Parser::getWord(int p0, String &buf)
1434     int p = p0;
1435     while (p<parselen)
1436         {
1437         XMLCh b = peek(p);
1438         if (b<=' ' || b=='/' || b=='>' || b=='=')
1439             break;
1440         buf.push_back(b);
1441         p++;
1442         }
1443     return p;
1446 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1449     int p = p0;
1450     if (peek(p) != '"' && peek(p) != '\'')
1451         return p0;
1452     p++;
1454     while ( p<parselen )
1455         {
1456         XMLCh b = peek(p);
1457         if (b=='"' || b=='\'')
1458             break;
1459         if (b=='&' && do_i_parse)
1460             {
1461             bool found = false;
1462             for (EntityEntry *ee = entities ; ee->value ; ee++)
1463                 {
1464                 int p2 = match(p, ee->escaped);
1465                 if (p2>p)
1466                     {
1467                     buf.push_back(ee->value);
1468                     p = p2;
1469                     found = true;
1470                     break;
1471                     }
1472                 }
1473             if (!found)
1474                 {
1475                 error("unterminated entity");
1476                 return false;
1477                 }
1478             }
1479         else
1480             {
1481             buf.push_back(b);
1482             p++;
1483             }
1484         }
1485     return p;
1488 int Parser::parseVersion(int p0)
1490     //printf("### parseVersion: %d\n", p0);
1492     int p = p0;
1494     p = skipwhite(p0);
1496     if (peek(p) != '<')
1497         return p0;
1499     p++;
1500     if (p>=parselen || peek(p)!='?')
1501         return p0;
1503     p++;
1505     String buf;
1507     while (p<parselen)
1508         {
1509         XMLCh ch = peek(p);
1510         if (ch=='?')
1511             {
1512             p++;
1513             break;
1514             }
1515         buf.push_back(ch);
1516         p++;
1517         }
1519     if (peek(p) != '>')
1520         return p0;
1521     p++;
1523     //printf("Got version:%s\n",buf.c_str());
1524     return p;
1527 int Parser::parseDoctype(int p0)
1529     //printf("### parseDoctype: %d\n", p0);
1531     int p = p0;
1532     p = skipwhite(p);
1534     if (p>=parselen || peek(p)!='<')
1535         return p0;
1537     p++;
1539     if (peek(p)!='!' || peek(p+1)=='-')
1540         return p0;
1541     p++;
1543     String buf;
1544     while (p<parselen)
1545         {
1546         XMLCh ch = peek(p);
1547         if (ch=='>')
1548             {
1549             p++;
1550             break;
1551             }
1552         buf.push_back(ch);
1553         p++;
1554         }
1556     //printf("Got doctype:%s\n",buf.c_str());
1557     return p;
1560 int Parser::parseElement(int p0, Element *par,int depth)
1563     int p = p0;
1565     int p2 = p;
1567     p = skipwhite(p);
1569     //## Get open tag
1570     XMLCh ch = peek(p);
1571     if (ch!='<')
1572         return p0;
1574     p++;
1576     String openTagName;
1577     p = skipwhite(p);
1578     p = getWord(p, openTagName);
1579     //printf("####tag :%s\n", openTagName.c_str());
1580     p = skipwhite(p);
1582     //Add element to tree
1583     Element *n = new Element(openTagName);
1584     n->parent = par;
1585     par->addChild(n);
1587     // Get attributes
1588     if (peek(p) != '>')
1589         {
1590         while (p<parselen)
1591             {
1592             p = skipwhite(p);
1593             ch = peek(p);
1594             //printf("ch:%c\n",ch);
1595             if (ch=='>')
1596                 break;
1597             else if (ch=='/' && p<parselen+1)
1598                 {
1599                 p++;
1600                 p = skipwhite(p);
1601                 ch = peek(p);
1602                 if (ch=='>')
1603                     {
1604                     p++;
1605                     //printf("quick close\n");
1606                     return p;
1607                     }
1608                 }
1609             String attrName;
1610             p2 = getWord(p, attrName);
1611             if (p2==p)
1612                 break;
1613             //printf("name:%s",buf);
1614             p=p2;
1615             p = skipwhite(p);
1616             ch = peek(p);
1617             //printf("ch:%c\n",ch);
1618             if (ch!='=')
1619                 break;
1620             p++;
1621             p = skipwhite(p);
1622             // ch = parsebuf[p];
1623             // printf("ch:%c\n",ch);
1624             String attrVal;
1625             p2 = getQuoted(p, attrVal, true);
1626             p=p2+1;
1627             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1628             char *namestr = (char *)attrName.c_str();
1629             if (strncmp(namestr, "xmlns:", 6)==0)
1630                 n->addNamespace(attrName, attrVal);
1631             else
1632                 n->addAttribute(attrName, attrVal);
1633             }
1634         }
1636     bool cdata = false;
1638     p++;
1639     // ### Get intervening data ### */
1640     String data;
1641     while (p<parselen)
1642         {
1643         //# COMMENT
1644         p2 = match(p, "<!--");
1645         if (!cdata && p2>p)
1646             {
1647             p = p2;
1648             while (p<parselen)
1649                 {
1650                 p2 = match(p, "-->");
1651                 if (p2 > p)
1652                     {
1653                     p = p2;
1654                     break;
1655                     }
1656                 p++;
1657                 }
1658             }
1660         ch = peek(p);
1661         //# END TAG
1662         if (ch=='<' && !cdata && peek(p+1)=='/')
1663             {
1664             break;
1665             }
1666         //# CDATA
1667         p2 = match(p, "<![CDATA[");
1668         if (p2 > p)
1669             {
1670             cdata = true;
1671             p = p2;
1672             continue;
1673             }
1675         //# CHILD ELEMENT
1676         if (ch == '<')
1677             {
1678             p2 = parseElement(p, n, depth+1);
1679             if (p2 == p)
1680                 {
1681                 /*
1682                 printf("problem on element:%s.  p2:%d p:%d\n",
1683                       openTagName.c_str(), p2, p);
1684                 */
1685                 return p0;
1686                 }
1687             p = p2;
1688             continue;
1689             }
1690         //# ENTITY
1691         if (ch=='&' && !cdata)
1692             {
1693             bool found = false;
1694             for (EntityEntry *ee = entities ; ee->value ; ee++)
1695                 {
1696                 int p2 = match(p, ee->escaped);
1697                 if (p2>p)
1698                     {
1699                     data.push_back(ee->value);
1700                     p = p2;
1701                     found = true;
1702                     break;
1703                     }
1704                 }
1705             if (!found)
1706                 {
1707                 error("unterminated entity");
1708                 return -1;
1709                 }
1710             continue;
1711             }
1713         //# NONE OF THE ABOVE
1714         data.push_back(ch);
1715         p++;
1716         }/*while*/
1719     n->value = data;
1720     //printf("%d : data:%s\n",p,data.c_str());
1722     //## Get close tag
1723     p = skipwhite(p);
1724     ch = peek(p);
1725     if (ch != '<')
1726         {
1727         error("no < for end tag\n");
1728         return p0;
1729         }
1730     p++;
1731     ch = peek(p);
1732     if (ch != '/')
1733         {
1734         error("no / on end tag");
1735         return p0;
1736         }
1737     p++;
1738     ch = peek(p);
1739     p = skipwhite(p);
1740     String closeTagName;
1741     p = getWord(p, closeTagName);
1742     if (openTagName != closeTagName)
1743         {
1744         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1745                 openTagName.c_str(), closeTagName.c_str());
1746         return p0;
1747         }
1748     p = skipwhite(p);
1749     if (peek(p) != '>')
1750         {
1751         error("no > on end tag for '%s'", closeTagName.c_str());
1752         return p0;
1753         }
1754     p++;
1755     // printf("close element:%s\n",closeTagName.c_str());
1756     p = skipwhite(p);
1757     return p;
1763 Element *Parser::parse(XMLCh *buf,int pos,int len)
1765     parselen = len;
1766     parsebuf = buf;
1767     Element *rootNode = new Element("root");
1768     pos = parseVersion(pos);
1769     pos = parseDoctype(pos);
1770     pos = parseElement(pos, rootNode, 0);
1771     return rootNode;
1775 Element *Parser::parse(const char *buf, int pos, int len)
1777     XMLCh *charbuf = new XMLCh[len + 1];
1778     long i = 0;
1779     for ( ; i < len ; i++)
1780         charbuf[i] = (XMLCh)buf[i];
1781     charbuf[i] = '\0';
1783     Element *n = parse(charbuf, pos, len);
1784     delete[] charbuf;
1785     return n;
1788 Element *Parser::parse(const String &buf)
1790     long len = (long)buf.size();
1791     XMLCh *charbuf = new XMLCh[len + 1];
1792     long i = 0;
1793     for ( ; i < len ; i++)
1794         charbuf[i] = (XMLCh)buf[i];
1795     charbuf[i] = '\0';
1797     Element *n = parse(charbuf, 0, len);
1798     delete[] charbuf;
1799     return n;
1802 Element *Parser::parseFile(const String &fileName)
1805     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1806     FILE *f = fopen(fileName.c_str(), "rb");
1807     if (!f)
1808         return NULL;
1810     struct stat  statBuf;
1811     if (fstat(fileno(f),&statBuf)<0)
1812         {
1813         fclose(f);
1814         return NULL;
1815         }
1816     long filelen = statBuf.st_size;
1818     //printf("length:%d\n",filelen);
1819     XMLCh *charbuf = new XMLCh[filelen + 1];
1820     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1821         {
1822         *p = (XMLCh)fgetc(f);
1823         }
1824     fclose(f);
1825     charbuf[filelen] = '\0';
1828     /*
1829     printf("nrbytes:%d\n",wc_count);
1830     printf("buf:%ls\n======\n",charbuf);
1831     */
1832     Element *n = parse(charbuf, 0, filelen);
1833     delete[] charbuf;
1834     return n;
1839 //########################################################################
1840 //########################################################################
1841 //##  E N D    X M L
1842 //########################################################################
1843 //########################################################################
1846 //########################################################################
1847 //########################################################################
1848 //##  U R I
1849 //########################################################################
1850 //########################################################################
1852 //This would normally be a call to a UNICODE function
1853 #define isLetter(x) isalpha(x)
1855 /**
1856  *  A class that implements the W3C URI resource reference.
1857  */
1858 class URI
1860 public:
1862     typedef enum
1863         {
1864         SCHEME_NONE =0,
1865         SCHEME_DATA,
1866         SCHEME_HTTP,
1867         SCHEME_HTTPS,
1868         SCHEME_FTP,
1869         SCHEME_FILE,
1870         SCHEME_LDAP,
1871         SCHEME_MAILTO,
1872         SCHEME_NEWS,
1873         SCHEME_TELNET
1874         } SchemeTypes;
1876     /**
1877      *
1878      */
1879     URI()
1880         {
1881         init();
1882         }
1884     /**
1885      *
1886      */
1887     URI(const String &str)
1888         {
1889         init();
1890         parse(str);
1891         }
1894     /**
1895      *
1896      */
1897     URI(const char *str)
1898         {
1899         init();
1900         String domStr = str;
1901         parse(domStr);
1902         }
1905     /**
1906      *
1907      */
1908     URI(const URI &other)
1909         {
1910         init();
1911         assign(other);
1912         }
1915     /**
1916      *
1917      */
1918     URI &operator=(const URI &other)
1919         {
1920         init();
1921         assign(other);
1922         return *this;
1923         }
1926     /**
1927      *
1928      */
1929     virtual ~URI()
1930         {}
1934     /**
1935      *
1936      */
1937     virtual bool parse(const String &str);
1939     /**
1940      *
1941      */
1942     virtual String toString() const;
1944     /**
1945      *
1946      */
1947     virtual int getScheme() const;
1949     /**
1950      *
1951      */
1952     virtual String getSchemeStr() const;
1954     /**
1955      *
1956      */
1957     virtual String getAuthority() const;
1959     /**
1960      *  Same as getAuthority, but if the port has been specified
1961      *  as host:port , the port will not be included
1962      */
1963     virtual String getHost() const;
1965     /**
1966      *
1967      */
1968     virtual int getPort() const;
1970     /**
1971      *
1972      */
1973     virtual String getPath() const;
1975     /**
1976      *
1977      */
1978     virtual String getNativePath() const;
1980     /**
1981      *
1982      */
1983     virtual bool isAbsolute() const;
1985     /**
1986      *
1987      */
1988     virtual bool isOpaque() const;
1990     /**
1991      *
1992      */
1993     virtual String getQuery() const;
1995     /**
1996      *
1997      */
1998     virtual String getFragment() const;
2000     /**
2001      *
2002      */
2003     virtual URI resolve(const URI &other) const;
2005     /**
2006      *
2007      */
2008     virtual void normalize();
2010 private:
2012     /**
2013      *
2014      */
2015     void init()
2016         {
2017         parsebuf  = NULL;
2018         parselen  = 0;
2019         scheme    = SCHEME_NONE;
2020         schemeStr = "";
2021         port      = 0;
2022         authority = "";
2023         path      = "";
2024         absolute  = false;
2025         opaque    = false;
2026         query     = "";
2027         fragment  = "";
2028         }
2031     /**
2032      *
2033      */
2034     void assign(const URI &other)
2035         {
2036         scheme    = other.scheme;
2037         schemeStr = other.schemeStr;
2038         authority = other.authority;
2039         port      = other.port;
2040         path      = other.path;
2041         absolute  = other.absolute;
2042         opaque    = other.opaque;
2043         query     = other.query;
2044         fragment  = other.fragment;
2045         }
2047     int scheme;
2049     String schemeStr;
2051     String authority;
2053     bool portSpecified;
2055     int port;
2057     String path;
2059     bool absolute;
2061     bool opaque;
2063     String query;
2065     String fragment;
2067     void error(const char *fmt, ...);
2069     void trace(const char *fmt, ...);
2072     int peek(int p);
2074     int match(int p, char *key);
2076     int parseScheme(int p);
2078     int parseHierarchicalPart(int p0);
2080     int parseQuery(int p0);
2082     int parseFragment(int p0);
2084     int parse(int p);
2086     char *parsebuf;
2088     int parselen;
2090 };
2094 typedef struct
2096     int  ival;
2097     char *sval;
2098     int  port;
2099 } LookupEntry;
2101 LookupEntry schemes[] =
2103     { URI::SCHEME_DATA,   "data:",    0 },
2104     { URI::SCHEME_HTTP,   "http:",   80 },
2105     { URI::SCHEME_HTTPS,  "https:", 443 },
2106     { URI::SCHEME_FTP,    "ftp",     12 },
2107     { URI::SCHEME_FILE,   "file:",    0 },
2108     { URI::SCHEME_LDAP,   "ldap:",  123 },
2109     { URI::SCHEME_MAILTO, "mailto:", 25 },
2110     { URI::SCHEME_NEWS,   "news:",  117 },
2111     { URI::SCHEME_TELNET, "telnet:", 23 },
2112     { 0,                  NULL,       0 }
2113 };
2116 String URI::toString() const
2118     String str = schemeStr;
2119     if (authority.size() > 0)
2120         {
2121         str.append("//");
2122         str.append(authority);
2123         }
2124     str.append(path);
2125     if (query.size() > 0)
2126         {
2127         str.append("?");
2128         str.append(query);
2129         }
2130     if (fragment.size() > 0)
2131         {
2132         str.append("#");
2133         str.append(fragment);
2134         }
2135     return str;
2139 int URI::getScheme() const
2141     return scheme;
2144 String URI::getSchemeStr() const
2146     return schemeStr;
2150 String URI::getAuthority() const
2152     String ret = authority;
2153     if (portSpecified && port>=0)
2154         {
2155         char buf[7];
2156         snprintf(buf, 6, ":%6d", port);
2157         ret.append(buf);
2158         }
2159     return ret;
2162 String URI::getHost() const
2164     return authority;
2167 int URI::getPort() const
2169     return port;
2173 String URI::getPath() const
2175     return path;
2178 String URI::getNativePath() const
2180     String npath;
2181 #ifdef __WIN32__
2182     unsigned int firstChar = 0;
2183     if (path.size() >= 3)
2184         {
2185         if (path[0] == '/' &&
2186             isLetter(path[1]) &&
2187             path[2] == ':')
2188             firstChar++;
2189          }
2190     for (unsigned int i=firstChar ; i<path.size() ; i++)
2191         {
2192         XMLCh ch = (XMLCh) path[i];
2193         if (ch == '/')
2194             npath.push_back((XMLCh)'\\');
2195         else
2196             npath.push_back(ch);
2197         }
2198 #else
2199     npath = path;
2200 #endif
2201     return npath;
2205 bool URI::isAbsolute() const
2207     return absolute;
2210 bool URI::isOpaque() const
2212     return opaque;
2216 String URI::getQuery() const
2218     return query;
2222 String URI::getFragment() const
2224     return fragment;
2228 URI URI::resolve(const URI &other) const
2230     //### According to w3c, this is handled in 3 cases
2232     //## 1
2233     if (opaque || other.isAbsolute())
2234         return other;
2236     //## 2
2237     if (other.fragment.size()  >  0 &&
2238         other.path.size()      == 0 &&
2239         other.scheme           == SCHEME_NONE &&
2240         other.authority.size() == 0 &&
2241         other.query.size()     == 0 )
2242         {
2243         URI fragUri = *this;
2244         fragUri.fragment = other.fragment;
2245         return fragUri;
2246         }
2248     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2249     URI newUri;
2250     //# 3.1
2251     newUri.scheme    = scheme;
2252     newUri.schemeStr = schemeStr;
2253     newUri.query     = other.query;
2254     newUri.fragment  = other.fragment;
2255     if (other.authority.size() > 0)
2256         {
2257         //# 3.2
2258         if (absolute || other.absolute)
2259             newUri.absolute = true;
2260         newUri.authority = other.authority;
2261         newUri.port      = other.port;//part of authority
2262         newUri.path      = other.path;
2263         }
2264     else
2265         {
2266         //# 3.3
2267         if (other.absolute)
2268             {
2269             newUri.absolute = true;
2270             newUri.path     = other.path;
2271             }
2272         else
2273             {
2274             unsigned int pos = path.find_last_of('/');
2275             if (pos != path.npos)
2276                 {
2277                 String tpath = path.substr(0, pos+1);
2278                 tpath.append(other.path);
2279                 newUri.path = tpath;
2280                 }
2281             else
2282                 newUri.path = other.path;
2283             }
2284         }
2286     newUri.normalize();
2287     return newUri;
2291 /**
2292  *  This follows the Java URI algorithm:
2293  *   1. All "." segments are removed.
2294  *   2. If a ".." segment is preceded by a non-".." segment
2295  *          then both of these segments are removed. This step
2296  *          is repeated until it is no longer applicable.
2297  *   3. If the path is relative, and if its first segment
2298  *          contains a colon character (':'), then a "." segment
2299  *          is prepended. This prevents a relative URI with a path
2300  *          such as "a:b/c/d" from later being re-parsed as an
2301  *          opaque URI with a scheme of "a" and a scheme-specific
2302  *          part of "b/c/d". (Deviation from RFC 2396)
2303  */
2304 void URI::normalize()
2306     std::vector<String> segments;
2308     //## Collect segments
2309     if (path.size()<2)
2310         return;
2311     bool abs = false;
2312     unsigned int pos=0;
2313     if (path[0]=='/')
2314         {
2315         abs = true;
2316         pos++;
2317         }
2318     while (pos < path.size())
2319         {
2320         unsigned int pos2 = path.find('/', pos);
2321         if (pos2==path.npos)
2322             {
2323             String seg = path.substr(pos);
2324             //printf("last segment:%s\n", seg.c_str());
2325             segments.push_back(seg);
2326             break;
2327             }
2328         if (pos2>pos)
2329             {
2330             String seg = path.substr(pos, pos2-pos);
2331             //printf("segment:%s\n", seg.c_str());
2332             segments.push_back(seg);
2333             }
2334         pos = pos2;
2335         pos++;
2336         }
2338     //## Clean up (normalize) segments
2339     bool edited = false;
2340     std::vector<String>::iterator iter;
2341     for (iter=segments.begin() ; iter!=segments.end() ; )
2342         {
2343         String s = *iter;
2344         if (s == ".")
2345             {
2346             iter = segments.erase(iter);
2347             edited = true;
2348             }
2349         else if (s == ".." &&
2350                  iter != segments.begin() &&
2351                  *(iter-1) != "..")
2352             {
2353             iter--; //back up, then erase two entries
2354             iter = segments.erase(iter);
2355             iter = segments.erase(iter);
2356             edited = true;
2357             }
2358         else
2359             iter++;
2360         }
2362     //## Rebuild path, if necessary
2363     if (edited)
2364         {
2365         path.clear();
2366         if (abs)
2367             {
2368             path.append("/");
2369             }
2370         std::vector<String>::iterator iter;
2371         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2372             {
2373             if (iter != segments.begin())
2374                 path.append("/");
2375             path.append(*iter);
2376             }
2377         }
2383 //#########################################################################
2384 //# M E S S A G E S
2385 //#########################################################################
2387 void URI::error(const char *fmt, ...)
2389     va_list args;
2390     fprintf(stderr, "URI error: ");
2391     va_start(args, fmt);
2392     vfprintf(stderr, fmt, args);
2393     va_end(args);
2394     fprintf(stderr, "\n");
2397 void URI::trace(const char *fmt, ...)
2399     va_list args;
2400     fprintf(stdout, "URI: ");
2401     va_start(args, fmt);
2402     vfprintf(stdout, fmt, args);
2403     va_end(args);
2404     fprintf(stdout, "\n");
2409 //#########################################################################
2410 //# P A R S I N G
2411 //#########################################################################
2415 int URI::peek(int p)
2417     if (p<0 || p>=parselen)
2418         return -1;
2419     return parsebuf[p];
2424 int URI::match(int p0, char *key)
2426     int p = p0;
2427     while (p < parselen)
2428         {
2429         if (*key == '\0')
2430             return p;
2431         else if (*key != parsebuf[p])
2432             break;
2433         p++; key++;
2434         }
2435     return p0;
2438 //#########################################################################
2439 //#  Parsing is performed according to:
2440 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2441 //#########################################################################
2443 int URI::parseScheme(int p0)
2445     int p = p0;
2446     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2447         {
2448         int p2 = match(p, entry->sval);
2449         if (p2 > p)
2450             {
2451             schemeStr = entry->sval;
2452             scheme    = entry->ival;
2453             port      = entry->port;
2454             p = p2;
2455             return p;
2456             }
2457         }
2459     return p;
2463 int URI::parseHierarchicalPart(int p0)
2465     int p = p0;
2466     int ch;
2468     //# Authority field (host and port, for example)
2469     int p2 = match(p, "//");
2470     if (p2 > p)
2471         {
2472         p = p2;
2473         portSpecified = false;
2474         String portStr;
2475         while (p < parselen)
2476             {
2477             ch = peek(p);
2478             if (ch == '/')
2479                 break;
2480             else if (ch == ':')
2481                 portSpecified = true;
2482             else if (portSpecified)
2483                 portStr.push_back((XMLCh)ch);
2484             else
2485                 authority.push_back((XMLCh)ch);
2486             p++;
2487             }
2488         if (portStr.size() > 0)
2489             {
2490             char *pstr = (char *)portStr.c_str();
2491             char *endStr;
2492             long val = strtol(pstr, &endStr, 10);
2493             if (endStr > pstr) //successful parse?
2494                 port = val;
2495             }
2496         }
2498     //# Are we absolute?
2499     ch = peek(p);
2500     if (isLetter(ch) && peek(p+1)==':')
2501         {
2502         absolute = true;
2503         path.push_back((XMLCh)'/');
2504         }
2505     else if (ch == '/')
2506         {
2507         absolute = true;
2508         if (p>p0) //in other words, if '/' is not the first char
2509             opaque = true;
2510         path.push_back((XMLCh)ch);
2511         p++;
2512         }
2514     while (p < parselen)
2515         {
2516         ch = peek(p);
2517         if (ch == '?' || ch == '#')
2518             break;
2519         path.push_back((XMLCh)ch);
2520         p++;
2521         }
2523     return p;
2526 int URI::parseQuery(int p0)
2528     int p = p0;
2529     int ch = peek(p);
2530     if (ch != '?')
2531         return p0;
2533     p++;
2534     while (p < parselen)
2535         {
2536         ch = peek(p);
2537         if (ch == '#')
2538             break;
2539         query.push_back((XMLCh)ch);
2540         p++;
2541         }
2544     return p;
2547 int URI::parseFragment(int p0)
2550     int p = p0;
2551     int ch = peek(p);
2552     if (ch != '#')
2553         return p0;
2555     p++;
2556     while (p < parselen)
2557         {
2558         ch = peek(p);
2559         if (ch == '?')
2560             break;
2561         fragment.push_back((XMLCh)ch);
2562         p++;
2563         }
2566     return p;
2570 int URI::parse(int p0)
2573     int p = p0;
2575     int p2 = parseScheme(p);
2576     if (p2 < 0)
2577         {
2578         error("Scheme");
2579         return -1;
2580         }
2581     p = p2;
2584     p2 = parseHierarchicalPart(p);
2585     if (p2 < 0)
2586         {
2587         error("Hierarchical part");
2588         return -1;
2589         }
2590     p = p2;
2592     p2 = parseQuery(p);
2593     if (p2 < 0)
2594         {
2595         error("Query");
2596         return -1;
2597         }
2598     p = p2;
2601     p2 = parseFragment(p);
2602     if (p2 < 0)
2603         {
2604         error("Fragment");
2605         return -1;
2606         }
2607     p = p2;
2609     return p;
2615 bool URI::parse(const String &str)
2617     init();
2618     
2619     parselen = str.size();
2621     String tmp;
2622     for (unsigned int i=0 ; i<str.size() ; i++)
2623         {
2624         XMLCh ch = (XMLCh) str[i];
2625         if (ch == '\\')
2626             tmp.push_back((XMLCh)'/');
2627         else
2628             tmp.push_back(ch);
2629         }
2630     parsebuf = (char *) tmp.c_str();
2633     int p = parse(0);
2634     normalize();
2636     if (p < 0)
2637         {
2638         error("Syntax error");
2639         return false;
2640         }
2642     //printf("uri:%s\n", toString().c_str());
2643     //printf("path:%s\n", path.c_str());
2645     return true;
2656 //########################################################################
2657 //########################################################################
2658 //##  M A K E
2659 //########################################################################
2660 //########################################################################
2662 //########################################################################
2663 //# F I L E S E T
2664 //########################################################################
2665 /**
2666  * This is the descriptor for a <fileset> item
2667  */
2668 class FileSet
2670 public:
2672     /**
2673      *
2674      */
2675     FileSet()
2676         {}
2678     /**
2679      *
2680      */
2681     FileSet(const FileSet &other)
2682         { assign(other); }
2684     /**
2685      *
2686      */
2687     FileSet &operator=(const FileSet &other)
2688         { assign(other); return *this; }
2690     /**
2691      *
2692      */
2693     virtual ~FileSet()
2694         {}
2696     /**
2697      *
2698      */
2699     String getDirectory()
2700         { return directory; }
2701         
2702     /**
2703      *
2704      */
2705     void setDirectory(const String &val)
2706         { directory = val; }
2708     /**
2709      *
2710      */
2711     void setFiles(const std::vector<String> &val)
2712         { files = val; }
2714     /**
2715      *
2716      */
2717     std::vector<String> getFiles()
2718         { return files; }
2719         
2720     /**
2721      *
2722      */
2723     void setIncludes(const std::vector<String> &val)
2724         { includes = val; }
2726     /**
2727      *
2728      */
2729     std::vector<String> getIncludes()
2730         { return includes; }
2731         
2732     /**
2733      *
2734      */
2735     void setExcludes(const std::vector<String> &val)
2736         { excludes = val; }
2738     /**
2739      *
2740      */
2741     std::vector<String> getExcludes()
2742         { return excludes; }
2743         
2744     /**
2745      *
2746      */
2747     unsigned int size()
2748         { return files.size(); }
2749         
2750     /**
2751      *
2752      */
2753     String operator[](int index)
2754         { return files[index]; }
2755         
2756     /**
2757      *
2758      */
2759     void clear()
2760         {
2761         directory = "";
2762         files.clear();
2763         includes.clear();
2764         excludes.clear();
2765         }
2766         
2768 private:
2770     void assign(const FileSet &other)
2771         {
2772         directory = other.directory;
2773         files     = other.files;
2774         includes  = other.includes;
2775         excludes  = other.excludes;
2776         }
2778     String directory;
2779     std::vector<String> files;
2780     std::vector<String> includes;
2781     std::vector<String> excludes;
2782 };
2787 //########################################################################
2788 //# M A K E    B A S E
2789 //########################################################################
2790 /**
2791  * Base class for all classes in this file
2792  */
2793 class MakeBase
2795 public:
2796     MakeBase()
2797         {}
2798     virtual ~MakeBase()
2799         {}
2801     /**
2802      *  Return the URI of the file associated with this object 
2803      */     
2804     URI getURI()
2805         { return uri; }
2807     /**
2808      * Set the uri to the given string
2809      */
2810     void setURI(const String &uristr)
2811         { uri.parse(uristr); }
2813     /**
2814      *  Resolve another path relative to this one
2815      */
2816     String resolve(const String &otherPath);
2818     /**
2819      *  Get an element attribute, performing substitutions if necessary
2820      */
2821     bool getAttribute(Element *elem, const String &name, String &result);
2823     /**
2824      * Get an element value, performing substitutions if necessary
2825      */
2826     bool getValue(Element *elem, String &result);
2828 protected:
2830     /**
2831      *  The path to the file associated with this object
2832      */     
2833     URI uri;
2836     /**
2837      *  Print a printf()-like formatted error message
2838      */
2839     void error(char *fmt, ...);
2841     /**
2842      *  Print a printf()-like formatted trace message
2843      */
2844     void status(char *fmt, ...);
2846     /**
2847      *  Print a printf()-like formatted trace message
2848      */
2849     void trace(char *fmt, ...);
2851     /**
2852      *  Check if a given string matches a given regex pattern
2853      */
2854     bool regexMatch(const String &str, const String &pattern);
2856     /**
2857      *
2858      */
2859     String getSuffix(const String &fname);
2861     /**
2862      * Break up a string into substrings delimited the characters
2863      * in delimiters.  Null-length substrings are ignored
2864      */  
2865     std::vector<String> tokenize(const String &val,
2866                               const String &delimiters);
2868     /**
2869      *  replace runs of whitespace with a space
2870      */
2871     String strip(const String &s);
2873     /**
2874      *  remove leading whitespace from each line
2875      */
2876     String leftJustify(const String &s);
2878     /**
2879      *  remove leading and trailing whitespace from string
2880      */
2881     String trim(const String &s);
2883     /**
2884      * Return the native format of the canonical
2885      * path which we store
2886      */
2887     String getNativePath(const String &path);
2889     /**
2890      * Execute a shell command.  Outbuf is a ref to a string
2891      * to catch the result.     
2892      */      
2893     bool executeCommand(const String &call,
2894                             const String &inbuf,
2895                                                 String &outbuf,
2896                                                 String &errbuf);
2897     /**
2898      * List all directories in a given base and starting directory
2899      * It is usually called like:
2900      *           bool ret = listDirectories("src", "", result);    
2901      */      
2902     bool listDirectories(const String &baseName,
2903                          const String &dirname,
2904                          std::vector<String> &res);
2906     /**
2907      * Find all files in the named directory 
2908      */      
2909     bool listFiles(const String &baseName,
2910                    const String &dirname,
2911                    std::vector<String> &result);
2913     /**
2914      * Perform a listing for a fileset 
2915      */      
2916     bool listFiles(MakeBase &propRef, FileSet &fileSet);
2918     /**
2919      * Parse a <patternset>
2920      */  
2921     bool parsePatternSet(Element *elem,
2922                        MakeBase &propRef,
2923                                            std::vector<String> &includes,
2924                                            std::vector<String> &excludes);
2926     /**
2927      * Parse a <fileset> entry, and determine which files
2928      * should be included
2929      */  
2930     bool parseFileSet(Element *elem,
2931                     MakeBase &propRef,
2932                                         FileSet &fileSet);
2934     /**
2935      * Return this object's property list
2936      */
2937     virtual std::map<String, String> &getProperties()
2938         { return properties; }
2940     /**
2941      * Return a named property if found, else a null string
2942      */
2943     virtual String getProperty(const String &name)
2944         {
2945         String val;
2946         std::map<String, String>::iterator iter;
2947         iter = properties.find(name);
2948         if (iter != properties.end())
2949             val = iter->second;
2950         return val;
2951         }
2954     std::map<String, String> properties;
2956     /**
2957      * Turn 'true' and 'false' into boolean values
2958      */             
2959     bool getBool(const String &str, bool &val);
2961     /**
2962      * Create a directory, making intermediate dirs
2963      * if necessary
2964      */                     
2965     bool createDirectory(const String &dirname);
2967     /**
2968      * Delete a directory and its children if desired
2969      */
2970     bool removeDirectory(const String &dirName);
2972     /**
2973      * Copy a file from one name to another. Perform only if needed
2974      */ 
2975     bool copyFile(const String &srcFile, const String &destFile);
2977     /**
2978      * Tests if the file exists and is a regular file
2979      */ 
2980     bool isRegularFile(const String &fileName);
2982     /**
2983      * Tests if the file exists and is a directory
2984      */ 
2985     bool isDirectory(const String &fileName);
2987     /**
2988      * Tests is the modification date of fileA is newer than fileB
2989      */ 
2990     bool isNewerThan(const String &fileA, const String &fileB);
2992 private:
2994     /**
2995      * replace variable refs like ${a} with their values
2996      */      
2997     bool getSubstitutions(const String &s, String &result);
3001 };
3006 /**
3007  *  Print a printf()-like formatted error message
3008  */
3009 void MakeBase::error(char *fmt, ...)
3011     va_list args;
3012     va_start(args,fmt);
3013     fprintf(stderr, "Make error: ");
3014     vfprintf(stderr, fmt, args);
3015     fprintf(stderr, "\n");
3016     va_end(args) ;
3021 /**
3022  *  Print a printf()-like formatted trace message
3023  */
3024 void MakeBase::status(char *fmt, ...)
3026     va_list args;
3027     va_start(args,fmt);
3028     //fprintf(stdout, " ");
3029     vfprintf(stdout, fmt, args);
3030     fprintf(stdout, "\n");
3031     va_end(args) ;
3036 /**
3037  *  Resolve another path relative to this one
3038  */
3039 String MakeBase::resolve(const String &otherPath)
3041     URI otherURI(otherPath);
3042     URI fullURI = uri.resolve(otherURI);
3043     String ret = fullURI.toString();
3044     return ret;
3048 /**
3049  *  Print a printf()-like formatted trace message
3050  */
3051 void MakeBase::trace(char *fmt, ...)
3053     va_list args;
3054     va_start(args,fmt);
3055     fprintf(stdout, "Make: ");
3056     vfprintf(stdout, fmt, args);
3057     fprintf(stdout, "\n");
3058     va_end(args) ;
3063 /**
3064  *  Check if a given string matches a given regex pattern
3065  */
3066 bool MakeBase::regexMatch(const String &str, const String &pattern)
3068         const TRexChar *terror = NULL;
3069         const TRexChar *cpat = pattern.c_str();
3070         TRex *expr = trex_compile(cpat, &terror);
3071         if (!expr)
3072             {
3073             if (!terror)
3074                 terror = "undefined";
3075                 error("compilation error [%s]!\n", terror);
3076             return false;
3077                 } 
3079     bool ret = true;
3081         const TRexChar *cstr = str.c_str();
3082         if (trex_match(expr, cstr))
3083                 {
3084                 ret = true;
3085                 }
3086         else
3087             {
3088                 ret = false;
3089                 }
3091         trex_free(expr);
3093     return ret;
3096 /**
3097  *  Return the suffix, if any, of a file name
3098  */
3099 String MakeBase::getSuffix(const String &fname)
3101     if (fname.size() < 2)
3102         return "";
3103     unsigned int pos = fname.find_last_of('.');
3104     if (pos == fname.npos)
3105         return "";
3106     pos++;
3107     String res = fname.substr(pos, fname.size()-pos);
3108     //trace("suffix:%s", res.c_str()); 
3109     return res;
3114 /**
3115  * Break up a string into substrings delimited the characters
3116  * in delimiters.  Null-length substrings are ignored
3117  */  
3118 std::vector<String> MakeBase::tokenize(const String &str,
3119                                 const String &delimiters)
3122     std::vector<String> res;
3123     char *del = (char *)delimiters.c_str();
3124     String dmp;
3125     for (unsigned int i=0 ; i<str.size() ; i++)
3126         {
3127         char ch = str[i];
3128         char *p = (char *)0;
3129         for (p=del ; *p ; p++)
3130             if (*p == ch)
3131                 break;
3132         if (*p)
3133             {
3134             if (dmp.size() > 0)
3135                 {
3136                 res.push_back(dmp);
3137                 dmp.clear();
3138                 }
3139             }
3140         else
3141             {
3142             dmp.push_back(ch);
3143             }
3144         }
3145     //Add tail
3146     if (dmp.size() > 0)
3147         {
3148         res.push_back(dmp);
3149         dmp.clear();
3150         }
3152     return res;
3157 /**
3158  *  replace runs of whitespace with a single space
3159  */
3160 String MakeBase::strip(const String &s)
3162     int len = s.size();
3163     String stripped;
3164     for (int i = 0 ; i<len ; i++)
3165         {
3166         char ch = s[i];
3167         if (isspace(ch))
3168             {
3169             stripped.push_back(' ');
3170             for ( ; i<len ; i++)
3171                 {
3172                 ch = s[i];
3173                 if (!isspace(ch))
3174                     {
3175                     stripped.push_back(ch);
3176                     break;
3177                     }
3178                 }
3179             }
3180         else
3181             {
3182             stripped.push_back(ch);
3183             }
3184         }
3185     return stripped;
3188 /**
3189  *  remove leading whitespace from each line
3190  */
3191 String MakeBase::leftJustify(const String &s)
3193     String out;
3194     int len = s.size();
3195     for (int i = 0 ; i<len ; )
3196         {
3197         char ch;
3198         //Skip to first visible character
3199         while (i<len)
3200             {
3201                         ch = s[i];
3202                         if (ch == '\n' || ch == '\r'
3203                           || !isspace(ch))
3204                               break;
3205                         i++;
3206                         }
3207         //Copy the rest of the line
3208                 while (i<len)
3209                     {
3210                     ch = s[i];
3211             if (ch == '\n' || ch == '\r')
3212                 {
3213                 if (ch != '\r')
3214                     out.push_back('\n');
3215                 i++;
3216                 break;
3217                 }
3218             else
3219                 {
3220                 out.push_back(ch);
3221                 }
3222             i++;
3223             }
3224         }
3225     return out;
3229 /**
3230  *  Removes whitespace from beginning and end of a string
3231  */
3232 String MakeBase::trim(const String &s)
3234     if (s.size() < 1)
3235         return s;
3236     
3237     //Find first non-ws char
3238     unsigned int begin = 0;
3239     for ( ; begin < s.size() ; begin++)
3240         {
3241         if (!isspace(s[begin]))
3242             break;
3243         }
3245     //Find first non-ws char, going in reverse
3246     unsigned int end = s.size() - 1;
3247     for ( ; end > begin ; end--)
3248         {
3249         if (!isspace(s[end]))
3250             break;
3251         }
3252     //trace("begin:%d  end:%d", begin, end);
3254     String res = s.substr(begin, end-begin+1);
3255     return res;
3258 /**
3259  * Return the native format of the canonical
3260  * path which we store
3261  */
3262 String MakeBase::getNativePath(const String &path)
3264 #ifdef __WIN32__
3265     String npath;
3266     unsigned int firstChar = 0;
3267     if (path.size() >= 3)
3268         {
3269         if (path[0] == '/' &&
3270             isalpha(path[1]) &&
3271             path[2] == ':')
3272             firstChar++;
3273         }
3274     for (unsigned int i=firstChar ; i<path.size() ; i++)
3275         {
3276         char ch = path[i];
3277         if (ch == '/')
3278             npath.push_back('\\');
3279         else
3280             npath.push_back(ch);
3281         }
3282     return npath;
3283 #else
3284     return path;
3285 #endif
3289 #ifdef __WIN32__
3290 #include <tchar.h>
3292 static String win32LastError()
3295     DWORD dw = GetLastError(); 
3297     LPVOID str;
3298     FormatMessage(
3299         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3300         FORMAT_MESSAGE_FROM_SYSTEM,
3301         NULL,
3302         dw,
3303         0,
3304         (LPTSTR) &str,
3305         0, NULL );
3306     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3307     if(p != NULL)
3308         { // lose CRLF
3309         *p = _T('\0');
3310         }
3311     String ret = (char *)str;
3312     LocalFree(str);
3314     return ret;
3316 #endif
3320 /**
3321  * Execute a system call, using pipes to send data to the
3322  * program's stdin,  and reading stdout and stderr.
3323  */
3324 bool MakeBase::executeCommand(const String &command,
3325                               const String &inbuf,
3326                                                           String &outbuf,
3327                                                           String &errbuf)
3330     status("============ cmd ============\n%s\n=============================",
3331                     command.c_str());
3333     outbuf.clear();
3334     errbuf.clear();
3335     
3336 #ifdef __WIN32__
3338     /*
3339     I really hate having win32 code in this program, but the
3340     read buffer in command.com and cmd.exe are just too small
3341     for the large commands we need for compiling and linking.
3342     */
3344     bool ret = true;
3346     //# Allocate a separate buffer for safety
3347     char *paramBuf = new char[command.size() + 1];
3348     if (!paramBuf)
3349        {
3350        error("executeCommand cannot allocate command buffer");
3351            return false;
3352        }
3353     strcpy(paramBuf, (char *)command.c_str());
3355     //# Create pipes
3356     SECURITY_ATTRIBUTES saAttr; 
3357     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3358     saAttr.bInheritHandle = TRUE; 
3359     saAttr.lpSecurityDescriptor = NULL; 
3360     HANDLE stdinRead,  stdinWrite;
3361     HANDLE stdoutRead, stdoutWrite;
3362     HANDLE stderrRead, stderrWrite;
3363     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3364             {
3365                 error("executeProgram: could not create pipe");
3366         delete[] paramBuf;
3367                 return false;
3368                 } 
3369     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3370         if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3371             {
3372                 error("executeProgram: could not create pipe");
3373         delete[] paramBuf;
3374                 return false;
3375                 } 
3376     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3377         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3378             {
3379                 error("executeProgram: could not create pipe");
3380         delete[] paramBuf;
3381                 return false;
3382                 } 
3383     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3385     // Create the process
3386     STARTUPINFO siStartupInfo;
3387     PROCESS_INFORMATION piProcessInfo;
3388     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3389     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3390     siStartupInfo.cb = sizeof(siStartupInfo);
3391     siStartupInfo.hStdError   =  stderrWrite;
3392     siStartupInfo.hStdOutput  =  stdoutWrite;
3393     siStartupInfo.hStdInput   =  stdinRead;
3394     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3395    
3396     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3397                 0, NULL, NULL, &siStartupInfo,
3398                 &piProcessInfo))
3399         {
3400         error("executeCommand : could not create process : %s",
3401                             win32LastError().c_str());
3402         ret = false;
3403         }
3405     DWORD bytesWritten;
3406     if (inbuf.size()>0 &&
3407         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3408                &bytesWritten, NULL))
3409         {
3410         error("executeCommand: could not write to pipe");
3411                 return false;
3412                 }       
3413     if (!CloseHandle(stdinWrite))
3414             {           
3415         error("executeCommand: could not close write pipe");
3416                 return false;
3417                 }
3418     if (!CloseHandle(stdoutWrite))
3419             {
3420         error("executeCommand: could not close read pipe");
3421                 return false;
3422                 }
3423     if (!CloseHandle(stderrWrite))
3424             {
3425         error("executeCommand: could not close read pipe");
3426                 return false;
3427                 }
3428         while (true)
3429         {
3430         //trace("## stderr");
3431         DWORD avail;
3432         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3433             break;
3434         if (avail > 0)
3435             {
3436             DWORD bytesRead = 0;
3437             char readBuf[1025];
3438             if (avail>1024) avail = 1024;
3439             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3440                 || bytesRead == 0)
3441                 {
3442                 break;
3443                 }
3444             for (unsigned int i=0 ; i<bytesRead ; i++)
3445                 errbuf.push_back(readBuf[i]);
3446             }
3447         //trace("## stdout");
3448         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3449             break;
3450         if (avail > 0)
3451             {
3452             DWORD bytesRead = 0;
3453             char readBuf[1025];
3454             if (avail>1024) avail = 1024;
3455             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3456                 || bytesRead==0)
3457                 {
3458                 break;
3459                 }
3460             for (unsigned int i=0 ; i<bytesRead ; i++)
3461                 outbuf.push_back(readBuf[i]);
3462             }
3463                 DWORD exitCode;
3464         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3465         if (exitCode != STILL_ACTIVE)
3466             break;
3467         Sleep(100);
3468         }       
3469     //trace("outbuf:%s", outbuf.c_str());
3470     if (!CloseHandle(stdoutRead))
3471         {
3472         error("executeCommand: could not close read pipe");
3473         return false;
3474         }
3475     if (!CloseHandle(stderrRead))
3476         {
3477         error("executeCommand: could not close read pipe");
3478         return false;
3479         }
3481     DWORD exitCode;
3482     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3483     //trace("exit code:%d", exitCode);
3484     if (exitCode != 0)
3485         {
3486         ret = false;
3487         }
3488     
3489     // Clean up
3490     CloseHandle(piProcessInfo.hProcess);
3491     CloseHandle(piProcessInfo.hThread);
3494     return ret;
3496 #else //do it unix-style
3498     String s;
3499     FILE *f = popen(command.c_str(), "r");
3500     int errnum = 0;
3501     if (f)
3502         {
3503         while (true)
3504             {
3505             int ch = fgetc(f);
3506             if (ch < 0)
3507                 break;
3508             s.push_back((char)ch);
3509             }
3510         errnum = pclose(f);
3511         }
3512     outbuf = s;
3513     if (errnum != 0)
3514         {
3515         error("exec of command '%s' failed : %s",
3516                      command.c_str(), strerror(errno));
3517         return false;
3518         }
3519     else
3520         return true;
3522 #endif
3523
3528 bool MakeBase::listDirectories(const String &baseName,
3529                               const String &dirName,
3530                               std::vector<String> &res)
3532     res.push_back(dirName);
3533     String fullPath = baseName;
3534     if (dirName.size()>0)
3535         {
3536         fullPath.append("/");
3537         fullPath.append(dirName);
3538         }
3539     DIR *dir = opendir(fullPath.c_str());
3540     while (true)
3541         {
3542         struct dirent *de = readdir(dir);
3543         if (!de)
3544             break;
3546         //Get the directory member name
3547         String s = de->d_name;
3548         if (s.size() == 0 || s[0] == '.')
3549             continue;
3550         String childName = dirName;
3551         childName.append("/");
3552         childName.append(s);
3554         String fullChildPath = baseName;
3555         fullChildPath.append("/");
3556         fullChildPath.append(childName);
3557         struct stat finfo;
3558         String childNative = getNativePath(fullChildPath);
3559         if (stat(childNative.c_str(), &finfo)<0)
3560             {
3561             error("cannot stat file:%s", childNative.c_str());
3562             }
3563         else if (S_ISDIR(finfo.st_mode))
3564             {
3565             //trace("directory: %s", childName.c_str());
3566             if (!listDirectories(baseName, childName, res))
3567                 return false;
3568             }
3569         }
3570     closedir(dir);
3572     return true;
3576 bool MakeBase::listFiles(const String &baseDir,
3577                          const String &dirName,
3578                          std::vector<String> &res)
3580     String fullDir = baseDir;
3581     if (dirName.size()>0)
3582         {
3583         fullDir.append("/");
3584         fullDir.append(dirName);
3585         }
3586     String dirNative = getNativePath(fullDir);
3588     std::vector<String> subdirs;
3589     DIR *dir = opendir(dirNative.c_str());
3590     if (!dir)
3591         {
3592         error("Could not open directory %s : %s",
3593               dirNative.c_str(), strerror(errno));
3594         return false;
3595         }
3596     while (true)
3597         {
3598         struct dirent *de = readdir(dir);
3599         if (!de)
3600             break;
3602         //Get the directory member name
3603         String s = de->d_name;
3604         if (s.size() == 0 || s[0] == '.')
3605             continue;
3606         String childName;
3607         if (dirName.size()>0)
3608             {
3609             childName.append(dirName);
3610             childName.append("/");
3611             }
3612         childName.append(s);
3613         String fullChild = baseDir;
3614         fullChild.append("/");
3615         fullChild.append(childName);
3616         
3617         if (isDirectory(fullChild))
3618             {
3619             //trace("directory: %s", childName.c_str());
3620             if (!listFiles(baseDir, childName, res))
3621                 return false;
3622             continue;
3623             }
3624         else if (!isRegularFile(fullChild))
3625             {
3626             error("unknown file:%s", childName.c_str());
3627             return false;
3628             }
3630        //all done!
3631         res.push_back(childName);
3633         }
3634     closedir(dir);
3636     return true;
3640 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3642     String baseDir = propRef.resolve(fileSet.getDirectory());
3643     std::vector<String> fileList;
3644     if (!listFiles(baseDir, "", fileList))
3645             return false;
3647     std::vector<String> includes = fileSet.getIncludes();
3648     std::vector<String> excludes = fileSet.getExcludes();
3650     std::vector<String> incs;
3651     std::vector<String>::iterator iter;
3653     std::sort(fileList.begin(), fileList.end());
3655     //If there are <includes>, then add files to the output
3656     //in the order of the include list
3657     if (includes.size()==0)
3658             incs = fileList;
3659         else
3660             {
3661         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3662             {
3663             String pattern = *iter;
3664             std::vector<String>::iterator siter;
3665             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3666                 {
3667                 String s = *siter;
3668                 if (regexMatch(s, pattern))
3669                     {
3670                     //trace("INCLUDED:%s", s.c_str());
3671                     incs.push_back(s);
3672                     }
3673                 }
3674             }
3675         }
3677     //Now trim off the <excludes>
3678     std::vector<String> res;
3679     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3680         {
3681         String s = *iter;
3682         bool skipme = false;
3683         std::vector<String>::iterator siter;
3684         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3685             {
3686             String pattern = *siter;
3687             if (regexMatch(s, pattern))
3688                 {
3689                 //trace("EXCLUDED:%s", s.c_str());
3690                 skipme = true;
3691                 break;
3692                 }
3693             }
3694         if (!skipme)
3695             res.push_back(s);
3696         }
3697         
3698         fileSet.setFiles(res);
3700         return true;
3707 bool MakeBase::getSubstitutions(const String &str, String &result)
3709     String s = trim(str);
3710     int len = (int)s.size();
3711     String val;
3712     for (int i=0 ; i<len ; i++)
3713         {
3714         char ch = s[i];
3715         if (ch == '$' && s[i+1] == '{')
3716                     {
3717             String varname;
3718                     int j = i+2;
3719                     for ( ; j<len ; j++)
3720                         {
3721                         ch = s[j];
3722                         if (ch == '$' && s[j+1] == '{')
3723                             {
3724                             error("attribute %s cannot have nested variable references",
3725                                    s.c_str());
3726                             return false;
3727                             }
3728                         else if (ch == '}')
3729                             {
3730                             std::map<String, String>::iterator iter;
3731                             iter = properties.find(trim(varname));
3732                             if (iter != properties.end())
3733                                 {
3734                                 val.append(iter->second);
3735                                 }
3736                             else
3737                                 {
3738                                 error("property ${%s} not found", varname.c_str());
3739                                 return false;
3740                                 }
3741                             break;
3742                             }
3743                         else
3744                             {
3745                             varname.push_back(ch);
3746                             }
3747                         }
3748                     i = j;
3749                         }
3750                 else
3751                     {
3752                     val.push_back(ch);
3753                     }
3754         }
3755     result = val;
3756     return true;
3760 bool MakeBase::getAttribute(Element *elem, const String &name,
3761                                     String &result)
3763     String s = elem->getAttribute(name);
3764     return getSubstitutions(s, result);
3768 bool MakeBase::getValue(Element *elem, String &result)
3770     String s = elem->getValue();
3771     //Replace all runs of whitespace with a single space
3772     return getSubstitutions(s, result);
3776 /**
3777  * Turn 'true' and 'false' into boolean values
3778  */                 
3779 bool MakeBase::getBool(const String &str, bool &val)
3781     if (str == "true")
3782         val = true;
3783     else if (str == "false")
3784         val = false;
3785     else
3786         {
3787         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3788         return false;
3789         }
3790     return true;
3796 /**
3797  * Parse a <patternset> entry
3798  */  
3799 bool MakeBase::parsePatternSet(Element *elem,
3800                           MakeBase &propRef,
3801                                                   std::vector<String> &includes,
3802                                                   std::vector<String> &excludes
3803                                                   )
3805     std::vector<Element *> children  = elem->getChildren();
3806     for (unsigned int i=0 ; i<children.size() ; i++)
3807         {
3808         Element *child = children[i];
3809         String tagName = child->getName();
3810         if (tagName == "exclude")
3811             {
3812             String fname;
3813                         if (!propRef.getAttribute(child, "name", fname))
3814                             return false;
3815             //trace("EXCLUDE: %s", fname.c_str());
3816             excludes.push_back(fname);
3817             }
3818         else if (tagName == "include")
3819             {
3820             String fname;
3821                         if (!propRef.getAttribute(child, "name", fname))
3822                             return false;
3823             //trace("INCLUDE: %s", fname.c_str());
3824             includes.push_back(fname);
3825             }
3826         }
3828     return true;
3834 /**
3835  * Parse a <fileset> entry, and determine which files
3836  * should be included
3837  */  
3838 bool MakeBase::parseFileSet(Element *elem,
3839                           MakeBase &propRef,
3840                                                   FileSet &fileSet)
3842     String name = elem->getName();
3843     if (name != "fileset")
3844         {
3845         error("expected <fileset>");
3846         return false;
3847         }
3850     std::vector<String> includes;
3851     std::vector<String> excludes;
3853     //A fileset has one implied patternset
3854     if (!parsePatternSet(elem, propRef, includes, excludes))
3855         {
3856         return false;
3857         }
3858     //Look for child tags, including more patternsets
3859     std::vector<Element *> children  = elem->getChildren();
3860     for (unsigned int i=0 ; i<children.size() ; i++)
3861         {
3862         Element *child = children[i];
3863         String tagName = child->getName();
3864         if (tagName == "patternset")
3865             {
3866             if (!parsePatternSet(child, propRef, includes, excludes))
3867                 {
3868                 return false;
3869                 }
3870             }
3871         }
3873     String dir;
3874     //Now do the stuff
3875     //Get the base directory for reading file names
3876     if (!propRef.getAttribute(elem, "dir", dir))
3877         return false;
3879     fileSet.setDirectory(dir);
3880     fileSet.setIncludes(includes);
3881     fileSet.setExcludes(excludes);
3882     
3883     /*
3884     std::vector<String> fileList;
3885     if (dir.size() > 0)
3886         {
3887         String baseDir = propRef.resolve(dir);
3888             if (!listFiles(baseDir, "", includes, excludes, fileList))
3889                 return false;
3890             }
3891     std::sort(fileList.begin(), fileList.end());
3892         result = fileList;
3893         */
3895         
3896         /*
3897         for (unsigned int i=0 ; i<result.size() ; i++)
3898             {
3899             trace("RES:%s", result[i].c_str());
3900             }
3901     */
3903     
3904     return true;
3909 /**
3910  * Create a directory, making intermediate dirs
3911  * if necessary
3912  */                         
3913 bool MakeBase::createDirectory(const String &dirname)
3915     //trace("## createDirectory: %s", dirname.c_str());
3916     //## first check if it exists
3917     struct stat finfo;
3918     String nativeDir = getNativePath(dirname);
3919     char *cnative = (char *) nativeDir.c_str();
3920 #ifdef __WIN32__
3921     if (strlen(cnative)==2 && cnative[1]==':')
3922         return true;
3923 #endif
3924     if (stat(cnative, &finfo)==0)
3925         {
3926         if (!S_ISDIR(finfo.st_mode))
3927             {
3928             error("mkdir: file %s exists but is not a directory",
3929                               cnative);
3930             return false;
3931             }
3932         else //exists
3933             {
3934             return true;
3935             }
3936         }
3938     //## 2: pull off the last path segment, if any,
3939     //## to make the dir 'above' this one, if necessary
3940     unsigned int pos = dirname.find_last_of('/');
3941     if (pos>0 && pos != dirname.npos)
3942         {
3943         String subpath = dirname.substr(0, pos);
3944         //A letter root (c:) ?
3945         if (!createDirectory(subpath))
3946             return false;
3947         }
3948         
3949     //## 3: now make
3950 #ifdef __WIN32__
3951     if (mkdir(cnative)<0)
3952 #else
3953     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3954 #endif
3955         {
3956         error("cannot make directory '%s' : %s",
3957                  cnative, strerror(errno));
3958         return false;
3959         }
3960         
3961     return true;
3965 /**
3966  * Remove a directory recursively
3967  */ 
3968 bool MakeBase::removeDirectory(const String &dirName)
3970     char *dname = (char *)dirName.c_str();
3972     DIR *dir = opendir(dname);
3973     if (!dir)
3974         {
3975         //# Let this fail nicely.
3976         return true;
3977         //error("error opening directory %s : %s", dname, strerror(errno));
3978         //return false;
3979         }
3980     
3981     while (true)
3982         {
3983         struct dirent *de = readdir(dir);
3984         if (!de)
3985             break;
3987         //Get the directory member name
3988         String s = de->d_name;
3989         if (s.size() == 0 || s[0] == '.')
3990             continue;
3991         String childName;
3992         if (dirName.size() > 0)
3993             {
3994             childName.append(dirName);
3995             childName.append("/");
3996             }
3997         childName.append(s);
4000         struct stat finfo;
4001         String childNative = getNativePath(childName);
4002         char *cnative = (char *)childNative.c_str();
4003         if (stat(cnative, &finfo)<0)
4004             {
4005             error("cannot stat file:%s", cnative);
4006             }
4007         else if (S_ISDIR(finfo.st_mode))
4008             {
4009             //trace("DEL dir: %s", childName.c_str());
4010                         if (!removeDirectory(childName))
4011                     {
4012                             return false;
4013                             }
4014             }
4015         else if (!S_ISREG(finfo.st_mode))
4016             {
4017             //trace("not regular: %s", cnative);
4018             }
4019         else
4020             {
4021             //trace("DEL file: %s", childName.c_str());
4022             if (remove(cnative)<0)
4023                 {
4024                 error("error deleting %s : %s",
4025                                      cnative, strerror(errno));
4026                                 return false;
4027                                 }
4028             }
4029         }
4030     closedir(dir);
4032     //Now delete the directory
4033     String native = getNativePath(dirName);
4034     if (rmdir(native.c_str())<0)
4035         {
4036         error("could not delete directory %s : %s",
4037             native.c_str() , strerror(errno));
4038         return false;
4039         }
4041     return true;
4042     
4046 /**
4047  * Copy a file from one name to another. Perform only if needed
4048  */ 
4049 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4051     //# 1 Check up-to-date times
4052     String srcNative = getNativePath(srcFile);
4053     struct stat srcinfo;
4054     if (stat(srcNative.c_str(), &srcinfo)<0)
4055         {
4056         error("source file %s for copy does not exist",
4057                          srcNative.c_str());
4058         return false;
4059         }
4061     String destNative = getNativePath(destFile);
4062     struct stat destinfo;
4063     if (stat(destNative.c_str(), &destinfo)==0)
4064         {
4065         if (destinfo.st_mtime >= srcinfo.st_mtime)
4066             return true;
4067         }
4068         
4069     //# 2 prepare a destination directory if necessary
4070     unsigned int pos = destFile.find_last_of('/');
4071     if (pos != destFile.npos)
4072         {
4073         String subpath = destFile.substr(0, pos);
4074         if (!createDirectory(subpath))
4075             return false;
4076         }
4078     //# 3 do the data copy
4079 #ifndef __WIN32__
4081     FILE *srcf = fopen(srcNative.c_str(), "rb");
4082     if (!srcf)
4083         {
4084         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4085         return false;
4086         }
4087     FILE *destf = fopen(destNative.c_str(), "wb");
4088     if (!destf)
4089         {
4090         error("copyFile cannot open %s for writing", srcNative.c_str());
4091         return false;
4092         }
4094     while (!feof(srcf))
4095         {
4096         int ch = fgetc(srcf);
4097         if (ch<0)
4098             break;
4099         fputc(ch, destf);
4100         }
4102     fclose(destf);
4103     fclose(srcf);
4105 #else
4106     
4107     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4108         {
4109         error("copyFile from %s to %s failed",
4110                      srcNative.c_str(), destNative.c_str());
4111         return false;
4112         }
4113         
4114 #endif /* __WIN32__ */
4117     return true;
4122 /**
4123  * Tests if the file exists and is a regular file
4124  */ 
4125 bool MakeBase::isRegularFile(const String &fileName)
4127     String native = getNativePath(fileName);
4128     struct stat finfo;
4129     
4130     //Exists?
4131     if (stat(native.c_str(), &finfo)<0)
4132                 return false;
4135     //check the file mode
4136     if (!S_ISREG(finfo.st_mode))
4137                 return false;
4139     return true;
4142 /**
4143  * Tests if the file exists and is a directory
4144  */ 
4145 bool MakeBase::isDirectory(const String &fileName)
4147     String native = getNativePath(fileName);
4148     struct stat finfo;
4149     
4150     //Exists?
4151     if (stat(native.c_str(), &finfo)<0)
4152                 return false;
4155     //check the file mode
4156     if (!S_ISDIR(finfo.st_mode))
4157                 return false;
4159     return true;
4164 /**
4165  * Tests is the modification of fileA is newer than fileB
4166  */ 
4167 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4169     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4170     String nativeA = getNativePath(fileA);
4171     struct stat infoA;
4172     //IF source does not exist, NOT newer
4173     if (stat(nativeA.c_str(), &infoA)<0)
4174         {
4175                 return false;
4176                 }
4178     String nativeB = getNativePath(fileB);
4179     struct stat infoB;
4180     //IF dest does not exist, YES, newer
4181     if (stat(nativeB.c_str(), &infoB)<0)
4182         {
4183                 return true;
4184                 }
4186     //check the actual times
4187     if (infoA.st_mtime > infoB.st_mtime)
4188         {
4189                 return true;
4190                 }
4192     return false;
4196 //########################################################################
4197 //# P K G    C O N F I G
4198 //########################################################################
4200 /**
4201  *
4202  */
4203 class PkgConfig : public MakeBase
4206 public:
4208     /**
4209      *
4210      */
4211     PkgConfig()
4212         { init(); }
4214     /**
4215      *
4216      */
4217     PkgConfig(const String &namearg)
4218         { init(); name = namearg; }
4220     /**
4221      *
4222      */
4223     PkgConfig(const PkgConfig &other)
4224         { assign(other); }
4226     /**
4227      *
4228      */
4229     PkgConfig &operator=(const PkgConfig &other)
4230         { assign(other); return *this; }
4232     /**
4233      *
4234      */
4235     virtual ~PkgConfig()
4236         { }
4238     /**
4239      *
4240      */
4241     virtual String getName()
4242         { return name; }
4244     /**
4245      *
4246      */
4247     virtual String getDescription()
4248         { return description; }
4250     /**
4251      *
4252      */
4253     virtual String getCflags()
4254         { return cflags; }
4256     /**
4257      *
4258      */
4259     virtual String getLibs()
4260         { return libs; }
4262     /**
4263      *
4264      */
4265     virtual String getVersion()
4266         { return version; }
4268     /**
4269      *
4270      */
4271     virtual int getMajorVersion()
4272         { return majorVersion; }
4274     /**
4275      *
4276      */
4277     virtual int getMinorVersion()
4278         { return minorVersion; }
4280     /**
4281      *
4282      */
4283     virtual int getMicroVersion()
4284         { return microVersion; }
4286     /**
4287      *
4288      */
4289     virtual std::map<String, String> &getAttributes()
4290         { return attrs; }
4292     /**
4293      *
4294      */
4295     virtual std::vector<String> &getRequireList()
4296         { return requireList; }
4298     virtual bool readFile(const String &fileName);
4300 private:
4302     void init()
4303         {
4304         name         = "";
4305         description  = "";
4306         cflags       = "";
4307         libs         = "";
4308         requires     = "";
4309         version      = "";
4310         majorVersion = 0;
4311         minorVersion = 0;
4312         microVersion = 0;
4313         fileName     = "";
4314         attrs.clear();
4315         requireList.clear();
4316         }
4318     void assign(const PkgConfig &other)
4319         {
4320         name         = other.name;
4321         description  = other.description;
4322         cflags       = other.cflags;
4323         libs         = other.libs;
4324         requires     = other.requires;
4325         version      = other.version;
4326         majorVersion = other.majorVersion;
4327         minorVersion = other.minorVersion;
4328         microVersion = other.microVersion;
4329         fileName     = other.fileName;
4330         attrs        = other.attrs;
4331         requireList  = other.requireList;
4332         }
4336     int get(int pos);
4338     int skipwhite(int pos);
4340     int getword(int pos, String &ret);
4342     void parseRequires();
4344     void parseVersion();
4346     bool parse(const String &buf);
4348     void dumpAttrs();
4350     String name;
4352     String description;
4354     String cflags;
4356     String libs;
4358     String requires;
4360     String version;
4362     int majorVersion;
4364     int minorVersion;
4366     int microVersion;
4368     String fileName;
4370     std::map<String, String> attrs;
4372     std::vector<String> requireList;
4374     char *parsebuf;
4375     int parselen;
4376 };
4379 /**
4380  * Get a character from the buffer at pos.  If out of range,
4381  * return -1 for safety
4382  */
4383 int PkgConfig::get(int pos)
4385     if (pos>parselen)
4386         return -1;
4387     return parsebuf[pos];
4392 /**
4393  *  Skip over all whitespace characters beginning at pos.  Return
4394  *  the position of the first non-whitespace character.
4395  */
4396 int PkgConfig::skipwhite(int pos)
4398     while (pos < parselen)
4399         {
4400         int ch = get(pos);
4401         if (ch < 0)
4402             break;
4403         if (!isspace(ch))
4404             break;
4405         pos++;
4406         }
4407     return pos;
4411 /**
4412  *  Parse the buffer beginning at pos, for a word.  Fill
4413  *  'ret' with the result.  Return the position after the
4414  *  word.
4415  */
4416 int PkgConfig::getword(int pos, String &ret)
4418     while (pos < parselen)
4419         {
4420         int ch = get(pos);
4421         if (ch < 0)
4422             break;
4423         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4424             break;
4425         ret.push_back((char)ch);
4426         pos++;
4427         }
4428     return pos;
4431 void PkgConfig::parseRequires()
4433     if (requires.size() == 0)
4434         return;
4435     parsebuf = (char *)requires.c_str();
4436     parselen = requires.size();
4437     int pos = 0;
4438     while (pos < parselen)
4439         {
4440         pos = skipwhite(pos);
4441         String val;
4442         int pos2 = getword(pos, val);
4443         if (pos2 == pos)
4444             break;
4445         pos = pos2;
4446         //trace("val %s", val.c_str());
4447         requireList.push_back(val);
4448         }
4451 static int getint(const String str)
4453     char *s = (char *)str.c_str();
4454     char *ends = NULL;
4455     long val = strtol(s, &ends, 10);
4456     if (ends == s)
4457         return 0L;
4458     else
4459         return val;
4462 void PkgConfig::parseVersion()
4464     if (version.size() == 0)
4465         return;
4466     String s1, s2, s3;
4467     unsigned int pos = 0;
4468     unsigned int pos2 = version.find('.', pos);
4469     if (pos2 == version.npos)
4470         {
4471         s1 = version;
4472         }
4473     else
4474         {
4475         s1 = version.substr(pos, pos2-pos);
4476         pos = pos2;
4477         pos++;
4478         if (pos < version.size())
4479             {
4480             pos2 = version.find('.', pos);
4481             if (pos2 == version.npos)
4482                 {
4483                 s2 = version.substr(pos, version.size()-pos);
4484                 }
4485             else
4486                 {
4487                 s2 = version.substr(pos, pos2-pos);
4488                 pos = pos2;
4489                 pos++;
4490                 if (pos < version.size())
4491                     s3 = version.substr(pos, pos2-pos);
4492                 }
4493             }
4494         }
4496     majorVersion = getint(s1);
4497     minorVersion = getint(s2);
4498     microVersion = getint(s3);
4499     //trace("version:%d.%d.%d", majorVersion,
4500     //          minorVersion, microVersion );
4504 bool PkgConfig::parse(const String &buf)
4506     init();
4508     parsebuf = (char *)buf.c_str();
4509     parselen = buf.size();
4510     int pos = 0;
4513     while (pos < parselen)
4514         {
4515         String attrName;
4516         pos = skipwhite(pos);
4517         int ch = get(pos);
4518         if (ch == '#')
4519             {
4520             //comment.  eat the rest of the line
4521             while (pos < parselen)
4522                 {
4523                 ch = get(pos);
4524                 if (ch == '\n' || ch < 0)
4525                     break;
4526                 pos++;
4527                 }
4528             continue;
4529             }
4530         pos = getword(pos, attrName);
4531         if (attrName.size() == 0)
4532             continue;
4533         pos = skipwhite(pos);
4534         ch = get(pos);
4535         if (ch != ':' && ch != '=')
4536             {
4537             error("expected ':' or '='");
4538             return false;
4539             }
4540         pos++;
4541         pos = skipwhite(pos);
4542         String attrVal;
4543         while (pos < parselen)
4544             {
4545             ch = get(pos);
4546             if (ch == '\n' || ch < 0)
4547                 break;
4548             else if (ch == '$' && get(pos+1) == '{')
4549                 {
4550                 //#  this is a ${substitution}
4551                 pos += 2;
4552                 String subName;
4553                 while (pos < parselen)
4554                     {
4555                     ch = get(pos);
4556                     if (ch < 0)
4557                         {
4558                         error("unterminated substitution");
4559                         return false;
4560                         }
4561                     else if (ch == '}')
4562                         break;
4563                     else
4564                         subName.push_back((char)ch);
4565                     pos++;
4566                     }
4567                 //trace("subName:%s", subName.c_str());
4568                 String subVal = attrs[subName];
4569                 //trace("subVal:%s", subVal.c_str());
4570                 attrVal.append(subVal);
4571                 }
4572             else
4573                 attrVal.push_back((char)ch);
4574             pos++;
4575             }
4577         attrVal = trim(attrVal);
4578         attrs[attrName] = attrVal;
4580         if (attrName == "Name")
4581             name = attrVal;
4582         else if (attrName == "Description")
4583             description = attrVal;
4584         else if (attrName == "Cflags")
4585             cflags = attrVal;
4586         else if (attrName == "Libs")
4587             libs = attrVal;
4588         else if (attrName == "Requires")
4589             requires = attrVal;
4590         else if (attrName == "Version")
4591             version = attrVal;
4593         //trace("name:'%s'  value:'%s'",
4594         //      attrName.c_str(), attrVal.c_str());
4595         }
4598     parseRequires();
4599     parseVersion();
4601     return true;
4604 void PkgConfig::dumpAttrs()
4606     //trace("### PkgConfig attributes for %s", fileName.c_str());
4607     std::map<String, String>::iterator iter;
4608     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4609         {
4610         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4611         }
4615 bool PkgConfig::readFile(const String &fileNameArg)
4617     fileName = fileNameArg;
4619     FILE *f = fopen(fileName.c_str(), "r");
4620     if (!f)
4621         {
4622         error("cannot open file '%s' for reading", fileName.c_str());
4623         return false;
4624         }
4625     String buf;
4626     while (true)
4627         {
4628         int ch = fgetc(f);
4629         if (ch < 0)
4630             break;
4631         buf.push_back((char)ch);
4632         }
4633     fclose(f);
4635     //trace("####### File:\n%s", buf.c_str());
4636     if (!parse(buf))
4637         {
4638         return false;
4639         }
4641     dumpAttrs();
4643     return true;
4650 //########################################################################
4651 //# D E P T O O L
4652 //########################################################################
4656 /**
4657  *  Class which holds information for each file.
4658  */
4659 class FileRec
4661 public:
4663     typedef enum
4664         {
4665         UNKNOWN,
4666         CFILE,
4667         HFILE,
4668         OFILE
4669         } FileType;
4671     /**
4672      *  Constructor
4673      */
4674     FileRec()
4675         {init(); type = UNKNOWN;}
4677     /**
4678      *  Copy constructor
4679      */
4680     FileRec(const FileRec &other)
4681         {init(); assign(other);}
4682     /**
4683      *  Constructor
4684      */
4685     FileRec(int typeVal)
4686         {init(); type = typeVal;}
4687     /**
4688      *  Assignment operator
4689      */
4690     FileRec &operator=(const FileRec &other)
4691         {init(); assign(other); return *this;}
4694     /**
4695      *  Destructor
4696      */
4697     ~FileRec()
4698         {}
4700     /**
4701      *  Directory part of the file name
4702      */
4703     String path;
4705     /**
4706      *  Base name, sans directory and suffix
4707      */
4708     String baseName;
4710     /**
4711      *  File extension, such as cpp or h
4712      */
4713     String suffix;
4715     /**
4716      *  Type of file: CFILE, HFILE, OFILE
4717      */
4718     int type;
4720     /**
4721      * Used to list files ref'd by this one
4722      */
4723     std::map<String, FileRec *> files;
4726 private:
4728     void init()
4729         {
4730         }
4732     void assign(const FileRec &other)
4733         {
4734         type     = other.type;
4735         baseName = other.baseName;
4736         suffix   = other.suffix;
4737         files    = other.files;
4738         }
4740 };
4744 /**
4745  *  Simpler dependency record
4746  */
4747 class DepRec
4749 public:
4751     /**
4752      *  Constructor
4753      */
4754     DepRec()
4755         {init();}
4757     /**
4758      *  Copy constructor
4759      */
4760     DepRec(const DepRec &other)
4761         {init(); assign(other);}
4762     /**
4763      *  Constructor
4764      */
4765     DepRec(const String &fname)
4766         {init(); name = fname; }
4767     /**
4768      *  Assignment operator
4769      */
4770     DepRec &operator=(const DepRec &other)
4771         {init(); assign(other); return *this;}
4774     /**
4775      *  Destructor
4776      */
4777     ~DepRec()
4778         {}
4780     /**
4781      *  Directory part of the file name
4782      */
4783     String path;
4785     /**
4786      *  Base name, without the path and suffix
4787      */
4788     String name;
4790     /**
4791      *  Suffix of the source
4792      */
4793     String suffix;
4796     /**
4797      * Used to list files ref'd by this one
4798      */
4799     std::vector<String> files;
4802 private:
4804     void init()
4805         {
4806         }
4808     void assign(const DepRec &other)
4809         {
4810         path     = other.path;
4811         name     = other.name;
4812         suffix   = other.suffix;
4813         files    = other.files;
4814         }
4816 };
4819 class DepTool : public MakeBase
4821 public:
4823     /**
4824      *  Constructor
4825      */
4826     DepTool()
4827         {init();}
4829     /**
4830      *  Copy constructor
4831      */
4832     DepTool(const DepTool &other)
4833         {init(); assign(other);}
4835     /**
4836      *  Assignment operator
4837      */
4838     DepTool &operator=(const DepTool &other)
4839         {init(); assign(other); return *this;}
4842     /**
4843      *  Destructor
4844      */
4845     ~DepTool()
4846         {}
4849     /**
4850      *  Reset this section of code
4851      */
4852     virtual void init();
4853     
4854     /**
4855      *  Reset this section of code
4856      */
4857     virtual void assign(const DepTool &other)
4858         {
4859         }
4860     
4861     /**
4862      *  Sets the source directory which will be scanned
4863      */
4864     virtual void setSourceDirectory(const String &val)
4865         { sourceDir = val; }
4867     /**
4868      *  Returns the source directory which will be scanned
4869      */
4870     virtual String getSourceDirectory()
4871         { return sourceDir; }
4873     /**
4874      *  Sets the list of files within the directory to analyze
4875      */
4876     virtual void setFileList(const std::vector<String> &list)
4877         { fileList = list; }
4879     /**
4880      * Creates the list of all file names which will be
4881      * candidates for further processing.  Reads make.exclude
4882      * to see which files for directories to leave out.
4883      */
4884     virtual bool createFileList();
4887     /**
4888      *  Generates the forward dependency list
4889      */
4890     virtual bool generateDependencies();
4893     /**
4894      *  Generates the forward dependency list, saving the file
4895      */
4896     virtual bool generateDependencies(const String &);
4899     /**
4900      *  Load a dependency file
4901      */
4902     std::vector<DepRec> loadDepFile(const String &fileName);
4904     /**
4905      *  Load a dependency file, generating one if necessary
4906      */
4907     std::vector<DepRec> getDepFile(const String &fileName,
4908               bool forceRefresh);
4910     /**
4911      *  Save a dependency file
4912      */
4913     bool saveDepFile(const String &fileName);
4916 private:
4919     /**
4920      *
4921      */
4922     void parseName(const String &fullname,
4923                    String &path,
4924                    String &basename,
4925                    String &suffix);
4927     /**
4928      *
4929      */
4930     int get(int pos);
4932     /**
4933      *
4934      */
4935     int skipwhite(int pos);
4937     /**
4938      *
4939      */
4940     int getword(int pos, String &ret);
4942     /**
4943      *
4944      */
4945     bool sequ(int pos, char *key);
4947     /**
4948      *
4949      */
4950     bool addIncludeFile(FileRec *frec, const String &fname);
4952     /**
4953      *
4954      */
4955     bool scanFile(const String &fname, FileRec *frec);
4957     /**
4958      *
4959      */
4960     bool processDependency(FileRec *ofile,
4961                            FileRec *include,
4962                            int depth);
4964     /**
4965      *
4966      */
4967     String sourceDir;
4969     /**
4970      *
4971      */
4972     std::vector<String> fileList;
4974     /**
4975      *
4976      */
4977     std::vector<String> directories;
4979     /**
4980      * A list of all files which will be processed for
4981      * dependencies.  This is the only list that has the actual
4982      * records.  All other lists have pointers to these records.     
4983      */
4984     std::map<String, FileRec *> allFiles;
4986     /**
4987      * The list of .o files, and the
4988      * dependencies upon them.
4989      */
4990     std::map<String, FileRec *> depFiles;
4992     int depFileSize;
4993     char *depFileBuf;
4995     static const int readBufSize = 8192;
4996     char readBuf[8193];//byte larger
4998 };
5004 /**
5005  *  Clean up after processing.  Called by the destructor, but should
5006  *  also be called before the object is reused.
5007  */
5008 void DepTool::init()
5010     sourceDir = ".";
5012     fileList.clear();
5013     directories.clear();
5014     
5015     //clear refs
5016     depFiles.clear();
5017     //clear records
5018     std::map<String, FileRec *>::iterator iter;
5019     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5020          delete iter->second;
5022     allFiles.clear(); 
5029 /**
5030  *  Parse a full path name into path, base name, and suffix
5031  */
5032 void DepTool::parseName(const String &fullname,
5033                         String &path,
5034                         String &basename,
5035                         String &suffix)
5037     if (fullname.size() < 2)
5038         return;
5040     unsigned int pos = fullname.find_last_of('/');
5041     if (pos != fullname.npos && pos<fullname.size()-1)
5042         {
5043         path = fullname.substr(0, pos);
5044         pos++;
5045         basename = fullname.substr(pos, fullname.size()-pos);
5046         }
5047     else
5048         {
5049         path = "";
5050         basename = fullname;
5051         }
5053     pos = basename.find_last_of('.');
5054     if (pos != basename.npos && pos<basename.size()-1)
5055         {
5056         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5057         basename = basename.substr(0, pos);
5058         }
5060     //trace("parsename:%s %s %s", path.c_str(),
5061     //        basename.c_str(), suffix.c_str()); 
5066 /**
5067  *  Generate our internal file list.
5068  */
5069 bool DepTool::createFileList()
5072     for (unsigned int i=0 ; i<fileList.size() ; i++)
5073         {
5074         String fileName = fileList[i];
5075         //trace("## FileName:%s", fileName.c_str());
5076         String path;
5077         String basename;
5078         String sfx;
5079         parseName(fileName, path, basename, sfx);
5080         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5081                     sfx == "cc" || sfx == "CC")
5082             {
5083             FileRec *fe         = new FileRec(FileRec::CFILE);
5084             fe->path            = path;
5085             fe->baseName        = basename;
5086             fe->suffix          = sfx;
5087             allFiles[fileName]  = fe;
5088             }
5089         else if (sfx == "h"   ||  sfx == "hh"  ||
5090                  sfx == "hpp" ||  sfx == "hxx")
5091             {
5092             FileRec *fe         = new FileRec(FileRec::HFILE);
5093             fe->path            = path;
5094             fe->baseName        = basename;
5095             fe->suffix          = sfx;
5096             allFiles[fileName]  = fe;
5097             }
5098         }
5100     if (!listDirectories(sourceDir, "", directories))
5101         return false;
5102         
5103     return true;
5110 /**
5111  * Get a character from the buffer at pos.  If out of range,
5112  * return -1 for safety
5113  */
5114 int DepTool::get(int pos)
5116     if (pos>depFileSize)
5117         return -1;
5118     return depFileBuf[pos];
5123 /**
5124  *  Skip over all whitespace characters beginning at pos.  Return
5125  *  the position of the first non-whitespace character.
5126  */
5127 int DepTool::skipwhite(int pos)
5129     while (pos < depFileSize)
5130         {
5131         int ch = get(pos);
5132         if (ch < 0)
5133             break;
5134         if (!isspace(ch))
5135             break;
5136         pos++;
5137         }
5138     return pos;
5142 /**
5143  *  Parse the buffer beginning at pos, for a word.  Fill
5144  *  'ret' with the result.  Return the position after the
5145  *  word.
5146  */
5147 int DepTool::getword(int pos, String &ret)
5149     while (pos < depFileSize)
5150         {
5151         int ch = get(pos);
5152         if (ch < 0)
5153             break;
5154         if (isspace(ch))
5155             break;
5156         ret.push_back((char)ch);
5157         pos++;
5158         }
5159     return pos;
5162 /**
5163  * Return whether the sequence of characters in the buffer
5164  * beginning at pos match the key,  for the length of the key
5165  */
5166 bool DepTool::sequ(int pos, char *key)
5168     while (*key)
5169         {
5170         if (*key != get(pos))
5171             return false;
5172         key++; pos++;
5173         }
5174     return true;
5179 /**
5180  *  Add an include file name to a file record.  If the name
5181  *  is not found in allFiles explicitly, try prepending include
5182  *  directory names to it and try again.
5183  */
5184 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5187     std::map<String, FileRec *>::iterator iter =
5188            allFiles.find(iname);
5189     if (iter != allFiles.end()) //already exists
5190         {
5191          //h file in same dir
5192         FileRec *other = iter->second;
5193         //trace("local: '%s'", iname.c_str());
5194         frec->files[iname] = other;
5195         return true;
5196         }
5197     else 
5198         {
5199         //look in other dirs
5200         std::vector<String>::iterator diter;
5201         for (diter=directories.begin() ;
5202              diter!=directories.end() ; diter++)
5203             {
5204             String dfname = *diter;
5205             dfname.append("/");
5206             dfname.append(iname);
5207             iter = allFiles.find(dfname);
5208             if (iter != allFiles.end())
5209                 {
5210                 FileRec *other = iter->second;
5211                 //trace("other: '%s'", iname.c_str());
5212                 frec->files[dfname] = other;
5213                 return true;
5214                 }
5215             }
5216         }
5217     return true;
5222 /**
5223  *  Lightly parse a file to find the #include directives.  Do
5224  *  a bit of state machine stuff to make sure that the directive
5225  *  is valid.  (Like not in a comment).
5226  */
5227 bool DepTool::scanFile(const String &fname, FileRec *frec)
5229     String fileName;
5230     if (sourceDir.size() > 0)
5231         {
5232         fileName.append(sourceDir);
5233         fileName.append("/");
5234         }
5235     fileName.append(fname);
5236     String nativeName = getNativePath(fileName);
5237     FILE *f = fopen(nativeName.c_str(), "r");
5238     if (!f)
5239         {
5240         error("Could not open '%s' for reading", fname.c_str());
5241         return false;
5242         }
5243     String buf;
5244     while (!feof(f))
5245         {
5246         int len = fread(readBuf, 1, readBufSize, f);
5247         readBuf[len] = '\0';
5248         buf.append(readBuf);
5249         }
5250     fclose(f);
5252     depFileSize = buf.size();
5253     depFileBuf  = (char *)buf.c_str();
5254     int pos = 0;
5257     while (pos < depFileSize)
5258         {
5259         //trace("p:%c", get(pos));
5261         //# Block comment
5262         if (get(pos) == '/' && get(pos+1) == '*')
5263             {
5264             pos += 2;
5265             while (pos < depFileSize)
5266                 {
5267                 if (get(pos) == '*' && get(pos+1) == '/')
5268                     {
5269                     pos += 2;
5270                     break;
5271                     }
5272                 else
5273                     pos++;
5274                 }
5275             }
5276         //# Line comment
5277         else if (get(pos) == '/' && get(pos+1) == '/')
5278             {
5279             pos += 2;
5280             while (pos < depFileSize)
5281                 {
5282                 if (get(pos) == '\n')
5283                     {
5284                     pos++;
5285                     break;
5286                     }
5287                 else
5288                     pos++;
5289                 }
5290             }
5291         //# #include! yaay
5292         else if (sequ(pos, "#include"))
5293             {
5294             pos += 8;
5295             pos = skipwhite(pos);
5296             String iname;
5297             pos = getword(pos, iname);
5298             if (iname.size()>2)
5299                 {
5300                 iname = iname.substr(1, iname.size()-2);
5301                 addIncludeFile(frec, iname);
5302                 }
5303             }
5304         else
5305             {
5306             pos++;
5307             }
5308         }
5310     return true;
5315 /**
5316  *  Recursively check include lists to find all files in allFiles to which
5317  *  a given file is dependent.
5318  */
5319 bool DepTool::processDependency(FileRec *ofile,
5320                              FileRec *include,
5321                              int depth)
5323     std::map<String, FileRec *>::iterator iter;
5324     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5325         {
5326         String fname  = iter->first;
5327         if (ofile->files.find(fname) != ofile->files.end())
5328             {
5329             //trace("file '%s' already seen", fname.c_str());
5330             continue;
5331             }
5332         FileRec *child  = iter->second;
5333         ofile->files[fname] = child;
5334       
5335         processDependency(ofile, child, depth+1);
5336         }
5339     return true;
5346 /**
5347  *  Generate the file dependency list.
5348  */
5349 bool DepTool::generateDependencies()
5351     std::map<String, FileRec *>::iterator iter;
5352     //# First pass.  Scan for all includes
5353     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5354         {
5355         FileRec *frec = iter->second;
5356         if (!scanFile(iter->first, frec))
5357             {
5358             //quit?
5359             }
5360         }
5362     //# Second pass.  Scan for all includes
5363     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5364         {
5365         FileRec *include = iter->second;
5366         if (include->type == FileRec::CFILE)
5367             {
5368             String cFileName = iter->first;
5369             FileRec *ofile      = new FileRec(FileRec::OFILE);
5370             ofile->path         = include->path;
5371             ofile->baseName     = include->baseName;
5372             ofile->suffix       = include->suffix;
5373             String fname     = include->path;
5374             if (fname.size()>0)
5375                 fname.append("/");
5376             fname.append(include->baseName);
5377             fname.append(".o");
5378             depFiles[fname]    = ofile;
5379             //add the .c file first?   no, don't
5380             //ofile->files[cFileName] = include;
5381             
5382             //trace("ofile:%s", fname.c_str());
5384             processDependency(ofile, include, 0);
5385             }
5386         }
5388       
5389     return true;
5394 /**
5395  *  High-level call to generate deps and optionally save them
5396  */
5397 bool DepTool::generateDependencies(const String &fileName)
5399     if (!createFileList())
5400         return false;
5401     if (!generateDependencies())
5402         return false;
5403     if (!saveDepFile(fileName))
5404         return false;
5405     return true;
5409 /**
5410  *   This saves the dependency cache.
5411  */
5412 bool DepTool::saveDepFile(const String &fileName)
5414     time_t tim;
5415     time(&tim);
5417     FILE *f = fopen(fileName.c_str(), "w");
5418     if (!f)
5419         {
5420         trace("cannot open '%s' for writing", fileName.c_str());
5421         }
5422     fprintf(f, "<?xml version='1.0'?>\n");
5423     fprintf(f, "<!--\n");
5424     fprintf(f, "########################################################\n");
5425     fprintf(f, "## File: build.dep\n");
5426     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5427     fprintf(f, "########################################################\n");
5428     fprintf(f, "-->\n");
5430     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5431     std::map<String, FileRec *>::iterator iter;
5432     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5433         {
5434         FileRec *frec = iter->second;
5435         if (frec->type == FileRec::OFILE)
5436             {
5437             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5438                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5439             std::map<String, FileRec *>::iterator citer;
5440             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5441                 {
5442                 String cfname = citer->first;
5443                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5444                 }
5445             fprintf(f, "</object>\n\n");
5446             }
5447         }
5449     fprintf(f, "</dependencies>\n");
5450     fprintf(f, "\n");
5451     fprintf(f, "<!--\n");
5452     fprintf(f, "########################################################\n");
5453     fprintf(f, "## E N D\n");
5454     fprintf(f, "########################################################\n");
5455     fprintf(f, "-->\n");
5457     fclose(f);
5459     return true;
5465 /**
5466  *   This loads the dependency cache.
5467  */
5468 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5470     std::vector<DepRec> result;
5471     
5472     Parser parser;
5473     Element *root = parser.parseFile(depFile.c_str());
5474     if (!root)
5475         {
5476         //error("Could not open %s for reading", depFile.c_str());
5477         return result;
5478         }
5480     if (root->getChildren().size()==0 ||
5481         root->getChildren()[0]->getName()!="dependencies")
5482         {
5483         error("Main xml element should be <dependencies>");
5484         delete root;
5485         return result;
5486         }
5488     //########## Start parsing
5489     Element *depList = root->getChildren()[0];
5491     std::vector<Element *> objects = depList->getChildren();
5492     for (unsigned int i=0 ; i<objects.size() ; i++)
5493         {
5494         Element *objectElem = objects[i];
5495         String tagName = objectElem->getName();
5496         if (tagName == "object")
5497             {
5498             String objName   = objectElem->getAttribute("name");
5499              //trace("object:%s", objName.c_str());
5500             DepRec depObject(objName);
5501             depObject.path   = objectElem->getAttribute("path");
5502             depObject.suffix = objectElem->getAttribute("suffix");
5503             //########## DESCRIPTION
5504             std::vector<Element *> depElems = objectElem->getChildren();
5505             for (unsigned int i=0 ; i<depElems.size() ; i++)
5506                 {
5507                 Element *depElem = depElems[i];
5508                 tagName = depElem->getName();
5509                 if (tagName == "dep")
5510                     {
5511                     String depName = depElem->getAttribute("name");
5512                     //trace("    dep:%s", depName.c_str());
5513                     depObject.files.push_back(depName);
5514                     }
5515                 }
5516             //Insert into the result list, in a sorted manner
5517             bool inserted = false;
5518             std::vector<DepRec>::iterator iter;
5519             for (iter = result.begin() ; iter != result.end() ; iter++)
5520                 {
5521                 String vpath = iter->path;
5522                 vpath.append("/");
5523                 vpath.append(iter->name);
5524                 String opath = depObject.path;
5525                 opath.append("/");
5526                 opath.append(depObject.name);
5527                 if (vpath > opath)
5528                     {
5529                     inserted = true;
5530                     iter = result.insert(iter, depObject);
5531                     break;
5532                     }
5533                 }
5534             if (!inserted)
5535                 result.push_back(depObject);
5536             }
5537         }
5539     delete root;
5541     return result;
5545 /**
5546  *   This loads the dependency cache.
5547  */
5548 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5549                    bool forceRefresh)
5551     std::vector<DepRec> result;
5552     if (forceRefresh)
5553         {
5554         generateDependencies(depFile);
5555         result = loadDepFile(depFile);
5556         }
5557     else
5558         {
5559         //try once
5560         result = loadDepFile(depFile);
5561         if (result.size() == 0)
5562             {
5563             //fail? try again
5564             generateDependencies(depFile);
5565             result = loadDepFile(depFile);
5566             }
5567         }
5568     return result;
5574 //########################################################################
5575 //# T A S K
5576 //########################################################################
5577 //forward decl
5578 class Target;
5579 class Make;
5581 /**
5582  *
5583  */
5584 class Task : public MakeBase
5587 public:
5589     typedef enum
5590         {
5591         TASK_NONE,
5592         TASK_CC,
5593         TASK_COPY,
5594         TASK_DELETE,
5595         TASK_JAR,
5596         TASK_JAVAC,
5597         TASK_LINK,
5598         TASK_MAKEFILE,
5599         TASK_MKDIR,
5600         TASK_MSGFMT,
5601         TASK_RANLIB,
5602         TASK_RC,
5603         TASK_SHAREDLIB,
5604         TASK_STATICLIB,
5605         TASK_STRIP,
5606         TASK_TSTAMP
5607         } TaskType;
5608         
5610     /**
5611      *
5612      */
5613     Task(MakeBase &par) : parent(par)
5614         { init(); }
5616     /**
5617      *
5618      */
5619     Task(const Task &other) : parent(other.parent)
5620         { init(); assign(other); }
5622     /**
5623      *
5624      */
5625     Task &operator=(const Task &other)
5626         { assign(other); return *this; }
5628     /**
5629      *
5630      */
5631     virtual ~Task()
5632         { }
5635     /**
5636      *
5637      */
5638     virtual MakeBase &getParent()
5639         { return parent; }
5641      /**
5642      *
5643      */
5644     virtual int  getType()
5645         { return type; }
5647     /**
5648      *
5649      */
5650     virtual void setType(int val)
5651         { type = val; }
5653     /**
5654      *
5655      */
5656     virtual String getName()
5657         { return name; }
5659     /**
5660      *
5661      */
5662     virtual bool execute()
5663         { return true; }
5665     /**
5666      *
5667      */
5668     virtual bool parse(Element *elem)
5669         { return true; }
5671     /**
5672      *
5673      */
5674     Task *createTask(Element *elem);
5677 protected:
5679     void init()
5680         {
5681         type = TASK_NONE;
5682         name = "none";
5683         }
5685     void assign(const Task &other)
5686         {
5687         type = other.type;
5688         name = other.name;
5689         }
5690         
5691     String getAttribute(Element *elem, const String &attrName)
5692         {
5693         String str;
5694         return str;
5695         }
5697     MakeBase &parent;
5699     int type;
5701     String name;
5702 };
5706 /**
5707  * This task runs the C/C++ compiler.  The compiler is invoked
5708  * for all .c or .cpp files which are newer than their correcsponding
5709  * .o files.  
5710  */
5711 class TaskCC : public Task
5713 public:
5715     TaskCC(MakeBase &par) : Task(par)
5716         {
5717                 type = TASK_CC; name = "cc";
5718                 ccCommand   = "gcc";
5719                 cxxCommand  = "g++";
5720                 source      = ".";
5721                 dest        = ".";
5722                 flags       = "";
5723                 defines     = "";
5724                 includes    = "";
5725                 fileSet.clear();
5726         }
5728     virtual ~TaskCC()
5729         {}
5731     virtual bool needsCompiling(const DepRec &depRec,
5732               const String &src, const String &dest)
5733         {
5734         return false;
5735         }
5737     virtual bool execute()
5738         {
5739         if (!listFiles(parent, fileSet))
5740             return false;
5742         bool refreshCache = false;
5743         String fullName = parent.resolve("build.dep");
5744         if (isNewerThan(parent.getURI().getPath(), fullName))
5745             {
5746             status("          : regenerating C/C++ dependency cache");
5747             refreshCache = true;
5748             }
5750         DepTool depTool;
5751         depTool.setSourceDirectory(source);
5752         depTool.setFileList(fileSet.getFiles());
5753         std::vector<DepRec> deps =
5754              depTool.getDepFile("build.dep", refreshCache);
5755         
5756         String incs;
5757         incs.append("-I");
5758         incs.append(parent.resolve("."));
5759         incs.append(" ");
5760         if (includes.size()>0)
5761             {
5762             incs.append(includes);
5763             incs.append(" ");
5764             }
5765         std::set<String> paths;
5766         std::vector<DepRec>::iterator viter;
5767         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5768             {
5769             DepRec dep = *viter;
5770             if (dep.path.size()>0)
5771                 paths.insert(dep.path);
5772             }
5773         if (source.size()>0)
5774             {
5775             incs.append(" -I");
5776             incs.append(parent.resolve(source));
5777             incs.append(" ");
5778             }
5779         std::set<String>::iterator setIter;
5780         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5781             {
5782             incs.append(" -I");
5783             String dname;
5784             if (source.size()>0)
5785                 {
5786                 dname.append(source);
5787                 dname.append("/");
5788                 }
5789             dname.append(*setIter);
5790             incs.append(parent.resolve(dname));
5791             }
5792         std::vector<String> cfiles;
5793         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5794             {
5795             DepRec dep = *viter;
5797             //## Select command
5798             String sfx = dep.suffix;
5799             String command = ccCommand;
5800             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5801                              || sfx == "CC")
5802                             command = cxxCommand;
5803  
5804             //## Make paths
5805             String destPath = dest;
5806             String srcPath  = source;
5807             if (dep.path.size()>0)
5808                             {
5809                 destPath.append("/");
5810                                 destPath.append(dep.path);
5811                 srcPath.append("/");
5812                                 srcPath.append(dep.path);
5813                             }
5814             //## Make sure destination directory exists
5815                         if (!createDirectory(destPath))
5816                             return false;
5817                             
5818             //## Check whether it needs to be done
5819                         String destName;
5820             if (destPath.size()>0)
5821                 {
5822                 destName.append(destPath);
5823                     destName.append("/");
5824                     }
5825                         destName.append(dep.name);
5826                         destName.append(".o");
5827                         String destFullName = parent.resolve(destName);
5828                         String srcName;
5829             if (srcPath.size()>0)
5830                 {
5831                 srcName.append(srcPath);
5832                 srcName.append("/");
5833                 }
5834                         srcName.append(dep.name);
5835                         srcName.append(".");
5836                         srcName.append(dep.suffix);
5837                         String srcFullName = parent.resolve(srcName);
5838                         bool compileMe = false;
5839             if (isNewerThan(srcFullName, destFullName))
5840                 {
5841                 status("          : compile of %s required by %s",
5842                         destFullName.c_str(), srcFullName.c_str());
5843                 compileMe = true;
5844                 }
5845             else
5846                 {
5847                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5848                     {
5849                     String depName;
5850                     if (srcPath.size()>0)
5851                         {
5852                         depName.append(srcPath);
5853                         depName.append("/");
5854                         }
5855                     depName.append(dep.files[i]);
5856                     String depFullName = parent.resolve(depName);
5857                     if (isNewerThan(depFullName, destFullName))
5858                         {
5859                         status("          : compile of %s required by %s",
5860                                 destFullName.c_str(), depFullName.c_str());
5861                         compileMe = true;
5862                         break;
5863                         }
5864                     }
5865                 }
5866             if (!compileMe)
5867                 {
5868                 continue;
5869                 }
5871             //## Assemble the command
5872             String cmd = command;
5873             cmd.append(" -c ");
5874             cmd.append(flags);
5875                         cmd.append(" ");
5876             cmd.append(defines);
5877                         cmd.append(" ");
5878             cmd.append(incs);
5879                         cmd.append(" ");
5880                         cmd.append(srcFullName);
5881             cmd.append(" -o ");
5882                         cmd.append(destFullName);
5884             //## Execute the command
5886             String outString, errString;
5887             if (!executeCommand(cmd.c_str(), "", outString, errString))
5888                 {
5889                 error("problem compiling: %s", errString.c_str());
5890                 return false;
5891                 }
5892             }
5893         
5894         return true;
5895         }
5897     virtual bool parse(Element *elem)
5898         {
5899         String s;
5900         if (!parent.getAttribute(elem, "command", s))
5901             return false;
5902         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5903         if (!parent.getAttribute(elem, "cc", s))
5904             return false;
5905         if (s.size()>0) ccCommand = s;
5906         if (!parent.getAttribute(elem, "cxx", s))
5907             return false;
5908         if (s.size()>0) cxxCommand = s;
5909         if (!parent.getAttribute(elem, "destdir", s))
5910             return false;
5911         if (s.size()>0) dest = s;
5913         std::vector<Element *> children = elem->getChildren();
5914         for (unsigned int i=0 ; i<children.size() ; i++)
5915             {
5916             Element *child = children[i];
5917             String tagName = child->getName();
5918             if (tagName == "flags")
5919                 {
5920                 if (!parent.getValue(child, flags))
5921                     return false;
5922                 flags = strip(flags);
5923                 }
5924             else if (tagName == "includes")
5925                 {
5926                 if (!parent.getValue(child, includes))
5927                     return false;
5928                 includes = strip(includes);
5929                 }
5930             else if (tagName == "defines")
5931                 {
5932                 if (!parent.getValue(child, defines))
5933                     return false;
5934                 defines = strip(defines);
5935                 }
5936             else if (tagName == "fileset")
5937                 {
5938                 if (!parseFileSet(child, parent, fileSet))
5939                     return false;
5940                 source = fileSet.getDirectory();
5941                 }
5942             }
5944         return true;
5945         }
5946         
5947 protected:
5949     String ccCommand;
5950     String cxxCommand;
5951     String source;
5952     String dest;
5953     String flags;
5954     String defines;
5955     String includes;
5956     FileSet fileSet;
5957     
5958 };
5962 /**
5963  *
5964  */
5965 class TaskCopy : public Task
5967 public:
5969     typedef enum
5970         {
5971         CP_NONE,
5972         CP_TOFILE,
5973         CP_TODIR
5974         } CopyType;
5976     TaskCopy(MakeBase &par) : Task(par)
5977         {
5978                 type = TASK_COPY; name = "copy";
5979                 cptype = CP_NONE;
5980                 verbose = false;
5981                 haveFileSet = false;
5982                 }
5984     virtual ~TaskCopy()
5985         {}
5987     virtual bool execute()
5988         {
5989         switch (cptype)
5990            {
5991            case CP_TOFILE:
5992                {
5993                if (fileName.size()>0)
5994                    {
5995                    status("          : %s to %s",
5996                         fileName.c_str(), toFileName.c_str());
5997                    String fullSource = parent.resolve(fileName);
5998                    String fullDest = parent.resolve(toFileName);
5999                    //trace("copy %s to file %s", fullSource.c_str(),
6000                                    //                       fullDest.c_str());
6001                                    if (!isRegularFile(fullSource))
6002                                        {
6003                        error("copy : file %s does not exist", fullSource.c_str());
6004                                        return false;
6005                                        }
6006                    if (!isNewerThan(fullSource, fullDest))
6007                        {
6008                        return true;
6009                        }
6010                    if (!copyFile(fullSource, fullDest))
6011                        return false;
6012                    status("          : 1 file copied");
6013                    }
6014                return true;
6015                }
6016            case CP_TODIR:
6017                {
6018                if (haveFileSet)
6019                    {
6020                    if (!listFiles(parent, fileSet))
6021                        return false;
6022                    String fileSetDir = fileSet.getDirectory();
6024                    status("          : %s to %s",
6025                        fileSetDir.c_str(), toDirName.c_str());
6027                    int nrFiles = 0;
6028                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6029                        {
6030                        String fileName = fileSet[i];
6032                        String sourcePath;
6033                        if (fileSetDir.size()>0)
6034                            {
6035                            sourcePath.append(fileSetDir);
6036                            sourcePath.append("/");
6037                            }
6038                        sourcePath.append(fileName);
6039                        String fullSource = parent.resolve(sourcePath);
6040                        
6041                        //Get the immediate parent directory's base name
6042                        String baseFileSetDir = fileSetDir;
6043                        unsigned int pos = baseFileSetDir.find_last_of('/');
6044                        if (pos!=baseFileSetDir.npos &&
6045                                                       pos < baseFileSetDir.size()-1)
6046                            baseFileSetDir =
6047                                                       baseFileSetDir.substr(pos+1,
6048                                                                baseFileSetDir.size());
6049                                            //Now make the new path
6050                        String destPath;
6051                        if (toDirName.size()>0)
6052                            {
6053                            destPath.append(toDirName);
6054                            destPath.append("/");
6055                            }
6056                        if (baseFileSetDir.size()>0)
6057                            {
6058                            destPath.append(baseFileSetDir);
6059                            destPath.append("/");
6060                            }
6061                        destPath.append(fileName);
6062                        String fullDest = parent.resolve(destPath);
6063                        //trace("fileName:%s", fileName.c_str());
6064                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6065                                        //                   fullDest.c_str());
6066                        if (!isNewerThan(fullSource, fullDest))
6067                            {
6068                            //trace("copy skipping %s", fullSource.c_str());
6069                            continue;
6070                            }
6071                        if (!copyFile(fullSource, fullDest))
6072                            return false;
6073                        nrFiles++;
6074                        }
6075                    status("          : %d file(s) copied", nrFiles);
6076                    }
6077                else //file source
6078                    {
6079                    //For file->dir we want only the basename of
6080                    //the source appended to the dest dir
6081                    status("          : %s to %s", 
6082                        fileName.c_str(), toDirName.c_str());
6083                    String baseName = fileName;
6084                    unsigned int pos = baseName.find_last_of('/');
6085                    if (pos!=baseName.npos && pos<baseName.size()-1)
6086                        baseName = baseName.substr(pos+1, baseName.size());
6087                    String fullSource = parent.resolve(fileName);
6088                    String destPath;
6089                    if (toDirName.size()>0)
6090                        {
6091                        destPath.append(toDirName);
6092                        destPath.append("/");
6093                        }
6094                    destPath.append(baseName);
6095                    String fullDest = parent.resolve(destPath);
6096                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6097                                    //                       fullDest.c_str());
6098                                    if (!isRegularFile(fullSource))
6099                                        {
6100                        error("copy : file %s does not exist", fullSource.c_str());
6101                                        return false;
6102                                        }
6103                    if (!isNewerThan(fullSource, fullDest))
6104                        {
6105                        return true;
6106                        }
6107                    if (!copyFile(fullSource, fullDest))
6108                        return false;
6109                    status("          : 1 file copied");
6110                    }
6111                return true;
6112                }
6113            }
6114         return true;
6115         }
6118     virtual bool parse(Element *elem)
6119         {
6120         if (!parent.getAttribute(elem, "file", fileName))
6121             return false;
6122         if (!parent.getAttribute(elem, "tofile", toFileName))
6123             return false;
6124         if (toFileName.size() > 0)
6125             cptype = CP_TOFILE;
6126         if (!parent.getAttribute(elem, "todir", toDirName))
6127             return false;
6128         if (toDirName.size() > 0)
6129             cptype = CP_TODIR;
6130         String ret;
6131         if (!parent.getAttribute(elem, "verbose", ret))
6132             return false;
6133         if (ret.size()>0 && !getBool(ret, verbose))
6134             return false;
6135             
6136         haveFileSet = false;
6137         
6138         std::vector<Element *> children = elem->getChildren();
6139         for (unsigned int i=0 ; i<children.size() ; i++)
6140             {
6141             Element *child = children[i];
6142             String tagName = child->getName();
6143             if (tagName == "fileset")
6144                 {
6145                 if (!parseFileSet(child, parent, fileSet))
6146                     {
6147                     error("problem getting fileset");
6148                                         return false;
6149                                         }
6150                                 haveFileSet = true;
6151                 }
6152             }
6154         //Perform validity checks
6155                 if (fileName.size()>0 && fileSet.size()>0)
6156                     {
6157                     error("<copy> can only have one of : file= and <fileset>");
6158                     return false;
6159                     }
6160         if (toFileName.size()>0 && toDirName.size()>0)
6161             {
6162             error("<copy> can only have one of : tofile= or todir=");
6163             return false;
6164             }
6165         if (haveFileSet && toDirName.size()==0)
6166             {
6167             error("a <copy> task with a <fileset> must have : todir=");
6168             return false;
6169             }
6170                 if (cptype == CP_TOFILE && fileName.size()==0)
6171                     {
6172                     error("<copy> tofile= must be associated with : file=");
6173                     return false;
6174                     }
6175                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6176                     {
6177                     error("<copy> todir= must be associated with : file= or <fileset>");
6178                     return false;
6179                     }
6181         return true;
6182         }
6183         
6184 private:
6186     int cptype;
6187     String fileName;
6188     FileSet fileSet;
6189     String toFileName;
6190     String toDirName;
6191     bool verbose;
6192     bool haveFileSet;
6193 };
6196 /**
6197  *
6198  */
6199 class TaskDelete : public Task
6201 public:
6203     typedef enum
6204         {
6205         DEL_FILE,
6206         DEL_DIR,
6207         DEL_FILESET
6208         } DeleteType;
6210     TaskDelete(MakeBase &par) : Task(par)
6211         { 
6212                   type        = TASK_DELETE;
6213                   name        = "delete";
6214                   delType     = DEL_FILE;
6215           verbose     = false;
6216           quiet       = false;
6217           failOnError = true;
6218                 }
6220     virtual ~TaskDelete()
6221         {}
6223     virtual bool execute()
6224         {
6225         struct stat finfo;
6226         switch (delType)
6227             {
6228             case DEL_FILE:
6229                 {
6230                 status("          : %s", fileName.c_str());
6231                 String fullName = parent.resolve(fileName);
6232                 char *fname = (char *)fullName.c_str();
6233                 //does not exist
6234                 if (stat(fname, &finfo)<0)
6235                     return true;
6236                 //exists but is not a regular file
6237                 if (!S_ISREG(finfo.st_mode))
6238                     {
6239                     error("<delete> failed. '%s' exists and is not a regular file",
6240                           fname);
6241                     return false;
6242                     }
6243                 if (remove(fname)<0)
6244                     {
6245                     error("<delete> failed: %s", strerror(errno));
6246                     return false;
6247                     }
6248                 return true;
6249                 }
6250             case DEL_DIR:
6251                 {
6252                 status("          : %s", dirName.c_str());
6253                 String fullDir = parent.resolve(dirName);
6254                 if (!removeDirectory(fullDir))
6255                     return false;
6256                 return true;
6257                 }
6258             }
6259         return true;
6260         }
6262     virtual bool parse(Element *elem)
6263         {
6264         if (!parent.getAttribute(elem, "file", fileName))
6265             return false;
6266         if (fileName.size() > 0)
6267             delType = DEL_FILE;
6268         if (!parent.getAttribute(elem, "dir", dirName))
6269             return false;
6270         if (dirName.size() > 0)
6271             delType = DEL_DIR;
6272         if (fileName.size()>0 && dirName.size()>0)
6273             {
6274             error("<delete> can only have one attribute of file= or dir=");
6275             return false;
6276             }
6277         String ret;
6278         if (!parent.getAttribute(elem, "verbose", ret))
6279             return false;
6280         if (ret.size()>0 && !getBool(ret, verbose))
6281             return false;
6282         if (!parent.getAttribute(elem, "quiet", ret))
6283             return false;
6284         if (ret.size()>0 && !getBool(ret, quiet))
6285             return false;
6286         if (!parent.getAttribute(elem, "failonerror", ret))
6287             return false;
6288         if (ret.size()>0 && !getBool(ret, failOnError))
6289             return false;
6290         return true;
6291         }
6293 private:
6295     int delType;
6296     String dirName;
6297     String fileName;
6298     bool verbose;
6299     bool quiet;
6300     bool failOnError;
6301 };
6304 /**
6305  *
6306  */
6307 class TaskJar : public Task
6309 public:
6311     TaskJar(MakeBase &par) : Task(par)
6312         { type = TASK_JAR; name = "jar"; }
6314     virtual ~TaskJar()
6315         {}
6317     virtual bool execute()
6318         {
6319         return true;
6320         }
6322     virtual bool parse(Element *elem)
6323         {
6324         return true;
6325         }
6326 };
6329 /**
6330  *
6331  */
6332 class TaskJavac : public Task
6334 public:
6336     TaskJavac(MakeBase &par) : Task(par)
6337         { type = TASK_JAVAC; name = "javac"; }
6339     virtual ~TaskJavac()
6340         {}
6342     virtual bool execute()
6343         {
6344         return true;
6345         }
6347     virtual bool parse(Element *elem)
6348         {
6349         return true;
6350         }
6351 };
6354 /**
6355  *
6356  */
6357 class TaskLink : public Task
6359 public:
6361     TaskLink(MakeBase &par) : Task(par)
6362         {
6363                 type = TASK_LINK; name = "link";
6364                 command = "g++";
6365                 doStrip = false;
6366                 stripCommand = "strip";
6367                 objcopyCommand = "objcopy";
6368                 }
6370     virtual ~TaskLink()
6371         {}
6373     virtual bool execute()
6374         {
6375         if (!listFiles(parent, fileSet))
6376             return false;
6377         String fileSetDir = fileSet.getDirectory();
6378         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6379         bool doit = false;
6380         String fullTarget = parent.resolve(fileName);
6381         String cmd = command;
6382         cmd.append(" -o ");
6383         cmd.append(fullTarget);
6384         cmd.append(" ");
6385         cmd.append(flags);
6386         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6387             {
6388             cmd.append(" ");
6389             String obj;
6390             if (fileSetDir.size()>0)
6391                             {
6392                                 obj.append(fileSetDir);
6393                 obj.append("/");
6394                 }
6395             obj.append(fileSet[i]);
6396             String fullObj = parent.resolve(obj);
6397             cmd.append(fullObj);
6398             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6399             //          fullObj.c_str());
6400             if (isNewerThan(fullObj, fullTarget))
6401                 doit = true;
6402             }
6403         cmd.append(" ");
6404         cmd.append(libs);
6405         if (!doit)
6406             {
6407             //trace("link not needed");
6408             return true;
6409             }
6410         //trace("LINK cmd:%s", cmd.c_str());
6413         String outbuf, errbuf;
6414         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6415             {
6416             error("LINK problem: %s", errbuf.c_str());
6417             return false;
6418             }
6420         if (symFileName.size()>0)
6421             {
6422             String symFullName = parent.resolve(symFileName);
6423             cmd = objcopyCommand;
6424             cmd.append(" --only-keep-debug ");
6425             cmd.append(getNativePath(fullTarget));
6426             cmd.append(" ");
6427             cmd.append(getNativePath(symFullName));
6428             if (!executeCommand(cmd, "", outbuf, errbuf))
6429                 {
6430                 error("<strip> symbol file failed : %s", errbuf.c_str());
6431                 return false;
6432                 }
6433             }
6434             
6435         if (doStrip)
6436             {
6437             cmd = stripCommand;
6438             cmd.append(" ");
6439             cmd.append(getNativePath(fullTarget));
6440             if (!executeCommand(cmd, "", outbuf, errbuf))
6441                {
6442                error("<strip> failed : %s", errbuf.c_str());
6443                return false;
6444                }
6445             }
6447         return true;
6448         }
6450     virtual bool parse(Element *elem)
6451         {
6452         String s;
6453         if (!parent.getAttribute(elem, "command", s))
6454             return false;
6455         if (s.size()>0)
6456             command = s;
6457         if (!parent.getAttribute(elem, "objcopycommand", s))
6458             return false;
6459         if (s.size()>0)
6460             objcopyCommand = s;
6461         if (!parent.getAttribute(elem, "stripcommand", s))
6462             return false;
6463         if (s.size()>0)
6464             stripCommand = s;
6465         if (!parent.getAttribute(elem, "out", fileName))
6466             return false;
6467         if (!parent.getAttribute(elem, "strip", s))
6468             return false;
6469         if (!getBool(s, doStrip))
6470             return false;
6471         if (!parent.getAttribute(elem, "symfile", symFileName))
6472             return false;
6473             
6474         std::vector<Element *> children = elem->getChildren();
6475         for (unsigned int i=0 ; i<children.size() ; i++)
6476             {
6477             Element *child = children[i];
6478             String tagName = child->getName();
6479             if (tagName == "fileset")
6480                 {
6481                 if (!parseFileSet(child, parent, fileSet))
6482                     return false;
6483                 }
6484             else if (tagName == "flags")
6485                 {
6486                 if (!parent.getValue(child, flags))
6487                     return false;
6488                 flags = strip(flags);
6489                 }
6490             else if (tagName == "libs")
6491                 {
6492                 if (!parent.getValue(child, libs))
6493                     return false;
6494                 libs = strip(libs);
6495                 }
6496             }
6497         return true;
6498         }
6500 private:
6502     String  command;
6503     String  fileName;
6504     String  flags;
6505     String  libs;
6506     FileSet fileSet;
6507     bool    doStrip;
6508     String  symFileName;
6509     String  stripCommand;
6510     String  objcopyCommand;
6512 };
6516 /**
6517  * Create a named directory
6518  */
6519 class TaskMakeFile : public Task
6521 public:
6523     TaskMakeFile(MakeBase &par) : Task(par)
6524         { type = TASK_MAKEFILE; name = "makefile"; }
6526     virtual ~TaskMakeFile()
6527         {}
6529     virtual bool execute()
6530         {
6531         status("          : %s", fileName.c_str());
6532         String fullName = parent.resolve(fileName);
6533         if (!isNewerThan(parent.getURI().getPath(), fullName))
6534             {
6535             //trace("skipped <makefile>");
6536             return true;
6537             }
6538         //trace("fullName:%s", fullName.c_str());
6539         FILE *f = fopen(fullName.c_str(), "w");
6540         if (!f)
6541             {
6542             error("<makefile> could not open %s for writing : %s",
6543                 fullName.c_str(), strerror(errno));
6544             return false;
6545             }
6546         for (unsigned int i=0 ; i<text.size() ; i++)
6547             fputc(text[i], f);
6548         fputc('\n', f);
6549         fclose(f);
6550         return true;
6551         }
6553     virtual bool parse(Element *elem)
6554         {
6555         if (!parent.getAttribute(elem, "file", fileName))
6556             return false;
6557         if (fileName.size() == 0)
6558             {
6559             error("<makefile> requires 'file=\"filename\"' attribute");
6560             return false;
6561             }
6562         if (!parent.getValue(elem, text))
6563             return false;
6564         text = leftJustify(text);
6565         //trace("dirname:%s", dirName.c_str());
6566         return true;
6567         }
6569 private:
6571     String fileName;
6572     String text;
6573 };
6577 /**
6578  * Create a named directory
6579  */
6580 class TaskMkDir : public Task
6582 public:
6584     TaskMkDir(MakeBase &par) : Task(par)
6585         { type = TASK_MKDIR; name = "mkdir"; }
6587     virtual ~TaskMkDir()
6588         {}
6590     virtual bool execute()
6591         {
6592         status("          : %s", dirName.c_str());
6593         String fullDir = parent.resolve(dirName);
6594         //trace("fullDir:%s", fullDir.c_str());
6595         if (!createDirectory(fullDir))
6596             return false;
6597         return true;
6598         }
6600     virtual bool parse(Element *elem)
6601         {
6602         if (!parent.getAttribute(elem, "dir", dirName))
6603             return false;
6604         if (dirName.size() == 0)
6605             {
6606             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6607             return false;
6608             }
6609         return true;
6610         }
6612 private:
6614     String dirName;
6615 };
6619 /**
6620  * Create a named directory
6621  */
6622 class TaskMsgFmt: public Task
6624 public:
6626     TaskMsgFmt(MakeBase &par) : Task(par)
6627          {
6628                  type    = TASK_MSGFMT;
6629                  name    = "msgfmt";
6630                  command = "msgfmt";
6631                  owndir  = false;
6632                  }
6634     virtual ~TaskMsgFmt()
6635         {}
6637     virtual bool execute()
6638         {
6639         if (!listFiles(parent, fileSet))
6640             return false;
6641         String fileSetDir = fileSet.getDirectory();
6643         //trace("msgfmt: %d", fileSet.size());
6644         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6645             {
6646             String fileName = fileSet[i];
6647             if (getSuffix(fileName) != "po")
6648                 continue;
6649             String sourcePath;
6650                         if (fileSetDir.size()>0)
6651                             {
6652                             sourcePath.append(fileSetDir);
6653                 sourcePath.append("/");
6654                 }
6655             sourcePath.append(fileName);
6656             String fullSource = parent.resolve(sourcePath);
6658             String destPath;
6659                         if (toDirName.size()>0)
6660                             {
6661                             destPath.append(toDirName);
6662                 destPath.append("/");
6663                 }
6664             if (owndir)
6665                 {
6666                 String subdir = fileName;
6667                 unsigned int pos = subdir.find_last_of('.');
6668                 if (pos != subdir.npos)
6669                     subdir = subdir.substr(0, pos);
6670                 destPath.append(subdir);
6671                 destPath.append("/");
6672                 }
6673             destPath.append(fileName);
6674             destPath[destPath.size()-2] = 'm';
6675             String fullDest = parent.resolve(destPath);
6677             if (!isNewerThan(fullSource, fullDest))
6678                 {
6679                 //trace("skip %s", fullSource.c_str());
6680                 continue;
6681                 }
6682                 
6683             String cmd = command;
6684             cmd.append(" ");
6685             cmd.append(fullSource);
6686             cmd.append(" -o ");
6687             cmd.append(fullDest);
6688             
6689             int pos = fullDest.find_last_of('/');
6690             if (pos>0)
6691                 {
6692                 String fullDestPath = fullDest.substr(0, pos);
6693                 if (!createDirectory(fullDestPath))
6694                     return false;
6695                 }
6699             String outString, errString;
6700             if (!executeCommand(cmd.c_str(), "", outString, errString))
6701                 {
6702                 error("<msgfmt> problem: %s", errString.c_str());
6703                 return false;
6704                 }
6705             }
6707         return true;
6708         }
6710     virtual bool parse(Element *elem)
6711         {
6712         String s;
6713         if (!parent.getAttribute(elem, "command", s))
6714             return false;
6715         if (s.size()>0)
6716             command = s;
6717         if (!parent.getAttribute(elem, "todir", toDirName))
6718             return false;
6719         if (!parent.getAttribute(elem, "owndir", s))
6720             return false;
6721         if (!getBool(s, owndir))
6722             return false;
6723             
6724         std::vector<Element *> children = elem->getChildren();
6725         for (unsigned int i=0 ; i<children.size() ; i++)
6726             {
6727             Element *child = children[i];
6728             String tagName = child->getName();
6729             if (tagName == "fileset")
6730                 {
6731                 if (!parseFileSet(child, parent, fileSet))
6732                     return false;
6733                 }
6734             }
6735         return true;
6736         }
6738 private:
6740     String command;
6741     String toDirName;
6742     FileSet fileSet;
6743     bool owndir;
6745 };
6751 /**
6752  *  Process an archive to allow random access
6753  */
6754 class TaskRanlib : public Task
6756 public:
6758     TaskRanlib(MakeBase &par) : Task(par)
6759         {
6760         type = TASK_RANLIB; name = "ranlib";
6761         command = "ranlib";
6762         }
6764     virtual ~TaskRanlib()
6765         {}
6767     virtual bool execute()
6768         {
6769         String fullName = parent.resolve(fileName);
6770         //trace("fullDir:%s", fullDir.c_str());
6771         String cmd = command;
6772         cmd.append(" ");
6773         cmd.append(fullName);
6774         String outbuf, errbuf;
6775         if (!executeCommand(cmd, "", outbuf, errbuf))
6776             return false;
6777         return true;
6778         }
6780     virtual bool parse(Element *elem)
6781         {
6782         String s;
6783         if (!parent.getAttribute(elem, "command", s))
6784             return false;
6785         if (s.size()>0)
6786            command = s;
6787         if (!parent.getAttribute(elem, "file", fileName))
6788             return false;
6789         if (fileName.size() == 0)
6790             {
6791             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6792             return false;
6793             }
6794         return true;
6795         }
6797 private:
6799     String fileName;
6800     String command;
6801 };
6805 /**
6806  * Run the "ar" command to archive .o's into a .a
6807  */
6808 class TaskRC : public Task
6810 public:
6812     TaskRC(MakeBase &par) : Task(par)
6813         {
6814                 type = TASK_RC; name = "rc";
6815                 command = "windres";
6816                 }
6818     virtual ~TaskRC()
6819         {}
6821     virtual bool execute()
6822         {
6823         String fullFile = parent.resolve(fileName);
6824         String fullOut  = parent.resolve(outName);
6825         if (!isNewerThan(fullFile, fullOut))
6826             return true;
6827         String cmd = command;
6828         cmd.append(" -o ");
6829         cmd.append(fullOut);
6830         cmd.append(" ");
6831         cmd.append(flags);
6832         cmd.append(" ");
6833         cmd.append(fullFile);
6835         String outString, errString;
6836         if (!executeCommand(cmd.c_str(), "", outString, errString))
6837             {
6838             error("RC problem: %s", errString.c_str());
6839             return false;
6840             }
6841         return true;
6842         }
6844     virtual bool parse(Element *elem)
6845         {
6846         if (!parent.getAttribute(elem, "command", command))
6847             return false;
6848         if (!parent.getAttribute(elem, "file", fileName))
6849             return false;
6850         if (!parent.getAttribute(elem, "out", outName))
6851             return false;
6852         std::vector<Element *> children = elem->getChildren();
6853         for (unsigned int i=0 ; i<children.size() ; i++)
6854             {
6855             Element *child = children[i];
6856             String tagName = child->getName();
6857             if (tagName == "flags")
6858                 {
6859                 if (!parent.getValue(child, flags))
6860                     return false;
6861                 }
6862             }
6863         return true;
6864         }
6866 private:
6868     String command;
6869     String flags;
6870     String fileName;
6871     String outName;
6873 };
6877 /**
6878  *  Collect .o's into a .so or DLL
6879  */
6880 class TaskSharedLib : public Task
6882 public:
6884     TaskSharedLib(MakeBase &par) : Task(par)
6885         {
6886                 type = TASK_SHAREDLIB; name = "dll";
6887                 command = "ar crv";
6888                 }
6890     virtual ~TaskSharedLib()
6891         {}
6893     virtual bool execute()
6894         {
6895         //trace("###########HERE %d", fileSet.size());
6896         bool doit = false;
6897         
6898         String fullOut = parent.resolve(fileName);
6899         //trace("ar fullout: %s", fullOut.c_str());
6900         
6901         if (!listFiles(parent, fileSet))
6902             return false;
6903         String fileSetDir = fileSet.getDirectory();
6905         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6906             {
6907             String fname;
6908                         if (fileSetDir.size()>0)
6909                             {
6910                             fname.append(fileSetDir);
6911                 fname.append("/");
6912                 }
6913             fname.append(fileSet[i]);
6914             String fullName = parent.resolve(fname);
6915             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6916             if (isNewerThan(fullName, fullOut))
6917                 doit = true;
6918             }
6919         //trace("Needs it:%d", doit);
6920         if (!doit)
6921             {
6922             return true;
6923             }
6925         String cmd = "dllwrap";
6926         cmd.append(" -o ");
6927         cmd.append(fullOut);
6928         if (defFileName.size()>0)
6929             {
6930             cmd.append(" --def ");
6931             cmd.append(defFileName);
6932             cmd.append(" ");
6933             }
6934         if (impFileName.size()>0)
6935             {
6936             cmd.append(" --implib ");
6937             cmd.append(impFileName);
6938             cmd.append(" ");
6939             }
6940         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6941             {
6942             String fname;
6943                         if (fileSetDir.size()>0)
6944                             {
6945                             fname.append(fileSetDir);
6946                 fname.append("/");
6947                 }
6948             fname.append(fileSet[i]);
6949             String fullName = parent.resolve(fname);
6951             cmd.append(" ");
6952             cmd.append(fullName);
6953             }
6954         cmd.append(" ");
6955         cmd.append(libs);
6957         String outString, errString;
6958         if (!executeCommand(cmd.c_str(), "", outString, errString))
6959             {
6960             error("<sharedlib> problem: %s", errString.c_str());
6961             return false;
6962             }
6964         return true;
6965         }
6967     virtual bool parse(Element *elem)
6968         {
6969         if (!parent.getAttribute(elem, "file", fileName))
6970             return false;
6971         if (!parent.getAttribute(elem, "import", impFileName))
6972             return false;
6973         if (!parent.getAttribute(elem, "def", defFileName))
6974             return false;
6975             
6976         std::vector<Element *> children = elem->getChildren();
6977         for (unsigned int i=0 ; i<children.size() ; i++)
6978             {
6979             Element *child = children[i];
6980             String tagName = child->getName();
6981             if (tagName == "fileset")
6982                 {
6983                 if (!parseFileSet(child, parent, fileSet))
6984                     return false;
6985                 }
6986             else if (tagName == "libs")
6987                 {
6988                 if (!parent.getValue(child, libs))
6989                     return false;
6990                 libs = strip(libs);
6991                 }
6992             }
6993         return true;
6994         }
6996 private:
6998     String command;
6999     String fileName;
7000     String defFileName;
7001     String impFileName;
7002     FileSet fileSet;
7003     String libs;
7005 };
7008 /**
7009  * Run the "ar" command to archive .o's into a .a
7010  */
7011 class TaskStaticLib : public Task
7013 public:
7015     TaskStaticLib(MakeBase &par) : Task(par)
7016         {
7017                 type = TASK_STATICLIB; name = "staticlib";
7018                 command = "ar crv";
7019                 }
7021     virtual ~TaskStaticLib()
7022         {}
7024     virtual bool execute()
7025         {
7026         //trace("###########HERE %d", fileSet.size());
7027         bool doit = false;
7028         
7029         String fullOut = parent.resolve(fileName);
7030         //trace("ar fullout: %s", fullOut.c_str());
7031         
7032         if (!listFiles(parent, fileSet))
7033             return false;
7034         String fileSetDir = fileSet.getDirectory();
7036         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7037             {
7038             String fname;
7039                         if (fileSetDir.size()>0)
7040                             {
7041                             fname.append(fileSetDir);
7042                 fname.append("/");
7043                 }
7044             fname.append(fileSet[i]);
7045             String fullName = parent.resolve(fname);
7046             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7047             if (isNewerThan(fullName, fullOut))
7048                 doit = true;
7049             }
7050         //trace("Needs it:%d", doit);
7051         if (!doit)
7052             {
7053             return true;
7054             }
7056         String cmd = command;
7057         cmd.append(" ");
7058         cmd.append(fullOut);
7059         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7060             {
7061             String fname;
7062                         if (fileSetDir.size()>0)
7063                             {
7064                             fname.append(fileSetDir);
7065                 fname.append("/");
7066                 }
7067             fname.append(fileSet[i]);
7068             String fullName = parent.resolve(fname);
7070             cmd.append(" ");
7071             cmd.append(fullName);
7072             }
7074         String outString, errString;
7075         if (!executeCommand(cmd.c_str(), "", outString, errString))
7076             {
7077             error("<staticlib> problem: %s", errString.c_str());
7078             return false;
7079             }
7081         return true;
7082         }
7084     virtual bool parse(Element *elem)
7085         {
7086         String s;
7087         if (!parent.getAttribute(elem, "command", s))
7088             return false;
7089         if (s.size()>0)
7090             command = s;
7091         if (!parent.getAttribute(elem, "file", fileName))
7092             return false;
7093             
7094         std::vector<Element *> children = elem->getChildren();
7095         for (unsigned int i=0 ; i<children.size() ; i++)
7096             {
7097             Element *child = children[i];
7098             String tagName = child->getName();
7099             if (tagName == "fileset")
7100                 {
7101                 if (!parseFileSet(child, parent, fileSet))
7102                     return false;
7103                 }
7104             }
7105         return true;
7106         }
7108 private:
7110     String command;
7111     String fileName;
7112     FileSet fileSet;
7114 };
7117 /**
7118  * Strip an executable
7119  */
7120 class TaskStrip : public Task
7122 public:
7124     TaskStrip(MakeBase &par) : Task(par)
7125         { type = TASK_STRIP; name = "strip"; }
7127     virtual ~TaskStrip()
7128         {}
7130     virtual bool execute()
7131         {
7132         String fullName = parent.resolve(fileName);
7133         //trace("fullDir:%s", fullDir.c_str());
7134         String cmd;
7135         String outbuf, errbuf;
7137         if (symFileName.size()>0)
7138             {
7139             String symFullName = parent.resolve(symFileName);
7140             cmd = "objcopy --only-keep-debug ";
7141             cmd.append(getNativePath(fullName));
7142             cmd.append(" ");
7143             cmd.append(getNativePath(symFullName));
7144             if (!executeCommand(cmd, "", outbuf, errbuf))
7145                 {
7146                 error("<strip> symbol file failed : %s", errbuf.c_str());
7147                 return false;
7148                 }
7149             }
7150             
7151         cmd = "strip ";
7152         cmd.append(getNativePath(fullName));
7153         if (!executeCommand(cmd, "", outbuf, errbuf))
7154             {
7155             error("<strip> failed : %s", errbuf.c_str());
7156             return false;
7157             }
7158         return true;
7159         }
7161     virtual bool parse(Element *elem)
7162         {
7163         if (!parent.getAttribute(elem, "file", fileName))
7164             return false;
7165         if (!parent.getAttribute(elem, "symfile", symFileName))
7166             return false;
7167         if (fileName.size() == 0)
7168             {
7169             error("<strip> requires 'file=\"fileName\"' attribute");
7170             return false;
7171             }
7172         return true;
7173         }
7175 private:
7177     String fileName;
7178     String symFileName;
7179 };
7182 /**
7183  *
7184  */
7185 class TaskTstamp : public Task
7187 public:
7189     TaskTstamp(MakeBase &par) : Task(par)
7190         { type = TASK_TSTAMP; name = "tstamp"; }
7192     virtual ~TaskTstamp()
7193         {}
7195     virtual bool execute()
7196         {
7197         return true;
7198         }
7200     virtual bool parse(Element *elem)
7201         {
7202         //trace("tstamp parse");
7203         return true;
7204         }
7205 };
7209 /**
7210  *
7211  */
7212 Task *Task::createTask(Element *elem)
7214     String tagName = elem->getName();
7215     //trace("task:%s", tagName.c_str());
7216     Task *task = NULL;
7217     if (tagName == "cc")
7218         task = new TaskCC(parent);
7219     else if (tagName == "copy")
7220         task = new TaskCopy(parent);
7221     else if (tagName == "delete")
7222         task = new TaskDelete(parent);
7223     else if (tagName == "jar")
7224         task = new TaskJar(parent);
7225     else if (tagName == "javac")
7226         task = new TaskJavac(parent);
7227     else if (tagName == "link")
7228         task = new TaskLink(parent);
7229     else if (tagName == "makefile")
7230         task = new TaskMakeFile(parent);
7231     else if (tagName == "mkdir")
7232         task = new TaskMkDir(parent);
7233     else if (tagName == "msgfmt")
7234         task = new TaskMsgFmt(parent);
7235     else if (tagName == "ranlib")
7236         task = new TaskRanlib(parent);
7237     else if (tagName == "rc")
7238         task = new TaskRC(parent);
7239     else if (tagName == "sharedlib")
7240         task = new TaskSharedLib(parent);
7241     else if (tagName == "staticlib")
7242         task = new TaskStaticLib(parent);
7243     else if (tagName == "strip")
7244         task = new TaskStrip(parent);
7245     else if (tagName == "tstamp")
7246         task = new TaskTstamp(parent);
7247     else
7248         {
7249         error("Unknown task '%s'", tagName.c_str());
7250         return NULL;
7251         }
7253     if (!task->parse(elem))
7254         {
7255         delete task;
7256         return NULL;
7257         }
7258     return task;
7263 //########################################################################
7264 //# T A R G E T
7265 //########################################################################
7267 /**
7268  *
7269  */
7270 class Target : public MakeBase
7273 public:
7275     /**
7276      *
7277      */
7278     Target(Make &par) : parent(par)
7279         { init(); }
7281     /**
7282      *
7283      */
7284     Target(const Target &other) : parent(other.parent)
7285         { init(); assign(other); }
7287     /**
7288      *
7289      */
7290     Target &operator=(const Target &other)
7291         { init(); assign(other); return *this; }
7293     /**
7294      *
7295      */
7296     virtual ~Target()
7297         { cleanup() ; }
7300     /**
7301      *
7302      */
7303     virtual Make &getParent()
7304         { return parent; }
7306     /**
7307      *
7308      */
7309     virtual String getName()
7310         { return name; }
7312     /**
7313      *
7314      */
7315     virtual void setName(const String &val)
7316         { name = val; }
7318     /**
7319      *
7320      */
7321     virtual String getDescription()
7322         { return description; }
7324     /**
7325      *
7326      */
7327     virtual void setDescription(const String &val)
7328         { description = val; }
7330     /**
7331      *
7332      */
7333     virtual void addDependency(const String &val)
7334         { deps.push_back(val); }
7336     /**
7337      *
7338      */
7339     virtual void parseDependencies(const String &val)
7340         { deps = tokenize(val, ", "); }
7342     /**
7343      *
7344      */
7345     virtual std::vector<String> &getDependencies()
7346         { return deps; }
7348     /**
7349      *
7350      */
7351     virtual String getIf()
7352         { return ifVar; }
7354     /**
7355      *
7356      */
7357     virtual void setIf(const String &val)
7358         { ifVar = val; }
7360     /**
7361      *
7362      */
7363     virtual String getUnless()
7364         { return unlessVar; }
7366     /**
7367      *
7368      */
7369     virtual void setUnless(const String &val)
7370         { unlessVar = val; }
7372     /**
7373      *
7374      */
7375     virtual void addTask(Task *val)
7376         { tasks.push_back(val); }
7378     /**
7379      *
7380      */
7381     virtual std::vector<Task *> &getTasks()
7382         { return tasks; }
7384 private:
7386     void init()
7387         {
7388         }
7390     void cleanup()
7391         {
7392         tasks.clear();
7393         }
7395     void assign(const Target &other)
7396         {
7397         //parent      = other.parent;
7398         name        = other.name;
7399         description = other.description;
7400         ifVar       = other.ifVar;
7401         unlessVar   = other.unlessVar;
7402         deps        = other.deps;
7403         tasks       = other.tasks;
7404         }
7406     Make &parent;
7408     String name;
7410     String description;
7412     String ifVar;
7414     String unlessVar;
7416     std::vector<String> deps;
7418     std::vector<Task *> tasks;
7420 };
7429 //########################################################################
7430 //# M A K E
7431 //########################################################################
7434 /**
7435  *
7436  */
7437 class Make : public MakeBase
7440 public:
7442     /**
7443      *
7444      */
7445     Make()
7446         { init(); }
7448     /**
7449      *
7450      */
7451     Make(const Make &other)
7452         { assign(other); }
7454     /**
7455      *
7456      */
7457     Make &operator=(const Make &other)
7458         { assign(other); return *this; }
7460     /**
7461      *
7462      */
7463     virtual ~Make()
7464         { cleanup(); }
7466     /**
7467      *
7468      */
7469     virtual std::map<String, Target> &getTargets()
7470         { return targets; }
7473     /**
7474      *
7475      */
7476     virtual String version()
7477         { return "BuildTool v0.6, 2006 Bob Jamison"; }
7479     /**
7480      * Overload a <property>
7481      */
7482     virtual bool specifyProperty(const String &name,
7483                                      const String &value);
7485     /**
7486      *
7487      */
7488     virtual bool run();
7490     /**
7491      *
7492      */
7493     virtual bool run(const String &target);
7497 private:
7499     /**
7500      *
7501      */
7502     void init();
7504     /**
7505      *
7506      */
7507     void cleanup();
7509     /**
7510      *
7511      */
7512     void assign(const Make &other);
7514     /**
7515      *
7516      */
7517     bool executeTask(Task &task);
7520     /**
7521      *
7522      */
7523     bool executeTarget(Target &target,
7524                  std::set<String> &targetsCompleted);
7527     /**
7528      *
7529      */
7530     bool execute();
7532     /**
7533      *
7534      */
7535     bool checkTargetDependencies(Target &prop,
7536                     std::vector<String> &depList);
7538     /**
7539      *
7540      */
7541     bool parsePropertyFile(const String &fileName,
7542                                const String &prefix);
7544     /**
7545      *
7546      */
7547     bool parseProperty(Element *elem);
7549     /**
7550      *
7551      */
7552     bool parseTask(Task &task, Element *elem);
7554     /**
7555      *
7556      */
7557     bool parseFile();
7559     /**
7560      *
7561      */
7562     std::vector<String> glob(const String &pattern);
7565     //###############
7566     //# Fields
7567     //###############
7569     String projectName;
7571     String currentTarget;
7573     String defaultTarget;
7575     String specifiedTarget;
7577     String baseDir;
7579     String description;
7580     
7581     String envAlias;
7583     //std::vector<Property> properties;
7584     
7585     std::map<String, Target> targets;
7587     std::vector<Task *> allTasks;
7588     
7589     std::map<String, String> specifiedProperties;
7591 };
7594 //########################################################################
7595 //# C L A S S  M A I N T E N A N C E
7596 //########################################################################
7598 /**
7599  *
7600  */
7601 void Make::init()
7603     uri             = "build.xml";
7604     projectName     = "";
7605     currentTarget   = "";
7606     defaultTarget   = "";
7607     specifiedTarget = "";
7608     baseDir         = "";
7609     description     = "";
7610     envAlias        = "";
7611     properties.clear();
7612     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7613         delete allTasks[i];
7614     allTasks.clear();
7619 /**
7620  *
7621  */
7622 void Make::cleanup()
7624     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7625         delete allTasks[i];
7626     allTasks.clear();
7631 /**
7632  *
7633  */
7634 void Make::assign(const Make &other)
7636     uri              = other.uri;
7637     projectName      = other.projectName;
7638     currentTarget    = other.currentTarget;
7639     defaultTarget    = other.defaultTarget;
7640     specifiedTarget  = other.specifiedTarget;
7641     baseDir          = other.baseDir;
7642     description      = other.description;
7643     properties       = other.properties;
7648 //########################################################################
7649 //# U T I L I T Y    T A S K S
7650 //########################################################################
7652 /**
7653  *  Perform a file globbing
7654  */
7655 std::vector<String> Make::glob(const String &pattern)
7657     std::vector<String> res;
7658     return res;
7662 //########################################################################
7663 //# P U B L I C    A P I
7664 //########################################################################
7668 /**
7669  *
7670  */
7671 bool Make::executeTarget(Target &target,
7672              std::set<String> &targetsCompleted)
7675     String name = target.getName();
7677     //First get any dependencies for this target
7678     std::vector<String> deps = target.getDependencies();
7679     for (unsigned int i=0 ; i<deps.size() ; i++)
7680         {
7681         String dep = deps[i];
7682         //Did we do it already?  Skip
7683         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7684             continue;
7685             
7686         std::map<String, Target> &tgts =
7687                target.getParent().getTargets();
7688         std::map<String, Target>::iterator iter =
7689                tgts.find(dep);
7690         if (iter == tgts.end())
7691             {
7692             error("Target '%s' dependency '%s' not found",
7693                       name.c_str(),  dep.c_str());
7694             return false;
7695             }
7696         Target depTarget = iter->second;
7697         if (!executeTarget(depTarget, targetsCompleted))
7698             {
7699             return false;
7700             }
7701         }
7703     status("## Target : %s", name.c_str());
7705     //Now let's do the tasks
7706     std::vector<Task *> &tasks = target.getTasks();
7707     for (unsigned int i=0 ; i<tasks.size() ; i++)
7708         {
7709         Task *task = tasks[i];
7710         status("---- task : %s", task->getName().c_str());
7711         if (!task->execute())
7712             {
7713             return false;
7714             }
7715         }
7716         
7717     targetsCompleted.insert(name);
7718     
7719     return true;
7724 /**
7725  *  Main execute() method.  Start here and work
7726  *  up the dependency tree 
7727  */
7728 bool Make::execute()
7730     status("######## EXECUTE");
7732     //Determine initial target
7733     if (specifiedTarget.size()>0)
7734         {
7735         currentTarget = specifiedTarget;
7736         }
7737     else if (defaultTarget.size()>0)
7738         {
7739         currentTarget = defaultTarget;
7740         }
7741     else
7742         {
7743         error("execute: no specified or default target requested");
7744         return false;
7745         }
7747     std::map<String, Target>::iterator iter =
7748                    targets.find(currentTarget);
7749     if (iter == targets.end())
7750         {
7751         error("Initial target '%s' not found",
7752                          currentTarget.c_str());
7753         return false;
7754         }
7755         
7756     //Now run
7757     Target target = iter->second;
7758     std::set<String> targetsCompleted;
7759     if (!executeTarget(target, targetsCompleted))
7760         {
7761         return false;
7762         }
7764     status("######## EXECUTE COMPLETE");
7765     return true;
7771 /**
7772  *
7773  */
7774 bool Make::checkTargetDependencies(Target &target, 
7775                             std::vector<String> &depList)
7777     String tgtName = target.getName().c_str();
7778     depList.push_back(tgtName);
7780     std::vector<String> deps = target.getDependencies();
7781     for (unsigned int i=0 ; i<deps.size() ; i++)
7782         {
7783         String dep = deps[i];
7784         //First thing entered was the starting Target
7785         if (dep == depList[0])
7786             {
7787             error("Circular dependency '%s' found at '%s'",
7788                       dep.c_str(), tgtName.c_str());
7789             std::vector<String>::iterator diter;
7790             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7791                 {
7792                 error("  %s", diter->c_str());
7793                 }
7794             return false;
7795             }
7797         std::map<String, Target> &tgts =
7798                   target.getParent().getTargets();
7799         std::map<String, Target>::iterator titer = tgts.find(dep);
7800         if (titer == tgts.end())
7801             {
7802             error("Target '%s' dependency '%s' not found",
7803                       tgtName.c_str(), dep.c_str());
7804             return false;
7805             }
7806         if (!checkTargetDependencies(titer->second, depList))
7807             {
7808             return false;
7809             }
7810         }
7811     return true;
7818 static int getword(int pos, const String &inbuf, String &result)
7820     int p = pos;
7821     int len = (int)inbuf.size();
7822     String val;
7823     while (p < len)
7824         {
7825         char ch = inbuf[p];
7826         if (!isalnum(ch) && ch!='.' && ch!='_')
7827             break;
7828         val.push_back(ch);
7829         p++;
7830         }
7831     result = val;
7832     return p;
7838 /**
7839  *
7840  */
7841 bool Make::parsePropertyFile(const String &fileName,
7842                              const String &prefix)
7844     FILE *f = fopen(fileName.c_str(), "r");
7845     if (!f)
7846         {
7847         error("could not open property file %s", fileName.c_str());
7848         return false;
7849         }
7850     int linenr = 0;
7851     while (!feof(f))
7852         {
7853         char buf[256];
7854         if (!fgets(buf, 255, f))
7855             break;
7856         linenr++;
7857         String s = buf;
7858         s = trim(s);
7859         int len = s.size();
7860         if (len == 0)
7861             continue;
7862         if (s[0] == '#')
7863             continue;
7864         String key;
7865         String val;
7866         int p = 0;
7867         int p2 = getword(p, s, key);
7868         if (p2 <= p)
7869             {
7870             error("property file %s, line %d: expected keyword",
7871                                 fileName.c_str(), linenr);
7872                         return false;
7873                         }
7874                 if (prefix.size() > 0)
7875                     {
7876                     key.insert(0, prefix);
7877                     }
7879         //skip whitespace
7880                 for (p=p2 ; p<len ; p++)
7881                     if (!isspace(s[p]))
7882                         break;
7884         if (p>=len || s[p]!='=')
7885             {
7886             error("property file %s, line %d: expected '='",
7887                                 fileName.c_str(), linenr);
7888             return false;
7889             }
7890         p++;
7892         //skip whitespace
7893                 for ( ; p<len ; p++)
7894                     if (!isspace(s[p]))
7895                         break;
7897         /* This way expects a word after the =
7898                 p2 = getword(p, s, val);
7899         if (p2 <= p)
7900             {
7901             error("property file %s, line %d: expected value",
7902                                 fileName.c_str(), linenr);
7903                         return false;
7904                         }
7905                 */
7906         // This way gets the rest of the line after the =
7907                 if (p>=len)
7908             {
7909             error("property file %s, line %d: expected value",
7910                                 fileName.c_str(), linenr);
7911                         return false;
7912                         }
7913         val = s.substr(p);
7914                 if (key.size()==0 || val.size()==0)
7915                     continue;
7917         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7918         //See if we wanted to overload this property
7919         std::map<String, String>::iterator iter =
7920             specifiedProperties.find(key);
7921         if (iter!=specifiedProperties.end())
7922             {
7923             val = iter->second;
7924             status("overloading property '%s' = '%s'",
7925                    key.c_str(), val.c_str());
7926             }
7927             properties[key] = val;
7928         }
7929     fclose(f);
7930     return true;
7936 /**
7937  *
7938  */
7939 bool Make::parseProperty(Element *elem)
7941     std::vector<Attribute> &attrs = elem->getAttributes();
7942     for (unsigned int i=0 ; i<attrs.size() ; i++)
7943         {
7944         String attrName = attrs[i].getName();
7945         String attrVal  = attrs[i].getValue();
7947         if (attrName == "name")
7948             {
7949             String val;
7950                         if (!getAttribute(elem, "value", val))
7951                             return false;
7952             if (val.size() > 0)
7953                 {
7954                 properties[attrVal] = val;
7955                 }
7956             else
7957                 {
7958                 if (!getAttribute(elem, "location", val))
7959                     return false;
7960                 if (val.size() > 0)
7961                     {
7962                     properties[attrVal] = val;
7963                     }
7964                 }
7965             //See if we wanted to overload this property
7966             std::map<String, String>::iterator iter =
7967                 specifiedProperties.find(attrVal);
7968             if (iter != specifiedProperties.end())
7969                 {
7970                 val = iter->second;
7971                 status("overloading property '%s' = '%s'",
7972                     attrVal.c_str(), val.c_str());
7973                 properties[attrVal] = val;
7974                 }
7975             }
7976         else if (attrName == "file")
7977             {
7978             String prefix;
7979                         if (!getAttribute(elem, "prefix", prefix))
7980                             return false;
7981             if (prefix.size() > 0)
7982                 {
7983                 if (prefix[prefix.size()-1] != '.')
7984                     prefix.push_back('.');
7985                 }
7986             if (!parsePropertyFile(attrName, prefix))
7987                 return false;
7988             }
7989         else if (attrName == "environment")
7990             {
7991             if (envAlias.size() > 0)
7992                 {
7993                 error("environment property can only be set once");
7994                 return false;
7995                 }
7996             envAlias = attrVal;
7997             }
7998         }
8000     return true;
8006 /**
8007  *
8008  */
8009 bool Make::parseFile()
8011     status("######## PARSE : %s", uri.getPath().c_str());
8013     Parser parser;
8014     Element *root = parser.parseFile(uri.getNativePath());
8015     if (!root)
8016         {
8017         error("Could not open %s for reading",
8018                       uri.getNativePath().c_str());
8019         return false;
8020         }
8022     if (root->getChildren().size()==0 ||
8023         root->getChildren()[0]->getName()!="project")
8024         {
8025         error("Main xml element should be <project>");
8026         delete root;
8027         return false;
8028         }
8030     //########## Project attributes
8031     Element *project = root->getChildren()[0];
8032     String s = project->getAttribute("name");
8033     if (s.size() > 0)
8034         projectName = s;
8035     s = project->getAttribute("default");
8036     if (s.size() > 0)
8037         defaultTarget = s;
8038     s = project->getAttribute("basedir");
8039     if (s.size() > 0)
8040         baseDir = s;
8042     //######### PARSE MEMBERS
8043     std::vector<Element *> children = project->getChildren();
8044     for (unsigned int i=0 ; i<children.size() ; i++)
8045         {
8046         Element *elem = children[i];
8047         String tagName = elem->getName();
8049         //########## DESCRIPTION
8050         if (tagName == "description")
8051             {
8052             description = parser.trim(elem->getValue());
8053             }
8055         //######### PROPERTY
8056         else if (tagName == "property")
8057             {
8058             if (!parseProperty(elem))
8059                 return false;
8060             }
8062         //######### TARGET
8063         else if (tagName == "target")
8064             {
8065             String tname   = elem->getAttribute("name");
8066             String tdesc   = elem->getAttribute("description");
8067             String tdeps   = elem->getAttribute("depends");
8068             String tif     = elem->getAttribute("if");
8069             String tunless = elem->getAttribute("unless");
8070             Target target(*this);
8071             target.setName(tname);
8072             target.setDescription(tdesc);
8073             target.parseDependencies(tdeps);
8074             target.setIf(tif);
8075             target.setUnless(tunless);
8076             std::vector<Element *> telems = elem->getChildren();
8077             for (unsigned int i=0 ; i<telems.size() ; i++)
8078                 {
8079                 Element *telem = telems[i];
8080                 Task breeder(*this);
8081                 Task *task = breeder.createTask(telem);
8082                 if (!task)
8083                     return false;
8084                 allTasks.push_back(task);
8085                 target.addTask(task);
8086                 }
8088             //Check name
8089             if (tname.size() == 0)
8090                 {
8091                 error("no name for target");
8092                 return false;
8093                 }
8094             //Check for duplicate name
8095             if (targets.find(tname) != targets.end())
8096                 {
8097                 error("target '%s' already defined", tname.c_str());
8098                 return false;
8099                 }
8100             //more work than targets[tname]=target, but avoids default allocator
8101             targets.insert(std::make_pair<String, Target>(tname, target));
8102             }
8104         }
8106     std::map<String, Target>::iterator iter;
8107     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8108         {
8109         Target tgt = iter->second;
8110         std::vector<String> depList;
8111         if (!checkTargetDependencies(tgt, depList))
8112             {
8113             return false;
8114             }
8115         }
8118     delete root;
8119     status("######## PARSE COMPLETE");
8120     return true;
8124 /**
8125  * Overload a <property>
8126  */
8127 bool Make::specifyProperty(const String &name, const String &value)
8129     if (specifiedProperties.find(name) != specifiedProperties.end())
8130         {
8131         error("Property %s already specified", name.c_str());
8132         return false;
8133         }
8134     specifiedProperties[name] = value;
8135     return true;
8140 /**
8141  *
8142  */
8143 bool Make::run()
8145     if (!parseFile())
8146         return false;
8147         
8148     if (!execute())
8149         return false;
8151     return true;
8155 /**
8156  *
8157  */
8158 bool Make::run(const String &target)
8160     status("####################################################");
8161     status("#   %s", version().c_str());
8162     status("####################################################");
8163     specifiedTarget = target;
8164     if (!run())
8165         return false;
8166     status("####################################################");
8167     status("#   BuildTool Completed ");
8168     status("####################################################");
8169     return true;
8178 }// namespace buildtool
8179 //########################################################################
8180 //# M A I N
8181 //########################################################################
8183 typedef buildtool::String String;
8185 /**
8186  *  Format an error message in printf() style
8187  */
8188 static void error(char *fmt, ...)
8190     va_list ap;
8191     va_start(ap, fmt);
8192     fprintf(stderr, "BuildTool error: ");
8193     vfprintf(stderr, fmt, ap);
8194     fprintf(stderr, "\n");
8195     va_end(ap);
8199 static bool parseProperty(const String &s, String &name, String &val)
8201     int len = s.size();
8202     int i;
8203     for (i=0 ; i<len ; i++)
8204         {
8205         char ch = s[i];
8206         if (ch == '=')
8207             break;
8208         name.push_back(ch);
8209         }
8210     if (i>=len || s[i]!='=')
8211         {
8212         error("property requires -Dname=value");
8213         return false;
8214         }
8215     i++;
8216     for ( ; i<len ; i++)
8217         {
8218         char ch = s[i];
8219         val.push_back(ch);
8220         }
8221     return true;
8225 /**
8226  * Compare a buffer with a key, for the length of the key
8227  */
8228 static bool sequ(const String &buf, char *key)
8230     int len = buf.size();
8231     for (int i=0 ; key[i] && i<len ; i++)
8232         {
8233         if (key[i] != buf[i])
8234             return false;
8235         }        
8236     return true;
8239 static void usage(int argc, char **argv)
8241     printf("usage:\n");
8242     printf("   %s [options] [target]\n", argv[0]);
8243     printf("Options:\n");
8244     printf("  -help, -h              print this message\n");
8245     printf("  -version               print the version information and exit\n");
8246     printf("  -file <file>           use given buildfile\n");
8247     printf("  -f <file>                 ''\n");
8248     printf("  -D<property>=<value>   use value for given property\n");
8254 /**
8255  * Parse the command-line args, get our options,
8256  * and run this thing
8257  */   
8258 static bool parseOptions(int argc, char **argv)
8260     if (argc < 1)
8261         {
8262         error("Cannot parse arguments");
8263         return false;
8264         }
8266     buildtool::Make make;
8268     String target;
8270     //char *progName = argv[0];
8271     for (int i=1 ; i<argc ; i++)
8272         {
8273         String arg = argv[i];
8274         if (arg.size()>1 && arg[0]=='-')
8275             {
8276             if (arg == "-h" || arg == "-help")
8277                 {
8278                 usage(argc,argv);
8279                 return true;
8280                 }
8281             else if (arg == "-version")
8282                 {
8283                 printf("%s", make.version().c_str());
8284                 return true;
8285                 }
8286             else if (arg == "-f" || arg == "-file")
8287                 {
8288                 if (i>=argc)
8289                    {
8290                    usage(argc, argv);
8291                    return false;
8292                    }
8293                 i++; //eat option
8294                 make.setURI(argv[i]);
8295                 }
8296             else if (arg.size()>2 && sequ(arg, "-D"))
8297                 {
8298                 String s = arg.substr(2, s.size());
8299                 String name, value;
8300                 if (!parseProperty(s, name, value))
8301                    {
8302                    usage(argc, argv);
8303                    return false;
8304                    }
8305                 if (!make.specifyProperty(name, value))
8306                     return false;
8307                 }
8308             else
8309                 {
8310                 error("Unknown option:%s", arg.c_str());
8311                 return false;
8312                 }
8313             }
8314         else
8315             {
8316             if (target.size()>0)
8317                 {
8318                 error("only one initial target");
8319                 usage(argc, argv);
8320                 return false;
8321                 }
8322             target = arg;
8323             }
8324         }
8326     //We have the options.  Now execute them
8327     if (!make.run(target))
8328         return false;
8330     return true;
8336 /*
8337 static bool runMake()
8339     buildtool::Make make;
8340     if (!make.run())
8341         return false;
8342     return true;
8346 static bool pkgConfigTest()
8348     buildtool::PkgConfig pkgConfig;
8349     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8350         return false;
8351     return true;
8356 static bool depTest()
8358     buildtool::DepTool deptool;
8359     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8360     if (!deptool.generateDependencies("build.dep"))
8361         return false;
8362     std::vector<buildtool::DepRec> res =
8363                deptool.loadDepFile("build.dep");
8364         if (res.size() == 0)
8365         return false;
8366     return true;
8369 static bool popenTest()
8371     buildtool::Make make;
8372     buildtool::String out, err;
8373         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8374     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8375     return true;
8379 static bool propFileTest()
8381     buildtool::Make make;
8382     make.parsePropertyFile("test.prop", "test.");
8383     return true;
8385 */
8387 int main(int argc, char **argv)
8390     if (!parseOptions(argc, argv))
8391         return 1;
8392     /*
8393     if (!popenTest())
8394         return 1;
8396     if (!depTest())
8397         return 1;
8398     if (!propFileTest())
8399         return 1;
8400     if (runMake())
8401         return 1;
8402     */
8403     return 0;
8407 //########################################################################
8408 //# E N D 
8409 //########################################################################