Code

Improve option parsing
[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);
4887     /**
4888      *  Save a dependency file
4889      */
4890     bool saveDepFile(const String &fileName);
4893 private:
4896     /**
4897      *
4898      */
4899     void parseName(const String &fullname,
4900                    String &path,
4901                    String &basename,
4902                    String &suffix);
4904     /**
4905      *
4906      */
4907     int get(int pos);
4909     /**
4910      *
4911      */
4912     int skipwhite(int pos);
4914     /**
4915      *
4916      */
4917     int getword(int pos, String &ret);
4919     /**
4920      *
4921      */
4922     bool sequ(int pos, char *key);
4924     /**
4925      *
4926      */
4927     bool addIncludeFile(FileRec *frec, const String &fname);
4929     /**
4930      *
4931      */
4932     bool scanFile(const String &fname, FileRec *frec);
4934     /**
4935      *
4936      */
4937     bool processDependency(FileRec *ofile,
4938                            FileRec *include,
4939                            int depth);
4941     /**
4942      *
4943      */
4944     String sourceDir;
4946     /**
4947      *
4948      */
4949     std::vector<String> fileList;
4951     /**
4952      *
4953      */
4954     std::vector<String> directories;
4956     /**
4957      * A list of all files which will be processed for
4958      * dependencies.  This is the only list that has the actual
4959      * records.  All other lists have pointers to these records.     
4960      */
4961     std::map<String, FileRec *> allFiles;
4963     /**
4964      * The list of .o files, and the
4965      * dependencies upon them.
4966      */
4967     std::map<String, FileRec *> depFiles;
4969     int depFileSize;
4970     char *depFileBuf;
4971     
4973 };
4979 /**
4980  *  Clean up after processing.  Called by the destructor, but should
4981  *  also be called before the object is reused.
4982  */
4983 void DepTool::init()
4985     sourceDir = ".";
4987     fileList.clear();
4988     directories.clear();
4989     
4990     //clear refs
4991     depFiles.clear();
4992     //clear records
4993     std::map<String, FileRec *>::iterator iter;
4994     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4995          delete iter->second;
4997     allFiles.clear(); 
5004 /**
5005  *  Parse a full path name into path, base name, and suffix
5006  */
5007 void DepTool::parseName(const String &fullname,
5008                         String &path,
5009                         String &basename,
5010                         String &suffix)
5012     if (fullname.size() < 2)
5013         return;
5015     unsigned int pos = fullname.find_last_of('/');
5016     if (pos != fullname.npos && pos<fullname.size()-1)
5017         {
5018         path = fullname.substr(0, pos);
5019         pos++;
5020         basename = fullname.substr(pos, fullname.size()-pos);
5021         }
5022     else
5023         {
5024         path = "";
5025         basename = fullname;
5026         }
5028     pos = basename.find_last_of('.');
5029     if (pos != basename.npos && pos<basename.size()-1)
5030         {
5031         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5032         basename = basename.substr(0, pos);
5033         }
5035     //trace("parsename:%s %s %s", path.c_str(),
5036     //        basename.c_str(), suffix.c_str()); 
5041 /**
5042  *  Generate our internal file list.
5043  */
5044 bool DepTool::createFileList()
5047     for (unsigned int i=0 ; i<fileList.size() ; i++)
5048         {
5049         String fileName = fileList[i];
5050         //trace("## FileName:%s", fileName.c_str());
5051         String path;
5052         String basename;
5053         String sfx;
5054         parseName(fileName, path, basename, sfx);
5055         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5056                     sfx == "cc" || sfx == "CC")
5057             {
5058             FileRec *fe         = new FileRec(FileRec::CFILE);
5059             fe->path            = path;
5060             fe->baseName        = basename;
5061             fe->suffix          = sfx;
5062             allFiles[fileName]  = fe;
5063             }
5064         else if (sfx == "h"   ||  sfx == "hh"  ||
5065                  sfx == "hpp" ||  sfx == "hxx")
5066             {
5067             FileRec *fe         = new FileRec(FileRec::HFILE);
5068             fe->path            = path;
5069             fe->baseName        = basename;
5070             fe->suffix          = sfx;
5071             allFiles[fileName]  = fe;
5072             }
5073         }
5075     if (!listDirectories(sourceDir, "", directories))
5076         return false;
5077         
5078     return true;
5085 /**
5086  * Get a character from the buffer at pos.  If out of range,
5087  * return -1 for safety
5088  */
5089 int DepTool::get(int pos)
5091     if (pos>depFileSize)
5092         return -1;
5093     return depFileBuf[pos];
5098 /**
5099  *  Skip over all whitespace characters beginning at pos.  Return
5100  *  the position of the first non-whitespace character.
5101  */
5102 int DepTool::skipwhite(int pos)
5104     while (pos < depFileSize)
5105         {
5106         int ch = get(pos);
5107         if (ch < 0)
5108             break;
5109         if (!isspace(ch))
5110             break;
5111         pos++;
5112         }
5113     return pos;
5117 /**
5118  *  Parse the buffer beginning at pos, for a word.  Fill
5119  *  'ret' with the result.  Return the position after the
5120  *  word.
5121  */
5122 int DepTool::getword(int pos, String &ret)
5124     while (pos < depFileSize)
5125         {
5126         int ch = get(pos);
5127         if (ch < 0)
5128             break;
5129         if (isspace(ch))
5130             break;
5131         ret.push_back((char)ch);
5132         pos++;
5133         }
5134     return pos;
5137 /**
5138  * Return whether the sequence of characters in the buffer
5139  * beginning at pos match the key,  for the length of the key
5140  */
5141 bool DepTool::sequ(int pos, char *key)
5143     while (*key)
5144         {
5145         if (*key != get(pos))
5146             return false;
5147         key++; pos++;
5148         }
5149     return true;
5154 /**
5155  *  Add an include file name to a file record.  If the name
5156  *  is not found in allFiles explicitly, try prepending include
5157  *  directory names to it and try again.
5158  */
5159 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5162     std::map<String, FileRec *>::iterator iter =
5163            allFiles.find(iname);
5164     if (iter != allFiles.end()) //already exists
5165         {
5166          //h file in same dir
5167         FileRec *other = iter->second;
5168         //trace("local: '%s'", iname.c_str());
5169         frec->files[iname] = other;
5170         return true;
5171         }
5172     else 
5173         {
5174         //look in other dirs
5175         std::vector<String>::iterator diter;
5176         for (diter=directories.begin() ;
5177              diter!=directories.end() ; diter++)
5178             {
5179             String dfname = *diter;
5180             dfname.append("/");
5181             dfname.append(iname);
5182             iter = allFiles.find(dfname);
5183             if (iter != allFiles.end())
5184                 {
5185                 FileRec *other = iter->second;
5186                 //trace("other: '%s'", iname.c_str());
5187                 frec->files[dfname] = other;
5188                 return true;
5189                 }
5190             }
5191         }
5192     return true;
5197 /**
5198  *  Lightly parse a file to find the #include directives.  Do
5199  *  a bit of state machine stuff to make sure that the directive
5200  *  is valid.  (Like not in a comment).
5201  */
5202 bool DepTool::scanFile(const String &fname, FileRec *frec)
5204     String fileName;
5205     if (sourceDir.size() > 0)
5206         {
5207         fileName.append(sourceDir);
5208         fileName.append("/");
5209         }
5210     fileName.append(fname);
5211     String nativeName = getNativePath(fileName);
5212     FILE *f = fopen(nativeName.c_str(), "r");
5213     if (!f)
5214         {
5215         error("Could not open '%s' for reading", fname.c_str());
5216         return false;
5217         }
5218     String buf;
5219     while (true)
5220         {
5221         int ch = fgetc(f);
5222         if (ch < 0)
5223             break;
5224         buf.push_back((char)ch);
5225         }
5226     fclose(f);
5228     depFileSize = buf.size();
5229     depFileBuf  = (char *)buf.c_str();
5230     int pos = 0;
5233     while (pos < depFileSize)
5234         {
5235         //trace("p:%c", get(pos));
5237         //# Block comment
5238         if (get(pos) == '/' && get(pos+1) == '*')
5239             {
5240             pos += 2;
5241             while (pos < depFileSize)
5242                 {
5243                 if (get(pos) == '*' && get(pos+1) == '/')
5244                     {
5245                     pos += 2;
5246                     break;
5247                     }
5248                 else
5249                     pos++;
5250                 }
5251             }
5252         //# Line comment
5253         else if (get(pos) == '/' && get(pos+1) == '/')
5254             {
5255             pos += 2;
5256             while (pos < depFileSize)
5257                 {
5258                 if (get(pos) == '\n')
5259                     {
5260                     pos++;
5261                     break;
5262                     }
5263                 else
5264                     pos++;
5265                 }
5266             }
5267         //# #include! yaay
5268         else if (sequ(pos, "#include"))
5269             {
5270             pos += 8;
5271             pos = skipwhite(pos);
5272             String iname;
5273             pos = getword(pos, iname);
5274             if (iname.size()>2)
5275                 {
5276                 iname = iname.substr(1, iname.size()-2);
5277                 addIncludeFile(frec, iname);
5278                 }
5279             }
5280         else
5281             {
5282             pos++;
5283             }
5284         }
5286     return true;
5291 /**
5292  *  Recursively check include lists to find all files in allFiles to which
5293  *  a given file is dependent.
5294  */
5295 bool DepTool::processDependency(FileRec *ofile,
5296                              FileRec *include,
5297                              int depth)
5299     std::map<String, FileRec *>::iterator iter;
5300     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5301         {
5302         String fname  = iter->first;
5303         if (ofile->files.find(fname) != ofile->files.end())
5304             {
5305             //trace("file '%s' already seen", fname.c_str());
5306             continue;
5307             }
5308         FileRec *child  = iter->second;
5309         ofile->files[fname] = child;
5310       
5311         processDependency(ofile, child, depth+1);
5312         }
5315     return true;
5322 /**
5323  *  Generate the file dependency list.
5324  */
5325 bool DepTool::generateDependencies()
5327     std::map<String, FileRec *>::iterator iter;
5328     //# First pass.  Scan for all includes
5329     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5330         {
5331         FileRec *frec = iter->second;
5332         if (!scanFile(iter->first, frec))
5333             {
5334             //quit?
5335             }
5336         }
5338     //# Second pass.  Scan for all includes
5339     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5340         {
5341         FileRec *include = iter->second;
5342         if (include->type == FileRec::CFILE)
5343             {
5344             String cFileName = iter->first;
5345             FileRec *ofile      = new FileRec(FileRec::OFILE);
5346             ofile->path         = include->path;
5347             ofile->baseName     = include->baseName;
5348             ofile->suffix       = include->suffix;
5349             String fname     = include->path;
5350             if (fname.size()>0)
5351                 fname.append("/");
5352             fname.append(include->baseName);
5353             fname.append(".o");
5354             depFiles[fname]    = ofile;
5355             //add the .c file first?   no, don't
5356             //ofile->files[cFileName] = include;
5357             
5358             //trace("ofile:%s", fname.c_str());
5360             processDependency(ofile, include, 0);
5361             }
5362         }
5364       
5365     return true;
5370 /**
5371  *  High-level call to generate deps and optionally save them
5372  */
5373 bool DepTool::generateDependencies(const String &fileName)
5375     if (!createFileList())
5376         return false;
5377     if (!generateDependencies())
5378         return false;
5379     if (!saveDepFile(fileName))
5380         return false;
5381     return true;
5385 /**
5386  *   This saves the dependency cache.
5387  */
5388 bool DepTool::saveDepFile(const String &fileName)
5390     time_t tim;
5391     time(&tim);
5393     FILE *f = fopen(fileName.c_str(), "w");
5394     if (!f)
5395         {
5396         trace("cannot open '%s' for writing", fileName.c_str());
5397         }
5398     fprintf(f, "<?xml version='1.0'?>\n");
5399     fprintf(f, "<!--\n");
5400     fprintf(f, "########################################################\n");
5401     fprintf(f, "## File: build.dep\n");
5402     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5403     fprintf(f, "########################################################\n");
5404     fprintf(f, "-->\n");
5406     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5407     std::map<String, FileRec *>::iterator iter;
5408     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5409         {
5410         FileRec *frec = iter->second;
5411         if (frec->type == FileRec::OFILE)
5412             {
5413             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5414                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5415             std::map<String, FileRec *>::iterator citer;
5416             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5417                 {
5418                 String cfname = citer->first;
5419                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5420                 }
5421             fprintf(f, "</object>\n\n");
5422             }
5423         }
5425     fprintf(f, "</dependencies>\n");
5426     fprintf(f, "\n");
5427     fprintf(f, "<!--\n");
5428     fprintf(f, "########################################################\n");
5429     fprintf(f, "## E N D\n");
5430     fprintf(f, "########################################################\n");
5431     fprintf(f, "-->\n");
5433     fclose(f);
5435     return true;
5441 /**
5442  *   This loads the dependency cache.
5443  */
5444 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5446     std::vector<DepRec> result;
5447     
5448     Parser parser;
5449     Element *root = parser.parseFile(depFile.c_str());
5450     if (!root)
5451         {
5452         error("Could not open %s for reading", depFile.c_str());
5453         return result;
5454         }
5456     if (root->getChildren().size()==0 ||
5457         root->getChildren()[0]->getName()!="dependencies")
5458         {
5459         error("Main xml element should be <dependencies>");
5460         delete root;
5461         return result;
5462         }
5464     //########## Start parsing
5465     Element *depList = root->getChildren()[0];
5467     std::vector<Element *> objects = depList->getChildren();
5468     for (unsigned int i=0 ; i<objects.size() ; i++)
5469         {
5470         Element *objectElem = objects[i];
5471         String tagName = objectElem->getName();
5472         if (tagName == "object")
5473             {
5474             String objName   = objectElem->getAttribute("name");
5475              //trace("object:%s", objName.c_str());
5476             DepRec depObject(objName);
5477             depObject.path   = objectElem->getAttribute("path");
5478             depObject.suffix = objectElem->getAttribute("suffix");
5479             //########## DESCRIPTION
5480             std::vector<Element *> depElems = objectElem->getChildren();
5481             for (unsigned int i=0 ; i<depElems.size() ; i++)
5482                 {
5483                 Element *depElem = depElems[i];
5484                 tagName = depElem->getName();
5485                 if (tagName == "dep")
5486                     {
5487                     String depName = depElem->getAttribute("name");
5488                     //trace("    dep:%s", depName.c_str());
5489                     depObject.files.push_back(depName);
5490                     }
5491                 }
5492             //Insert into the result list, in a sorted manner
5493             bool inserted = false;
5494             std::vector<DepRec>::iterator iter;
5495             for (iter = result.begin() ; iter != result.end() ; iter++)
5496                 {
5497                 String vpath = iter->path;
5498                 vpath.append("/");
5499                 vpath.append(iter->name);
5500                 String opath = depObject.path;
5501                 opath.append("/");
5502                 opath.append(depObject.name);
5503                 if (vpath > opath)
5504                     {
5505                     inserted = true;
5506                     iter = result.insert(iter, depObject);
5507                     break;
5508                     }
5509                 }
5510             if (!inserted)
5511                 result.push_back(depObject);
5512             }
5513         }
5515     delete root;
5517     return result;
5521 /**
5522  *   This loads the dependency cache.
5523  */
5524 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
5526     std::vector<DepRec> result = loadDepFile(depFile);
5527     if (result.size() == 0)
5528         {
5529         generateDependencies(depFile);
5530         result = loadDepFile(depFile);
5531         }
5532     return result;
5538 //########################################################################
5539 //# T A S K
5540 //########################################################################
5541 //forward decl
5542 class Target;
5543 class Make;
5545 /**
5546  *
5547  */
5548 class Task : public MakeBase
5551 public:
5553     typedef enum
5554         {
5555         TASK_NONE,
5556         TASK_AR,
5557         TASK_CC,
5558         TASK_COPY,
5559         TASK_DELETE,
5560         TASK_JAR,
5561         TASK_JAVAC,
5562         TASK_LINK,
5563         TASK_MAKEFILE,
5564         TASK_MKDIR,
5565         TASK_MSGFMT,
5566         TASK_RANLIB,
5567         TASK_RC,
5568         TASK_STRIP,
5569         TASK_TSTAMP
5570         } TaskType;
5571         
5573     /**
5574      *
5575      */
5576     Task(MakeBase &par) : parent(par)
5577         { init(); }
5579     /**
5580      *
5581      */
5582     Task(const Task &other) : parent(other.parent)
5583         { init(); assign(other); }
5585     /**
5586      *
5587      */
5588     Task &operator=(const Task &other)
5589         { assign(other); return *this; }
5591     /**
5592      *
5593      */
5594     virtual ~Task()
5595         { }
5598     /**
5599      *
5600      */
5601     virtual MakeBase &getParent()
5602         { return parent; }
5604      /**
5605      *
5606      */
5607     virtual int  getType()
5608         { return type; }
5610     /**
5611      *
5612      */
5613     virtual void setType(int val)
5614         { type = val; }
5616     /**
5617      *
5618      */
5619     virtual String getName()
5620         { return name; }
5622     /**
5623      *
5624      */
5625     virtual bool execute()
5626         { return true; }
5628     /**
5629      *
5630      */
5631     virtual bool parse(Element *elem)
5632         { return true; }
5634     /**
5635      *
5636      */
5637     Task *createTask(Element *elem);
5640 protected:
5642     void init()
5643         {
5644         type = TASK_NONE;
5645         name = "none";
5646         }
5648     void assign(const Task &other)
5649         {
5650         type = other.type;
5651         name = other.name;
5652         }
5653         
5654     String getAttribute(Element *elem, const String &attrName)
5655         {
5656         String str;
5657         return str;
5658         }
5660     MakeBase &parent;
5662     int type;
5664     String name;
5665 };
5670 /**
5671  * Run the "ar" command to archive .o's into a .a
5672  */
5673 class TaskAr : public Task
5675 public:
5677     TaskAr(MakeBase &par) : Task(par)
5678         {
5679                 type = TASK_AR; name = "ar";
5680                 command = "ar crv";
5681                 }
5683     virtual ~TaskAr()
5684         {}
5686     virtual bool execute()
5687         {
5688         //trace("###########HERE %d", fileSet.size());
5689         bool doit = false;
5690         
5691         String fullOut = parent.resolve(fileName);
5692         //trace("ar fullout: %s", fullOut.c_str());
5693         
5694         if (!listFiles(parent, fileSet))
5695             return false;
5696         String fileSetDir = fileSet.getDirectory();
5698         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5699             {
5700             String fname;
5701                         if (fileSetDir.size()>0)
5702                             {
5703                             fname.append(fileSetDir);
5704                 fname.append("/");
5705                 }
5706             fname.append(fileSet[i]);
5707             String fullName = parent.resolve(fname);
5708             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
5709             if (isNewerThan(fullName, fullOut))
5710                 doit = true;
5711             }
5712         //trace("Needs it:%d", doit);
5713         if (!doit)
5714             {
5715             return true;
5716             }
5718         String cmd = command;
5719         cmd.append(" ");
5720         cmd.append(fullOut);
5721         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5722             {
5723             String fname;
5724                         if (fileSetDir.size()>0)
5725                             {
5726                             fname.append(fileSetDir);
5727                 fname.append("/");
5728                 }
5729             fname.append(fileSet[i]);
5730             String fullName = parent.resolve(fname);
5732             cmd.append(" ");
5733             cmd.append(fullName);
5734             }
5736         String outString, errString;
5737         if (!executeCommand(cmd.c_str(), "", outString, errString))
5738             {
5739             error("AR problem: %s", errString.c_str());
5740             return false;
5741             }
5743         return true;
5744         }
5746     virtual bool parse(Element *elem)
5747         {
5748         if (!parent.getAttribute(elem, "file", fileName))
5749             return false;
5750             
5751         std::vector<Element *> children = elem->getChildren();
5752         for (unsigned int i=0 ; i<children.size() ; i++)
5753             {
5754             Element *child = children[i];
5755             String tagName = child->getName();
5756             if (tagName == "fileset")
5757                 {
5758                 if (!parseFileSet(child, parent, fileSet))
5759                     return false;
5760                 }
5761             }
5762         return true;
5763         }
5765 private:
5767     String command;
5768     String fileName;
5769     FileSet fileSet;
5771 };
5774 /**
5775  * This task runs the C/C++ compiler.  The compiler is invoked
5776  * for all .c or .cpp files which are newer than their correcsponding
5777  * .o files.  
5778  */
5779 class TaskCC : public Task
5781 public:
5783     TaskCC(MakeBase &par) : Task(par)
5784         {
5785                 type = TASK_CC; name = "cc";
5786                 ccCommand   = "gcc";
5787                 cxxCommand  = "g++";
5788                 source      = ".";
5789                 dest        = ".";
5790                 flags       = "";
5791                 defines     = "";
5792                 includes    = "";
5793                 fileSet.clear();
5794         }
5796     virtual ~TaskCC()
5797         {}
5799     virtual bool execute()
5800         {
5801         if (!listFiles(parent, fileSet))
5802             return false;
5804         DepTool depTool;
5805         depTool.setSourceDirectory(source);
5806         depTool.setFileList(fileSet.getFiles());
5807         std::vector<DepRec> deps = depTool.getDepFile("build.dep");
5808         
5809         String incs;
5810         incs.append("-I");
5811         incs.append(parent.resolve("."));
5812         incs.append(" ");
5813         if (includes.size()>0)
5814             {
5815             incs.append(includes);
5816             incs.append(" ");
5817             }
5818         std::set<String> paths;
5819         std::vector<DepRec>::iterator viter;
5820         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5821             {
5822             DepRec dep = *viter;
5823             if (dep.path.size()>0)
5824                 paths.insert(dep.path);
5825             }
5826         if (source.size()>0)
5827             {
5828             incs.append(" -I");
5829             incs.append(parent.resolve(source));
5830             incs.append(" ");
5831             }
5832         std::set<String>::iterator setIter;
5833         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5834             {
5835             incs.append(" -I");
5836             String dname;
5837             if (source.size()>0)
5838                 {
5839                 dname.append(source);
5840                 dname.append("/");
5841                 }
5842             dname.append(*setIter);
5843             incs.append(parent.resolve(dname));
5844             }
5845         std::vector<String> cfiles;
5846         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5847             {
5848             DepRec dep = *viter;
5850             //## Select command
5851             String sfx = dep.suffix;
5852             String command = ccCommand;
5853             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5854                              || sfx == "CC")
5855                             command = cxxCommand;
5856  
5857             //## Make paths
5858             String destPath = dest;
5859             String srcPath  = source;
5860             if (dep.path.size()>0)
5861                             {
5862                 destPath.append("/");
5863                                 destPath.append(dep.path);
5864                 srcPath.append("/");
5865                                 srcPath.append(dep.path);
5866                             }
5867             //## Make sure destination directory exists
5868                         if (!createDirectory(destPath))
5869                             return false;
5870                             
5871             //## Check whether it needs to be done
5872                         String destFullName = destPath;
5873                         destFullName.append("/");
5874                         destFullName.append(dep.name);
5875                         destFullName.append(".o");
5876                         String srcFullName = srcPath;
5877                         srcFullName.append("/");
5878                         srcFullName.append(dep.name);
5879                         srcFullName.append(".");
5880                         srcFullName.append(dep.suffix);
5881             if (!isNewerThan(srcFullName, destFullName))
5882                 {
5883                 //trace("%s skipped", srcFullName.c_str());
5884                 continue;
5885                 }
5887             //## Assemble the command
5888             String cmd = command;
5889             cmd.append(" -c ");
5890             cmd.append(flags);
5891                         cmd.append(" ");
5892             cmd.append(defines);
5893                         cmd.append(" ");
5894             cmd.append(incs);
5895                         cmd.append(" ");
5896                         cmd.append(srcFullName);
5897             cmd.append(" -o ");
5898                         cmd.append(destFullName);
5900             //## Execute the command
5902             String outString, errString;
5903             if (!executeCommand(cmd.c_str(), "", outString, errString))
5904                 {
5905                 error("problem compiling: %s", errString.c_str());
5906                 return false;
5907                 }
5908             }
5909         
5910         return true;
5911         }
5913     virtual bool parse(Element *elem)
5914         {
5915         String s;
5916         if (!parent.getAttribute(elem, "command", s))
5917             return false;
5918         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5919         if (!parent.getAttribute(elem, "cc", s))
5920             return false;
5921         if (s.size()>0) ccCommand = s;
5922         if (!parent.getAttribute(elem, "cxx", s))
5923             return false;
5924         if (s.size()>0) cxxCommand = s;
5925         if (!parent.getAttribute(elem, "destdir", s))
5926             return false;
5927         if (s.size()>0) dest = s;
5929         std::vector<Element *> children = elem->getChildren();
5930         for (unsigned int i=0 ; i<children.size() ; i++)
5931             {
5932             Element *child = children[i];
5933             String tagName = child->getName();
5934             if (tagName == "flags")
5935                 {
5936                 if (!parent.getValue(child, flags))
5937                     return false;
5938                 flags = strip(flags);
5939                 }
5940             else if (tagName == "includes")
5941                 {
5942                 if (!parent.getValue(child, includes))
5943                     return false;
5944                 includes = strip(includes);
5945                 }
5946             else if (tagName == "defines")
5947                 {
5948                 if (!parent.getValue(child, defines))
5949                     return false;
5950                 defines = strip(defines);
5951                 }
5952             else if (tagName == "fileset")
5953                 {
5954                 if (!parseFileSet(child, parent, fileSet))
5955                     return false;
5956                 source = fileSet.getDirectory();
5957                 }
5958             }
5960         return true;
5961         }
5962         
5963 protected:
5965     String ccCommand;
5966     String cxxCommand;
5967     String source;
5968     String dest;
5969     String flags;
5970     String defines;
5971     String includes;
5972     FileSet fileSet;
5973     
5974 };
5978 /**
5979  *
5980  */
5981 class TaskCopy : public Task
5983 public:
5985     typedef enum
5986         {
5987         CP_NONE,
5988         CP_TOFILE,
5989         CP_TODIR
5990         } CopyType;
5992     TaskCopy(MakeBase &par) : Task(par)
5993         {
5994                 type = TASK_COPY; name = "copy";
5995                 cptype = CP_NONE;
5996                 verbose = false;
5997                 haveFileSet = false;
5998                 }
6000     virtual ~TaskCopy()
6001         {}
6003     virtual bool execute()
6004         {
6005         switch (cptype)
6006            {
6007            case CP_TOFILE:
6008                {
6009                if (fileName.size()>0)
6010                    {
6011                    status("          : %s", fileName.c_str());
6012                    String fullSource = parent.resolve(fileName);
6013                    String fullDest = parent.resolve(toFileName);
6014                    //trace("copy %s to file %s", fullSource.c_str(),
6015                                    //                       fullDest.c_str());
6016                                    if (!isRegularFile(fullSource))
6017                                        {
6018                        error("copy : file %s does not exist", fullSource.c_str());
6019                                        return false;
6020                                        }
6021                    if (!isNewerThan(fullSource, fullDest))
6022                        {
6023                        return true;
6024                        }
6025                    if (!copyFile(fullSource, fullDest))
6026                        return false;
6027                    status("          : 1 file copied");
6028                    }
6029                return true;
6030                }
6031            case CP_TODIR:
6032                {
6033                if (haveFileSet)
6034                    {
6035                    if (!listFiles(parent, fileSet))
6036                        return false;
6037                    String fileSetDir = fileSet.getDirectory();
6039                    int nrFiles = 0;
6040                    status("          : %s", fileSetDir.c_str());
6041                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6042                        {
6043                        String fileName = fileSet[i];
6045                        String sourcePath;
6046                        if (fileSetDir.size()>0)
6047                            {
6048                            sourcePath.append(fileSetDir);
6049                            sourcePath.append("/");
6050                            }
6051                        sourcePath.append(fileName);
6052                        String fullSource = parent.resolve(sourcePath);
6053                        
6054                        //Get the immediate parent directory's base name
6055                        String baseFileSetDir = fileSetDir;
6056                        unsigned int pos = baseFileSetDir.find_last_of('/');
6057                        if (pos!=baseFileSetDir.npos &&
6058                                                       pos < baseFileSetDir.size()-1)
6059                            baseFileSetDir =
6060                                                       baseFileSetDir.substr(pos+1,
6061                                                                baseFileSetDir.size());
6062                                            //Now make the new path
6063                        String destPath;
6064                        if (toDirName.size()>0)
6065                            {
6066                            destPath.append(toDirName);
6067                            destPath.append("/");
6068                            }
6069                        if (baseFileSetDir.size()>0)
6070                            {
6071                            destPath.append(baseFileSetDir);
6072                            destPath.append("/");
6073                            }
6074                        destPath.append(fileName);
6075                        String fullDest = parent.resolve(destPath);
6076                        //trace("fileName:%s", fileName.c_str());
6077                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6078                                        //                   fullDest.c_str());
6079                        if (!isNewerThan(fullSource, fullDest))
6080                            {
6081                            //trace("copy skipping %s", fullSource.c_str());
6082                            continue;
6083                            }
6084                        if (!copyFile(fullSource, fullDest))
6085                            return false;
6086                        nrFiles++;
6087                        }
6088                    status("          : %d file(s) copied", nrFiles);
6089                    }
6090                else //file source
6091                    {
6092                    //For file->dir we want only the basename of
6093                    //the source appended to the dest dir
6094                    status("          : %s", fileName.c_str());
6095                    String baseName = fileName;
6096                    unsigned int pos = baseName.find_last_of('/');
6097                    if (pos!=baseName.npos && pos<baseName.size()-1)
6098                        baseName = baseName.substr(pos+1, baseName.size());
6099                    String fullSource = parent.resolve(fileName);
6100                    String destPath;
6101                    if (toDirName.size()>0)
6102                        {
6103                        destPath.append(toDirName);
6104                        destPath.append("/");
6105                        }
6106                    destPath.append(baseName);
6107                    String fullDest = parent.resolve(destPath);
6108                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6109                                    //                       fullDest.c_str());
6110                                    if (!isRegularFile(fullSource))
6111                                        {
6112                        error("copy : file %s does not exist", fullSource.c_str());
6113                                        return false;
6114                                        }
6115                    if (!isNewerThan(fullSource, fullDest))
6116                        {
6117                        return true;
6118                        }
6119                    if (!copyFile(fullSource, fullDest))
6120                        return false;
6121                    status("          : 1 file copied");
6122                    }
6123                return true;
6124                }
6125            }
6126         return true;
6127         }
6130     virtual bool parse(Element *elem)
6131         {
6132         if (!parent.getAttribute(elem, "file", fileName))
6133             return false;
6134         if (!parent.getAttribute(elem, "tofile", toFileName))
6135             return false;
6136         if (toFileName.size() > 0)
6137             cptype = CP_TOFILE;
6138         if (!parent.getAttribute(elem, "todir", toDirName))
6139             return false;
6140         if (toDirName.size() > 0)
6141             cptype = CP_TODIR;
6142         String ret;
6143         if (!parent.getAttribute(elem, "verbose", ret))
6144             return false;
6145         if (ret.size()>0 && !getBool(ret, verbose))
6146             return false;
6147             
6148         haveFileSet = false;
6149         
6150         std::vector<Element *> children = elem->getChildren();
6151         for (unsigned int i=0 ; i<children.size() ; i++)
6152             {
6153             Element *child = children[i];
6154             String tagName = child->getName();
6155             if (tagName == "fileset")
6156                 {
6157                 if (!parseFileSet(child, parent, fileSet))
6158                     {
6159                     error("problem getting fileset");
6160                                         return false;
6161                                         }
6162                                 haveFileSet = true;
6163                 }
6164             }
6166         //Perform validity checks
6167                 if (fileName.size()>0 && fileSet.size()>0)
6168                     {
6169                     error("<copy> can only have one of : file= and <fileset>");
6170                     return false;
6171                     }
6172         if (toFileName.size()>0 && toDirName.size()>0)
6173             {
6174             error("<copy> can only have one of : tofile= or todir=");
6175             return false;
6176             }
6177         if (haveFileSet && toDirName.size()==0)
6178             {
6179             error("a <copy> task with a <fileset> must have : todir=");
6180             return false;
6181             }
6182                 if (cptype == CP_TOFILE && fileName.size()==0)
6183                     {
6184                     error("<copy> tofile= must be associated with : file=");
6185                     return false;
6186                     }
6187                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6188                     {
6189                     error("<copy> todir= must be associated with : file= or <fileset>");
6190                     return false;
6191                     }
6193         return true;
6194         }
6195         
6196 private:
6198     int cptype;
6199     String fileName;
6200     FileSet fileSet;
6201     String toFileName;
6202     String toDirName;
6203     bool verbose;
6204     bool haveFileSet;
6205 };
6208 /**
6209  *
6210  */
6211 class TaskDelete : public Task
6213 public:
6215     typedef enum
6216         {
6217         DEL_FILE,
6218         DEL_DIR,
6219         DEL_FILESET
6220         } DeleteType;
6222     TaskDelete(MakeBase &par) : Task(par)
6223         { 
6224                   type        = TASK_DELETE;
6225                   name        = "delete";
6226                   delType     = DEL_FILE;
6227           verbose     = false;
6228           quiet       = false;
6229           failOnError = true;
6230                 }
6232     virtual ~TaskDelete()
6233         {}
6235     virtual bool execute()
6236         {
6237         struct stat finfo;
6238         switch (delType)
6239             {
6240             case DEL_FILE:
6241                 {
6242                 status("          : %s", fileName.c_str());
6243                 String fullName = parent.resolve(fileName);
6244                 char *fname = (char *)fullName.c_str();
6245                 //does not exist
6246                 if (stat(fname, &finfo)<0)
6247                     return true;
6248                 //exists but is not a regular file
6249                 if (!S_ISREG(finfo.st_mode))
6250                     {
6251                     error("<delete> failed. '%s' exists and is not a regular file",
6252                           fname);
6253                     return false;
6254                     }
6255                 if (remove(fname)<0)
6256                     {
6257                     error("<delete> failed: %s", strerror(errno));
6258                     return false;
6259                     }
6260                 return true;
6261                 }
6262             case DEL_DIR:
6263                 {
6264                 status("          : %s", dirName.c_str());
6265                 String fullDir = parent.resolve(dirName);
6266                 if (!removeDirectory(fullDir))
6267                     return false;
6268                 return true;
6269                 }
6270             }
6271         return true;
6272         }
6274     virtual bool parse(Element *elem)
6275         {
6276         if (!parent.getAttribute(elem, "file", fileName))
6277             return false;
6278         if (fileName.size() > 0)
6279             delType = DEL_FILE;
6280         if (!parent.getAttribute(elem, "dir", dirName))
6281             return false;
6282         if (dirName.size() > 0)
6283             delType = DEL_DIR;
6284         if (fileName.size()>0 && dirName.size()>0)
6285             {
6286             error("<delete> can only have one attribute of file= or dir=");
6287             return false;
6288             }
6289         String ret;
6290         if (!parent.getAttribute(elem, "verbose", ret))
6291             return false;
6292         if (ret.size()>0 && !getBool(ret, verbose))
6293             return false;
6294         if (!parent.getAttribute(elem, "quiet", ret))
6295             return false;
6296         if (ret.size()>0 && !getBool(ret, quiet))
6297             return false;
6298         if (!parent.getAttribute(elem, "failonerror", ret))
6299             return false;
6300         if (ret.size()>0 && !getBool(ret, failOnError))
6301             return false;
6302         return true;
6303         }
6305 private:
6307     int delType;
6308     String dirName;
6309     String fileName;
6310     bool verbose;
6311     bool quiet;
6312     bool failOnError;
6313 };
6316 /**
6317  *
6318  */
6319 class TaskJar : public Task
6321 public:
6323     TaskJar(MakeBase &par) : Task(par)
6324         { type = TASK_JAR; name = "jar"; }
6326     virtual ~TaskJar()
6327         {}
6329     virtual bool execute()
6330         {
6331         return true;
6332         }
6334     virtual bool parse(Element *elem)
6335         {
6336         return true;
6337         }
6338 };
6341 /**
6342  *
6343  */
6344 class TaskJavac : public Task
6346 public:
6348     TaskJavac(MakeBase &par) : Task(par)
6349         { type = TASK_JAVAC; name = "javac"; }
6351     virtual ~TaskJavac()
6352         {}
6354     virtual bool execute()
6355         {
6356         return true;
6357         }
6359     virtual bool parse(Element *elem)
6360         {
6361         return true;
6362         }
6363 };
6366 /**
6367  *
6368  */
6369 class TaskLink : public Task
6371 public:
6373     TaskLink(MakeBase &par) : Task(par)
6374         {
6375                 type = TASK_LINK; name = "link";
6376                 command = "g++";
6377                 }
6379     virtual ~TaskLink()
6380         {}
6382     virtual bool execute()
6383         {
6384         if (!listFiles(parent, fileSet))
6385             return false;
6386         String fileSetDir = fileSet.getDirectory();
6387         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6388         bool doit = false;
6389         String fullTarget = parent.resolve(fileName);
6390         String cmd = command;
6391         cmd.append(" -o ");
6392         cmd.append(fullTarget);
6393         cmd.append(" ");
6394         cmd.append(flags);
6395         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6396             {
6397             cmd.append(" ");
6398             String obj;
6399             if (fileSetDir.size()>0)
6400                             {
6401                                 obj.append(fileSetDir);
6402                 obj.append("/");
6403                 }
6404             obj.append(fileSet[i]);
6405             String fullObj = parent.resolve(obj);
6406             cmd.append(fullObj);
6407             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6408             //          fullObj.c_str());
6409             if (isNewerThan(fullObj, fullTarget))
6410                 doit = true;
6411             }
6412         cmd.append(" ");
6413         cmd.append(libs);
6414         if (!doit)
6415             {
6416             //trace("link not needed");
6417             return true;
6418             }
6419         //trace("LINK cmd:%s", cmd.c_str());
6422         String outString, errString;
6423         if (!executeCommand(cmd.c_str(), "", outString, errString))
6424             {
6425             error("LINK problem: %s", errString.c_str());
6426             return false;
6427             }
6428         return true;
6429         }
6431     virtual bool parse(Element *elem)
6432         {
6433         if (!parent.getAttribute(elem, "command", command))
6434             return false;
6435         if (!parent.getAttribute(elem, "out", fileName))
6436             return false;
6437             
6438         std::vector<Element *> children = elem->getChildren();
6439         for (unsigned int i=0 ; i<children.size() ; i++)
6440             {
6441             Element *child = children[i];
6442             String tagName = child->getName();
6443             if (tagName == "fileset")
6444                 {
6445                 if (!parseFileSet(child, parent, fileSet))
6446                     return false;
6447                 }
6448             else if (tagName == "flags")
6449                 {
6450                 if (!parent.getValue(child, flags))
6451                     return false;
6452                 flags = strip(flags);
6453                 }
6454             else if (tagName == "libs")
6455                 {
6456                 if (!parent.getValue(child, libs))
6457                     return false;
6458                 libs = strip(libs);
6459                 }
6460             }
6461         return true;
6462         }
6464 private:
6466     String command;
6467     String fileName;
6468     String flags;
6469     String libs;
6470     FileSet fileSet;
6472 };
6476 /**
6477  * Create a named directory
6478  */
6479 class TaskMakeFile : public Task
6481 public:
6483     TaskMakeFile(MakeBase &par) : Task(par)
6484         { type = TASK_MAKEFILE; name = "makefile"; }
6486     virtual ~TaskMakeFile()
6487         {}
6489     virtual bool execute()
6490         {
6491         status("          : %s", fileName.c_str());
6492         String fullName = parent.resolve(fileName);
6493         if (!isNewerThan(parent.getURI().getPath(), fullName))
6494             {
6495             //trace("skipped <makefile>");
6496             return true;
6497             }
6498         //trace("fullName:%s", fullName.c_str());
6499         FILE *f = fopen(fullName.c_str(), "w");
6500         if (!f)
6501             {
6502             error("<makefile> could not open %s for writing : %s",
6503                 fullName.c_str(), strerror(errno));
6504             return false;
6505             }
6506         for (unsigned int i=0 ; i<text.size() ; i++)
6507             fputc(text[i], f);
6508         fclose(f);
6509         return true;
6510         }
6512     virtual bool parse(Element *elem)
6513         {
6514         if (!parent.getAttribute(elem, "file", fileName))
6515             return false;
6516         if (fileName.size() == 0)
6517             {
6518             error("<makefile> requires 'file=\"filename\"' attribute");
6519             return false;
6520             }
6521         if (!parent.getValue(elem, text))
6522             return false;
6523         text = leftJustify(text);
6524         //trace("dirname:%s", dirName.c_str());
6525         return true;
6526         }
6528 private:
6530     String fileName;
6531     String text;
6532 };
6536 /**
6537  * Create a named directory
6538  */
6539 class TaskMkDir : public Task
6541 public:
6543     TaskMkDir(MakeBase &par) : Task(par)
6544         { type = TASK_MKDIR; name = "mkdir"; }
6546     virtual ~TaskMkDir()
6547         {}
6549     virtual bool execute()
6550         {
6551         status("          : %s", dirName.c_str());
6552         String fullDir = parent.resolve(dirName);
6553         //trace("fullDir:%s", fullDir.c_str());
6554         if (!createDirectory(fullDir))
6555             return false;
6556         return true;
6557         }
6559     virtual bool parse(Element *elem)
6560         {
6561         if (!parent.getAttribute(elem, "dir", dirName))
6562             return false;
6563         if (dirName.size() == 0)
6564             {
6565             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6566             return false;
6567             }
6568         return true;
6569         }
6571 private:
6573     String dirName;
6574 };
6578 /**
6579  * Create a named directory
6580  */
6581 class TaskMsgFmt: public Task
6583 public:
6585     TaskMsgFmt(MakeBase &par) : Task(par)
6586          {
6587                  type = TASK_MSGFMT;
6588                  name = "msgfmt";
6589                  command = "msgfmt";
6590                  }
6592     virtual ~TaskMsgFmt()
6593         {}
6595     virtual bool execute()
6596         {
6597         if (!listFiles(parent, fileSet))
6598             return false;
6599         String fileSetDir = fileSet.getDirectory();
6601         //trace("msgfmt: %d", fileSet.size());
6602         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6603             {
6604             String fileName = fileSet[i];
6605             if (getSuffix(fileName) != "po")
6606                 continue;
6607             String sourcePath;
6608                         if (fileSetDir.size()>0)
6609                             {
6610                             sourcePath.append(fileSetDir);
6611                 sourcePath.append("/");
6612                 }
6613             sourcePath.append(fileName);
6614             String fullSource = parent.resolve(sourcePath);
6616             String destPath;
6617                         if (toDirName.size()>0)
6618                             {
6619                             destPath.append(toDirName);
6620                 destPath.append("/");
6621                 }
6622             destPath.append(fileName);
6623             destPath[destPath.size()-2] = 'm';
6624             String fullDest = parent.resolve(destPath);
6626             if (!isNewerThan(fullSource, fullDest))
6627                 {
6628                 //trace("skip %s", fullSource.c_str());
6629                 continue;
6630                 }
6631                 
6632             String cmd = command;
6633             cmd.append(" ");
6634             cmd.append(fullSource);
6635             cmd.append(" -o ");
6636             cmd.append(fullDest);
6637             
6638             int pos = fullDest.find_last_of('/');
6639             if (pos>0)
6640                 {
6641                 String fullDestPath = fullDest.substr(0, pos);
6642                 if (!createDirectory(fullDestPath))
6643                     return false;
6644                 }
6648             String outString, errString;
6649             if (!executeCommand(cmd.c_str(), "", outString, errString))
6650                 {
6651                 error("<msgfmt> problem: %s", errString.c_str());
6652                 return false;
6653                 }
6654             }
6656         return true;
6657         }
6659     virtual bool parse(Element *elem)
6660         {
6661         if (!parent.getAttribute(elem, "todir", toDirName))
6662             return false;
6663             
6664         std::vector<Element *> children = elem->getChildren();
6665         for (unsigned int i=0 ; i<children.size() ; i++)
6666             {
6667             Element *child = children[i];
6668             String tagName = child->getName();
6669             if (tagName == "fileset")
6670                 {
6671                 if (!parseFileSet(child, parent, fileSet))
6672                     return false;
6673                 }
6674             }
6675         return true;
6676         }
6678 private:
6680     String command;
6681     String toDirName;
6682     FileSet fileSet;
6684 };
6690 /**
6691  *  Process an archive to allow random access
6692  */
6693 class TaskRanlib : public Task
6695 public:
6697     TaskRanlib(MakeBase &par) : Task(par)
6698         { type = TASK_RANLIB; name = "ranlib"; }
6700     virtual ~TaskRanlib()
6701         {}
6703     virtual bool execute()
6704         {
6705         String fullName = parent.resolve(fileName);
6706         //trace("fullDir:%s", fullDir.c_str());
6707         String cmd = "ranlib ";
6708         cmd.append(fullName);
6709         String outbuf, errbuf;
6710         if (!executeCommand(cmd, "", outbuf, errbuf))
6711             return false;
6712         return true;
6713         }
6715     virtual bool parse(Element *elem)
6716         {
6717         if (!parent.getAttribute(elem, "file", fileName))
6718             return false;
6719         if (fileName.size() == 0)
6720             {
6721             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6722             return false;
6723             }
6724         return true;
6725         }
6727 private:
6729     String fileName;
6730 };
6734 /**
6735  * Run the "ar" command to archive .o's into a .a
6736  */
6737 class TaskRC : public Task
6739 public:
6741     TaskRC(MakeBase &par) : Task(par)
6742         {
6743                 type = TASK_RC; name = "rc";
6744                 command = "windres -o";
6745                 }
6747     virtual ~TaskRC()
6748         {}
6750     virtual bool execute()
6751         {
6752         String fullFile = parent.resolve(fileName);
6753         String fullOut  = parent.resolve(outName);
6754         if (!isNewerThan(fullFile, fullOut))
6755             return true;
6756         String cmd = command;
6757         cmd.append(" ");
6758         cmd.append(fullOut);
6759         cmd.append(" ");
6760         cmd.append(flags);
6761         cmd.append(" ");
6762         cmd.append(fullFile);
6764         String outString, errString;
6765         if (!executeCommand(cmd.c_str(), "", outString, errString))
6766             {
6767             error("RC problem: %s", errString.c_str());
6768             return false;
6769             }
6770         return true;
6771         }
6773     virtual bool parse(Element *elem)
6774         {
6775         if (!parent.getAttribute(elem, "command", command))
6776             return false;
6777         if (!parent.getAttribute(elem, "file", fileName))
6778             return false;
6779         if (!parent.getAttribute(elem, "out", outName))
6780             return false;
6781         std::vector<Element *> children = elem->getChildren();
6782         for (unsigned int i=0 ; i<children.size() ; i++)
6783             {
6784             Element *child = children[i];
6785             String tagName = child->getName();
6786             if (tagName == "flags")
6787                 {
6788                 if (!parent.getValue(child, flags))
6789                     return false;
6790                 }
6791             }
6792         return true;
6793         }
6795 private:
6797     String command;
6798     String flags;
6799     String fileName;
6800     String outName;
6802 };
6806 /**
6807  * Strip an executable
6808  */
6809 class TaskStrip : public Task
6811 public:
6813     TaskStrip(MakeBase &par) : Task(par)
6814         { type = TASK_STRIP; name = "strip"; }
6816     virtual ~TaskStrip()
6817         {}
6819     virtual bool execute()
6820         {
6821         String fullName = parent.resolve(fileName);
6822         //trace("fullDir:%s", fullDir.c_str());
6823         String cmd = "strip ";
6824         cmd.append(fullName);
6826         String outbuf, errbuf;
6827         if (!executeCommand(cmd, "", outbuf, errbuf))
6828             return false;
6829         return true;
6830         }
6832     virtual bool parse(Element *elem)
6833         {
6834         if (!parent.getAttribute(elem, "file", fileName))
6835             return false;
6836         if (fileName.size() == 0)
6837             {
6838             error("<strip> requires 'file=\"fileNname\"' attribute");
6839             return false;
6840             }
6841         return true;
6842         }
6844 private:
6846     String fileName;
6847 };
6850 /**
6851  *
6852  */
6853 class TaskTstamp : public Task
6855 public:
6857     TaskTstamp(MakeBase &par) : Task(par)
6858         { type = TASK_TSTAMP; name = "tstamp"; }
6860     virtual ~TaskTstamp()
6861         {}
6863     virtual bool execute()
6864         {
6865         return true;
6866         }
6868     virtual bool parse(Element *elem)
6869         {
6870         //trace("tstamp parse");
6871         return true;
6872         }
6873 };
6877 /**
6878  *
6879  */
6880 Task *Task::createTask(Element *elem)
6882     String tagName = elem->getName();
6883     //trace("task:%s", tagName.c_str());
6884     Task *task = NULL;
6885     if (tagName == "ar")
6886         task = new TaskAr(parent);
6887     else if (tagName == "cc")
6888         task = new TaskCC(parent);
6889     else if (tagName == "copy")
6890         task = new TaskCopy(parent);
6891     else if (tagName == "delete")
6892         task = new TaskDelete(parent);
6893     else if (tagName == "jar")
6894         task = new TaskJar(parent);
6895     else if (tagName == "javac")
6896         task = new TaskJavac(parent);
6897     else if (tagName == "link")
6898         task = new TaskLink(parent);
6899     else if (tagName == "makefile")
6900         task = new TaskMakeFile(parent);
6901     else if (tagName == "mkdir")
6902         task = new TaskMkDir(parent);
6903     else if (tagName == "msgfmt")
6904         task = new TaskMsgFmt(parent);
6905     else if (tagName == "ranlib")
6906         task = new TaskRanlib(parent);
6907     else if (tagName == "rc")
6908         task = new TaskRC(parent);
6909     else if (tagName == "strip")
6910         task = new TaskStrip(parent);
6911     else if (tagName == "tstamp")
6912         task = new TaskTstamp(parent);
6913     else
6914         {
6915         error("Unknown task '%s'", tagName.c_str());
6916         return NULL;
6917         }
6919     if (!task->parse(elem))
6920         {
6921         delete task;
6922         return NULL;
6923         }
6924     return task;
6929 //########################################################################
6930 //# T A R G E T
6931 //########################################################################
6933 /**
6934  *
6935  */
6936 class Target : public MakeBase
6939 public:
6941     /**
6942      *
6943      */
6944     Target(Make &par) : parent(par)
6945         { init(); }
6947     /**
6948      *
6949      */
6950     Target(const Target &other) : parent(other.parent)
6951         { init(); assign(other); }
6953     /**
6954      *
6955      */
6956     Target &operator=(const Target &other)
6957         { init(); assign(other); return *this; }
6959     /**
6960      *
6961      */
6962     virtual ~Target()
6963         { cleanup() ; }
6966     /**
6967      *
6968      */
6969     virtual Make &getParent()
6970         { return parent; }
6972     /**
6973      *
6974      */
6975     virtual String getName()
6976         { return name; }
6978     /**
6979      *
6980      */
6981     virtual void setName(const String &val)
6982         { name = val; }
6984     /**
6985      *
6986      */
6987     virtual String getDescription()
6988         { return description; }
6990     /**
6991      *
6992      */
6993     virtual void setDescription(const String &val)
6994         { description = val; }
6996     /**
6997      *
6998      */
6999     virtual void addDependency(const String &val)
7000         { deps.push_back(val); }
7002     /**
7003      *
7004      */
7005     virtual void parseDependencies(const String &val)
7006         { deps = tokenize(val, ", "); }
7008     /**
7009      *
7010      */
7011     virtual std::vector<String> &getDependencies()
7012         { return deps; }
7014     /**
7015      *
7016      */
7017     virtual String getIf()
7018         { return ifVar; }
7020     /**
7021      *
7022      */
7023     virtual void setIf(const String &val)
7024         { ifVar = val; }
7026     /**
7027      *
7028      */
7029     virtual String getUnless()
7030         { return unlessVar; }
7032     /**
7033      *
7034      */
7035     virtual void setUnless(const String &val)
7036         { unlessVar = val; }
7038     /**
7039      *
7040      */
7041     virtual void addTask(Task *val)
7042         { tasks.push_back(val); }
7044     /**
7045      *
7046      */
7047     virtual std::vector<Task *> &getTasks()
7048         { return tasks; }
7050 private:
7052     void init()
7053         {
7054         }
7056     void cleanup()
7057         {
7058         tasks.clear();
7059         }
7061     void assign(const Target &other)
7062         {
7063         //parent      = other.parent;
7064         name        = other.name;
7065         description = other.description;
7066         ifVar       = other.ifVar;
7067         unlessVar   = other.unlessVar;
7068         deps        = other.deps;
7069         tasks       = other.tasks;
7070         }
7072     Make &parent;
7074     String name;
7076     String description;
7078     String ifVar;
7080     String unlessVar;
7082     std::vector<String> deps;
7084     std::vector<Task *> tasks;
7086 };
7095 //########################################################################
7096 //# M A K E
7097 //########################################################################
7100 /**
7101  *
7102  */
7103 class Make : public MakeBase
7106 public:
7108     /**
7109      *
7110      */
7111     Make()
7112         { init(); }
7114     /**
7115      *
7116      */
7117     Make(const Make &other)
7118         { assign(other); }
7120     /**
7121      *
7122      */
7123     Make &operator=(const Make &other)
7124         { assign(other); return *this; }
7126     /**
7127      *
7128      */
7129     virtual ~Make()
7130         { cleanup(); }
7132     /**
7133      *
7134      */
7135     virtual std::map<String, Target> &getTargets()
7136         { return targets; }
7139     /**
7140      *
7141      */
7142     virtual String version()
7143         { return "BuildTool v0.3, 2006 Bob Jamison"; }
7145     /**
7146      * Overload a <property>
7147      */
7148     virtual bool specifyProperty(const String &name,
7149                                      const String &value);
7151     /**
7152      *
7153      */
7154     virtual bool run();
7156     /**
7157      *
7158      */
7159     virtual bool run(const String &target);
7163 private:
7165     /**
7166      *
7167      */
7168     void init();
7170     /**
7171      *
7172      */
7173     void cleanup();
7175     /**
7176      *
7177      */
7178     void assign(const Make &other);
7180     /**
7181      *
7182      */
7183     bool executeTask(Task &task);
7186     /**
7187      *
7188      */
7189     bool executeTarget(Target &target,
7190                  std::set<String> &targetsCompleted);
7193     /**
7194      *
7195      */
7196     bool execute();
7198     /**
7199      *
7200      */
7201     bool checkTargetDependencies(Target &prop,
7202                     std::vector<String> &depList);
7204     /**
7205      *
7206      */
7207     bool parsePropertyFile(const String &fileName,
7208                                const String &prefix);
7210     /**
7211      *
7212      */
7213     bool parseProperty(Element *elem);
7215     /**
7216      *
7217      */
7218     bool parseTask(Task &task, Element *elem);
7220     /**
7221      *
7222      */
7223     bool parseFile();
7225     /**
7226      *
7227      */
7228     std::vector<String> glob(const String &pattern);
7231     //###############
7232     //# Fields
7233     //###############
7235     String projectName;
7237     String currentTarget;
7239     String defaultTarget;
7241     String specifiedTarget;
7243     String baseDir;
7245     String description;
7246     
7247     String envAlias;
7249     //std::vector<Property> properties;
7250     
7251     std::map<String, Target> targets;
7253     std::vector<Task *> allTasks;
7254     
7255     std::map<String, String> specifiedProperties;
7257 };
7260 //########################################################################
7261 //# C L A S S  M A I N T E N A N C E
7262 //########################################################################
7264 /**
7265  *
7266  */
7267 void Make::init()
7269     uri             = "build.xml";
7270     projectName     = "";
7271     currentTarget   = "";
7272     defaultTarget   = "";
7273     specifiedTarget = "";
7274     baseDir         = "";
7275     description     = "";
7276     envAlias        = "";
7277     properties.clear();
7278     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7279         delete allTasks[i];
7280     allTasks.clear();
7285 /**
7286  *
7287  */
7288 void Make::cleanup()
7290     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7291         delete allTasks[i];
7292     allTasks.clear();
7297 /**
7298  *
7299  */
7300 void Make::assign(const Make &other)
7302     uri              = other.uri;
7303     projectName      = other.projectName;
7304     currentTarget    = other.currentTarget;
7305     defaultTarget    = other.defaultTarget;
7306     specifiedTarget  = other.specifiedTarget;
7307     baseDir          = other.baseDir;
7308     description      = other.description;
7309     properties       = other.properties;
7314 //########################################################################
7315 //# U T I L I T Y    T A S K S
7316 //########################################################################
7318 /**
7319  *  Perform a file globbing
7320  */
7321 std::vector<String> Make::glob(const String &pattern)
7323     std::vector<String> res;
7324     return res;
7328 //########################################################################
7329 //# P U B L I C    A P I
7330 //########################################################################
7334 /**
7335  *
7336  */
7337 bool Make::executeTarget(Target &target,
7338              std::set<String> &targetsCompleted)
7341     String name = target.getName();
7343     //First get any dependencies for this target
7344     std::vector<String> deps = target.getDependencies();
7345     for (unsigned int i=0 ; i<deps.size() ; i++)
7346         {
7347         String dep = deps[i];
7348         //Did we do it already?  Skip
7349         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7350             continue;
7351             
7352         std::map<String, Target> &tgts =
7353                target.getParent().getTargets();
7354         std::map<String, Target>::iterator iter =
7355                tgts.find(dep);
7356         if (iter == tgts.end())
7357             {
7358             error("Target '%s' dependency '%s' not found",
7359                       name.c_str(),  dep.c_str());
7360             return false;
7361             }
7362         Target depTarget = iter->second;
7363         if (!executeTarget(depTarget, targetsCompleted))
7364             {
7365             return false;
7366             }
7367         }
7369     status("## Target : %s", name.c_str());
7371     //Now let's do the tasks
7372     std::vector<Task *> &tasks = target.getTasks();
7373     for (unsigned int i=0 ; i<tasks.size() ; i++)
7374         {
7375         Task *task = tasks[i];
7376         status("---- task : %s", task->getName().c_str());
7377         if (!task->execute())
7378             {
7379             return false;
7380             }
7381         }
7382         
7383     targetsCompleted.insert(name);
7384     
7385     return true;
7390 /**
7391  *  Main execute() method.  Start here and work
7392  *  up the dependency tree 
7393  */
7394 bool Make::execute()
7396     status("######## EXECUTE");
7398     //Determine initial target
7399     if (specifiedTarget.size()>0)
7400         {
7401         currentTarget = specifiedTarget;
7402         }
7403     else if (defaultTarget.size()>0)
7404         {
7405         currentTarget = defaultTarget;
7406         }
7407     else
7408         {
7409         error("execute: no specified or default target requested");
7410         return false;
7411         }
7413     std::map<String, Target>::iterator iter =
7414                    targets.find(currentTarget);
7415     if (iter == targets.end())
7416         {
7417         error("Initial target '%s' not found",
7418                          currentTarget.c_str());
7419         return false;
7420         }
7421         
7422     //Now run
7423     Target target = iter->second;
7424     std::set<String> targetsCompleted;
7425     if (!executeTarget(target, targetsCompleted))
7426         {
7427         return false;
7428         }
7430     status("######## EXECUTE COMPLETE");
7431     return true;
7437 /**
7438  *
7439  */
7440 bool Make::checkTargetDependencies(Target &target, 
7441                             std::vector<String> &depList)
7443     String tgtName = target.getName().c_str();
7444     depList.push_back(tgtName);
7446     std::vector<String> deps = target.getDependencies();
7447     for (unsigned int i=0 ; i<deps.size() ; i++)
7448         {
7449         String dep = deps[i];
7450         //First thing entered was the starting Target
7451         if (dep == depList[0])
7452             {
7453             error("Circular dependency '%s' found at '%s'",
7454                       dep.c_str(), tgtName.c_str());
7455             std::vector<String>::iterator diter;
7456             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7457                 {
7458                 error("  %s", diter->c_str());
7459                 }
7460             return false;
7461             }
7463         std::map<String, Target> &tgts =
7464                   target.getParent().getTargets();
7465         std::map<String, Target>::iterator titer = tgts.find(dep);
7466         if (titer == tgts.end())
7467             {
7468             error("Target '%s' dependency '%s' not found",
7469                       tgtName.c_str(), dep.c_str());
7470             return false;
7471             }
7472         if (!checkTargetDependencies(titer->second, depList))
7473             {
7474             return false;
7475             }
7476         }
7477     return true;
7484 static int getword(int pos, const String &inbuf, String &result)
7486     int p = pos;
7487     int len = (int)inbuf.size();
7488     String val;
7489     while (p < len)
7490         {
7491         char ch = inbuf[p];
7492         if (!isalnum(ch) && ch!='.' && ch!='_')
7493             break;
7494         val.push_back(ch);
7495         p++;
7496         }
7497     result = val;
7498     return p;
7504 /**
7505  *
7506  */
7507 bool Make::parsePropertyFile(const String &fileName,
7508                              const String &prefix)
7510     FILE *f = fopen(fileName.c_str(), "r");
7511     if (!f)
7512         {
7513         error("could not open property file %s", fileName.c_str());
7514         return false;
7515         }
7516     int linenr = 0;
7517     while (!feof(f))
7518         {
7519         char buf[256];
7520         if (!fgets(buf, 255, f))
7521             break;
7522         linenr++;
7523         String s = buf;
7524         s = trim(s);
7525         int len = s.size();
7526         if (len == 0)
7527             continue;
7528         if (s[0] == '#')
7529             continue;
7530         String key;
7531         String val;
7532         int p = 0;
7533         int p2 = getword(p, s, key);
7534         if (p2 <= p)
7535             {
7536             error("property file %s, line %d: expected keyword",
7537                                 fileName.c_str(), linenr);
7538                         return false;
7539                         }
7540                 if (prefix.size() > 0)
7541                     {
7542                     key.insert(0, prefix);
7543                     }
7545         //skip whitespace
7546                 for (p=p2 ; p<len ; p++)
7547                     if (!isspace(s[p]))
7548                         break;
7550         if (p>=len || s[p]!='=')
7551             {
7552             error("property file %s, line %d: expected '='",
7553                                 fileName.c_str(), linenr);
7554             return false;
7555             }
7556         p++;
7558         //skip whitespace
7559                 for ( ; p<len ; p++)
7560                     if (!isspace(s[p]))
7561                         break;
7563         /* This way expects a word after the =
7564                 p2 = getword(p, s, val);
7565         if (p2 <= p)
7566             {
7567             error("property file %s, line %d: expected value",
7568                                 fileName.c_str(), linenr);
7569                         return false;
7570                         }
7571                 */
7572         // This way gets the rest of the line after the =
7573                 if (p>=len)
7574             {
7575             error("property file %s, line %d: expected value",
7576                                 fileName.c_str(), linenr);
7577                         return false;
7578                         }
7579         val = s.substr(p);
7580                 if (key.size()==0 || val.size()==0)
7581                     continue;
7583         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7584             properties[key] = val;
7585         }
7586     fclose(f);
7587     return true;
7593 /**
7594  *
7595  */
7596 bool Make::parseProperty(Element *elem)
7598     std::vector<Attribute> &attrs = elem->getAttributes();
7599     for (unsigned int i=0 ; i<attrs.size() ; i++)
7600         {
7601         String attrName = attrs[i].getName();
7602         String attrVal  = attrs[i].getValue();
7604         if (attrName == "name")
7605             {
7606             String val;
7607                         if (!getAttribute(elem, "value", val))
7608                             return false;
7609             if (val.size() > 0)
7610                 {
7611                 properties[attrVal] = val;
7612                 continue;
7613                 }
7614             if (!getAttribute(elem, "location", val))
7615                 return false;
7616             if (val.size() > 0)
7617                 {
7618                 //TODO:  process a path relative to build.xml
7619                 properties[attrVal] = val;
7620                 continue;
7621                 }
7622             }
7623         else if (attrName == "file")
7624             {
7625             String prefix;
7626                         if (!getAttribute(elem, "prefix", prefix))
7627                             return false;
7628             if (prefix.size() > 0)
7629                 {
7630                 if (prefix[prefix.size()-1] != '.')
7631                     prefix.push_back('.');
7632                 }
7633             if (!parsePropertyFile(attrName, prefix))
7634                 return false;
7635             }
7636         else if (attrName == "environment")
7637             {
7638             if (envAlias.size() > 0)
7639                 {
7640                 error("environment property can only be set once");
7641                 return false;
7642                 }
7643             envAlias = attrVal;
7644             }
7645         }
7647     return true;
7653 /**
7654  *
7655  */
7656 bool Make::parseFile()
7658     status("######## PARSE : %s", uri.getPath().c_str());
7660     Parser parser;
7661     Element *root = parser.parseFile(uri.getNativePath());
7662     if (!root)
7663         {
7664         error("Could not open %s for reading",
7665                       uri.getNativePath().c_str());
7666         return false;
7667         }
7669     if (root->getChildren().size()==0 ||
7670         root->getChildren()[0]->getName()!="project")
7671         {
7672         error("Main xml element should be <project>");
7673         delete root;
7674         return false;
7675         }
7677     //########## Project attributes
7678     Element *project = root->getChildren()[0];
7679     String s = project->getAttribute("name");
7680     if (s.size() > 0)
7681         projectName = s;
7682     s = project->getAttribute("default");
7683     if (s.size() > 0)
7684         defaultTarget = s;
7685     s = project->getAttribute("basedir");
7686     if (s.size() > 0)
7687         baseDir = s;
7689     //######### PARSE MEMBERS
7690     std::vector<Element *> children = project->getChildren();
7691     for (unsigned int i=0 ; i<children.size() ; i++)
7692         {
7693         Element *elem = children[i];
7694         String tagName = elem->getName();
7696         //########## DESCRIPTION
7697         if (tagName == "description")
7698             {
7699             description = parser.trim(elem->getValue());
7700             }
7702         //######### PROPERTY
7703         else if (tagName == "property")
7704             {
7705             if (!parseProperty(elem))
7706                 return false;
7707             }
7709         //######### TARGET
7710         else if (tagName == "target")
7711             {
7712             String tname   = elem->getAttribute("name");
7713             String tdesc   = elem->getAttribute("description");
7714             String tdeps   = elem->getAttribute("depends");
7715             String tif     = elem->getAttribute("if");
7716             String tunless = elem->getAttribute("unless");
7717             Target target(*this);
7718             target.setName(tname);
7719             target.setDescription(tdesc);
7720             target.parseDependencies(tdeps);
7721             target.setIf(tif);
7722             target.setUnless(tunless);
7723             std::vector<Element *> telems = elem->getChildren();
7724             for (unsigned int i=0 ; i<telems.size() ; i++)
7725                 {
7726                 Element *telem = telems[i];
7727                 Task breeder(*this);
7728                 Task *task = breeder.createTask(telem);
7729                 if (!task)
7730                     return false;
7731                 allTasks.push_back(task);
7732                 target.addTask(task);
7733                 }
7735             //Check name
7736             if (tname.size() == 0)
7737                 {
7738                 error("no name for target");
7739                 return false;
7740                 }
7741             //Check for duplicate name
7742             if (targets.find(tname) != targets.end())
7743                 {
7744                 error("target '%s' already defined", tname.c_str());
7745                 return false;
7746                 }
7747             //more work than targets[tname]=target, but avoids default allocator
7748             targets.insert(std::make_pair<String, Target>(tname, target));
7749             }
7751         }
7753     std::map<String, Target>::iterator iter;
7754     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
7755         {
7756         Target tgt = iter->second;
7757         std::vector<String> depList;
7758         if (!checkTargetDependencies(tgt, depList))
7759             {
7760             return false;
7761             }
7762         }
7765     delete root;
7766     status("######## PARSE COMPLETE");
7767     return true;
7771 /**
7772  * Overload a <property>
7773  */
7774 bool Make::specifyProperty(const String &name, const String &value)
7776     if (specifiedProperties.find(name) != specifiedProperties.end())
7777         {
7778         error("Property %s already specified", name.c_str());
7779         return false;
7780         }
7781     specifiedProperties[name] = value;
7782     return true;
7787 /**
7788  *
7789  */
7790 bool Make::run()
7792     if (!parseFile())
7793         return false;
7794         
7795     //Overload properties
7796     std::map<String, String>::iterator iter;
7797     for (iter = specifiedProperties.begin() ; 
7798                  iter!=specifiedProperties.end() ; iter++)
7799             {
7800             String name = iter->first;
7801             String value = iter->second;
7802                 if (properties.find(name) == properties.end())
7803                     {
7804                     error("Overloading property:%s not found", name.c_str());
7805                     return false;
7806                     }
7807                 properties[name] = value;
7808             }
7810     if (!execute())
7811         return false;
7812     return true;
7816 /**
7817  *
7818  */
7819 bool Make::run(const String &target)
7821     status("##################################");
7822     status("#   BuildTool");
7823     status("#   version 0.3");
7824     status("#   16 Nov 06");
7825     status("##################################");
7826     specifiedTarget = target;
7827     if (!run())
7828         return false;
7829     status("##################################");
7830     status("#   BuildTool Completed");
7831     status("##################################");
7832     return true;
7841 }// namespace buildtool
7842 //########################################################################
7843 //# M A I N
7844 //########################################################################
7846 typedef buildtool::String String;
7848 /**
7849  *  Format an error message in printf() style
7850  */
7851 static void error(char *fmt, ...)
7853     va_list ap;
7854     va_start(ap, fmt);
7855     fprintf(stderr, "BuildTool error: ");
7856     vfprintf(stderr, fmt, ap);
7857     fprintf(stderr, "\n");
7858     va_end(ap);
7862 static bool parseProperty(const String &s, String &name, String &val)
7864     int len = s.size();
7865     int i;
7866     for (i=0 ; i<len ; i++)
7867         {
7868         char ch = s[i];
7869         if (ch == '=')
7870             break;
7871         name.push_back(ch);
7872         }
7873     if (i>=len || s[i]!='=')
7874         {
7875         error("property requires -Dname=value");
7876         return false;
7877         }
7878     i++;
7879     for ( ; i<len ; i++)
7880         {
7881         char ch = s[i];
7882         val.push_back(ch);
7883         }
7884     return true;
7888 /**
7889  * Compare a buffer with a key, for the length of the key
7890  */
7891 static bool sequ(const String &buf, char *key)
7893     int len = buf.size();
7894     for (int i=0 ; key[i] && i<len ; i++)
7895         {
7896         if (key[i] != buf[i])
7897             return false;
7898         }        
7899     return true;
7902 static void usage(int argc, char **argv)
7904     printf("usage:\n");
7905     printf("   %s [options] [target]\n", argv[0]);
7906     printf("Options:\n");
7907     printf("  -help, -h              print this message\n");
7908     printf("  -version               print the version information and exit\n");
7909     printf("  -file <file>           use given buildfile\n");
7910     printf("  -f <file>                 ''\n");
7911     printf("  -D<property>=<value>   use value for given property\n");
7917 /**
7918  * Parse the command-line args, get our options,
7919  * and run this thing
7920  */   
7921 static bool parseOptions(int argc, char **argv)
7923     if (argc < 1)
7924         {
7925         error("Cannot parse arguments");
7926         return false;
7927         }
7929     buildtool::Make make;
7931     String target;
7933     //char *progName = argv[0];
7934     for (int i=1 ; i<argc ; i++)
7935         {
7936         String arg = argv[i];
7937         if (arg.size()>1 && arg[0]=='-')
7938             {
7939             if (arg == "-h" || arg == "-help")
7940                 {
7941                 usage(argc,argv);
7942                 return true;
7943                 }
7944             else if (arg == "-version")
7945                 {
7946                 printf("%s", make.version().c_str());
7947                 return true;
7948                 }
7949             else if (arg == "-f" || arg == "-file")
7950                 {
7951                 if (i>=argc)
7952                    {
7953                    usage(argc, argv);
7954                    return false;
7955                    }
7956                 i++; //eat option
7957                 make.setURI(argv[i]);
7958                 }
7959             else if (arg.size()>2 && sequ(arg, "-D"))
7960                 {
7961                 String s = arg.substr(2, s.size());
7962                 String name, value;
7963                 if (!parseProperty(s, name, value))
7964                    {
7965                    usage(argc, argv);
7966                    return false;
7967                    }
7968                 if (!make.specifyProperty(name, value))
7969                     return false;
7970                 }
7971             else
7972                 {
7973                 error("Unknown option:%s", arg.c_str());
7974                 return false;
7975                 }
7976             }
7977         else
7978             {
7979             if (target.size()>0)
7980                 {
7981                 error("only one initial target");
7982                 usage(argc, argv);
7983                 return false;
7984                 }
7985             target = arg;
7986             }
7987         }
7989     //We have the options.  Now execute them
7990     if (!make.run(target))
7991         return false;
7993     return true;
7999 /*
8000 static bool runMake()
8002     buildtool::Make make;
8003     if (!make.run())
8004         return false;
8005     return true;
8009 static bool pkgConfigTest()
8011     buildtool::PkgConfig pkgConfig;
8012     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8013         return false;
8014     return true;
8019 static bool depTest()
8021     buildtool::DepTool deptool;
8022     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8023     if (!deptool.generateDependencies("build.dep"))
8024         return false;
8025     std::vector<buildtool::DepRec> res =
8026                deptool.loadDepFile("build.dep");
8027         if (res.size() == 0)
8028         return false;
8029     return true;
8032 static bool popenTest()
8034     buildtool::Make make;
8035     buildtool::String out, err;
8036         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8037     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8038     return true;
8042 static bool propFileTest()
8044     buildtool::Make make;
8045     make.parsePropertyFile("test.prop", "test.");
8046     return true;
8048 */
8050 int main(int argc, char **argv)
8053     if (!parseOptions(argc, argv))
8054         return 1;
8055     /*
8056     if (!popenTest())
8057         return 1;
8059     if (!depTest())
8060         return 1;
8061     if (!propFileTest())
8062         return 1;
8063     if (runMake())
8064         return 1;
8065     */
8066     return 0;
8070 //########################################################################
8071 //# E N D 
8072 //########################################################################