Code

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