Code

remove tabs
[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.2, 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;
6165         name            = "cc";
6166         ccCommand       = "gcc";
6167         cxxCommand      = "g++";
6168         source          = ".";
6169         dest            = ".";
6170         flags           = "";
6171         defines         = "";
6172         includes        = "";
6173         continueOnError = false;
6174         fileSet.clear();
6175         excludeInc.clear();
6176         }
6178     virtual ~TaskCC()
6179         {}
6181     virtual bool isExcludedInc(const String &dirname)
6182         {
6183         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6184             {
6185             String fname = excludeInc[i];
6186             if (fname == dirname)
6187                 return true;
6188             }
6189         return false;
6190         }
6192     virtual bool execute()
6193         {
6194         if (!listFiles(parent, fileSet))
6195             return false;
6196             
6197         FILE *f = NULL;
6198         f = fopen("compile.lst", "w");
6200         bool refreshCache = false;
6201         String fullName = parent.resolve("build.dep");
6202         if (isNewerThan(parent.getURI().getPath(), fullName))
6203             {
6204             taskstatus("regenerating C/C++ dependency cache");
6205             refreshCache = true;
6206             }
6208         DepTool depTool;
6209         depTool.setSourceDirectory(source);
6210         depTool.setFileList(fileSet.getFiles());
6211         std::vector<DepRec> deps =
6212              depTool.getDepFile("build.dep", refreshCache);
6213         
6214         String incs;
6215         incs.append("-I");
6216         incs.append(parent.resolve("."));
6217         incs.append(" ");
6218         if (includes.size()>0)
6219             {
6220             incs.append(includes);
6221             incs.append(" ");
6222             }
6223         std::set<String> paths;
6224         std::vector<DepRec>::iterator viter;
6225         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6226             {
6227             DepRec dep = *viter;
6228             if (dep.path.size()>0)
6229                 paths.insert(dep.path);
6230             }
6231         if (source.size()>0)
6232             {
6233             incs.append(" -I");
6234             incs.append(parent.resolve(source));
6235             incs.append(" ");
6236             }
6237         std::set<String>::iterator setIter;
6238         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6239             {
6240             String dirName = *setIter;
6241             //check excludeInc to see if we dont want to include this dir
6242             if (isExcludedInc(dirName))
6243                 continue;
6244             incs.append(" -I");
6245             String dname;
6246             if (source.size()>0)
6247                 {
6248                 dname.append(source);
6249                 dname.append("/");
6250                 }
6251             dname.append(dirName);
6252             incs.append(parent.resolve(dname));
6253             }
6254             
6255         /**
6256          * Compile each of the C files that need it
6257          */
6258         bool errorOccurred = false;                 
6259         std::vector<String> cfiles;
6260         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6261             {
6262             DepRec dep = *viter;
6264             //## Select command
6265             String sfx = dep.suffix;
6266             String command = ccCommand;
6267             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6268                  sfx == "cc" || sfx == "CC")
6269                 command = cxxCommand;
6270  
6271             //## Make paths
6272             String destPath = dest;
6273             String srcPath  = source;
6274             if (dep.path.size()>0)
6275                 {
6276                 destPath.append("/");
6277                 destPath.append(dep.path);
6278                 srcPath.append("/");
6279                 srcPath.append(dep.path);
6280                 }
6281             //## Make sure destination directory exists
6282             if (!createDirectory(destPath))
6283                 return false;
6284                 
6285             //## Check whether it needs to be done
6286             String destName;
6287             if (destPath.size()>0)
6288                 {
6289                 destName.append(destPath);
6290                 destName.append("/");
6291                 }
6292             destName.append(dep.name);
6293             destName.append(".o");
6294             String destFullName = parent.resolve(destName);
6295             String srcName;
6296             if (srcPath.size()>0)
6297                 {
6298                 srcName.append(srcPath);
6299                 srcName.append("/");
6300                 }
6301             srcName.append(dep.name);
6302             srcName.append(".");
6303             srcName.append(dep.suffix);
6304             String srcFullName = parent.resolve(srcName);
6305             bool compileMe = false;
6306             //# First we check if the source is newer than the .o
6307             if (isNewerThan(srcFullName, destFullName))
6308                 {
6309                 taskstatus("compile of %s required by source: %s",
6310                         destFullName.c_str(), srcFullName.c_str());
6311                 compileMe = true;
6312                 }
6313             else
6314                 {
6315                 //# secondly, we check if any of the included dependencies
6316                 //# of the .c/.cpp is newer than the .o
6317                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6318                     {
6319                     String depName;
6320                     if (source.size()>0)
6321                         {
6322                         depName.append(source);
6323                         depName.append("/");
6324                         }
6325                     depName.append(dep.files[i]);
6326                     String depFullName = parent.resolve(depName);
6327                     bool depRequires = isNewerThan(depFullName, destFullName);
6328                     //trace("%d %s %s\n", depRequires,
6329                     //        destFullName.c_str(), depFullName.c_str());
6330                     if (depRequires)
6331                         {
6332                         taskstatus("compile of %s required by included: %s",
6333                                 destFullName.c_str(), depFullName.c_str());
6334                         compileMe = true;
6335                         break;
6336                         }
6337                     }
6338                 }
6339             if (!compileMe)
6340                 {
6341                 continue;
6342                 }
6344             //## Assemble the command
6345             String cmd = command;
6346             cmd.append(" -c ");
6347             cmd.append(flags);
6348             cmd.append(" ");
6349             cmd.append(defines);
6350             cmd.append(" ");
6351             cmd.append(incs);
6352             cmd.append(" ");
6353             cmd.append(srcFullName);
6354             cmd.append(" -o ");
6355             cmd.append(destFullName);
6357             //## Execute the command
6359             String outString, errString;
6360             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6362             if (f)
6363                 {
6364                 fprintf(f, "########################### File : %s\n",
6365                              srcFullName.c_str());
6366                 fprintf(f, "#### COMMAND ###\n");
6367                 int col = 0;
6368                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6369                     {
6370                     char ch = cmd[i];
6371                     if (isspace(ch)  && col > 63)
6372                         {
6373                         fputc('\n', f);
6374                         col = 0;
6375                         }
6376                     else
6377                         {
6378                         fputc(ch, f);
6379                         col++;
6380                         }
6381                     if (col > 76)
6382                         {
6383                         fputc('\n', f);
6384                         col = 0;
6385                         }
6386                     }
6387                 fprintf(f, "\n");
6388                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6389                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6390                 fflush(f);
6391                 }
6392             if (!ret)
6393                 {
6394                 error("problem compiling: %s", errString.c_str());
6395                 errorOccurred = true;
6396                 }
6397             if (errorOccurred && !continueOnError)
6398                 break;
6399             }
6401         if (f)
6402             {
6403             fclose(f);
6404             }
6405         
6406         return !errorOccurred;
6407         }
6410     virtual bool parse(Element *elem)
6411         {
6412         String s;
6413         if (!parent.getAttribute(elem, "command", s))
6414             return false;
6415         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6416         if (!parent.getAttribute(elem, "cc", s))
6417             return false;
6418         if (s.size()>0) ccCommand = s;
6419         if (!parent.getAttribute(elem, "cxx", s))
6420             return false;
6421         if (s.size()>0) cxxCommand = s;
6422         if (!parent.getAttribute(elem, "destdir", s))
6423             return false;
6424         if (s.size()>0) dest = s;
6425         if (!parent.getAttribute(elem, "continueOnError", s))
6426             return false;
6427         if (s=="true" || s=="yes")
6428             continueOnError = true;
6430         std::vector<Element *> children = elem->getChildren();
6431         for (unsigned int i=0 ; i<children.size() ; i++)
6432             {
6433             Element *child = children[i];
6434             String tagName = child->getName();
6435             if (tagName == "flags")
6436                 {
6437                 if (!parent.getValue(child, flags))
6438                     return false;
6439                 flags = strip(flags);
6440                 }
6441             else if (tagName == "includes")
6442                 {
6443                 if (!parent.getValue(child, includes))
6444                     return false;
6445                 includes = strip(includes);
6446                 }
6447             else if (tagName == "defines")
6448                 {
6449                 if (!parent.getValue(child, defines))
6450                     return false;
6451                 defines = strip(defines);
6452                 }
6453             else if (tagName == "fileset")
6454                 {
6455                 if (!parseFileSet(child, parent, fileSet))
6456                     return false;
6457                 source = fileSet.getDirectory();
6458                 }
6459             else if (tagName == "excludeinc")
6460                 {
6461                 if (!parseFileList(child, parent, excludeInc))
6462                     return false;
6463                 }
6464             }
6466         return true;
6467         }
6468         
6469 protected:
6471     String   ccCommand;
6472     String   cxxCommand;
6473     String   source;
6474     String   dest;
6475     String   flags;
6476     String   lastflags;
6477     String   defines;
6478     String   includes;
6479     bool     continueOnError;
6480     FileSet  fileSet;
6481     FileList excludeInc;
6482     
6483 };
6487 /**
6488  *
6489  */
6490 class TaskCopy : public Task
6492 public:
6494     typedef enum
6495         {
6496         CP_NONE,
6497         CP_TOFILE,
6498         CP_TODIR
6499         } CopyType;
6501     TaskCopy(MakeBase &par) : Task(par)
6502         {
6503         type = TASK_COPY; name = "copy";
6504         cptype = CP_NONE;
6505         verbose = false;
6506         haveFileSet = false;
6507         }
6509     virtual ~TaskCopy()
6510         {}
6512     virtual bool execute()
6513         {
6514         switch (cptype)
6515            {
6516            case CP_TOFILE:
6517                {
6518                if (fileName.size()>0)
6519                    {
6520                    taskstatus("%s to %s",
6521                         fileName.c_str(), toFileName.c_str());
6522                    String fullSource = parent.resolve(fileName);
6523                    String fullDest = parent.resolve(toFileName);
6524                    //trace("copy %s to file %s", fullSource.c_str(),
6525                    //                       fullDest.c_str());
6526                    if (!isRegularFile(fullSource))
6527                        {
6528                        error("copy : file %s does not exist", fullSource.c_str());
6529                        return false;
6530                        }
6531                    if (!isNewerThan(fullSource, fullDest))
6532                        {
6533                        taskstatus("skipped");
6534                        return true;
6535                        }
6536                    if (!copyFile(fullSource, fullDest))
6537                        return false;
6538                    taskstatus("1 file copied");
6539                    }
6540                return true;
6541                }
6542            case CP_TODIR:
6543                {
6544                if (haveFileSet)
6545                    {
6546                    if (!listFiles(parent, fileSet))
6547                        return false;
6548                    String fileSetDir = fileSet.getDirectory();
6550                    taskstatus("%s to %s",
6551                        fileSetDir.c_str(), toDirName.c_str());
6553                    int nrFiles = 0;
6554                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6555                        {
6556                        String fileName = fileSet[i];
6558                        String sourcePath;
6559                        if (fileSetDir.size()>0)
6560                            {
6561                            sourcePath.append(fileSetDir);
6562                            sourcePath.append("/");
6563                            }
6564                        sourcePath.append(fileName);
6565                        String fullSource = parent.resolve(sourcePath);
6566                        
6567                        //Get the immediate parent directory's base name
6568                        String baseFileSetDir = fileSetDir;
6569                        unsigned int pos = baseFileSetDir.find_last_of('/');
6570                        if (pos!=baseFileSetDir.npos &&
6571                                   pos < baseFileSetDir.size()-1)
6572                            baseFileSetDir =
6573                               baseFileSetDir.substr(pos+1,
6574                                    baseFileSetDir.size());
6575                        //Now make the new path
6576                        String destPath;
6577                        if (toDirName.size()>0)
6578                            {
6579                            destPath.append(toDirName);
6580                            destPath.append("/");
6581                            }
6582                        if (baseFileSetDir.size()>0)
6583                            {
6584                            destPath.append(baseFileSetDir);
6585                            destPath.append("/");
6586                            }
6587                        destPath.append(fileName);
6588                        String fullDest = parent.resolve(destPath);
6589                        //trace("fileName:%s", fileName.c_str());
6590                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6591                        //                   fullDest.c_str());
6592                        if (!isNewerThan(fullSource, fullDest))
6593                            {
6594                            //trace("copy skipping %s", fullSource.c_str());
6595                            continue;
6596                            }
6597                        if (!copyFile(fullSource, fullDest))
6598                            return false;
6599                        nrFiles++;
6600                        }
6601                    taskstatus("%d file(s) copied", nrFiles);
6602                    }
6603                else //file source
6604                    {
6605                    //For file->dir we want only the basename of
6606                    //the source appended to the dest dir
6607                    taskstatus("%s to %s", 
6608                        fileName.c_str(), toDirName.c_str());
6609                    String baseName = fileName;
6610                    unsigned int pos = baseName.find_last_of('/');
6611                    if (pos!=baseName.npos && pos<baseName.size()-1)
6612                        baseName = baseName.substr(pos+1, baseName.size());
6613                    String fullSource = parent.resolve(fileName);
6614                    String destPath;
6615                    if (toDirName.size()>0)
6616                        {
6617                        destPath.append(toDirName);
6618                        destPath.append("/");
6619                        }
6620                    destPath.append(baseName);
6621                    String fullDest = parent.resolve(destPath);
6622                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6623                    //                       fullDest.c_str());
6624                    if (!isRegularFile(fullSource))
6625                        {
6626                        error("copy : file %s does not exist", fullSource.c_str());
6627                        return false;
6628                        }
6629                    if (!isNewerThan(fullSource, fullDest))
6630                        {
6631                        taskstatus("skipped");
6632                        return true;
6633                        }
6634                    if (!copyFile(fullSource, fullDest))
6635                        return false;
6636                    taskstatus("1 file copied");
6637                    }
6638                return true;
6639                }
6640            }
6641         return true;
6642         }
6645     virtual bool parse(Element *elem)
6646         {
6647         if (!parent.getAttribute(elem, "file", fileName))
6648             return false;
6649         if (!parent.getAttribute(elem, "tofile", toFileName))
6650             return false;
6651         if (toFileName.size() > 0)
6652             cptype = CP_TOFILE;
6653         if (!parent.getAttribute(elem, "todir", toDirName))
6654             return false;
6655         if (toDirName.size() > 0)
6656             cptype = CP_TODIR;
6657         String ret;
6658         if (!parent.getAttribute(elem, "verbose", ret))
6659             return false;
6660         if (ret.size()>0 && !getBool(ret, verbose))
6661             return false;
6662             
6663         haveFileSet = false;
6664         
6665         std::vector<Element *> children = elem->getChildren();
6666         for (unsigned int i=0 ; i<children.size() ; i++)
6667             {
6668             Element *child = children[i];
6669             String tagName = child->getName();
6670             if (tagName == "fileset")
6671                 {
6672                 if (!parseFileSet(child, parent, fileSet))
6673                     {
6674                     error("problem getting fileset");
6675                     return false;
6676                     }
6677                 haveFileSet = true;
6678                 }
6679             }
6681         //Perform validity checks
6682         if (fileName.size()>0 && fileSet.size()>0)
6683             {
6684             error("<copy> can only have one of : file= and <fileset>");
6685             return false;
6686             }
6687         if (toFileName.size()>0 && toDirName.size()>0)
6688             {
6689             error("<copy> can only have one of : tofile= or todir=");
6690             return false;
6691             }
6692         if (haveFileSet && toDirName.size()==0)
6693             {
6694             error("a <copy> task with a <fileset> must have : todir=");
6695             return false;
6696             }
6697         if (cptype == CP_TOFILE && fileName.size()==0)
6698             {
6699             error("<copy> tofile= must be associated with : file=");
6700             return false;
6701             }
6702         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6703             {
6704             error("<copy> todir= must be associated with : file= or <fileset>");
6705             return false;
6706             }
6708         return true;
6709         }
6710         
6711 private:
6713     int cptype;
6714     String fileName;
6715     FileSet fileSet;
6716     String toFileName;
6717     String toDirName;
6718     bool verbose;
6719     bool haveFileSet;
6720 };
6723 /**
6724  *
6725  */
6726 class TaskDelete : public Task
6728 public:
6730     typedef enum
6731         {
6732         DEL_FILE,
6733         DEL_DIR,
6734         DEL_FILESET
6735         } DeleteType;
6737     TaskDelete(MakeBase &par) : Task(par)
6738         { 
6739           type        = TASK_DELETE;
6740           name        = "delete";
6741           delType     = DEL_FILE;
6742           verbose     = false;
6743           quiet       = false;
6744           failOnError = true;
6745         }
6747     virtual ~TaskDelete()
6748         {}
6750     virtual bool execute()
6751         {
6752         struct stat finfo;
6753         switch (delType)
6754             {
6755             case DEL_FILE:
6756                 {
6757                 status("          : %s", fileName.c_str());
6758                 String fullName = parent.resolve(fileName);
6759                 char *fname = (char *)fullName.c_str();
6760                 //does not exist
6761                 if (stat(fname, &finfo)<0)
6762                     return true;
6763                 //exists but is not a regular file
6764                 if (!S_ISREG(finfo.st_mode))
6765                     {
6766                     error("<delete> failed. '%s' exists and is not a regular file",
6767                           fname);
6768                     return false;
6769                     }
6770                 if (remove(fname)<0)
6771                     {
6772                     error("<delete> failed: %s", strerror(errno));
6773                     return false;
6774                     }
6775                 return true;
6776                 }
6777             case DEL_DIR:
6778                 {
6779                 taskstatus("%s", dirName.c_str());
6780                 String fullDir = parent.resolve(dirName);
6781                 if (!removeDirectory(fullDir))
6782                     return false;
6783                 return true;
6784                 }
6785             }
6786         return true;
6787         }
6789     virtual bool parse(Element *elem)
6790         {
6791         if (!parent.getAttribute(elem, "file", fileName))
6792             return false;
6793         if (fileName.size() > 0)
6794             delType = DEL_FILE;
6795         if (!parent.getAttribute(elem, "dir", dirName))
6796             return false;
6797         if (dirName.size() > 0)
6798             delType = DEL_DIR;
6799         if (fileName.size()>0 && dirName.size()>0)
6800             {
6801             error("<delete> can have one attribute of file= or dir=");
6802             return false;
6803             }
6804         if (fileName.size()==0 && dirName.size()==0)
6805             {
6806             error("<delete> must have one attribute of file= or dir=");
6807             return false;
6808             }
6809         String ret;
6810         if (!parent.getAttribute(elem, "verbose", ret))
6811             return false;
6812         if (ret.size()>0 && !getBool(ret, verbose))
6813             return false;
6814         if (!parent.getAttribute(elem, "quiet", ret))
6815             return false;
6816         if (ret.size()>0 && !getBool(ret, quiet))
6817             return false;
6818         if (!parent.getAttribute(elem, "failonerror", ret))
6819             return false;
6820         if (ret.size()>0 && !getBool(ret, failOnError))
6821             return false;
6822         return true;
6823         }
6825 private:
6827     int delType;
6828     String dirName;
6829     String fileName;
6830     bool verbose;
6831     bool quiet;
6832     bool failOnError;
6833 };
6836 /**
6837  *
6838  */
6839 class TaskJar : public Task
6841 public:
6843     TaskJar(MakeBase &par) : Task(par)
6844         { type = TASK_JAR; name = "jar"; command = "jar";}
6846     virtual ~TaskJar()
6847         {}
6849     virtual bool execute()
6850         {
6851         String cmd = command;
6852         cmd.append(" -cf ");
6853         cmd.append(destfile);
6854         cmd.append(" -C ");
6855         cmd.append(basedir);
6856         cmd.append(" .");
6858         String execCmd = cmd;
6860         String outString, errString;
6861         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6862         if (!ret)
6863             {
6864             error("<jar> command '%s' failed :\n %s",
6865                                       execCmd.c_str(), errString.c_str());
6866             return false;
6867             }
6868         return true;
6869         }
6871     virtual bool parse(Element *elem)
6872         {
6873         String s;
6874         if (!parent.getAttribute(elem, "command", s))
6875             return false;
6876         if (s.size() > 0)
6877             command = s;
6878         if (!parent.getAttribute(elem, "basedir", basedir))
6879             return false;
6880         if (!parent.getAttribute(elem, "destfile", destfile))
6881             return false;
6882         if (basedir.size() == 0 || destfile.size() == 0)
6883             {
6884             error("<jar> required both basedir and destfile attributes to be set");
6885             return false;
6886             }
6887         return true;
6888         }
6890 private:
6891     String command;
6892     String basedir;
6893     String destfile;
6894 };
6897 /**
6898  *
6899  */
6900 class TaskJavac : public Task
6902 public:
6904     TaskJavac(MakeBase &par) : Task(par)
6905         { 
6906         type = TASK_JAVAC; name = "javac";
6907         command = "javac";
6908         }
6910     virtual ~TaskJavac()
6911         {}
6913     virtual bool execute()
6914         {
6915         std::vector<String> fileList;
6916         if (!listFiles(srcdir, "", fileList))
6917             {
6918             return false;
6919             }
6920         String cmd = command;
6921         cmd.append(" -d ");
6922         cmd.append(destdir);
6923         cmd.append(" -classpath ");
6924         cmd.append(destdir);
6925         cmd.append(" -sourcepath ");
6926         cmd.append(srcdir);
6927         cmd.append(" ");
6928         if (target.size()>0)
6929             {
6930             cmd.append(" -target ");
6931             cmd.append(target);
6932             cmd.append(" ");
6933             }
6934         String fname = "javalist.btool";
6935         FILE *f = fopen(fname.c_str(), "w");
6936         int count = 0;
6937         for (unsigned int i=0 ; i<fileList.size() ; i++)
6938             {
6939             String fname = fileList[i];
6940             String srcName = fname;
6941             if (fname.size()<6) //x.java
6942                 continue;
6943             if (fname.compare(fname.size()-5, 5, ".java") != 0)
6944                 continue;
6945             String baseName = fname.substr(0, fname.size()-5);
6946             String destName = baseName;
6947             destName.append(".class");
6949             String fullSrc = srcdir;
6950             fullSrc.append("/");
6951             fullSrc.append(fname);
6952             String fullDest = destdir;
6953             fullDest.append("/");
6954             fullDest.append(destName);
6955             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6956             if (!isNewerThan(fullSrc, fullDest))
6957                 continue;
6959             count++;
6960             fprintf(f, "%s\n", fullSrc.c_str());
6961             }
6962         fclose(f);
6963         if (!count)
6964             {
6965             taskstatus("nothing to do");
6966             return true;
6967             }
6969         taskstatus("compiling %d files", count);
6971         String execCmd = cmd;
6972         execCmd.append("@");
6973         execCmd.append(fname);
6975         String outString, errString;
6976         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6977         if (!ret)
6978             {
6979             error("<javac> command '%s' failed :\n %s",
6980                                       execCmd.c_str(), errString.c_str());
6981             return false;
6982             }
6983         return true;
6984         }
6986     virtual bool parse(Element *elem)
6987         {
6988         String s;
6989         if (!parent.getAttribute(elem, "command", s))
6990             return false;
6991         if (s.size() > 0)
6992             command = s;
6993         if (!parent.getAttribute(elem, "srcdir", srcdir))
6994             return false;
6995         if (!parent.getAttribute(elem, "destdir", destdir))
6996             return false;
6997         if (srcdir.size() == 0 || destdir.size() == 0)
6998             {
6999             error("<javac> required both srcdir and destdir attributes to be set");
7000             return false;
7001             }
7002         if (!parent.getAttribute(elem, "target", target))
7003             return false;
7004         return true;
7005         }
7007 private:
7009     String command;
7010     String srcdir;
7011     String destdir;
7012     String target;
7014 };
7017 /**
7018  *
7019  */
7020 class TaskLink : public Task
7022 public:
7024     TaskLink(MakeBase &par) : Task(par)
7025         {
7026         type = TASK_LINK; name = "link";
7027         command = "g++";
7028         doStrip = false;
7029         stripCommand = "strip";
7030         objcopyCommand = "objcopy";
7031         }
7033     virtual ~TaskLink()
7034         {}
7036     virtual bool execute()
7037         {
7038         if (!listFiles(parent, fileSet))
7039             return false;
7040         String fileSetDir = fileSet.getDirectory();
7041         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7042         bool doit = false;
7043         String fullTarget = parent.resolve(fileName);
7044         String cmd = command;
7045         cmd.append(" -o ");
7046         cmd.append(fullTarget);
7047         cmd.append(" ");
7048         cmd.append(flags);
7049         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7050             {
7051             cmd.append(" ");
7052             String obj;
7053             if (fileSetDir.size()>0)
7054                 {
7055                 obj.append(fileSetDir);
7056                 obj.append("/");
7057                 }
7058             obj.append(fileSet[i]);
7059             String fullObj = parent.resolve(obj);
7060             String nativeFullObj = getNativePath(fullObj);
7061             cmd.append(nativeFullObj);
7062             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7063             //          fullObj.c_str());
7064             if (isNewerThan(fullObj, fullTarget))
7065                 doit = true;
7066             }
7067         cmd.append(" ");
7068         cmd.append(libs);
7069         if (!doit)
7070             {
7071             //trace("link not needed");
7072             return true;
7073             }
7074         //trace("LINK cmd:%s", cmd.c_str());
7077         String outbuf, errbuf;
7078         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7079             {
7080             error("LINK problem: %s", errbuf.c_str());
7081             return false;
7082             }
7084         if (symFileName.size()>0)
7085             {
7086             String symFullName = parent.resolve(symFileName);
7087             cmd = objcopyCommand;
7088             cmd.append(" --only-keep-debug ");
7089             cmd.append(getNativePath(fullTarget));
7090             cmd.append(" ");
7091             cmd.append(getNativePath(symFullName));
7092             if (!executeCommand(cmd, "", outbuf, errbuf))
7093                 {
7094                 error("<strip> symbol file failed : %s", errbuf.c_str());
7095                 return false;
7096                 }
7097             }
7098             
7099         if (doStrip)
7100             {
7101             cmd = stripCommand;
7102             cmd.append(" ");
7103             cmd.append(getNativePath(fullTarget));
7104             if (!executeCommand(cmd, "", outbuf, errbuf))
7105                {
7106                error("<strip> failed : %s", errbuf.c_str());
7107                return false;
7108                }
7109             }
7111         return true;
7112         }
7114     virtual bool parse(Element *elem)
7115         {
7116         String s;
7117         if (!parent.getAttribute(elem, "command", s))
7118             return false;
7119         if (s.size()>0)
7120             command = s;
7121         if (!parent.getAttribute(elem, "objcopycommand", s))
7122             return false;
7123         if (s.size()>0)
7124             objcopyCommand = s;
7125         if (!parent.getAttribute(elem, "stripcommand", s))
7126             return false;
7127         if (s.size()>0)
7128             stripCommand = s;
7129         if (!parent.getAttribute(elem, "out", fileName))
7130             return false;
7131         if (!parent.getAttribute(elem, "strip", s))
7132             return false;
7133         if (s.size()>0 && !getBool(s, doStrip))
7134             return false;
7135         if (!parent.getAttribute(elem, "symfile", symFileName))
7136             return false;
7137             
7138         std::vector<Element *> children = elem->getChildren();
7139         for (unsigned int i=0 ; i<children.size() ; i++)
7140             {
7141             Element *child = children[i];
7142             String tagName = child->getName();
7143             if (tagName == "fileset")
7144                 {
7145                 if (!parseFileSet(child, parent, fileSet))
7146                     return false;
7147                 }
7148             else if (tagName == "flags")
7149                 {
7150                 if (!parent.getValue(child, flags))
7151                     return false;
7152                 flags = strip(flags);
7153                 }
7154             else if (tagName == "libs")
7155                 {
7156                 if (!parent.getValue(child, libs))
7157                     return false;
7158                 libs = strip(libs);
7159                 }
7160             }
7161         return true;
7162         }
7164 private:
7166     String  command;
7167     String  fileName;
7168     String  flags;
7169     String  libs;
7170     FileSet fileSet;
7171     bool    doStrip;
7172     String  symFileName;
7173     String  stripCommand;
7174     String  objcopyCommand;
7176 };
7180 /**
7181  * Create a named directory
7182  */
7183 class TaskMakeFile : public Task
7185 public:
7187     TaskMakeFile(MakeBase &par) : Task(par)
7188         { type = TASK_MAKEFILE; name = "makefile"; }
7190     virtual ~TaskMakeFile()
7191         {}
7193     virtual bool execute()
7194         {
7195         taskstatus("%s", fileName.c_str());
7196         String fullName = parent.resolve(fileName);
7197         if (!isNewerThan(parent.getURI().getPath(), fullName))
7198             {
7199             //trace("skipped <makefile>");
7200             return true;
7201             }
7202         String fullNative = getNativePath(fullName);
7203         //trace("fullName:%s", fullName.c_str());
7204         FILE *f = fopen(fullNative.c_str(), "w");
7205         if (!f)
7206             {
7207             error("<makefile> could not open %s for writing : %s",
7208                 fullName.c_str(), strerror(errno));
7209             return false;
7210             }
7211         for (unsigned int i=0 ; i<text.size() ; i++)
7212             fputc(text[i], f);
7213         fputc('\n', f);
7214         fclose(f);
7215         return true;
7216         }
7218     virtual bool parse(Element *elem)
7219         {
7220         if (!parent.getAttribute(elem, "file", fileName))
7221             return false;
7222         if (fileName.size() == 0)
7223             {
7224             error("<makefile> requires 'file=\"filename\"' attribute");
7225             return false;
7226             }
7227         if (!parent.getValue(elem, text))
7228             return false;
7229         text = leftJustify(text);
7230         //trace("dirname:%s", dirName.c_str());
7231         return true;
7232         }
7234 private:
7236     String fileName;
7237     String text;
7238 };
7242 /**
7243  * Create a named directory
7244  */
7245 class TaskMkDir : public Task
7247 public:
7249     TaskMkDir(MakeBase &par) : Task(par)
7250         { type = TASK_MKDIR; name = "mkdir"; }
7252     virtual ~TaskMkDir()
7253         {}
7255     virtual bool execute()
7256         {
7257         taskstatus("%s", dirName.c_str());
7258         String fullDir = parent.resolve(dirName);
7259         //trace("fullDir:%s", fullDir.c_str());
7260         if (!createDirectory(fullDir))
7261             return false;
7262         return true;
7263         }
7265     virtual bool parse(Element *elem)
7266         {
7267         if (!parent.getAttribute(elem, "dir", dirName))
7268             return false;
7269         if (dirName.size() == 0)
7270             {
7271             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7272             return false;
7273             }
7274         return true;
7275         }
7277 private:
7279     String dirName;
7280 };
7284 /**
7285  * Create a named directory
7286  */
7287 class TaskMsgFmt: public Task
7289 public:
7291     TaskMsgFmt(MakeBase &par) : Task(par)
7292          {
7293          type    = TASK_MSGFMT;
7294          name    = "msgfmt";
7295          command = "msgfmt";
7296          owndir  = false;
7297          outName = "";
7298          }
7300     virtual ~TaskMsgFmt()
7301         {}
7303     virtual bool execute()
7304         {
7305         if (!listFiles(parent, fileSet))
7306             return false;
7307         String fileSetDir = fileSet.getDirectory();
7309         //trace("msgfmt: %d", fileSet.size());
7310         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7311             {
7312             String fileName = fileSet[i];
7313             if (getSuffix(fileName) != "po")
7314                 continue;
7315             String sourcePath;
7316             if (fileSetDir.size()>0)
7317                 {
7318                 sourcePath.append(fileSetDir);
7319                 sourcePath.append("/");
7320                 }
7321             sourcePath.append(fileName);
7322             String fullSource = parent.resolve(sourcePath);
7324             String destPath;
7325             if (toDirName.size()>0)
7326                 {
7327                 destPath.append(toDirName);
7328                 destPath.append("/");
7329                 }
7330             if (owndir)
7331                 {
7332                 String subdir = fileName;
7333                 unsigned int pos = subdir.find_last_of('.');
7334                 if (pos != subdir.npos)
7335                     subdir = subdir.substr(0, pos);
7336                 destPath.append(subdir);
7337                 destPath.append("/");
7338                 }
7339             //Pick the output file name
7340             if (outName.size() > 0)
7341                 {
7342                 destPath.append(outName);
7343                 }
7344             else
7345                 {
7346                 destPath.append(fileName);
7347                 destPath[destPath.size()-2] = 'm';
7348                 }
7350             String fullDest = parent.resolve(destPath);
7352             if (!isNewerThan(fullSource, fullDest))
7353                 {
7354                 //trace("skip %s", fullSource.c_str());
7355                 continue;
7356                 }
7357                 
7358             String cmd = command;
7359             cmd.append(" ");
7360             cmd.append(fullSource);
7361             cmd.append(" -o ");
7362             cmd.append(fullDest);
7363             
7364             int pos = fullDest.find_last_of('/');
7365             if (pos>0)
7366                 {
7367                 String fullDestPath = fullDest.substr(0, pos);
7368                 if (!createDirectory(fullDestPath))
7369                     return false;
7370                 }
7374             String outString, errString;
7375             if (!executeCommand(cmd.c_str(), "", outString, errString))
7376                 {
7377                 error("<msgfmt> problem: %s", errString.c_str());
7378                 return false;
7379                 }
7380             }
7382         return true;
7383         }
7385     virtual bool parse(Element *elem)
7386         {
7387         String s;
7388         if (!parent.getAttribute(elem, "command", s))
7389             return false;
7390         if (s.size()>0)
7391             command = s;
7392         if (!parent.getAttribute(elem, "todir", toDirName))
7393             return false;
7394         if (!parent.getAttribute(elem, "out", outName))
7395             return false;
7396         if (!parent.getAttribute(elem, "owndir", s))
7397             return false;
7398         if (s.size()>0 && !getBool(s, owndir))
7399             return false;
7400             
7401         std::vector<Element *> children = elem->getChildren();
7402         for (unsigned int i=0 ; i<children.size() ; i++)
7403             {
7404             Element *child = children[i];
7405             String tagName = child->getName();
7406             if (tagName == "fileset")
7407                 {
7408                 if (!parseFileSet(child, parent, fileSet))
7409                     return false;
7410                 }
7411             }
7412         return true;
7413         }
7415 private:
7417     String  command;
7418     String  toDirName;
7419     String  outName;
7420     FileSet fileSet;
7421     bool    owndir;
7423 };
7427 /**
7428  *  Perform a Package-Config query similar to pkg-config
7429  */
7430 class TaskPkgConfig : public Task
7432 public:
7434     typedef enum
7435         {
7436         PKG_CONFIG_QUERY_CFLAGS,
7437         PKG_CONFIG_QUERY_LIBS,
7438         PKG_CONFIG_QUERY_ALL
7439         } QueryTypes;
7441     TaskPkgConfig(MakeBase &par) : Task(par)
7442         {
7443         type = TASK_PKG_CONFIG;
7444         name = "pkg-config";
7445         }
7447     virtual ~TaskPkgConfig()
7448         {}
7450     virtual bool execute()
7451         {
7452         String path = parent.resolve(pkg_config_path);
7453         PkgConfig pkgconfig;
7454         pkgconfig.setPath(path);
7455         pkgconfig.setPrefix(prefix);
7456         if (!pkgconfig.query(pkgName))
7457             {
7458             error("<pkg-config> query failed for '%s", name.c_str());
7459             return false;
7460             }
7461         String ret;
7462         switch (query)
7463             {
7464             case PKG_CONFIG_QUERY_CFLAGS:
7465                 {
7466                 ret = pkgconfig.getCflags();
7467                 break;
7468                 }
7469             case PKG_CONFIG_QUERY_LIBS:
7470                 {
7471                 ret = pkgconfig.getLibs();
7472                 break;
7473                 }
7474             case PKG_CONFIG_QUERY_ALL:
7475                 {
7476                 ret = pkgconfig.getAll();
7477                 break;
7478                 }
7479             default:
7480                 {
7481                 error("<pkg-config> unhandled query : %d", query);
7482                 return false;
7483                 }
7484             
7485             }
7486         taskstatus("%s", ret.c_str());
7487         parent.setProperty(propName, ret);
7488         return true;
7489         }
7491     virtual bool parse(Element *elem)
7492         {
7493         String s;
7494         //# NAME
7495         if (!parent.getAttribute(elem, "name", s))
7496             return false;
7497         if (s.size()>0)
7498            pkgName = s;
7499         else
7500             {
7501             error("<pkg-config> requires 'name=\"package\"' attribute");
7502             return false;
7503             }
7505         //# PROPERTY
7506         if (!parent.getAttribute(elem, "property", s))
7507             return false;
7508         if (s.size()>0)
7509            propName = s;
7510         else
7511             {
7512             error("<pkg-config> requires 'property=\"name\"' attribute");
7513             return false;
7514             }
7515         if (parent.hasProperty(propName))
7516             {
7517             error("<pkg-config> property '%s' is already defined",
7518                           propName.c_str());
7519             return false;
7520             }
7521         parent.setProperty(propName, "undefined");
7523         //# PATH
7524         if (!parent.getAttribute(elem, "path", s))
7525             return false;
7526         if (s.size()>0)
7527            pkg_config_path = s;
7529         //# PREFIX
7530         if (!parent.getAttribute(elem, "prefix", s))
7531             return false;
7532         if (s.size()>0)
7533            prefix = s;
7535         //# QUERY
7536         if (!parent.getAttribute(elem, "query", s))
7537             return false;
7538         if (s == "cflags")
7539             query = PKG_CONFIG_QUERY_CFLAGS;
7540         else if (s == "libs")
7541             query = PKG_CONFIG_QUERY_LIBS;
7542         else if (s == "both")
7543             query = PKG_CONFIG_QUERY_ALL;
7544         else
7545             {
7546             error("<pkg-config> requires 'query=\"type\"' attribute");
7547             error("where type = cflags, libs, or both");
7548             return false;
7549             }
7550         return true;
7551         }
7553 private:
7555     String pkgName;
7556     String prefix;
7557     String propName;
7558     String pkg_config_path;
7559     int query;
7561 };
7568 /**
7569  *  Process an archive to allow random access
7570  */
7571 class TaskRanlib : public Task
7573 public:
7575     TaskRanlib(MakeBase &par) : Task(par)
7576         {
7577         type = TASK_RANLIB; name = "ranlib";
7578         command = "ranlib";
7579         }
7581     virtual ~TaskRanlib()
7582         {}
7584     virtual bool execute()
7585         {
7586         String fullName = parent.resolve(fileName);
7587         //trace("fullDir:%s", fullDir.c_str());
7588         String cmd = command;
7589         cmd.append(" ");
7590         cmd.append(fullName);
7591         String outbuf, errbuf;
7592         if (!executeCommand(cmd, "", outbuf, errbuf))
7593             return false;
7594         return true;
7595         }
7597     virtual bool parse(Element *elem)
7598         {
7599         String s;
7600         if (!parent.getAttribute(elem, "command", s))
7601             return false;
7602         if (s.size()>0)
7603            command = s;
7604         if (!parent.getAttribute(elem, "file", fileName))
7605             return false;
7606         if (fileName.size() == 0)
7607             {
7608             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7609             return false;
7610             }
7611         return true;
7612         }
7614 private:
7616     String fileName;
7617     String command;
7618 };
7622 /**
7623  * Run the "ar" command to archive .o's into a .a
7624  */
7625 class TaskRC : public Task
7627 public:
7629     TaskRC(MakeBase &par) : Task(par)
7630         {
7631         type = TASK_RC; name = "rc";
7632         command = "windres";
7633         }
7635     virtual ~TaskRC()
7636         {}
7638     virtual bool execute()
7639         {
7640         String fullFile = parent.resolve(fileName);
7641         String fullOut  = parent.resolve(outName);
7642         if (!isNewerThan(fullFile, fullOut))
7643             return true;
7644         String cmd = command;
7645         cmd.append(" -o ");
7646         cmd.append(fullOut);
7647         cmd.append(" ");
7648         cmd.append(flags);
7649         cmd.append(" ");
7650         cmd.append(fullFile);
7652         String outString, errString;
7653         if (!executeCommand(cmd.c_str(), "", outString, errString))
7654             {
7655             error("RC problem: %s", errString.c_str());
7656             return false;
7657             }
7658         return true;
7659         }
7661     virtual bool parse(Element *elem)
7662         {
7663         if (!parent.getAttribute(elem, "command", command))
7664             return false;
7665         if (!parent.getAttribute(elem, "file", fileName))
7666             return false;
7667         if (!parent.getAttribute(elem, "out", outName))
7668             return false;
7669         std::vector<Element *> children = elem->getChildren();
7670         for (unsigned int i=0 ; i<children.size() ; i++)
7671             {
7672             Element *child = children[i];
7673             String tagName = child->getName();
7674             if (tagName == "flags")
7675                 {
7676                 if (!parent.getValue(child, flags))
7677                     return false;
7678                 }
7679             }
7680         return true;
7681         }
7683 private:
7685     String command;
7686     String flags;
7687     String fileName;
7688     String outName;
7690 };
7694 /**
7695  *  Collect .o's into a .so or DLL
7696  */
7697 class TaskSharedLib : public Task
7699 public:
7701     TaskSharedLib(MakeBase &par) : Task(par)
7702         {
7703         type = TASK_SHAREDLIB; name = "dll";
7704         command = "dllwrap";
7705         }
7707     virtual ~TaskSharedLib()
7708         {}
7710     virtual bool execute()
7711         {
7712         //trace("###########HERE %d", fileSet.size());
7713         bool doit = false;
7714         
7715         String fullOut = parent.resolve(fileName);
7716         //trace("ar fullout: %s", fullOut.c_str());
7717         
7718         if (!listFiles(parent, fileSet))
7719             return false;
7720         String fileSetDir = fileSet.getDirectory();
7722         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7723             {
7724             String fname;
7725             if (fileSetDir.size()>0)
7726                 {
7727                 fname.append(fileSetDir);
7728                 fname.append("/");
7729                 }
7730             fname.append(fileSet[i]);
7731             String fullName = parent.resolve(fname);
7732             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7733             if (isNewerThan(fullName, fullOut))
7734                 doit = true;
7735             }
7736         //trace("Needs it:%d", doit);
7737         if (!doit)
7738             {
7739             return true;
7740             }
7742         String cmd = "dllwrap";
7743         cmd.append(" -o ");
7744         cmd.append(fullOut);
7745         if (defFileName.size()>0)
7746             {
7747             cmd.append(" --def ");
7748             cmd.append(defFileName);
7749             cmd.append(" ");
7750             }
7751         if (impFileName.size()>0)
7752             {
7753             cmd.append(" --implib ");
7754             cmd.append(impFileName);
7755             cmd.append(" ");
7756             }
7757         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7758             {
7759             String fname;
7760             if (fileSetDir.size()>0)
7761                 {
7762                 fname.append(fileSetDir);
7763                 fname.append("/");
7764                 }
7765             fname.append(fileSet[i]);
7766             String fullName = parent.resolve(fname);
7768             cmd.append(" ");
7769             cmd.append(fullName);
7770             }
7771         cmd.append(" ");
7772         cmd.append(libs);
7774         String outString, errString;
7775         if (!executeCommand(cmd.c_str(), "", outString, errString))
7776             {
7777             error("<sharedlib> problem: %s", errString.c_str());
7778             return false;
7779             }
7781         return true;
7782         }
7784     virtual bool parse(Element *elem)
7785         {
7786         if (!parent.getAttribute(elem, "file", fileName))
7787             return false;
7788         if (!parent.getAttribute(elem, "import", impFileName))
7789             return false;
7790         if (!parent.getAttribute(elem, "def", defFileName))
7791             return false;
7792             
7793         std::vector<Element *> children = elem->getChildren();
7794         for (unsigned int i=0 ; i<children.size() ; i++)
7795             {
7796             Element *child = children[i];
7797             String tagName = child->getName();
7798             if (tagName == "fileset")
7799                 {
7800                 if (!parseFileSet(child, parent, fileSet))
7801                     return false;
7802                 }
7803             else if (tagName == "libs")
7804                 {
7805                 if (!parent.getValue(child, libs))
7806                     return false;
7807                 libs = strip(libs);
7808                 }
7809             }
7810         return true;
7811         }
7813 private:
7815     String command;
7816     String fileName;
7817     String defFileName;
7818     String impFileName;
7819     FileSet fileSet;
7820     String libs;
7822 };
7826 /**
7827  * Run the "ar" command to archive .o's into a .a
7828  */
7829 class TaskStaticLib : public Task
7831 public:
7833     TaskStaticLib(MakeBase &par) : Task(par)
7834         {
7835         type = TASK_STATICLIB; name = "staticlib";
7836         command = "ar crv";
7837         }
7839     virtual ~TaskStaticLib()
7840         {}
7842     virtual bool execute()
7843         {
7844         //trace("###########HERE %d", fileSet.size());
7845         bool doit = false;
7846         
7847         String fullOut = parent.resolve(fileName);
7848         //trace("ar fullout: %s", fullOut.c_str());
7849         
7850         if (!listFiles(parent, fileSet))
7851             return false;
7852         String fileSetDir = fileSet.getDirectory();
7854         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7855             {
7856             String fname;
7857             if (fileSetDir.size()>0)
7858                 {
7859                 fname.append(fileSetDir);
7860                 fname.append("/");
7861                 }
7862             fname.append(fileSet[i]);
7863             String fullName = parent.resolve(fname);
7864             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7865             if (isNewerThan(fullName, fullOut))
7866                 doit = true;
7867             }
7868         //trace("Needs it:%d", doit);
7869         if (!doit)
7870             {
7871             return true;
7872             }
7874         String cmd = command;
7875         cmd.append(" ");
7876         cmd.append(fullOut);
7877         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7878             {
7879             String fname;
7880             if (fileSetDir.size()>0)
7881                 {
7882                 fname.append(fileSetDir);
7883                 fname.append("/");
7884                 }
7885             fname.append(fileSet[i]);
7886             String fullName = parent.resolve(fname);
7888             cmd.append(" ");
7889             cmd.append(fullName);
7890             }
7892         String outString, errString;
7893         if (!executeCommand(cmd.c_str(), "", outString, errString))
7894             {
7895             error("<staticlib> problem: %s", errString.c_str());
7896             return false;
7897             }
7899         return true;
7900         }
7903     virtual bool parse(Element *elem)
7904         {
7905         String s;
7906         if (!parent.getAttribute(elem, "command", s))
7907             return false;
7908         if (s.size()>0)
7909             command = s;
7910         if (!parent.getAttribute(elem, "file", fileName))
7911             return false;
7912             
7913         std::vector<Element *> children = elem->getChildren();
7914         for (unsigned int i=0 ; i<children.size() ; i++)
7915             {
7916             Element *child = children[i];
7917             String tagName = child->getName();
7918             if (tagName == "fileset")
7919                 {
7920                 if (!parseFileSet(child, parent, fileSet))
7921                     return false;
7922                 }
7923             }
7924         return true;
7925         }
7927 private:
7929     String command;
7930     String fileName;
7931     FileSet fileSet;
7933 };
7938 /**
7939  * Strip an executable
7940  */
7941 class TaskStrip : public Task
7943 public:
7945     TaskStrip(MakeBase &par) : Task(par)
7946         { type = TASK_STRIP; name = "strip"; }
7948     virtual ~TaskStrip()
7949         {}
7951     virtual bool execute()
7952         {
7953         String fullName = parent.resolve(fileName);
7954         //trace("fullDir:%s", fullDir.c_str());
7955         String cmd;
7956         String outbuf, errbuf;
7958         if (symFileName.size()>0)
7959             {
7960             String symFullName = parent.resolve(symFileName);
7961             cmd = "objcopy --only-keep-debug ";
7962             cmd.append(getNativePath(fullName));
7963             cmd.append(" ");
7964             cmd.append(getNativePath(symFullName));
7965             if (!executeCommand(cmd, "", outbuf, errbuf))
7966                 {
7967                 error("<strip> symbol file failed : %s", errbuf.c_str());
7968                 return false;
7969                 }
7970             }
7971             
7972         cmd = "strip ";
7973         cmd.append(getNativePath(fullName));
7974         if (!executeCommand(cmd, "", outbuf, errbuf))
7975             {
7976             error("<strip> failed : %s", errbuf.c_str());
7977             return false;
7978             }
7979         return true;
7980         }
7982     virtual bool parse(Element *elem)
7983         {
7984         if (!parent.getAttribute(elem, "file", fileName))
7985             return false;
7986         if (!parent.getAttribute(elem, "symfile", symFileName))
7987             return false;
7988         if (fileName.size() == 0)
7989             {
7990             error("<strip> requires 'file=\"fileName\"' attribute");
7991             return false;
7992             }
7993         return true;
7994         }
7996 private:
7998     String fileName;
7999     String symFileName;
8000 };
8003 /**
8004  *
8005  */
8006 class TaskTouch : public Task
8008 public:
8010     TaskTouch(MakeBase &par) : Task(par)
8011         { type = TASK_TOUCH; name = "touch"; }
8013     virtual ~TaskTouch()
8014         {}
8016     virtual bool execute()
8017         {
8018         String fullName = parent.resolve(fileName);
8019         String nativeFile = getNativePath(fullName);
8020         if (!isRegularFile(fullName) && !isDirectory(fullName))
8021             {            
8022             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8023             int ret = creat(nativeFile.c_str(), 0666);
8024             if (ret != 0) 
8025                 {
8026                 error("<touch> could not create '%s' : %s",
8027                     nativeFile.c_str(), strerror(ret));
8028                 return false;
8029                 }
8030             return true;
8031             }
8032         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8033         if (ret != 0)
8034             {
8035             error("<touch> could not update the modification time for '%s' : %s",
8036                 nativeFile.c_str(), strerror(ret));
8037             return false;
8038             }
8039         return true;
8040         }
8042     virtual bool parse(Element *elem)
8043         {
8044         //trace("touch parse");
8045         if (!parent.getAttribute(elem, "file", fileName))
8046             return false;
8047         if (fileName.size() == 0)
8048             {
8049             error("<touch> requires 'file=\"fileName\"' attribute");
8050             return false;
8051             }
8052         return true;
8053         }
8055     String fileName;
8056 };
8059 /**
8060  *
8061  */
8062 class TaskTstamp : public Task
8064 public:
8066     TaskTstamp(MakeBase &par) : Task(par)
8067         { type = TASK_TSTAMP; name = "tstamp"; }
8069     virtual ~TaskTstamp()
8070         {}
8072     virtual bool execute()
8073         {
8074         return true;
8075         }
8077     virtual bool parse(Element *elem)
8078         {
8079         //trace("tstamp parse");
8080         return true;
8081         }
8082 };
8086 /**
8087  *
8088  */
8089 Task *Task::createTask(Element *elem, int lineNr)
8091     String tagName = elem->getName();
8092     //trace("task:%s", tagName.c_str());
8093     Task *task = NULL;
8094     if (tagName == "cc")
8095         task = new TaskCC(parent);
8096     else if (tagName == "copy")
8097         task = new TaskCopy(parent);
8098     else if (tagName == "delete")
8099         task = new TaskDelete(parent);
8100     else if (tagName == "jar")
8101         task = new TaskJar(parent);
8102     else if (tagName == "javac")
8103         task = new TaskJavac(parent);
8104     else if (tagName == "link")
8105         task = new TaskLink(parent);
8106     else if (tagName == "makefile")
8107         task = new TaskMakeFile(parent);
8108     else if (tagName == "mkdir")
8109         task = new TaskMkDir(parent);
8110     else if (tagName == "msgfmt")
8111         task = new TaskMsgFmt(parent);
8112     else if (tagName == "pkg-config")
8113         task = new TaskPkgConfig(parent);
8114     else if (tagName == "ranlib")
8115         task = new TaskRanlib(parent);
8116     else if (tagName == "rc")
8117         task = new TaskRC(parent);
8118     else if (tagName == "sharedlib")
8119         task = new TaskSharedLib(parent);
8120     else if (tagName == "staticlib")
8121         task = new TaskStaticLib(parent);
8122     else if (tagName == "strip")
8123         task = new TaskStrip(parent);
8124     else if (tagName == "touch")
8125         task = new TaskTouch(parent);
8126     else if (tagName == "tstamp")
8127         task = new TaskTstamp(parent);
8128     else
8129         {
8130         error("Unknown task '%s'", tagName.c_str());
8131         return NULL;
8132         }
8134     task->setLine(lineNr);
8136     if (!task->parse(elem))
8137         {
8138         delete task;
8139         return NULL;
8140         }
8141     return task;
8146 //########################################################################
8147 //# T A R G E T
8148 //########################################################################
8150 /**
8151  *
8152  */
8153 class Target : public MakeBase
8156 public:
8158     /**
8159      *
8160      */
8161     Target(Make &par) : parent(par)
8162         { init(); }
8164     /**
8165      *
8166      */
8167     Target(const Target &other) : parent(other.parent)
8168         { init(); assign(other); }
8170     /**
8171      *
8172      */
8173     Target &operator=(const Target &other)
8174         { init(); assign(other); return *this; }
8176     /**
8177      *
8178      */
8179     virtual ~Target()
8180         { cleanup() ; }
8183     /**
8184      *
8185      */
8186     virtual Make &getParent()
8187         { return parent; }
8189     /**
8190      *
8191      */
8192     virtual String getName()
8193         { return name; }
8195     /**
8196      *
8197      */
8198     virtual void setName(const String &val)
8199         { name = val; }
8201     /**
8202      *
8203      */
8204     virtual String getDescription()
8205         { return description; }
8207     /**
8208      *
8209      */
8210     virtual void setDescription(const String &val)
8211         { description = val; }
8213     /**
8214      *
8215      */
8216     virtual void addDependency(const String &val)
8217         { deps.push_back(val); }
8219     /**
8220      *
8221      */
8222     virtual void parseDependencies(const String &val)
8223         { deps = tokenize(val, ", "); }
8225     /**
8226      *
8227      */
8228     virtual std::vector<String> &getDependencies()
8229         { return deps; }
8231     /**
8232      *
8233      */
8234     virtual String getIf()
8235         { return ifVar; }
8237     /**
8238      *
8239      */
8240     virtual void setIf(const String &val)
8241         { ifVar = val; }
8243     /**
8244      *
8245      */
8246     virtual String getUnless()
8247         { return unlessVar; }
8249     /**
8250      *
8251      */
8252     virtual void setUnless(const String &val)
8253         { unlessVar = val; }
8255     /**
8256      *
8257      */
8258     virtual void addTask(Task *val)
8259         { tasks.push_back(val); }
8261     /**
8262      *
8263      */
8264     virtual std::vector<Task *> &getTasks()
8265         { return tasks; }
8267 private:
8269     void init()
8270         {
8271         }
8273     void cleanup()
8274         {
8275         tasks.clear();
8276         }
8278     void assign(const Target &other)
8279         {
8280         //parent      = other.parent;
8281         name        = other.name;
8282         description = other.description;
8283         ifVar       = other.ifVar;
8284         unlessVar   = other.unlessVar;
8285         deps        = other.deps;
8286         tasks       = other.tasks;
8287         }
8289     Make &parent;
8291     String name;
8293     String description;
8295     String ifVar;
8297     String unlessVar;
8299     std::vector<String> deps;
8301     std::vector<Task *> tasks;
8303 };
8312 //########################################################################
8313 //# M A K E
8314 //########################################################################
8317 /**
8318  *
8319  */
8320 class Make : public MakeBase
8323 public:
8325     /**
8326      *
8327      */
8328     Make()
8329         { init(); }
8331     /**
8332      *
8333      */
8334     Make(const Make &other)
8335         { assign(other); }
8337     /**
8338      *
8339      */
8340     Make &operator=(const Make &other)
8341         { assign(other); return *this; }
8343     /**
8344      *
8345      */
8346     virtual ~Make()
8347         { cleanup(); }
8349     /**
8350      *
8351      */
8352     virtual std::map<String, Target> &getTargets()
8353         { return targets; }
8356     /**
8357      *
8358      */
8359     virtual String version()
8360         { return BUILDTOOL_VERSION; }
8362     /**
8363      * Overload a <property>
8364      */
8365     virtual bool specifyProperty(const String &name,
8366                                  const String &value);
8368     /**
8369      *
8370      */
8371     virtual bool run();
8373     /**
8374      *
8375      */
8376     virtual bool run(const String &target);
8380 private:
8382     /**
8383      *
8384      */
8385     void init();
8387     /**
8388      *
8389      */
8390     void cleanup();
8392     /**
8393      *
8394      */
8395     void assign(const Make &other);
8397     /**
8398      *
8399      */
8400     bool executeTask(Task &task);
8403     /**
8404      *
8405      */
8406     bool executeTarget(Target &target,
8407              std::set<String> &targetsCompleted);
8410     /**
8411      *
8412      */
8413     bool execute();
8415     /**
8416      *
8417      */
8418     bool checkTargetDependencies(Target &prop,
8419                     std::vector<String> &depList);
8421     /**
8422      *
8423      */
8424     bool parsePropertyFile(const String &fileName,
8425                            const String &prefix);
8427     /**
8428      *
8429      */
8430     bool parseProperty(Element *elem);
8432     /**
8433      *
8434      */
8435     bool parseFile();
8437     /**
8438      *
8439      */
8440     std::vector<String> glob(const String &pattern);
8443     //###############
8444     //# Fields
8445     //###############
8447     String projectName;
8449     String currentTarget;
8451     String defaultTarget;
8453     String specifiedTarget;
8455     String baseDir;
8457     String description;
8458     
8459     //std::vector<Property> properties;
8460     
8461     std::map<String, Target> targets;
8463     std::vector<Task *> allTasks;
8464     
8465     std::map<String, String> specifiedProperties;
8467 };
8470 //########################################################################
8471 //# C L A S S  M A I N T E N A N C E
8472 //########################################################################
8474 /**
8475  *
8476  */
8477 void Make::init()
8479     uri             = "build.xml";
8480     projectName     = "";
8481     currentTarget   = "";
8482     defaultTarget   = "";
8483     specifiedTarget = "";
8484     baseDir         = "";
8485     description     = "";
8486     envPrefix       = "";
8487     properties.clear();
8488     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8489         delete allTasks[i];
8490     allTasks.clear();
8495 /**
8496  *
8497  */
8498 void Make::cleanup()
8500     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8501         delete allTasks[i];
8502     allTasks.clear();
8507 /**
8508  *
8509  */
8510 void Make::assign(const Make &other)
8512     uri              = other.uri;
8513     projectName      = other.projectName;
8514     currentTarget    = other.currentTarget;
8515     defaultTarget    = other.defaultTarget;
8516     specifiedTarget  = other.specifiedTarget;
8517     baseDir          = other.baseDir;
8518     description      = other.description;
8519     properties       = other.properties;
8524 //########################################################################
8525 //# U T I L I T Y    T A S K S
8526 //########################################################################
8528 /**
8529  *  Perform a file globbing
8530  */
8531 std::vector<String> Make::glob(const String &pattern)
8533     std::vector<String> res;
8534     return res;
8538 //########################################################################
8539 //# P U B L I C    A P I
8540 //########################################################################
8544 /**
8545  *
8546  */
8547 bool Make::executeTarget(Target &target,
8548              std::set<String> &targetsCompleted)
8551     String name = target.getName();
8553     //First get any dependencies for this target
8554     std::vector<String> deps = target.getDependencies();
8555     for (unsigned int i=0 ; i<deps.size() ; i++)
8556         {
8557         String dep = deps[i];
8558         //Did we do it already?  Skip
8559         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8560             continue;
8561             
8562         std::map<String, Target> &tgts =
8563                target.getParent().getTargets();
8564         std::map<String, Target>::iterator iter =
8565                tgts.find(dep);
8566         if (iter == tgts.end())
8567             {
8568             error("Target '%s' dependency '%s' not found",
8569                       name.c_str(),  dep.c_str());
8570             return false;
8571             }
8572         Target depTarget = iter->second;
8573         if (!executeTarget(depTarget, targetsCompleted))
8574             {
8575             return false;
8576             }
8577         }
8579     status("##### Target : %s\n##### %s", name.c_str(),
8580             target.getDescription().c_str());
8582     //Now let's do the tasks
8583     std::vector<Task *> &tasks = target.getTasks();
8584     for (unsigned int i=0 ; i<tasks.size() ; i++)
8585         {
8586         Task *task = tasks[i];
8587         status("--- %s / %s", name.c_str(), task->getName().c_str());
8588         if (!task->execute())
8589             {
8590             return false;
8591             }
8592         }
8593         
8594     targetsCompleted.insert(name);
8595     
8596     return true;
8601 /**
8602  *  Main execute() method.  Start here and work
8603  *  up the dependency tree 
8604  */
8605 bool Make::execute()
8607     status("######## EXECUTE");
8609     //Determine initial target
8610     if (specifiedTarget.size()>0)
8611         {
8612         currentTarget = specifiedTarget;
8613         }
8614     else if (defaultTarget.size()>0)
8615         {
8616         currentTarget = defaultTarget;
8617         }
8618     else
8619         {
8620         error("execute: no specified or default target requested");
8621         return false;
8622         }
8624     std::map<String, Target>::iterator iter =
8625                targets.find(currentTarget);
8626     if (iter == targets.end())
8627         {
8628         error("Initial target '%s' not found",
8629                  currentTarget.c_str());
8630         return false;
8631         }
8632         
8633     //Now run
8634     Target target = iter->second;
8635     std::set<String> targetsCompleted;
8636     if (!executeTarget(target, targetsCompleted))
8637         {
8638         return false;
8639         }
8641     status("######## EXECUTE COMPLETE");
8642     return true;
8648 /**
8649  *
8650  */
8651 bool Make::checkTargetDependencies(Target &target, 
8652                             std::vector<String> &depList)
8654     String tgtName = target.getName().c_str();
8655     depList.push_back(tgtName);
8657     std::vector<String> deps = target.getDependencies();
8658     for (unsigned int i=0 ; i<deps.size() ; i++)
8659         {
8660         String dep = deps[i];
8661         //First thing entered was the starting Target
8662         if (dep == depList[0])
8663             {
8664             error("Circular dependency '%s' found at '%s'",
8665                       dep.c_str(), tgtName.c_str());
8666             std::vector<String>::iterator diter;
8667             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8668                 {
8669                 error("  %s", diter->c_str());
8670                 }
8671             return false;
8672             }
8674         std::map<String, Target> &tgts =
8675                   target.getParent().getTargets();
8676         std::map<String, Target>::iterator titer = tgts.find(dep);
8677         if (titer == tgts.end())
8678             {
8679             error("Target '%s' dependency '%s' not found",
8680                       tgtName.c_str(), dep.c_str());
8681             return false;
8682             }
8683         if (!checkTargetDependencies(titer->second, depList))
8684             {
8685             return false;
8686             }
8687         }
8688     return true;
8695 static int getword(int pos, const String &inbuf, String &result)
8697     int p = pos;
8698     int len = (int)inbuf.size();
8699     String val;
8700     while (p < len)
8701         {
8702         char ch = inbuf[p];
8703         if (!isalnum(ch) && ch!='.' && ch!='_')
8704             break;
8705         val.push_back(ch);
8706         p++;
8707         }
8708     result = val;
8709     return p;
8715 /**
8716  *
8717  */
8718 bool Make::parsePropertyFile(const String &fileName,
8719                              const String &prefix)
8721     FILE *f = fopen(fileName.c_str(), "r");
8722     if (!f)
8723         {
8724         error("could not open property file %s", fileName.c_str());
8725         return false;
8726         }
8727     int linenr = 0;
8728     while (!feof(f))
8729         {
8730         char buf[256];
8731         if (!fgets(buf, 255, f))
8732             break;
8733         linenr++;
8734         String s = buf;
8735         s = trim(s);
8736         int len = s.size();
8737         if (len == 0)
8738             continue;
8739         if (s[0] == '#')
8740             continue;
8741         String key;
8742         String val;
8743         int p = 0;
8744         int p2 = getword(p, s, key);
8745         if (p2 <= p)
8746             {
8747             error("property file %s, line %d: expected keyword",
8748                     fileName.c_str(), linenr);
8749             return false;
8750             }
8751         if (prefix.size() > 0)
8752             {
8753             key.insert(0, prefix);
8754             }
8756         //skip whitespace
8757         for (p=p2 ; p<len ; p++)
8758             if (!isspace(s[p]))
8759                 break;
8761         if (p>=len || s[p]!='=')
8762             {
8763             error("property file %s, line %d: expected '='",
8764                     fileName.c_str(), linenr);
8765             return false;
8766             }
8767         p++;
8769         //skip whitespace
8770         for ( ; p<len ; p++)
8771             if (!isspace(s[p]))
8772                 break;
8774         /* This way expects a word after the =
8775         p2 = getword(p, s, val);
8776         if (p2 <= p)
8777             {
8778             error("property file %s, line %d: expected value",
8779                     fileName.c_str(), linenr);
8780             return false;
8781             }
8782         */
8783         // This way gets the rest of the line after the =
8784         if (p>=len)
8785             {
8786             error("property file %s, line %d: expected value",
8787                     fileName.c_str(), linenr);
8788             return false;
8789             }
8790         val = s.substr(p);
8791         if (key.size()==0)
8792             continue;
8793         //allow property to be set, even if val=""
8795         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8796         //See if we wanted to overload this property
8797         std::map<String, String>::iterator iter =
8798             specifiedProperties.find(key);
8799         if (iter!=specifiedProperties.end())
8800             {
8801             val = iter->second;
8802             status("overloading property '%s' = '%s'",
8803                    key.c_str(), val.c_str());
8804             }
8805         properties[key] = val;
8806         }
8807     fclose(f);
8808     return true;
8814 /**
8815  *
8816  */
8817 bool Make::parseProperty(Element *elem)
8819     std::vector<Attribute> &attrs = elem->getAttributes();
8820     for (unsigned int i=0 ; i<attrs.size() ; i++)
8821         {
8822         String attrName = attrs[i].getName();
8823         String attrVal  = attrs[i].getValue();
8825         if (attrName == "name")
8826             {
8827             String val;
8828             if (!getAttribute(elem, "value", val))
8829                 return false;
8830             if (val.size() > 0)
8831                 {
8832                 properties[attrVal] = val;
8833                 }
8834             else
8835                 {
8836                 if (!getAttribute(elem, "location", val))
8837                     return false;
8838                 //let the property exist, even if not defined
8839                 properties[attrVal] = val;
8840                 }
8841             //See if we wanted to overload this property
8842             std::map<String, String>::iterator iter =
8843                 specifiedProperties.find(attrVal);
8844             if (iter != specifiedProperties.end())
8845                 {
8846                 val = iter->second;
8847                 status("overloading property '%s' = '%s'",
8848                     attrVal.c_str(), val.c_str());
8849                 properties[attrVal] = val;
8850                 }
8851             }
8852         else if (attrName == "file")
8853             {
8854             String prefix;
8855             if (!getAttribute(elem, "prefix", prefix))
8856                 return false;
8857             if (prefix.size() > 0)
8858                 {
8859                 if (prefix[prefix.size()-1] != '.')
8860                     prefix.push_back('.');
8861                 }
8862             if (!parsePropertyFile(attrName, prefix))
8863                 return false;
8864             }
8865         else if (attrName == "environment")
8866             {
8867             if (envPrefix.size() > 0)
8868                 {
8869                 error("environment prefix can only be set once");
8870                 return false;
8871                 }
8872             if (attrVal.find('.') != attrVal.npos)
8873                 {
8874                 error("environment prefix cannot have a '.' in it");
8875                 return false;
8876                 }
8877             envPrefix = attrVal;
8878             envPrefix.push_back('.');
8879             }
8880         }
8882     return true;
8888 /**
8889  *
8890  */
8891 bool Make::parseFile()
8893     status("######## PARSE : %s", uri.getPath().c_str());
8895     setLine(0);
8897     Parser parser;
8898     Element *root = parser.parseFile(uri.getNativePath());
8899     if (!root)
8900         {
8901         error("Could not open %s for reading",
8902               uri.getNativePath().c_str());
8903         return false;
8904         }
8905     
8906     setLine(root->getLine());
8908     if (root->getChildren().size()==0 ||
8909         root->getChildren()[0]->getName()!="project")
8910         {
8911         error("Main xml element should be <project>");
8912         delete root;
8913         return false;
8914         }
8916     //########## Project attributes
8917     Element *project = root->getChildren()[0];
8918     String s = project->getAttribute("name");
8919     if (s.size() > 0)
8920         projectName = s;
8921     s = project->getAttribute("default");
8922     if (s.size() > 0)
8923         defaultTarget = s;
8924     s = project->getAttribute("basedir");
8925     if (s.size() > 0)
8926         baseDir = s;
8928     //######### PARSE MEMBERS
8929     std::vector<Element *> children = project->getChildren();
8930     for (unsigned int i=0 ; i<children.size() ; i++)
8931         {
8932         Element *elem = children[i];
8933         setLine(elem->getLine());
8934         String tagName = elem->getName();
8936         //########## DESCRIPTION
8937         if (tagName == "description")
8938             {
8939             description = parser.trim(elem->getValue());
8940             }
8942         //######### PROPERTY
8943         else if (tagName == "property")
8944             {
8945             if (!parseProperty(elem))
8946                 return false;
8947             }
8949         //######### TARGET
8950         else if (tagName == "target")
8951             {
8952             String tname   = elem->getAttribute("name");
8953             String tdesc   = elem->getAttribute("description");
8954             String tdeps   = elem->getAttribute("depends");
8955             String tif     = elem->getAttribute("if");
8956             String tunless = elem->getAttribute("unless");
8957             Target target(*this);
8958             target.setName(tname);
8959             target.setDescription(tdesc);
8960             target.parseDependencies(tdeps);
8961             target.setIf(tif);
8962             target.setUnless(tunless);
8963             std::vector<Element *> telems = elem->getChildren();
8964             for (unsigned int i=0 ; i<telems.size() ; i++)
8965                 {
8966                 Element *telem = telems[i];
8967                 Task breeder(*this);
8968                 Task *task = breeder.createTask(telem, telem->getLine());
8969                 if (!task)
8970                     return false;
8971                 allTasks.push_back(task);
8972                 target.addTask(task);
8973                 }
8975             //Check name
8976             if (tname.size() == 0)
8977                 {
8978                 error("no name for target");
8979                 return false;
8980                 }
8981             //Check for duplicate name
8982             if (targets.find(tname) != targets.end())
8983                 {
8984                 error("target '%s' already defined", tname.c_str());
8985                 return false;
8986                 }
8987             //more work than targets[tname]=target, but avoids default allocator
8988             targets.insert(std::make_pair<String, Target>(tname, target));
8989             }
8990         //######### none of the above
8991         else
8992             {
8993             error("unknown toplevel tag: <%s>", tagName.c_str());
8994             return false;
8995             }
8997         }
8999     std::map<String, Target>::iterator iter;
9000     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9001         {
9002         Target tgt = iter->second;
9003         std::vector<String> depList;
9004         if (!checkTargetDependencies(tgt, depList))
9005             {
9006             return false;
9007             }
9008         }
9011     delete root;
9012     status("######## PARSE COMPLETE");
9013     return true;
9017 /**
9018  * Overload a <property>
9019  */
9020 bool Make::specifyProperty(const String &name, const String &value)
9022     if (specifiedProperties.find(name) != specifiedProperties.end())
9023         {
9024         error("Property %s already specified", name.c_str());
9025         return false;
9026         }
9027     specifiedProperties[name] = value;
9028     return true;
9033 /**
9034  *
9035  */
9036 bool Make::run()
9038     if (!parseFile())
9039         return false;
9040         
9041     if (!execute())
9042         return false;
9044     return true;
9050 /**
9051  * Get a formatted MM:SS.sss time elapsed string
9052  */ 
9053 static String
9054 timeDiffString(struct timeval &x, struct timeval &y)
9056     long microsX  = x.tv_usec;
9057     long secondsX = x.tv_sec;
9058     long microsY  = y.tv_usec;
9059     long secondsY = y.tv_sec;
9060     if (microsX < microsY)
9061         {
9062         microsX += 1000000;
9063         secondsX -= 1;
9064         }
9066     int seconds = (int)(secondsX - secondsY);
9067     int millis  = (int)((microsX - microsY)/1000);
9069     int minutes = seconds/60;
9070     seconds -= minutes*60;
9071     char buf[80];
9072     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9073     String ret = buf;
9074     return ret;
9075     
9078 /**
9079  *
9080  */
9081 bool Make::run(const String &target)
9083     status("####################################################");
9084     status("#   %s", version().c_str());
9085     status("####################################################");
9086     struct timeval timeStart, timeEnd;
9087     ::gettimeofday(&timeStart, NULL);
9088     specifiedTarget = target;
9089     if (!run())
9090         return false;
9091     ::gettimeofday(&timeEnd, NULL);
9092     String timeStr = timeDiffString(timeEnd, timeStart);
9093     status("####################################################");
9094     status("#   BuildTool Completed : %s", timeStr.c_str());
9095     status("####################################################");
9096     return true;
9105 }// namespace buildtool
9106 //########################################################################
9107 //# M A I N
9108 //########################################################################
9110 typedef buildtool::String String;
9112 /**
9113  *  Format an error message in printf() style
9114  */
9115 static void error(const char *fmt, ...)
9117     va_list ap;
9118     va_start(ap, fmt);
9119     fprintf(stderr, "BuildTool error: ");
9120     vfprintf(stderr, fmt, ap);
9121     fprintf(stderr, "\n");
9122     va_end(ap);
9126 static bool parseProperty(const String &s, String &name, String &val)
9128     int len = s.size();
9129     int i;
9130     for (i=0 ; i<len ; i++)
9131         {
9132         char ch = s[i];
9133         if (ch == '=')
9134             break;
9135         name.push_back(ch);
9136         }
9137     if (i>=len || s[i]!='=')
9138         {
9139         error("property requires -Dname=value");
9140         return false;
9141         }
9142     i++;
9143     for ( ; i<len ; i++)
9144         {
9145         char ch = s[i];
9146         val.push_back(ch);
9147         }
9148     return true;
9152 /**
9153  * Compare a buffer with a key, for the length of the key
9154  */
9155 static bool sequ(const String &buf, const char *key)
9157     int len = buf.size();
9158     for (int i=0 ; key[i] && i<len ; i++)
9159         {
9160         if (key[i] != buf[i])
9161             return false;
9162         }        
9163     return true;
9166 static void usage(int argc, char **argv)
9168     printf("usage:\n");
9169     printf("   %s [options] [target]\n", argv[0]);
9170     printf("Options:\n");
9171     printf("  -help, -h              print this message\n");
9172     printf("  -version               print the version information and exit\n");
9173     printf("  -file <file>           use given buildfile\n");
9174     printf("  -f <file>                 ''\n");
9175     printf("  -D<property>=<value>   use value for given property\n");
9181 /**
9182  * Parse the command-line args, get our options,
9183  * and run this thing
9184  */   
9185 static bool parseOptions(int argc, char **argv)
9187     if (argc < 1)
9188         {
9189         error("Cannot parse arguments");
9190         return false;
9191         }
9193     buildtool::Make make;
9195     String target;
9197     //char *progName = argv[0];
9198     for (int i=1 ; i<argc ; i++)
9199         {
9200         String arg = argv[i];
9201         if (arg.size()>1 && arg[0]=='-')
9202             {
9203             if (arg == "-h" || arg == "-help")
9204                 {
9205                 usage(argc,argv);
9206                 return true;
9207                 }
9208             else if (arg == "-version")
9209                 {
9210                 printf("%s", make.version().c_str());
9211                 return true;
9212                 }
9213             else if (arg == "-f" || arg == "-file")
9214                 {
9215                 if (i>=argc)
9216                    {
9217                    usage(argc, argv);
9218                    return false;
9219                    }
9220                 i++; //eat option
9221                 make.setURI(argv[i]);
9222                 }
9223             else if (arg.size()>2 && sequ(arg, "-D"))
9224                 {
9225                 String s = arg.substr(2, arg.size());
9226                 String name, value;
9227                 if (!parseProperty(s, name, value))
9228                    {
9229                    usage(argc, argv);
9230                    return false;
9231                    }
9232                 if (!make.specifyProperty(name, value))
9233                     return false;
9234                 }
9235             else
9236                 {
9237                 error("Unknown option:%s", arg.c_str());
9238                 return false;
9239                 }
9240             }
9241         else
9242             {
9243             if (target.size()>0)
9244                 {
9245                 error("only one initial target");
9246                 usage(argc, argv);
9247                 return false;
9248                 }
9249             target = arg;
9250             }
9251         }
9253     //We have the options.  Now execute them
9254     if (!make.run(target))
9255         return false;
9257     return true;
9263 /*
9264 static bool runMake()
9266     buildtool::Make make;
9267     if (!make.run())
9268         return false;
9269     return true;
9273 static bool pkgConfigTest()
9275     buildtool::PkgConfig pkgConfig;
9276     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9277         return false;
9278     return true;
9283 static bool depTest()
9285     buildtool::DepTool deptool;
9286     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9287     if (!deptool.generateDependencies("build.dep"))
9288         return false;
9289     std::vector<buildtool::FileRec> res =
9290            deptool.loadDepFile("build.dep");
9291     if (res.size() == 0)
9292         return false;
9293     return true;
9296 static bool popenTest()
9298     buildtool::Make make;
9299     buildtool::String out, err;
9300     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9301     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9302     return true;
9306 static bool propFileTest()
9308     buildtool::Make make;
9309     make.parsePropertyFile("test.prop", "test.");
9310     return true;
9312 */
9314 int main(int argc, char **argv)
9317     if (!parseOptions(argc, argv))
9318         return 1;
9319     /*
9320     if (!popenTest())
9321         return 1;
9323     if (!depTest())
9324         return 1;
9325     if (!propFileTest())
9326         return 1;
9327     if (runMake())
9328         return 1;
9329     */
9330     return 0;
9334 //########################################################################
9335 //# E N D 
9336 //########################################################################