Code

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