Code

Increase substitution depth, remove unused vars warnings. Implement verbose, quiet...
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2008 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 /**
25  * To use this file, compile with:
26  * <pre>
27  * g++ -O3 buildtool.cpp -o btool.exe
28  * (or whatever your compiler might be)
29  * Then
30  * btool
31  * or
32  * btool {target}
33  *
34  * Note: if you are using MinGW, and a not very recent version of it,
35  * gettimeofday() might be missing.  If so, just build this file with
36  * this command:
37  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38  *
39  */
41 #define BUILDTOOL_VERSION  "BuildTool v0.9.2"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73       int tz_minuteswest; /* minutes west of Greenwich */
74       int tz_dsttime;     /* type of dst correction */
75     };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79    struct _timeb tb;
81    if (!tv)
82       return (-1);
84     _ftime (&tb);
85     tv->tv_sec  = tb.time;
86     tv->tv_usec = tb.millitm * 1000 + 500;
87     if (tz)
88         {
89         tz->tz_minuteswest = -60 * _timezone;
90         tz->tz_dsttime = _daylight;
91         }
92     return 0;
93 }
95 #endif
103 namespace buildtool
109 //########################################################################
110 //########################################################################
111 //##  R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116  * This is the T-Rex regular expression library, which we
117  * gratefully acknowledge.  It's clean code and small size allow
118  * us to embed it in BuildTool without adding a dependency
119  *
120  */    
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127     T-Rex a tiny regular expression library
129     Copyright (C) 2003-2006 Alberto Demichelis
131     This software is provided 'as-is', without any express 
132     or implied warranty. In no event will the authors be held 
133     liable for any damages arising from the use of this software.
135     Permission is granted to anyone to use this software for 
136     any purpose, including commercial applications, and to alter
137     it and redistribute it freely, subject to the following restrictions:
139         1. The origin of this software must not be misrepresented;
140         you must not claim that you wrote the original software.
141         If you use this software in a product, an acknowledgment
142         in the product documentation would be appreciated but
143         is not required.
145         2. Altered source versions must be plainly marked as such,
146         and must not be misrepresented as being the original software.
148         3. This notice may not be removed or altered from any
149         source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c 
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c) 
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178     const TRexChar *begin;
179     int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
224     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
225     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
226     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR            (MAX_CHAR+2)
233 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT            (MAX_CHAR+5)
236 #define OP_CLASS        (MAX_CHAR+6)
237 #define OP_CCLASS        (MAX_CHAR+7)
238 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE        (MAX_CHAR+9)
240 #define OP_CHAR            (MAX_CHAR+10)
241 #define OP_EOL            (MAX_CHAR+11)
242 #define OP_BOL            (MAX_CHAR+12)
243 #define OP_WB            (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258     TRexNodeType type;
259     int left;
260     int right;
261     int next;
262 }TRexNode;
264 struct TRex{
265     const TRexChar *_eol;
266     const TRexChar *_bol;
267     const TRexChar *_p;
268     int _first;
269     int _op;
270     TRexNode *_nodes;
271     int _nallocated;
272     int _nsize;
273     int _nsubexpr;
274     TRexMatch *_matches;
275     int _currsubexp;
276     void *_jmpbuf;
277     const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
284     TRexNode n;
285     int newid;
286     n.type = type;
287     n.next = n.right = n.left = -1;
288     if(type == OP_EXPR)
289         n.right = exp->_nsubexpr++;
290     if(exp->_nallocated < (exp->_nsize + 1)) {
291         //int oldsize = exp->_nallocated;
292         exp->_nallocated *= 2;
293         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294     }
295     exp->_nodes[exp->_nsize++] = n;
296     newid = exp->_nsize - 1;
297     return (int)newid;
300 static void trex_error(TRex *exp,const TRexChar *error)
302     if(exp->_error) *exp->_error = error;
303     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
306 static void trex_expect(TRex *exp, int n){
307     if((*exp->_p) != n) 
308         trex_error(exp, _SC("expected paren"));
309     exp->_p++;
312 static TRexChar trex_escapechar(TRex *exp)
314     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315         exp->_p++;
316         switch(*exp->_p) {
317         case 'v': exp->_p++; return '\v';
318         case 'n': exp->_p++; return '\n';
319         case 't': exp->_p++; return '\t';
320         case 'r': exp->_p++; return '\r';
321         case 'f': exp->_p++; return '\f';
322         default: return (*exp->_p++);
323         }
324     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325     return (*exp->_p++);
328 static int trex_charclass(TRex *exp,int classid)
330     int n = trex_newnode(exp,OP_CCLASS);
331     exp->_nodes[n].left = classid;
332     return n;
335 static int trex_charnode(TRex *exp,TRexBool isclass)
337     TRexChar t;
338     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339         exp->_p++;
340         switch(*exp->_p) {
341             case 'n': exp->_p++; return trex_newnode(exp,'\n');
342             case 't': exp->_p++; return trex_newnode(exp,'\t');
343             case 'r': exp->_p++; return trex_newnode(exp,'\r');
344             case 'f': exp->_p++; return trex_newnode(exp,'\f');
345             case 'v': exp->_p++; return trex_newnode(exp,'\v');
346             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
347             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
348             case 'p': case 'P': case 'l': case 'u': 
349                 {
350                 t = *exp->_p; exp->_p++; 
351                 return trex_charclass(exp,t);
352                 }
353             case 'b': 
354             case 'B':
355                 if(!isclass) {
356                     int node = trex_newnode(exp,OP_WB);
357                     exp->_nodes[node].left = *exp->_p;
358                     exp->_p++; 
359                     return node;
360                 } //else default
361             default: 
362                 t = *exp->_p; exp->_p++; 
363                 return trex_newnode(exp,t);
364         }
365     }
366     else if(!scisprint(*exp->_p)) {
367         
368         trex_error(exp,_SC("letter expected"));
369     }
370     t = *exp->_p; exp->_p++; 
371     return trex_newnode(exp,t);
373 static int trex_class(TRex *exp)
375     int ret = -1;
376     int first = -1,chain;
377     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378         ret = trex_newnode(exp,OP_NCLASS);
379         exp->_p++;
380     }else ret = trex_newnode(exp,OP_CLASS);
381     
382     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383     chain = ret;
384     while(*exp->_p != ']' && exp->_p != exp->_eol) {
385         if(*exp->_p == '-' && first != -1){ 
386             int r,t;
387             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388             r = trex_newnode(exp,OP_RANGE);
389             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391             exp->_nodes[r].left = exp->_nodes[first].type;
392             t = trex_escapechar(exp);
393             exp->_nodes[r].right = t;
394             exp->_nodes[chain].next = r;
395             chain = r;
396             first = -1;
397         }
398         else{
399             if(first!=-1){
400                 int c = first;
401                 exp->_nodes[chain].next = c;
402                 chain = c;
403                 first = trex_charnode(exp,TRex_True);
404             }
405             else{
406                 first = trex_charnode(exp,TRex_True);
407             }
408         }
409     }
410     if(first!=-1){
411         int c = first;
412         exp->_nodes[chain].next = c;
413         chain = c;
414         first = -1;
415     }
416     /* hack? */
417     exp->_nodes[ret].left = exp->_nodes[ret].next;
418     exp->_nodes[ret].next = -1;
419     return ret;
422 static int trex_parsenumber(TRex *exp)
424     int ret = *exp->_p-'0';
425     int positions = 10;
426     exp->_p++;
427     while(isdigit(*exp->_p)) {
428         ret = ret*10+(*exp->_p++-'0');
429         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430         positions *= 10;
431     };
432     return ret;
435 static int trex_element(TRex *exp)
437     int ret = -1;
438     switch(*exp->_p)
439     {
440     case '(': {
441         int expr,newn;
442         exp->_p++;
445         if(*exp->_p =='?') {
446             exp->_p++;
447             trex_expect(exp,':');
448             expr = trex_newnode(exp,OP_NOCAPEXPR);
449         }
450         else
451             expr = trex_newnode(exp,OP_EXPR);
452         newn = trex_list(exp);
453         exp->_nodes[expr].left = newn;
454         ret = expr;
455         trex_expect(exp,')');
456               }
457               break;
458     case '[':
459         exp->_p++;
460         ret = trex_class(exp);
461         trex_expect(exp,']');
462         break;
463     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465     default:
466         ret = trex_charnode(exp,TRex_False);
467         break;
468     }
470     {
471         int op;
472         TRexBool isgreedy = TRex_False;
473         unsigned short p0 = 0, p1 = 0;
474         switch(*exp->_p){
475             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478             case '{':
479                 exp->_p++;
480                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481                 p0 = (unsigned short)trex_parsenumber(exp);
482                 /*******************************/
483                 switch(*exp->_p) {
484             case '}':
485                 p1 = p0; exp->_p++;
486                 break;
487             case ',':
488                 exp->_p++;
489                 p1 = 0xFFFF;
490                 if(isdigit(*exp->_p)){
491                     p1 = (unsigned short)trex_parsenumber(exp);
492                 }
493                 trex_expect(exp,'}');
494                 break;
495             default:
496                 trex_error(exp,_SC(", or } expected"));
497         }
498         /*******************************/
499         isgreedy = TRex_True; 
500         break;
502         }
503         if(isgreedy) {
504             int nnode = trex_newnode(exp,OP_GREEDY);
505             op = OP_GREEDY;
506             exp->_nodes[nnode].left = ret;
507             exp->_nodes[nnode].right = ((p0)<<16)|p1;
508             ret = nnode;
509         }
510     }
511     if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
512         int nnode = trex_element(exp);
513         exp->_nodes[ret].next = nnode;
514     }
516     return ret;
519 static int trex_list(TRex *exp)
521     int ret=-1,e;
522     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523         exp->_p++;
524         ret = trex_newnode(exp,OP_BOL);
525     }
526     e = trex_element(exp);
527     if(ret != -1) {
528         exp->_nodes[ret].next = e;
529     }
530     else ret = e;
532     if(*exp->_p == TREX_SYMBOL_BRANCH) {
533         int temp,tright;
534         exp->_p++;
535         temp = trex_newnode(exp,OP_OR);
536         exp->_nodes[temp].left = ret;
537         tright = trex_list(exp);
538         exp->_nodes[temp].right = tright;
539         ret = temp;
540     }
541     return ret;
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
546     switch(cclass) {
547     case 'a': return isalpha(c)?TRex_True:TRex_False;
548     case 'A': return !isalpha(c)?TRex_True:TRex_False;
549     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551     case 's': return isspace(c)?TRex_True:TRex_False;
552     case 'S': return !isspace(c)?TRex_True:TRex_False;
553     case 'd': return isdigit(c)?TRex_True:TRex_False;
554     case 'D': return !isdigit(c)?TRex_True:TRex_False;
555     case 'x': return isxdigit(c)?TRex_True:TRex_False;
556     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557     case 'c': return iscntrl(c)?TRex_True:TRex_False;
558     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559     case 'p': return ispunct(c)?TRex_True:TRex_False;
560     case 'P': return !ispunct(c)?TRex_True:TRex_False;
561     case 'l': return islower(c)?TRex_True:TRex_False;
562     case 'u': return isupper(c)?TRex_True:TRex_False;
563     }
564     return TRex_False; /*cannot happen*/
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
569     do {
570         switch(node->type) {
571             case OP_RANGE:
572                 if(c >= node->left && c <= node->right) return TRex_True;
573                 break;
574             case OP_CCLASS:
575                 if(trex_matchcclass(node->left,c)) return TRex_True;
576                 break;
577             default:
578                 if(c == node->type)return TRex_True;
579         }
580     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581     return TRex_False;
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
586     
587     TRexNodeType type = node->type;
588     switch(type) {
589     case OP_GREEDY: {
590         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591         TRexNode *greedystop = NULL;
592         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593         const TRexChar *s=str, *good = str;
595         if(node->next != -1) {
596             greedystop = &exp->_nodes[node->next];
597         }
598         else {
599             greedystop = next;
600         }
602         while((nmaches == 0xFFFF || nmaches < p1)) {
604             const TRexChar *stop;
605             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606                 break;
607             nmaches++;
608             good=s;
609             if(greedystop) {
610                 //checks that 0 matches satisfy the expression(if so skips)
611                 //if not would always stop(for instance if is a '?')
612                 if(greedystop->type != OP_GREEDY ||
613                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614                 {
615                     TRexNode *gnext = NULL;
616                     if(greedystop->next != -1) {
617                         gnext = &exp->_nodes[greedystop->next];
618                     }else if(next && next->next != -1){
619                         gnext = &exp->_nodes[next->next];
620                     }
621                     stop = trex_matchnode(exp,greedystop,s,gnext);
622                     if(stop) {
623                         //if satisfied stop it
624                         if(p0 == p1 && p0 == nmaches) break;
625                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
626                         else if(nmaches >= p0 && nmaches <= p1) break;
627                     }
628                 }
629             }
630             
631             if(s >= exp->_eol)
632                 break;
633         }
634         if(p0 == p1 && p0 == nmaches) return good;
635         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636         else if(nmaches >= p0 && nmaches <= p1) return good;
637         return NULL;
638     }
639     case OP_OR: {
640             const TRexChar *asd = str;
641             TRexNode *temp=&exp->_nodes[node->left];
642             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643                 if(temp->next != -1)
644                     temp = &exp->_nodes[temp->next];
645                 else
646                     return asd;
647             }
648             asd = str;
649             temp = &exp->_nodes[node->right];
650             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651                 if(temp->next != -1)
652                     temp = &exp->_nodes[temp->next];
653                 else
654                     return asd;
655             }
656             return NULL;
657             break;
658     }
659     case OP_EXPR:
660     case OP_NOCAPEXPR:{
661             TRexNode *n = &exp->_nodes[node->left];
662             const TRexChar *cur = str;
663             int capture = -1;
664             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665                 capture = exp->_currsubexp;
666                 exp->_matches[capture].begin = cur;
667                 exp->_currsubexp++;
668             }
669             
670             do {
671                 TRexNode *subnext = NULL;
672                 if(n->next != -1) {
673                     subnext = &exp->_nodes[n->next];
674                 }else {
675                     subnext = next;
676                 }
677                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678                     if(capture != -1){
679                         exp->_matches[capture].begin = 0;
680                         exp->_matches[capture].len = 0;
681                     }
682                     return NULL;
683                 }
684             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686             if(capture != -1) 
687                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688             return cur;
689     }                 
690     case OP_WB:
691         if(str == exp->_bol && !isspace(*str)
692          || (str == exp->_eol && !isspace(*(str-1)))
693          || (!isspace(*str) && isspace(*(str+1)))
694          || (isspace(*str) && !isspace(*(str+1))) ) {
695             return (node->left == 'b')?str:NULL;
696         }
697         return (node->left == 'b')?NULL:str;
698     case OP_BOL:
699         if(str == exp->_bol) return str;
700         return NULL;
701     case OP_EOL:
702         if(str == exp->_eol) return str;
703         return NULL;
704     case OP_DOT:{
705         *str++;
706                 }
707         return str;
708     case OP_NCLASS:
709     case OP_CLASS:
710         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711             *str++;
712             return str;
713         }
714         return NULL;
715     case OP_CCLASS:
716         if(trex_matchcclass(node->left,*str)) {
717             *str++;
718             return str;
719         }
720         return NULL;
721     default: /* char */
722         if(*str != node->type) return NULL;
723         *str++;
724         return str;
725     }
726     return NULL;
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
732     TRex *exp = (TRex *)malloc(sizeof(TRex));
733     exp->_eol = exp->_bol = NULL;
734     exp->_p = pattern;
735     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737     exp->_nsize = 0;
738     exp->_matches = 0;
739     exp->_nsubexpr = 0;
740     exp->_first = trex_newnode(exp,OP_EXPR);
741     exp->_error = error;
742     exp->_jmpbuf = malloc(sizeof(jmp_buf));
743     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744         int res = trex_list(exp);
745         exp->_nodes[exp->_first].left = res;
746         if(*exp->_p!='\0')
747             trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749         {
750             int nsize,i;
751             TRexNode *t;
752             nsize = exp->_nsize;
753             t = &exp->_nodes[0];
754             scprintf(_SC("\n"));
755             for(i = 0;i < nsize; i++) {
756                 if(exp->_nodes[i].type>MAX_CHAR)
757                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758                 else
759                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761             }
762             scprintf(_SC("\n"));
763         }
764 #endif
765         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767     }
768     else{
769         trex_free(exp);
770         return NULL;
771     }
772     return exp;
775 void trex_free(TRex *exp)
777     if(exp)    {
778         if(exp->_nodes) free(exp->_nodes);
779         if(exp->_jmpbuf) free(exp->_jmpbuf);
780         if(exp->_matches) free(exp->_matches);
781         free(exp);
782     }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
787     const TRexChar* res = NULL;
788     exp->_bol = text;
789     exp->_eol = text + scstrlen(text);
790     exp->_currsubexp = 0;
791     res = trex_matchnode(exp,exp->_nodes,text,NULL);
792     if(res == NULL || res != exp->_eol)
793         return TRex_False;
794     return TRex_True;
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
799     const TRexChar *cur = NULL;
800     int node = exp->_first;
801     if(text_begin >= text_end) return TRex_False;
802     exp->_bol = text_begin;
803     exp->_eol = text_end;
804     do {
805         cur = text_begin;
806         while(node != -1) {
807             exp->_currsubexp = 0;
808             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809             if(!cur)
810                 break;
811             node = exp->_nodes[node].next;
812         }
813         *text_begin++;
814     } while(cur == NULL && text_begin != text_end);
816     if(cur == NULL)
817         return TRex_False;
819     --text_begin;
821     if(out_begin) *out_begin = text_begin;
822     if(out_end) *out_end = cur;
823     return TRex_True;
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
828     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
831 int trex_getsubexpcount(TRex* exp)
833     return exp->_nsubexpr;
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
838     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839     *subexp = exp->_matches[n];
840     return TRex_True;
844 //########################################################################
845 //########################################################################
846 //##  E N D    R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //##  X M L
857 //########################################################################
858 //########################################################################
860 // Note:  This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
869 public:
870     Namespace()
871         {}
873     Namespace(const String &prefixArg, const String &namespaceURIArg)
874         {
875         prefix       = prefixArg;
876         namespaceURI = namespaceURIArg;
877         }
879     Namespace(const Namespace &other)
880         {
881         assign(other);
882         }
884     Namespace &operator=(const Namespace &other)
885         {
886         assign(other);
887         return *this;
888         }
890     virtual ~Namespace()
891         {}
893     virtual String getPrefix()
894         { return prefix; }
896     virtual String getNamespaceURI()
897         { return namespaceURI; }
899 protected:
901     void assign(const Namespace &other)
902         {
903         prefix       = other.prefix;
904         namespaceURI = other.namespaceURI;
905         }
907     String prefix;
908     String namespaceURI;
910 };
912 class Attribute
914 public:
915     Attribute()
916         {}
918     Attribute(const String &nameArg, const String &valueArg)
919         {
920         name  = nameArg;
921         value = valueArg;
922         }
924     Attribute(const Attribute &other)
925         {
926         assign(other);
927         }
929     Attribute &operator=(const Attribute &other)
930         {
931         assign(other);
932         return *this;
933         }
935     virtual ~Attribute()
936         {}
938     virtual String getName()
939         { return name; }
941     virtual String getValue()
942         { return value; }
944 protected:
946     void assign(const Attribute &other)
947         {
948         name  = other.name;
949         value = other.value;
950         }
952     String name;
953     String value;
955 };
958 class Element
960 friend class Parser;
962 public:
963     Element()
964         {
965         init();
966         }
968     Element(const String &nameArg)
969         {
970         init();
971         name   = nameArg;
972         }
974     Element(const String &nameArg, const String &valueArg)
975         {
976         init();
977         name   = nameArg;
978         value  = valueArg;
979         }
981     Element(const Element &other)
982         {
983         assign(other);
984         }
986     Element &operator=(const Element &other)
987         {
988         assign(other);
989         return *this;
990         }
992     virtual Element *clone();
994     virtual ~Element()
995         {
996         for (unsigned int i=0 ; i<children.size() ; i++)
997             delete children[i];
998         }
1000     virtual String getName()
1001         { return name; }
1003     virtual String getValue()
1004         { return value; }
1006     Element *getParent()
1007         { return parent; }
1009     std::vector<Element *> getChildren()
1010         { return children; }
1012     std::vector<Element *> findElements(const String &name);
1014     String getAttribute(const String &name);
1016     std::vector<Attribute> &getAttributes()
1017         { return attributes; } 
1019     String getTagAttribute(const String &tagName, const String &attrName);
1021     String getTagValue(const String &tagName);
1023     void addChild(Element *child);
1025     void addAttribute(const String &name, const String &value);
1027     void addNamespace(const String &prefix, const String &namespaceURI);
1030     /**
1031      * Prettyprint an XML tree to an output stream.  Elements are indented
1032      * according to element hierarchy.
1033      * @param f a stream to receive the output
1034      * @param elem the element to output
1035      */
1036     void writeIndented(FILE *f);
1038     /**
1039      * Prettyprint an XML tree to standard output.  This is the equivalent of
1040      * writeIndented(stdout).
1041      * @param elem the element to output
1042      */
1043     void print();
1044     
1045     int getLine()
1046         { return line; }
1048 protected:
1050     void init()
1051         {
1052         parent = NULL;
1053         line   = 0;
1054         }
1056     void assign(const Element &other)
1057         {
1058         parent     = other.parent;
1059         children   = other.children;
1060         attributes = other.attributes;
1061         namespaces = other.namespaces;
1062         name       = other.name;
1063         value      = other.value;
1064         line       = other.line;
1065         }
1067     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069     void writeIndentedRecursive(FILE *f, int indent);
1071     Element *parent;
1073     std::vector<Element *>children;
1075     std::vector<Attribute> attributes;
1076     std::vector<Namespace> namespaces;
1078     String name;
1079     String value;
1080     
1081     int line;
1082 };
1088 class Parser
1090 public:
1091     /**
1092      * Constructor
1093      */
1094     Parser()
1095         { init(); }
1097     virtual ~Parser()
1098         {}
1100     /**
1101      * Parse XML in a char buffer.
1102      * @param buf a character buffer to parse
1103      * @param pos position to start parsing
1104      * @param len number of chars, from pos, to parse.
1105      * @return a pointer to the root of the XML document;
1106      */
1107     Element *parse(const char *buf,int pos,int len);
1109     /**
1110      * Parse XML in a char buffer.
1111      * @param buf a character buffer to parse
1112      * @param pos position to start parsing
1113      * @param len number of chars, from pos, to parse.
1114      * @return a pointer to the root of the XML document;
1115      */
1116     Element *parse(const String &buf);
1118     /**
1119      * Parse a named XML file.  The file is loaded like a data file;
1120      * the original format is not preserved.
1121      * @param fileName the name of the file to read
1122      * @return a pointer to the root of the XML document;
1123      */
1124     Element *parseFile(const String &fileName);
1126     /**
1127      * Utility method to preprocess a string for XML
1128      * output, escaping its entities.
1129      * @param str the string to encode
1130      */
1131     static String encode(const String &str);
1133     /**
1134      *  Removes whitespace from beginning and end of a string
1135      */
1136     String trim(const String &s);
1138 private:
1140     void init()
1141         {
1142         keepGoing       = true;
1143         currentNode     = NULL;
1144         parselen        = 0;
1145         parsebuf        = NULL;
1146         currentPosition = 0;
1147         }
1149     int countLines(int begin, int end);
1151     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153     void error(const char *fmt, ...);
1155     int peek(int pos);
1157     int match(int pos, const char *text);
1159     int skipwhite(int p);
1161     int getWord(int p0, String &buf);
1163     int getQuoted(int p0, String &buf, int do_i_parse);
1165     int parseVersion(int p0);
1167     int parseDoctype(int p0);
1169     int parseElement(int p0, Element *par,int depth);
1171     Element *parse(XMLCh *buf,int pos,int len);
1173     bool       keepGoing;
1174     Element    *currentNode;
1175     int        parselen;
1176     XMLCh      *parsebuf;
1177     String     cdatabuf;
1178     int        currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1190     Element *elem = new Element(name, value);
1191     elem->parent     = parent;
1192     elem->attributes = attributes;
1193     elem->namespaces = namespaces;
1194     elem->line       = line;
1196     std::vector<Element *>::iterator iter;
1197     for (iter = children.begin(); iter != children.end() ; iter++)
1198         {
1199         elem->addChild((*iter)->clone());
1200         }
1201     return elem;
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1207     if (getName() == name)
1208         {
1209         res.push_back(this);
1210         }
1211     for (unsigned int i=0; i<children.size() ; i++)
1212         children[i]->findElementsRecursive(res, name);
1215 std::vector<Element *> Element::findElements(const String &name)
1217     std::vector<Element *> res;
1218     findElementsRecursive(res, name);
1219     return res;
1222 String Element::getAttribute(const String &name)
1224     for (unsigned int i=0 ; i<attributes.size() ; i++)
1225         if (attributes[i].getName() ==name)
1226             return attributes[i].getValue();
1227     return "";
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1232     std::vector<Element *>elems = findElements(tagName);
1233     if (elems.size() <1)
1234         return "";
1235     String res = elems[0]->getAttribute(attrName);
1236     return res;
1239 String Element::getTagValue(const String &tagName)
1241     std::vector<Element *>elems = findElements(tagName);
1242     if (elems.size() <1)
1243         return "";
1244     String res = elems[0]->getValue();
1245     return res;
1248 void Element::addChild(Element *child)
1250     if (!child)
1251         return;
1252     child->parent = this;
1253     children.push_back(child);
1257 void Element::addAttribute(const String &name, const String &value)
1259     Attribute attr(name, value);
1260     attributes.push_back(attr);
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1265     Namespace ns(prefix, namespaceURI);
1266     namespaces.push_back(ns);
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1271     int i;
1272     if (!f)
1273         return;
1274     //Opening tag, and attributes
1275     for (i=0;i<indent;i++)
1276         fputc(' ',f);
1277     fprintf(f,"<%s",name.c_str());
1278     for (unsigned int i=0 ; i<attributes.size() ; i++)
1279         {
1280         fprintf(f," %s=\"%s\"",
1281               attributes[i].getName().c_str(),
1282               attributes[i].getValue().c_str());
1283         }
1284     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285         {
1286         fprintf(f," xmlns:%s=\"%s\"",
1287               namespaces[i].getPrefix().c_str(),
1288               namespaces[i].getNamespaceURI().c_str());
1289         }
1290     fprintf(f,">\n");
1292     //Between the tags
1293     if (value.size() > 0)
1294         {
1295         for (int i=0;i<indent;i++)
1296             fputc(' ', f);
1297         fprintf(f," %s\n", value.c_str());
1298         }
1300     for (unsigned int i=0 ; i<children.size() ; i++)
1301         children[i]->writeIndentedRecursive(f, indent+2);
1303     //Closing tag
1304     for (int i=0; i<indent; i++)
1305         fputc(' ',f);
1306     fprintf(f,"</%s>\n", name.c_str());
1309 void Element::writeIndented(FILE *f)
1311     writeIndentedRecursive(f, 0);
1314 void Element::print()
1316     writeIndented(stdout);
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327     {
1328     const char *escaped;
1329     char value;
1330     } EntityEntry;
1332 static EntityEntry entities[] =
1334     { "&amp;" , '&'  },
1335     { "&lt;"  , '<'  },
1336     { "&gt;"  , '>'  },
1337     { "&apos;", '\'' },
1338     { "&quot;", '"'  },
1339     { NULL    , '\0' }
1340 };
1344 /**
1345  *  Removes whitespace from beginning and end of a string
1346  */
1347 String Parser::trim(const String &s)
1349     if (s.size() < 1)
1350         return s;
1351     
1352     //Find first non-ws char
1353     unsigned int begin = 0;
1354     for ( ; begin < s.size() ; begin++)
1355         {
1356         if (!isspace(s[begin]))
1357             break;
1358         }
1360     //Find first non-ws char, going in reverse
1361     unsigned int end = s.size() - 1;
1362     for ( ; end > begin ; end--)
1363         {
1364         if (!isspace(s[end]))
1365             break;
1366         }
1367     //trace("begin:%d  end:%d", begin, end);
1369     String res = s.substr(begin, end-begin+1);
1370     return res;
1374 int Parser::countLines(int begin, int end)
1376     int count = 0;
1377     for (int i=begin ; i<end ; i++)
1378         {
1379         XMLCh ch = parsebuf[i];
1380         if (ch == '\n' || ch == '\r')
1381             count++;
1382         }
1383     return count;
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1389     int line = 1;
1390     int col  = 1;
1391     for (long i=0 ; i<pos ; i++)
1392         {
1393         XMLCh ch = parsebuf[i];
1394         if (ch == '\n' || ch == '\r')
1395             {
1396             col = 0;
1397             line ++;
1398             }
1399         else
1400             col++;
1401         }
1402     *lineNr = line;
1403     *colNr  = col;
1408 void Parser::error(const char *fmt, ...)
1410     int lineNr;
1411     int colNr;
1412     getLineAndColumn(currentPosition, &lineNr, &colNr);
1413     va_list args;
1414     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415     va_start(args,fmt);
1416     vfprintf(stderr,fmt,args);
1417     va_end(args) ;
1418     fprintf(stderr, "\n");
1423 int Parser::peek(int pos)
1425     if (pos >= parselen)
1426         return -1;
1427     currentPosition = pos;
1428     int ch = parsebuf[pos];
1429     //printf("ch:%c\n", ch);
1430     return ch;
1435 String Parser::encode(const String &str)
1437     String ret;
1438     for (unsigned int i=0 ; i<str.size() ; i++)
1439         {
1440         XMLCh ch = (XMLCh)str[i];
1441         if (ch == '&')
1442             ret.append("&amp;");
1443         else if (ch == '<')
1444             ret.append("&lt;");
1445         else if (ch == '>')
1446             ret.append("&gt;");
1447         else if (ch == '\'')
1448             ret.append("&apos;");
1449         else if (ch == '"')
1450             ret.append("&quot;");
1451         else
1452             ret.push_back(ch);
1454         }
1455     return ret;
1459 int Parser::match(int p0, const char *text)
1461     int p = p0;
1462     while (*text)
1463         {
1464         if (peek(p) != *text)
1465             return p0;
1466         p++; text++;
1467         }
1468     return p;
1473 int Parser::skipwhite(int p)
1476     while (p<parselen)
1477         {
1478         int p2 = match(p, "<!--");
1479         if (p2 > p)
1480             {
1481             p = p2;
1482             while (p<parselen)
1483               {
1484               p2 = match(p, "-->");
1485               if (p2 > p)
1486                   {
1487                   p = p2;
1488                   break;
1489                   }
1490               p++;
1491               }
1492           }
1493       XMLCh b = peek(p);
1494       if (!isspace(b))
1495           break;
1496       p++;
1497       }
1498   return p;
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1504     int p = p0;
1505     while (p<parselen)
1506         {
1507         XMLCh b = peek(p);
1508         if (b<=' ' || b=='/' || b=='>' || b=='=')
1509             break;
1510         buf.push_back(b);
1511         p++;
1512         }
1513     return p;
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1519     int p = p0;
1520     if (peek(p) != '"' && peek(p) != '\'')
1521         return p0;
1522     p++;
1524     while ( p<parselen )
1525         {
1526         XMLCh b = peek(p);
1527         if (b=='"' || b=='\'')
1528             break;
1529         if (b=='&' && do_i_parse)
1530             {
1531             bool found = false;
1532             for (EntityEntry *ee = entities ; ee->value ; ee++)
1533                 {
1534                 int p2 = match(p, ee->escaped);
1535                 if (p2>p)
1536                     {
1537                     buf.push_back(ee->value);
1538                     p = p2;
1539                     found = true;
1540                     break;
1541                     }
1542                 }
1543             if (!found)
1544                 {
1545                 error("unterminated entity");
1546                 return false;
1547                 }
1548             }
1549         else
1550             {
1551             buf.push_back(b);
1552             p++;
1553             }
1554         }
1555     return p;
1558 int Parser::parseVersion(int p0)
1560     //printf("### parseVersion: %d\n", p0);
1562     int p = p0;
1564     p = skipwhite(p0);
1566     if (peek(p) != '<')
1567         return p0;
1569     p++;
1570     if (p>=parselen || peek(p)!='?')
1571         return p0;
1573     p++;
1575     String buf;
1577     while (p<parselen)
1578         {
1579         XMLCh ch = peek(p);
1580         if (ch=='?')
1581             {
1582             p++;
1583             break;
1584             }
1585         buf.push_back(ch);
1586         p++;
1587         }
1589     if (peek(p) != '>')
1590         return p0;
1591     p++;
1593     //printf("Got version:%s\n",buf.c_str());
1594     return p;
1597 int Parser::parseDoctype(int p0)
1599     //printf("### parseDoctype: %d\n", p0);
1601     int p = p0;
1602     p = skipwhite(p);
1604     if (p>=parselen || peek(p)!='<')
1605         return p0;
1607     p++;
1609     if (peek(p)!='!' || peek(p+1)=='-')
1610         return p0;
1611     p++;
1613     String buf;
1614     while (p<parselen)
1615         {
1616         XMLCh ch = peek(p);
1617         if (ch=='>')
1618             {
1619             p++;
1620             break;
1621             }
1622         buf.push_back(ch);
1623         p++;
1624         }
1626     //printf("Got doctype:%s\n",buf.c_str());
1627     return p;
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1635     int p = p0;
1637     int p2 = p;
1639     p = skipwhite(p);
1641     //## Get open tag
1642     XMLCh ch = peek(p);
1643     if (ch!='<')
1644         return p0;
1646     //int line, col;
1647     //getLineAndColumn(p, &line, &col);
1649     p++;
1651     String openTagName;
1652     p = skipwhite(p);
1653     p = getWord(p, openTagName);
1654     //printf("####tag :%s\n", openTagName.c_str());
1655     p = skipwhite(p);
1657     //Add element to tree
1658     Element *n = new Element(openTagName);
1659     n->line = lineNr + countLines(p0, p);
1660     n->parent = par;
1661     par->addChild(n);
1663     // Get attributes
1664     if (peek(p) != '>')
1665         {
1666         while (p<parselen)
1667             {
1668             p = skipwhite(p);
1669             ch = peek(p);
1670             //printf("ch:%c\n",ch);
1671             if (ch=='>')
1672                 break;
1673             else if (ch=='/' && p<parselen+1)
1674                 {
1675                 p++;
1676                 p = skipwhite(p);
1677                 ch = peek(p);
1678                 if (ch=='>')
1679                     {
1680                     p++;
1681                     //printf("quick close\n");
1682                     return p;
1683                     }
1684                 }
1685             String attrName;
1686             p2 = getWord(p, attrName);
1687             if (p2==p)
1688                 break;
1689             //printf("name:%s",buf);
1690             p=p2;
1691             p = skipwhite(p);
1692             ch = peek(p);
1693             //printf("ch:%c\n",ch);
1694             if (ch!='=')
1695                 break;
1696             p++;
1697             p = skipwhite(p);
1698             // ch = parsebuf[p];
1699             // printf("ch:%c\n",ch);
1700             String attrVal;
1701             p2 = getQuoted(p, attrVal, true);
1702             p=p2+1;
1703             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704             char *namestr = (char *)attrName.c_str();
1705             if (strncmp(namestr, "xmlns:", 6)==0)
1706                 n->addNamespace(attrName, attrVal);
1707             else
1708                 n->addAttribute(attrName, attrVal);
1709             }
1710         }
1712     bool cdata = false;
1714     p++;
1715     // ### Get intervening data ### */
1716     String data;
1717     while (p<parselen)
1718         {
1719         //# COMMENT
1720         p2 = match(p, "<!--");
1721         if (!cdata && p2>p)
1722             {
1723             p = p2;
1724             while (p<parselen)
1725                 {
1726                 p2 = match(p, "-->");
1727                 if (p2 > p)
1728                     {
1729                     p = p2;
1730                     break;
1731                     }
1732                 p++;
1733                 }
1734             }
1736         ch = peek(p);
1737         //# END TAG
1738         if (ch=='<' && !cdata && peek(p+1)=='/')
1739             {
1740             break;
1741             }
1742         //# CDATA
1743         p2 = match(p, "<![CDATA[");
1744         if (p2 > p)
1745             {
1746             cdata = true;
1747             p = p2;
1748             continue;
1749             }
1751         //# CHILD ELEMENT
1752         if (ch == '<')
1753             {
1754             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755             if (p2 == p)
1756                 {
1757                 /*
1758                 printf("problem on element:%s.  p2:%d p:%d\n",
1759                       openTagName.c_str(), p2, p);
1760                 */
1761                 return p0;
1762                 }
1763             p = p2;
1764             continue;
1765             }
1766         //# ENTITY
1767         if (ch=='&' && !cdata)
1768             {
1769             bool found = false;
1770             for (EntityEntry *ee = entities ; ee->value ; ee++)
1771                 {
1772                 int p2 = match(p, ee->escaped);
1773                 if (p2>p)
1774                     {
1775                     data.push_back(ee->value);
1776                     p = p2;
1777                     found = true;
1778                     break;
1779                     }
1780                 }
1781             if (!found)
1782                 {
1783                 error("unterminated entity");
1784                 return -1;
1785                 }
1786             continue;
1787             }
1789         //# NONE OF THE ABOVE
1790         data.push_back(ch);
1791         p++;
1792         }/*while*/
1795     n->value = data;
1796     //printf("%d : data:%s\n",p,data.c_str());
1798     //## Get close tag
1799     p = skipwhite(p);
1800     ch = peek(p);
1801     if (ch != '<')
1802         {
1803         error("no < for end tag\n");
1804         return p0;
1805         }
1806     p++;
1807     ch = peek(p);
1808     if (ch != '/')
1809         {
1810         error("no / on end tag");
1811         return p0;
1812         }
1813     p++;
1814     ch = peek(p);
1815     p = skipwhite(p);
1816     String closeTagName;
1817     p = getWord(p, closeTagName);
1818     if (openTagName != closeTagName)
1819         {
1820         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1821                 openTagName.c_str(), closeTagName.c_str());
1822         return p0;
1823         }
1824     p = skipwhite(p);
1825     if (peek(p) != '>')
1826         {
1827         error("no > on end tag for '%s'", closeTagName.c_str());
1828         return p0;
1829         }
1830     p++;
1831     // printf("close element:%s\n",closeTagName.c_str());
1832     p = skipwhite(p);
1833     return p;
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1841     parselen = len;
1842     parsebuf = buf;
1843     Element *rootNode = new Element("root");
1844     pos = parseVersion(pos);
1845     pos = parseDoctype(pos);
1846     pos = parseElement(pos, rootNode, 1);
1847     return rootNode;
1851 Element *Parser::parse(const char *buf, int pos, int len)
1853     XMLCh *charbuf = new XMLCh[len + 1];
1854     long i = 0;
1855     for ( ; i < len ; i++)
1856         charbuf[i] = (XMLCh)buf[i];
1857     charbuf[i] = '\0';
1859     Element *n = parse(charbuf, pos, len);
1860     delete[] charbuf;
1861     return n;
1864 Element *Parser::parse(const String &buf)
1866     long len = (long)buf.size();
1867     XMLCh *charbuf = new XMLCh[len + 1];
1868     long i = 0;
1869     for ( ; i < len ; i++)
1870         charbuf[i] = (XMLCh)buf[i];
1871     charbuf[i] = '\0';
1873     Element *n = parse(charbuf, 0, len);
1874     delete[] charbuf;
1875     return n;
1878 Element *Parser::parseFile(const String &fileName)
1881     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882     FILE *f = fopen(fileName.c_str(), "rb");
1883     if (!f)
1884         return NULL;
1886     struct stat  statBuf;
1887     if (fstat(fileno(f),&statBuf)<0)
1888         {
1889         fclose(f);
1890         return NULL;
1891         }
1892     long filelen = statBuf.st_size;
1894     //printf("length:%d\n",filelen);
1895     XMLCh *charbuf = new XMLCh[filelen + 1];
1896     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897         {
1898         *p = (XMLCh)fgetc(f);
1899         }
1900     fclose(f);
1901     charbuf[filelen] = '\0';
1904     /*
1905     printf("nrbytes:%d\n",wc_count);
1906     printf("buf:%ls\n======\n",charbuf);
1907     */
1908     Element *n = parse(charbuf, 0, filelen);
1909     delete[] charbuf;
1910     return n;
1913 //########################################################################
1914 //########################################################################
1915 //##  E N D    X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //##  U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934  *  A class that implements the W3C URI resource reference.
1935  */
1936 class URI
1938 public:
1940     typedef enum
1941         {
1942         SCHEME_NONE =0,
1943         SCHEME_DATA,
1944         SCHEME_HTTP,
1945         SCHEME_HTTPS,
1946         SCHEME_FTP,
1947         SCHEME_FILE,
1948         SCHEME_LDAP,
1949         SCHEME_MAILTO,
1950         SCHEME_NEWS,
1951         SCHEME_TELNET
1952         } SchemeTypes;
1954     /**
1955      *
1956      */
1957     URI()
1958         {
1959         init();
1960         }
1962     /**
1963      *
1964      */
1965     URI(const String &str)
1966         {
1967         init();
1968         parse(str);
1969         }
1972     /**
1973      *
1974      */
1975     URI(const char *str)
1976         {
1977         init();
1978         String domStr = str;
1979         parse(domStr);
1980         }
1983     /**
1984      *
1985      */
1986     URI(const URI &other)
1987         {
1988         init();
1989         assign(other);
1990         }
1993     /**
1994      *
1995      */
1996     URI &operator=(const URI &other)
1997         {
1998         init();
1999         assign(other);
2000         return *this;
2001         }
2004     /**
2005      *
2006      */
2007     virtual ~URI()
2008         {}
2012     /**
2013      *
2014      */
2015     virtual bool parse(const String &str);
2017     /**
2018      *
2019      */
2020     virtual String toString() const;
2022     /**
2023      *
2024      */
2025     virtual int getScheme() const;
2027     /**
2028      *
2029      */
2030     virtual String getSchemeStr() const;
2032     /**
2033      *
2034      */
2035     virtual String getAuthority() const;
2037     /**
2038      *  Same as getAuthority, but if the port has been specified
2039      *  as host:port , the port will not be included
2040      */
2041     virtual String getHost() const;
2043     /**
2044      *
2045      */
2046     virtual int getPort() const;
2048     /**
2049      *
2050      */
2051     virtual String getPath() const;
2053     /**
2054      *
2055      */
2056     virtual String getNativePath() const;
2058     /**
2059      *
2060      */
2061     virtual bool isAbsolute() const;
2063     /**
2064      *
2065      */
2066     virtual bool isOpaque() const;
2068     /**
2069      *
2070      */
2071     virtual String getQuery() const;
2073     /**
2074      *
2075      */
2076     virtual String getFragment() const;
2078     /**
2079      *
2080      */
2081     virtual URI resolve(const URI &other) const;
2083     /**
2084      *
2085      */
2086     virtual void normalize();
2088 private:
2090     /**
2091      *
2092      */
2093     void init()
2094         {
2095         parsebuf  = NULL;
2096         parselen  = 0;
2097         scheme    = SCHEME_NONE;
2098         schemeStr = "";
2099         port      = 0;
2100         authority = "";
2101         path      = "";
2102         absolute  = false;
2103         opaque    = false;
2104         query     = "";
2105         fragment  = "";
2106         }
2109     /**
2110      *
2111      */
2112     void assign(const URI &other)
2113         {
2114         scheme    = other.scheme;
2115         schemeStr = other.schemeStr;
2116         authority = other.authority;
2117         port      = other.port;
2118         path      = other.path;
2119         absolute  = other.absolute;
2120         opaque    = other.opaque;
2121         query     = other.query;
2122         fragment  = other.fragment;
2123         }
2125     int scheme;
2127     String schemeStr;
2129     String authority;
2131     bool portSpecified;
2133     int port;
2135     String path;
2137     bool absolute;
2139     bool opaque;
2141     String query;
2143     String fragment;
2145     void error(const char *fmt, ...);
2147     void trace(const char *fmt, ...);
2150     int peek(int p);
2152     int match(int p, const char *key);
2154     int parseScheme(int p);
2156     int parseHierarchicalPart(int p0);
2158     int parseQuery(int p0);
2160     int parseFragment(int p0);
2162     int parse(int p);
2164     char *parsebuf;
2166     int parselen;
2168 };
2172 typedef struct
2174     int         ival;
2175     const char *sval;
2176     int         port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2181     { URI::SCHEME_DATA,   "data:",    0 },
2182     { URI::SCHEME_HTTP,   "http:",   80 },
2183     { URI::SCHEME_HTTPS,  "https:", 443 },
2184     { URI::SCHEME_FTP,    "ftp",     12 },
2185     { URI::SCHEME_FILE,   "file:",    0 },
2186     { URI::SCHEME_LDAP,   "ldap:",  123 },
2187     { URI::SCHEME_MAILTO, "mailto:", 25 },
2188     { URI::SCHEME_NEWS,   "news:",  117 },
2189     { URI::SCHEME_TELNET, "telnet:", 23 },
2190     { 0,                  NULL,       0 }
2191 };
2194 String URI::toString() const
2196     String str = schemeStr;
2197     if (authority.size() > 0)
2198         {
2199         str.append("//");
2200         str.append(authority);
2201         }
2202     str.append(path);
2203     if (query.size() > 0)
2204         {
2205         str.append("?");
2206         str.append(query);
2207         }
2208     if (fragment.size() > 0)
2209         {
2210         str.append("#");
2211         str.append(fragment);
2212         }
2213     return str;
2217 int URI::getScheme() const
2219     return scheme;
2222 String URI::getSchemeStr() const
2224     return schemeStr;
2228 String URI::getAuthority() const
2230     String ret = authority;
2231     if (portSpecified && port>=0)
2232         {
2233         char buf[7];
2234         snprintf(buf, 6, ":%6d", port);
2235         ret.append(buf);
2236         }
2237     return ret;
2240 String URI::getHost() const
2242     return authority;
2245 int URI::getPort() const
2247     return port;
2251 String URI::getPath() const
2253     return path;
2256 String URI::getNativePath() const
2258     String npath;
2259 #ifdef __WIN32__
2260     unsigned int firstChar = 0;
2261     if (path.size() >= 3)
2262         {
2263         if (path[0] == '/' &&
2264             isLetter(path[1]) &&
2265             path[2] == ':')
2266             firstChar++;
2267          }
2268     for (unsigned int i=firstChar ; i<path.size() ; i++)
2269         {
2270         XMLCh ch = (XMLCh) path[i];
2271         if (ch == '/')
2272             npath.push_back((XMLCh)'\\');
2273         else
2274             npath.push_back(ch);
2275         }
2276 #else
2277     npath = path;
2278 #endif
2279     return npath;
2283 bool URI::isAbsolute() const
2285     return absolute;
2288 bool URI::isOpaque() const
2290     return opaque;
2294 String URI::getQuery() const
2296     return query;
2300 String URI::getFragment() const
2302     return fragment;
2306 URI URI::resolve(const URI &other) const
2308     //### According to w3c, this is handled in 3 cases
2310     //## 1
2311     if (opaque || other.isAbsolute())
2312         return other;
2314     //## 2
2315     if (other.fragment.size()  >  0 &&
2316         other.path.size()      == 0 &&
2317         other.scheme           == SCHEME_NONE &&
2318         other.authority.size() == 0 &&
2319         other.query.size()     == 0 )
2320         {
2321         URI fragUri = *this;
2322         fragUri.fragment = other.fragment;
2323         return fragUri;
2324         }
2326     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327     URI newUri;
2328     //# 3.1
2329     newUri.scheme    = scheme;
2330     newUri.schemeStr = schemeStr;
2331     newUri.query     = other.query;
2332     newUri.fragment  = other.fragment;
2333     if (other.authority.size() > 0)
2334         {
2335         //# 3.2
2336         if (absolute || other.absolute)
2337             newUri.absolute = true;
2338         newUri.authority = other.authority;
2339         newUri.port      = other.port;//part of authority
2340         newUri.path      = other.path;
2341         }
2342     else
2343         {
2344         //# 3.3
2345         if (other.absolute)
2346             {
2347             newUri.absolute = true;
2348             newUri.path     = other.path;
2349             }
2350         else
2351             {
2352             unsigned int pos = path.find_last_of('/');
2353             if (pos != path.npos)
2354                 {
2355                 String tpath = path.substr(0, pos+1);
2356                 tpath.append(other.path);
2357                 newUri.path = tpath;
2358                 }
2359             else
2360                 newUri.path = other.path;
2361             }
2362         }
2364     newUri.normalize();
2365     return newUri;
2370 /**
2371  *  This follows the Java URI algorithm:
2372  *   1. All "." segments are removed.
2373  *   2. If a ".." segment is preceded by a non-".." segment
2374  *          then both of these segments are removed. This step
2375  *          is repeated until it is no longer applicable.
2376  *   3. If the path is relative, and if its first segment
2377  *          contains a colon character (':'), then a "." segment
2378  *          is prepended. This prevents a relative URI with a path
2379  *          such as "a:b/c/d" from later being re-parsed as an
2380  *          opaque URI with a scheme of "a" and a scheme-specific
2381  *          part of "b/c/d". (Deviation from RFC 2396)
2382  */
2383 void URI::normalize()
2385     std::vector<String> segments;
2387     //## Collect segments
2388     if (path.size()<2)
2389         return;
2390     bool abs = false;
2391     unsigned int pos=0;
2392     if (path[0]=='/')
2393         {
2394         abs = true;
2395         pos++;
2396         }
2397     while (pos < path.size())
2398         {
2399         unsigned int pos2 = path.find('/', pos);
2400         if (pos2==path.npos)
2401             {
2402             String seg = path.substr(pos);
2403             //printf("last segment:%s\n", seg.c_str());
2404             segments.push_back(seg);
2405             break;
2406             }
2407         if (pos2>pos)
2408             {
2409             String seg = path.substr(pos, pos2-pos);
2410             //printf("segment:%s\n", seg.c_str());
2411             segments.push_back(seg);
2412             }
2413         pos = pos2;
2414         pos++;
2415         }
2417     //## Clean up (normalize) segments
2418     bool edited = false;
2419     std::vector<String>::iterator iter;
2420     for (iter=segments.begin() ; iter!=segments.end() ; )
2421         {
2422         String s = *iter;
2423         if (s == ".")
2424             {
2425             iter = segments.erase(iter);
2426             edited = true;
2427             }
2428         else if (s == ".." &&
2429                  iter != segments.begin() &&
2430                  *(iter-1) != "..")
2431             {
2432             iter--; //back up, then erase two entries
2433             iter = segments.erase(iter);
2434             iter = segments.erase(iter);
2435             edited = true;
2436             }
2437         else
2438             iter++;
2439         }
2441     //## Rebuild path, if necessary
2442     if (edited)
2443         {
2444         path.clear();
2445         if (abs)
2446             {
2447             path.append("/");
2448             }
2449         std::vector<String>::iterator iter;
2450         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451             {
2452             if (iter != segments.begin())
2453                 path.append("/");
2454             path.append(*iter);
2455             }
2456         }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2468     va_list args;
2469     fprintf(stderr, "URI error: ");
2470     va_start(args, fmt);
2471     vfprintf(stderr, fmt, args);
2472     va_end(args);
2473     fprintf(stderr, "\n");
2476 void URI::trace(const char *fmt, ...)
2478     va_list args;
2479     fprintf(stdout, "URI: ");
2480     va_start(args, fmt);
2481     vfprintf(stdout, fmt, args);
2482     va_end(args);
2483     fprintf(stdout, "\n");
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2497     if (p<0 || p>=parselen)
2498         return -1;
2499     return parsebuf[p];
2504 int URI::match(int p0, const char *key)
2506     int p = p0;
2507     while (p < parselen)
2508         {
2509         if (*key == '\0')
2510             return p;
2511         else if (*key != parsebuf[p])
2512             break;
2513         p++; key++;
2514         }
2515     return p0;
2518 //#########################################################################
2519 //#  Parsing is performed according to:
2520 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2525     int p = p0;
2526     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527         {
2528         int p2 = match(p, entry->sval);
2529         if (p2 > p)
2530             {
2531             schemeStr = entry->sval;
2532             scheme    = entry->ival;
2533             port      = entry->port;
2534             p = p2;
2535             return p;
2536             }
2537         }
2539     return p;
2543 int URI::parseHierarchicalPart(int p0)
2545     int p = p0;
2546     int ch;
2548     //# Authority field (host and port, for example)
2549     int p2 = match(p, "//");
2550     if (p2 > p)
2551         {
2552         p = p2;
2553         portSpecified = false;
2554         String portStr;
2555         while (p < parselen)
2556             {
2557             ch = peek(p);
2558             if (ch == '/')
2559                 break;
2560             else if (ch == ':')
2561                 portSpecified = true;
2562             else if (portSpecified)
2563                 portStr.push_back((XMLCh)ch);
2564             else
2565                 authority.push_back((XMLCh)ch);
2566             p++;
2567             }
2568         if (portStr.size() > 0)
2569             {
2570             char *pstr = (char *)portStr.c_str();
2571             char *endStr;
2572             long val = strtol(pstr, &endStr, 10);
2573             if (endStr > pstr) //successful parse?
2574                 port = val;
2575             }
2576         }
2578     //# Are we absolute?
2579     ch = peek(p);
2580     if (isLetter(ch) && peek(p+1)==':')
2581         {
2582         absolute = true;
2583         path.push_back((XMLCh)'/');
2584         }
2585     else if (ch == '/')
2586         {
2587         absolute = true;
2588         if (p>p0) //in other words, if '/' is not the first char
2589             opaque = true;
2590         path.push_back((XMLCh)ch);
2591         p++;
2592         }
2594     while (p < parselen)
2595         {
2596         ch = peek(p);
2597         if (ch == '?' || ch == '#')
2598             break;
2599         path.push_back((XMLCh)ch);
2600         p++;
2601         }
2603     return p;
2606 int URI::parseQuery(int p0)
2608     int p = p0;
2609     int ch = peek(p);
2610     if (ch != '?')
2611         return p0;
2613     p++;
2614     while (p < parselen)
2615         {
2616         ch = peek(p);
2617         if (ch == '#')
2618             break;
2619         query.push_back((XMLCh)ch);
2620         p++;
2621         }
2624     return p;
2627 int URI::parseFragment(int p0)
2630     int p = p0;
2631     int ch = peek(p);
2632     if (ch != '#')
2633         return p0;
2635     p++;
2636     while (p < parselen)
2637         {
2638         ch = peek(p);
2639         if (ch == '?')
2640             break;
2641         fragment.push_back((XMLCh)ch);
2642         p++;
2643         }
2646     return p;
2650 int URI::parse(int p0)
2653     int p = p0;
2655     int p2 = parseScheme(p);
2656     if (p2 < 0)
2657         {
2658         error("Scheme");
2659         return -1;
2660         }
2661     p = p2;
2664     p2 = parseHierarchicalPart(p);
2665     if (p2 < 0)
2666         {
2667         error("Hierarchical part");
2668         return -1;
2669         }
2670     p = p2;
2672     p2 = parseQuery(p);
2673     if (p2 < 0)
2674         {
2675         error("Query");
2676         return -1;
2677         }
2678     p = p2;
2681     p2 = parseFragment(p);
2682     if (p2 < 0)
2683         {
2684         error("Fragment");
2685         return -1;
2686         }
2687     p = p2;
2689     return p;
2695 bool URI::parse(const String &str)
2697     init();
2698     
2699     parselen = str.size();
2701     String tmp;
2702     for (unsigned int i=0 ; i<str.size() ; i++)
2703         {
2704         XMLCh ch = (XMLCh) str[i];
2705         if (ch == '\\')
2706             tmp.push_back((XMLCh)'/');
2707         else
2708             tmp.push_back(ch);
2709         }
2710     parsebuf = (char *) tmp.c_str();
2713     int p = parse(0);
2714     normalize();
2716     if (p < 0)
2717         {
2718         error("Syntax error");
2719         return false;
2720         }
2722     //printf("uri:%s\n", toString().c_str());
2723     //printf("path:%s\n", path.c_str());
2725     return true;
2736 //########################################################################
2737 //########################################################################
2738 //##  M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746  * This is the descriptor for a <fileset> item
2747  */
2748 class FileSet
2750 public:
2752     /**
2753      *
2754      */
2755     FileSet()
2756         {}
2758     /**
2759      *
2760      */
2761     FileSet(const FileSet &other)
2762         { assign(other); }
2764     /**
2765      *
2766      */
2767     FileSet &operator=(const FileSet &other)
2768         { assign(other); return *this; }
2770     /**
2771      *
2772      */
2773     virtual ~FileSet()
2774         {}
2776     /**
2777      *
2778      */
2779     String getDirectory()
2780         { return directory; }
2781         
2782     /**
2783      *
2784      */
2785     void setDirectory(const String &val)
2786         { directory = val; }
2788     /**
2789      *
2790      */
2791     void setFiles(const std::vector<String> &val)
2792         { files = val; }
2794     /**
2795      *
2796      */
2797     std::vector<String> getFiles()
2798         { return files; }
2799         
2800     /**
2801      *
2802      */
2803     void setIncludes(const std::vector<String> &val)
2804         { includes = val; }
2806     /**
2807      *
2808      */
2809     std::vector<String> getIncludes()
2810         { return includes; }
2811         
2812     /**
2813      *
2814      */
2815     void setExcludes(const std::vector<String> &val)
2816         { excludes = val; }
2818     /**
2819      *
2820      */
2821     std::vector<String> getExcludes()
2822         { return excludes; }
2823         
2824     /**
2825      *
2826      */
2827     unsigned int size()
2828         { return files.size(); }
2829         
2830     /**
2831      *
2832      */
2833     String operator[](int index)
2834         { return files[index]; }
2835         
2836     /**
2837      *
2838      */
2839     void clear()
2840         {
2841         directory = "";
2842         files.clear();
2843         includes.clear();
2844         excludes.clear();
2845         }
2846         
2848 private:
2850     void assign(const FileSet &other)
2851         {
2852         directory = other.directory;
2853         files     = other.files;
2854         includes  = other.includes;
2855         excludes  = other.excludes;
2856         }
2858     String directory;
2859     std::vector<String> files;
2860     std::vector<String> includes;
2861     std::vector<String> excludes;
2862 };
2865 //########################################################################
2866 //# F I L E L I S T
2867 //########################################################################
2868 /**
2869  * This is a simpler, explicitly-named list of files
2870  */
2871 class FileList
2873 public:
2875     /**
2876      *
2877      */
2878     FileList()
2879         {}
2881     /**
2882      *
2883      */
2884     FileList(const FileList &other)
2885         { assign(other); }
2887     /**
2888      *
2889      */
2890     FileList &operator=(const FileList &other)
2891         { assign(other); return *this; }
2893     /**
2894      *
2895      */
2896     virtual ~FileList()
2897         {}
2899     /**
2900      *
2901      */
2902     String getDirectory()
2903         { return directory; }
2904         
2905     /**
2906      *
2907      */
2908     void setDirectory(const String &val)
2909         { directory = val; }
2911     /**
2912      *
2913      */
2914     void setFiles(const std::vector<String> &val)
2915         { files = val; }
2917     /**
2918      *
2919      */
2920     std::vector<String> getFiles()
2921         { return files; }
2922         
2923     /**
2924      *
2925      */
2926     unsigned int size()
2927         { return files.size(); }
2928         
2929     /**
2930      *
2931      */
2932     String operator[](int index)
2933         { return files[index]; }
2934         
2935     /**
2936      *
2937      */
2938     void clear()
2939         {
2940         directory = "";
2941         files.clear();
2942         }
2943         
2945 private:
2947     void assign(const FileList &other)
2948         {
2949         directory = other.directory;
2950         files     = other.files;
2951         }
2953     String directory;
2954     std::vector<String> files;
2955 };
2960 //########################################################################
2961 //# M A K E    B A S E
2962 //########################################################################
2963 /**
2964  * Base class for all classes in this file
2965  */
2966 class MakeBase
2968 public:
2970     MakeBase()
2971         { line = 0; }
2972     virtual ~MakeBase()
2973         {}
2975     /**
2976      *     Return the URI of the file associated with this object 
2977      */     
2978     URI getURI()
2979         { return uri; }
2981     /**
2982      * Set the uri to the given string
2983      */
2984     void setURI(const String &uristr)
2985         { uri.parse(uristr); }
2987     /**
2988      *  Resolve another path relative to this one
2989      */
2990     String resolve(const String &otherPath);
2992     /**
2993      * replace variable refs like ${a} with their values
2994      * Assume that the string has already been syntax validated
2995      */
2996     String eval(const String &s, const String &defaultVal);
2998     /**
2999      * replace variable refs like ${a} with their values
3000      * return true or false
3001      * Assume that the string has already been syntax validated
3002      */
3003     bool evalBool(const String &s, bool defaultVal);
3005     /**
3006      *  Get an element attribute, performing substitutions if necessary
3007      */
3008     bool getAttribute(Element *elem, const String &name, String &result);
3010     /**
3011      * Get an element value, performing substitutions if necessary
3012      */
3013     bool getValue(Element *elem, String &result);
3014     
3015     /**
3016      * Set the current line number in the file
3017      */         
3018     void setLine(int val)
3019         { line = val; }
3020         
3021     /**
3022      * Get the current line number in the file
3023      */         
3024     int getLine()
3025         { return line; }
3028     /**
3029      * Set a property to a given value
3030      */
3031     virtual void setProperty(const String &name, const String &val)
3032         {
3033         properties[name] = val;
3034         }
3036     /**
3037      * Return a named property is found, else a null string
3038      */
3039     virtual String getProperty(const String &name)
3040         {
3041         String val;
3042         std::map<String, String>::iterator iter = properties.find(name);
3043         if (iter != properties.end())
3044             val = iter->second;
3045         String sval;
3046         if (!getSubstitutions(val, sval))
3047             return false;
3048         return sval;
3049         }
3051     /**
3052      * Return true if a named property is found, else false
3053      */
3054     virtual bool hasProperty(const String &name)
3055         {
3056         std::map<String, String>::iterator iter = properties.find(name);
3057         if (iter == properties.end())
3058             return false;
3059         return true;
3060         }
3063 protected:
3065     /**
3066      *    The path to the file associated with this object
3067      */     
3068     URI uri;
3069     
3070     /**
3071      *    If this prefix is seen in a substitution, use an environment
3072      *    variable.
3073      *             example:  <property environment="env"/>
3074      *             ${env.JAVA_HOME}
3075      */
3076     String envPrefix;
3078     /**
3079      *    If this prefix is seen in a substitution, use as a
3080      *    pkg-config 'all' query
3081      *             example:  <property pkg-config="pc"/>
3082      *             ${pc.gtkmm}
3083      */
3084     String pcPrefix;
3086     /**
3087      *    If this prefix is seen in a substitution, use as a
3088      *    pkg-config 'cflags' query
3089      *             example:  <property pkg-config="pcc"/>
3090      *             ${pcc.gtkmm}
3091      */
3092     String pccPrefix;
3094     /**
3095      *    If this prefix is seen in a substitution, use as a
3096      *    pkg-config 'libs' query
3097      *             example:  <property pkg-config="pcl"/>
3098      *             ${pcl.gtkmm}
3099      */
3100     String pclPrefix;
3106     /**
3107      *  Print a printf()-like formatted error message
3108      */
3109     void error(const char *fmt, ...);
3111     /**
3112      *  Print a printf()-like formatted trace message
3113      */
3114     void status(const char *fmt, ...);
3116     /**
3117      *  Show target status
3118      */
3119     void targetstatus(const char *fmt, ...);
3121     /**
3122      *  Print a printf()-like formatted trace message
3123      */
3124     void trace(const char *fmt, ...);
3126     /**
3127      *  Check if a given string matches a given regex pattern
3128      */
3129     bool regexMatch(const String &str, const String &pattern);
3131     /**
3132      *
3133      */
3134     String getSuffix(const String &fname);
3136     /**
3137      * Break up a string into substrings delimited the characters
3138      * in delimiters.  Null-length substrings are ignored
3139      */  
3140     std::vector<String> tokenize(const String &val,
3141                           const String &delimiters);
3143     /**
3144      *  replace runs of whitespace with a space
3145      */
3146     String strip(const String &s);
3148     /**
3149      *  remove leading whitespace from each line
3150      */
3151     String leftJustify(const String &s);
3153     /**
3154      *  remove leading and trailing whitespace from string
3155      */
3156     String trim(const String &s);
3158     /**
3159      *  Return a lower case version of the given string
3160      */
3161     String toLower(const String &s);
3163     /**
3164      * Return the native format of the canonical
3165      * path which we store
3166      */
3167     String getNativePath(const String &path);
3169     /**
3170      * Execute a shell command.  Outbuf is a ref to a string
3171      * to catch the result.     
3172      */         
3173     bool executeCommand(const String &call,
3174                         const String &inbuf,
3175                         String &outbuf,
3176                         String &errbuf);
3177     /**
3178      * List all directories in a given base and starting directory
3179      * It is usually called like:
3180      *        bool ret = listDirectories("src", "", result);    
3181      */         
3182     bool listDirectories(const String &baseName,
3183                          const String &dirname,
3184                          std::vector<String> &res);
3186     /**
3187      * Find all files in the named directory 
3188      */         
3189     bool listFiles(const String &baseName,
3190                    const String &dirname,
3191                    std::vector<String> &result);
3193     /**
3194      * Perform a listing for a fileset 
3195      */         
3196     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3198     /**
3199      * Parse a <patternset>
3200      */  
3201     bool parsePatternSet(Element *elem,
3202                        MakeBase &propRef,
3203                        std::vector<String> &includes,
3204                        std::vector<String> &excludes);
3206     /**
3207      * Parse a <fileset> entry, and determine which files
3208      * should be included
3209      */  
3210     bool parseFileSet(Element *elem,
3211                     MakeBase &propRef,
3212                     FileSet &fileSet);
3213     /**
3214      * Parse a <filelist> entry
3215      */  
3216     bool parseFileList(Element *elem,
3217                     MakeBase &propRef,
3218                     FileList &fileList);
3220     /**
3221      * Return this object's property list
3222      */
3223     virtual std::map<String, String> &getProperties()
3224         { return properties; }
3227     std::map<String, String> properties;
3229     /**
3230      * Create a directory, making intermediate dirs
3231      * if necessary
3232      */                  
3233     bool createDirectory(const String &dirname);
3235     /**
3236      * Delete a directory and its children if desired
3237      */
3238     bool removeDirectory(const String &dirName);
3240     /**
3241      * Copy a file from one name to another. Perform only if needed
3242      */ 
3243     bool copyFile(const String &srcFile, const String &destFile);
3245     /**
3246      * Tests if the file exists and is a regular file
3247      */ 
3248     bool isRegularFile(const String &fileName);
3250     /**
3251      * Tests if the file exists and is a directory
3252      */ 
3253     bool isDirectory(const String &fileName);
3255     /**
3256      * Tests is the modification date of fileA is newer than fileB
3257      */ 
3258     bool isNewerThan(const String &fileA, const String &fileB);
3260 private:
3262     bool pkgConfigRecursive(const String packageName,
3263                             const String &path, 
3264                             const String &prefix, 
3265                             int query,
3266                             String &result,
3267                             std::set<String> &deplist);
3269     /**
3270      * utility method to query for "all", "cflags", or "libs" for this package and its
3271      * dependencies.  0, 1, 2
3272      */          
3273     bool pkgConfigQuery(const String &packageName, int query, String &result);
3275     /**
3276      * replace a variable ref like ${a} with a value
3277      */
3278     bool lookupProperty(const String &s, String &result);
3279     
3280     /**
3281      * called by getSubstitutions().  This is in case a looked-up string
3282      * has substitutions also.     
3283      */
3284     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3286     /**
3287      * replace variable refs in a string like ${a} with their values
3288      */
3289     bool getSubstitutions(const String &s, String &result);
3291     int line;
3294 };
3298 /**
3299  * Define the pkg-config class here, since it will be used in MakeBase method
3300  * implementations. 
3301  */
3302 class PkgConfig : public MakeBase
3305 public:
3307     /**
3308      *
3309      */
3310     PkgConfig()
3311         {
3312          path   = ".";
3313          prefix = "/target";
3314          init();
3315          }
3317     /**
3318      *
3319      */
3320     PkgConfig(const PkgConfig &other)
3321         { assign(other); }
3323     /**
3324      *
3325      */
3326     PkgConfig &operator=(const PkgConfig &other)
3327         { assign(other); return *this; }
3329     /**
3330      *
3331      */
3332     virtual ~PkgConfig()
3333         { }
3335     /**
3336      *
3337      */
3338     virtual String getName()
3339         { return name; }
3341     /**
3342      *
3343      */
3344     virtual String getPath()
3345         { return path; }
3347     /**
3348      *
3349      */
3350     virtual void setPath(const String &val)
3351         { path = val; }
3353     /**
3354      *
3355      */
3356     virtual String getPrefix()
3357         { return prefix; }
3359     /**
3360      *  Allow the user to override the prefix in the file
3361      */
3362     virtual void setPrefix(const String &val)
3363         { prefix = val; }
3365     /**
3366      *
3367      */
3368     virtual String getDescription()
3369         { return description; }
3371     /**
3372      *
3373      */
3374     virtual String getCflags()
3375         { return cflags; }
3377     /**
3378      *
3379      */
3380     virtual String getLibs()
3381         { return libs; }
3383     /**
3384      *
3385      */
3386     virtual String getAll()
3387         {
3388          String ret = cflags;
3389          ret.append(" ");
3390          ret.append(libs);
3391          return ret;
3392         }
3394     /**
3395      *
3396      */
3397     virtual String getVersion()
3398         { return version; }
3400     /**
3401      *
3402      */
3403     virtual int getMajorVersion()
3404         { return majorVersion; }
3406     /**
3407      *
3408      */
3409     virtual int getMinorVersion()
3410         { return minorVersion; }
3412     /**
3413      *
3414      */
3415     virtual int getMicroVersion()
3416         { return microVersion; }
3418     /**
3419      *
3420      */
3421     virtual std::map<String, String> &getAttributes()
3422         { return attrs; }
3424     /**
3425      *
3426      */
3427     virtual std::vector<String> &getRequireList()
3428         { return requireList; }
3430     /**
3431      *  Read a file for its details
3432      */         
3433     virtual bool readFile(const String &fileName);
3435     /**
3436      *  Read a file for its details
3437      */         
3438     virtual bool query(const String &name);
3440 private:
3442     void init()
3443         {
3444         //do not set path and prefix here
3445         name         = "";
3446         description  = "";
3447         cflags       = "";
3448         libs         = "";
3449         requires     = "";
3450         version      = "";
3451         majorVersion = 0;
3452         minorVersion = 0;
3453         microVersion = 0;
3454         fileName     = "";
3455         attrs.clear();
3456         requireList.clear();
3457         }
3459     void assign(const PkgConfig &other)
3460         {
3461         name         = other.name;
3462         path         = other.path;
3463         prefix       = other.prefix;
3464         description  = other.description;
3465         cflags       = other.cflags;
3466         libs         = other.libs;
3467         requires     = other.requires;
3468         version      = other.version;
3469         majorVersion = other.majorVersion;
3470         minorVersion = other.minorVersion;
3471         microVersion = other.microVersion;
3472         fileName     = other.fileName;
3473         attrs        = other.attrs;
3474         requireList  = other.requireList;
3475         }
3479     int get(int pos);
3481     int skipwhite(int pos);
3483     int getword(int pos, String &ret);
3485     /**
3486      * Very important
3487      */         
3488     bool parseRequires();
3490     void parseVersion();
3492     bool parseLine(const String &lineBuf);
3494     bool parse(const String &buf);
3496     void dumpAttrs();
3498     String name;
3500     String path;
3502     String prefix;
3504     String description;
3506     String cflags;
3508     String libs;
3510     String requires;
3512     String version;
3514     int majorVersion;
3516     int minorVersion;
3518     int microVersion;
3520     String fileName;
3522     std::map<String, String> attrs;
3524     std::vector<String> requireList;
3526     char *parsebuf;
3527     int parselen;
3528 };
3533 /**
3534  *  Print a printf()-like formatted error message
3535  */
3536 void MakeBase::error(const char *fmt, ...)
3538     va_list args;
3539     va_start(args,fmt);
3540     fprintf(stderr, "Make error line %d: ", line);
3541     vfprintf(stderr, fmt, args);
3542     fprintf(stderr, "\n");
3543     va_end(args) ;
3548 /**
3549  *  Print a printf()-like formatted trace message
3550  */
3551 void MakeBase::status(const char *fmt, ...)
3553     va_list args;
3554     va_start(args,fmt);
3555     //fprintf(stdout, " ");
3556     vfprintf(stdout, fmt, args);
3557     fprintf(stdout, "\n");
3558     va_end(args) ;
3562 /**
3563  *  Resolve another path relative to this one
3564  */
3565 String MakeBase::resolve(const String &otherPath)
3567     URI otherURI(otherPath);
3568     URI fullURI = uri.resolve(otherURI);
3569     String ret = fullURI.toString();
3570     return ret;
3574 /**
3575  *  Print a printf()-like formatted trace message
3576  */
3577 void MakeBase::trace(const char *fmt, ...)
3579     va_list args;
3580     va_start(args,fmt);
3581     fprintf(stdout, "Make: ");
3582     vfprintf(stdout, fmt, args);
3583     fprintf(stdout, "\n");
3584     va_end(args) ;
3589 /**
3590  *  Check if a given string matches a given regex pattern
3591  */
3592 bool MakeBase::regexMatch(const String &str, const String &pattern)
3594     const TRexChar *terror = NULL;
3595     const TRexChar *cpat = pattern.c_str();
3596     TRex *expr = trex_compile(cpat, &terror);
3597     if (!expr)
3598         {
3599         if (!terror)
3600             terror = "undefined";
3601         error("compilation error [%s]!\n", terror);
3602         return false;
3603         } 
3605     bool ret = true;
3607     const TRexChar *cstr = str.c_str();
3608     if (trex_match(expr, cstr))
3609         {
3610         ret = true;
3611         }
3612     else
3613         {
3614         ret = false;
3615         }
3617     trex_free(expr);
3619     return ret;
3622 /**
3623  *  Return the suffix, if any, of a file name
3624  */
3625 String MakeBase::getSuffix(const String &fname)
3627     if (fname.size() < 2)
3628         return "";
3629     unsigned int pos = fname.find_last_of('.');
3630     if (pos == fname.npos)
3631         return "";
3632     pos++;
3633     String res = fname.substr(pos, fname.size()-pos);
3634     //trace("suffix:%s", res.c_str()); 
3635     return res;
3640 /**
3641  * Break up a string into substrings delimited the characters
3642  * in delimiters.  Null-length substrings are ignored
3643  */  
3644 std::vector<String> MakeBase::tokenize(const String &str,
3645                                 const String &delimiters)
3648     std::vector<String> res;
3649     char *del = (char *)delimiters.c_str();
3650     String dmp;
3651     for (unsigned int i=0 ; i<str.size() ; i++)
3652         {
3653         char ch = str[i];
3654         char *p = (char *)0;
3655         for (p=del ; *p ; p++)
3656             if (*p == ch)
3657                 break;
3658         if (*p)
3659             {
3660             if (dmp.size() > 0)
3661                 {
3662                 res.push_back(dmp);
3663                 dmp.clear();
3664                 }
3665             }
3666         else
3667             {
3668             dmp.push_back(ch);
3669             }
3670         }
3671     //Add tail
3672     if (dmp.size() > 0)
3673         {
3674         res.push_back(dmp);
3675         dmp.clear();
3676         }
3678     return res;
3683 /**
3684  *  replace runs of whitespace with a single space
3685  */
3686 String MakeBase::strip(const String &s)
3688     int len = s.size();
3689     String stripped;
3690     for (int i = 0 ; i<len ; i++)
3691         {
3692         char ch = s[i];
3693         if (isspace(ch))
3694             {
3695             stripped.push_back(' ');
3696             for ( ; i<len ; i++)
3697                 {
3698                 ch = s[i];
3699                 if (!isspace(ch))
3700                     {
3701                     stripped.push_back(ch);
3702                     break;
3703                     }
3704                 }
3705             }
3706         else
3707             {
3708             stripped.push_back(ch);
3709             }
3710         }
3711     return stripped;
3714 /**
3715  *  remove leading whitespace from each line
3716  */
3717 String MakeBase::leftJustify(const String &s)
3719     String out;
3720     int len = s.size();
3721     for (int i = 0 ; i<len ; )
3722         {
3723         char ch;
3724         //Skip to first visible character
3725         while (i<len)
3726             {
3727             ch = s[i];
3728             if (ch == '\n' || ch == '\r'
3729               || !isspace(ch))
3730                   break;
3731             i++;
3732             }
3733         //Copy the rest of the line
3734         while (i<len)
3735             {
3736             ch = s[i];
3737             if (ch == '\n' || ch == '\r')
3738                 {
3739                 if (ch != '\r')
3740                     out.push_back('\n');
3741                 i++;
3742                 break;
3743                 }
3744             else
3745                 {
3746                 out.push_back(ch);
3747                 }
3748             i++;
3749             }
3750         }
3751     return out;
3755 /**
3756  *  Removes whitespace from beginning and end of a string
3757  */
3758 String MakeBase::trim(const String &s)
3760     if (s.size() < 1)
3761         return s;
3762     
3763     //Find first non-ws char
3764     unsigned int begin = 0;
3765     for ( ; begin < s.size() ; begin++)
3766         {
3767         if (!isspace(s[begin]))
3768             break;
3769         }
3771     //Find first non-ws char, going in reverse
3772     unsigned int end = s.size() - 1;
3773     for ( ; end > begin ; end--)
3774         {
3775         if (!isspace(s[end]))
3776             break;
3777         }
3778     //trace("begin:%d  end:%d", begin, end);
3780     String res = s.substr(begin, end-begin+1);
3781     return res;
3785 /**
3786  *  Return a lower case version of the given string
3787  */
3788 String MakeBase::toLower(const String &s)
3790     if (s.size()==0)
3791         return s;
3793     String ret;
3794     for(unsigned int i=0; i<s.size() ; i++)
3795         {
3796         ret.push_back(tolower(s[i]));
3797         }
3798     return ret;
3802 /**
3803  * Return the native format of the canonical
3804  * path which we store
3805  */
3806 String MakeBase::getNativePath(const String &path)
3808 #ifdef __WIN32__
3809     String npath;
3810     unsigned int firstChar = 0;
3811     if (path.size() >= 3)
3812         {
3813         if (path[0] == '/' &&
3814             isalpha(path[1]) &&
3815             path[2] == ':')
3816             firstChar++;
3817         }
3818     for (unsigned int i=firstChar ; i<path.size() ; i++)
3819         {
3820         char ch = path[i];
3821         if (ch == '/')
3822             npath.push_back('\\');
3823         else
3824             npath.push_back(ch);
3825         }
3826     return npath;
3827 #else
3828     return path;
3829 #endif
3833 #ifdef __WIN32__
3834 #include <tchar.h>
3836 static String win32LastError()
3839     DWORD dw = GetLastError(); 
3841     LPVOID str;
3842     FormatMessage(
3843         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3844         FORMAT_MESSAGE_FROM_SYSTEM,
3845         NULL,
3846         dw,
3847         0,
3848         (LPTSTR) &str,
3849         0, NULL );
3850     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3851     if(p != NULL)
3852         { // lose CRLF
3853         *p = _T('\0');
3854         }
3855     String ret = (char *)str;
3856     LocalFree(str);
3858     return ret;
3860 #endif
3864 /**
3865  * Execute a system call, using pipes to send data to the
3866  * program's stdin,  and reading stdout and stderr.
3867  */
3868 bool MakeBase::executeCommand(const String &command,
3869                               const String &inbuf,
3870                               String &outbuf,
3871                               String &errbuf)
3874     status("============ cmd ============\n%s\n=============================",
3875                 command.c_str());
3877     outbuf.clear();
3878     errbuf.clear();
3879     
3880 #ifdef __WIN32__
3882     /*
3883     I really hate having win32 code in this program, but the
3884     read buffer in command.com and cmd.exe are just too small
3885     for the large commands we need for compiling and linking.
3886     */
3888     bool ret = true;
3890     //# Allocate a separate buffer for safety
3891     char *paramBuf = new char[command.size() + 1];
3892     if (!paramBuf)
3893        {
3894        error("executeCommand cannot allocate command buffer");
3895        return false;
3896        }
3897     strcpy(paramBuf, (char *)command.c_str());
3899     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3900     //# to see how Win32 pipes work
3902     //# Create pipes
3903     SECURITY_ATTRIBUTES saAttr; 
3904     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3905     saAttr.bInheritHandle = TRUE; 
3906     saAttr.lpSecurityDescriptor = NULL; 
3907     HANDLE stdinRead,  stdinWrite;
3908     HANDLE stdoutRead, stdoutWrite;
3909     HANDLE stderrRead, stderrWrite;
3910     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3911         {
3912         error("executeProgram: could not create pipe");
3913         delete[] paramBuf;
3914         return false;
3915         } 
3916     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3917     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3918         {
3919         error("executeProgram: could not create pipe");
3920         delete[] paramBuf;
3921         return false;
3922         } 
3923     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3924     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3925         {
3926         error("executeProgram: could not create pipe");
3927         delete[] paramBuf;
3928         return false;
3929         } 
3930     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3932     // Create the process
3933     STARTUPINFO siStartupInfo;
3934     PROCESS_INFORMATION piProcessInfo;
3935     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3936     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3937     siStartupInfo.cb = sizeof(siStartupInfo);
3938     siStartupInfo.hStdError   =  stderrWrite;
3939     siStartupInfo.hStdOutput  =  stdoutWrite;
3940     siStartupInfo.hStdInput   =  stdinRead;
3941     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3942    
3943     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3944                 0, NULL, NULL, &siStartupInfo,
3945                 &piProcessInfo))
3946         {
3947         error("executeCommand : could not create process : %s",
3948                     win32LastError().c_str());
3949         ret = false;
3950         }
3952     delete[] paramBuf;
3954     DWORD bytesWritten;
3955     if (inbuf.size()>0 &&
3956         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3957                &bytesWritten, NULL))
3958         {
3959         error("executeCommand: could not write to pipe");
3960         return false;
3961         }    
3962     if (!CloseHandle(stdinWrite))
3963         {          
3964         error("executeCommand: could not close write pipe");
3965         return false;
3966         }
3967     if (!CloseHandle(stdoutWrite))
3968         {
3969         error("executeCommand: could not close read pipe");
3970         return false;
3971         }
3972     if (!CloseHandle(stderrWrite))
3973         {
3974         error("executeCommand: could not close read pipe");
3975         return false;
3976         }
3978     bool lastLoop = false;
3979     while (true)
3980         {
3981         DWORD avail;
3982         DWORD bytesRead;
3983         char readBuf[4096];
3985         //trace("## stderr");
3986         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3987         if (avail > 0)
3988             {
3989             bytesRead = 0;
3990             if (avail>4096) avail = 4096;
3991             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3992             if (bytesRead > 0)
3993                 {
3994                 for (unsigned int i=0 ; i<bytesRead ; i++)
3995                     errbuf.push_back(readBuf[i]);
3996                 }
3997             }
3999         //trace("## stdout");
4000         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4001         if (avail > 0)
4002             {
4003             bytesRead = 0;
4004             if (avail>4096) avail = 4096;
4005             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4006             if (bytesRead > 0)
4007                 {
4008                 for (unsigned int i=0 ; i<bytesRead ; i++)
4009                     outbuf.push_back(readBuf[i]);
4010                 }
4011             }
4012             
4013         //Was this the final check after program done?
4014         if (lastLoop)
4015             break;
4017         DWORD exitCode;
4018         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4019         if (exitCode != STILL_ACTIVE)
4020             lastLoop = true;
4022         Sleep(10);
4023         }    
4024     //trace("outbuf:%s", outbuf.c_str());
4025     if (!CloseHandle(stdoutRead))
4026         {
4027         error("executeCommand: could not close read pipe");
4028         return false;
4029         }
4030     if (!CloseHandle(stderrRead))
4031         {
4032         error("executeCommand: could not close read pipe");
4033         return false;
4034         }
4036     DWORD exitCode;
4037     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4038     //trace("exit code:%d", exitCode);
4039     if (exitCode != 0)
4040         {
4041         ret = false;
4042         }
4043     
4044     CloseHandle(piProcessInfo.hProcess);
4045     CloseHandle(piProcessInfo.hThread);
4047     return ret;
4049 #else //do it unix-style
4051     String s;
4052     FILE *f = popen(command.c_str(), "r");
4053     int errnum = 0;
4054     if (f)
4055         {
4056         while (true)
4057             {
4058             int ch = fgetc(f);
4059             if (ch < 0)
4060                 break;
4061             s.push_back((char)ch);
4062             }
4063         errnum = pclose(f);
4064         }
4065     outbuf = s;
4066     if (errnum != 0)
4067         {
4068         error("exec of command '%s' failed : %s",
4069              command.c_str(), strerror(errno));
4070         return false;
4071         }
4072     else
4073         return true;
4075 #endif
4076
4081 bool MakeBase::listDirectories(const String &baseName,
4082                               const String &dirName,
4083                               std::vector<String> &res)
4085     res.push_back(dirName);
4086     String fullPath = baseName;
4087     if (dirName.size()>0)
4088         {
4089         fullPath.append("/");
4090         fullPath.append(dirName);
4091         }
4092     DIR *dir = opendir(fullPath.c_str());
4093     while (true)
4094         {
4095         struct dirent *de = readdir(dir);
4096         if (!de)
4097             break;
4099         //Get the directory member name
4100         String s = de->d_name;
4101         if (s.size() == 0 || s[0] == '.')
4102             continue;
4103         String childName = dirName;
4104         childName.append("/");
4105         childName.append(s);
4107         String fullChildPath = baseName;
4108         fullChildPath.append("/");
4109         fullChildPath.append(childName);
4110         struct stat finfo;
4111         String childNative = getNativePath(fullChildPath);
4112         if (stat(childNative.c_str(), &finfo)<0)
4113             {
4114             error("cannot stat file:%s", childNative.c_str());
4115             }
4116         else if (S_ISDIR(finfo.st_mode))
4117             {
4118             //trace("directory: %s", childName.c_str());
4119             if (!listDirectories(baseName, childName, res))
4120                 return false;
4121             }
4122         }
4123     closedir(dir);
4125     return true;
4129 bool MakeBase::listFiles(const String &baseDir,
4130                          const String &dirName,
4131                          std::vector<String> &res)
4133     String fullDir = baseDir;
4134     if (dirName.size()>0)
4135         {
4136         fullDir.append("/");
4137         fullDir.append(dirName);
4138         }
4139     String dirNative = getNativePath(fullDir);
4141     std::vector<String> subdirs;
4142     DIR *dir = opendir(dirNative.c_str());
4143     if (!dir)
4144         {
4145         error("Could not open directory %s : %s",
4146               dirNative.c_str(), strerror(errno));
4147         return false;
4148         }
4149     while (true)
4150         {
4151         struct dirent *de = readdir(dir);
4152         if (!de)
4153             break;
4155         //Get the directory member name
4156         String s = de->d_name;
4157         if (s.size() == 0 || s[0] == '.')
4158             continue;
4159         String childName;
4160         if (dirName.size()>0)
4161             {
4162             childName.append(dirName);
4163             childName.append("/");
4164             }
4165         childName.append(s);
4166         String fullChild = baseDir;
4167         fullChild.append("/");
4168         fullChild.append(childName);
4169         
4170         if (isDirectory(fullChild))
4171             {
4172             //trace("directory: %s", childName.c_str());
4173             if (!listFiles(baseDir, childName, res))
4174                 return false;
4175             continue;
4176             }
4177         else if (!isRegularFile(fullChild))
4178             {
4179             error("unknown file:%s", childName.c_str());
4180             return false;
4181             }
4183        //all done!
4184         res.push_back(childName);
4186         }
4187     closedir(dir);
4189     return true;
4193 /**
4194  * Several different classes extend MakeBase.  By "propRef", we mean
4195  * the one holding the properties.  Likely "Make" itself
4196  */
4197 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4199     //before doing the list,  resolve any property references
4200     //that might have been specified in the directory name, such as ${src}
4201     String fsDir = fileSet.getDirectory();
4202     String dir;
4203     if (!propRef.getSubstitutions(fsDir, dir))
4204         return false;
4205     String baseDir = propRef.resolve(dir);
4206     std::vector<String> fileList;
4207     if (!listFiles(baseDir, "", fileList))
4208         return false;
4210     std::vector<String> includes = fileSet.getIncludes();
4211     std::vector<String> excludes = fileSet.getExcludes();
4213     std::vector<String> incs;
4214     std::vector<String>::iterator iter;
4216     std::sort(fileList.begin(), fileList.end());
4218     //If there are <includes>, then add files to the output
4219     //in the order of the include list
4220     if (includes.size()==0)
4221         incs = fileList;
4222     else
4223         {
4224         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4225             {
4226             String &pattern = *iter;
4227             std::vector<String>::iterator siter;
4228             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4229                 {
4230                 String s = *siter;
4231                 if (regexMatch(s, pattern))
4232                     {
4233                     //trace("INCLUDED:%s", s.c_str());
4234                     incs.push_back(s);
4235                     }
4236                 }
4237             }
4238         }
4240     //Now trim off the <excludes>
4241     std::vector<String> res;
4242     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4243         {
4244         String s = *iter;
4245         bool skipme = false;
4246         std::vector<String>::iterator siter;
4247         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4248             {
4249             String &pattern = *siter;
4250             if (regexMatch(s, pattern))
4251                 {
4252                 //trace("EXCLUDED:%s", s.c_str());
4253                 skipme = true;
4254                 break;
4255                 }
4256             }
4257         if (!skipme)
4258             res.push_back(s);
4259         }
4260         
4261     fileSet.setFiles(res);
4263     return true;
4267 /**
4268  * 0 == all, 1 = cflags, 2 = libs
4269  */ 
4270 bool MakeBase::pkgConfigRecursive(const String packageName,
4271                                   const String &path, 
4272                                   const String &prefix, 
4273                                   int query,
4274                                   String &result,
4275                                   std::set<String> &deplist) 
4277     PkgConfig pkgConfig;
4278     if (path.size() > 0)
4279         pkgConfig.setPath(path);
4280     if (prefix.size() > 0)
4281         pkgConfig.setPrefix(prefix);
4282     if (!pkgConfig.query(packageName))
4283         return false;
4284     if (query == 0)
4285         result = pkgConfig.getAll();
4286     else if (query == 1)
4287         result = pkgConfig.getCflags();
4288     else
4289         result = pkgConfig.getLibs();
4290     deplist.insert(packageName);
4291     std::vector<String> list = pkgConfig.getRequireList();
4292     for (unsigned int i = 0 ; i<list.size() ; i++)
4293         {
4294         String depPkgName = list[i];
4295         if (deplist.find(depPkgName) != deplist.end())
4296             continue;
4297         String val;
4298         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4299             {
4300             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4301             return false;
4302             }
4303         result.append(" ");
4304         result.append(val);
4305         }
4307     return true;
4310 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4312     std::set<String> deplist;
4313     String path = getProperty("pkg-config-path");
4314     if (path.size()>0)
4315         path = resolve(path);
4316     String prefix = getProperty("pkg-config-prefix");
4317     String val;
4318     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4319         return false;
4320     result = val;
4321     return true;
4326 /**
4327  * replace a variable ref like ${a} with a value
4328  */
4329 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4331     String varname = propertyName;
4332     if (envPrefix.size() > 0 &&
4333         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4334         {
4335         varname = varname.substr(envPrefix.size());
4336         char *envstr = getenv(varname.c_str());
4337         if (!envstr)
4338             {
4339             error("environment variable '%s' not defined", varname.c_str());
4340             return false;
4341             }
4342         result = envstr;
4343         }
4344     else if (pcPrefix.size() > 0 &&
4345         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4346         {
4347         varname = varname.substr(pcPrefix.size());
4348         String val;
4349         if (!pkgConfigQuery(varname, 0, val))
4350             return false;
4351         result = val;
4352         }
4353     else if (pccPrefix.size() > 0 &&
4354         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4355         {
4356         varname = varname.substr(pccPrefix.size());
4357         String val;
4358         if (!pkgConfigQuery(varname, 1, val))
4359             return false;
4360         result = val;
4361         }
4362     else if (pclPrefix.size() > 0 &&
4363         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4364         {
4365         varname = varname.substr(pclPrefix.size());
4366         String val;
4367         if (!pkgConfigQuery(varname, 2, val))
4368             return false;
4369         result = val;
4370         }
4371     else
4372         {
4373         std::map<String, String>::iterator iter;
4374         iter = properties.find(varname);
4375         if (iter != properties.end())
4376             {
4377             result = iter->second;
4378             }
4379         else
4380             {
4381             error("property '%s' not found", varname.c_str());
4382             return false;
4383             }
4384         }
4385     return true;
4391 /**
4392  * Analyse a string, looking for any substitutions or other
4393  * things that need resolution 
4394  */
4395 bool MakeBase::getSubstitutionsRecursive(const String &str,
4396                                          String &result, int depth)
4398     if (depth > 10)
4399         {
4400         error("nesting of substitutions too deep (>10) for '%s'",
4401                         str.c_str());
4402         return false;
4403         }
4404     String s = trim(str);
4405     int len = (int)s.size();
4406     String val;
4407     for (int i=0 ; i<len ; i++)
4408         {
4409         char ch = s[i];
4410         if (ch == '$' && s[i+1] == '{')
4411             {
4412             String varname;
4413             int j = i+2;
4414             for ( ; j<len ; j++)
4415                 {
4416                 ch = s[j];
4417                 if (ch == '$' && s[j+1] == '{')
4418                     {
4419                     error("attribute %s cannot have nested variable references",
4420                            s.c_str());
4421                     return false;
4422                     }
4423                 else if (ch == '}')
4424                     {
4425                     varname = trim(varname);
4426                     String varval;
4427                     if (!lookupProperty(varname, varval))
4428                         return false;
4429                     String varval2;
4430                     //Now see if the answer has ${} in it, too
4431                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4432                         return false;
4433                     val.append(varval2);
4434                     break;
4435                     }
4436                 else
4437                     {
4438                     varname.push_back(ch);
4439                     }
4440                 }
4441             i = j;
4442             }
4443         else
4444             {
4445             val.push_back(ch);
4446             }
4447         }
4448     result = val;
4449     return true;
4452 /**
4453  * Analyse a string, looking for any substitutions or other
4454  * things that need resilution 
4455  */
4456 bool MakeBase::getSubstitutions(const String &str, String &result)
4458     return getSubstitutionsRecursive(str, result, 0);
4463 /**
4464  * replace variable refs like ${a} with their values
4465  * Assume that the string has already been syntax validated
4466  */
4467 String MakeBase::eval(const String &s, const String &defaultVal)
4469     if (s.size()==0)
4470         return defaultVal;
4471     String ret;
4472     if (getSubstitutions(s, ret))
4473         return ret;
4474     else
4475         return defaultVal;
4479 /**
4480  * replace variable refs like ${a} with their values
4481  * return true or false
4482  * Assume that the string has already been syntax validated
4483  */
4484 bool MakeBase::evalBool(const String &s, bool defaultVal)
4486     if (s.size()==0)
4487         return defaultVal;
4488     String val = eval(s, "false");
4489     if (s == "true" || s == "TRUE")
4490         return true;
4491     else
4492         return defaultVal;
4496 /**
4497  * Get a string attribute, testing it for proper syntax and
4498  * property names.
4499  */
4500 bool MakeBase::getAttribute(Element *elem, const String &name,
4501                                     String &result)
4503     String s = elem->getAttribute(name);
4504     String tmp;
4505     bool ret = getSubstitutions(s, tmp);
4506     if (ret)
4507         result = s;  //assign -if- ok
4508     return ret;
4512 /**
4513  * Get a string value, testing it for proper syntax and
4514  * property names.
4515  */
4516 bool MakeBase::getValue(Element *elem, String &result)
4518     String s = elem->getValue();
4519     String tmp;
4520     bool ret = getSubstitutions(s, tmp);
4521     if (ret)
4522         result = s;  //assign -if- ok
4523     return ret;
4529 /**
4530  * Parse a <patternset> entry
4531  */  
4532 bool MakeBase::parsePatternSet(Element *elem,
4533                           MakeBase &propRef,
4534                           std::vector<String> &includes,
4535                           std::vector<String> &excludes
4536                           )
4538     std::vector<Element *> children  = elem->getChildren();
4539     for (unsigned int i=0 ; i<children.size() ; i++)
4540         {
4541         Element *child = children[i];
4542         String tagName = child->getName();
4543         if (tagName == "exclude")
4544             {
4545             String fname;
4546             if (!propRef.getAttribute(child, "name", fname))
4547                 return false;
4548             //trace("EXCLUDE: %s", fname.c_str());
4549             excludes.push_back(fname);
4550             }
4551         else if (tagName == "include")
4552             {
4553             String fname;
4554             if (!propRef.getAttribute(child, "name", fname))
4555                 return false;
4556             //trace("INCLUDE: %s", fname.c_str());
4557             includes.push_back(fname);
4558             }
4559         }
4561     return true;
4567 /**
4568  * Parse a <fileset> entry, and determine which files
4569  * should be included
4570  */  
4571 bool MakeBase::parseFileSet(Element *elem,
4572                           MakeBase &propRef,
4573                           FileSet &fileSet)
4575     String name = elem->getName();
4576     if (name != "fileset")
4577         {
4578         error("expected <fileset>");
4579         return false;
4580         }
4583     std::vector<String> includes;
4584     std::vector<String> excludes;
4586     //A fileset has one implied patternset
4587     if (!parsePatternSet(elem, propRef, includes, excludes))
4588         {
4589         return false;
4590         }
4591     //Look for child tags, including more patternsets
4592     std::vector<Element *> children  = elem->getChildren();
4593     for (unsigned int i=0 ; i<children.size() ; i++)
4594         {
4595         Element *child = children[i];
4596         String tagName = child->getName();
4597         if (tagName == "patternset")
4598             {
4599             if (!parsePatternSet(child, propRef, includes, excludes))
4600                 {
4601                 return false;
4602                 }
4603             }
4604         }
4606     String dir;
4607     //Now do the stuff
4608     //Get the base directory for reading file names
4609     if (!propRef.getAttribute(elem, "dir", dir))
4610         return false;
4612     fileSet.setDirectory(dir);
4613     fileSet.setIncludes(includes);
4614     fileSet.setExcludes(excludes);
4615     
4616     /*
4617     std::vector<String> fileList;
4618     if (dir.size() > 0)
4619         {
4620         String baseDir = propRef.resolve(dir);
4621         if (!listFiles(baseDir, "", includes, excludes, fileList))
4622             return false;
4623         }
4624     std::sort(fileList.begin(), fileList.end());
4625     result = fileList;
4626     */
4628     
4629     /*
4630     for (unsigned int i=0 ; i<result.size() ; i++)
4631         {
4632         trace("RES:%s", result[i].c_str());
4633         }
4634     */
4636     
4637     return true;
4640 /**
4641  * Parse a <filelist> entry.  This is far simpler than FileSet,
4642  * since no directory scanning is needed.  The file names are listed
4643  * explicitly.
4644  */  
4645 bool MakeBase::parseFileList(Element *elem,
4646                           MakeBase &propRef,
4647                           FileList &fileList)
4649     std::vector<String> fnames;
4650     //Look for child tags, namely "file"
4651     std::vector<Element *> children  = elem->getChildren();
4652     for (unsigned int i=0 ; i<children.size() ; i++)
4653         {
4654         Element *child = children[i];
4655         String tagName = child->getName();
4656         if (tagName == "file")
4657             {
4658             String fname = child->getAttribute("name");
4659             if (fname.size()==0)
4660                 {
4661                 error("<file> element requires name="" attribute");
4662                 return false;
4663                 }
4664             fnames.push_back(fname);
4665             }
4666         else
4667             {
4668             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4669             return false;
4670             }
4671         }
4673     String dir;
4674     //Get the base directory for reading file names
4675     if (!propRef.getAttribute(elem, "dir", dir))
4676         return false;
4677     fileList.setDirectory(dir);
4678     fileList.setFiles(fnames);
4680     return true;
4685 /**
4686  * Create a directory, making intermediate dirs
4687  * if necessary
4688  */                  
4689 bool MakeBase::createDirectory(const String &dirname)
4691     //trace("## createDirectory: %s", dirname.c_str());
4692     //## first check if it exists
4693     struct stat finfo;
4694     String nativeDir = getNativePath(dirname);
4695     char *cnative = (char *) nativeDir.c_str();
4696 #ifdef __WIN32__
4697     if (strlen(cnative)==2 && cnative[1]==':')
4698         return true;
4699 #endif
4700     if (stat(cnative, &finfo)==0)
4701         {
4702         if (!S_ISDIR(finfo.st_mode))
4703             {
4704             error("mkdir: file %s exists but is not a directory",
4705                   cnative);
4706             return false;
4707             }
4708         else //exists
4709             {
4710             return true;
4711             }
4712         }
4714     //## 2: pull off the last path segment, if any,
4715     //## to make the dir 'above' this one, if necessary
4716     unsigned int pos = dirname.find_last_of('/');
4717     if (pos>0 && pos != dirname.npos)
4718         {
4719         String subpath = dirname.substr(0, pos);
4720         //A letter root (c:) ?
4721         if (!createDirectory(subpath))
4722             return false;
4723         }
4724         
4725     //## 3: now make
4726 #ifdef __WIN32__
4727     if (mkdir(cnative)<0)
4728 #else
4729     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4730 #endif
4731         {
4732         error("cannot make directory '%s' : %s",
4733                  cnative, strerror(errno));
4734         return false;
4735         }
4736         
4737     return true;
4741 /**
4742  * Remove a directory recursively
4743  */ 
4744 bool MakeBase::removeDirectory(const String &dirName)
4746     char *dname = (char *)dirName.c_str();
4748     DIR *dir = opendir(dname);
4749     if (!dir)
4750         {
4751         //# Let this fail nicely.
4752         return true;
4753         //error("error opening directory %s : %s", dname, strerror(errno));
4754         //return false;
4755         }
4756     
4757     while (true)
4758         {
4759         struct dirent *de = readdir(dir);
4760         if (!de)
4761             break;
4763         //Get the directory member name
4764         String s = de->d_name;
4765         if (s.size() == 0 || s[0] == '.')
4766             continue;
4767         String childName;
4768         if (dirName.size() > 0)
4769             {
4770             childName.append(dirName);
4771             childName.append("/");
4772             }
4773         childName.append(s);
4776         struct stat finfo;
4777         String childNative = getNativePath(childName);
4778         char *cnative = (char *)childNative.c_str();
4779         if (stat(cnative, &finfo)<0)
4780             {
4781             error("cannot stat file:%s", cnative);
4782             }
4783         else if (S_ISDIR(finfo.st_mode))
4784             {
4785             //trace("DEL dir: %s", childName.c_str());
4786             if (!removeDirectory(childName))
4787                 {
4788                 return false;
4789                 }
4790             }
4791         else if (!S_ISREG(finfo.st_mode))
4792             {
4793             //trace("not regular: %s", cnative);
4794             }
4795         else
4796             {
4797             //trace("DEL file: %s", childName.c_str());
4798             if (remove(cnative)<0)
4799                 {
4800                 error("error deleting %s : %s",
4801                      cnative, strerror(errno));
4802                 return false;
4803                 }
4804             }
4805         }
4806     closedir(dir);
4808     //Now delete the directory
4809     String native = getNativePath(dirName);
4810     if (rmdir(native.c_str())<0)
4811         {
4812         error("could not delete directory %s : %s",
4813             native.c_str() , strerror(errno));
4814         return false;
4815         }
4817     return true;
4818     
4822 /**
4823  * Copy a file from one name to another. Perform only if needed
4824  */ 
4825 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4827     //# 1 Check up-to-date times
4828     String srcNative = getNativePath(srcFile);
4829     struct stat srcinfo;
4830     if (stat(srcNative.c_str(), &srcinfo)<0)
4831         {
4832         error("source file %s for copy does not exist",
4833                  srcNative.c_str());
4834         return false;
4835         }
4837     String destNative = getNativePath(destFile);
4838     struct stat destinfo;
4839     if (stat(destNative.c_str(), &destinfo)==0)
4840         {
4841         if (destinfo.st_mtime >= srcinfo.st_mtime)
4842             return true;
4843         }
4844         
4845     //# 2 prepare a destination directory if necessary
4846     unsigned int pos = destFile.find_last_of('/');
4847     if (pos != destFile.npos)
4848         {
4849         String subpath = destFile.substr(0, pos);
4850         if (!createDirectory(subpath))
4851             return false;
4852         }
4854     //# 3 do the data copy
4855 #ifndef __WIN32__
4857     FILE *srcf = fopen(srcNative.c_str(), "rb");
4858     if (!srcf)
4859         {
4860         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4861         return false;
4862         }
4863     FILE *destf = fopen(destNative.c_str(), "wb");
4864     if (!destf)
4865         {
4866         error("copyFile cannot open %s for writing", srcNative.c_str());
4867         return false;
4868         }
4870     while (!feof(srcf))
4871         {
4872         int ch = fgetc(srcf);
4873         if (ch<0)
4874             break;
4875         fputc(ch, destf);
4876         }
4878     fclose(destf);
4879     fclose(srcf);
4881 #else
4882     
4883     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4884         {
4885         error("copyFile from %s to %s failed",
4886              srcNative.c_str(), destNative.c_str());
4887         return false;
4888         }
4889         
4890 #endif /* __WIN32__ */
4893     return true;
4898 /**
4899  * Tests if the file exists and is a regular file
4900  */ 
4901 bool MakeBase::isRegularFile(const String &fileName)
4903     String native = getNativePath(fileName);
4904     struct stat finfo;
4905     
4906     //Exists?
4907     if (stat(native.c_str(), &finfo)<0)
4908         return false;
4911     //check the file mode
4912     if (!S_ISREG(finfo.st_mode))
4913         return false;
4915     return true;
4918 /**
4919  * Tests if the file exists and is a directory
4920  */ 
4921 bool MakeBase::isDirectory(const String &fileName)
4923     String native = getNativePath(fileName);
4924     struct stat finfo;
4925     
4926     //Exists?
4927     if (stat(native.c_str(), &finfo)<0)
4928         return false;
4931     //check the file mode
4932     if (!S_ISDIR(finfo.st_mode))
4933         return false;
4935     return true;
4940 /**
4941  * Tests is the modification of fileA is newer than fileB
4942  */ 
4943 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4945     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4946     String nativeA = getNativePath(fileA);
4947     struct stat infoA;
4948     //IF source does not exist, NOT newer
4949     if (stat(nativeA.c_str(), &infoA)<0)
4950         {
4951         return false;
4952         }
4954     String nativeB = getNativePath(fileB);
4955     struct stat infoB;
4956     //IF dest does not exist, YES, newer
4957     if (stat(nativeB.c_str(), &infoB)<0)
4958         {
4959         return true;
4960         }
4962     //check the actual times
4963     if (infoA.st_mtime > infoB.st_mtime)
4964         {
4965         return true;
4966         }
4968     return false;
4972 //########################################################################
4973 //# P K G    C O N F I G
4974 //########################################################################
4977 /**
4978  * Get a character from the buffer at pos.  If out of range,
4979  * return -1 for safety
4980  */
4981 int PkgConfig::get(int pos)
4983     if (pos>parselen)
4984         return -1;
4985     return parsebuf[pos];
4990 /**
4991  *  Skip over all whitespace characters beginning at pos.  Return
4992  *  the position of the first non-whitespace character.
4993  *  Pkg-config is line-oriented, so check for newline
4994  */
4995 int PkgConfig::skipwhite(int pos)
4997     while (pos < parselen)
4998         {
4999         int ch = get(pos);
5000         if (ch < 0)
5001             break;
5002         if (!isspace(ch))
5003             break;
5004         pos++;
5005         }
5006     return pos;
5010 /**
5011  *  Parse the buffer beginning at pos, for a word.  Fill
5012  *  'ret' with the result.  Return the position after the
5013  *  word.
5014  */
5015 int PkgConfig::getword(int pos, String &ret)
5017     while (pos < parselen)
5018         {
5019         int ch = get(pos);
5020         if (ch < 0)
5021             break;
5022         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5023             break;
5024         ret.push_back((char)ch);
5025         pos++;
5026         }
5027     return pos;
5030 bool PkgConfig::parseRequires()
5032     if (requires.size() == 0)
5033         return true;
5034     parsebuf = (char *)requires.c_str();
5035     parselen = requires.size();
5036     int pos = 0;
5037     while (pos < parselen)
5038         {
5039         pos = skipwhite(pos);
5040         String val;
5041         int pos2 = getword(pos, val);
5042         if (pos2 == pos)
5043             break;
5044         pos = pos2;
5045         //trace("val %s", val.c_str());
5046         requireList.push_back(val);
5047         }
5048     return true;
5052 static int getint(const String str)
5054     char *s = (char *)str.c_str();
5055     char *ends = NULL;
5056     long val = strtol(s, &ends, 10);
5057     if (ends == s)
5058         return 0L;
5059     else
5060         return val;
5063 void PkgConfig::parseVersion()
5065     if (version.size() == 0)
5066         return;
5067     String s1, s2, s3;
5068     unsigned int pos = 0;
5069     unsigned int pos2 = version.find('.', pos);
5070     if (pos2 == version.npos)
5071         {
5072         s1 = version;
5073         }
5074     else
5075         {
5076         s1 = version.substr(pos, pos2-pos);
5077         pos = pos2;
5078         pos++;
5079         if (pos < version.size())
5080             {
5081             pos2 = version.find('.', pos);
5082             if (pos2 == version.npos)
5083                 {
5084                 s2 = version.substr(pos, version.size()-pos);
5085                 }
5086             else
5087                 {
5088                 s2 = version.substr(pos, pos2-pos);
5089                 pos = pos2;
5090                 pos++;
5091                 if (pos < version.size())
5092                     s3 = version.substr(pos, pos2-pos);
5093                 }
5094             }
5095         }
5097     majorVersion = getint(s1);
5098     minorVersion = getint(s2);
5099     microVersion = getint(s3);
5100     //trace("version:%d.%d.%d", majorVersion,
5101     //          minorVersion, microVersion );
5105 bool PkgConfig::parseLine(const String &lineBuf)
5107     parsebuf = (char *)lineBuf.c_str();
5108     parselen = lineBuf.size();
5109     int pos = 0;
5110     
5111     while (pos < parselen)
5112         {
5113         String attrName;
5114         pos = skipwhite(pos);
5115         int ch = get(pos);
5116         if (ch == '#')
5117             {
5118             //comment.  eat the rest of the line
5119             while (pos < parselen)
5120                 {
5121                 ch = get(pos);
5122                 if (ch == '\n' || ch < 0)
5123                     break;
5124                 pos++;
5125                 }
5126             continue;
5127             }
5128         pos = getword(pos, attrName);
5129         if (attrName.size() == 0)
5130             continue;
5131         
5132         pos = skipwhite(pos);
5133         ch = get(pos);
5134         if (ch != ':' && ch != '=')
5135             {
5136             error("expected ':' or '='");
5137             return false;
5138             }
5139         pos++;
5140         pos = skipwhite(pos);
5141         String attrVal;
5142         while (pos < parselen)
5143             {
5144             ch = get(pos);
5145             if (ch == '\n' || ch < 0)
5146                 break;
5147             else if (ch == '$' && get(pos+1) == '{')
5148                 {
5149                 //#  this is a ${substitution}
5150                 pos += 2;
5151                 String subName;
5152                 while (pos < parselen)
5153                     {
5154                     ch = get(pos);
5155                     if (ch < 0)
5156                         {
5157                         error("unterminated substitution");
5158                         return false;
5159                         }
5160                     else if (ch == '}')
5161                         break;
5162                     else
5163                         subName.push_back((char)ch);
5164                     pos++;
5165                     }
5166                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5167                 if (subName == "prefix" && prefix.size()>0)
5168                     {
5169                     attrVal.append(prefix);
5170                     //trace("prefix override:%s", prefix.c_str());
5171                     }
5172                 else
5173                     {
5174                     String subVal = attrs[subName];
5175                     //trace("subVal:%s", subVal.c_str());
5176                     attrVal.append(subVal);
5177                     }
5178                 }
5179             else
5180                 attrVal.push_back((char)ch);
5181             pos++;
5182             }
5184         attrVal = trim(attrVal);
5185         attrs[attrName] = attrVal;
5187         String attrNameL = toLower(attrName);
5189         if (attrNameL == "name")
5190             name = attrVal;
5191         else if (attrNameL == "description")
5192             description = attrVal;
5193         else if (attrNameL == "cflags")
5194             cflags = attrVal;
5195         else if (attrNameL == "libs")
5196             libs = attrVal;
5197         else if (attrNameL == "requires")
5198             requires = attrVal;
5199         else if (attrNameL == "version")
5200             version = attrVal;
5202         //trace("name:'%s'  value:'%s'",
5203         //      attrName.c_str(), attrVal.c_str());
5204         }
5206     return true;
5210 bool PkgConfig::parse(const String &buf)
5212     init();
5214     String line;
5215     int lineNr = 0;
5216     for (unsigned int p=0 ; p<buf.size() ; p++)
5217         {
5218         int ch = buf[p];
5219         if (ch == '\n' || ch == '\r')
5220             {
5221             if (!parseLine(line))
5222                 return false;
5223             line.clear();
5224             lineNr++;
5225             }
5226         else
5227             {
5228             line.push_back(ch);
5229             }
5230         }
5231     if (line.size()>0)
5232         {
5233         if (!parseLine(line))
5234             return false;
5235         }
5237     parseRequires();
5238     parseVersion();
5240     return true;
5246 void PkgConfig::dumpAttrs()
5248     //trace("### PkgConfig attributes for %s", fileName.c_str());
5249     std::map<String, String>::iterator iter;
5250     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5251         {
5252         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5253         }
5257 bool PkgConfig::readFile(const String &fname)
5259     fileName = getNativePath(fname);
5261     FILE *f = fopen(fileName.c_str(), "r");
5262     if (!f)
5263         {
5264         error("cannot open file '%s' for reading", fileName.c_str());
5265         return false;
5266         }
5267     String buf;
5268     while (true)
5269         {
5270         int ch = fgetc(f);
5271         if (ch < 0)
5272             break;
5273         buf.push_back((char)ch);
5274         }
5275     fclose(f);
5277     //trace("####### File:\n%s", buf.c_str());
5278     if (!parse(buf))
5279         {
5280         return false;
5281         }
5283     //dumpAttrs();
5285     return true;
5290 bool PkgConfig::query(const String &pkgName)
5292     name = pkgName;
5294     String fname = path;
5295     fname.append("/");
5296     fname.append(name);
5297     fname.append(".pc");
5299     if (!readFile(fname))
5300         return false;
5301     
5302     return true;
5309 //########################################################################
5310 //# D E P T O O L
5311 //########################################################################
5315 /**
5316  *  Class which holds information for each file.
5317  */
5318 class FileRec
5320 public:
5322     typedef enum
5323         {
5324         UNKNOWN,
5325         CFILE,
5326         HFILE,
5327         OFILE
5328         } FileType;
5330     /**
5331      *  Constructor
5332      */
5333     FileRec()
5334         { init(); type = UNKNOWN; }
5336     /**
5337      *  Copy constructor
5338      */
5339     FileRec(const FileRec &other)
5340         { init(); assign(other); }
5341     /**
5342      *  Constructor
5343      */
5344     FileRec(int typeVal)
5345         { init(); type = typeVal; }
5346     /**
5347      *  Assignment operator
5348      */
5349     FileRec &operator=(const FileRec &other)
5350         { init(); assign(other); return *this; }
5353     /**
5354      *  Destructor
5355      */
5356     ~FileRec()
5357         {}
5359     /**
5360      *  Directory part of the file name
5361      */
5362     String path;
5364     /**
5365      *  Base name, sans directory and suffix
5366      */
5367     String baseName;
5369     /**
5370      *  File extension, such as cpp or h
5371      */
5372     String suffix;
5374     /**
5375      *  Type of file: CFILE, HFILE, OFILE
5376      */
5377     int type;
5379     /**
5380      * Used to list files ref'd by this one
5381      */
5382     std::map<String, FileRec *> files;
5385 private:
5387     void init()
5388         {
5389         }
5391     void assign(const FileRec &other)
5392         {
5393         type     = other.type;
5394         baseName = other.baseName;
5395         suffix   = other.suffix;
5396         files    = other.files;
5397         }
5399 };
5403 /**
5404  *  Simpler dependency record
5405  */
5406 class DepRec
5408 public:
5410     /**
5411      *  Constructor
5412      */
5413     DepRec()
5414         {init();}
5416     /**
5417      *  Copy constructor
5418      */
5419     DepRec(const DepRec &other)
5420         {init(); assign(other);}
5421     /**
5422      *  Constructor
5423      */
5424     DepRec(const String &fname)
5425         {init(); name = fname; }
5426     /**
5427      *  Assignment operator
5428      */
5429     DepRec &operator=(const DepRec &other)
5430         {init(); assign(other); return *this;}
5433     /**
5434      *  Destructor
5435      */
5436     ~DepRec()
5437         {}
5439     /**
5440      *  Directory part of the file name
5441      */
5442     String path;
5444     /**
5445      *  Base name, without the path and suffix
5446      */
5447     String name;
5449     /**
5450      *  Suffix of the source
5451      */
5452     String suffix;
5455     /**
5456      * Used to list files ref'd by this one
5457      */
5458     std::vector<String> files;
5461 private:
5463     void init()
5464         {
5465         }
5467     void assign(const DepRec &other)
5468         {
5469         path     = other.path;
5470         name     = other.name;
5471         suffix   = other.suffix;
5472         files    = other.files; //avoid recursion
5473         }
5475 };
5478 class DepTool : public MakeBase
5480 public:
5482     /**
5483      *  Constructor
5484      */
5485     DepTool()
5486         { init(); }
5488     /**
5489      *  Copy constructor
5490      */
5491     DepTool(const DepTool &other)
5492         { init(); assign(other); }
5494     /**
5495      *  Assignment operator
5496      */
5497     DepTool &operator=(const DepTool &other)
5498         { init(); assign(other); return *this; }
5501     /**
5502      *  Destructor
5503      */
5504     ~DepTool()
5505         {}
5508     /**
5509      *  Reset this section of code
5510      */
5511     virtual void init();
5512     
5513     /**
5514      *  Reset this section of code
5515      */
5516     virtual void assign(const DepTool &other)
5517         {
5518         }
5519     
5520     /**
5521      *  Sets the source directory which will be scanned
5522      */
5523     virtual void setSourceDirectory(const String &val)
5524         { sourceDir = val; }
5526     /**
5527      *  Returns the source directory which will be scanned
5528      */
5529     virtual String getSourceDirectory()
5530         { return sourceDir; }
5532     /**
5533      *  Sets the list of files within the directory to analyze
5534      */
5535     virtual void setFileList(const std::vector<String> &list)
5536         { fileList = list; }
5538     /**
5539      * Creates the list of all file names which will be
5540      * candidates for further processing.  Reads make.exclude
5541      * to see which files for directories to leave out.
5542      */
5543     virtual bool createFileList();
5546     /**
5547      *  Generates the forward dependency list
5548      */
5549     virtual bool generateDependencies();
5552     /**
5553      *  Generates the forward dependency list, saving the file
5554      */
5555     virtual bool generateDependencies(const String &);
5558     /**
5559      *  Load a dependency file
5560      */
5561     std::vector<DepRec> loadDepFile(const String &fileName);
5563     /**
5564      *  Load a dependency file, generating one if necessary
5565      */
5566     std::vector<DepRec> getDepFile(const String &fileName,
5567               bool forceRefresh);
5569     /**
5570      *  Save a dependency file
5571      */
5572     bool saveDepFile(const String &fileName);
5575 private:
5578     /**
5579      *
5580      */
5581     void parseName(const String &fullname,
5582                    String &path,
5583                    String &basename,
5584                    String &suffix);
5586     /**
5587      *
5588      */
5589     int get(int pos);
5591     /**
5592      *
5593      */
5594     int skipwhite(int pos);
5596     /**
5597      *
5598      */
5599     int getword(int pos, String &ret);
5601     /**
5602      *
5603      */
5604     bool sequ(int pos, const char *key);
5606     /**
5607      *
5608      */
5609     bool addIncludeFile(FileRec *frec, const String &fname);
5611     /**
5612      *
5613      */
5614     bool scanFile(const String &fname, FileRec *frec);
5616     /**
5617      *
5618      */
5619     bool processDependency(FileRec *ofile, FileRec *include);
5621     /**
5622      *
5623      */
5624     String sourceDir;
5626     /**
5627      *
5628      */
5629     std::vector<String> fileList;
5631     /**
5632      *
5633      */
5634     std::vector<String> directories;
5636     /**
5637      * A list of all files which will be processed for
5638      * dependencies.
5639      */
5640     std::map<String, FileRec *> allFiles;
5642     /**
5643      * The list of .o files, and the
5644      * dependencies upon them.
5645      */
5646     std::map<String, FileRec *> oFiles;
5648     int depFileSize;
5649     char *depFileBuf;
5651     static const int readBufSize = 8192;
5652     char readBuf[8193];//byte larger
5654 };
5660 /**
5661  *  Clean up after processing.  Called by the destructor, but should
5662  *  also be called before the object is reused.
5663  */
5664 void DepTool::init()
5666     sourceDir = ".";
5668     fileList.clear();
5669     directories.clear();
5670     
5671     //clear output file list
5672     std::map<String, FileRec *>::iterator iter;
5673     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5674         delete iter->second;
5675     oFiles.clear();
5677     //allFiles actually contains the master copies. delete them
5678     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5679         delete iter->second;
5680     allFiles.clear(); 
5687 /**
5688  *  Parse a full path name into path, base name, and suffix
5689  */
5690 void DepTool::parseName(const String &fullname,
5691                         String &path,
5692                         String &basename,
5693                         String &suffix)
5695     if (fullname.size() < 2)
5696         return;
5698     unsigned int pos = fullname.find_last_of('/');
5699     if (pos != fullname.npos && pos<fullname.size()-1)
5700         {
5701         path = fullname.substr(0, pos);
5702         pos++;
5703         basename = fullname.substr(pos, fullname.size()-pos);
5704         }
5705     else
5706         {
5707         path = "";
5708         basename = fullname;
5709         }
5711     pos = basename.find_last_of('.');
5712     if (pos != basename.npos && pos<basename.size()-1)
5713         {
5714         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5715         basename = basename.substr(0, pos);
5716         }
5718     //trace("parsename:%s %s %s", path.c_str(),
5719     //        basename.c_str(), suffix.c_str()); 
5724 /**
5725  *  Generate our internal file list.
5726  */
5727 bool DepTool::createFileList()
5730     for (unsigned int i=0 ; i<fileList.size() ; i++)
5731         {
5732         String fileName = fileList[i];
5733         //trace("## FileName:%s", fileName.c_str());
5734         String path;
5735         String basename;
5736         String sfx;
5737         parseName(fileName, path, basename, sfx);
5738         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5739             sfx == "cc" || sfx == "CC")
5740             {
5741             FileRec *fe         = new FileRec(FileRec::CFILE);
5742             fe->path            = path;
5743             fe->baseName        = basename;
5744             fe->suffix          = sfx;
5745             allFiles[fileName]  = fe;
5746             }
5747         else if (sfx == "h"   ||  sfx == "hh"  ||
5748                  sfx == "hpp" ||  sfx == "hxx")
5749             {
5750             FileRec *fe         = new FileRec(FileRec::HFILE);
5751             fe->path            = path;
5752             fe->baseName        = basename;
5753             fe->suffix          = sfx;
5754             allFiles[fileName]  = fe;
5755             }
5756         }
5758     if (!listDirectories(sourceDir, "", directories))
5759         return false;
5760         
5761     return true;
5768 /**
5769  * Get a character from the buffer at pos.  If out of range,
5770  * return -1 for safety
5771  */
5772 int DepTool::get(int pos)
5774     if (pos>depFileSize)
5775         return -1;
5776     return depFileBuf[pos];
5781 /**
5782  *  Skip over all whitespace characters beginning at pos.  Return
5783  *  the position of the first non-whitespace character.
5784  */
5785 int DepTool::skipwhite(int pos)
5787     while (pos < depFileSize)
5788         {
5789         int ch = get(pos);
5790         if (ch < 0)
5791             break;
5792         if (!isspace(ch))
5793             break;
5794         pos++;
5795         }
5796     return pos;
5800 /**
5801  *  Parse the buffer beginning at pos, for a word.  Fill
5802  *  'ret' with the result.  Return the position after the
5803  *  word.
5804  */
5805 int DepTool::getword(int pos, String &ret)
5807     while (pos < depFileSize)
5808         {
5809         int ch = get(pos);
5810         if (ch < 0)
5811             break;
5812         if (isspace(ch))
5813             break;
5814         ret.push_back((char)ch);
5815         pos++;
5816         }
5817     return pos;
5820 /**
5821  * Return whether the sequence of characters in the buffer
5822  * beginning at pos match the key,  for the length of the key
5823  */
5824 bool DepTool::sequ(int pos, const char *key)
5826     while (*key)
5827         {
5828         if (*key != get(pos))
5829             return false;
5830         key++; pos++;
5831         }
5832     return true;
5837 /**
5838  *  Add an include file name to a file record.  If the name
5839  *  is not found in allFiles explicitly, try prepending include
5840  *  directory names to it and try again.
5841  */
5842 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5844     //# if the name is an exact match to a path name
5845     //# in allFiles, like "myinc.h"
5846     std::map<String, FileRec *>::iterator iter =
5847            allFiles.find(iname);
5848     if (iter != allFiles.end()) //already exists
5849         {
5850          //h file in same dir
5851         FileRec *other = iter->second;
5852         //trace("local: '%s'", iname.c_str());
5853         frec->files[iname] = other;
5854         return true;
5855         }
5856     else 
5857         {
5858         //## Ok, it was not found directly
5859         //look in other dirs
5860         std::vector<String>::iterator diter;
5861         for (diter=directories.begin() ;
5862              diter!=directories.end() ; diter++)
5863             {
5864             String dfname = *diter;
5865             dfname.append("/");
5866             dfname.append(iname);
5867             URI fullPathURI(dfname);  //normalize path name
5868             String fullPath = fullPathURI.getPath();
5869             if (fullPath[0] == '/')
5870                 fullPath = fullPath.substr(1);
5871             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5872             iter = allFiles.find(fullPath);
5873             if (iter != allFiles.end())
5874                 {
5875                 FileRec *other = iter->second;
5876                 //trace("other: '%s'", iname.c_str());
5877                 frec->files[fullPath] = other;
5878                 return true;
5879                 }
5880             }
5881         }
5882     return true;
5887 /**
5888  *  Lightly parse a file to find the #include directives.  Do
5889  *  a bit of state machine stuff to make sure that the directive
5890  *  is valid.  (Like not in a comment).
5891  */
5892 bool DepTool::scanFile(const String &fname, FileRec *frec)
5894     String fileName;
5895     if (sourceDir.size() > 0)
5896         {
5897         fileName.append(sourceDir);
5898         fileName.append("/");
5899         }
5900     fileName.append(fname);
5901     String nativeName = getNativePath(fileName);
5902     FILE *f = fopen(nativeName.c_str(), "r");
5903     if (!f)
5904         {
5905         error("Could not open '%s' for reading", fname.c_str());
5906         return false;
5907         }
5908     String buf;
5909     while (!feof(f))
5910         {
5911         int nrbytes = fread(readBuf, 1, readBufSize, f);
5912         readBuf[nrbytes] = '\0';
5913         buf.append(readBuf);
5914         }
5915     fclose(f);
5917     depFileSize = buf.size();
5918     depFileBuf  = (char *)buf.c_str();
5919     int pos = 0;
5922     while (pos < depFileSize)
5923         {
5924         //trace("p:%c", get(pos));
5926         //# Block comment
5927         if (get(pos) == '/' && get(pos+1) == '*')
5928             {
5929             pos += 2;
5930             while (pos < depFileSize)
5931                 {
5932                 if (get(pos) == '*' && get(pos+1) == '/')
5933                     {
5934                     pos += 2;
5935                     break;
5936                     }
5937                 else
5938                     pos++;
5939                 }
5940             }
5941         //# Line comment
5942         else if (get(pos) == '/' && get(pos+1) == '/')
5943             {
5944             pos += 2;
5945             while (pos < depFileSize)
5946                 {
5947                 if (get(pos) == '\n')
5948                     {
5949                     pos++;
5950                     break;
5951                     }
5952                 else
5953                     pos++;
5954                 }
5955             }
5956         //# #include! yaay
5957         else if (sequ(pos, "#include"))
5958             {
5959             pos += 8;
5960             pos = skipwhite(pos);
5961             String iname;
5962             pos = getword(pos, iname);
5963             if (iname.size()>2)
5964                 {
5965                 iname = iname.substr(1, iname.size()-2);
5966                 addIncludeFile(frec, iname);
5967                 }
5968             }
5969         else
5970             {
5971             pos++;
5972             }
5973         }
5975     return true;
5980 /**
5981  *  Recursively check include lists to find all files in allFiles to which
5982  *  a given file is dependent.
5983  */
5984 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5986     std::map<String, FileRec *>::iterator iter;
5987     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5988         {
5989         String fname  = iter->first;
5990         if (ofile->files.find(fname) != ofile->files.end())
5991             {
5992             //trace("file '%s' already seen", fname.c_str());
5993             continue;
5994             }
5995         FileRec *child  = iter->second;
5996         ofile->files[fname] = child;
5997       
5998         processDependency(ofile, child);
5999         }
6002     return true;
6009 /**
6010  *  Generate the file dependency list.
6011  */
6012 bool DepTool::generateDependencies()
6014     std::map<String, FileRec *>::iterator iter;
6015     //# First pass.  Scan for all includes
6016     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6017         {
6018         FileRec *frec = iter->second;
6019         if (!scanFile(iter->first, frec))
6020             {
6021             //quit?
6022             }
6023         }
6025     //# Second pass.  Scan for all includes
6026     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6027         {
6028         FileRec *include = iter->second;
6029         if (include->type == FileRec::CFILE)
6030             {
6031             //String cFileName   = iter->first;
6032             FileRec *ofile     = new FileRec(FileRec::OFILE);
6033             ofile->path        = include->path;
6034             ofile->baseName    = include->baseName;
6035             ofile->suffix      = include->suffix;
6036             String fname       = include->path;
6037             if (fname.size()>0)
6038                 fname.append("/");
6039             fname.append(include->baseName);
6040             fname.append(".o");
6041             oFiles[fname]    = ofile;
6042             //add the .c file first?   no, don't
6043             //ofile->files[cFileName] = include;
6044             
6045             //trace("ofile:%s", fname.c_str());
6047             processDependency(ofile, include);
6048             }
6049         }
6051       
6052     return true;
6057 /**
6058  *  High-level call to generate deps and optionally save them
6059  */
6060 bool DepTool::generateDependencies(const String &fileName)
6062     if (!createFileList())
6063         return false;
6064     if (!generateDependencies())
6065         return false;
6066     if (!saveDepFile(fileName))
6067         return false;
6068     return true;
6072 /**
6073  *   This saves the dependency cache.
6074  */
6075 bool DepTool::saveDepFile(const String &fileName)
6077     time_t tim;
6078     time(&tim);
6080     FILE *f = fopen(fileName.c_str(), "w");
6081     if (!f)
6082         {
6083         trace("cannot open '%s' for writing", fileName.c_str());
6084         }
6085     fprintf(f, "<?xml version='1.0'?>\n");
6086     fprintf(f, "<!--\n");
6087     fprintf(f, "########################################################\n");
6088     fprintf(f, "## File: build.dep\n");
6089     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6090     fprintf(f, "########################################################\n");
6091     fprintf(f, "-->\n");
6093     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6094     std::map<String, FileRec *>::iterator iter;
6095     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6096         {
6097         FileRec *frec = iter->second;
6098         if (frec->type == FileRec::OFILE)
6099             {
6100             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6101                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6102             std::map<String, FileRec *>::iterator citer;
6103             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6104                 {
6105                 String cfname = citer->first;
6106                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6107                 }
6108             fprintf(f, "</object>\n\n");
6109             }
6110         }
6112     fprintf(f, "</dependencies>\n");
6113     fprintf(f, "\n");
6114     fprintf(f, "<!--\n");
6115     fprintf(f, "########################################################\n");
6116     fprintf(f, "## E N D\n");
6117     fprintf(f, "########################################################\n");
6118     fprintf(f, "-->\n");
6120     fclose(f);
6122     return true;
6128 /**
6129  *   This loads the dependency cache.
6130  */
6131 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6133     std::vector<DepRec> result;
6134     
6135     Parser parser;
6136     Element *root = parser.parseFile(depFile.c_str());
6137     if (!root)
6138         {
6139         //error("Could not open %s for reading", depFile.c_str());
6140         return result;
6141         }
6143     if (root->getChildren().size()==0 ||
6144         root->getChildren()[0]->getName()!="dependencies")
6145         {
6146         error("loadDepFile: main xml element should be <dependencies>");
6147         delete root;
6148         return result;
6149         }
6151     //########## Start parsing
6152     Element *depList = root->getChildren()[0];
6154     std::vector<Element *> objects = depList->getChildren();
6155     for (unsigned int i=0 ; i<objects.size() ; i++)
6156         {
6157         Element *objectElem = objects[i];
6158         String tagName = objectElem->getName();
6159         if (tagName != "object")
6160             {
6161             error("loadDepFile: <dependencies> should have only <object> children");
6162             return result;
6163             }
6165         String objName   = objectElem->getAttribute("name");
6166          //trace("object:%s", objName.c_str());
6167         DepRec depObject(objName);
6168         depObject.path   = objectElem->getAttribute("path");
6169         depObject.suffix = objectElem->getAttribute("suffix");
6170         //########## DESCRIPTION
6171         std::vector<Element *> depElems = objectElem->getChildren();
6172         for (unsigned int i=0 ; i<depElems.size() ; i++)
6173             {
6174             Element *depElem = depElems[i];
6175             tagName = depElem->getName();
6176             if (tagName != "dep")
6177                 {
6178                 error("loadDepFile: <object> should have only <dep> children");
6179                 return result;
6180                 }
6181             String depName = depElem->getAttribute("name");
6182             //trace("    dep:%s", depName.c_str());
6183             depObject.files.push_back(depName);
6184             }
6186         //Insert into the result list, in a sorted manner
6187         bool inserted = false;
6188         std::vector<DepRec>::iterator iter;
6189         for (iter = result.begin() ; iter != result.end() ; iter++)
6190             {
6191             String vpath = iter->path;
6192             vpath.append("/");
6193             vpath.append(iter->name);
6194             String opath = depObject.path;
6195             opath.append("/");
6196             opath.append(depObject.name);
6197             if (vpath > opath)
6198                 {
6199                 inserted = true;
6200                 iter = result.insert(iter, depObject);
6201                 break;
6202                 }
6203             }
6204         if (!inserted)
6205             result.push_back(depObject);
6206         }
6208     delete root;
6210     return result;
6214 /**
6215  *   This loads the dependency cache.
6216  */
6217 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6218                    bool forceRefresh)
6220     std::vector<DepRec> result;
6221     if (forceRefresh)
6222         {
6223         generateDependencies(depFile);
6224         result = loadDepFile(depFile);
6225         }
6226     else
6227         {
6228         //try once
6229         result = loadDepFile(depFile);
6230         if (result.size() == 0)
6231             {
6232             //fail? try again
6233             generateDependencies(depFile);
6234             result = loadDepFile(depFile);
6235             }
6236         }
6237     return result;
6243 //########################################################################
6244 //# T A S K
6245 //########################################################################
6246 //forward decl
6247 class Target;
6248 class Make;
6250 /**
6251  *
6252  */
6253 class Task : public MakeBase
6256 public:
6258     typedef enum
6259         {
6260         TASK_NONE,
6261         TASK_CC,
6262         TASK_COPY,
6263         TASK_DELETE,
6264         TASK_ECHO,
6265         TASK_JAR,
6266         TASK_JAVAC,
6267         TASK_LINK,
6268         TASK_MAKEFILE,
6269         TASK_MKDIR,
6270         TASK_MSGFMT,
6271         TASK_PKG_CONFIG,
6272         TASK_RANLIB,
6273         TASK_RC,
6274         TASK_SHAREDLIB,
6275         TASK_STATICLIB,
6276         TASK_STRIP,
6277         TASK_TOUCH,
6278         TASK_TSTAMP
6279         } TaskType;
6280         
6282     /**
6283      *
6284      */
6285     Task(MakeBase &par) : parent(par)
6286         { init(); }
6288     /**
6289      *
6290      */
6291     Task(const Task &other) : parent(other.parent)
6292         { init(); assign(other); }
6294     /**
6295      *
6296      */
6297     Task &operator=(const Task &other)
6298         { assign(other); return *this; }
6300     /**
6301      *
6302      */
6303     virtual ~Task()
6304         { }
6307     /**
6308      *
6309      */
6310     virtual MakeBase &getParent()
6311         { return parent; }
6313      /**
6314      *
6315      */
6316     virtual int  getType()
6317         { return type; }
6319     /**
6320      *
6321      */
6322     virtual void setType(int val)
6323         { type = val; }
6325     /**
6326      *
6327      */
6328     virtual String getName()
6329         { return name; }
6331     /**
6332      *
6333      */
6334     virtual bool execute()
6335         { return true; }
6337     /**
6338      *
6339      */
6340     virtual bool parse(Element *elem)
6341         { return true; }
6343     /**
6344      *
6345      */
6346     Task *createTask(Element *elem, int lineNr);
6349 protected:
6351     void init()
6352         {
6353         type = TASK_NONE;
6354         name = "none";
6355         }
6357     void assign(const Task &other)
6358         {
6359         type = other.type;
6360         name = other.name;
6361         }
6362         
6363     /**
6364      *  Show task status
6365      */
6366     void taskstatus(const char *fmt, ...)
6367         {
6368         va_list args;
6369         va_start(args,fmt);
6370         fprintf(stdout, "    %s : ", name.c_str());
6371         vfprintf(stdout, fmt, args);
6372         fprintf(stdout, "\n");
6373         va_end(args) ;
6374         }
6376     String getAttribute(Element *elem, const String &attrName)
6377         {
6378         String str;
6379         return str;
6380         }
6382     MakeBase &parent;
6384     int type;
6386     String name;
6387 };
6391 /**
6392  * This task runs the C/C++ compiler.  The compiler is invoked
6393  * for all .c or .cpp files which are newer than their correcsponding
6394  * .o files.  
6395  */
6396 class TaskCC : public Task
6398 public:
6400     TaskCC(MakeBase &par) : Task(par)
6401         {
6402         type = TASK_CC;
6403         name = "cc";
6404         }
6406     virtual ~TaskCC()
6407         {}
6408         
6409     virtual bool isExcludedInc(const String &dirname)
6410         {
6411         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6412             {
6413             String fname = excludeInc[i];
6414             if (fname == dirname)
6415                 return true;
6416             }
6417         return false;
6418         }
6420     virtual bool execute()
6421         {
6422         //evaluate our parameters
6423         String command         = parent.eval(commandOpt, "gcc");
6424         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6425         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6426         String source          = parent.eval(sourceOpt, ".");
6427         String dest            = parent.eval(destOpt, ".");
6428         String flags           = parent.eval(flagsOpt, "");
6429         String defines         = parent.eval(definesOpt, "");
6430         String includes        = parent.eval(includesOpt, "");
6431         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6432         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6434         if (!listFiles(parent, fileSet))
6435             return false;
6436             
6437         FILE *f = NULL;
6438         f = fopen("compile.lst", "w");
6440         //refreshCache is probably false here, unless specified otherwise
6441         String fullName = parent.resolve("build.dep");
6442         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6443             {
6444             taskstatus("regenerating C/C++ dependency cache");
6445             refreshCache = true;
6446             }
6448         DepTool depTool;
6449         depTool.setSourceDirectory(source);
6450         depTool.setFileList(fileSet.getFiles());
6451         std::vector<DepRec> deps =
6452              depTool.getDepFile("build.dep", refreshCache);
6453         
6454         String incs;
6455         incs.append("-I");
6456         incs.append(parent.resolve("."));
6457         incs.append(" ");
6458         if (includes.size()>0)
6459             {
6460             incs.append(includes);
6461             incs.append(" ");
6462             }
6463         std::set<String> paths;
6464         std::vector<DepRec>::iterator viter;
6465         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6466             {
6467             DepRec dep = *viter;
6468             if (dep.path.size()>0)
6469                 paths.insert(dep.path);
6470             }
6471         if (source.size()>0)
6472             {
6473             incs.append(" -I");
6474             incs.append(parent.resolve(source));
6475             incs.append(" ");
6476             }
6477         std::set<String>::iterator setIter;
6478         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6479             {
6480             String dirName = *setIter;
6481             //check excludeInc to see if we dont want to include this dir
6482             if (isExcludedInc(dirName))
6483                 continue;
6484             incs.append(" -I");
6485             String dname;
6486             if (source.size()>0)
6487                 {
6488                 dname.append(source);
6489                 dname.append("/");
6490                 }
6491             dname.append(dirName);
6492             incs.append(parent.resolve(dname));
6493             }
6494             
6495         /**
6496          * Compile each of the C files that need it
6497          */
6498         bool errorOccurred = false;                 
6499         std::vector<String> cfiles;
6500         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6501             {
6502             DepRec dep = *viter;
6504             //## Select command
6505             String sfx = dep.suffix;
6506             String command = ccCommand;
6507             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6508                  sfx == "cc" || sfx == "CC")
6509                 command = cxxCommand;
6510  
6511             //## Make paths
6512             String destPath = dest;
6513             String srcPath  = source;
6514             if (dep.path.size()>0)
6515                 {
6516                 destPath.append("/");
6517                 destPath.append(dep.path);
6518                 srcPath.append("/");
6519                 srcPath.append(dep.path);
6520                 }
6521             //## Make sure destination directory exists
6522             if (!createDirectory(destPath))
6523                 return false;
6524                 
6525             //## Check whether it needs to be done
6526             String destName;
6527             if (destPath.size()>0)
6528                 {
6529                 destName.append(destPath);
6530                 destName.append("/");
6531                 }
6532             destName.append(dep.name);
6533             destName.append(".o");
6534             String destFullName = parent.resolve(destName);
6535             String srcName;
6536             if (srcPath.size()>0)
6537                 {
6538                 srcName.append(srcPath);
6539                 srcName.append("/");
6540                 }
6541             srcName.append(dep.name);
6542             srcName.append(".");
6543             srcName.append(dep.suffix);
6544             String srcFullName = parent.resolve(srcName);
6545             bool compileMe = false;
6546             //# First we check if the source is newer than the .o
6547             if (isNewerThan(srcFullName, destFullName))
6548                 {
6549                 taskstatus("compile of %s required by source: %s",
6550                         destFullName.c_str(), srcFullName.c_str());
6551                 compileMe = true;
6552                 }
6553             else
6554                 {
6555                 //# secondly, we check if any of the included dependencies
6556                 //# of the .c/.cpp is newer than the .o
6557                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6558                     {
6559                     String depName;
6560                     if (source.size()>0)
6561                         {
6562                         depName.append(source);
6563                         depName.append("/");
6564                         }
6565                     depName.append(dep.files[i]);
6566                     String depFullName = parent.resolve(depName);
6567                     bool depRequires = isNewerThan(depFullName, destFullName);
6568                     //trace("%d %s %s\n", depRequires,
6569                     //        destFullName.c_str(), depFullName.c_str());
6570                     if (depRequires)
6571                         {
6572                         taskstatus("compile of %s required by included: %s",
6573                                 destFullName.c_str(), depFullName.c_str());
6574                         compileMe = true;
6575                         break;
6576                         }
6577                     }
6578                 }
6579             if (!compileMe)
6580                 {
6581                 continue;
6582                 }
6584             //## Assemble the command
6585             String cmd = command;
6586             cmd.append(" -c ");
6587             cmd.append(flags);
6588             cmd.append(" ");
6589             cmd.append(defines);
6590             cmd.append(" ");
6591             cmd.append(incs);
6592             cmd.append(" ");
6593             cmd.append(srcFullName);
6594             cmd.append(" -o ");
6595             cmd.append(destFullName);
6597             //## Execute the command
6599             String outString, errString;
6600             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6602             if (f)
6603                 {
6604                 fprintf(f, "########################### File : %s\n",
6605                              srcFullName.c_str());
6606                 fprintf(f, "#### COMMAND ###\n");
6607                 int col = 0;
6608                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6609                     {
6610                     char ch = cmd[i];
6611                     if (isspace(ch)  && col > 63)
6612                         {
6613                         fputc('\n', f);
6614                         col = 0;
6615                         }
6616                     else
6617                         {
6618                         fputc(ch, f);
6619                         col++;
6620                         }
6621                     if (col > 76)
6622                         {
6623                         fputc('\n', f);
6624                         col = 0;
6625                         }
6626                     }
6627                 fprintf(f, "\n");
6628                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6629                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6630                 fflush(f);
6631                 }
6632             if (!ret)
6633                 {
6634                 error("problem compiling: %s", errString.c_str());
6635                 errorOccurred = true;
6636                 }
6637             if (errorOccurred && !continueOnError)
6638                 break;
6639             }
6641         if (f)
6642             {
6643             fclose(f);
6644             }
6645         
6646         return !errorOccurred;
6647         }
6650     virtual bool parse(Element *elem)
6651         {
6652         String s;
6653         if (!parent.getAttribute(elem, "command", commandOpt))
6654             return false;
6655         if (commandOpt.size()>0)
6656             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6657         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6658             return false;
6659         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6660             return false;
6661         if (!parent.getAttribute(elem, "destdir", destOpt))
6662             return false;
6663         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6664             return false;
6665         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6666             return false;
6668         std::vector<Element *> children = elem->getChildren();
6669         for (unsigned int i=0 ; i<children.size() ; i++)
6670             {
6671             Element *child = children[i];
6672             String tagName = child->getName();
6673             if (tagName == "flags")
6674                 {
6675                 if (!parent.getValue(child, flagsOpt))
6676                     return false;
6677                 flagsOpt = strip(flagsOpt);
6678                 }
6679             else if (tagName == "includes")
6680                 {
6681                 if (!parent.getValue(child, includesOpt))
6682                     return false;
6683                 includesOpt = strip(includesOpt);
6684                 }
6685             else if (tagName == "defines")
6686                 {
6687                 if (!parent.getValue(child, definesOpt))
6688                     return false;
6689                 definesOpt = strip(definesOpt);
6690                 }
6691             else if (tagName == "fileset")
6692                 {
6693                 if (!parseFileSet(child, parent, fileSet))
6694                     return false;
6695                 sourceOpt = fileSet.getDirectory();
6696                 }
6697             else if (tagName == "excludeinc")
6698                 {
6699                 if (!parseFileList(child, parent, excludeInc))
6700                     return false;
6701                 }
6702             }
6704         return true;
6705         }
6706         
6707 protected:
6709     String   commandOpt;
6710     String   ccCommandOpt;
6711     String   cxxCommandOpt;
6712     String   sourceOpt;
6713     String   destOpt;
6714     String   flagsOpt;
6715     String   definesOpt;
6716     String   includesOpt;
6717     String   continueOnErrorOpt;
6718     String   refreshCacheOpt;
6719     FileSet  fileSet;
6720     FileList excludeInc;
6721     
6722 };
6726 /**
6727  *
6728  */
6729 class TaskCopy : public Task
6731 public:
6733     typedef enum
6734         {
6735         CP_NONE,
6736         CP_TOFILE,
6737         CP_TODIR
6738         } CopyType;
6740     TaskCopy(MakeBase &par) : Task(par)
6741         {
6742         type        = TASK_COPY;
6743         name        = "copy";
6744         cptype      = CP_NONE;
6745         haveFileSet = false;
6746         }
6748     virtual ~TaskCopy()
6749         {}
6751     virtual bool execute()
6752         {
6753         String fileName   = parent.eval(fileNameOpt   , ".");
6754         String toFileName = parent.eval(toFileNameOpt , ".");
6755         String toDirName  = parent.eval(toDirNameOpt  , ".");
6756         bool   verbose    = parent.evalBool(verboseOpt, false);
6757         switch (cptype)
6758            {
6759            case CP_TOFILE:
6760                {
6761                if (fileName.size()>0)
6762                    {
6763                    taskstatus("%s to %s",
6764                         fileName.c_str(), toFileName.c_str());
6765                    String fullSource = parent.resolve(fileName);
6766                    String fullDest = parent.resolve(toFileName);
6767                    if (verbose)
6768                        taskstatus("copy %s to file %s", fullSource.c_str(),
6769                                           fullDest.c_str());
6770                    if (!isRegularFile(fullSource))
6771                        {
6772                        error("copy : file %s does not exist", fullSource.c_str());
6773                        return false;
6774                        }
6775                    if (!isNewerThan(fullSource, fullDest))
6776                        {
6777                        taskstatus("skipped");
6778                        return true;
6779                        }
6780                    if (!copyFile(fullSource, fullDest))
6781                        return false;
6782                    taskstatus("1 file copied");
6783                    }
6784                return true;
6785                }
6786            case CP_TODIR:
6787                {
6788                if (haveFileSet)
6789                    {
6790                    if (!listFiles(parent, fileSet))
6791                        return false;
6792                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6794                    taskstatus("%s to %s",
6795                        fileSetDir.c_str(), toDirName.c_str());
6797                    int nrFiles = 0;
6798                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6799                        {
6800                        String fileName = fileSet[i];
6802                        String sourcePath;
6803                        if (fileSetDir.size()>0)
6804                            {
6805                            sourcePath.append(fileSetDir);
6806                            sourcePath.append("/");
6807                            }
6808                        sourcePath.append(fileName);
6809                        String fullSource = parent.resolve(sourcePath);
6810                        
6811                        //Get the immediate parent directory's base name
6812                        String baseFileSetDir = fileSetDir;
6813                        unsigned int pos = baseFileSetDir.find_last_of('/');
6814                        if (pos!=baseFileSetDir.npos &&
6815                                   pos < baseFileSetDir.size()-1)
6816                            baseFileSetDir =
6817                               baseFileSetDir.substr(pos+1,
6818                                    baseFileSetDir.size());
6819                        //Now make the new path
6820                        String destPath;
6821                        if (toDirName.size()>0)
6822                            {
6823                            destPath.append(toDirName);
6824                            destPath.append("/");
6825                            }
6826                        if (baseFileSetDir.size()>0)
6827                            {
6828                            destPath.append(baseFileSetDir);
6829                            destPath.append("/");
6830                            }
6831                        destPath.append(fileName);
6832                        String fullDest = parent.resolve(destPath);
6833                        //trace("fileName:%s", fileName.c_str());
6834                        if (verbose)
6835                            taskstatus("copy %s to new dir : %s",
6836                                  fullSource.c_str(), fullDest.c_str());
6837                        if (!isNewerThan(fullSource, fullDest))
6838                            {
6839                            if (verbose)
6840                                taskstatus("copy skipping %s", fullSource.c_str());
6841                            continue;
6842                            }
6843                        if (!copyFile(fullSource, fullDest))
6844                            return false;
6845                        nrFiles++;
6846                        }
6847                    taskstatus("%d file(s) copied", nrFiles);
6848                    }
6849                else //file source
6850                    {
6851                    //For file->dir we want only the basename of
6852                    //the source appended to the dest dir
6853                    taskstatus("%s to %s", 
6854                        fileName.c_str(), toDirName.c_str());
6855                    String baseName = fileName;
6856                    unsigned int pos = baseName.find_last_of('/');
6857                    if (pos!=baseName.npos && pos<baseName.size()-1)
6858                        baseName = baseName.substr(pos+1, baseName.size());
6859                    String fullSource = parent.resolve(fileName);
6860                    String destPath;
6861                    if (toDirName.size()>0)
6862                        {
6863                        destPath.append(toDirName);
6864                        destPath.append("/");
6865                        }
6866                    destPath.append(baseName);
6867                    String fullDest = parent.resolve(destPath);
6868                    if (verbose)
6869                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6870                                           fullDest.c_str());
6871                    if (!isRegularFile(fullSource))
6872                        {
6873                        error("copy : file %s does not exist", fullSource.c_str());
6874                        return false;
6875                        }
6876                    if (!isNewerThan(fullSource, fullDest))
6877                        {
6878                        taskstatus("skipped");
6879                        return true;
6880                        }
6881                    if (!copyFile(fullSource, fullDest))
6882                        return false;
6883                    taskstatus("1 file copied");
6884                    }
6885                return true;
6886                }
6887            }
6888         return true;
6889         }
6892     virtual bool parse(Element *elem)
6893         {
6894         if (!parent.getAttribute(elem, "file", fileNameOpt))
6895             return false;
6896         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6897             return false;
6898         if (toFileNameOpt.size() > 0)
6899             cptype = CP_TOFILE;
6900         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6901             return false;
6902         if (toDirNameOpt.size() > 0)
6903             cptype = CP_TODIR;
6904         if (!parent.getAttribute(elem, "verbose", verboseOpt))
6905             return false;
6906             
6907         haveFileSet = false;
6908         
6909         std::vector<Element *> children = elem->getChildren();
6910         for (unsigned int i=0 ; i<children.size() ; i++)
6911             {
6912             Element *child = children[i];
6913             String tagName = child->getName();
6914             if (tagName == "fileset")
6915                 {
6916                 if (!parseFileSet(child, parent, fileSet))
6917                     {
6918                     error("problem getting fileset");
6919                     return false;
6920                     }
6921                 haveFileSet = true;
6922                 }
6923             }
6925         //Perform validity checks
6926         if (fileNameOpt.size()>0 && fileSet.size()>0)
6927             {
6928             error("<copy> can only have one of : file= and <fileset>");
6929             return false;
6930             }
6931         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6932             {
6933             error("<copy> can only have one of : tofile= or todir=");
6934             return false;
6935             }
6936         if (haveFileSet && toDirNameOpt.size()==0)
6937             {
6938             error("a <copy> task with a <fileset> must have : todir=");
6939             return false;
6940             }
6941         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6942             {
6943             error("<copy> tofile= must be associated with : file=");
6944             return false;
6945             }
6946         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6947             {
6948             error("<copy> todir= must be associated with : file= or <fileset>");
6949             return false;
6950             }
6952         return true;
6953         }
6954         
6955 private:
6957     int cptype;
6958     bool haveFileSet;
6960     FileSet fileSet;
6961     String  fileNameOpt;
6962     String  toFileNameOpt;
6963     String  toDirNameOpt;
6964     String  verboseOpt;
6965 };
6968 /**
6969  *
6970  */
6971 class TaskDelete : public Task
6973 public:
6975     typedef enum
6976         {
6977         DEL_FILE,
6978         DEL_DIR,
6979         DEL_FILESET
6980         } DeleteType;
6982     TaskDelete(MakeBase &par) : Task(par)
6983         { 
6984         type        = TASK_DELETE;
6985         name        = "delete";
6986         delType     = DEL_FILE;
6987         }
6989     virtual ~TaskDelete()
6990         {}
6992     virtual bool execute()
6993         {
6994         String dirName   = parent.eval(dirNameOpt, ".");
6995         String fileName  = parent.eval(fileNameOpt, ".");
6996         bool verbose     = parent.evalBool(verboseOpt, false);
6997         bool quiet       = parent.evalBool(quietOpt, false);
6998         bool failOnError = parent.evalBool(failOnErrorOpt, true);
6999         struct stat finfo;
7000         switch (delType)
7001             {
7002             case DEL_FILE:
7003                 {
7004                 taskstatus("file: %s", fileName.c_str());
7005                 String fullName = parent.resolve(fileName);
7006                 char *fname = (char *)fullName.c_str();
7007                 if (!quiet && verbose)
7008                     taskstatus("path: %s", fname);
7009                 //does not exist
7010                 if (stat(fname, &finfo)<0)
7011                     {
7012                     if (failOnError)
7013                         return false;
7014                     else
7015                         return true;
7016                     }
7017                 //exists but is not a regular file
7018                 if (!S_ISREG(finfo.st_mode))
7019                     {
7020                     error("<delete> failed. '%s' exists and is not a regular file",
7021                           fname);
7022                     return false;
7023                     }
7024                 if (remove(fname)<0)
7025                     {
7026                     error("<delete> failed: %s", strerror(errno));
7027                     return false;
7028                     }
7029                 return true;
7030                 }
7031             case DEL_DIR:
7032                 {
7033                 taskstatus("dir: %s", dirName.c_str());
7034                 String fullDir = parent.resolve(dirName);
7035                 if (!quiet && verbose)
7036                     taskstatus("path: %s", fullDir.c_str());
7037                 if (!removeDirectory(fullDir))
7038                     return false;
7039                 return true;
7040                 }
7041             }
7042         return true;
7043         }
7045     virtual bool parse(Element *elem)
7046         {
7047         if (!parent.getAttribute(elem, "file", fileNameOpt))
7048             return false;
7049         if (fileNameOpt.size() > 0)
7050             delType = DEL_FILE;
7051         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7052             return false;
7053         if (dirNameOpt.size() > 0)
7054             delType = DEL_DIR;
7055         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7056             {
7057             error("<delete> can have one attribute of file= or dir=");
7058             return false;
7059             }
7060         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7061             {
7062             error("<delete> must have one attribute of file= or dir=");
7063             return false;
7064             }
7065         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7066             return false;
7067         if (!parent.getAttribute(elem, "quiet", quietOpt))
7068             return false;
7069         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7070             return false;
7071         return true;
7072         }
7074 private:
7076     int delType;
7077     String dirNameOpt;
7078     String fileNameOpt;
7079     String verboseOpt;
7080     String quietOpt;
7081     String failOnErrorOpt;
7082 };
7085 /**
7086  * Send a message to stdout
7087  */
7088 class TaskEcho : public Task
7090 public:
7092     TaskEcho(MakeBase &par) : Task(par)
7093         { type = TASK_ECHO; name = "echo"; }
7095     virtual ~TaskEcho()
7096         {}
7098     virtual bool execute()
7099         {
7100         //let message have priority over text
7101         String message = parent.eval(messageOpt, "");
7102         String text    = parent.eval(textOpt, "");
7103         if (message.size() > 0)
7104             {
7105             fprintf(stdout, "%s\n", message.c_str());
7106             }
7107         else if (text.size() > 0)
7108             {
7109             fprintf(stdout, "%s\n", text.c_str());
7110             }
7111         return true;
7112         }
7114     virtual bool parse(Element *elem)
7115         {
7116         if (!parent.getValue(elem, textOpt))
7117             return false;
7118         textOpt    = leftJustify(textOpt);
7119         if (!parent.getAttribute(elem, "message", messageOpt))
7120             return false;
7121         return true;
7122         }
7124 private:
7126     String messageOpt;
7127     String textOpt;
7128 };
7132 /**
7133  *
7134  */
7135 class TaskJar : public Task
7137 public:
7139     TaskJar(MakeBase &par) : Task(par)
7140         { type = TASK_JAR; name = "jar"; }
7142     virtual ~TaskJar()
7143         {}
7145     virtual bool execute()
7146         {
7147         String command  = parent.eval(commandOpt, "jar");
7148         String basedir  = parent.eval(basedirOpt, ".");
7149         String destfile = parent.eval(destfileOpt, ".");
7151         String cmd = command;
7152         cmd.append(" -cf ");
7153         cmd.append(destfile);
7154         cmd.append(" -C ");
7155         cmd.append(basedir);
7156         cmd.append(" .");
7158         String execCmd = cmd;
7160         String outString, errString;
7161         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7162         if (!ret)
7163             {
7164             error("<jar> command '%s' failed :\n %s",
7165                                       execCmd.c_str(), errString.c_str());
7166             return false;
7167             }
7168         return true;
7169         }
7171     virtual bool parse(Element *elem)
7172         {
7173         if (!parent.getAttribute(elem, "command", commandOpt))
7174             return false;
7175         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7176             return false;
7177         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7178             return false;
7179         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7180             {
7181             error("<jar> required both basedir and destfile attributes to be set");
7182             return false;
7183             }
7184         return true;
7185         }
7187 private:
7189     String commandOpt;
7190     String basedirOpt;
7191     String destfileOpt;
7192 };
7195 /**
7196  *
7197  */
7198 class TaskJavac : public Task
7200 public:
7202     TaskJavac(MakeBase &par) : Task(par)
7203         { 
7204         type = TASK_JAVAC; name = "javac";
7205         }
7207     virtual ~TaskJavac()
7208         {}
7210     virtual bool execute()
7211         {
7212         String command  = parent.eval(commandOpt, "javac");
7213         String srcdir   = parent.eval(srcdirOpt, ".");
7214         String destdir  = parent.eval(destdirOpt, ".");
7215         String target   = parent.eval(targetOpt, "");
7217         std::vector<String> fileList;
7218         if (!listFiles(srcdir, "", fileList))
7219             {
7220             return false;
7221             }
7222         String cmd = command;
7223         cmd.append(" -d ");
7224         cmd.append(destdir);
7225         cmd.append(" -classpath ");
7226         cmd.append(destdir);
7227         cmd.append(" -sourcepath ");
7228         cmd.append(srcdir);
7229         cmd.append(" ");
7230         if (target.size()>0)
7231             {
7232             cmd.append(" -target ");
7233             cmd.append(target);
7234             cmd.append(" ");
7235             }
7236         String fname = "javalist.btool";
7237         FILE *f = fopen(fname.c_str(), "w");
7238         int count = 0;
7239         for (unsigned int i=0 ; i<fileList.size() ; i++)
7240             {
7241             String fname = fileList[i];
7242             String srcName = fname;
7243             if (fname.size()<6) //x.java
7244                 continue;
7245             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7246                 continue;
7247             String baseName = fname.substr(0, fname.size()-5);
7248             String destName = baseName;
7249             destName.append(".class");
7251             String fullSrc = srcdir;
7252             fullSrc.append("/");
7253             fullSrc.append(fname);
7254             String fullDest = destdir;
7255             fullDest.append("/");
7256             fullDest.append(destName);
7257             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7258             if (!isNewerThan(fullSrc, fullDest))
7259                 continue;
7261             count++;
7262             fprintf(f, "%s\n", fullSrc.c_str());
7263             }
7264         fclose(f);
7265         if (!count)
7266             {
7267             taskstatus("nothing to do");
7268             return true;
7269             }
7271         taskstatus("compiling %d files", count);
7273         String execCmd = cmd;
7274         execCmd.append("@");
7275         execCmd.append(fname);
7277         String outString, errString;
7278         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7279         if (!ret)
7280             {
7281             error("<javac> command '%s' failed :\n %s",
7282                                       execCmd.c_str(), errString.c_str());
7283             return false;
7284             }
7285         return true;
7286         }
7288     virtual bool parse(Element *elem)
7289         {
7290         if (!parent.getAttribute(elem, "command", commandOpt))
7291             return false;
7292         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7293             return false;
7294         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7295             return false;
7296         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7297             {
7298             error("<javac> required both srcdir and destdir attributes to be set");
7299             return false;
7300             }
7301         if (!parent.getAttribute(elem, "target", targetOpt))
7302             return false;
7303         return true;
7304         }
7306 private:
7308     String commandOpt;
7309     String srcdirOpt;
7310     String destdirOpt;
7311     String targetOpt;
7313 };
7316 /**
7317  *
7318  */
7319 class TaskLink : public Task
7321 public:
7323     TaskLink(MakeBase &par) : Task(par)
7324         {
7325         type = TASK_LINK; name = "link";
7326         }
7328     virtual ~TaskLink()
7329         {}
7331     virtual bool execute()
7332         {
7333         String  command        = parent.eval(commandOpt, "g++");
7334         String  fileName       = parent.eval(fileNameOpt, "");
7335         String  flags          = parent.eval(flagsOpt, "");
7336         String  libs           = parent.eval(libsOpt, "");
7337         bool    doStrip        = parent.evalBool(doStripOpt, false);
7338         String  symFileName    = parent.eval(symFileNameOpt, "");
7339         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7340         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7342         if (!listFiles(parent, fileSet))
7343             return false;
7344         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7345         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7346         bool doit = false;
7347         String fullTarget = parent.resolve(fileName);
7348         String cmd = command;
7349         cmd.append(" -o ");
7350         cmd.append(fullTarget);
7351         cmd.append(" ");
7352         cmd.append(flags);
7353         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7354             {
7355             cmd.append(" ");
7356             String obj;
7357             if (fileSetDir.size()>0)
7358                 {
7359                 obj.append(fileSetDir);
7360                 obj.append("/");
7361                 }
7362             obj.append(fileSet[i]);
7363             String fullObj = parent.resolve(obj);
7364             String nativeFullObj = getNativePath(fullObj);
7365             cmd.append(nativeFullObj);
7366             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7367             //          fullObj.c_str());
7368             if (isNewerThan(fullObj, fullTarget))
7369                 doit = true;
7370             }
7371         cmd.append(" ");
7372         cmd.append(libs);
7373         if (!doit)
7374             {
7375             //trace("link not needed");
7376             return true;
7377             }
7378         //trace("LINK cmd:%s", cmd.c_str());
7381         String outbuf, errbuf;
7382         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7383             {
7384             error("LINK problem: %s", errbuf.c_str());
7385             return false;
7386             }
7388         if (symFileName.size()>0)
7389             {
7390             String symFullName = parent.resolve(symFileName);
7391             cmd = objcopyCommand;
7392             cmd.append(" --only-keep-debug ");
7393             cmd.append(getNativePath(fullTarget));
7394             cmd.append(" ");
7395             cmd.append(getNativePath(symFullName));
7396             if (!executeCommand(cmd, "", outbuf, errbuf))
7397                 {
7398                 error("<strip> symbol file failed : %s", errbuf.c_str());
7399                 return false;
7400                 }
7401             }
7402             
7403         if (doStrip)
7404             {
7405             cmd = stripCommand;
7406             cmd.append(" ");
7407             cmd.append(getNativePath(fullTarget));
7408             if (!executeCommand(cmd, "", outbuf, errbuf))
7409                {
7410                error("<strip> failed : %s", errbuf.c_str());
7411                return false;
7412                }
7413             }
7415         return true;
7416         }
7418     virtual bool parse(Element *elem)
7419         {
7420         if (!parent.getAttribute(elem, "command", commandOpt))
7421             return false;
7422         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7423             return false;
7424         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7425             return false;
7426         if (!parent.getAttribute(elem, "out", fileNameOpt))
7427             return false;
7428         if (!parent.getAttribute(elem, "strip", doStripOpt))
7429             return false;
7430         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7431             return false;
7432             
7433         std::vector<Element *> children = elem->getChildren();
7434         for (unsigned int i=0 ; i<children.size() ; i++)
7435             {
7436             Element *child = children[i];
7437             String tagName = child->getName();
7438             if (tagName == "fileset")
7439                 {
7440                 if (!parseFileSet(child, parent, fileSet))
7441                     return false;
7442                 }
7443             else if (tagName == "flags")
7444                 {
7445                 if (!parent.getValue(child, flagsOpt))
7446                     return false;
7447                 flagsOpt = strip(flagsOpt);
7448                 }
7449             else if (tagName == "libs")
7450                 {
7451                 if (!parent.getValue(child, libsOpt))
7452                     return false;
7453                 libsOpt = strip(libsOpt);
7454                 }
7455             }
7456         return true;
7457         }
7459 private:
7461     FileSet fileSet;
7463     String  commandOpt;
7464     String  fileNameOpt;
7465     String  flagsOpt;
7466     String  libsOpt;
7467     String  doStripOpt;
7468     String  symFileNameOpt;
7469     String  stripCommandOpt;
7470     String  objcopyCommandOpt;
7472 };
7476 /**
7477  * Create a named file
7478  */
7479 class TaskMakeFile : public Task
7481 public:
7483     TaskMakeFile(MakeBase &par) : Task(par)
7484         { type = TASK_MAKEFILE; name = "makefile"; }
7486     virtual ~TaskMakeFile()
7487         {}
7489     virtual bool execute()
7490         {
7491         String fileName = parent.eval(fileNameOpt, "");
7492         String text     = parent.eval(textOpt, "");
7494         taskstatus("%s", fileName.c_str());
7495         String fullName = parent.resolve(fileName);
7496         if (!isNewerThan(parent.getURI().getPath(), fullName))
7497             {
7498             //trace("skipped <makefile>");
7499             return true;
7500             }
7501         String fullNative = getNativePath(fullName);
7502         //trace("fullName:%s", fullName.c_str());
7503         FILE *f = fopen(fullNative.c_str(), "w");
7504         if (!f)
7505             {
7506             error("<makefile> could not open %s for writing : %s",
7507                 fullName.c_str(), strerror(errno));
7508             return false;
7509             }
7510         for (unsigned int i=0 ; i<text.size() ; i++)
7511             fputc(text[i], f);
7512         fputc('\n', f);
7513         fclose(f);
7514         return true;
7515         }
7517     virtual bool parse(Element *elem)
7518         {
7519         if (!parent.getAttribute(elem, "file", fileNameOpt))
7520             return false;
7521         if (fileNameOpt.size() == 0)
7522             {
7523             error("<makefile> requires 'file=\"filename\"' attribute");
7524             return false;
7525             }
7526         if (!parent.getValue(elem, textOpt))
7527             return false;
7528         textOpt = leftJustify(textOpt);
7529         //trace("dirname:%s", dirName.c_str());
7530         return true;
7531         }
7533 private:
7535     String fileNameOpt;
7536     String textOpt;
7537 };
7541 /**
7542  * Create a named directory
7543  */
7544 class TaskMkDir : public Task
7546 public:
7548     TaskMkDir(MakeBase &par) : Task(par)
7549         { type = TASK_MKDIR; name = "mkdir"; }
7551     virtual ~TaskMkDir()
7552         {}
7554     virtual bool execute()
7555         {
7556         String dirName = parent.eval(dirNameOpt, ".");
7557         
7558         taskstatus("%s", dirName.c_str());
7559         String fullDir = parent.resolve(dirName);
7560         //trace("fullDir:%s", fullDir.c_str());
7561         if (!createDirectory(fullDir))
7562             return false;
7563         return true;
7564         }
7566     virtual bool parse(Element *elem)
7567         {
7568         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7569             return false;
7570         if (dirNameOpt.size() == 0)
7571             {
7572             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7573             return false;
7574             }
7575         return true;
7576         }
7578 private:
7580     String dirNameOpt;
7581 };
7585 /**
7586  * Create a named directory
7587  */
7588 class TaskMsgFmt: public Task
7590 public:
7592     TaskMsgFmt(MakeBase &par) : Task(par)
7593          { type = TASK_MSGFMT;  name = "msgfmt"; }
7595     virtual ~TaskMsgFmt()
7596         {}
7598     virtual bool execute()
7599         {
7600         String  command   = parent.eval(commandOpt, "msgfmt");
7601         String  toDirName = parent.eval(toDirNameOpt, ".");
7602         String  outName   = parent.eval(outNameOpt, "");
7603         bool    owndir    = parent.evalBool(owndirOpt, false);
7605         if (!listFiles(parent, fileSet))
7606             return false;
7607         String fileSetDir = fileSet.getDirectory();
7609         //trace("msgfmt: %d", fileSet.size());
7610         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7611             {
7612             String fileName = fileSet[i];
7613             if (getSuffix(fileName) != "po")
7614                 continue;
7615             String sourcePath;
7616             if (fileSetDir.size()>0)
7617                 {
7618                 sourcePath.append(fileSetDir);
7619                 sourcePath.append("/");
7620                 }
7621             sourcePath.append(fileName);
7622             String fullSource = parent.resolve(sourcePath);
7624             String destPath;
7625             if (toDirName.size()>0)
7626                 {
7627                 destPath.append(toDirName);
7628                 destPath.append("/");
7629                 }
7630             if (owndir)
7631                 {
7632                 String subdir = fileName;
7633                 unsigned int pos = subdir.find_last_of('.');
7634                 if (pos != subdir.npos)
7635                     subdir = subdir.substr(0, pos);
7636                 destPath.append(subdir);
7637                 destPath.append("/");
7638                 }
7639             //Pick the output file name
7640             if (outName.size() > 0)
7641                 {
7642                 destPath.append(outName);
7643                 }
7644             else
7645                 {
7646                 destPath.append(fileName);
7647                 destPath[destPath.size()-2] = 'm';
7648                 }
7650             String fullDest = parent.resolve(destPath);
7652             if (!isNewerThan(fullSource, fullDest))
7653                 {
7654                 //trace("skip %s", fullSource.c_str());
7655                 continue;
7656                 }
7657                 
7658             String cmd = command;
7659             cmd.append(" ");
7660             cmd.append(fullSource);
7661             cmd.append(" -o ");
7662             cmd.append(fullDest);
7663             
7664             int pos = fullDest.find_last_of('/');
7665             if (pos>0)
7666                 {
7667                 String fullDestPath = fullDest.substr(0, pos);
7668                 if (!createDirectory(fullDestPath))
7669                     return false;
7670                 }
7674             String outString, errString;
7675             if (!executeCommand(cmd.c_str(), "", outString, errString))
7676                 {
7677                 error("<msgfmt> problem: %s", errString.c_str());
7678                 return false;
7679                 }
7680             }
7682         return true;
7683         }
7685     virtual bool parse(Element *elem)
7686         {
7687         if (!parent.getAttribute(elem, "command", commandOpt))
7688             return false;
7689         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7690             return false;
7691         if (!parent.getAttribute(elem, "out", outNameOpt))
7692             return false;
7693         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7694             return false;
7695             
7696         std::vector<Element *> children = elem->getChildren();
7697         for (unsigned int i=0 ; i<children.size() ; i++)
7698             {
7699             Element *child = children[i];
7700             String tagName = child->getName();
7701             if (tagName == "fileset")
7702                 {
7703                 if (!parseFileSet(child, parent, fileSet))
7704                     return false;
7705                 }
7706             }
7707         return true;
7708         }
7710 private:
7712     FileSet fileSet;
7714     String  commandOpt;
7715     String  toDirNameOpt;
7716     String  outNameOpt;
7717     String  owndirOpt;
7719 };
7723 /**
7724  *  Perform a Package-Config query similar to pkg-config
7725  */
7726 class TaskPkgConfig : public Task
7728 public:
7730     typedef enum
7731         {
7732         PKG_CONFIG_QUERY_CFLAGS,
7733         PKG_CONFIG_QUERY_LIBS,
7734         PKG_CONFIG_QUERY_ALL
7735         } QueryTypes;
7737     TaskPkgConfig(MakeBase &par) : Task(par)
7738         {
7739         type = TASK_PKG_CONFIG;
7740         name = "pkg-config";
7741         }
7743     virtual ~TaskPkgConfig()
7744         {}
7746     virtual bool execute()
7747         {
7748         String pkgName       = parent.eval(pkgNameOpt,      "");
7749         String prefix        = parent.eval(prefixOpt,       "");
7750         String propName      = parent.eval(propNameOpt,     "");
7751         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7752         String query         = parent.eval(queryOpt,        "all");
7754         String path = parent.resolve(pkgConfigPath);
7755         PkgConfig pkgconfig;
7756         pkgconfig.setPath(path);
7757         pkgconfig.setPrefix(prefix);
7758         if (!pkgconfig.query(pkgName))
7759             {
7760             error("<pkg-config> query failed for '%s", name.c_str());
7761             return false;
7762             }
7763             
7764         String val = "";
7765         if (query == "cflags")
7766             val = pkgconfig.getCflags();
7767         else if (query == "libs")
7768             val =pkgconfig.getLibs();
7769         else if (query == "all")
7770             val = pkgconfig.getAll();
7771         else
7772             {
7773             error("<pkg-config> unhandled query : %s", query.c_str());
7774             return false;
7775             }
7776         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7777         parent.setProperty(propName, val);
7778         return true;
7779         }
7781     virtual bool parse(Element *elem)
7782         {
7783         //# NAME
7784         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7785             return false;
7786         if (pkgNameOpt.size()==0)
7787             {
7788             error("<pkg-config> requires 'name=\"package\"' attribute");
7789             return false;
7790             }
7792         //# PROPERTY
7793         if (!parent.getAttribute(elem, "property", propNameOpt))
7794             return false;
7795         if (propNameOpt.size()==0)
7796             {
7797             error("<pkg-config> requires 'property=\"name\"' attribute");
7798             return false;
7799             }
7800         //# PATH
7801         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7802             return false;
7803         //# PREFIX
7804         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7805             return false;
7806         //# QUERY
7807         if (!parent.getAttribute(elem, "query", queryOpt))
7808             return false;
7810         return true;
7811         }
7813 private:
7815     String queryOpt;
7816     String pkgNameOpt;
7817     String prefixOpt;
7818     String propNameOpt;
7819     String pkgConfigPathOpt;
7821 };
7828 /**
7829  *  Process an archive to allow random access
7830  */
7831 class TaskRanlib : public Task
7833 public:
7835     TaskRanlib(MakeBase &par) : Task(par)
7836         { type = TASK_RANLIB; name = "ranlib"; }
7838     virtual ~TaskRanlib()
7839         {}
7841     virtual bool execute()
7842         {
7843         String fileName = parent.eval(fileNameOpt, "");
7844         String command  = parent.eval(commandOpt, "ranlib");
7846         String fullName = parent.resolve(fileName);
7847         //trace("fullDir:%s", fullDir.c_str());
7848         String cmd = command;
7849         cmd.append(" ");
7850         cmd.append(fullName);
7851         String outbuf, errbuf;
7852         if (!executeCommand(cmd, "", outbuf, errbuf))
7853             return false;
7854         return true;
7855         }
7857     virtual bool parse(Element *elem)
7858         {
7859         if (!parent.getAttribute(elem, "command", commandOpt))
7860             return false;
7861         if (!parent.getAttribute(elem, "file", fileNameOpt))
7862             return false;
7863         if (fileNameOpt.size() == 0)
7864             {
7865             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7866             return false;
7867             }
7868         return true;
7869         }
7871 private:
7873     String fileNameOpt;
7874     String commandOpt;
7875 };
7879 /**
7880  * Compile a resource file into a binary object
7881  */
7882 class TaskRC : public Task
7884 public:
7886     TaskRC(MakeBase &par) : Task(par)
7887         { type = TASK_RC; name = "rc"; }
7889     virtual ~TaskRC()
7890         {}
7892     virtual bool execute()
7893         {
7894         String command  = parent.eval(commandOpt,  "windres");
7895         String flags    = parent.eval(flagsOpt,    "");
7896         String fileName = parent.eval(fileNameOpt, "");
7897         String outName  = parent.eval(outNameOpt,  "");
7899         String fullFile = parent.resolve(fileName);
7900         String fullOut  = parent.resolve(outName);
7901         if (!isNewerThan(fullFile, fullOut))
7902             return true;
7903         String cmd = command;
7904         cmd.append(" -o ");
7905         cmd.append(fullOut);
7906         cmd.append(" ");
7907         cmd.append(flags);
7908         cmd.append(" ");
7909         cmd.append(fullFile);
7911         String outString, errString;
7912         if (!executeCommand(cmd.c_str(), "", outString, errString))
7913             {
7914             error("RC problem: %s", errString.c_str());
7915             return false;
7916             }
7917         return true;
7918         }
7920     virtual bool parse(Element *elem)
7921         {
7922         if (!parent.getAttribute(elem, "command", commandOpt))
7923             return false;
7924         if (!parent.getAttribute(elem, "file", fileNameOpt))
7925             return false;
7926         if (!parent.getAttribute(elem, "out", outNameOpt))
7927             return false;
7928         std::vector<Element *> children = elem->getChildren();
7929         for (unsigned int i=0 ; i<children.size() ; i++)
7930             {
7931             Element *child = children[i];
7932             String tagName = child->getName();
7933             if (tagName == "flags")
7934                 {
7935                 if (!parent.getValue(child, flagsOpt))
7936                     return false;
7937                 }
7938             }
7939         return true;
7940         }
7942 private:
7944     String commandOpt;
7945     String flagsOpt;
7946     String fileNameOpt;
7947     String outNameOpt;
7949 };
7953 /**
7954  *  Collect .o's into a .so or DLL
7955  */
7956 class TaskSharedLib : public Task
7958 public:
7960     TaskSharedLib(MakeBase &par) : Task(par)
7961         { type = TASK_SHAREDLIB; name = "dll"; }
7963     virtual ~TaskSharedLib()
7964         {}
7966     virtual bool execute()
7967         {
7968         String command     = parent.eval(commandOpt, "dllwrap");
7969         String fileName    = parent.eval(fileNameOpt, "");
7970         String defFileName = parent.eval(defFileNameOpt, "");
7971         String impFileName = parent.eval(impFileNameOpt, "");
7972         String libs        = parent.eval(libsOpt, "");
7974         //trace("###########HERE %d", fileSet.size());
7975         bool doit = false;
7976         
7977         String fullOut = parent.resolve(fileName);
7978         //trace("ar fullout: %s", fullOut.c_str());
7979         
7980         if (!listFiles(parent, fileSet))
7981             return false;
7982         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7984         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7985             {
7986             String fname;
7987             if (fileSetDir.size()>0)
7988                 {
7989                 fname.append(fileSetDir);
7990                 fname.append("/");
7991                 }
7992             fname.append(fileSet[i]);
7993             String fullName = parent.resolve(fname);
7994             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7995             if (isNewerThan(fullName, fullOut))
7996                 doit = true;
7997             }
7998         //trace("Needs it:%d", doit);
7999         if (!doit)
8000             {
8001             return true;
8002             }
8004         String cmd = "dllwrap";
8005         cmd.append(" -o ");
8006         cmd.append(fullOut);
8007         if (defFileName.size()>0)
8008             {
8009             cmd.append(" --def ");
8010             cmd.append(defFileName);
8011             cmd.append(" ");
8012             }
8013         if (impFileName.size()>0)
8014             {
8015             cmd.append(" --implib ");
8016             cmd.append(impFileName);
8017             cmd.append(" ");
8018             }
8019         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8020             {
8021             String fname;
8022             if (fileSetDir.size()>0)
8023                 {
8024                 fname.append(fileSetDir);
8025                 fname.append("/");
8026                 }
8027             fname.append(fileSet[i]);
8028             String fullName = parent.resolve(fname);
8030             cmd.append(" ");
8031             cmd.append(fullName);
8032             }
8033         cmd.append(" ");
8034         cmd.append(libs);
8036         String outString, errString;
8037         if (!executeCommand(cmd.c_str(), "", outString, errString))
8038             {
8039             error("<sharedlib> problem: %s", errString.c_str());
8040             return false;
8041             }
8043         return true;
8044         }
8046     virtual bool parse(Element *elem)
8047         {
8048         if (!parent.getAttribute(elem, "command", commandOpt))
8049             return false;
8050         if (!parent.getAttribute(elem, "file", fileNameOpt))
8051             return false;
8052         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8053             return false;
8054         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8055             return false;
8056             
8057         std::vector<Element *> children = elem->getChildren();
8058         for (unsigned int i=0 ; i<children.size() ; i++)
8059             {
8060             Element *child = children[i];
8061             String tagName = child->getName();
8062             if (tagName == "fileset")
8063                 {
8064                 if (!parseFileSet(child, parent, fileSet))
8065                     return false;
8066                 }
8067             else if (tagName == "libs")
8068                 {
8069                 if (!parent.getValue(child, libsOpt))
8070                     return false;
8071                 libsOpt = strip(libsOpt);
8072                 }
8073             }
8074         return true;
8075         }
8077 private:
8079     FileSet fileSet;
8081     String commandOpt;
8082     String fileNameOpt;
8083     String defFileNameOpt;
8084     String impFileNameOpt;
8085     String libsOpt;
8087 };
8091 /**
8092  * Run the "ar" command to archive .o's into a .a
8093  */
8094 class TaskStaticLib : public Task
8096 public:
8098     TaskStaticLib(MakeBase &par) : Task(par)
8099         { type = TASK_STATICLIB; name = "staticlib"; }
8101     virtual ~TaskStaticLib()
8102         {}
8104     virtual bool execute()
8105         {
8106         String command = parent.eval(commandOpt, "ar crv");
8107         String fileName = parent.eval(fileNameOpt, "");
8109         bool doit = false;
8110         
8111         String fullOut = parent.resolve(fileName);
8112         //trace("ar fullout: %s", fullOut.c_str());
8113         
8114         if (!listFiles(parent, fileSet))
8115             return false;
8116         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8117         //trace("###########HERE %s", fileSetDir.c_str());
8119         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8120             {
8121             String fname;
8122             if (fileSetDir.size()>0)
8123                 {
8124                 fname.append(fileSetDir);
8125                 fname.append("/");
8126                 }
8127             fname.append(fileSet[i]);
8128             String fullName = parent.resolve(fname);
8129             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8130             if (isNewerThan(fullName, fullOut))
8131                 doit = true;
8132             }
8133         //trace("Needs it:%d", doit);
8134         if (!doit)
8135             {
8136             return true;
8137             }
8139         String cmd = command;
8140         cmd.append(" ");
8141         cmd.append(fullOut);
8142         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8143             {
8144             String fname;
8145             if (fileSetDir.size()>0)
8146                 {
8147                 fname.append(fileSetDir);
8148                 fname.append("/");
8149                 }
8150             fname.append(fileSet[i]);
8151             String fullName = parent.resolve(fname);
8153             cmd.append(" ");
8154             cmd.append(fullName);
8155             }
8157         String outString, errString;
8158         if (!executeCommand(cmd.c_str(), "", outString, errString))
8159             {
8160             error("<staticlib> problem: %s", errString.c_str());
8161             return false;
8162             }
8164         return true;
8165         }
8168     virtual bool parse(Element *elem)
8169         {
8170         if (!parent.getAttribute(elem, "command", commandOpt))
8171             return false;
8172         if (!parent.getAttribute(elem, "file", fileNameOpt))
8173             return false;
8174             
8175         std::vector<Element *> children = elem->getChildren();
8176         for (unsigned int i=0 ; i<children.size() ; i++)
8177             {
8178             Element *child = children[i];
8179             String tagName = child->getName();
8180             if (tagName == "fileset")
8181                 {
8182                 if (!parseFileSet(child, parent, fileSet))
8183                     return false;
8184                 }
8185             }
8186         return true;
8187         }
8189 private:
8191     FileSet fileSet;
8193     String commandOpt;
8194     String fileNameOpt;
8196 };
8201 /**
8202  * Strip an executable
8203  */
8204 class TaskStrip : public Task
8206 public:
8208     TaskStrip(MakeBase &par) : Task(par)
8209         { type = TASK_STRIP; name = "strip"; }
8211     virtual ~TaskStrip()
8212         {}
8214     virtual bool execute()
8215         {
8216         String command     = parent.eval(commandOpt, "strip");
8217         String fileName    = parent.eval(fileNameOpt, "");
8218         String symFileName = parent.eval(symFileNameOpt, "");
8220         String fullName = parent.resolve(fileName);
8221         //trace("fullDir:%s", fullDir.c_str());
8222         String cmd;
8223         String outbuf, errbuf;
8225         if (symFileName.size()>0)
8226             {
8227             String symFullName = parent.resolve(symFileName);
8228             cmd = "objcopy --only-keep-debug ";
8229             cmd.append(getNativePath(fullName));
8230             cmd.append(" ");
8231             cmd.append(getNativePath(symFullName));
8232             if (!executeCommand(cmd, "", outbuf, errbuf))
8233                 {
8234                 error("<strip> symbol file failed : %s", errbuf.c_str());
8235                 return false;
8236                 }
8237             }
8238             
8239         cmd = command;
8240         cmd.append(getNativePath(fullName));
8241         if (!executeCommand(cmd, "", outbuf, errbuf))
8242             {
8243             error("<strip> failed : %s", errbuf.c_str());
8244             return false;
8245             }
8246         return true;
8247         }
8249     virtual bool parse(Element *elem)
8250         {
8251         if (!parent.getAttribute(elem, "command", commandOpt))
8252             return false;
8253         if (!parent.getAttribute(elem, "file", fileNameOpt))
8254             return false;
8255         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8256             return false;
8257         if (fileNameOpt.size() == 0)
8258             {
8259             error("<strip> requires 'file=\"fileName\"' attribute");
8260             return false;
8261             }
8262         return true;
8263         }
8265 private:
8267     String commandOpt;
8268     String fileNameOpt;
8269     String symFileNameOpt;
8270 };
8273 /**
8274  *
8275  */
8276 class TaskTouch : public Task
8278 public:
8280     TaskTouch(MakeBase &par) : Task(par)
8281         { type = TASK_TOUCH; name = "touch"; }
8283     virtual ~TaskTouch()
8284         {}
8286     virtual bool execute()
8287         {
8288         String fileName = parent.eval(fileNameOpt, "");
8290         String fullName = parent.resolve(fileName);
8291         String nativeFile = getNativePath(fullName);
8292         if (!isRegularFile(fullName) && !isDirectory(fullName))
8293             {            
8294             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8295             int ret = creat(nativeFile.c_str(), 0666);
8296             if (ret != 0) 
8297                 {
8298                 error("<touch> could not create '%s' : %s",
8299                     nativeFile.c_str(), strerror(ret));
8300                 return false;
8301                 }
8302             return true;
8303             }
8304         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8305         if (ret != 0)
8306             {
8307             error("<touch> could not update the modification time for '%s' : %s",
8308                 nativeFile.c_str(), strerror(ret));
8309             return false;
8310             }
8311         return true;
8312         }
8314     virtual bool parse(Element *elem)
8315         {
8316         //trace("touch parse");
8317         if (!parent.getAttribute(elem, "file", fileNameOpt))
8318             return false;
8319         if (fileNameOpt.size() == 0)
8320             {
8321             error("<touch> requires 'file=\"fileName\"' attribute");
8322             return false;
8323             }
8324         return true;
8325         }
8327     String fileNameOpt;
8328 };
8331 /**
8332  *
8333  */
8334 class TaskTstamp : public Task
8336 public:
8338     TaskTstamp(MakeBase &par) : Task(par)
8339         { type = TASK_TSTAMP; name = "tstamp"; }
8341     virtual ~TaskTstamp()
8342         {}
8344     virtual bool execute()
8345         {
8346         return true;
8347         }
8349     virtual bool parse(Element *elem)
8350         {
8351         //trace("tstamp parse");
8352         return true;
8353         }
8354 };
8358 /**
8359  *
8360  */
8361 Task *Task::createTask(Element *elem, int lineNr)
8363     String tagName = elem->getName();
8364     //trace("task:%s", tagName.c_str());
8365     Task *task = NULL;
8366     if (tagName == "cc")
8367         task = new TaskCC(parent);
8368     else if (tagName == "copy")
8369         task = new TaskCopy(parent);
8370     else if (tagName == "delete")
8371         task = new TaskDelete(parent);
8372     else if (tagName == "echo")
8373         task = new TaskEcho(parent);
8374     else if (tagName == "jar")
8375         task = new TaskJar(parent);
8376     else if (tagName == "javac")
8377         task = new TaskJavac(parent);
8378     else if (tagName == "link")
8379         task = new TaskLink(parent);
8380     else if (tagName == "makefile")
8381         task = new TaskMakeFile(parent);
8382     else if (tagName == "mkdir")
8383         task = new TaskMkDir(parent);
8384     else if (tagName == "msgfmt")
8385         task = new TaskMsgFmt(parent);
8386     else if (tagName == "pkg-config")
8387         task = new TaskPkgConfig(parent);
8388     else if (tagName == "ranlib")
8389         task = new TaskRanlib(parent);
8390     else if (tagName == "rc")
8391         task = new TaskRC(parent);
8392     else if (tagName == "sharedlib")
8393         task = new TaskSharedLib(parent);
8394     else if (tagName == "staticlib")
8395         task = new TaskStaticLib(parent);
8396     else if (tagName == "strip")
8397         task = new TaskStrip(parent);
8398     else if (tagName == "touch")
8399         task = new TaskTouch(parent);
8400     else if (tagName == "tstamp")
8401         task = new TaskTstamp(parent);
8402     else
8403         {
8404         error("Unknown task '%s'", tagName.c_str());
8405         return NULL;
8406         }
8408     task->setLine(lineNr);
8410     if (!task->parse(elem))
8411         {
8412         delete task;
8413         return NULL;
8414         }
8415     return task;
8420 //########################################################################
8421 //# T A R G E T
8422 //########################################################################
8424 /**
8425  *
8426  */
8427 class Target : public MakeBase
8430 public:
8432     /**
8433      *
8434      */
8435     Target(Make &par) : parent(par)
8436         { init(); }
8438     /**
8439      *
8440      */
8441     Target(const Target &other) : parent(other.parent)
8442         { init(); assign(other); }
8444     /**
8445      *
8446      */
8447     Target &operator=(const Target &other)
8448         { init(); assign(other); return *this; }
8450     /**
8451      *
8452      */
8453     virtual ~Target()
8454         { cleanup() ; }
8457     /**
8458      *
8459      */
8460     virtual Make &getParent()
8461         { return parent; }
8463     /**
8464      *
8465      */
8466     virtual String getName()
8467         { return name; }
8469     /**
8470      *
8471      */
8472     virtual void setName(const String &val)
8473         { name = val; }
8475     /**
8476      *
8477      */
8478     virtual String getDescription()
8479         { return description; }
8481     /**
8482      *
8483      */
8484     virtual void setDescription(const String &val)
8485         { description = val; }
8487     /**
8488      *
8489      */
8490     virtual void addDependency(const String &val)
8491         { deps.push_back(val); }
8493     /**
8494      *
8495      */
8496     virtual void parseDependencies(const String &val)
8497         { deps = tokenize(val, ", "); }
8499     /**
8500      *
8501      */
8502     virtual std::vector<String> &getDependencies()
8503         { return deps; }
8505     /**
8506      *
8507      */
8508     virtual String getIf()
8509         { return ifVar; }
8511     /**
8512      *
8513      */
8514     virtual void setIf(const String &val)
8515         { ifVar = val; }
8517     /**
8518      *
8519      */
8520     virtual String getUnless()
8521         { return unlessVar; }
8523     /**
8524      *
8525      */
8526     virtual void setUnless(const String &val)
8527         { unlessVar = val; }
8529     /**
8530      *
8531      */
8532     virtual void addTask(Task *val)
8533         { tasks.push_back(val); }
8535     /**
8536      *
8537      */
8538     virtual std::vector<Task *> &getTasks()
8539         { return tasks; }
8541 private:
8543     void init()
8544         {
8545         }
8547     void cleanup()
8548         {
8549         tasks.clear();
8550         }
8552     void assign(const Target &other)
8553         {
8554         //parent      = other.parent;
8555         name        = other.name;
8556         description = other.description;
8557         ifVar       = other.ifVar;
8558         unlessVar   = other.unlessVar;
8559         deps        = other.deps;
8560         tasks       = other.tasks;
8561         }
8563     Make &parent;
8565     String name;
8567     String description;
8569     String ifVar;
8571     String unlessVar;
8573     std::vector<String> deps;
8575     std::vector<Task *> tasks;
8577 };
8586 //########################################################################
8587 //# M A K E
8588 //########################################################################
8591 /**
8592  *
8593  */
8594 class Make : public MakeBase
8597 public:
8599     /**
8600      *
8601      */
8602     Make()
8603         { init(); }
8605     /**
8606      *
8607      */
8608     Make(const Make &other)
8609         { assign(other); }
8611     /**
8612      *
8613      */
8614     Make &operator=(const Make &other)
8615         { assign(other); return *this; }
8617     /**
8618      *
8619      */
8620     virtual ~Make()
8621         { cleanup(); }
8623     /**
8624      *
8625      */
8626     virtual std::map<String, Target> &getTargets()
8627         { return targets; }
8630     /**
8631      *
8632      */
8633     virtual String version()
8634         { return BUILDTOOL_VERSION; }
8636     /**
8637      * Overload a <property>
8638      */
8639     virtual bool specifyProperty(const String &name,
8640                                  const String &value);
8642     /**
8643      *
8644      */
8645     virtual bool run();
8647     /**
8648      *
8649      */
8650     virtual bool run(const String &target);
8654 private:
8656     /**
8657      *
8658      */
8659     void init();
8661     /**
8662      *
8663      */
8664     void cleanup();
8666     /**
8667      *
8668      */
8669     void assign(const Make &other);
8671     /**
8672      *
8673      */
8674     bool executeTask(Task &task);
8677     /**
8678      *
8679      */
8680     bool executeTarget(Target &target,
8681              std::set<String> &targetsCompleted);
8684     /**
8685      *
8686      */
8687     bool execute();
8689     /**
8690      *
8691      */
8692     bool checkTargetDependencies(Target &prop,
8693                     std::vector<String> &depList);
8695     /**
8696      *
8697      */
8698     bool parsePropertyFile(const String &fileName,
8699                            const String &prefix);
8701     /**
8702      *
8703      */
8704     bool parseProperty(Element *elem);
8706     /**
8707      *
8708      */
8709     bool parseFile();
8711     /**
8712      *
8713      */
8714     std::vector<String> glob(const String &pattern);
8717     //###############
8718     //# Fields
8719     //###############
8721     String projectName;
8723     String currentTarget;
8725     String defaultTarget;
8727     String specifiedTarget;
8729     String baseDir;
8731     String description;
8732     
8733     //std::vector<Property> properties;
8734     
8735     std::map<String, Target> targets;
8737     std::vector<Task *> allTasks;
8738     
8739     std::map<String, String> specifiedProperties;
8741 };
8744 //########################################################################
8745 //# C L A S S  M A I N T E N A N C E
8746 //########################################################################
8748 /**
8749  *
8750  */
8751 void Make::init()
8753     uri             = "build.xml";
8754     projectName     = "";
8755     currentTarget   = "";
8756     defaultTarget   = "";
8757     specifiedTarget = "";
8758     baseDir         = "";
8759     description     = "";
8760     envPrefix       = "env.";
8761     pcPrefix        = "pc.";
8762     pccPrefix       = "pcc.";
8763     pclPrefix       = "pcl.";
8764     properties.clear();
8765     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8766         delete allTasks[i];
8767     allTasks.clear();
8772 /**
8773  *
8774  */
8775 void Make::cleanup()
8777     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8778         delete allTasks[i];
8779     allTasks.clear();
8784 /**
8785  *
8786  */
8787 void Make::assign(const Make &other)
8789     uri              = other.uri;
8790     projectName      = other.projectName;
8791     currentTarget    = other.currentTarget;
8792     defaultTarget    = other.defaultTarget;
8793     specifiedTarget  = other.specifiedTarget;
8794     baseDir          = other.baseDir;
8795     description      = other.description;
8796     properties       = other.properties;
8801 //########################################################################
8802 //# U T I L I T Y    T A S K S
8803 //########################################################################
8805 /**
8806  *  Perform a file globbing
8807  */
8808 std::vector<String> Make::glob(const String &pattern)
8810     std::vector<String> res;
8811     return res;
8815 //########################################################################
8816 //# P U B L I C    A P I
8817 //########################################################################
8821 /**
8822  *
8823  */
8824 bool Make::executeTarget(Target &target,
8825              std::set<String> &targetsCompleted)
8828     String name = target.getName();
8830     //First get any dependencies for this target
8831     std::vector<String> deps = target.getDependencies();
8832     for (unsigned int i=0 ; i<deps.size() ; i++)
8833         {
8834         String dep = deps[i];
8835         //Did we do it already?  Skip
8836         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8837             continue;
8838             
8839         std::map<String, Target> &tgts =
8840                target.getParent().getTargets();
8841         std::map<String, Target>::iterator iter =
8842                tgts.find(dep);
8843         if (iter == tgts.end())
8844             {
8845             error("Target '%s' dependency '%s' not found",
8846                       name.c_str(),  dep.c_str());
8847             return false;
8848             }
8849         Target depTarget = iter->second;
8850         if (!executeTarget(depTarget, targetsCompleted))
8851             {
8852             return false;
8853             }
8854         }
8856     status("##### Target : %s\n##### %s", name.c_str(),
8857             target.getDescription().c_str());
8859     //Now let's do the tasks
8860     std::vector<Task *> &tasks = target.getTasks();
8861     for (unsigned int i=0 ; i<tasks.size() ; i++)
8862         {
8863         Task *task = tasks[i];
8864         status("--- %s / %s", name.c_str(), task->getName().c_str());
8865         if (!task->execute())
8866             {
8867             return false;
8868             }
8869         }
8870         
8871     targetsCompleted.insert(name);
8872     
8873     return true;
8878 /**
8879  *  Main execute() method.  Start here and work
8880  *  up the dependency tree 
8881  */
8882 bool Make::execute()
8884     status("######## EXECUTE");
8886     //Determine initial target
8887     if (specifiedTarget.size()>0)
8888         {
8889         currentTarget = specifiedTarget;
8890         }
8891     else if (defaultTarget.size()>0)
8892         {
8893         currentTarget = defaultTarget;
8894         }
8895     else
8896         {
8897         error("execute: no specified or default target requested");
8898         return false;
8899         }
8901     std::map<String, Target>::iterator iter =
8902                targets.find(currentTarget);
8903     if (iter == targets.end())
8904         {
8905         error("Initial target '%s' not found",
8906                  currentTarget.c_str());
8907         return false;
8908         }
8909         
8910     //Now run
8911     Target target = iter->second;
8912     std::set<String> targetsCompleted;
8913     if (!executeTarget(target, targetsCompleted))
8914         {
8915         return false;
8916         }
8918     status("######## EXECUTE COMPLETE");
8919     return true;
8925 /**
8926  *
8927  */
8928 bool Make::checkTargetDependencies(Target &target, 
8929                             std::vector<String> &depList)
8931     String tgtName = target.getName().c_str();
8932     depList.push_back(tgtName);
8934     std::vector<String> deps = target.getDependencies();
8935     for (unsigned int i=0 ; i<deps.size() ; i++)
8936         {
8937         String dep = deps[i];
8938         //First thing entered was the starting Target
8939         if (dep == depList[0])
8940             {
8941             error("Circular dependency '%s' found at '%s'",
8942                       dep.c_str(), tgtName.c_str());
8943             std::vector<String>::iterator diter;
8944             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8945                 {
8946                 error("  %s", diter->c_str());
8947                 }
8948             return false;
8949             }
8951         std::map<String, Target> &tgts =
8952                   target.getParent().getTargets();
8953         std::map<String, Target>::iterator titer = tgts.find(dep);
8954         if (titer == tgts.end())
8955             {
8956             error("Target '%s' dependency '%s' not found",
8957                       tgtName.c_str(), dep.c_str());
8958             return false;
8959             }
8960         if (!checkTargetDependencies(titer->second, depList))
8961             {
8962             return false;
8963             }
8964         }
8965     return true;
8972 static int getword(int pos, const String &inbuf, String &result)
8974     int p = pos;
8975     int len = (int)inbuf.size();
8976     String val;
8977     while (p < len)
8978         {
8979         char ch = inbuf[p];
8980         if (!isalnum(ch) && ch!='.' && ch!='_')
8981             break;
8982         val.push_back(ch);
8983         p++;
8984         }
8985     result = val;
8986     return p;
8992 /**
8993  *
8994  */
8995 bool Make::parsePropertyFile(const String &fileName,
8996                              const String &prefix)
8998     FILE *f = fopen(fileName.c_str(), "r");
8999     if (!f)
9000         {
9001         error("could not open property file %s", fileName.c_str());
9002         return false;
9003         }
9004     int linenr = 0;
9005     while (!feof(f))
9006         {
9007         char buf[256];
9008         if (!fgets(buf, 255, f))
9009             break;
9010         linenr++;
9011         String s = buf;
9012         s = trim(s);
9013         int len = s.size();
9014         if (len == 0)
9015             continue;
9016         if (s[0] == '#')
9017             continue;
9018         String key;
9019         String val;
9020         int p = 0;
9021         int p2 = getword(p, s, key);
9022         if (p2 <= p)
9023             {
9024             error("property file %s, line %d: expected keyword",
9025                     fileName.c_str(), linenr);
9026             return false;
9027             }
9028         if (prefix.size() > 0)
9029             {
9030             key.insert(0, prefix);
9031             }
9033         //skip whitespace
9034         for (p=p2 ; p<len ; p++)
9035             if (!isspace(s[p]))
9036                 break;
9038         if (p>=len || s[p]!='=')
9039             {
9040             error("property file %s, line %d: expected '='",
9041                     fileName.c_str(), linenr);
9042             return false;
9043             }
9044         p++;
9046         //skip whitespace
9047         for ( ; p<len ; p++)
9048             if (!isspace(s[p]))
9049                 break;
9051         /* This way expects a word after the =
9052         p2 = getword(p, s, val);
9053         if (p2 <= p)
9054             {
9055             error("property file %s, line %d: expected value",
9056                     fileName.c_str(), linenr);
9057             return false;
9058             }
9059         */
9060         // This way gets the rest of the line after the =
9061         if (p>=len)
9062             {
9063             error("property file %s, line %d: expected value",
9064                     fileName.c_str(), linenr);
9065             return false;
9066             }
9067         val = s.substr(p);
9068         if (key.size()==0)
9069             continue;
9070         //allow property to be set, even if val=""
9072         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9073         //See if we wanted to overload this property
9074         std::map<String, String>::iterator iter =
9075             specifiedProperties.find(key);
9076         if (iter!=specifiedProperties.end())
9077             {
9078             val = iter->second;
9079             status("overloading property '%s' = '%s'",
9080                    key.c_str(), val.c_str());
9081             }
9082         properties[key] = val;
9083         }
9084     fclose(f);
9085     return true;
9091 /**
9092  *
9093  */
9094 bool Make::parseProperty(Element *elem)
9096     std::vector<Attribute> &attrs = elem->getAttributes();
9097     for (unsigned int i=0 ; i<attrs.size() ; i++)
9098         {
9099         String attrName = attrs[i].getName();
9100         String attrVal  = attrs[i].getValue();
9102         if (attrName == "name")
9103             {
9104             String val;
9105             if (!getAttribute(elem, "value", val))
9106                 return false;
9107             if (val.size() > 0)
9108                 {
9109                 properties[attrVal] = val;
9110                 }
9111             else
9112                 {
9113                 if (!getAttribute(elem, "location", val))
9114                     return false;
9115                 //let the property exist, even if not defined
9116                 properties[attrVal] = val;
9117                 }
9118             //See if we wanted to overload this property
9119             std::map<String, String>::iterator iter =
9120                 specifiedProperties.find(attrVal);
9121             if (iter != specifiedProperties.end())
9122                 {
9123                 val = iter->second;
9124                 status("overloading property '%s' = '%s'",
9125                     attrVal.c_str(), val.c_str());
9126                 properties[attrVal] = val;
9127                 }
9128             }
9129         else if (attrName == "file")
9130             {
9131             String prefix;
9132             if (!getAttribute(elem, "prefix", prefix))
9133                 return false;
9134             if (prefix.size() > 0)
9135                 {
9136                 if (prefix[prefix.size()-1] != '.')
9137                     prefix.push_back('.');
9138                 }
9139             if (!parsePropertyFile(attrName, prefix))
9140                 return false;
9141             }
9142         else if (attrName == "environment")
9143             {
9144             if (attrVal.find('.') != attrVal.npos)
9145                 {
9146                 error("environment prefix cannot have a '.' in it");
9147                 return false;
9148                 }
9149             envPrefix = attrVal;
9150             envPrefix.push_back('.');
9151             }
9152         else if (attrName == "pkg-config")
9153             {
9154             if (attrVal.find('.') != attrVal.npos)
9155                 {
9156                 error("pkg-config prefix cannot have a '.' in it");
9157                 return false;
9158                 }
9159             pcPrefix = attrVal;
9160             pcPrefix.push_back('.');
9161             }
9162         else if (attrName == "pkg-config-cflags")
9163             {
9164             if (attrVal.find('.') != attrVal.npos)
9165                 {
9166                 error("pkg-config-cflags prefix cannot have a '.' in it");
9167                 return false;
9168                 }
9169             pccPrefix = attrVal;
9170             pccPrefix.push_back('.');
9171             }
9172         else if (attrName == "pkg-config-libs")
9173             {
9174             if (attrVal.find('.') != attrVal.npos)
9175                 {
9176                 error("pkg-config-libs prefix cannot have a '.' in it");
9177                 return false;
9178                 }
9179             pclPrefix = attrVal;
9180             pclPrefix.push_back('.');
9181             }
9182         }
9184     return true;
9190 /**
9191  *
9192  */
9193 bool Make::parseFile()
9195     status("######## PARSE : %s", uri.getPath().c_str());
9197     setLine(0);
9199     Parser parser;
9200     Element *root = parser.parseFile(uri.getNativePath());
9201     if (!root)
9202         {
9203         error("Could not open %s for reading",
9204               uri.getNativePath().c_str());
9205         return false;
9206         }
9207     
9208     setLine(root->getLine());
9210     if (root->getChildren().size()==0 ||
9211         root->getChildren()[0]->getName()!="project")
9212         {
9213         error("Main xml element should be <project>");
9214         delete root;
9215         return false;
9216         }
9218     //########## Project attributes
9219     Element *project = root->getChildren()[0];
9220     String s = project->getAttribute("name");
9221     if (s.size() > 0)
9222         projectName = s;
9223     s = project->getAttribute("default");
9224     if (s.size() > 0)
9225         defaultTarget = s;
9226     s = project->getAttribute("basedir");
9227     if (s.size() > 0)
9228         baseDir = s;
9230     //######### PARSE MEMBERS
9231     std::vector<Element *> children = project->getChildren();
9232     for (unsigned int i=0 ; i<children.size() ; i++)
9233         {
9234         Element *elem = children[i];
9235         setLine(elem->getLine());
9236         String tagName = elem->getName();
9238         //########## DESCRIPTION
9239         if (tagName == "description")
9240             {
9241             description = parser.trim(elem->getValue());
9242             }
9244         //######### PROPERTY
9245         else if (tagName == "property")
9246             {
9247             if (!parseProperty(elem))
9248                 return false;
9249             }
9251         //######### TARGET
9252         else if (tagName == "target")
9253             {
9254             String tname   = elem->getAttribute("name");
9255             String tdesc   = elem->getAttribute("description");
9256             String tdeps   = elem->getAttribute("depends");
9257             String tif     = elem->getAttribute("if");
9258             String tunless = elem->getAttribute("unless");
9259             Target target(*this);
9260             target.setName(tname);
9261             target.setDescription(tdesc);
9262             target.parseDependencies(tdeps);
9263             target.setIf(tif);
9264             target.setUnless(tunless);
9265             std::vector<Element *> telems = elem->getChildren();
9266             for (unsigned int i=0 ; i<telems.size() ; i++)
9267                 {
9268                 Element *telem = telems[i];
9269                 Task breeder(*this);
9270                 Task *task = breeder.createTask(telem, telem->getLine());
9271                 if (!task)
9272                     return false;
9273                 allTasks.push_back(task);
9274                 target.addTask(task);
9275                 }
9277             //Check name
9278             if (tname.size() == 0)
9279                 {
9280                 error("no name for target");
9281                 return false;
9282                 }
9283             //Check for duplicate name
9284             if (targets.find(tname) != targets.end())
9285                 {
9286                 error("target '%s' already defined", tname.c_str());
9287                 return false;
9288                 }
9289             //more work than targets[tname]=target, but avoids default allocator
9290             targets.insert(std::make_pair<String, Target>(tname, target));
9291             }
9292         //######### none of the above
9293         else
9294             {
9295             error("unknown toplevel tag: <%s>", tagName.c_str());
9296             return false;
9297             }
9299         }
9301     std::map<String, Target>::iterator iter;
9302     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9303         {
9304         Target tgt = iter->second;
9305         std::vector<String> depList;
9306         if (!checkTargetDependencies(tgt, depList))
9307             {
9308             return false;
9309             }
9310         }
9313     delete root;
9314     status("######## PARSE COMPLETE");
9315     return true;
9319 /**
9320  * Overload a <property>
9321  */
9322 bool Make::specifyProperty(const String &name, const String &value)
9324     if (specifiedProperties.find(name) != specifiedProperties.end())
9325         {
9326         error("Property %s already specified", name.c_str());
9327         return false;
9328         }
9329     specifiedProperties[name] = value;
9330     return true;
9335 /**
9336  *
9337  */
9338 bool Make::run()
9340     if (!parseFile())
9341         return false;
9342         
9343     if (!execute())
9344         return false;
9346     return true;
9352 /**
9353  * Get a formatted MM:SS.sss time elapsed string
9354  */ 
9355 static String
9356 timeDiffString(struct timeval &x, struct timeval &y)
9358     long microsX  = x.tv_usec;
9359     long secondsX = x.tv_sec;
9360     long microsY  = y.tv_usec;
9361     long secondsY = y.tv_sec;
9362     if (microsX < microsY)
9363         {
9364         microsX += 1000000;
9365         secondsX -= 1;
9366         }
9368     int seconds = (int)(secondsX - secondsY);
9369     int millis  = (int)((microsX - microsY)/1000);
9371     int minutes = seconds/60;
9372     seconds -= minutes*60;
9373     char buf[80];
9374     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9375     String ret = buf;
9376     return ret;
9377     
9380 /**
9381  *
9382  */
9383 bool Make::run(const String &target)
9385     status("####################################################");
9386     status("#   %s", version().c_str());
9387     status("####################################################");
9388     struct timeval timeStart, timeEnd;
9389     ::gettimeofday(&timeStart, NULL);
9390     specifiedTarget = target;
9391     if (!run())
9392         return false;
9393     ::gettimeofday(&timeEnd, NULL);
9394     String timeStr = timeDiffString(timeEnd, timeStart);
9395     status("####################################################");
9396     status("#   BuildTool Completed : %s", timeStr.c_str());
9397     status("####################################################");
9398     return true;
9407 }// namespace buildtool
9408 //########################################################################
9409 //# M A I N
9410 //########################################################################
9412 typedef buildtool::String String;
9414 /**
9415  *  Format an error message in printf() style
9416  */
9417 static void error(const char *fmt, ...)
9419     va_list ap;
9420     va_start(ap, fmt);
9421     fprintf(stderr, "BuildTool error: ");
9422     vfprintf(stderr, fmt, ap);
9423     fprintf(stderr, "\n");
9424     va_end(ap);
9428 static bool parseProperty(const String &s, String &name, String &val)
9430     int len = s.size();
9431     int i;
9432     for (i=0 ; i<len ; i++)
9433         {
9434         char ch = s[i];
9435         if (ch == '=')
9436             break;
9437         name.push_back(ch);
9438         }
9439     if (i>=len || s[i]!='=')
9440         {
9441         error("property requires -Dname=value");
9442         return false;
9443         }
9444     i++;
9445     for ( ; i<len ; i++)
9446         {
9447         char ch = s[i];
9448         val.push_back(ch);
9449         }
9450     return true;
9454 /**
9455  * Compare a buffer with a key, for the length of the key
9456  */
9457 static bool sequ(const String &buf, const char *key)
9459     int len = buf.size();
9460     for (int i=0 ; key[i] && i<len ; i++)
9461         {
9462         if (key[i] != buf[i])
9463             return false;
9464         }        
9465     return true;
9468 static void usage(int argc, char **argv)
9470     printf("usage:\n");
9471     printf("   %s [options] [target]\n", argv[0]);
9472     printf("Options:\n");
9473     printf("  -help, -h              print this message\n");
9474     printf("  -version               print the version information and exit\n");
9475     printf("  -file <file>           use given buildfile\n");
9476     printf("  -f <file>                 ''\n");
9477     printf("  -D<property>=<value>   use value for given property\n");
9483 /**
9484  * Parse the command-line args, get our options,
9485  * and run this thing
9486  */   
9487 static bool parseOptions(int argc, char **argv)
9489     if (argc < 1)
9490         {
9491         error("Cannot parse arguments");
9492         return false;
9493         }
9495     buildtool::Make make;
9497     String target;
9499     //char *progName = argv[0];
9500     for (int i=1 ; i<argc ; i++)
9501         {
9502         String arg = argv[i];
9503         if (arg.size()>1 && arg[0]=='-')
9504             {
9505             if (arg == "-h" || arg == "-help")
9506                 {
9507                 usage(argc,argv);
9508                 return true;
9509                 }
9510             else if (arg == "-version")
9511                 {
9512                 printf("%s", make.version().c_str());
9513                 return true;
9514                 }
9515             else if (arg == "-f" || arg == "-file")
9516                 {
9517                 if (i>=argc)
9518                    {
9519                    usage(argc, argv);
9520                    return false;
9521                    }
9522                 i++; //eat option
9523                 make.setURI(argv[i]);
9524                 }
9525             else if (arg.size()>2 && sequ(arg, "-D"))
9526                 {
9527                 String s = arg.substr(2, arg.size());
9528                 String name, value;
9529                 if (!parseProperty(s, name, value))
9530                    {
9531                    usage(argc, argv);
9532                    return false;
9533                    }
9534                 if (!make.specifyProperty(name, value))
9535                     return false;
9536                 }
9537             else
9538                 {
9539                 error("Unknown option:%s", arg.c_str());
9540                 return false;
9541                 }
9542             }
9543         else
9544             {
9545             if (target.size()>0)
9546                 {
9547                 error("only one initial target");
9548                 usage(argc, argv);
9549                 return false;
9550                 }
9551             target = arg;
9552             }
9553         }
9555     //We have the options.  Now execute them
9556     if (!make.run(target))
9557         return false;
9559     return true;
9565 /*
9566 static bool runMake()
9568     buildtool::Make make;
9569     if (!make.run())
9570         return false;
9571     return true;
9575 static bool pkgConfigTest()
9577     buildtool::PkgConfig pkgConfig;
9578     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9579         return false;
9580     return true;
9585 static bool depTest()
9587     buildtool::DepTool deptool;
9588     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9589     if (!deptool.generateDependencies("build.dep"))
9590         return false;
9591     std::vector<buildtool::FileRec> res =
9592            deptool.loadDepFile("build.dep");
9593     if (res.size() == 0)
9594         return false;
9595     return true;
9598 static bool popenTest()
9600     buildtool::Make make;
9601     buildtool::String out, err;
9602     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9603     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9604     return true;
9608 static bool propFileTest()
9610     buildtool::Make make;
9611     make.parsePropertyFile("test.prop", "test.");
9612     return true;
9614 */
9616 int main(int argc, char **argv)
9619     if (!parseOptions(argc, argv))
9620         return 1;
9621     /*
9622     if (!popenTest())
9623         return 1;
9625     if (!depTest())
9626         return 1;
9627     if (!propFileTest())
9628         return 1;
9629     if (runMake())
9630         return 1;
9631     */
9632     return 0;
9636 //########################################################################
9637 //# E N D 
9638 //########################################################################