Code

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