Code

Fixed absolute paths in createDirectory()
[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(cnative, &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>0 && pos != dirname.npos)
3924         {
3925         String subpath = dirname.substr(0, pos);
3926         //A letter root (c:) ?
3927         if (!createDirectory(subpath))
3928             return false;
3929         }
3930         
3931     //## 3: now make
3932     if (mkdir(cnative)<0)
3933         {
3934         error("cannot make directory '%s'", cnative);
3935         return false;
3936         }
3937         
3938     return true;
3942 /**
3943  * Remove a directory recursively
3944  */ 
3945 bool MakeBase::removeDirectory(const String &dirName)
3947     char *dname = (char *)dirName.c_str();
3949     DIR *dir = opendir(dname);
3950     if (!dir)
3951         {
3952         //# Let this fail nicely.
3953         return true;
3954         //error("error opening directory %s : %s", dname, strerror(errno));
3955         //return false;
3956         }
3957     
3958     while (true)
3959         {
3960         struct dirent *de = readdir(dir);
3961         if (!de)
3962             break;
3964         //Get the directory member name
3965         String s = de->d_name;
3966         if (s.size() == 0 || s[0] == '.')
3967             continue;
3968         String childName;
3969         if (dirName.size() > 0)
3970             {
3971             childName.append(dirName);
3972             childName.append("/");
3973             }
3974         childName.append(s);
3977         struct stat finfo;
3978         String childNative = getNativePath(childName);
3979         char *cnative = (char *)childNative.c_str();
3980         if (stat(cnative, &finfo)<0)
3981             {
3982             error("cannot stat file:%s", cnative);
3983             }
3984         else if (S_ISDIR(finfo.st_mode))
3985             {
3986             //trace("DEL dir: %s", childName.c_str());
3987                         if (!removeDirectory(childName))
3988                     {
3989                             return false;
3990                             }
3991             }
3992         else if (!S_ISREG(finfo.st_mode))
3993             {
3994             //trace("not regular: %s", cnative);
3995             }
3996         else
3997             {
3998             //trace("DEL file: %s", childName.c_str());
3999             if (remove(cnative)<0)
4000                 {
4001                 error("error deleting %s : %s",
4002                                      cnative, strerror(errno));
4003                                 return false;
4004                                 }
4005             }
4006         }
4007     closedir(dir);
4009     //Now delete the directory
4010     String native = getNativePath(dirName);
4011     if (rmdir(native.c_str())<0)
4012         {
4013         error("could not delete directory %s : %s",
4014             native.c_str() , strerror(errno));
4015         return false;
4016         }
4018     return true;
4019     
4023 /**
4024  * Copy a file from one name to another. Perform only if needed
4025  */ 
4026 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4028     //# 1 Check up-to-date times
4029     String srcNative = getNativePath(srcFile);
4030     struct stat srcinfo;
4031     if (stat(srcNative.c_str(), &srcinfo)<0)
4032         {
4033         error("source file %s for copy does not exist",
4034                          srcNative.c_str());
4035         return false;
4036         }
4038     String destNative = getNativePath(destFile);
4039     struct stat destinfo;
4040     if (stat(destNative.c_str(), &destinfo)==0)
4041         {
4042         if (destinfo.st_mtime >= srcinfo.st_mtime)
4043             return true;
4044         }
4045         
4046     //# 2 prepare a destination directory if necessary
4047     unsigned int pos = destFile.find_last_of('/');
4048     if (pos != destFile.npos)
4049         {
4050         String subpath = destFile.substr(0, pos);
4051         if (!createDirectory(subpath))
4052             return false;
4053         }
4055     //# 3 do the data copy
4056 #ifndef __WIN32__
4058     FILE *srcf = fopen(srcNative.c_str(), "rb");
4059     if (!srcf)
4060         {
4061         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4062         return false;
4063         }
4064     FILE *destf = fopen(destNative.c_str(), "wb");
4065     if (!destf)
4066         {
4067         error("copyFile cannot open %s for writing", srcNative.c_str());
4068         return false;
4069         }
4071     while (!feof(srcf))
4072         {
4073         int ch = fgetc(srcf);
4074         if (ch<0)
4075             break;
4076         fputc(ch, destf);
4077         }
4079     fclose(destf);
4080     fclose(srcf);
4082 #else
4083     
4084     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4085         {
4086         error("copyFile from %s to %s failed",
4087                      srcNative.c_str(), destNative.c_str());
4088         return false;
4089         }
4090         
4091 #endif /* __WIN32__ */
4094     return true;
4099 /**
4100  * Tests if the file exists and is a regular file
4101  */ 
4102 bool MakeBase::isRegularFile(const String &fileName)
4104     String native = getNativePath(fileName);
4105     struct stat finfo;
4106     
4107     //Exists?
4108     if (stat(native.c_str(), &finfo)<0)
4109                 return false;
4112     //check the file mode
4113     if (!S_ISREG(finfo.st_mode))
4114                 return false;
4116     return true;
4119 /**
4120  * Tests if the file exists and is a directory
4121  */ 
4122 bool MakeBase::isDirectory(const String &fileName)
4124     String native = getNativePath(fileName);
4125     struct stat finfo;
4126     
4127     //Exists?
4128     if (stat(native.c_str(), &finfo)<0)
4129                 return false;
4132     //check the file mode
4133     if (!S_ISDIR(finfo.st_mode))
4134                 return false;
4136     return true;
4141 /**
4142  * Tests is the modification of fileA is newer than fileB
4143  */ 
4144 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4146     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4147     String nativeA = getNativePath(fileA);
4148     struct stat infoA;
4149     //IF source does not exist, NOT newer
4150     if (stat(nativeA.c_str(), &infoA)<0)
4151         {
4152                 return false;
4153                 }
4155     String nativeB = getNativePath(fileB);
4156     struct stat infoB;
4157     //IF dest does not exist, YES, newer
4158     if (stat(nativeB.c_str(), &infoB)<0)
4159         {
4160                 return true;
4161                 }
4163     //check the actual times
4164     if (infoA.st_mtime > infoB.st_mtime)
4165         {
4166                 return true;
4167                 }
4169     return false;
4173 //########################################################################
4174 //# P K G    C O N F I G
4175 //########################################################################
4177 /**
4178  *
4179  */
4180 class PkgConfig : public MakeBase
4183 public:
4185     /**
4186      *
4187      */
4188     PkgConfig()
4189         { init(); }
4191     /**
4192      *
4193      */
4194     PkgConfig(const String &namearg)
4195         { init(); name = namearg; }
4197     /**
4198      *
4199      */
4200     PkgConfig(const PkgConfig &other)
4201         { assign(other); }
4203     /**
4204      *
4205      */
4206     PkgConfig &operator=(const PkgConfig &other)
4207         { assign(other); return *this; }
4209     /**
4210      *
4211      */
4212     virtual ~PkgConfig()
4213         { }
4215     /**
4216      *
4217      */
4218     virtual String getName()
4219         { return name; }
4221     /**
4222      *
4223      */
4224     virtual String getDescription()
4225         { return description; }
4227     /**
4228      *
4229      */
4230     virtual String getCflags()
4231         { return cflags; }
4233     /**
4234      *
4235      */
4236     virtual String getLibs()
4237         { return libs; }
4239     /**
4240      *
4241      */
4242     virtual String getVersion()
4243         { return version; }
4245     /**
4246      *
4247      */
4248     virtual int getMajorVersion()
4249         { return majorVersion; }
4251     /**
4252      *
4253      */
4254     virtual int getMinorVersion()
4255         { return minorVersion; }
4257     /**
4258      *
4259      */
4260     virtual int getMicroVersion()
4261         { return microVersion; }
4263     /**
4264      *
4265      */
4266     virtual std::map<String, String> &getAttributes()
4267         { return attrs; }
4269     /**
4270      *
4271      */
4272     virtual std::vector<String> &getRequireList()
4273         { return requireList; }
4275     virtual bool readFile(const String &fileName);
4277 private:
4279     void init()
4280         {
4281         name         = "";
4282         description  = "";
4283         cflags       = "";
4284         libs         = "";
4285         requires     = "";
4286         version      = "";
4287         majorVersion = 0;
4288         minorVersion = 0;
4289         microVersion = 0;
4290         fileName     = "";
4291         attrs.clear();
4292         requireList.clear();
4293         }
4295     void assign(const PkgConfig &other)
4296         {
4297         name         = other.name;
4298         description  = other.description;
4299         cflags       = other.cflags;
4300         libs         = other.libs;
4301         requires     = other.requires;
4302         version      = other.version;
4303         majorVersion = other.majorVersion;
4304         minorVersion = other.minorVersion;
4305         microVersion = other.microVersion;
4306         fileName     = other.fileName;
4307         attrs        = other.attrs;
4308         requireList  = other.requireList;
4309         }
4313     int get(int pos);
4315     int skipwhite(int pos);
4317     int getword(int pos, String &ret);
4319     void parseRequires();
4321     void parseVersion();
4323     bool parse(const String &buf);
4325     void dumpAttrs();
4327     String name;
4329     String description;
4331     String cflags;
4333     String libs;
4335     String requires;
4337     String version;
4339     int majorVersion;
4341     int minorVersion;
4343     int microVersion;
4345     String fileName;
4347     std::map<String, String> attrs;
4349     std::vector<String> requireList;
4351     char *parsebuf;
4352     int parselen;
4353 };
4356 /**
4357  * Get a character from the buffer at pos.  If out of range,
4358  * return -1 for safety
4359  */
4360 int PkgConfig::get(int pos)
4362     if (pos>parselen)
4363         return -1;
4364     return parsebuf[pos];
4369 /**
4370  *  Skip over all whitespace characters beginning at pos.  Return
4371  *  the position of the first non-whitespace character.
4372  */
4373 int PkgConfig::skipwhite(int pos)
4375     while (pos < parselen)
4376         {
4377         int ch = get(pos);
4378         if (ch < 0)
4379             break;
4380         if (!isspace(ch))
4381             break;
4382         pos++;
4383         }
4384     return pos;
4388 /**
4389  *  Parse the buffer beginning at pos, for a word.  Fill
4390  *  'ret' with the result.  Return the position after the
4391  *  word.
4392  */
4393 int PkgConfig::getword(int pos, String &ret)
4395     while (pos < parselen)
4396         {
4397         int ch = get(pos);
4398         if (ch < 0)
4399             break;
4400         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4401             break;
4402         ret.push_back((char)ch);
4403         pos++;
4404         }
4405     return pos;
4408 void PkgConfig::parseRequires()
4410     if (requires.size() == 0)
4411         return;
4412     parsebuf = (char *)requires.c_str();
4413     parselen = requires.size();
4414     int pos = 0;
4415     while (pos < parselen)
4416         {
4417         pos = skipwhite(pos);
4418         String val;
4419         int pos2 = getword(pos, val);
4420         if (pos2 == pos)
4421             break;
4422         pos = pos2;
4423         //trace("val %s", val.c_str());
4424         requireList.push_back(val);
4425         }
4428 static int getint(const String str)
4430     char *s = (char *)str.c_str();
4431     char *ends = NULL;
4432     long val = strtol(s, &ends, 10);
4433     if (ends == s)
4434         return 0L;
4435     else
4436         return val;
4439 void PkgConfig::parseVersion()
4441     if (version.size() == 0)
4442         return;
4443     String s1, s2, s3;
4444     unsigned int pos = 0;
4445     unsigned int pos2 = version.find('.', pos);
4446     if (pos2 == version.npos)
4447         {
4448         s1 = version;
4449         }
4450     else
4451         {
4452         s1 = version.substr(pos, pos2-pos);
4453         pos = pos2;
4454         pos++;
4455         if (pos < version.size())
4456             {
4457             pos2 = version.find('.', pos);
4458             if (pos2 == version.npos)
4459                 {
4460                 s2 = version.substr(pos, version.size()-pos);
4461                 }
4462             else
4463                 {
4464                 s2 = version.substr(pos, pos2-pos);
4465                 pos = pos2;
4466                 pos++;
4467                 if (pos < version.size())
4468                     s3 = version.substr(pos, pos2-pos);
4469                 }
4470             }
4471         }
4473     majorVersion = getint(s1);
4474     minorVersion = getint(s2);
4475     microVersion = getint(s3);
4476     //trace("version:%d.%d.%d", majorVersion,
4477     //          minorVersion, microVersion );
4481 bool PkgConfig::parse(const String &buf)
4483     init();
4485     parsebuf = (char *)buf.c_str();
4486     parselen = buf.size();
4487     int pos = 0;
4490     while (pos < parselen)
4491         {
4492         String attrName;
4493         pos = skipwhite(pos);
4494         int ch = get(pos);
4495         if (ch == '#')
4496             {
4497             //comment.  eat the rest of the line
4498             while (pos < parselen)
4499                 {
4500                 ch = get(pos);
4501                 if (ch == '\n' || ch < 0)
4502                     break;
4503                 pos++;
4504                 }
4505             continue;
4506             }
4507         pos = getword(pos, attrName);
4508         if (attrName.size() == 0)
4509             continue;
4510         pos = skipwhite(pos);
4511         ch = get(pos);
4512         if (ch != ':' && ch != '=')
4513             {
4514             error("expected ':' or '='");
4515             return false;
4516             }
4517         pos++;
4518         pos = skipwhite(pos);
4519         String attrVal;
4520         while (pos < parselen)
4521             {
4522             ch = get(pos);
4523             if (ch == '\n' || ch < 0)
4524                 break;
4525             else if (ch == '$' && get(pos+1) == '{')
4526                 {
4527                 //#  this is a ${substitution}
4528                 pos += 2;
4529                 String subName;
4530                 while (pos < parselen)
4531                     {
4532                     ch = get(pos);
4533                     if (ch < 0)
4534                         {
4535                         error("unterminated substitution");
4536                         return false;
4537                         }
4538                     else if (ch == '}')
4539                         break;
4540                     else
4541                         subName.push_back((char)ch);
4542                     pos++;
4543                     }
4544                 //trace("subName:%s", subName.c_str());
4545                 String subVal = attrs[subName];
4546                 //trace("subVal:%s", subVal.c_str());
4547                 attrVal.append(subVal);
4548                 }
4549             else
4550                 attrVal.push_back((char)ch);
4551             pos++;
4552             }
4554         attrVal = trim(attrVal);
4555         attrs[attrName] = attrVal;
4557         if (attrName == "Name")
4558             name = attrVal;
4559         else if (attrName == "Description")
4560             description = attrVal;
4561         else if (attrName == "Cflags")
4562             cflags = attrVal;
4563         else if (attrName == "Libs")
4564             libs = attrVal;
4565         else if (attrName == "Requires")
4566             requires = attrVal;
4567         else if (attrName == "Version")
4568             version = attrVal;
4570         //trace("name:'%s'  value:'%s'",
4571         //      attrName.c_str(), attrVal.c_str());
4572         }
4575     parseRequires();
4576     parseVersion();
4578     return true;
4581 void PkgConfig::dumpAttrs()
4583     //trace("### PkgConfig attributes for %s", fileName.c_str());
4584     std::map<String, String>::iterator iter;
4585     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4586         {
4587         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4588         }
4592 bool PkgConfig::readFile(const String &fileNameArg)
4594     fileName = fileNameArg;
4596     FILE *f = fopen(fileName.c_str(), "r");
4597     if (!f)
4598         {
4599         error("cannot open file '%s' for reading", fileName.c_str());
4600         return false;
4601         }
4602     String buf;
4603     while (true)
4604         {
4605         int ch = fgetc(f);
4606         if (ch < 0)
4607             break;
4608         buf.push_back((char)ch);
4609         }
4610     fclose(f);
4612     //trace("####### File:\n%s", buf.c_str());
4613     if (!parse(buf))
4614         {
4615         return false;
4616         }
4618     dumpAttrs();
4620     return true;
4627 //########################################################################
4628 //# D E P T O O L
4629 //########################################################################
4633 /**
4634  *  Class which holds information for each file.
4635  */
4636 class FileRec
4638 public:
4640     typedef enum
4641         {
4642         UNKNOWN,
4643         CFILE,
4644         HFILE,
4645         OFILE
4646         } FileType;
4648     /**
4649      *  Constructor
4650      */
4651     FileRec()
4652         {init(); type = UNKNOWN;}
4654     /**
4655      *  Copy constructor
4656      */
4657     FileRec(const FileRec &other)
4658         {init(); assign(other);}
4659     /**
4660      *  Constructor
4661      */
4662     FileRec(int typeVal)
4663         {init(); type = typeVal;}
4664     /**
4665      *  Assignment operator
4666      */
4667     FileRec &operator=(const FileRec &other)
4668         {init(); assign(other); return *this;}
4671     /**
4672      *  Destructor
4673      */
4674     ~FileRec()
4675         {}
4677     /**
4678      *  Directory part of the file name
4679      */
4680     String path;
4682     /**
4683      *  Base name, sans directory and suffix
4684      */
4685     String baseName;
4687     /**
4688      *  File extension, such as cpp or h
4689      */
4690     String suffix;
4692     /**
4693      *  Type of file: CFILE, HFILE, OFILE
4694      */
4695     int type;
4697     /**
4698      * Used to list files ref'd by this one
4699      */
4700     std::map<String, FileRec *> files;
4703 private:
4705     void init()
4706         {
4707         }
4709     void assign(const FileRec &other)
4710         {
4711         type     = other.type;
4712         baseName = other.baseName;
4713         suffix   = other.suffix;
4714         files    = other.files;
4715         }
4717 };
4721 /**
4722  *  Simpler dependency record
4723  */
4724 class DepRec
4726 public:
4728     /**
4729      *  Constructor
4730      */
4731     DepRec()
4732         {init();}
4734     /**
4735      *  Copy constructor
4736      */
4737     DepRec(const DepRec &other)
4738         {init(); assign(other);}
4739     /**
4740      *  Constructor
4741      */
4742     DepRec(const String &fname)
4743         {init(); name = fname; }
4744     /**
4745      *  Assignment operator
4746      */
4747     DepRec &operator=(const DepRec &other)
4748         {init(); assign(other); return *this;}
4751     /**
4752      *  Destructor
4753      */
4754     ~DepRec()
4755         {}
4757     /**
4758      *  Directory part of the file name
4759      */
4760     String path;
4762     /**
4763      *  Base name, without the path and suffix
4764      */
4765     String name;
4767     /**
4768      *  Suffix of the source
4769      */
4770     String suffix;
4773     /**
4774      * Used to list files ref'd by this one
4775      */
4776     std::vector<String> files;
4779 private:
4781     void init()
4782         {
4783         }
4785     void assign(const DepRec &other)
4786         {
4787         path     = other.path;
4788         name     = other.name;
4789         suffix   = other.suffix;
4790         files    = other.files;
4791         }
4793 };
4796 class DepTool : public MakeBase
4798 public:
4800     /**
4801      *  Constructor
4802      */
4803     DepTool()
4804         {init();}
4806     /**
4807      *  Copy constructor
4808      */
4809     DepTool(const DepTool &other)
4810         {init(); assign(other);}
4812     /**
4813      *  Assignment operator
4814      */
4815     DepTool &operator=(const DepTool &other)
4816         {init(); assign(other); return *this;}
4819     /**
4820      *  Destructor
4821      */
4822     ~DepTool()
4823         {}
4826     /**
4827      *  Reset this section of code
4828      */
4829     virtual void init();
4830     
4831     /**
4832      *  Reset this section of code
4833      */
4834     virtual void assign(const DepTool &other)
4835         {
4836         }
4837     
4838     /**
4839      *  Sets the source directory which will be scanned
4840      */
4841     virtual void setSourceDirectory(const String &val)
4842         { sourceDir = val; }
4844     /**
4845      *  Returns the source directory which will be scanned
4846      */
4847     virtual String getSourceDirectory()
4848         { return sourceDir; }
4850     /**
4851      *  Sets the list of files within the directory to analyze
4852      */
4853     virtual void setFileList(const std::vector<String> &list)
4854         { fileList = list; }
4856     /**
4857      * Creates the list of all file names which will be
4858      * candidates for further processing.  Reads make.exclude
4859      * to see which files for directories to leave out.
4860      */
4861     virtual bool createFileList();
4864     /**
4865      *  Generates the forward dependency list
4866      */
4867     virtual bool generateDependencies();
4870     /**
4871      *  Generates the forward dependency list, saving the file
4872      */
4873     virtual bool generateDependencies(const String &);
4876     /**
4877      *  Load a dependency file
4878      */
4879     std::vector<DepRec> loadDepFile(const String &fileName);
4881     /**
4882      *  Load a dependency file, generating one if necessary
4883      */
4884     std::vector<DepRec> getDepFile(const String &fileName);
4886     /**
4887      *  Save a dependency file
4888      */
4889     bool saveDepFile(const String &fileName);
4892 private:
4895     /**
4896      *
4897      */
4898     void parseName(const String &fullname,
4899                    String &path,
4900                    String &basename,
4901                    String &suffix);
4903     /**
4904      *
4905      */
4906     int get(int pos);
4908     /**
4909      *
4910      */
4911     int skipwhite(int pos);
4913     /**
4914      *
4915      */
4916     int getword(int pos, String &ret);
4918     /**
4919      *
4920      */
4921     bool sequ(int pos, char *key);
4923     /**
4924      *
4925      */
4926     bool addIncludeFile(FileRec *frec, const String &fname);
4928     /**
4929      *
4930      */
4931     bool scanFile(const String &fname, FileRec *frec);
4933     /**
4934      *
4935      */
4936     bool processDependency(FileRec *ofile,
4937                            FileRec *include,
4938                            int depth);
4940     /**
4941      *
4942      */
4943     String sourceDir;
4945     /**
4946      *
4947      */
4948     std::vector<String> fileList;
4950     /**
4951      *
4952      */
4953     std::vector<String> directories;
4955     /**
4956      * A list of all files which will be processed for
4957      * dependencies.  This is the only list that has the actual
4958      * records.  All other lists have pointers to these records.     
4959      */
4960     std::map<String, FileRec *> allFiles;
4962     /**
4963      * The list of .o files, and the
4964      * dependencies upon them.
4965      */
4966     std::map<String, FileRec *> depFiles;
4968     int depFileSize;
4969     char *depFileBuf;
4970     
4972 };
4978 /**
4979  *  Clean up after processing.  Called by the destructor, but should
4980  *  also be called before the object is reused.
4981  */
4982 void DepTool::init()
4984     sourceDir = ".";
4986     fileList.clear();
4987     directories.clear();
4988     
4989     //clear refs
4990     depFiles.clear();
4991     //clear records
4992     std::map<String, FileRec *>::iterator iter;
4993     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4994          delete iter->second;
4996     allFiles.clear(); 
5003 /**
5004  *  Parse a full path name into path, base name, and suffix
5005  */
5006 void DepTool::parseName(const String &fullname,
5007                         String &path,
5008                         String &basename,
5009                         String &suffix)
5011     if (fullname.size() < 2)
5012         return;
5014     unsigned int pos = fullname.find_last_of('/');
5015     if (pos != fullname.npos && pos<fullname.size()-1)
5016         {
5017         path = fullname.substr(0, pos);
5018         pos++;
5019         basename = fullname.substr(pos, fullname.size()-pos);
5020         }
5021     else
5022         {
5023         path = "";
5024         basename = fullname;
5025         }
5027     pos = basename.find_last_of('.');
5028     if (pos != basename.npos && pos<basename.size()-1)
5029         {
5030         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5031         basename = basename.substr(0, pos);
5032         }
5034     //trace("parsename:%s %s %s", path.c_str(),
5035     //        basename.c_str(), suffix.c_str()); 
5040 /**
5041  *  Generate our internal file list.
5042  */
5043 bool DepTool::createFileList()
5046     for (unsigned int i=0 ; i<fileList.size() ; i++)
5047         {
5048         String fileName = fileList[i];
5049         //trace("## FileName:%s", fileName.c_str());
5050         String path;
5051         String basename;
5052         String sfx;
5053         parseName(fileName, path, basename, sfx);
5054         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5055                     sfx == "cc" || sfx == "CC")
5056             {
5057             FileRec *fe         = new FileRec(FileRec::CFILE);
5058             fe->path            = path;
5059             fe->baseName        = basename;
5060             fe->suffix          = sfx;
5061             allFiles[fileName]  = fe;
5062             }
5063         else if (sfx == "h"   ||  sfx == "hh"  ||
5064                  sfx == "hpp" ||  sfx == "hxx")
5065             {
5066             FileRec *fe         = new FileRec(FileRec::HFILE);
5067             fe->path            = path;
5068             fe->baseName        = basename;
5069             fe->suffix          = sfx;
5070             allFiles[fileName]  = fe;
5071             }
5072         }
5074     if (!listDirectories(sourceDir, "", directories))
5075         return false;
5076         
5077     return true;
5084 /**
5085  * Get a character from the buffer at pos.  If out of range,
5086  * return -1 for safety
5087  */
5088 int DepTool::get(int pos)
5090     if (pos>depFileSize)
5091         return -1;
5092     return depFileBuf[pos];
5097 /**
5098  *  Skip over all whitespace characters beginning at pos.  Return
5099  *  the position of the first non-whitespace character.
5100  */
5101 int DepTool::skipwhite(int pos)
5103     while (pos < depFileSize)
5104         {
5105         int ch = get(pos);
5106         if (ch < 0)
5107             break;
5108         if (!isspace(ch))
5109             break;
5110         pos++;
5111         }
5112     return pos;
5116 /**
5117  *  Parse the buffer beginning at pos, for a word.  Fill
5118  *  'ret' with the result.  Return the position after the
5119  *  word.
5120  */
5121 int DepTool::getword(int pos, String &ret)
5123     while (pos < depFileSize)
5124         {
5125         int ch = get(pos);
5126         if (ch < 0)
5127             break;
5128         if (isspace(ch))
5129             break;
5130         ret.push_back((char)ch);
5131         pos++;
5132         }
5133     return pos;
5136 /**
5137  * Return whether the sequence of characters in the buffer
5138  * beginning at pos match the key,  for the length of the key
5139  */
5140 bool DepTool::sequ(int pos, char *key)
5142     while (*key)
5143         {
5144         if (*key != get(pos))
5145             return false;
5146         key++; pos++;
5147         }
5148     return true;
5153 /**
5154  *  Add an include file name to a file record.  If the name
5155  *  is not found in allFiles explicitly, try prepending include
5156  *  directory names to it and try again.
5157  */
5158 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5161     std::map<String, FileRec *>::iterator iter =
5162            allFiles.find(iname);
5163     if (iter != allFiles.end()) //already exists
5164         {
5165          //h file in same dir
5166         FileRec *other = iter->second;
5167         //trace("local: '%s'", iname.c_str());
5168         frec->files[iname] = other;
5169         return true;
5170         }
5171     else 
5172         {
5173         //look in other dirs
5174         std::vector<String>::iterator diter;
5175         for (diter=directories.begin() ;
5176              diter!=directories.end() ; diter++)
5177             {
5178             String dfname = *diter;
5179             dfname.append("/");
5180             dfname.append(iname);
5181             iter = allFiles.find(dfname);
5182             if (iter != allFiles.end())
5183                 {
5184                 FileRec *other = iter->second;
5185                 //trace("other: '%s'", iname.c_str());
5186                 frec->files[dfname] = other;
5187                 return true;
5188                 }
5189             }
5190         }
5191     return true;
5196 /**
5197  *  Lightly parse a file to find the #include directives.  Do
5198  *  a bit of state machine stuff to make sure that the directive
5199  *  is valid.  (Like not in a comment).
5200  */
5201 bool DepTool::scanFile(const String &fname, FileRec *frec)
5203     String fileName;
5204     if (sourceDir.size() > 0)
5205         {
5206         fileName.append(sourceDir);
5207         fileName.append("/");
5208         }
5209     fileName.append(fname);
5210     String nativeName = getNativePath(fileName);
5211     FILE *f = fopen(nativeName.c_str(), "r");
5212     if (!f)
5213         {
5214         error("Could not open '%s' for reading", fname.c_str());
5215         return false;
5216         }
5217     String buf;
5218     while (true)
5219         {
5220         int ch = fgetc(f);
5221         if (ch < 0)
5222             break;
5223         buf.push_back((char)ch);
5224         }
5225     fclose(f);
5227     depFileSize = buf.size();
5228     depFileBuf  = (char *)buf.c_str();
5229     int pos = 0;
5232     while (pos < depFileSize)
5233         {
5234         //trace("p:%c", get(pos));
5236         //# Block comment
5237         if (get(pos) == '/' && get(pos+1) == '*')
5238             {
5239             pos += 2;
5240             while (pos < depFileSize)
5241                 {
5242                 if (get(pos) == '*' && get(pos+1) == '/')
5243                     {
5244                     pos += 2;
5245                     break;
5246                     }
5247                 else
5248                     pos++;
5249                 }
5250             }
5251         //# Line comment
5252         else if (get(pos) == '/' && get(pos+1) == '/')
5253             {
5254             pos += 2;
5255             while (pos < depFileSize)
5256                 {
5257                 if (get(pos) == '\n')
5258                     {
5259                     pos++;
5260                     break;
5261                     }
5262                 else
5263                     pos++;
5264                 }
5265             }
5266         //# #include! yaay
5267         else if (sequ(pos, "#include"))
5268             {
5269             pos += 8;
5270             pos = skipwhite(pos);
5271             String iname;
5272             pos = getword(pos, iname);
5273             if (iname.size()>2)
5274                 {
5275                 iname = iname.substr(1, iname.size()-2);
5276                 addIncludeFile(frec, iname);
5277                 }
5278             }
5279         else
5280             {
5281             pos++;
5282             }
5283         }
5285     return true;
5290 /**
5291  *  Recursively check include lists to find all files in allFiles to which
5292  *  a given file is dependent.
5293  */
5294 bool DepTool::processDependency(FileRec *ofile,
5295                              FileRec *include,
5296                              int depth)
5298     std::map<String, FileRec *>::iterator iter;
5299     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5300         {
5301         String fname  = iter->first;
5302         if (ofile->files.find(fname) != ofile->files.end())
5303             {
5304             //trace("file '%s' already seen", fname.c_str());
5305             continue;
5306             }
5307         FileRec *child  = iter->second;
5308         ofile->files[fname] = child;
5309       
5310         processDependency(ofile, child, depth+1);
5311         }
5314     return true;
5321 /**
5322  *  Generate the file dependency list.
5323  */
5324 bool DepTool::generateDependencies()
5326     std::map<String, FileRec *>::iterator iter;
5327     //# First pass.  Scan for all includes
5328     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5329         {
5330         FileRec *frec = iter->second;
5331         if (!scanFile(iter->first, frec))
5332             {
5333             //quit?
5334             }
5335         }
5337     //# Second pass.  Scan for all includes
5338     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5339         {
5340         FileRec *include = iter->second;
5341         if (include->type == FileRec::CFILE)
5342             {
5343             String cFileName = iter->first;
5344             FileRec *ofile      = new FileRec(FileRec::OFILE);
5345             ofile->path         = include->path;
5346             ofile->baseName     = include->baseName;
5347             ofile->suffix       = include->suffix;
5348             String fname     = include->path;
5349             if (fname.size()>0)
5350                 fname.append("/");
5351             fname.append(include->baseName);
5352             fname.append(".o");
5353             depFiles[fname]    = ofile;
5354             //add the .c file first?   no, don't
5355             //ofile->files[cFileName] = include;
5356             
5357             //trace("ofile:%s", fname.c_str());
5359             processDependency(ofile, include, 0);
5360             }
5361         }
5363       
5364     return true;
5369 /**
5370  *  High-level call to generate deps and optionally save them
5371  */
5372 bool DepTool::generateDependencies(const String &fileName)
5374     if (!createFileList())
5375         return false;
5376     if (!generateDependencies())
5377         return false;
5378     if (!saveDepFile(fileName))
5379         return false;
5380     return true;
5384 /**
5385  *   This saves the dependency cache.
5386  */
5387 bool DepTool::saveDepFile(const String &fileName)
5389     time_t tim;
5390     time(&tim);
5392     FILE *f = fopen(fileName.c_str(), "w");
5393     if (!f)
5394         {
5395         trace("cannot open '%s' for writing", fileName.c_str());
5396         }
5397     fprintf(f, "<?xml version='1.0'?>\n");
5398     fprintf(f, "<!--\n");
5399     fprintf(f, "########################################################\n");
5400     fprintf(f, "## File: build.dep\n");
5401     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5402     fprintf(f, "########################################################\n");
5403     fprintf(f, "-->\n");
5405     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5406     std::map<String, FileRec *>::iterator iter;
5407     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5408         {
5409         FileRec *frec = iter->second;
5410         if (frec->type == FileRec::OFILE)
5411             {
5412             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5413                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5414             std::map<String, FileRec *>::iterator citer;
5415             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5416                 {
5417                 String cfname = citer->first;
5418                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5419                 }
5420             fprintf(f, "</object>\n\n");
5421             }
5422         }
5424     fprintf(f, "</dependencies>\n");
5425     fprintf(f, "\n");
5426     fprintf(f, "<!--\n");
5427     fprintf(f, "########################################################\n");
5428     fprintf(f, "## E N D\n");
5429     fprintf(f, "########################################################\n");
5430     fprintf(f, "-->\n");
5432     fclose(f);
5434     return true;
5440 /**
5441  *   This loads the dependency cache.
5442  */
5443 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5445     std::vector<DepRec> result;
5446     
5447     Parser parser;
5448     Element *root = parser.parseFile(depFile.c_str());
5449     if (!root)
5450         {
5451         error("Could not open %s for reading", depFile.c_str());
5452         return result;
5453         }
5455     if (root->getChildren().size()==0 ||
5456         root->getChildren()[0]->getName()!="dependencies")
5457         {
5458         error("Main xml element should be <dependencies>");
5459         delete root;
5460         return result;
5461         }
5463     //########## Start parsing
5464     Element *depList = root->getChildren()[0];
5466     std::vector<Element *> objects = depList->getChildren();
5467     for (unsigned int i=0 ; i<objects.size() ; i++)
5468         {
5469         Element *objectElem = objects[i];
5470         String tagName = objectElem->getName();
5471         if (tagName == "object")
5472             {
5473             String objName   = objectElem->getAttribute("name");
5474              //trace("object:%s", objName.c_str());
5475             DepRec depObject(objName);
5476             depObject.path   = objectElem->getAttribute("path");
5477             depObject.suffix = objectElem->getAttribute("suffix");
5478             //########## DESCRIPTION
5479             std::vector<Element *> depElems = objectElem->getChildren();
5480             for (unsigned int i=0 ; i<depElems.size() ; i++)
5481                 {
5482                 Element *depElem = depElems[i];
5483                 tagName = depElem->getName();
5484                 if (tagName == "dep")
5485                     {
5486                     String depName = depElem->getAttribute("name");
5487                     //trace("    dep:%s", depName.c_str());
5488                     depObject.files.push_back(depName);
5489                     }
5490                 }
5491             //Insert into the result list, in a sorted manner
5492             bool inserted = false;
5493             std::vector<DepRec>::iterator iter;
5494             for (iter = result.begin() ; iter != result.end() ; iter++)
5495                 {
5496                 String vpath = iter->path;
5497                 vpath.append("/");
5498                 vpath.append(iter->name);
5499                 String opath = depObject.path;
5500                 opath.append("/");
5501                 opath.append(depObject.name);
5502                 if (vpath > opath)
5503                     {
5504                     inserted = true;
5505                     iter = result.insert(iter, depObject);
5506                     break;
5507                     }
5508                 }
5509             if (!inserted)
5510                 result.push_back(depObject);
5511             }
5512         }
5514     delete root;
5516     return result;
5520 /**
5521  *   This loads the dependency cache.
5522  */
5523 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
5525     std::vector<DepRec> result = loadDepFile(depFile);
5526     if (result.size() == 0)
5527         {
5528         generateDependencies(depFile);
5529         result = loadDepFile(depFile);
5530         }
5531     return result;
5537 //########################################################################
5538 //# T A S K
5539 //########################################################################
5540 //forward decl
5541 class Target;
5542 class Make;
5544 /**
5545  *
5546  */
5547 class Task : public MakeBase
5550 public:
5552     typedef enum
5553         {
5554         TASK_NONE,
5555         TASK_AR,
5556         TASK_CC,
5557         TASK_COPY,
5558         TASK_DELETE,
5559         TASK_JAR,
5560         TASK_JAVAC,
5561         TASK_LINK,
5562         TASK_MAKEFILE,
5563         TASK_MKDIR,
5564         TASK_MSGFMT,
5565         TASK_RANLIB,
5566         TASK_RC,
5567         TASK_STRIP,
5568         TASK_TSTAMP
5569         } TaskType;
5570         
5572     /**
5573      *
5574      */
5575     Task(MakeBase &par) : parent(par)
5576         { init(); }
5578     /**
5579      *
5580      */
5581     Task(const Task &other) : parent(other.parent)
5582         { init(); assign(other); }
5584     /**
5585      *
5586      */
5587     Task &operator=(const Task &other)
5588         { assign(other); return *this; }
5590     /**
5591      *
5592      */
5593     virtual ~Task()
5594         { }
5597     /**
5598      *
5599      */
5600     virtual MakeBase &getParent()
5601         { return parent; }
5603      /**
5604      *
5605      */
5606     virtual int  getType()
5607         { return type; }
5609     /**
5610      *
5611      */
5612     virtual void setType(int val)
5613         { type = val; }
5615     /**
5616      *
5617      */
5618     virtual String getName()
5619         { return name; }
5621     /**
5622      *
5623      */
5624     virtual bool execute()
5625         { return true; }
5627     /**
5628      *
5629      */
5630     virtual bool parse(Element *elem)
5631         { return true; }
5633     /**
5634      *
5635      */
5636     Task *createTask(Element *elem);
5639 protected:
5641     void init()
5642         {
5643         type = TASK_NONE;
5644         name = "none";
5645         }
5647     void assign(const Task &other)
5648         {
5649         type = other.type;
5650         name = other.name;
5651         }
5652         
5653     String getAttribute(Element *elem, const String &attrName)
5654         {
5655         String str;
5656         return str;
5657         }
5659     MakeBase &parent;
5661     int type;
5663     String name;
5664 };
5669 /**
5670  * Run the "ar" command to archive .o's into a .a
5671  */
5672 class TaskAr : public Task
5674 public:
5676     TaskAr(MakeBase &par) : Task(par)
5677         {
5678                 type = TASK_AR; name = "ar";
5679                 command = "ar crv";
5680                 }
5682     virtual ~TaskAr()
5683         {}
5685     virtual bool execute()
5686         {
5687         //trace("###########HERE %d", fileSet.size());
5688         bool doit = false;
5689         
5690         String fullOut = parent.resolve(fileName);
5691         //trace("ar fullout: %s", fullOut.c_str());
5692         
5693         if (!listFiles(parent, fileSet))
5694             return false;
5695         String fileSetDir = fileSet.getDirectory();
5697         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5698             {
5699             String fname;
5700                         if (fileSetDir.size()>0)
5701                             {
5702                             fname.append(fileSetDir);
5703                 fname.append("/");
5704                 }
5705             fname.append(fileSet[i]);
5706             String fullName = parent.resolve(fname);
5707             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
5708             if (isNewerThan(fullName, fullOut))
5709                 doit = true;
5710             }
5711         //trace("Needs it:%d", doit);
5712         if (!doit)
5713             {
5714             return true;
5715             }
5717         String cmd = command;
5718         cmd.append(" ");
5719         cmd.append(fullOut);
5720         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5721             {
5722             String fname;
5723                         if (fileSetDir.size()>0)
5724                             {
5725                             fname.append(fileSetDir);
5726                 fname.append("/");
5727                 }
5728             fname.append(fileSet[i]);
5729             String fullName = parent.resolve(fname);
5731             cmd.append(" ");
5732             cmd.append(fullName);
5733             }
5735         String outString, errString;
5736         if (!executeCommand(cmd.c_str(), "", outString, errString))
5737             {
5738             error("AR problem: %s", errString.c_str());
5739             return false;
5740             }
5742         return true;
5743         }
5745     virtual bool parse(Element *elem)
5746         {
5747         if (!parent.getAttribute(elem, "file", fileName))
5748             return false;
5749             
5750         std::vector<Element *> children = elem->getChildren();
5751         for (unsigned int i=0 ; i<children.size() ; i++)
5752             {
5753             Element *child = children[i];
5754             String tagName = child->getName();
5755             if (tagName == "fileset")
5756                 {
5757                 if (!parseFileSet(child, parent, fileSet))
5758                     return false;
5759                 }
5760             }
5761         return true;
5762         }
5764 private:
5766     String command;
5767     String fileName;
5768     FileSet fileSet;
5770 };
5773 /**
5774  * This task runs the C/C++ compiler.  The compiler is invoked
5775  * for all .c or .cpp files which are newer than their correcsponding
5776  * .o files.  
5777  */
5778 class TaskCC : public Task
5780 public:
5782     TaskCC(MakeBase &par) : Task(par)
5783         {
5784                 type = TASK_CC; name = "cc";
5785                 ccCommand   = "gcc";
5786                 cxxCommand  = "g++";
5787                 source      = ".";
5788                 dest        = ".";
5789                 flags       = "";
5790                 defines     = "";
5791                 includes    = "";
5792                 fileSet.clear();
5793         }
5795     virtual ~TaskCC()
5796         {}
5798     virtual bool execute()
5799         {
5800         if (!listFiles(parent, fileSet))
5801             return false;
5803         DepTool depTool;
5804         depTool.setSourceDirectory(source);
5805         depTool.setFileList(fileSet.getFiles());
5806         std::vector<DepRec> deps = depTool.getDepFile("build.dep");
5807         
5808         String incs;
5809         incs.append("-I");
5810         incs.append(parent.resolve("."));
5811         incs.append(" ");
5812         if (includes.size()>0)
5813             {
5814             incs.append(includes);
5815             incs.append(" ");
5816             }
5817         std::set<String> paths;
5818         std::vector<DepRec>::iterator viter;
5819         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5820             {
5821             DepRec dep = *viter;
5822             if (dep.path.size()>0)
5823                 paths.insert(dep.path);
5824             }
5825         if (source.size()>0)
5826             {
5827             incs.append(" -I");
5828             incs.append(parent.resolve(source));
5829             incs.append(" ");
5830             }
5831         std::set<String>::iterator setIter;
5832         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5833             {
5834             incs.append(" -I");
5835             String dname;
5836             if (source.size()>0)
5837                 {
5838                 dname.append(source);
5839                 dname.append("/");
5840                 }
5841             dname.append(*setIter);
5842             incs.append(parent.resolve(dname));
5843             }
5844         std::vector<String> cfiles;
5845         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5846             {
5847             DepRec dep = *viter;
5849             //## Select command
5850             String sfx = dep.suffix;
5851             String command = ccCommand;
5852             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5853                              || sfx == "CC")
5854                             command = cxxCommand;
5855  
5856             //## Make paths
5857             String destPath = dest;
5858             String srcPath  = source;
5859             if (dep.path.size()>0)
5860                             {
5861                 destPath.append("/");
5862                                 destPath.append(dep.path);
5863                 srcPath.append("/");
5864                                 srcPath.append(dep.path);
5865                             }
5866             //## Make sure destination directory exists
5867                         if (!createDirectory(destPath))
5868                             return false;
5869                             
5870             //## Check whether it needs to be done
5871                         String destFullName = destPath;
5872                         destFullName.append("/");
5873                         destFullName.append(dep.name);
5874                         destFullName.append(".o");
5875                         String srcFullName = srcPath;
5876                         srcFullName.append("/");
5877                         srcFullName.append(dep.name);
5878                         srcFullName.append(".");
5879                         srcFullName.append(dep.suffix);
5880             if (!isNewerThan(srcFullName, destFullName))
5881                 {
5882                 //trace("%s skipped", srcFullName.c_str());
5883                 continue;
5884                 }
5886             //## Assemble the command
5887             String cmd = command;
5888             cmd.append(" -c ");
5889             cmd.append(flags);
5890                         cmd.append(" ");
5891             cmd.append(defines);
5892                         cmd.append(" ");
5893             cmd.append(incs);
5894                         cmd.append(" ");
5895                         cmd.append(srcFullName);
5896             cmd.append(" -o ");
5897                         cmd.append(destFullName);
5899             //## Execute the command
5901             String outString, errString;
5902             if (!executeCommand(cmd.c_str(), "", outString, errString))
5903                 {
5904                 error("problem compiling: %s", errString.c_str());
5905                 return false;
5906                 }
5907             }
5908         
5909         return true;
5910         }
5912     virtual bool parse(Element *elem)
5913         {
5914         String s;
5915         if (!parent.getAttribute(elem, "command", s))
5916             return false;
5917         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5918         if (!parent.getAttribute(elem, "cc", s))
5919             return false;
5920         if (s.size()>0) ccCommand = s;
5921         if (!parent.getAttribute(elem, "cxx", s))
5922             return false;
5923         if (s.size()>0) cxxCommand = s;
5924         if (!parent.getAttribute(elem, "destdir", s))
5925             return false;
5926         if (s.size()>0) dest = s;
5928         std::vector<Element *> children = elem->getChildren();
5929         for (unsigned int i=0 ; i<children.size() ; i++)
5930             {
5931             Element *child = children[i];
5932             String tagName = child->getName();
5933             if (tagName == "flags")
5934                 {
5935                 if (!parent.getValue(child, flags))
5936                     return false;
5937                 flags = strip(flags);
5938                 }
5939             else if (tagName == "includes")
5940                 {
5941                 if (!parent.getValue(child, includes))
5942                     return false;
5943                 includes = strip(includes);
5944                 }
5945             else if (tagName == "defines")
5946                 {
5947                 if (!parent.getValue(child, defines))
5948                     return false;
5949                 defines = strip(defines);
5950                 }
5951             else if (tagName == "fileset")
5952                 {
5953                 if (!parseFileSet(child, parent, fileSet))
5954                     return false;
5955                 source = fileSet.getDirectory();
5956                 }
5957             }
5959         return true;
5960         }
5961         
5962 protected:
5964     String ccCommand;
5965     String cxxCommand;
5966     String source;
5967     String dest;
5968     String flags;
5969     String defines;
5970     String includes;
5971     FileSet fileSet;
5972     
5973 };
5977 /**
5978  *
5979  */
5980 class TaskCopy : public Task
5982 public:
5984     typedef enum
5985         {
5986         CP_NONE,
5987         CP_TOFILE,
5988         CP_TODIR
5989         } CopyType;
5991     TaskCopy(MakeBase &par) : Task(par)
5992         {
5993                 type = TASK_COPY; name = "copy";
5994                 cptype = CP_NONE;
5995                 verbose = false;
5996                 haveFileSet = false;
5997                 }
5999     virtual ~TaskCopy()
6000         {}
6002     virtual bool execute()
6003         {
6004         switch (cptype)
6005            {
6006            case CP_TOFILE:
6007                {
6008                if (fileName.size()>0)
6009                    {
6010                    status("          : %s", fileName.c_str());
6011                    String fullSource = parent.resolve(fileName);
6012                    String fullDest = parent.resolve(toFileName);
6013                    //trace("copy %s to file %s", fullSource.c_str(),
6014                                    //                       fullDest.c_str());
6015                                    if (!isRegularFile(fullSource))
6016                                        {
6017                        error("copy : file %s does not exist", fullSource.c_str());
6018                                        return false;
6019                                        }
6020                    if (!isNewerThan(fullSource, fullDest))
6021                        {
6022                        return true;
6023                        }
6024                    if (!copyFile(fullSource, fullDest))
6025                        return false;
6026                    status("          : 1 file copied");
6027                    }
6028                return true;
6029                }
6030            case CP_TODIR:
6031                {
6032                if (haveFileSet)
6033                    {
6034                    if (!listFiles(parent, fileSet))
6035                        return false;
6036                    String fileSetDir = fileSet.getDirectory();
6038                    int nrFiles = 0;
6039                    status("          : %s", fileSetDir.c_str());
6040                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6041                        {
6042                        String fileName = fileSet[i];
6044                        String sourcePath;
6045                        if (fileSetDir.size()>0)
6046                            {
6047                            sourcePath.append(fileSetDir);
6048                            sourcePath.append("/");
6049                            }
6050                        sourcePath.append(fileName);
6051                        String fullSource = parent.resolve(sourcePath);
6052                        
6053                        //Get the immediate parent directory's base name
6054                        String baseFileSetDir = fileSetDir;
6055                        unsigned int pos = baseFileSetDir.find_last_of('/');
6056                        if (pos!=baseFileSetDir.npos &&
6057                                                       pos < baseFileSetDir.size()-1)
6058                            baseFileSetDir =
6059                                                       baseFileSetDir.substr(pos+1,
6060                                                                baseFileSetDir.size());
6061                                            //Now make the new path
6062                        String destPath;
6063                        if (toDirName.size()>0)
6064                            {
6065                            destPath.append(toDirName);
6066                            destPath.append("/");
6067                            }
6068                        if (baseFileSetDir.size()>0)
6069                            {
6070                            destPath.append(baseFileSetDir);
6071                            destPath.append("/");
6072                            }
6073                        destPath.append(fileName);
6074                        String fullDest = parent.resolve(destPath);
6075                        //trace("fileName:%s", fileName.c_str());
6076                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6077                                        //                   fullDest.c_str());
6078                        if (!isNewerThan(fullSource, fullDest))
6079                            {
6080                            //trace("copy skipping %s", fullSource.c_str());
6081                            continue;
6082                            }
6083                        if (!copyFile(fullSource, fullDest))
6084                            return false;
6085                        nrFiles++;
6086                        }
6087                    status("          : %d file(s) copied", nrFiles);
6088                    }
6089                else //file source
6090                    {
6091                    //For file->dir we want only the basename of
6092                    //the source appended to the dest dir
6093                    status("          : %s", fileName.c_str());
6094                    String baseName = fileName;
6095                    unsigned int pos = baseName.find_last_of('/');
6096                    if (pos!=baseName.npos && pos<baseName.size()-1)
6097                        baseName = baseName.substr(pos+1, baseName.size());
6098                    String fullSource = parent.resolve(fileName);
6099                    String destPath;
6100                    if (toDirName.size()>0)
6101                        {
6102                        destPath.append(toDirName);
6103                        destPath.append("/");
6104                        }
6105                    destPath.append(baseName);
6106                    String fullDest = parent.resolve(destPath);
6107                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6108                                    //                       fullDest.c_str());
6109                                    if (!isRegularFile(fullSource))
6110                                        {
6111                        error("copy : file %s does not exist", fullSource.c_str());
6112                                        return false;
6113                                        }
6114                    if (!isNewerThan(fullSource, fullDest))
6115                        {
6116                        return true;
6117                        }
6118                    if (!copyFile(fullSource, fullDest))
6119                        return false;
6120                    status("          : 1 file copied");
6121                    }
6122                return true;
6123                }
6124            }
6125         return true;
6126         }
6129     virtual bool parse(Element *elem)
6130         {
6131         if (!parent.getAttribute(elem, "file", fileName))
6132             return false;
6133         if (!parent.getAttribute(elem, "tofile", toFileName))
6134             return false;
6135         if (toFileName.size() > 0)
6136             cptype = CP_TOFILE;
6137         if (!parent.getAttribute(elem, "todir", toDirName))
6138             return false;
6139         if (toDirName.size() > 0)
6140             cptype = CP_TODIR;
6141         String ret;
6142         if (!parent.getAttribute(elem, "verbose", ret))
6143             return false;
6144         if (ret.size()>0 && !getBool(ret, verbose))
6145             return false;
6146             
6147         haveFileSet = false;
6148         
6149         std::vector<Element *> children = elem->getChildren();
6150         for (unsigned int i=0 ; i<children.size() ; i++)
6151             {
6152             Element *child = children[i];
6153             String tagName = child->getName();
6154             if (tagName == "fileset")
6155                 {
6156                 if (!parseFileSet(child, parent, fileSet))
6157                     {
6158                     error("problem getting fileset");
6159                                         return false;
6160                                         }
6161                                 haveFileSet = true;
6162                 }
6163             }
6165         //Perform validity checks
6166                 if (fileName.size()>0 && fileSet.size()>0)
6167                     {
6168                     error("<copy> can only have one of : file= and <fileset>");
6169                     return false;
6170                     }
6171         if (toFileName.size()>0 && toDirName.size()>0)
6172             {
6173             error("<copy> can only have one of : tofile= or todir=");
6174             return false;
6175             }
6176         if (haveFileSet && toDirName.size()==0)
6177             {
6178             error("a <copy> task with a <fileset> must have : todir=");
6179             return false;
6180             }
6181                 if (cptype == CP_TOFILE && fileName.size()==0)
6182                     {
6183                     error("<copy> tofile= must be associated with : file=");
6184                     return false;
6185                     }
6186                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6187                     {
6188                     error("<copy> todir= must be associated with : file= or <fileset>");
6189                     return false;
6190                     }
6192         return true;
6193         }
6194         
6195 private:
6197     int cptype;
6198     String fileName;
6199     FileSet fileSet;
6200     String toFileName;
6201     String toDirName;
6202     bool verbose;
6203     bool haveFileSet;
6204 };
6207 /**
6208  *
6209  */
6210 class TaskDelete : public Task
6212 public:
6214     typedef enum
6215         {
6216         DEL_FILE,
6217         DEL_DIR,
6218         DEL_FILESET
6219         } DeleteType;
6221     TaskDelete(MakeBase &par) : Task(par)
6222         { 
6223                   type        = TASK_DELETE;
6224                   name        = "delete";
6225                   delType     = DEL_FILE;
6226           verbose     = false;
6227           quiet       = false;
6228           failOnError = true;
6229                 }
6231     virtual ~TaskDelete()
6232         {}
6234     virtual bool execute()
6235         {
6236         struct stat finfo;
6237         switch (delType)
6238             {
6239             case DEL_FILE:
6240                 {
6241                 status("          : %s", fileName.c_str());
6242                 String fullName = parent.resolve(fileName);
6243                 char *fname = (char *)fullName.c_str();
6244                 //does not exist
6245                 if (stat(fname, &finfo)<0)
6246                     return true;
6247                 //exists but is not a regular file
6248                 if (!S_ISREG(finfo.st_mode))
6249                     {
6250                     error("<delete> failed. '%s' exists and is not a regular file",
6251                           fname);
6252                     return false;
6253                     }
6254                 if (remove(fname)<0)
6255                     {
6256                     error("<delete> failed: %s", strerror(errno));
6257                     return false;
6258                     }
6259                 return true;
6260                 }
6261             case DEL_DIR:
6262                 {
6263                 status("          : %s", dirName.c_str());
6264                 String fullDir = parent.resolve(dirName);
6265                 if (!removeDirectory(fullDir))
6266                     return false;
6267                 return true;
6268                 }
6269             }
6270         return true;
6271         }
6273     virtual bool parse(Element *elem)
6274         {
6275         if (!parent.getAttribute(elem, "file", fileName))
6276             return false;
6277         if (fileName.size() > 0)
6278             delType = DEL_FILE;
6279         if (!parent.getAttribute(elem, "dir", dirName))
6280             return false;
6281         if (dirName.size() > 0)
6282             delType = DEL_DIR;
6283         if (fileName.size()>0 && dirName.size()>0)
6284             {
6285             error("<delete> can only have one attribute of file= or dir=");
6286             return false;
6287             }
6288         String ret;
6289         if (!parent.getAttribute(elem, "verbose", ret))
6290             return false;
6291         if (ret.size()>0 && !getBool(ret, verbose))
6292             return false;
6293         if (!parent.getAttribute(elem, "quiet", ret))
6294             return false;
6295         if (ret.size()>0 && !getBool(ret, quiet))
6296             return false;
6297         if (!parent.getAttribute(elem, "failonerror", ret))
6298             return false;
6299         if (ret.size()>0 && !getBool(ret, failOnError))
6300             return false;
6301         return true;
6302         }
6304 private:
6306     int delType;
6307     String dirName;
6308     String fileName;
6309     bool verbose;
6310     bool quiet;
6311     bool failOnError;
6312 };
6315 /**
6316  *
6317  */
6318 class TaskJar : public Task
6320 public:
6322     TaskJar(MakeBase &par) : Task(par)
6323         { type = TASK_JAR; name = "jar"; }
6325     virtual ~TaskJar()
6326         {}
6328     virtual bool execute()
6329         {
6330         return true;
6331         }
6333     virtual bool parse(Element *elem)
6334         {
6335         return true;
6336         }
6337 };
6340 /**
6341  *
6342  */
6343 class TaskJavac : public Task
6345 public:
6347     TaskJavac(MakeBase &par) : Task(par)
6348         { type = TASK_JAVAC; name = "javac"; }
6350     virtual ~TaskJavac()
6351         {}
6353     virtual bool execute()
6354         {
6355         return true;
6356         }
6358     virtual bool parse(Element *elem)
6359         {
6360         return true;
6361         }
6362 };
6365 /**
6366  *
6367  */
6368 class TaskLink : public Task
6370 public:
6372     TaskLink(MakeBase &par) : Task(par)
6373         {
6374                 type = TASK_LINK; name = "link";
6375                 command = "g++";
6376                 }
6378     virtual ~TaskLink()
6379         {}
6381     virtual bool execute()
6382         {
6383         if (!listFiles(parent, fileSet))
6384             return false;
6385         String fileSetDir = fileSet.getDirectory();
6386         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6387         bool doit = false;
6388         String fullTarget = parent.resolve(fileName);
6389         String cmd = command;
6390         cmd.append(" -o ");
6391         cmd.append(fullTarget);
6392         cmd.append(" ");
6393         cmd.append(flags);
6394         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6395             {
6396             cmd.append(" ");
6397             String obj;
6398             if (fileSetDir.size()>0)
6399                             {
6400                                 obj.append(fileSetDir);
6401                 obj.append("/");
6402                 }
6403             obj.append(fileSet[i]);
6404             String fullObj = parent.resolve(obj);
6405             cmd.append(fullObj);
6406             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6407             //          fullObj.c_str());
6408             if (isNewerThan(fullObj, fullTarget))
6409                 doit = true;
6410             }
6411         cmd.append(" ");
6412         cmd.append(libs);
6413         if (!doit)
6414             {
6415             //trace("link not needed");
6416             return true;
6417             }
6418         //trace("LINK cmd:%s", cmd.c_str());
6421         String outString, errString;
6422         if (!executeCommand(cmd.c_str(), "", outString, errString))
6423             {
6424             error("LINK problem: %s", errString.c_str());
6425             return false;
6426             }
6427         return true;
6428         }
6430     virtual bool parse(Element *elem)
6431         {
6432         if (!parent.getAttribute(elem, "command", command))
6433             return false;
6434         if (!parent.getAttribute(elem, "out", fileName))
6435             return false;
6436             
6437         std::vector<Element *> children = elem->getChildren();
6438         for (unsigned int i=0 ; i<children.size() ; i++)
6439             {
6440             Element *child = children[i];
6441             String tagName = child->getName();
6442             if (tagName == "fileset")
6443                 {
6444                 if (!parseFileSet(child, parent, fileSet))
6445                     return false;
6446                 }
6447             else if (tagName == "flags")
6448                 {
6449                 if (!parent.getValue(child, flags))
6450                     return false;
6451                 flags = strip(flags);
6452                 }
6453             else if (tagName == "libs")
6454                 {
6455                 if (!parent.getValue(child, libs))
6456                     return false;
6457                 libs = strip(libs);
6458                 }
6459             }
6460         return true;
6461         }
6463 private:
6465     String command;
6466     String fileName;
6467     String flags;
6468     String libs;
6469     FileSet fileSet;
6471 };
6475 /**
6476  * Create a named directory
6477  */
6478 class TaskMakeFile : public Task
6480 public:
6482     TaskMakeFile(MakeBase &par) : Task(par)
6483         { type = TASK_MAKEFILE; name = "makefile"; }
6485     virtual ~TaskMakeFile()
6486         {}
6488     virtual bool execute()
6489         {
6490         status("          : %s", fileName.c_str());
6491         String fullName = parent.resolve(fileName);
6492         if (!isNewerThan(parent.getURI().getPath(), fullName))
6493             {
6494             //trace("skipped <makefile>");
6495             return true;
6496             }
6497         //trace("fullName:%s", fullName.c_str());
6498         FILE *f = fopen(fullName.c_str(), "w");
6499         if (!f)
6500             {
6501             error("<makefile> could not open %s for writing : %s",
6502                 fullName.c_str(), strerror(errno));
6503             return false;
6504             }
6505         for (unsigned int i=0 ; i<text.size() ; i++)
6506             fputc(text[i], f);
6507         fclose(f);
6508         return true;
6509         }
6511     virtual bool parse(Element *elem)
6512         {
6513         if (!parent.getAttribute(elem, "file", fileName))
6514             return false;
6515         if (fileName.size() == 0)
6516             {
6517             error("<makefile> requires 'file=\"filename\"' attribute");
6518             return false;
6519             }
6520         if (!parent.getValue(elem, text))
6521             return false;
6522         text = leftJustify(text);
6523         //trace("dirname:%s", dirName.c_str());
6524         return true;
6525         }
6527 private:
6529     String fileName;
6530     String text;
6531 };
6535 /**
6536  * Create a named directory
6537  */
6538 class TaskMkDir : public Task
6540 public:
6542     TaskMkDir(MakeBase &par) : Task(par)
6543         { type = TASK_MKDIR; name = "mkdir"; }
6545     virtual ~TaskMkDir()
6546         {}
6548     virtual bool execute()
6549         {
6550         status("          : %s", dirName.c_str());
6551         String fullDir = parent.resolve(dirName);
6552         //trace("fullDir:%s", fullDir.c_str());
6553         if (!createDirectory(fullDir))
6554             return false;
6555         return true;
6556         }
6558     virtual bool parse(Element *elem)
6559         {
6560         if (!parent.getAttribute(elem, "dir", dirName))
6561             return false;
6562         if (dirName.size() == 0)
6563             {
6564             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6565             return false;
6566             }
6567         trace("dirname:%s", dirName.c_str());
6568         return true;
6569         }
6571 private:
6573     String dirName;
6574 };
6578 /**
6579  * Create a named directory
6580  */
6581 class TaskMsgFmt: public Task
6583 public:
6585     TaskMsgFmt(MakeBase &par) : Task(par)
6586          {
6587                  type = TASK_MSGFMT;
6588                  name = "msgfmt";
6589                  command = "msgfmt";
6590                  }
6592     virtual ~TaskMsgFmt()
6593         {}
6595     virtual bool execute()
6596         {
6597         if (!listFiles(parent, fileSet))
6598             return false;
6599         String fileSetDir = fileSet.getDirectory();
6601         //trace("msgfmt: %d", fileSet.size());
6602         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6603             {
6604             String fileName = fileSet[i];
6605             if (getSuffix(fileName) != "po")
6606                 continue;
6607             String sourcePath;
6608                         if (fileSetDir.size()>0)
6609                             {
6610                             sourcePath.append(fileSetDir);
6611                 sourcePath.append("/");
6612                 }
6613             sourcePath.append(fileName);
6614             String fullSource = parent.resolve(sourcePath);
6616             String destPath;
6617                         if (toDirName.size()>0)
6618                             {
6619                             destPath.append(toDirName);
6620                 destPath.append("/");
6621                 }
6622             destPath.append(fileName);
6623             destPath[destPath.size()-2] = 'm';
6624             String fullDest = parent.resolve(destPath);
6626             if (!isNewerThan(fullSource, fullDest))
6627                 {
6628                 //trace("skip %s", fullSource.c_str());
6629                 continue;
6630                 }
6631                 
6632             String cmd = command;
6633             cmd.append(" ");
6634             cmd.append(fullSource);
6635             cmd.append(" -o ");
6636             cmd.append(fullDest);
6637             
6638             int pos = fullDest.find_last_of('/');
6639             if (pos>0)
6640                 {
6641                 String fullDestPath = fullDest.substr(0, pos);
6642                 if (!createDirectory(fullDestPath))
6643                     return false;
6644                 }
6648             String outString, errString;
6649             if (!executeCommand(cmd.c_str(), "", outString, errString))
6650                 {
6651                 error("<msgfmt> problem: %s", errString.c_str());
6652                 return false;
6653                 }
6654             }
6656         return true;
6657         }
6659     virtual bool parse(Element *elem)
6660         {
6661         if (!parent.getAttribute(elem, "todir", toDirName))
6662             return false;
6663             
6664         std::vector<Element *> children = elem->getChildren();
6665         for (unsigned int i=0 ; i<children.size() ; i++)
6666             {
6667             Element *child = children[i];
6668             String tagName = child->getName();
6669             if (tagName == "fileset")
6670                 {
6671                 if (!parseFileSet(child, parent, fileSet))
6672                     return false;
6673                 }
6674             }
6675         return true;
6676         }
6678 private:
6680     String command;
6681     String toDirName;
6682     FileSet fileSet;
6684 };
6690 /**
6691  *  Process an archive to allow random access
6692  */
6693 class TaskRanlib : public Task
6695 public:
6697     TaskRanlib(MakeBase &par) : Task(par)
6698         { type = TASK_RANLIB; name = "ranlib"; }
6700     virtual ~TaskRanlib()
6701         {}
6703     virtual bool execute()
6704         {
6705         String fullName = parent.resolve(fileName);
6706         //trace("fullDir:%s", fullDir.c_str());
6707         String cmd = "ranlib ";
6708         cmd.append(fullName);
6709         String outbuf, errbuf;
6710         if (!executeCommand(cmd, "", outbuf, errbuf))
6711             return false;
6712         return true;
6713         }
6715     virtual bool parse(Element *elem)
6716         {
6717         if (!parent.getAttribute(elem, "file", fileName))
6718             return false;
6719         if (fileName.size() == 0)
6720             {
6721             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6722             return false;
6723             }
6724         return true;
6725         }
6727 private:
6729     String fileName;
6730 };
6734 /**
6735  * Run the "ar" command to archive .o's into a .a
6736  */
6737 class TaskRC : public Task
6739 public:
6741     TaskRC(MakeBase &par) : Task(par)
6742         {
6743                 type = TASK_RC; name = "rc";
6744                 command = "windres -o";
6745                 }
6747     virtual ~TaskRC()
6748         {}
6750     virtual bool execute()
6751         {
6752         String fullFile = parent.resolve(fileName);
6753         String fullOut  = parent.resolve(outName);
6754         if (!isNewerThan(fullFile, fullOut))
6755             return true;
6756         String cmd = command;
6757         cmd.append(" ");
6758         cmd.append(fullOut);
6759         cmd.append(" ");
6760         cmd.append(flags);
6761         cmd.append(" ");
6762         cmd.append(fullFile);
6764         String outString, errString;
6765         if (!executeCommand(cmd.c_str(), "", outString, errString))
6766             {
6767             error("RC problem: %s", errString.c_str());
6768             return false;
6769             }
6770         return true;
6771         }
6773     virtual bool parse(Element *elem)
6774         {
6775         if (!parent.getAttribute(elem, "command", command))
6776             return false;
6777         if (!parent.getAttribute(elem, "file", fileName))
6778             return false;
6779         if (!parent.getAttribute(elem, "out", outName))
6780             return false;
6781         std::vector<Element *> children = elem->getChildren();
6782         for (unsigned int i=0 ; i<children.size() ; i++)
6783             {
6784             Element *child = children[i];
6785             String tagName = child->getName();
6786             if (tagName == "flags")
6787                 {
6788                 if (!parent.getValue(child, flags))
6789                     return false;
6790                 }
6791             }
6792         return true;
6793         }
6795 private:
6797     String command;
6798     String flags;
6799     String fileName;
6800     String outName;
6802 };
6806 /**
6807  * Strip an executable
6808  */
6809 class TaskStrip : public Task
6811 public:
6813     TaskStrip(MakeBase &par) : Task(par)
6814         { type = TASK_STRIP; name = "strip"; }
6816     virtual ~TaskStrip()
6817         {}
6819     virtual bool execute()
6820         {
6821         String fullName = parent.resolve(fileName);
6822         //trace("fullDir:%s", fullDir.c_str());
6823         String cmd = "strip ";
6824         cmd.append(fullName);
6826         String outbuf, errbuf;
6827         if (!executeCommand(cmd, "", outbuf, errbuf))
6828             return false;
6829         return true;
6830         }
6832     virtual bool parse(Element *elem)
6833         {
6834         if (!parent.getAttribute(elem, "file", fileName))
6835             return false;
6836         if (fileName.size() == 0)
6837             {
6838             error("<strip> requires 'file=\"fileNname\"' attribute");
6839             return false;
6840             }
6841         return true;
6842         }
6844 private:
6846     String fileName;
6847 };
6850 /**
6851  *
6852  */
6853 class TaskTstamp : public Task
6855 public:
6857     TaskTstamp(MakeBase &par) : Task(par)
6858         { type = TASK_TSTAMP; name = "tstamp"; }
6860     virtual ~TaskTstamp()
6861         {}
6863     virtual bool execute()
6864         {
6865         return true;
6866         }
6868     virtual bool parse(Element *elem)
6869         {
6870         //trace("tstamp parse");
6871         return true;
6872         }
6873 };
6877 /**
6878  *
6879  */
6880 Task *Task::createTask(Element *elem)
6882     String tagName = elem->getName();
6883     //trace("task:%s", tagName.c_str());
6884     Task *task = NULL;
6885     if (tagName == "ar")
6886         task = new TaskAr(parent);
6887     else if (tagName == "cc")
6888         task = new TaskCC(parent);
6889     else if (tagName == "copy")
6890         task = new TaskCopy(parent);
6891     else if (tagName == "delete")
6892         task = new TaskDelete(parent);
6893     else if (tagName == "jar")
6894         task = new TaskJar(parent);
6895     else if (tagName == "javac")
6896         task = new TaskJavac(parent);
6897     else if (tagName == "link")
6898         task = new TaskLink(parent);
6899     else if (tagName == "makefile")
6900         task = new TaskMakeFile(parent);
6901     else if (tagName == "mkdir")
6902         task = new TaskMkDir(parent);
6903     else if (tagName == "msgfmt")
6904         task = new TaskMsgFmt(parent);
6905     else if (tagName == "ranlib")
6906         task = new TaskRanlib(parent);
6907     else if (tagName == "rc")
6908         task = new TaskRC(parent);
6909     else if (tagName == "strip")
6910         task = new TaskStrip(parent);
6911     else if (tagName == "tstamp")
6912         task = new TaskTstamp(parent);
6913     else
6914         {
6915         error("Unknown task '%s'", tagName.c_str());
6916         return NULL;
6917         }
6919     if (!task->parse(elem))
6920         {
6921         delete task;
6922         return NULL;
6923         }
6924     return task;
6929 //########################################################################
6930 //# T A R G E T
6931 //########################################################################
6933 /**
6934  *
6935  */
6936 class Target : public MakeBase
6939 public:
6941     /**
6942      *
6943      */
6944     Target(Make &par) : parent(par)
6945         { init(); }
6947     /**
6948      *
6949      */
6950     Target(const Target &other) : parent(other.parent)
6951         { init(); assign(other); }
6953     /**
6954      *
6955      */
6956     Target &operator=(const Target &other)
6957         { init(); assign(other); return *this; }
6959     /**
6960      *
6961      */
6962     virtual ~Target()
6963         { cleanup() ; }
6966     /**
6967      *
6968      */
6969     virtual Make &getParent()
6970         { return parent; }
6972     /**
6973      *
6974      */
6975     virtual String getName()
6976         { return name; }
6978     /**
6979      *
6980      */
6981     virtual void setName(const String &val)
6982         { name = val; }
6984     /**
6985      *
6986      */
6987     virtual String getDescription()
6988         { return description; }
6990     /**
6991      *
6992      */
6993     virtual void setDescription(const String &val)
6994         { description = val; }
6996     /**
6997      *
6998      */
6999     virtual void addDependency(const String &val)
7000         { deps.push_back(val); }
7002     /**
7003      *
7004      */
7005     virtual void parseDependencies(const String &val)
7006         { deps = tokenize(val, ", "); }
7008     /**
7009      *
7010      */
7011     virtual std::vector<String> &getDependencies()
7012         { return deps; }
7014     /**
7015      *
7016      */
7017     virtual String getIf()
7018         { return ifVar; }
7020     /**
7021      *
7022      */
7023     virtual void setIf(const String &val)
7024         { ifVar = val; }
7026     /**
7027      *
7028      */
7029     virtual String getUnless()
7030         { return unlessVar; }
7032     /**
7033      *
7034      */
7035     virtual void setUnless(const String &val)
7036         { unlessVar = val; }
7038     /**
7039      *
7040      */
7041     virtual void addTask(Task *val)
7042         { tasks.push_back(val); }
7044     /**
7045      *
7046      */
7047     virtual std::vector<Task *> &getTasks()
7048         { return tasks; }
7050 private:
7052     void init()
7053         {
7054         }
7056     void cleanup()
7057         {
7058         tasks.clear();
7059         }
7061     void assign(const Target &other)
7062         {
7063         //parent      = other.parent;
7064         name        = other.name;
7065         description = other.description;
7066         ifVar       = other.ifVar;
7067         unlessVar   = other.unlessVar;
7068         deps        = other.deps;
7069         tasks       = other.tasks;
7070         }
7072     Make &parent;
7074     String name;
7076     String description;
7078     String ifVar;
7080     String unlessVar;
7082     std::vector<String> deps;
7084     std::vector<Task *> tasks;
7086 };
7095 //########################################################################
7096 //# M A K E
7097 //########################################################################
7100 /**
7101  *
7102  */
7103 class Make : public MakeBase
7106 public:
7108     /**
7109      *
7110      */
7111     Make()
7112         { init(); }
7114     /**
7115      *
7116      */
7117     Make(const Make &other)
7118         { assign(other); }
7120     /**
7121      *
7122      */
7123     Make &operator=(const Make &other)
7124         { assign(other); return *this; }
7126     /**
7127      *
7128      */
7129     virtual ~Make()
7130         { cleanup(); }
7132     /**
7133      *
7134      */
7135     virtual std::map<String, Target> &getTargets()
7136         { return targets; }
7139     /**
7140      *
7141      */
7142     bool run();
7144     /**
7145      *
7146      */
7147     bool run(const String &target);
7151 private:
7153     /**
7154      *
7155      */
7156     void init();
7158     /**
7159      *
7160      */
7161     void cleanup();
7163     /**
7164      *
7165      */
7166     void assign(const Make &other);
7168     /**
7169      *
7170      */
7171     bool executeTask(Task &task);
7174     /**
7175      *
7176      */
7177     bool executeTarget(Target &target,
7178                  std::set<String> &targetsCompleted);
7181     /**
7182      *
7183      */
7184     bool execute();
7186     /**
7187      *
7188      */
7189     bool checkTargetDependencies(Target &prop,
7190                     std::vector<String> &depList);
7192     /**
7193      *
7194      */
7195     bool parsePropertyFile(const String &fileName,
7196                                const String &prefix);
7198     /**
7199      *
7200      */
7201     bool parseProperty(Element *elem);
7203     /**
7204      *
7205      */
7206     bool parseTask(Task &task, Element *elem);
7208     /**
7209      *
7210      */
7211     bool parseFile();
7213     /**
7214      *
7215      */
7216     std::vector<String> glob(const String &pattern);
7219     //###############
7220     //# Fields
7221     //###############
7223     String projectName;
7225     String currentTarget;
7227     String defaultTarget;
7229     String specifiedTarget;
7231     String baseDir;
7233     String description;
7234     
7235     String envAlias;
7237     //std::vector<Property> properties;
7238     
7239     std::map<String, Target> targets;
7241     std::vector<Task *> allTasks;
7244 };
7247 //########################################################################
7248 //# C L A S S  M A I N T E N A N C E
7249 //########################################################################
7251 /**
7252  *
7253  */
7254 void Make::init()
7256     uri             = "build.xml";
7257     projectName     = "";
7258     currentTarget   = "";
7259     defaultTarget   = "";
7260     specifiedTarget = "";
7261     baseDir         = "";
7262     description     = "";
7263     envAlias        = "";
7264     properties.clear();
7265     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7266         delete allTasks[i];
7267     allTasks.clear();
7272 /**
7273  *
7274  */
7275 void Make::cleanup()
7277     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7278         delete allTasks[i];
7279     allTasks.clear();
7284 /**
7285  *
7286  */
7287 void Make::assign(const Make &other)
7289     uri              = other.uri;
7290     projectName      = other.projectName;
7291     currentTarget    = other.currentTarget;
7292     defaultTarget    = other.defaultTarget;
7293     specifiedTarget  = other.specifiedTarget;
7294     baseDir          = other.baseDir;
7295     description      = other.description;
7296     properties       = other.properties;
7301 //########################################################################
7302 //# U T I L I T Y    T A S K S
7303 //########################################################################
7305 /**
7306  *  Perform a file globbing
7307  */
7308 std::vector<String> Make::glob(const String &pattern)
7310     std::vector<String> res;
7311     return res;
7315 //########################################################################
7316 //# P U B L I C    A P I
7317 //########################################################################
7321 /**
7322  *
7323  */
7324 bool Make::executeTarget(Target &target,
7325              std::set<String> &targetsCompleted)
7328     String name = target.getName();
7330     //First get any dependencies for this target
7331     std::vector<String> deps = target.getDependencies();
7332     for (unsigned int i=0 ; i<deps.size() ; i++)
7333         {
7334         String dep = deps[i];
7335         //Did we do it already?  Skip
7336         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7337             continue;
7338             
7339         std::map<String, Target> &tgts =
7340                target.getParent().getTargets();
7341         std::map<String, Target>::iterator iter =
7342                tgts.find(dep);
7343         if (iter == tgts.end())
7344             {
7345             error("Target '%s' dependency '%s' not found",
7346                       name.c_str(),  dep.c_str());
7347             return false;
7348             }
7349         Target depTarget = iter->second;
7350         if (!executeTarget(depTarget, targetsCompleted))
7351             {
7352             return false;
7353             }
7354         }
7356     status("## Target : %s", name.c_str());
7358     //Now let's do the tasks
7359     std::vector<Task *> &tasks = target.getTasks();
7360     for (unsigned int i=0 ; i<tasks.size() ; i++)
7361         {
7362         Task *task = tasks[i];
7363         status("---- task : %s", task->getName().c_str());
7364         if (!task->execute())
7365             {
7366             return false;
7367             }
7368         }
7369         
7370     targetsCompleted.insert(name);
7371     
7372     return true;
7377 /**
7378  *  Main execute() method.  Start here and work
7379  *  up the dependency tree 
7380  */
7381 bool Make::execute()
7383     status("######## EXECUTE");
7385     //Determine initial target
7386     if (specifiedTarget.size()>0)
7387         {
7388         currentTarget = specifiedTarget;
7389         }
7390     else if (defaultTarget.size()>0)
7391         {
7392         currentTarget = defaultTarget;
7393         }
7394     else
7395         {
7396         error("execute: no specified or default target requested");
7397         return false;
7398         }
7400     std::map<String, Target>::iterator iter =
7401                    targets.find(currentTarget);
7402     if (iter == targets.end())
7403         {
7404         error("Initial target '%s' not found",
7405                          currentTarget.c_str());
7406         return false;
7407         }
7408         
7409     //Now run
7410     Target target = iter->second;
7411     std::set<String> targetsCompleted;
7412     if (!executeTarget(target, targetsCompleted))
7413         {
7414         return false;
7415         }
7417     status("######## EXECUTE COMPLETE");
7418     return true;
7424 /**
7425  *
7426  */
7427 bool Make::checkTargetDependencies(Target &target, 
7428                             std::vector<String> &depList)
7430     String tgtName = target.getName().c_str();
7431     depList.push_back(tgtName);
7433     std::vector<String> deps = target.getDependencies();
7434     for (unsigned int i=0 ; i<deps.size() ; i++)
7435         {
7436         String dep = deps[i];
7437         //First thing entered was the starting Target
7438         if (dep == depList[0])
7439             {
7440             error("Circular dependency '%s' found at '%s'",
7441                       dep.c_str(), tgtName.c_str());
7442             std::vector<String>::iterator diter;
7443             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7444                 {
7445                 error("  %s", diter->c_str());
7446                 }
7447             return false;
7448             }
7450         std::map<String, Target> &tgts =
7451                   target.getParent().getTargets();
7452         std::map<String, Target>::iterator titer = tgts.find(dep);
7453         if (titer == tgts.end())
7454             {
7455             error("Target '%s' dependency '%s' not found",
7456                       tgtName.c_str(), dep.c_str());
7457             return false;
7458             }
7459         if (!checkTargetDependencies(titer->second, depList))
7460             {
7461             return false;
7462             }
7463         }
7464     return true;
7471 static int getword(int pos, const String &inbuf, String &result)
7473     int p = pos;
7474     int len = (int)inbuf.size();
7475     String val;
7476     while (p < len)
7477         {
7478         char ch = inbuf[p];
7479         if (!isalnum(ch) && ch!='.' && ch!='_')
7480             break;
7481         val.push_back(ch);
7482         p++;
7483         }
7484     result = val;
7485     return p;
7491 /**
7492  *
7493  */
7494 bool Make::parsePropertyFile(const String &fileName,
7495                              const String &prefix)
7497     FILE *f = fopen(fileName.c_str(), "r");
7498     if (!f)
7499         {
7500         error("could not open property file %s", fileName.c_str());
7501         return false;
7502         }
7503     int linenr = 0;
7504     while (!feof(f))
7505         {
7506         char buf[256];
7507         if (!fgets(buf, 255, f))
7508             break;
7509         linenr++;
7510         String s = buf;
7511         s = trim(s);
7512         int len = s.size();
7513         if (len == 0)
7514             continue;
7515         if (s[0] == '#')
7516             continue;
7517         String key;
7518         String val;
7519         int p = 0;
7520         int p2 = getword(p, s, key);
7521         if (p2 <= p)
7522             {
7523             error("property file %s, line %d: expected keyword",
7524                                 fileName.c_str(), linenr);
7525                         return false;
7526                         }
7527                 if (prefix.size() > 0)
7528                     {
7529                     key.insert(0, prefix);
7530                     }
7532         //skip whitespace
7533                 for (p=p2 ; p<len ; p++)
7534                     if (!isspace(s[p]))
7535                         break;
7537         if (p>=len || s[p]!='=')
7538             {
7539             error("property file %s, line %d: expected '='",
7540                                 fileName.c_str(), linenr);
7541             return false;
7542             }
7543         p++;
7545         //skip whitespace
7546                 for ( ; p<len ; p++)
7547                     if (!isspace(s[p]))
7548                         break;
7550         /* This way expects a word after the =
7551                 p2 = getword(p, s, val);
7552         if (p2 <= p)
7553             {
7554             error("property file %s, line %d: expected value",
7555                                 fileName.c_str(), linenr);
7556                         return false;
7557                         }
7558                 */
7559         // This way gets the rest of the line after the =
7560                 if (p>=len)
7561             {
7562             error("property file %s, line %d: expected value",
7563                                 fileName.c_str(), linenr);
7564                         return false;
7565                         }
7566         val = s.substr(p);
7567                 if (key.size()==0 || val.size()==0)
7568                     continue;
7570         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7571             properties[key] = val;
7572         }
7573     fclose(f);
7574     return true;
7580 /**
7581  *
7582  */
7583 bool Make::parseProperty(Element *elem)
7585     std::vector<Attribute> &attrs = elem->getAttributes();
7586     for (unsigned int i=0 ; i<attrs.size() ; i++)
7587         {
7588         String attrName = attrs[i].getName();
7589         String attrVal  = attrs[i].getValue();
7591         if (attrName == "name")
7592             {
7593             String val;
7594                         if (!getAttribute(elem, "value", val))
7595                             return false;
7596             if (val.size() > 0)
7597                 {
7598                 properties[attrVal] = val;
7599                 continue;
7600                 }
7601             if (!getAttribute(elem, "location", val))
7602                 return false;
7603             if (val.size() > 0)
7604                 {
7605                 //TODO:  process a path relative to build.xml
7606                 properties[attrVal] = val;
7607                 continue;
7608                 }
7609             }
7610         else if (attrName == "file")
7611             {
7612             String prefix;
7613                         if (!getAttribute(elem, "prefix", prefix))
7614                             return false;
7615             if (prefix.size() > 0)
7616                 {
7617                 if (prefix[prefix.size()-1] != '.')
7618                     prefix.push_back('.');
7619                 }
7620             if (!parsePropertyFile(attrName, prefix))
7621                 return false;
7622             }
7623         else if (attrName == "environment")
7624             {
7625             if (envAlias.size() > 0)
7626                 {
7627                 error("environment property can only be set once");
7628                 return false;
7629                 }
7630             envAlias = attrVal;
7631             }
7632         }
7634     return true;
7640 /**
7641  *
7642  */
7643 bool Make::parseFile()
7645     status("######## PARSE");
7647     Parser parser;
7648     Element *root = parser.parseFile(uri.getNativePath());
7649     if (!root)
7650         {
7651         error("Could not open %s for reading",
7652                       uri.getNativePath().c_str());
7653         return false;
7654         }
7656     if (root->getChildren().size()==0 ||
7657         root->getChildren()[0]->getName()!="project")
7658         {
7659         error("Main xml element should be <project>");
7660         delete root;
7661         return false;
7662         }
7664     //########## Project attributes
7665     Element *project = root->getChildren()[0];
7666     String s = project->getAttribute("name");
7667     if (s.size() > 0)
7668         projectName = s;
7669     s = project->getAttribute("default");
7670     if (s.size() > 0)
7671         defaultTarget = s;
7672     s = project->getAttribute("basedir");
7673     if (s.size() > 0)
7674         baseDir = s;
7676     //######### PARSE MEMBERS
7677     std::vector<Element *> children = project->getChildren();
7678     for (unsigned int i=0 ; i<children.size() ; i++)
7679         {
7680         Element *elem = children[i];
7681         String tagName = elem->getName();
7683         //########## DESCRIPTION
7684         if (tagName == "description")
7685             {
7686             description = parser.trim(elem->getValue());
7687             }
7689         //######### PROPERTY
7690         else if (tagName == "property")
7691             {
7692             if (!parseProperty(elem))
7693                 return false;
7694             }
7696         //######### TARGET
7697         else if (tagName == "target")
7698             {
7699             String tname   = elem->getAttribute("name");
7700             String tdesc   = elem->getAttribute("description");
7701             String tdeps   = elem->getAttribute("depends");
7702             String tif     = elem->getAttribute("if");
7703             String tunless = elem->getAttribute("unless");
7704             Target target(*this);
7705             target.setName(tname);
7706             target.setDescription(tdesc);
7707             target.parseDependencies(tdeps);
7708             target.setIf(tif);
7709             target.setUnless(tunless);
7710             std::vector<Element *> telems = elem->getChildren();
7711             for (unsigned int i=0 ; i<telems.size() ; i++)
7712                 {
7713                 Element *telem = telems[i];
7714                 Task breeder(*this);
7715                 Task *task = breeder.createTask(telem);
7716                 if (!task)
7717                     return false;
7718                 allTasks.push_back(task);
7719                 target.addTask(task);
7720                 }
7722             //Check name
7723             if (tname.size() == 0)
7724                 {
7725                 error("no name for target");
7726                 return false;
7727                 }
7728             //Check for duplicate name
7729             if (targets.find(tname) != targets.end())
7730                 {
7731                 error("target '%s' already defined", tname.c_str());
7732                 return false;
7733                 }
7734             //more work than targets[tname]=target, but avoids default allocator
7735             targets.insert(std::make_pair<String, Target>(tname, target));
7736             }
7738         }
7740     std::map<String, Target>::iterator iter;
7741     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
7742         {
7743         Target tgt = iter->second;
7744         std::vector<String> depList;
7745         if (!checkTargetDependencies(tgt, depList))
7746             {
7747             return false;
7748             }
7749         }
7752     delete root;
7753     status("######## PARSE COMPLETE");
7754     return true;
7758 /**
7759  *
7760  */
7761 bool Make::run()
7763     if (!parseFile())
7764         return false;
7765     if (!execute())
7766         return false;
7767     return true;
7771 /**
7772  *
7773  */
7774 bool Make::run(const String &target)
7776     status("##################################");
7777     status("#   BuildTool");
7778     status("#   version 0.3");
7779     status("#   16 Nov 06");
7780     status("##################################");
7781     specifiedTarget = target;
7782     if (!run())
7783         return false;
7784     status("##################################");
7785     status("#   BuildTool Completed");
7786     status("##################################");
7787     return true;
7796 }// namespace buildtool
7797 //########################################################################
7798 //# M A I N
7799 //########################################################################
7801 /**
7802  *  Format an error message in printf() style
7803  */
7804 static void error(char *fmt, ...)
7806     va_list ap;
7807     va_start(ap, fmt);
7808     fprintf(stderr, "BuildTool error: ");
7809     vfprintf(stderr, fmt, ap);
7810     fprintf(stderr, "\n");
7811     va_end(ap);
7815 /**
7816  * Compare a buffer with a key, for the length of the key
7817  */
7818 static bool sequ(const buildtool::String &buf, char *key)
7820     for (int i=0 ; key[i] ; i++)
7821         {
7822         if (key[i] != buf[i])
7823             return false;
7824         }        
7825     return true;
7828 /**
7829  * Parse the command-line args, get our options,
7830  * and run this thing
7831  */   
7832 static bool parseOptions(int argc, char **argv)
7834     if (argc < 1)
7835         {
7836         error("Cannot parse arguments");
7837         return false;
7838         }
7840     buildtool::String buildFile;
7841     buildtool::String target;
7843     //char *progName = argv[0];
7844     for (int i=1 ; i<argc ; i++)
7845         {
7846         buildtool::String arg = argv[i];
7847         if (sequ(arg, "--"))
7848             {
7849             if (sequ(arg, "--file=") && arg.size()>7)
7850                 {
7851                 buildFile = arg.substr(7, arg.size()-7);
7852                 }
7853             else
7854                 {
7855                 error("Unknown option:%s", arg.c_str());
7856                 return false;
7857                 }
7858             }
7859         else if (sequ(arg, "-"))
7860             {
7861             for (unsigned int p=1 ; p<arg.size() ; p++)
7862                 {
7863                 int ch = arg[p];
7864                 if (0)//put options here
7865                     {
7866                     }
7867                 else
7868                     {
7869                     error("Unknown option '%c'", ch);
7870                     return false;
7871                     }
7872                 }
7873             }
7874         else
7875             {
7876             target = arg;
7877             }
7878         }
7880     //We have the options.  Now execute them
7881     buildtool::Make make;
7882     if (buildFile.size() > 0)
7883         {
7884         make.setURI(buildFile);
7885         }
7886     if (!make.run(target))
7887         return false;
7889     return true;
7895 /*
7896 static bool runMake()
7898     buildtool::Make make;
7899     if (!make.run())
7900         return false;
7901     return true;
7905 static bool pkgConfigTest()
7907     buildtool::PkgConfig pkgConfig;
7908     if (!pkgConfig.readFile("gtk+-2.0.pc"))
7909         return false;
7910     return true;
7915 static bool depTest()
7917     buildtool::DepTool deptool;
7918     deptool.setSourceDirectory("/dev/ink/inkscape/src");
7919     if (!deptool.generateDependencies("build.dep"))
7920         return false;
7921     std::vector<buildtool::DepRec> res =
7922                deptool.loadDepFile("build.dep");
7923         if (res.size() == 0)
7924         return false;
7925     return true;
7928 static bool popenTest()
7930     buildtool::Make make;
7931     buildtool::String out, err;
7932         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
7933     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
7934     return true;
7938 static bool propFileTest()
7940     buildtool::Make make;
7941     make.parsePropertyFile("test.prop", "test.");
7942     return true;
7944 */
7946 int main(int argc, char **argv)
7949     if (!parseOptions(argc, argv))
7950         return 1;
7951     /*
7952     if (!popenTest())
7953         return 1;
7955     if (!depTest())
7956         return 1;
7957     if (!propFileTest())
7958         return 1;
7959     if (runMake())
7960         return 1;
7961     */
7962     return 0;
7966 //########################################################################
7967 //# E N D 
7968 //########################################################################