Code

minor typo in comparison
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2008 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 btool.exe
28  * (or whatever your compiler might be)
29  * Then
30  * btool
31  * or
32  * btool {target}
33  *
34  * Note: if you are using MinGW, and a not very recent version of it,
35  * gettimeofday() might be missing.  If so, just build this file with
36  * this command:
37  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38  *
39  */
41 #define BUILDTOOL_VERSION  "BuildTool v0.9.4"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
57 #include <algorithm>
60 #ifdef __WIN32__
61 #include <windows.h>
62 #else
63 #include <sys/wait.h>
64 #endif
67 #include <errno.h>
70 //########################################################################
71 //# Definition of gettimeofday() for those who don't have it
72 //########################################################################
73 #ifdef NEED_GETTIMEOFDAY
74 #include <sys/timeb.h>
76 struct timezone {
77       int tz_minuteswest; /* minutes west of Greenwich */
78       int tz_dsttime;     /* type of dst correction */
79     };
81 static int gettimeofday (struct timeval *tv, struct timezone *tz)
82 {
83    struct _timeb tb;
85    if (!tv)
86       return (-1);
88     _ftime (&tb);
89     tv->tv_sec  = tb.time;
90     tv->tv_usec = tb.millitm * 1000 + 500;
91     if (tz)
92         {
93         tz->tz_minuteswest = -60 * _timezone;
94         tz->tz_dsttime = _daylight;
95         }
96     return 0;
97 }
99 #endif
107 namespace buildtool
113 //########################################################################
114 //########################################################################
115 //##  R E G E X P
116 //########################################################################
117 //########################################################################
119 /**
120  * This is the T-Rex regular expression library, which we
121  * gratefully acknowledge.  It's clean code and small size allow
122  * us to embed it in BuildTool without adding a dependency
123  *
124  */    
126 //begin trex.h
128 #ifndef _TREX_H_
129 #define _TREX_H_
130 /***************************************************************
131     T-Rex a tiny regular expression library
133     Copyright (C) 2003-2006 Alberto Demichelis
135     This software is provided 'as-is', without any express 
136     or implied warranty. In no event will the authors be held 
137     liable for any damages arising from the use of this software.
139     Permission is granted to anyone to use this software for 
140     any purpose, including commercial applications, and to alter
141     it and redistribute it freely, subject to the following restrictions:
143         1. The origin of this software must not be misrepresented;
144         you must not claim that you wrote the original software.
145         If you use this software in a product, an acknowledgment
146         in the product documentation would be appreciated but
147         is not required.
149         2. Altered source versions must be plainly marked as such,
150         and must not be misrepresented as being the original software.
152         3. This notice may not be removed or altered from any
153         source distribution.
155 ****************************************************************/
157 #ifdef _UNICODE
158 #define TRexChar unsigned short
159 #define MAX_CHAR 0xFFFF
160 #define _TREXC(c) L##c 
161 #define trex_strlen wcslen
162 #define trex_printf wprintf
163 #else
164 #define TRexChar char
165 #define MAX_CHAR 0xFF
166 #define _TREXC(c) (c) 
167 #define trex_strlen strlen
168 #define trex_printf printf
169 #endif
171 #ifndef TREX_API
172 #define TREX_API extern
173 #endif
175 #define TRex_True 1
176 #define TRex_False 0
178 typedef unsigned int TRexBool;
179 typedef struct TRex TRex;
181 typedef struct {
182     const TRexChar *begin;
183     int len;
184 } TRexMatch;
186 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
187 TREX_API void trex_free(TRex *exp);
188 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
189 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
191 TREX_API int trex_getsubexpcount(TRex* exp);
192 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
194 #endif
196 //end trex.h
198 //start trex.c
201 #include <stdio.h>
202 #include <string>
204 /* see copyright notice in trex.h */
205 #include <string.h>
206 #include <stdlib.h>
207 #include <ctype.h>
208 #include <setjmp.h>
209 //#include "trex.h"
211 #ifdef _UINCODE
212 #define scisprint iswprint
213 #define scstrlen wcslen
214 #define scprintf wprintf
215 #define _SC(x) L(x)
216 #else
217 #define scisprint isprint
218 #define scstrlen strlen
219 #define scprintf printf
220 #define _SC(x) (x)
221 #endif
223 #ifdef _DEBUG
224 #include <stdio.h>
226 static const TRexChar *g_nnames[] =
228     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
229     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
230     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
231     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
232 };
234 #endif
235 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
236 #define OP_OR            (MAX_CHAR+2)
237 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
238 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
239 #define OP_DOT            (MAX_CHAR+5)
240 #define OP_CLASS        (MAX_CHAR+6)
241 #define OP_CCLASS        (MAX_CHAR+7)
242 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
243 #define OP_RANGE        (MAX_CHAR+9)
244 #define OP_CHAR            (MAX_CHAR+10)
245 #define OP_EOL            (MAX_CHAR+11)
246 #define OP_BOL            (MAX_CHAR+12)
247 #define OP_WB            (MAX_CHAR+13)
249 #define TREX_SYMBOL_ANY_CHAR ('.')
250 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
252 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
253 #define TREX_SYMBOL_BRANCH ('|')
254 #define TREX_SYMBOL_END_OF_STRING ('$')
255 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
256 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
259 typedef int TRexNodeType;
261 typedef struct tagTRexNode{
262     TRexNodeType type;
263     int left;
264     int right;
265     int next;
266 }TRexNode;
268 struct TRex{
269     const TRexChar *_eol;
270     const TRexChar *_bol;
271     const TRexChar *_p;
272     int _first;
273     int _op;
274     TRexNode *_nodes;
275     int _nallocated;
276     int _nsize;
277     int _nsubexpr;
278     TRexMatch *_matches;
279     int _currsubexp;
280     void *_jmpbuf;
281     const TRexChar **_error;
282 };
284 static int trex_list(TRex *exp);
286 static int trex_newnode(TRex *exp, TRexNodeType type)
288     TRexNode n;
289     int newid;
290     n.type = type;
291     n.next = n.right = n.left = -1;
292     if(type == OP_EXPR)
293         n.right = exp->_nsubexpr++;
294     if(exp->_nallocated < (exp->_nsize + 1)) {
295         //int oldsize = exp->_nallocated;
296         exp->_nallocated *= 2;
297         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
298     }
299     exp->_nodes[exp->_nsize++] = n;
300     newid = exp->_nsize - 1;
301     return (int)newid;
304 static void trex_error(TRex *exp,const TRexChar *error)
306     if(exp->_error) *exp->_error = error;
307     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
310 static void trex_expect(TRex *exp, int n){
311     if((*exp->_p) != n) 
312         trex_error(exp, _SC("expected paren"));
313     exp->_p++;
316 static TRexChar trex_escapechar(TRex *exp)
318     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
319         exp->_p++;
320         switch(*exp->_p) {
321         case 'v': exp->_p++; return '\v';
322         case 'n': exp->_p++; return '\n';
323         case 't': exp->_p++; return '\t';
324         case 'r': exp->_p++; return '\r';
325         case 'f': exp->_p++; return '\f';
326         default: return (*exp->_p++);
327         }
328     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
329     return (*exp->_p++);
332 static int trex_charclass(TRex *exp,int classid)
334     int n = trex_newnode(exp,OP_CCLASS);
335     exp->_nodes[n].left = classid;
336     return n;
339 static int trex_charnode(TRex *exp,TRexBool isclass)
341     TRexChar t;
342     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
343         exp->_p++;
344         switch(*exp->_p) {
345             case 'n': exp->_p++; return trex_newnode(exp,'\n');
346             case 't': exp->_p++; return trex_newnode(exp,'\t');
347             case 'r': exp->_p++; return trex_newnode(exp,'\r');
348             case 'f': exp->_p++; return trex_newnode(exp,'\f');
349             case 'v': exp->_p++; return trex_newnode(exp,'\v');
350             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
351             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
352             case 'p': case 'P': case 'l': case 'u': 
353                 {
354                 t = *exp->_p; exp->_p++; 
355                 return trex_charclass(exp,t);
356                 }
357             case 'b': 
358             case 'B':
359                 if(!isclass) {
360                     int node = trex_newnode(exp,OP_WB);
361                     exp->_nodes[node].left = *exp->_p;
362                     exp->_p++; 
363                     return node;
364                 } //else default
365             default: 
366                 t = *exp->_p; exp->_p++; 
367                 return trex_newnode(exp,t);
368         }
369     }
370     else if(!scisprint(*exp->_p)) {
371         
372         trex_error(exp,_SC("letter expected"));
373     }
374     t = *exp->_p; exp->_p++; 
375     return trex_newnode(exp,t);
377 static int trex_class(TRex *exp)
379     int ret = -1;
380     int first = -1,chain;
381     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
382         ret = trex_newnode(exp,OP_NCLASS);
383         exp->_p++;
384     }else ret = trex_newnode(exp,OP_CLASS);
385     
386     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
387     chain = ret;
388     while(*exp->_p != ']' && exp->_p != exp->_eol) {
389         if(*exp->_p == '-' && first != -1){ 
390             int r,t;
391             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
392             r = trex_newnode(exp,OP_RANGE);
393             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
394             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
395             exp->_nodes[r].left = exp->_nodes[first].type;
396             t = trex_escapechar(exp);
397             exp->_nodes[r].right = t;
398             exp->_nodes[chain].next = r;
399             chain = r;
400             first = -1;
401         }
402         else{
403             if(first!=-1){
404                 int c = first;
405                 exp->_nodes[chain].next = c;
406                 chain = c;
407                 first = trex_charnode(exp,TRex_True);
408             }
409             else{
410                 first = trex_charnode(exp,TRex_True);
411             }
412         }
413     }
414     if(first!=-1){
415         int c = first;
416         exp->_nodes[chain].next = c;
417         chain = c;
418         first = -1;
419     }
420     /* hack? */
421     exp->_nodes[ret].left = exp->_nodes[ret].next;
422     exp->_nodes[ret].next = -1;
423     return ret;
426 static int trex_parsenumber(TRex *exp)
428     int ret = *exp->_p-'0';
429     int positions = 10;
430     exp->_p++;
431     while(isdigit(*exp->_p)) {
432         ret = ret*10+(*exp->_p++-'0');
433         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
434         positions *= 10;
435     };
436     return ret;
439 static int trex_element(TRex *exp)
441     int ret = -1;
442     switch(*exp->_p)
443     {
444     case '(': {
445         int expr,newn;
446         exp->_p++;
449         if(*exp->_p =='?') {
450             exp->_p++;
451             trex_expect(exp,':');
452             expr = trex_newnode(exp,OP_NOCAPEXPR);
453         }
454         else
455             expr = trex_newnode(exp,OP_EXPR);
456         newn = trex_list(exp);
457         exp->_nodes[expr].left = newn;
458         ret = expr;
459         trex_expect(exp,')');
460               }
461               break;
462     case '[':
463         exp->_p++;
464         ret = trex_class(exp);
465         trex_expect(exp,']');
466         break;
467     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
468     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
469     default:
470         ret = trex_charnode(exp,TRex_False);
471         break;
472     }
474     {
475         int op;
476         TRexBool isgreedy = TRex_False;
477         unsigned short p0 = 0, p1 = 0;
478         switch(*exp->_p){
479             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
481             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
482             case '{':
483                 exp->_p++;
484                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
485                 p0 = (unsigned short)trex_parsenumber(exp);
486                 /*******************************/
487                 switch(*exp->_p) {
488             case '}':
489                 p1 = p0; exp->_p++;
490                 break;
491             case ',':
492                 exp->_p++;
493                 p1 = 0xFFFF;
494                 if(isdigit(*exp->_p)){
495                     p1 = (unsigned short)trex_parsenumber(exp);
496                 }
497                 trex_expect(exp,'}');
498                 break;
499             default:
500                 trex_error(exp,_SC(", or } expected"));
501         }
502         /*******************************/
503         isgreedy = TRex_True; 
504         break;
506         }
507         if(isgreedy) {
508             int nnode = trex_newnode(exp,OP_GREEDY);
509             op = OP_GREEDY;
510             exp->_nodes[nnode].left = ret;
511             exp->_nodes[nnode].right = ((p0)<<16)|p1;
512             ret = nnode;
513         }
514     }
515     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')) {
516         int nnode = trex_element(exp);
517         exp->_nodes[ret].next = nnode;
518     }
520     return ret;
523 static int trex_list(TRex *exp)
525     int ret=-1,e;
526     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
527         exp->_p++;
528         ret = trex_newnode(exp,OP_BOL);
529     }
530     e = trex_element(exp);
531     if(ret != -1) {
532         exp->_nodes[ret].next = e;
533     }
534     else ret = e;
536     if(*exp->_p == TREX_SYMBOL_BRANCH) {
537         int temp,tright;
538         exp->_p++;
539         temp = trex_newnode(exp,OP_OR);
540         exp->_nodes[temp].left = ret;
541         tright = trex_list(exp);
542         exp->_nodes[temp].right = tright;
543         ret = temp;
544     }
545     return ret;
548 static TRexBool trex_matchcclass(int cclass,TRexChar c)
550     switch(cclass) {
551     case 'a': return isalpha(c)?TRex_True:TRex_False;
552     case 'A': return !isalpha(c)?TRex_True:TRex_False;
553     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
554     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
555     case 's': return isspace(c)?TRex_True:TRex_False;
556     case 'S': return !isspace(c)?TRex_True:TRex_False;
557     case 'd': return isdigit(c)?TRex_True:TRex_False;
558     case 'D': return !isdigit(c)?TRex_True:TRex_False;
559     case 'x': return isxdigit(c)?TRex_True:TRex_False;
560     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
561     case 'c': return iscntrl(c)?TRex_True:TRex_False;
562     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
563     case 'p': return ispunct(c)?TRex_True:TRex_False;
564     case 'P': return !ispunct(c)?TRex_True:TRex_False;
565     case 'l': return islower(c)?TRex_True:TRex_False;
566     case 'u': return isupper(c)?TRex_True:TRex_False;
567     }
568     return TRex_False; /*cannot happen*/
571 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
573     do {
574         switch(node->type) {
575             case OP_RANGE:
576                 if(c >= node->left && c <= node->right) return TRex_True;
577                 break;
578             case OP_CCLASS:
579                 if(trex_matchcclass(node->left,c)) return TRex_True;
580                 break;
581             default:
582                 if(c == node->type)return TRex_True;
583         }
584     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
585     return TRex_False;
588 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
590     
591     TRexNodeType type = node->type;
592     switch(type) {
593     case OP_GREEDY: {
594         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
595         TRexNode *greedystop = NULL;
596         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
597         const TRexChar *s=str, *good = str;
599         if(node->next != -1) {
600             greedystop = &exp->_nodes[node->next];
601         }
602         else {
603             greedystop = next;
604         }
606         while((nmaches == 0xFFFF || nmaches < p1)) {
608             const TRexChar *stop;
609             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
610                 break;
611             nmaches++;
612             good=s;
613             if(greedystop) {
614                 //checks that 0 matches satisfy the expression(if so skips)
615                 //if not would always stop(for instance if is a '?')
616                 if(greedystop->type != OP_GREEDY ||
617                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
618                 {
619                     TRexNode *gnext = NULL;
620                     if(greedystop->next != -1) {
621                         gnext = &exp->_nodes[greedystop->next];
622                     }else if(next && next->next != -1){
623                         gnext = &exp->_nodes[next->next];
624                     }
625                     stop = trex_matchnode(exp,greedystop,s,gnext);
626                     if(stop) {
627                         //if satisfied stop it
628                         if(p0 == p1 && p0 == nmaches) break;
629                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
630                         else if(nmaches >= p0 && nmaches <= p1) break;
631                     }
632                 }
633             }
634             
635             if(s >= exp->_eol)
636                 break;
637         }
638         if(p0 == p1 && p0 == nmaches) return good;
639         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
640         else if(nmaches >= p0 && nmaches <= p1) return good;
641         return NULL;
642     }
643     case OP_OR: {
644             const TRexChar *asd = str;
645             TRexNode *temp=&exp->_nodes[node->left];
646             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
647                 if(temp->next != -1)
648                     temp = &exp->_nodes[temp->next];
649                 else
650                     return asd;
651             }
652             asd = str;
653             temp = &exp->_nodes[node->right];
654             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
655                 if(temp->next != -1)
656                     temp = &exp->_nodes[temp->next];
657                 else
658                     return asd;
659             }
660             return NULL;
661             break;
662     }
663     case OP_EXPR:
664     case OP_NOCAPEXPR:{
665             TRexNode *n = &exp->_nodes[node->left];
666             const TRexChar *cur = str;
667             int capture = -1;
668             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
669                 capture = exp->_currsubexp;
670                 exp->_matches[capture].begin = cur;
671                 exp->_currsubexp++;
672             }
673             
674             do {
675                 TRexNode *subnext = NULL;
676                 if(n->next != -1) {
677                     subnext = &exp->_nodes[n->next];
678                 }else {
679                     subnext = next;
680                 }
681                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
682                     if(capture != -1){
683                         exp->_matches[capture].begin = 0;
684                         exp->_matches[capture].len = 0;
685                     }
686                     return NULL;
687                 }
688             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
690             if(capture != -1) 
691                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
692             return cur;
693     }                 
694     case OP_WB:
695         if((str == exp->_bol && !isspace(*str))
696          || (str == exp->_eol && !isspace(*(str-1)))
697          || (!isspace(*str) && isspace(*(str+1)))
698          || (isspace(*str) && !isspace(*(str+1))) ) {
699             return (node->left == 'b')?str:NULL;
700         }
701         return (node->left == 'b')?NULL:str;
702     case OP_BOL:
703         if(str == exp->_bol) return str;
704         return NULL;
705     case OP_EOL:
706         if(str == exp->_eol) return str;
707         return NULL;
708     case OP_DOT:{
709         *str++;
710                 }
711         return str;
712     case OP_NCLASS:
713     case OP_CLASS:
714         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
715             *str++;
716             return str;
717         }
718         return NULL;
719     case OP_CCLASS:
720         if(trex_matchcclass(node->left,*str)) {
721             *str++;
722             return str;
723         }
724         return NULL;
725     default: /* char */
726         if(*str != node->type) return NULL;
727         *str++;
728         return str;
729     }
730     return NULL;
733 /* public api */
734 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
736     TRex *exp = (TRex *)malloc(sizeof(TRex));
737     exp->_eol = exp->_bol = NULL;
738     exp->_p = pattern;
739     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
740     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
741     exp->_nsize = 0;
742     exp->_matches = 0;
743     exp->_nsubexpr = 0;
744     exp->_first = trex_newnode(exp,OP_EXPR);
745     exp->_error = error;
746     exp->_jmpbuf = malloc(sizeof(jmp_buf));
747     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
748         int res = trex_list(exp);
749         exp->_nodes[exp->_first].left = res;
750         if(*exp->_p!='\0')
751             trex_error(exp,_SC("unexpected character"));
752 #ifdef _DEBUG
753         {
754             int nsize,i;
755             TRexNode *t;
756             nsize = exp->_nsize;
757             t = &exp->_nodes[0];
758             scprintf(_SC("\n"));
759             for(i = 0;i < nsize; i++) {
760                 if(exp->_nodes[i].type>MAX_CHAR)
761                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
762                 else
763                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
764                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
765             }
766             scprintf(_SC("\n"));
767         }
768 #endif
769         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
770         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
771     }
772     else{
773         trex_free(exp);
774         return NULL;
775     }
776     return exp;
779 void trex_free(TRex *exp)
781     if(exp)    {
782         if(exp->_nodes) free(exp->_nodes);
783         if(exp->_jmpbuf) free(exp->_jmpbuf);
784         if(exp->_matches) free(exp->_matches);
785         free(exp);
786     }
789 TRexBool trex_match(TRex* exp,const TRexChar* text)
791     const TRexChar* res = NULL;
792     exp->_bol = text;
793     exp->_eol = text + scstrlen(text);
794     exp->_currsubexp = 0;
795     res = trex_matchnode(exp,exp->_nodes,text,NULL);
796     if(res == NULL || res != exp->_eol)
797         return TRex_False;
798     return TRex_True;
801 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
803     const TRexChar *cur = NULL;
804     int node = exp->_first;
805     if(text_begin >= text_end) return TRex_False;
806     exp->_bol = text_begin;
807     exp->_eol = text_end;
808     do {
809         cur = text_begin;
810         while(node != -1) {
811             exp->_currsubexp = 0;
812             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
813             if(!cur)
814                 break;
815             node = exp->_nodes[node].next;
816         }
817         *text_begin++;
818     } while(cur == NULL && text_begin != text_end);
820     if(cur == NULL)
821         return TRex_False;
823     --text_begin;
825     if(out_begin) *out_begin = text_begin;
826     if(out_end) *out_end = cur;
827     return TRex_True;
830 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
832     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
835 int trex_getsubexpcount(TRex* exp)
837     return exp->_nsubexpr;
840 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
842     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
843     *subexp = exp->_matches[n];
844     return TRex_True;
848 //########################################################################
849 //########################################################################
850 //##  E N D    R E G E X P
851 //########################################################################
852 //########################################################################
858 //########################################################################
859 //########################################################################
860 //##  X M L
861 //########################################################################
862 //########################################################################
864 // Note:  This mini-dom library comes from Pedro, another little project
865 // of mine.
867 typedef std::string String;
868 typedef unsigned int XMLCh;
871 class Namespace
873 public:
874     Namespace()
875         {}
877     Namespace(const String &prefixArg, const String &namespaceURIArg)
878         {
879         prefix       = prefixArg;
880         namespaceURI = namespaceURIArg;
881         }
883     Namespace(const Namespace &other)
884         {
885         assign(other);
886         }
888     Namespace &operator=(const Namespace &other)
889         {
890         assign(other);
891         return *this;
892         }
894     virtual ~Namespace()
895         {}
897     virtual String getPrefix()
898         { return prefix; }
900     virtual String getNamespaceURI()
901         { return namespaceURI; }
903 protected:
905     void assign(const Namespace &other)
906         {
907         prefix       = other.prefix;
908         namespaceURI = other.namespaceURI;
909         }
911     String prefix;
912     String namespaceURI;
914 };
916 class Attribute
918 public:
919     Attribute()
920         {}
922     Attribute(const String &nameArg, const String &valueArg)
923         {
924         name  = nameArg;
925         value = valueArg;
926         }
928     Attribute(const Attribute &other)
929         {
930         assign(other);
931         }
933     Attribute &operator=(const Attribute &other)
934         {
935         assign(other);
936         return *this;
937         }
939     virtual ~Attribute()
940         {}
942     virtual String getName()
943         { return name; }
945     virtual String getValue()
946         { return value; }
948 protected:
950     void assign(const Attribute &other)
951         {
952         name  = other.name;
953         value = other.value;
954         }
956     String name;
957     String value;
959 };
962 class Element
964 friend class Parser;
966 public:
967     Element()
968         {
969         init();
970         }
972     Element(const String &nameArg)
973         {
974         init();
975         name   = nameArg;
976         }
978     Element(const String &nameArg, const String &valueArg)
979         {
980         init();
981         name   = nameArg;
982         value  = valueArg;
983         }
985     Element(const Element &other)
986         {
987         assign(other);
988         }
990     Element &operator=(const Element &other)
991         {
992         assign(other);
993         return *this;
994         }
996     virtual Element *clone();
998     virtual ~Element()
999         {
1000         for (unsigned int i=0 ; i<children.size() ; i++)
1001             delete children[i];
1002         }
1004     virtual String getName()
1005         { return name; }
1007     virtual String getValue()
1008         { return value; }
1010     Element *getParent()
1011         { return parent; }
1013     std::vector<Element *> getChildren()
1014         { return children; }
1016     std::vector<Element *> findElements(const String &name);
1018     String getAttribute(const String &name);
1020     std::vector<Attribute> &getAttributes()
1021         { return attributes; } 
1023     String getTagAttribute(const String &tagName, const String &attrName);
1025     String getTagValue(const String &tagName);
1027     void addChild(Element *child);
1029     void addAttribute(const String &name, const String &value);
1031     void addNamespace(const String &prefix, const String &namespaceURI);
1034     /**
1035      * Prettyprint an XML tree to an output stream.  Elements are indented
1036      * according to element hierarchy.
1037      * @param f a stream to receive the output
1038      * @param elem the element to output
1039      */
1040     void writeIndented(FILE *f);
1042     /**
1043      * Prettyprint an XML tree to standard output.  This is the equivalent of
1044      * writeIndented(stdout).
1045      * @param elem the element to output
1046      */
1047     void print();
1048     
1049     int getLine()
1050         { return line; }
1052 protected:
1054     void init()
1055         {
1056         parent = NULL;
1057         line   = 0;
1058         }
1060     void assign(const Element &other)
1061         {
1062         parent     = other.parent;
1063         children   = other.children;
1064         attributes = other.attributes;
1065         namespaces = other.namespaces;
1066         name       = other.name;
1067         value      = other.value;
1068         line       = other.line;
1069         }
1071     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1073     void writeIndentedRecursive(FILE *f, int indent);
1075     Element *parent;
1077     std::vector<Element *>children;
1079     std::vector<Attribute> attributes;
1080     std::vector<Namespace> namespaces;
1082     String name;
1083     String value;
1084     
1085     int line;
1086 };
1092 class Parser
1094 public:
1095     /**
1096      * Constructor
1097      */
1098     Parser()
1099         { init(); }
1101     virtual ~Parser()
1102         {}
1104     /**
1105      * Parse XML in a char buffer.
1106      * @param buf a character buffer to parse
1107      * @param pos position to start parsing
1108      * @param len number of chars, from pos, to parse.
1109      * @return a pointer to the root of the XML document;
1110      */
1111     Element *parse(const char *buf,int pos,int len);
1113     /**
1114      * Parse XML in a char buffer.
1115      * @param buf a character buffer to parse
1116      * @param pos position to start parsing
1117      * @param len number of chars, from pos, to parse.
1118      * @return a pointer to the root of the XML document;
1119      */
1120     Element *parse(const String &buf);
1122     /**
1123      * Parse a named XML file.  The file is loaded like a data file;
1124      * the original format is not preserved.
1125      * @param fileName the name of the file to read
1126      * @return a pointer to the root of the XML document;
1127      */
1128     Element *parseFile(const String &fileName);
1130     /**
1131      * Utility method to preprocess a string for XML
1132      * output, escaping its entities.
1133      * @param str the string to encode
1134      */
1135     static String encode(const String &str);
1137     /**
1138      *  Removes whitespace from beginning and end of a string
1139      */
1140     String trim(const String &s);
1142 private:
1144     void init()
1145         {
1146         keepGoing       = true;
1147         currentNode     = NULL;
1148         parselen        = 0;
1149         parsebuf        = NULL;
1150         currentPosition = 0;
1151         }
1153     int countLines(int begin, int end);
1155     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1157     void error(const char *fmt, ...);
1159     int peek(int pos);
1161     int match(int pos, const char *text);
1163     int skipwhite(int p);
1165     int getWord(int p0, String &buf);
1167     int getQuoted(int p0, String &buf, int do_i_parse);
1169     int parseVersion(int p0);
1171     int parseDoctype(int p0);
1173     int parseElement(int p0, Element *par,int depth);
1175     Element *parse(XMLCh *buf,int pos,int len);
1177     bool       keepGoing;
1178     Element    *currentNode;
1179     int        parselen;
1180     XMLCh      *parsebuf;
1181     String     cdatabuf;
1182     int        currentPosition;
1183 };
1188 //########################################################################
1189 //# E L E M E N T
1190 //########################################################################
1192 Element *Element::clone()
1194     Element *elem = new Element(name, value);
1195     elem->parent     = parent;
1196     elem->attributes = attributes;
1197     elem->namespaces = namespaces;
1198     elem->line       = line;
1200     std::vector<Element *>::iterator iter;
1201     for (iter = children.begin(); iter != children.end() ; iter++)
1202         {
1203         elem->addChild((*iter)->clone());
1204         }
1205     return elem;
1209 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1211     if (getName() == name)
1212         {
1213         res.push_back(this);
1214         }
1215     for (unsigned int i=0; i<children.size() ; i++)
1216         children[i]->findElementsRecursive(res, name);
1219 std::vector<Element *> Element::findElements(const String &name)
1221     std::vector<Element *> res;
1222     findElementsRecursive(res, name);
1223     return res;
1226 String Element::getAttribute(const String &name)
1228     for (unsigned int i=0 ; i<attributes.size() ; i++)
1229         if (attributes[i].getName() ==name)
1230             return attributes[i].getValue();
1231     return "";
1234 String Element::getTagAttribute(const String &tagName, const String &attrName)
1236     std::vector<Element *>elems = findElements(tagName);
1237     if (elems.size() <1)
1238         return "";
1239     String res = elems[0]->getAttribute(attrName);
1240     return res;
1243 String Element::getTagValue(const String &tagName)
1245     std::vector<Element *>elems = findElements(tagName);
1246     if (elems.size() <1)
1247         return "";
1248     String res = elems[0]->getValue();
1249     return res;
1252 void Element::addChild(Element *child)
1254     if (!child)
1255         return;
1256     child->parent = this;
1257     children.push_back(child);
1261 void Element::addAttribute(const String &name, const String &value)
1263     Attribute attr(name, value);
1264     attributes.push_back(attr);
1267 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1269     Namespace ns(prefix, namespaceURI);
1270     namespaces.push_back(ns);
1273 void Element::writeIndentedRecursive(FILE *f, int indent)
1275     int i;
1276     if (!f)
1277         return;
1278     //Opening tag, and attributes
1279     for (i=0;i<indent;i++)
1280         fputc(' ',f);
1281     fprintf(f,"<%s",name.c_str());
1282     for (unsigned int i=0 ; i<attributes.size() ; i++)
1283         {
1284         fprintf(f," %s=\"%s\"",
1285               attributes[i].getName().c_str(),
1286               attributes[i].getValue().c_str());
1287         }
1288     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1289         {
1290         fprintf(f," xmlns:%s=\"%s\"",
1291               namespaces[i].getPrefix().c_str(),
1292               namespaces[i].getNamespaceURI().c_str());
1293         }
1294     fprintf(f,">\n");
1296     //Between the tags
1297     if (value.size() > 0)
1298         {
1299         for (int i=0;i<indent;i++)
1300             fputc(' ', f);
1301         fprintf(f," %s\n", value.c_str());
1302         }
1304     for (unsigned int i=0 ; i<children.size() ; i++)
1305         children[i]->writeIndentedRecursive(f, indent+2);
1307     //Closing tag
1308     for (int i=0; i<indent; i++)
1309         fputc(' ',f);
1310     fprintf(f,"</%s>\n", name.c_str());
1313 void Element::writeIndented(FILE *f)
1315     writeIndentedRecursive(f, 0);
1318 void Element::print()
1320     writeIndented(stdout);
1324 //########################################################################
1325 //# P A R S E R
1326 //########################################################################
1330 typedef struct
1331     {
1332     const char *escaped;
1333     char value;
1334     } EntityEntry;
1336 static EntityEntry entities[] =
1338     { "&amp;" , '&'  },
1339     { "&lt;"  , '<'  },
1340     { "&gt;"  , '>'  },
1341     { "&apos;", '\'' },
1342     { "&quot;", '"'  },
1343     { NULL    , '\0' }
1344 };
1348 /**
1349  *  Removes whitespace from beginning and end of a string
1350  */
1351 String Parser::trim(const String &s)
1353     if (s.size() < 1)
1354         return s;
1355     
1356     //Find first non-ws char
1357     unsigned int begin = 0;
1358     for ( ; begin < s.size() ; begin++)
1359         {
1360         if (!isspace(s[begin]))
1361             break;
1362         }
1364     //Find first non-ws char, going in reverse
1365     unsigned int end = s.size() - 1;
1366     for ( ; end > begin ; end--)
1367         {
1368         if (!isspace(s[end]))
1369             break;
1370         }
1371     //trace("begin:%d  end:%d", begin, end);
1373     String res = s.substr(begin, end-begin+1);
1374     return res;
1378 int Parser::countLines(int begin, int end)
1380     int count = 0;
1381     for (int i=begin ; i<end ; i++)
1382         {
1383         XMLCh ch = parsebuf[i];
1384         if (ch == '\n' || ch == '\r')
1385             count++;
1386         }
1387     return count;
1391 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1393     int line = 1;
1394     int col  = 1;
1395     for (long i=0 ; i<pos ; i++)
1396         {
1397         XMLCh ch = parsebuf[i];
1398         if (ch == '\n' || ch == '\r')
1399             {
1400             col = 0;
1401             line ++;
1402             }
1403         else
1404             col++;
1405         }
1406     *lineNr = line;
1407     *colNr  = col;
1412 void Parser::error(const char *fmt, ...)
1414     int lineNr;
1415     int colNr;
1416     getLineAndColumn(currentPosition, &lineNr, &colNr);
1417     va_list args;
1418     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1419     va_start(args,fmt);
1420     vfprintf(stderr,fmt,args);
1421     va_end(args) ;
1422     fprintf(stderr, "\n");
1427 int Parser::peek(int pos)
1429     if (pos >= parselen)
1430         return -1;
1431     currentPosition = pos;
1432     int ch = parsebuf[pos];
1433     //printf("ch:%c\n", ch);
1434     return ch;
1439 String Parser::encode(const String &str)
1441     String ret;
1442     for (unsigned int i=0 ; i<str.size() ; i++)
1443         {
1444         XMLCh ch = (XMLCh)str[i];
1445         if (ch == '&')
1446             ret.append("&amp;");
1447         else if (ch == '<')
1448             ret.append("&lt;");
1449         else if (ch == '>')
1450             ret.append("&gt;");
1451         else if (ch == '\'')
1452             ret.append("&apos;");
1453         else if (ch == '"')
1454             ret.append("&quot;");
1455         else
1456             ret.push_back(ch);
1458         }
1459     return ret;
1463 int Parser::match(int p0, const char *text)
1465     int p = p0;
1466     while (*text)
1467         {
1468         if (peek(p) != *text)
1469             return p0;
1470         p++; text++;
1471         }
1472     return p;
1477 int Parser::skipwhite(int p)
1480     while (p<parselen)
1481         {
1482         int p2 = match(p, "<!--");
1483         if (p2 > p)
1484             {
1485             p = p2;
1486             while (p<parselen)
1487               {
1488               p2 = match(p, "-->");
1489               if (p2 > p)
1490                   {
1491                   p = p2;
1492                   break;
1493                   }
1494               p++;
1495               }
1496           }
1497       XMLCh b = peek(p);
1498       if (!isspace(b))
1499           break;
1500       p++;
1501       }
1502   return p;
1505 /* modify this to allow all chars for an element or attribute name*/
1506 int Parser::getWord(int p0, String &buf)
1508     int p = p0;
1509     while (p<parselen)
1510         {
1511         XMLCh b = peek(p);
1512         if (b<=' ' || b=='/' || b=='>' || b=='=')
1513             break;
1514         buf.push_back(b);
1515         p++;
1516         }
1517     return p;
1520 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1523     int p = p0;
1524     if (peek(p) != '"' && peek(p) != '\'')
1525         return p0;
1526     p++;
1528     while ( p<parselen )
1529         {
1530         XMLCh b = peek(p);
1531         if (b=='"' || b=='\'')
1532             break;
1533         if (b=='&' && do_i_parse)
1534             {
1535             bool found = false;
1536             for (EntityEntry *ee = entities ; ee->value ; ee++)
1537                 {
1538                 int p2 = match(p, ee->escaped);
1539                 if (p2>p)
1540                     {
1541                     buf.push_back(ee->value);
1542                     p = p2;
1543                     found = true;
1544                     break;
1545                     }
1546                 }
1547             if (!found)
1548                 {
1549                 error("unterminated entity");
1550                 return false;
1551                 }
1552             }
1553         else
1554             {
1555             buf.push_back(b);
1556             p++;
1557             }
1558         }
1559     return p;
1562 int Parser::parseVersion(int p0)
1564     //printf("### parseVersion: %d\n", p0);
1566     int p = p0;
1568     p = skipwhite(p0);
1570     if (peek(p) != '<')
1571         return p0;
1573     p++;
1574     if (p>=parselen || peek(p)!='?')
1575         return p0;
1577     p++;
1579     String buf;
1581     while (p<parselen)
1582         {
1583         XMLCh ch = peek(p);
1584         if (ch=='?')
1585             {
1586             p++;
1587             break;
1588             }
1589         buf.push_back(ch);
1590         p++;
1591         }
1593     if (peek(p) != '>')
1594         return p0;
1595     p++;
1597     //printf("Got version:%s\n",buf.c_str());
1598     return p;
1601 int Parser::parseDoctype(int p0)
1603     //printf("### parseDoctype: %d\n", p0);
1605     int p = p0;
1606     p = skipwhite(p);
1608     if (p>=parselen || peek(p)!='<')
1609         return p0;
1611     p++;
1613     if (peek(p)!='!' || peek(p+1)=='-')
1614         return p0;
1615     p++;
1617     String buf;
1618     while (p<parselen)
1619         {
1620         XMLCh ch = peek(p);
1621         if (ch=='>')
1622             {
1623             p++;
1624             break;
1625             }
1626         buf.push_back(ch);
1627         p++;
1628         }
1630     //printf("Got doctype:%s\n",buf.c_str());
1631     return p;
1636 int Parser::parseElement(int p0, Element *par,int lineNr)
1639     int p = p0;
1641     int p2 = p;
1643     p = skipwhite(p);
1645     //## Get open tag
1646     XMLCh ch = peek(p);
1647     if (ch!='<')
1648         return p0;
1650     //int line, col;
1651     //getLineAndColumn(p, &line, &col);
1653     p++;
1655     String openTagName;
1656     p = skipwhite(p);
1657     p = getWord(p, openTagName);
1658     //printf("####tag :%s\n", openTagName.c_str());
1659     p = skipwhite(p);
1661     //Add element to tree
1662     Element *n = new Element(openTagName);
1663     n->line = lineNr + countLines(p0, p);
1664     n->parent = par;
1665     par->addChild(n);
1667     // Get attributes
1668     if (peek(p) != '>')
1669         {
1670         while (p<parselen)
1671             {
1672             p = skipwhite(p);
1673             ch = peek(p);
1674             //printf("ch:%c\n",ch);
1675             if (ch=='>')
1676                 break;
1677             else if (ch=='/' && p<parselen+1)
1678                 {
1679                 p++;
1680                 p = skipwhite(p);
1681                 ch = peek(p);
1682                 if (ch=='>')
1683                     {
1684                     p++;
1685                     //printf("quick close\n");
1686                     return p;
1687                     }
1688                 }
1689             String attrName;
1690             p2 = getWord(p, attrName);
1691             if (p2==p)
1692                 break;
1693             //printf("name:%s",buf);
1694             p=p2;
1695             p = skipwhite(p);
1696             ch = peek(p);
1697             //printf("ch:%c\n",ch);
1698             if (ch!='=')
1699                 break;
1700             p++;
1701             p = skipwhite(p);
1702             // ch = parsebuf[p];
1703             // printf("ch:%c\n",ch);
1704             String attrVal;
1705             p2 = getQuoted(p, attrVal, true);
1706             p=p2+1;
1707             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1708             char *namestr = (char *)attrName.c_str();
1709             if (strncmp(namestr, "xmlns:", 6)==0)
1710                 n->addNamespace(attrName, attrVal);
1711             else
1712                 n->addAttribute(attrName, attrVal);
1713             }
1714         }
1716     bool cdata = false;
1718     p++;
1719     // ### Get intervening data ### */
1720     String data;
1721     while (p<parselen)
1722         {
1723         //# COMMENT
1724         p2 = match(p, "<!--");
1725         if (!cdata && p2>p)
1726             {
1727             p = p2;
1728             while (p<parselen)
1729                 {
1730                 p2 = match(p, "-->");
1731                 if (p2 > p)
1732                     {
1733                     p = p2;
1734                     break;
1735                     }
1736                 p++;
1737                 }
1738             }
1740         ch = peek(p);
1741         //# END TAG
1742         if (ch=='<' && !cdata && peek(p+1)=='/')
1743             {
1744             break;
1745             }
1746         //# CDATA
1747         p2 = match(p, "<![CDATA[");
1748         if (p2 > p)
1749             {
1750             cdata = true;
1751             p = p2;
1752             continue;
1753             }
1755         //# CHILD ELEMENT
1756         if (ch == '<')
1757             {
1758             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1759             if (p2 == p)
1760                 {
1761                 /*
1762                 printf("problem on element:%s.  p2:%d p:%d\n",
1763                       openTagName.c_str(), p2, p);
1764                 */
1765                 return p0;
1766                 }
1767             p = p2;
1768             continue;
1769             }
1770         //# ENTITY
1771         if (ch=='&' && !cdata)
1772             {
1773             bool found = false;
1774             for (EntityEntry *ee = entities ; ee->value ; ee++)
1775                 {
1776                 int p2 = match(p, ee->escaped);
1777                 if (p2>p)
1778                     {
1779                     data.push_back(ee->value);
1780                     p = p2;
1781                     found = true;
1782                     break;
1783                     }
1784                 }
1785             if (!found)
1786                 {
1787                 error("unterminated entity");
1788                 return -1;
1789                 }
1790             continue;
1791             }
1793         //# NONE OF THE ABOVE
1794         data.push_back(ch);
1795         p++;
1796         }/*while*/
1799     n->value = data;
1800     //printf("%d : data:%s\n",p,data.c_str());
1802     //## Get close tag
1803     p = skipwhite(p);
1804     ch = peek(p);
1805     if (ch != '<')
1806         {
1807         error("no < for end tag\n");
1808         return p0;
1809         }
1810     p++;
1811     ch = peek(p);
1812     if (ch != '/')
1813         {
1814         error("no / on end tag");
1815         return p0;
1816         }
1817     p++;
1818     ch = peek(p);
1819     p = skipwhite(p);
1820     String closeTagName;
1821     p = getWord(p, closeTagName);
1822     if (openTagName != closeTagName)
1823         {
1824         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1825                 openTagName.c_str(), closeTagName.c_str());
1826         return p0;
1827         }
1828     p = skipwhite(p);
1829     if (peek(p) != '>')
1830         {
1831         error("no > on end tag for '%s'", closeTagName.c_str());
1832         return p0;
1833         }
1834     p++;
1835     // printf("close element:%s\n",closeTagName.c_str());
1836     p = skipwhite(p);
1837     return p;
1843 Element *Parser::parse(XMLCh *buf,int pos,int len)
1845     parselen = len;
1846     parsebuf = buf;
1847     Element *rootNode = new Element("root");
1848     pos = parseVersion(pos);
1849     pos = parseDoctype(pos);
1850     pos = parseElement(pos, rootNode, 1);
1851     return rootNode;
1855 Element *Parser::parse(const char *buf, int pos, int len)
1857     XMLCh *charbuf = new XMLCh[len + 1];
1858     long i = 0;
1859     for ( ; i < len ; i++)
1860         charbuf[i] = (XMLCh)buf[i];
1861     charbuf[i] = '\0';
1863     Element *n = parse(charbuf, pos, len);
1864     delete[] charbuf;
1865     return n;
1868 Element *Parser::parse(const String &buf)
1870     long len = (long)buf.size();
1871     XMLCh *charbuf = new XMLCh[len + 1];
1872     long i = 0;
1873     for ( ; i < len ; i++)
1874         charbuf[i] = (XMLCh)buf[i];
1875     charbuf[i] = '\0';
1877     Element *n = parse(charbuf, 0, len);
1878     delete[] charbuf;
1879     return n;
1882 Element *Parser::parseFile(const String &fileName)
1885     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1886     FILE *f = fopen(fileName.c_str(), "rb");
1887     if (!f)
1888         return NULL;
1890     struct stat  statBuf;
1891     if (fstat(fileno(f),&statBuf)<0)
1892         {
1893         fclose(f);
1894         return NULL;
1895         }
1896     long filelen = statBuf.st_size;
1898     //printf("length:%d\n",filelen);
1899     XMLCh *charbuf = new XMLCh[filelen + 1];
1900     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1901         {
1902         *p = (XMLCh)fgetc(f);
1903         }
1904     fclose(f);
1905     charbuf[filelen] = '\0';
1908     /*
1909     printf("nrbytes:%d\n",wc_count);
1910     printf("buf:%ls\n======\n",charbuf);
1911     */
1912     Element *n = parse(charbuf, 0, filelen);
1913     delete[] charbuf;
1914     return n;
1917 //########################################################################
1918 //########################################################################
1919 //##  E N D    X M L
1920 //########################################################################
1921 //########################################################################
1928 //########################################################################
1929 //########################################################################
1930 //##  U R I
1931 //########################################################################
1932 //########################################################################
1934 //This would normally be a call to a UNICODE function
1935 #define isLetter(x) isalpha(x)
1937 /**
1938  *  A class that implements the W3C URI resource reference.
1939  */
1940 class URI
1942 public:
1944     typedef enum
1945         {
1946         SCHEME_NONE =0,
1947         SCHEME_DATA,
1948         SCHEME_HTTP,
1949         SCHEME_HTTPS,
1950         SCHEME_FTP,
1951         SCHEME_FILE,
1952         SCHEME_LDAP,
1953         SCHEME_MAILTO,
1954         SCHEME_NEWS,
1955         SCHEME_TELNET
1956         } SchemeTypes;
1958     /**
1959      *
1960      */
1961     URI()
1962         {
1963         init();
1964         }
1966     /**
1967      *
1968      */
1969     URI(const String &str)
1970         {
1971         init();
1972         parse(str);
1973         }
1976     /**
1977      *
1978      */
1979     URI(const char *str)
1980         {
1981         init();
1982         String domStr = str;
1983         parse(domStr);
1984         }
1987     /**
1988      *
1989      */
1990     URI(const URI &other)
1991         {
1992         init();
1993         assign(other);
1994         }
1997     /**
1998      *
1999      */
2000     URI &operator=(const URI &other)
2001         {
2002         init();
2003         assign(other);
2004         return *this;
2005         }
2008     /**
2009      *
2010      */
2011     virtual ~URI()
2012         {}
2016     /**
2017      *
2018      */
2019     virtual bool parse(const String &str);
2021     /**
2022      *
2023      */
2024     virtual String toString() const;
2026     /**
2027      *
2028      */
2029     virtual int getScheme() const;
2031     /**
2032      *
2033      */
2034     virtual String getSchemeStr() const;
2036     /**
2037      *
2038      */
2039     virtual String getAuthority() const;
2041     /**
2042      *  Same as getAuthority, but if the port has been specified
2043      *  as host:port , the port will not be included
2044      */
2045     virtual String getHost() const;
2047     /**
2048      *
2049      */
2050     virtual int getPort() const;
2052     /**
2053      *
2054      */
2055     virtual String getPath() const;
2057     /**
2058      *
2059      */
2060     virtual String getNativePath() const;
2062     /**
2063      *
2064      */
2065     virtual bool isAbsolute() const;
2067     /**
2068      *
2069      */
2070     virtual bool isOpaque() const;
2072     /**
2073      *
2074      */
2075     virtual String getQuery() const;
2077     /**
2078      *
2079      */
2080     virtual String getFragment() const;
2082     /**
2083      *
2084      */
2085     virtual URI resolve(const URI &other) const;
2087     /**
2088      *
2089      */
2090     virtual void normalize();
2092 private:
2094     /**
2095      *
2096      */
2097     void init()
2098         {
2099         parsebuf  = NULL;
2100         parselen  = 0;
2101         scheme    = SCHEME_NONE;
2102         schemeStr = "";
2103         port      = 0;
2104         authority = "";
2105         path      = "";
2106         absolute  = false;
2107         opaque    = false;
2108         query     = "";
2109         fragment  = "";
2110         }
2113     /**
2114      *
2115      */
2116     void assign(const URI &other)
2117         {
2118         scheme    = other.scheme;
2119         schemeStr = other.schemeStr;
2120         authority = other.authority;
2121         port      = other.port;
2122         path      = other.path;
2123         absolute  = other.absolute;
2124         opaque    = other.opaque;
2125         query     = other.query;
2126         fragment  = other.fragment;
2127         }
2129     int scheme;
2131     String schemeStr;
2133     String authority;
2135     bool portSpecified;
2137     int port;
2139     String path;
2141     bool absolute;
2143     bool opaque;
2145     String query;
2147     String fragment;
2149     void error(const char *fmt, ...);
2151     void trace(const char *fmt, ...);
2154     int peek(int p);
2156     int match(int p, const char *key);
2158     int parseScheme(int p);
2160     int parseHierarchicalPart(int p0);
2162     int parseQuery(int p0);
2164     int parseFragment(int p0);
2166     int parse(int p);
2168     char *parsebuf;
2170     int parselen;
2172 };
2176 typedef struct
2178     int         ival;
2179     const char *sval;
2180     int         port;
2181 } LookupEntry;
2183 LookupEntry schemes[] =
2185     { URI::SCHEME_DATA,   "data:",    0 },
2186     { URI::SCHEME_HTTP,   "http:",   80 },
2187     { URI::SCHEME_HTTPS,  "https:", 443 },
2188     { URI::SCHEME_FTP,    "ftp",     12 },
2189     { URI::SCHEME_FILE,   "file:",    0 },
2190     { URI::SCHEME_LDAP,   "ldap:",  123 },
2191     { URI::SCHEME_MAILTO, "mailto:", 25 },
2192     { URI::SCHEME_NEWS,   "news:",  117 },
2193     { URI::SCHEME_TELNET, "telnet:", 23 },
2194     { 0,                  NULL,       0 }
2195 };
2198 String URI::toString() const
2200     String str = schemeStr;
2201     if (authority.size() > 0)
2202         {
2203         str.append("//");
2204         str.append(authority);
2205         }
2206     str.append(path);
2207     if (query.size() > 0)
2208         {
2209         str.append("?");
2210         str.append(query);
2211         }
2212     if (fragment.size() > 0)
2213         {
2214         str.append("#");
2215         str.append(fragment);
2216         }
2217     return str;
2221 int URI::getScheme() const
2223     return scheme;
2226 String URI::getSchemeStr() const
2228     return schemeStr;
2232 String URI::getAuthority() const
2234     String ret = authority;
2235     if (portSpecified && port>=0)
2236         {
2237         char buf[7];
2238         snprintf(buf, 6, ":%6d", port);
2239         ret.append(buf);
2240         }
2241     return ret;
2244 String URI::getHost() const
2246     return authority;
2249 int URI::getPort() const
2251     return port;
2255 String URI::getPath() const
2257     return path;
2260 String URI::getNativePath() const
2262     String npath;
2263 #ifdef __WIN32__
2264     unsigned int firstChar = 0;
2265     if (path.size() >= 3)
2266         {
2267         if (path[0] == '/' &&
2268             isLetter(path[1]) &&
2269             path[2] == ':')
2270             firstChar++;
2271          }
2272     for (unsigned int i=firstChar ; i<path.size() ; i++)
2273         {
2274         XMLCh ch = (XMLCh) path[i];
2275         if (ch == '/')
2276             npath.push_back((XMLCh)'\\');
2277         else
2278             npath.push_back(ch);
2279         }
2280 #else
2281     npath = path;
2282 #endif
2283     return npath;
2287 bool URI::isAbsolute() const
2289     return absolute;
2292 bool URI::isOpaque() const
2294     return opaque;
2298 String URI::getQuery() const
2300     return query;
2304 String URI::getFragment() const
2306     return fragment;
2310 URI URI::resolve(const URI &other) const
2312     //### According to w3c, this is handled in 3 cases
2314     //## 1
2315     if (opaque || other.isAbsolute())
2316         return other;
2318     //## 2
2319     if (other.fragment.size()  >  0 &&
2320         other.path.size()      == 0 &&
2321         other.scheme           == SCHEME_NONE &&
2322         other.authority.size() == 0 &&
2323         other.query.size()     == 0 )
2324         {
2325         URI fragUri = *this;
2326         fragUri.fragment = other.fragment;
2327         return fragUri;
2328         }
2330     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2331     URI newUri;
2332     //# 3.1
2333     newUri.scheme    = scheme;
2334     newUri.schemeStr = schemeStr;
2335     newUri.query     = other.query;
2336     newUri.fragment  = other.fragment;
2337     if (other.authority.size() > 0)
2338         {
2339         //# 3.2
2340         if (absolute || other.absolute)
2341             newUri.absolute = true;
2342         newUri.authority = other.authority;
2343         newUri.port      = other.port;//part of authority
2344         newUri.path      = other.path;
2345         }
2346     else
2347         {
2348         //# 3.3
2349         if (other.absolute)
2350             {
2351             newUri.absolute = true;
2352             newUri.path     = other.path;
2353             }
2354         else
2355             {
2356             unsigned int pos = path.find_last_of('/');
2357             if (pos != path.npos)
2358                 {
2359                 String tpath = path.substr(0, pos+1);
2360                 tpath.append(other.path);
2361                 newUri.path = tpath;
2362                 }
2363             else
2364                 newUri.path = other.path;
2365             }
2366         }
2368     newUri.normalize();
2369     return newUri;
2374 /**
2375  *  This follows the Java URI algorithm:
2376  *   1. All "." segments are removed.
2377  *   2. If a ".." segment is preceded by a non-".." segment
2378  *          then both of these segments are removed. This step
2379  *          is repeated until it is no longer applicable.
2380  *   3. If the path is relative, and if its first segment
2381  *          contains a colon character (':'), then a "." segment
2382  *          is prepended. This prevents a relative URI with a path
2383  *          such as "a:b/c/d" from later being re-parsed as an
2384  *          opaque URI with a scheme of "a" and a scheme-specific
2385  *          part of "b/c/d". (Deviation from RFC 2396)
2386  */
2387 void URI::normalize()
2389     std::vector<String> segments;
2391     //## Collect segments
2392     if (path.size()<2)
2393         return;
2394     bool abs = false;
2395     unsigned int pos=0;
2396     if (path[0]=='/')
2397         {
2398         abs = true;
2399         pos++;
2400         }
2401     while (pos < path.size())
2402         {
2403         unsigned int pos2 = path.find('/', pos);
2404         if (pos2==path.npos)
2405             {
2406             String seg = path.substr(pos);
2407             //printf("last segment:%s\n", seg.c_str());
2408             segments.push_back(seg);
2409             break;
2410             }
2411         if (pos2>pos)
2412             {
2413             String seg = path.substr(pos, pos2-pos);
2414             //printf("segment:%s\n", seg.c_str());
2415             segments.push_back(seg);
2416             }
2417         pos = pos2;
2418         pos++;
2419         }
2421     //## Clean up (normalize) segments
2422     bool edited = false;
2423     std::vector<String>::iterator iter;
2424     for (iter=segments.begin() ; iter!=segments.end() ; )
2425         {
2426         String s = *iter;
2427         if (s == ".")
2428             {
2429             iter = segments.erase(iter);
2430             edited = true;
2431             }
2432         else if (s == ".." &&
2433                  iter != segments.begin() &&
2434                  *(iter-1) != "..")
2435             {
2436             iter--; //back up, then erase two entries
2437             iter = segments.erase(iter);
2438             iter = segments.erase(iter);
2439             edited = true;
2440             }
2441         else
2442             iter++;
2443         }
2445     //## Rebuild path, if necessary
2446     if (edited)
2447         {
2448         path.clear();
2449         if (abs)
2450             {
2451             path.append("/");
2452             }
2453         std::vector<String>::iterator iter;
2454         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2455             {
2456             if (iter != segments.begin())
2457                 path.append("/");
2458             path.append(*iter);
2459             }
2460         }
2466 //#########################################################################
2467 //# M E S S A G E S
2468 //#########################################################################
2470 void URI::error(const char *fmt, ...)
2472     va_list args;
2473     fprintf(stderr, "URI error: ");
2474     va_start(args, fmt);
2475     vfprintf(stderr, fmt, args);
2476     va_end(args);
2477     fprintf(stderr, "\n");
2480 void URI::trace(const char *fmt, ...)
2482     va_list args;
2483     fprintf(stdout, "URI: ");
2484     va_start(args, fmt);
2485     vfprintf(stdout, fmt, args);
2486     va_end(args);
2487     fprintf(stdout, "\n");
2493 //#########################################################################
2494 //# P A R S I N G
2495 //#########################################################################
2499 int URI::peek(int p)
2501     if (p<0 || p>=parselen)
2502         return -1;
2503     return parsebuf[p];
2508 int URI::match(int p0, const char *key)
2510     int p = p0;
2511     while (p < parselen)
2512         {
2513         if (*key == '\0')
2514             return p;
2515         else if (*key != parsebuf[p])
2516             break;
2517         p++; key++;
2518         }
2519     return p0;
2522 //#########################################################################
2523 //#  Parsing is performed according to:
2524 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2525 //#########################################################################
2527 int URI::parseScheme(int p0)
2529     int p = p0;
2530     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2531         {
2532         int p2 = match(p, entry->sval);
2533         if (p2 > p)
2534             {
2535             schemeStr = entry->sval;
2536             scheme    = entry->ival;
2537             port      = entry->port;
2538             p = p2;
2539             return p;
2540             }
2541         }
2543     return p;
2547 int URI::parseHierarchicalPart(int p0)
2549     int p = p0;
2550     int ch;
2552     //# Authority field (host and port, for example)
2553     int p2 = match(p, "//");
2554     if (p2 > p)
2555         {
2556         p = p2;
2557         portSpecified = false;
2558         String portStr;
2559         while (p < parselen)
2560             {
2561             ch = peek(p);
2562             if (ch == '/')
2563                 break;
2564             else if (ch == ':')
2565                 portSpecified = true;
2566             else if (portSpecified)
2567                 portStr.push_back((XMLCh)ch);
2568             else
2569                 authority.push_back((XMLCh)ch);
2570             p++;
2571             }
2572         if (portStr.size() > 0)
2573             {
2574             char *pstr = (char *)portStr.c_str();
2575             char *endStr;
2576             long val = strtol(pstr, &endStr, 10);
2577             if (endStr > pstr) //successful parse?
2578                 port = val;
2579             }
2580         }
2582     //# Are we absolute?
2583     ch = peek(p);
2584     if (isLetter(ch) && peek(p+1)==':')
2585         {
2586         absolute = true;
2587         path.push_back((XMLCh)'/');
2588         }
2589     else if (ch == '/')
2590         {
2591         absolute = true;
2592         if (p>p0) //in other words, if '/' is not the first char
2593             opaque = true;
2594         path.push_back((XMLCh)ch);
2595         p++;
2596         }
2598     while (p < parselen)
2599         {
2600         ch = peek(p);
2601         if (ch == '?' || ch == '#')
2602             break;
2603         path.push_back((XMLCh)ch);
2604         p++;
2605         }
2607     return p;
2610 int URI::parseQuery(int p0)
2612     int p = p0;
2613     int ch = peek(p);
2614     if (ch != '?')
2615         return p0;
2617     p++;
2618     while (p < parselen)
2619         {
2620         ch = peek(p);
2621         if (ch == '#')
2622             break;
2623         query.push_back((XMLCh)ch);
2624         p++;
2625         }
2628     return p;
2631 int URI::parseFragment(int p0)
2634     int p = p0;
2635     int ch = peek(p);
2636     if (ch != '#')
2637         return p0;
2639     p++;
2640     while (p < parselen)
2641         {
2642         ch = peek(p);
2643         if (ch == '?')
2644             break;
2645         fragment.push_back((XMLCh)ch);
2646         p++;
2647         }
2650     return p;
2654 int URI::parse(int p0)
2657     int p = p0;
2659     int p2 = parseScheme(p);
2660     if (p2 < 0)
2661         {
2662         error("Scheme");
2663         return -1;
2664         }
2665     p = p2;
2668     p2 = parseHierarchicalPart(p);
2669     if (p2 < 0)
2670         {
2671         error("Hierarchical part");
2672         return -1;
2673         }
2674     p = p2;
2676     p2 = parseQuery(p);
2677     if (p2 < 0)
2678         {
2679         error("Query");
2680         return -1;
2681         }
2682     p = p2;
2685     p2 = parseFragment(p);
2686     if (p2 < 0)
2687         {
2688         error("Fragment");
2689         return -1;
2690         }
2691     p = p2;
2693     return p;
2699 bool URI::parse(const String &str)
2701     init();
2702     
2703     parselen = str.size();
2705     String tmp;
2706     for (unsigned int i=0 ; i<str.size() ; i++)
2707         {
2708         XMLCh ch = (XMLCh) str[i];
2709         if (ch == '\\')
2710             tmp.push_back((XMLCh)'/');
2711         else
2712             tmp.push_back(ch);
2713         }
2714     parsebuf = (char *) tmp.c_str();
2717     int p = parse(0);
2718     normalize();
2720     if (p < 0)
2721         {
2722         error("Syntax error");
2723         return false;
2724         }
2726     //printf("uri:%s\n", toString().c_str());
2727     //printf("path:%s\n", path.c_str());
2729     return true;
2740 //########################################################################
2741 //########################################################################
2742 //##  M A K E
2743 //########################################################################
2744 //########################################################################
2746 //########################################################################
2747 //# F I L E S E T
2748 //########################################################################
2749 /**
2750  * This is the descriptor for a <fileset> item
2751  */
2752 class FileSet
2754 public:
2756     /**
2757      *
2758      */
2759     FileSet()
2760         {}
2762     /**
2763      *
2764      */
2765     FileSet(const FileSet &other)
2766         { assign(other); }
2768     /**
2769      *
2770      */
2771     FileSet &operator=(const FileSet &other)
2772         { assign(other); return *this; }
2774     /**
2775      *
2776      */
2777     virtual ~FileSet()
2778         {}
2780     /**
2781      *
2782      */
2783     String getDirectory()
2784         { return directory; }
2785         
2786     /**
2787      *
2788      */
2789     void setDirectory(const String &val)
2790         { directory = val; }
2792     /**
2793      *
2794      */
2795     void setFiles(const std::vector<String> &val)
2796         { files = val; }
2798     /**
2799      *
2800      */
2801     std::vector<String> getFiles()
2802         { return files; }
2803         
2804     /**
2805      *
2806      */
2807     void setIncludes(const std::vector<String> &val)
2808         { includes = val; }
2810     /**
2811      *
2812      */
2813     std::vector<String> getIncludes()
2814         { return includes; }
2815         
2816     /**
2817      *
2818      */
2819     void setExcludes(const std::vector<String> &val)
2820         { excludes = val; }
2822     /**
2823      *
2824      */
2825     std::vector<String> getExcludes()
2826         { return excludes; }
2827         
2828     /**
2829      *
2830      */
2831     unsigned int size()
2832         { return files.size(); }
2833         
2834     /**
2835      *
2836      */
2837     String operator[](int index)
2838         { return files[index]; }
2839         
2840     /**
2841      *
2842      */
2843     void clear()
2844         {
2845         directory = "";
2846         files.clear();
2847         includes.clear();
2848         excludes.clear();
2849         }
2850         
2852 private:
2854     void assign(const FileSet &other)
2855         {
2856         directory = other.directory;
2857         files     = other.files;
2858         includes  = other.includes;
2859         excludes  = other.excludes;
2860         }
2862     String directory;
2863     std::vector<String> files;
2864     std::vector<String> includes;
2865     std::vector<String> excludes;
2866 };
2869 //########################################################################
2870 //# F I L E L I S T
2871 //########################################################################
2872 /**
2873  * This is a simpler, explicitly-named list of files
2874  */
2875 class FileList
2877 public:
2879     /**
2880      *
2881      */
2882     FileList()
2883         {}
2885     /**
2886      *
2887      */
2888     FileList(const FileList &other)
2889         { assign(other); }
2891     /**
2892      *
2893      */
2894     FileList &operator=(const FileList &other)
2895         { assign(other); return *this; }
2897     /**
2898      *
2899      */
2900     virtual ~FileList()
2901         {}
2903     /**
2904      *
2905      */
2906     String getDirectory()
2907         { return directory; }
2908         
2909     /**
2910      *
2911      */
2912     void setDirectory(const String &val)
2913         { directory = val; }
2915     /**
2916      *
2917      */
2918     void setFiles(const std::vector<String> &val)
2919         { files = val; }
2921     /**
2922      *
2923      */
2924     std::vector<String> getFiles()
2925         { return files; }
2926         
2927     /**
2928      *
2929      */
2930     unsigned int size()
2931         { return files.size(); }
2932         
2933     /**
2934      *
2935      */
2936     String operator[](int index)
2937         { return files[index]; }
2938         
2939     /**
2940      *
2941      */
2942     void clear()
2943         {
2944         directory = "";
2945         files.clear();
2946         }
2947         
2949 private:
2951     void assign(const FileList &other)
2952         {
2953         directory = other.directory;
2954         files     = other.files;
2955         }
2957     String directory;
2958     std::vector<String> files;
2959 };
2964 //########################################################################
2965 //# M A K E    B A S E
2966 //########################################################################
2967 /**
2968  * Base class for all classes in this file
2969  */
2970 class MakeBase
2972 public:
2974     MakeBase()
2975         { line = 0; }
2976     virtual ~MakeBase()
2977         {}
2979     /**
2980      *     Return the URI of the file associated with this object 
2981      */     
2982     URI getURI()
2983         { return uri; }
2985     /**
2986      * Set the uri to the given string
2987      */
2988     void setURI(const String &uristr)
2989         { uri.parse(uristr); }
2991     /**
2992      *  Resolve another path relative to this one
2993      */
2994     String resolve(const String &otherPath);
2996     /**
2997      * replace variable refs like ${a} with their values
2998      * Assume that the string has already been syntax validated
2999      */
3000     String eval(const String &s, const String &defaultVal);
3002     /**
3003      * replace variable refs like ${a} with their values
3004      * return true or false
3005      * Assume that the string has already been syntax validated
3006      */
3007     bool evalBool(const String &s, bool defaultVal);
3009     /**
3010      *  Get an element attribute, performing substitutions if necessary
3011      */
3012     bool getAttribute(Element *elem, const String &name, String &result);
3014     /**
3015      * Get an element value, performing substitutions if necessary
3016      */
3017     bool getValue(Element *elem, String &result);
3018     
3019     /**
3020      * Set the current line number in the file
3021      */         
3022     void setLine(int val)
3023         { line = val; }
3024         
3025     /**
3026      * Get the current line number in the file
3027      */         
3028     int getLine()
3029         { return line; }
3032     /**
3033      * Set a property to a given value
3034      */
3035     virtual void setProperty(const String &name, const String &val)
3036         {
3037         properties[name] = val;
3038         }
3040     /**
3041      * Return a named property is found, else a null string
3042      */
3043     virtual String getProperty(const String &name)
3044         {
3045         String val;
3046         std::map<String, String>::iterator iter = properties.find(name);
3047         if (iter != properties.end())
3048             val = iter->second;
3049         String sval;
3050         if (!getSubstitutions(val, sval))
3051             return false;
3052         return sval;
3053         }
3055     /**
3056      * Return true if a named property is found, else false
3057      */
3058     virtual bool hasProperty(const String &name)
3059         {
3060         std::map<String, String>::iterator iter = properties.find(name);
3061         if (iter == properties.end())
3062             return false;
3063         return true;
3064         }
3067 protected:
3069     /**
3070      *    The path to the file associated with this object
3071      */     
3072     URI uri;
3073     
3074     /**
3075      *    If this prefix is seen in a substitution, use an environment
3076      *    variable.
3077      *             example:  <property environment="env"/>
3078      *             ${env.JAVA_HOME}
3079      */
3080     String envPrefix;
3082     /**
3083      *    If this prefix is seen in a substitution, use as a
3084      *    pkg-config 'all' query
3085      *             example:  <property pkg-config="pc"/>
3086      *             ${pc.gtkmm}
3087      */
3088     String pcPrefix;
3090     /**
3091      *    If this prefix is seen in a substitution, use as a
3092      *    pkg-config 'cflags' query
3093      *             example:  <property pkg-config="pcc"/>
3094      *             ${pcc.gtkmm}
3095      */
3096     String pccPrefix;
3098     /**
3099      *    If this prefix is seen in a substitution, use as a
3100      *    pkg-config 'libs' query
3101      *             example:  <property pkg-config="pcl"/>
3102      *             ${pcl.gtkmm}
3103      */
3104     String pclPrefix;
3110     /**
3111      *  Print a printf()-like formatted error message
3112      */
3113     void error(const char *fmt, ...);
3115     /**
3116      *  Print a printf()-like formatted trace message
3117      */
3118     void status(const char *fmt, ...);
3120     /**
3121      *  Show target status
3122      */
3123     void targetstatus(const char *fmt, ...);
3125     /**
3126      *  Print a printf()-like formatted trace message
3127      */
3128     void trace(const char *fmt, ...);
3130     /**
3131      *  Check if a given string matches a given regex pattern
3132      */
3133     bool regexMatch(const String &str, const String &pattern);
3135     /**
3136      *
3137      */
3138     String getSuffix(const String &fname);
3140     /**
3141      * Break up a string into substrings delimited the characters
3142      * in delimiters.  Null-length substrings are ignored
3143      */  
3144     std::vector<String> tokenize(const String &val,
3145                           const String &delimiters);
3147     /**
3148      *  replace runs of whitespace with a space
3149      */
3150     String strip(const String &s);
3152     /**
3153      *  remove leading whitespace from each line
3154      */
3155     String leftJustify(const String &s);
3157     /**
3158      *  remove leading and trailing whitespace from string
3159      */
3160     String trim(const String &s);
3162     /**
3163      *  Return a lower case version of the given string
3164      */
3165     String toLower(const String &s);
3167     /**
3168      * Return the native format of the canonical
3169      * path which we store
3170      */
3171     String getNativePath(const String &path);
3173     /**
3174      * Execute a shell command.  Outbuf is a ref to a string
3175      * to catch the result.     
3176      */         
3177     bool executeCommand(const String &call,
3178                         const String &inbuf,
3179                         String &outbuf,
3180                         String &errbuf);
3181     /**
3182      * List all directories in a given base and starting directory
3183      * It is usually called like:
3184      *        bool ret = listDirectories("src", "", result);    
3185      */         
3186     bool listDirectories(const String &baseName,
3187                          const String &dirname,
3188                          std::vector<String> &res);
3190     /**
3191      * Find all files in the named directory 
3192      */         
3193     bool listFiles(const String &baseName,
3194                    const String &dirname,
3195                    std::vector<String> &result);
3197     /**
3198      * Perform a listing for a fileset 
3199      */         
3200     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3202     /**
3203      * Parse a <patternset>
3204      */  
3205     bool parsePatternSet(Element *elem,
3206                        MakeBase &propRef,
3207                        std::vector<String> &includes,
3208                        std::vector<String> &excludes);
3210     /**
3211      * Parse a <fileset> entry, and determine which files
3212      * should be included
3213      */  
3214     bool parseFileSet(Element *elem,
3215                     MakeBase &propRef,
3216                     FileSet &fileSet);
3217     /**
3218      * Parse a <filelist> entry
3219      */  
3220     bool parseFileList(Element *elem,
3221                     MakeBase &propRef,
3222                     FileList &fileList);
3224     /**
3225      * Return this object's property list
3226      */
3227     virtual std::map<String, String> &getProperties()
3228         { return properties; }
3231     std::map<String, String> properties;
3233     /**
3234      * Create a directory, making intermediate dirs
3235      * if necessary
3236      */                  
3237     bool createDirectory(const String &dirname);
3239     /**
3240      * Delete a directory and its children if desired
3241      */
3242     bool removeDirectory(const String &dirName);
3244     /**
3245      * Copy a file from one name to another. Perform only if needed
3246      */ 
3247     bool copyFile(const String &srcFile, const String &destFile);
3249     /**
3250      * Tests if the file exists and is a regular file
3251      */ 
3252     bool isRegularFile(const String &fileName);
3254     /**
3255      * Tests if the file exists and is a directory
3256      */ 
3257     bool isDirectory(const String &fileName);
3259     /**
3260      * Tests is the modification date of fileA is newer than fileB
3261      */ 
3262     bool isNewerThan(const String &fileA, const String &fileB);
3264 private:
3266     bool pkgConfigRecursive(const String packageName,
3267                             const String &path, 
3268                             const String &prefix, 
3269                             int query,
3270                             String &result,
3271                             std::set<String> &deplist);
3273     /**
3274      * utility method to query for "all", "cflags", or "libs" for this package and its
3275      * dependencies.  0, 1, 2
3276      */          
3277     bool pkgConfigQuery(const String &packageName, int query, String &result);
3279     /**
3280      * replace a variable ref like ${a} with a value
3281      */
3282     bool lookupProperty(const String &s, String &result);
3283     
3284     /**
3285      * called by getSubstitutions().  This is in case a looked-up string
3286      * has substitutions also.     
3287      */
3288     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3290     /**
3291      * replace variable refs in a string like ${a} with their values
3292      */
3293     bool getSubstitutions(const String &s, String &result);
3295     int line;
3298 };
3302 /**
3303  * Define the pkg-config class here, since it will be used in MakeBase method
3304  * implementations. 
3305  */
3306 class PkgConfig : public MakeBase
3309 public:
3311     /**
3312      *
3313      */
3314     PkgConfig()
3315         {
3316          path   = ".";
3317          prefix = "/target";
3318          init();
3319          }
3321     /**
3322      *
3323      */
3324     PkgConfig(const PkgConfig &other)
3325         { assign(other); }
3327     /**
3328      *
3329      */
3330     PkgConfig &operator=(const PkgConfig &other)
3331         { assign(other); return *this; }
3333     /**
3334      *
3335      */
3336     virtual ~PkgConfig()
3337         { }
3339     /**
3340      *
3341      */
3342     virtual String getName()
3343         { return name; }
3345     /**
3346      *
3347      */
3348     virtual String getPath()
3349         { return path; }
3351     /**
3352      *
3353      */
3354     virtual void setPath(const String &val)
3355         { path = val; }
3357     /**
3358      *
3359      */
3360     virtual String getPrefix()
3361         { return prefix; }
3363     /**
3364      *  Allow the user to override the prefix in the file
3365      */
3366     virtual void setPrefix(const String &val)
3367         { prefix = val; }
3369     /**
3370      *
3371      */
3372     virtual String getDescription()
3373         { return description; }
3375     /**
3376      *
3377      */
3378     virtual String getCflags()
3379         { return cflags; }
3381     /**
3382      *
3383      */
3384     virtual String getLibs()
3385         { return libs; }
3387     /**
3388      *
3389      */
3390     virtual String getAll()
3391         {
3392          String ret = cflags;
3393          ret.append(" ");
3394          ret.append(libs);
3395          return ret;
3396         }
3398     /**
3399      *
3400      */
3401     virtual String getVersion()
3402         { return version; }
3404     /**
3405      *
3406      */
3407     virtual int getMajorVersion()
3408         { return majorVersion; }
3410     /**
3411      *
3412      */
3413     virtual int getMinorVersion()
3414         { return minorVersion; }
3416     /**
3417      *
3418      */
3419     virtual int getMicroVersion()
3420         { return microVersion; }
3422     /**
3423      *
3424      */
3425     virtual std::map<String, String> &getAttributes()
3426         { return attrs; }
3428     /**
3429      *
3430      */
3431     virtual std::vector<String> &getRequireList()
3432         { return requireList; }
3434     /**
3435      *  Read a file for its details
3436      */         
3437     virtual bool readFile(const String &fileName);
3439     /**
3440      *  Read a file for its details
3441      */         
3442     virtual bool query(const String &name);
3444 private:
3446     void init()
3447         {
3448         //do not set path and prefix here
3449         name         = "";
3450         description  = "";
3451         cflags       = "";
3452         libs         = "";
3453         requires     = "";
3454         version      = "";
3455         majorVersion = 0;
3456         minorVersion = 0;
3457         microVersion = 0;
3458         fileName     = "";
3459         attrs.clear();
3460         requireList.clear();
3461         }
3463     void assign(const PkgConfig &other)
3464         {
3465         name         = other.name;
3466         path         = other.path;
3467         prefix       = other.prefix;
3468         description  = other.description;
3469         cflags       = other.cflags;
3470         libs         = other.libs;
3471         requires     = other.requires;
3472         version      = other.version;
3473         majorVersion = other.majorVersion;
3474         minorVersion = other.minorVersion;
3475         microVersion = other.microVersion;
3476         fileName     = other.fileName;
3477         attrs        = other.attrs;
3478         requireList  = other.requireList;
3479         }
3483     int get(int pos);
3485     int skipwhite(int pos);
3487     int getword(int pos, String &ret);
3489     /**
3490      * Very important
3491      */         
3492     bool parseRequires();
3494     void parseVersion();
3496     bool parseLine(const String &lineBuf);
3498     bool parse(const String &buf);
3500     void dumpAttrs();
3502     String name;
3504     String path;
3506     String prefix;
3508     String description;
3510     String cflags;
3512     String libs;
3514     String requires;
3516     String version;
3518     int majorVersion;
3520     int minorVersion;
3522     int microVersion;
3524     String fileName;
3526     std::map<String, String> attrs;
3528     std::vector<String> requireList;
3530     char *parsebuf;
3531     int parselen;
3532 };
3537 /**
3538  *  Print a printf()-like formatted error message
3539  */
3540 void MakeBase::error(const char *fmt, ...)
3542     va_list args;
3543     va_start(args,fmt);
3544     fprintf(stderr, "Make error line %d: ", line);
3545     vfprintf(stderr, fmt, args);
3546     fprintf(stderr, "\n");
3547     va_end(args) ;
3552 /**
3553  *  Print a printf()-like formatted trace message
3554  */
3555 void MakeBase::status(const char *fmt, ...)
3557     va_list args;
3558     //fprintf(stdout, " ");
3559     va_start(args,fmt);
3560     vfprintf(stdout, fmt, args);
3561     va_end(args);
3562     fprintf(stdout, "\n");
3563     fflush(stdout);
3567 /**
3568  *  Print a printf()-like formatted trace message
3569  */
3570 void MakeBase::trace(const char *fmt, ...)
3572     va_list args;
3573     fprintf(stdout, "Make: ");
3574     va_start(args,fmt);
3575     vfprintf(stdout, fmt, args);
3576     va_end(args) ;
3577     fprintf(stdout, "\n");
3578     fflush(stdout);
3583 /**
3584  *  Resolve another path relative to this one
3585  */
3586 String MakeBase::resolve(const String &otherPath)
3588     URI otherURI(otherPath);
3589     URI fullURI = uri.resolve(otherURI);
3590     String ret = fullURI.toString();
3591     return ret;
3596 /**
3597  *  Check if a given string matches a given regex pattern
3598  */
3599 bool MakeBase::regexMatch(const String &str, const String &pattern)
3601     const TRexChar *terror = NULL;
3602     const TRexChar *cpat = pattern.c_str();
3603     TRex *expr = trex_compile(cpat, &terror);
3604     if (!expr)
3605         {
3606         if (!terror)
3607             terror = "undefined";
3608         error("compilation error [%s]!\n", terror);
3609         return false;
3610         } 
3612     bool ret = true;
3614     const TRexChar *cstr = str.c_str();
3615     if (trex_match(expr, cstr))
3616         {
3617         ret = true;
3618         }
3619     else
3620         {
3621         ret = false;
3622         }
3624     trex_free(expr);
3626     return ret;
3629 /**
3630  *  Return the suffix, if any, of a file name
3631  */
3632 String MakeBase::getSuffix(const String &fname)
3634     if (fname.size() < 2)
3635         return "";
3636     unsigned int pos = fname.find_last_of('.');
3637     if (pos == fname.npos)
3638         return "";
3639     pos++;
3640     String res = fname.substr(pos, fname.size()-pos);
3641     //trace("suffix:%s", res.c_str()); 
3642     return res;
3647 /**
3648  * Break up a string into substrings delimited the characters
3649  * in delimiters.  Null-length substrings are ignored
3650  */  
3651 std::vector<String> MakeBase::tokenize(const String &str,
3652                                 const String &delimiters)
3655     std::vector<String> res;
3656     char *del = (char *)delimiters.c_str();
3657     String dmp;
3658     for (unsigned int i=0 ; i<str.size() ; i++)
3659         {
3660         char ch = str[i];
3661         char *p = (char *)0;
3662         for (p=del ; *p ; p++)
3663             if (*p == ch)
3664                 break;
3665         if (*p)
3666             {
3667             if (dmp.size() > 0)
3668                 {
3669                 res.push_back(dmp);
3670                 dmp.clear();
3671                 }
3672             }
3673         else
3674             {
3675             dmp.push_back(ch);
3676             }
3677         }
3678     //Add tail
3679     if (dmp.size() > 0)
3680         {
3681         res.push_back(dmp);
3682         dmp.clear();
3683         }
3685     return res;
3690 /**
3691  *  replace runs of whitespace with a single space
3692  */
3693 String MakeBase::strip(const String &s)
3695     int len = s.size();
3696     String stripped;
3697     for (int i = 0 ; i<len ; i++)
3698         {
3699         char ch = s[i];
3700         if (isspace(ch))
3701             {
3702             stripped.push_back(' ');
3703             for ( ; i<len ; i++)
3704                 {
3705                 ch = s[i];
3706                 if (!isspace(ch))
3707                     {
3708                     stripped.push_back(ch);
3709                     break;
3710                     }
3711                 }
3712             }
3713         else
3714             {
3715             stripped.push_back(ch);
3716             }
3717         }
3718     return stripped;
3721 /**
3722  *  remove leading whitespace from each line
3723  */
3724 String MakeBase::leftJustify(const String &s)
3726     String out;
3727     int len = s.size();
3728     for (int i = 0 ; i<len ; )
3729         {
3730         char ch;
3731         //Skip to first visible character
3732         while (i<len)
3733             {
3734             ch = s[i];
3735             if (ch == '\n' || ch == '\r'
3736               || !isspace(ch))
3737                   break;
3738             i++;
3739             }
3740         //Copy the rest of the line
3741         while (i<len)
3742             {
3743             ch = s[i];
3744             if (ch == '\n' || ch == '\r')
3745                 {
3746                 if (ch != '\r')
3747                     out.push_back('\n');
3748                 i++;
3749                 break;
3750                 }
3751             else
3752                 {
3753                 out.push_back(ch);
3754                 }
3755             i++;
3756             }
3757         }
3758     return out;
3762 /**
3763  *  Removes whitespace from beginning and end of a string
3764  */
3765 String MakeBase::trim(const String &s)
3767     if (s.size() < 1)
3768         return s;
3769     
3770     //Find first non-ws char
3771     unsigned int begin = 0;
3772     for ( ; begin < s.size() ; begin++)
3773         {
3774         if (!isspace(s[begin]))
3775             break;
3776         }
3778     //Find first non-ws char, going in reverse
3779     unsigned int end = s.size() - 1;
3780     for ( ; end > begin ; end--)
3781         {
3782         if (!isspace(s[end]))
3783             break;
3784         }
3785     //trace("begin:%d  end:%d", begin, end);
3787     String res = s.substr(begin, end-begin+1);
3788     return res;
3792 /**
3793  *  Return a lower case version of the given string
3794  */
3795 String MakeBase::toLower(const String &s)
3797     if (s.size()==0)
3798         return s;
3800     String ret;
3801     for(unsigned int i=0; i<s.size() ; i++)
3802         {
3803         ret.push_back(tolower(s[i]));
3804         }
3805     return ret;
3809 /**
3810  * Return the native format of the canonical
3811  * path which we store
3812  */
3813 String MakeBase::getNativePath(const String &path)
3815 #ifdef __WIN32__
3816     String npath;
3817     unsigned int firstChar = 0;
3818     if (path.size() >= 3)
3819         {
3820         if (path[0] == '/' &&
3821             isalpha(path[1]) &&
3822             path[2] == ':')
3823             firstChar++;
3824         }
3825     for (unsigned int i=firstChar ; i<path.size() ; i++)
3826         {
3827         char ch = path[i];
3828         if (ch == '/')
3829             npath.push_back('\\');
3830         else
3831             npath.push_back(ch);
3832         }
3833     return npath;
3834 #else
3835     return path;
3836 #endif
3840 #ifdef __WIN32__
3841 #include <tchar.h>
3843 static String win32LastError()
3846     DWORD dw = GetLastError(); 
3848     LPVOID str;
3849     FormatMessage(
3850         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3851         FORMAT_MESSAGE_FROM_SYSTEM,
3852         NULL,
3853         dw,
3854         0,
3855         (LPTSTR) &str,
3856         0, NULL );
3857     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3858     if(p != NULL)
3859         { // lose CRLF
3860         *p = _T('\0');
3861         }
3862     String ret = (char *)str;
3863     LocalFree(str);
3865     return ret;
3867 #endif
3872 #ifdef __WIN32__
3874 /**
3875  * Execute a system call, using pipes to send data to the
3876  * program's stdin,  and reading stdout and stderr.
3877  */
3878 bool MakeBase::executeCommand(const String &command,
3879                               const String &inbuf,
3880                               String &outbuf,
3881                               String &errbuf)
3884     status("============ cmd ============\n%s\n=============================",
3885                 command.c_str());
3887     outbuf.clear();
3888     errbuf.clear();
3889     
3891     /*
3892     I really hate having win32 code in this program, but the
3893     read buffer in command.com and cmd.exe are just too small
3894     for the large commands we need for compiling and linking.
3895     */
3897     bool ret = true;
3899     //# Allocate a separate buffer for safety
3900     char *paramBuf = new char[command.size() + 1];
3901     if (!paramBuf)
3902        {
3903        error("executeCommand cannot allocate command buffer");
3904        return false;
3905        }
3906     strcpy(paramBuf, (char *)command.c_str());
3908     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3909     //# to see how Win32 pipes work
3911     //# Create pipes
3912     SECURITY_ATTRIBUTES saAttr; 
3913     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3914     saAttr.bInheritHandle = TRUE; 
3915     saAttr.lpSecurityDescriptor = NULL; 
3916     HANDLE stdinRead,  stdinWrite;
3917     HANDLE stdoutRead, stdoutWrite;
3918     HANDLE stderrRead, stderrWrite;
3919     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3920         {
3921         error("executeProgram: could not create pipe");
3922         delete[] paramBuf;
3923         return false;
3924         } 
3925     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3926     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3927         {
3928         error("executeProgram: could not create pipe");
3929         delete[] paramBuf;
3930         return false;
3931         } 
3932     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3933     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3934         {
3935         error("executeProgram: could not create pipe");
3936         delete[] paramBuf;
3937         return false;
3938         } 
3939     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3941     // Create the process
3942     STARTUPINFO siStartupInfo;
3943     PROCESS_INFORMATION piProcessInfo;
3944     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3945     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3946     siStartupInfo.cb = sizeof(siStartupInfo);
3947     siStartupInfo.hStdError   =  stderrWrite;
3948     siStartupInfo.hStdOutput  =  stdoutWrite;
3949     siStartupInfo.hStdInput   =  stdinRead;
3950     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3951    
3952     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3953                 0, NULL, NULL, &siStartupInfo,
3954                 &piProcessInfo))
3955         {
3956         error("executeCommand : could not create process : %s",
3957                     win32LastError().c_str());
3958         ret = false;
3959         }
3961     delete[] paramBuf;
3963     DWORD bytesWritten;
3964     if (inbuf.size()>0 &&
3965         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3966                &bytesWritten, NULL))
3967         {
3968         error("executeCommand: could not write to pipe");
3969         return false;
3970         }    
3971     if (!CloseHandle(stdinWrite))
3972         {          
3973         error("executeCommand: could not close write pipe");
3974         return false;
3975         }
3976     if (!CloseHandle(stdoutWrite))
3977         {
3978         error("executeCommand: could not close read pipe");
3979         return false;
3980         }
3981     if (!CloseHandle(stderrWrite))
3982         {
3983         error("executeCommand: could not close read pipe");
3984         return false;
3985         }
3987     bool lastLoop = false;
3988     while (true)
3989         {
3990         DWORD avail;
3991         DWORD bytesRead;
3992         char readBuf[4096];
3994         //trace("## stderr");
3995         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3996         if (avail > 0)
3997             {
3998             bytesRead = 0;
3999             if (avail>4096) avail = 4096;
4000             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4001             if (bytesRead > 0)
4002                 {
4003                 for (unsigned int i=0 ; i<bytesRead ; i++)
4004                     errbuf.push_back(readBuf[i]);
4005                 }
4006             }
4008         //trace("## stdout");
4009         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4010         if (avail > 0)
4011             {
4012             bytesRead = 0;
4013             if (avail>4096) avail = 4096;
4014             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4015             if (bytesRead > 0)
4016                 {
4017                 for (unsigned int i=0 ; i<bytesRead ; i++)
4018                     outbuf.push_back(readBuf[i]);
4019                 }
4020             }
4021             
4022         //Was this the final check after program done?
4023         if (lastLoop)
4024             break;
4026         DWORD exitCode;
4027         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4028         if (exitCode != STILL_ACTIVE)
4029             lastLoop = true;
4031         Sleep(10);
4032         }    
4033     //trace("outbuf:%s", outbuf.c_str());
4034     if (!CloseHandle(stdoutRead))
4035         {
4036         error("executeCommand: could not close read pipe");
4037         return false;
4038         }
4039     if (!CloseHandle(stderrRead))
4040         {
4041         error("executeCommand: could not close read pipe");
4042         return false;
4043         }
4045     DWORD exitCode;
4046     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4047     //trace("exit code:%d", exitCode);
4048     if (exitCode != 0)
4049         {
4050         ret = false;
4051         }
4052     
4053     CloseHandle(piProcessInfo.hProcess);
4054     CloseHandle(piProcessInfo.hThread);
4056     return ret;
4058
4060 #else  /*do it unix style*/
4062 /**
4063  * Execute a system call, using pipes to send data to the
4064  * program's stdin,  and reading stdout and stderr.
4065  */
4066 bool MakeBase::executeCommand(const String &command,
4067                               const String &inbuf,
4068                               String &outbuf,
4069                               String &errbuf)
4072     status("============ cmd ============\n%s\n=============================",
4073                 command.c_str());
4075     outbuf.clear();
4076     errbuf.clear();
4077     
4079     int outfds[2];
4080     if (pipe(outfds) < 0)
4081         return false;
4082     int errfds[2];
4083     if (pipe(errfds) < 0)
4084         return false;
4085     int pid = fork();
4086     if (pid < 0)
4087         {
4088         close(outfds[0]);
4089         close(outfds[1]);
4090         close(errfds[0]);
4091         close(errfds[1]);
4092         error("launch of command '%s' failed : %s",
4093              command.c_str(), strerror(errno));
4094         return false;
4095         }
4096     else if (pid > 0) // parent
4097         {
4098         close(outfds[1]);
4099         close(errfds[1]);
4100         }
4101     else // == 0, child
4102         {
4103         close(outfds[0]);
4104         dup2(outfds[1], STDOUT_FILENO);
4105         close(outfds[1]);
4106         close(errfds[0]);
4107         dup2(errfds[1], STDERR_FILENO);
4108         close(errfds[1]);
4110         char *args[4];
4111         args[0] = (char *)"sh";
4112         args[1] = (char *)"-c";
4113         args[2] = (char *)command.c_str();
4114         args[3] = NULL;
4115         execv("/bin/sh", args);
4116         _exit(EXIT_FAILURE);
4117         }
4119     String outb;
4120     String errb;
4122     while (1)
4123         {
4124         unsigned char outch;
4125         int rout = read(outfds[0], &outch, 1);
4126         if (rout>0)
4127              outb.push_back(outch);
4128         unsigned char errch;
4129         int rerr = read(errfds[0], &errch, 1);
4130         if (rerr>0)
4131             errb.push_back(errch);
4132         if (rout <=0 && rerr <=0)
4133             break;
4134         }
4136     int childReturnValue;
4137     wait(&childReturnValue);
4139     close(outfds[0]);
4140     close(errfds[0]);
4142     outbuf = outb;
4143     errbuf = errb;
4145     if (childReturnValue != 0)
4146         {
4147         error("exec of command '%s' failed : %s",
4148              command.c_str(), strerror(childReturnValue));
4149         return false;
4150         }
4152     return true;
4153
4155 #endif
4160 bool MakeBase::listDirectories(const String &baseName,
4161                               const String &dirName,
4162                               std::vector<String> &res)
4164     res.push_back(dirName);
4165     String fullPath = baseName;
4166     if (dirName.size()>0)
4167         {
4168         fullPath.append("/");
4169         fullPath.append(dirName);
4170         }
4171     DIR *dir = opendir(fullPath.c_str());
4172     while (true)
4173         {
4174         struct dirent *de = readdir(dir);
4175         if (!de)
4176             break;
4178         //Get the directory member name
4179         String s = de->d_name;
4180         if (s.size() == 0 || s[0] == '.')
4181             continue;
4182         String childName = dirName;
4183         childName.append("/");
4184         childName.append(s);
4186         String fullChildPath = baseName;
4187         fullChildPath.append("/");
4188         fullChildPath.append(childName);
4189         struct stat finfo;
4190         String childNative = getNativePath(fullChildPath);
4191         if (stat(childNative.c_str(), &finfo)<0)
4192             {
4193             error("cannot stat file:%s", childNative.c_str());
4194             }
4195         else if (S_ISDIR(finfo.st_mode))
4196             {
4197             //trace("directory: %s", childName.c_str());
4198             if (!listDirectories(baseName, childName, res))
4199                 return false;
4200             }
4201         }
4202     closedir(dir);
4204     return true;
4208 bool MakeBase::listFiles(const String &baseDir,
4209                          const String &dirName,
4210                          std::vector<String> &res)
4212     String fullDir = baseDir;
4213     if (dirName.size()>0)
4214         {
4215         fullDir.append("/");
4216         fullDir.append(dirName);
4217         }
4218     String dirNative = getNativePath(fullDir);
4220     std::vector<String> subdirs;
4221     DIR *dir = opendir(dirNative.c_str());
4222     if (!dir)
4223         {
4224         error("Could not open directory %s : %s",
4225               dirNative.c_str(), strerror(errno));
4226         return false;
4227         }
4228     while (true)
4229         {
4230         struct dirent *de = readdir(dir);
4231         if (!de)
4232             break;
4234         //Get the directory member name
4235         String s = de->d_name;
4236         if (s.size() == 0 || s[0] == '.')
4237             continue;
4238         String childName;
4239         if (dirName.size()>0)
4240             {
4241             childName.append(dirName);
4242             childName.append("/");
4243             }
4244         childName.append(s);
4245         String fullChild = baseDir;
4246         fullChild.append("/");
4247         fullChild.append(childName);
4248         
4249         if (isDirectory(fullChild))
4250             {
4251             //trace("directory: %s", childName.c_str());
4252             if (!listFiles(baseDir, childName, res))
4253                 return false;
4254             continue;
4255             }
4256         else if (!isRegularFile(fullChild))
4257             {
4258             error("unknown file:%s", childName.c_str());
4259             return false;
4260             }
4262        //all done!
4263         res.push_back(childName);
4265         }
4266     closedir(dir);
4268     return true;
4272 /**
4273  * Several different classes extend MakeBase.  By "propRef", we mean
4274  * the one holding the properties.  Likely "Make" itself
4275  */
4276 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4278     //before doing the list,  resolve any property references
4279     //that might have been specified in the directory name, such as ${src}
4280     String fsDir = fileSet.getDirectory();
4281     String dir;
4282     if (!propRef.getSubstitutions(fsDir, dir))
4283         return false;
4284     String baseDir = propRef.resolve(dir);
4285     std::vector<String> fileList;
4286     if (!listFiles(baseDir, "", fileList))
4287         return false;
4289     std::vector<String> includes = fileSet.getIncludes();
4290     std::vector<String> excludes = fileSet.getExcludes();
4292     std::vector<String> incs;
4293     std::vector<String>::iterator iter;
4295     std::sort(fileList.begin(), fileList.end());
4297     //If there are <includes>, then add files to the output
4298     //in the order of the include list
4299     if (includes.size()==0)
4300         incs = fileList;
4301     else
4302         {
4303         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4304             {
4305             String &pattern = *iter;
4306             std::vector<String>::iterator siter;
4307             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4308                 {
4309                 String s = *siter;
4310                 if (regexMatch(s, pattern))
4311                     {
4312                     //trace("INCLUDED:%s", s.c_str());
4313                     incs.push_back(s);
4314                     }
4315                 }
4316             }
4317         }
4319     //Now trim off the <excludes>
4320     std::vector<String> res;
4321     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4322         {
4323         String s = *iter;
4324         bool skipme = false;
4325         std::vector<String>::iterator siter;
4326         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4327             {
4328             String &pattern = *siter;
4329             if (regexMatch(s, pattern))
4330                 {
4331                 //trace("EXCLUDED:%s", s.c_str());
4332                 skipme = true;
4333                 break;
4334                 }
4335             }
4336         if (!skipme)
4337             res.push_back(s);
4338         }
4339         
4340     fileSet.setFiles(res);
4342     return true;
4346 /**
4347  * 0 == all, 1 = cflags, 2 = libs
4348  */ 
4349 bool MakeBase::pkgConfigRecursive(const String packageName,
4350                                   const String &path, 
4351                                   const String &prefix, 
4352                                   int query,
4353                                   String &result,
4354                                   std::set<String> &deplist) 
4356     PkgConfig pkgConfig;
4357     if (path.size() > 0)
4358         pkgConfig.setPath(path);
4359     if (prefix.size() > 0)
4360         pkgConfig.setPrefix(prefix);
4361     if (!pkgConfig.query(packageName))
4362         return false;
4363     if (query == 0)
4364         result = pkgConfig.getAll();
4365     else if (query == 1)
4366         result = pkgConfig.getCflags();
4367     else
4368         result = pkgConfig.getLibs();
4369     deplist.insert(packageName);
4370     std::vector<String> list = pkgConfig.getRequireList();
4371     for (unsigned int i = 0 ; i<list.size() ; i++)
4372         {
4373         String depPkgName = list[i];
4374         if (deplist.find(depPkgName) != deplist.end())
4375             continue;
4376         String val;
4377         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4378             {
4379             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4380             return false;
4381             }
4382         result.append(" ");
4383         result.append(val);
4384         }
4386     return true;
4389 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4391     std::set<String> deplist;
4392     String path = getProperty("pkg-config-path");
4393     if (path.size()>0)
4394         path = resolve(path);
4395     String prefix = getProperty("pkg-config-prefix");
4396     String val;
4397     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4398         return false;
4399     result = val;
4400     return true;
4405 /**
4406  * replace a variable ref like ${a} with a value
4407  */
4408 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4410     String varname = propertyName;
4411     if (envPrefix.size() > 0 &&
4412         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4413         {
4414         varname = varname.substr(envPrefix.size());
4415         char *envstr = getenv(varname.c_str());
4416         if (!envstr)
4417             {
4418             error("environment variable '%s' not defined", varname.c_str());
4419             return false;
4420             }
4421         result = envstr;
4422         }
4423     else if (pcPrefix.size() > 0 &&
4424         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4425         {
4426         varname = varname.substr(pcPrefix.size());
4427         String val;
4428         if (!pkgConfigQuery(varname, 0, val))
4429             return false;
4430         result = val;
4431         }
4432     else if (pccPrefix.size() > 0 &&
4433         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4434         {
4435         varname = varname.substr(pccPrefix.size());
4436         String val;
4437         if (!pkgConfigQuery(varname, 1, val))
4438             return false;
4439         result = val;
4440         }
4441     else if (pclPrefix.size() > 0 &&
4442         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4443         {
4444         varname = varname.substr(pclPrefix.size());
4445         String val;
4446         if (!pkgConfigQuery(varname, 2, val))
4447             return false;
4448         result = val;
4449         }
4450     else
4451         {
4452         std::map<String, String>::iterator iter;
4453         iter = properties.find(varname);
4454         if (iter != properties.end())
4455             {
4456             result = iter->second;
4457             }
4458         else
4459             {
4460             error("property '%s' not found", varname.c_str());
4461             return false;
4462             }
4463         }
4464     return true;
4470 /**
4471  * Analyse a string, looking for any substitutions or other
4472  * things that need resolution 
4473  */
4474 bool MakeBase::getSubstitutionsRecursive(const String &str,
4475                                          String &result, int depth)
4477     if (depth > 10)
4478         {
4479         error("nesting of substitutions too deep (>10) for '%s'",
4480                         str.c_str());
4481         return false;
4482         }
4483     String s = trim(str);
4484     int len = (int)s.size();
4485     String val;
4486     for (int i=0 ; i<len ; i++)
4487         {
4488         char ch = s[i];
4489         if (ch == '$' && s[i+1] == '{')
4490             {
4491             String varname;
4492             int j = i+2;
4493             for ( ; j<len ; j++)
4494                 {
4495                 ch = s[j];
4496                 if (ch == '$' && s[j+1] == '{')
4497                     {
4498                     error("attribute %s cannot have nested variable references",
4499                            s.c_str());
4500                     return false;
4501                     }
4502                 else if (ch == '}')
4503                     {
4504                     varname = trim(varname);
4505                     String varval;
4506                     if (!lookupProperty(varname, varval))
4507                         return false;
4508                     String varval2;
4509                     //Now see if the answer has ${} in it, too
4510                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4511                         return false;
4512                     val.append(varval2);
4513                     break;
4514                     }
4515                 else
4516                     {
4517                     varname.push_back(ch);
4518                     }
4519                 }
4520             i = j;
4521             }
4522         else
4523             {
4524             val.push_back(ch);
4525             }
4526         }
4527     result = val;
4528     return true;
4531 /**
4532  * Analyse a string, looking for any substitutions or other
4533  * things that need resilution 
4534  */
4535 bool MakeBase::getSubstitutions(const String &str, String &result)
4537     return getSubstitutionsRecursive(str, result, 0);
4542 /**
4543  * replace variable refs like ${a} with their values
4544  * Assume that the string has already been syntax validated
4545  */
4546 String MakeBase::eval(const String &s, const String &defaultVal)
4548     if (s.size()==0)
4549         return defaultVal;
4550     String ret;
4551     if (getSubstitutions(s, ret))
4552         return ret;
4553     else
4554         return defaultVal;
4558 /**
4559  * replace variable refs like ${a} with their values
4560  * return true or false
4561  * Assume that the string has already been syntax validated
4562  */
4563 bool MakeBase::evalBool(const String &s, bool defaultVal)
4565     if (s.size()==0)
4566         return defaultVal;
4567     String val = eval(s, "false");
4568     if (val.size()==0)
4569         return defaultVal;
4570     if (val == "true" || val == "TRUE")
4571         return true;
4572     else
4573         return false;
4577 /**
4578  * Get a string attribute, testing it for proper syntax and
4579  * property names.
4580  */
4581 bool MakeBase::getAttribute(Element *elem, const String &name,
4582                                     String &result)
4584     String s = elem->getAttribute(name);
4585     String tmp;
4586     bool ret = getSubstitutions(s, tmp);
4587     if (ret)
4588         result = s;  //assign -if- ok
4589     return ret;
4593 /**
4594  * Get a string value, testing it for proper syntax and
4595  * property names.
4596  */
4597 bool MakeBase::getValue(Element *elem, String &result)
4599     String s = elem->getValue();
4600     String tmp;
4601     bool ret = getSubstitutions(s, tmp);
4602     if (ret)
4603         result = s;  //assign -if- ok
4604     return ret;
4610 /**
4611  * Parse a <patternset> entry
4612  */  
4613 bool MakeBase::parsePatternSet(Element *elem,
4614                           MakeBase &propRef,
4615                           std::vector<String> &includes,
4616                           std::vector<String> &excludes
4617                           )
4619     std::vector<Element *> children  = elem->getChildren();
4620     for (unsigned int i=0 ; i<children.size() ; i++)
4621         {
4622         Element *child = children[i];
4623         String tagName = child->getName();
4624         if (tagName == "exclude")
4625             {
4626             String fname;
4627             if (!propRef.getAttribute(child, "name", fname))
4628                 return false;
4629             //trace("EXCLUDE: %s", fname.c_str());
4630             excludes.push_back(fname);
4631             }
4632         else if (tagName == "include")
4633             {
4634             String fname;
4635             if (!propRef.getAttribute(child, "name", fname))
4636                 return false;
4637             //trace("INCLUDE: %s", fname.c_str());
4638             includes.push_back(fname);
4639             }
4640         }
4642     return true;
4648 /**
4649  * Parse a <fileset> entry, and determine which files
4650  * should be included
4651  */  
4652 bool MakeBase::parseFileSet(Element *elem,
4653                           MakeBase &propRef,
4654                           FileSet &fileSet)
4656     String name = elem->getName();
4657     if (name != "fileset")
4658         {
4659         error("expected <fileset>");
4660         return false;
4661         }
4664     std::vector<String> includes;
4665     std::vector<String> excludes;
4667     //A fileset has one implied patternset
4668     if (!parsePatternSet(elem, propRef, includes, excludes))
4669         {
4670         return false;
4671         }
4672     //Look for child tags, including more patternsets
4673     std::vector<Element *> children  = elem->getChildren();
4674     for (unsigned int i=0 ; i<children.size() ; i++)
4675         {
4676         Element *child = children[i];
4677         String tagName = child->getName();
4678         if (tagName == "patternset")
4679             {
4680             if (!parsePatternSet(child, propRef, includes, excludes))
4681                 {
4682                 return false;
4683                 }
4684             }
4685         }
4687     String dir;
4688     //Now do the stuff
4689     //Get the base directory for reading file names
4690     if (!propRef.getAttribute(elem, "dir", dir))
4691         return false;
4693     fileSet.setDirectory(dir);
4694     fileSet.setIncludes(includes);
4695     fileSet.setExcludes(excludes);
4696     
4697     /*
4698     std::vector<String> fileList;
4699     if (dir.size() > 0)
4700         {
4701         String baseDir = propRef.resolve(dir);
4702         if (!listFiles(baseDir, "", includes, excludes, fileList))
4703             return false;
4704         }
4705     std::sort(fileList.begin(), fileList.end());
4706     result = fileList;
4707     */
4709     
4710     /*
4711     for (unsigned int i=0 ; i<result.size() ; i++)
4712         {
4713         trace("RES:%s", result[i].c_str());
4714         }
4715     */
4717     
4718     return true;
4721 /**
4722  * Parse a <filelist> entry.  This is far simpler than FileSet,
4723  * since no directory scanning is needed.  The file names are listed
4724  * explicitly.
4725  */  
4726 bool MakeBase::parseFileList(Element *elem,
4727                           MakeBase &propRef,
4728                           FileList &fileList)
4730     std::vector<String> fnames;
4731     //Look for child tags, namely "file"
4732     std::vector<Element *> children  = elem->getChildren();
4733     for (unsigned int i=0 ; i<children.size() ; i++)
4734         {
4735         Element *child = children[i];
4736         String tagName = child->getName();
4737         if (tagName == "file")
4738             {
4739             String fname = child->getAttribute("name");
4740             if (fname.size()==0)
4741                 {
4742                 error("<file> element requires name="" attribute");
4743                 return false;
4744                 }
4745             fnames.push_back(fname);
4746             }
4747         else
4748             {
4749             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4750             return false;
4751             }
4752         }
4754     String dir;
4755     //Get the base directory for reading file names
4756     if (!propRef.getAttribute(elem, "dir", dir))
4757         return false;
4758     fileList.setDirectory(dir);
4759     fileList.setFiles(fnames);
4761     return true;
4766 /**
4767  * Create a directory, making intermediate dirs
4768  * if necessary
4769  */                  
4770 bool MakeBase::createDirectory(const String &dirname)
4772     //trace("## createDirectory: %s", dirname.c_str());
4773     //## first check if it exists
4774     struct stat finfo;
4775     String nativeDir = getNativePath(dirname);
4776     char *cnative = (char *) nativeDir.c_str();
4777 #ifdef __WIN32__
4778     if (strlen(cnative)==2 && cnative[1]==':')
4779         return true;
4780 #endif
4781     if (stat(cnative, &finfo)==0)
4782         {
4783         if (!S_ISDIR(finfo.st_mode))
4784             {
4785             error("mkdir: file %s exists but is not a directory",
4786                   cnative);
4787             return false;
4788             }
4789         else //exists
4790             {
4791             return true;
4792             }
4793         }
4795     //## 2: pull off the last path segment, if any,
4796     //## to make the dir 'above' this one, if necessary
4797     unsigned int pos = dirname.find_last_of('/');
4798     if (pos>0 && pos != dirname.npos)
4799         {
4800         String subpath = dirname.substr(0, pos);
4801         //A letter root (c:) ?
4802         if (!createDirectory(subpath))
4803             return false;
4804         }
4805         
4806     //## 3: now make
4807 #ifdef __WIN32__
4808     if (mkdir(cnative)<0)
4809 #else
4810     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4811 #endif
4812         {
4813         error("cannot make directory '%s' : %s",
4814                  cnative, strerror(errno));
4815         return false;
4816         }
4817         
4818     return true;
4822 /**
4823  * Remove a directory recursively
4824  */ 
4825 bool MakeBase::removeDirectory(const String &dirName)
4827     char *dname = (char *)dirName.c_str();
4829     DIR *dir = opendir(dname);
4830     if (!dir)
4831         {
4832         //# Let this fail nicely.
4833         return true;
4834         //error("error opening directory %s : %s", dname, strerror(errno));
4835         //return false;
4836         }
4837     
4838     while (true)
4839         {
4840         struct dirent *de = readdir(dir);
4841         if (!de)
4842             break;
4844         //Get the directory member name
4845         String s = de->d_name;
4846         if (s.size() == 0 || s[0] == '.')
4847             continue;
4848         String childName;
4849         if (dirName.size() > 0)
4850             {
4851             childName.append(dirName);
4852             childName.append("/");
4853             }
4854         childName.append(s);
4857         struct stat finfo;
4858         String childNative = getNativePath(childName);
4859         char *cnative = (char *)childNative.c_str();
4860         if (stat(cnative, &finfo)<0)
4861             {
4862             error("cannot stat file:%s", cnative);
4863             }
4864         else if (S_ISDIR(finfo.st_mode))
4865             {
4866             //trace("DEL dir: %s", childName.c_str());
4867             if (!removeDirectory(childName))
4868                 {
4869                 return false;
4870                 }
4871             }
4872         else if (!S_ISREG(finfo.st_mode))
4873             {
4874             //trace("not regular: %s", cnative);
4875             }
4876         else
4877             {
4878             //trace("DEL file: %s", childName.c_str());
4879             if (remove(cnative)<0)
4880                 {
4881                 error("error deleting %s : %s",
4882                      cnative, strerror(errno));
4883                 return false;
4884                 }
4885             }
4886         }
4887     closedir(dir);
4889     //Now delete the directory
4890     String native = getNativePath(dirName);
4891     if (rmdir(native.c_str())<0)
4892         {
4893         error("could not delete directory %s : %s",
4894             native.c_str() , strerror(errno));
4895         return false;
4896         }
4898     return true;
4899     
4903 /**
4904  * Copy a file from one name to another. Perform only if needed
4905  */ 
4906 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4908     //# 1 Check up-to-date times
4909     String srcNative = getNativePath(srcFile);
4910     struct stat srcinfo;
4911     if (stat(srcNative.c_str(), &srcinfo)<0)
4912         {
4913         error("source file %s for copy does not exist",
4914                  srcNative.c_str());
4915         return false;
4916         }
4918     String destNative = getNativePath(destFile);
4919     struct stat destinfo;
4920     if (stat(destNative.c_str(), &destinfo)==0)
4921         {
4922         if (destinfo.st_mtime >= srcinfo.st_mtime)
4923             return true;
4924         }
4925         
4926     //# 2 prepare a destination directory if necessary
4927     unsigned int pos = destFile.find_last_of('/');
4928     if (pos != destFile.npos)
4929         {
4930         String subpath = destFile.substr(0, pos);
4931         if (!createDirectory(subpath))
4932             return false;
4933         }
4935     //# 3 do the data copy
4936 #ifndef __WIN32__
4938     FILE *srcf = fopen(srcNative.c_str(), "rb");
4939     if (!srcf)
4940         {
4941         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4942         return false;
4943         }
4944     FILE *destf = fopen(destNative.c_str(), "wb");
4945     if (!destf)
4946         {
4947         error("copyFile cannot open %s for writing", srcNative.c_str());
4948         return false;
4949         }
4951     while (!feof(srcf))
4952         {
4953         int ch = fgetc(srcf);
4954         if (ch<0)
4955             break;
4956         fputc(ch, destf);
4957         }
4959     fclose(destf);
4960     fclose(srcf);
4962 #else
4963     
4964     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4965         {
4966         error("copyFile from %s to %s failed",
4967              srcNative.c_str(), destNative.c_str());
4968         return false;
4969         }
4970         
4971 #endif /* __WIN32__ */
4974     return true;
4979 /**
4980  * Tests if the file exists and is a regular file
4981  */ 
4982 bool MakeBase::isRegularFile(const String &fileName)
4984     String native = getNativePath(fileName);
4985     struct stat finfo;
4986     
4987     //Exists?
4988     if (stat(native.c_str(), &finfo)<0)
4989         return false;
4992     //check the file mode
4993     if (!S_ISREG(finfo.st_mode))
4994         return false;
4996     return true;
4999 /**
5000  * Tests if the file exists and is a directory
5001  */ 
5002 bool MakeBase::isDirectory(const String &fileName)
5004     String native = getNativePath(fileName);
5005     struct stat finfo;
5006     
5007     //Exists?
5008     if (stat(native.c_str(), &finfo)<0)
5009         return false;
5012     //check the file mode
5013     if (!S_ISDIR(finfo.st_mode))
5014         return false;
5016     return true;
5021 /**
5022  * Tests is the modification of fileA is newer than fileB
5023  */ 
5024 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5026     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5027     String nativeA = getNativePath(fileA);
5028     struct stat infoA;
5029     //IF source does not exist, NOT newer
5030     if (stat(nativeA.c_str(), &infoA)<0)
5031         {
5032         return false;
5033         }
5035     String nativeB = getNativePath(fileB);
5036     struct stat infoB;
5037     //IF dest does not exist, YES, newer
5038     if (stat(nativeB.c_str(), &infoB)<0)
5039         {
5040         return true;
5041         }
5043     //check the actual times
5044     if (infoA.st_mtime > infoB.st_mtime)
5045         {
5046         return true;
5047         }
5049     return false;
5053 //########################################################################
5054 //# P K G    C O N F I G
5055 //########################################################################
5058 /**
5059  * Get a character from the buffer at pos.  If out of range,
5060  * return -1 for safety
5061  */
5062 int PkgConfig::get(int pos)
5064     if (pos>parselen)
5065         return -1;
5066     return parsebuf[pos];
5071 /**
5072  *  Skip over all whitespace characters beginning at pos.  Return
5073  *  the position of the first non-whitespace character.
5074  *  Pkg-config is line-oriented, so check for newline
5075  */
5076 int PkgConfig::skipwhite(int pos)
5078     while (pos < parselen)
5079         {
5080         int ch = get(pos);
5081         if (ch < 0)
5082             break;
5083         if (!isspace(ch))
5084             break;
5085         pos++;
5086         }
5087     return pos;
5091 /**
5092  *  Parse the buffer beginning at pos, for a word.  Fill
5093  *  'ret' with the result.  Return the position after the
5094  *  word.
5095  */
5096 int PkgConfig::getword(int pos, String &ret)
5098     while (pos < parselen)
5099         {
5100         int ch = get(pos);
5101         if (ch < 0)
5102             break;
5103         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5104             break;
5105         ret.push_back((char)ch);
5106         pos++;
5107         }
5108     return pos;
5111 bool PkgConfig::parseRequires()
5113     if (requires.size() == 0)
5114         return true;
5115     parsebuf = (char *)requires.c_str();
5116     parselen = requires.size();
5117     int pos = 0;
5118     while (pos < parselen)
5119         {
5120         pos = skipwhite(pos);
5121         String val;
5122         int pos2 = getword(pos, val);
5123         if (pos2 == pos)
5124             break;
5125         pos = pos2;
5126         //trace("val %s", val.c_str());
5127         requireList.push_back(val);
5128         }
5129     return true;
5133 static int getint(const String str)
5135     char *s = (char *)str.c_str();
5136     char *ends = NULL;
5137     long val = strtol(s, &ends, 10);
5138     if (ends == s)
5139         return 0L;
5140     else
5141         return val;
5144 void PkgConfig::parseVersion()
5146     if (version.size() == 0)
5147         return;
5148     String s1, s2, s3;
5149     unsigned int pos = 0;
5150     unsigned int pos2 = version.find('.', pos);
5151     if (pos2 == version.npos)
5152         {
5153         s1 = version;
5154         }
5155     else
5156         {
5157         s1 = version.substr(pos, pos2-pos);
5158         pos = pos2;
5159         pos++;
5160         if (pos < version.size())
5161             {
5162             pos2 = version.find('.', pos);
5163             if (pos2 == version.npos)
5164                 {
5165                 s2 = version.substr(pos, version.size()-pos);
5166                 }
5167             else
5168                 {
5169                 s2 = version.substr(pos, pos2-pos);
5170                 pos = pos2;
5171                 pos++;
5172                 if (pos < version.size())
5173                     s3 = version.substr(pos, pos2-pos);
5174                 }
5175             }
5176         }
5178     majorVersion = getint(s1);
5179     minorVersion = getint(s2);
5180     microVersion = getint(s3);
5181     //trace("version:%d.%d.%d", majorVersion,
5182     //          minorVersion, microVersion );
5186 bool PkgConfig::parseLine(const String &lineBuf)
5188     parsebuf = (char *)lineBuf.c_str();
5189     parselen = lineBuf.size();
5190     int pos = 0;
5191     
5192     while (pos < parselen)
5193         {
5194         String attrName;
5195         pos = skipwhite(pos);
5196         int ch = get(pos);
5197         if (ch == '#')
5198             {
5199             //comment.  eat the rest of the line
5200             while (pos < parselen)
5201                 {
5202                 ch = get(pos);
5203                 if (ch == '\n' || ch < 0)
5204                     break;
5205                 pos++;
5206                 }
5207             continue;
5208             }
5209         pos = getword(pos, attrName);
5210         if (attrName.size() == 0)
5211             continue;
5212         
5213         pos = skipwhite(pos);
5214         ch = get(pos);
5215         if (ch != ':' && ch != '=')
5216             {
5217             error("expected ':' or '='");
5218             return false;
5219             }
5220         pos++;
5221         pos = skipwhite(pos);
5222         String attrVal;
5223         while (pos < parselen)
5224             {
5225             ch = get(pos);
5226             if (ch == '\n' || ch < 0)
5227                 break;
5228             else if (ch == '$' && get(pos+1) == '{')
5229                 {
5230                 //#  this is a ${substitution}
5231                 pos += 2;
5232                 String subName;
5233                 while (pos < parselen)
5234                     {
5235                     ch = get(pos);
5236                     if (ch < 0)
5237                         {
5238                         error("unterminated substitution");
5239                         return false;
5240                         }
5241                     else if (ch == '}')
5242                         break;
5243                     else
5244                         subName.push_back((char)ch);
5245                     pos++;
5246                     }
5247                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5248                 if (subName == "prefix" && prefix.size()>0)
5249                     {
5250                     attrVal.append(prefix);
5251                     //trace("prefix override:%s", prefix.c_str());
5252                     }
5253                 else
5254                     {
5255                     String subVal = attrs[subName];
5256                     //trace("subVal:%s", subVal.c_str());
5257                     attrVal.append(subVal);
5258                     }
5259                 }
5260             else
5261                 attrVal.push_back((char)ch);
5262             pos++;
5263             }
5265         attrVal = trim(attrVal);
5266         attrs[attrName] = attrVal;
5268         String attrNameL = toLower(attrName);
5270         if (attrNameL == "name")
5271             name = attrVal;
5272         else if (attrNameL == "description")
5273             description = attrVal;
5274         else if (attrNameL == "cflags")
5275             cflags = attrVal;
5276         else if (attrNameL == "libs")
5277             libs = attrVal;
5278         else if (attrNameL == "requires")
5279             requires = attrVal;
5280         else if (attrNameL == "version")
5281             version = attrVal;
5283         //trace("name:'%s'  value:'%s'",
5284         //      attrName.c_str(), attrVal.c_str());
5285         }
5287     return true;
5291 bool PkgConfig::parse(const String &buf)
5293     init();
5295     String line;
5296     int lineNr = 0;
5297     for (unsigned int p=0 ; p<buf.size() ; p++)
5298         {
5299         int ch = buf[p];
5300         if (ch == '\n' || ch == '\r')
5301             {
5302             if (!parseLine(line))
5303                 return false;
5304             line.clear();
5305             lineNr++;
5306             }
5307         else
5308             {
5309             line.push_back(ch);
5310             }
5311         }
5312     if (line.size()>0)
5313         {
5314         if (!parseLine(line))
5315             return false;
5316         }
5318     parseRequires();
5319     parseVersion();
5321     return true;
5327 void PkgConfig::dumpAttrs()
5329     //trace("### PkgConfig attributes for %s", fileName.c_str());
5330     std::map<String, String>::iterator iter;
5331     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5332         {
5333         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5334         }
5338 bool PkgConfig::readFile(const String &fname)
5340     fileName = getNativePath(fname);
5342     FILE *f = fopen(fileName.c_str(), "r");
5343     if (!f)
5344         {
5345         error("cannot open file '%s' for reading", fileName.c_str());
5346         return false;
5347         }
5348     String buf;
5349     while (true)
5350         {
5351         int ch = fgetc(f);
5352         if (ch < 0)
5353             break;
5354         buf.push_back((char)ch);
5355         }
5356     fclose(f);
5358     //trace("####### File:\n%s", buf.c_str());
5359     if (!parse(buf))
5360         {
5361         return false;
5362         }
5364     //dumpAttrs();
5366     return true;
5371 bool PkgConfig::query(const String &pkgName)
5373     name = pkgName;
5375     String fname = path;
5376     fname.append("/");
5377     fname.append(name);
5378     fname.append(".pc");
5380     if (!readFile(fname))
5381         return false;
5382     
5383     return true;
5390 //########################################################################
5391 //# D E P T O O L
5392 //########################################################################
5396 /**
5397  *  Class which holds information for each file.
5398  */
5399 class FileRec
5401 public:
5403     typedef enum
5404         {
5405         UNKNOWN,
5406         CFILE,
5407         HFILE,
5408         OFILE
5409         } FileType;
5411     /**
5412      *  Constructor
5413      */
5414     FileRec()
5415         { init(); type = UNKNOWN; }
5417     /**
5418      *  Copy constructor
5419      */
5420     FileRec(const FileRec &other)
5421         { init(); assign(other); }
5422     /**
5423      *  Constructor
5424      */
5425     FileRec(int typeVal)
5426         { init(); type = typeVal; }
5427     /**
5428      *  Assignment operator
5429      */
5430     FileRec &operator=(const FileRec &other)
5431         { init(); assign(other); return *this; }
5434     /**
5435      *  Destructor
5436      */
5437     ~FileRec()
5438         {}
5440     /**
5441      *  Directory part of the file name
5442      */
5443     String path;
5445     /**
5446      *  Base name, sans directory and suffix
5447      */
5448     String baseName;
5450     /**
5451      *  File extension, such as cpp or h
5452      */
5453     String suffix;
5455     /**
5456      *  Type of file: CFILE, HFILE, OFILE
5457      */
5458     int type;
5460     /**
5461      * Used to list files ref'd by this one
5462      */
5463     std::map<String, FileRec *> files;
5466 private:
5468     void init()
5469         {
5470         }
5472     void assign(const FileRec &other)
5473         {
5474         type     = other.type;
5475         baseName = other.baseName;
5476         suffix   = other.suffix;
5477         files    = other.files;
5478         }
5480 };
5484 /**
5485  *  Simpler dependency record
5486  */
5487 class DepRec
5489 public:
5491     /**
5492      *  Constructor
5493      */
5494     DepRec()
5495         {init();}
5497     /**
5498      *  Copy constructor
5499      */
5500     DepRec(const DepRec &other)
5501         {init(); assign(other);}
5502     /**
5503      *  Constructor
5504      */
5505     DepRec(const String &fname)
5506         {init(); name = fname; }
5507     /**
5508      *  Assignment operator
5509      */
5510     DepRec &operator=(const DepRec &other)
5511         {init(); assign(other); return *this;}
5514     /**
5515      *  Destructor
5516      */
5517     ~DepRec()
5518         {}
5520     /**
5521      *  Directory part of the file name
5522      */
5523     String path;
5525     /**
5526      *  Base name, without the path and suffix
5527      */
5528     String name;
5530     /**
5531      *  Suffix of the source
5532      */
5533     String suffix;
5536     /**
5537      * Used to list files ref'd by this one
5538      */
5539     std::vector<String> files;
5542 private:
5544     void init()
5545         {
5546         }
5548     void assign(const DepRec &other)
5549         {
5550         path     = other.path;
5551         name     = other.name;
5552         suffix   = other.suffix;
5553         files    = other.files; //avoid recursion
5554         }
5556 };
5559 class DepTool : public MakeBase
5561 public:
5563     /**
5564      *  Constructor
5565      */
5566     DepTool()
5567         { init(); }
5569     /**
5570      *  Copy constructor
5571      */
5572     DepTool(const DepTool &other)
5573         { init(); assign(other); }
5575     /**
5576      *  Assignment operator
5577      */
5578     DepTool &operator=(const DepTool &other)
5579         { init(); assign(other); return *this; }
5582     /**
5583      *  Destructor
5584      */
5585     ~DepTool()
5586         {}
5589     /**
5590      *  Reset this section of code
5591      */
5592     virtual void init();
5593     
5594     /**
5595      *  Reset this section of code
5596      */
5597     virtual void assign(const DepTool &other)
5598         {
5599         }
5600     
5601     /**
5602      *  Sets the source directory which will be scanned
5603      */
5604     virtual void setSourceDirectory(const String &val)
5605         { sourceDir = val; }
5607     /**
5608      *  Returns the source directory which will be scanned
5609      */
5610     virtual String getSourceDirectory()
5611         { return sourceDir; }
5613     /**
5614      *  Sets the list of files within the directory to analyze
5615      */
5616     virtual void setFileList(const std::vector<String> &list)
5617         { fileList = list; }
5619     /**
5620      * Creates the list of all file names which will be
5621      * candidates for further processing.  Reads make.exclude
5622      * to see which files for directories to leave out.
5623      */
5624     virtual bool createFileList();
5627     /**
5628      *  Generates the forward dependency list
5629      */
5630     virtual bool generateDependencies();
5633     /**
5634      *  Generates the forward dependency list, saving the file
5635      */
5636     virtual bool generateDependencies(const String &);
5639     /**
5640      *  Load a dependency file
5641      */
5642     std::vector<DepRec> loadDepFile(const String &fileName);
5644     /**
5645      *  Load a dependency file, generating one if necessary
5646      */
5647     std::vector<DepRec> getDepFile(const String &fileName,
5648               bool forceRefresh);
5650     /**
5651      *  Save a dependency file
5652      */
5653     bool saveDepFile(const String &fileName);
5656 private:
5659     /**
5660      *
5661      */
5662     void parseName(const String &fullname,
5663                    String &path,
5664                    String &basename,
5665                    String &suffix);
5667     /**
5668      *
5669      */
5670     int get(int pos);
5672     /**
5673      *
5674      */
5675     int skipwhite(int pos);
5677     /**
5678      *
5679      */
5680     int getword(int pos, String &ret);
5682     /**
5683      *
5684      */
5685     bool sequ(int pos, const char *key);
5687     /**
5688      *
5689      */
5690     bool addIncludeFile(FileRec *frec, const String &fname);
5692     /**
5693      *
5694      */
5695     bool scanFile(const String &fname, FileRec *frec);
5697     /**
5698      *
5699      */
5700     bool processDependency(FileRec *ofile, FileRec *include);
5702     /**
5703      *
5704      */
5705     String sourceDir;
5707     /**
5708      *
5709      */
5710     std::vector<String> fileList;
5712     /**
5713      *
5714      */
5715     std::vector<String> directories;
5717     /**
5718      * A list of all files which will be processed for
5719      * dependencies.
5720      */
5721     std::map<String, FileRec *> allFiles;
5723     /**
5724      * The list of .o files, and the
5725      * dependencies upon them.
5726      */
5727     std::map<String, FileRec *> oFiles;
5729     int depFileSize;
5730     char *depFileBuf;
5732     static const int readBufSize = 8192;
5733     char readBuf[8193];//byte larger
5735 };
5741 /**
5742  *  Clean up after processing.  Called by the destructor, but should
5743  *  also be called before the object is reused.
5744  */
5745 void DepTool::init()
5747     sourceDir = ".";
5749     fileList.clear();
5750     directories.clear();
5751     
5752     //clear output file list
5753     std::map<String, FileRec *>::iterator iter;
5754     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5755         delete iter->second;
5756     oFiles.clear();
5758     //allFiles actually contains the master copies. delete them
5759     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5760         delete iter->second;
5761     allFiles.clear(); 
5768 /**
5769  *  Parse a full path name into path, base name, and suffix
5770  */
5771 void DepTool::parseName(const String &fullname,
5772                         String &path,
5773                         String &basename,
5774                         String &suffix)
5776     if (fullname.size() < 2)
5777         return;
5779     unsigned int pos = fullname.find_last_of('/');
5780     if (pos != fullname.npos && pos<fullname.size()-1)
5781         {
5782         path = fullname.substr(0, pos);
5783         pos++;
5784         basename = fullname.substr(pos, fullname.size()-pos);
5785         }
5786     else
5787         {
5788         path = "";
5789         basename = fullname;
5790         }
5792     pos = basename.find_last_of('.');
5793     if (pos != basename.npos && pos<basename.size()-1)
5794         {
5795         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5796         basename = basename.substr(0, pos);
5797         }
5799     //trace("parsename:%s %s %s", path.c_str(),
5800     //        basename.c_str(), suffix.c_str()); 
5805 /**
5806  *  Generate our internal file list.
5807  */
5808 bool DepTool::createFileList()
5811     for (unsigned int i=0 ; i<fileList.size() ; i++)
5812         {
5813         String fileName = fileList[i];
5814         //trace("## FileName:%s", fileName.c_str());
5815         String path;
5816         String basename;
5817         String sfx;
5818         parseName(fileName, path, basename, sfx);
5819         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5820             sfx == "cc" || sfx == "CC")
5821             {
5822             FileRec *fe         = new FileRec(FileRec::CFILE);
5823             fe->path            = path;
5824             fe->baseName        = basename;
5825             fe->suffix          = sfx;
5826             allFiles[fileName]  = fe;
5827             }
5828         else if (sfx == "h"   ||  sfx == "hh"  ||
5829                  sfx == "hpp" ||  sfx == "hxx")
5830             {
5831             FileRec *fe         = new FileRec(FileRec::HFILE);
5832             fe->path            = path;
5833             fe->baseName        = basename;
5834             fe->suffix          = sfx;
5835             allFiles[fileName]  = fe;
5836             }
5837         }
5839     if (!listDirectories(sourceDir, "", directories))
5840         return false;
5841         
5842     return true;
5849 /**
5850  * Get a character from the buffer at pos.  If out of range,
5851  * return -1 for safety
5852  */
5853 int DepTool::get(int pos)
5855     if (pos>depFileSize)
5856         return -1;
5857     return depFileBuf[pos];
5862 /**
5863  *  Skip over all whitespace characters beginning at pos.  Return
5864  *  the position of the first non-whitespace character.
5865  */
5866 int DepTool::skipwhite(int pos)
5868     while (pos < depFileSize)
5869         {
5870         int ch = get(pos);
5871         if (ch < 0)
5872             break;
5873         if (!isspace(ch))
5874             break;
5875         pos++;
5876         }
5877     return pos;
5881 /**
5882  *  Parse the buffer beginning at pos, for a word.  Fill
5883  *  'ret' with the result.  Return the position after the
5884  *  word.
5885  */
5886 int DepTool::getword(int pos, String &ret)
5888     while (pos < depFileSize)
5889         {
5890         int ch = get(pos);
5891         if (ch < 0)
5892             break;
5893         if (isspace(ch))
5894             break;
5895         ret.push_back((char)ch);
5896         pos++;
5897         }
5898     return pos;
5901 /**
5902  * Return whether the sequence of characters in the buffer
5903  * beginning at pos match the key,  for the length of the key
5904  */
5905 bool DepTool::sequ(int pos, const char *key)
5907     while (*key)
5908         {
5909         if (*key != get(pos))
5910             return false;
5911         key++; pos++;
5912         }
5913     return true;
5918 /**
5919  *  Add an include file name to a file record.  If the name
5920  *  is not found in allFiles explicitly, try prepending include
5921  *  directory names to it and try again.
5922  */
5923 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5925     //# if the name is an exact match to a path name
5926     //# in allFiles, like "myinc.h"
5927     std::map<String, FileRec *>::iterator iter =
5928            allFiles.find(iname);
5929     if (iter != allFiles.end()) //already exists
5930         {
5931          //h file in same dir
5932         FileRec *other = iter->second;
5933         //trace("local: '%s'", iname.c_str());
5934         frec->files[iname] = other;
5935         return true;
5936         }
5937     else 
5938         {
5939         //## Ok, it was not found directly
5940         //look in other dirs
5941         std::vector<String>::iterator diter;
5942         for (diter=directories.begin() ;
5943              diter!=directories.end() ; diter++)
5944             {
5945             String dfname = *diter;
5946             dfname.append("/");
5947             dfname.append(iname);
5948             URI fullPathURI(dfname);  //normalize path name
5949             String fullPath = fullPathURI.getPath();
5950             if (fullPath[0] == '/')
5951                 fullPath = fullPath.substr(1);
5952             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5953             iter = allFiles.find(fullPath);
5954             if (iter != allFiles.end())
5955                 {
5956                 FileRec *other = iter->second;
5957                 //trace("other: '%s'", iname.c_str());
5958                 frec->files[fullPath] = other;
5959                 return true;
5960                 }
5961             }
5962         }
5963     return true;
5968 /**
5969  *  Lightly parse a file to find the #include directives.  Do
5970  *  a bit of state machine stuff to make sure that the directive
5971  *  is valid.  (Like not in a comment).
5972  */
5973 bool DepTool::scanFile(const String &fname, FileRec *frec)
5975     String fileName;
5976     if (sourceDir.size() > 0)
5977         {
5978         fileName.append(sourceDir);
5979         fileName.append("/");
5980         }
5981     fileName.append(fname);
5982     String nativeName = getNativePath(fileName);
5983     FILE *f = fopen(nativeName.c_str(), "r");
5984     if (!f)
5985         {
5986         error("Could not open '%s' for reading", fname.c_str());
5987         return false;
5988         }
5989     String buf;
5990     while (!feof(f))
5991         {
5992         int nrbytes = fread(readBuf, 1, readBufSize, f);
5993         readBuf[nrbytes] = '\0';
5994         buf.append(readBuf);
5995         }
5996     fclose(f);
5998     depFileSize = buf.size();
5999     depFileBuf  = (char *)buf.c_str();
6000     int pos = 0;
6003     while (pos < depFileSize)
6004         {
6005         //trace("p:%c", get(pos));
6007         //# Block comment
6008         if (get(pos) == '/' && get(pos+1) == '*')
6009             {
6010             pos += 2;
6011             while (pos < depFileSize)
6012                 {
6013                 if (get(pos) == '*' && get(pos+1) == '/')
6014                     {
6015                     pos += 2;
6016                     break;
6017                     }
6018                 else
6019                     pos++;
6020                 }
6021             }
6022         //# Line comment
6023         else if (get(pos) == '/' && get(pos+1) == '/')
6024             {
6025             pos += 2;
6026             while (pos < depFileSize)
6027                 {
6028                 if (get(pos) == '\n')
6029                     {
6030                     pos++;
6031                     break;
6032                     }
6033                 else
6034                     pos++;
6035                 }
6036             }
6037         //# #include! yaay
6038         else if (sequ(pos, "#include"))
6039             {
6040             pos += 8;
6041             pos = skipwhite(pos);
6042             String iname;
6043             pos = getword(pos, iname);
6044             if (iname.size()>2)
6045                 {
6046                 iname = iname.substr(1, iname.size()-2);
6047                 addIncludeFile(frec, iname);
6048                 }
6049             }
6050         else
6051             {
6052             pos++;
6053             }
6054         }
6056     return true;
6061 /**
6062  *  Recursively check include lists to find all files in allFiles to which
6063  *  a given file is dependent.
6064  */
6065 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6067     std::map<String, FileRec *>::iterator iter;
6068     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6069         {
6070         String fname  = iter->first;
6071         if (ofile->files.find(fname) != ofile->files.end())
6072             {
6073             //trace("file '%s' already seen", fname.c_str());
6074             continue;
6075             }
6076         FileRec *child  = iter->second;
6077         ofile->files[fname] = child;
6078       
6079         processDependency(ofile, child);
6080         }
6083     return true;
6090 /**
6091  *  Generate the file dependency list.
6092  */
6093 bool DepTool::generateDependencies()
6095     std::map<String, FileRec *>::iterator iter;
6096     //# First pass.  Scan for all includes
6097     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6098         {
6099         FileRec *frec = iter->second;
6100         if (!scanFile(iter->first, frec))
6101             {
6102             //quit?
6103             }
6104         }
6106     //# Second pass.  Scan for all includes
6107     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6108         {
6109         FileRec *include = iter->second;
6110         if (include->type == FileRec::CFILE)
6111             {
6112             //String cFileName   = iter->first;
6113             FileRec *ofile     = new FileRec(FileRec::OFILE);
6114             ofile->path        = include->path;
6115             ofile->baseName    = include->baseName;
6116             ofile->suffix      = include->suffix;
6117             String fname       = include->path;
6118             if (fname.size()>0)
6119                 fname.append("/");
6120             fname.append(include->baseName);
6121             fname.append(".o");
6122             oFiles[fname]    = ofile;
6123             //add the .c file first?   no, don't
6124             //ofile->files[cFileName] = include;
6125             
6126             //trace("ofile:%s", fname.c_str());
6128             processDependency(ofile, include);
6129             }
6130         }
6132       
6133     return true;
6138 /**
6139  *  High-level call to generate deps and optionally save them
6140  */
6141 bool DepTool::generateDependencies(const String &fileName)
6143     if (!createFileList())
6144         return false;
6145     if (!generateDependencies())
6146         return false;
6147     if (!saveDepFile(fileName))
6148         return false;
6149     return true;
6153 /**
6154  *   This saves the dependency cache.
6155  */
6156 bool DepTool::saveDepFile(const String &fileName)
6158     time_t tim;
6159     time(&tim);
6161     FILE *f = fopen(fileName.c_str(), "w");
6162     if (!f)
6163         {
6164         trace("cannot open '%s' for writing", fileName.c_str());
6165         }
6166     fprintf(f, "<?xml version='1.0'?>\n");
6167     fprintf(f, "<!--\n");
6168     fprintf(f, "########################################################\n");
6169     fprintf(f, "## File: build.dep\n");
6170     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6171     fprintf(f, "########################################################\n");
6172     fprintf(f, "-->\n");
6174     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6175     std::map<String, FileRec *>::iterator iter;
6176     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6177         {
6178         FileRec *frec = iter->second;
6179         if (frec->type == FileRec::OFILE)
6180             {
6181             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6182                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6183             std::map<String, FileRec *>::iterator citer;
6184             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6185                 {
6186                 String cfname = citer->first;
6187                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6188                 }
6189             fprintf(f, "</object>\n\n");
6190             }
6191         }
6193     fprintf(f, "</dependencies>\n");
6194     fprintf(f, "\n");
6195     fprintf(f, "<!--\n");
6196     fprintf(f, "########################################################\n");
6197     fprintf(f, "## E N D\n");
6198     fprintf(f, "########################################################\n");
6199     fprintf(f, "-->\n");
6201     fclose(f);
6203     return true;
6209 /**
6210  *   This loads the dependency cache.
6211  */
6212 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6214     std::vector<DepRec> result;
6215     
6216     Parser parser;
6217     Element *root = parser.parseFile(depFile.c_str());
6218     if (!root)
6219         {
6220         //error("Could not open %s for reading", depFile.c_str());
6221         return result;
6222         }
6224     if (root->getChildren().size()==0 ||
6225         root->getChildren()[0]->getName()!="dependencies")
6226         {
6227         error("loadDepFile: main xml element should be <dependencies>");
6228         delete root;
6229         return result;
6230         }
6232     //########## Start parsing
6233     Element *depList = root->getChildren()[0];
6235     std::vector<Element *> objects = depList->getChildren();
6236     for (unsigned int i=0 ; i<objects.size() ; i++)
6237         {
6238         Element *objectElem = objects[i];
6239         String tagName = objectElem->getName();
6240         if (tagName != "object")
6241             {
6242             error("loadDepFile: <dependencies> should have only <object> children");
6243             return result;
6244             }
6246         String objName   = objectElem->getAttribute("name");
6247          //trace("object:%s", objName.c_str());
6248         DepRec depObject(objName);
6249         depObject.path   = objectElem->getAttribute("path");
6250         depObject.suffix = objectElem->getAttribute("suffix");
6251         //########## DESCRIPTION
6252         std::vector<Element *> depElems = objectElem->getChildren();
6253         for (unsigned int i=0 ; i<depElems.size() ; i++)
6254             {
6255             Element *depElem = depElems[i];
6256             tagName = depElem->getName();
6257             if (tagName != "dep")
6258                 {
6259                 error("loadDepFile: <object> should have only <dep> children");
6260                 return result;
6261                 }
6262             String depName = depElem->getAttribute("name");
6263             //trace("    dep:%s", depName.c_str());
6264             depObject.files.push_back(depName);
6265             }
6267         //Insert into the result list, in a sorted manner
6268         bool inserted = false;
6269         std::vector<DepRec>::iterator iter;
6270         for (iter = result.begin() ; iter != result.end() ; iter++)
6271             {
6272             String vpath = iter->path;
6273             vpath.append("/");
6274             vpath.append(iter->name);
6275             String opath = depObject.path;
6276             opath.append("/");
6277             opath.append(depObject.name);
6278             if (vpath > opath)
6279                 {
6280                 inserted = true;
6281                 iter = result.insert(iter, depObject);
6282                 break;
6283                 }
6284             }
6285         if (!inserted)
6286             result.push_back(depObject);
6287         }
6289     delete root;
6291     return result;
6295 /**
6296  *   This loads the dependency cache.
6297  */
6298 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6299                    bool forceRefresh)
6301     std::vector<DepRec> result;
6302     if (forceRefresh)
6303         {
6304         generateDependencies(depFile);
6305         result = loadDepFile(depFile);
6306         }
6307     else
6308         {
6309         //try once
6310         result = loadDepFile(depFile);
6311         if (result.size() == 0)
6312             {
6313             //fail? try again
6314             generateDependencies(depFile);
6315             result = loadDepFile(depFile);
6316             }
6317         }
6318     return result;
6324 //########################################################################
6325 //# T A S K
6326 //########################################################################
6327 //forward decl
6328 class Target;
6329 class Make;
6331 /**
6332  *
6333  */
6334 class Task : public MakeBase
6337 public:
6339     typedef enum
6340         {
6341         TASK_NONE,
6342         TASK_CC,
6343         TASK_COPY,
6344         TASK_DELETE,
6345         TASK_ECHO,
6346         TASK_JAR,
6347         TASK_JAVAC,
6348         TASK_LINK,
6349         TASK_MAKEFILE,
6350         TASK_MKDIR,
6351         TASK_MSGFMT,
6352         TASK_PKG_CONFIG,
6353         TASK_RANLIB,
6354         TASK_RC,
6355         TASK_SHAREDLIB,
6356         TASK_STATICLIB,
6357         TASK_STRIP,
6358         TASK_TOUCH,
6359         TASK_TSTAMP
6360         } TaskType;
6361         
6363     /**
6364      *
6365      */
6366     Task(MakeBase &par) : parent(par)
6367         { init(); }
6369     /**
6370      *
6371      */
6372     Task(const Task &other) : parent(other.parent)
6373         { init(); assign(other); }
6375     /**
6376      *
6377      */
6378     Task &operator=(const Task &other)
6379         { assign(other); return *this; }
6381     /**
6382      *
6383      */
6384     virtual ~Task()
6385         { }
6388     /**
6389      *
6390      */
6391     virtual MakeBase &getParent()
6392         { return parent; }
6394      /**
6395      *
6396      */
6397     virtual int  getType()
6398         { return type; }
6400     /**
6401      *
6402      */
6403     virtual void setType(int val)
6404         { type = val; }
6406     /**
6407      *
6408      */
6409     virtual String getName()
6410         { return name; }
6412     /**
6413      *
6414      */
6415     virtual bool execute()
6416         { return true; }
6418     /**
6419      *
6420      */
6421     virtual bool parse(Element *elem)
6422         { return true; }
6424     /**
6425      *
6426      */
6427     Task *createTask(Element *elem, int lineNr);
6430 protected:
6432     void init()
6433         {
6434         type = TASK_NONE;
6435         name = "none";
6436         }
6438     void assign(const Task &other)
6439         {
6440         type = other.type;
6441         name = other.name;
6442         }
6443         
6444     /**
6445      *  Show task status
6446      */
6447     void taskstatus(const char *fmt, ...)
6448         {
6449         va_list args;
6450         va_start(args,fmt);
6451         fprintf(stdout, "    %s : ", name.c_str());
6452         vfprintf(stdout, fmt, args);
6453         fprintf(stdout, "\n");
6454         va_end(args) ;
6455         }
6457     String getAttribute(Element *elem, const String &attrName)
6458         {
6459         String str;
6460         return str;
6461         }
6463     MakeBase &parent;
6465     int type;
6467     String name;
6468 };
6472 /**
6473  * This task runs the C/C++ compiler.  The compiler is invoked
6474  * for all .c or .cpp files which are newer than their correcsponding
6475  * .o files.  
6476  */
6477 class TaskCC : public Task
6479 public:
6481     TaskCC(MakeBase &par) : Task(par)
6482         {
6483         type = TASK_CC;
6484         name = "cc";
6485         }
6487     virtual ~TaskCC()
6488         {}
6489         
6490     virtual bool isExcludedInc(const String &dirname)
6491         {
6492         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6493             {
6494             String fname = excludeInc[i];
6495             if (fname == dirname)
6496                 return true;
6497             }
6498         return false;
6499         }
6501     virtual bool execute()
6502         {
6503         //evaluate our parameters
6504         String command         = parent.eval(commandOpt, "gcc");
6505         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6506         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6507         String source          = parent.eval(sourceOpt, ".");
6508         String dest            = parent.eval(destOpt, ".");
6509         String flags           = parent.eval(flagsOpt, "");
6510         String defines         = parent.eval(definesOpt, "");
6511         String includes        = parent.eval(includesOpt, "");
6512         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6513         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6515         if (!listFiles(parent, fileSet))
6516             return false;
6517             
6518         FILE *f = NULL;
6519         f = fopen("compile.lst", "w");
6521         //refreshCache is probably false here, unless specified otherwise
6522         String fullName = parent.resolve("build.dep");
6523         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6524             {
6525             taskstatus("regenerating C/C++ dependency cache");
6526             refreshCache = true;
6527             }
6529         DepTool depTool;
6530         depTool.setSourceDirectory(source);
6531         depTool.setFileList(fileSet.getFiles());
6532         std::vector<DepRec> deps =
6533              depTool.getDepFile("build.dep", refreshCache);
6534         
6535         String incs;
6536         incs.append("-I");
6537         incs.append(parent.resolve("."));
6538         incs.append(" ");
6539         if (includes.size()>0)
6540             {
6541             incs.append(includes);
6542             incs.append(" ");
6543             }
6544         std::set<String> paths;
6545         std::vector<DepRec>::iterator viter;
6546         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6547             {
6548             DepRec dep = *viter;
6549             if (dep.path.size()>0)
6550                 paths.insert(dep.path);
6551             }
6552         if (source.size()>0)
6553             {
6554             incs.append(" -I");
6555             incs.append(parent.resolve(source));
6556             incs.append(" ");
6557             }
6558         std::set<String>::iterator setIter;
6559         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6560             {
6561             String dirName = *setIter;
6562             //check excludeInc to see if we dont want to include this dir
6563             if (isExcludedInc(dirName))
6564                 continue;
6565             incs.append(" -I");
6566             String dname;
6567             if (source.size()>0)
6568                 {
6569                 dname.append(source);
6570                 dname.append("/");
6571                 }
6572             dname.append(dirName);
6573             incs.append(parent.resolve(dname));
6574             }
6575             
6576         /**
6577          * Compile each of the C files that need it
6578          */
6579         bool errorOccurred = false;                 
6580         std::vector<String> cfiles;
6581         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6582             {
6583             DepRec dep = *viter;
6585             //## Select command
6586             String sfx = dep.suffix;
6587             String command = ccCommand;
6588             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6589                  sfx == "cc" || sfx == "CC")
6590                 command = cxxCommand;
6591  
6592             //## Make paths
6593             String destPath = dest;
6594             String srcPath  = source;
6595             if (dep.path.size()>0)
6596                 {
6597                 destPath.append("/");
6598                 destPath.append(dep.path);
6599                 srcPath.append("/");
6600                 srcPath.append(dep.path);
6601                 }
6602             //## Make sure destination directory exists
6603             if (!createDirectory(destPath))
6604                 return false;
6605                 
6606             //## Check whether it needs to be done
6607             String destName;
6608             if (destPath.size()>0)
6609                 {
6610                 destName.append(destPath);
6611                 destName.append("/");
6612                 }
6613             destName.append(dep.name);
6614             destName.append(".o");
6615             String destFullName = parent.resolve(destName);
6616             String srcName;
6617             if (srcPath.size()>0)
6618                 {
6619                 srcName.append(srcPath);
6620                 srcName.append("/");
6621                 }
6622             srcName.append(dep.name);
6623             srcName.append(".");
6624             srcName.append(dep.suffix);
6625             String srcFullName = parent.resolve(srcName);
6626             bool compileMe = false;
6627             //# First we check if the source is newer than the .o
6628             if (isNewerThan(srcFullName, destFullName))
6629                 {
6630                 taskstatus("compile of %s required by source: %s",
6631                         destFullName.c_str(), srcFullName.c_str());
6632                 compileMe = true;
6633                 }
6634             else
6635                 {
6636                 //# secondly, we check if any of the included dependencies
6637                 //# of the .c/.cpp is newer than the .o
6638                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6639                     {
6640                     String depName;
6641                     if (source.size()>0)
6642                         {
6643                         depName.append(source);
6644                         depName.append("/");
6645                         }
6646                     depName.append(dep.files[i]);
6647                     String depFullName = parent.resolve(depName);
6648                     bool depRequires = isNewerThan(depFullName, destFullName);
6649                     //trace("%d %s %s\n", depRequires,
6650                     //        destFullName.c_str(), depFullName.c_str());
6651                     if (depRequires)
6652                         {
6653                         taskstatus("compile of %s required by included: %s",
6654                                 destFullName.c_str(), depFullName.c_str());
6655                         compileMe = true;
6656                         break;
6657                         }
6658                     }
6659                 }
6660             if (!compileMe)
6661                 {
6662                 continue;
6663                 }
6665             //## Assemble the command
6666             String cmd = command;
6667             cmd.append(" -c ");
6668             cmd.append(flags);
6669             cmd.append(" ");
6670             cmd.append(defines);
6671             cmd.append(" ");
6672             cmd.append(incs);
6673             cmd.append(" ");
6674             cmd.append(srcFullName);
6675             cmd.append(" -o ");
6676             cmd.append(destFullName);
6678             //## Execute the command
6680             String outString, errString;
6681             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6683             if (f)
6684                 {
6685                 fprintf(f, "########################### File : %s\n",
6686                              srcFullName.c_str());
6687                 fprintf(f, "#### COMMAND ###\n");
6688                 int col = 0;
6689                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6690                     {
6691                     char ch = cmd[i];
6692                     if (isspace(ch)  && col > 63)
6693                         {
6694                         fputc('\n', f);
6695                         col = 0;
6696                         }
6697                     else
6698                         {
6699                         fputc(ch, f);
6700                         col++;
6701                         }
6702                     if (col > 76)
6703                         {
6704                         fputc('\n', f);
6705                         col = 0;
6706                         }
6707                     }
6708                 fprintf(f, "\n");
6709                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6710                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6711                 fflush(f);
6712                 }
6713             if (!ret)
6714                 {
6715                 error("problem compiling: %s", errString.c_str());
6716                 errorOccurred = true;
6717                 }
6718             if (errorOccurred && !continueOnError)
6719                 break;
6720             }
6722         if (f)
6723             {
6724             fclose(f);
6725             }
6726         
6727         return !errorOccurred;
6728         }
6731     virtual bool parse(Element *elem)
6732         {
6733         String s;
6734         if (!parent.getAttribute(elem, "command", commandOpt))
6735             return false;
6736         if (commandOpt.size()>0)
6737             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6738         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6739             return false;
6740         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6741             return false;
6742         if (!parent.getAttribute(elem, "destdir", destOpt))
6743             return false;
6744         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6745             return false;
6746         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6747             return false;
6749         std::vector<Element *> children = elem->getChildren();
6750         for (unsigned int i=0 ; i<children.size() ; i++)
6751             {
6752             Element *child = children[i];
6753             String tagName = child->getName();
6754             if (tagName == "flags")
6755                 {
6756                 if (!parent.getValue(child, flagsOpt))
6757                     return false;
6758                 flagsOpt = strip(flagsOpt);
6759                 }
6760             else if (tagName == "includes")
6761                 {
6762                 if (!parent.getValue(child, includesOpt))
6763                     return false;
6764                 includesOpt = strip(includesOpt);
6765                 }
6766             else if (tagName == "defines")
6767                 {
6768                 if (!parent.getValue(child, definesOpt))
6769                     return false;
6770                 definesOpt = strip(definesOpt);
6771                 }
6772             else if (tagName == "fileset")
6773                 {
6774                 if (!parseFileSet(child, parent, fileSet))
6775                     return false;
6776                 sourceOpt = fileSet.getDirectory();
6777                 }
6778             else if (tagName == "excludeinc")
6779                 {
6780                 if (!parseFileList(child, parent, excludeInc))
6781                     return false;
6782                 }
6783             }
6785         return true;
6786         }
6787         
6788 protected:
6790     String   commandOpt;
6791     String   ccCommandOpt;
6792     String   cxxCommandOpt;
6793     String   sourceOpt;
6794     String   destOpt;
6795     String   flagsOpt;
6796     String   definesOpt;
6797     String   includesOpt;
6798     String   continueOnErrorOpt;
6799     String   refreshCacheOpt;
6800     FileSet  fileSet;
6801     FileList excludeInc;
6802     
6803 };
6807 /**
6808  *
6809  */
6810 class TaskCopy : public Task
6812 public:
6814     typedef enum
6815         {
6816         CP_NONE,
6817         CP_TOFILE,
6818         CP_TODIR
6819         } CopyType;
6821     TaskCopy(MakeBase &par) : Task(par)
6822         {
6823         type        = TASK_COPY;
6824         name        = "copy";
6825         cptype      = CP_NONE;
6826         haveFileSet = false;
6827         }
6829     virtual ~TaskCopy()
6830         {}
6832     virtual bool execute()
6833         {
6834         String fileName   = parent.eval(fileNameOpt   , ".");
6835         String toFileName = parent.eval(toFileNameOpt , ".");
6836         String toDirName  = parent.eval(toDirNameOpt  , ".");
6837         bool   verbose    = parent.evalBool(verboseOpt, false);
6838         switch (cptype)
6839            {
6840            case CP_TOFILE:
6841                {
6842                if (fileName.size()>0)
6843                    {
6844                    taskstatus("%s to %s",
6845                         fileName.c_str(), toFileName.c_str());
6846                    String fullSource = parent.resolve(fileName);
6847                    String fullDest = parent.resolve(toFileName);
6848                    if (verbose)
6849                        taskstatus("copy %s to file %s", fullSource.c_str(),
6850                                           fullDest.c_str());
6851                    if (!isRegularFile(fullSource))
6852                        {
6853                        error("copy : file %s does not exist", fullSource.c_str());
6854                        return false;
6855                        }
6856                    if (!isNewerThan(fullSource, fullDest))
6857                        {
6858                        taskstatus("skipped");
6859                        return true;
6860                        }
6861                    if (!copyFile(fullSource, fullDest))
6862                        return false;
6863                    taskstatus("1 file copied");
6864                    }
6865                return true;
6866                }
6867            case CP_TODIR:
6868                {
6869                if (haveFileSet)
6870                    {
6871                    if (!listFiles(parent, fileSet))
6872                        return false;
6873                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6875                    taskstatus("%s to %s",
6876                        fileSetDir.c_str(), toDirName.c_str());
6878                    int nrFiles = 0;
6879                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6880                        {
6881                        String fileName = fileSet[i];
6883                        String sourcePath;
6884                        if (fileSetDir.size()>0)
6885                            {
6886                            sourcePath.append(fileSetDir);
6887                            sourcePath.append("/");
6888                            }
6889                        sourcePath.append(fileName);
6890                        String fullSource = parent.resolve(sourcePath);
6891                        
6892                        //Get the immediate parent directory's base name
6893                        String baseFileSetDir = fileSetDir;
6894                        unsigned int pos = baseFileSetDir.find_last_of('/');
6895                        if (pos!=baseFileSetDir.npos &&
6896                                   pos < baseFileSetDir.size()-1)
6897                            baseFileSetDir =
6898                               baseFileSetDir.substr(pos+1,
6899                                    baseFileSetDir.size());
6900                        //Now make the new path
6901                        String destPath;
6902                        if (toDirName.size()>0)
6903                            {
6904                            destPath.append(toDirName);
6905                            destPath.append("/");
6906                            }
6907                        if (baseFileSetDir.size()>0)
6908                            {
6909                            destPath.append(baseFileSetDir);
6910                            destPath.append("/");
6911                            }
6912                        destPath.append(fileName);
6913                        String fullDest = parent.resolve(destPath);
6914                        //trace("fileName:%s", fileName.c_str());
6915                        if (verbose)
6916                            taskstatus("copy %s to new dir : %s",
6917                                  fullSource.c_str(), fullDest.c_str());
6918                        if (!isNewerThan(fullSource, fullDest))
6919                            {
6920                            if (verbose)
6921                                taskstatus("copy skipping %s", fullSource.c_str());
6922                            continue;
6923                            }
6924                        if (!copyFile(fullSource, fullDest))
6925                            return false;
6926                        nrFiles++;
6927                        }
6928                    taskstatus("%d file(s) copied", nrFiles);
6929                    }
6930                else //file source
6931                    {
6932                    //For file->dir we want only the basename of
6933                    //the source appended to the dest dir
6934                    taskstatus("%s to %s", 
6935                        fileName.c_str(), toDirName.c_str());
6936                    String baseName = fileName;
6937                    unsigned int pos = baseName.find_last_of('/');
6938                    if (pos!=baseName.npos && pos<baseName.size()-1)
6939                        baseName = baseName.substr(pos+1, baseName.size());
6940                    String fullSource = parent.resolve(fileName);
6941                    String destPath;
6942                    if (toDirName.size()>0)
6943                        {
6944                        destPath.append(toDirName);
6945                        destPath.append("/");
6946                        }
6947                    destPath.append(baseName);
6948                    String fullDest = parent.resolve(destPath);
6949                    if (verbose)
6950                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6951                                           fullDest.c_str());
6952                    if (!isRegularFile(fullSource))
6953                        {
6954                        error("copy : file %s does not exist", fullSource.c_str());
6955                        return false;
6956                        }
6957                    if (!isNewerThan(fullSource, fullDest))
6958                        {
6959                        taskstatus("skipped");
6960                        return true;
6961                        }
6962                    if (!copyFile(fullSource, fullDest))
6963                        return false;
6964                    taskstatus("1 file copied");
6965                    }
6966                return true;
6967                }
6968            }
6969         return true;
6970         }
6973     virtual bool parse(Element *elem)
6974         {
6975         if (!parent.getAttribute(elem, "file", fileNameOpt))
6976             return false;
6977         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6978             return false;
6979         if (toFileNameOpt.size() > 0)
6980             cptype = CP_TOFILE;
6981         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6982             return false;
6983         if (toDirNameOpt.size() > 0)
6984             cptype = CP_TODIR;
6985         if (!parent.getAttribute(elem, "verbose", verboseOpt))
6986             return false;
6987             
6988         haveFileSet = false;
6989         
6990         std::vector<Element *> children = elem->getChildren();
6991         for (unsigned int i=0 ; i<children.size() ; i++)
6992             {
6993             Element *child = children[i];
6994             String tagName = child->getName();
6995             if (tagName == "fileset")
6996                 {
6997                 if (!parseFileSet(child, parent, fileSet))
6998                     {
6999                     error("problem getting fileset");
7000                     return false;
7001                     }
7002                 haveFileSet = true;
7003                 }
7004             }
7006         //Perform validity checks
7007         if (fileNameOpt.size()>0 && fileSet.size()>0)
7008             {
7009             error("<copy> can only have one of : file= and <fileset>");
7010             return false;
7011             }
7012         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7013             {
7014             error("<copy> can only have one of : tofile= or todir=");
7015             return false;
7016             }
7017         if (haveFileSet && toDirNameOpt.size()==0)
7018             {
7019             error("a <copy> task with a <fileset> must have : todir=");
7020             return false;
7021             }
7022         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7023             {
7024             error("<copy> tofile= must be associated with : file=");
7025             return false;
7026             }
7027         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7028             {
7029             error("<copy> todir= must be associated with : file= or <fileset>");
7030             return false;
7031             }
7033         return true;
7034         }
7035         
7036 private:
7038     int cptype;
7039     bool haveFileSet;
7041     FileSet fileSet;
7042     String  fileNameOpt;
7043     String  toFileNameOpt;
7044     String  toDirNameOpt;
7045     String  verboseOpt;
7046 };
7049 /**
7050  *
7051  */
7052 class TaskDelete : public Task
7054 public:
7056     typedef enum
7057         {
7058         DEL_FILE,
7059         DEL_DIR,
7060         DEL_FILESET
7061         } DeleteType;
7063     TaskDelete(MakeBase &par) : Task(par)
7064         { 
7065         type        = TASK_DELETE;
7066         name        = "delete";
7067         delType     = DEL_FILE;
7068         }
7070     virtual ~TaskDelete()
7071         {}
7073     virtual bool execute()
7074         {
7075         String dirName   = parent.eval(dirNameOpt, ".");
7076         String fileName  = parent.eval(fileNameOpt, ".");
7077         bool verbose     = parent.evalBool(verboseOpt, false);
7078         bool quiet       = parent.evalBool(quietOpt, false);
7079         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7080         struct stat finfo;
7081         switch (delType)
7082             {
7083             case DEL_FILE:
7084                 {
7085                 taskstatus("file: %s", fileName.c_str());
7086                 String fullName = parent.resolve(fileName);
7087                 char *fname = (char *)fullName.c_str();
7088                 if (!quiet && verbose)
7089                     taskstatus("path: %s", fname);
7090                 //does not exist
7091                 if (stat(fname, &finfo)<0)
7092                     {
7093                     if (failOnError)
7094                         return false;
7095                     else
7096                         return true;
7097                     }
7098                 //exists but is not a regular file
7099                 if (!S_ISREG(finfo.st_mode))
7100                     {
7101                     error("<delete> failed. '%s' exists and is not a regular file",
7102                           fname);
7103                     return false;
7104                     }
7105                 if (remove(fname)<0)
7106                     {
7107                     error("<delete> failed: %s", strerror(errno));
7108                     return false;
7109                     }
7110                 return true;
7111                 }
7112             case DEL_DIR:
7113                 {
7114                 taskstatus("dir: %s", dirName.c_str());
7115                 String fullDir = parent.resolve(dirName);
7116                 if (!quiet && verbose)
7117                     taskstatus("path: %s", fullDir.c_str());
7118                 if (!removeDirectory(fullDir))
7119                     return false;
7120                 return true;
7121                 }
7122             }
7123         return true;
7124         }
7126     virtual bool parse(Element *elem)
7127         {
7128         if (!parent.getAttribute(elem, "file", fileNameOpt))
7129             return false;
7130         if (fileNameOpt.size() > 0)
7131             delType = DEL_FILE;
7132         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7133             return false;
7134         if (dirNameOpt.size() > 0)
7135             delType = DEL_DIR;
7136         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7137             {
7138             error("<delete> can have one attribute of file= or dir=");
7139             return false;
7140             }
7141         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7142             {
7143             error("<delete> must have one attribute of file= or dir=");
7144             return false;
7145             }
7146         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7147             return false;
7148         if (!parent.getAttribute(elem, "quiet", quietOpt))
7149             return false;
7150         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7151             return false;
7152         return true;
7153         }
7155 private:
7157     int delType;
7158     String dirNameOpt;
7159     String fileNameOpt;
7160     String verboseOpt;
7161     String quietOpt;
7162     String failOnErrorOpt;
7163 };
7166 /**
7167  * Send a message to stdout
7168  */
7169 class TaskEcho : public Task
7171 public:
7173     TaskEcho(MakeBase &par) : Task(par)
7174         { type = TASK_ECHO; name = "echo"; }
7176     virtual ~TaskEcho()
7177         {}
7179     virtual bool execute()
7180         {
7181         //let message have priority over text
7182         String message = parent.eval(messageOpt, "");
7183         String text    = parent.eval(textOpt, "");
7184         if (message.size() > 0)
7185             {
7186             fprintf(stdout, "%s\n", message.c_str());
7187             }
7188         else if (text.size() > 0)
7189             {
7190             fprintf(stdout, "%s\n", text.c_str());
7191             }
7192         return true;
7193         }
7195     virtual bool parse(Element *elem)
7196         {
7197         if (!parent.getValue(elem, textOpt))
7198             return false;
7199         textOpt    = leftJustify(textOpt);
7200         if (!parent.getAttribute(elem, "message", messageOpt))
7201             return false;
7202         return true;
7203         }
7205 private:
7207     String messageOpt;
7208     String textOpt;
7209 };
7213 /**
7214  *
7215  */
7216 class TaskJar : public Task
7218 public:
7220     TaskJar(MakeBase &par) : Task(par)
7221         { type = TASK_JAR; name = "jar"; }
7223     virtual ~TaskJar()
7224         {}
7226     virtual bool execute()
7227         {
7228         String command  = parent.eval(commandOpt, "jar");
7229         String basedir  = parent.eval(basedirOpt, ".");
7230         String destfile = parent.eval(destfileOpt, ".");
7232         String cmd = command;
7233         cmd.append(" -cf ");
7234         cmd.append(destfile);
7235         cmd.append(" -C ");
7236         cmd.append(basedir);
7237         cmd.append(" .");
7239         String execCmd = cmd;
7241         String outString, errString;
7242         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7243         if (!ret)
7244             {
7245             error("<jar> command '%s' failed :\n %s",
7246                                       execCmd.c_str(), errString.c_str());
7247             return false;
7248             }
7249         return true;
7250         }
7252     virtual bool parse(Element *elem)
7253         {
7254         if (!parent.getAttribute(elem, "command", commandOpt))
7255             return false;
7256         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7257             return false;
7258         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7259             return false;
7260         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7261             {
7262             error("<jar> required both basedir and destfile attributes to be set");
7263             return false;
7264             }
7265         return true;
7266         }
7268 private:
7270     String commandOpt;
7271     String basedirOpt;
7272     String destfileOpt;
7273 };
7276 /**
7277  *
7278  */
7279 class TaskJavac : public Task
7281 public:
7283     TaskJavac(MakeBase &par) : Task(par)
7284         { 
7285         type = TASK_JAVAC; name = "javac";
7286         }
7288     virtual ~TaskJavac()
7289         {}
7291     virtual bool execute()
7292         {
7293         String command  = parent.eval(commandOpt, "javac");
7294         String srcdir   = parent.eval(srcdirOpt, ".");
7295         String destdir  = parent.eval(destdirOpt, ".");
7296         String target   = parent.eval(targetOpt, "");
7298         std::vector<String> fileList;
7299         if (!listFiles(srcdir, "", fileList))
7300             {
7301             return false;
7302             }
7303         String cmd = command;
7304         cmd.append(" -d ");
7305         cmd.append(destdir);
7306         cmd.append(" -classpath ");
7307         cmd.append(destdir);
7308         cmd.append(" -sourcepath ");
7309         cmd.append(srcdir);
7310         cmd.append(" ");
7311         if (target.size()>0)
7312             {
7313             cmd.append(" -target ");
7314             cmd.append(target);
7315             cmd.append(" ");
7316             }
7317         String fname = "javalist.btool";
7318         FILE *f = fopen(fname.c_str(), "w");
7319         int count = 0;
7320         for (unsigned int i=0 ; i<fileList.size() ; i++)
7321             {
7322             String fname = fileList[i];
7323             String srcName = fname;
7324             if (fname.size()<6) //x.java
7325                 continue;
7326             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7327                 continue;
7328             String baseName = fname.substr(0, fname.size()-5);
7329             String destName = baseName;
7330             destName.append(".class");
7332             String fullSrc = srcdir;
7333             fullSrc.append("/");
7334             fullSrc.append(fname);
7335             String fullDest = destdir;
7336             fullDest.append("/");
7337             fullDest.append(destName);
7338             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7339             if (!isNewerThan(fullSrc, fullDest))
7340                 continue;
7342             count++;
7343             fprintf(f, "%s\n", fullSrc.c_str());
7344             }
7345         fclose(f);
7346         if (!count)
7347             {
7348             taskstatus("nothing to do");
7349             return true;
7350             }
7352         taskstatus("compiling %d files", count);
7354         String execCmd = cmd;
7355         execCmd.append("@");
7356         execCmd.append(fname);
7358         String outString, errString;
7359         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7360         if (!ret)
7361             {
7362             error("<javac> command '%s' failed :\n %s",
7363                                       execCmd.c_str(), errString.c_str());
7364             return false;
7365             }
7366         return true;
7367         }
7369     virtual bool parse(Element *elem)
7370         {
7371         if (!parent.getAttribute(elem, "command", commandOpt))
7372             return false;
7373         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7374             return false;
7375         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7376             return false;
7377         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7378             {
7379             error("<javac> required both srcdir and destdir attributes to be set");
7380             return false;
7381             }
7382         if (!parent.getAttribute(elem, "target", targetOpt))
7383             return false;
7384         return true;
7385         }
7387 private:
7389     String commandOpt;
7390     String srcdirOpt;
7391     String destdirOpt;
7392     String targetOpt;
7394 };
7397 /**
7398  *
7399  */
7400 class TaskLink : public Task
7402 public:
7404     TaskLink(MakeBase &par) : Task(par)
7405         {
7406         type = TASK_LINK; name = "link";
7407         }
7409     virtual ~TaskLink()
7410         {}
7412     virtual bool execute()
7413         {
7414         String  command        = parent.eval(commandOpt, "g++");
7415         String  fileName       = parent.eval(fileNameOpt, "");
7416         String  flags          = parent.eval(flagsOpt, "");
7417         String  libs           = parent.eval(libsOpt, "");
7418         bool    doStrip        = parent.evalBool(doStripOpt, false);
7419         String  symFileName    = parent.eval(symFileNameOpt, "");
7420         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7421         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7423         if (!listFiles(parent, fileSet))
7424             return false;
7425         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7426         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7427         bool doit = false;
7428         String fullTarget = parent.resolve(fileName);
7429         String cmd = command;
7430         cmd.append(" -o ");
7431         cmd.append(fullTarget);
7432         cmd.append(" ");
7433         cmd.append(flags);
7434         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7435             {
7436             cmd.append(" ");
7437             String obj;
7438             if (fileSetDir.size()>0)
7439                 {
7440                 obj.append(fileSetDir);
7441                 obj.append("/");
7442                 }
7443             obj.append(fileSet[i]);
7444             String fullObj = parent.resolve(obj);
7445             String nativeFullObj = getNativePath(fullObj);
7446             cmd.append(nativeFullObj);
7447             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7448             //          fullObj.c_str());
7449             if (isNewerThan(fullObj, fullTarget))
7450                 doit = true;
7451             }
7452         cmd.append(" ");
7453         cmd.append(libs);
7454         if (!doit)
7455             {
7456             //trace("link not needed");
7457             return true;
7458             }
7459         //trace("LINK cmd:%s", cmd.c_str());
7462         String outbuf, errbuf;
7463         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7464             {
7465             error("LINK problem: %s", errbuf.c_str());
7466             return false;
7467             }
7469         if (symFileName.size()>0)
7470             {
7471             String symFullName = parent.resolve(symFileName);
7472             cmd = objcopyCommand;
7473             cmd.append(" --only-keep-debug ");
7474             cmd.append(getNativePath(fullTarget));
7475             cmd.append(" ");
7476             cmd.append(getNativePath(symFullName));
7477             if (!executeCommand(cmd, "", outbuf, errbuf))
7478                 {
7479                 error("<strip> symbol file failed : %s", errbuf.c_str());
7480                 return false;
7481                 }
7482             }
7483             
7484         if (doStrip)
7485             {
7486             cmd = stripCommand;
7487             cmd.append(" ");
7488             cmd.append(getNativePath(fullTarget));
7489             if (!executeCommand(cmd, "", outbuf, errbuf))
7490                {
7491                error("<strip> failed : %s", errbuf.c_str());
7492                return false;
7493                }
7494             }
7496         return true;
7497         }
7499     virtual bool parse(Element *elem)
7500         {
7501         if (!parent.getAttribute(elem, "command", commandOpt))
7502             return false;
7503         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7504             return false;
7505         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7506             return false;
7507         if (!parent.getAttribute(elem, "out", fileNameOpt))
7508             return false;
7509         if (!parent.getAttribute(elem, "strip", doStripOpt))
7510             return false;
7511         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7512             return false;
7513             
7514         std::vector<Element *> children = elem->getChildren();
7515         for (unsigned int i=0 ; i<children.size() ; i++)
7516             {
7517             Element *child = children[i];
7518             String tagName = child->getName();
7519             if (tagName == "fileset")
7520                 {
7521                 if (!parseFileSet(child, parent, fileSet))
7522                     return false;
7523                 }
7524             else if (tagName == "flags")
7525                 {
7526                 if (!parent.getValue(child, flagsOpt))
7527                     return false;
7528                 flagsOpt = strip(flagsOpt);
7529                 }
7530             else if (tagName == "libs")
7531                 {
7532                 if (!parent.getValue(child, libsOpt))
7533                     return false;
7534                 libsOpt = strip(libsOpt);
7535                 }
7536             }
7537         return true;
7538         }
7540 private:
7542     FileSet fileSet;
7544     String  commandOpt;
7545     String  fileNameOpt;
7546     String  flagsOpt;
7547     String  libsOpt;
7548     String  doStripOpt;
7549     String  symFileNameOpt;
7550     String  stripCommandOpt;
7551     String  objcopyCommandOpt;
7553 };
7557 /**
7558  * Create a named file
7559  */
7560 class TaskMakeFile : public Task
7562 public:
7564     TaskMakeFile(MakeBase &par) : Task(par)
7565         { type = TASK_MAKEFILE; name = "makefile"; }
7567     virtual ~TaskMakeFile()
7568         {}
7570     virtual bool execute()
7571         {
7572         String fileName = parent.eval(fileNameOpt, "");
7573         String text     = parent.eval(textOpt, "");
7575         taskstatus("%s", fileName.c_str());
7576         String fullName = parent.resolve(fileName);
7577         if (!isNewerThan(parent.getURI().getPath(), fullName))
7578             {
7579             //trace("skipped <makefile>");
7580             return true;
7581             }
7582         String fullNative = getNativePath(fullName);
7583         //trace("fullName:%s", fullName.c_str());
7584         FILE *f = fopen(fullNative.c_str(), "w");
7585         if (!f)
7586             {
7587             error("<makefile> could not open %s for writing : %s",
7588                 fullName.c_str(), strerror(errno));
7589             return false;
7590             }
7591         for (unsigned int i=0 ; i<text.size() ; i++)
7592             fputc(text[i], f);
7593         fputc('\n', f);
7594         fclose(f);
7595         return true;
7596         }
7598     virtual bool parse(Element *elem)
7599         {
7600         if (!parent.getAttribute(elem, "file", fileNameOpt))
7601             return false;
7602         if (fileNameOpt.size() == 0)
7603             {
7604             error("<makefile> requires 'file=\"filename\"' attribute");
7605             return false;
7606             }
7607         if (!parent.getValue(elem, textOpt))
7608             return false;
7609         textOpt = leftJustify(textOpt);
7610         //trace("dirname:%s", dirName.c_str());
7611         return true;
7612         }
7614 private:
7616     String fileNameOpt;
7617     String textOpt;
7618 };
7622 /**
7623  * Create a named directory
7624  */
7625 class TaskMkDir : public Task
7627 public:
7629     TaskMkDir(MakeBase &par) : Task(par)
7630         { type = TASK_MKDIR; name = "mkdir"; }
7632     virtual ~TaskMkDir()
7633         {}
7635     virtual bool execute()
7636         {
7637         String dirName = parent.eval(dirNameOpt, ".");
7638         
7639         taskstatus("%s", dirName.c_str());
7640         String fullDir = parent.resolve(dirName);
7641         //trace("fullDir:%s", fullDir.c_str());
7642         if (!createDirectory(fullDir))
7643             return false;
7644         return true;
7645         }
7647     virtual bool parse(Element *elem)
7648         {
7649         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7650             return false;
7651         if (dirNameOpt.size() == 0)
7652             {
7653             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7654             return false;
7655             }
7656         return true;
7657         }
7659 private:
7661     String dirNameOpt;
7662 };
7666 /**
7667  * Create a named directory
7668  */
7669 class TaskMsgFmt: public Task
7671 public:
7673     TaskMsgFmt(MakeBase &par) : Task(par)
7674          { type = TASK_MSGFMT;  name = "msgfmt"; }
7676     virtual ~TaskMsgFmt()
7677         {}
7679     virtual bool execute()
7680         {
7681         String  command   = parent.eval(commandOpt, "msgfmt");
7682         String  toDirName = parent.eval(toDirNameOpt, ".");
7683         String  outName   = parent.eval(outNameOpt, "");
7684         bool    owndir    = parent.evalBool(owndirOpt, false);
7686         if (!listFiles(parent, fileSet))
7687             return false;
7688         String fileSetDir = fileSet.getDirectory();
7690         //trace("msgfmt: %d", fileSet.size());
7691         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7692             {
7693             String fileName = fileSet[i];
7694             if (getSuffix(fileName) != "po")
7695                 continue;
7696             String sourcePath;
7697             if (fileSetDir.size()>0)
7698                 {
7699                 sourcePath.append(fileSetDir);
7700                 sourcePath.append("/");
7701                 }
7702             sourcePath.append(fileName);
7703             String fullSource = parent.resolve(sourcePath);
7705             String destPath;
7706             if (toDirName.size()>0)
7707                 {
7708                 destPath.append(toDirName);
7709                 destPath.append("/");
7710                 }
7711             if (owndir)
7712                 {
7713                 String subdir = fileName;
7714                 unsigned int pos = subdir.find_last_of('.');
7715                 if (pos != subdir.npos)
7716                     subdir = subdir.substr(0, pos);
7717                 destPath.append(subdir);
7718                 destPath.append("/");
7719                 }
7720             //Pick the output file name
7721             if (outName.size() > 0)
7722                 {
7723                 destPath.append(outName);
7724                 }
7725             else
7726                 {
7727                 destPath.append(fileName);
7728                 destPath[destPath.size()-2] = 'm';
7729                 }
7731             String fullDest = parent.resolve(destPath);
7733             if (!isNewerThan(fullSource, fullDest))
7734                 {
7735                 //trace("skip %s", fullSource.c_str());
7736                 continue;
7737                 }
7738                 
7739             String cmd = command;
7740             cmd.append(" ");
7741             cmd.append(fullSource);
7742             cmd.append(" -o ");
7743             cmd.append(fullDest);
7744             
7745             int pos = fullDest.find_last_of('/');
7746             if (pos>0)
7747                 {
7748                 String fullDestPath = fullDest.substr(0, pos);
7749                 if (!createDirectory(fullDestPath))
7750                     return false;
7751                 }
7755             String outString, errString;
7756             if (!executeCommand(cmd.c_str(), "", outString, errString))
7757                 {
7758                 error("<msgfmt> problem: %s", errString.c_str());
7759                 return false;
7760                 }
7761             }
7763         return true;
7764         }
7766     virtual bool parse(Element *elem)
7767         {
7768         if (!parent.getAttribute(elem, "command", commandOpt))
7769             return false;
7770         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7771             return false;
7772         if (!parent.getAttribute(elem, "out", outNameOpt))
7773             return false;
7774         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7775             return false;
7776             
7777         std::vector<Element *> children = elem->getChildren();
7778         for (unsigned int i=0 ; i<children.size() ; i++)
7779             {
7780             Element *child = children[i];
7781             String tagName = child->getName();
7782             if (tagName == "fileset")
7783                 {
7784                 if (!parseFileSet(child, parent, fileSet))
7785                     return false;
7786                 }
7787             }
7788         return true;
7789         }
7791 private:
7793     FileSet fileSet;
7795     String  commandOpt;
7796     String  toDirNameOpt;
7797     String  outNameOpt;
7798     String  owndirOpt;
7800 };
7804 /**
7805  *  Perform a Package-Config query similar to pkg-config
7806  */
7807 class TaskPkgConfig : public Task
7809 public:
7811     typedef enum
7812         {
7813         PKG_CONFIG_QUERY_CFLAGS,
7814         PKG_CONFIG_QUERY_LIBS,
7815         PKG_CONFIG_QUERY_ALL
7816         } QueryTypes;
7818     TaskPkgConfig(MakeBase &par) : Task(par)
7819         {
7820         type = TASK_PKG_CONFIG;
7821         name = "pkg-config";
7822         }
7824     virtual ~TaskPkgConfig()
7825         {}
7827     virtual bool execute()
7828         {
7829         String pkgName       = parent.eval(pkgNameOpt,      "");
7830         String prefix        = parent.eval(prefixOpt,       "");
7831         String propName      = parent.eval(propNameOpt,     "");
7832         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7833         String query         = parent.eval(queryOpt,        "all");
7835         String path = parent.resolve(pkgConfigPath);
7836         PkgConfig pkgconfig;
7837         pkgconfig.setPath(path);
7838         pkgconfig.setPrefix(prefix);
7839         if (!pkgconfig.query(pkgName))
7840             {
7841             error("<pkg-config> query failed for '%s", name.c_str());
7842             return false;
7843             }
7844             
7845         String val = "";
7846         if (query == "cflags")
7847             val = pkgconfig.getCflags();
7848         else if (query == "libs")
7849             val =pkgconfig.getLibs();
7850         else if (query == "all")
7851             val = pkgconfig.getAll();
7852         else
7853             {
7854             error("<pkg-config> unhandled query : %s", query.c_str());
7855             return false;
7856             }
7857         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7858         parent.setProperty(propName, val);
7859         return true;
7860         }
7862     virtual bool parse(Element *elem)
7863         {
7864         //# NAME
7865         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7866             return false;
7867         if (pkgNameOpt.size()==0)
7868             {
7869             error("<pkg-config> requires 'name=\"package\"' attribute");
7870             return false;
7871             }
7873         //# PROPERTY
7874         if (!parent.getAttribute(elem, "property", propNameOpt))
7875             return false;
7876         if (propNameOpt.size()==0)
7877             {
7878             error("<pkg-config> requires 'property=\"name\"' attribute");
7879             return false;
7880             }
7881         //# PATH
7882         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7883             return false;
7884         //# PREFIX
7885         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7886             return false;
7887         //# QUERY
7888         if (!parent.getAttribute(elem, "query", queryOpt))
7889             return false;
7891         return true;
7892         }
7894 private:
7896     String queryOpt;
7897     String pkgNameOpt;
7898     String prefixOpt;
7899     String propNameOpt;
7900     String pkgConfigPathOpt;
7902 };
7909 /**
7910  *  Process an archive to allow random access
7911  */
7912 class TaskRanlib : public Task
7914 public:
7916     TaskRanlib(MakeBase &par) : Task(par)
7917         { type = TASK_RANLIB; name = "ranlib"; }
7919     virtual ~TaskRanlib()
7920         {}
7922     virtual bool execute()
7923         {
7924         String fileName = parent.eval(fileNameOpt, "");
7925         String command  = parent.eval(commandOpt, "ranlib");
7927         String fullName = parent.resolve(fileName);
7928         //trace("fullDir:%s", fullDir.c_str());
7929         String cmd = command;
7930         cmd.append(" ");
7931         cmd.append(fullName);
7932         String outbuf, errbuf;
7933         if (!executeCommand(cmd, "", outbuf, errbuf))
7934             return false;
7935         return true;
7936         }
7938     virtual bool parse(Element *elem)
7939         {
7940         if (!parent.getAttribute(elem, "command", commandOpt))
7941             return false;
7942         if (!parent.getAttribute(elem, "file", fileNameOpt))
7943             return false;
7944         if (fileNameOpt.size() == 0)
7945             {
7946             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7947             return false;
7948             }
7949         return true;
7950         }
7952 private:
7954     String fileNameOpt;
7955     String commandOpt;
7956 };
7960 /**
7961  * Compile a resource file into a binary object
7962  */
7963 class TaskRC : public Task
7965 public:
7967     TaskRC(MakeBase &par) : Task(par)
7968         { type = TASK_RC; name = "rc"; }
7970     virtual ~TaskRC()
7971         {}
7973     virtual bool execute()
7974         {
7975         String command  = parent.eval(commandOpt,  "windres");
7976         String flags    = parent.eval(flagsOpt,    "");
7977         String fileName = parent.eval(fileNameOpt, "");
7978         String outName  = parent.eval(outNameOpt,  "");
7980         String fullFile = parent.resolve(fileName);
7981         String fullOut  = parent.resolve(outName);
7982         if (!isNewerThan(fullFile, fullOut))
7983             return true;
7984         String cmd = command;
7985         cmd.append(" -o ");
7986         cmd.append(fullOut);
7987         cmd.append(" ");
7988         cmd.append(flags);
7989         cmd.append(" ");
7990         cmd.append(fullFile);
7992         String outString, errString;
7993         if (!executeCommand(cmd.c_str(), "", outString, errString))
7994             {
7995             error("RC problem: %s", errString.c_str());
7996             return false;
7997             }
7998         return true;
7999         }
8001     virtual bool parse(Element *elem)
8002         {
8003         if (!parent.getAttribute(elem, "command", commandOpt))
8004             return false;
8005         if (!parent.getAttribute(elem, "file", fileNameOpt))
8006             return false;
8007         if (!parent.getAttribute(elem, "out", outNameOpt))
8008             return false;
8009         std::vector<Element *> children = elem->getChildren();
8010         for (unsigned int i=0 ; i<children.size() ; i++)
8011             {
8012             Element *child = children[i];
8013             String tagName = child->getName();
8014             if (tagName == "flags")
8015                 {
8016                 if (!parent.getValue(child, flagsOpt))
8017                     return false;
8018                 }
8019             }
8020         return true;
8021         }
8023 private:
8025     String commandOpt;
8026     String flagsOpt;
8027     String fileNameOpt;
8028     String outNameOpt;
8030 };
8034 /**
8035  *  Collect .o's into a .so or DLL
8036  */
8037 class TaskSharedLib : public Task
8039 public:
8041     TaskSharedLib(MakeBase &par) : Task(par)
8042         { type = TASK_SHAREDLIB; name = "dll"; }
8044     virtual ~TaskSharedLib()
8045         {}
8047     virtual bool execute()
8048         {
8049         String command     = parent.eval(commandOpt, "dllwrap");
8050         String fileName    = parent.eval(fileNameOpt, "");
8051         String defFileName = parent.eval(defFileNameOpt, "");
8052         String impFileName = parent.eval(impFileNameOpt, "");
8053         String libs        = parent.eval(libsOpt, "");
8055         //trace("###########HERE %d", fileSet.size());
8056         bool doit = false;
8057         
8058         String fullOut = parent.resolve(fileName);
8059         //trace("ar fullout: %s", fullOut.c_str());
8060         
8061         if (!listFiles(parent, fileSet))
8062             return false;
8063         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8065         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8066             {
8067             String fname;
8068             if (fileSetDir.size()>0)
8069                 {
8070                 fname.append(fileSetDir);
8071                 fname.append("/");
8072                 }
8073             fname.append(fileSet[i]);
8074             String fullName = parent.resolve(fname);
8075             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8076             if (isNewerThan(fullName, fullOut))
8077                 doit = true;
8078             }
8079         //trace("Needs it:%d", doit);
8080         if (!doit)
8081             {
8082             return true;
8083             }
8085         String cmd = "dllwrap";
8086         cmd.append(" -o ");
8087         cmd.append(fullOut);
8088         if (defFileName.size()>0)
8089             {
8090             cmd.append(" --def ");
8091             cmd.append(defFileName);
8092             cmd.append(" ");
8093             }
8094         if (impFileName.size()>0)
8095             {
8096             cmd.append(" --implib ");
8097             cmd.append(impFileName);
8098             cmd.append(" ");
8099             }
8100         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8101             {
8102             String fname;
8103             if (fileSetDir.size()>0)
8104                 {
8105                 fname.append(fileSetDir);
8106                 fname.append("/");
8107                 }
8108             fname.append(fileSet[i]);
8109             String fullName = parent.resolve(fname);
8111             cmd.append(" ");
8112             cmd.append(fullName);
8113             }
8114         cmd.append(" ");
8115         cmd.append(libs);
8117         String outString, errString;
8118         if (!executeCommand(cmd.c_str(), "", outString, errString))
8119             {
8120             error("<sharedlib> problem: %s", errString.c_str());
8121             return false;
8122             }
8124         return true;
8125         }
8127     virtual bool parse(Element *elem)
8128         {
8129         if (!parent.getAttribute(elem, "command", commandOpt))
8130             return false;
8131         if (!parent.getAttribute(elem, "file", fileNameOpt))
8132             return false;
8133         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8134             return false;
8135         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8136             return false;
8137             
8138         std::vector<Element *> children = elem->getChildren();
8139         for (unsigned int i=0 ; i<children.size() ; i++)
8140             {
8141             Element *child = children[i];
8142             String tagName = child->getName();
8143             if (tagName == "fileset")
8144                 {
8145                 if (!parseFileSet(child, parent, fileSet))
8146                     return false;
8147                 }
8148             else if (tagName == "libs")
8149                 {
8150                 if (!parent.getValue(child, libsOpt))
8151                     return false;
8152                 libsOpt = strip(libsOpt);
8153                 }
8154             }
8155         return true;
8156         }
8158 private:
8160     FileSet fileSet;
8162     String commandOpt;
8163     String fileNameOpt;
8164     String defFileNameOpt;
8165     String impFileNameOpt;
8166     String libsOpt;
8168 };
8172 /**
8173  * Run the "ar" command to archive .o's into a .a
8174  */
8175 class TaskStaticLib : public Task
8177 public:
8179     TaskStaticLib(MakeBase &par) : Task(par)
8180         { type = TASK_STATICLIB; name = "staticlib"; }
8182     virtual ~TaskStaticLib()
8183         {}
8185     virtual bool execute()
8186         {
8187         String command = parent.eval(commandOpt, "ar crv");
8188         String fileName = parent.eval(fileNameOpt, "");
8190         bool doit = false;
8191         
8192         String fullOut = parent.resolve(fileName);
8193         //trace("ar fullout: %s", fullOut.c_str());
8194         
8195         if (!listFiles(parent, fileSet))
8196             return false;
8197         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8198         //trace("###########HERE %s", fileSetDir.c_str());
8200         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8201             {
8202             String fname;
8203             if (fileSetDir.size()>0)
8204                 {
8205                 fname.append(fileSetDir);
8206                 fname.append("/");
8207                 }
8208             fname.append(fileSet[i]);
8209             String fullName = parent.resolve(fname);
8210             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8211             if (isNewerThan(fullName, fullOut))
8212                 doit = true;
8213             }
8214         //trace("Needs it:%d", doit);
8215         if (!doit)
8216             {
8217             return true;
8218             }
8220         String cmd = command;
8221         cmd.append(" ");
8222         cmd.append(fullOut);
8223         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8224             {
8225             String fname;
8226             if (fileSetDir.size()>0)
8227                 {
8228                 fname.append(fileSetDir);
8229                 fname.append("/");
8230                 }
8231             fname.append(fileSet[i]);
8232             String fullName = parent.resolve(fname);
8234             cmd.append(" ");
8235             cmd.append(fullName);
8236             }
8238         String outString, errString;
8239         if (!executeCommand(cmd.c_str(), "", outString, errString))
8240             {
8241             error("<staticlib> problem: %s", errString.c_str());
8242             return false;
8243             }
8245         return true;
8246         }
8249     virtual bool parse(Element *elem)
8250         {
8251         if (!parent.getAttribute(elem, "command", commandOpt))
8252             return false;
8253         if (!parent.getAttribute(elem, "file", fileNameOpt))
8254             return false;
8255             
8256         std::vector<Element *> children = elem->getChildren();
8257         for (unsigned int i=0 ; i<children.size() ; i++)
8258             {
8259             Element *child = children[i];
8260             String tagName = child->getName();
8261             if (tagName == "fileset")
8262                 {
8263                 if (!parseFileSet(child, parent, fileSet))
8264                     return false;
8265                 }
8266             }
8267         return true;
8268         }
8270 private:
8272     FileSet fileSet;
8274     String commandOpt;
8275     String fileNameOpt;
8277 };
8282 /**
8283  * Strip an executable
8284  */
8285 class TaskStrip : public Task
8287 public:
8289     TaskStrip(MakeBase &par) : Task(par)
8290         { type = TASK_STRIP; name = "strip"; }
8292     virtual ~TaskStrip()
8293         {}
8295     virtual bool execute()
8296         {
8297         String command     = parent.eval(commandOpt, "strip");
8298         String fileName    = parent.eval(fileNameOpt, "");
8299         String symFileName = parent.eval(symFileNameOpt, "");
8301         String fullName = parent.resolve(fileName);
8302         //trace("fullDir:%s", fullDir.c_str());
8303         String cmd;
8304         String outbuf, errbuf;
8306         if (symFileName.size()>0)
8307             {
8308             String symFullName = parent.resolve(symFileName);
8309             cmd = "objcopy --only-keep-debug ";
8310             cmd.append(getNativePath(fullName));
8311             cmd.append(" ");
8312             cmd.append(getNativePath(symFullName));
8313             if (!executeCommand(cmd, "", outbuf, errbuf))
8314                 {
8315                 error("<strip> symbol file failed : %s", errbuf.c_str());
8316                 return false;
8317                 }
8318             }
8319             
8320         cmd = command;
8321         cmd.append(getNativePath(fullName));
8322         if (!executeCommand(cmd, "", outbuf, errbuf))
8323             {
8324             error("<strip> failed : %s", errbuf.c_str());
8325             return false;
8326             }
8327         return true;
8328         }
8330     virtual bool parse(Element *elem)
8331         {
8332         if (!parent.getAttribute(elem, "command", commandOpt))
8333             return false;
8334         if (!parent.getAttribute(elem, "file", fileNameOpt))
8335             return false;
8336         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8337             return false;
8338         if (fileNameOpt.size() == 0)
8339             {
8340             error("<strip> requires 'file=\"fileName\"' attribute");
8341             return false;
8342             }
8343         return true;
8344         }
8346 private:
8348     String commandOpt;
8349     String fileNameOpt;
8350     String symFileNameOpt;
8351 };
8354 /**
8355  *
8356  */
8357 class TaskTouch : public Task
8359 public:
8361     TaskTouch(MakeBase &par) : Task(par)
8362         { type = TASK_TOUCH; name = "touch"; }
8364     virtual ~TaskTouch()
8365         {}
8367     virtual bool execute()
8368         {
8369         String fileName = parent.eval(fileNameOpt, "");
8371         String fullName = parent.resolve(fileName);
8372         String nativeFile = getNativePath(fullName);
8373         if (!isRegularFile(fullName) && !isDirectory(fullName))
8374             {            
8375             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8376             int ret = creat(nativeFile.c_str(), 0666);
8377             if (ret != 0) 
8378                 {
8379                 error("<touch> could not create '%s' : %s",
8380                     nativeFile.c_str(), strerror(ret));
8381                 return false;
8382                 }
8383             return true;
8384             }
8385         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8386         if (ret != 0)
8387             {
8388             error("<touch> could not update the modification time for '%s' : %s",
8389                 nativeFile.c_str(), strerror(ret));
8390             return false;
8391             }
8392         return true;
8393         }
8395     virtual bool parse(Element *elem)
8396         {
8397         //trace("touch parse");
8398         if (!parent.getAttribute(elem, "file", fileNameOpt))
8399             return false;
8400         if (fileNameOpt.size() == 0)
8401             {
8402             error("<touch> requires 'file=\"fileName\"' attribute");
8403             return false;
8404             }
8405         return true;
8406         }
8408     String fileNameOpt;
8409 };
8412 /**
8413  *
8414  */
8415 class TaskTstamp : public Task
8417 public:
8419     TaskTstamp(MakeBase &par) : Task(par)
8420         { type = TASK_TSTAMP; name = "tstamp"; }
8422     virtual ~TaskTstamp()
8423         {}
8425     virtual bool execute()
8426         {
8427         return true;
8428         }
8430     virtual bool parse(Element *elem)
8431         {
8432         //trace("tstamp parse");
8433         return true;
8434         }
8435 };
8439 /**
8440  *
8441  */
8442 Task *Task::createTask(Element *elem, int lineNr)
8444     String tagName = elem->getName();
8445     //trace("task:%s", tagName.c_str());
8446     Task *task = NULL;
8447     if (tagName == "cc")
8448         task = new TaskCC(parent);
8449     else if (tagName == "copy")
8450         task = new TaskCopy(parent);
8451     else if (tagName == "delete")
8452         task = new TaskDelete(parent);
8453     else if (tagName == "echo")
8454         task = new TaskEcho(parent);
8455     else if (tagName == "jar")
8456         task = new TaskJar(parent);
8457     else if (tagName == "javac")
8458         task = new TaskJavac(parent);
8459     else if (tagName == "link")
8460         task = new TaskLink(parent);
8461     else if (tagName == "makefile")
8462         task = new TaskMakeFile(parent);
8463     else if (tagName == "mkdir")
8464         task = new TaskMkDir(parent);
8465     else if (tagName == "msgfmt")
8466         task = new TaskMsgFmt(parent);
8467     else if (tagName == "pkg-config")
8468         task = new TaskPkgConfig(parent);
8469     else if (tagName == "ranlib")
8470         task = new TaskRanlib(parent);
8471     else if (tagName == "rc")
8472         task = new TaskRC(parent);
8473     else if (tagName == "sharedlib")
8474         task = new TaskSharedLib(parent);
8475     else if (tagName == "staticlib")
8476         task = new TaskStaticLib(parent);
8477     else if (tagName == "strip")
8478         task = new TaskStrip(parent);
8479     else if (tagName == "touch")
8480         task = new TaskTouch(parent);
8481     else if (tagName == "tstamp")
8482         task = new TaskTstamp(parent);
8483     else
8484         {
8485         error("Unknown task '%s'", tagName.c_str());
8486         return NULL;
8487         }
8489     task->setLine(lineNr);
8491     if (!task->parse(elem))
8492         {
8493         delete task;
8494         return NULL;
8495         }
8496     return task;
8501 //########################################################################
8502 //# T A R G E T
8503 //########################################################################
8505 /**
8506  *
8507  */
8508 class Target : public MakeBase
8511 public:
8513     /**
8514      *
8515      */
8516     Target(Make &par) : parent(par)
8517         { init(); }
8519     /**
8520      *
8521      */
8522     Target(const Target &other) : parent(other.parent)
8523         { init(); assign(other); }
8525     /**
8526      *
8527      */
8528     Target &operator=(const Target &other)
8529         { init(); assign(other); return *this; }
8531     /**
8532      *
8533      */
8534     virtual ~Target()
8535         { cleanup() ; }
8538     /**
8539      *
8540      */
8541     virtual Make &getParent()
8542         { return parent; }
8544     /**
8545      *
8546      */
8547     virtual String getName()
8548         { return name; }
8550     /**
8551      *
8552      */
8553     virtual void setName(const String &val)
8554         { name = val; }
8556     /**
8557      *
8558      */
8559     virtual String getDescription()
8560         { return description; }
8562     /**
8563      *
8564      */
8565     virtual void setDescription(const String &val)
8566         { description = val; }
8568     /**
8569      *
8570      */
8571     virtual void addDependency(const String &val)
8572         { deps.push_back(val); }
8574     /**
8575      *
8576      */
8577     virtual void parseDependencies(const String &val)
8578         { deps = tokenize(val, ", "); }
8580     /**
8581      *
8582      */
8583     virtual std::vector<String> &getDependencies()
8584         { return deps; }
8586     /**
8587      *
8588      */
8589     virtual String getIf()
8590         { return ifVar; }
8592     /**
8593      *
8594      */
8595     virtual void setIf(const String &val)
8596         { ifVar = val; }
8598     /**
8599      *
8600      */
8601     virtual String getUnless()
8602         { return unlessVar; }
8604     /**
8605      *
8606      */
8607     virtual void setUnless(const String &val)
8608         { unlessVar = val; }
8610     /**
8611      *
8612      */
8613     virtual void addTask(Task *val)
8614         { tasks.push_back(val); }
8616     /**
8617      *
8618      */
8619     virtual std::vector<Task *> &getTasks()
8620         { return tasks; }
8622 private:
8624     void init()
8625         {
8626         }
8628     void cleanup()
8629         {
8630         tasks.clear();
8631         }
8633     void assign(const Target &other)
8634         {
8635         //parent      = other.parent;
8636         name        = other.name;
8637         description = other.description;
8638         ifVar       = other.ifVar;
8639         unlessVar   = other.unlessVar;
8640         deps        = other.deps;
8641         tasks       = other.tasks;
8642         }
8644     Make &parent;
8646     String name;
8648     String description;
8650     String ifVar;
8652     String unlessVar;
8654     std::vector<String> deps;
8656     std::vector<Task *> tasks;
8658 };
8667 //########################################################################
8668 //# M A K E
8669 //########################################################################
8672 /**
8673  *
8674  */
8675 class Make : public MakeBase
8678 public:
8680     /**
8681      *
8682      */
8683     Make()
8684         { init(); }
8686     /**
8687      *
8688      */
8689     Make(const Make &other)
8690         { assign(other); }
8692     /**
8693      *
8694      */
8695     Make &operator=(const Make &other)
8696         { assign(other); return *this; }
8698     /**
8699      *
8700      */
8701     virtual ~Make()
8702         { cleanup(); }
8704     /**
8705      *
8706      */
8707     virtual std::map<String, Target> &getTargets()
8708         { return targets; }
8711     /**
8712      *
8713      */
8714     virtual String version()
8715         { return BUILDTOOL_VERSION; }
8717     /**
8718      * Overload a <property>
8719      */
8720     virtual bool specifyProperty(const String &name,
8721                                  const String &value);
8723     /**
8724      *
8725      */
8726     virtual bool run();
8728     /**
8729      *
8730      */
8731     virtual bool run(const String &target);
8735 private:
8737     /**
8738      *
8739      */
8740     void init();
8742     /**
8743      *
8744      */
8745     void cleanup();
8747     /**
8748      *
8749      */
8750     void assign(const Make &other);
8752     /**
8753      *
8754      */
8755     bool executeTask(Task &task);
8758     /**
8759      *
8760      */
8761     bool executeTarget(Target &target,
8762              std::set<String> &targetsCompleted);
8765     /**
8766      *
8767      */
8768     bool execute();
8770     /**
8771      *
8772      */
8773     bool checkTargetDependencies(Target &prop,
8774                     std::vector<String> &depList);
8776     /**
8777      *
8778      */
8779     bool parsePropertyFile(const String &fileName,
8780                            const String &prefix);
8782     /**
8783      *
8784      */
8785     bool parseProperty(Element *elem);
8787     /**
8788      *
8789      */
8790     bool parseFile();
8792     /**
8793      *
8794      */
8795     std::vector<String> glob(const String &pattern);
8798     //###############
8799     //# Fields
8800     //###############
8802     String projectName;
8804     String currentTarget;
8806     String defaultTarget;
8808     String specifiedTarget;
8810     String baseDir;
8812     String description;
8813     
8814     //std::vector<Property> properties;
8815     
8816     std::map<String, Target> targets;
8818     std::vector<Task *> allTasks;
8819     
8820     std::map<String, String> specifiedProperties;
8822 };
8825 //########################################################################
8826 //# C L A S S  M A I N T E N A N C E
8827 //########################################################################
8829 /**
8830  *
8831  */
8832 void Make::init()
8834     uri             = "build.xml";
8835     projectName     = "";
8836     currentTarget   = "";
8837     defaultTarget   = "";
8838     specifiedTarget = "";
8839     baseDir         = "";
8840     description     = "";
8841     envPrefix       = "env.";
8842     pcPrefix        = "pc.";
8843     pccPrefix       = "pcc.";
8844     pclPrefix       = "pcl.";
8845     properties.clear();
8846     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8847         delete allTasks[i];
8848     allTasks.clear();
8853 /**
8854  *
8855  */
8856 void Make::cleanup()
8858     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8859         delete allTasks[i];
8860     allTasks.clear();
8865 /**
8866  *
8867  */
8868 void Make::assign(const Make &other)
8870     uri              = other.uri;
8871     projectName      = other.projectName;
8872     currentTarget    = other.currentTarget;
8873     defaultTarget    = other.defaultTarget;
8874     specifiedTarget  = other.specifiedTarget;
8875     baseDir          = other.baseDir;
8876     description      = other.description;
8877     properties       = other.properties;
8882 //########################################################################
8883 //# U T I L I T Y    T A S K S
8884 //########################################################################
8886 /**
8887  *  Perform a file globbing
8888  */
8889 std::vector<String> Make::glob(const String &pattern)
8891     std::vector<String> res;
8892     return res;
8896 //########################################################################
8897 //# P U B L I C    A P I
8898 //########################################################################
8902 /**
8903  *
8904  */
8905 bool Make::executeTarget(Target &target,
8906              std::set<String> &targetsCompleted)
8909     String name = target.getName();
8911     //First get any dependencies for this target
8912     std::vector<String> deps = target.getDependencies();
8913     for (unsigned int i=0 ; i<deps.size() ; i++)
8914         {
8915         String dep = deps[i];
8916         //Did we do it already?  Skip
8917         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8918             continue;
8919             
8920         std::map<String, Target> &tgts =
8921                target.getParent().getTargets();
8922         std::map<String, Target>::iterator iter =
8923                tgts.find(dep);
8924         if (iter == tgts.end())
8925             {
8926             error("Target '%s' dependency '%s' not found",
8927                       name.c_str(),  dep.c_str());
8928             return false;
8929             }
8930         Target depTarget = iter->second;
8931         if (!executeTarget(depTarget, targetsCompleted))
8932             {
8933             return false;
8934             }
8935         }
8937     status("##### Target : %s\n##### %s", name.c_str(),
8938             target.getDescription().c_str());
8940     //Now let's do the tasks
8941     std::vector<Task *> &tasks = target.getTasks();
8942     for (unsigned int i=0 ; i<tasks.size() ; i++)
8943         {
8944         Task *task = tasks[i];
8945         status("--- %s / %s", name.c_str(), task->getName().c_str());
8946         if (!task->execute())
8947             {
8948             return false;
8949             }
8950         }
8951         
8952     targetsCompleted.insert(name);
8953     
8954     return true;
8959 /**
8960  *  Main execute() method.  Start here and work
8961  *  up the dependency tree 
8962  */
8963 bool Make::execute()
8965     status("######## EXECUTE");
8967     //Determine initial target
8968     if (specifiedTarget.size()>0)
8969         {
8970         currentTarget = specifiedTarget;
8971         }
8972     else if (defaultTarget.size()>0)
8973         {
8974         currentTarget = defaultTarget;
8975         }
8976     else
8977         {
8978         error("execute: no specified or default target requested");
8979         return false;
8980         }
8982     std::map<String, Target>::iterator iter =
8983                targets.find(currentTarget);
8984     if (iter == targets.end())
8985         {
8986         error("Initial target '%s' not found",
8987                  currentTarget.c_str());
8988         return false;
8989         }
8990         
8991     //Now run
8992     Target target = iter->second;
8993     std::set<String> targetsCompleted;
8994     if (!executeTarget(target, targetsCompleted))
8995         {
8996         return false;
8997         }
8999     status("######## EXECUTE COMPLETE");
9000     return true;
9006 /**
9007  *
9008  */
9009 bool Make::checkTargetDependencies(Target &target, 
9010                             std::vector<String> &depList)
9012     String tgtName = target.getName().c_str();
9013     depList.push_back(tgtName);
9015     std::vector<String> deps = target.getDependencies();
9016     for (unsigned int i=0 ; i<deps.size() ; i++)
9017         {
9018         String dep = deps[i];
9019         //First thing entered was the starting Target
9020         if (dep == depList[0])
9021             {
9022             error("Circular dependency '%s' found at '%s'",
9023                       dep.c_str(), tgtName.c_str());
9024             std::vector<String>::iterator diter;
9025             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9026                 {
9027                 error("  %s", diter->c_str());
9028                 }
9029             return false;
9030             }
9032         std::map<String, Target> &tgts =
9033                   target.getParent().getTargets();
9034         std::map<String, Target>::iterator titer = tgts.find(dep);
9035         if (titer == tgts.end())
9036             {
9037             error("Target '%s' dependency '%s' not found",
9038                       tgtName.c_str(), dep.c_str());
9039             return false;
9040             }
9041         if (!checkTargetDependencies(titer->second, depList))
9042             {
9043             return false;
9044             }
9045         }
9046     return true;
9053 static int getword(int pos, const String &inbuf, String &result)
9055     int p = pos;
9056     int len = (int)inbuf.size();
9057     String val;
9058     while (p < len)
9059         {
9060         char ch = inbuf[p];
9061         if (!isalnum(ch) && ch!='.' && ch!='_')
9062             break;
9063         val.push_back(ch);
9064         p++;
9065         }
9066     result = val;
9067     return p;
9073 /**
9074  *
9075  */
9076 bool Make::parsePropertyFile(const String &fileName,
9077                              const String &prefix)
9079     FILE *f = fopen(fileName.c_str(), "r");
9080     if (!f)
9081         {
9082         error("could not open property file %s", fileName.c_str());
9083         return false;
9084         }
9085     int linenr = 0;
9086     while (!feof(f))
9087         {
9088         char buf[256];
9089         if (!fgets(buf, 255, f))
9090             break;
9091         linenr++;
9092         String s = buf;
9093         s = trim(s);
9094         int len = s.size();
9095         if (len == 0)
9096             continue;
9097         if (s[0] == '#')
9098             continue;
9099         String key;
9100         String val;
9101         int p = 0;
9102         int p2 = getword(p, s, key);
9103         if (p2 <= p)
9104             {
9105             error("property file %s, line %d: expected keyword",
9106                     fileName.c_str(), linenr);
9107             return false;
9108             }
9109         if (prefix.size() > 0)
9110             {
9111             key.insert(0, prefix);
9112             }
9114         //skip whitespace
9115         for (p=p2 ; p<len ; p++)
9116             if (!isspace(s[p]))
9117                 break;
9119         if (p>=len || s[p]!='=')
9120             {
9121             error("property file %s, line %d: expected '='",
9122                     fileName.c_str(), linenr);
9123             return false;
9124             }
9125         p++;
9127         //skip whitespace
9128         for ( ; p<len ; p++)
9129             if (!isspace(s[p]))
9130                 break;
9132         /* This way expects a word after the =
9133         p2 = getword(p, s, val);
9134         if (p2 <= p)
9135             {
9136             error("property file %s, line %d: expected value",
9137                     fileName.c_str(), linenr);
9138             return false;
9139             }
9140         */
9141         // This way gets the rest of the line after the =
9142         if (p>=len)
9143             {
9144             error("property file %s, line %d: expected value",
9145                     fileName.c_str(), linenr);
9146             return false;
9147             }
9148         val = s.substr(p);
9149         if (key.size()==0)
9150             continue;
9151         //allow property to be set, even if val=""
9153         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9154         //See if we wanted to overload this property
9155         std::map<String, String>::iterator iter =
9156             specifiedProperties.find(key);
9157         if (iter!=specifiedProperties.end())
9158             {
9159             val = iter->second;
9160             status("overloading property '%s' = '%s'",
9161                    key.c_str(), val.c_str());
9162             }
9163         properties[key] = val;
9164         }
9165     fclose(f);
9166     return true;
9172 /**
9173  *
9174  */
9175 bool Make::parseProperty(Element *elem)
9177     std::vector<Attribute> &attrs = elem->getAttributes();
9178     for (unsigned int i=0 ; i<attrs.size() ; i++)
9179         {
9180         String attrName = attrs[i].getName();
9181         String attrVal  = attrs[i].getValue();
9183         if (attrName == "name")
9184             {
9185             String val;
9186             if (!getAttribute(elem, "value", val))
9187                 return false;
9188             if (val.size() > 0)
9189                 {
9190                 properties[attrVal] = val;
9191                 }
9192             else
9193                 {
9194                 if (!getAttribute(elem, "location", val))
9195                     return false;
9196                 //let the property exist, even if not defined
9197                 properties[attrVal] = val;
9198                 }
9199             //See if we wanted to overload this property
9200             std::map<String, String>::iterator iter =
9201                 specifiedProperties.find(attrVal);
9202             if (iter != specifiedProperties.end())
9203                 {
9204                 val = iter->second;
9205                 status("overloading property '%s' = '%s'",
9206                     attrVal.c_str(), val.c_str());
9207                 properties[attrVal] = val;
9208                 }
9209             }
9210         else if (attrName == "file")
9211             {
9212             String prefix;
9213             if (!getAttribute(elem, "prefix", prefix))
9214                 return false;
9215             if (prefix.size() > 0)
9216                 {
9217                 if (prefix[prefix.size()-1] != '.')
9218                     prefix.push_back('.');
9219                 }
9220             if (!parsePropertyFile(attrName, prefix))
9221                 return false;
9222             }
9223         else if (attrName == "environment")
9224             {
9225             if (attrVal.find('.') != attrVal.npos)
9226                 {
9227                 error("environment prefix cannot have a '.' in it");
9228                 return false;
9229                 }
9230             envPrefix = attrVal;
9231             envPrefix.push_back('.');
9232             }
9233         else if (attrName == "pkg-config")
9234             {
9235             if (attrVal.find('.') != attrVal.npos)
9236                 {
9237                 error("pkg-config prefix cannot have a '.' in it");
9238                 return false;
9239                 }
9240             pcPrefix = attrVal;
9241             pcPrefix.push_back('.');
9242             }
9243         else if (attrName == "pkg-config-cflags")
9244             {
9245             if (attrVal.find('.') != attrVal.npos)
9246                 {
9247                 error("pkg-config-cflags prefix cannot have a '.' in it");
9248                 return false;
9249                 }
9250             pccPrefix = attrVal;
9251             pccPrefix.push_back('.');
9252             }
9253         else if (attrName == "pkg-config-libs")
9254             {
9255             if (attrVal.find('.') != attrVal.npos)
9256                 {
9257                 error("pkg-config-libs prefix cannot have a '.' in it");
9258                 return false;
9259                 }
9260             pclPrefix = attrVal;
9261             pclPrefix.push_back('.');
9262             }
9263         }
9265     return true;
9271 /**
9272  *
9273  */
9274 bool Make::parseFile()
9276     status("######## PARSE : %s", uri.getPath().c_str());
9278     setLine(0);
9280     Parser parser;
9281     Element *root = parser.parseFile(uri.getNativePath());
9282     if (!root)
9283         {
9284         error("Could not open %s for reading",
9285               uri.getNativePath().c_str());
9286         return false;
9287         }
9288     
9289     setLine(root->getLine());
9291     if (root->getChildren().size()==0 ||
9292         root->getChildren()[0]->getName()!="project")
9293         {
9294         error("Main xml element should be <project>");
9295         delete root;
9296         return false;
9297         }
9299     //########## Project attributes
9300     Element *project = root->getChildren()[0];
9301     String s = project->getAttribute("name");
9302     if (s.size() > 0)
9303         projectName = s;
9304     s = project->getAttribute("default");
9305     if (s.size() > 0)
9306         defaultTarget = s;
9307     s = project->getAttribute("basedir");
9308     if (s.size() > 0)
9309         baseDir = s;
9311     //######### PARSE MEMBERS
9312     std::vector<Element *> children = project->getChildren();
9313     for (unsigned int i=0 ; i<children.size() ; i++)
9314         {
9315         Element *elem = children[i];
9316         setLine(elem->getLine());
9317         String tagName = elem->getName();
9319         //########## DESCRIPTION
9320         if (tagName == "description")
9321             {
9322             description = parser.trim(elem->getValue());
9323             }
9325         //######### PROPERTY
9326         else if (tagName == "property")
9327             {
9328             if (!parseProperty(elem))
9329                 return false;
9330             }
9332         //######### TARGET
9333         else if (tagName == "target")
9334             {
9335             String tname   = elem->getAttribute("name");
9336             String tdesc   = elem->getAttribute("description");
9337             String tdeps   = elem->getAttribute("depends");
9338             String tif     = elem->getAttribute("if");
9339             String tunless = elem->getAttribute("unless");
9340             Target target(*this);
9341             target.setName(tname);
9342             target.setDescription(tdesc);
9343             target.parseDependencies(tdeps);
9344             target.setIf(tif);
9345             target.setUnless(tunless);
9346             std::vector<Element *> telems = elem->getChildren();
9347             for (unsigned int i=0 ; i<telems.size() ; i++)
9348                 {
9349                 Element *telem = telems[i];
9350                 Task breeder(*this);
9351                 Task *task = breeder.createTask(telem, telem->getLine());
9352                 if (!task)
9353                     return false;
9354                 allTasks.push_back(task);
9355                 target.addTask(task);
9356                 }
9358             //Check name
9359             if (tname.size() == 0)
9360                 {
9361                 error("no name for target");
9362                 return false;
9363                 }
9364             //Check for duplicate name
9365             if (targets.find(tname) != targets.end())
9366                 {
9367                 error("target '%s' already defined", tname.c_str());
9368                 return false;
9369                 }
9370             //more work than targets[tname]=target, but avoids default allocator
9371             targets.insert(std::make_pair<String, Target>(tname, target));
9372             }
9373         //######### none of the above
9374         else
9375             {
9376             error("unknown toplevel tag: <%s>", tagName.c_str());
9377             return false;
9378             }
9380         }
9382     std::map<String, Target>::iterator iter;
9383     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9384         {
9385         Target tgt = iter->second;
9386         std::vector<String> depList;
9387         if (!checkTargetDependencies(tgt, depList))
9388             {
9389             return false;
9390             }
9391         }
9394     delete root;
9395     status("######## PARSE COMPLETE");
9396     return true;
9400 /**
9401  * Overload a <property>
9402  */
9403 bool Make::specifyProperty(const String &name, const String &value)
9405     if (specifiedProperties.find(name) != specifiedProperties.end())
9406         {
9407         error("Property %s already specified", name.c_str());
9408         return false;
9409         }
9410     specifiedProperties[name] = value;
9411     return true;
9416 /**
9417  *
9418  */
9419 bool Make::run()
9421     if (!parseFile())
9422         return false;
9423         
9424     if (!execute())
9425         return false;
9427     return true;
9433 /**
9434  * Get a formatted MM:SS.sss time elapsed string
9435  */ 
9436 static String
9437 timeDiffString(struct timeval &x, struct timeval &y)
9439     long microsX  = x.tv_usec;
9440     long secondsX = x.tv_sec;
9441     long microsY  = y.tv_usec;
9442     long secondsY = y.tv_sec;
9443     if (microsX < microsY)
9444         {
9445         microsX += 1000000;
9446         secondsX -= 1;
9447         }
9449     int seconds = (int)(secondsX - secondsY);
9450     int millis  = (int)((microsX - microsY)/1000);
9452     int minutes = seconds/60;
9453     seconds -= minutes*60;
9454     char buf[80];
9455     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9456     String ret = buf;
9457     return ret;
9458     
9461 /**
9462  *
9463  */
9464 bool Make::run(const String &target)
9466     status("####################################################");
9467     status("#   %s", version().c_str());
9468     status("####################################################");
9469     struct timeval timeStart, timeEnd;
9470     ::gettimeofday(&timeStart, NULL);
9471     specifiedTarget = target;
9472     if (!run())
9473         return false;
9474     ::gettimeofday(&timeEnd, NULL);
9475     String timeStr = timeDiffString(timeEnd, timeStart);
9476     status("####################################################");
9477     status("#   BuildTool Completed : %s", timeStr.c_str());
9478     status("####################################################");
9479     return true;
9488 }// namespace buildtool
9489 //########################################################################
9490 //# M A I N
9491 //########################################################################
9493 typedef buildtool::String String;
9495 /**
9496  *  Format an error message in printf() style
9497  */
9498 static void error(const char *fmt, ...)
9500     va_list ap;
9501     va_start(ap, fmt);
9502     fprintf(stderr, "BuildTool error: ");
9503     vfprintf(stderr, fmt, ap);
9504     fprintf(stderr, "\n");
9505     va_end(ap);
9509 static bool parseProperty(const String &s, String &name, String &val)
9511     int len = s.size();
9512     int i;
9513     for (i=0 ; i<len ; i++)
9514         {
9515         char ch = s[i];
9516         if (ch == '=')
9517             break;
9518         name.push_back(ch);
9519         }
9520     if (i>=len || s[i]!='=')
9521         {
9522         error("property requires -Dname=value");
9523         return false;
9524         }
9525     i++;
9526     for ( ; i<len ; i++)
9527         {
9528         char ch = s[i];
9529         val.push_back(ch);
9530         }
9531     return true;
9535 /**
9536  * Compare a buffer with a key, for the length of the key
9537  */
9538 static bool sequ(const String &buf, const char *key)
9540     int len = buf.size();
9541     for (int i=0 ; key[i] && i<len ; i++)
9542         {
9543         if (key[i] != buf[i])
9544             return false;
9545         }        
9546     return true;
9549 static void usage(int argc, char **argv)
9551     printf("usage:\n");
9552     printf("   %s [options] [target]\n", argv[0]);
9553     printf("Options:\n");
9554     printf("  -help, -h              print this message\n");
9555     printf("  -version               print the version information and exit\n");
9556     printf("  -file <file>           use given buildfile\n");
9557     printf("  -f <file>                 ''\n");
9558     printf("  -D<property>=<value>   use value for given property\n");
9564 /**
9565  * Parse the command-line args, get our options,
9566  * and run this thing
9567  */   
9568 static bool parseOptions(int argc, char **argv)
9570     if (argc < 1)
9571         {
9572         error("Cannot parse arguments");
9573         return false;
9574         }
9576     buildtool::Make make;
9578     String target;
9580     //char *progName = argv[0];
9581     for (int i=1 ; i<argc ; i++)
9582         {
9583         String arg = argv[i];
9584         if (arg.size()>1 && arg[0]=='-')
9585             {
9586             if (arg == "-h" || arg == "-help")
9587                 {
9588                 usage(argc,argv);
9589                 return true;
9590                 }
9591             else if (arg == "-version")
9592                 {
9593                 printf("%s", make.version().c_str());
9594                 return true;
9595                 }
9596             else if (arg == "-f" || arg == "-file")
9597                 {
9598                 if (i>=argc)
9599                    {
9600                    usage(argc, argv);
9601                    return false;
9602                    }
9603                 i++; //eat option
9604                 make.setURI(argv[i]);
9605                 }
9606             else if (arg.size()>2 && sequ(arg, "-D"))
9607                 {
9608                 String s = arg.substr(2, arg.size());
9609                 String name, value;
9610                 if (!parseProperty(s, name, value))
9611                    {
9612                    usage(argc, argv);
9613                    return false;
9614                    }
9615                 if (!make.specifyProperty(name, value))
9616                     return false;
9617                 }
9618             else
9619                 {
9620                 error("Unknown option:%s", arg.c_str());
9621                 return false;
9622                 }
9623             }
9624         else
9625             {
9626             if (target.size()>0)
9627                 {
9628                 error("only one initial target");
9629                 usage(argc, argv);
9630                 return false;
9631                 }
9632             target = arg;
9633             }
9634         }
9636     //We have the options.  Now execute them
9637     if (!make.run(target))
9638         return false;
9640     return true;
9646 /*
9647 static bool runMake()
9649     buildtool::Make make;
9650     if (!make.run())
9651         return false;
9652     return true;
9656 static bool pkgConfigTest()
9658     buildtool::PkgConfig pkgConfig;
9659     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9660         return false;
9661     return true;
9666 static bool depTest()
9668     buildtool::DepTool deptool;
9669     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9670     if (!deptool.generateDependencies("build.dep"))
9671         return false;
9672     std::vector<buildtool::FileRec> res =
9673            deptool.loadDepFile("build.dep");
9674     if (res.size() == 0)
9675         return false;
9676     return true;
9679 static bool popenTest()
9681     buildtool::Make make;
9682     buildtool::String out, err;
9683     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9684     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9685     return true;
9689 static bool propFileTest()
9691     buildtool::Make make;
9692     make.parsePropertyFile("test.prop", "test.");
9693     return true;
9695 */
9697 int main(int argc, char **argv)
9700     if (!parseOptions(argc, argv))
9701         return 1;
9702     /*
9703     if (!popenTest())
9704         return 1;
9706     if (!depTest())
9707         return 1;
9708     if (!propFileTest())
9709         return 1;
9710     if (runMake())
9711         return 1;
9712     */
9713     return 0;
9717 //########################################################################
9718 //# E N D 
9719 //########################################################################