Code

3bbe13d4499ed0950044d07ea7339b428726b078
[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.4"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
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      * replace variable refs like ${a} with their values
2994      */         
2995     bool eval(const String &s, String &result);
2997     /**
2998      *  Get an element attribute, performing substitutions if necessary
2999      */
3000     bool getAttribute(Element *elem, const String &name, String &result);
3002     /**
3003      * Get an element value, performing substitutions if necessary
3004      */
3005     bool getValue(Element *elem, String &result);
3006     
3007     /**
3008      * Set the current line number in the file
3009      */         
3010     void setLine(int val)
3011         { line = val; }
3012         
3013     /**
3014      * Get the current line number in the file
3015      */         
3016     int getLine()
3017         { return line; }
3020     /**
3021      * Set a property to a given value
3022      */
3023     virtual void setProperty(const String &name, const String &val)
3024         {
3025         properties[name] = val;
3026         }
3028     /**
3029      * Return a named property is found, else a null string
3030      */
3031     virtual String getProperty(const String &name)
3032         {
3033         String val;
3034         std::map<String, String>::iterator iter = properties.find(name);
3035         if (iter != properties.end())
3036             val = iter->second;
3037         return val;
3038         }
3040     /**
3041      * Return true if a named property is found, else false
3042      */
3043     virtual bool hasProperty(const String &name)
3044         {
3045         std::map<String, String>::iterator iter = properties.find(name);
3046         if (iter == properties.end())
3047             return false;
3048         return true;
3049         }
3052 protected:
3054     /**
3055      *    The path to the file associated with this object
3056      */     
3057     URI uri;
3058     
3059     /**
3060      *    If this prefix is seen in a substitution, use an environment
3061      *    variable.
3062      *             example:  <property environment="env"/>
3063      *             ${env.JAVA_HOME}              
3064      */     
3065     String envPrefix;
3070     /**
3071      *  Print a printf()-like formatted error message
3072      */
3073     void error(const char *fmt, ...);
3075     /**
3076      *  Print a printf()-like formatted trace message
3077      */
3078     void status(const char *fmt, ...);
3080     /**
3081      *  Show target status
3082      */
3083     void targetstatus(const char *fmt, ...);
3085     /**
3086      *  Print a printf()-like formatted trace message
3087      */
3088     void trace(const char *fmt, ...);
3090     /**
3091      *  Check if a given string matches a given regex pattern
3092      */
3093     bool regexMatch(const String &str, const String &pattern);
3095     /**
3096      *
3097      */
3098     String getSuffix(const String &fname);
3100     /**
3101      * Break up a string into substrings delimited the characters
3102      * in delimiters.  Null-length substrings are ignored
3103      */  
3104     std::vector<String> tokenize(const String &val,
3105                           const String &delimiters);
3107     /**
3108      *  replace runs of whitespace with a space
3109      */
3110     String strip(const String &s);
3112     /**
3113      *  remove leading whitespace from each line
3114      */
3115     String leftJustify(const String &s);
3117     /**
3118      *  remove leading and trailing whitespace from string
3119      */
3120     String trim(const String &s);
3122     /**
3123      *  Return a lower case version of the given string
3124      */
3125     String toLower(const String &s);
3127     /**
3128      * Return the native format of the canonical
3129      * path which we store
3130      */
3131     String getNativePath(const String &path);
3133     /**
3134      * Execute a shell command.  Outbuf is a ref to a string
3135      * to catch the result.     
3136      */         
3137     bool executeCommand(const String &call,
3138                         const String &inbuf,
3139                         String &outbuf,
3140                         String &errbuf);
3141     /**
3142      * List all directories in a given base and starting directory
3143      * It is usually called like:
3144      *        bool ret = listDirectories("src", "", result);    
3145      */         
3146     bool listDirectories(const String &baseName,
3147                          const String &dirname,
3148                          std::vector<String> &res);
3150     /**
3151      * Find all files in the named directory 
3152      */         
3153     bool listFiles(const String &baseName,
3154                    const String &dirname,
3155                    std::vector<String> &result);
3157     /**
3158      * Perform a listing for a fileset 
3159      */         
3160     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3162     /**
3163      * Parse a <patternset>
3164      */  
3165     bool parsePatternSet(Element *elem,
3166                        MakeBase &propRef,
3167                        std::vector<String> &includes,
3168                        std::vector<String> &excludes);
3170     /**
3171      * Parse a <fileset> entry, and determine which files
3172      * should be included
3173      */  
3174     bool parseFileSet(Element *elem,
3175                     MakeBase &propRef,
3176                     FileSet &fileSet);
3177     /**
3178      * Parse a <filelist> entry
3179      */  
3180     bool parseFileList(Element *elem,
3181                     MakeBase &propRef,
3182                     FileList &fileList);
3184     /**
3185      * Return this object's property list
3186      */
3187     virtual std::map<String, String> &getProperties()
3188         { return properties; }
3191     std::map<String, String> properties;
3193     /**
3194      * Turn 'true' and 'false' into boolean values
3195      */             
3196     bool getBool(const String &str, bool &val);
3198     /**
3199      * Create a directory, making intermediate dirs
3200      * if necessary
3201      */                  
3202     bool createDirectory(const String &dirname);
3204     /**
3205      * Delete a directory and its children if desired
3206      */
3207     bool removeDirectory(const String &dirName);
3209     /**
3210      * Copy a file from one name to another. Perform only if needed
3211      */ 
3212     bool copyFile(const String &srcFile, const String &destFile);
3214     /**
3215      * Tests if the file exists and is a regular file
3216      */ 
3217     bool isRegularFile(const String &fileName);
3219     /**
3220      * Tests if the file exists and is a directory
3221      */ 
3222     bool isDirectory(const String &fileName);
3224     /**
3225      * Tests is the modification date of fileA is newer than fileB
3226      */ 
3227     bool isNewerThan(const String &fileA, const String &fileB);
3229 private:
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;
3965 /**
3966  * Analyse a string, looking for any substitutions or other
3967  * things that need resilution 
3968  */
3969 bool MakeBase::eval(const String &str, String &result)
3971     String s = trim(str);
3972     int len = (int)s.size();
3973     String val;
3974     for (int i=0 ; i<len ; i++)
3975         {
3976         char ch = s[i];
3977         if (ch == '$' && s[i+1] == '{')
3978             {
3979             String varname;
3980             int j = i+2;
3981             for ( ; j<len ; j++)
3982                 {
3983                 ch = s[j];
3984                 if (ch == '$' && s[j+1] == '{')
3985                     {
3986                     error("attribute %s cannot have nested variable references",
3987                            s.c_str());
3988                     return false;
3989                     }
3990                 else if (ch == '}')
3991                     {
3992                     std::map<String, String>::iterator iter;
3993                     varname = trim(varname);
3994                     if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3995                         {
3996                         varname = varname.substr(envPrefix.size());
3997                         char *envstr = getenv(varname.c_str());
3998                         if (!envstr)
3999                             {
4000                             error("environment variable '%s' not defined", varname.c_str());
4001                             return false;
4002                             }
4003                         val.append(envstr);
4004                         }
4005                     else
4006                         {
4007                         iter = properties.find(varname);
4008                         if (iter != properties.end())
4009                             {
4010                             val.append(iter->second);
4011                             }
4012                         else
4013                             {
4014                             error("property ${%s} not found", varname.c_str());
4015                             return false;
4016                             }
4017                         }
4018                     break;
4019                     }
4020                 else
4021                     {
4022                     varname.push_back(ch);
4023                     }
4024                 }
4025             i = j;
4026             }
4027         else
4028             {
4029             val.push_back(ch);
4030             }
4031         }
4032     result = val;
4033     return true;
4037 bool MakeBase::getAttribute(Element *elem, const String &name,
4038                                     String &result)
4040     String s = elem->getAttribute(name);
4041     return eval(s, result);
4045 bool MakeBase::getValue(Element *elem, String &result)
4047     String s = elem->getValue();
4048     //Replace all runs of whitespace with a single space
4049     return eval(s, result);
4053 /**
4054  * Turn 'true' and 'false' into boolean values
4055  */             
4056 bool MakeBase::getBool(const String &str, bool &val)
4058     if (str == "true")
4059         val = true;
4060     else if (str == "false")
4061         val = false;
4062     else
4063         {
4064         error("expected 'true' or 'false'.  found '%s'", str.c_str());
4065         return false;
4066         }
4067     return true;
4073 /**
4074  * Parse a <patternset> entry
4075  */  
4076 bool MakeBase::parsePatternSet(Element *elem,
4077                           MakeBase &propRef,
4078                           std::vector<String> &includes,
4079                           std::vector<String> &excludes
4080                           )
4082     std::vector<Element *> children  = elem->getChildren();
4083     for (unsigned int i=0 ; i<children.size() ; i++)
4084         {
4085         Element *child = children[i];
4086         String tagName = child->getName();
4087         if (tagName == "exclude")
4088             {
4089             String fname;
4090             if (!propRef.getAttribute(child, "name", fname))
4091                 return false;
4092             //trace("EXCLUDE: %s", fname.c_str());
4093             excludes.push_back(fname);
4094             }
4095         else if (tagName == "include")
4096             {
4097             String fname;
4098             if (!propRef.getAttribute(child, "name", fname))
4099                 return false;
4100             //trace("INCLUDE: %s", fname.c_str());
4101             includes.push_back(fname);
4102             }
4103         }
4105     return true;
4111 /**
4112  * Parse a <fileset> entry, and determine which files
4113  * should be included
4114  */  
4115 bool MakeBase::parseFileSet(Element *elem,
4116                           MakeBase &propRef,
4117                           FileSet &fileSet)
4119     String name = elem->getName();
4120     if (name != "fileset")
4121         {
4122         error("expected <fileset>");
4123         return false;
4124         }
4127     std::vector<String> includes;
4128     std::vector<String> excludes;
4130     //A fileset has one implied patternset
4131     if (!parsePatternSet(elem, propRef, includes, excludes))
4132         {
4133         return false;
4134         }
4135     //Look for child tags, including more patternsets
4136     std::vector<Element *> children  = elem->getChildren();
4137     for (unsigned int i=0 ; i<children.size() ; i++)
4138         {
4139         Element *child = children[i];
4140         String tagName = child->getName();
4141         if (tagName == "patternset")
4142             {
4143             if (!parsePatternSet(child, propRef, includes, excludes))
4144                 {
4145                 return false;
4146                 }
4147             }
4148         }
4150     String dir;
4151     //Now do the stuff
4152     //Get the base directory for reading file names
4153     if (!propRef.getAttribute(elem, "dir", dir))
4154         return false;
4156     fileSet.setDirectory(dir);
4157     fileSet.setIncludes(includes);
4158     fileSet.setExcludes(excludes);
4159     
4160     /*
4161     std::vector<String> fileList;
4162     if (dir.size() > 0)
4163         {
4164         String baseDir = propRef.resolve(dir);
4165         if (!listFiles(baseDir, "", includes, excludes, fileList))
4166             return false;
4167         }
4168     std::sort(fileList.begin(), fileList.end());
4169     result = fileList;
4170     */
4172     
4173     /*
4174     for (unsigned int i=0 ; i<result.size() ; i++)
4175         {
4176         trace("RES:%s", result[i].c_str());
4177         }
4178     */
4180     
4181     return true;
4184 /**
4185  * Parse a <filelist> entry.  This is far simpler than FileSet,
4186  * since no directory scanning is needed.  The file names are listed
4187  * explicitly.
4188  */  
4189 bool MakeBase::parseFileList(Element *elem,
4190                           MakeBase &propRef,
4191                           FileList &fileList)
4193     std::vector<String> fnames;
4194     //Look for child tags, namely "file"
4195     std::vector<Element *> children  = elem->getChildren();
4196     for (unsigned int i=0 ; i<children.size() ; i++)
4197         {
4198         Element *child = children[i];
4199         String tagName = child->getName();
4200         if (tagName == "file")
4201             {
4202             String fname = child->getAttribute("name");
4203             if (fname.size()==0)
4204                 {
4205                 error("<file> element requires name="" attribute");
4206                 return false;
4207                 }
4208             fnames.push_back(fname);
4209             }
4210         else
4211             {
4212             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4213             return false;
4214             }
4215         }
4217     String dir;
4218     //Get the base directory for reading file names
4219     if (!propRef.getAttribute(elem, "dir", dir))
4220         return false;
4221     fileList.setDirectory(dir);
4222     fileList.setFiles(fnames);
4224     return true;
4229 /**
4230  * Create a directory, making intermediate dirs
4231  * if necessary
4232  */                  
4233 bool MakeBase::createDirectory(const String &dirname)
4235     //trace("## createDirectory: %s", dirname.c_str());
4236     //## first check if it exists
4237     struct stat finfo;
4238     String nativeDir = getNativePath(dirname);
4239     char *cnative = (char *) nativeDir.c_str();
4240 #ifdef __WIN32__
4241     if (strlen(cnative)==2 && cnative[1]==':')
4242         return true;
4243 #endif
4244     if (stat(cnative, &finfo)==0)
4245         {
4246         if (!S_ISDIR(finfo.st_mode))
4247             {
4248             error("mkdir: file %s exists but is not a directory",
4249                   cnative);
4250             return false;
4251             }
4252         else //exists
4253             {
4254             return true;
4255             }
4256         }
4258     //## 2: pull off the last path segment, if any,
4259     //## to make the dir 'above' this one, if necessary
4260     unsigned int pos = dirname.find_last_of('/');
4261     if (pos>0 && pos != dirname.npos)
4262         {
4263         String subpath = dirname.substr(0, pos);
4264         //A letter root (c:) ?
4265         if (!createDirectory(subpath))
4266             return false;
4267         }
4268         
4269     //## 3: now make
4270 #ifdef __WIN32__
4271     if (mkdir(cnative)<0)
4272 #else
4273     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4274 #endif
4275         {
4276         error("cannot make directory '%s' : %s",
4277                  cnative, strerror(errno));
4278         return false;
4279         }
4280         
4281     return true;
4285 /**
4286  * Remove a directory recursively
4287  */ 
4288 bool MakeBase::removeDirectory(const String &dirName)
4290     char *dname = (char *)dirName.c_str();
4292     DIR *dir = opendir(dname);
4293     if (!dir)
4294         {
4295         //# Let this fail nicely.
4296         return true;
4297         //error("error opening directory %s : %s", dname, strerror(errno));
4298         //return false;
4299         }
4300     
4301     while (true)
4302         {
4303         struct dirent *de = readdir(dir);
4304         if (!de)
4305             break;
4307         //Get the directory member name
4308         String s = de->d_name;
4309         if (s.size() == 0 || s[0] == '.')
4310             continue;
4311         String childName;
4312         if (dirName.size() > 0)
4313             {
4314             childName.append(dirName);
4315             childName.append("/");
4316             }
4317         childName.append(s);
4320         struct stat finfo;
4321         String childNative = getNativePath(childName);
4322         char *cnative = (char *)childNative.c_str();
4323         if (stat(cnative, &finfo)<0)
4324             {
4325             error("cannot stat file:%s", cnative);
4326             }
4327         else if (S_ISDIR(finfo.st_mode))
4328             {
4329             //trace("DEL dir: %s", childName.c_str());
4330             if (!removeDirectory(childName))
4331                 {
4332                 return false;
4333                 }
4334             }
4335         else if (!S_ISREG(finfo.st_mode))
4336             {
4337             //trace("not regular: %s", cnative);
4338             }
4339         else
4340             {
4341             //trace("DEL file: %s", childName.c_str());
4342             if (remove(cnative)<0)
4343                 {
4344                 error("error deleting %s : %s",
4345                      cnative, strerror(errno));
4346                 return false;
4347                 }
4348             }
4349         }
4350     closedir(dir);
4352     //Now delete the directory
4353     String native = getNativePath(dirName);
4354     if (rmdir(native.c_str())<0)
4355         {
4356         error("could not delete directory %s : %s",
4357             native.c_str() , strerror(errno));
4358         return false;
4359         }
4361     return true;
4362     
4366 /**
4367  * Copy a file from one name to another. Perform only if needed
4368  */ 
4369 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4371     //# 1 Check up-to-date times
4372     String srcNative = getNativePath(srcFile);
4373     struct stat srcinfo;
4374     if (stat(srcNative.c_str(), &srcinfo)<0)
4375         {
4376         error("source file %s for copy does not exist",
4377                  srcNative.c_str());
4378         return false;
4379         }
4381     String destNative = getNativePath(destFile);
4382     struct stat destinfo;
4383     if (stat(destNative.c_str(), &destinfo)==0)
4384         {
4385         if (destinfo.st_mtime >= srcinfo.st_mtime)
4386             return true;
4387         }
4388         
4389     //# 2 prepare a destination directory if necessary
4390     unsigned int pos = destFile.find_last_of('/');
4391     if (pos != destFile.npos)
4392         {
4393         String subpath = destFile.substr(0, pos);
4394         if (!createDirectory(subpath))
4395             return false;
4396         }
4398     //# 3 do the data copy
4399 #ifndef __WIN32__
4401     FILE *srcf = fopen(srcNative.c_str(), "rb");
4402     if (!srcf)
4403         {
4404         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4405         return false;
4406         }
4407     FILE *destf = fopen(destNative.c_str(), "wb");
4408     if (!destf)
4409         {
4410         error("copyFile cannot open %s for writing", srcNative.c_str());
4411         return false;
4412         }
4414     while (!feof(srcf))
4415         {
4416         int ch = fgetc(srcf);
4417         if (ch<0)
4418             break;
4419         fputc(ch, destf);
4420         }
4422     fclose(destf);
4423     fclose(srcf);
4425 #else
4426     
4427     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4428         {
4429         error("copyFile from %s to %s failed",
4430              srcNative.c_str(), destNative.c_str());
4431         return false;
4432         }
4433         
4434 #endif /* __WIN32__ */
4437     return true;
4442 /**
4443  * Tests if the file exists and is a regular file
4444  */ 
4445 bool MakeBase::isRegularFile(const String &fileName)
4447     String native = getNativePath(fileName);
4448     struct stat finfo;
4449     
4450     //Exists?
4451     if (stat(native.c_str(), &finfo)<0)
4452         return false;
4455     //check the file mode
4456     if (!S_ISREG(finfo.st_mode))
4457         return false;
4459     return true;
4462 /**
4463  * Tests if the file exists and is a directory
4464  */ 
4465 bool MakeBase::isDirectory(const String &fileName)
4467     String native = getNativePath(fileName);
4468     struct stat finfo;
4469     
4470     //Exists?
4471     if (stat(native.c_str(), &finfo)<0)
4472         return false;
4475     //check the file mode
4476     if (!S_ISDIR(finfo.st_mode))
4477         return false;
4479     return true;
4484 /**
4485  * Tests is the modification of fileA is newer than fileB
4486  */ 
4487 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4489     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4490     String nativeA = getNativePath(fileA);
4491     struct stat infoA;
4492     //IF source does not exist, NOT newer
4493     if (stat(nativeA.c_str(), &infoA)<0)
4494         {
4495         return false;
4496         }
4498     String nativeB = getNativePath(fileB);
4499     struct stat infoB;
4500     //IF dest does not exist, YES, newer
4501     if (stat(nativeB.c_str(), &infoB)<0)
4502         {
4503         return true;
4504         }
4506     //check the actual times
4507     if (infoA.st_mtime > infoB.st_mtime)
4508         {
4509         return true;
4510         }
4512     return false;
4516 //########################################################################
4517 //# P K G    C O N F I G
4518 //########################################################################
4520 /**
4521  *
4522  */
4523 class PkgConfig : public MakeBase
4526 public:
4528     /**
4529      *
4530      */
4531     PkgConfig()
4532         { path="."; init(); }
4534     /**
4535      *
4536      */
4537     PkgConfig(const PkgConfig &other)
4538         { assign(other); }
4540     /**
4541      *
4542      */
4543     PkgConfig &operator=(const PkgConfig &other)
4544         { assign(other); return *this; }
4546     /**
4547      *
4548      */
4549     virtual ~PkgConfig()
4550         { }
4552     /**
4553      *
4554      */
4555     virtual String getName()
4556         { return name; }
4558     /**
4559      *
4560      */
4561     virtual String getPath()
4562         { return path; }
4564     /**
4565      *
4566      */
4567     virtual void setPath(const String &val)
4568         { path = val; }
4570     /**
4571      *
4572      */
4573     virtual String getPrefix()
4574         { return prefix; }
4576     /**
4577      *  Allow the user to override the prefix in the file
4578      */
4579     virtual void setPrefix(const String &val)
4580         { prefix = val; }
4582     /**
4583      *
4584      */
4585     virtual String getDescription()
4586         { return description; }
4588     /**
4589      *
4590      */
4591     virtual String getCflags()
4592         { return cflags; }
4594     /**
4595      *
4596      */
4597     virtual String getLibs()
4598         { return libs; }
4600     /**
4601      *
4602      */
4603     virtual String getAll()
4604         {
4605          String ret = cflags;
4606          ret.append(" ");
4607          ret.append(libs);
4608          return ret;
4609         }
4611     /**
4612      *
4613      */
4614     virtual String getVersion()
4615         { return version; }
4617     /**
4618      *
4619      */
4620     virtual int getMajorVersion()
4621         { return majorVersion; }
4623     /**
4624      *
4625      */
4626     virtual int getMinorVersion()
4627         { return minorVersion; }
4629     /**
4630      *
4631      */
4632     virtual int getMicroVersion()
4633         { return microVersion; }
4635     /**
4636      *
4637      */
4638     virtual std::map<String, String> &getAttributes()
4639         { return attrs; }
4641     /**
4642      *
4643      */
4644     virtual std::vector<String> &getRequireList()
4645         { return requireList; }
4647     /**
4648      *  Read a file for its details
4649      */         
4650     virtual bool readFile(const String &fileName);
4652     /**
4653      *  Read a file for its details
4654      */         
4655     virtual bool query(const String &name);
4657 private:
4659     void init()
4660         {
4661         //do not set path or prefix here
4662         name         = "";
4663         description  = "";
4664         cflags       = "";
4665         libs         = "";
4666         requires     = "";
4667         version      = "";
4668         majorVersion = 0;
4669         minorVersion = 0;
4670         microVersion = 0;
4671         fileName     = "";
4672         attrs.clear();
4673         requireList.clear();
4674         }
4676     void assign(const PkgConfig &other)
4677         {
4678         name         = other.name;
4679         path         = other.path;
4680         prefix       = other.prefix;
4681         description  = other.description;
4682         cflags       = other.cflags;
4683         libs         = other.libs;
4684         requires     = other.requires;
4685         version      = other.version;
4686         majorVersion = other.majorVersion;
4687         minorVersion = other.minorVersion;
4688         microVersion = other.microVersion;
4689         fileName     = other.fileName;
4690         attrs        = other.attrs;
4691         requireList  = other.requireList;
4692         }
4696     int get(int pos);
4698     int skipwhite(int pos);
4700     int getword(int pos, String &ret);
4702     void parseRequires();
4704     void parseVersion();
4706     bool parseLine(const String &lineBuf);
4708     bool parse(const String &buf);
4710     void dumpAttrs();
4712     String name;
4714     String path;
4716     String prefix;
4718     String description;
4720     String cflags;
4722     String libs;
4724     String requires;
4726     String version;
4728     int majorVersion;
4730     int minorVersion;
4732     int microVersion;
4734     String fileName;
4736     std::map<String, String> attrs;
4738     std::vector<String> requireList;
4740     char *parsebuf;
4741     int parselen;
4742 };
4745 /**
4746  * Get a character from the buffer at pos.  If out of range,
4747  * return -1 for safety
4748  */
4749 int PkgConfig::get(int pos)
4751     if (pos>parselen)
4752         return -1;
4753     return parsebuf[pos];
4758 /**
4759  *  Skip over all whitespace characters beginning at pos.  Return
4760  *  the position of the first non-whitespace character.
4761  *  Pkg-config is line-oriented, so check for newline
4762  */
4763 int PkgConfig::skipwhite(int pos)
4765     while (pos < parselen)
4766         {
4767         int ch = get(pos);
4768         if (ch < 0)
4769             break;
4770         if (!isspace(ch))
4771             break;
4772         pos++;
4773         }
4774     return pos;
4778 /**
4779  *  Parse the buffer beginning at pos, for a word.  Fill
4780  *  'ret' with the result.  Return the position after the
4781  *  word.
4782  */
4783 int PkgConfig::getword(int pos, String &ret)
4785     while (pos < parselen)
4786         {
4787         int ch = get(pos);
4788         if (ch < 0)
4789             break;
4790         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4791             break;
4792         ret.push_back((char)ch);
4793         pos++;
4794         }
4795     return pos;
4798 void PkgConfig::parseRequires()
4800     if (requires.size() == 0)
4801         return;
4802     parsebuf = (char *)requires.c_str();
4803     parselen = requires.size();
4804     int pos = 0;
4805     while (pos < parselen)
4806         {
4807         pos = skipwhite(pos);
4808         String val;
4809         int pos2 = getword(pos, val);
4810         if (pos2 == pos)
4811             break;
4812         pos = pos2;
4813         //trace("val %s", val.c_str());
4814         requireList.push_back(val);
4815         }
4818 static int getint(const String str)
4820     char *s = (char *)str.c_str();
4821     char *ends = NULL;
4822     long val = strtol(s, &ends, 10);
4823     if (ends == s)
4824         return 0L;
4825     else
4826         return val;
4829 void PkgConfig::parseVersion()
4831     if (version.size() == 0)
4832         return;
4833     String s1, s2, s3;
4834     unsigned int pos = 0;
4835     unsigned int pos2 = version.find('.', pos);
4836     if (pos2 == version.npos)
4837         {
4838         s1 = version;
4839         }
4840     else
4841         {
4842         s1 = version.substr(pos, pos2-pos);
4843         pos = pos2;
4844         pos++;
4845         if (pos < version.size())
4846             {
4847             pos2 = version.find('.', pos);
4848             if (pos2 == version.npos)
4849                 {
4850                 s2 = version.substr(pos, version.size()-pos);
4851                 }
4852             else
4853                 {
4854                 s2 = version.substr(pos, pos2-pos);
4855                 pos = pos2;
4856                 pos++;
4857                 if (pos < version.size())
4858                     s3 = version.substr(pos, pos2-pos);
4859                 }
4860             }
4861         }
4863     majorVersion = getint(s1);
4864     minorVersion = getint(s2);
4865     microVersion = getint(s3);
4866     //trace("version:%d.%d.%d", majorVersion,
4867     //          minorVersion, microVersion );
4871 bool PkgConfig::parseLine(const String &lineBuf)
4873     parsebuf = (char *)lineBuf.c_str();
4874     parselen = lineBuf.size();
4875     int pos = 0;
4876     
4877     while (pos < parselen)
4878         {
4879         String attrName;
4880         pos = skipwhite(pos);
4881         int ch = get(pos);
4882         if (ch == '#')
4883             {
4884             //comment.  eat the rest of the line
4885             while (pos < parselen)
4886                 {
4887                 ch = get(pos);
4888                 if (ch == '\n' || ch < 0)
4889                     break;
4890                 pos++;
4891                 }
4892             continue;
4893             }
4894         pos = getword(pos, attrName);
4895         if (attrName.size() == 0)
4896             continue;
4897         
4898         pos = skipwhite(pos);
4899         ch = get(pos);
4900         if (ch != ':' && ch != '=')
4901             {
4902             error("expected ':' or '='");
4903             return false;
4904             }
4905         pos++;
4906         pos = skipwhite(pos);
4907         String attrVal;
4908         while (pos < parselen)
4909             {
4910             ch = get(pos);
4911             if (ch == '\n' || ch < 0)
4912                 break;
4913             else if (ch == '$' && get(pos+1) == '{')
4914                 {
4915                 //#  this is a ${substitution}
4916                 pos += 2;
4917                 String subName;
4918                 while (pos < parselen)
4919                     {
4920                     ch = get(pos);
4921                     if (ch < 0)
4922                         {
4923                         error("unterminated substitution");
4924                         return false;
4925                         }
4926                     else if (ch == '}')
4927                         break;
4928                     else
4929                         subName.push_back((char)ch);
4930                     pos++;
4931                     }
4932                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4933                 if (subName == "prefix" && prefix.size()>0)
4934                     {
4935                     attrVal.append(prefix);
4936                     //trace("prefix override:%s", prefix.c_str());
4937                     }
4938                 else
4939                     {
4940                     String subVal = attrs[subName];
4941                     //trace("subVal:%s", subVal.c_str());
4942                     attrVal.append(subVal);
4943                     }
4944                 }
4945             else
4946                 attrVal.push_back((char)ch);
4947             pos++;
4948             }
4950         attrVal = trim(attrVal);
4951         attrs[attrName] = attrVal;
4953         String attrNameL = toLower(attrName);
4955         if (attrNameL == "name")
4956             name = attrVal;
4957         else if (attrNameL == "description")
4958             description = attrVal;
4959         else if (attrNameL == "cflags")
4960             cflags = attrVal;
4961         else if (attrNameL == "libs")
4962             libs = attrVal;
4963         else if (attrNameL == "requires")
4964             requires = attrVal;
4965         else if (attrNameL == "version")
4966             version = attrVal;
4968         //trace("name:'%s'  value:'%s'",
4969         //      attrName.c_str(), attrVal.c_str());
4970         }
4972     return true;
4976 bool PkgConfig::parse(const String &buf)
4978     init();
4980     String line;
4981     int lineNr = 0;
4982     for (unsigned int p=0 ; p<buf.size() ; p++)
4983         {
4984         int ch = buf[p];
4985         if (ch == '\n' || ch == '\r')
4986             {
4987             if (!parseLine(line))
4988                 return false;
4989             line.clear();
4990             lineNr++;
4991             }
4992         else
4993             {
4994             line.push_back(ch);
4995             }
4996         }
4997     if (line.size()>0)
4998         {
4999         if (!parseLine(line))
5000             return false;
5001         }
5003     parseRequires();
5004     parseVersion();
5006     return true;
5012 void PkgConfig::dumpAttrs()
5014     //trace("### PkgConfig attributes for %s", fileName.c_str());
5015     std::map<String, String>::iterator iter;
5016     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5017         {
5018         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5019         }
5023 bool PkgConfig::readFile(const String &fname)
5025     fileName = getNativePath(fname);
5027     FILE *f = fopen(fileName.c_str(), "r");
5028     if (!f)
5029         {
5030         error("cannot open file '%s' for reading", fileName.c_str());
5031         return false;
5032         }
5033     String buf;
5034     while (true)
5035         {
5036         int ch = fgetc(f);
5037         if (ch < 0)
5038             break;
5039         buf.push_back((char)ch);
5040         }
5041     fclose(f);
5043     //trace("####### File:\n%s", buf.c_str());
5044     if (!parse(buf))
5045         {
5046         return false;
5047         }
5049     //dumpAttrs();
5051     return true;
5056 bool PkgConfig::query(const String &pkgName)
5058     name = pkgName;
5060     String fname = path;
5061     fname.append("/");
5062     fname.append(name);
5063     fname.append(".pc");
5065     if (!readFile(fname))
5066         return false;
5067     
5068     return true;
5075 //########################################################################
5076 //# D E P T O O L
5077 //########################################################################
5081 /**
5082  *  Class which holds information for each file.
5083  */
5084 class FileRec
5086 public:
5088     typedef enum
5089         {
5090         UNKNOWN,
5091         CFILE,
5092         HFILE,
5093         OFILE
5094         } FileType;
5096     /**
5097      *  Constructor
5098      */
5099     FileRec()
5100         { init(); type = UNKNOWN; }
5102     /**
5103      *  Copy constructor
5104      */
5105     FileRec(const FileRec &other)
5106         { init(); assign(other); }
5107     /**
5108      *  Constructor
5109      */
5110     FileRec(int typeVal)
5111         { init(); type = typeVal; }
5112     /**
5113      *  Assignment operator
5114      */
5115     FileRec &operator=(const FileRec &other)
5116         { init(); assign(other); return *this; }
5119     /**
5120      *  Destructor
5121      */
5122     ~FileRec()
5123         {}
5125     /**
5126      *  Directory part of the file name
5127      */
5128     String path;
5130     /**
5131      *  Base name, sans directory and suffix
5132      */
5133     String baseName;
5135     /**
5136      *  File extension, such as cpp or h
5137      */
5138     String suffix;
5140     /**
5141      *  Type of file: CFILE, HFILE, OFILE
5142      */
5143     int type;
5145     /**
5146      * Used to list files ref'd by this one
5147      */
5148     std::map<String, FileRec *> files;
5151 private:
5153     void init()
5154         {
5155         }
5157     void assign(const FileRec &other)
5158         {
5159         type     = other.type;
5160         baseName = other.baseName;
5161         suffix   = other.suffix;
5162         files    = other.files;
5163         }
5165 };
5169 /**
5170  *  Simpler dependency record
5171  */
5172 class DepRec
5174 public:
5176     /**
5177      *  Constructor
5178      */
5179     DepRec()
5180         {init();}
5182     /**
5183      *  Copy constructor
5184      */
5185     DepRec(const DepRec &other)
5186         {init(); assign(other);}
5187     /**
5188      *  Constructor
5189      */
5190     DepRec(const String &fname)
5191         {init(); name = fname; }
5192     /**
5193      *  Assignment operator
5194      */
5195     DepRec &operator=(const DepRec &other)
5196         {init(); assign(other); return *this;}
5199     /**
5200      *  Destructor
5201      */
5202     ~DepRec()
5203         {}
5205     /**
5206      *  Directory part of the file name
5207      */
5208     String path;
5210     /**
5211      *  Base name, without the path and suffix
5212      */
5213     String name;
5215     /**
5216      *  Suffix of the source
5217      */
5218     String suffix;
5221     /**
5222      * Used to list files ref'd by this one
5223      */
5224     std::vector<String> files;
5227 private:
5229     void init()
5230         {
5231         }
5233     void assign(const DepRec &other)
5234         {
5235         path     = other.path;
5236         name     = other.name;
5237         suffix   = other.suffix;
5238         files    = other.files; //avoid recursion
5239         }
5241 };
5244 class DepTool : public MakeBase
5246 public:
5248     /**
5249      *  Constructor
5250      */
5251     DepTool()
5252         { init(); }
5254     /**
5255      *  Copy constructor
5256      */
5257     DepTool(const DepTool &other)
5258         { init(); assign(other); }
5260     /**
5261      *  Assignment operator
5262      */
5263     DepTool &operator=(const DepTool &other)
5264         { init(); assign(other); return *this; }
5267     /**
5268      *  Destructor
5269      */
5270     ~DepTool()
5271         {}
5274     /**
5275      *  Reset this section of code
5276      */
5277     virtual void init();
5278     
5279     /**
5280      *  Reset this section of code
5281      */
5282     virtual void assign(const DepTool &other)
5283         {
5284         }
5285     
5286     /**
5287      *  Sets the source directory which will be scanned
5288      */
5289     virtual void setSourceDirectory(const String &val)
5290         { sourceDir = val; }
5292     /**
5293      *  Returns the source directory which will be scanned
5294      */
5295     virtual String getSourceDirectory()
5296         { return sourceDir; }
5298     /**
5299      *  Sets the list of files within the directory to analyze
5300      */
5301     virtual void setFileList(const std::vector<String> &list)
5302         { fileList = list; }
5304     /**
5305      * Creates the list of all file names which will be
5306      * candidates for further processing.  Reads make.exclude
5307      * to see which files for directories to leave out.
5308      */
5309     virtual bool createFileList();
5312     /**
5313      *  Generates the forward dependency list
5314      */
5315     virtual bool generateDependencies();
5318     /**
5319      *  Generates the forward dependency list, saving the file
5320      */
5321     virtual bool generateDependencies(const String &);
5324     /**
5325      *  Load a dependency file
5326      */
5327     std::vector<DepRec> loadDepFile(const String &fileName);
5329     /**
5330      *  Load a dependency file, generating one if necessary
5331      */
5332     std::vector<DepRec> getDepFile(const String &fileName,
5333               bool forceRefresh);
5335     /**
5336      *  Save a dependency file
5337      */
5338     bool saveDepFile(const String &fileName);
5341 private:
5344     /**
5345      *
5346      */
5347     void parseName(const String &fullname,
5348                    String &path,
5349                    String &basename,
5350                    String &suffix);
5352     /**
5353      *
5354      */
5355     int get(int pos);
5357     /**
5358      *
5359      */
5360     int skipwhite(int pos);
5362     /**
5363      *
5364      */
5365     int getword(int pos, String &ret);
5367     /**
5368      *
5369      */
5370     bool sequ(int pos, const char *key);
5372     /**
5373      *
5374      */
5375     bool addIncludeFile(FileRec *frec, const String &fname);
5377     /**
5378      *
5379      */
5380     bool scanFile(const String &fname, FileRec *frec);
5382     /**
5383      *
5384      */
5385     bool processDependency(FileRec *ofile, FileRec *include);
5387     /**
5388      *
5389      */
5390     String sourceDir;
5392     /**
5393      *
5394      */
5395     std::vector<String> fileList;
5397     /**
5398      *
5399      */
5400     std::vector<String> directories;
5402     /**
5403      * A list of all files which will be processed for
5404      * dependencies.
5405      */
5406     std::map<String, FileRec *> allFiles;
5408     /**
5409      * The list of .o files, and the
5410      * dependencies upon them.
5411      */
5412     std::map<String, FileRec *> oFiles;
5414     int depFileSize;
5415     char *depFileBuf;
5417     static const int readBufSize = 8192;
5418     char readBuf[8193];//byte larger
5420 };
5426 /**
5427  *  Clean up after processing.  Called by the destructor, but should
5428  *  also be called before the object is reused.
5429  */
5430 void DepTool::init()
5432     sourceDir = ".";
5434     fileList.clear();
5435     directories.clear();
5436     
5437     //clear output file list
5438     std::map<String, FileRec *>::iterator iter;
5439     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5440         delete iter->second;
5441     oFiles.clear();
5443     //allFiles actually contains the master copies. delete them
5444     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5445         delete iter->second;
5446     allFiles.clear(); 
5453 /**
5454  *  Parse a full path name into path, base name, and suffix
5455  */
5456 void DepTool::parseName(const String &fullname,
5457                         String &path,
5458                         String &basename,
5459                         String &suffix)
5461     if (fullname.size() < 2)
5462         return;
5464     unsigned int pos = fullname.find_last_of('/');
5465     if (pos != fullname.npos && pos<fullname.size()-1)
5466         {
5467         path = fullname.substr(0, pos);
5468         pos++;
5469         basename = fullname.substr(pos, fullname.size()-pos);
5470         }
5471     else
5472         {
5473         path = "";
5474         basename = fullname;
5475         }
5477     pos = basename.find_last_of('.');
5478     if (pos != basename.npos && pos<basename.size()-1)
5479         {
5480         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5481         basename = basename.substr(0, pos);
5482         }
5484     //trace("parsename:%s %s %s", path.c_str(),
5485     //        basename.c_str(), suffix.c_str()); 
5490 /**
5491  *  Generate our internal file list.
5492  */
5493 bool DepTool::createFileList()
5496     for (unsigned int i=0 ; i<fileList.size() ; i++)
5497         {
5498         String fileName = fileList[i];
5499         //trace("## FileName:%s", fileName.c_str());
5500         String path;
5501         String basename;
5502         String sfx;
5503         parseName(fileName, path, basename, sfx);
5504         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5505             sfx == "cc" || sfx == "CC")
5506             {
5507             FileRec *fe         = new FileRec(FileRec::CFILE);
5508             fe->path            = path;
5509             fe->baseName        = basename;
5510             fe->suffix          = sfx;
5511             allFiles[fileName]  = fe;
5512             }
5513         else if (sfx == "h"   ||  sfx == "hh"  ||
5514                  sfx == "hpp" ||  sfx == "hxx")
5515             {
5516             FileRec *fe         = new FileRec(FileRec::HFILE);
5517             fe->path            = path;
5518             fe->baseName        = basename;
5519             fe->suffix          = sfx;
5520             allFiles[fileName]  = fe;
5521             }
5522         }
5524     if (!listDirectories(sourceDir, "", directories))
5525         return false;
5526         
5527     return true;
5534 /**
5535  * Get a character from the buffer at pos.  If out of range,
5536  * return -1 for safety
5537  */
5538 int DepTool::get(int pos)
5540     if (pos>depFileSize)
5541         return -1;
5542     return depFileBuf[pos];
5547 /**
5548  *  Skip over all whitespace characters beginning at pos.  Return
5549  *  the position of the first non-whitespace character.
5550  */
5551 int DepTool::skipwhite(int pos)
5553     while (pos < depFileSize)
5554         {
5555         int ch = get(pos);
5556         if (ch < 0)
5557             break;
5558         if (!isspace(ch))
5559             break;
5560         pos++;
5561         }
5562     return pos;
5566 /**
5567  *  Parse the buffer beginning at pos, for a word.  Fill
5568  *  'ret' with the result.  Return the position after the
5569  *  word.
5570  */
5571 int DepTool::getword(int pos, String &ret)
5573     while (pos < depFileSize)
5574         {
5575         int ch = get(pos);
5576         if (ch < 0)
5577             break;
5578         if (isspace(ch))
5579             break;
5580         ret.push_back((char)ch);
5581         pos++;
5582         }
5583     return pos;
5586 /**
5587  * Return whether the sequence of characters in the buffer
5588  * beginning at pos match the key,  for the length of the key
5589  */
5590 bool DepTool::sequ(int pos, const char *key)
5592     while (*key)
5593         {
5594         if (*key != get(pos))
5595             return false;
5596         key++; pos++;
5597         }
5598     return true;
5603 /**
5604  *  Add an include file name to a file record.  If the name
5605  *  is not found in allFiles explicitly, try prepending include
5606  *  directory names to it and try again.
5607  */
5608 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5610     //# if the name is an exact match to a path name
5611     //# in allFiles, like "myinc.h"
5612     std::map<String, FileRec *>::iterator iter =
5613            allFiles.find(iname);
5614     if (iter != allFiles.end()) //already exists
5615         {
5616          //h file in same dir
5617         FileRec *other = iter->second;
5618         //trace("local: '%s'", iname.c_str());
5619         frec->files[iname] = other;
5620         return true;
5621         }
5622     else 
5623         {
5624         //## Ok, it was not found directly
5625         //look in other dirs
5626         std::vector<String>::iterator diter;
5627         for (diter=directories.begin() ;
5628              diter!=directories.end() ; diter++)
5629             {
5630             String dfname = *diter;
5631             dfname.append("/");
5632             dfname.append(iname);
5633             URI fullPathURI(dfname);  //normalize path name
5634             String fullPath = fullPathURI.getPath();
5635             if (fullPath[0] == '/')
5636                 fullPath = fullPath.substr(1);
5637             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5638             iter = allFiles.find(fullPath);
5639             if (iter != allFiles.end())
5640                 {
5641                 FileRec *other = iter->second;
5642                 //trace("other: '%s'", iname.c_str());
5643                 frec->files[fullPath] = other;
5644                 return true;
5645                 }
5646             }
5647         }
5648     return true;
5653 /**
5654  *  Lightly parse a file to find the #include directives.  Do
5655  *  a bit of state machine stuff to make sure that the directive
5656  *  is valid.  (Like not in a comment).
5657  */
5658 bool DepTool::scanFile(const String &fname, FileRec *frec)
5660     String fileName;
5661     if (sourceDir.size() > 0)
5662         {
5663         fileName.append(sourceDir);
5664         fileName.append("/");
5665         }
5666     fileName.append(fname);
5667     String nativeName = getNativePath(fileName);
5668     FILE *f = fopen(nativeName.c_str(), "r");
5669     if (!f)
5670         {
5671         error("Could not open '%s' for reading", fname.c_str());
5672         return false;
5673         }
5674     String buf;
5675     while (!feof(f))
5676         {
5677         int nrbytes = fread(readBuf, 1, readBufSize, f);
5678         readBuf[nrbytes] = '\0';
5679         buf.append(readBuf);
5680         }
5681     fclose(f);
5683     depFileSize = buf.size();
5684     depFileBuf  = (char *)buf.c_str();
5685     int pos = 0;
5688     while (pos < depFileSize)
5689         {
5690         //trace("p:%c", get(pos));
5692         //# Block comment
5693         if (get(pos) == '/' && get(pos+1) == '*')
5694             {
5695             pos += 2;
5696             while (pos < depFileSize)
5697                 {
5698                 if (get(pos) == '*' && get(pos+1) == '/')
5699                     {
5700                     pos += 2;
5701                     break;
5702                     }
5703                 else
5704                     pos++;
5705                 }
5706             }
5707         //# Line comment
5708         else if (get(pos) == '/' && get(pos+1) == '/')
5709             {
5710             pos += 2;
5711             while (pos < depFileSize)
5712                 {
5713                 if (get(pos) == '\n')
5714                     {
5715                     pos++;
5716                     break;
5717                     }
5718                 else
5719                     pos++;
5720                 }
5721             }
5722         //# #include! yaay
5723         else if (sequ(pos, "#include"))
5724             {
5725             pos += 8;
5726             pos = skipwhite(pos);
5727             String iname;
5728             pos = getword(pos, iname);
5729             if (iname.size()>2)
5730                 {
5731                 iname = iname.substr(1, iname.size()-2);
5732                 addIncludeFile(frec, iname);
5733                 }
5734             }
5735         else
5736             {
5737             pos++;
5738             }
5739         }
5741     return true;
5746 /**
5747  *  Recursively check include lists to find all files in allFiles to which
5748  *  a given file is dependent.
5749  */
5750 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5752     std::map<String, FileRec *>::iterator iter;
5753     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5754         {
5755         String fname  = iter->first;
5756         if (ofile->files.find(fname) != ofile->files.end())
5757             {
5758             //trace("file '%s' already seen", fname.c_str());
5759             continue;
5760             }
5761         FileRec *child  = iter->second;
5762         ofile->files[fname] = child;
5763       
5764         processDependency(ofile, child);
5765         }
5768     return true;
5775 /**
5776  *  Generate the file dependency list.
5777  */
5778 bool DepTool::generateDependencies()
5780     std::map<String, FileRec *>::iterator iter;
5781     //# First pass.  Scan for all includes
5782     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5783         {
5784         FileRec *frec = iter->second;
5785         if (!scanFile(iter->first, frec))
5786             {
5787             //quit?
5788             }
5789         }
5791     //# Second pass.  Scan for all includes
5792     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5793         {
5794         FileRec *include = iter->second;
5795         if (include->type == FileRec::CFILE)
5796             {
5797             //String cFileName   = iter->first;
5798             FileRec *ofile     = new FileRec(FileRec::OFILE);
5799             ofile->path        = include->path;
5800             ofile->baseName    = include->baseName;
5801             ofile->suffix      = include->suffix;
5802             String fname       = include->path;
5803             if (fname.size()>0)
5804                 fname.append("/");
5805             fname.append(include->baseName);
5806             fname.append(".o");
5807             oFiles[fname]    = ofile;
5808             //add the .c file first?   no, don't
5809             //ofile->files[cFileName] = include;
5810             
5811             //trace("ofile:%s", fname.c_str());
5813             processDependency(ofile, include);
5814             }
5815         }
5817       
5818     return true;
5823 /**
5824  *  High-level call to generate deps and optionally save them
5825  */
5826 bool DepTool::generateDependencies(const String &fileName)
5828     if (!createFileList())
5829         return false;
5830     if (!generateDependencies())
5831         return false;
5832     if (!saveDepFile(fileName))
5833         return false;
5834     return true;
5838 /**
5839  *   This saves the dependency cache.
5840  */
5841 bool DepTool::saveDepFile(const String &fileName)
5843     time_t tim;
5844     time(&tim);
5846     FILE *f = fopen(fileName.c_str(), "w");
5847     if (!f)
5848         {
5849         trace("cannot open '%s' for writing", fileName.c_str());
5850         }
5851     fprintf(f, "<?xml version='1.0'?>\n");
5852     fprintf(f, "<!--\n");
5853     fprintf(f, "########################################################\n");
5854     fprintf(f, "## File: build.dep\n");
5855     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5856     fprintf(f, "########################################################\n");
5857     fprintf(f, "-->\n");
5859     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5860     std::map<String, FileRec *>::iterator iter;
5861     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5862         {
5863         FileRec *frec = iter->second;
5864         if (frec->type == FileRec::OFILE)
5865             {
5866             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5867                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5868             std::map<String, FileRec *>::iterator citer;
5869             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5870                 {
5871                 String cfname = citer->first;
5872                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5873                 }
5874             fprintf(f, "</object>\n\n");
5875             }
5876         }
5878     fprintf(f, "</dependencies>\n");
5879     fprintf(f, "\n");
5880     fprintf(f, "<!--\n");
5881     fprintf(f, "########################################################\n");
5882     fprintf(f, "## E N D\n");
5883     fprintf(f, "########################################################\n");
5884     fprintf(f, "-->\n");
5886     fclose(f);
5888     return true;
5894 /**
5895  *   This loads the dependency cache.
5896  */
5897 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5899     std::vector<DepRec> result;
5900     
5901     Parser parser;
5902     Element *root = parser.parseFile(depFile.c_str());
5903     if (!root)
5904         {
5905         //error("Could not open %s for reading", depFile.c_str());
5906         return result;
5907         }
5909     if (root->getChildren().size()==0 ||
5910         root->getChildren()[0]->getName()!="dependencies")
5911         {
5912         error("loadDepFile: main xml element should be <dependencies>");
5913         delete root;
5914         return result;
5915         }
5917     //########## Start parsing
5918     Element *depList = root->getChildren()[0];
5920     std::vector<Element *> objects = depList->getChildren();
5921     for (unsigned int i=0 ; i<objects.size() ; i++)
5922         {
5923         Element *objectElem = objects[i];
5924         String tagName = objectElem->getName();
5925         if (tagName != "object")
5926             {
5927             error("loadDepFile: <dependencies> should have only <object> children");
5928             return result;
5929             }
5931         String objName   = objectElem->getAttribute("name");
5932          //trace("object:%s", objName.c_str());
5933         DepRec depObject(objName);
5934         depObject.path   = objectElem->getAttribute("path");
5935         depObject.suffix = objectElem->getAttribute("suffix");
5936         //########## DESCRIPTION
5937         std::vector<Element *> depElems = objectElem->getChildren();
5938         for (unsigned int i=0 ; i<depElems.size() ; i++)
5939             {
5940             Element *depElem = depElems[i];
5941             tagName = depElem->getName();
5942             if (tagName != "dep")
5943                 {
5944                 error("loadDepFile: <object> should have only <dep> children");
5945                 return result;
5946                 }
5947             String depName = depElem->getAttribute("name");
5948             //trace("    dep:%s", depName.c_str());
5949             depObject.files.push_back(depName);
5950             }
5952         //Insert into the result list, in a sorted manner
5953         bool inserted = false;
5954         std::vector<DepRec>::iterator iter;
5955         for (iter = result.begin() ; iter != result.end() ; iter++)
5956             {
5957             String vpath = iter->path;
5958             vpath.append("/");
5959             vpath.append(iter->name);
5960             String opath = depObject.path;
5961             opath.append("/");
5962             opath.append(depObject.name);
5963             if (vpath > opath)
5964                 {
5965                 inserted = true;
5966                 iter = result.insert(iter, depObject);
5967                 break;
5968                 }
5969             }
5970         if (!inserted)
5971             result.push_back(depObject);
5972         }
5974     delete root;
5976     return result;
5980 /**
5981  *   This loads the dependency cache.
5982  */
5983 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5984                    bool forceRefresh)
5986     std::vector<DepRec> result;
5987     if (forceRefresh)
5988         {
5989         generateDependencies(depFile);
5990         result = loadDepFile(depFile);
5991         }
5992     else
5993         {
5994         //try once
5995         result = loadDepFile(depFile);
5996         if (result.size() == 0)
5997             {
5998             //fail? try again
5999             generateDependencies(depFile);
6000             result = loadDepFile(depFile);
6001             }
6002         }
6003     return result;
6009 //########################################################################
6010 //# T A S K
6011 //########################################################################
6012 //forward decl
6013 class Target;
6014 class Make;
6016 /**
6017  *
6018  */
6019 class Task : public MakeBase
6022 public:
6024     typedef enum
6025         {
6026         TASK_NONE,
6027         TASK_CC,
6028         TASK_COPY,
6029         TASK_DELETE,
6030         TASK_ECHO,
6031         TASK_JAR,
6032         TASK_JAVAC,
6033         TASK_LINK,
6034         TASK_MAKEFILE,
6035         TASK_MKDIR,
6036         TASK_MSGFMT,
6037         TASK_PKG_CONFIG,
6038         TASK_RANLIB,
6039         TASK_RC,
6040         TASK_SHAREDLIB,
6041         TASK_STATICLIB,
6042         TASK_STRIP,
6043         TASK_TOUCH,
6044         TASK_TSTAMP
6045         } TaskType;
6046         
6048     /**
6049      *
6050      */
6051     Task(MakeBase &par) : parent(par)
6052         { init(); }
6054     /**
6055      *
6056      */
6057     Task(const Task &other) : parent(other.parent)
6058         { init(); assign(other); }
6060     /**
6061      *
6062      */
6063     Task &operator=(const Task &other)
6064         { assign(other); return *this; }
6066     /**
6067      *
6068      */
6069     virtual ~Task()
6070         { }
6073     /**
6074      *
6075      */
6076     virtual MakeBase &getParent()
6077         { return parent; }
6079      /**
6080      *
6081      */
6082     virtual int  getType()
6083         { return type; }
6085     /**
6086      *
6087      */
6088     virtual void setType(int val)
6089         { type = val; }
6091     /**
6092      *
6093      */
6094     virtual String getName()
6095         { return name; }
6097     /**
6098      *
6099      */
6100     virtual bool execute()
6101         { return true; }
6103     /**
6104      *
6105      */
6106     virtual bool parse(Element *elem)
6107         { return true; }
6109     /**
6110      *
6111      */
6112     Task *createTask(Element *elem, int lineNr);
6115 protected:
6117     void init()
6118         {
6119         type = TASK_NONE;
6120         name = "none";
6121         }
6123     void assign(const Task &other)
6124         {
6125         type = other.type;
6126         name = other.name;
6127         }
6128         
6129     /**
6130      *  Show task status
6131      */
6132     void taskstatus(const char *fmt, ...)
6133         {
6134         va_list args;
6135         va_start(args,fmt);
6136         fprintf(stdout, "    %s : ", name.c_str());
6137         vfprintf(stdout, fmt, args);
6138         fprintf(stdout, "\n");
6139         va_end(args) ;
6140         }
6142     String getAttribute(Element *elem, const String &attrName)
6143         {
6144         String str;
6145         return str;
6146         }
6148     MakeBase &parent;
6150     int type;
6152     String name;
6153 };
6157 /**
6158  * This task runs the C/C++ compiler.  The compiler is invoked
6159  * for all .c or .cpp files which are newer than their correcsponding
6160  * .o files.  
6161  */
6162 class TaskCC : public Task
6164 public:
6166     TaskCC(MakeBase &par) : Task(par)
6167         {
6168         type = TASK_CC;
6169         name            = "cc";
6170         ccCommand       = "gcc";
6171         cxxCommand      = "g++";
6172         source          = ".";
6173         dest            = ".";
6174         flags           = "";
6175         defines         = "";
6176         includes        = "";
6177         continueOnError = false;
6178         refreshCache    = false;
6179         fileSet.clear();
6180         excludeInc.clear();
6181         }
6183     virtual ~TaskCC()
6184         {}
6186     virtual bool isExcludedInc(const String &dirname)
6187         {
6188         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6189             {
6190             String fname = excludeInc[i];
6191             if (fname == dirname)
6192                 return true;
6193             }
6194         return false;
6195         }
6197     virtual bool execute()
6198         {
6199         if (!listFiles(parent, fileSet))
6200             return false;
6201             
6202         FILE *f = NULL;
6203         f = fopen("compile.lst", "w");
6205         //refreshCache is probably false here, unless specified otherwise
6206         String fullName = parent.resolve("build.dep");
6207         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6208             {
6209             taskstatus("regenerating C/C++ dependency cache");
6210             refreshCache = true;
6211             }
6213         DepTool depTool;
6214         depTool.setSourceDirectory(source);
6215         depTool.setFileList(fileSet.getFiles());
6216         std::vector<DepRec> deps =
6217              depTool.getDepFile("build.dep", refreshCache);
6218         
6219         String incs;
6220         incs.append("-I");
6221         incs.append(parent.resolve("."));
6222         incs.append(" ");
6223         if (includes.size()>0)
6224             {
6225             incs.append(includes);
6226             incs.append(" ");
6227             }
6228         std::set<String> paths;
6229         std::vector<DepRec>::iterator viter;
6230         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6231             {
6232             DepRec dep = *viter;
6233             if (dep.path.size()>0)
6234                 paths.insert(dep.path);
6235             }
6236         if (source.size()>0)
6237             {
6238             incs.append(" -I");
6239             incs.append(parent.resolve(source));
6240             incs.append(" ");
6241             }
6242         std::set<String>::iterator setIter;
6243         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6244             {
6245             String dirName = *setIter;
6246             //check excludeInc to see if we dont want to include this dir
6247             if (isExcludedInc(dirName))
6248                 continue;
6249             incs.append(" -I");
6250             String dname;
6251             if (source.size()>0)
6252                 {
6253                 dname.append(source);
6254                 dname.append("/");
6255                 }
6256             dname.append(dirName);
6257             incs.append(parent.resolve(dname));
6258             }
6259             
6260         /**
6261          * Compile each of the C files that need it
6262          */
6263         bool errorOccurred = false;                 
6264         std::vector<String> cfiles;
6265         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6266             {
6267             DepRec dep = *viter;
6269             //## Select command
6270             String sfx = dep.suffix;
6271             String command = ccCommand;
6272             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6273                  sfx == "cc" || sfx == "CC")
6274                 command = cxxCommand;
6275  
6276             //## Make paths
6277             String destPath = dest;
6278             String srcPath  = source;
6279             if (dep.path.size()>0)
6280                 {
6281                 destPath.append("/");
6282                 destPath.append(dep.path);
6283                 srcPath.append("/");
6284                 srcPath.append(dep.path);
6285                 }
6286             //## Make sure destination directory exists
6287             if (!createDirectory(destPath))
6288                 return false;
6289                 
6290             //## Check whether it needs to be done
6291             String destName;
6292             if (destPath.size()>0)
6293                 {
6294                 destName.append(destPath);
6295                 destName.append("/");
6296                 }
6297             destName.append(dep.name);
6298             destName.append(".o");
6299             String destFullName = parent.resolve(destName);
6300             String srcName;
6301             if (srcPath.size()>0)
6302                 {
6303                 srcName.append(srcPath);
6304                 srcName.append("/");
6305                 }
6306             srcName.append(dep.name);
6307             srcName.append(".");
6308             srcName.append(dep.suffix);
6309             String srcFullName = parent.resolve(srcName);
6310             bool compileMe = false;
6311             //# First we check if the source is newer than the .o
6312             if (isNewerThan(srcFullName, destFullName))
6313                 {
6314                 taskstatus("compile of %s required by source: %s",
6315                         destFullName.c_str(), srcFullName.c_str());
6316                 compileMe = true;
6317                 }
6318             else
6319                 {
6320                 //# secondly, we check if any of the included dependencies
6321                 //# of the .c/.cpp is newer than the .o
6322                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6323                     {
6324                     String depName;
6325                     if (source.size()>0)
6326                         {
6327                         depName.append(source);
6328                         depName.append("/");
6329                         }
6330                     depName.append(dep.files[i]);
6331                     String depFullName = parent.resolve(depName);
6332                     bool depRequires = isNewerThan(depFullName, destFullName);
6333                     //trace("%d %s %s\n", depRequires,
6334                     //        destFullName.c_str(), depFullName.c_str());
6335                     if (depRequires)
6336                         {
6337                         taskstatus("compile of %s required by included: %s",
6338                                 destFullName.c_str(), depFullName.c_str());
6339                         compileMe = true;
6340                         break;
6341                         }
6342                     }
6343                 }
6344             if (!compileMe)
6345                 {
6346                 continue;
6347                 }
6349             //## Assemble the command
6350             String cmd = command;
6351             cmd.append(" -c ");
6352             cmd.append(flags);
6353             cmd.append(" ");
6354             cmd.append(defines);
6355             cmd.append(" ");
6356             cmd.append(incs);
6357             cmd.append(" ");
6358             cmd.append(srcFullName);
6359             cmd.append(" -o ");
6360             cmd.append(destFullName);
6362             //## Execute the command
6364             String outString, errString;
6365             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6367             if (f)
6368                 {
6369                 fprintf(f, "########################### File : %s\n",
6370                              srcFullName.c_str());
6371                 fprintf(f, "#### COMMAND ###\n");
6372                 int col = 0;
6373                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6374                     {
6375                     char ch = cmd[i];
6376                     if (isspace(ch)  && col > 63)
6377                         {
6378                         fputc('\n', f);
6379                         col = 0;
6380                         }
6381                     else
6382                         {
6383                         fputc(ch, f);
6384                         col++;
6385                         }
6386                     if (col > 76)
6387                         {
6388                         fputc('\n', f);
6389                         col = 0;
6390                         }
6391                     }
6392                 fprintf(f, "\n");
6393                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6394                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6395                 fflush(f);
6396                 }
6397             if (!ret)
6398                 {
6399                 error("problem compiling: %s", errString.c_str());
6400                 errorOccurred = true;
6401                 }
6402             if (errorOccurred && !continueOnError)
6403                 break;
6404             }
6406         if (f)
6407             {
6408             fclose(f);
6409             }
6410         
6411         return !errorOccurred;
6412         }
6415     virtual bool parse(Element *elem)
6416         {
6417         String s;
6418         if (!parent.getAttribute(elem, "command", s))
6419             return false;
6420         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6421         if (!parent.getAttribute(elem, "cc", s))
6422             return false;
6423         if (s.size()>0) ccCommand = s;
6424         if (!parent.getAttribute(elem, "cxx", s))
6425             return false;
6426         if (s.size()>0) cxxCommand = s;
6427         if (!parent.getAttribute(elem, "destdir", s))
6428             return false;
6429         if (s.size()>0) dest = s;
6430         if (!parent.getAttribute(elem, "continueOnError", s))
6431             return false;
6432         if (s=="true" || s=="yes")
6433             continueOnError = true;
6434         if (!parent.getAttribute(elem, "refreshCache", s))
6435             return false;
6436         if (s=="true" || s=="yes")
6437             refreshCache = true;
6439         std::vector<Element *> children = elem->getChildren();
6440         for (unsigned int i=0 ; i<children.size() ; i++)
6441             {
6442             Element *child = children[i];
6443             String tagName = child->getName();
6444             if (tagName == "flags")
6445                 {
6446                 if (!parent.getValue(child, flags))
6447                     return false;
6448                 flags = strip(flags);
6449                 }
6450             else if (tagName == "includes")
6451                 {
6452                 if (!parent.getValue(child, includes))
6453                     return false;
6454                 includes = strip(includes);
6455                 }
6456             else if (tagName == "defines")
6457                 {
6458                 if (!parent.getValue(child, defines))
6459                     return false;
6460                 defines = strip(defines);
6461                 }
6462             else if (tagName == "fileset")
6463                 {
6464                 if (!parseFileSet(child, parent, fileSet))
6465                     return false;
6466                 source = fileSet.getDirectory();
6467                 }
6468             else if (tagName == "excludeinc")
6469                 {
6470                 if (!parseFileList(child, parent, excludeInc))
6471                     return false;
6472                 }
6473             }
6475         return true;
6476         }
6477         
6478 protected:
6480     String   ccCommand;
6481     String   cxxCommand;
6482     String   source;
6483     String   dest;
6484     String   flags;
6485     String   lastflags;
6486     String   defines;
6487     String   includes;
6488     bool     continueOnError;
6489     bool     refreshCache;
6490     FileSet  fileSet;
6491     FileList excludeInc;
6492     
6493 };
6497 /**
6498  *
6499  */
6500 class TaskCopy : public Task
6502 public:
6504     typedef enum
6505         {
6506         CP_NONE,
6507         CP_TOFILE,
6508         CP_TODIR
6509         } CopyType;
6511     TaskCopy(MakeBase &par) : Task(par)
6512         {
6513         type = TASK_COPY; name = "copy";
6514         cptype = CP_NONE;
6515         verbose = false;
6516         haveFileSet = false;
6517         }
6519     virtual ~TaskCopy()
6520         {}
6522     virtual bool execute()
6523         {
6524         switch (cptype)
6525            {
6526            case CP_TOFILE:
6527                {
6528                if (fileName.size()>0)
6529                    {
6530                    taskstatus("%s to %s",
6531                         fileName.c_str(), toFileName.c_str());
6532                    String fullSource = parent.resolve(fileName);
6533                    String fullDest = parent.resolve(toFileName);
6534                    //trace("copy %s to file %s", fullSource.c_str(),
6535                    //                       fullDest.c_str());
6536                    if (!isRegularFile(fullSource))
6537                        {
6538                        error("copy : file %s does not exist", fullSource.c_str());
6539                        return false;
6540                        }
6541                    if (!isNewerThan(fullSource, fullDest))
6542                        {
6543                        taskstatus("skipped");
6544                        return true;
6545                        }
6546                    if (!copyFile(fullSource, fullDest))
6547                        return false;
6548                    taskstatus("1 file copied");
6549                    }
6550                return true;
6551                }
6552            case CP_TODIR:
6553                {
6554                if (haveFileSet)
6555                    {
6556                    if (!listFiles(parent, fileSet))
6557                        return false;
6558                    String fileSetDir = fileSet.getDirectory();
6560                    taskstatus("%s to %s",
6561                        fileSetDir.c_str(), toDirName.c_str());
6563                    int nrFiles = 0;
6564                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6565                        {
6566                        String fileName = fileSet[i];
6568                        String sourcePath;
6569                        if (fileSetDir.size()>0)
6570                            {
6571                            sourcePath.append(fileSetDir);
6572                            sourcePath.append("/");
6573                            }
6574                        sourcePath.append(fileName);
6575                        String fullSource = parent.resolve(sourcePath);
6576                        
6577                        //Get the immediate parent directory's base name
6578                        String baseFileSetDir = fileSetDir;
6579                        unsigned int pos = baseFileSetDir.find_last_of('/');
6580                        if (pos!=baseFileSetDir.npos &&
6581                                   pos < baseFileSetDir.size()-1)
6582                            baseFileSetDir =
6583                               baseFileSetDir.substr(pos+1,
6584                                    baseFileSetDir.size());
6585                        //Now make the new path
6586                        String destPath;
6587                        if (toDirName.size()>0)
6588                            {
6589                            destPath.append(toDirName);
6590                            destPath.append("/");
6591                            }
6592                        if (baseFileSetDir.size()>0)
6593                            {
6594                            destPath.append(baseFileSetDir);
6595                            destPath.append("/");
6596                            }
6597                        destPath.append(fileName);
6598                        String fullDest = parent.resolve(destPath);
6599                        //trace("fileName:%s", fileName.c_str());
6600                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6601                        //                   fullDest.c_str());
6602                        if (!isNewerThan(fullSource, fullDest))
6603                            {
6604                            //trace("copy skipping %s", fullSource.c_str());
6605                            continue;
6606                            }
6607                        if (!copyFile(fullSource, fullDest))
6608                            return false;
6609                        nrFiles++;
6610                        }
6611                    taskstatus("%d file(s) copied", nrFiles);
6612                    }
6613                else //file source
6614                    {
6615                    //For file->dir we want only the basename of
6616                    //the source appended to the dest dir
6617                    taskstatus("%s to %s", 
6618                        fileName.c_str(), toDirName.c_str());
6619                    String baseName = fileName;
6620                    unsigned int pos = baseName.find_last_of('/');
6621                    if (pos!=baseName.npos && pos<baseName.size()-1)
6622                        baseName = baseName.substr(pos+1, baseName.size());
6623                    String fullSource = parent.resolve(fileName);
6624                    String destPath;
6625                    if (toDirName.size()>0)
6626                        {
6627                        destPath.append(toDirName);
6628                        destPath.append("/");
6629                        }
6630                    destPath.append(baseName);
6631                    String fullDest = parent.resolve(destPath);
6632                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6633                    //                       fullDest.c_str());
6634                    if (!isRegularFile(fullSource))
6635                        {
6636                        error("copy : file %s does not exist", fullSource.c_str());
6637                        return false;
6638                        }
6639                    if (!isNewerThan(fullSource, fullDest))
6640                        {
6641                        taskstatus("skipped");
6642                        return true;
6643                        }
6644                    if (!copyFile(fullSource, fullDest))
6645                        return false;
6646                    taskstatus("1 file copied");
6647                    }
6648                return true;
6649                }
6650            }
6651         return true;
6652         }
6655     virtual bool parse(Element *elem)
6656         {
6657         if (!parent.getAttribute(elem, "file", fileName))
6658             return false;
6659         if (!parent.getAttribute(elem, "tofile", toFileName))
6660             return false;
6661         if (toFileName.size() > 0)
6662             cptype = CP_TOFILE;
6663         if (!parent.getAttribute(elem, "todir", toDirName))
6664             return false;
6665         if (toDirName.size() > 0)
6666             cptype = CP_TODIR;
6667         String ret;
6668         if (!parent.getAttribute(elem, "verbose", ret))
6669             return false;
6670         if (ret.size()>0 && !getBool(ret, verbose))
6671             return false;
6672             
6673         haveFileSet = false;
6674         
6675         std::vector<Element *> children = elem->getChildren();
6676         for (unsigned int i=0 ; i<children.size() ; i++)
6677             {
6678             Element *child = children[i];
6679             String tagName = child->getName();
6680             if (tagName == "fileset")
6681                 {
6682                 if (!parseFileSet(child, parent, fileSet))
6683                     {
6684                     error("problem getting fileset");
6685                     return false;
6686                     }
6687                 haveFileSet = true;
6688                 }
6689             }
6691         //Perform validity checks
6692         if (fileName.size()>0 && fileSet.size()>0)
6693             {
6694             error("<copy> can only have one of : file= and <fileset>");
6695             return false;
6696             }
6697         if (toFileName.size()>0 && toDirName.size()>0)
6698             {
6699             error("<copy> can only have one of : tofile= or todir=");
6700             return false;
6701             }
6702         if (haveFileSet && toDirName.size()==0)
6703             {
6704             error("a <copy> task with a <fileset> must have : todir=");
6705             return false;
6706             }
6707         if (cptype == CP_TOFILE && fileName.size()==0)
6708             {
6709             error("<copy> tofile= must be associated with : file=");
6710             return false;
6711             }
6712         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6713             {
6714             error("<copy> todir= must be associated with : file= or <fileset>");
6715             return false;
6716             }
6718         return true;
6719         }
6720         
6721 private:
6723     int cptype;
6724     String fileName;
6725     FileSet fileSet;
6726     String toFileName;
6727     String toDirName;
6728     bool verbose;
6729     bool haveFileSet;
6730 };
6733 /**
6734  *
6735  */
6736 class TaskDelete : public Task
6738 public:
6740     typedef enum
6741         {
6742         DEL_FILE,
6743         DEL_DIR,
6744         DEL_FILESET
6745         } DeleteType;
6747     TaskDelete(MakeBase &par) : Task(par)
6748         { 
6749           type        = TASK_DELETE;
6750           name        = "delete";
6751           delType     = DEL_FILE;
6752           verbose     = false;
6753           quiet       = false;
6754           failOnError = true;
6755         }
6757     virtual ~TaskDelete()
6758         {}
6760     virtual bool execute()
6761         {
6762         struct stat finfo;
6763         switch (delType)
6764             {
6765             case DEL_FILE:
6766                 {
6767                 status("          : %s", fileName.c_str());
6768                 String fullName = parent.resolve(fileName);
6769                 char *fname = (char *)fullName.c_str();
6770                 //does not exist
6771                 if (stat(fname, &finfo)<0)
6772                     return true;
6773                 //exists but is not a regular file
6774                 if (!S_ISREG(finfo.st_mode))
6775                     {
6776                     error("<delete> failed. '%s' exists and is not a regular file",
6777                           fname);
6778                     return false;
6779                     }
6780                 if (remove(fname)<0)
6781                     {
6782                     error("<delete> failed: %s", strerror(errno));
6783                     return false;
6784                     }
6785                 return true;
6786                 }
6787             case DEL_DIR:
6788                 {
6789                 taskstatus("%s", dirName.c_str());
6790                 String fullDir = parent.resolve(dirName);
6791                 if (!removeDirectory(fullDir))
6792                     return false;
6793                 return true;
6794                 }
6795             }
6796         return true;
6797         }
6799     virtual bool parse(Element *elem)
6800         {
6801         if (!parent.getAttribute(elem, "file", fileName))
6802             return false;
6803         if (fileName.size() > 0)
6804             delType = DEL_FILE;
6805         if (!parent.getAttribute(elem, "dir", dirName))
6806             return false;
6807         if (dirName.size() > 0)
6808             delType = DEL_DIR;
6809         if (fileName.size()>0 && dirName.size()>0)
6810             {
6811             error("<delete> can have one attribute of file= or dir=");
6812             return false;
6813             }
6814         if (fileName.size()==0 && dirName.size()==0)
6815             {
6816             error("<delete> must have one attribute of file= or dir=");
6817             return false;
6818             }
6819         String ret;
6820         if (!parent.getAttribute(elem, "verbose", ret))
6821             return false;
6822         if (ret.size()>0 && !getBool(ret, verbose))
6823             return false;
6824         if (!parent.getAttribute(elem, "quiet", ret))
6825             return false;
6826         if (ret.size()>0 && !getBool(ret, quiet))
6827             return false;
6828         if (!parent.getAttribute(elem, "failonerror", ret))
6829             return false;
6830         if (ret.size()>0 && !getBool(ret, failOnError))
6831             return false;
6832         return true;
6833         }
6835 private:
6837     int delType;
6838     String dirName;
6839     String fileName;
6840     bool verbose;
6841     bool quiet;
6842     bool failOnError;
6843 };
6846 /**
6847  * Send a message to stdout
6848  */
6849 class TaskEcho : public Task
6851 public:
6853     TaskEcho(MakeBase &par) : Task(par)
6854         { type = TASK_ECHO; name = "echo"; }
6856     virtual ~TaskEcho()
6857         {}
6859     virtual bool execute()
6860         {
6861         //let message have priority over text
6862         if (message.size() > 0)
6863             {
6864             String s;
6865                         if (!parent.eval(message, s))
6866                             return false;
6867             fprintf(stdout, "%s\n", s.c_str());
6868             }
6869         else if (text.size() > 0)
6870             {
6871             String s;
6872                         if (!parent.eval(text, s))
6873                             return false;
6874             fprintf(stdout, "%s\n", s.c_str());
6875             }
6876         return true;
6877         }
6879     virtual bool parse(Element *elem)
6880         {
6881         text = elem->getValue();
6882         text = leftJustify(text);
6883         message = elem->getAttribute("message");
6884         return true;
6885         }
6887 private:
6889     String message;
6890     String text;
6891 };
6895 /**
6896  *
6897  */
6898 class TaskJar : public Task
6900 public:
6902     TaskJar(MakeBase &par) : Task(par)
6903         { type = TASK_JAR; name = "jar"; command = "jar";}
6905     virtual ~TaskJar()
6906         {}
6908     virtual bool execute()
6909         {
6910         String cmd = command;
6911         cmd.append(" -cf ");
6912         cmd.append(destfile);
6913         cmd.append(" -C ");
6914         cmd.append(basedir);
6915         cmd.append(" .");
6917         String execCmd = cmd;
6919         String outString, errString;
6920         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6921         if (!ret)
6922             {
6923             error("<jar> command '%s' failed :\n %s",
6924                                       execCmd.c_str(), errString.c_str());
6925             return false;
6926             }
6927         return true;
6928         }
6930     virtual bool parse(Element *elem)
6931         {
6932         String s;
6933         if (!parent.getAttribute(elem, "command", s))
6934             return false;
6935         if (s.size() > 0)
6936             command = s;
6937         if (!parent.getAttribute(elem, "basedir", basedir))
6938             return false;
6939         if (!parent.getAttribute(elem, "destfile", destfile))
6940             return false;
6941         if (basedir.size() == 0 || destfile.size() == 0)
6942             {
6943             error("<jar> required both basedir and destfile attributes to be set");
6944             return false;
6945             }
6946         return true;
6947         }
6949 private:
6950     String command;
6951     String basedir;
6952     String destfile;
6953 };
6956 /**
6957  *
6958  */
6959 class TaskJavac : public Task
6961 public:
6963     TaskJavac(MakeBase &par) : Task(par)
6964         { 
6965         type = TASK_JAVAC; name = "javac";
6966         command = "javac";
6967         }
6969     virtual ~TaskJavac()
6970         {}
6972     virtual bool execute()
6973         {
6974         std::vector<String> fileList;
6975         if (!listFiles(srcdir, "", fileList))
6976             {
6977             return false;
6978             }
6979         String cmd = command;
6980         cmd.append(" -d ");
6981         cmd.append(destdir);
6982         cmd.append(" -classpath ");
6983         cmd.append(destdir);
6984         cmd.append(" -sourcepath ");
6985         cmd.append(srcdir);
6986         cmd.append(" ");
6987         if (target.size()>0)
6988             {
6989             cmd.append(" -target ");
6990             cmd.append(target);
6991             cmd.append(" ");
6992             }
6993         String fname = "javalist.btool";
6994         FILE *f = fopen(fname.c_str(), "w");
6995         int count = 0;
6996         for (unsigned int i=0 ; i<fileList.size() ; i++)
6997             {
6998             String fname = fileList[i];
6999             String srcName = fname;
7000             if (fname.size()<6) //x.java
7001                 continue;
7002             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7003                 continue;
7004             String baseName = fname.substr(0, fname.size()-5);
7005             String destName = baseName;
7006             destName.append(".class");
7008             String fullSrc = srcdir;
7009             fullSrc.append("/");
7010             fullSrc.append(fname);
7011             String fullDest = destdir;
7012             fullDest.append("/");
7013             fullDest.append(destName);
7014             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7015             if (!isNewerThan(fullSrc, fullDest))
7016                 continue;
7018             count++;
7019             fprintf(f, "%s\n", fullSrc.c_str());
7020             }
7021         fclose(f);
7022         if (!count)
7023             {
7024             taskstatus("nothing to do");
7025             return true;
7026             }
7028         taskstatus("compiling %d files", count);
7030         String execCmd = cmd;
7031         execCmd.append("@");
7032         execCmd.append(fname);
7034         String outString, errString;
7035         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7036         if (!ret)
7037             {
7038             error("<javac> command '%s' failed :\n %s",
7039                                       execCmd.c_str(), errString.c_str());
7040             return false;
7041             }
7042         return true;
7043         }
7045     virtual bool parse(Element *elem)
7046         {
7047         String s;
7048         if (!parent.getAttribute(elem, "command", s))
7049             return false;
7050         if (s.size() > 0)
7051             command = s;
7052         if (!parent.getAttribute(elem, "srcdir", srcdir))
7053             return false;
7054         if (!parent.getAttribute(elem, "destdir", destdir))
7055             return false;
7056         if (srcdir.size() == 0 || destdir.size() == 0)
7057             {
7058             error("<javac> required both srcdir and destdir attributes to be set");
7059             return false;
7060             }
7061         if (!parent.getAttribute(elem, "target", target))
7062             return false;
7063         return true;
7064         }
7066 private:
7068     String command;
7069     String srcdir;
7070     String destdir;
7071     String target;
7073 };
7076 /**
7077  *
7078  */
7079 class TaskLink : public Task
7081 public:
7083     TaskLink(MakeBase &par) : Task(par)
7084         {
7085         type = TASK_LINK; name = "link";
7086         command = "g++";
7087         doStrip = false;
7088         stripCommand = "strip";
7089         objcopyCommand = "objcopy";
7090         }
7092     virtual ~TaskLink()
7093         {}
7095     virtual bool execute()
7096         {
7097         if (!listFiles(parent, fileSet))
7098             return false;
7099         String fileSetDir = fileSet.getDirectory();
7100         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7101         bool doit = false;
7102         String fullTarget = parent.resolve(fileName);
7103         String cmd = command;
7104         cmd.append(" -o ");
7105         cmd.append(fullTarget);
7106         cmd.append(" ");
7107         cmd.append(flags);
7108         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7109             {
7110             cmd.append(" ");
7111             String obj;
7112             if (fileSetDir.size()>0)
7113                 {
7114                 obj.append(fileSetDir);
7115                 obj.append("/");
7116                 }
7117             obj.append(fileSet[i]);
7118             String fullObj = parent.resolve(obj);
7119             String nativeFullObj = getNativePath(fullObj);
7120             cmd.append(nativeFullObj);
7121             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7122             //          fullObj.c_str());
7123             if (isNewerThan(fullObj, fullTarget))
7124                 doit = true;
7125             }
7126         cmd.append(" ");
7127         cmd.append(libs);
7128         if (!doit)
7129             {
7130             //trace("link not needed");
7131             return true;
7132             }
7133         //trace("LINK cmd:%s", cmd.c_str());
7136         String outbuf, errbuf;
7137         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7138             {
7139             error("LINK problem: %s", errbuf.c_str());
7140             return false;
7141             }
7143         if (symFileName.size()>0)
7144             {
7145             String symFullName = parent.resolve(symFileName);
7146             cmd = objcopyCommand;
7147             cmd.append(" --only-keep-debug ");
7148             cmd.append(getNativePath(fullTarget));
7149             cmd.append(" ");
7150             cmd.append(getNativePath(symFullName));
7151             if (!executeCommand(cmd, "", outbuf, errbuf))
7152                 {
7153                 error("<strip> symbol file failed : %s", errbuf.c_str());
7154                 return false;
7155                 }
7156             }
7157             
7158         if (doStrip)
7159             {
7160             cmd = stripCommand;
7161             cmd.append(" ");
7162             cmd.append(getNativePath(fullTarget));
7163             if (!executeCommand(cmd, "", outbuf, errbuf))
7164                {
7165                error("<strip> failed : %s", errbuf.c_str());
7166                return false;
7167                }
7168             }
7170         return true;
7171         }
7173     virtual bool parse(Element *elem)
7174         {
7175         String s;
7176         if (!parent.getAttribute(elem, "command", s))
7177             return false;
7178         if (s.size()>0)
7179             command = s;
7180         if (!parent.getAttribute(elem, "objcopycommand", s))
7181             return false;
7182         if (s.size()>0)
7183             objcopyCommand = s;
7184         if (!parent.getAttribute(elem, "stripcommand", s))
7185             return false;
7186         if (s.size()>0)
7187             stripCommand = s;
7188         if (!parent.getAttribute(elem, "out", fileName))
7189             return false;
7190         if (!parent.getAttribute(elem, "strip", s))
7191             return false;
7192         if (s.size()>0 && !getBool(s, doStrip))
7193             return false;
7194         if (!parent.getAttribute(elem, "symfile", symFileName))
7195             return false;
7196             
7197         std::vector<Element *> children = elem->getChildren();
7198         for (unsigned int i=0 ; i<children.size() ; i++)
7199             {
7200             Element *child = children[i];
7201             String tagName = child->getName();
7202             if (tagName == "fileset")
7203                 {
7204                 if (!parseFileSet(child, parent, fileSet))
7205                     return false;
7206                 }
7207             else if (tagName == "flags")
7208                 {
7209                 if (!parent.getValue(child, flags))
7210                     return false;
7211                 flags = strip(flags);
7212                 }
7213             else if (tagName == "libs")
7214                 {
7215                 if (!parent.getValue(child, libs))
7216                     return false;
7217                 libs = strip(libs);
7218                 }
7219             }
7220         return true;
7221         }
7223 private:
7225     String  command;
7226     String  fileName;
7227     String  flags;
7228     String  libs;
7229     FileSet fileSet;
7230     bool    doStrip;
7231     String  symFileName;
7232     String  stripCommand;
7233     String  objcopyCommand;
7235 };
7239 /**
7240  * Create a named file
7241  */
7242 class TaskMakeFile : public Task
7244 public:
7246     TaskMakeFile(MakeBase &par) : Task(par)
7247         { type = TASK_MAKEFILE; name = "makefile"; }
7249     virtual ~TaskMakeFile()
7250         {}
7252     virtual bool execute()
7253         {
7254         taskstatus("%s", fileName.c_str());
7255         String fullName = parent.resolve(fileName);
7256         if (!isNewerThan(parent.getURI().getPath(), fullName))
7257             {
7258             //trace("skipped <makefile>");
7259             return true;
7260             }
7261         String fullNative = getNativePath(fullName);
7262         //trace("fullName:%s", fullName.c_str());
7263         FILE *f = fopen(fullNative.c_str(), "w");
7264         if (!f)
7265             {
7266             error("<makefile> could not open %s for writing : %s",
7267                 fullName.c_str(), strerror(errno));
7268             return false;
7269             }
7270         for (unsigned int i=0 ; i<text.size() ; i++)
7271             fputc(text[i], f);
7272         fputc('\n', f);
7273         fclose(f);
7274         return true;
7275         }
7277     virtual bool parse(Element *elem)
7278         {
7279         if (!parent.getAttribute(elem, "file", fileName))
7280             return false;
7281         if (fileName.size() == 0)
7282             {
7283             error("<makefile> requires 'file=\"filename\"' attribute");
7284             return false;
7285             }
7286         if (!parent.getValue(elem, text))
7287             return false;
7288         text = leftJustify(text);
7289         //trace("dirname:%s", dirName.c_str());
7290         return true;
7291         }
7293 private:
7295     String fileName;
7296     String text;
7297 };
7301 /**
7302  * Create a named directory
7303  */
7304 class TaskMkDir : public Task
7306 public:
7308     TaskMkDir(MakeBase &par) : Task(par)
7309         { type = TASK_MKDIR; name = "mkdir"; }
7311     virtual ~TaskMkDir()
7312         {}
7314     virtual bool execute()
7315         {
7316         taskstatus("%s", dirName.c_str());
7317         String fullDir = parent.resolve(dirName);
7318         //trace("fullDir:%s", fullDir.c_str());
7319         if (!createDirectory(fullDir))
7320             return false;
7321         return true;
7322         }
7324     virtual bool parse(Element *elem)
7325         {
7326         if (!parent.getAttribute(elem, "dir", dirName))
7327             return false;
7328         if (dirName.size() == 0)
7329             {
7330             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7331             return false;
7332             }
7333         return true;
7334         }
7336 private:
7338     String dirName;
7339 };
7343 /**
7344  * Create a named directory
7345  */
7346 class TaskMsgFmt: public Task
7348 public:
7350     TaskMsgFmt(MakeBase &par) : Task(par)
7351          {
7352          type    = TASK_MSGFMT;
7353          name    = "msgfmt";
7354          command = "msgfmt";
7355          owndir  = false;
7356          outName = "";
7357          }
7359     virtual ~TaskMsgFmt()
7360         {}
7362     virtual bool execute()
7363         {
7364         if (!listFiles(parent, fileSet))
7365             return false;
7366         String fileSetDir = fileSet.getDirectory();
7368         //trace("msgfmt: %d", fileSet.size());
7369         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7370             {
7371             String fileName = fileSet[i];
7372             if (getSuffix(fileName) != "po")
7373                 continue;
7374             String sourcePath;
7375             if (fileSetDir.size()>0)
7376                 {
7377                 sourcePath.append(fileSetDir);
7378                 sourcePath.append("/");
7379                 }
7380             sourcePath.append(fileName);
7381             String fullSource = parent.resolve(sourcePath);
7383             String destPath;
7384             if (toDirName.size()>0)
7385                 {
7386                 destPath.append(toDirName);
7387                 destPath.append("/");
7388                 }
7389             if (owndir)
7390                 {
7391                 String subdir = fileName;
7392                 unsigned int pos = subdir.find_last_of('.');
7393                 if (pos != subdir.npos)
7394                     subdir = subdir.substr(0, pos);
7395                 destPath.append(subdir);
7396                 destPath.append("/");
7397                 }
7398             //Pick the output file name
7399             if (outName.size() > 0)
7400                 {
7401                 destPath.append(outName);
7402                 }
7403             else
7404                 {
7405                 destPath.append(fileName);
7406                 destPath[destPath.size()-2] = 'm';
7407                 }
7409             String fullDest = parent.resolve(destPath);
7411             if (!isNewerThan(fullSource, fullDest))
7412                 {
7413                 //trace("skip %s", fullSource.c_str());
7414                 continue;
7415                 }
7416                 
7417             String cmd = command;
7418             cmd.append(" ");
7419             cmd.append(fullSource);
7420             cmd.append(" -o ");
7421             cmd.append(fullDest);
7422             
7423             int pos = fullDest.find_last_of('/');
7424             if (pos>0)
7425                 {
7426                 String fullDestPath = fullDest.substr(0, pos);
7427                 if (!createDirectory(fullDestPath))
7428                     return false;
7429                 }
7433             String outString, errString;
7434             if (!executeCommand(cmd.c_str(), "", outString, errString))
7435                 {
7436                 error("<msgfmt> problem: %s", errString.c_str());
7437                 return false;
7438                 }
7439             }
7441         return true;
7442         }
7444     virtual bool parse(Element *elem)
7445         {
7446         String s;
7447         if (!parent.getAttribute(elem, "command", s))
7448             return false;
7449         if (s.size()>0)
7450             command = s;
7451         if (!parent.getAttribute(elem, "todir", toDirName))
7452             return false;
7453         if (!parent.getAttribute(elem, "out", outName))
7454             return false;
7455         if (!parent.getAttribute(elem, "owndir", s))
7456             return false;
7457         if (s.size()>0 && !getBool(s, owndir))
7458             return false;
7459             
7460         std::vector<Element *> children = elem->getChildren();
7461         for (unsigned int i=0 ; i<children.size() ; i++)
7462             {
7463             Element *child = children[i];
7464             String tagName = child->getName();
7465             if (tagName == "fileset")
7466                 {
7467                 if (!parseFileSet(child, parent, fileSet))
7468                     return false;
7469                 }
7470             }
7471         return true;
7472         }
7474 private:
7476     String  command;
7477     String  toDirName;
7478     String  outName;
7479     FileSet fileSet;
7480     bool    owndir;
7482 };
7486 /**
7487  *  Perform a Package-Config query similar to pkg-config
7488  */
7489 class TaskPkgConfig : public Task
7491 public:
7493     typedef enum
7494         {
7495         PKG_CONFIG_QUERY_CFLAGS,
7496         PKG_CONFIG_QUERY_LIBS,
7497         PKG_CONFIG_QUERY_ALL
7498         } QueryTypes;
7500     TaskPkgConfig(MakeBase &par) : Task(par)
7501         {
7502         type = TASK_PKG_CONFIG;
7503         name = "pkg-config";
7504         }
7506     virtual ~TaskPkgConfig()
7507         {}
7509     virtual bool execute()
7510         {
7511         String path = parent.resolve(pkg_config_path);
7512         PkgConfig pkgconfig;
7513         pkgconfig.setPath(path);
7514         pkgconfig.setPrefix(prefix);
7515         if (!pkgconfig.query(pkgName))
7516             {
7517             error("<pkg-config> query failed for '%s", name.c_str());
7518             return false;
7519             }
7520         String ret;
7521         switch (query)
7522             {
7523             case PKG_CONFIG_QUERY_CFLAGS:
7524                 {
7525                 ret = pkgconfig.getCflags();
7526                 break;
7527                 }
7528             case PKG_CONFIG_QUERY_LIBS:
7529                 {
7530                 ret = pkgconfig.getLibs();
7531                 break;
7532                 }
7533             case PKG_CONFIG_QUERY_ALL:
7534                 {
7535                 ret = pkgconfig.getAll();
7536                 break;
7537                 }
7538             default:
7539                 {
7540                 error("<pkg-config> unhandled query : %d", query);
7541                 return false;
7542                 }
7543             
7544             }
7545         taskstatus("property %s = '%s'", propName.c_str(), ret.c_str());
7546         parent.setProperty(propName, ret);
7547         return true;
7548         }
7550     virtual bool parse(Element *elem)
7551         {
7552         String s;
7553         //# NAME
7554         if (!parent.getAttribute(elem, "name", s))
7555             return false;
7556         if (s.size()>0)
7557            pkgName = s;
7558         else
7559             {
7560             error("<pkg-config> requires 'name=\"package\"' attribute");
7561             return false;
7562             }
7564         //# PROPERTY
7565         if (!parent.getAttribute(elem, "property", s))
7566             return false;
7567         if (s.size()>0)
7568            propName = s;
7569         else
7570             {
7571             error("<pkg-config> requires 'property=\"name\"' attribute");
7572             return false;
7573             }
7574         if (parent.hasProperty(propName))
7575             {
7576             error("<pkg-config> property '%s' is already defined",
7577                           propName.c_str());
7578             return false;
7579             }
7580         parent.setProperty(propName, "undefined");
7582         //# PATH
7583         if (!parent.getAttribute(elem, "path", s))
7584             return false;
7585         if (s.size()>0)
7586            pkg_config_path = s;
7588         //# PREFIX
7589         if (!parent.getAttribute(elem, "prefix", s))
7590             return false;
7591         if (s.size()>0)
7592            prefix = s;
7594         //# QUERY
7595         if (!parent.getAttribute(elem, "query", s))
7596             return false;
7597         if (s == "cflags")
7598             query = PKG_CONFIG_QUERY_CFLAGS;
7599         else if (s == "libs")
7600             query = PKG_CONFIG_QUERY_LIBS;
7601         else if (s == "both")
7602             query = PKG_CONFIG_QUERY_ALL;
7603         else
7604             {
7605             error("<pkg-config> requires 'query=\"type\"' attribute");
7606             error("where type = cflags, libs, or both");
7607             return false;
7608             }
7609         return true;
7610         }
7612 private:
7614     String pkgName;
7615     String prefix;
7616     String propName;
7617     String pkg_config_path;
7618     int query;
7620 };
7627 /**
7628  *  Process an archive to allow random access
7629  */
7630 class TaskRanlib : public Task
7632 public:
7634     TaskRanlib(MakeBase &par) : Task(par)
7635         {
7636         type = TASK_RANLIB; name = "ranlib";
7637         command = "ranlib";
7638         }
7640     virtual ~TaskRanlib()
7641         {}
7643     virtual bool execute()
7644         {
7645         String fullName = parent.resolve(fileName);
7646         //trace("fullDir:%s", fullDir.c_str());
7647         String cmd = command;
7648         cmd.append(" ");
7649         cmd.append(fullName);
7650         String outbuf, errbuf;
7651         if (!executeCommand(cmd, "", outbuf, errbuf))
7652             return false;
7653         return true;
7654         }
7656     virtual bool parse(Element *elem)
7657         {
7658         String s;
7659         if (!parent.getAttribute(elem, "command", s))
7660             return false;
7661         if (s.size()>0)
7662            command = s;
7663         if (!parent.getAttribute(elem, "file", fileName))
7664             return false;
7665         if (fileName.size() == 0)
7666             {
7667             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7668             return false;
7669             }
7670         return true;
7671         }
7673 private:
7675     String fileName;
7676     String command;
7677 };
7681 /**
7682  * Run the "ar" command to archive .o's into a .a
7683  */
7684 class TaskRC : public Task
7686 public:
7688     TaskRC(MakeBase &par) : Task(par)
7689         {
7690         type = TASK_RC; name = "rc";
7691         command = "windres";
7692         }
7694     virtual ~TaskRC()
7695         {}
7697     virtual bool execute()
7698         {
7699         String fullFile = parent.resolve(fileName);
7700         String fullOut  = parent.resolve(outName);
7701         if (!isNewerThan(fullFile, fullOut))
7702             return true;
7703         String cmd = command;
7704         cmd.append(" -o ");
7705         cmd.append(fullOut);
7706         cmd.append(" ");
7707         cmd.append(flags);
7708         cmd.append(" ");
7709         cmd.append(fullFile);
7711         String outString, errString;
7712         if (!executeCommand(cmd.c_str(), "", outString, errString))
7713             {
7714             error("RC problem: %s", errString.c_str());
7715             return false;
7716             }
7717         return true;
7718         }
7720     virtual bool parse(Element *elem)
7721         {
7722         if (!parent.getAttribute(elem, "command", command))
7723             return false;
7724         if (!parent.getAttribute(elem, "file", fileName))
7725             return false;
7726         if (!parent.getAttribute(elem, "out", outName))
7727             return false;
7728         std::vector<Element *> children = elem->getChildren();
7729         for (unsigned int i=0 ; i<children.size() ; i++)
7730             {
7731             Element *child = children[i];
7732             String tagName = child->getName();
7733             if (tagName == "flags")
7734                 {
7735                 if (!parent.getValue(child, flags))
7736                     return false;
7737                 }
7738             }
7739         return true;
7740         }
7742 private:
7744     String command;
7745     String flags;
7746     String fileName;
7747     String outName;
7749 };
7753 /**
7754  *  Collect .o's into a .so or DLL
7755  */
7756 class TaskSharedLib : public Task
7758 public:
7760     TaskSharedLib(MakeBase &par) : Task(par)
7761         {
7762         type = TASK_SHAREDLIB; name = "dll";
7763         command = "dllwrap";
7764         }
7766     virtual ~TaskSharedLib()
7767         {}
7769     virtual bool execute()
7770         {
7771         //trace("###########HERE %d", fileSet.size());
7772         bool doit = false;
7773         
7774         String fullOut = parent.resolve(fileName);
7775         //trace("ar fullout: %s", fullOut.c_str());
7776         
7777         if (!listFiles(parent, fileSet))
7778             return false;
7779         String fileSetDir = fileSet.getDirectory();
7781         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7782             {
7783             String fname;
7784             if (fileSetDir.size()>0)
7785                 {
7786                 fname.append(fileSetDir);
7787                 fname.append("/");
7788                 }
7789             fname.append(fileSet[i]);
7790             String fullName = parent.resolve(fname);
7791             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7792             if (isNewerThan(fullName, fullOut))
7793                 doit = true;
7794             }
7795         //trace("Needs it:%d", doit);
7796         if (!doit)
7797             {
7798             return true;
7799             }
7801         String cmd = "dllwrap";
7802         cmd.append(" -o ");
7803         cmd.append(fullOut);
7804         if (defFileName.size()>0)
7805             {
7806             cmd.append(" --def ");
7807             cmd.append(defFileName);
7808             cmd.append(" ");
7809             }
7810         if (impFileName.size()>0)
7811             {
7812             cmd.append(" --implib ");
7813             cmd.append(impFileName);
7814             cmd.append(" ");
7815             }
7816         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7817             {
7818             String fname;
7819             if (fileSetDir.size()>0)
7820                 {
7821                 fname.append(fileSetDir);
7822                 fname.append("/");
7823                 }
7824             fname.append(fileSet[i]);
7825             String fullName = parent.resolve(fname);
7827             cmd.append(" ");
7828             cmd.append(fullName);
7829             }
7830         cmd.append(" ");
7831         cmd.append(libs);
7833         String outString, errString;
7834         if (!executeCommand(cmd.c_str(), "", outString, errString))
7835             {
7836             error("<sharedlib> problem: %s", errString.c_str());
7837             return false;
7838             }
7840         return true;
7841         }
7843     virtual bool parse(Element *elem)
7844         {
7845         if (!parent.getAttribute(elem, "file", fileName))
7846             return false;
7847         if (!parent.getAttribute(elem, "import", impFileName))
7848             return false;
7849         if (!parent.getAttribute(elem, "def", defFileName))
7850             return false;
7851             
7852         std::vector<Element *> children = elem->getChildren();
7853         for (unsigned int i=0 ; i<children.size() ; i++)
7854             {
7855             Element *child = children[i];
7856             String tagName = child->getName();
7857             if (tagName == "fileset")
7858                 {
7859                 if (!parseFileSet(child, parent, fileSet))
7860                     return false;
7861                 }
7862             else if (tagName == "libs")
7863                 {
7864                 if (!parent.getValue(child, libs))
7865                     return false;
7866                 libs = strip(libs);
7867                 }
7868             }
7869         return true;
7870         }
7872 private:
7874     String command;
7875     String fileName;
7876     String defFileName;
7877     String impFileName;
7878     FileSet fileSet;
7879     String libs;
7881 };
7885 /**
7886  * Run the "ar" command to archive .o's into a .a
7887  */
7888 class TaskStaticLib : public Task
7890 public:
7892     TaskStaticLib(MakeBase &par) : Task(par)
7893         {
7894         type = TASK_STATICLIB; name = "staticlib";
7895         command = "ar crv";
7896         }
7898     virtual ~TaskStaticLib()
7899         {}
7901     virtual bool execute()
7902         {
7903         //trace("###########HERE %d", fileSet.size());
7904         bool doit = false;
7905         
7906         String fullOut = parent.resolve(fileName);
7907         //trace("ar fullout: %s", fullOut.c_str());
7908         
7909         if (!listFiles(parent, fileSet))
7910             return false;
7911         String fileSetDir = fileSet.getDirectory();
7913         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7914             {
7915             String fname;
7916             if (fileSetDir.size()>0)
7917                 {
7918                 fname.append(fileSetDir);
7919                 fname.append("/");
7920                 }
7921             fname.append(fileSet[i]);
7922             String fullName = parent.resolve(fname);
7923             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7924             if (isNewerThan(fullName, fullOut))
7925                 doit = true;
7926             }
7927         //trace("Needs it:%d", doit);
7928         if (!doit)
7929             {
7930             return true;
7931             }
7933         String cmd = command;
7934         cmd.append(" ");
7935         cmd.append(fullOut);
7936         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7937             {
7938             String fname;
7939             if (fileSetDir.size()>0)
7940                 {
7941                 fname.append(fileSetDir);
7942                 fname.append("/");
7943                 }
7944             fname.append(fileSet[i]);
7945             String fullName = parent.resolve(fname);
7947             cmd.append(" ");
7948             cmd.append(fullName);
7949             }
7951         String outString, errString;
7952         if (!executeCommand(cmd.c_str(), "", outString, errString))
7953             {
7954             error("<staticlib> problem: %s", errString.c_str());
7955             return false;
7956             }
7958         return true;
7959         }
7962     virtual bool parse(Element *elem)
7963         {
7964         String s;
7965         if (!parent.getAttribute(elem, "command", s))
7966             return false;
7967         if (s.size()>0)
7968             command = s;
7969         if (!parent.getAttribute(elem, "file", fileName))
7970             return false;
7971             
7972         std::vector<Element *> children = elem->getChildren();
7973         for (unsigned int i=0 ; i<children.size() ; i++)
7974             {
7975             Element *child = children[i];
7976             String tagName = child->getName();
7977             if (tagName == "fileset")
7978                 {
7979                 if (!parseFileSet(child, parent, fileSet))
7980                     return false;
7981                 }
7982             }
7983         return true;
7984         }
7986 private:
7988     String command;
7989     String fileName;
7990     FileSet fileSet;
7992 };
7997 /**
7998  * Strip an executable
7999  */
8000 class TaskStrip : public Task
8002 public:
8004     TaskStrip(MakeBase &par) : Task(par)
8005         { type = TASK_STRIP; name = "strip"; }
8007     virtual ~TaskStrip()
8008         {}
8010     virtual bool execute()
8011         {
8012         String fullName = parent.resolve(fileName);
8013         //trace("fullDir:%s", fullDir.c_str());
8014         String cmd;
8015         String outbuf, errbuf;
8017         if (symFileName.size()>0)
8018             {
8019             String symFullName = parent.resolve(symFileName);
8020             cmd = "objcopy --only-keep-debug ";
8021             cmd.append(getNativePath(fullName));
8022             cmd.append(" ");
8023             cmd.append(getNativePath(symFullName));
8024             if (!executeCommand(cmd, "", outbuf, errbuf))
8025                 {
8026                 error("<strip> symbol file failed : %s", errbuf.c_str());
8027                 return false;
8028                 }
8029             }
8030             
8031         cmd = "strip ";
8032         cmd.append(getNativePath(fullName));
8033         if (!executeCommand(cmd, "", outbuf, errbuf))
8034             {
8035             error("<strip> failed : %s", errbuf.c_str());
8036             return false;
8037             }
8038         return true;
8039         }
8041     virtual bool parse(Element *elem)
8042         {
8043         if (!parent.getAttribute(elem, "file", fileName))
8044             return false;
8045         if (!parent.getAttribute(elem, "symfile", symFileName))
8046             return false;
8047         if (fileName.size() == 0)
8048             {
8049             error("<strip> requires 'file=\"fileName\"' attribute");
8050             return false;
8051             }
8052         return true;
8053         }
8055 private:
8057     String fileName;
8058     String symFileName;
8059 };
8062 /**
8063  *
8064  */
8065 class TaskTouch : public Task
8067 public:
8069     TaskTouch(MakeBase &par) : Task(par)
8070         { type = TASK_TOUCH; name = "touch"; }
8072     virtual ~TaskTouch()
8073         {}
8075     virtual bool execute()
8076         {
8077         String fullName = parent.resolve(fileName);
8078         String nativeFile = getNativePath(fullName);
8079         if (!isRegularFile(fullName) && !isDirectory(fullName))
8080             {            
8081             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8082             int ret = creat(nativeFile.c_str(), 0666);
8083             if (ret != 0) 
8084                 {
8085                 error("<touch> could not create '%s' : %s",
8086                     nativeFile.c_str(), strerror(ret));
8087                 return false;
8088                 }
8089             return true;
8090             }
8091         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8092         if (ret != 0)
8093             {
8094             error("<touch> could not update the modification time for '%s' : %s",
8095                 nativeFile.c_str(), strerror(ret));
8096             return false;
8097             }
8098         return true;
8099         }
8101     virtual bool parse(Element *elem)
8102         {
8103         //trace("touch parse");
8104         if (!parent.getAttribute(elem, "file", fileName))
8105             return false;
8106         if (fileName.size() == 0)
8107             {
8108             error("<touch> requires 'file=\"fileName\"' attribute");
8109             return false;
8110             }
8111         return true;
8112         }
8114     String fileName;
8115 };
8118 /**
8119  *
8120  */
8121 class TaskTstamp : public Task
8123 public:
8125     TaskTstamp(MakeBase &par) : Task(par)
8126         { type = TASK_TSTAMP; name = "tstamp"; }
8128     virtual ~TaskTstamp()
8129         {}
8131     virtual bool execute()
8132         {
8133         return true;
8134         }
8136     virtual bool parse(Element *elem)
8137         {
8138         //trace("tstamp parse");
8139         return true;
8140         }
8141 };
8145 /**
8146  *
8147  */
8148 Task *Task::createTask(Element *elem, int lineNr)
8150     String tagName = elem->getName();
8151     //trace("task:%s", tagName.c_str());
8152     Task *task = NULL;
8153     if (tagName == "cc")
8154         task = new TaskCC(parent);
8155     else if (tagName == "copy")
8156         task = new TaskCopy(parent);
8157     else if (tagName == "delete")
8158         task = new TaskDelete(parent);
8159     else if (tagName == "echo")
8160         task = new TaskEcho(parent);
8161     else if (tagName == "jar")
8162         task = new TaskJar(parent);
8163     else if (tagName == "javac")
8164         task = new TaskJavac(parent);
8165     else if (tagName == "link")
8166         task = new TaskLink(parent);
8167     else if (tagName == "makefile")
8168         task = new TaskMakeFile(parent);
8169     else if (tagName == "mkdir")
8170         task = new TaskMkDir(parent);
8171     else if (tagName == "msgfmt")
8172         task = new TaskMsgFmt(parent);
8173     else if (tagName == "pkg-config")
8174         task = new TaskPkgConfig(parent);
8175     else if (tagName == "ranlib")
8176         task = new TaskRanlib(parent);
8177     else if (tagName == "rc")
8178         task = new TaskRC(parent);
8179     else if (tagName == "sharedlib")
8180         task = new TaskSharedLib(parent);
8181     else if (tagName == "staticlib")
8182         task = new TaskStaticLib(parent);
8183     else if (tagName == "strip")
8184         task = new TaskStrip(parent);
8185     else if (tagName == "touch")
8186         task = new TaskTouch(parent);
8187     else if (tagName == "tstamp")
8188         task = new TaskTstamp(parent);
8189     else
8190         {
8191         error("Unknown task '%s'", tagName.c_str());
8192         return NULL;
8193         }
8195     task->setLine(lineNr);
8197     if (!task->parse(elem))
8198         {
8199         delete task;
8200         return NULL;
8201         }
8202     return task;
8207 //########################################################################
8208 //# T A R G E T
8209 //########################################################################
8211 /**
8212  *
8213  */
8214 class Target : public MakeBase
8217 public:
8219     /**
8220      *
8221      */
8222     Target(Make &par) : parent(par)
8223         { init(); }
8225     /**
8226      *
8227      */
8228     Target(const Target &other) : parent(other.parent)
8229         { init(); assign(other); }
8231     /**
8232      *
8233      */
8234     Target &operator=(const Target &other)
8235         { init(); assign(other); return *this; }
8237     /**
8238      *
8239      */
8240     virtual ~Target()
8241         { cleanup() ; }
8244     /**
8245      *
8246      */
8247     virtual Make &getParent()
8248         { return parent; }
8250     /**
8251      *
8252      */
8253     virtual String getName()
8254         { return name; }
8256     /**
8257      *
8258      */
8259     virtual void setName(const String &val)
8260         { name = val; }
8262     /**
8263      *
8264      */
8265     virtual String getDescription()
8266         { return description; }
8268     /**
8269      *
8270      */
8271     virtual void setDescription(const String &val)
8272         { description = val; }
8274     /**
8275      *
8276      */
8277     virtual void addDependency(const String &val)
8278         { deps.push_back(val); }
8280     /**
8281      *
8282      */
8283     virtual void parseDependencies(const String &val)
8284         { deps = tokenize(val, ", "); }
8286     /**
8287      *
8288      */
8289     virtual std::vector<String> &getDependencies()
8290         { return deps; }
8292     /**
8293      *
8294      */
8295     virtual String getIf()
8296         { return ifVar; }
8298     /**
8299      *
8300      */
8301     virtual void setIf(const String &val)
8302         { ifVar = val; }
8304     /**
8305      *
8306      */
8307     virtual String getUnless()
8308         { return unlessVar; }
8310     /**
8311      *
8312      */
8313     virtual void setUnless(const String &val)
8314         { unlessVar = val; }
8316     /**
8317      *
8318      */
8319     virtual void addTask(Task *val)
8320         { tasks.push_back(val); }
8322     /**
8323      *
8324      */
8325     virtual std::vector<Task *> &getTasks()
8326         { return tasks; }
8328 private:
8330     void init()
8331         {
8332         }
8334     void cleanup()
8335         {
8336         tasks.clear();
8337         }
8339     void assign(const Target &other)
8340         {
8341         //parent      = other.parent;
8342         name        = other.name;
8343         description = other.description;
8344         ifVar       = other.ifVar;
8345         unlessVar   = other.unlessVar;
8346         deps        = other.deps;
8347         tasks       = other.tasks;
8348         }
8350     Make &parent;
8352     String name;
8354     String description;
8356     String ifVar;
8358     String unlessVar;
8360     std::vector<String> deps;
8362     std::vector<Task *> tasks;
8364 };
8373 //########################################################################
8374 //# M A K E
8375 //########################################################################
8378 /**
8379  *
8380  */
8381 class Make : public MakeBase
8384 public:
8386     /**
8387      *
8388      */
8389     Make()
8390         { init(); }
8392     /**
8393      *
8394      */
8395     Make(const Make &other)
8396         { assign(other); }
8398     /**
8399      *
8400      */
8401     Make &operator=(const Make &other)
8402         { assign(other); return *this; }
8404     /**
8405      *
8406      */
8407     virtual ~Make()
8408         { cleanup(); }
8410     /**
8411      *
8412      */
8413     virtual std::map<String, Target> &getTargets()
8414         { return targets; }
8417     /**
8418      *
8419      */
8420     virtual String version()
8421         { return BUILDTOOL_VERSION; }
8423     /**
8424      * Overload a <property>
8425      */
8426     virtual bool specifyProperty(const String &name,
8427                                  const String &value);
8429     /**
8430      *
8431      */
8432     virtual bool run();
8434     /**
8435      *
8436      */
8437     virtual bool run(const String &target);
8441 private:
8443     /**
8444      *
8445      */
8446     void init();
8448     /**
8449      *
8450      */
8451     void cleanup();
8453     /**
8454      *
8455      */
8456     void assign(const Make &other);
8458     /**
8459      *
8460      */
8461     bool executeTask(Task &task);
8464     /**
8465      *
8466      */
8467     bool executeTarget(Target &target,
8468              std::set<String> &targetsCompleted);
8471     /**
8472      *
8473      */
8474     bool execute();
8476     /**
8477      *
8478      */
8479     bool checkTargetDependencies(Target &prop,
8480                     std::vector<String> &depList);
8482     /**
8483      *
8484      */
8485     bool parsePropertyFile(const String &fileName,
8486                            const String &prefix);
8488     /**
8489      *
8490      */
8491     bool parseProperty(Element *elem);
8493     /**
8494      *
8495      */
8496     bool parseFile();
8498     /**
8499      *
8500      */
8501     std::vector<String> glob(const String &pattern);
8504     //###############
8505     //# Fields
8506     //###############
8508     String projectName;
8510     String currentTarget;
8512     String defaultTarget;
8514     String specifiedTarget;
8516     String baseDir;
8518     String description;
8519     
8520     //std::vector<Property> properties;
8521     
8522     std::map<String, Target> targets;
8524     std::vector<Task *> allTasks;
8525     
8526     std::map<String, String> specifiedProperties;
8528 };
8531 //########################################################################
8532 //# C L A S S  M A I N T E N A N C E
8533 //########################################################################
8535 /**
8536  *
8537  */
8538 void Make::init()
8540     uri             = "build.xml";
8541     projectName     = "";
8542     currentTarget   = "";
8543     defaultTarget   = "";
8544     specifiedTarget = "";
8545     baseDir         = "";
8546     description     = "";
8547     envPrefix       = "";
8548     properties.clear();
8549     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8550         delete allTasks[i];
8551     allTasks.clear();
8556 /**
8557  *
8558  */
8559 void Make::cleanup()
8561     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8562         delete allTasks[i];
8563     allTasks.clear();
8568 /**
8569  *
8570  */
8571 void Make::assign(const Make &other)
8573     uri              = other.uri;
8574     projectName      = other.projectName;
8575     currentTarget    = other.currentTarget;
8576     defaultTarget    = other.defaultTarget;
8577     specifiedTarget  = other.specifiedTarget;
8578     baseDir          = other.baseDir;
8579     description      = other.description;
8580     properties       = other.properties;
8585 //########################################################################
8586 //# U T I L I T Y    T A S K S
8587 //########################################################################
8589 /**
8590  *  Perform a file globbing
8591  */
8592 std::vector<String> Make::glob(const String &pattern)
8594     std::vector<String> res;
8595     return res;
8599 //########################################################################
8600 //# P U B L I C    A P I
8601 //########################################################################
8605 /**
8606  *
8607  */
8608 bool Make::executeTarget(Target &target,
8609              std::set<String> &targetsCompleted)
8612     String name = target.getName();
8614     //First get any dependencies for this target
8615     std::vector<String> deps = target.getDependencies();
8616     for (unsigned int i=0 ; i<deps.size() ; i++)
8617         {
8618         String dep = deps[i];
8619         //Did we do it already?  Skip
8620         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8621             continue;
8622             
8623         std::map<String, Target> &tgts =
8624                target.getParent().getTargets();
8625         std::map<String, Target>::iterator iter =
8626                tgts.find(dep);
8627         if (iter == tgts.end())
8628             {
8629             error("Target '%s' dependency '%s' not found",
8630                       name.c_str(),  dep.c_str());
8631             return false;
8632             }
8633         Target depTarget = iter->second;
8634         if (!executeTarget(depTarget, targetsCompleted))
8635             {
8636             return false;
8637             }
8638         }
8640     status("##### Target : %s\n##### %s", name.c_str(),
8641             target.getDescription().c_str());
8643     //Now let's do the tasks
8644     std::vector<Task *> &tasks = target.getTasks();
8645     for (unsigned int i=0 ; i<tasks.size() ; i++)
8646         {
8647         Task *task = tasks[i];
8648         status("--- %s / %s", name.c_str(), task->getName().c_str());
8649         if (!task->execute())
8650             {
8651             return false;
8652             }
8653         }
8654         
8655     targetsCompleted.insert(name);
8656     
8657     return true;
8662 /**
8663  *  Main execute() method.  Start here and work
8664  *  up the dependency tree 
8665  */
8666 bool Make::execute()
8668     status("######## EXECUTE");
8670     //Determine initial target
8671     if (specifiedTarget.size()>0)
8672         {
8673         currentTarget = specifiedTarget;
8674         }
8675     else if (defaultTarget.size()>0)
8676         {
8677         currentTarget = defaultTarget;
8678         }
8679     else
8680         {
8681         error("execute: no specified or default target requested");
8682         return false;
8683         }
8685     std::map<String, Target>::iterator iter =
8686                targets.find(currentTarget);
8687     if (iter == targets.end())
8688         {
8689         error("Initial target '%s' not found",
8690                  currentTarget.c_str());
8691         return false;
8692         }
8693         
8694     //Now run
8695     Target target = iter->second;
8696     std::set<String> targetsCompleted;
8697     if (!executeTarget(target, targetsCompleted))
8698         {
8699         return false;
8700         }
8702     status("######## EXECUTE COMPLETE");
8703     return true;
8709 /**
8710  *
8711  */
8712 bool Make::checkTargetDependencies(Target &target, 
8713                             std::vector<String> &depList)
8715     String tgtName = target.getName().c_str();
8716     depList.push_back(tgtName);
8718     std::vector<String> deps = target.getDependencies();
8719     for (unsigned int i=0 ; i<deps.size() ; i++)
8720         {
8721         String dep = deps[i];
8722         //First thing entered was the starting Target
8723         if (dep == depList[0])
8724             {
8725             error("Circular dependency '%s' found at '%s'",
8726                       dep.c_str(), tgtName.c_str());
8727             std::vector<String>::iterator diter;
8728             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8729                 {
8730                 error("  %s", diter->c_str());
8731                 }
8732             return false;
8733             }
8735         std::map<String, Target> &tgts =
8736                   target.getParent().getTargets();
8737         std::map<String, Target>::iterator titer = tgts.find(dep);
8738         if (titer == tgts.end())
8739             {
8740             error("Target '%s' dependency '%s' not found",
8741                       tgtName.c_str(), dep.c_str());
8742             return false;
8743             }
8744         if (!checkTargetDependencies(titer->second, depList))
8745             {
8746             return false;
8747             }
8748         }
8749     return true;
8756 static int getword(int pos, const String &inbuf, String &result)
8758     int p = pos;
8759     int len = (int)inbuf.size();
8760     String val;
8761     while (p < len)
8762         {
8763         char ch = inbuf[p];
8764         if (!isalnum(ch) && ch!='.' && ch!='_')
8765             break;
8766         val.push_back(ch);
8767         p++;
8768         }
8769     result = val;
8770     return p;
8776 /**
8777  *
8778  */
8779 bool Make::parsePropertyFile(const String &fileName,
8780                              const String &prefix)
8782     FILE *f = fopen(fileName.c_str(), "r");
8783     if (!f)
8784         {
8785         error("could not open property file %s", fileName.c_str());
8786         return false;
8787         }
8788     int linenr = 0;
8789     while (!feof(f))
8790         {
8791         char buf[256];
8792         if (!fgets(buf, 255, f))
8793             break;
8794         linenr++;
8795         String s = buf;
8796         s = trim(s);
8797         int len = s.size();
8798         if (len == 0)
8799             continue;
8800         if (s[0] == '#')
8801             continue;
8802         String key;
8803         String val;
8804         int p = 0;
8805         int p2 = getword(p, s, key);
8806         if (p2 <= p)
8807             {
8808             error("property file %s, line %d: expected keyword",
8809                     fileName.c_str(), linenr);
8810             return false;
8811             }
8812         if (prefix.size() > 0)
8813             {
8814             key.insert(0, prefix);
8815             }
8817         //skip whitespace
8818         for (p=p2 ; p<len ; p++)
8819             if (!isspace(s[p]))
8820                 break;
8822         if (p>=len || s[p]!='=')
8823             {
8824             error("property file %s, line %d: expected '='",
8825                     fileName.c_str(), linenr);
8826             return false;
8827             }
8828         p++;
8830         //skip whitespace
8831         for ( ; p<len ; p++)
8832             if (!isspace(s[p]))
8833                 break;
8835         /* This way expects a word after the =
8836         p2 = getword(p, s, val);
8837         if (p2 <= p)
8838             {
8839             error("property file %s, line %d: expected value",
8840                     fileName.c_str(), linenr);
8841             return false;
8842             }
8843         */
8844         // This way gets the rest of the line after the =
8845         if (p>=len)
8846             {
8847             error("property file %s, line %d: expected value",
8848                     fileName.c_str(), linenr);
8849             return false;
8850             }
8851         val = s.substr(p);
8852         if (key.size()==0)
8853             continue;
8854         //allow property to be set, even if val=""
8856         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8857         //See if we wanted to overload this property
8858         std::map<String, String>::iterator iter =
8859             specifiedProperties.find(key);
8860         if (iter!=specifiedProperties.end())
8861             {
8862             val = iter->second;
8863             status("overloading property '%s' = '%s'",
8864                    key.c_str(), val.c_str());
8865             }
8866         properties[key] = val;
8867         }
8868     fclose(f);
8869     return true;
8875 /**
8876  *
8877  */
8878 bool Make::parseProperty(Element *elem)
8880     std::vector<Attribute> &attrs = elem->getAttributes();
8881     for (unsigned int i=0 ; i<attrs.size() ; i++)
8882         {
8883         String attrName = attrs[i].getName();
8884         String attrVal  = attrs[i].getValue();
8886         if (attrName == "name")
8887             {
8888             String val;
8889             if (!getAttribute(elem, "value", val))
8890                 return false;
8891             if (val.size() > 0)
8892                 {
8893                 properties[attrVal] = val;
8894                 }
8895             else
8896                 {
8897                 if (!getAttribute(elem, "location", val))
8898                     return false;
8899                 //let the property exist, even if not defined
8900                 properties[attrVal] = val;
8901                 }
8902             //See if we wanted to overload this property
8903             std::map<String, String>::iterator iter =
8904                 specifiedProperties.find(attrVal);
8905             if (iter != specifiedProperties.end())
8906                 {
8907                 val = iter->second;
8908                 status("overloading property '%s' = '%s'",
8909                     attrVal.c_str(), val.c_str());
8910                 properties[attrVal] = val;
8911                 }
8912             }
8913         else if (attrName == "file")
8914             {
8915             String prefix;
8916             if (!getAttribute(elem, "prefix", prefix))
8917                 return false;
8918             if (prefix.size() > 0)
8919                 {
8920                 if (prefix[prefix.size()-1] != '.')
8921                     prefix.push_back('.');
8922                 }
8923             if (!parsePropertyFile(attrName, prefix))
8924                 return false;
8925             }
8926         else if (attrName == "environment")
8927             {
8928             if (envPrefix.size() > 0)
8929                 {
8930                 error("environment prefix can only be set once");
8931                 return false;
8932                 }
8933             if (attrVal.find('.') != attrVal.npos)
8934                 {
8935                 error("environment prefix cannot have a '.' in it");
8936                 return false;
8937                 }
8938             envPrefix = attrVal;
8939             envPrefix.push_back('.');
8940             }
8941         }
8943     return true;
8949 /**
8950  *
8951  */
8952 bool Make::parseFile()
8954     status("######## PARSE : %s", uri.getPath().c_str());
8956     setLine(0);
8958     Parser parser;
8959     Element *root = parser.parseFile(uri.getNativePath());
8960     if (!root)
8961         {
8962         error("Could not open %s for reading",
8963               uri.getNativePath().c_str());
8964         return false;
8965         }
8966     
8967     setLine(root->getLine());
8969     if (root->getChildren().size()==0 ||
8970         root->getChildren()[0]->getName()!="project")
8971         {
8972         error("Main xml element should be <project>");
8973         delete root;
8974         return false;
8975         }
8977     //########## Project attributes
8978     Element *project = root->getChildren()[0];
8979     String s = project->getAttribute("name");
8980     if (s.size() > 0)
8981         projectName = s;
8982     s = project->getAttribute("default");
8983     if (s.size() > 0)
8984         defaultTarget = s;
8985     s = project->getAttribute("basedir");
8986     if (s.size() > 0)
8987         baseDir = s;
8989     //######### PARSE MEMBERS
8990     std::vector<Element *> children = project->getChildren();
8991     for (unsigned int i=0 ; i<children.size() ; i++)
8992         {
8993         Element *elem = children[i];
8994         setLine(elem->getLine());
8995         String tagName = elem->getName();
8997         //########## DESCRIPTION
8998         if (tagName == "description")
8999             {
9000             description = parser.trim(elem->getValue());
9001             }
9003         //######### PROPERTY
9004         else if (tagName == "property")
9005             {
9006             if (!parseProperty(elem))
9007                 return false;
9008             }
9010         //######### TARGET
9011         else if (tagName == "target")
9012             {
9013             String tname   = elem->getAttribute("name");
9014             String tdesc   = elem->getAttribute("description");
9015             String tdeps   = elem->getAttribute("depends");
9016             String tif     = elem->getAttribute("if");
9017             String tunless = elem->getAttribute("unless");
9018             Target target(*this);
9019             target.setName(tname);
9020             target.setDescription(tdesc);
9021             target.parseDependencies(tdeps);
9022             target.setIf(tif);
9023             target.setUnless(tunless);
9024             std::vector<Element *> telems = elem->getChildren();
9025             for (unsigned int i=0 ; i<telems.size() ; i++)
9026                 {
9027                 Element *telem = telems[i];
9028                 Task breeder(*this);
9029                 Task *task = breeder.createTask(telem, telem->getLine());
9030                 if (!task)
9031                     return false;
9032                 allTasks.push_back(task);
9033                 target.addTask(task);
9034                 }
9036             //Check name
9037             if (tname.size() == 0)
9038                 {
9039                 error("no name for target");
9040                 return false;
9041                 }
9042             //Check for duplicate name
9043             if (targets.find(tname) != targets.end())
9044                 {
9045                 error("target '%s' already defined", tname.c_str());
9046                 return false;
9047                 }
9048             //more work than targets[tname]=target, but avoids default allocator
9049             targets.insert(std::make_pair<String, Target>(tname, target));
9050             }
9051         //######### none of the above
9052         else
9053             {
9054             error("unknown toplevel tag: <%s>", tagName.c_str());
9055             return false;
9056             }
9058         }
9060     std::map<String, Target>::iterator iter;
9061     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9062         {
9063         Target tgt = iter->second;
9064         std::vector<String> depList;
9065         if (!checkTargetDependencies(tgt, depList))
9066             {
9067             return false;
9068             }
9069         }
9072     delete root;
9073     status("######## PARSE COMPLETE");
9074     return true;
9078 /**
9079  * Overload a <property>
9080  */
9081 bool Make::specifyProperty(const String &name, const String &value)
9083     if (specifiedProperties.find(name) != specifiedProperties.end())
9084         {
9085         error("Property %s already specified", name.c_str());
9086         return false;
9087         }
9088     specifiedProperties[name] = value;
9089     return true;
9094 /**
9095  *
9096  */
9097 bool Make::run()
9099     if (!parseFile())
9100         return false;
9101         
9102     if (!execute())
9103         return false;
9105     return true;
9111 /**
9112  * Get a formatted MM:SS.sss time elapsed string
9113  */ 
9114 static String
9115 timeDiffString(struct timeval &x, struct timeval &y)
9117     long microsX  = x.tv_usec;
9118     long secondsX = x.tv_sec;
9119     long microsY  = y.tv_usec;
9120     long secondsY = y.tv_sec;
9121     if (microsX < microsY)
9122         {
9123         microsX += 1000000;
9124         secondsX -= 1;
9125         }
9127     int seconds = (int)(secondsX - secondsY);
9128     int millis  = (int)((microsX - microsY)/1000);
9130     int minutes = seconds/60;
9131     seconds -= minutes*60;
9132     char buf[80];
9133     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9134     String ret = buf;
9135     return ret;
9136     
9139 /**
9140  *
9141  */
9142 bool Make::run(const String &target)
9144     status("####################################################");
9145     status("#   %s", version().c_str());
9146     status("####################################################");
9147     struct timeval timeStart, timeEnd;
9148     ::gettimeofday(&timeStart, NULL);
9149     specifiedTarget = target;
9150     if (!run())
9151         return false;
9152     ::gettimeofday(&timeEnd, NULL);
9153     String timeStr = timeDiffString(timeEnd, timeStart);
9154     status("####################################################");
9155     status("#   BuildTool Completed : %s", timeStr.c_str());
9156     status("####################################################");
9157     return true;
9166 }// namespace buildtool
9167 //########################################################################
9168 //# M A I N
9169 //########################################################################
9171 typedef buildtool::String String;
9173 /**
9174  *  Format an error message in printf() style
9175  */
9176 static void error(const char *fmt, ...)
9178     va_list ap;
9179     va_start(ap, fmt);
9180     fprintf(stderr, "BuildTool error: ");
9181     vfprintf(stderr, fmt, ap);
9182     fprintf(stderr, "\n");
9183     va_end(ap);
9187 static bool parseProperty(const String &s, String &name, String &val)
9189     int len = s.size();
9190     int i;
9191     for (i=0 ; i<len ; i++)
9192         {
9193         char ch = s[i];
9194         if (ch == '=')
9195             break;
9196         name.push_back(ch);
9197         }
9198     if (i>=len || s[i]!='=')
9199         {
9200         error("property requires -Dname=value");
9201         return false;
9202         }
9203     i++;
9204     for ( ; i<len ; i++)
9205         {
9206         char ch = s[i];
9207         val.push_back(ch);
9208         }
9209     return true;
9213 /**
9214  * Compare a buffer with a key, for the length of the key
9215  */
9216 static bool sequ(const String &buf, const char *key)
9218     int len = buf.size();
9219     for (int i=0 ; key[i] && i<len ; i++)
9220         {
9221         if (key[i] != buf[i])
9222             return false;
9223         }        
9224     return true;
9227 static void usage(int argc, char **argv)
9229     printf("usage:\n");
9230     printf("   %s [options] [target]\n", argv[0]);
9231     printf("Options:\n");
9232     printf("  -help, -h              print this message\n");
9233     printf("  -version               print the version information and exit\n");
9234     printf("  -file <file>           use given buildfile\n");
9235     printf("  -f <file>                 ''\n");
9236     printf("  -D<property>=<value>   use value for given property\n");
9242 /**
9243  * Parse the command-line args, get our options,
9244  * and run this thing
9245  */   
9246 static bool parseOptions(int argc, char **argv)
9248     if (argc < 1)
9249         {
9250         error("Cannot parse arguments");
9251         return false;
9252         }
9254     buildtool::Make make;
9256     String target;
9258     //char *progName = argv[0];
9259     for (int i=1 ; i<argc ; i++)
9260         {
9261         String arg = argv[i];
9262         if (arg.size()>1 && arg[0]=='-')
9263             {
9264             if (arg == "-h" || arg == "-help")
9265                 {
9266                 usage(argc,argv);
9267                 return true;
9268                 }
9269             else if (arg == "-version")
9270                 {
9271                 printf("%s", make.version().c_str());
9272                 return true;
9273                 }
9274             else if (arg == "-f" || arg == "-file")
9275                 {
9276                 if (i>=argc)
9277                    {
9278                    usage(argc, argv);
9279                    return false;
9280                    }
9281                 i++; //eat option
9282                 make.setURI(argv[i]);
9283                 }
9284             else if (arg.size()>2 && sequ(arg, "-D"))
9285                 {
9286                 String s = arg.substr(2, arg.size());
9287                 String name, value;
9288                 if (!parseProperty(s, name, value))
9289                    {
9290                    usage(argc, argv);
9291                    return false;
9292                    }
9293                 if (!make.specifyProperty(name, value))
9294                     return false;
9295                 }
9296             else
9297                 {
9298                 error("Unknown option:%s", arg.c_str());
9299                 return false;
9300                 }
9301             }
9302         else
9303             {
9304             if (target.size()>0)
9305                 {
9306                 error("only one initial target");
9307                 usage(argc, argv);
9308                 return false;
9309                 }
9310             target = arg;
9311             }
9312         }
9314     //We have the options.  Now execute them
9315     if (!make.run(target))
9316         return false;
9318     return true;
9324 /*
9325 static bool runMake()
9327     buildtool::Make make;
9328     if (!make.run())
9329         return false;
9330     return true;
9334 static bool pkgConfigTest()
9336     buildtool::PkgConfig pkgConfig;
9337     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9338         return false;
9339     return true;
9344 static bool depTest()
9346     buildtool::DepTool deptool;
9347     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9348     if (!deptool.generateDependencies("build.dep"))
9349         return false;
9350     std::vector<buildtool::FileRec> res =
9351            deptool.loadDepFile("build.dep");
9352     if (res.size() == 0)
9353         return false;
9354     return true;
9357 static bool popenTest()
9359     buildtool::Make make;
9360     buildtool::String out, err;
9361     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9362     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9363     return true;
9367 static bool propFileTest()
9369     buildtool::Make make;
9370     make.parsePropertyFile("test.prop", "test.");
9371     return true;
9373 */
9375 int main(int argc, char **argv)
9378     if (!parseOptions(argc, argv))
9379         return 1;
9380     /*
9381     if (!popenTest())
9382         return 1;
9384     if (!depTest())
9385         return 1;
9386     if (!propFileTest())
9387         return 1;
9388     if (runMake())
9389         return 1;
9390     */
9391     return 0;
9395 //########################################################################
9396 //# E N D 
9397 //########################################################################