Code

Added regex. Greatly improved <fileset> scanning.
[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)
2614     parselen = str.size();
2616     String tmp;
2617     for (unsigned int i=0 ; i<str.size() ; i++)
2618         {
2619         XMLCh ch = (XMLCh) str[i];
2620         if (ch == '\\')
2621             tmp.push_back((XMLCh)'/');
2622         else
2623             tmp.push_back(ch);
2624         }
2625     parsebuf = (char *) tmp.c_str();
2628     int p = parse(0);
2629     normalize();
2631     if (p < 0)
2632         {
2633         error("Syntax error");
2634         return false;
2635         }
2637     //printf("uri:%s\n", toString().c_str());
2638     //printf("path:%s\n", path.c_str());
2640     return true;
2651 //########################################################################
2652 //########################################################################
2653 //##  M A K E
2654 //########################################################################
2655 //########################################################################
2657 //########################################################################
2658 //# F I L E S E T
2659 //########################################################################
2660 /**
2661  * This is the descriptor for a <fileset> item
2662  */
2663 class FileSet
2665 public:
2667     /**
2668      *
2669      */
2670     FileSet()
2671         {}
2673     /**
2674      *
2675      */
2676     FileSet(const FileSet &other)
2677         { assign(other); }
2679     /**
2680      *
2681      */
2682     FileSet &operator=(const FileSet &other)
2683         { assign(other); return *this; }
2685     /**
2686      *
2687      */
2688     virtual ~FileSet()
2689         {}
2691     /**
2692      *
2693      */
2694     String getDirectory()
2695         { return directory; }
2696         
2697     /**
2698      *
2699      */
2700     void setDirectory(const String &val)
2701         { directory = val; }
2703     /**
2704      *
2705      */
2706     void setFiles(const std::vector<String> &val)
2707         { files = val; }
2709     /**
2710      *
2711      */
2712     std::vector<String> getFiles()
2713         { return files; }
2714         
2715     /**
2716      *
2717      */
2718     void setIncludes(const std::vector<String> &val)
2719         { includes = val; }
2721     /**
2722      *
2723      */
2724     std::vector<String> getIncludes()
2725         { return includes; }
2726         
2727     /**
2728      *
2729      */
2730     void setExcludes(const std::vector<String> &val)
2731         { excludes = val; }
2733     /**
2734      *
2735      */
2736     std::vector<String> getExcludes()
2737         { return excludes; }
2738         
2739     /**
2740      *
2741      */
2742     unsigned int size()
2743         { return files.size(); }
2744         
2745     /**
2746      *
2747      */
2748     String operator[](int index)
2749         { return files[index]; }
2750         
2751     /**
2752      *
2753      */
2754     void clear()
2755         {
2756         directory = "";
2757         files.clear();
2758         includes.clear();
2759         excludes.clear();
2760         }
2761         
2763 private:
2765     void assign(const FileSet &other)
2766         {
2767         directory = other.directory;
2768         files     = other.files;
2769         includes  = other.includes;
2770         excludes  = other.excludes;
2771         }
2773     String directory;
2774     std::vector<String> files;
2775     std::vector<String> includes;
2776     std::vector<String> excludes;
2777 };
2782 //########################################################################
2783 //# M A K E    B A S E
2784 //########################################################################
2785 /**
2786  * Base class for all classes in this file
2787  */
2788 class MakeBase
2790 public:
2791     MakeBase()
2792         {}
2793     virtual ~MakeBase()
2794         {}
2796     /**
2797      *  Return the URI of the file associated with this object 
2798      */     
2799     URI getURI()
2800         { return uri; }
2802     /**
2803      * Set the uri to the given string
2804      */
2805     void setURI(const String &uristr)
2806         { uri.parse(uristr); }
2808     /**
2809      *  Resolve another path relative to this one
2810      */
2811     String resolve(const String &otherPath);
2813     /**
2814      *  Get an element attribute, performing substitutions if necessary
2815      */
2816     bool getAttribute(Element *elem, const String &name, String &result);
2818     /**
2819      * Get an element value, performing substitutions if necessary
2820      */
2821     bool getValue(Element *elem, String &result);
2823 protected:
2825     /**
2826      *  The path to the file associated with this object
2827      */     
2828     URI uri;
2831     /**
2832      *  Print a printf()-like formatted error message
2833      */
2834     void error(char *fmt, ...);
2836     /**
2837      *  Print a printf()-like formatted trace message
2838      */
2839     void status(char *fmt, ...);
2841     /**
2842      *  Print a printf()-like formatted trace message
2843      */
2844     void trace(char *fmt, ...);
2846     /**
2847      *  Check if a given string matches a given regex pattern
2848      */
2849     bool regexMatch(const String &str, const String &pattern);
2851     /**
2852      *
2853      */
2854     String getSuffix(const String &fname);
2856     /**
2857      * Break up a string into substrings delimited the characters
2858      * in delimiters.  Null-length substrings are ignored
2859      */  
2860     std::vector<String> tokenize(const String &val,
2861                               const String &delimiters);
2863     /**
2864      *  replace runs of whitespace with a space
2865      */
2866     String strip(const String &s);
2868     /**
2869      *  remove leading whitespace from each line
2870      */
2871     String leftJustify(const String &s);
2873     /**
2874      *  remove leading and trailing whitespace from string
2875      */
2876     String trim(const String &s);
2878     /**
2879      * Return the native format of the canonical
2880      * path which we store
2881      */
2882     String getNativePath(const String &path);
2884     /**
2885      * Execute a shell command.  Outbuf is a ref to a string
2886      * to catch the result.     
2887      */      
2888     bool executeCommand(const String &call,
2889                             const String &inbuf,
2890                                                 String &outbuf,
2891                                                 String &errbuf);
2892     /**
2893      * List all directories in a given base and starting directory
2894      * It is usually called like:
2895      *           bool ret = listDirectories("src", "", result);    
2896      */      
2897     bool listDirectories(const String &baseName,
2898                          const String &dirname,
2899                          std::vector<String> &res);
2901     /**
2902      * Find all files in the named directory 
2903      */      
2904     bool listFiles(const String &baseName,
2905                    const String &dirname,
2906                    std::vector<String> &result);
2908     /**
2909      * Perform a listing for a fileset 
2910      */      
2911     bool listFiles(MakeBase &propRef, FileSet &fileSet);
2913     /**
2914      * Parse a <patternset>
2915      */  
2916     bool parsePatternSet(Element *elem,
2917                        MakeBase &propRef,
2918                                            std::vector<String> &includes,
2919                                            std::vector<String> &excludes);
2921     /**
2922      * Parse a <fileset> entry, and determine which files
2923      * should be included
2924      */  
2925     bool parseFileSet(Element *elem,
2926                     MakeBase &propRef,
2927                                         FileSet &fileSet);
2929     /**
2930      * Return this object's property list
2931      */
2932     virtual std::map<String, String> &getProperties()
2933         { return properties; }
2935     /**
2936      * Return a named property if found, else a null string
2937      */
2938     virtual String getProperty(const String &name)
2939         {
2940         String val;
2941         std::map<String, String>::iterator iter;
2942         iter = properties.find(name);
2943         if (iter != properties.end())
2944             val = iter->second;
2945         return val;
2946         }
2949     std::map<String, String> properties;
2951     /**
2952      * Turn 'true' and 'false' into boolean values
2953      */             
2954     bool getBool(const String &str, bool &val);
2956     /**
2957      * Create a directory, making intermediate dirs
2958      * if necessary
2959      */                     
2960     bool createDirectory(const String &dirname);
2962     /**
2963      * Delete a directory and its children if desired
2964      */
2965     bool removeDirectory(const String &dirName);
2967     /**
2968      * Copy a file from one name to another. Perform only if needed
2969      */ 
2970     bool copyFile(const String &srcFile, const String &destFile);
2972     /**
2973      * Tests if the file exists and is a regular file
2974      */ 
2975     bool isRegularFile(const String &fileName);
2977     /**
2978      * Tests if the file exists and is a directory
2979      */ 
2980     bool isDirectory(const String &fileName);
2982     /**
2983      * Tests is the modification date of fileA is newer than fileB
2984      */ 
2985     bool isNewerThan(const String &fileA, const String &fileB);
2987 private:
2989     /**
2990      * replace variable refs like ${a} with their values
2991      */      
2992     bool getSubstitutions(const String &s, String &result);
2996 };
3001 /**
3002  *  Print a printf()-like formatted error message
3003  */
3004 void MakeBase::error(char *fmt, ...)
3006     va_list args;
3007     va_start(args,fmt);
3008     fprintf(stderr, "Make error: ");
3009     vfprintf(stderr, fmt, args);
3010     fprintf(stderr, "\n");
3011     va_end(args) ;
3016 /**
3017  *  Print a printf()-like formatted trace message
3018  */
3019 void MakeBase::status(char *fmt, ...)
3021     va_list args;
3022     va_start(args,fmt);
3023     //fprintf(stdout, " ");
3024     vfprintf(stdout, fmt, args);
3025     fprintf(stdout, "\n");
3026     va_end(args) ;
3031 /**
3032  *  Resolve another path relative to this one
3033  */
3034 String MakeBase::resolve(const String &otherPath)
3036     URI otherURI(otherPath);
3037     URI fullURI = uri.resolve(otherURI);
3038     String ret = fullURI.toString();
3039     return ret;
3043 /**
3044  *  Print a printf()-like formatted trace message
3045  */
3046 void MakeBase::trace(char *fmt, ...)
3048     va_list args;
3049     va_start(args,fmt);
3050     fprintf(stdout, "Make: ");
3051     vfprintf(stdout, fmt, args);
3052     fprintf(stdout, "\n");
3053     va_end(args) ;
3058 /**
3059  *  Check if a given string matches a given regex pattern
3060  */
3061 bool MakeBase::regexMatch(const String &str, const String &pattern)
3063         const TRexChar *terror = NULL;
3064         const TRexChar *cpat = pattern.c_str();
3065         TRex *expr = trex_compile(cpat, &terror);
3066         if (!expr)
3067             {
3068             if (!terror)
3069                 terror = "undefined";
3070                 error("compilation error [%s]!\n", terror);
3071             return false;
3072                 } 
3074     bool ret = true;
3076         const TRexChar *cstr = str.c_str();
3077         if (trex_match(expr, cstr))
3078                 {
3079                 ret = true;
3080                 }
3081         else
3082             {
3083                 ret = false;
3084                 }
3086         trex_free(expr);
3088     return ret;
3091 /**
3092  *  Return the suffix, if any, of a file name
3093  */
3094 String MakeBase::getSuffix(const String &fname)
3096     if (fname.size() < 2)
3097         return "";
3098     unsigned int pos = fname.find_last_of('.');
3099     if (pos == fname.npos)
3100         return "";
3101     pos++;
3102     String res = fname.substr(pos, fname.size()-pos);
3103     //trace("suffix:%s", res.c_str()); 
3104     return res;
3109 /**
3110  * Break up a string into substrings delimited the characters
3111  * in delimiters.  Null-length substrings are ignored
3112  */  
3113 std::vector<String> MakeBase::tokenize(const String &str,
3114                                 const String &delimiters)
3117     std::vector<String> res;
3118     char *del = (char *)delimiters.c_str();
3119     String dmp;
3120     for (unsigned int i=0 ; i<str.size() ; i++)
3121         {
3122         char ch = str[i];
3123         char *p = (char *)0;
3124         for (p=del ; *p ; p++)
3125             if (*p == ch)
3126                 break;
3127         if (*p)
3128             {
3129             if (dmp.size() > 0)
3130                 {
3131                 res.push_back(dmp);
3132                 dmp.clear();
3133                 }
3134             }
3135         else
3136             {
3137             dmp.push_back(ch);
3138             }
3139         }
3140     //Add tail
3141     if (dmp.size() > 0)
3142         {
3143         res.push_back(dmp);
3144         dmp.clear();
3145         }
3147     return res;
3152 /**
3153  *  replace runs of whitespace with a single space
3154  */
3155 String MakeBase::strip(const String &s)
3157     int len = s.size();
3158     String stripped;
3159     for (int i = 0 ; i<len ; i++)
3160         {
3161         char ch = s[i];
3162         if (isspace(ch))
3163             {
3164             stripped.push_back(' ');
3165             for ( ; i<len ; i++)
3166                 {
3167                 ch = s[i];
3168                 if (!isspace(ch))
3169                     {
3170                     stripped.push_back(ch);
3171                     break;
3172                     }
3173                 }
3174             }
3175         else
3176             {
3177             stripped.push_back(ch);
3178             }
3179         }
3180     return stripped;
3183 /**
3184  *  remove leading whitespace from each line
3185  */
3186 String MakeBase::leftJustify(const String &s)
3188     String out;
3189     int len = s.size();
3190     for (int i = 0 ; i<len ; )
3191         {
3192         char ch;
3193         //Skip to first visible character
3194         while (i<len)
3195             {
3196                         ch = s[i];
3197                         if (ch == '\n' || ch == '\r'
3198                           || !isspace(ch))
3199                               break;
3200                         i++;
3201                         }
3202         //Copy the rest of the line
3203                 while (i<len)
3204                     {
3205                     ch = s[i];
3206             if (ch == '\n' || ch == '\r')
3207                 {
3208                 if (ch != '\r')
3209                     out.push_back('\n');
3210                 i++;
3211                 break;
3212                 }
3213             else
3214                 {
3215                 out.push_back(ch);
3216                 }
3217             i++;
3218             }
3219         }
3220     return out;
3224 /**
3225  *  Removes whitespace from beginning and end of a string
3226  */
3227 String MakeBase::trim(const String &s)
3229     if (s.size() < 1)
3230         return s;
3231     
3232     //Find first non-ws char
3233     unsigned int begin = 0;
3234     for ( ; begin < s.size() ; begin++)
3235         {
3236         if (!isspace(s[begin]))
3237             break;
3238         }
3240     //Find first non-ws char, going in reverse
3241     unsigned int end = s.size() - 1;
3242     for ( ; end > begin ; end--)
3243         {
3244         if (!isspace(s[end]))
3245             break;
3246         }
3247     //trace("begin:%d  end:%d", begin, end);
3249     String res = s.substr(begin, end-begin+1);
3250     return res;
3253 /**
3254  * Return the native format of the canonical
3255  * path which we store
3256  */
3257 String MakeBase::getNativePath(const String &path)
3259 #ifdef __WIN32__
3260     String npath;
3261     unsigned int firstChar = 0;
3262     if (path.size() >= 3)
3263         {
3264         if (path[0] == '/' &&
3265             isalpha(path[1]) &&
3266             path[2] == ':')
3267             firstChar++;
3268         }
3269     for (unsigned int i=firstChar ; i<path.size() ; i++)
3270         {
3271         char ch = path[i];
3272         if (ch == '/')
3273             npath.push_back('\\');
3274         else
3275             npath.push_back(ch);
3276         }
3277     return npath;
3278 #else
3279     return path;
3280 #endif
3284 #ifdef __WIN32__
3285 #include <tchar.h>
3287 static String win32LastError()
3290     DWORD dw = GetLastError(); 
3292     LPVOID str;
3293     FormatMessage(
3294         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3295         FORMAT_MESSAGE_FROM_SYSTEM,
3296         NULL,
3297         dw,
3298         0,
3299         (LPTSTR) &str,
3300         0, NULL );
3301     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3302     if(p != NULL)
3303         { // lose CRLF
3304         *p = _T('\0');
3305         }
3306     String ret = (char *)str;
3307     LocalFree(str);
3309     return ret;
3311 #endif
3315 /**
3316  * Execute a system call, using pipes to send data to the
3317  * program's stdin,  and reading stdout and stderr.
3318  */
3319 bool MakeBase::executeCommand(const String &command,
3320                               const String &inbuf,
3321                                                           String &outbuf,
3322                                                           String &errbuf)
3325     status("============ cmd ============\n%s\n=============================",
3326                     command.c_str());
3328 #ifdef __WIN32__
3330     /*
3331     I really hate having win32 code in this program, but the
3332     read buffer in command.com and cmd.exe are just too small
3333     for the large commands we need for compiling and linking.
3334     */
3336     bool ret = true;
3338     //# Allocate a separate buffer for safety
3339     char *paramBuf = new char[command.size() + 1];
3340     if (!paramBuf)
3341        {
3342        error("executeCommand cannot allocate command buffer");
3343            return false;
3344        }
3345     strcpy(paramBuf, (char *)command.c_str());
3347     //# Create pipes
3348     SECURITY_ATTRIBUTES saAttr; 
3349     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3350     saAttr.bInheritHandle = TRUE; 
3351     saAttr.lpSecurityDescriptor = NULL; 
3352     HANDLE stdinRead,  stdinWrite;
3353     HANDLE stdoutRead, stdoutWrite;
3354     HANDLE stderrRead, stderrWrite;
3355     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3356             {
3357                 error("executeProgram: could not create pipe");
3358         delete[] paramBuf;
3359                 return false;
3360                 } 
3361     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3362         if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3363             {
3364                 error("executeProgram: could not create pipe");
3365         delete[] paramBuf;
3366                 return false;
3367                 } 
3368     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3369         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3370             {
3371                 error("executeProgram: could not create pipe");
3372         delete[] paramBuf;
3373                 return false;
3374                 } 
3375     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3377     // Create the process
3378     STARTUPINFO siStartupInfo;
3379     PROCESS_INFORMATION piProcessInfo;
3380     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3381     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3382     siStartupInfo.cb = sizeof(siStartupInfo);
3383     siStartupInfo.hStdError   =  stderrWrite;
3384     siStartupInfo.hStdOutput  =  stdoutWrite;
3385     siStartupInfo.hStdInput   =  stdinRead;
3386     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3387    
3388     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3389                 0, NULL, NULL, &siStartupInfo,
3390                 &piProcessInfo))
3391         {
3392         error("executeCommand : could not create process : %s",
3393                             win32LastError().c_str());
3394         ret = false;
3395         }
3397     DWORD bytesWritten;
3398     if (inbuf.size()>0 &&
3399         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3400                &bytesWritten, NULL))
3401         {
3402         error("executeCommand: could not write to pipe");
3403                 return false;
3404                 }       
3405     if (!CloseHandle(stdinWrite))
3406             {           
3407         error("executeCommand: could not close write pipe");
3408                 return false;
3409                 }
3410     if (!CloseHandle(stdoutWrite))
3411             {
3412         error("executeCommand: could not close read pipe");
3413                 return false;
3414                 }
3415     if (!CloseHandle(stderrWrite))
3416             {
3417         error("executeCommand: could not close read pipe");
3418                 return false;
3419                 }
3420         while (true)
3421         {
3422         //trace("## stderr");
3423         DWORD avail;
3424         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3425             break;
3426         if (avail > 0)
3427             {
3428             DWORD bytesRead = 0;
3429             char readBuf[1025];
3430             if (avail>1024) avail = 1024;
3431             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3432                 || bytesRead == 0)
3433                 {
3434                 break;
3435                 }
3436             for (unsigned int i=0 ; i<bytesRead ; i++)
3437                 errbuf.push_back(readBuf[i]);
3438             }
3439         //trace("## stdout");
3440         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3441             break;
3442         if (avail > 0)
3443             {
3444             DWORD bytesRead = 0;
3445             char readBuf[1025];
3446             if (avail>1024) avail = 1024;
3447             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3448                 || bytesRead==0)
3449                 {
3450                 break;
3451                 }
3452             for (unsigned int i=0 ; i<bytesRead ; i++)
3453                 outbuf.push_back(readBuf[i]);
3454             }
3455                 DWORD exitCode;
3456         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3457         if (exitCode != STILL_ACTIVE)
3458             break;
3459         Sleep(100);
3460         }       
3461     //trace("outbuf:%s", outbuf.c_str());
3462     if (!CloseHandle(stdoutRead))
3463         {
3464         error("executeCommand: could not close read pipe");
3465         return false;
3466         }
3467     if (!CloseHandle(stderrRead))
3468         {
3469         error("executeCommand: could not close read pipe");
3470         return false;
3471         }
3473     DWORD exitCode;
3474     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3475     //trace("exit code:%d", exitCode);
3476     if (exitCode != 0)
3477         {
3478         ret = false;
3479         }
3480     
3481     // Clean up
3482     CloseHandle(piProcessInfo.hProcess);
3483     CloseHandle(piProcessInfo.hThread);
3486     return ret;
3488 #else //do it unix-style
3490     String s;
3491     FILE *f = popen(command.c_str(), "r");
3492     int errnum = 0;
3493     if (f)
3494         {
3495         while (true)
3496             {
3497             int ch = fgetc(f);
3498             if (ch < 0)
3499                 break;
3500             s.push_back((char)ch);
3501             }
3502         errnum = pclose(f);
3503         }
3504         outbuf = s;
3505         if (errnum < 0)
3506             {
3507             error("exec of command '%s' failed : %s",
3508                      command.c_str(), strerror(errno));
3509             return false;
3510             }
3511         else
3512             return true;
3514 #endif
3515
3520 bool MakeBase::listDirectories(const String &baseName,
3521                               const String &dirName,
3522                               std::vector<String> &res)
3524     res.push_back(dirName);
3525     String fullPath = baseName;
3526     if (dirName.size()>0)
3527         {
3528         fullPath.append("/");
3529         fullPath.append(dirName);
3530         }
3531     DIR *dir = opendir(fullPath.c_str());
3532     while (true)
3533         {
3534         struct dirent *de = readdir(dir);
3535         if (!de)
3536             break;
3538         //Get the directory member name
3539         String s = de->d_name;
3540         if (s.size() == 0 || s[0] == '.')
3541             continue;
3542         String childName = dirName;
3543         childName.append("/");
3544         childName.append(s);
3546         String fullChildPath = baseName;
3547         fullChildPath.append("/");
3548         fullChildPath.append(childName);
3549         struct stat finfo;
3550         String childNative = getNativePath(fullChildPath);
3551         if (stat(childNative.c_str(), &finfo)<0)
3552             {
3553             error("cannot stat file:%s", childNative.c_str());
3554             }
3555         else if (S_ISDIR(finfo.st_mode))
3556             {
3557             //trace("directory: %s", childName.c_str());
3558             if (!listDirectories(baseName, childName, res))
3559                 return false;
3560             }
3561         }
3562     closedir(dir);
3564     return true;
3568 bool MakeBase::listFiles(const String &baseDir,
3569                          const String &dirName,
3570                          std::vector<String> &res)
3572     String fullDir = baseDir;
3573     if (dirName.size()>0)
3574         {
3575         fullDir.append("/");
3576         fullDir.append(dirName);
3577         }
3578     String dirNative = getNativePath(fullDir);
3580     std::vector<String> subdirs;
3581     DIR *dir = opendir(dirNative.c_str());
3582     while (true)
3583         {
3584         struct dirent *de = readdir(dir);
3585         if (!de)
3586             break;
3588         //Get the directory member name
3589         String s = de->d_name;
3590         if (s.size() == 0 || s[0] == '.')
3591             continue;
3592         String childName;
3593         if (dirName.size()>0)
3594             {
3595             childName.append(dirName);
3596             childName.append("/");
3597             }
3598         childName.append(s);
3599         String fullChild = baseDir;
3600         fullChild.append("/");
3601         fullChild.append(childName);
3602         
3603         if (isDirectory(fullChild))
3604             {
3605             //trace("directory: %s", childName.c_str());
3606             if (!listFiles(baseDir, childName, res))
3607                 return false;
3608             continue;
3609             }
3610         else if (!isRegularFile(fullChild))
3611             {
3612             error("unknown file:%s", childName.c_str());
3613             return false;
3614             }
3616        //all done!
3617         res.push_back(childName);
3619         }
3620     closedir(dir);
3622     return true;
3626 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3628     String baseDir = propRef.resolve(fileSet.getDirectory());
3629     std::vector<String> fileList;
3630     if (!listFiles(baseDir, "", fileList))
3631             return false;
3633     std::vector<String> includes = fileSet.getIncludes();
3634     std::vector<String> excludes = fileSet.getExcludes();
3636     std::vector<String> incs;
3637     std::vector<String>::iterator iter;
3639     std::sort(fileList.begin(), fileList.end());
3641     //If there are <includes>, then add files to the output
3642     //in the order of the include list
3643     if (includes.size()==0)
3644             incs = fileList;
3645         else
3646             {
3647         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3648             {
3649             String pattern = *iter;
3650             std::vector<String>::iterator siter;
3651             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3652                 {
3653                 String s = *siter;
3654                 if (regexMatch(s, pattern))
3655                     {
3656                     //trace("INCLUDED:%s", s.c_str());
3657                     incs.push_back(s);
3658                     }
3659                 }
3660             }
3661         }
3663     //Now trim off the <excludes>
3664     std::vector<String> res;
3665     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3666         {
3667         String s = *iter;
3668         bool skipme = false;
3669         std::vector<String>::iterator siter;
3670         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3671             {
3672             String pattern = *siter;
3673             if (regexMatch(s, pattern))
3674                 {
3675                 //trace("EXCLUDED:%s", s.c_str());
3676                 skipme = true;
3677                 break;
3678                 }
3679             }
3680         if (!skipme)
3681             res.push_back(s);
3682         }
3683         
3684         fileSet.setFiles(res);
3686         return true;
3693 bool MakeBase::getSubstitutions(const String &str, String &result)
3695     String s = trim(str);
3696     int len = (int)s.size();
3697     String val;
3698     for (int i=0 ; i<len ; i++)
3699         {
3700         char ch = s[i];
3701         if (ch == '$' && s[i+1] == '{')
3702                     {
3703             String varname;
3704                     int j = i+2;
3705                     for ( ; j<len ; j++)
3706                         {
3707                         ch = s[j];
3708                         if (ch == '$' && s[j+1] == '{')
3709                             {
3710                             error("attribute %s cannot have nested variable references",
3711                                    s.c_str());
3712                             return false;
3713                             }
3714                         else if (ch == '}')
3715                             {
3716                             std::map<String, String>::iterator iter;
3717                             iter = properties.find(trim(varname));
3718                             if (iter != properties.end())
3719                                 {
3720                                 val.append(iter->second);
3721                                 }
3722                             else
3723                                 {
3724                                 error("property ${%s} not found", varname.c_str());
3725                                 return false;
3726                                 }
3727                             break;
3728                             }
3729                         else
3730                             {
3731                             varname.push_back(ch);
3732                             }
3733                         }
3734                     i = j;
3735                         }
3736                 else
3737                     {
3738                     val.push_back(ch);
3739                     }
3740         }
3741     result = val;
3742     return true;
3746 bool MakeBase::getAttribute(Element *elem, const String &name,
3747                                     String &result)
3749     String s = elem->getAttribute(name);
3750     return getSubstitutions(s, result);
3754 bool MakeBase::getValue(Element *elem, String &result)
3756     String s = elem->getValue();
3757     //Replace all runs of whitespace with a single space
3758     return getSubstitutions(s, result);
3762 /**
3763  * Turn 'true' and 'false' into boolean values
3764  */                 
3765 bool MakeBase::getBool(const String &str, bool &val)
3767     if (str == "true")
3768         val = true;
3769     else if (str == "false")
3770         val = false;
3771     else
3772         {
3773         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3774         return false;
3775         }
3776     return true;
3782 /**
3783  * Parse a <patternset> entry
3784  */  
3785 bool MakeBase::parsePatternSet(Element *elem,
3786                           MakeBase &propRef,
3787                                                   std::vector<String> &includes,
3788                                                   std::vector<String> &excludes
3789                                                   )
3791     std::vector<Element *> children  = elem->getChildren();
3792     for (unsigned int i=0 ; i<children.size() ; i++)
3793         {
3794         Element *child = children[i];
3795         String tagName = child->getName();
3796         if (tagName == "exclude")
3797             {
3798             String fname;
3799                         if (!propRef.getAttribute(child, "name", fname))
3800                             return false;
3801             //trace("EXCLUDE: %s", fname.c_str());
3802             excludes.push_back(fname);
3803             }
3804         else if (tagName == "include")
3805             {
3806             String fname;
3807                         if (!propRef.getAttribute(child, "name", fname))
3808                             return false;
3809             //trace("INCLUDE: %s", fname.c_str());
3810             includes.push_back(fname);
3811             }
3812         }
3814     return true;
3820 /**
3821  * Parse a <fileset> entry, and determine which files
3822  * should be included
3823  */  
3824 bool MakeBase::parseFileSet(Element *elem,
3825                           MakeBase &propRef,
3826                                                   FileSet &fileSet)
3828     String name = elem->getName();
3829     if (name != "fileset")
3830         {
3831         error("expected <fileset>");
3832         return false;
3833         }
3836     std::vector<String> includes;
3837     std::vector<String> excludes;
3839     //A fileset has one implied patternset
3840     if (!parsePatternSet(elem, propRef, includes, excludes))
3841         {
3842         return false;
3843         }
3844     //Look for child tags, including more patternsets
3845     std::vector<Element *> children  = elem->getChildren();
3846     for (unsigned int i=0 ; i<children.size() ; i++)
3847         {
3848         Element *child = children[i];
3849         String tagName = child->getName();
3850         if (tagName == "patternset")
3851             {
3852             if (!parsePatternSet(child, propRef, includes, excludes))
3853                 {
3854                 return false;
3855                 }
3856             }
3857         }
3859     String dir;
3860     //Now do the stuff
3861     //Get the base directory for reading file names
3862     if (!propRef.getAttribute(elem, "dir", dir))
3863         return false;
3865     fileSet.setDirectory(dir);
3866     fileSet.setIncludes(includes);
3867     fileSet.setExcludes(excludes);
3868     
3869     /*
3870     std::vector<String> fileList;
3871     if (dir.size() > 0)
3872         {
3873         String baseDir = propRef.resolve(dir);
3874             if (!listFiles(baseDir, "", includes, excludes, fileList))
3875                 return false;
3876             }
3877     std::sort(fileList.begin(), fileList.end());
3878         result = fileList;
3879         */
3881         
3882         /*
3883         for (unsigned int i=0 ; i<result.size() ; i++)
3884             {
3885             trace("RES:%s", result[i].c_str());
3886             }
3887     */
3889     
3890     return true;
3895 /**
3896  * Create a directory, making intermediate dirs
3897  * if necessary
3898  */                         
3899 bool MakeBase::createDirectory(const String &dirname)
3901     //trace("## createDirectory: %s", dirname.c_str());
3902     //## first check if it exists
3903     struct stat finfo;
3904     String nativeDir = getNativePath(dirname);
3905     char *cnative = (char *) nativeDir.c_str();
3906     if (stat(dirname.c_str(), &finfo)==0)
3907         {
3908         if (!S_ISDIR(finfo.st_mode))
3909             {
3910             error("mkdir: file %s exists but is not a directory",
3911                               cnative);
3912             return false;
3913             }
3914         else //exists
3915             {
3916             return true;
3917             }
3918         }
3920     //## 2: pull off the last path segment, if any,
3921     //## to make the dir 'above' this one, if necessary
3922     unsigned int pos = dirname.find_last_of('/');
3923     if (pos != dirname.npos)
3924         {
3925         String subpath = dirname.substr(0, pos);
3926         if (!createDirectory(subpath))
3927             return false;
3928         }
3929         
3930     //## 3: now make
3931     if (mkdir(cnative)<0)
3932         {
3933         error("cannot make directory %s", cnative);
3934         return false;
3935         }
3936         
3937     return true;
3941 /**
3942  * Remove a directory recursively
3943  */ 
3944 bool MakeBase::removeDirectory(const String &dirName)
3946     char *dname = (char *)dirName.c_str();
3948     DIR *dir = opendir(dname);
3949     if (!dir)
3950         {
3951         //# Let this fail nicely.
3952         return true;
3953         //error("error opening directory %s : %s", dname, strerror(errno));
3954         //return false;
3955         }
3956     
3957     while (true)
3958         {
3959         struct dirent *de = readdir(dir);
3960         if (!de)
3961             break;
3963         //Get the directory member name
3964         String s = de->d_name;
3965         if (s.size() == 0 || s[0] == '.')
3966             continue;
3967         String childName;
3968         if (dirName.size() > 0)
3969             {
3970             childName.append(dirName);
3971             childName.append("/");
3972             }
3973         childName.append(s);
3976         struct stat finfo;
3977         String childNative = getNativePath(childName);
3978         char *cnative = (char *)childNative.c_str();
3979         if (stat(cnative, &finfo)<0)
3980             {
3981             error("cannot stat file:%s", cnative);
3982             }
3983         else if (S_ISDIR(finfo.st_mode))
3984             {
3985             //trace("DEL dir: %s", childName.c_str());
3986                         if (!removeDirectory(childName))
3987                     {
3988                             return false;
3989                             }
3990             }
3991         else if (!S_ISREG(finfo.st_mode))
3992             {
3993             //trace("not regular: %s", cnative);
3994             }
3995         else
3996             {
3997             //trace("DEL file: %s", childName.c_str());
3998             if (remove(cnative)<0)
3999                 {
4000                 error("error deleting %s : %s",
4001                                      cnative, strerror(errno));
4002                                 return false;
4003                                 }
4004             }
4005         }
4006     closedir(dir);
4008     //Now delete the directory
4009     String native = getNativePath(dirName);
4010     if (rmdir(native.c_str())<0)
4011         {
4012         error("could not delete directory %s : %s",
4013             native.c_str() , strerror(errno));
4014         return false;
4015         }
4017     return true;
4018     
4022 /**
4023  * Copy a file from one name to another. Perform only if needed
4024  */ 
4025 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4027     //# 1 Check up-to-date times
4028     String srcNative = getNativePath(srcFile);
4029     struct stat srcinfo;
4030     if (stat(srcNative.c_str(), &srcinfo)<0)
4031         {
4032         error("source file %s for copy does not exist",
4033                          srcNative.c_str());
4034         return false;
4035         }
4037     String destNative = getNativePath(destFile);
4038     struct stat destinfo;
4039     if (stat(destNative.c_str(), &destinfo)==0)
4040         {
4041         if (destinfo.st_mtime >= srcinfo.st_mtime)
4042             return true;
4043         }
4044         
4045     //# 2 prepare a destination directory if necessary
4046     unsigned int pos = destFile.find_last_of('/');
4047     if (pos != destFile.npos)
4048         {
4049         String subpath = destFile.substr(0, pos);
4050         if (!createDirectory(subpath))
4051             return false;
4052         }
4054     //# 3 do the data copy
4055 #ifndef __WIN32__
4057     FILE *srcf = fopen(srcNative.c_str(), "rb");
4058     if (!srcf)
4059         {
4060         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4061         return false;
4062         }
4063     FILE *destf = fopen(destNative.c_str(), "wb");
4064     if (!destf)
4065         {
4066         error("copyFile cannot open %s for writing", srcNative.c_str());
4067         return false;
4068         }
4070     while (!feof(srcf))
4071         {
4072         int ch = fgetc(srcf);
4073         if (ch<0)
4074             break;
4075         fputc(ch, destf);
4076         }
4078     fclose(destf);
4079     fclose(srcf);
4081 #else
4082     
4083     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4084         {
4085         error("copyFile from %s to %s failed",
4086                      srcNative.c_str(), destNative.c_str());
4087         return false;
4088         }
4089         
4090 #endif /* __WIN32__ */
4093     return true;
4098 /**
4099  * Tests if the file exists and is a regular file
4100  */ 
4101 bool MakeBase::isRegularFile(const String &fileName)
4103     String native = getNativePath(fileName);
4104     struct stat finfo;
4105     
4106     //Exists?
4107     if (stat(native.c_str(), &finfo)<0)
4108                 return false;
4111     //check the file mode
4112     if (!S_ISREG(finfo.st_mode))
4113                 return false;
4115     return true;
4118 /**
4119  * Tests if the file exists and is a directory
4120  */ 
4121 bool MakeBase::isDirectory(const String &fileName)
4123     String native = getNativePath(fileName);
4124     struct stat finfo;
4125     
4126     //Exists?
4127     if (stat(native.c_str(), &finfo)<0)
4128                 return false;
4131     //check the file mode
4132     if (!S_ISDIR(finfo.st_mode))
4133                 return false;
4135     return true;
4140 /**
4141  * Tests is the modification of fileA is newer than fileB
4142  */ 
4143 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4145     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4146     String nativeA = getNativePath(fileA);
4147     struct stat infoA;
4148     //IF source does not exist, NOT newer
4149     if (stat(nativeA.c_str(), &infoA)<0)
4150         {
4151                 return false;
4152                 }
4154     String nativeB = getNativePath(fileB);
4155     struct stat infoB;
4156     //IF dest does not exist, YES, newer
4157     if (stat(nativeB.c_str(), &infoB)<0)
4158         {
4159                 return true;
4160                 }
4162     //check the actual times
4163     if (infoA.st_mtime > infoB.st_mtime)
4164         {
4165                 return true;
4166                 }
4168     return false;
4172 //########################################################################
4173 //# P K G    C O N F I G
4174 //########################################################################
4176 /**
4177  *
4178  */
4179 class PkgConfig : public MakeBase
4182 public:
4184     /**
4185      *
4186      */
4187     PkgConfig()
4188         { init(); }
4190     /**
4191      *
4192      */
4193     PkgConfig(const String &namearg)
4194         { init(); name = namearg; }
4196     /**
4197      *
4198      */
4199     PkgConfig(const PkgConfig &other)
4200         { assign(other); }
4202     /**
4203      *
4204      */
4205     PkgConfig &operator=(const PkgConfig &other)
4206         { assign(other); return *this; }
4208     /**
4209      *
4210      */
4211     virtual ~PkgConfig()
4212         { }
4214     /**
4215      *
4216      */
4217     virtual String getName()
4218         { return name; }
4220     /**
4221      *
4222      */
4223     virtual String getDescription()
4224         { return description; }
4226     /**
4227      *
4228      */
4229     virtual String getCflags()
4230         { return cflags; }
4232     /**
4233      *
4234      */
4235     virtual String getLibs()
4236         { return libs; }
4238     /**
4239      *
4240      */
4241     virtual String getVersion()
4242         { return version; }
4244     /**
4245      *
4246      */
4247     virtual int getMajorVersion()
4248         { return majorVersion; }
4250     /**
4251      *
4252      */
4253     virtual int getMinorVersion()
4254         { return minorVersion; }
4256     /**
4257      *
4258      */
4259     virtual int getMicroVersion()
4260         { return microVersion; }
4262     /**
4263      *
4264      */
4265     virtual std::map<String, String> &getAttributes()
4266         { return attrs; }
4268     /**
4269      *
4270      */
4271     virtual std::vector<String> &getRequireList()
4272         { return requireList; }
4274     virtual bool readFile(const String &fileName);
4276 private:
4278     void init()
4279         {
4280         name         = "";
4281         description  = "";
4282         cflags       = "";
4283         libs         = "";
4284         requires     = "";
4285         version      = "";
4286         majorVersion = 0;
4287         minorVersion = 0;
4288         microVersion = 0;
4289         fileName     = "";
4290         attrs.clear();
4291         requireList.clear();
4292         }
4294     void assign(const PkgConfig &other)
4295         {
4296         name         = other.name;
4297         description  = other.description;
4298         cflags       = other.cflags;
4299         libs         = other.libs;
4300         requires     = other.requires;
4301         version      = other.version;
4302         majorVersion = other.majorVersion;
4303         minorVersion = other.minorVersion;
4304         microVersion = other.microVersion;
4305         fileName     = other.fileName;
4306         attrs        = other.attrs;
4307         requireList  = other.requireList;
4308         }
4312     int get(int pos);
4314     int skipwhite(int pos);
4316     int getword(int pos, String &ret);
4318     void parseRequires();
4320     void parseVersion();
4322     bool parse(const String &buf);
4324     void dumpAttrs();
4326     String name;
4328     String description;
4330     String cflags;
4332     String libs;
4334     String requires;
4336     String version;
4338     int majorVersion;
4340     int minorVersion;
4342     int microVersion;
4344     String fileName;
4346     std::map<String, String> attrs;
4348     std::vector<String> requireList;
4350     char *parsebuf;
4351     int parselen;
4352 };
4355 /**
4356  * Get a character from the buffer at pos.  If out of range,
4357  * return -1 for safety
4358  */
4359 int PkgConfig::get(int pos)
4361     if (pos>parselen)
4362         return -1;
4363     return parsebuf[pos];
4368 /**
4369  *  Skip over all whitespace characters beginning at pos.  Return
4370  *  the position of the first non-whitespace character.
4371  */
4372 int PkgConfig::skipwhite(int pos)
4374     while (pos < parselen)
4375         {
4376         int ch = get(pos);
4377         if (ch < 0)
4378             break;
4379         if (!isspace(ch))
4380             break;
4381         pos++;
4382         }
4383     return pos;
4387 /**
4388  *  Parse the buffer beginning at pos, for a word.  Fill
4389  *  'ret' with the result.  Return the position after the
4390  *  word.
4391  */
4392 int PkgConfig::getword(int pos, String &ret)
4394     while (pos < parselen)
4395         {
4396         int ch = get(pos);
4397         if (ch < 0)
4398             break;
4399         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4400             break;
4401         ret.push_back((char)ch);
4402         pos++;
4403         }
4404     return pos;
4407 void PkgConfig::parseRequires()
4409     if (requires.size() == 0)
4410         return;
4411     parsebuf = (char *)requires.c_str();
4412     parselen = requires.size();
4413     int pos = 0;
4414     while (pos < parselen)
4415         {
4416         pos = skipwhite(pos);
4417         String val;
4418         int pos2 = getword(pos, val);
4419         if (pos2 == pos)
4420             break;
4421         pos = pos2;
4422         //trace("val %s", val.c_str());
4423         requireList.push_back(val);
4424         }
4427 static int getint(const String str)
4429     char *s = (char *)str.c_str();
4430     char *ends = NULL;
4431     long val = strtol(s, &ends, 10);
4432     if (ends == s)
4433         return 0L;
4434     else
4435         return val;
4438 void PkgConfig::parseVersion()
4440     if (version.size() == 0)
4441         return;
4442     String s1, s2, s3;
4443     unsigned int pos = 0;
4444     unsigned int pos2 = version.find('.', pos);
4445     if (pos2 == version.npos)
4446         {
4447         s1 = version;
4448         }
4449     else
4450         {
4451         s1 = version.substr(pos, pos2-pos);
4452         pos = pos2;
4453         pos++;
4454         if (pos < version.size())
4455             {
4456             pos2 = version.find('.', pos);
4457             if (pos2 == version.npos)
4458                 {
4459                 s2 = version.substr(pos, version.size()-pos);
4460                 }
4461             else
4462                 {
4463                 s2 = version.substr(pos, pos2-pos);
4464                 pos = pos2;
4465                 pos++;
4466                 if (pos < version.size())
4467                     s3 = version.substr(pos, pos2-pos);
4468                 }
4469             }
4470         }
4472     majorVersion = getint(s1);
4473     minorVersion = getint(s2);
4474     microVersion = getint(s3);
4475     //trace("version:%d.%d.%d", majorVersion,
4476     //          minorVersion, microVersion );
4480 bool PkgConfig::parse(const String &buf)
4482     init();
4484     parsebuf = (char *)buf.c_str();
4485     parselen = buf.size();
4486     int pos = 0;
4489     while (pos < parselen)
4490         {
4491         String attrName;
4492         pos = skipwhite(pos);
4493         int ch = get(pos);
4494         if (ch == '#')
4495             {
4496             //comment.  eat the rest of the line
4497             while (pos < parselen)
4498                 {
4499                 ch = get(pos);
4500                 if (ch == '\n' || ch < 0)
4501                     break;
4502                 pos++;
4503                 }
4504             continue;
4505             }
4506         pos = getword(pos, attrName);
4507         if (attrName.size() == 0)
4508             continue;
4509         pos = skipwhite(pos);
4510         ch = get(pos);
4511         if (ch != ':' && ch != '=')
4512             {
4513             error("expected ':' or '='");
4514             return false;
4515             }
4516         pos++;
4517         pos = skipwhite(pos);
4518         String attrVal;
4519         while (pos < parselen)
4520             {
4521             ch = get(pos);
4522             if (ch == '\n' || ch < 0)
4523                 break;
4524             else if (ch == '$' && get(pos+1) == '{')
4525                 {
4526                 //#  this is a ${substitution}
4527                 pos += 2;
4528                 String subName;
4529                 while (pos < parselen)
4530                     {
4531                     ch = get(pos);
4532                     if (ch < 0)
4533                         {
4534                         error("unterminated substitution");
4535                         return false;
4536                         }
4537                     else if (ch == '}')
4538                         break;
4539                     else
4540                         subName.push_back((char)ch);
4541                     pos++;
4542                     }
4543                 //trace("subName:%s", subName.c_str());
4544                 String subVal = attrs[subName];
4545                 //trace("subVal:%s", subVal.c_str());
4546                 attrVal.append(subVal);
4547                 }
4548             else
4549                 attrVal.push_back((char)ch);
4550             pos++;
4551             }
4553         attrVal = trim(attrVal);
4554         attrs[attrName] = attrVal;
4556         if (attrName == "Name")
4557             name = attrVal;
4558         else if (attrName == "Description")
4559             description = attrVal;
4560         else if (attrName == "Cflags")
4561             cflags = attrVal;
4562         else if (attrName == "Libs")
4563             libs = attrVal;
4564         else if (attrName == "Requires")
4565             requires = attrVal;
4566         else if (attrName == "Version")
4567             version = attrVal;
4569         //trace("name:'%s'  value:'%s'",
4570         //      attrName.c_str(), attrVal.c_str());
4571         }
4574     parseRequires();
4575     parseVersion();
4577     return true;
4580 void PkgConfig::dumpAttrs()
4582     //trace("### PkgConfig attributes for %s", fileName.c_str());
4583     std::map<String, String>::iterator iter;
4584     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4585         {
4586         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4587         }
4591 bool PkgConfig::readFile(const String &fileNameArg)
4593     fileName = fileNameArg;
4595     FILE *f = fopen(fileName.c_str(), "r");
4596     if (!f)
4597         {
4598         error("cannot open file '%s' for reading", fileName.c_str());
4599         return false;
4600         }
4601     String buf;
4602     while (true)
4603         {
4604         int ch = fgetc(f);
4605         if (ch < 0)
4606             break;
4607         buf.push_back((char)ch);
4608         }
4609     fclose(f);
4611     //trace("####### File:\n%s", buf.c_str());
4612     if (!parse(buf))
4613         {
4614         return false;
4615         }
4617     dumpAttrs();
4619     return true;
4626 //########################################################################
4627 //# D E P T O O L
4628 //########################################################################
4632 /**
4633  *  Class which holds information for each file.
4634  */
4635 class FileRec
4637 public:
4639     typedef enum
4640         {
4641         UNKNOWN,
4642         CFILE,
4643         HFILE,
4644         OFILE
4645         } FileType;
4647     /**
4648      *  Constructor
4649      */
4650     FileRec()
4651         {init(); type = UNKNOWN;}
4653     /**
4654      *  Copy constructor
4655      */
4656     FileRec(const FileRec &other)
4657         {init(); assign(other);}
4658     /**
4659      *  Constructor
4660      */
4661     FileRec(int typeVal)
4662         {init(); type = typeVal;}
4663     /**
4664      *  Assignment operator
4665      */
4666     FileRec &operator=(const FileRec &other)
4667         {init(); assign(other); return *this;}
4670     /**
4671      *  Destructor
4672      */
4673     ~FileRec()
4674         {}
4676     /**
4677      *  Directory part of the file name
4678      */
4679     String path;
4681     /**
4682      *  Base name, sans directory and suffix
4683      */
4684     String baseName;
4686     /**
4687      *  File extension, such as cpp or h
4688      */
4689     String suffix;
4691     /**
4692      *  Type of file: CFILE, HFILE, OFILE
4693      */
4694     int type;
4696     /**
4697      * Used to list files ref'd by this one
4698      */
4699     std::map<String, FileRec *> files;
4702 private:
4704     void init()
4705         {
4706         }
4708     void assign(const FileRec &other)
4709         {
4710         type     = other.type;
4711         baseName = other.baseName;
4712         suffix   = other.suffix;
4713         files    = other.files;
4714         }
4716 };
4720 /**
4721  *  Simpler dependency record
4722  */
4723 class DepRec
4725 public:
4727     /**
4728      *  Constructor
4729      */
4730     DepRec()
4731         {init();}
4733     /**
4734      *  Copy constructor
4735      */
4736     DepRec(const DepRec &other)
4737         {init(); assign(other);}
4738     /**
4739      *  Constructor
4740      */
4741     DepRec(const String &fname)
4742         {init(); name = fname; }
4743     /**
4744      *  Assignment operator
4745      */
4746     DepRec &operator=(const DepRec &other)
4747         {init(); assign(other); return *this;}
4750     /**
4751      *  Destructor
4752      */
4753     ~DepRec()
4754         {}
4756     /**
4757      *  Directory part of the file name
4758      */
4759     String path;
4761     /**
4762      *  Base name, without the path and suffix
4763      */
4764     String name;
4766     /**
4767      *  Suffix of the source
4768      */
4769     String suffix;
4772     /**
4773      * Used to list files ref'd by this one
4774      */
4775     std::vector<String> files;
4778 private:
4780     void init()
4781         {
4782         }
4784     void assign(const DepRec &other)
4785         {
4786         path     = other.path;
4787         name     = other.name;
4788         suffix   = other.suffix;
4789         files    = other.files;
4790         }
4792 };
4795 class DepTool : public MakeBase
4797 public:
4799     /**
4800      *  Constructor
4801      */
4802     DepTool()
4803         {init();}
4805     /**
4806      *  Copy constructor
4807      */
4808     DepTool(const DepTool &other)
4809         {init(); assign(other);}
4811     /**
4812      *  Assignment operator
4813      */
4814     DepTool &operator=(const DepTool &other)
4815         {init(); assign(other); return *this;}
4818     /**
4819      *  Destructor
4820      */
4821     ~DepTool()
4822         {}
4825     /**
4826      *  Reset this section of code
4827      */
4828     virtual void init();
4829     
4830     /**
4831      *  Reset this section of code
4832      */
4833     virtual void assign(const DepTool &other)
4834         {
4835         }
4836     
4837     /**
4838      *  Sets the source directory which will be scanned
4839      */
4840     virtual void setSourceDirectory(const String &val)
4841         { sourceDir = val; }
4843     /**
4844      *  Returns the source directory which will be scanned
4845      */
4846     virtual String getSourceDirectory()
4847         { return sourceDir; }
4849     /**
4850      *  Sets the list of files within the directory to analyze
4851      */
4852     virtual void setFileList(const std::vector<String> &list)
4853         { fileList = list; }
4855     /**
4856      * Creates the list of all file names which will be
4857      * candidates for further processing.  Reads make.exclude
4858      * to see which files for directories to leave out.
4859      */
4860     virtual bool createFileList();
4863     /**
4864      *  Generates the forward dependency list
4865      */
4866     virtual bool generateDependencies();
4869     /**
4870      *  Generates the forward dependency list, saving the file
4871      */
4872     virtual bool generateDependencies(const String &);
4875     /**
4876      *  Load a dependency file
4877      */
4878     std::vector<DepRec> loadDepFile(const String &fileName);
4880     /**
4881      *  Load a dependency file, generating one if necessary
4882      */
4883     std::vector<DepRec> getDepFile(const String &fileName);
4885     /**
4886      *  Save a dependency file
4887      */
4888     bool saveDepFile(const String &fileName);
4891 private:
4894     /**
4895      *
4896      */
4897     void parseName(const String &fullname,
4898                    String &path,
4899                    String &basename,
4900                    String &suffix);
4902     /**
4903      *
4904      */
4905     int get(int pos);
4907     /**
4908      *
4909      */
4910     int skipwhite(int pos);
4912     /**
4913      *
4914      */
4915     int getword(int pos, String &ret);
4917     /**
4918      *
4919      */
4920     bool sequ(int pos, char *key);
4922     /**
4923      *
4924      */
4925     bool addIncludeFile(FileRec *frec, const String &fname);
4927     /**
4928      *
4929      */
4930     bool scanFile(const String &fname, FileRec *frec);
4932     /**
4933      *
4934      */
4935     bool processDependency(FileRec *ofile,
4936                            FileRec *include,
4937                            int depth);
4939     /**
4940      *
4941      */
4942     String sourceDir;
4944     /**
4945      *
4946      */
4947     std::vector<String> fileList;
4949     /**
4950      *
4951      */
4952     std::vector<String> directories;
4954     /**
4955      * A list of all files which will be processed for
4956      * dependencies.  This is the only list that has the actual
4957      * records.  All other lists have pointers to these records.     
4958      */
4959     std::map<String, FileRec *> allFiles;
4961     /**
4962      * The list of .o files, and the
4963      * dependencies upon them.
4964      */
4965     std::map<String, FileRec *> depFiles;
4967     int depFileSize;
4968     char *depFileBuf;
4969     
4971 };
4977 /**
4978  *  Clean up after processing.  Called by the destructor, but should
4979  *  also be called before the object is reused.
4980  */
4981 void DepTool::init()
4983     sourceDir = ".";
4985     fileList.clear();
4986     directories.clear();
4987     
4988     //clear refs
4989     depFiles.clear();
4990     //clear records
4991     std::map<String, FileRec *>::iterator iter;
4992     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4993          delete iter->second;
4995     allFiles.clear(); 
5002 /**
5003  *  Parse a full path name into path, base name, and suffix
5004  */
5005 void DepTool::parseName(const String &fullname,
5006                         String &path,
5007                         String &basename,
5008                         String &suffix)
5010     if (fullname.size() < 2)
5011         return;
5013     unsigned int pos = fullname.find_last_of('/');
5014     if (pos != fullname.npos && pos<fullname.size()-1)
5015         {
5016         path = fullname.substr(0, pos);
5017         pos++;
5018         basename = fullname.substr(pos, fullname.size()-pos);
5019         }
5020     else
5021         {
5022         path = "";
5023         basename = fullname;
5024         }
5026     pos = basename.find_last_of('.');
5027     if (pos != basename.npos && pos<basename.size()-1)
5028         {
5029         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5030         basename = basename.substr(0, pos);
5031         }
5033     //trace("parsename:%s %s %s", path.c_str(),
5034     //        basename.c_str(), suffix.c_str()); 
5039 /**
5040  *  Generate our internal file list.
5041  */
5042 bool DepTool::createFileList()
5045     for (unsigned int i=0 ; i<fileList.size() ; i++)
5046         {
5047         String fileName = fileList[i];
5048         //trace("## FileName:%s", fileName.c_str());
5049         String path;
5050         String basename;
5051         String sfx;
5052         parseName(fileName, path, basename, sfx);
5053         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5054                     sfx == "cc" || sfx == "CC")
5055             {
5056             FileRec *fe         = new FileRec(FileRec::CFILE);
5057             fe->path            = path;
5058             fe->baseName        = basename;
5059             fe->suffix          = sfx;
5060             allFiles[fileName]  = fe;
5061             }
5062         else if (sfx == "h"   ||  sfx == "hh"  ||
5063                  sfx == "hpp" ||  sfx == "hxx")
5064             {
5065             FileRec *fe         = new FileRec(FileRec::HFILE);
5066             fe->path            = path;
5067             fe->baseName        = basename;
5068             fe->suffix          = sfx;
5069             allFiles[fileName]  = fe;
5070             }
5071         }
5073     if (!listDirectories(sourceDir, "", directories))
5074         return false;
5075         
5076     return true;
5083 /**
5084  * Get a character from the buffer at pos.  If out of range,
5085  * return -1 for safety
5086  */
5087 int DepTool::get(int pos)
5089     if (pos>depFileSize)
5090         return -1;
5091     return depFileBuf[pos];
5096 /**
5097  *  Skip over all whitespace characters beginning at pos.  Return
5098  *  the position of the first non-whitespace character.
5099  */
5100 int DepTool::skipwhite(int pos)
5102     while (pos < depFileSize)
5103         {
5104         int ch = get(pos);
5105         if (ch < 0)
5106             break;
5107         if (!isspace(ch))
5108             break;
5109         pos++;
5110         }
5111     return pos;
5115 /**
5116  *  Parse the buffer beginning at pos, for a word.  Fill
5117  *  'ret' with the result.  Return the position after the
5118  *  word.
5119  */
5120 int DepTool::getword(int pos, String &ret)
5122     while (pos < depFileSize)
5123         {
5124         int ch = get(pos);
5125         if (ch < 0)
5126             break;
5127         if (isspace(ch))
5128             break;
5129         ret.push_back((char)ch);
5130         pos++;
5131         }
5132     return pos;
5135 /**
5136  * Return whether the sequence of characters in the buffer
5137  * beginning at pos match the key,  for the length of the key
5138  */
5139 bool DepTool::sequ(int pos, char *key)
5141     while (*key)
5142         {
5143         if (*key != get(pos))
5144             return false;
5145         key++; pos++;
5146         }
5147     return true;
5152 /**
5153  *  Add an include file name to a file record.  If the name
5154  *  is not found in allFiles explicitly, try prepending include
5155  *  directory names to it and try again.
5156  */
5157 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5160     std::map<String, FileRec *>::iterator iter =
5161            allFiles.find(iname);
5162     if (iter != allFiles.end()) //already exists
5163         {
5164          //h file in same dir
5165         FileRec *other = iter->second;
5166         //trace("local: '%s'", iname.c_str());
5167         frec->files[iname] = other;
5168         return true;
5169         }
5170     else 
5171         {
5172         //look in other dirs
5173         std::vector<String>::iterator diter;
5174         for (diter=directories.begin() ;
5175              diter!=directories.end() ; diter++)
5176             {
5177             String dfname = *diter;
5178             dfname.append("/");
5179             dfname.append(iname);
5180             iter = allFiles.find(dfname);
5181             if (iter != allFiles.end())
5182                 {
5183                 FileRec *other = iter->second;
5184                 //trace("other: '%s'", iname.c_str());
5185                 frec->files[dfname] = other;
5186                 return true;
5187                 }
5188             }
5189         }
5190     return true;
5195 /**
5196  *  Lightly parse a file to find the #include directives.  Do
5197  *  a bit of state machine stuff to make sure that the directive
5198  *  is valid.  (Like not in a comment).
5199  */
5200 bool DepTool::scanFile(const String &fname, FileRec *frec)
5202     String fileName;
5203     if (sourceDir.size() > 0)
5204         {
5205         fileName.append(sourceDir);
5206         fileName.append("/");
5207         }
5208     fileName.append(fname);
5209     String nativeName = getNativePath(fileName);
5210     FILE *f = fopen(nativeName.c_str(), "r");
5211     if (!f)
5212         {
5213         error("Could not open '%s' for reading", fname.c_str());
5214         return false;
5215         }
5216     String buf;
5217     while (true)
5218         {
5219         int ch = fgetc(f);
5220         if (ch < 0)
5221             break;
5222         buf.push_back((char)ch);
5223         }
5224     fclose(f);
5226     depFileSize = buf.size();
5227     depFileBuf  = (char *)buf.c_str();
5228     int pos = 0;
5231     while (pos < depFileSize)
5232         {
5233         //trace("p:%c", get(pos));
5235         //# Block comment
5236         if (get(pos) == '/' && get(pos+1) == '*')
5237             {
5238             pos += 2;
5239             while (pos < depFileSize)
5240                 {
5241                 if (get(pos) == '*' && get(pos+1) == '/')
5242                     {
5243                     pos += 2;
5244                     break;
5245                     }
5246                 else
5247                     pos++;
5248                 }
5249             }
5250         //# Line comment
5251         else if (get(pos) == '/' && get(pos+1) == '/')
5252             {
5253             pos += 2;
5254             while (pos < depFileSize)
5255                 {
5256                 if (get(pos) == '\n')
5257                     {
5258                     pos++;
5259                     break;
5260                     }
5261                 else
5262                     pos++;
5263                 }
5264             }
5265         //# #include! yaay
5266         else if (sequ(pos, "#include"))
5267             {
5268             pos += 8;
5269             pos = skipwhite(pos);
5270             String iname;
5271             pos = getword(pos, iname);
5272             if (iname.size()>2)
5273                 {
5274                 iname = iname.substr(1, iname.size()-2);
5275                 addIncludeFile(frec, iname);
5276                 }
5277             }
5278         else
5279             {
5280             pos++;
5281             }
5282         }
5284     return true;
5289 /**
5290  *  Recursively check include lists to find all files in allFiles to which
5291  *  a given file is dependent.
5292  */
5293 bool DepTool::processDependency(FileRec *ofile,
5294                              FileRec *include,
5295                              int depth)
5297     std::map<String, FileRec *>::iterator iter;
5298     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5299         {
5300         String fname  = iter->first;
5301         if (ofile->files.find(fname) != ofile->files.end())
5302             {
5303             //trace("file '%s' already seen", fname.c_str());
5304             continue;
5305             }
5306         FileRec *child  = iter->second;
5307         ofile->files[fname] = child;
5308       
5309         processDependency(ofile, child, depth+1);
5310         }
5313     return true;
5320 /**
5321  *  Generate the file dependency list.
5322  */
5323 bool DepTool::generateDependencies()
5325     std::map<String, FileRec *>::iterator iter;
5326     //# First pass.  Scan for all includes
5327     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5328         {
5329         FileRec *frec = iter->second;
5330         if (!scanFile(iter->first, frec))
5331             {
5332             //quit?
5333             }
5334         }
5336     //# Second pass.  Scan for all includes
5337     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5338         {
5339         FileRec *include = iter->second;
5340         if (include->type == FileRec::CFILE)
5341             {
5342             String cFileName = iter->first;
5343             FileRec *ofile      = new FileRec(FileRec::OFILE);
5344             ofile->path         = include->path;
5345             ofile->baseName     = include->baseName;
5346             ofile->suffix       = include->suffix;
5347             String fname     = include->path;
5348             if (fname.size()>0)
5349                 fname.append("/");
5350             fname.append(include->baseName);
5351             fname.append(".o");
5352             depFiles[fname]    = ofile;
5353             //add the .c file first?   no, don't
5354             //ofile->files[cFileName] = include;
5355             
5356             //trace("ofile:%s", fname.c_str());
5358             processDependency(ofile, include, 0);
5359             }
5360         }
5362       
5363     return true;
5368 /**
5369  *  High-level call to generate deps and optionally save them
5370  */
5371 bool DepTool::generateDependencies(const String &fileName)
5373     if (!createFileList())
5374         return false;
5375     if (!generateDependencies())
5376         return false;
5377     if (!saveDepFile(fileName))
5378         return false;
5379     return true;
5383 /**
5384  *   This saves the dependency cache.
5385  */
5386 bool DepTool::saveDepFile(const String &fileName)
5388     time_t tim;
5389     time(&tim);
5391     FILE *f = fopen(fileName.c_str(), "w");
5392     if (!f)
5393         {
5394         trace("cannot open '%s' for writing", fileName.c_str());
5395         }
5396     fprintf(f, "<?xml version='1.0'?>\n");
5397     fprintf(f, "<!--\n");
5398     fprintf(f, "########################################################\n");
5399     fprintf(f, "## File: build.dep\n");
5400     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5401     fprintf(f, "########################################################\n");
5402     fprintf(f, "-->\n");
5404     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5405     std::map<String, FileRec *>::iterator iter;
5406     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5407         {
5408         FileRec *frec = iter->second;
5409         if (frec->type == FileRec::OFILE)
5410             {
5411             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5412                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5413             std::map<String, FileRec *>::iterator citer;
5414             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5415                 {
5416                 String cfname = citer->first;
5417                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5418                 }
5419             fprintf(f, "</object>\n\n");
5420             }
5421         }
5423     fprintf(f, "</dependencies>\n");
5424     fprintf(f, "\n");
5425     fprintf(f, "<!--\n");
5426     fprintf(f, "########################################################\n");
5427     fprintf(f, "## E N D\n");
5428     fprintf(f, "########################################################\n");
5429     fprintf(f, "-->\n");
5431     fclose(f);
5433     return true;
5439 /**
5440  *   This loads the dependency cache.
5441  */
5442 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5444     std::vector<DepRec> result;
5445     
5446     Parser parser;
5447     Element *root = parser.parseFile(depFile.c_str());
5448     if (!root)
5449         {
5450         error("Could not open %s for reading", depFile.c_str());
5451         return result;
5452         }
5454     if (root->getChildren().size()==0 ||
5455         root->getChildren()[0]->getName()!="dependencies")
5456         {
5457         error("Main xml element should be <dependencies>");
5458         delete root;
5459         return result;
5460         }
5462     //########## Start parsing
5463     Element *depList = root->getChildren()[0];
5465     std::vector<Element *> objects = depList->getChildren();
5466     for (unsigned int i=0 ; i<objects.size() ; i++)
5467         {
5468         Element *objectElem = objects[i];
5469         String tagName = objectElem->getName();
5470         if (tagName == "object")
5471             {
5472             String objName   = objectElem->getAttribute("name");
5473              //trace("object:%s", objName.c_str());
5474             DepRec depObject(objName);
5475             depObject.path   = objectElem->getAttribute("path");
5476             depObject.suffix = objectElem->getAttribute("suffix");
5477             //########## DESCRIPTION
5478             std::vector<Element *> depElems = objectElem->getChildren();
5479             for (unsigned int i=0 ; i<depElems.size() ; i++)
5480                 {
5481                 Element *depElem = depElems[i];
5482                 tagName = depElem->getName();
5483                 if (tagName == "dep")
5484                     {
5485                     String depName = depElem->getAttribute("name");
5486                     //trace("    dep:%s", depName.c_str());
5487                     depObject.files.push_back(depName);
5488                     }
5489                 }
5490             //Insert into the result list, in a sorted manner
5491             bool inserted = false;
5492             std::vector<DepRec>::iterator iter;
5493             for (iter = result.begin() ; iter != result.end() ; iter++)
5494                 {
5495                 String vpath = iter->path;
5496                 vpath.append("/");
5497                 vpath.append(iter->name);
5498                 String opath = depObject.path;
5499                 opath.append("/");
5500                 opath.append(depObject.name);
5501                 if (vpath > opath)
5502                     {
5503                     inserted = true;
5504                     iter = result.insert(iter, depObject);
5505                     break;
5506                     }
5507                 }
5508             if (!inserted)
5509                 result.push_back(depObject);
5510             }
5511         }
5513     delete root;
5515     return result;
5519 /**
5520  *   This loads the dependency cache.
5521  */
5522 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
5524     std::vector<DepRec> result = loadDepFile(depFile);
5525     if (result.size() == 0)
5526         {
5527         generateDependencies(depFile);
5528         result = loadDepFile(depFile);
5529         }
5530     return result;
5536 //########################################################################
5537 //# T A S K
5538 //########################################################################
5539 //forward decl
5540 class Target;
5541 class Make;
5543 /**
5544  *
5545  */
5546 class Task : public MakeBase
5549 public:
5551     typedef enum
5552         {
5553         TASK_NONE,
5554         TASK_AR,
5555         TASK_CC,
5556         TASK_COPY,
5557         TASK_DELETE,
5558         TASK_JAR,
5559         TASK_JAVAC,
5560         TASK_LINK,
5561         TASK_MAKEFILE,
5562         TASK_MKDIR,
5563         TASK_MSGFMT,
5564         TASK_RANLIB,
5565         TASK_RC,
5566         TASK_STRIP,
5567         TASK_TSTAMP
5568         } TaskType;
5569         
5571     /**
5572      *
5573      */
5574     Task(MakeBase &par) : parent(par)
5575         { init(); }
5577     /**
5578      *
5579      */
5580     Task(const Task &other) : parent(other.parent)
5581         { init(); assign(other); }
5583     /**
5584      *
5585      */
5586     Task &operator=(const Task &other)
5587         { assign(other); return *this; }
5589     /**
5590      *
5591      */
5592     virtual ~Task()
5593         { }
5596     /**
5597      *
5598      */
5599     virtual MakeBase &getParent()
5600         { return parent; }
5602      /**
5603      *
5604      */
5605     virtual int  getType()
5606         { return type; }
5608     /**
5609      *
5610      */
5611     virtual void setType(int val)
5612         { type = val; }
5614     /**
5615      *
5616      */
5617     virtual String getName()
5618         { return name; }
5620     /**
5621      *
5622      */
5623     virtual bool execute()
5624         { return true; }
5626     /**
5627      *
5628      */
5629     virtual bool parse(Element *elem)
5630         { return true; }
5632     /**
5633      *
5634      */
5635     Task *createTask(Element *elem);
5638 protected:
5640     void init()
5641         {
5642         type = TASK_NONE;
5643         name = "none";
5644         }
5646     void assign(const Task &other)
5647         {
5648         type = other.type;
5649         name = other.name;
5650         }
5651         
5652     String getAttribute(Element *elem, const String &attrName)
5653         {
5654         String str;
5655         return str;
5656         }
5658     MakeBase &parent;
5660     int type;
5662     String name;
5663 };
5668 /**
5669  * Run the "ar" command to archive .o's into a .a
5670  */
5671 class TaskAr : public Task
5673 public:
5675     TaskAr(MakeBase &par) : Task(par)
5676         {
5677                 type = TASK_AR; name = "ar";
5678                 command = "ar crv";
5679                 }
5681     virtual ~TaskAr()
5682         {}
5684     virtual bool execute()
5685         {
5686         //trace("###########HERE %d", fileSet.size());
5687         bool doit = false;
5688         
5689         String fullOut = parent.resolve(fileName);
5690         //trace("ar fullout: %s", fullOut.c_str());
5691         
5692         if (!listFiles(parent, fileSet))
5693             return false;
5694         String fileSetDir = fileSet.getDirectory();
5696         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5697             {
5698             String fname;
5699                         if (fileSetDir.size()>0)
5700                             {
5701                             fname.append(fileSetDir);
5702                 fname.append("/");
5703                 }
5704             fname.append(fileSet[i]);
5705             String fullName = parent.resolve(fname);
5706             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
5707             if (isNewerThan(fullName, fullOut))
5708                 doit = true;
5709             }
5710         //trace("Needs it:%d", doit);
5711         if (!doit)
5712             {
5713             return true;
5714             }
5716         String cmd = command;
5717         cmd.append(" ");
5718         cmd.append(fullOut);
5719         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5720             {
5721             String fname;
5722                         if (fileSetDir.size()>0)
5723                             {
5724                             fname.append(fileSetDir);
5725                 fname.append("/");
5726                 }
5727             fname.append(fileSet[i]);
5728             String fullName = parent.resolve(fname);
5730             cmd.append(" ");
5731             cmd.append(fullName);
5732             }
5734         String outString, errString;
5735         if (!executeCommand(cmd.c_str(), "", outString, errString))
5736             {
5737             error("AR problem: %s", errString.c_str());
5738             return false;
5739             }
5741         return true;
5742         }
5744     virtual bool parse(Element *elem)
5745         {
5746         if (!parent.getAttribute(elem, "file", fileName))
5747             return false;
5748             
5749         std::vector<Element *> children = elem->getChildren();
5750         for (unsigned int i=0 ; i<children.size() ; i++)
5751             {
5752             Element *child = children[i];
5753             String tagName = child->getName();
5754             if (tagName == "fileset")
5755                 {
5756                 if (!parseFileSet(child, parent, fileSet))
5757                     return false;
5758                 }
5759             }
5760         return true;
5761         }
5763 private:
5765     String command;
5766     String fileName;
5767     FileSet fileSet;
5769 };
5772 /**
5773  * This task runs the C/C++ compiler.  The compiler is invoked
5774  * for all .c or .cpp files which are newer than their correcsponding
5775  * .o files.  
5776  */
5777 class TaskCC : public Task
5779 public:
5781     TaskCC(MakeBase &par) : Task(par)
5782         {
5783                 type = TASK_CC; name = "cc";
5784                 ccCommand   = "gcc";
5785                 cxxCommand  = "g++";
5786                 source      = ".";
5787                 dest        = ".";
5788                 flags       = "";
5789                 defines     = "";
5790                 includes    = "";
5791                 fileSet.clear();
5792         }
5794     virtual ~TaskCC()
5795         {}
5797     virtual bool execute()
5798         {
5799         if (!listFiles(parent, fileSet))
5800             return false;
5802         DepTool depTool;
5803         depTool.setSourceDirectory(source);
5804         depTool.setFileList(fileSet.getFiles());
5805         std::vector<DepRec> deps = depTool.getDepFile("build.dep");
5806         
5807         String incs;
5808         incs.append("-I");
5809         incs.append(parent.resolve("."));
5810         incs.append(" ");
5811         if (includes.size()>0)
5812             {
5813             incs.append(includes);
5814             incs.append(" ");
5815             }
5816         std::set<String> paths;
5817         std::vector<DepRec>::iterator viter;
5818         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5819             {
5820             DepRec dep = *viter;
5821             if (dep.path.size()>0)
5822                 paths.insert(dep.path);
5823             }
5824         if (source.size()>0)
5825             {
5826             incs.append(" -I");
5827             incs.append(parent.resolve(source));
5828             incs.append(" ");
5829             }
5830         std::set<String>::iterator setIter;
5831         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5832             {
5833             incs.append(" -I");
5834             String dname;
5835             if (source.size()>0)
5836                 {
5837                 dname.append(source);
5838                 dname.append("/");
5839                 }
5840             dname.append(*setIter);
5841             incs.append(parent.resolve(dname));
5842             }
5843         std::vector<String> cfiles;
5844         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5845             {
5846             DepRec dep = *viter;
5848             //## Select command
5849             String sfx = dep.suffix;
5850             String command = ccCommand;
5851             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5852                              || sfx == "CC")
5853                             command = cxxCommand;
5854  
5855             //## Make paths
5856             String destPath = dest;
5857             String srcPath  = source;
5858             if (dep.path.size()>0)
5859                             {
5860                 destPath.append("/");
5861                                 destPath.append(dep.path);
5862                 srcPath.append("/");
5863                                 srcPath.append(dep.path);
5864                             }
5865             //## Make sure destination directory exists
5866                         if (!createDirectory(destPath))
5867                             return false;
5868                             
5869             //## Check whether it needs to be done
5870                         String destFullName = destPath;
5871                         destFullName.append("/");
5872                         destFullName.append(dep.name);
5873                         destFullName.append(".o");
5874                         String srcFullName = srcPath;
5875                         srcFullName.append("/");
5876                         srcFullName.append(dep.name);
5877                         srcFullName.append(".");
5878                         srcFullName.append(dep.suffix);
5879             if (!isNewerThan(srcFullName, destFullName))
5880                 {
5881                 //trace("%s skipped", srcFullName.c_str());
5882                 continue;
5883                 }
5885             //## Assemble the command
5886             String cmd = command;
5887             cmd.append(" -c ");
5888             cmd.append(flags);
5889                         cmd.append(" ");
5890             cmd.append(defines);
5891                         cmd.append(" ");
5892             cmd.append(incs);
5893                         cmd.append(" ");
5894                         cmd.append(srcFullName);
5895             cmd.append(" -o ");
5896                         cmd.append(destFullName);
5898             //## Execute the command
5900             String outString, errString;
5901             if (!executeCommand(cmd.c_str(), "", outString, errString))
5902                 {
5903                 error("problem compiling: %s", errString.c_str());
5904                 return false;
5905                 }
5906             }
5907         
5908         return true;
5909         }
5911     virtual bool parse(Element *elem)
5912         {
5913         String s;
5914         if (!parent.getAttribute(elem, "command", s))
5915             return false;
5916         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5917         if (!parent.getAttribute(elem, "cc", s))
5918             return false;
5919         if (s.size()>0) ccCommand = s;
5920         if (!parent.getAttribute(elem, "cxx", s))
5921             return false;
5922         if (s.size()>0) cxxCommand = s;
5923         if (!parent.getAttribute(elem, "destdir", s))
5924             return false;
5925         if (s.size()>0) dest = s;
5927         std::vector<Element *> children = elem->getChildren();
5928         for (unsigned int i=0 ; i<children.size() ; i++)
5929             {
5930             Element *child = children[i];
5931             String tagName = child->getName();
5932             if (tagName == "flags")
5933                 {
5934                 if (!parent.getValue(child, flags))
5935                     return false;
5936                 flags = strip(flags);
5937                 }
5938             else if (tagName == "includes")
5939                 {
5940                 if (!parent.getValue(child, includes))
5941                     return false;
5942                 includes = strip(includes);
5943                 }
5944             else if (tagName == "defines")
5945                 {
5946                 if (!parent.getValue(child, defines))
5947                     return false;
5948                 defines = strip(defines);
5949                 }
5950             else if (tagName == "fileset")
5951                 {
5952                 if (!parseFileSet(child, parent, fileSet))
5953                     return false;
5954                 source = fileSet.getDirectory();
5955                 }
5956             }
5958         return true;
5959         }
5960         
5961 protected:
5963     String ccCommand;
5964     String cxxCommand;
5965     String source;
5966     String dest;
5967     String flags;
5968     String defines;
5969     String includes;
5970     FileSet fileSet;
5971     
5972 };
5976 /**
5977  *
5978  */
5979 class TaskCopy : public Task
5981 public:
5983     typedef enum
5984         {
5985         CP_NONE,
5986         CP_TOFILE,
5987         CP_TODIR
5988         } CopyType;
5990     TaskCopy(MakeBase &par) : Task(par)
5991         {
5992                 type = TASK_COPY; name = "copy";
5993                 cptype = CP_NONE;
5994                 verbose = false;
5995                 haveFileSet = false;
5996                 }
5998     virtual ~TaskCopy()
5999         {}
6001     virtual bool execute()
6002         {
6003         switch (cptype)
6004            {
6005            case CP_TOFILE:
6006                {
6007                if (fileName.size()>0)
6008                    {
6009                    status("          : %s", fileName.c_str());
6010                    String fullSource = parent.resolve(fileName);
6011                    String fullDest = parent.resolve(toFileName);
6012                    //trace("copy %s to file %s", fullSource.c_str(),
6013                                    //                       fullDest.c_str());
6014                                    if (!isRegularFile(fullSource))
6015                                        {
6016                        error("copy : file %s does not exist", fullSource.c_str());
6017                                        return false;
6018                                        }
6019                    if (!isNewerThan(fullSource, fullDest))
6020                        {
6021                        return true;
6022                        }
6023                    if (!copyFile(fullSource, fullDest))
6024                        return false;
6025                    status("          : 1 file copied");
6026                    }
6027                return true;
6028                }
6029            case CP_TODIR:
6030                {
6031                if (haveFileSet)
6032                    {
6033                    if (!listFiles(parent, fileSet))
6034                        return false;
6035                    String fileSetDir = fileSet.getDirectory();
6037                    int nrFiles = 0;
6038                    status("          : %s", fileSetDir.c_str());
6039                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6040                        {
6041                        String fileName = fileSet[i];
6043                        String sourcePath;
6044                        if (fileSetDir.size()>0)
6045                            {
6046                            sourcePath.append(fileSetDir);
6047                            sourcePath.append("/");
6048                            }
6049                        sourcePath.append(fileName);
6050                        String fullSource = parent.resolve(sourcePath);
6051                        
6052                        //Get the immediate parent directory's base name
6053                        String baseFileSetDir = fileSetDir;
6054                        unsigned int pos = baseFileSetDir.find_last_of('/');
6055                        if (pos!=baseFileSetDir.npos &&
6056                                                       pos < baseFileSetDir.size()-1)
6057                            baseFileSetDir =
6058                                                       baseFileSetDir.substr(pos+1,
6059                                                                baseFileSetDir.size());
6060                                            //Now make the new path
6061                        String destPath;
6062                        if (toDirName.size()>0)
6063                            {
6064                            destPath.append(toDirName);
6065                            destPath.append("/");
6066                            }
6067                        if (baseFileSetDir.size()>0)
6068                            {
6069                            destPath.append(baseFileSetDir);
6070                            destPath.append("/");
6071                            }
6072                        destPath.append(fileName);
6073                        String fullDest = parent.resolve(destPath);
6074                        //trace("fileName:%s", fileName.c_str());
6075                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6076                                        //                   fullDest.c_str());
6077                        if (!isNewerThan(fullSource, fullDest))
6078                            {
6079                            //trace("copy skipping %s", fullSource.c_str());
6080                            continue;
6081                            }
6082                        if (!copyFile(fullSource, fullDest))
6083                            return false;
6084                        nrFiles++;
6085                        }
6086                    status("          : %d file(s) copied", nrFiles);
6087                    }
6088                else //file source
6089                    {
6090                    //For file->dir we want only the basename of
6091                    //the source appended to the dest dir
6092                    status("          : %s", fileName.c_str());
6093                    String baseName = fileName;
6094                    unsigned int pos = baseName.find_last_of('/');
6095                    if (pos!=baseName.npos && pos<baseName.size()-1)
6096                        baseName = baseName.substr(pos+1, baseName.size());
6097                    String fullSource = parent.resolve(fileName);
6098                    String destPath;
6099                    if (toDirName.size()>0)
6100                        {
6101                        destPath.append(toDirName);
6102                        destPath.append("/");
6103                        }
6104                    destPath.append(baseName);
6105                    String fullDest = parent.resolve(destPath);
6106                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6107                                    //                       fullDest.c_str());
6108                                    if (!isRegularFile(fullSource))
6109                                        {
6110                        error("copy : file %s does not exist", fullSource.c_str());
6111                                        return false;
6112                                        }
6113                    if (!isNewerThan(fullSource, fullDest))
6114                        {
6115                        return true;
6116                        }
6117                    if (!copyFile(fullSource, fullDest))
6118                        return false;
6119                    status("          : 1 file copied");
6120                    }
6121                return true;
6122                }
6123            }
6124         return true;
6125         }
6128     virtual bool parse(Element *elem)
6129         {
6130         if (!parent.getAttribute(elem, "file", fileName))
6131             return false;
6132         if (!parent.getAttribute(elem, "tofile", toFileName))
6133             return false;
6134         if (toFileName.size() > 0)
6135             cptype = CP_TOFILE;
6136         if (!parent.getAttribute(elem, "todir", toDirName))
6137             return false;
6138         if (toDirName.size() > 0)
6139             cptype = CP_TODIR;
6140         String ret;
6141         if (!parent.getAttribute(elem, "verbose", ret))
6142             return false;
6143         if (ret.size()>0 && !getBool(ret, verbose))
6144             return false;
6145             
6146         haveFileSet = false;
6147         
6148         std::vector<Element *> children = elem->getChildren();
6149         for (unsigned int i=0 ; i<children.size() ; i++)
6150             {
6151             Element *child = children[i];
6152             String tagName = child->getName();
6153             if (tagName == "fileset")
6154                 {
6155                 if (!parseFileSet(child, parent, fileSet))
6156                     {
6157                     error("problem getting fileset");
6158                                         return false;
6159                                         }
6160                                 haveFileSet = true;
6161                 }
6162             }
6164         //Perform validity checks
6165                 if (fileName.size()>0 && fileSet.size()>0)
6166                     {
6167                     error("<copy> can only have one of : file= and <fileset>");
6168                     return false;
6169                     }
6170         if (toFileName.size()>0 && toDirName.size()>0)
6171             {
6172             error("<copy> can only have one of : tofile= or todir=");
6173             return false;
6174             }
6175         if (haveFileSet && toDirName.size()==0)
6176             {
6177             error("a <copy> task with a <fileset> must have : todir=");
6178             return false;
6179             }
6180                 if (cptype == CP_TOFILE && fileName.size()==0)
6181                     {
6182                     error("<copy> tofile= must be associated with : file=");
6183                     return false;
6184                     }
6185                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6186                     {
6187                     error("<copy> todir= must be associated with : file= or <fileset>");
6188                     return false;
6189                     }
6191         return true;
6192         }
6193         
6194 private:
6196     int cptype;
6197     String fileName;
6198     FileSet fileSet;
6199     String toFileName;
6200     String toDirName;
6201     bool verbose;
6202     bool haveFileSet;
6203 };
6206 /**
6207  *
6208  */
6209 class TaskDelete : public Task
6211 public:
6213     typedef enum
6214         {
6215         DEL_FILE,
6216         DEL_DIR,
6217         DEL_FILESET
6218         } DeleteType;
6220     TaskDelete(MakeBase &par) : Task(par)
6221         { 
6222                   type        = TASK_DELETE;
6223                   name        = "delete";
6224                   delType     = DEL_FILE;
6225           verbose     = false;
6226           quiet       = false;
6227           failOnError = true;
6228                 }
6230     virtual ~TaskDelete()
6231         {}
6233     virtual bool execute()
6234         {
6235         struct stat finfo;
6236         switch (delType)
6237             {
6238             case DEL_FILE:
6239                 {
6240                 status("          : %s", fileName.c_str());
6241                 String fullName = parent.resolve(fileName);
6242                 char *fname = (char *)fullName.c_str();
6243                 //does not exist
6244                 if (stat(fname, &finfo)<0)
6245                     return true;
6246                 //exists but is not a regular file
6247                 if (!S_ISREG(finfo.st_mode))
6248                     {
6249                     error("<delete> failed. '%s' exists and is not a regular file",
6250                           fname);
6251                     return false;
6252                     }
6253                 if (remove(fname)<0)
6254                     {
6255                     error("<delete> failed: %s", strerror(errno));
6256                     return false;
6257                     }
6258                 return true;
6259                 }
6260             case DEL_DIR:
6261                 {
6262                 status("          : %s", dirName.c_str());
6263                 String fullDir = parent.resolve(dirName);
6264                 if (!removeDirectory(fullDir))
6265                     return false;
6266                 return true;
6267                 }
6268             }
6269         return true;
6270         }
6272     virtual bool parse(Element *elem)
6273         {
6274         if (!parent.getAttribute(elem, "file", fileName))
6275             return false;
6276         if (fileName.size() > 0)
6277             delType = DEL_FILE;
6278         if (!parent.getAttribute(elem, "dir", dirName))
6279             return false;
6280         if (dirName.size() > 0)
6281             delType = DEL_DIR;
6282         if (fileName.size()>0 && dirName.size()>0)
6283             {
6284             error("<delete> can only have one attribute of file= or dir=");
6285             return false;
6286             }
6287         String ret;
6288         if (!parent.getAttribute(elem, "verbose", ret))
6289             return false;
6290         if (ret.size()>0 && !getBool(ret, verbose))
6291             return false;
6292         if (!parent.getAttribute(elem, "quiet", ret))
6293             return false;
6294         if (ret.size()>0 && !getBool(ret, quiet))
6295             return false;
6296         if (!parent.getAttribute(elem, "failonerror", ret))
6297             return false;
6298         if (ret.size()>0 && !getBool(ret, failOnError))
6299             return false;
6300         return true;
6301         }
6303 private:
6305     int delType;
6306     String dirName;
6307     String fileName;
6308     bool verbose;
6309     bool quiet;
6310     bool failOnError;
6311 };
6314 /**
6315  *
6316  */
6317 class TaskJar : public Task
6319 public:
6321     TaskJar(MakeBase &par) : Task(par)
6322         { type = TASK_JAR; name = "jar"; }
6324     virtual ~TaskJar()
6325         {}
6327     virtual bool execute()
6328         {
6329         return true;
6330         }
6332     virtual bool parse(Element *elem)
6333         {
6334         return true;
6335         }
6336 };
6339 /**
6340  *
6341  */
6342 class TaskJavac : public Task
6344 public:
6346     TaskJavac(MakeBase &par) : Task(par)
6347         { type = TASK_JAVAC; name = "javac"; }
6349     virtual ~TaskJavac()
6350         {}
6352     virtual bool execute()
6353         {
6354         return true;
6355         }
6357     virtual bool parse(Element *elem)
6358         {
6359         return true;
6360         }
6361 };
6364 /**
6365  *
6366  */
6367 class TaskLink : public Task
6369 public:
6371     TaskLink(MakeBase &par) : Task(par)
6372         {
6373                 type = TASK_LINK; name = "link";
6374                 command = "g++";
6375                 }
6377     virtual ~TaskLink()
6378         {}
6380     virtual bool execute()
6381         {
6382         if (!listFiles(parent, fileSet))
6383             return false;
6384         String fileSetDir = fileSet.getDirectory();
6385         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6386         bool doit = false;
6387         String fullTarget = parent.resolve(fileName);
6388         String cmd = command;
6389         cmd.append(" -o ");
6390         cmd.append(fullTarget);
6391         cmd.append(" ");
6392         cmd.append(flags);
6393         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6394             {
6395             cmd.append(" ");
6396             String obj;
6397             if (fileSetDir.size()>0)
6398                             {
6399                                 obj.append(fileSetDir);
6400                 obj.append("/");
6401                 }
6402             obj.append(fileSet[i]);
6403             String fullObj = parent.resolve(obj);
6404             cmd.append(fullObj);
6405             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6406             //          fullObj.c_str());
6407             if (isNewerThan(fullObj, fullTarget))
6408                 doit = true;
6409             }
6410         cmd.append(" ");
6411         cmd.append(libs);
6412         if (!doit)
6413             {
6414             //trace("link not needed");
6415             return true;
6416             }
6417         //trace("LINK cmd:%s", cmd.c_str());
6420         String outString, errString;
6421         if (!executeCommand(cmd.c_str(), "", outString, errString))
6422             {
6423             error("LINK problem: %s", errString.c_str());
6424             return false;
6425             }
6426         return true;
6427         }
6429     virtual bool parse(Element *elem)
6430         {
6431         if (!parent.getAttribute(elem, "command", command))
6432             return false;
6433         if (!parent.getAttribute(elem, "out", fileName))
6434             return false;
6435             
6436         std::vector<Element *> children = elem->getChildren();
6437         for (unsigned int i=0 ; i<children.size() ; i++)
6438             {
6439             Element *child = children[i];
6440             String tagName = child->getName();
6441             if (tagName == "fileset")
6442                 {
6443                 if (!parseFileSet(child, parent, fileSet))
6444                     return false;
6445                 }
6446             else if (tagName == "flags")
6447                 {
6448                 if (!parent.getValue(child, flags))
6449                     return false;
6450                 flags = strip(flags);
6451                 }
6452             else if (tagName == "libs")
6453                 {
6454                 if (!parent.getValue(child, libs))
6455                     return false;
6456                 libs = strip(libs);
6457                 }
6458             }
6459         return true;
6460         }
6462 private:
6464     String command;
6465     String fileName;
6466     String flags;
6467     String libs;
6468     FileSet fileSet;
6470 };
6474 /**
6475  * Create a named directory
6476  */
6477 class TaskMakeFile : public Task
6479 public:
6481     TaskMakeFile(MakeBase &par) : Task(par)
6482         { type = TASK_MAKEFILE; name = "makefile"; }
6484     virtual ~TaskMakeFile()
6485         {}
6487     virtual bool execute()
6488         {
6489         status("          : %s", fileName.c_str());
6490         String fullName = parent.resolve(fileName);
6491         if (!isNewerThan(parent.getURI().getPath(), fullName))
6492             {
6493             //trace("skipped <makefile>");
6494             return true;
6495             }
6496         //trace("fullName:%s", fullName.c_str());
6497         FILE *f = fopen(fullName.c_str(), "w");
6498         if (!f)
6499             {
6500             error("<makefile> could not open %s for writing : %s",
6501                 fullName.c_str(), strerror(errno));
6502             return false;
6503             }
6504         for (unsigned int i=0 ; i<text.size() ; i++)
6505             fputc(text[i], f);
6506         fclose(f);
6507         return true;
6508         }
6510     virtual bool parse(Element *elem)
6511         {
6512         if (!parent.getAttribute(elem, "file", fileName))
6513             return false;
6514         if (fileName.size() == 0)
6515             {
6516             error("<makefile> requires 'file=\"filename\"' attribute");
6517             return false;
6518             }
6519         if (!parent.getValue(elem, text))
6520             return false;
6521         text = leftJustify(text);
6522         //trace("dirname:%s", dirName.c_str());
6523         return true;
6524         }
6526 private:
6528     String fileName;
6529     String text;
6530 };
6534 /**
6535  * Create a named directory
6536  */
6537 class TaskMkDir : public Task
6539 public:
6541     TaskMkDir(MakeBase &par) : Task(par)
6542         { type = TASK_MKDIR; name = "mkdir"; }
6544     virtual ~TaskMkDir()
6545         {}
6547     virtual bool execute()
6548         {
6549         status("          : %s", dirName.c_str());
6550         String fullDir = parent.resolve(dirName);
6551         //trace("fullDir:%s", fullDir.c_str());
6552         if (!createDirectory(fullDir))
6553             return false;
6554         return true;
6555         }
6557     virtual bool parse(Element *elem)
6558         {
6559         if (!parent.getAttribute(elem, "dir", dirName))
6560             return false;
6561         if (dirName.size() == 0)
6562             {
6563             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6564             return false;
6565             }
6566         //trace("dirname:%s", dirName.c_str());
6567         return true;
6568         }
6570 private:
6572     String dirName;
6573 };
6577 /**
6578  * Create a named directory
6579  */
6580 class TaskMsgFmt: public Task
6582 public:
6584     TaskMsgFmt(MakeBase &par) : Task(par)
6585          {
6586                  type = TASK_MSGFMT;
6587                  name = "msgfmt";
6588                  command = "msgfmt";
6589                  }
6591     virtual ~TaskMsgFmt()
6592         {}
6594     virtual bool execute()
6595         {
6596         if (!listFiles(parent, fileSet))
6597             return false;
6598         String fileSetDir = fileSet.getDirectory();
6600         //trace("msgfmt: %d", fileSet.size());
6601         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6602             {
6603             String fileName = fileSet[i];
6604             if (getSuffix(fileName) != "po")
6605                 continue;
6606             String sourcePath;
6607                         if (fileSetDir.size()>0)
6608                             {
6609                             sourcePath.append(fileSetDir);
6610                 sourcePath.append("/");
6611                 }
6612             sourcePath.append(fileName);
6613             String fullSource = parent.resolve(sourcePath);
6615             String destPath;
6616                         if (toDirName.size()>0)
6617                             {
6618                             destPath.append(toDirName);
6619                 destPath.append("/");
6620                 }
6621             destPath.append(fileName);
6622             destPath[destPath.size()-2] = 'm';
6623             String fullDest = parent.resolve(destPath);
6625             if (!isNewerThan(fullSource, fullDest))
6626                 {
6627                 //trace("skip %s", fullSource.c_str());
6628                 continue;
6629                 }
6630                 
6631             String cmd = command;
6632             cmd.append(" ");
6633             cmd.append(fullSource);
6634             cmd.append(" -o ");
6635             cmd.append(fullDest);
6636             
6637             int pos = fullDest.find_last_of('/');
6638             if (pos>0)
6639                 {
6640                 String fullDestPath = fullDest.substr(0, pos);
6641                 if (!createDirectory(fullDestPath))
6642                     return false;
6643                 }
6647             String outString, errString;
6648             if (!executeCommand(cmd.c_str(), "", outString, errString))
6649                 {
6650                 error("<msgfmt> problem: %s", errString.c_str());
6651                 return false;
6652                 }
6653             }
6655         return true;
6656         }
6658     virtual bool parse(Element *elem)
6659         {
6660         if (!parent.getAttribute(elem, "todir", toDirName))
6661             return false;
6662             
6663         std::vector<Element *> children = elem->getChildren();
6664         for (unsigned int i=0 ; i<children.size() ; i++)
6665             {
6666             Element *child = children[i];
6667             String tagName = child->getName();
6668             if (tagName == "fileset")
6669                 {
6670                 if (!parseFileSet(child, parent, fileSet))
6671                     return false;
6672                 }
6673             }
6674         return true;
6675         }
6677 private:
6679     String command;
6680     String toDirName;
6681     FileSet fileSet;
6683 };
6689 /**
6690  *  Process an archive to allow random access
6691  */
6692 class TaskRanlib : public Task
6694 public:
6696     TaskRanlib(MakeBase &par) : Task(par)
6697         { type = TASK_RANLIB; name = "ranlib"; }
6699     virtual ~TaskRanlib()
6700         {}
6702     virtual bool execute()
6703         {
6704         String fullName = parent.resolve(fileName);
6705         //trace("fullDir:%s", fullDir.c_str());
6706         String cmd = "ranlib ";
6707         cmd.append(fullName);
6708         String outbuf, errbuf;
6709         if (!executeCommand(cmd, "", outbuf, errbuf))
6710             return false;
6711         return true;
6712         }
6714     virtual bool parse(Element *elem)
6715         {
6716         if (!parent.getAttribute(elem, "file", fileName))
6717             return false;
6718         if (fileName.size() == 0)
6719             {
6720             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6721             return false;
6722             }
6723         return true;
6724         }
6726 private:
6728     String fileName;
6729 };
6733 /**
6734  * Run the "ar" command to archive .o's into a .a
6735  */
6736 class TaskRC : public Task
6738 public:
6740     TaskRC(MakeBase &par) : Task(par)
6741         {
6742                 type = TASK_RC; name = "rc";
6743                 command = "windres -o";
6744                 }
6746     virtual ~TaskRC()
6747         {}
6749     virtual bool execute()
6750         {
6751         String fullFile = parent.resolve(fileName);
6752         String fullOut  = parent.resolve(outName);
6753         if (!isNewerThan(fullFile, fullOut))
6754             return true;
6755         String cmd = command;
6756         cmd.append(" ");
6757         cmd.append(fullOut);
6758         cmd.append(" ");
6759         cmd.append(flags);
6760         cmd.append(" ");
6761         cmd.append(fullFile);
6763         String outString, errString;
6764         if (!executeCommand(cmd.c_str(), "", outString, errString))
6765             {
6766             error("RC problem: %s", errString.c_str());
6767             return false;
6768             }
6769         return true;
6770         }
6772     virtual bool parse(Element *elem)
6773         {
6774         if (!parent.getAttribute(elem, "command", command))
6775             return false;
6776         if (!parent.getAttribute(elem, "file", fileName))
6777             return false;
6778         if (!parent.getAttribute(elem, "out", outName))
6779             return false;
6780         std::vector<Element *> children = elem->getChildren();
6781         for (unsigned int i=0 ; i<children.size() ; i++)
6782             {
6783             Element *child = children[i];
6784             String tagName = child->getName();
6785             if (tagName == "flags")
6786                 {
6787                 if (!parent.getValue(child, flags))
6788                     return false;
6789                 }
6790             }
6791         return true;
6792         }
6794 private:
6796     String command;
6797     String flags;
6798     String fileName;
6799     String outName;
6801 };
6805 /**
6806  * Strip an executable
6807  */
6808 class TaskStrip : public Task
6810 public:
6812     TaskStrip(MakeBase &par) : Task(par)
6813         { type = TASK_STRIP; name = "strip"; }
6815     virtual ~TaskStrip()
6816         {}
6818     virtual bool execute()
6819         {
6820         String fullName = parent.resolve(fileName);
6821         //trace("fullDir:%s", fullDir.c_str());
6822         String cmd = "strip ";
6823         cmd.append(fullName);
6825         String outbuf, errbuf;
6826         if (!executeCommand(cmd, "", outbuf, errbuf))
6827             return false;
6828         return true;
6829         }
6831     virtual bool parse(Element *elem)
6832         {
6833         if (!parent.getAttribute(elem, "file", fileName))
6834             return false;
6835         if (fileName.size() == 0)
6836             {
6837             error("<strip> requires 'file=\"fileNname\"' attribute");
6838             return false;
6839             }
6840         return true;
6841         }
6843 private:
6845     String fileName;
6846 };
6849 /**
6850  *
6851  */
6852 class TaskTstamp : public Task
6854 public:
6856     TaskTstamp(MakeBase &par) : Task(par)
6857         { type = TASK_TSTAMP; name = "tstamp"; }
6859     virtual ~TaskTstamp()
6860         {}
6862     virtual bool execute()
6863         {
6864         return true;
6865         }
6867     virtual bool parse(Element *elem)
6868         {
6869         //trace("tstamp parse");
6870         return true;
6871         }
6872 };
6876 /**
6877  *
6878  */
6879 Task *Task::createTask(Element *elem)
6881     String tagName = elem->getName();
6882     //trace("task:%s", tagName.c_str());
6883     Task *task = NULL;
6884     if (tagName == "ar")
6885         task = new TaskAr(parent);
6886     else if (tagName == "cc")
6887         task = new TaskCC(parent);
6888     else if (tagName == "copy")
6889         task = new TaskCopy(parent);
6890     else if (tagName == "delete")
6891         task = new TaskDelete(parent);
6892     else if (tagName == "jar")
6893         task = new TaskJar(parent);
6894     else if (tagName == "javac")
6895         task = new TaskJavac(parent);
6896     else if (tagName == "link")
6897         task = new TaskLink(parent);
6898     else if (tagName == "makefile")
6899         task = new TaskMakeFile(parent);
6900     else if (tagName == "mkdir")
6901         task = new TaskMkDir(parent);
6902     else if (tagName == "msgfmt")
6903         task = new TaskMsgFmt(parent);
6904     else if (tagName == "ranlib")
6905         task = new TaskRanlib(parent);
6906     else if (tagName == "rc")
6907         task = new TaskRC(parent);
6908     else if (tagName == "strip")
6909         task = new TaskStrip(parent);
6910     else if (tagName == "tstamp")
6911         task = new TaskTstamp(parent);
6912     else
6913         {
6914         error("Unknown task '%s'", tagName.c_str());
6915         return NULL;
6916         }
6918     if (!task->parse(elem))
6919         {
6920         delete task;
6921         return NULL;
6922         }
6923     return task;
6928 //########################################################################
6929 //# T A R G E T
6930 //########################################################################
6932 /**
6933  *
6934  */
6935 class Target : public MakeBase
6938 public:
6940     /**
6941      *
6942      */
6943     Target(Make &par) : parent(par)
6944         { init(); }
6946     /**
6947      *
6948      */
6949     Target(const Target &other) : parent(other.parent)
6950         { init(); assign(other); }
6952     /**
6953      *
6954      */
6955     Target &operator=(const Target &other)
6956         { init(); assign(other); return *this; }
6958     /**
6959      *
6960      */
6961     virtual ~Target()
6962         { cleanup() ; }
6965     /**
6966      *
6967      */
6968     virtual Make &getParent()
6969         { return parent; }
6971     /**
6972      *
6973      */
6974     virtual String getName()
6975         { return name; }
6977     /**
6978      *
6979      */
6980     virtual void setName(const String &val)
6981         { name = val; }
6983     /**
6984      *
6985      */
6986     virtual String getDescription()
6987         { return description; }
6989     /**
6990      *
6991      */
6992     virtual void setDescription(const String &val)
6993         { description = val; }
6995     /**
6996      *
6997      */
6998     virtual void addDependency(const String &val)
6999         { deps.push_back(val); }
7001     /**
7002      *
7003      */
7004     virtual void parseDependencies(const String &val)
7005         { deps = tokenize(val, ", "); }
7007     /**
7008      *
7009      */
7010     virtual std::vector<String> &getDependencies()
7011         { return deps; }
7013     /**
7014      *
7015      */
7016     virtual String getIf()
7017         { return ifVar; }
7019     /**
7020      *
7021      */
7022     virtual void setIf(const String &val)
7023         { ifVar = val; }
7025     /**
7026      *
7027      */
7028     virtual String getUnless()
7029         { return unlessVar; }
7031     /**
7032      *
7033      */
7034     virtual void setUnless(const String &val)
7035         { unlessVar = val; }
7037     /**
7038      *
7039      */
7040     virtual void addTask(Task *val)
7041         { tasks.push_back(val); }
7043     /**
7044      *
7045      */
7046     virtual std::vector<Task *> &getTasks()
7047         { return tasks; }
7049 private:
7051     void init()
7052         {
7053         }
7055     void cleanup()
7056         {
7057         tasks.clear();
7058         }
7060     void assign(const Target &other)
7061         {
7062         //parent      = other.parent;
7063         name        = other.name;
7064         description = other.description;
7065         ifVar       = other.ifVar;
7066         unlessVar   = other.unlessVar;
7067         deps        = other.deps;
7068         tasks       = other.tasks;
7069         }
7071     Make &parent;
7073     String name;
7075     String description;
7077     String ifVar;
7079     String unlessVar;
7081     std::vector<String> deps;
7083     std::vector<Task *> tasks;
7085 };
7094 //########################################################################
7095 //# M A K E
7096 //########################################################################
7099 /**
7100  *
7101  */
7102 class Make : public MakeBase
7105 public:
7107     /**
7108      *
7109      */
7110     Make()
7111         { init(); }
7113     /**
7114      *
7115      */
7116     Make(const Make &other)
7117         { assign(other); }
7119     /**
7120      *
7121      */
7122     Make &operator=(const Make &other)
7123         { assign(other); return *this; }
7125     /**
7126      *
7127      */
7128     virtual ~Make()
7129         { cleanup(); }
7131     /**
7132      *
7133      */
7134     virtual std::map<String, Target> &getTargets()
7135         { return targets; }
7138     /**
7139      *
7140      */
7141     bool run();
7143     /**
7144      *
7145      */
7146     bool run(const String &target);
7150 private:
7152     /**
7153      *
7154      */
7155     void init();
7157     /**
7158      *
7159      */
7160     void cleanup();
7162     /**
7163      *
7164      */
7165     void assign(const Make &other);
7167     /**
7168      *
7169      */
7170     bool executeTask(Task &task);
7173     /**
7174      *
7175      */
7176     bool executeTarget(Target &target,
7177                  std::set<String> &targetsCompleted);
7180     /**
7181      *
7182      */
7183     bool execute();
7185     /**
7186      *
7187      */
7188     bool checkTargetDependencies(Target &prop,
7189                     std::vector<String> &depList);
7191     /**
7192      *
7193      */
7194     bool parsePropertyFile(const String &fileName,
7195                                const String &prefix);
7197     /**
7198      *
7199      */
7200     bool parseProperty(Element *elem);
7202     /**
7203      *
7204      */
7205     bool parseTask(Task &task, Element *elem);
7207     /**
7208      *
7209      */
7210     bool parseFile();
7212     /**
7213      *
7214      */
7215     std::vector<String> glob(const String &pattern);
7218     //###############
7219     //# Fields
7220     //###############
7222     String projectName;
7224     String currentTarget;
7226     String defaultTarget;
7228     String specifiedTarget;
7230     String baseDir;
7232     String description;
7233     
7234     String envAlias;
7236     //std::vector<Property> properties;
7237     
7238     std::map<String, Target> targets;
7240     std::vector<Task *> allTasks;
7243 };
7246 //########################################################################
7247 //# C L A S S  M A I N T E N A N C E
7248 //########################################################################
7250 /**
7251  *
7252  */
7253 void Make::init()
7255     uri             = "build.xml";
7256     projectName     = "";
7257     currentTarget   = "";
7258     defaultTarget   = "";
7259     specifiedTarget = "";
7260     baseDir         = "";
7261     description     = "";
7262     envAlias        = "";
7263     properties.clear();
7264     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7265         delete allTasks[i];
7266     allTasks.clear();
7271 /**
7272  *
7273  */
7274 void Make::cleanup()
7276     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7277         delete allTasks[i];
7278     allTasks.clear();
7283 /**
7284  *
7285  */
7286 void Make::assign(const Make &other)
7288     uri              = other.uri;
7289     projectName      = other.projectName;
7290     currentTarget    = other.currentTarget;
7291     defaultTarget    = other.defaultTarget;
7292     specifiedTarget  = other.specifiedTarget;
7293     baseDir          = other.baseDir;
7294     description      = other.description;
7295     properties       = other.properties;
7300 //########################################################################
7301 //# U T I L I T Y    T A S K S
7302 //########################################################################
7304 /**
7305  *  Perform a file globbing
7306  */
7307 std::vector<String> Make::glob(const String &pattern)
7309     std::vector<String> res;
7310     return res;
7314 //########################################################################
7315 //# P U B L I C    A P I
7316 //########################################################################
7320 /**
7321  *
7322  */
7323 bool Make::executeTarget(Target &target,
7324              std::set<String> &targetsCompleted)
7327     String name = target.getName();
7329     //First get any dependencies for this target
7330     std::vector<String> deps = target.getDependencies();
7331     for (unsigned int i=0 ; i<deps.size() ; i++)
7332         {
7333         String dep = deps[i];
7334         //Did we do it already?  Skip
7335         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7336             continue;
7337             
7338         std::map<String, Target> &tgts =
7339                target.getParent().getTargets();
7340         std::map<String, Target>::iterator iter =
7341                tgts.find(dep);
7342         if (iter == tgts.end())
7343             {
7344             error("Target '%s' dependency '%s' not found",
7345                       name.c_str(),  dep.c_str());
7346             return false;
7347             }
7348         Target depTarget = iter->second;
7349         if (!executeTarget(depTarget, targetsCompleted))
7350             {
7351             return false;
7352             }
7353         }
7355     status("## Target : %s", name.c_str());
7357     //Now let's do the tasks
7358     std::vector<Task *> &tasks = target.getTasks();
7359     for (unsigned int i=0 ; i<tasks.size() ; i++)
7360         {
7361         Task *task = tasks[i];
7362         status("---- task : %s", task->getName().c_str());
7363         if (!task->execute())
7364             {
7365             return false;
7366             }
7367         }
7368         
7369     targetsCompleted.insert(name);
7370     
7371     return true;
7376 /**
7377  *  Main execute() method.  Start here and work
7378  *  up the dependency tree 
7379  */
7380 bool Make::execute()
7382     status("######## EXECUTE");
7384     //Determine initial target
7385     if (specifiedTarget.size()>0)
7386         {
7387         currentTarget = specifiedTarget;
7388         }
7389     else if (defaultTarget.size()>0)
7390         {
7391         currentTarget = defaultTarget;
7392         }
7393     else
7394         {
7395         error("execute: no specified or default target requested");
7396         return false;
7397         }
7399     std::map<String, Target>::iterator iter =
7400                    targets.find(currentTarget);
7401     if (iter == targets.end())
7402         {
7403         error("Initial target '%s' not found",
7404                          currentTarget.c_str());
7405         return false;
7406         }
7407         
7408     //Now run
7409     Target target = iter->second;
7410     std::set<String> targetsCompleted;
7411     if (!executeTarget(target, targetsCompleted))
7412         {
7413         return false;
7414         }
7416     status("######## EXECUTE COMPLETE");
7417     return true;
7423 /**
7424  *
7425  */
7426 bool Make::checkTargetDependencies(Target &target, 
7427                             std::vector<String> &depList)
7429     String tgtName = target.getName().c_str();
7430     depList.push_back(tgtName);
7432     std::vector<String> deps = target.getDependencies();
7433     for (unsigned int i=0 ; i<deps.size() ; i++)
7434         {
7435         String dep = deps[i];
7436         //First thing entered was the starting Target
7437         if (dep == depList[0])
7438             {
7439             error("Circular dependency '%s' found at '%s'",
7440                       dep.c_str(), tgtName.c_str());
7441             std::vector<String>::iterator diter;
7442             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7443                 {
7444                 error("  %s", diter->c_str());
7445                 }
7446             return false;
7447             }
7449         std::map<String, Target> &tgts =
7450                   target.getParent().getTargets();
7451         std::map<String, Target>::iterator titer = tgts.find(dep);
7452         if (titer == tgts.end())
7453             {
7454             error("Target '%s' dependency '%s' not found",
7455                       tgtName.c_str(), dep.c_str());
7456             return false;
7457             }
7458         if (!checkTargetDependencies(titer->second, depList))
7459             {
7460             return false;
7461             }
7462         }
7463     return true;
7470 static int getword(int pos, const String &inbuf, String &result)
7472     int p = pos;
7473     int len = (int)inbuf.size();
7474     String val;
7475     while (p < len)
7476         {
7477         char ch = inbuf[p];
7478         if (!isalnum(ch) && ch!='.' && ch!='_')
7479             break;
7480         val.push_back(ch);
7481         p++;
7482         }
7483     result = val;
7484     return p;
7490 /**
7491  *
7492  */
7493 bool Make::parsePropertyFile(const String &fileName,
7494                              const String &prefix)
7496     FILE *f = fopen(fileName.c_str(), "r");
7497     if (!f)
7498         {
7499         error("could not open property file %s", fileName.c_str());
7500         return false;
7501         }
7502     int linenr = 0;
7503     while (!feof(f))
7504         {
7505         char buf[256];
7506         if (!fgets(buf, 255, f))
7507             break;
7508         linenr++;
7509         String s = buf;
7510         s = trim(s);
7511         int len = s.size();
7512         if (len == 0)
7513             continue;
7514         if (s[0] == '#')
7515             continue;
7516         String key;
7517         String val;
7518         int p = 0;
7519         int p2 = getword(p, s, key);
7520         if (p2 <= p)
7521             {
7522             error("property file %s, line %d: expected keyword",
7523                                 fileName.c_str(), linenr);
7524                         return false;
7525                         }
7526                 if (prefix.size() > 0)
7527                     {
7528                     key.insert(0, prefix);
7529                     }
7531         //skip whitespace
7532                 for (p=p2 ; p<len ; p++)
7533                     if (!isspace(s[p]))
7534                         break;
7536         if (p>=len || s[p]!='=')
7537             {
7538             error("property file %s, line %d: expected '='",
7539                                 fileName.c_str(), linenr);
7540             return false;
7541             }
7542         p++;
7544         //skip whitespace
7545                 for ( ; p<len ; p++)
7546                     if (!isspace(s[p]))
7547                         break;
7549         /* This way expects a word after the =
7550                 p2 = getword(p, s, val);
7551         if (p2 <= p)
7552             {
7553             error("property file %s, line %d: expected value",
7554                                 fileName.c_str(), linenr);
7555                         return false;
7556                         }
7557                 */
7558         // This way gets the rest of the line after the =
7559                 if (p>=len)
7560             {
7561             error("property file %s, line %d: expected value",
7562                                 fileName.c_str(), linenr);
7563                         return false;
7564                         }
7565         val = s.substr(p);
7566                 if (key.size()==0 || val.size()==0)
7567                     continue;
7569         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7570             properties[key] = val;
7571         }
7572     fclose(f);
7573     return true;
7579 /**
7580  *
7581  */
7582 bool Make::parseProperty(Element *elem)
7584     std::vector<Attribute> &attrs = elem->getAttributes();
7585     for (unsigned int i=0 ; i<attrs.size() ; i++)
7586         {
7587         String attrName = attrs[i].getName();
7588         String attrVal  = attrs[i].getValue();
7590         if (attrName == "name")
7591             {
7592             String val;
7593                         if (!getAttribute(elem, "value", val))
7594                             return false;
7595             if (val.size() > 0)
7596                 {
7597                 properties[attrVal] = val;
7598                 continue;
7599                 }
7600             if (!getAttribute(elem, "location", val))
7601                 return false;
7602             if (val.size() > 0)
7603                 {
7604                 //TODO:  process a path relative to build.xml
7605                 properties[attrVal] = val;
7606                 continue;
7607                 }
7608             }
7609         else if (attrName == "file")
7610             {
7611             String prefix;
7612                         if (!getAttribute(elem, "prefix", prefix))
7613                             return false;
7614             if (prefix.size() > 0)
7615                 {
7616                 if (prefix[prefix.size()-1] != '.')
7617                     prefix.push_back('.');
7618                 }
7619             if (!parsePropertyFile(attrName, prefix))
7620                 return false;
7621             }
7622         else if (attrName == "environment")
7623             {
7624             if (envAlias.size() > 0)
7625                 {
7626                 error("environment property can only be set once");
7627                 return false;
7628                 }
7629             envAlias = attrVal;
7630             }
7631         }
7633     return true;
7639 /**
7640  *
7641  */
7642 bool Make::parseFile()
7644     status("######## PARSE");
7646     Parser parser;
7647     Element *root = parser.parseFile(uri.getNativePath());
7648     if (!root)
7649         {
7650         error("Could not open %s for reading",
7651                       uri.getNativePath().c_str());
7652         return false;
7653         }
7655     if (root->getChildren().size()==0 ||
7656         root->getChildren()[0]->getName()!="project")
7657         {
7658         error("Main xml element should be <project>");
7659         delete root;
7660         return false;
7661         }
7663     //########## Project attributes
7664     Element *project = root->getChildren()[0];
7665     String s = project->getAttribute("name");
7666     if (s.size() > 0)
7667         projectName = s;
7668     s = project->getAttribute("default");
7669     if (s.size() > 0)
7670         defaultTarget = s;
7671     s = project->getAttribute("basedir");
7672     if (s.size() > 0)
7673         baseDir = s;
7675     //######### PARSE MEMBERS
7676     std::vector<Element *> children = project->getChildren();
7677     for (unsigned int i=0 ; i<children.size() ; i++)
7678         {
7679         Element *elem = children[i];
7680         String tagName = elem->getName();
7682         //########## DESCRIPTION
7683         if (tagName == "description")
7684             {
7685             description = parser.trim(elem->getValue());
7686             }
7688         //######### PROPERTY
7689         else if (tagName == "property")
7690             {
7691             if (!parseProperty(elem))
7692                 return false;
7693             }
7695         //######### TARGET
7696         else if (tagName == "target")
7697             {
7698             String tname   = elem->getAttribute("name");
7699             String tdesc   = elem->getAttribute("description");
7700             String tdeps   = elem->getAttribute("depends");
7701             String tif     = elem->getAttribute("if");
7702             String tunless = elem->getAttribute("unless");
7703             Target target(*this);
7704             target.setName(tname);
7705             target.setDescription(tdesc);
7706             target.parseDependencies(tdeps);
7707             target.setIf(tif);
7708             target.setUnless(tunless);
7709             std::vector<Element *> telems = elem->getChildren();
7710             for (unsigned int i=0 ; i<telems.size() ; i++)
7711                 {
7712                 Element *telem = telems[i];
7713                 Task breeder(*this);
7714                 Task *task = breeder.createTask(telem);
7715                 if (!task)
7716                     return false;
7717                 allTasks.push_back(task);
7718                 target.addTask(task);
7719                 }
7721             //Check name
7722             if (tname.size() == 0)
7723                 {
7724                 error("no name for target");
7725                 return false;
7726                 }
7727             //Check for duplicate name
7728             if (targets.find(tname) != targets.end())
7729                 {
7730                 error("target '%s' already defined", tname.c_str());
7731                 return false;
7732                 }
7733             //more work than targets[tname]=target, but avoids default allocator
7734             targets.insert(std::make_pair<String, Target>(tname, target));
7735             }
7737         }
7739     std::map<String, Target>::iterator iter;
7740     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
7741         {
7742         Target tgt = iter->second;
7743         std::vector<String> depList;
7744         if (!checkTargetDependencies(tgt, depList))
7745             {
7746             return false;
7747             }
7748         }
7751     delete root;
7752     status("######## PARSE COMPLETE");
7753     return true;
7757 /**
7758  *
7759  */
7760 bool Make::run()
7762     if (!parseFile())
7763         return false;
7764     if (!execute())
7765         return false;
7766     return true;
7770 /**
7771  *
7772  */
7773 bool Make::run(const String &target)
7775     status("##################################");
7776     status("#   BuildTool");
7777     status("#   version 0.3");
7778     status("#   16 Nov 06");
7779     status("##################################");
7780     specifiedTarget = target;
7781     if (!run())
7782         return false;
7783     status("##################################");
7784     status("#   BuildTool Completed");
7785     status("##################################");
7786     return true;
7795 }// namespace buildtool
7796 //########################################################################
7797 //# M A I N
7798 //########################################################################
7800 /**
7801  *  Format an error message in printf() style
7802  */
7803 static void error(char *fmt, ...)
7805     va_list ap;
7806     va_start(ap, fmt);
7807     fprintf(stderr, "BuildTool error: ");
7808     vfprintf(stderr, fmt, ap);
7809     fprintf(stderr, "\n");
7810     va_end(ap);
7814 /**
7815  * Compare a buffer with a key, for the length of the key
7816  */
7817 static bool sequ(const buildtool::String &buf, char *key)
7819     for (int i=0 ; key[i] ; i++)
7820         {
7821         if (key[i] != buf[i])
7822             return false;
7823         }        
7824     return true;
7827 /**
7828  * Parse the command-line args, get our options,
7829  * and run this thing
7830  */   
7831 static bool parseOptions(int argc, char **argv)
7833     if (argc < 1)
7834         {
7835         error("Cannot parse arguments");
7836         return false;
7837         }
7839     buildtool::String buildFile;
7840     buildtool::String target;
7842     //char *progName = argv[0];
7843     for (int i=1 ; i<argc ; i++)
7844         {
7845         buildtool::String arg = argv[i];
7846         if (sequ(arg, "--"))
7847             {
7848             if (sequ(arg, "--file=") && arg.size()>7)
7849                 {
7850                 buildFile = arg.substr(7, arg.size()-7);
7851                 }
7852             else
7853                 {
7854                 error("Unknown option:%s", arg.c_str());
7855                 return false;
7856                 }
7857             }
7858         else if (sequ(arg, "-"))
7859             {
7860             for (unsigned int p=1 ; p<arg.size() ; p++)
7861                 {
7862                 int ch = arg[p];
7863                 if (0)//put options here
7864                     {
7865                     }
7866                 else
7867                     {
7868                     error("Unknown option '%c'", ch);
7869                     return false;
7870                     }
7871                 }
7872             }
7873         else
7874             {
7875             target = arg;
7876             }
7877         }
7879     //We have the options.  Now execute them
7880     buildtool::Make make;
7881     if (buildFile.size() > 0)
7882         {
7883         make.setURI(buildFile);
7884         }
7885     if (!make.run(target))
7886         return false;
7888     return true;
7894 /*
7895 static bool runMake()
7897     buildtool::Make make;
7898     if (!make.run())
7899         return false;
7900     return true;
7904 static bool pkgConfigTest()
7906     buildtool::PkgConfig pkgConfig;
7907     if (!pkgConfig.readFile("gtk+-2.0.pc"))
7908         return false;
7909     return true;
7914 static bool depTest()
7916     buildtool::DepTool deptool;
7917     deptool.setSourceDirectory("/dev/ink/inkscape/src");
7918     if (!deptool.generateDependencies("build.dep"))
7919         return false;
7920     std::vector<buildtool::DepRec> res =
7921                deptool.loadDepFile("build.dep");
7922         if (res.size() == 0)
7923         return false;
7924     return true;
7927 static bool popenTest()
7929     buildtool::Make make;
7930     buildtool::String out, err;
7931         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
7932     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
7933     return true;
7937 static bool propFileTest()
7939     buildtool::Make make;
7940     make.parsePropertyFile("test.prop", "test.");
7941     return true;
7943 */
7945 int main(int argc, char **argv)
7948     if (!parseOptions(argc, argv))
7949         return 1;
7950     /*
7951     if (!popenTest())
7952         return 1;
7954     if (!depTest())
7955         return 1;
7956     if (!propFileTest())
7957         return 1;
7958     if (runMake())
7959         return 1;
7960     */
7961     return 0;
7965 //########################################################################
7966 //# E N D 
7967 //########################################################################