Code

make dependency messages a bit better
[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.8.1, 2007-2008 Bob Jamison"
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>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73       int tz_minuteswest; /* minutes west of Greenwich */
74       int tz_dsttime;     /* type of dst correction */
75     };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79    struct _timeb tb;
81    if (!tv)
82       return (-1);
84     _ftime (&tb);
85     tv->tv_sec  = tb.time;
86     tv->tv_usec = tb.millitm * 1000 + 500;
87     if (tz)
88         {
89         tz->tz_minuteswest = -60 * _timezone;
90         tz->tz_dsttime = _daylight;
91         }
92     return 0;
93 }
95 #endif
103 namespace buildtool
109 //########################################################################
110 //########################################################################
111 //##  R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116  * This is the T-Rex regular expression library, which we
117  * gratefully acknowledge.  It's clean code and small size allow
118  * us to embed it in BuildTool without adding a dependency
119  *
120  */    
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127     T-Rex a tiny regular expression library
129     Copyright (C) 2003-2006 Alberto Demichelis
131     This software is provided 'as-is', without any express 
132     or implied warranty. In no event will the authors be held 
133     liable for any damages arising from the use of this software.
135     Permission is granted to anyone to use this software for 
136     any purpose, including commercial applications, and to alter
137     it and redistribute it freely, subject to the following restrictions:
139         1. The origin of this software must not be misrepresented;
140         you must not claim that you wrote the original software.
141         If you use this software in a product, an acknowledgment
142         in the product documentation would be appreciated but
143         is not required.
145         2. Altered source versions must be plainly marked as such,
146         and must not be misrepresented as being the original software.
148         3. This notice may not be removed or altered from any
149         source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c 
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c) 
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178     const TRexChar *begin;
179     int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
224     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
225     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
226     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR            (MAX_CHAR+2)
233 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT            (MAX_CHAR+5)
236 #define OP_CLASS        (MAX_CHAR+6)
237 #define OP_CCLASS        (MAX_CHAR+7)
238 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE        (MAX_CHAR+9)
240 #define OP_CHAR            (MAX_CHAR+10)
241 #define OP_EOL            (MAX_CHAR+11)
242 #define OP_BOL            (MAX_CHAR+12)
243 #define OP_WB            (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258     TRexNodeType type;
259     int left;
260     int right;
261     int next;
262 }TRexNode;
264 struct TRex{
265     const TRexChar *_eol;
266     const TRexChar *_bol;
267     const TRexChar *_p;
268     int _first;
269     int _op;
270     TRexNode *_nodes;
271     int _nallocated;
272     int _nsize;
273     int _nsubexpr;
274     TRexMatch *_matches;
275     int _currsubexp;
276     void *_jmpbuf;
277     const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
284     TRexNode n;
285     int newid;
286     n.type = type;
287     n.next = n.right = n.left = -1;
288     if(type == OP_EXPR)
289         n.right = exp->_nsubexpr++;
290     if(exp->_nallocated < (exp->_nsize + 1)) {
291         //int oldsize = exp->_nallocated;
292         exp->_nallocated *= 2;
293         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294     }
295     exp->_nodes[exp->_nsize++] = n;
296     newid = exp->_nsize - 1;
297     return (int)newid;
300 static void trex_error(TRex *exp,const TRexChar *error)
302     if(exp->_error) *exp->_error = error;
303     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
306 static void trex_expect(TRex *exp, int n){
307     if((*exp->_p) != n) 
308         trex_error(exp, _SC("expected paren"));
309     exp->_p++;
312 static TRexChar trex_escapechar(TRex *exp)
314     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315         exp->_p++;
316         switch(*exp->_p) {
317         case 'v': exp->_p++; return '\v';
318         case 'n': exp->_p++; return '\n';
319         case 't': exp->_p++; return '\t';
320         case 'r': exp->_p++; return '\r';
321         case 'f': exp->_p++; return '\f';
322         default: return (*exp->_p++);
323         }
324     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325     return (*exp->_p++);
328 static int trex_charclass(TRex *exp,int classid)
330     int n = trex_newnode(exp,OP_CCLASS);
331     exp->_nodes[n].left = classid;
332     return n;
335 static int trex_charnode(TRex *exp,TRexBool isclass)
337     TRexChar t;
338     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339         exp->_p++;
340         switch(*exp->_p) {
341             case 'n': exp->_p++; return trex_newnode(exp,'\n');
342             case 't': exp->_p++; return trex_newnode(exp,'\t');
343             case 'r': exp->_p++; return trex_newnode(exp,'\r');
344             case 'f': exp->_p++; return trex_newnode(exp,'\f');
345             case 'v': exp->_p++; return trex_newnode(exp,'\v');
346             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
347             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
348             case 'p': case 'P': case 'l': case 'u': 
349                 {
350                 t = *exp->_p; exp->_p++; 
351                 return trex_charclass(exp,t);
352                 }
353             case 'b': 
354             case 'B':
355                 if(!isclass) {
356                     int node = trex_newnode(exp,OP_WB);
357                     exp->_nodes[node].left = *exp->_p;
358                     exp->_p++; 
359                     return node;
360                 } //else default
361             default: 
362                 t = *exp->_p; exp->_p++; 
363                 return trex_newnode(exp,t);
364         }
365     }
366     else if(!scisprint(*exp->_p)) {
367         
368         trex_error(exp,_SC("letter expected"));
369     }
370     t = *exp->_p; exp->_p++; 
371     return trex_newnode(exp,t);
373 static int trex_class(TRex *exp)
375     int ret = -1;
376     int first = -1,chain;
377     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378         ret = trex_newnode(exp,OP_NCLASS);
379         exp->_p++;
380     }else ret = trex_newnode(exp,OP_CLASS);
381     
382     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383     chain = ret;
384     while(*exp->_p != ']' && exp->_p != exp->_eol) {
385         if(*exp->_p == '-' && first != -1){ 
386             int r,t;
387             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388             r = trex_newnode(exp,OP_RANGE);
389             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391             exp->_nodes[r].left = exp->_nodes[first].type;
392             t = trex_escapechar(exp);
393             exp->_nodes[r].right = t;
394             exp->_nodes[chain].next = r;
395             chain = r;
396             first = -1;
397         }
398         else{
399             if(first!=-1){
400                 int c = first;
401                 exp->_nodes[chain].next = c;
402                 chain = c;
403                 first = trex_charnode(exp,TRex_True);
404             }
405             else{
406                 first = trex_charnode(exp,TRex_True);
407             }
408         }
409     }
410     if(first!=-1){
411         int c = first;
412         exp->_nodes[chain].next = c;
413         chain = c;
414         first = -1;
415     }
416     /* hack? */
417     exp->_nodes[ret].left = exp->_nodes[ret].next;
418     exp->_nodes[ret].next = -1;
419     return ret;
422 static int trex_parsenumber(TRex *exp)
424     int ret = *exp->_p-'0';
425     int positions = 10;
426     exp->_p++;
427     while(isdigit(*exp->_p)) {
428         ret = ret*10+(*exp->_p++-'0');
429         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430         positions *= 10;
431     };
432     return ret;
435 static int trex_element(TRex *exp)
437     int ret = -1;
438     switch(*exp->_p)
439     {
440     case '(': {
441         int expr,newn;
442         exp->_p++;
445         if(*exp->_p =='?') {
446             exp->_p++;
447             trex_expect(exp,':');
448             expr = trex_newnode(exp,OP_NOCAPEXPR);
449         }
450         else
451             expr = trex_newnode(exp,OP_EXPR);
452         newn = trex_list(exp);
453         exp->_nodes[expr].left = newn;
454         ret = expr;
455         trex_expect(exp,')');
456               }
457               break;
458     case '[':
459         exp->_p++;
460         ret = trex_class(exp);
461         trex_expect(exp,']');
462         break;
463     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465     default:
466         ret = trex_charnode(exp,TRex_False);
467         break;
468     }
470     {
471         int op;
472         TRexBool isgreedy = TRex_False;
473         unsigned short p0 = 0, p1 = 0;
474         switch(*exp->_p){
475             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478             case '{':
479                 exp->_p++;
480                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481                 p0 = (unsigned short)trex_parsenumber(exp);
482                 /*******************************/
483                 switch(*exp->_p) {
484             case '}':
485                 p1 = p0; exp->_p++;
486                 break;
487             case ',':
488                 exp->_p++;
489                 p1 = 0xFFFF;
490                 if(isdigit(*exp->_p)){
491                     p1 = (unsigned short)trex_parsenumber(exp);
492                 }
493                 trex_expect(exp,'}');
494                 break;
495             default:
496                 trex_error(exp,_SC(", or } expected"));
497         }
498         /*******************************/
499         isgreedy = TRex_True; 
500         break;
502         }
503         if(isgreedy) {
504             int nnode = trex_newnode(exp,OP_GREEDY);
505             op = OP_GREEDY;
506             exp->_nodes[nnode].left = ret;
507             exp->_nodes[nnode].right = ((p0)<<16)|p1;
508             ret = nnode;
509         }
510     }
511     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')) {
512         int nnode = trex_element(exp);
513         exp->_nodes[ret].next = nnode;
514     }
516     return ret;
519 static int trex_list(TRex *exp)
521     int ret=-1,e;
522     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523         exp->_p++;
524         ret = trex_newnode(exp,OP_BOL);
525     }
526     e = trex_element(exp);
527     if(ret != -1) {
528         exp->_nodes[ret].next = e;
529     }
530     else ret = e;
532     if(*exp->_p == TREX_SYMBOL_BRANCH) {
533         int temp,tright;
534         exp->_p++;
535         temp = trex_newnode(exp,OP_OR);
536         exp->_nodes[temp].left = ret;
537         tright = trex_list(exp);
538         exp->_nodes[temp].right = tright;
539         ret = temp;
540     }
541     return ret;
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
546     switch(cclass) {
547     case 'a': return isalpha(c)?TRex_True:TRex_False;
548     case 'A': return !isalpha(c)?TRex_True:TRex_False;
549     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551     case 's': return isspace(c)?TRex_True:TRex_False;
552     case 'S': return !isspace(c)?TRex_True:TRex_False;
553     case 'd': return isdigit(c)?TRex_True:TRex_False;
554     case 'D': return !isdigit(c)?TRex_True:TRex_False;
555     case 'x': return isxdigit(c)?TRex_True:TRex_False;
556     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557     case 'c': return iscntrl(c)?TRex_True:TRex_False;
558     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559     case 'p': return ispunct(c)?TRex_True:TRex_False;
560     case 'P': return !ispunct(c)?TRex_True:TRex_False;
561     case 'l': return islower(c)?TRex_True:TRex_False;
562     case 'u': return isupper(c)?TRex_True:TRex_False;
563     }
564     return TRex_False; /*cannot happen*/
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
569     do {
570         switch(node->type) {
571             case OP_RANGE:
572                 if(c >= node->left && c <= node->right) return TRex_True;
573                 break;
574             case OP_CCLASS:
575                 if(trex_matchcclass(node->left,c)) return TRex_True;
576                 break;
577             default:
578                 if(c == node->type)return TRex_True;
579         }
580     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581     return TRex_False;
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
586     
587     TRexNodeType type = node->type;
588     switch(type) {
589     case OP_GREEDY: {
590         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591         TRexNode *greedystop = NULL;
592         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593         const TRexChar *s=str, *good = str;
595         if(node->next != -1) {
596             greedystop = &exp->_nodes[node->next];
597         }
598         else {
599             greedystop = next;
600         }
602         while((nmaches == 0xFFFF || nmaches < p1)) {
604             const TRexChar *stop;
605             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606                 break;
607             nmaches++;
608             good=s;
609             if(greedystop) {
610                 //checks that 0 matches satisfy the expression(if so skips)
611                 //if not would always stop(for instance if is a '?')
612                 if(greedystop->type != OP_GREEDY ||
613                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614                 {
615                     TRexNode *gnext = NULL;
616                     if(greedystop->next != -1) {
617                         gnext = &exp->_nodes[greedystop->next];
618                     }else if(next && next->next != -1){
619                         gnext = &exp->_nodes[next->next];
620                     }
621                     stop = trex_matchnode(exp,greedystop,s,gnext);
622                     if(stop) {
623                         //if satisfied stop it
624                         if(p0 == p1 && p0 == nmaches) break;
625                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
626                         else if(nmaches >= p0 && nmaches <= p1) break;
627                     }
628                 }
629             }
630             
631             if(s >= exp->_eol)
632                 break;
633         }
634         if(p0 == p1 && p0 == nmaches) return good;
635         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636         else if(nmaches >= p0 && nmaches <= p1) return good;
637         return NULL;
638     }
639     case OP_OR: {
640             const TRexChar *asd = str;
641             TRexNode *temp=&exp->_nodes[node->left];
642             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643                 if(temp->next != -1)
644                     temp = &exp->_nodes[temp->next];
645                 else
646                     return asd;
647             }
648             asd = str;
649             temp = &exp->_nodes[node->right];
650             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651                 if(temp->next != -1)
652                     temp = &exp->_nodes[temp->next];
653                 else
654                     return asd;
655             }
656             return NULL;
657             break;
658     }
659     case OP_EXPR:
660     case OP_NOCAPEXPR:{
661             TRexNode *n = &exp->_nodes[node->left];
662             const TRexChar *cur = str;
663             int capture = -1;
664             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665                 capture = exp->_currsubexp;
666                 exp->_matches[capture].begin = cur;
667                 exp->_currsubexp++;
668             }
669             
670             do {
671                 TRexNode *subnext = NULL;
672                 if(n->next != -1) {
673                     subnext = &exp->_nodes[n->next];
674                 }else {
675                     subnext = next;
676                 }
677                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678                     if(capture != -1){
679                         exp->_matches[capture].begin = 0;
680                         exp->_matches[capture].len = 0;
681                     }
682                     return NULL;
683                 }
684             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686             if(capture != -1) 
687                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688             return cur;
689     }                 
690     case OP_WB:
691         if(str == exp->_bol && !isspace(*str)
692          || (str == exp->_eol && !isspace(*(str-1)))
693          || (!isspace(*str) && isspace(*(str+1)))
694          || (isspace(*str) && !isspace(*(str+1))) ) {
695             return (node->left == 'b')?str:NULL;
696         }
697         return (node->left == 'b')?NULL:str;
698     case OP_BOL:
699         if(str == exp->_bol) return str;
700         return NULL;
701     case OP_EOL:
702         if(str == exp->_eol) return str;
703         return NULL;
704     case OP_DOT:{
705         *str++;
706                 }
707         return str;
708     case OP_NCLASS:
709     case OP_CLASS:
710         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711             *str++;
712             return str;
713         }
714         return NULL;
715     case OP_CCLASS:
716         if(trex_matchcclass(node->left,*str)) {
717             *str++;
718             return str;
719         }
720         return NULL;
721     default: /* char */
722         if(*str != node->type) return NULL;
723         *str++;
724         return str;
725     }
726     return NULL;
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
732     TRex *exp = (TRex *)malloc(sizeof(TRex));
733     exp->_eol = exp->_bol = NULL;
734     exp->_p = pattern;
735     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737     exp->_nsize = 0;
738     exp->_matches = 0;
739     exp->_nsubexpr = 0;
740     exp->_first = trex_newnode(exp,OP_EXPR);
741     exp->_error = error;
742     exp->_jmpbuf = malloc(sizeof(jmp_buf));
743     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744         int res = trex_list(exp);
745         exp->_nodes[exp->_first].left = res;
746         if(*exp->_p!='\0')
747             trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749         {
750             int nsize,i;
751             TRexNode *t;
752             nsize = exp->_nsize;
753             t = &exp->_nodes[0];
754             scprintf(_SC("\n"));
755             for(i = 0;i < nsize; i++) {
756                 if(exp->_nodes[i].type>MAX_CHAR)
757                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758                 else
759                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761             }
762             scprintf(_SC("\n"));
763         }
764 #endif
765         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767     }
768     else{
769         trex_free(exp);
770         return NULL;
771     }
772     return exp;
775 void trex_free(TRex *exp)
777     if(exp)    {
778         if(exp->_nodes) free(exp->_nodes);
779         if(exp->_jmpbuf) free(exp->_jmpbuf);
780         if(exp->_matches) free(exp->_matches);
781         free(exp);
782     }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
787     const TRexChar* res = NULL;
788     exp->_bol = text;
789     exp->_eol = text + scstrlen(text);
790     exp->_currsubexp = 0;
791     res = trex_matchnode(exp,exp->_nodes,text,NULL);
792     if(res == NULL || res != exp->_eol)
793         return TRex_False;
794     return TRex_True;
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
799     const TRexChar *cur = NULL;
800     int node = exp->_first;
801     if(text_begin >= text_end) return TRex_False;
802     exp->_bol = text_begin;
803     exp->_eol = text_end;
804     do {
805         cur = text_begin;
806         while(node != -1) {
807             exp->_currsubexp = 0;
808             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809             if(!cur)
810                 break;
811             node = exp->_nodes[node].next;
812         }
813         *text_begin++;
814     } while(cur == NULL && text_begin != text_end);
816     if(cur == NULL)
817         return TRex_False;
819     --text_begin;
821     if(out_begin) *out_begin = text_begin;
822     if(out_end) *out_end = cur;
823     return TRex_True;
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
828     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
831 int trex_getsubexpcount(TRex* exp)
833     return exp->_nsubexpr;
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
838     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839     *subexp = exp->_matches[n];
840     return TRex_True;
844 //########################################################################
845 //########################################################################
846 //##  E N D    R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //##  X M L
857 //########################################################################
858 //########################################################################
860 // Note:  This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
869 public:
870     Namespace()
871         {}
873     Namespace(const String &prefixArg, const String &namespaceURIArg)
874         {
875         prefix       = prefixArg;
876         namespaceURI = namespaceURIArg;
877         }
879     Namespace(const Namespace &other)
880         {
881         assign(other);
882         }
884     Namespace &operator=(const Namespace &other)
885         {
886         assign(other);
887         return *this;
888         }
890     virtual ~Namespace()
891         {}
893     virtual String getPrefix()
894         { return prefix; }
896     virtual String getNamespaceURI()
897         { return namespaceURI; }
899 protected:
901     void assign(const Namespace &other)
902         {
903         prefix       = other.prefix;
904         namespaceURI = other.namespaceURI;
905         }
907     String prefix;
908     String namespaceURI;
910 };
912 class Attribute
914 public:
915     Attribute()
916         {}
918     Attribute(const String &nameArg, const String &valueArg)
919         {
920         name  = nameArg;
921         value = valueArg;
922         }
924     Attribute(const Attribute &other)
925         {
926         assign(other);
927         }
929     Attribute &operator=(const Attribute &other)
930         {
931         assign(other);
932         return *this;
933         }
935     virtual ~Attribute()
936         {}
938     virtual String getName()
939         { return name; }
941     virtual String getValue()
942         { return value; }
944 protected:
946     void assign(const Attribute &other)
947         {
948         name  = other.name;
949         value = other.value;
950         }
952     String name;
953     String value;
955 };
958 class Element
960 friend class Parser;
962 public:
963     Element()
964         {
965         init();
966         }
968     Element(const String &nameArg)
969         {
970         init();
971         name   = nameArg;
972         }
974     Element(const String &nameArg, const String &valueArg)
975         {
976         init();
977         name   = nameArg;
978         value  = valueArg;
979         }
981     Element(const Element &other)
982         {
983         assign(other);
984         }
986     Element &operator=(const Element &other)
987         {
988         assign(other);
989         return *this;
990         }
992     virtual Element *clone();
994     virtual ~Element()
995         {
996         for (unsigned int i=0 ; i<children.size() ; i++)
997             delete children[i];
998         }
1000     virtual String getName()
1001         { return name; }
1003     virtual String getValue()
1004         { return value; }
1006     Element *getParent()
1007         { return parent; }
1009     std::vector<Element *> getChildren()
1010         { return children; }
1012     std::vector<Element *> findElements(const String &name);
1014     String getAttribute(const String &name);
1016     std::vector<Attribute> &getAttributes()
1017         { return attributes; } 
1019     String getTagAttribute(const String &tagName, const String &attrName);
1021     String getTagValue(const String &tagName);
1023     void addChild(Element *child);
1025     void addAttribute(const String &name, const String &value);
1027     void addNamespace(const String &prefix, const String &namespaceURI);
1030     /**
1031      * Prettyprint an XML tree to an output stream.  Elements are indented
1032      * according to element hierarchy.
1033      * @param f a stream to receive the output
1034      * @param elem the element to output
1035      */
1036     void writeIndented(FILE *f);
1038     /**
1039      * Prettyprint an XML tree to standard output.  This is the equivalent of
1040      * writeIndented(stdout).
1041      * @param elem the element to output
1042      */
1043     void print();
1044     
1045     int getLine()
1046         { return line; }
1048 protected:
1050     void init()
1051         {
1052         parent = NULL;
1053         line   = 0;
1054         }
1056     void assign(const Element &other)
1057         {
1058         parent     = other.parent;
1059         children   = other.children;
1060         attributes = other.attributes;
1061         namespaces = other.namespaces;
1062         name       = other.name;
1063         value      = other.value;
1064         line       = other.line;
1065         }
1067     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069     void writeIndentedRecursive(FILE *f, int indent);
1071     Element *parent;
1073     std::vector<Element *>children;
1075     std::vector<Attribute> attributes;
1076     std::vector<Namespace> namespaces;
1078     String name;
1079     String value;
1080     
1081     int line;
1082 };
1088 class Parser
1090 public:
1091     /**
1092      * Constructor
1093      */
1094     Parser()
1095         { init(); }
1097     virtual ~Parser()
1098         {}
1100     /**
1101      * Parse XML in a char buffer.
1102      * @param buf a character buffer to parse
1103      * @param pos position to start parsing
1104      * @param len number of chars, from pos, to parse.
1105      * @return a pointer to the root of the XML document;
1106      */
1107     Element *parse(const char *buf,int pos,int len);
1109     /**
1110      * Parse XML in a char buffer.
1111      * @param buf a character buffer to parse
1112      * @param pos position to start parsing
1113      * @param len number of chars, from pos, to parse.
1114      * @return a pointer to the root of the XML document;
1115      */
1116     Element *parse(const String &buf);
1118     /**
1119      * Parse a named XML file.  The file is loaded like a data file;
1120      * the original format is not preserved.
1121      * @param fileName the name of the file to read
1122      * @return a pointer to the root of the XML document;
1123      */
1124     Element *parseFile(const String &fileName);
1126     /**
1127      * Utility method to preprocess a string for XML
1128      * output, escaping its entities.
1129      * @param str the string to encode
1130      */
1131     static String encode(const String &str);
1133     /**
1134      *  Removes whitespace from beginning and end of a string
1135      */
1136     String trim(const String &s);
1138 private:
1140     void init()
1141         {
1142         keepGoing       = true;
1143         currentNode     = NULL;
1144         parselen        = 0;
1145         parsebuf        = NULL;
1146         currentPosition = 0;
1147         }
1149     int countLines(int begin, int end);
1151     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153     void error(const char *fmt, ...);
1155     int peek(int pos);
1157     int match(int pos, const char *text);
1159     int skipwhite(int p);
1161     int getWord(int p0, String &buf);
1163     int getQuoted(int p0, String &buf, int do_i_parse);
1165     int parseVersion(int p0);
1167     int parseDoctype(int p0);
1169     int parseElement(int p0, Element *par,int depth);
1171     Element *parse(XMLCh *buf,int pos,int len);
1173     bool       keepGoing;
1174     Element    *currentNode;
1175     int        parselen;
1176     XMLCh      *parsebuf;
1177     String     cdatabuf;
1178     int        currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1190     Element *elem = new Element(name, value);
1191     elem->parent     = parent;
1192     elem->attributes = attributes;
1193     elem->namespaces = namespaces;
1194     elem->line       = line;
1196     std::vector<Element *>::iterator iter;
1197     for (iter = children.begin(); iter != children.end() ; iter++)
1198         {
1199         elem->addChild((*iter)->clone());
1200         }
1201     return elem;
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1207     if (getName() == name)
1208         {
1209         res.push_back(this);
1210         }
1211     for (unsigned int i=0; i<children.size() ; i++)
1212         children[i]->findElementsRecursive(res, name);
1215 std::vector<Element *> Element::findElements(const String &name)
1217     std::vector<Element *> res;
1218     findElementsRecursive(res, name);
1219     return res;
1222 String Element::getAttribute(const String &name)
1224     for (unsigned int i=0 ; i<attributes.size() ; i++)
1225         if (attributes[i].getName() ==name)
1226             return attributes[i].getValue();
1227     return "";
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1232     std::vector<Element *>elems = findElements(tagName);
1233     if (elems.size() <1)
1234         return "";
1235     String res = elems[0]->getAttribute(attrName);
1236     return res;
1239 String Element::getTagValue(const String &tagName)
1241     std::vector<Element *>elems = findElements(tagName);
1242     if (elems.size() <1)
1243         return "";
1244     String res = elems[0]->getValue();
1245     return res;
1248 void Element::addChild(Element *child)
1250     if (!child)
1251         return;
1252     child->parent = this;
1253     children.push_back(child);
1257 void Element::addAttribute(const String &name, const String &value)
1259     Attribute attr(name, value);
1260     attributes.push_back(attr);
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1265     Namespace ns(prefix, namespaceURI);
1266     namespaces.push_back(ns);
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1271     int i;
1272     if (!f)
1273         return;
1274     //Opening tag, and attributes
1275     for (i=0;i<indent;i++)
1276         fputc(' ',f);
1277     fprintf(f,"<%s",name.c_str());
1278     for (unsigned int i=0 ; i<attributes.size() ; i++)
1279         {
1280         fprintf(f," %s=\"%s\"",
1281               attributes[i].getName().c_str(),
1282               attributes[i].getValue().c_str());
1283         }
1284     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285         {
1286         fprintf(f," xmlns:%s=\"%s\"",
1287               namespaces[i].getPrefix().c_str(),
1288               namespaces[i].getNamespaceURI().c_str());
1289         }
1290     fprintf(f,">\n");
1292     //Between the tags
1293     if (value.size() > 0)
1294         {
1295         for (int i=0;i<indent;i++)
1296             fputc(' ', f);
1297         fprintf(f," %s\n", value.c_str());
1298         }
1300     for (unsigned int i=0 ; i<children.size() ; i++)
1301         children[i]->writeIndentedRecursive(f, indent+2);
1303     //Closing tag
1304     for (int i=0; i<indent; i++)
1305         fputc(' ',f);
1306     fprintf(f,"</%s>\n", name.c_str());
1309 void Element::writeIndented(FILE *f)
1311     writeIndentedRecursive(f, 0);
1314 void Element::print()
1316     writeIndented(stdout);
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327     {
1328     const char *escaped;
1329     char value;
1330     } EntityEntry;
1332 static EntityEntry entities[] =
1334     { "&amp;" , '&'  },
1335     { "&lt;"  , '<'  },
1336     { "&gt;"  , '>'  },
1337     { "&apos;", '\'' },
1338     { "&quot;", '"'  },
1339     { NULL    , '\0' }
1340 };
1344 /**
1345  *  Removes whitespace from beginning and end of a string
1346  */
1347 String Parser::trim(const String &s)
1349     if (s.size() < 1)
1350         return s;
1351     
1352     //Find first non-ws char
1353     unsigned int begin = 0;
1354     for ( ; begin < s.size() ; begin++)
1355         {
1356         if (!isspace(s[begin]))
1357             break;
1358         }
1360     //Find first non-ws char, going in reverse
1361     unsigned int end = s.size() - 1;
1362     for ( ; end > begin ; end--)
1363         {
1364         if (!isspace(s[end]))
1365             break;
1366         }
1367     //trace("begin:%d  end:%d", begin, end);
1369     String res = s.substr(begin, end-begin+1);
1370     return res;
1374 int Parser::countLines(int begin, int end)
1376     int count = 0;
1377     for (int i=begin ; i<end ; i++)
1378         {
1379         XMLCh ch = parsebuf[i];
1380         if (ch == '\n' || ch == '\r')
1381             count++;
1382         }
1383     return count;
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1389     int line = 1;
1390     int col  = 1;
1391     for (long i=0 ; i<pos ; i++)
1392         {
1393         XMLCh ch = parsebuf[i];
1394         if (ch == '\n' || ch == '\r')
1395             {
1396             col = 0;
1397             line ++;
1398             }
1399         else
1400             col++;
1401         }
1402     *lineNr = line;
1403     *colNr  = col;
1408 void Parser::error(const char *fmt, ...)
1410     int lineNr;
1411     int colNr;
1412     getLineAndColumn(currentPosition, &lineNr, &colNr);
1413     va_list args;
1414     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415     va_start(args,fmt);
1416     vfprintf(stderr,fmt,args);
1417     va_end(args) ;
1418     fprintf(stderr, "\n");
1423 int Parser::peek(int pos)
1425     if (pos >= parselen)
1426         return -1;
1427     currentPosition = pos;
1428     int ch = parsebuf[pos];
1429     //printf("ch:%c\n", ch);
1430     return ch;
1435 String Parser::encode(const String &str)
1437     String ret;
1438     for (unsigned int i=0 ; i<str.size() ; i++)
1439         {
1440         XMLCh ch = (XMLCh)str[i];
1441         if (ch == '&')
1442             ret.append("&amp;");
1443         else if (ch == '<')
1444             ret.append("&lt;");
1445         else if (ch == '>')
1446             ret.append("&gt;");
1447         else if (ch == '\'')
1448             ret.append("&apos;");
1449         else if (ch == '"')
1450             ret.append("&quot;");
1451         else
1452             ret.push_back(ch);
1454         }
1455     return ret;
1459 int Parser::match(int p0, const char *text)
1461     int p = p0;
1462     while (*text)
1463         {
1464         if (peek(p) != *text)
1465             return p0;
1466         p++; text++;
1467         }
1468     return p;
1473 int Parser::skipwhite(int p)
1476     while (p<parselen)
1477         {
1478         int p2 = match(p, "<!--");
1479         if (p2 > p)
1480             {
1481             p = p2;
1482             while (p<parselen)
1483               {
1484               p2 = match(p, "-->");
1485               if (p2 > p)
1486                   {
1487                   p = p2;
1488                   break;
1489                   }
1490               p++;
1491               }
1492           }
1493       XMLCh b = peek(p);
1494       if (!isspace(b))
1495           break;
1496       p++;
1497       }
1498   return p;
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1504     int p = p0;
1505     while (p<parselen)
1506         {
1507         XMLCh b = peek(p);
1508         if (b<=' ' || b=='/' || b=='>' || b=='=')
1509             break;
1510         buf.push_back(b);
1511         p++;
1512         }
1513     return p;
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1519     int p = p0;
1520     if (peek(p) != '"' && peek(p) != '\'')
1521         return p0;
1522     p++;
1524     while ( p<parselen )
1525         {
1526         XMLCh b = peek(p);
1527         if (b=='"' || b=='\'')
1528             break;
1529         if (b=='&' && do_i_parse)
1530             {
1531             bool found = false;
1532             for (EntityEntry *ee = entities ; ee->value ; ee++)
1533                 {
1534                 int p2 = match(p, ee->escaped);
1535                 if (p2>p)
1536                     {
1537                     buf.push_back(ee->value);
1538                     p = p2;
1539                     found = true;
1540                     break;
1541                     }
1542                 }
1543             if (!found)
1544                 {
1545                 error("unterminated entity");
1546                 return false;
1547                 }
1548             }
1549         else
1550             {
1551             buf.push_back(b);
1552             p++;
1553             }
1554         }
1555     return p;
1558 int Parser::parseVersion(int p0)
1560     //printf("### parseVersion: %d\n", p0);
1562     int p = p0;
1564     p = skipwhite(p0);
1566     if (peek(p) != '<')
1567         return p0;
1569     p++;
1570     if (p>=parselen || peek(p)!='?')
1571         return p0;
1573     p++;
1575     String buf;
1577     while (p<parselen)
1578         {
1579         XMLCh ch = peek(p);
1580         if (ch=='?')
1581             {
1582             p++;
1583             break;
1584             }
1585         buf.push_back(ch);
1586         p++;
1587         }
1589     if (peek(p) != '>')
1590         return p0;
1591     p++;
1593     //printf("Got version:%s\n",buf.c_str());
1594     return p;
1597 int Parser::parseDoctype(int p0)
1599     //printf("### parseDoctype: %d\n", p0);
1601     int p = p0;
1602     p = skipwhite(p);
1604     if (p>=parselen || peek(p)!='<')
1605         return p0;
1607     p++;
1609     if (peek(p)!='!' || peek(p+1)=='-')
1610         return p0;
1611     p++;
1613     String buf;
1614     while (p<parselen)
1615         {
1616         XMLCh ch = peek(p);
1617         if (ch=='>')
1618             {
1619             p++;
1620             break;
1621             }
1622         buf.push_back(ch);
1623         p++;
1624         }
1626     //printf("Got doctype:%s\n",buf.c_str());
1627     return p;
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1635     int p = p0;
1637     int p2 = p;
1639     p = skipwhite(p);
1641     //## Get open tag
1642     XMLCh ch = peek(p);
1643     if (ch!='<')
1644         return p0;
1646     //int line, col;
1647     //getLineAndColumn(p, &line, &col);
1649     p++;
1651     String openTagName;
1652     p = skipwhite(p);
1653     p = getWord(p, openTagName);
1654     //printf("####tag :%s\n", openTagName.c_str());
1655     p = skipwhite(p);
1657     //Add element to tree
1658     Element *n = new Element(openTagName);
1659     n->line = lineNr + countLines(p0, p);
1660     n->parent = par;
1661     par->addChild(n);
1663     // Get attributes
1664     if (peek(p) != '>')
1665         {
1666         while (p<parselen)
1667             {
1668             p = skipwhite(p);
1669             ch = peek(p);
1670             //printf("ch:%c\n",ch);
1671             if (ch=='>')
1672                 break;
1673             else if (ch=='/' && p<parselen+1)
1674                 {
1675                 p++;
1676                 p = skipwhite(p);
1677                 ch = peek(p);
1678                 if (ch=='>')
1679                     {
1680                     p++;
1681                     //printf("quick close\n");
1682                     return p;
1683                     }
1684                 }
1685             String attrName;
1686             p2 = getWord(p, attrName);
1687             if (p2==p)
1688                 break;
1689             //printf("name:%s",buf);
1690             p=p2;
1691             p = skipwhite(p);
1692             ch = peek(p);
1693             //printf("ch:%c\n",ch);
1694             if (ch!='=')
1695                 break;
1696             p++;
1697             p = skipwhite(p);
1698             // ch = parsebuf[p];
1699             // printf("ch:%c\n",ch);
1700             String attrVal;
1701             p2 = getQuoted(p, attrVal, true);
1702             p=p2+1;
1703             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704             char *namestr = (char *)attrName.c_str();
1705             if (strncmp(namestr, "xmlns:", 6)==0)
1706                 n->addNamespace(attrName, attrVal);
1707             else
1708                 n->addAttribute(attrName, attrVal);
1709             }
1710         }
1712     bool cdata = false;
1714     p++;
1715     // ### Get intervening data ### */
1716     String data;
1717     while (p<parselen)
1718         {
1719         //# COMMENT
1720         p2 = match(p, "<!--");
1721         if (!cdata && p2>p)
1722             {
1723             p = p2;
1724             while (p<parselen)
1725                 {
1726                 p2 = match(p, "-->");
1727                 if (p2 > p)
1728                     {
1729                     p = p2;
1730                     break;
1731                     }
1732                 p++;
1733                 }
1734             }
1736         ch = peek(p);
1737         //# END TAG
1738         if (ch=='<' && !cdata && peek(p+1)=='/')
1739             {
1740             break;
1741             }
1742         //# CDATA
1743         p2 = match(p, "<![CDATA[");
1744         if (p2 > p)
1745             {
1746             cdata = true;
1747             p = p2;
1748             continue;
1749             }
1751         //# CHILD ELEMENT
1752         if (ch == '<')
1753             {
1754             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755             if (p2 == p)
1756                 {
1757                 /*
1758                 printf("problem on element:%s.  p2:%d p:%d\n",
1759                       openTagName.c_str(), p2, p);
1760                 */
1761                 return p0;
1762                 }
1763             p = p2;
1764             continue;
1765             }
1766         //# ENTITY
1767         if (ch=='&' && !cdata)
1768             {
1769             bool found = false;
1770             for (EntityEntry *ee = entities ; ee->value ; ee++)
1771                 {
1772                 int p2 = match(p, ee->escaped);
1773                 if (p2>p)
1774                     {
1775                     data.push_back(ee->value);
1776                     p = p2;
1777                     found = true;
1778                     break;
1779                     }
1780                 }
1781             if (!found)
1782                 {
1783                 error("unterminated entity");
1784                 return -1;
1785                 }
1786             continue;
1787             }
1789         //# NONE OF THE ABOVE
1790         data.push_back(ch);
1791         p++;
1792         }/*while*/
1795     n->value = data;
1796     //printf("%d : data:%s\n",p,data.c_str());
1798     //## Get close tag
1799     p = skipwhite(p);
1800     ch = peek(p);
1801     if (ch != '<')
1802         {
1803         error("no < for end tag\n");
1804         return p0;
1805         }
1806     p++;
1807     ch = peek(p);
1808     if (ch != '/')
1809         {
1810         error("no / on end tag");
1811         return p0;
1812         }
1813     p++;
1814     ch = peek(p);
1815     p = skipwhite(p);
1816     String closeTagName;
1817     p = getWord(p, closeTagName);
1818     if (openTagName != closeTagName)
1819         {
1820         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1821                 openTagName.c_str(), closeTagName.c_str());
1822         return p0;
1823         }
1824     p = skipwhite(p);
1825     if (peek(p) != '>')
1826         {
1827         error("no > on end tag for '%s'", closeTagName.c_str());
1828         return p0;
1829         }
1830     p++;
1831     // printf("close element:%s\n",closeTagName.c_str());
1832     p = skipwhite(p);
1833     return p;
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1841     parselen = len;
1842     parsebuf = buf;
1843     Element *rootNode = new Element("root");
1844     pos = parseVersion(pos);
1845     pos = parseDoctype(pos);
1846     pos = parseElement(pos, rootNode, 1);
1847     return rootNode;
1851 Element *Parser::parse(const char *buf, int pos, int len)
1853     XMLCh *charbuf = new XMLCh[len + 1];
1854     long i = 0;
1855     for ( ; i < len ; i++)
1856         charbuf[i] = (XMLCh)buf[i];
1857     charbuf[i] = '\0';
1859     Element *n = parse(charbuf, pos, len);
1860     delete[] charbuf;
1861     return n;
1864 Element *Parser::parse(const String &buf)
1866     long len = (long)buf.size();
1867     XMLCh *charbuf = new XMLCh[len + 1];
1868     long i = 0;
1869     for ( ; i < len ; i++)
1870         charbuf[i] = (XMLCh)buf[i];
1871     charbuf[i] = '\0';
1873     Element *n = parse(charbuf, 0, len);
1874     delete[] charbuf;
1875     return n;
1878 Element *Parser::parseFile(const String &fileName)
1881     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882     FILE *f = fopen(fileName.c_str(), "rb");
1883     if (!f)
1884         return NULL;
1886     struct stat  statBuf;
1887     if (fstat(fileno(f),&statBuf)<0)
1888         {
1889         fclose(f);
1890         return NULL;
1891         }
1892     long filelen = statBuf.st_size;
1894     //printf("length:%d\n",filelen);
1895     XMLCh *charbuf = new XMLCh[filelen + 1];
1896     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897         {
1898         *p = (XMLCh)fgetc(f);
1899         }
1900     fclose(f);
1901     charbuf[filelen] = '\0';
1904     /*
1905     printf("nrbytes:%d\n",wc_count);
1906     printf("buf:%ls\n======\n",charbuf);
1907     */
1908     Element *n = parse(charbuf, 0, filelen);
1909     delete[] charbuf;
1910     return n;
1913 //########################################################################
1914 //########################################################################
1915 //##  E N D    X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //##  U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934  *  A class that implements the W3C URI resource reference.
1935  */
1936 class URI
1938 public:
1940     typedef enum
1941         {
1942         SCHEME_NONE =0,
1943         SCHEME_DATA,
1944         SCHEME_HTTP,
1945         SCHEME_HTTPS,
1946         SCHEME_FTP,
1947         SCHEME_FILE,
1948         SCHEME_LDAP,
1949         SCHEME_MAILTO,
1950         SCHEME_NEWS,
1951         SCHEME_TELNET
1952         } SchemeTypes;
1954     /**
1955      *
1956      */
1957     URI()
1958         {
1959         init();
1960         }
1962     /**
1963      *
1964      */
1965     URI(const String &str)
1966         {
1967         init();
1968         parse(str);
1969         }
1972     /**
1973      *
1974      */
1975     URI(const char *str)
1976         {
1977         init();
1978         String domStr = str;
1979         parse(domStr);
1980         }
1983     /**
1984      *
1985      */
1986     URI(const URI &other)
1987         {
1988         init();
1989         assign(other);
1990         }
1993     /**
1994      *
1995      */
1996     URI &operator=(const URI &other)
1997         {
1998         init();
1999         assign(other);
2000         return *this;
2001         }
2004     /**
2005      *
2006      */
2007     virtual ~URI()
2008         {}
2012     /**
2013      *
2014      */
2015     virtual bool parse(const String &str);
2017     /**
2018      *
2019      */
2020     virtual String toString() const;
2022     /**
2023      *
2024      */
2025     virtual int getScheme() const;
2027     /**
2028      *
2029      */
2030     virtual String getSchemeStr() const;
2032     /**
2033      *
2034      */
2035     virtual String getAuthority() const;
2037     /**
2038      *  Same as getAuthority, but if the port has been specified
2039      *  as host:port , the port will not be included
2040      */
2041     virtual String getHost() const;
2043     /**
2044      *
2045      */
2046     virtual int getPort() const;
2048     /**
2049      *
2050      */
2051     virtual String getPath() const;
2053     /**
2054      *
2055      */
2056     virtual String getNativePath() const;
2058     /**
2059      *
2060      */
2061     virtual bool isAbsolute() const;
2063     /**
2064      *
2065      */
2066     virtual bool isOpaque() const;
2068     /**
2069      *
2070      */
2071     virtual String getQuery() const;
2073     /**
2074      *
2075      */
2076     virtual String getFragment() const;
2078     /**
2079      *
2080      */
2081     virtual URI resolve(const URI &other) const;
2083     /**
2084      *
2085      */
2086     virtual void normalize();
2088 private:
2090     /**
2091      *
2092      */
2093     void init()
2094         {
2095         parsebuf  = NULL;
2096         parselen  = 0;
2097         scheme    = SCHEME_NONE;
2098         schemeStr = "";
2099         port      = 0;
2100         authority = "";
2101         path      = "";
2102         absolute  = false;
2103         opaque    = false;
2104         query     = "";
2105         fragment  = "";
2106         }
2109     /**
2110      *
2111      */
2112     void assign(const URI &other)
2113         {
2114         scheme    = other.scheme;
2115         schemeStr = other.schemeStr;
2116         authority = other.authority;
2117         port      = other.port;
2118         path      = other.path;
2119         absolute  = other.absolute;
2120         opaque    = other.opaque;
2121         query     = other.query;
2122         fragment  = other.fragment;
2123         }
2125     int scheme;
2127     String schemeStr;
2129     String authority;
2131     bool portSpecified;
2133     int port;
2135     String path;
2137     bool absolute;
2139     bool opaque;
2141     String query;
2143     String fragment;
2145     void error(const char *fmt, ...);
2147     void trace(const char *fmt, ...);
2150     int peek(int p);
2152     int match(int p, const char *key);
2154     int parseScheme(int p);
2156     int parseHierarchicalPart(int p0);
2158     int parseQuery(int p0);
2160     int parseFragment(int p0);
2162     int parse(int p);
2164     char *parsebuf;
2166     int parselen;
2168 };
2172 typedef struct
2174     int         ival;
2175     const char *sval;
2176     int         port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2181     { URI::SCHEME_DATA,   "data:",    0 },
2182     { URI::SCHEME_HTTP,   "http:",   80 },
2183     { URI::SCHEME_HTTPS,  "https:", 443 },
2184     { URI::SCHEME_FTP,    "ftp",     12 },
2185     { URI::SCHEME_FILE,   "file:",    0 },
2186     { URI::SCHEME_LDAP,   "ldap:",  123 },
2187     { URI::SCHEME_MAILTO, "mailto:", 25 },
2188     { URI::SCHEME_NEWS,   "news:",  117 },
2189     { URI::SCHEME_TELNET, "telnet:", 23 },
2190     { 0,                  NULL,       0 }
2191 };
2194 String URI::toString() const
2196     String str = schemeStr;
2197     if (authority.size() > 0)
2198         {
2199         str.append("//");
2200         str.append(authority);
2201         }
2202     str.append(path);
2203     if (query.size() > 0)
2204         {
2205         str.append("?");
2206         str.append(query);
2207         }
2208     if (fragment.size() > 0)
2209         {
2210         str.append("#");
2211         str.append(fragment);
2212         }
2213     return str;
2217 int URI::getScheme() const
2219     return scheme;
2222 String URI::getSchemeStr() const
2224     return schemeStr;
2228 String URI::getAuthority() const
2230     String ret = authority;
2231     if (portSpecified && port>=0)
2232         {
2233         char buf[7];
2234         snprintf(buf, 6, ":%6d", port);
2235         ret.append(buf);
2236         }
2237     return ret;
2240 String URI::getHost() const
2242     return authority;
2245 int URI::getPort() const
2247     return port;
2251 String URI::getPath() const
2253     return path;
2256 String URI::getNativePath() const
2258     String npath;
2259 #ifdef __WIN32__
2260     unsigned int firstChar = 0;
2261     if (path.size() >= 3)
2262         {
2263         if (path[0] == '/' &&
2264             isLetter(path[1]) &&
2265             path[2] == ':')
2266             firstChar++;
2267          }
2268     for (unsigned int i=firstChar ; i<path.size() ; i++)
2269         {
2270         XMLCh ch = (XMLCh) path[i];
2271         if (ch == '/')
2272             npath.push_back((XMLCh)'\\');
2273         else
2274             npath.push_back(ch);
2275         }
2276 #else
2277     npath = path;
2278 #endif
2279     return npath;
2283 bool URI::isAbsolute() const
2285     return absolute;
2288 bool URI::isOpaque() const
2290     return opaque;
2294 String URI::getQuery() const
2296     return query;
2300 String URI::getFragment() const
2302     return fragment;
2306 URI URI::resolve(const URI &other) const
2308     //### According to w3c, this is handled in 3 cases
2310     //## 1
2311     if (opaque || other.isAbsolute())
2312         return other;
2314     //## 2
2315     if (other.fragment.size()  >  0 &&
2316         other.path.size()      == 0 &&
2317         other.scheme           == SCHEME_NONE &&
2318         other.authority.size() == 0 &&
2319         other.query.size()     == 0 )
2320         {
2321         URI fragUri = *this;
2322         fragUri.fragment = other.fragment;
2323         return fragUri;
2324         }
2326     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327     URI newUri;
2328     //# 3.1
2329     newUri.scheme    = scheme;
2330     newUri.schemeStr = schemeStr;
2331     newUri.query     = other.query;
2332     newUri.fragment  = other.fragment;
2333     if (other.authority.size() > 0)
2334         {
2335         //# 3.2
2336         if (absolute || other.absolute)
2337             newUri.absolute = true;
2338         newUri.authority = other.authority;
2339         newUri.port      = other.port;//part of authority
2340         newUri.path      = other.path;
2341         }
2342     else
2343         {
2344         //# 3.3
2345         if (other.absolute)
2346             {
2347             newUri.absolute = true;
2348             newUri.path     = other.path;
2349             }
2350         else
2351             {
2352             unsigned int pos = path.find_last_of('/');
2353             if (pos != path.npos)
2354                 {
2355                 String tpath = path.substr(0, pos+1);
2356                 tpath.append(other.path);
2357                 newUri.path = tpath;
2358                 }
2359             else
2360                 newUri.path = other.path;
2361             }
2362         }
2364     newUri.normalize();
2365     return newUri;
2370 /**
2371  *  This follows the Java URI algorithm:
2372  *   1. All "." segments are removed.
2373  *   2. If a ".." segment is preceded by a non-".." segment
2374  *          then both of these segments are removed. This step
2375  *          is repeated until it is no longer applicable.
2376  *   3. If the path is relative, and if its first segment
2377  *          contains a colon character (':'), then a "." segment
2378  *          is prepended. This prevents a relative URI with a path
2379  *          such as "a:b/c/d" from later being re-parsed as an
2380  *          opaque URI with a scheme of "a" and a scheme-specific
2381  *          part of "b/c/d". (Deviation from RFC 2396)
2382  */
2383 void URI::normalize()
2385     std::vector<String> segments;
2387     //## Collect segments
2388     if (path.size()<2)
2389         return;
2390     bool abs = false;
2391     unsigned int pos=0;
2392     if (path[0]=='/')
2393         {
2394         abs = true;
2395         pos++;
2396         }
2397     while (pos < path.size())
2398         {
2399         unsigned int pos2 = path.find('/', pos);
2400         if (pos2==path.npos)
2401             {
2402             String seg = path.substr(pos);
2403             //printf("last segment:%s\n", seg.c_str());
2404             segments.push_back(seg);
2405             break;
2406             }
2407         if (pos2>pos)
2408             {
2409             String seg = path.substr(pos, pos2-pos);
2410             //printf("segment:%s\n", seg.c_str());
2411             segments.push_back(seg);
2412             }
2413         pos = pos2;
2414         pos++;
2415         }
2417     //## Clean up (normalize) segments
2418     bool edited = false;
2419     std::vector<String>::iterator iter;
2420     for (iter=segments.begin() ; iter!=segments.end() ; )
2421         {
2422         String s = *iter;
2423         if (s == ".")
2424             {
2425             iter = segments.erase(iter);
2426             edited = true;
2427             }
2428         else if (s == ".." &&
2429                  iter != segments.begin() &&
2430                  *(iter-1) != "..")
2431             {
2432             iter--; //back up, then erase two entries
2433             iter = segments.erase(iter);
2434             iter = segments.erase(iter);
2435             edited = true;
2436             }
2437         else
2438             iter++;
2439         }
2441     //## Rebuild path, if necessary
2442     if (edited)
2443         {
2444         path.clear();
2445         if (abs)
2446             {
2447             path.append("/");
2448             }
2449         std::vector<String>::iterator iter;
2450         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451             {
2452             if (iter != segments.begin())
2453                 path.append("/");
2454             path.append(*iter);
2455             }
2456         }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2468     va_list args;
2469     fprintf(stderr, "URI error: ");
2470     va_start(args, fmt);
2471     vfprintf(stderr, fmt, args);
2472     va_end(args);
2473     fprintf(stderr, "\n");
2476 void URI::trace(const char *fmt, ...)
2478     va_list args;
2479     fprintf(stdout, "URI: ");
2480     va_start(args, fmt);
2481     vfprintf(stdout, fmt, args);
2482     va_end(args);
2483     fprintf(stdout, "\n");
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2497     if (p<0 || p>=parselen)
2498         return -1;
2499     return parsebuf[p];
2504 int URI::match(int p0, const char *key)
2506     int p = p0;
2507     while (p < parselen)
2508         {
2509         if (*key == '\0')
2510             return p;
2511         else if (*key != parsebuf[p])
2512             break;
2513         p++; key++;
2514         }
2515     return p0;
2518 //#########################################################################
2519 //#  Parsing is performed according to:
2520 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2525     int p = p0;
2526     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527         {
2528         int p2 = match(p, entry->sval);
2529         if (p2 > p)
2530             {
2531             schemeStr = entry->sval;
2532             scheme    = entry->ival;
2533             port      = entry->port;
2534             p = p2;
2535             return p;
2536             }
2537         }
2539     return p;
2543 int URI::parseHierarchicalPart(int p0)
2545     int p = p0;
2546     int ch;
2548     //# Authority field (host and port, for example)
2549     int p2 = match(p, "//");
2550     if (p2 > p)
2551         {
2552         p = p2;
2553         portSpecified = false;
2554         String portStr;
2555         while (p < parselen)
2556             {
2557             ch = peek(p);
2558             if (ch == '/')
2559                 break;
2560             else if (ch == ':')
2561                 portSpecified = true;
2562             else if (portSpecified)
2563                 portStr.push_back((XMLCh)ch);
2564             else
2565                 authority.push_back((XMLCh)ch);
2566             p++;
2567             }
2568         if (portStr.size() > 0)
2569             {
2570             char *pstr = (char *)portStr.c_str();
2571             char *endStr;
2572             long val = strtol(pstr, &endStr, 10);
2573             if (endStr > pstr) //successful parse?
2574                 port = val;
2575             }
2576         }
2578     //# Are we absolute?
2579     ch = peek(p);
2580     if (isLetter(ch) && peek(p+1)==':')
2581         {
2582         absolute = true;
2583         path.push_back((XMLCh)'/');
2584         }
2585     else if (ch == '/')
2586         {
2587         absolute = true;
2588         if (p>p0) //in other words, if '/' is not the first char
2589             opaque = true;
2590         path.push_back((XMLCh)ch);
2591         p++;
2592         }
2594     while (p < parselen)
2595         {
2596         ch = peek(p);
2597         if (ch == '?' || ch == '#')
2598             break;
2599         path.push_back((XMLCh)ch);
2600         p++;
2601         }
2603     return p;
2606 int URI::parseQuery(int p0)
2608     int p = p0;
2609     int ch = peek(p);
2610     if (ch != '?')
2611         return p0;
2613     p++;
2614     while (p < parselen)
2615         {
2616         ch = peek(p);
2617         if (ch == '#')
2618             break;
2619         query.push_back((XMLCh)ch);
2620         p++;
2621         }
2624     return p;
2627 int URI::parseFragment(int p0)
2630     int p = p0;
2631     int ch = peek(p);
2632     if (ch != '#')
2633         return p0;
2635     p++;
2636     while (p < parselen)
2637         {
2638         ch = peek(p);
2639         if (ch == '?')
2640             break;
2641         fragment.push_back((XMLCh)ch);
2642         p++;
2643         }
2646     return p;
2650 int URI::parse(int p0)
2653     int p = p0;
2655     int p2 = parseScheme(p);
2656     if (p2 < 0)
2657         {
2658         error("Scheme");
2659         return -1;
2660         }
2661     p = p2;
2664     p2 = parseHierarchicalPart(p);
2665     if (p2 < 0)
2666         {
2667         error("Hierarchical part");
2668         return -1;
2669         }
2670     p = p2;
2672     p2 = parseQuery(p);
2673     if (p2 < 0)
2674         {
2675         error("Query");
2676         return -1;
2677         }
2678     p = p2;
2681     p2 = parseFragment(p);
2682     if (p2 < 0)
2683         {
2684         error("Fragment");
2685         return -1;
2686         }
2687     p = p2;
2689     return p;
2695 bool URI::parse(const String &str)
2697     init();
2698     
2699     parselen = str.size();
2701     String tmp;
2702     for (unsigned int i=0 ; i<str.size() ; i++)
2703         {
2704         XMLCh ch = (XMLCh) str[i];
2705         if (ch == '\\')
2706             tmp.push_back((XMLCh)'/');
2707         else
2708             tmp.push_back(ch);
2709         }
2710     parsebuf = (char *) tmp.c_str();
2713     int p = parse(0);
2714     normalize();
2716     if (p < 0)
2717         {
2718         error("Syntax error");
2719         return false;
2720         }
2722     //printf("uri:%s\n", toString().c_str());
2723     //printf("path:%s\n", path.c_str());
2725     return true;
2736 //########################################################################
2737 //########################################################################
2738 //##  M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746  * This is the descriptor for a <fileset> item
2747  */
2748 class FileSet
2750 public:
2752     /**
2753      *
2754      */
2755     FileSet()
2756         {}
2758     /**
2759      *
2760      */
2761     FileSet(const FileSet &other)
2762         { assign(other); }
2764     /**
2765      *
2766      */
2767     FileSet &operator=(const FileSet &other)
2768         { assign(other); return *this; }
2770     /**
2771      *
2772      */
2773     virtual ~FileSet()
2774         {}
2776     /**
2777      *
2778      */
2779     String getDirectory()
2780         { return directory; }
2781         
2782     /**
2783      *
2784      */
2785     void setDirectory(const String &val)
2786         { directory = val; }
2788     /**
2789      *
2790      */
2791     void setFiles(const std::vector<String> &val)
2792         { files = val; }
2794     /**
2795      *
2796      */
2797     std::vector<String> getFiles()
2798         { return files; }
2799         
2800     /**
2801      *
2802      */
2803     void setIncludes(const std::vector<String> &val)
2804         { includes = val; }
2806     /**
2807      *
2808      */
2809     std::vector<String> getIncludes()
2810         { return includes; }
2811         
2812     /**
2813      *
2814      */
2815     void setExcludes(const std::vector<String> &val)
2816         { excludes = val; }
2818     /**
2819      *
2820      */
2821     std::vector<String> getExcludes()
2822         { return excludes; }
2823         
2824     /**
2825      *
2826      */
2827     unsigned int size()
2828         { return files.size(); }
2829         
2830     /**
2831      *
2832      */
2833     String operator[](int index)
2834         { return files[index]; }
2835         
2836     /**
2837      *
2838      */
2839     void clear()
2840         {
2841         directory = "";
2842         files.clear();
2843         includes.clear();
2844         excludes.clear();
2845         }
2846         
2848 private:
2850     void assign(const FileSet &other)
2851         {
2852         directory = other.directory;
2853         files     = other.files;
2854         includes  = other.includes;
2855         excludes  = other.excludes;
2856         }
2858     String directory;
2859     std::vector<String> files;
2860     std::vector<String> includes;
2861     std::vector<String> excludes;
2862 };
2865 //########################################################################
2866 //# F I L E L I S T
2867 //########################################################################
2868 /**
2869  * This is a simpler, explicitly-named list of files
2870  */
2871 class FileList
2873 public:
2875     /**
2876      *
2877      */
2878     FileList()
2879         {}
2881     /**
2882      *
2883      */
2884     FileList(const FileList &other)
2885         { assign(other); }
2887     /**
2888      *
2889      */
2890     FileList &operator=(const FileList &other)
2891         { assign(other); return *this; }
2893     /**
2894      *
2895      */
2896     virtual ~FileList()
2897         {}
2899     /**
2900      *
2901      */
2902     String getDirectory()
2903         { return directory; }
2904         
2905     /**
2906      *
2907      */
2908     void setDirectory(const String &val)
2909         { directory = val; }
2911     /**
2912      *
2913      */
2914     void setFiles(const std::vector<String> &val)
2915         { files = val; }
2917     /**
2918      *
2919      */
2920     std::vector<String> getFiles()
2921         { return files; }
2922         
2923     /**
2924      *
2925      */
2926     unsigned int size()
2927         { return files.size(); }
2928         
2929     /**
2930      *
2931      */
2932     String operator[](int index)
2933         { return files[index]; }
2934         
2935     /**
2936      *
2937      */
2938     void clear()
2939         {
2940         directory = "";
2941         files.clear();
2942         }
2943         
2945 private:
2947     void assign(const FileList &other)
2948         {
2949         directory = other.directory;
2950         files     = other.files;
2951         }
2953     String directory;
2954     std::vector<String> files;
2955 };
2960 //########################################################################
2961 //# M A K E    B A S E
2962 //########################################################################
2963 /**
2964  * Base class for all classes in this file
2965  */
2966 class MakeBase
2968 public:
2970     MakeBase()
2971         { line = 0; }
2972     virtual ~MakeBase()
2973         {}
2975     /**
2976      *     Return the URI of the file associated with this object 
2977      */     
2978     URI getURI()
2979         { return uri; }
2981     /**
2982      * Set the uri to the given string
2983      */
2984     void setURI(const String &uristr)
2985         { uri.parse(uristr); }
2987     /**
2988      *  Resolve another path relative to this one
2989      */
2990     String resolve(const String &otherPath);
2992     /**
2993      *  Get an element attribute, performing substitutions if necessary
2994      */
2995     bool getAttribute(Element *elem, const String &name, String &result);
2997     /**
2998      * Get an element value, performing substitutions if necessary
2999      */
3000     bool getValue(Element *elem, String &result);
3001     
3002     /**
3003      * Set the current line number in the file
3004      */         
3005     void setLine(int val)
3006         { line = val; }
3007         
3008     /**
3009      * Get the current line number in the file
3010      */         
3011     int getLine()
3012         { return line; }
3015     /**
3016      * Set a property to a given value
3017      */
3018     virtual void setProperty(const String &name, const String &val)
3019         {
3020         properties[name] = val;
3021         }
3023     /**
3024      * Return a named property is found, else a null string
3025      */
3026     virtual String getProperty(const String &name)
3027         {
3028         String val;
3029         std::map<String, String>::iterator iter = properties.find(name);
3030         if (iter != properties.end())
3031             val = iter->second;
3032         return val;
3033         }
3035     /**
3036      * Return true if a named property is found, else false
3037      */
3038     virtual bool hasProperty(const String &name)
3039         {
3040         std::map<String, String>::iterator iter = properties.find(name);
3041         if (iter == properties.end())
3042             return false;
3043         return true;
3044         }
3047 protected:
3049     /**
3050      *    The path to the file associated with this object
3051      */     
3052     URI uri;
3053     
3054     /**
3055      *    If this prefix is seen in a substitution, use an environment
3056      *    variable.
3057      *             example:  <property environment="env"/>
3058      *             ${env.JAVA_HOME}              
3059      */     
3060     String envPrefix;
3065     /**
3066      *  Print a printf()-like formatted error message
3067      */
3068     void error(const char *fmt, ...);
3070     /**
3071      *  Print a printf()-like formatted trace message
3072      */
3073     void status(const char *fmt, ...);
3075     /**
3076      *  Show target status
3077      */
3078     void targetstatus(const char *fmt, ...);
3080     /**
3081      *  Print a printf()-like formatted trace message
3082      */
3083     void trace(const char *fmt, ...);
3085     /**
3086      *  Check if a given string matches a given regex pattern
3087      */
3088     bool regexMatch(const String &str, const String &pattern);
3090     /**
3091      *
3092      */
3093     String getSuffix(const String &fname);
3095     /**
3096      * Break up a string into substrings delimited the characters
3097      * in delimiters.  Null-length substrings are ignored
3098      */  
3099     std::vector<String> tokenize(const String &val,
3100                           const String &delimiters);
3102     /**
3103      *  replace runs of whitespace with a space
3104      */
3105     String strip(const String &s);
3107     /**
3108      *  remove leading whitespace from each line
3109      */
3110     String leftJustify(const String &s);
3112     /**
3113      *  remove leading and trailing whitespace from string
3114      */
3115     String trim(const String &s);
3117     /**
3118      *  Return a lower case version of the given string
3119      */
3120     String toLower(const String &s);
3122     /**
3123      * Return the native format of the canonical
3124      * path which we store
3125      */
3126     String getNativePath(const String &path);
3128     /**
3129      * Execute a shell command.  Outbuf is a ref to a string
3130      * to catch the result.     
3131      */         
3132     bool executeCommand(const String &call,
3133                         const String &inbuf,
3134                         String &outbuf,
3135                         String &errbuf);
3136     /**
3137      * List all directories in a given base and starting directory
3138      * It is usually called like:
3139      *        bool ret = listDirectories("src", "", result);    
3140      */         
3141     bool listDirectories(const String &baseName,
3142                          const String &dirname,
3143                          std::vector<String> &res);
3145     /**
3146      * Find all files in the named directory 
3147      */         
3148     bool listFiles(const String &baseName,
3149                    const String &dirname,
3150                    std::vector<String> &result);
3152     /**
3153      * Perform a listing for a fileset 
3154      */         
3155     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3157     /**
3158      * Parse a <patternset>
3159      */  
3160     bool parsePatternSet(Element *elem,
3161                        MakeBase &propRef,
3162                        std::vector<String> &includes,
3163                        std::vector<String> &excludes);
3165     /**
3166      * Parse a <fileset> entry, and determine which files
3167      * should be included
3168      */  
3169     bool parseFileSet(Element *elem,
3170                     MakeBase &propRef,
3171                     FileSet &fileSet);
3172     /**
3173      * Parse a <filelist> entry
3174      */  
3175     bool parseFileList(Element *elem,
3176                     MakeBase &propRef,
3177                     FileList &fileList);
3179     /**
3180      * Return this object's property list
3181      */
3182     virtual std::map<String, String> &getProperties()
3183         { return properties; }
3186     std::map<String, String> properties;
3188     /**
3189      * Turn 'true' and 'false' into boolean values
3190      */             
3191     bool getBool(const String &str, bool &val);
3193     /**
3194      * Create a directory, making intermediate dirs
3195      * if necessary
3196      */                  
3197     bool createDirectory(const String &dirname);
3199     /**
3200      * Delete a directory and its children if desired
3201      */
3202     bool removeDirectory(const String &dirName);
3204     /**
3205      * Copy a file from one name to another. Perform only if needed
3206      */ 
3207     bool copyFile(const String &srcFile, const String &destFile);
3209     /**
3210      * Tests if the file exists and is a regular file
3211      */ 
3212     bool isRegularFile(const String &fileName);
3214     /**
3215      * Tests if the file exists and is a directory
3216      */ 
3217     bool isDirectory(const String &fileName);
3219     /**
3220      * Tests is the modification date of fileA is newer than fileB
3221      */ 
3222     bool isNewerThan(const String &fileA, const String &fileB);
3224 private:
3226     /**
3227      * replace variable refs like ${a} with their values
3228      */         
3229     bool getSubstitutions(const String &s, String &result);
3231     int line;
3234 };
3239 /**
3240  *  Print a printf()-like formatted error message
3241  */
3242 void MakeBase::error(const char *fmt, ...)
3244     va_list args;
3245     va_start(args,fmt);
3246     fprintf(stderr, "Make error line %d: ", line);
3247     vfprintf(stderr, fmt, args);
3248     fprintf(stderr, "\n");
3249     va_end(args) ;
3254 /**
3255  *  Print a printf()-like formatted trace message
3256  */
3257 void MakeBase::status(const char *fmt, ...)
3259     va_list args;
3260     va_start(args,fmt);
3261     //fprintf(stdout, " ");
3262     vfprintf(stdout, fmt, args);
3263     fprintf(stdout, "\n");
3264     va_end(args) ;
3268 /**
3269  *  Resolve another path relative to this one
3270  */
3271 String MakeBase::resolve(const String &otherPath)
3273     URI otherURI(otherPath);
3274     URI fullURI = uri.resolve(otherURI);
3275     String ret = fullURI.toString();
3276     return ret;
3280 /**
3281  *  Print a printf()-like formatted trace message
3282  */
3283 void MakeBase::trace(const char *fmt, ...)
3285     va_list args;
3286     va_start(args,fmt);
3287     fprintf(stdout, "Make: ");
3288     vfprintf(stdout, fmt, args);
3289     fprintf(stdout, "\n");
3290     va_end(args) ;
3295 /**
3296  *  Check if a given string matches a given regex pattern
3297  */
3298 bool MakeBase::regexMatch(const String &str, const String &pattern)
3300     const TRexChar *terror = NULL;
3301     const TRexChar *cpat = pattern.c_str();
3302     TRex *expr = trex_compile(cpat, &terror);
3303     if (!expr)
3304         {
3305         if (!terror)
3306             terror = "undefined";
3307         error("compilation error [%s]!\n", terror);
3308         return false;
3309         } 
3311     bool ret = true;
3313     const TRexChar *cstr = str.c_str();
3314     if (trex_match(expr, cstr))
3315         {
3316         ret = true;
3317         }
3318     else
3319         {
3320         ret = false;
3321         }
3323     trex_free(expr);
3325     return ret;
3328 /**
3329  *  Return the suffix, if any, of a file name
3330  */
3331 String MakeBase::getSuffix(const String &fname)
3333     if (fname.size() < 2)
3334         return "";
3335     unsigned int pos = fname.find_last_of('.');
3336     if (pos == fname.npos)
3337         return "";
3338     pos++;
3339     String res = fname.substr(pos, fname.size()-pos);
3340     //trace("suffix:%s", res.c_str()); 
3341     return res;
3346 /**
3347  * Break up a string into substrings delimited the characters
3348  * in delimiters.  Null-length substrings are ignored
3349  */  
3350 std::vector<String> MakeBase::tokenize(const String &str,
3351                                 const String &delimiters)
3354     std::vector<String> res;
3355     char *del = (char *)delimiters.c_str();
3356     String dmp;
3357     for (unsigned int i=0 ; i<str.size() ; i++)
3358         {
3359         char ch = str[i];
3360         char *p = (char *)0;
3361         for (p=del ; *p ; p++)
3362             if (*p == ch)
3363                 break;
3364         if (*p)
3365             {
3366             if (dmp.size() > 0)
3367                 {
3368                 res.push_back(dmp);
3369                 dmp.clear();
3370                 }
3371             }
3372         else
3373             {
3374             dmp.push_back(ch);
3375             }
3376         }
3377     //Add tail
3378     if (dmp.size() > 0)
3379         {
3380         res.push_back(dmp);
3381         dmp.clear();
3382         }
3384     return res;
3389 /**
3390  *  replace runs of whitespace with a single space
3391  */
3392 String MakeBase::strip(const String &s)
3394     int len = s.size();
3395     String stripped;
3396     for (int i = 0 ; i<len ; i++)
3397         {
3398         char ch = s[i];
3399         if (isspace(ch))
3400             {
3401             stripped.push_back(' ');
3402             for ( ; i<len ; i++)
3403                 {
3404                 ch = s[i];
3405                 if (!isspace(ch))
3406                     {
3407                     stripped.push_back(ch);
3408                     break;
3409                     }
3410                 }
3411             }
3412         else
3413             {
3414             stripped.push_back(ch);
3415             }
3416         }
3417     return stripped;
3420 /**
3421  *  remove leading whitespace from each line
3422  */
3423 String MakeBase::leftJustify(const String &s)
3425     String out;
3426     int len = s.size();
3427     for (int i = 0 ; i<len ; )
3428         {
3429         char ch;
3430         //Skip to first visible character
3431         while (i<len)
3432             {
3433             ch = s[i];
3434             if (ch == '\n' || ch == '\r'
3435               || !isspace(ch))
3436                   break;
3437             i++;
3438             }
3439         //Copy the rest of the line
3440         while (i<len)
3441             {
3442             ch = s[i];
3443             if (ch == '\n' || ch == '\r')
3444                 {
3445                 if (ch != '\r')
3446                     out.push_back('\n');
3447                 i++;
3448                 break;
3449                 }
3450             else
3451                 {
3452                 out.push_back(ch);
3453                 }
3454             i++;
3455             }
3456         }
3457     return out;
3461 /**
3462  *  Removes whitespace from beginning and end of a string
3463  */
3464 String MakeBase::trim(const String &s)
3466     if (s.size() < 1)
3467         return s;
3468     
3469     //Find first non-ws char
3470     unsigned int begin = 0;
3471     for ( ; begin < s.size() ; begin++)
3472         {
3473         if (!isspace(s[begin]))
3474             break;
3475         }
3477     //Find first non-ws char, going in reverse
3478     unsigned int end = s.size() - 1;
3479     for ( ; end > begin ; end--)
3480         {
3481         if (!isspace(s[end]))
3482             break;
3483         }
3484     //trace("begin:%d  end:%d", begin, end);
3486     String res = s.substr(begin, end-begin+1);
3487     return res;
3491 /**
3492  *  Return a lower case version of the given string
3493  */
3494 String MakeBase::toLower(const String &s)
3496     if (s.size()==0)
3497         return s;
3499     String ret;
3500     for(unsigned int i=0; i<s.size() ; i++)
3501         {
3502         ret.push_back(tolower(s[i]));
3503         }
3504     return ret;
3508 /**
3509  * Return the native format of the canonical
3510  * path which we store
3511  */
3512 String MakeBase::getNativePath(const String &path)
3514 #ifdef __WIN32__
3515     String npath;
3516     unsigned int firstChar = 0;
3517     if (path.size() >= 3)
3518         {
3519         if (path[0] == '/' &&
3520             isalpha(path[1]) &&
3521             path[2] == ':')
3522             firstChar++;
3523         }
3524     for (unsigned int i=firstChar ; i<path.size() ; i++)
3525         {
3526         char ch = path[i];
3527         if (ch == '/')
3528             npath.push_back('\\');
3529         else
3530             npath.push_back(ch);
3531         }
3532     return npath;
3533 #else
3534     return path;
3535 #endif
3539 #ifdef __WIN32__
3540 #include <tchar.h>
3542 static String win32LastError()
3545     DWORD dw = GetLastError(); 
3547     LPVOID str;
3548     FormatMessage(
3549         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3550         FORMAT_MESSAGE_FROM_SYSTEM,
3551         NULL,
3552         dw,
3553         0,
3554         (LPTSTR) &str,
3555         0, NULL );
3556     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3557     if(p != NULL)
3558         { // lose CRLF
3559         *p = _T('\0');
3560         }
3561     String ret = (char *)str;
3562     LocalFree(str);
3564     return ret;
3566 #endif
3570 /**
3571  * Execute a system call, using pipes to send data to the
3572  * program's stdin,  and reading stdout and stderr.
3573  */
3574 bool MakeBase::executeCommand(const String &command,
3575                               const String &inbuf,
3576                               String &outbuf,
3577                               String &errbuf)
3580     status("============ cmd ============\n%s\n=============================",
3581                 command.c_str());
3583     outbuf.clear();
3584     errbuf.clear();
3585     
3586 #ifdef __WIN32__
3588     /*
3589     I really hate having win32 code in this program, but the
3590     read buffer in command.com and cmd.exe are just too small
3591     for the large commands we need for compiling and linking.
3592     */
3594     bool ret = true;
3596     //# Allocate a separate buffer for safety
3597     char *paramBuf = new char[command.size() + 1];
3598     if (!paramBuf)
3599        {
3600        error("executeCommand cannot allocate command buffer");
3601        return false;
3602        }
3603     strcpy(paramBuf, (char *)command.c_str());
3605     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3606     //# to see how Win32 pipes work
3608     //# Create pipes
3609     SECURITY_ATTRIBUTES saAttr; 
3610     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3611     saAttr.bInheritHandle = TRUE; 
3612     saAttr.lpSecurityDescriptor = NULL; 
3613     HANDLE stdinRead,  stdinWrite;
3614     HANDLE stdoutRead, stdoutWrite;
3615     HANDLE stderrRead, stderrWrite;
3616     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3617         {
3618         error("executeProgram: could not create pipe");
3619         delete[] paramBuf;
3620         return false;
3621         } 
3622     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3623     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3624         {
3625         error("executeProgram: could not create pipe");
3626         delete[] paramBuf;
3627         return false;
3628         } 
3629     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3630     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3631         {
3632         error("executeProgram: could not create pipe");
3633         delete[] paramBuf;
3634         return false;
3635         } 
3636     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3638     // Create the process
3639     STARTUPINFO siStartupInfo;
3640     PROCESS_INFORMATION piProcessInfo;
3641     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3642     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3643     siStartupInfo.cb = sizeof(siStartupInfo);
3644     siStartupInfo.hStdError   =  stderrWrite;
3645     siStartupInfo.hStdOutput  =  stdoutWrite;
3646     siStartupInfo.hStdInput   =  stdinRead;
3647     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3648    
3649     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3650                 0, NULL, NULL, &siStartupInfo,
3651                 &piProcessInfo))
3652         {
3653         error("executeCommand : could not create process : %s",
3654                     win32LastError().c_str());
3655         ret = false;
3656         }
3658     delete[] paramBuf;
3660     DWORD bytesWritten;
3661     if (inbuf.size()>0 &&
3662         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3663                &bytesWritten, NULL))
3664         {
3665         error("executeCommand: could not write to pipe");
3666         return false;
3667         }    
3668     if (!CloseHandle(stdinWrite))
3669         {          
3670         error("executeCommand: could not close write pipe");
3671         return false;
3672         }
3673     if (!CloseHandle(stdoutWrite))
3674         {
3675         error("executeCommand: could not close read pipe");
3676         return false;
3677         }
3678     if (!CloseHandle(stderrWrite))
3679         {
3680         error("executeCommand: could not close read pipe");
3681         return false;
3682         }
3684     bool lastLoop = false;
3685     while (true)
3686         {
3687         DWORD avail;
3688         DWORD bytesRead;
3689         char readBuf[4096];
3691         //trace("## stderr");
3692         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3693         if (avail > 0)
3694             {
3695             bytesRead = 0;
3696             if (avail>4096) avail = 4096;
3697             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3698             if (bytesRead > 0)
3699                 {
3700                 for (unsigned int i=0 ; i<bytesRead ; i++)
3701                     errbuf.push_back(readBuf[i]);
3702                 }
3703             }
3705         //trace("## stdout");
3706         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3707         if (avail > 0)
3708             {
3709             bytesRead = 0;
3710             if (avail>4096) avail = 4096;
3711             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3712             if (bytesRead > 0)
3713                 {
3714                 for (unsigned int i=0 ; i<bytesRead ; i++)
3715                     outbuf.push_back(readBuf[i]);
3716                 }
3717             }
3718             
3719         //Was this the final check after program done?
3720         if (lastLoop)
3721             break;
3723         DWORD exitCode;
3724         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3725         if (exitCode != STILL_ACTIVE)
3726             lastLoop = true;
3728         Sleep(10);
3729         }    
3730     //trace("outbuf:%s", outbuf.c_str());
3731     if (!CloseHandle(stdoutRead))
3732         {
3733         error("executeCommand: could not close read pipe");
3734         return false;
3735         }
3736     if (!CloseHandle(stderrRead))
3737         {
3738         error("executeCommand: could not close read pipe");
3739         return false;
3740         }
3742     DWORD exitCode;
3743     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3744     //trace("exit code:%d", exitCode);
3745     if (exitCode != 0)
3746         {
3747         ret = false;
3748         }
3749     
3750     CloseHandle(piProcessInfo.hProcess);
3751     CloseHandle(piProcessInfo.hThread);
3753     return ret;
3755 #else //do it unix-style
3757     String s;
3758     FILE *f = popen(command.c_str(), "r");
3759     int errnum = 0;
3760     if (f)
3761         {
3762         while (true)
3763             {
3764             int ch = fgetc(f);
3765             if (ch < 0)
3766                 break;
3767             s.push_back((char)ch);
3768             }
3769         errnum = pclose(f);
3770         }
3771     outbuf = s;
3772     if (errnum != 0)
3773         {
3774         error("exec of command '%s' failed : %s",
3775              command.c_str(), strerror(errno));
3776         return false;
3777         }
3778     else
3779         return true;
3781 #endif
3782
3787 bool MakeBase::listDirectories(const String &baseName,
3788                               const String &dirName,
3789                               std::vector<String> &res)
3791     res.push_back(dirName);
3792     String fullPath = baseName;
3793     if (dirName.size()>0)
3794         {
3795         fullPath.append("/");
3796         fullPath.append(dirName);
3797         }
3798     DIR *dir = opendir(fullPath.c_str());
3799     while (true)
3800         {
3801         struct dirent *de = readdir(dir);
3802         if (!de)
3803             break;
3805         //Get the directory member name
3806         String s = de->d_name;
3807         if (s.size() == 0 || s[0] == '.')
3808             continue;
3809         String childName = dirName;
3810         childName.append("/");
3811         childName.append(s);
3813         String fullChildPath = baseName;
3814         fullChildPath.append("/");
3815         fullChildPath.append(childName);
3816         struct stat finfo;
3817         String childNative = getNativePath(fullChildPath);
3818         if (stat(childNative.c_str(), &finfo)<0)
3819             {
3820             error("cannot stat file:%s", childNative.c_str());
3821             }
3822         else if (S_ISDIR(finfo.st_mode))
3823             {
3824             //trace("directory: %s", childName.c_str());
3825             if (!listDirectories(baseName, childName, res))
3826                 return false;
3827             }
3828         }
3829     closedir(dir);
3831     return true;
3835 bool MakeBase::listFiles(const String &baseDir,
3836                          const String &dirName,
3837                          std::vector<String> &res)
3839     String fullDir = baseDir;
3840     if (dirName.size()>0)
3841         {
3842         fullDir.append("/");
3843         fullDir.append(dirName);
3844         }
3845     String dirNative = getNativePath(fullDir);
3847     std::vector<String> subdirs;
3848     DIR *dir = opendir(dirNative.c_str());
3849     if (!dir)
3850         {
3851         error("Could not open directory %s : %s",
3852               dirNative.c_str(), strerror(errno));
3853         return false;
3854         }
3855     while (true)
3856         {
3857         struct dirent *de = readdir(dir);
3858         if (!de)
3859             break;
3861         //Get the directory member name
3862         String s = de->d_name;
3863         if (s.size() == 0 || s[0] == '.')
3864             continue;
3865         String childName;
3866         if (dirName.size()>0)
3867             {
3868             childName.append(dirName);
3869             childName.append("/");
3870             }
3871         childName.append(s);
3872         String fullChild = baseDir;
3873         fullChild.append("/");
3874         fullChild.append(childName);
3875         
3876         if (isDirectory(fullChild))
3877             {
3878             //trace("directory: %s", childName.c_str());
3879             if (!listFiles(baseDir, childName, res))
3880                 return false;
3881             continue;
3882             }
3883         else if (!isRegularFile(fullChild))
3884             {
3885             error("unknown file:%s", childName.c_str());
3886             return false;
3887             }
3889        //all done!
3890         res.push_back(childName);
3892         }
3893     closedir(dir);
3895     return true;
3899 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3901     String baseDir = propRef.resolve(fileSet.getDirectory());
3902     std::vector<String> fileList;
3903     if (!listFiles(baseDir, "", fileList))
3904         return false;
3906     std::vector<String> includes = fileSet.getIncludes();
3907     std::vector<String> excludes = fileSet.getExcludes();
3909     std::vector<String> incs;
3910     std::vector<String>::iterator iter;
3912     std::sort(fileList.begin(), fileList.end());
3914     //If there are <includes>, then add files to the output
3915     //in the order of the include list
3916     if (includes.size()==0)
3917         incs = fileList;
3918     else
3919         {
3920         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3921             {
3922             String pattern = *iter;
3923             std::vector<String>::iterator siter;
3924             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3925                 {
3926                 String s = *siter;
3927                 if (regexMatch(s, pattern))
3928                     {
3929                     //trace("INCLUDED:%s", s.c_str());
3930                     incs.push_back(s);
3931                     }
3932                 }
3933             }
3934         }
3936     //Now trim off the <excludes>
3937     std::vector<String> res;
3938     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3939         {
3940         String s = *iter;
3941         bool skipme = false;
3942         std::vector<String>::iterator siter;
3943         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3944             {
3945             String pattern = *siter;
3946             if (regexMatch(s, pattern))
3947                 {
3948                 //trace("EXCLUDED:%s", s.c_str());
3949                 skipme = true;
3950                 break;
3951                 }
3952             }
3953         if (!skipme)
3954             res.push_back(s);
3955         }
3956         
3957     fileSet.setFiles(res);
3959     return true;
3966 bool MakeBase::getSubstitutions(const String &str, String &result)
3968     String s = trim(str);
3969     int len = (int)s.size();
3970     String val;
3971     for (int i=0 ; i<len ; i++)
3972         {
3973         char ch = s[i];
3974         if (ch == '$' && s[i+1] == '{')
3975             {
3976             String varname;
3977             int j = i+2;
3978             for ( ; j<len ; j++)
3979                 {
3980                 ch = s[j];
3981                 if (ch == '$' && s[j+1] == '{')
3982                     {
3983                     error("attribute %s cannot have nested variable references",
3984                            s.c_str());
3985                     return false;
3986                     }
3987                 else if (ch == '}')
3988                     {
3989                     std::map<String, String>::iterator iter;
3990                     varname = trim(varname);
3991                     if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3992                         {
3993                         varname = varname.substr(envPrefix.size());
3994                         char *envstr = getenv(varname.c_str());
3995                         if (!envstr)
3996                             {
3997                             error("environment variable '%s' not defined", varname.c_str());
3998                             return false;
3999                             }
4000                         val.append(envstr);
4001                         }
4002                     else
4003                         {
4004                         iter = properties.find(varname);
4005                         if (iter != properties.end())
4006                             {
4007                             val.append(iter->second);
4008                             }
4009                         else
4010                             {
4011                             error("property ${%s} not found", varname.c_str());
4012                             return false;
4013                             }
4014                         }
4015                     break;
4016                     }
4017                 else
4018                     {
4019                     varname.push_back(ch);
4020                     }
4021                 }
4022             i = j;
4023             }
4024         else
4025             {
4026             val.push_back(ch);
4027             }
4028         }
4029     result = val;
4030     return true;
4034 bool MakeBase::getAttribute(Element *elem, const String &name,
4035                                     String &result)
4037     String s = elem->getAttribute(name);
4038     return getSubstitutions(s, result);
4042 bool MakeBase::getValue(Element *elem, String &result)
4044     String s = elem->getValue();
4045     //Replace all runs of whitespace with a single space
4046     return getSubstitutions(s, result);
4050 /**
4051  * Turn 'true' and 'false' into boolean values
4052  */             
4053 bool MakeBase::getBool(const String &str, bool &val)
4055     if (str == "true")
4056         val = true;
4057     else if (str == "false")
4058         val = false;
4059     else
4060         {
4061         error("expected 'true' or 'false'.  found '%s'", str.c_str());
4062         return false;
4063         }
4064     return true;
4070 /**
4071  * Parse a <patternset> entry
4072  */  
4073 bool MakeBase::parsePatternSet(Element *elem,
4074                           MakeBase &propRef,
4075                           std::vector<String> &includes,
4076                           std::vector<String> &excludes
4077                           )
4079     std::vector<Element *> children  = elem->getChildren();
4080     for (unsigned int i=0 ; i<children.size() ; i++)
4081         {
4082         Element *child = children[i];
4083         String tagName = child->getName();
4084         if (tagName == "exclude")
4085             {
4086             String fname;
4087             if (!propRef.getAttribute(child, "name", fname))
4088                 return false;
4089             //trace("EXCLUDE: %s", fname.c_str());
4090             excludes.push_back(fname);
4091             }
4092         else if (tagName == "include")
4093             {
4094             String fname;
4095             if (!propRef.getAttribute(child, "name", fname))
4096                 return false;
4097             //trace("INCLUDE: %s", fname.c_str());
4098             includes.push_back(fname);
4099             }
4100         }
4102     return true;
4108 /**
4109  * Parse a <fileset> entry, and determine which files
4110  * should be included
4111  */  
4112 bool MakeBase::parseFileSet(Element *elem,
4113                           MakeBase &propRef,
4114                           FileSet &fileSet)
4116     String name = elem->getName();
4117     if (name != "fileset")
4118         {
4119         error("expected <fileset>");
4120         return false;
4121         }
4124     std::vector<String> includes;
4125     std::vector<String> excludes;
4127     //A fileset has one implied patternset
4128     if (!parsePatternSet(elem, propRef, includes, excludes))
4129         {
4130         return false;
4131         }
4132     //Look for child tags, including more patternsets
4133     std::vector<Element *> children  = elem->getChildren();
4134     for (unsigned int i=0 ; i<children.size() ; i++)
4135         {
4136         Element *child = children[i];
4137         String tagName = child->getName();
4138         if (tagName == "patternset")
4139             {
4140             if (!parsePatternSet(child, propRef, includes, excludes))
4141                 {
4142                 return false;
4143                 }
4144             }
4145         }
4147     String dir;
4148     //Now do the stuff
4149     //Get the base directory for reading file names
4150     if (!propRef.getAttribute(elem, "dir", dir))
4151         return false;
4153     fileSet.setDirectory(dir);
4154     fileSet.setIncludes(includes);
4155     fileSet.setExcludes(excludes);
4156     
4157     /*
4158     std::vector<String> fileList;
4159     if (dir.size() > 0)
4160         {
4161         String baseDir = propRef.resolve(dir);
4162         if (!listFiles(baseDir, "", includes, excludes, fileList))
4163             return false;
4164         }
4165     std::sort(fileList.begin(), fileList.end());
4166     result = fileList;
4167     */
4169     
4170     /*
4171     for (unsigned int i=0 ; i<result.size() ; i++)
4172         {
4173         trace("RES:%s", result[i].c_str());
4174         }
4175     */
4177     
4178     return true;
4181 /**
4182  * Parse a <filelist> entry.  This is far simpler than FileSet,
4183  * since no directory scanning is needed.  The file names are listed
4184  * explicitly.
4185  */  
4186 bool MakeBase::parseFileList(Element *elem,
4187                           MakeBase &propRef,
4188                           FileList &fileList)
4190     std::vector<String> fnames;
4191     //Look for child tags, namely "file"
4192     std::vector<Element *> children  = elem->getChildren();
4193     for (unsigned int i=0 ; i<children.size() ; i++)
4194         {
4195         Element *child = children[i];
4196         String tagName = child->getName();
4197         if (tagName == "file")
4198             {
4199             String fname = child->getAttribute("name");
4200             if (fname.size()==0)
4201                 {
4202                 error("<file> element requires name="" attribute");
4203                 return false;
4204                 }
4205             fnames.push_back(fname);
4206             }
4207         else
4208             {
4209             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4210             return false;
4211                         }
4212         }
4214     String dir;
4215     //Get the base directory for reading file names
4216     if (!propRef.getAttribute(elem, "dir", dir))
4217         return false;
4218     fileList.setDirectory(dir);
4219     fileList.setFiles(fnames);
4221     return true;
4226 /**
4227  * Create a directory, making intermediate dirs
4228  * if necessary
4229  */                  
4230 bool MakeBase::createDirectory(const String &dirname)
4232     //trace("## createDirectory: %s", dirname.c_str());
4233     //## first check if it exists
4234     struct stat finfo;
4235     String nativeDir = getNativePath(dirname);
4236     char *cnative = (char *) nativeDir.c_str();
4237 #ifdef __WIN32__
4238     if (strlen(cnative)==2 && cnative[1]==':')
4239         return true;
4240 #endif
4241     if (stat(cnative, &finfo)==0)
4242         {
4243         if (!S_ISDIR(finfo.st_mode))
4244             {
4245             error("mkdir: file %s exists but is not a directory",
4246                   cnative);
4247             return false;
4248             }
4249         else //exists
4250             {
4251             return true;
4252             }
4253         }
4255     //## 2: pull off the last path segment, if any,
4256     //## to make the dir 'above' this one, if necessary
4257     unsigned int pos = dirname.find_last_of('/');
4258     if (pos>0 && pos != dirname.npos)
4259         {
4260         String subpath = dirname.substr(0, pos);
4261         //A letter root (c:) ?
4262         if (!createDirectory(subpath))
4263             return false;
4264         }
4265         
4266     //## 3: now make
4267 #ifdef __WIN32__
4268     if (mkdir(cnative)<0)
4269 #else
4270     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4271 #endif
4272         {
4273         error("cannot make directory '%s' : %s",
4274                  cnative, strerror(errno));
4275         return false;
4276         }
4277         
4278     return true;
4282 /**
4283  * Remove a directory recursively
4284  */ 
4285 bool MakeBase::removeDirectory(const String &dirName)
4287     char *dname = (char *)dirName.c_str();
4289     DIR *dir = opendir(dname);
4290     if (!dir)
4291         {
4292         //# Let this fail nicely.
4293         return true;
4294         //error("error opening directory %s : %s", dname, strerror(errno));
4295         //return false;
4296         }
4297     
4298     while (true)
4299         {
4300         struct dirent *de = readdir(dir);
4301         if (!de)
4302             break;
4304         //Get the directory member name
4305         String s = de->d_name;
4306         if (s.size() == 0 || s[0] == '.')
4307             continue;
4308         String childName;
4309         if (dirName.size() > 0)
4310             {
4311             childName.append(dirName);
4312             childName.append("/");
4313             }
4314         childName.append(s);
4317         struct stat finfo;
4318         String childNative = getNativePath(childName);
4319         char *cnative = (char *)childNative.c_str();
4320         if (stat(cnative, &finfo)<0)
4321             {
4322             error("cannot stat file:%s", cnative);
4323             }
4324         else if (S_ISDIR(finfo.st_mode))
4325             {
4326             //trace("DEL dir: %s", childName.c_str());
4327             if (!removeDirectory(childName))
4328                 {
4329                 return false;
4330                 }
4331             }
4332         else if (!S_ISREG(finfo.st_mode))
4333             {
4334             //trace("not regular: %s", cnative);
4335             }
4336         else
4337             {
4338             //trace("DEL file: %s", childName.c_str());
4339             if (remove(cnative)<0)
4340                 {
4341                 error("error deleting %s : %s",
4342                      cnative, strerror(errno));
4343                 return false;
4344                 }
4345             }
4346         }
4347     closedir(dir);
4349     //Now delete the directory
4350     String native = getNativePath(dirName);
4351     if (rmdir(native.c_str())<0)
4352         {
4353         error("could not delete directory %s : %s",
4354             native.c_str() , strerror(errno));
4355         return false;
4356         }
4358     return true;
4359     
4363 /**
4364  * Copy a file from one name to another. Perform only if needed
4365  */ 
4366 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4368     //# 1 Check up-to-date times
4369     String srcNative = getNativePath(srcFile);
4370     struct stat srcinfo;
4371     if (stat(srcNative.c_str(), &srcinfo)<0)
4372         {
4373         error("source file %s for copy does not exist",
4374                  srcNative.c_str());
4375         return false;
4376         }
4378     String destNative = getNativePath(destFile);
4379     struct stat destinfo;
4380     if (stat(destNative.c_str(), &destinfo)==0)
4381         {
4382         if (destinfo.st_mtime >= srcinfo.st_mtime)
4383             return true;
4384         }
4385         
4386     //# 2 prepare a destination directory if necessary
4387     unsigned int pos = destFile.find_last_of('/');
4388     if (pos != destFile.npos)
4389         {
4390         String subpath = destFile.substr(0, pos);
4391         if (!createDirectory(subpath))
4392             return false;
4393         }
4395     //# 3 do the data copy
4396 #ifndef __WIN32__
4398     FILE *srcf = fopen(srcNative.c_str(), "rb");
4399     if (!srcf)
4400         {
4401         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4402         return false;
4403         }
4404     FILE *destf = fopen(destNative.c_str(), "wb");
4405     if (!destf)
4406         {
4407         error("copyFile cannot open %s for writing", srcNative.c_str());
4408         return false;
4409         }
4411     while (!feof(srcf))
4412         {
4413         int ch = fgetc(srcf);
4414         if (ch<0)
4415             break;
4416         fputc(ch, destf);
4417         }
4419     fclose(destf);
4420     fclose(srcf);
4422 #else
4423     
4424     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4425         {
4426         error("copyFile from %s to %s failed",
4427              srcNative.c_str(), destNative.c_str());
4428         return false;
4429         }
4430         
4431 #endif /* __WIN32__ */
4434     return true;
4439 /**
4440  * Tests if the file exists and is a regular file
4441  */ 
4442 bool MakeBase::isRegularFile(const String &fileName)
4444     String native = getNativePath(fileName);
4445     struct stat finfo;
4446     
4447     //Exists?
4448     if (stat(native.c_str(), &finfo)<0)
4449         return false;
4452     //check the file mode
4453     if (!S_ISREG(finfo.st_mode))
4454         return false;
4456     return true;
4459 /**
4460  * Tests if the file exists and is a directory
4461  */ 
4462 bool MakeBase::isDirectory(const String &fileName)
4464     String native = getNativePath(fileName);
4465     struct stat finfo;
4466     
4467     //Exists?
4468     if (stat(native.c_str(), &finfo)<0)
4469         return false;
4472     //check the file mode
4473     if (!S_ISDIR(finfo.st_mode))
4474         return false;
4476     return true;
4481 /**
4482  * Tests is the modification of fileA is newer than fileB
4483  */ 
4484 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4486     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4487     String nativeA = getNativePath(fileA);
4488     struct stat infoA;
4489     //IF source does not exist, NOT newer
4490     if (stat(nativeA.c_str(), &infoA)<0)
4491         {
4492         return false;
4493         }
4495     String nativeB = getNativePath(fileB);
4496     struct stat infoB;
4497     //IF dest does not exist, YES, newer
4498     if (stat(nativeB.c_str(), &infoB)<0)
4499         {
4500         return true;
4501         }
4503     //check the actual times
4504     if (infoA.st_mtime > infoB.st_mtime)
4505         {
4506         return true;
4507         }
4509     return false;
4513 //########################################################################
4514 //# P K G    C O N F I G
4515 //########################################################################
4517 /**
4518  *
4519  */
4520 class PkgConfig : public MakeBase
4523 public:
4525     /**
4526      *
4527      */
4528     PkgConfig()
4529         { path="."; init(); }
4531     /**
4532      *
4533      */
4534     PkgConfig(const PkgConfig &other)
4535         { assign(other); }
4537     /**
4538      *
4539      */
4540     PkgConfig &operator=(const PkgConfig &other)
4541         { assign(other); return *this; }
4543     /**
4544      *
4545      */
4546     virtual ~PkgConfig()
4547         { }
4549     /**
4550      *
4551      */
4552     virtual String getName()
4553         { return name; }
4555     /**
4556      *
4557      */
4558     virtual String getPath()
4559         { return path; }
4561     /**
4562      *
4563      */
4564     virtual void setPath(const String &val)
4565         { path = val; }
4567     /**
4568      *
4569      */
4570     virtual String getPrefix()
4571         { return prefix; }
4573     /**
4574      *  Allow the user to override the prefix in the file
4575      */
4576     virtual void setPrefix(const String &val)
4577         { prefix = val; }
4579     /**
4580      *
4581      */
4582     virtual String getDescription()
4583         { return description; }
4585     /**
4586      *
4587      */
4588     virtual String getCflags()
4589         { return cflags; }
4591     /**
4592      *
4593      */
4594     virtual String getLibs()
4595         { return libs; }
4597     /**
4598      *
4599      */
4600     virtual String getAll()
4601         {
4602          String ret = cflags;
4603          ret.append(" ");
4604          ret.append(libs);
4605          return ret;
4606         }
4608     /**
4609      *
4610      */
4611     virtual String getVersion()
4612         { return version; }
4614     /**
4615      *
4616      */
4617     virtual int getMajorVersion()
4618         { return majorVersion; }
4620     /**
4621      *
4622      */
4623     virtual int getMinorVersion()
4624         { return minorVersion; }
4626     /**
4627      *
4628      */
4629     virtual int getMicroVersion()
4630         { return microVersion; }
4632     /**
4633      *
4634      */
4635     virtual std::map<String, String> &getAttributes()
4636         { return attrs; }
4638     /**
4639      *
4640      */
4641     virtual std::vector<String> &getRequireList()
4642         { return requireList; }
4644     /**
4645      *  Read a file for its details
4646      */         
4647     virtual bool readFile(const String &fileName);
4649     /**
4650      *  Read a file for its details
4651      */         
4652     virtual bool query(const String &name);
4654 private:
4656     void init()
4657         {
4658         //do not set path or prefix here
4659         name         = "";
4660         description  = "";
4661         cflags       = "";
4662         libs         = "";
4663         requires     = "";
4664         version      = "";
4665         majorVersion = 0;
4666         minorVersion = 0;
4667         microVersion = 0;
4668         fileName     = "";
4669         attrs.clear();
4670         requireList.clear();
4671         }
4673     void assign(const PkgConfig &other)
4674         {
4675         name         = other.name;
4676         path         = other.path;
4677         prefix       = other.prefix;
4678         description  = other.description;
4679         cflags       = other.cflags;
4680         libs         = other.libs;
4681         requires     = other.requires;
4682         version      = other.version;
4683         majorVersion = other.majorVersion;
4684         minorVersion = other.minorVersion;
4685         microVersion = other.microVersion;
4686         fileName     = other.fileName;
4687         attrs        = other.attrs;
4688         requireList  = other.requireList;
4689         }
4693     int get(int pos);
4695     int skipwhite(int pos);
4697     int getword(int pos, String &ret);
4699     void parseRequires();
4701     void parseVersion();
4703     bool parseLine(const String &lineBuf);
4705     bool parse(const String &buf);
4707     void dumpAttrs();
4709     String name;
4711     String path;
4713     String prefix;
4715     String description;
4717     String cflags;
4719     String libs;
4721     String requires;
4723     String version;
4725     int majorVersion;
4727     int minorVersion;
4729     int microVersion;
4731     String fileName;
4733     std::map<String, String> attrs;
4735     std::vector<String> requireList;
4737     char *parsebuf;
4738     int parselen;
4739 };
4742 /**
4743  * Get a character from the buffer at pos.  If out of range,
4744  * return -1 for safety
4745  */
4746 int PkgConfig::get(int pos)
4748     if (pos>parselen)
4749         return -1;
4750     return parsebuf[pos];
4755 /**
4756  *  Skip over all whitespace characters beginning at pos.  Return
4757  *  the position of the first non-whitespace character.
4758  *  Pkg-config is line-oriented, so check for newline
4759  */
4760 int PkgConfig::skipwhite(int pos)
4762     while (pos < parselen)
4763         {
4764         int ch = get(pos);
4765         if (ch < 0)
4766             break;
4767         if (!isspace(ch))
4768             break;
4769         pos++;
4770         }
4771     return pos;
4775 /**
4776  *  Parse the buffer beginning at pos, for a word.  Fill
4777  *  'ret' with the result.  Return the position after the
4778  *  word.
4779  */
4780 int PkgConfig::getword(int pos, String &ret)
4782     while (pos < parselen)
4783         {
4784         int ch = get(pos);
4785         if (ch < 0)
4786             break;
4787         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4788             break;
4789         ret.push_back((char)ch);
4790         pos++;
4791         }
4792     return pos;
4795 void PkgConfig::parseRequires()
4797     if (requires.size() == 0)
4798         return;
4799     parsebuf = (char *)requires.c_str();
4800     parselen = requires.size();
4801     int pos = 0;
4802     while (pos < parselen)
4803         {
4804         pos = skipwhite(pos);
4805         String val;
4806         int pos2 = getword(pos, val);
4807         if (pos2 == pos)
4808             break;
4809         pos = pos2;
4810         //trace("val %s", val.c_str());
4811         requireList.push_back(val);
4812         }
4815 static int getint(const String str)
4817     char *s = (char *)str.c_str();
4818     char *ends = NULL;
4819     long val = strtol(s, &ends, 10);
4820     if (ends == s)
4821         return 0L;
4822     else
4823         return val;
4826 void PkgConfig::parseVersion()
4828     if (version.size() == 0)
4829         return;
4830     String s1, s2, s3;
4831     unsigned int pos = 0;
4832     unsigned int pos2 = version.find('.', pos);
4833     if (pos2 == version.npos)
4834         {
4835         s1 = version;
4836         }
4837     else
4838         {
4839         s1 = version.substr(pos, pos2-pos);
4840         pos = pos2;
4841         pos++;
4842         if (pos < version.size())
4843             {
4844             pos2 = version.find('.', pos);
4845             if (pos2 == version.npos)
4846                 {
4847                 s2 = version.substr(pos, version.size()-pos);
4848                 }
4849             else
4850                 {
4851                 s2 = version.substr(pos, pos2-pos);
4852                 pos = pos2;
4853                 pos++;
4854                 if (pos < version.size())
4855                     s3 = version.substr(pos, pos2-pos);
4856                 }
4857             }
4858         }
4860     majorVersion = getint(s1);
4861     minorVersion = getint(s2);
4862     microVersion = getint(s3);
4863     //trace("version:%d.%d.%d", majorVersion,
4864     //          minorVersion, microVersion );
4868 bool PkgConfig::parseLine(const String &lineBuf)
4870     parsebuf = (char *)lineBuf.c_str();
4871     parselen = lineBuf.size();
4872     int pos = 0;
4873     
4874     while (pos < parselen)
4875         {
4876         String attrName;
4877         pos = skipwhite(pos);
4878         int ch = get(pos);
4879         if (ch == '#')
4880             {
4881             //comment.  eat the rest of the line
4882             while (pos < parselen)
4883                 {
4884                 ch = get(pos);
4885                 if (ch == '\n' || ch < 0)
4886                     break;
4887                 pos++;
4888                 }
4889             continue;
4890             }
4891         pos = getword(pos, attrName);
4892         if (attrName.size() == 0)
4893             continue;
4894         
4895         pos = skipwhite(pos);
4896         ch = get(pos);
4897         if (ch != ':' && ch != '=')
4898             {
4899             error("expected ':' or '='");
4900             return false;
4901             }
4902         pos++;
4903         pos = skipwhite(pos);
4904         String attrVal;
4905         while (pos < parselen)
4906             {
4907             ch = get(pos);
4908             if (ch == '\n' || ch < 0)
4909                 break;
4910             else if (ch == '$' && get(pos+1) == '{')
4911                 {
4912                 //#  this is a ${substitution}
4913                 pos += 2;
4914                 String subName;
4915                 while (pos < parselen)
4916                     {
4917                     ch = get(pos);
4918                     if (ch < 0)
4919                         {
4920                         error("unterminated substitution");
4921                         return false;
4922                         }
4923                     else if (ch == '}')
4924                         break;
4925                     else
4926                         subName.push_back((char)ch);
4927                     pos++;
4928                     }
4929                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4930                 if (subName == "prefix" && prefix.size()>0)
4931                     {
4932                     attrVal.append(prefix);
4933                     //trace("prefix override:%s", prefix.c_str());
4934                     }
4935                 else
4936                     {
4937                     String subVal = attrs[subName];
4938                     //trace("subVal:%s", subVal.c_str());
4939                     attrVal.append(subVal);
4940                     }
4941                 }
4942             else
4943                 attrVal.push_back((char)ch);
4944             pos++;
4945             }
4947         attrVal = trim(attrVal);
4948         attrs[attrName] = attrVal;
4950         String attrNameL = toLower(attrName);
4952         if (attrNameL == "name")
4953             name = attrVal;
4954         else if (attrNameL == "description")
4955             description = attrVal;
4956         else if (attrNameL == "cflags")
4957             cflags = attrVal;
4958         else if (attrNameL == "libs")
4959             libs = attrVal;
4960         else if (attrNameL == "requires")
4961             requires = attrVal;
4962         else if (attrNameL == "version")
4963             version = attrVal;
4965         //trace("name:'%s'  value:'%s'",
4966         //      attrName.c_str(), attrVal.c_str());
4967         }
4969     return true;
4973 bool PkgConfig::parse(const String &buf)
4975     init();
4977     String line;
4978     int lineNr = 0;
4979     for (unsigned int p=0 ; p<buf.size() ; p++)
4980         {
4981         int ch = buf[p];
4982         if (ch == '\n' || ch == '\r')
4983             {
4984             if (!parseLine(line))
4985                 return false;
4986             line.clear();
4987             lineNr++;
4988             }
4989         else
4990             {
4991             line.push_back(ch);
4992             }
4993         }
4994     if (line.size()>0)
4995         {
4996         if (!parseLine(line))
4997             return false;
4998         }
5000     parseRequires();
5001     parseVersion();
5003     return true;
5009 void PkgConfig::dumpAttrs()
5011     //trace("### PkgConfig attributes for %s", fileName.c_str());
5012     std::map<String, String>::iterator iter;
5013     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5014         {
5015         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5016         }
5020 bool PkgConfig::readFile(const String &fname)
5022     fileName = getNativePath(fname);
5024     FILE *f = fopen(fileName.c_str(), "r");
5025     if (!f)
5026         {
5027         error("cannot open file '%s' for reading", fileName.c_str());
5028         return false;
5029         }
5030     String buf;
5031     while (true)
5032         {
5033         int ch = fgetc(f);
5034         if (ch < 0)
5035             break;
5036         buf.push_back((char)ch);
5037         }
5038     fclose(f);
5040     //trace("####### File:\n%s", buf.c_str());
5041     if (!parse(buf))
5042         {
5043         return false;
5044         }
5046     //dumpAttrs();
5048     return true;
5053 bool PkgConfig::query(const String &pkgName)
5055     name = pkgName;
5057     String fname = path;
5058     fname.append("/");
5059     fname.append(name);
5060     fname.append(".pc");
5062     if (!readFile(fname))
5063         return false;
5064     
5065     return true;
5072 //########################################################################
5073 //# D E P T O O L
5074 //########################################################################
5078 /**
5079  *  Class which holds information for each file.
5080  */
5081 class FileRec
5083 public:
5085     typedef enum
5086         {
5087         UNKNOWN,
5088         CFILE,
5089         HFILE,
5090         OFILE
5091         } FileType;
5093     /**
5094      *  Constructor
5095      */
5096     FileRec()
5097         { init(); type = UNKNOWN; }
5099     /**
5100      *  Copy constructor
5101      */
5102     FileRec(const FileRec &other)
5103         { init(); assign(other); }
5104     /**
5105      *  Constructor
5106      */
5107     FileRec(int typeVal)
5108         { init(); type = typeVal; }
5109     /**
5110      *  Assignment operator
5111      */
5112     FileRec &operator=(const FileRec &other)
5113         { init(); assign(other); return *this; }
5116     /**
5117      *  Destructor
5118      */
5119     ~FileRec()
5120         {}
5122     /**
5123      *  Directory part of the file name
5124      */
5125     String path;
5127     /**
5128      *  Base name, sans directory and suffix
5129      */
5130     String baseName;
5132     /**
5133      *  File extension, such as cpp or h
5134      */
5135     String suffix;
5137     /**
5138      *  Type of file: CFILE, HFILE, OFILE
5139      */
5140     int type;
5142     /**
5143      * Used to list files ref'd by this one
5144      */
5145     std::map<String, FileRec *> files;
5148 private:
5150     void init()
5151         {
5152         }
5154     void assign(const FileRec &other)
5155         {
5156         type     = other.type;
5157         baseName = other.baseName;
5158         suffix   = other.suffix;
5159         files    = other.files;
5160         }
5162 };
5166 /**
5167  *  Simpler dependency record
5168  */
5169 class DepRec
5171 public:
5173     /**
5174      *  Constructor
5175      */
5176     DepRec()
5177         {init();}
5179     /**
5180      *  Copy constructor
5181      */
5182     DepRec(const DepRec &other)
5183         {init(); assign(other);}
5184     /**
5185      *  Constructor
5186      */
5187     DepRec(const String &fname)
5188         {init(); name = fname; }
5189     /**
5190      *  Assignment operator
5191      */
5192     DepRec &operator=(const DepRec &other)
5193         {init(); assign(other); return *this;}
5196     /**
5197      *  Destructor
5198      */
5199     ~DepRec()
5200         {}
5202     /**
5203      *  Directory part of the file name
5204      */
5205     String path;
5207     /**
5208      *  Base name, without the path and suffix
5209      */
5210     String name;
5212     /**
5213      *  Suffix of the source
5214      */
5215     String suffix;
5218     /**
5219      * Used to list files ref'd by this one
5220      */
5221     std::vector<String> files;
5224 private:
5226     void init()
5227         {
5228         }
5230     void assign(const DepRec &other)
5231         {
5232         path     = other.path;
5233         name     = other.name;
5234         suffix   = other.suffix;
5235         files    = other.files; //avoid recursion
5236         }
5238 };
5241 class DepTool : public MakeBase
5243 public:
5245     /**
5246      *  Constructor
5247      */
5248     DepTool()
5249         { init(); }
5251     /**
5252      *  Copy constructor
5253      */
5254     DepTool(const DepTool &other)
5255         { init(); assign(other); }
5257     /**
5258      *  Assignment operator
5259      */
5260     DepTool &operator=(const DepTool &other)
5261         { init(); assign(other); return *this; }
5264     /**
5265      *  Destructor
5266      */
5267     ~DepTool()
5268         {}
5271     /**
5272      *  Reset this section of code
5273      */
5274     virtual void init();
5275     
5276     /**
5277      *  Reset this section of code
5278      */
5279     virtual void assign(const DepTool &other)
5280         {
5281         }
5282     
5283     /**
5284      *  Sets the source directory which will be scanned
5285      */
5286     virtual void setSourceDirectory(const String &val)
5287         { sourceDir = val; }
5289     /**
5290      *  Returns the source directory which will be scanned
5291      */
5292     virtual String getSourceDirectory()
5293         { return sourceDir; }
5295     /**
5296      *  Sets the list of files within the directory to analyze
5297      */
5298     virtual void setFileList(const std::vector<String> &list)
5299         { fileList = list; }
5301     /**
5302      * Creates the list of all file names which will be
5303      * candidates for further processing.  Reads make.exclude
5304      * to see which files for directories to leave out.
5305      */
5306     virtual bool createFileList();
5309     /**
5310      *  Generates the forward dependency list
5311      */
5312     virtual bool generateDependencies();
5315     /**
5316      *  Generates the forward dependency list, saving the file
5317      */
5318     virtual bool generateDependencies(const String &);
5321     /**
5322      *  Load a dependency file
5323      */
5324     std::vector<DepRec> loadDepFile(const String &fileName);
5326     /**
5327      *  Load a dependency file, generating one if necessary
5328      */
5329     std::vector<DepRec> getDepFile(const String &fileName,
5330               bool forceRefresh);
5332     /**
5333      *  Save a dependency file
5334      */
5335     bool saveDepFile(const String &fileName);
5338 private:
5341     /**
5342      *
5343      */
5344     void parseName(const String &fullname,
5345                    String &path,
5346                    String &basename,
5347                    String &suffix);
5349     /**
5350      *
5351      */
5352     int get(int pos);
5354     /**
5355      *
5356      */
5357     int skipwhite(int pos);
5359     /**
5360      *
5361      */
5362     int getword(int pos, String &ret);
5364     /**
5365      *
5366      */
5367     bool sequ(int pos, const char *key);
5369     /**
5370      *
5371      */
5372     bool addIncludeFile(FileRec *frec, const String &fname);
5374     /**
5375      *
5376      */
5377     bool scanFile(const String &fname, FileRec *frec);
5379     /**
5380      *
5381      */
5382     bool processDependency(FileRec *ofile, FileRec *include);
5384     /**
5385      *
5386      */
5387     String sourceDir;
5389     /**
5390      *
5391      */
5392     std::vector<String> fileList;
5394     /**
5395      *
5396      */
5397     std::vector<String> directories;
5399     /**
5400      * A list of all files which will be processed for
5401      * dependencies.
5402      */
5403     std::map<String, FileRec *> allFiles;
5405     /**
5406      * The list of .o files, and the
5407      * dependencies upon them.
5408      */
5409     std::map<String, FileRec *> oFiles;
5411     int depFileSize;
5412     char *depFileBuf;
5414     static const int readBufSize = 8192;
5415     char readBuf[8193];//byte larger
5417 };
5423 /**
5424  *  Clean up after processing.  Called by the destructor, but should
5425  *  also be called before the object is reused.
5426  */
5427 void DepTool::init()
5429     sourceDir = ".";
5431     fileList.clear();
5432     directories.clear();
5433     
5434     //clear output file list
5435     std::map<String, FileRec *>::iterator iter;
5436     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5437         delete iter->second;
5438     oFiles.clear();
5440     //allFiles actually contains the master copies. delete them
5441     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5442         delete iter->second;
5443     allFiles.clear(); 
5450 /**
5451  *  Parse a full path name into path, base name, and suffix
5452  */
5453 void DepTool::parseName(const String &fullname,
5454                         String &path,
5455                         String &basename,
5456                         String &suffix)
5458     if (fullname.size() < 2)
5459         return;
5461     unsigned int pos = fullname.find_last_of('/');
5462     if (pos != fullname.npos && pos<fullname.size()-1)
5463         {
5464         path = fullname.substr(0, pos);
5465         pos++;
5466         basename = fullname.substr(pos, fullname.size()-pos);
5467         }
5468     else
5469         {
5470         path = "";
5471         basename = fullname;
5472         }
5474     pos = basename.find_last_of('.');
5475     if (pos != basename.npos && pos<basename.size()-1)
5476         {
5477         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5478         basename = basename.substr(0, pos);
5479         }
5481     //trace("parsename:%s %s %s", path.c_str(),
5482     //        basename.c_str(), suffix.c_str()); 
5487 /**
5488  *  Generate our internal file list.
5489  */
5490 bool DepTool::createFileList()
5493     for (unsigned int i=0 ; i<fileList.size() ; i++)
5494         {
5495         String fileName = fileList[i];
5496         //trace("## FileName:%s", fileName.c_str());
5497         String path;
5498         String basename;
5499         String sfx;
5500         parseName(fileName, path, basename, sfx);
5501         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5502             sfx == "cc" || sfx == "CC")
5503             {
5504             FileRec *fe         = new FileRec(FileRec::CFILE);
5505             fe->path            = path;
5506             fe->baseName        = basename;
5507             fe->suffix          = sfx;
5508             allFiles[fileName]  = fe;
5509             }
5510         else if (sfx == "h"   ||  sfx == "hh"  ||
5511                  sfx == "hpp" ||  sfx == "hxx")
5512             {
5513             FileRec *fe         = new FileRec(FileRec::HFILE);
5514             fe->path            = path;
5515             fe->baseName        = basename;
5516             fe->suffix          = sfx;
5517             allFiles[fileName]  = fe;
5518             }
5519         }
5521     if (!listDirectories(sourceDir, "", directories))
5522         return false;
5523         
5524     return true;
5531 /**
5532  * Get a character from the buffer at pos.  If out of range,
5533  * return -1 for safety
5534  */
5535 int DepTool::get(int pos)
5537     if (pos>depFileSize)
5538         return -1;
5539     return depFileBuf[pos];
5544 /**
5545  *  Skip over all whitespace characters beginning at pos.  Return
5546  *  the position of the first non-whitespace character.
5547  */
5548 int DepTool::skipwhite(int pos)
5550     while (pos < depFileSize)
5551         {
5552         int ch = get(pos);
5553         if (ch < 0)
5554             break;
5555         if (!isspace(ch))
5556             break;
5557         pos++;
5558         }
5559     return pos;
5563 /**
5564  *  Parse the buffer beginning at pos, for a word.  Fill
5565  *  'ret' with the result.  Return the position after the
5566  *  word.
5567  */
5568 int DepTool::getword(int pos, String &ret)
5570     while (pos < depFileSize)
5571         {
5572         int ch = get(pos);
5573         if (ch < 0)
5574             break;
5575         if (isspace(ch))
5576             break;
5577         ret.push_back((char)ch);
5578         pos++;
5579         }
5580     return pos;
5583 /**
5584  * Return whether the sequence of characters in the buffer
5585  * beginning at pos match the key,  for the length of the key
5586  */
5587 bool DepTool::sequ(int pos, const char *key)
5589     while (*key)
5590         {
5591         if (*key != get(pos))
5592             return false;
5593         key++; pos++;
5594         }
5595     return true;
5600 /**
5601  *  Add an include file name to a file record.  If the name
5602  *  is not found in allFiles explicitly, try prepending include
5603  *  directory names to it and try again.
5604  */
5605 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5607     //# if the name is an exact match to a path name
5608     //# in allFiles, like "myinc.h"
5609     std::map<String, FileRec *>::iterator iter =
5610            allFiles.find(iname);
5611     if (iter != allFiles.end()) //already exists
5612         {
5613          //h file in same dir
5614         FileRec *other = iter->second;
5615         //trace("local: '%s'", iname.c_str());
5616         frec->files[iname] = other;
5617         return true;
5618         }
5619     else 
5620         {
5621         //## Ok, it was not found directly
5622         //look in other dirs
5623         std::vector<String>::iterator diter;
5624         for (diter=directories.begin() ;
5625              diter!=directories.end() ; diter++)
5626             {
5627             String dfname = *diter;
5628             dfname.append("/");
5629             dfname.append(iname);
5630             URI fullPathURI(dfname);  //normalize path name
5631             String fullPath = fullPathURI.getPath();
5632             if (fullPath[0] == '/')
5633                 fullPath = fullPath.substr(1);
5634             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5635             iter = allFiles.find(fullPath);
5636             if (iter != allFiles.end())
5637                 {
5638                 FileRec *other = iter->second;
5639                 //trace("other: '%s'", iname.c_str());
5640                 frec->files[fullPath] = other;
5641                 return true;
5642                 }
5643             }
5644         }
5645     return true;
5650 /**
5651  *  Lightly parse a file to find the #include directives.  Do
5652  *  a bit of state machine stuff to make sure that the directive
5653  *  is valid.  (Like not in a comment).
5654  */
5655 bool DepTool::scanFile(const String &fname, FileRec *frec)
5657     String fileName;
5658     if (sourceDir.size() > 0)
5659         {
5660         fileName.append(sourceDir);
5661         fileName.append("/");
5662         }
5663     fileName.append(fname);
5664     String nativeName = getNativePath(fileName);
5665     FILE *f = fopen(nativeName.c_str(), "r");
5666     if (!f)
5667         {
5668         error("Could not open '%s' for reading", fname.c_str());
5669         return false;
5670         }
5671     String buf;
5672     while (!feof(f))
5673         {
5674         int len = fread(readBuf, 1, readBufSize, f);
5675         readBuf[len] = '\0';
5676         buf.append(readBuf);
5677         }
5678     fclose(f);
5680     depFileSize = buf.size();
5681     depFileBuf  = (char *)buf.c_str();
5682     int pos = 0;
5685     while (pos < depFileSize)
5686         {
5687         //trace("p:%c", get(pos));
5689         //# Block comment
5690         if (get(pos) == '/' && get(pos+1) == '*')
5691             {
5692             pos += 2;
5693             while (pos < depFileSize)
5694                 {
5695                 if (get(pos) == '*' && get(pos+1) == '/')
5696                     {
5697                     pos += 2;
5698                     break;
5699                     }
5700                 else
5701                     pos++;
5702                 }
5703             }
5704         //# Line comment
5705         else if (get(pos) == '/' && get(pos+1) == '/')
5706             {
5707             pos += 2;
5708             while (pos < depFileSize)
5709                 {
5710                 if (get(pos) == '\n')
5711                     {
5712                     pos++;
5713                     break;
5714                     }
5715                 else
5716                     pos++;
5717                 }
5718             }
5719         //# #include! yaay
5720         else if (sequ(pos, "#include"))
5721             {
5722             pos += 8;
5723             pos = skipwhite(pos);
5724             String iname;
5725             pos = getword(pos, iname);
5726             if (iname.size()>2)
5727                 {
5728                 iname = iname.substr(1, iname.size()-2);
5729                 addIncludeFile(frec, iname);
5730                 }
5731             }
5732         else
5733             {
5734             pos++;
5735             }
5736         }
5738     return true;
5743 /**
5744  *  Recursively check include lists to find all files in allFiles to which
5745  *  a given file is dependent.
5746  */
5747 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5749     std::map<String, FileRec *>::iterator iter;
5750     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5751         {
5752         String fname  = iter->first;
5753         if (ofile->files.find(fname) != ofile->files.end())
5754             {
5755             //trace("file '%s' already seen", fname.c_str());
5756             continue;
5757             }
5758         FileRec *child  = iter->second;
5759         ofile->files[fname] = child;
5760       
5761         processDependency(ofile, child);
5762         }
5765     return true;
5772 /**
5773  *  Generate the file dependency list.
5774  */
5775 bool DepTool::generateDependencies()
5777     std::map<String, FileRec *>::iterator iter;
5778     //# First pass.  Scan for all includes
5779     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5780         {
5781         FileRec *frec = iter->second;
5782         if (!scanFile(iter->first, frec))
5783             {
5784             //quit?
5785             }
5786         }
5788     //# Second pass.  Scan for all includes
5789     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5790         {
5791         FileRec *include = iter->second;
5792         if (include->type == FileRec::CFILE)
5793             {
5794             String cFileName   = iter->first;
5795             FileRec *ofile     = new FileRec(FileRec::OFILE);
5796             ofile->path        = include->path;
5797             ofile->baseName    = include->baseName;
5798             ofile->suffix      = include->suffix;
5799             String fname       = include->path;
5800             if (fname.size()>0)
5801                 fname.append("/");
5802             fname.append(include->baseName);
5803             fname.append(".o");
5804             oFiles[fname]    = ofile;
5805             //add the .c file first?   no, don't
5806             //ofile->files[cFileName] = include;
5807             
5808             //trace("ofile:%s", fname.c_str());
5810             processDependency(ofile, include);
5811             }
5812         }
5814       
5815     return true;
5820 /**
5821  *  High-level call to generate deps and optionally save them
5822  */
5823 bool DepTool::generateDependencies(const String &fileName)
5825     if (!createFileList())
5826         return false;
5827     if (!generateDependencies())
5828         return false;
5829     if (!saveDepFile(fileName))
5830         return false;
5831     return true;
5835 /**
5836  *   This saves the dependency cache.
5837  */
5838 bool DepTool::saveDepFile(const String &fileName)
5840     time_t tim;
5841     time(&tim);
5843     FILE *f = fopen(fileName.c_str(), "w");
5844     if (!f)
5845         {
5846         trace("cannot open '%s' for writing", fileName.c_str());
5847         }
5848     fprintf(f, "<?xml version='1.0'?>\n");
5849     fprintf(f, "<!--\n");
5850     fprintf(f, "########################################################\n");
5851     fprintf(f, "## File: build.dep\n");
5852     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5853     fprintf(f, "########################################################\n");
5854     fprintf(f, "-->\n");
5856     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5857     std::map<String, FileRec *>::iterator iter;
5858     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5859         {
5860         FileRec *frec = iter->second;
5861         if (frec->type == FileRec::OFILE)
5862             {
5863             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5864                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5865             std::map<String, FileRec *>::iterator citer;
5866             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5867                 {
5868                 String cfname = citer->first;
5869                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5870                 }
5871             fprintf(f, "</object>\n\n");
5872             }
5873         }
5875     fprintf(f, "</dependencies>\n");
5876     fprintf(f, "\n");
5877     fprintf(f, "<!--\n");
5878     fprintf(f, "########################################################\n");
5879     fprintf(f, "## E N D\n");
5880     fprintf(f, "########################################################\n");
5881     fprintf(f, "-->\n");
5883     fclose(f);
5885     return true;
5891 /**
5892  *   This loads the dependency cache.
5893  */
5894 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5896     std::vector<DepRec> result;
5897     
5898     Parser parser;
5899     Element *root = parser.parseFile(depFile.c_str());
5900     if (!root)
5901         {
5902         //error("Could not open %s for reading", depFile.c_str());
5903         return result;
5904         }
5906     if (root->getChildren().size()==0 ||
5907         root->getChildren()[0]->getName()!="dependencies")
5908         {
5909         error("loadDepFile: main xml element should be <dependencies>");
5910         delete root;
5911         return result;
5912         }
5914     //########## Start parsing
5915     Element *depList = root->getChildren()[0];
5917     std::vector<Element *> objects = depList->getChildren();
5918     for (unsigned int i=0 ; i<objects.size() ; i++)
5919         {
5920         Element *objectElem = objects[i];
5921         String tagName = objectElem->getName();
5922         if (tagName != "object")
5923             {
5924             error("loadDepFile: <dependencies> should have only <object> children");
5925             return result;
5926             }
5928         String objName   = objectElem->getAttribute("name");
5929          //trace("object:%s", objName.c_str());
5930         DepRec depObject(objName);
5931         depObject.path   = objectElem->getAttribute("path");
5932         depObject.suffix = objectElem->getAttribute("suffix");
5933         //########## DESCRIPTION
5934         std::vector<Element *> depElems = objectElem->getChildren();
5935         for (unsigned int i=0 ; i<depElems.size() ; i++)
5936             {
5937             Element *depElem = depElems[i];
5938             tagName = depElem->getName();
5939             if (tagName != "dep")
5940                 {
5941                 error("loadDepFile: <object> should have only <dep> children");
5942                 return result;
5943                 }
5944             String depName = depElem->getAttribute("name");
5945             //trace("    dep:%s", depName.c_str());
5946             depObject.files.push_back(depName);
5947             }
5949         //Insert into the result list, in a sorted manner
5950         bool inserted = false;
5951         std::vector<DepRec>::iterator iter;
5952         for (iter = result.begin() ; iter != result.end() ; iter++)
5953             {
5954             String vpath = iter->path;
5955             vpath.append("/");
5956             vpath.append(iter->name);
5957             String opath = depObject.path;
5958             opath.append("/");
5959             opath.append(depObject.name);
5960             if (vpath > opath)
5961                 {
5962                 inserted = true;
5963                 iter = result.insert(iter, depObject);
5964                 break;
5965                 }
5966             }
5967         if (!inserted)
5968             result.push_back(depObject);
5969         }
5971     delete root;
5973     return result;
5977 /**
5978  *   This loads the dependency cache.
5979  */
5980 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5981                    bool forceRefresh)
5983     std::vector<DepRec> result;
5984     if (forceRefresh)
5985         {
5986         generateDependencies(depFile);
5987         result = loadDepFile(depFile);
5988         }
5989     else
5990         {
5991         //try once
5992         result = loadDepFile(depFile);
5993         if (result.size() == 0)
5994             {
5995             //fail? try again
5996             generateDependencies(depFile);
5997             result = loadDepFile(depFile);
5998             }
5999         }
6000     return result;
6006 //########################################################################
6007 //# T A S K
6008 //########################################################################
6009 //forward decl
6010 class Target;
6011 class Make;
6013 /**
6014  *
6015  */
6016 class Task : public MakeBase
6019 public:
6021     typedef enum
6022         {
6023         TASK_NONE,
6024         TASK_CC,
6025         TASK_COPY,
6026         TASK_DELETE,
6027         TASK_JAR,
6028         TASK_JAVAC,
6029         TASK_LINK,
6030         TASK_MAKEFILE,
6031         TASK_MKDIR,
6032         TASK_MSGFMT,
6033         TASK_PKG_CONFIG,
6034         TASK_RANLIB,
6035         TASK_RC,
6036         TASK_SHAREDLIB,
6037         TASK_STATICLIB,
6038         TASK_STRIP,
6039         TASK_TOUCH,
6040         TASK_TSTAMP
6041         } TaskType;
6042         
6044     /**
6045      *
6046      */
6047     Task(MakeBase &par) : parent(par)
6048         { init(); }
6050     /**
6051      *
6052      */
6053     Task(const Task &other) : parent(other.parent)
6054         { init(); assign(other); }
6056     /**
6057      *
6058      */
6059     Task &operator=(const Task &other)
6060         { assign(other); return *this; }
6062     /**
6063      *
6064      */
6065     virtual ~Task()
6066         { }
6069     /**
6070      *
6071      */
6072     virtual MakeBase &getParent()
6073         { return parent; }
6075      /**
6076      *
6077      */
6078     virtual int  getType()
6079         { return type; }
6081     /**
6082      *
6083      */
6084     virtual void setType(int val)
6085         { type = val; }
6087     /**
6088      *
6089      */
6090     virtual String getName()
6091         { return name; }
6093     /**
6094      *
6095      */
6096     virtual bool execute()
6097         { return true; }
6099     /**
6100      *
6101      */
6102     virtual bool parse(Element *elem)
6103         { return true; }
6105     /**
6106      *
6107      */
6108     Task *createTask(Element *elem, int lineNr);
6111 protected:
6113     void init()
6114         {
6115         type = TASK_NONE;
6116         name = "none";
6117         }
6119     void assign(const Task &other)
6120         {
6121         type = other.type;
6122         name = other.name;
6123         }
6124         
6125     /**
6126      *  Show task status
6127      */
6128     void taskstatus(const char *fmt, ...)
6129         {
6130         va_list args;
6131         va_start(args,fmt);
6132         fprintf(stdout, "    --- <%s> : ", name.c_str());
6133         vfprintf(stdout, fmt, args);
6134         fprintf(stdout, "\n");
6135         va_end(args) ;
6136         }
6138     String getAttribute(Element *elem, const String &attrName)
6139         {
6140         String str;
6141         return str;
6142         }
6144     MakeBase &parent;
6146     int type;
6148     String name;
6149 };
6153 /**
6154  * This task runs the C/C++ compiler.  The compiler is invoked
6155  * for all .c or .cpp files which are newer than their correcsponding
6156  * .o files.  
6157  */
6158 class TaskCC : public Task
6160 public:
6162     TaskCC(MakeBase &par) : Task(par)
6163         {
6164         type = TASK_CC; name = "cc";
6165         ccCommand   = "gcc";
6166         cxxCommand  = "g++";
6167         source      = ".";
6168         dest        = ".";
6169         flags       = "";
6170         defines     = "";
6171         includes    = "";
6172         fileSet.clear();
6173         excludeInc.clear();
6174         }
6176     virtual ~TaskCC()
6177         {}
6179     virtual bool isExcludedInc(const String &dirname)
6180         {
6181         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6182             {
6183             String fname = excludeInc[i];
6184             if (fname == dirname)
6185                 return true;
6186                         }
6187         return false;
6188                 }
6190     virtual bool execute()
6191         {
6192         if (!listFiles(parent, fileSet))
6193             return false;
6194             
6195         FILE *f = NULL;
6196         f = fopen("compile.lst", "w");
6198         bool refreshCache = false;
6199         String fullName = parent.resolve("build.dep");
6200         if (isNewerThan(parent.getURI().getPath(), fullName))
6201             {
6202             taskstatus("regenerating C/C++ dependency cache");
6203             refreshCache = true;
6204             }
6206         DepTool depTool;
6207         depTool.setSourceDirectory(source);
6208         depTool.setFileList(fileSet.getFiles());
6209         std::vector<DepRec> deps =
6210              depTool.getDepFile("build.dep", refreshCache);
6211         
6212         String incs;
6213         incs.append("-I");
6214         incs.append(parent.resolve("."));
6215         incs.append(" ");
6216         if (includes.size()>0)
6217             {
6218             incs.append(includes);
6219             incs.append(" ");
6220             }
6221         std::set<String> paths;
6222         std::vector<DepRec>::iterator viter;
6223         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6224             {
6225             DepRec dep = *viter;
6226             if (dep.path.size()>0)
6227                 paths.insert(dep.path);
6228             }
6229         if (source.size()>0)
6230             {
6231             incs.append(" -I");
6232             incs.append(parent.resolve(source));
6233             incs.append(" ");
6234             }
6235         std::set<String>::iterator setIter;
6236         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6237             {
6238             String dirName = *setIter;
6239             //check excludeInc to see if we dont want to include this dir
6240             if (isExcludedInc(dirName))
6241                 continue;
6242             incs.append(" -I");
6243             String dname;
6244             if (source.size()>0)
6245                 {
6246                 dname.append(source);
6247                 dname.append("/");
6248                 }
6249             dname.append(dirName);
6250             incs.append(parent.resolve(dname));
6251             }
6252         std::vector<String> cfiles;
6253         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6254             {
6255             DepRec dep = *viter;
6257             //## Select command
6258             String sfx = dep.suffix;
6259             String command = ccCommand;
6260             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6261                  sfx == "cc" || sfx == "CC")
6262                 command = cxxCommand;
6263  
6264             //## Make paths
6265             String destPath = dest;
6266             String srcPath  = source;
6267             if (dep.path.size()>0)
6268                 {
6269                 destPath.append("/");
6270                 destPath.append(dep.path);
6271                 srcPath.append("/");
6272                 srcPath.append(dep.path);
6273                 }
6274             //## Make sure destination directory exists
6275             if (!createDirectory(destPath))
6276                 return false;
6277                 
6278             //## Check whether it needs to be done
6279             String destName;
6280             if (destPath.size()>0)
6281                 {
6282                 destName.append(destPath);
6283                 destName.append("/");
6284                 }
6285             destName.append(dep.name);
6286             destName.append(".o");
6287             String destFullName = parent.resolve(destName);
6288             String srcName;
6289             if (srcPath.size()>0)
6290                 {
6291                 srcName.append(srcPath);
6292                 srcName.append("/");
6293                 }
6294             srcName.append(dep.name);
6295             srcName.append(".");
6296             srcName.append(dep.suffix);
6297             String srcFullName = parent.resolve(srcName);
6298             bool compileMe = false;
6299             //# First we check if the source is newer than the .o
6300             if (isNewerThan(srcFullName, destFullName))
6301                 {
6302                 taskstatus("compile of %s required by source: %s",
6303                         destFullName.c_str(), srcFullName.c_str());
6304                 compileMe = true;
6305                 }
6306             else
6307                 {
6308                 //# secondly, we check if any of the included dependencies
6309                 //# of the .c/.cpp is newer than the .o
6310                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6311                     {
6312                     String depName;
6313                     if (source.size()>0)
6314                         {
6315                         depName.append(source);
6316                         depName.append("/");
6317                         }
6318                     depName.append(dep.files[i]);
6319                     String depFullName = parent.resolve(depName);
6320                     bool depRequires = isNewerThan(depFullName, destFullName);
6321                     //trace("%d %s %s\n", depRequires,
6322                     //        destFullName.c_str(), depFullName.c_str());
6323                     if (depRequires)
6324                         {
6325                         taskstatus("compile of %s required by included: %s",
6326                                 destFullName.c_str(), depFullName.c_str());
6327                         compileMe = true;
6328                         break;
6329                         }
6330                     }
6331                 }
6332             if (!compileMe)
6333                 {
6334                 continue;
6335                 }
6337             //## Assemble the command
6338             String cmd = command;
6339             cmd.append(" -c ");
6340             cmd.append(flags);
6341             cmd.append(" ");
6342             cmd.append(defines);
6343             cmd.append(" ");
6344             cmd.append(incs);
6345             cmd.append(" ");
6346             cmd.append(srcFullName);
6347             cmd.append(" -o ");
6348             cmd.append(destFullName);
6350             //## Execute the command
6352             String outString, errString;
6353             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6355             if (f)
6356                 {
6357                 fprintf(f, "########################### File : %s\n",
6358                              srcFullName.c_str());
6359                 fprintf(f, "#### COMMAND ###\n");
6360                 int col = 0;
6361                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6362                     {
6363                     char ch = cmd[i];
6364                     if (isspace(ch)  && col > 63)
6365                         {
6366                         fputc('\n', f);
6367                         col = 0;
6368                         }
6369                     else
6370                         {
6371                         fputc(ch, f);
6372                         col++;
6373                         }
6374                     if (col > 76)
6375                         {
6376                         fputc('\n', f);
6377                         col = 0;
6378                         }
6379                     }
6380                 fprintf(f, "\n");
6381                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6382                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6383                 }
6384             if (!ret)
6385                 {
6386                 error("problem compiling: %s", errString.c_str());
6387                 return false;
6388                 }
6389                 
6390             }
6392         if (f)
6393             {
6394             fclose(f);
6395             }
6396         
6397         return true;
6398         }
6400     virtual bool parse(Element *elem)
6401         {
6402         String s;
6403         if (!parent.getAttribute(elem, "command", s))
6404             return false;
6405         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6406         if (!parent.getAttribute(elem, "cc", s))
6407             return false;
6408         if (s.size()>0) ccCommand = s;
6409         if (!parent.getAttribute(elem, "cxx", s))
6410             return false;
6411         if (s.size()>0) cxxCommand = s;
6412         if (!parent.getAttribute(elem, "destdir", s))
6413             return false;
6414         if (s.size()>0) dest = s;
6416         std::vector<Element *> children = elem->getChildren();
6417         for (unsigned int i=0 ; i<children.size() ; i++)
6418             {
6419             Element *child = children[i];
6420             String tagName = child->getName();
6421             if (tagName == "flags")
6422                 {
6423                 if (!parent.getValue(child, flags))
6424                     return false;
6425                 flags = strip(flags);
6426                 }
6427             else if (tagName == "includes")
6428                 {
6429                 if (!parent.getValue(child, includes))
6430                     return false;
6431                 includes = strip(includes);
6432                 }
6433             else if (tagName == "defines")
6434                 {
6435                 if (!parent.getValue(child, defines))
6436                     return false;
6437                 defines = strip(defines);
6438                 }
6439             else if (tagName == "fileset")
6440                 {
6441                 if (!parseFileSet(child, parent, fileSet))
6442                     return false;
6443                 source = fileSet.getDirectory();
6444                 }
6445             else if (tagName == "excludeinc")
6446                 {
6447                 if (!parseFileList(child, parent, excludeInc))
6448                     return false;
6449                 }
6450             }
6452         return true;
6453         }
6454         
6455 protected:
6457     String   ccCommand;
6458     String   cxxCommand;
6459     String   source;
6460     String   dest;
6461     String   flags;
6462     String   lastflags;
6463     String   defines;
6464     String   includes;
6465     FileSet  fileSet;
6466     FileList excludeInc;
6467     
6468 };
6472 /**
6473  *
6474  */
6475 class TaskCopy : public Task
6477 public:
6479     typedef enum
6480         {
6481         CP_NONE,
6482         CP_TOFILE,
6483         CP_TODIR
6484         } CopyType;
6486     TaskCopy(MakeBase &par) : Task(par)
6487         {
6488         type = TASK_COPY; name = "copy";
6489         cptype = CP_NONE;
6490         verbose = false;
6491         haveFileSet = false;
6492         }
6494     virtual ~TaskCopy()
6495         {}
6497     virtual bool execute()
6498         {
6499         switch (cptype)
6500            {
6501            case CP_TOFILE:
6502                {
6503                if (fileName.size()>0)
6504                    {
6505                    taskstatus("%s to %s",
6506                         fileName.c_str(), toFileName.c_str());
6507                    String fullSource = parent.resolve(fileName);
6508                    String fullDest = parent.resolve(toFileName);
6509                    //trace("copy %s to file %s", fullSource.c_str(),
6510                    //                       fullDest.c_str());
6511                    if (!isRegularFile(fullSource))
6512                        {
6513                        error("copy : file %s does not exist", fullSource.c_str());
6514                        return false;
6515                        }
6516                    if (!isNewerThan(fullSource, fullDest))
6517                        {
6518                        taskstatus("skipped");
6519                        return true;
6520                        }
6521                    if (!copyFile(fullSource, fullDest))
6522                        return false;
6523                    taskstatus("1 file copied");
6524                    }
6525                return true;
6526                }
6527            case CP_TODIR:
6528                {
6529                if (haveFileSet)
6530                    {
6531                    if (!listFiles(parent, fileSet))
6532                        return false;
6533                    String fileSetDir = fileSet.getDirectory();
6535                    taskstatus("%s to %s",
6536                        fileSetDir.c_str(), toDirName.c_str());
6538                    int nrFiles = 0;
6539                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6540                        {
6541                        String fileName = fileSet[i];
6543                        String sourcePath;
6544                        if (fileSetDir.size()>0)
6545                            {
6546                            sourcePath.append(fileSetDir);
6547                            sourcePath.append("/");
6548                            }
6549                        sourcePath.append(fileName);
6550                        String fullSource = parent.resolve(sourcePath);
6551                        
6552                        //Get the immediate parent directory's base name
6553                        String baseFileSetDir = fileSetDir;
6554                        unsigned int pos = baseFileSetDir.find_last_of('/');
6555                        if (pos!=baseFileSetDir.npos &&
6556                                   pos < baseFileSetDir.size()-1)
6557                            baseFileSetDir =
6558                               baseFileSetDir.substr(pos+1,
6559                                    baseFileSetDir.size());
6560                        //Now make the new path
6561                        String destPath;
6562                        if (toDirName.size()>0)
6563                            {
6564                            destPath.append(toDirName);
6565                            destPath.append("/");
6566                            }
6567                        if (baseFileSetDir.size()>0)
6568                            {
6569                            destPath.append(baseFileSetDir);
6570                            destPath.append("/");
6571                            }
6572                        destPath.append(fileName);
6573                        String fullDest = parent.resolve(destPath);
6574                        //trace("fileName:%s", fileName.c_str());
6575                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6576                        //                   fullDest.c_str());
6577                        if (!isNewerThan(fullSource, fullDest))
6578                            {
6579                            //trace("copy skipping %s", fullSource.c_str());
6580                            continue;
6581                            }
6582                        if (!copyFile(fullSource, fullDest))
6583                            return false;
6584                        nrFiles++;
6585                        }
6586                    taskstatus("%d file(s) copied", nrFiles);
6587                    }
6588                else //file source
6589                    {
6590                    //For file->dir we want only the basename of
6591                    //the source appended to the dest dir
6592                    taskstatus("%s to %s", 
6593                        fileName.c_str(), toDirName.c_str());
6594                    String baseName = fileName;
6595                    unsigned int pos = baseName.find_last_of('/');
6596                    if (pos!=baseName.npos && pos<baseName.size()-1)
6597                        baseName = baseName.substr(pos+1, baseName.size());
6598                    String fullSource = parent.resolve(fileName);
6599                    String destPath;
6600                    if (toDirName.size()>0)
6601                        {
6602                        destPath.append(toDirName);
6603                        destPath.append("/");
6604                        }
6605                    destPath.append(baseName);
6606                    String fullDest = parent.resolve(destPath);
6607                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6608                    //                       fullDest.c_str());
6609                    if (!isRegularFile(fullSource))
6610                        {
6611                        error("copy : file %s does not exist", fullSource.c_str());
6612                        return false;
6613                        }
6614                    if (!isNewerThan(fullSource, fullDest))
6615                        {
6616                        taskstatus("skipped");
6617                        return true;
6618                        }
6619                    if (!copyFile(fullSource, fullDest))
6620                        return false;
6621                    taskstatus("1 file copied");
6622                    }
6623                return true;
6624                }
6625            }
6626         return true;
6627         }
6630     virtual bool parse(Element *elem)
6631         {
6632         if (!parent.getAttribute(elem, "file", fileName))
6633             return false;
6634         if (!parent.getAttribute(elem, "tofile", toFileName))
6635             return false;
6636         if (toFileName.size() > 0)
6637             cptype = CP_TOFILE;
6638         if (!parent.getAttribute(elem, "todir", toDirName))
6639             return false;
6640         if (toDirName.size() > 0)
6641             cptype = CP_TODIR;
6642         String ret;
6643         if (!parent.getAttribute(elem, "verbose", ret))
6644             return false;
6645         if (ret.size()>0 && !getBool(ret, verbose))
6646             return false;
6647             
6648         haveFileSet = false;
6649         
6650         std::vector<Element *> children = elem->getChildren();
6651         for (unsigned int i=0 ; i<children.size() ; i++)
6652             {
6653             Element *child = children[i];
6654             String tagName = child->getName();
6655             if (tagName == "fileset")
6656                 {
6657                 if (!parseFileSet(child, parent, fileSet))
6658                     {
6659                     error("problem getting fileset");
6660                     return false;
6661                     }
6662                 haveFileSet = true;
6663                 }
6664             }
6666         //Perform validity checks
6667         if (fileName.size()>0 && fileSet.size()>0)
6668             {
6669             error("<copy> can only have one of : file= and <fileset>");
6670             return false;
6671             }
6672         if (toFileName.size()>0 && toDirName.size()>0)
6673             {
6674             error("<copy> can only have one of : tofile= or todir=");
6675             return false;
6676             }
6677         if (haveFileSet && toDirName.size()==0)
6678             {
6679             error("a <copy> task with a <fileset> must have : todir=");
6680             return false;
6681             }
6682         if (cptype == CP_TOFILE && fileName.size()==0)
6683             {
6684             error("<copy> tofile= must be associated with : file=");
6685             return false;
6686             }
6687         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6688             {
6689             error("<copy> todir= must be associated with : file= or <fileset>");
6690             return false;
6691             }
6693         return true;
6694         }
6695         
6696 private:
6698     int cptype;
6699     String fileName;
6700     FileSet fileSet;
6701     String toFileName;
6702     String toDirName;
6703     bool verbose;
6704     bool haveFileSet;
6705 };
6708 /**
6709  *
6710  */
6711 class TaskDelete : public Task
6713 public:
6715     typedef enum
6716         {
6717         DEL_FILE,
6718         DEL_DIR,
6719         DEL_FILESET
6720         } DeleteType;
6722     TaskDelete(MakeBase &par) : Task(par)
6723         { 
6724           type        = TASK_DELETE;
6725           name        = "delete";
6726           delType     = DEL_FILE;
6727           verbose     = false;
6728           quiet       = false;
6729           failOnError = true;
6730         }
6732     virtual ~TaskDelete()
6733         {}
6735     virtual bool execute()
6736         {
6737         struct stat finfo;
6738         switch (delType)
6739             {
6740             case DEL_FILE:
6741                 {
6742                 status("          : %s", fileName.c_str());
6743                 String fullName = parent.resolve(fileName);
6744                 char *fname = (char *)fullName.c_str();
6745                 //does not exist
6746                 if (stat(fname, &finfo)<0)
6747                     return true;
6748                 //exists but is not a regular file
6749                 if (!S_ISREG(finfo.st_mode))
6750                     {
6751                     error("<delete> failed. '%s' exists and is not a regular file",
6752                           fname);
6753                     return false;
6754                     }
6755                 if (remove(fname)<0)
6756                     {
6757                     error("<delete> failed: %s", strerror(errno));
6758                     return false;
6759                     }
6760                 return true;
6761                 }
6762             case DEL_DIR:
6763                 {
6764                 taskstatus("%s", dirName.c_str());
6765                 String fullDir = parent.resolve(dirName);
6766                 if (!removeDirectory(fullDir))
6767                     return false;
6768                 return true;
6769                 }
6770             }
6771         return true;
6772         }
6774     virtual bool parse(Element *elem)
6775         {
6776         if (!parent.getAttribute(elem, "file", fileName))
6777             return false;
6778         if (fileName.size() > 0)
6779             delType = DEL_FILE;
6780         if (!parent.getAttribute(elem, "dir", dirName))
6781             return false;
6782         if (dirName.size() > 0)
6783             delType = DEL_DIR;
6784         if (fileName.size()>0 && dirName.size()>0)
6785             {
6786             error("<delete> can have one attribute of file= or dir=");
6787             return false;
6788             }
6789         if (fileName.size()==0 && dirName.size()==0)
6790             {
6791             error("<delete> must have one attribute of file= or dir=");
6792             return false;
6793             }
6794         String ret;
6795         if (!parent.getAttribute(elem, "verbose", ret))
6796             return false;
6797         if (ret.size()>0 && !getBool(ret, verbose))
6798             return false;
6799         if (!parent.getAttribute(elem, "quiet", ret))
6800             return false;
6801         if (ret.size()>0 && !getBool(ret, quiet))
6802             return false;
6803         if (!parent.getAttribute(elem, "failonerror", ret))
6804             return false;
6805         if (ret.size()>0 && !getBool(ret, failOnError))
6806             return false;
6807         return true;
6808         }
6810 private:
6812     int delType;
6813     String dirName;
6814     String fileName;
6815     bool verbose;
6816     bool quiet;
6817     bool failOnError;
6818 };
6821 /**
6822  *
6823  */
6824 class TaskJar : public Task
6826 public:
6828     TaskJar(MakeBase &par) : Task(par)
6829         { type = TASK_JAR; name = "jar"; command = "jar";}
6831     virtual ~TaskJar()
6832         {}
6834     virtual bool execute()
6835         {
6836         String cmd = command;
6837         cmd.append(" -cf ");
6838         cmd.append(destfile);
6839         cmd.append(" -C ");
6840         cmd.append(basedir);
6841         cmd.append(" .");
6843         String execCmd = cmd;
6845         String outString, errString;
6846         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6847         if (!ret)
6848             {
6849             error("<jar> command '%s' failed :\n %s",
6850                                       execCmd.c_str(), errString.c_str());
6851             return false;
6852             }
6853         return true;
6854         }
6856     virtual bool parse(Element *elem)
6857         {
6858         String s;
6859         if (!parent.getAttribute(elem, "command", s))
6860             return false;
6861         if (s.size() > 0)
6862             command = s;
6863         if (!parent.getAttribute(elem, "basedir", basedir))
6864             return false;
6865         if (!parent.getAttribute(elem, "destfile", destfile))
6866             return false;
6867         if (basedir.size() == 0 || destfile.size() == 0)
6868             {
6869             error("<jar> required both basedir and destfile attributes to be set");
6870             return false;
6871             }
6872         return true;
6873         }
6875 private:
6876     String command;
6877     String basedir;
6878     String destfile;
6879 };
6882 /**
6883  *
6884  */
6885 class TaskJavac : public Task
6887 public:
6889     TaskJavac(MakeBase &par) : Task(par)
6890         { 
6891         type = TASK_JAVAC; name = "javac";
6892         command = "javac";
6893         }
6895     virtual ~TaskJavac()
6896         {}
6898     virtual bool execute()
6899         {
6900         std::vector<String> fileList;
6901         if (!listFiles(srcdir, "", fileList))
6902             {
6903             return false;
6904             }
6905         String cmd = command;
6906         cmd.append(" -d ");
6907         cmd.append(destdir);
6908         cmd.append(" -classpath ");
6909         cmd.append(destdir);
6910         cmd.append(" -sourcepath ");
6911         cmd.append(srcdir);
6912         cmd.append(" ");
6913         if (target.size()>0)
6914             {
6915             cmd.append(" -target ");
6916             cmd.append(target);
6917             cmd.append(" ");
6918                         }
6919                 String fname = "javalist.btool";
6920                 FILE *f = fopen(fname.c_str(), "w");
6921                 int count = 0;
6922         for (unsigned int i=0 ; i<fileList.size() ; i++)
6923             {
6924             String fname = fileList[i];
6925             String srcName = fname;
6926             if (fname.size()<6) //x.java
6927                 continue;
6928             if (fname.compare(fname.size()-5, 5, ".java") != 0)
6929                 continue;
6930             String baseName = fname.substr(0, fname.size()-5);
6931             String destName = baseName;
6932             destName.append(".class");
6934             String fullSrc = srcdir;
6935             fullSrc.append("/");
6936             fullSrc.append(fname);
6937             String fullDest = destdir;
6938             fullDest.append("/");
6939             fullDest.append(destName);
6940             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6941             if (!isNewerThan(fullSrc, fullDest))
6942                 continue;
6944             count++;
6945             fprintf(f, "%s\n", fullSrc.c_str());
6946             }
6947         fclose(f);
6948         if (!count)
6949             {
6950             taskstatus("nothing to do");
6951             return true;
6952                         }
6954         taskstatus("compiling %d files", count);
6956         String execCmd = cmd;
6957         execCmd.append("@");
6958         execCmd.append(fname);
6960         String outString, errString;
6961         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6962         if (!ret)
6963             {
6964             error("<javac> command '%s' failed :\n %s",
6965                                       execCmd.c_str(), errString.c_str());
6966             return false;
6967             }
6968         return true;
6969         }
6971     virtual bool parse(Element *elem)
6972         {
6973         String s;
6974         if (!parent.getAttribute(elem, "command", s))
6975             return false;
6976         if (s.size() > 0)
6977             command = s;
6978         if (!parent.getAttribute(elem, "srcdir", srcdir))
6979             return false;
6980         if (!parent.getAttribute(elem, "destdir", destdir))
6981             return false;
6982         if (srcdir.size() == 0 || destdir.size() == 0)
6983             {
6984             error("<javac> required both srcdir and destdir attributes to be set");
6985             return false;
6986             }
6987         if (!parent.getAttribute(elem, "target", target))
6988             return false;
6989         return true;
6990         }
6992 private:
6994     String command;
6995     String srcdir;
6996     String destdir;
6997     String target;
6999 };
7002 /**
7003  *
7004  */
7005 class TaskLink : public Task
7007 public:
7009     TaskLink(MakeBase &par) : Task(par)
7010         {
7011         type = TASK_LINK; name = "link";
7012         command = "g++";
7013         doStrip = false;
7014         stripCommand = "strip";
7015         objcopyCommand = "objcopy";
7016         }
7018     virtual ~TaskLink()
7019         {}
7021     virtual bool execute()
7022         {
7023         if (!listFiles(parent, fileSet))
7024             return false;
7025         String fileSetDir = fileSet.getDirectory();
7026         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7027         bool doit = false;
7028         String fullTarget = parent.resolve(fileName);
7029         String cmd = command;
7030         cmd.append(" -o ");
7031         cmd.append(fullTarget);
7032         cmd.append(" ");
7033         cmd.append(flags);
7034         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7035             {
7036             cmd.append(" ");
7037             String obj;
7038             if (fileSetDir.size()>0)
7039                 {
7040                 obj.append(fileSetDir);
7041                 obj.append("/");
7042                 }
7043             obj.append(fileSet[i]);
7044             String fullObj = parent.resolve(obj);
7045             String nativeFullObj = getNativePath(fullObj);
7046             cmd.append(nativeFullObj);
7047             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7048             //          fullObj.c_str());
7049             if (isNewerThan(fullObj, fullTarget))
7050                 doit = true;
7051             }
7052         cmd.append(" ");
7053         cmd.append(libs);
7054         if (!doit)
7055             {
7056             //trace("link not needed");
7057             return true;
7058             }
7059         //trace("LINK cmd:%s", cmd.c_str());
7062         String outbuf, errbuf;
7063         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7064             {
7065             error("LINK problem: %s", errbuf.c_str());
7066             return false;
7067             }
7069         if (symFileName.size()>0)
7070             {
7071             String symFullName = parent.resolve(symFileName);
7072             cmd = objcopyCommand;
7073             cmd.append(" --only-keep-debug ");
7074             cmd.append(getNativePath(fullTarget));
7075             cmd.append(" ");
7076             cmd.append(getNativePath(symFullName));
7077             if (!executeCommand(cmd, "", outbuf, errbuf))
7078                 {
7079                 error("<strip> symbol file failed : %s", errbuf.c_str());
7080                 return false;
7081                 }
7082             }
7083             
7084         if (doStrip)
7085             {
7086             cmd = stripCommand;
7087             cmd.append(" ");
7088             cmd.append(getNativePath(fullTarget));
7089             if (!executeCommand(cmd, "", outbuf, errbuf))
7090                {
7091                error("<strip> failed : %s", errbuf.c_str());
7092                return false;
7093                }
7094             }
7096         return true;
7097         }
7099     virtual bool parse(Element *elem)
7100         {
7101         String s;
7102         if (!parent.getAttribute(elem, "command", s))
7103             return false;
7104         if (s.size()>0)
7105             command = s;
7106         if (!parent.getAttribute(elem, "objcopycommand", s))
7107             return false;
7108         if (s.size()>0)
7109             objcopyCommand = s;
7110         if (!parent.getAttribute(elem, "stripcommand", s))
7111             return false;
7112         if (s.size()>0)
7113             stripCommand = s;
7114         if (!parent.getAttribute(elem, "out", fileName))
7115             return false;
7116         if (!parent.getAttribute(elem, "strip", s))
7117             return false;
7118         if (s.size()>0 && !getBool(s, doStrip))
7119             return false;
7120         if (!parent.getAttribute(elem, "symfile", symFileName))
7121             return false;
7122             
7123         std::vector<Element *> children = elem->getChildren();
7124         for (unsigned int i=0 ; i<children.size() ; i++)
7125             {
7126             Element *child = children[i];
7127             String tagName = child->getName();
7128             if (tagName == "fileset")
7129                 {
7130                 if (!parseFileSet(child, parent, fileSet))
7131                     return false;
7132                 }
7133             else if (tagName == "flags")
7134                 {
7135                 if (!parent.getValue(child, flags))
7136                     return false;
7137                 flags = strip(flags);
7138                 }
7139             else if (tagName == "libs")
7140                 {
7141                 if (!parent.getValue(child, libs))
7142                     return false;
7143                 libs = strip(libs);
7144                 }
7145             }
7146         return true;
7147         }
7149 private:
7151     String  command;
7152     String  fileName;
7153     String  flags;
7154     String  libs;
7155     FileSet fileSet;
7156     bool    doStrip;
7157     String  symFileName;
7158     String  stripCommand;
7159     String  objcopyCommand;
7161 };
7165 /**
7166  * Create a named directory
7167  */
7168 class TaskMakeFile : public Task
7170 public:
7172     TaskMakeFile(MakeBase &par) : Task(par)
7173         { type = TASK_MAKEFILE; name = "makefile"; }
7175     virtual ~TaskMakeFile()
7176         {}
7178     virtual bool execute()
7179         {
7180         taskstatus("%s", fileName.c_str());
7181         String fullName = parent.resolve(fileName);
7182         if (!isNewerThan(parent.getURI().getPath(), fullName))
7183             {
7184             //trace("skipped <makefile>");
7185             return true;
7186             }
7187         String fullNative = getNativePath(fullName);
7188         //trace("fullName:%s", fullName.c_str());
7189         FILE *f = fopen(fullNative.c_str(), "w");
7190         if (!f)
7191             {
7192             error("<makefile> could not open %s for writing : %s",
7193                 fullName.c_str(), strerror(errno));
7194             return false;
7195             }
7196         for (unsigned int i=0 ; i<text.size() ; i++)
7197             fputc(text[i], f);
7198         fputc('\n', f);
7199         fclose(f);
7200         return true;
7201         }
7203     virtual bool parse(Element *elem)
7204         {
7205         if (!parent.getAttribute(elem, "file", fileName))
7206             return false;
7207         if (fileName.size() == 0)
7208             {
7209             error("<makefile> requires 'file=\"filename\"' attribute");
7210             return false;
7211             }
7212         if (!parent.getValue(elem, text))
7213             return false;
7214         text = leftJustify(text);
7215         //trace("dirname:%s", dirName.c_str());
7216         return true;
7217         }
7219 private:
7221     String fileName;
7222     String text;
7223 };
7227 /**
7228  * Create a named directory
7229  */
7230 class TaskMkDir : public Task
7232 public:
7234     TaskMkDir(MakeBase &par) : Task(par)
7235         { type = TASK_MKDIR; name = "mkdir"; }
7237     virtual ~TaskMkDir()
7238         {}
7240     virtual bool execute()
7241         {
7242         taskstatus("%s", dirName.c_str());
7243         String fullDir = parent.resolve(dirName);
7244         //trace("fullDir:%s", fullDir.c_str());
7245         if (!createDirectory(fullDir))
7246             return false;
7247         return true;
7248         }
7250     virtual bool parse(Element *elem)
7251         {
7252         if (!parent.getAttribute(elem, "dir", dirName))
7253             return false;
7254         if (dirName.size() == 0)
7255             {
7256             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7257             return false;
7258             }
7259         return true;
7260         }
7262 private:
7264     String dirName;
7265 };
7269 /**
7270  * Create a named directory
7271  */
7272 class TaskMsgFmt: public Task
7274 public:
7276     TaskMsgFmt(MakeBase &par) : Task(par)
7277          {
7278          type    = TASK_MSGFMT;
7279          name    = "msgfmt";
7280          command = "msgfmt";
7281          owndir  = false;
7282          outName = "";
7283          }
7285     virtual ~TaskMsgFmt()
7286         {}
7288     virtual bool execute()
7289         {
7290         if (!listFiles(parent, fileSet))
7291             return false;
7292         String fileSetDir = fileSet.getDirectory();
7294         //trace("msgfmt: %d", fileSet.size());
7295         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7296             {
7297             String fileName = fileSet[i];
7298             if (getSuffix(fileName) != "po")
7299                 continue;
7300             String sourcePath;
7301             if (fileSetDir.size()>0)
7302                 {
7303                 sourcePath.append(fileSetDir);
7304                 sourcePath.append("/");
7305                 }
7306             sourcePath.append(fileName);
7307             String fullSource = parent.resolve(sourcePath);
7309             String destPath;
7310             if (toDirName.size()>0)
7311                 {
7312                 destPath.append(toDirName);
7313                 destPath.append("/");
7314                 }
7315             if (owndir)
7316                 {
7317                 String subdir = fileName;
7318                 unsigned int pos = subdir.find_last_of('.');
7319                 if (pos != subdir.npos)
7320                     subdir = subdir.substr(0, pos);
7321                 destPath.append(subdir);
7322                 destPath.append("/");
7323                 }
7324             //Pick the output file name
7325             if (outName.size() > 0)
7326                 {
7327                 destPath.append(outName);
7328                 }
7329             else
7330                 {
7331                 destPath.append(fileName);
7332                 destPath[destPath.size()-2] = 'm';
7333                 }
7335             String fullDest = parent.resolve(destPath);
7337             if (!isNewerThan(fullSource, fullDest))
7338                 {
7339                 //trace("skip %s", fullSource.c_str());
7340                 continue;
7341                 }
7342                 
7343             String cmd = command;
7344             cmd.append(" ");
7345             cmd.append(fullSource);
7346             cmd.append(" -o ");
7347             cmd.append(fullDest);
7348             
7349             int pos = fullDest.find_last_of('/');
7350             if (pos>0)
7351                 {
7352                 String fullDestPath = fullDest.substr(0, pos);
7353                 if (!createDirectory(fullDestPath))
7354                     return false;
7355                 }
7359             String outString, errString;
7360             if (!executeCommand(cmd.c_str(), "", outString, errString))
7361                 {
7362                 error("<msgfmt> problem: %s", errString.c_str());
7363                 return false;
7364                 }
7365             }
7367         return true;
7368         }
7370     virtual bool parse(Element *elem)
7371         {
7372         String s;
7373         if (!parent.getAttribute(elem, "command", s))
7374             return false;
7375         if (s.size()>0)
7376             command = s;
7377         if (!parent.getAttribute(elem, "todir", toDirName))
7378             return false;
7379         if (!parent.getAttribute(elem, "out", outName))
7380             return false;
7381         if (!parent.getAttribute(elem, "owndir", s))
7382             return false;
7383         if (s.size()>0 && !getBool(s, owndir))
7384             return false;
7385             
7386         std::vector<Element *> children = elem->getChildren();
7387         for (unsigned int i=0 ; i<children.size() ; i++)
7388             {
7389             Element *child = children[i];
7390             String tagName = child->getName();
7391             if (tagName == "fileset")
7392                 {
7393                 if (!parseFileSet(child, parent, fileSet))
7394                     return false;
7395                 }
7396             }
7397         return true;
7398         }
7400 private:
7402     String  command;
7403     String  toDirName;
7404     String  outName;
7405     FileSet fileSet;
7406     bool    owndir;
7408 };
7412 /**
7413  *  Perform a Package-Config query similar to pkg-config
7414  */
7415 class TaskPkgConfig : public Task
7417 public:
7419     typedef enum
7420         {
7421         PKG_CONFIG_QUERY_CFLAGS,
7422         PKG_CONFIG_QUERY_LIBS,
7423         PKG_CONFIG_QUERY_ALL
7424         } QueryTypes;
7426     TaskPkgConfig(MakeBase &par) : Task(par)
7427         {
7428         type = TASK_PKG_CONFIG;
7429         name = "pkg-config";
7430         }
7432     virtual ~TaskPkgConfig()
7433         {}
7435     virtual bool execute()
7436         {
7437         String path = parent.resolve(pkg_config_path);
7438         PkgConfig pkgconfig;
7439         pkgconfig.setPath(path);
7440         pkgconfig.setPrefix(prefix);
7441         if (!pkgconfig.query(pkgName))
7442             {
7443             error("<pkg-config> query failed for '%s", name.c_str());
7444             return false;
7445             }
7446         String ret;
7447         switch (query)
7448             {
7449             case PKG_CONFIG_QUERY_CFLAGS:
7450                 {
7451                 ret = pkgconfig.getCflags();
7452                 break;
7453                 }
7454             case PKG_CONFIG_QUERY_LIBS:
7455                 {
7456                 ret = pkgconfig.getLibs();
7457                 break;
7458                 }
7459             case PKG_CONFIG_QUERY_ALL:
7460                 {
7461                 ret = pkgconfig.getAll();
7462                 break;
7463                 }
7464             default:
7465                 {
7466                 error("<pkg-config> unhandled query : %d", query);
7467                 return false;
7468                 }
7469             
7470             }
7471         taskstatus("%s", ret.c_str());
7472         parent.setProperty(propName, ret);
7473         return true;
7474         }
7476     virtual bool parse(Element *elem)
7477         {
7478         String s;
7479         //# NAME
7480         if (!parent.getAttribute(elem, "name", s))
7481             return false;
7482         if (s.size()>0)
7483            pkgName = s;
7484         else
7485             {
7486             error("<pkg-config> requires 'name=\"package\"' attribute");
7487             return false;
7488             }
7490         //# PROPERTY
7491         if (!parent.getAttribute(elem, "property", s))
7492             return false;
7493         if (s.size()>0)
7494            propName = s;
7495         else
7496             {
7497             error("<pkg-config> requires 'property=\"name\"' attribute");
7498             return false;
7499             }
7500         if (parent.hasProperty(propName))
7501             {
7502             error("<pkg-config> property '%s' is already defined",
7503                           propName.c_str());
7504             return false;
7505             }
7506         parent.setProperty(propName, "undefined");
7508         //# PATH
7509         if (!parent.getAttribute(elem, "path", s))
7510             return false;
7511         if (s.size()>0)
7512            pkg_config_path = s;
7514         //# PREFIX
7515         if (!parent.getAttribute(elem, "prefix", s))
7516             return false;
7517         if (s.size()>0)
7518            prefix = s;
7520         //# QUERY
7521         if (!parent.getAttribute(elem, "query", s))
7522             return false;
7523         if (s == "cflags")
7524             query = PKG_CONFIG_QUERY_CFLAGS;
7525         else if (s == "libs")
7526             query = PKG_CONFIG_QUERY_LIBS;
7527         else if (s == "both")
7528             query = PKG_CONFIG_QUERY_ALL;
7529         else
7530             {
7531             error("<pkg-config> requires 'query=\"type\"' attribute");
7532             error("where type = cflags, libs, or both");
7533             return false;
7534             }
7535         return true;
7536         }
7538 private:
7540     String pkgName;
7541     String prefix;
7542     String propName;
7543     String pkg_config_path;
7544     int query;
7546 };
7553 /**
7554  *  Process an archive to allow random access
7555  */
7556 class TaskRanlib : public Task
7558 public:
7560     TaskRanlib(MakeBase &par) : Task(par)
7561         {
7562         type = TASK_RANLIB; name = "ranlib";
7563         command = "ranlib";
7564         }
7566     virtual ~TaskRanlib()
7567         {}
7569     virtual bool execute()
7570         {
7571         String fullName = parent.resolve(fileName);
7572         //trace("fullDir:%s", fullDir.c_str());
7573         String cmd = command;
7574         cmd.append(" ");
7575         cmd.append(fullName);
7576         String outbuf, errbuf;
7577         if (!executeCommand(cmd, "", outbuf, errbuf))
7578             return false;
7579         return true;
7580         }
7582     virtual bool parse(Element *elem)
7583         {
7584         String s;
7585         if (!parent.getAttribute(elem, "command", s))
7586             return false;
7587         if (s.size()>0)
7588            command = s;
7589         if (!parent.getAttribute(elem, "file", fileName))
7590             return false;
7591         if (fileName.size() == 0)
7592             {
7593             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7594             return false;
7595             }
7596         return true;
7597         }
7599 private:
7601     String fileName;
7602     String command;
7603 };
7607 /**
7608  * Run the "ar" command to archive .o's into a .a
7609  */
7610 class TaskRC : public Task
7612 public:
7614     TaskRC(MakeBase &par) : Task(par)
7615         {
7616         type = TASK_RC; name = "rc";
7617         command = "windres";
7618         }
7620     virtual ~TaskRC()
7621         {}
7623     virtual bool execute()
7624         {
7625         String fullFile = parent.resolve(fileName);
7626         String fullOut  = parent.resolve(outName);
7627         if (!isNewerThan(fullFile, fullOut))
7628             return true;
7629         String cmd = command;
7630         cmd.append(" -o ");
7631         cmd.append(fullOut);
7632         cmd.append(" ");
7633         cmd.append(flags);
7634         cmd.append(" ");
7635         cmd.append(fullFile);
7637         String outString, errString;
7638         if (!executeCommand(cmd.c_str(), "", outString, errString))
7639             {
7640             error("RC problem: %s", errString.c_str());
7641             return false;
7642             }
7643         return true;
7644         }
7646     virtual bool parse(Element *elem)
7647         {
7648         if (!parent.getAttribute(elem, "command", command))
7649             return false;
7650         if (!parent.getAttribute(elem, "file", fileName))
7651             return false;
7652         if (!parent.getAttribute(elem, "out", outName))
7653             return false;
7654         std::vector<Element *> children = elem->getChildren();
7655         for (unsigned int i=0 ; i<children.size() ; i++)
7656             {
7657             Element *child = children[i];
7658             String tagName = child->getName();
7659             if (tagName == "flags")
7660                 {
7661                 if (!parent.getValue(child, flags))
7662                     return false;
7663                 }
7664             }
7665         return true;
7666         }
7668 private:
7670     String command;
7671     String flags;
7672     String fileName;
7673     String outName;
7675 };
7679 /**
7680  *  Collect .o's into a .so or DLL
7681  */
7682 class TaskSharedLib : public Task
7684 public:
7686     TaskSharedLib(MakeBase &par) : Task(par)
7687         {
7688         type = TASK_SHAREDLIB; name = "dll";
7689         command = "dllwrap";
7690         }
7692     virtual ~TaskSharedLib()
7693         {}
7695     virtual bool execute()
7696         {
7697         //trace("###########HERE %d", fileSet.size());
7698         bool doit = false;
7699         
7700         String fullOut = parent.resolve(fileName);
7701         //trace("ar fullout: %s", fullOut.c_str());
7702         
7703         if (!listFiles(parent, fileSet))
7704             return false;
7705         String fileSetDir = fileSet.getDirectory();
7707         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7708             {
7709             String fname;
7710             if (fileSetDir.size()>0)
7711                 {
7712                 fname.append(fileSetDir);
7713                 fname.append("/");
7714                 }
7715             fname.append(fileSet[i]);
7716             String fullName = parent.resolve(fname);
7717             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7718             if (isNewerThan(fullName, fullOut))
7719                 doit = true;
7720             }
7721         //trace("Needs it:%d", doit);
7722         if (!doit)
7723             {
7724             return true;
7725             }
7727         String cmd = "dllwrap";
7728         cmd.append(" -o ");
7729         cmd.append(fullOut);
7730         if (defFileName.size()>0)
7731             {
7732             cmd.append(" --def ");
7733             cmd.append(defFileName);
7734             cmd.append(" ");
7735             }
7736         if (impFileName.size()>0)
7737             {
7738             cmd.append(" --implib ");
7739             cmd.append(impFileName);
7740             cmd.append(" ");
7741             }
7742         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7743             {
7744             String fname;
7745             if (fileSetDir.size()>0)
7746                 {
7747                 fname.append(fileSetDir);
7748                 fname.append("/");
7749                 }
7750             fname.append(fileSet[i]);
7751             String fullName = parent.resolve(fname);
7753             cmd.append(" ");
7754             cmd.append(fullName);
7755             }
7756         cmd.append(" ");
7757         cmd.append(libs);
7759         String outString, errString;
7760         if (!executeCommand(cmd.c_str(), "", outString, errString))
7761             {
7762             error("<sharedlib> problem: %s", errString.c_str());
7763             return false;
7764             }
7766         return true;
7767         }
7769     virtual bool parse(Element *elem)
7770         {
7771         if (!parent.getAttribute(elem, "file", fileName))
7772             return false;
7773         if (!parent.getAttribute(elem, "import", impFileName))
7774             return false;
7775         if (!parent.getAttribute(elem, "def", defFileName))
7776             return false;
7777             
7778         std::vector<Element *> children = elem->getChildren();
7779         for (unsigned int i=0 ; i<children.size() ; i++)
7780             {
7781             Element *child = children[i];
7782             String tagName = child->getName();
7783             if (tagName == "fileset")
7784                 {
7785                 if (!parseFileSet(child, parent, fileSet))
7786                     return false;
7787                 }
7788             else if (tagName == "libs")
7789                 {
7790                 if (!parent.getValue(child, libs))
7791                     return false;
7792                 libs = strip(libs);
7793                 }
7794             }
7795         return true;
7796         }
7798 private:
7800     String command;
7801     String fileName;
7802     String defFileName;
7803     String impFileName;
7804     FileSet fileSet;
7805     String libs;
7807 };
7811 /**
7812  * Run the "ar" command to archive .o's into a .a
7813  */
7814 class TaskStaticLib : public Task
7816 public:
7818     TaskStaticLib(MakeBase &par) : Task(par)
7819         {
7820         type = TASK_STATICLIB; name = "staticlib";
7821         command = "ar crv";
7822         }
7824     virtual ~TaskStaticLib()
7825         {}
7827     virtual bool execute()
7828         {
7829         //trace("###########HERE %d", fileSet.size());
7830         bool doit = false;
7831         
7832         String fullOut = parent.resolve(fileName);
7833         //trace("ar fullout: %s", fullOut.c_str());
7834         
7835         if (!listFiles(parent, fileSet))
7836             return false;
7837         String fileSetDir = fileSet.getDirectory();
7839         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7840             {
7841             String fname;
7842             if (fileSetDir.size()>0)
7843                 {
7844                 fname.append(fileSetDir);
7845                 fname.append("/");
7846                 }
7847             fname.append(fileSet[i]);
7848             String fullName = parent.resolve(fname);
7849             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7850             if (isNewerThan(fullName, fullOut))
7851                 doit = true;
7852             }
7853         //trace("Needs it:%d", doit);
7854         if (!doit)
7855             {
7856             return true;
7857             }
7859         String cmd = command;
7860         cmd.append(" ");
7861         cmd.append(fullOut);
7862         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7863             {
7864             String fname;
7865             if (fileSetDir.size()>0)
7866                 {
7867                 fname.append(fileSetDir);
7868                 fname.append("/");
7869                 }
7870             fname.append(fileSet[i]);
7871             String fullName = parent.resolve(fname);
7873             cmd.append(" ");
7874             cmd.append(fullName);
7875             }
7877         String outString, errString;
7878         if (!executeCommand(cmd.c_str(), "", outString, errString))
7879             {
7880             error("<staticlib> problem: %s", errString.c_str());
7881             return false;
7882             }
7884         return true;
7885         }
7888     virtual bool parse(Element *elem)
7889         {
7890         String s;
7891         if (!parent.getAttribute(elem, "command", s))
7892             return false;
7893         if (s.size()>0)
7894             command = s;
7895         if (!parent.getAttribute(elem, "file", fileName))
7896             return false;
7897             
7898         std::vector<Element *> children = elem->getChildren();
7899         for (unsigned int i=0 ; i<children.size() ; i++)
7900             {
7901             Element *child = children[i];
7902             String tagName = child->getName();
7903             if (tagName == "fileset")
7904                 {
7905                 if (!parseFileSet(child, parent, fileSet))
7906                     return false;
7907                 }
7908             }
7909         return true;
7910         }
7912 private:
7914     String command;
7915     String fileName;
7916     FileSet fileSet;
7918 };
7923 /**
7924  * Strip an executable
7925  */
7926 class TaskStrip : public Task
7928 public:
7930     TaskStrip(MakeBase &par) : Task(par)
7931         { type = TASK_STRIP; name = "strip"; }
7933     virtual ~TaskStrip()
7934         {}
7936     virtual bool execute()
7937         {
7938         String fullName = parent.resolve(fileName);
7939         //trace("fullDir:%s", fullDir.c_str());
7940         String cmd;
7941         String outbuf, errbuf;
7943         if (symFileName.size()>0)
7944             {
7945             String symFullName = parent.resolve(symFileName);
7946             cmd = "objcopy --only-keep-debug ";
7947             cmd.append(getNativePath(fullName));
7948             cmd.append(" ");
7949             cmd.append(getNativePath(symFullName));
7950             if (!executeCommand(cmd, "", outbuf, errbuf))
7951                 {
7952                 error("<strip> symbol file failed : %s", errbuf.c_str());
7953                 return false;
7954                 }
7955             }
7956             
7957         cmd = "strip ";
7958         cmd.append(getNativePath(fullName));
7959         if (!executeCommand(cmd, "", outbuf, errbuf))
7960             {
7961             error("<strip> failed : %s", errbuf.c_str());
7962             return false;
7963             }
7964         return true;
7965         }
7967     virtual bool parse(Element *elem)
7968         {
7969         if (!parent.getAttribute(elem, "file", fileName))
7970             return false;
7971         if (!parent.getAttribute(elem, "symfile", symFileName))
7972             return false;
7973         if (fileName.size() == 0)
7974             {
7975             error("<strip> requires 'file=\"fileName\"' attribute");
7976             return false;
7977             }
7978         return true;
7979         }
7981 private:
7983     String fileName;
7984     String symFileName;
7985 };
7988 /**
7989  *
7990  */
7991 class TaskTouch : public Task
7993 public:
7995     TaskTouch(MakeBase &par) : Task(par)
7996         { type = TASK_TOUCH; name = "touch"; }
7998     virtual ~TaskTouch()
7999         {}
8001     virtual bool execute()
8002         {
8003         String fullName = parent.resolve(fileName);
8004         String nativeFile = getNativePath(fullName);
8005         if (!isRegularFile(fullName) && !isDirectory(fullName))
8006             {            
8007             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8008             int ret = creat(nativeFile.c_str(), 0666);
8009             if (ret != 0) 
8010                 {
8011                 error("<touch> could not create '%s' : %s",
8012                     nativeFile.c_str(), strerror(ret));
8013                 return false;
8014                 }
8015             return true;
8016             }
8017         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8018         if (ret != 0)
8019             {
8020             error("<touch> could not update the modification time for '%s' : %s",
8021                 nativeFile.c_str(), strerror(ret));
8022             return false;
8023             }
8024         return true;
8025         }
8027     virtual bool parse(Element *elem)
8028         {
8029         //trace("touch parse");
8030         if (!parent.getAttribute(elem, "file", fileName))
8031             return false;
8032         if (fileName.size() == 0)
8033             {
8034             error("<touch> requires 'file=\"fileName\"' attribute");
8035             return false;
8036             }
8037         return true;
8038         }
8040     String fileName;
8041 };
8044 /**
8045  *
8046  */
8047 class TaskTstamp : public Task
8049 public:
8051     TaskTstamp(MakeBase &par) : Task(par)
8052         { type = TASK_TSTAMP; name = "tstamp"; }
8054     virtual ~TaskTstamp()
8055         {}
8057     virtual bool execute()
8058         {
8059         return true;
8060         }
8062     virtual bool parse(Element *elem)
8063         {
8064         //trace("tstamp parse");
8065         return true;
8066         }
8067 };
8071 /**
8072  *
8073  */
8074 Task *Task::createTask(Element *elem, int lineNr)
8076     String tagName = elem->getName();
8077     //trace("task:%s", tagName.c_str());
8078     Task *task = NULL;
8079     if (tagName == "cc")
8080         task = new TaskCC(parent);
8081     else if (tagName == "copy")
8082         task = new TaskCopy(parent);
8083     else if (tagName == "delete")
8084         task = new TaskDelete(parent);
8085     else if (tagName == "jar")
8086         task = new TaskJar(parent);
8087     else if (tagName == "javac")
8088         task = new TaskJavac(parent);
8089     else if (tagName == "link")
8090         task = new TaskLink(parent);
8091     else if (tagName == "makefile")
8092         task = new TaskMakeFile(parent);
8093     else if (tagName == "mkdir")
8094         task = new TaskMkDir(parent);
8095     else if (tagName == "msgfmt")
8096         task = new TaskMsgFmt(parent);
8097     else if (tagName == "pkg-config")
8098         task = new TaskPkgConfig(parent);
8099     else if (tagName == "ranlib")
8100         task = new TaskRanlib(parent);
8101     else if (tagName == "rc")
8102         task = new TaskRC(parent);
8103     else if (tagName == "sharedlib")
8104         task = new TaskSharedLib(parent);
8105     else if (tagName == "staticlib")
8106         task = new TaskStaticLib(parent);
8107     else if (tagName == "strip")
8108         task = new TaskStrip(parent);
8109     else if (tagName == "touch")
8110         task = new TaskTouch(parent);
8111     else if (tagName == "tstamp")
8112         task = new TaskTstamp(parent);
8113     else
8114         {
8115         error("Unknown task '%s'", tagName.c_str());
8116         return NULL;
8117         }
8119     task->setLine(lineNr);
8121     if (!task->parse(elem))
8122         {
8123         delete task;
8124         return NULL;
8125         }
8126     return task;
8131 //########################################################################
8132 //# T A R G E T
8133 //########################################################################
8135 /**
8136  *
8137  */
8138 class Target : public MakeBase
8141 public:
8143     /**
8144      *
8145      */
8146     Target(Make &par) : parent(par)
8147         { init(); }
8149     /**
8150      *
8151      */
8152     Target(const Target &other) : parent(other.parent)
8153         { init(); assign(other); }
8155     /**
8156      *
8157      */
8158     Target &operator=(const Target &other)
8159         { init(); assign(other); return *this; }
8161     /**
8162      *
8163      */
8164     virtual ~Target()
8165         { cleanup() ; }
8168     /**
8169      *
8170      */
8171     virtual Make &getParent()
8172         { return parent; }
8174     /**
8175      *
8176      */
8177     virtual String getName()
8178         { return name; }
8180     /**
8181      *
8182      */
8183     virtual void setName(const String &val)
8184         { name = val; }
8186     /**
8187      *
8188      */
8189     virtual String getDescription()
8190         { return description; }
8192     /**
8193      *
8194      */
8195     virtual void setDescription(const String &val)
8196         { description = val; }
8198     /**
8199      *
8200      */
8201     virtual void addDependency(const String &val)
8202         { deps.push_back(val); }
8204     /**
8205      *
8206      */
8207     virtual void parseDependencies(const String &val)
8208         { deps = tokenize(val, ", "); }
8210     /**
8211      *
8212      */
8213     virtual std::vector<String> &getDependencies()
8214         { return deps; }
8216     /**
8217      *
8218      */
8219     virtual String getIf()
8220         { return ifVar; }
8222     /**
8223      *
8224      */
8225     virtual void setIf(const String &val)
8226         { ifVar = val; }
8228     /**
8229      *
8230      */
8231     virtual String getUnless()
8232         { return unlessVar; }
8234     /**
8235      *
8236      */
8237     virtual void setUnless(const String &val)
8238         { unlessVar = val; }
8240     /**
8241      *
8242      */
8243     virtual void addTask(Task *val)
8244         { tasks.push_back(val); }
8246     /**
8247      *
8248      */
8249     virtual std::vector<Task *> &getTasks()
8250         { return tasks; }
8252 private:
8254     void init()
8255         {
8256         }
8258     void cleanup()
8259         {
8260         tasks.clear();
8261         }
8263     void assign(const Target &other)
8264         {
8265         //parent      = other.parent;
8266         name        = other.name;
8267         description = other.description;
8268         ifVar       = other.ifVar;
8269         unlessVar   = other.unlessVar;
8270         deps        = other.deps;
8271         tasks       = other.tasks;
8272         }
8274     Make &parent;
8276     String name;
8278     String description;
8280     String ifVar;
8282     String unlessVar;
8284     std::vector<String> deps;
8286     std::vector<Task *> tasks;
8288 };
8297 //########################################################################
8298 //# M A K E
8299 //########################################################################
8302 /**
8303  *
8304  */
8305 class Make : public MakeBase
8308 public:
8310     /**
8311      *
8312      */
8313     Make()
8314         { init(); }
8316     /**
8317      *
8318      */
8319     Make(const Make &other)
8320         { assign(other); }
8322     /**
8323      *
8324      */
8325     Make &operator=(const Make &other)
8326         { assign(other); return *this; }
8328     /**
8329      *
8330      */
8331     virtual ~Make()
8332         { cleanup(); }
8334     /**
8335      *
8336      */
8337     virtual std::map<String, Target> &getTargets()
8338         { return targets; }
8341     /**
8342      *
8343      */
8344     virtual String version()
8345         { return BUILDTOOL_VERSION; }
8347     /**
8348      * Overload a <property>
8349      */
8350     virtual bool specifyProperty(const String &name,
8351                                  const String &value);
8353     /**
8354      *
8355      */
8356     virtual bool run();
8358     /**
8359      *
8360      */
8361     virtual bool run(const String &target);
8365 private:
8367     /**
8368      *
8369      */
8370     void init();
8372     /**
8373      *
8374      */
8375     void cleanup();
8377     /**
8378      *
8379      */
8380     void assign(const Make &other);
8382     /**
8383      *
8384      */
8385     bool executeTask(Task &task);
8388     /**
8389      *
8390      */
8391     bool executeTarget(Target &target,
8392              std::set<String> &targetsCompleted);
8395     /**
8396      *
8397      */
8398     bool execute();
8400     /**
8401      *
8402      */
8403     bool checkTargetDependencies(Target &prop,
8404                     std::vector<String> &depList);
8406     /**
8407      *
8408      */
8409     bool parsePropertyFile(const String &fileName,
8410                            const String &prefix);
8412     /**
8413      *
8414      */
8415     bool parseProperty(Element *elem);
8417     /**
8418      *
8419      */
8420     bool parseFile();
8422     /**
8423      *
8424      */
8425     std::vector<String> glob(const String &pattern);
8428     //###############
8429     //# Fields
8430     //###############
8432     String projectName;
8434     String currentTarget;
8436     String defaultTarget;
8438     String specifiedTarget;
8440     String baseDir;
8442     String description;
8443     
8444     //std::vector<Property> properties;
8445     
8446     std::map<String, Target> targets;
8448     std::vector<Task *> allTasks;
8449     
8450     std::map<String, String> specifiedProperties;
8452 };
8455 //########################################################################
8456 //# C L A S S  M A I N T E N A N C E
8457 //########################################################################
8459 /**
8460  *
8461  */
8462 void Make::init()
8464     uri             = "build.xml";
8465     projectName     = "";
8466     currentTarget   = "";
8467     defaultTarget   = "";
8468     specifiedTarget = "";
8469     baseDir         = "";
8470     description     = "";
8471     envPrefix       = "";
8472     properties.clear();
8473     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8474         delete allTasks[i];
8475     allTasks.clear();
8480 /**
8481  *
8482  */
8483 void Make::cleanup()
8485     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8486         delete allTasks[i];
8487     allTasks.clear();
8492 /**
8493  *
8494  */
8495 void Make::assign(const Make &other)
8497     uri              = other.uri;
8498     projectName      = other.projectName;
8499     currentTarget    = other.currentTarget;
8500     defaultTarget    = other.defaultTarget;
8501     specifiedTarget  = other.specifiedTarget;
8502     baseDir          = other.baseDir;
8503     description      = other.description;
8504     properties       = other.properties;
8509 //########################################################################
8510 //# U T I L I T Y    T A S K S
8511 //########################################################################
8513 /**
8514  *  Perform a file globbing
8515  */
8516 std::vector<String> Make::glob(const String &pattern)
8518     std::vector<String> res;
8519     return res;
8523 //########################################################################
8524 //# P U B L I C    A P I
8525 //########################################################################
8529 /**
8530  *
8531  */
8532 bool Make::executeTarget(Target &target,
8533              std::set<String> &targetsCompleted)
8536     String name = target.getName();
8538     //First get any dependencies for this target
8539     std::vector<String> deps = target.getDependencies();
8540     for (unsigned int i=0 ; i<deps.size() ; i++)
8541         {
8542         String dep = deps[i];
8543         //Did we do it already?  Skip
8544         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8545             continue;
8546             
8547         std::map<String, Target> &tgts =
8548                target.getParent().getTargets();
8549         std::map<String, Target>::iterator iter =
8550                tgts.find(dep);
8551         if (iter == tgts.end())
8552             {
8553             error("Target '%s' dependency '%s' not found",
8554                       name.c_str(),  dep.c_str());
8555             return false;
8556             }
8557         Target depTarget = iter->second;
8558         if (!executeTarget(depTarget, targetsCompleted))
8559             {
8560             return false;
8561             }
8562         }
8564     status("## Target : %s : %s", name.c_str(),
8565             target.getDescription().c_str());
8567     //Now let's do the tasks
8568     std::vector<Task *> &tasks = target.getTasks();
8569     for (unsigned int i=0 ; i<tasks.size() ; i++)
8570         {
8571         Task *task = tasks[i];
8572         status("task : %s", task->getName().c_str());
8573         if (!task->execute())
8574             {
8575             return false;
8576             }
8577         }
8578         
8579     targetsCompleted.insert(name);
8580     
8581     return true;
8586 /**
8587  *  Main execute() method.  Start here and work
8588  *  up the dependency tree 
8589  */
8590 bool Make::execute()
8592     status("######## EXECUTE");
8594     //Determine initial target
8595     if (specifiedTarget.size()>0)
8596         {
8597         currentTarget = specifiedTarget;
8598         }
8599     else if (defaultTarget.size()>0)
8600         {
8601         currentTarget = defaultTarget;
8602         }
8603     else
8604         {
8605         error("execute: no specified or default target requested");
8606         return false;
8607         }
8609     std::map<String, Target>::iterator iter =
8610                targets.find(currentTarget);
8611     if (iter == targets.end())
8612         {
8613         error("Initial target '%s' not found",
8614                  currentTarget.c_str());
8615         return false;
8616         }
8617         
8618     //Now run
8619     Target target = iter->second;
8620     std::set<String> targetsCompleted;
8621     if (!executeTarget(target, targetsCompleted))
8622         {
8623         return false;
8624         }
8626     status("######## EXECUTE COMPLETE");
8627     return true;
8633 /**
8634  *
8635  */
8636 bool Make::checkTargetDependencies(Target &target, 
8637                             std::vector<String> &depList)
8639     String tgtName = target.getName().c_str();
8640     depList.push_back(tgtName);
8642     std::vector<String> deps = target.getDependencies();
8643     for (unsigned int i=0 ; i<deps.size() ; i++)
8644         {
8645         String dep = deps[i];
8646         //First thing entered was the starting Target
8647         if (dep == depList[0])
8648             {
8649             error("Circular dependency '%s' found at '%s'",
8650                       dep.c_str(), tgtName.c_str());
8651             std::vector<String>::iterator diter;
8652             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8653                 {
8654                 error("  %s", diter->c_str());
8655                 }
8656             return false;
8657             }
8659         std::map<String, Target> &tgts =
8660                   target.getParent().getTargets();
8661         std::map<String, Target>::iterator titer = tgts.find(dep);
8662         if (titer == tgts.end())
8663             {
8664             error("Target '%s' dependency '%s' not found",
8665                       tgtName.c_str(), dep.c_str());
8666             return false;
8667             }
8668         if (!checkTargetDependencies(titer->second, depList))
8669             {
8670             return false;
8671             }
8672         }
8673     return true;
8680 static int getword(int pos, const String &inbuf, String &result)
8682     int p = pos;
8683     int len = (int)inbuf.size();
8684     String val;
8685     while (p < len)
8686         {
8687         char ch = inbuf[p];
8688         if (!isalnum(ch) && ch!='.' && ch!='_')
8689             break;
8690         val.push_back(ch);
8691         p++;
8692         }
8693     result = val;
8694     return p;
8700 /**
8701  *
8702  */
8703 bool Make::parsePropertyFile(const String &fileName,
8704                              const String &prefix)
8706     FILE *f = fopen(fileName.c_str(), "r");
8707     if (!f)
8708         {
8709         error("could not open property file %s", fileName.c_str());
8710         return false;
8711         }
8712     int linenr = 0;
8713     while (!feof(f))
8714         {
8715         char buf[256];
8716         if (!fgets(buf, 255, f))
8717             break;
8718         linenr++;
8719         String s = buf;
8720         s = trim(s);
8721         int len = s.size();
8722         if (len == 0)
8723             continue;
8724         if (s[0] == '#')
8725             continue;
8726         String key;
8727         String val;
8728         int p = 0;
8729         int p2 = getword(p, s, key);
8730         if (p2 <= p)
8731             {
8732             error("property file %s, line %d: expected keyword",
8733                     fileName.c_str(), linenr);
8734             return false;
8735             }
8736         if (prefix.size() > 0)
8737             {
8738             key.insert(0, prefix);
8739             }
8741         //skip whitespace
8742         for (p=p2 ; p<len ; p++)
8743             if (!isspace(s[p]))
8744                 break;
8746         if (p>=len || s[p]!='=')
8747             {
8748             error("property file %s, line %d: expected '='",
8749                     fileName.c_str(), linenr);
8750             return false;
8751             }
8752         p++;
8754         //skip whitespace
8755         for ( ; p<len ; p++)
8756             if (!isspace(s[p]))
8757                 break;
8759         /* This way expects a word after the =
8760         p2 = getword(p, s, val);
8761         if (p2 <= p)
8762             {
8763             error("property file %s, line %d: expected value",
8764                     fileName.c_str(), linenr);
8765             return false;
8766             }
8767         */
8768         // This way gets the rest of the line after the =
8769         if (p>=len)
8770             {
8771             error("property file %s, line %d: expected value",
8772                     fileName.c_str(), linenr);
8773             return false;
8774             }
8775         val = s.substr(p);
8776         if (key.size()==0)
8777             continue;
8778         //allow property to be set, even if val=""
8780         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8781         //See if we wanted to overload this property
8782         std::map<String, String>::iterator iter =
8783             specifiedProperties.find(key);
8784         if (iter!=specifiedProperties.end())
8785             {
8786             val = iter->second;
8787             status("overloading property '%s' = '%s'",
8788                    key.c_str(), val.c_str());
8789             }
8790         properties[key] = val;
8791         }
8792     fclose(f);
8793     return true;
8799 /**
8800  *
8801  */
8802 bool Make::parseProperty(Element *elem)
8804     std::vector<Attribute> &attrs = elem->getAttributes();
8805     for (unsigned int i=0 ; i<attrs.size() ; i++)
8806         {
8807         String attrName = attrs[i].getName();
8808         String attrVal  = attrs[i].getValue();
8810         if (attrName == "name")
8811             {
8812             String val;
8813             if (!getAttribute(elem, "value", val))
8814                 return false;
8815             if (val.size() > 0)
8816                 {
8817                 properties[attrVal] = val;
8818                 }
8819             else
8820                 {
8821                 if (!getAttribute(elem, "location", val))
8822                     return false;
8823                 //let the property exist, even if not defined
8824                 properties[attrVal] = val;
8825                 }
8826             //See if we wanted to overload this property
8827             std::map<String, String>::iterator iter =
8828                 specifiedProperties.find(attrVal);
8829             if (iter != specifiedProperties.end())
8830                 {
8831                 val = iter->second;
8832                 status("overloading property '%s' = '%s'",
8833                     attrVal.c_str(), val.c_str());
8834                 properties[attrVal] = val;
8835                 }
8836             }
8837         else if (attrName == "file")
8838             {
8839             String prefix;
8840             if (!getAttribute(elem, "prefix", prefix))
8841                 return false;
8842             if (prefix.size() > 0)
8843                 {
8844                 if (prefix[prefix.size()-1] != '.')
8845                     prefix.push_back('.');
8846                 }
8847             if (!parsePropertyFile(attrName, prefix))
8848                 return false;
8849             }
8850         else if (attrName == "environment")
8851             {
8852             if (envPrefix.size() > 0)
8853                 {
8854                 error("environment prefix can only be set once");
8855                 return false;
8856                 }
8857             if (attrVal.find('.') != attrVal.npos)
8858                 {
8859                 error("environment prefix cannot have a '.' in it");
8860                 return false;
8861                 }
8862             envPrefix = attrVal;
8863             envPrefix.push_back('.');
8864             }
8865         }
8867     return true;
8873 /**
8874  *
8875  */
8876 bool Make::parseFile()
8878     status("######## PARSE : %s", uri.getPath().c_str());
8880     setLine(0);
8882     Parser parser;
8883     Element *root = parser.parseFile(uri.getNativePath());
8884     if (!root)
8885         {
8886         error("Could not open %s for reading",
8887               uri.getNativePath().c_str());
8888         return false;
8889         }
8890     
8891     setLine(root->getLine());
8893     if (root->getChildren().size()==0 ||
8894         root->getChildren()[0]->getName()!="project")
8895         {
8896         error("Main xml element should be <project>");
8897         delete root;
8898         return false;
8899         }
8901     //########## Project attributes
8902     Element *project = root->getChildren()[0];
8903     String s = project->getAttribute("name");
8904     if (s.size() > 0)
8905         projectName = s;
8906     s = project->getAttribute("default");
8907     if (s.size() > 0)
8908         defaultTarget = s;
8909     s = project->getAttribute("basedir");
8910     if (s.size() > 0)
8911         baseDir = s;
8913     //######### PARSE MEMBERS
8914     std::vector<Element *> children = project->getChildren();
8915     for (unsigned int i=0 ; i<children.size() ; i++)
8916         {
8917         Element *elem = children[i];
8918         setLine(elem->getLine());
8919         String tagName = elem->getName();
8921         //########## DESCRIPTION
8922         if (tagName == "description")
8923             {
8924             description = parser.trim(elem->getValue());
8925             }
8927         //######### PROPERTY
8928         else if (tagName == "property")
8929             {
8930             if (!parseProperty(elem))
8931                 return false;
8932             }
8934         //######### TARGET
8935         else if (tagName == "target")
8936             {
8937             String tname   = elem->getAttribute("name");
8938             String tdesc   = elem->getAttribute("description");
8939             String tdeps   = elem->getAttribute("depends");
8940             String tif     = elem->getAttribute("if");
8941             String tunless = elem->getAttribute("unless");
8942             Target target(*this);
8943             target.setName(tname);
8944             target.setDescription(tdesc);
8945             target.parseDependencies(tdeps);
8946             target.setIf(tif);
8947             target.setUnless(tunless);
8948             std::vector<Element *> telems = elem->getChildren();
8949             for (unsigned int i=0 ; i<telems.size() ; i++)
8950                 {
8951                 Element *telem = telems[i];
8952                 Task breeder(*this);
8953                 Task *task = breeder.createTask(telem, telem->getLine());
8954                 if (!task)
8955                     return false;
8956                 allTasks.push_back(task);
8957                 target.addTask(task);
8958                 }
8960             //Check name
8961             if (tname.size() == 0)
8962                 {
8963                 error("no name for target");
8964                 return false;
8965                 }
8966             //Check for duplicate name
8967             if (targets.find(tname) != targets.end())
8968                 {
8969                 error("target '%s' already defined", tname.c_str());
8970                 return false;
8971                 }
8972             //more work than targets[tname]=target, but avoids default allocator
8973             targets.insert(std::make_pair<String, Target>(tname, target));
8974             }
8975         //######### none of the above
8976         else
8977             {
8978             error("unknown toplevel tag: <%s>", tagName.c_str());
8979             return false;
8980             }
8982         }
8984     std::map<String, Target>::iterator iter;
8985     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8986         {
8987         Target tgt = iter->second;
8988         std::vector<String> depList;
8989         if (!checkTargetDependencies(tgt, depList))
8990             {
8991             return false;
8992             }
8993         }
8996     delete root;
8997     status("######## PARSE COMPLETE");
8998     return true;
9002 /**
9003  * Overload a <property>
9004  */
9005 bool Make::specifyProperty(const String &name, const String &value)
9007     if (specifiedProperties.find(name) != specifiedProperties.end())
9008         {
9009         error("Property %s already specified", name.c_str());
9010         return false;
9011         }
9012     specifiedProperties[name] = value;
9013     return true;
9018 /**
9019  *
9020  */
9021 bool Make::run()
9023     if (!parseFile())
9024         return false;
9025         
9026     if (!execute())
9027         return false;
9029     return true;
9035 /**
9036  * Get a formatted MM:SS.sss time elapsed string
9037  */ 
9038 static String
9039 timeDiffString(struct timeval &x, struct timeval &y)
9041     long microsX  = x.tv_usec;
9042     long secondsX = x.tv_sec;
9043     long microsY  = y.tv_usec;
9044     long secondsY = y.tv_sec;
9045     if (microsX < microsY)
9046         {
9047         microsX += 1000000;
9048         secondsX -= 1;
9049         }
9051     int seconds = (int)(secondsX - secondsY);
9052     int millis  = (int)((microsX - microsY)/1000);
9054     int minutes = seconds/60;
9055     seconds -= minutes*60;
9056     char buf[80];
9057     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9058     String ret = buf;
9059     return ret;
9060     
9063 /**
9064  *
9065  */
9066 bool Make::run(const String &target)
9068     status("####################################################");
9069     status("#   %s", version().c_str());
9070     status("####################################################");
9071     struct timeval timeStart, timeEnd;
9072     ::gettimeofday(&timeStart, NULL);
9073     specifiedTarget = target;
9074     if (!run())
9075         return false;
9076     ::gettimeofday(&timeEnd, NULL);
9077     String timeStr = timeDiffString(timeEnd, timeStart);
9078     status("####################################################");
9079     status("#   BuildTool Completed : %s", timeStr.c_str());
9080     status("####################################################");
9081     return true;
9090 }// namespace buildtool
9091 //########################################################################
9092 //# M A I N
9093 //########################################################################
9095 typedef buildtool::String String;
9097 /**
9098  *  Format an error message in printf() style
9099  */
9100 static void error(const char *fmt, ...)
9102     va_list ap;
9103     va_start(ap, fmt);
9104     fprintf(stderr, "BuildTool error: ");
9105     vfprintf(stderr, fmt, ap);
9106     fprintf(stderr, "\n");
9107     va_end(ap);
9111 static bool parseProperty(const String &s, String &name, String &val)
9113     int len = s.size();
9114     int i;
9115     for (i=0 ; i<len ; i++)
9116         {
9117         char ch = s[i];
9118         if (ch == '=')
9119             break;
9120         name.push_back(ch);
9121         }
9122     if (i>=len || s[i]!='=')
9123         {
9124         error("property requires -Dname=value");
9125         return false;
9126         }
9127     i++;
9128     for ( ; i<len ; i++)
9129         {
9130         char ch = s[i];
9131         val.push_back(ch);
9132         }
9133     return true;
9137 /**
9138  * Compare a buffer with a key, for the length of the key
9139  */
9140 static bool sequ(const String &buf, const char *key)
9142     int len = buf.size();
9143     for (int i=0 ; key[i] && i<len ; i++)
9144         {
9145         if (key[i] != buf[i])
9146             return false;
9147         }        
9148     return true;
9151 static void usage(int argc, char **argv)
9153     printf("usage:\n");
9154     printf("   %s [options] [target]\n", argv[0]);
9155     printf("Options:\n");
9156     printf("  -help, -h              print this message\n");
9157     printf("  -version               print the version information and exit\n");
9158     printf("  -file <file>           use given buildfile\n");
9159     printf("  -f <file>                 ''\n");
9160     printf("  -D<property>=<value>   use value for given property\n");
9166 /**
9167  * Parse the command-line args, get our options,
9168  * and run this thing
9169  */   
9170 static bool parseOptions(int argc, char **argv)
9172     if (argc < 1)
9173         {
9174         error("Cannot parse arguments");
9175         return false;
9176         }
9178     buildtool::Make make;
9180     String target;
9182     //char *progName = argv[0];
9183     for (int i=1 ; i<argc ; i++)
9184         {
9185         String arg = argv[i];
9186         if (arg.size()>1 && arg[0]=='-')
9187             {
9188             if (arg == "-h" || arg == "-help")
9189                 {
9190                 usage(argc,argv);
9191                 return true;
9192                 }
9193             else if (arg == "-version")
9194                 {
9195                 printf("%s", make.version().c_str());
9196                 return true;
9197                 }
9198             else if (arg == "-f" || arg == "-file")
9199                 {
9200                 if (i>=argc)
9201                    {
9202                    usage(argc, argv);
9203                    return false;
9204                    }
9205                 i++; //eat option
9206                 make.setURI(argv[i]);
9207                 }
9208             else if (arg.size()>2 && sequ(arg, "-D"))
9209                 {
9210                 String s = arg.substr(2, arg.size());
9211                 String name, value;
9212                 if (!parseProperty(s, name, value))
9213                    {
9214                    usage(argc, argv);
9215                    return false;
9216                    }
9217                 if (!make.specifyProperty(name, value))
9218                     return false;
9219                 }
9220             else
9221                 {
9222                 error("Unknown option:%s", arg.c_str());
9223                 return false;
9224                 }
9225             }
9226         else
9227             {
9228             if (target.size()>0)
9229                 {
9230                 error("only one initial target");
9231                 usage(argc, argv);
9232                 return false;
9233                 }
9234             target = arg;
9235             }
9236         }
9238     //We have the options.  Now execute them
9239     if (!make.run(target))
9240         return false;
9242     return true;
9248 /*
9249 static bool runMake()
9251     buildtool::Make make;
9252     if (!make.run())
9253         return false;
9254     return true;
9258 static bool pkgConfigTest()
9260     buildtool::PkgConfig pkgConfig;
9261     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9262         return false;
9263     return true;
9268 static bool depTest()
9270     buildtool::DepTool deptool;
9271     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9272     if (!deptool.generateDependencies("build.dep"))
9273         return false;
9274     std::vector<buildtool::FileRec> res =
9275            deptool.loadDepFile("build.dep");
9276     if (res.size() == 0)
9277         return false;
9278     return true;
9281 static bool popenTest()
9283     buildtool::Make make;
9284     buildtool::String out, err;
9285     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9286     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9287     return true;
9291 static bool propFileTest()
9293     buildtool::Make make;
9294     make.parsePropertyFile("test.prop", "test.");
9295     return true;
9297 */
9299 int main(int argc, char **argv)
9302     if (!parseOptions(argc, argv))
9303         return 1;
9304     /*
9305     if (!popenTest())
9306         return 1;
9308     if (!depTest())
9309         return 1;
9310     if (!propFileTest())
9311         return 1;
9312     if (runMake())
9313         return 1;
9314     */
9315     return 0;
9319 //########################################################################
9320 //# E N D 
9321 //########################################################################