Code

improve piping
[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.3"
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     //fprintf(stdout, " ");
3555     va_start(args,fmt);
3556     vfprintf(stdout, fmt, args);
3557     va_end(args);
3558     fprintf(stdout, "\n");
3559     fflush(stdout);
3563 /**
3564  *  Print a printf()-like formatted trace message
3565  */
3566 void MakeBase::trace(const char *fmt, ...)
3568     va_list args;
3569     fprintf(stdout, "Make: ");
3570     va_start(args,fmt);
3571     vfprintf(stdout, fmt, args);
3572     va_end(args) ;
3573     fprintf(stdout, "\n");
3574     fflush(stdout);
3579 /**
3580  *  Resolve another path relative to this one
3581  */
3582 String MakeBase::resolve(const String &otherPath)
3584     URI otherURI(otherPath);
3585     URI fullURI = uri.resolve(otherURI);
3586     String ret = fullURI.toString();
3587     return ret;
3592 /**
3593  *  Check if a given string matches a given regex pattern
3594  */
3595 bool MakeBase::regexMatch(const String &str, const String &pattern)
3597     const TRexChar *terror = NULL;
3598     const TRexChar *cpat = pattern.c_str();
3599     TRex *expr = trex_compile(cpat, &terror);
3600     if (!expr)
3601         {
3602         if (!terror)
3603             terror = "undefined";
3604         error("compilation error [%s]!\n", terror);
3605         return false;
3606         } 
3608     bool ret = true;
3610     const TRexChar *cstr = str.c_str();
3611     if (trex_match(expr, cstr))
3612         {
3613         ret = true;
3614         }
3615     else
3616         {
3617         ret = false;
3618         }
3620     trex_free(expr);
3622     return ret;
3625 /**
3626  *  Return the suffix, if any, of a file name
3627  */
3628 String MakeBase::getSuffix(const String &fname)
3630     if (fname.size() < 2)
3631         return "";
3632     unsigned int pos = fname.find_last_of('.');
3633     if (pos == fname.npos)
3634         return "";
3635     pos++;
3636     String res = fname.substr(pos, fname.size()-pos);
3637     //trace("suffix:%s", res.c_str()); 
3638     return res;
3643 /**
3644  * Break up a string into substrings delimited the characters
3645  * in delimiters.  Null-length substrings are ignored
3646  */  
3647 std::vector<String> MakeBase::tokenize(const String &str,
3648                                 const String &delimiters)
3651     std::vector<String> res;
3652     char *del = (char *)delimiters.c_str();
3653     String dmp;
3654     for (unsigned int i=0 ; i<str.size() ; i++)
3655         {
3656         char ch = str[i];
3657         char *p = (char *)0;
3658         for (p=del ; *p ; p++)
3659             if (*p == ch)
3660                 break;
3661         if (*p)
3662             {
3663             if (dmp.size() > 0)
3664                 {
3665                 res.push_back(dmp);
3666                 dmp.clear();
3667                 }
3668             }
3669         else
3670             {
3671             dmp.push_back(ch);
3672             }
3673         }
3674     //Add tail
3675     if (dmp.size() > 0)
3676         {
3677         res.push_back(dmp);
3678         dmp.clear();
3679         }
3681     return res;
3686 /**
3687  *  replace runs of whitespace with a single space
3688  */
3689 String MakeBase::strip(const String &s)
3691     int len = s.size();
3692     String stripped;
3693     for (int i = 0 ; i<len ; i++)
3694         {
3695         char ch = s[i];
3696         if (isspace(ch))
3697             {
3698             stripped.push_back(' ');
3699             for ( ; i<len ; i++)
3700                 {
3701                 ch = s[i];
3702                 if (!isspace(ch))
3703                     {
3704                     stripped.push_back(ch);
3705                     break;
3706                     }
3707                 }
3708             }
3709         else
3710             {
3711             stripped.push_back(ch);
3712             }
3713         }
3714     return stripped;
3717 /**
3718  *  remove leading whitespace from each line
3719  */
3720 String MakeBase::leftJustify(const String &s)
3722     String out;
3723     int len = s.size();
3724     for (int i = 0 ; i<len ; )
3725         {
3726         char ch;
3727         //Skip to first visible character
3728         while (i<len)
3729             {
3730             ch = s[i];
3731             if (ch == '\n' || ch == '\r'
3732               || !isspace(ch))
3733                   break;
3734             i++;
3735             }
3736         //Copy the rest of the line
3737         while (i<len)
3738             {
3739             ch = s[i];
3740             if (ch == '\n' || ch == '\r')
3741                 {
3742                 if (ch != '\r')
3743                     out.push_back('\n');
3744                 i++;
3745                 break;
3746                 }
3747             else
3748                 {
3749                 out.push_back(ch);
3750                 }
3751             i++;
3752             }
3753         }
3754     return out;
3758 /**
3759  *  Removes whitespace from beginning and end of a string
3760  */
3761 String MakeBase::trim(const String &s)
3763     if (s.size() < 1)
3764         return s;
3765     
3766     //Find first non-ws char
3767     unsigned int begin = 0;
3768     for ( ; begin < s.size() ; begin++)
3769         {
3770         if (!isspace(s[begin]))
3771             break;
3772         }
3774     //Find first non-ws char, going in reverse
3775     unsigned int end = s.size() - 1;
3776     for ( ; end > begin ; end--)
3777         {
3778         if (!isspace(s[end]))
3779             break;
3780         }
3781     //trace("begin:%d  end:%d", begin, end);
3783     String res = s.substr(begin, end-begin+1);
3784     return res;
3788 /**
3789  *  Return a lower case version of the given string
3790  */
3791 String MakeBase::toLower(const String &s)
3793     if (s.size()==0)
3794         return s;
3796     String ret;
3797     for(unsigned int i=0; i<s.size() ; i++)
3798         {
3799         ret.push_back(tolower(s[i]));
3800         }
3801     return ret;
3805 /**
3806  * Return the native format of the canonical
3807  * path which we store
3808  */
3809 String MakeBase::getNativePath(const String &path)
3811 #ifdef __WIN32__
3812     String npath;
3813     unsigned int firstChar = 0;
3814     if (path.size() >= 3)
3815         {
3816         if (path[0] == '/' &&
3817             isalpha(path[1]) &&
3818             path[2] == ':')
3819             firstChar++;
3820         }
3821     for (unsigned int i=firstChar ; i<path.size() ; i++)
3822         {
3823         char ch = path[i];
3824         if (ch == '/')
3825             npath.push_back('\\');
3826         else
3827             npath.push_back(ch);
3828         }
3829     return npath;
3830 #else
3831     return path;
3832 #endif
3836 #ifdef __WIN32__
3837 #include <tchar.h>
3839 static String win32LastError()
3842     DWORD dw = GetLastError(); 
3844     LPVOID str;
3845     FormatMessage(
3846         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3847         FORMAT_MESSAGE_FROM_SYSTEM,
3848         NULL,
3849         dw,
3850         0,
3851         (LPTSTR) &str,
3852         0, NULL );
3853     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3854     if(p != NULL)
3855         { // lose CRLF
3856         *p = _T('\0');
3857         }
3858     String ret = (char *)str;
3859     LocalFree(str);
3861     return ret;
3863 #endif
3867 /**
3868  * Execute a system call, using pipes to send data to the
3869  * program's stdin,  and reading stdout and stderr.
3870  */
3871 bool MakeBase::executeCommand(const String &command,
3872                               const String &inbuf,
3873                               String &outbuf,
3874                               String &errbuf)
3877     status("============ cmd ============\n%s\n=============================",
3878                 command.c_str());
3880     outbuf.clear();
3881     errbuf.clear();
3882     
3883 #ifdef __WIN32__
3885     /*
3886     I really hate having win32 code in this program, but the
3887     read buffer in command.com and cmd.exe are just too small
3888     for the large commands we need for compiling and linking.
3889     */
3891     bool ret = true;
3893     //# Allocate a separate buffer for safety
3894     char *paramBuf = new char[command.size() + 1];
3895     if (!paramBuf)
3896        {
3897        error("executeCommand cannot allocate command buffer");
3898        return false;
3899        }
3900     strcpy(paramBuf, (char *)command.c_str());
3902     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3903     //# to see how Win32 pipes work
3905     //# Create pipes
3906     SECURITY_ATTRIBUTES saAttr; 
3907     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3908     saAttr.bInheritHandle = TRUE; 
3909     saAttr.lpSecurityDescriptor = NULL; 
3910     HANDLE stdinRead,  stdinWrite;
3911     HANDLE stdoutRead, stdoutWrite;
3912     HANDLE stderrRead, stderrWrite;
3913     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3914         {
3915         error("executeProgram: could not create pipe");
3916         delete[] paramBuf;
3917         return false;
3918         } 
3919     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3920     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3921         {
3922         error("executeProgram: could not create pipe");
3923         delete[] paramBuf;
3924         return false;
3925         } 
3926     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3927     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3928         {
3929         error("executeProgram: could not create pipe");
3930         delete[] paramBuf;
3931         return false;
3932         } 
3933     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3935     // Create the process
3936     STARTUPINFO siStartupInfo;
3937     PROCESS_INFORMATION piProcessInfo;
3938     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3939     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3940     siStartupInfo.cb = sizeof(siStartupInfo);
3941     siStartupInfo.hStdError   =  stderrWrite;
3942     siStartupInfo.hStdOutput  =  stdoutWrite;
3943     siStartupInfo.hStdInput   =  stdinRead;
3944     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3945    
3946     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3947                 0, NULL, NULL, &siStartupInfo,
3948                 &piProcessInfo))
3949         {
3950         error("executeCommand : could not create process : %s",
3951                     win32LastError().c_str());
3952         ret = false;
3953         }
3955     delete[] paramBuf;
3957     DWORD bytesWritten;
3958     if (inbuf.size()>0 &&
3959         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3960                &bytesWritten, NULL))
3961         {
3962         error("executeCommand: could not write to pipe");
3963         return false;
3964         }    
3965     if (!CloseHandle(stdinWrite))
3966         {          
3967         error("executeCommand: could not close write pipe");
3968         return false;
3969         }
3970     if (!CloseHandle(stdoutWrite))
3971         {
3972         error("executeCommand: could not close read pipe");
3973         return false;
3974         }
3975     if (!CloseHandle(stderrWrite))
3976         {
3977         error("executeCommand: could not close read pipe");
3978         return false;
3979         }
3981     bool lastLoop = false;
3982     while (true)
3983         {
3984         DWORD avail;
3985         DWORD bytesRead;
3986         char readBuf[4096];
3988         //trace("## stderr");
3989         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3990         if (avail > 0)
3991             {
3992             bytesRead = 0;
3993             if (avail>4096) avail = 4096;
3994             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3995             if (bytesRead > 0)
3996                 {
3997                 for (unsigned int i=0 ; i<bytesRead ; i++)
3998                     errbuf.push_back(readBuf[i]);
3999                 }
4000             }
4002         //trace("## stdout");
4003         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4004         if (avail > 0)
4005             {
4006             bytesRead = 0;
4007             if (avail>4096) avail = 4096;
4008             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4009             if (bytesRead > 0)
4010                 {
4011                 for (unsigned int i=0 ; i<bytesRead ; i++)
4012                     outbuf.push_back(readBuf[i]);
4013                 }
4014             }
4015             
4016         //Was this the final check after program done?
4017         if (lastLoop)
4018             break;
4020         DWORD exitCode;
4021         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4022         if (exitCode != STILL_ACTIVE)
4023             lastLoop = true;
4025         Sleep(10);
4026         }    
4027     //trace("outbuf:%s", outbuf.c_str());
4028     if (!CloseHandle(stdoutRead))
4029         {
4030         error("executeCommand: could not close read pipe");
4031         return false;
4032         }
4033     if (!CloseHandle(stderrRead))
4034         {
4035         error("executeCommand: could not close read pipe");
4036         return false;
4037         }
4039     DWORD exitCode;
4040     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4041     //trace("exit code:%d", exitCode);
4042     if (exitCode != 0)
4043         {
4044         ret = false;
4045         }
4046     
4047     CloseHandle(piProcessInfo.hProcess);
4048     CloseHandle(piProcessInfo.hThread);
4050     return ret;
4052 #else //do it unix-style
4054     String pipeCommand = command;
4055     pipeCommand.append("  2>&1");
4056     String s;
4057     FILE *f = popen(pipeCommand.c_str(), "r");
4058     int errnum = 0;
4059     if (f)
4060         {
4061         while (true)
4062             {
4063             int ch = fgetc(f);
4064             if (ch < 0)
4065                 break;
4066             s.push_back((char)ch);
4067             }
4068         errnum = pclose(f);
4069         }
4070     outbuf = s;
4071     if (errnum != 0)
4072         {
4073         error("exec of command '%s' failed : %s",
4074              command.c_str(), strerror(errno));
4075         return false;
4076         }
4077     else
4078         return true;
4080 #endif
4081
4086 bool MakeBase::listDirectories(const String &baseName,
4087                               const String &dirName,
4088                               std::vector<String> &res)
4090     res.push_back(dirName);
4091     String fullPath = baseName;
4092     if (dirName.size()>0)
4093         {
4094         fullPath.append("/");
4095         fullPath.append(dirName);
4096         }
4097     DIR *dir = opendir(fullPath.c_str());
4098     while (true)
4099         {
4100         struct dirent *de = readdir(dir);
4101         if (!de)
4102             break;
4104         //Get the directory member name
4105         String s = de->d_name;
4106         if (s.size() == 0 || s[0] == '.')
4107             continue;
4108         String childName = dirName;
4109         childName.append("/");
4110         childName.append(s);
4112         String fullChildPath = baseName;
4113         fullChildPath.append("/");
4114         fullChildPath.append(childName);
4115         struct stat finfo;
4116         String childNative = getNativePath(fullChildPath);
4117         if (stat(childNative.c_str(), &finfo)<0)
4118             {
4119             error("cannot stat file:%s", childNative.c_str());
4120             }
4121         else if (S_ISDIR(finfo.st_mode))
4122             {
4123             //trace("directory: %s", childName.c_str());
4124             if (!listDirectories(baseName, childName, res))
4125                 return false;
4126             }
4127         }
4128     closedir(dir);
4130     return true;
4134 bool MakeBase::listFiles(const String &baseDir,
4135                          const String &dirName,
4136                          std::vector<String> &res)
4138     String fullDir = baseDir;
4139     if (dirName.size()>0)
4140         {
4141         fullDir.append("/");
4142         fullDir.append(dirName);
4143         }
4144     String dirNative = getNativePath(fullDir);
4146     std::vector<String> subdirs;
4147     DIR *dir = opendir(dirNative.c_str());
4148     if (!dir)
4149         {
4150         error("Could not open directory %s : %s",
4151               dirNative.c_str(), strerror(errno));
4152         return false;
4153         }
4154     while (true)
4155         {
4156         struct dirent *de = readdir(dir);
4157         if (!de)
4158             break;
4160         //Get the directory member name
4161         String s = de->d_name;
4162         if (s.size() == 0 || s[0] == '.')
4163             continue;
4164         String childName;
4165         if (dirName.size()>0)
4166             {
4167             childName.append(dirName);
4168             childName.append("/");
4169             }
4170         childName.append(s);
4171         String fullChild = baseDir;
4172         fullChild.append("/");
4173         fullChild.append(childName);
4174         
4175         if (isDirectory(fullChild))
4176             {
4177             //trace("directory: %s", childName.c_str());
4178             if (!listFiles(baseDir, childName, res))
4179                 return false;
4180             continue;
4181             }
4182         else if (!isRegularFile(fullChild))
4183             {
4184             error("unknown file:%s", childName.c_str());
4185             return false;
4186             }
4188        //all done!
4189         res.push_back(childName);
4191         }
4192     closedir(dir);
4194     return true;
4198 /**
4199  * Several different classes extend MakeBase.  By "propRef", we mean
4200  * the one holding the properties.  Likely "Make" itself
4201  */
4202 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4204     //before doing the list,  resolve any property references
4205     //that might have been specified in the directory name, such as ${src}
4206     String fsDir = fileSet.getDirectory();
4207     String dir;
4208     if (!propRef.getSubstitutions(fsDir, dir))
4209         return false;
4210     String baseDir = propRef.resolve(dir);
4211     std::vector<String> fileList;
4212     if (!listFiles(baseDir, "", fileList))
4213         return false;
4215     std::vector<String> includes = fileSet.getIncludes();
4216     std::vector<String> excludes = fileSet.getExcludes();
4218     std::vector<String> incs;
4219     std::vector<String>::iterator iter;
4221     std::sort(fileList.begin(), fileList.end());
4223     //If there are <includes>, then add files to the output
4224     //in the order of the include list
4225     if (includes.size()==0)
4226         incs = fileList;
4227     else
4228         {
4229         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4230             {
4231             String &pattern = *iter;
4232             std::vector<String>::iterator siter;
4233             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4234                 {
4235                 String s = *siter;
4236                 if (regexMatch(s, pattern))
4237                     {
4238                     //trace("INCLUDED:%s", s.c_str());
4239                     incs.push_back(s);
4240                     }
4241                 }
4242             }
4243         }
4245     //Now trim off the <excludes>
4246     std::vector<String> res;
4247     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4248         {
4249         String s = *iter;
4250         bool skipme = false;
4251         std::vector<String>::iterator siter;
4252         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4253             {
4254             String &pattern = *siter;
4255             if (regexMatch(s, pattern))
4256                 {
4257                 //trace("EXCLUDED:%s", s.c_str());
4258                 skipme = true;
4259                 break;
4260                 }
4261             }
4262         if (!skipme)
4263             res.push_back(s);
4264         }
4265         
4266     fileSet.setFiles(res);
4268     return true;
4272 /**
4273  * 0 == all, 1 = cflags, 2 = libs
4274  */ 
4275 bool MakeBase::pkgConfigRecursive(const String packageName,
4276                                   const String &path, 
4277                                   const String &prefix, 
4278                                   int query,
4279                                   String &result,
4280                                   std::set<String> &deplist) 
4282     PkgConfig pkgConfig;
4283     if (path.size() > 0)
4284         pkgConfig.setPath(path);
4285     if (prefix.size() > 0)
4286         pkgConfig.setPrefix(prefix);
4287     if (!pkgConfig.query(packageName))
4288         return false;
4289     if (query == 0)
4290         result = pkgConfig.getAll();
4291     else if (query == 1)
4292         result = pkgConfig.getCflags();
4293     else
4294         result = pkgConfig.getLibs();
4295     deplist.insert(packageName);
4296     std::vector<String> list = pkgConfig.getRequireList();
4297     for (unsigned int i = 0 ; i<list.size() ; i++)
4298         {
4299         String depPkgName = list[i];
4300         if (deplist.find(depPkgName) != deplist.end())
4301             continue;
4302         String val;
4303         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4304             {
4305             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4306             return false;
4307             }
4308         result.append(" ");
4309         result.append(val);
4310         }
4312     return true;
4315 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4317     std::set<String> deplist;
4318     String path = getProperty("pkg-config-path");
4319     if (path.size()>0)
4320         path = resolve(path);
4321     String prefix = getProperty("pkg-config-prefix");
4322     String val;
4323     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4324         return false;
4325     result = val;
4326     return true;
4331 /**
4332  * replace a variable ref like ${a} with a value
4333  */
4334 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4336     String varname = propertyName;
4337     if (envPrefix.size() > 0 &&
4338         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4339         {
4340         varname = varname.substr(envPrefix.size());
4341         char *envstr = getenv(varname.c_str());
4342         if (!envstr)
4343             {
4344             error("environment variable '%s' not defined", varname.c_str());
4345             return false;
4346             }
4347         result = envstr;
4348         }
4349     else if (pcPrefix.size() > 0 &&
4350         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4351         {
4352         varname = varname.substr(pcPrefix.size());
4353         String val;
4354         if (!pkgConfigQuery(varname, 0, val))
4355             return false;
4356         result = val;
4357         }
4358     else if (pccPrefix.size() > 0 &&
4359         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4360         {
4361         varname = varname.substr(pccPrefix.size());
4362         String val;
4363         if (!pkgConfigQuery(varname, 1, val))
4364             return false;
4365         result = val;
4366         }
4367     else if (pclPrefix.size() > 0 &&
4368         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4369         {
4370         varname = varname.substr(pclPrefix.size());
4371         String val;
4372         if (!pkgConfigQuery(varname, 2, val))
4373             return false;
4374         result = val;
4375         }
4376     else
4377         {
4378         std::map<String, String>::iterator iter;
4379         iter = properties.find(varname);
4380         if (iter != properties.end())
4381             {
4382             result = iter->second;
4383             }
4384         else
4385             {
4386             error("property '%s' not found", varname.c_str());
4387             return false;
4388             }
4389         }
4390     return true;
4396 /**
4397  * Analyse a string, looking for any substitutions or other
4398  * things that need resolution 
4399  */
4400 bool MakeBase::getSubstitutionsRecursive(const String &str,
4401                                          String &result, int depth)
4403     if (depth > 10)
4404         {
4405         error("nesting of substitutions too deep (>10) for '%s'",
4406                         str.c_str());
4407         return false;
4408         }
4409     String s = trim(str);
4410     int len = (int)s.size();
4411     String val;
4412     for (int i=0 ; i<len ; i++)
4413         {
4414         char ch = s[i];
4415         if (ch == '$' && s[i+1] == '{')
4416             {
4417             String varname;
4418             int j = i+2;
4419             for ( ; j<len ; j++)
4420                 {
4421                 ch = s[j];
4422                 if (ch == '$' && s[j+1] == '{')
4423                     {
4424                     error("attribute %s cannot have nested variable references",
4425                            s.c_str());
4426                     return false;
4427                     }
4428                 else if (ch == '}')
4429                     {
4430                     varname = trim(varname);
4431                     String varval;
4432                     if (!lookupProperty(varname, varval))
4433                         return false;
4434                     String varval2;
4435                     //Now see if the answer has ${} in it, too
4436                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4437                         return false;
4438                     val.append(varval2);
4439                     break;
4440                     }
4441                 else
4442                     {
4443                     varname.push_back(ch);
4444                     }
4445                 }
4446             i = j;
4447             }
4448         else
4449             {
4450             val.push_back(ch);
4451             }
4452         }
4453     result = val;
4454     return true;
4457 /**
4458  * Analyse a string, looking for any substitutions or other
4459  * things that need resilution 
4460  */
4461 bool MakeBase::getSubstitutions(const String &str, String &result)
4463     return getSubstitutionsRecursive(str, result, 0);
4468 /**
4469  * replace variable refs like ${a} with their values
4470  * Assume that the string has already been syntax validated
4471  */
4472 String MakeBase::eval(const String &s, const String &defaultVal)
4474     if (s.size()==0)
4475         return defaultVal;
4476     String ret;
4477     if (getSubstitutions(s, ret))
4478         return ret;
4479     else
4480         return defaultVal;
4484 /**
4485  * replace variable refs like ${a} with their values
4486  * return true or false
4487  * Assume that the string has already been syntax validated
4488  */
4489 bool MakeBase::evalBool(const String &s, bool defaultVal)
4491     if (s.size()==0)
4492         return defaultVal;
4493     String val = eval(s, "false");
4494     if (s == "true" || s == "TRUE")
4495         return true;
4496     else
4497         return defaultVal;
4501 /**
4502  * Get a string attribute, testing it for proper syntax and
4503  * property names.
4504  */
4505 bool MakeBase::getAttribute(Element *elem, const String &name,
4506                                     String &result)
4508     String s = elem->getAttribute(name);
4509     String tmp;
4510     bool ret = getSubstitutions(s, tmp);
4511     if (ret)
4512         result = s;  //assign -if- ok
4513     return ret;
4517 /**
4518  * Get a string value, testing it for proper syntax and
4519  * property names.
4520  */
4521 bool MakeBase::getValue(Element *elem, String &result)
4523     String s = elem->getValue();
4524     String tmp;
4525     bool ret = getSubstitutions(s, tmp);
4526     if (ret)
4527         result = s;  //assign -if- ok
4528     return ret;
4534 /**
4535  * Parse a <patternset> entry
4536  */  
4537 bool MakeBase::parsePatternSet(Element *elem,
4538                           MakeBase &propRef,
4539                           std::vector<String> &includes,
4540                           std::vector<String> &excludes
4541                           )
4543     std::vector<Element *> children  = elem->getChildren();
4544     for (unsigned int i=0 ; i<children.size() ; i++)
4545         {
4546         Element *child = children[i];
4547         String tagName = child->getName();
4548         if (tagName == "exclude")
4549             {
4550             String fname;
4551             if (!propRef.getAttribute(child, "name", fname))
4552                 return false;
4553             //trace("EXCLUDE: %s", fname.c_str());
4554             excludes.push_back(fname);
4555             }
4556         else if (tagName == "include")
4557             {
4558             String fname;
4559             if (!propRef.getAttribute(child, "name", fname))
4560                 return false;
4561             //trace("INCLUDE: %s", fname.c_str());
4562             includes.push_back(fname);
4563             }
4564         }
4566     return true;
4572 /**
4573  * Parse a <fileset> entry, and determine which files
4574  * should be included
4575  */  
4576 bool MakeBase::parseFileSet(Element *elem,
4577                           MakeBase &propRef,
4578                           FileSet &fileSet)
4580     String name = elem->getName();
4581     if (name != "fileset")
4582         {
4583         error("expected <fileset>");
4584         return false;
4585         }
4588     std::vector<String> includes;
4589     std::vector<String> excludes;
4591     //A fileset has one implied patternset
4592     if (!parsePatternSet(elem, propRef, includes, excludes))
4593         {
4594         return false;
4595         }
4596     //Look for child tags, including more patternsets
4597     std::vector<Element *> children  = elem->getChildren();
4598     for (unsigned int i=0 ; i<children.size() ; i++)
4599         {
4600         Element *child = children[i];
4601         String tagName = child->getName();
4602         if (tagName == "patternset")
4603             {
4604             if (!parsePatternSet(child, propRef, includes, excludes))
4605                 {
4606                 return false;
4607                 }
4608             }
4609         }
4611     String dir;
4612     //Now do the stuff
4613     //Get the base directory for reading file names
4614     if (!propRef.getAttribute(elem, "dir", dir))
4615         return false;
4617     fileSet.setDirectory(dir);
4618     fileSet.setIncludes(includes);
4619     fileSet.setExcludes(excludes);
4620     
4621     /*
4622     std::vector<String> fileList;
4623     if (dir.size() > 0)
4624         {
4625         String baseDir = propRef.resolve(dir);
4626         if (!listFiles(baseDir, "", includes, excludes, fileList))
4627             return false;
4628         }
4629     std::sort(fileList.begin(), fileList.end());
4630     result = fileList;
4631     */
4633     
4634     /*
4635     for (unsigned int i=0 ; i<result.size() ; i++)
4636         {
4637         trace("RES:%s", result[i].c_str());
4638         }
4639     */
4641     
4642     return true;
4645 /**
4646  * Parse a <filelist> entry.  This is far simpler than FileSet,
4647  * since no directory scanning is needed.  The file names are listed
4648  * explicitly.
4649  */  
4650 bool MakeBase::parseFileList(Element *elem,
4651                           MakeBase &propRef,
4652                           FileList &fileList)
4654     std::vector<String> fnames;
4655     //Look for child tags, namely "file"
4656     std::vector<Element *> children  = elem->getChildren();
4657     for (unsigned int i=0 ; i<children.size() ; i++)
4658         {
4659         Element *child = children[i];
4660         String tagName = child->getName();
4661         if (tagName == "file")
4662             {
4663             String fname = child->getAttribute("name");
4664             if (fname.size()==0)
4665                 {
4666                 error("<file> element requires name="" attribute");
4667                 return false;
4668                 }
4669             fnames.push_back(fname);
4670             }
4671         else
4672             {
4673             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4674             return false;
4675             }
4676         }
4678     String dir;
4679     //Get the base directory for reading file names
4680     if (!propRef.getAttribute(elem, "dir", dir))
4681         return false;
4682     fileList.setDirectory(dir);
4683     fileList.setFiles(fnames);
4685     return true;
4690 /**
4691  * Create a directory, making intermediate dirs
4692  * if necessary
4693  */                  
4694 bool MakeBase::createDirectory(const String &dirname)
4696     //trace("## createDirectory: %s", dirname.c_str());
4697     //## first check if it exists
4698     struct stat finfo;
4699     String nativeDir = getNativePath(dirname);
4700     char *cnative = (char *) nativeDir.c_str();
4701 #ifdef __WIN32__
4702     if (strlen(cnative)==2 && cnative[1]==':')
4703         return true;
4704 #endif
4705     if (stat(cnative, &finfo)==0)
4706         {
4707         if (!S_ISDIR(finfo.st_mode))
4708             {
4709             error("mkdir: file %s exists but is not a directory",
4710                   cnative);
4711             return false;
4712             }
4713         else //exists
4714             {
4715             return true;
4716             }
4717         }
4719     //## 2: pull off the last path segment, if any,
4720     //## to make the dir 'above' this one, if necessary
4721     unsigned int pos = dirname.find_last_of('/');
4722     if (pos>0 && pos != dirname.npos)
4723         {
4724         String subpath = dirname.substr(0, pos);
4725         //A letter root (c:) ?
4726         if (!createDirectory(subpath))
4727             return false;
4728         }
4729         
4730     //## 3: now make
4731 #ifdef __WIN32__
4732     if (mkdir(cnative)<0)
4733 #else
4734     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4735 #endif
4736         {
4737         error("cannot make directory '%s' : %s",
4738                  cnative, strerror(errno));
4739         return false;
4740         }
4741         
4742     return true;
4746 /**
4747  * Remove a directory recursively
4748  */ 
4749 bool MakeBase::removeDirectory(const String &dirName)
4751     char *dname = (char *)dirName.c_str();
4753     DIR *dir = opendir(dname);
4754     if (!dir)
4755         {
4756         //# Let this fail nicely.
4757         return true;
4758         //error("error opening directory %s : %s", dname, strerror(errno));
4759         //return false;
4760         }
4761     
4762     while (true)
4763         {
4764         struct dirent *de = readdir(dir);
4765         if (!de)
4766             break;
4768         //Get the directory member name
4769         String s = de->d_name;
4770         if (s.size() == 0 || s[0] == '.')
4771             continue;
4772         String childName;
4773         if (dirName.size() > 0)
4774             {
4775             childName.append(dirName);
4776             childName.append("/");
4777             }
4778         childName.append(s);
4781         struct stat finfo;
4782         String childNative = getNativePath(childName);
4783         char *cnative = (char *)childNative.c_str();
4784         if (stat(cnative, &finfo)<0)
4785             {
4786             error("cannot stat file:%s", cnative);
4787             }
4788         else if (S_ISDIR(finfo.st_mode))
4789             {
4790             //trace("DEL dir: %s", childName.c_str());
4791             if (!removeDirectory(childName))
4792                 {
4793                 return false;
4794                 }
4795             }
4796         else if (!S_ISREG(finfo.st_mode))
4797             {
4798             //trace("not regular: %s", cnative);
4799             }
4800         else
4801             {
4802             //trace("DEL file: %s", childName.c_str());
4803             if (remove(cnative)<0)
4804                 {
4805                 error("error deleting %s : %s",
4806                      cnative, strerror(errno));
4807                 return false;
4808                 }
4809             }
4810         }
4811     closedir(dir);
4813     //Now delete the directory
4814     String native = getNativePath(dirName);
4815     if (rmdir(native.c_str())<0)
4816         {
4817         error("could not delete directory %s : %s",
4818             native.c_str() , strerror(errno));
4819         return false;
4820         }
4822     return true;
4823     
4827 /**
4828  * Copy a file from one name to another. Perform only if needed
4829  */ 
4830 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4832     //# 1 Check up-to-date times
4833     String srcNative = getNativePath(srcFile);
4834     struct stat srcinfo;
4835     if (stat(srcNative.c_str(), &srcinfo)<0)
4836         {
4837         error("source file %s for copy does not exist",
4838                  srcNative.c_str());
4839         return false;
4840         }
4842     String destNative = getNativePath(destFile);
4843     struct stat destinfo;
4844     if (stat(destNative.c_str(), &destinfo)==0)
4845         {
4846         if (destinfo.st_mtime >= srcinfo.st_mtime)
4847             return true;
4848         }
4849         
4850     //# 2 prepare a destination directory if necessary
4851     unsigned int pos = destFile.find_last_of('/');
4852     if (pos != destFile.npos)
4853         {
4854         String subpath = destFile.substr(0, pos);
4855         if (!createDirectory(subpath))
4856             return false;
4857         }
4859     //# 3 do the data copy
4860 #ifndef __WIN32__
4862     FILE *srcf = fopen(srcNative.c_str(), "rb");
4863     if (!srcf)
4864         {
4865         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4866         return false;
4867         }
4868     FILE *destf = fopen(destNative.c_str(), "wb");
4869     if (!destf)
4870         {
4871         error("copyFile cannot open %s for writing", srcNative.c_str());
4872         return false;
4873         }
4875     while (!feof(srcf))
4876         {
4877         int ch = fgetc(srcf);
4878         if (ch<0)
4879             break;
4880         fputc(ch, destf);
4881         }
4883     fclose(destf);
4884     fclose(srcf);
4886 #else
4887     
4888     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4889         {
4890         error("copyFile from %s to %s failed",
4891              srcNative.c_str(), destNative.c_str());
4892         return false;
4893         }
4894         
4895 #endif /* __WIN32__ */
4898     return true;
4903 /**
4904  * Tests if the file exists and is a regular file
4905  */ 
4906 bool MakeBase::isRegularFile(const String &fileName)
4908     String native = getNativePath(fileName);
4909     struct stat finfo;
4910     
4911     //Exists?
4912     if (stat(native.c_str(), &finfo)<0)
4913         return false;
4916     //check the file mode
4917     if (!S_ISREG(finfo.st_mode))
4918         return false;
4920     return true;
4923 /**
4924  * Tests if the file exists and is a directory
4925  */ 
4926 bool MakeBase::isDirectory(const String &fileName)
4928     String native = getNativePath(fileName);
4929     struct stat finfo;
4930     
4931     //Exists?
4932     if (stat(native.c_str(), &finfo)<0)
4933         return false;
4936     //check the file mode
4937     if (!S_ISDIR(finfo.st_mode))
4938         return false;
4940     return true;
4945 /**
4946  * Tests is the modification of fileA is newer than fileB
4947  */ 
4948 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4950     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4951     String nativeA = getNativePath(fileA);
4952     struct stat infoA;
4953     //IF source does not exist, NOT newer
4954     if (stat(nativeA.c_str(), &infoA)<0)
4955         {
4956         return false;
4957         }
4959     String nativeB = getNativePath(fileB);
4960     struct stat infoB;
4961     //IF dest does not exist, YES, newer
4962     if (stat(nativeB.c_str(), &infoB)<0)
4963         {
4964         return true;
4965         }
4967     //check the actual times
4968     if (infoA.st_mtime > infoB.st_mtime)
4969         {
4970         return true;
4971         }
4973     return false;
4977 //########################################################################
4978 //# P K G    C O N F I G
4979 //########################################################################
4982 /**
4983  * Get a character from the buffer at pos.  If out of range,
4984  * return -1 for safety
4985  */
4986 int PkgConfig::get(int pos)
4988     if (pos>parselen)
4989         return -1;
4990     return parsebuf[pos];
4995 /**
4996  *  Skip over all whitespace characters beginning at pos.  Return
4997  *  the position of the first non-whitespace character.
4998  *  Pkg-config is line-oriented, so check for newline
4999  */
5000 int PkgConfig::skipwhite(int pos)
5002     while (pos < parselen)
5003         {
5004         int ch = get(pos);
5005         if (ch < 0)
5006             break;
5007         if (!isspace(ch))
5008             break;
5009         pos++;
5010         }
5011     return pos;
5015 /**
5016  *  Parse the buffer beginning at pos, for a word.  Fill
5017  *  'ret' with the result.  Return the position after the
5018  *  word.
5019  */
5020 int PkgConfig::getword(int pos, String &ret)
5022     while (pos < parselen)
5023         {
5024         int ch = get(pos);
5025         if (ch < 0)
5026             break;
5027         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5028             break;
5029         ret.push_back((char)ch);
5030         pos++;
5031         }
5032     return pos;
5035 bool PkgConfig::parseRequires()
5037     if (requires.size() == 0)
5038         return true;
5039     parsebuf = (char *)requires.c_str();
5040     parselen = requires.size();
5041     int pos = 0;
5042     while (pos < parselen)
5043         {
5044         pos = skipwhite(pos);
5045         String val;
5046         int pos2 = getword(pos, val);
5047         if (pos2 == pos)
5048             break;
5049         pos = pos2;
5050         //trace("val %s", val.c_str());
5051         requireList.push_back(val);
5052         }
5053     return true;
5057 static int getint(const String str)
5059     char *s = (char *)str.c_str();
5060     char *ends = NULL;
5061     long val = strtol(s, &ends, 10);
5062     if (ends == s)
5063         return 0L;
5064     else
5065         return val;
5068 void PkgConfig::parseVersion()
5070     if (version.size() == 0)
5071         return;
5072     String s1, s2, s3;
5073     unsigned int pos = 0;
5074     unsigned int pos2 = version.find('.', pos);
5075     if (pos2 == version.npos)
5076         {
5077         s1 = version;
5078         }
5079     else
5080         {
5081         s1 = version.substr(pos, pos2-pos);
5082         pos = pos2;
5083         pos++;
5084         if (pos < version.size())
5085             {
5086             pos2 = version.find('.', pos);
5087             if (pos2 == version.npos)
5088                 {
5089                 s2 = version.substr(pos, version.size()-pos);
5090                 }
5091             else
5092                 {
5093                 s2 = version.substr(pos, pos2-pos);
5094                 pos = pos2;
5095                 pos++;
5096                 if (pos < version.size())
5097                     s3 = version.substr(pos, pos2-pos);
5098                 }
5099             }
5100         }
5102     majorVersion = getint(s1);
5103     minorVersion = getint(s2);
5104     microVersion = getint(s3);
5105     //trace("version:%d.%d.%d", majorVersion,
5106     //          minorVersion, microVersion );
5110 bool PkgConfig::parseLine(const String &lineBuf)
5112     parsebuf = (char *)lineBuf.c_str();
5113     parselen = lineBuf.size();
5114     int pos = 0;
5115     
5116     while (pos < parselen)
5117         {
5118         String attrName;
5119         pos = skipwhite(pos);
5120         int ch = get(pos);
5121         if (ch == '#')
5122             {
5123             //comment.  eat the rest of the line
5124             while (pos < parselen)
5125                 {
5126                 ch = get(pos);
5127                 if (ch == '\n' || ch < 0)
5128                     break;
5129                 pos++;
5130                 }
5131             continue;
5132             }
5133         pos = getword(pos, attrName);
5134         if (attrName.size() == 0)
5135             continue;
5136         
5137         pos = skipwhite(pos);
5138         ch = get(pos);
5139         if (ch != ':' && ch != '=')
5140             {
5141             error("expected ':' or '='");
5142             return false;
5143             }
5144         pos++;
5145         pos = skipwhite(pos);
5146         String attrVal;
5147         while (pos < parselen)
5148             {
5149             ch = get(pos);
5150             if (ch == '\n' || ch < 0)
5151                 break;
5152             else if (ch == '$' && get(pos+1) == '{')
5153                 {
5154                 //#  this is a ${substitution}
5155                 pos += 2;
5156                 String subName;
5157                 while (pos < parselen)
5158                     {
5159                     ch = get(pos);
5160                     if (ch < 0)
5161                         {
5162                         error("unterminated substitution");
5163                         return false;
5164                         }
5165                     else if (ch == '}')
5166                         break;
5167                     else
5168                         subName.push_back((char)ch);
5169                     pos++;
5170                     }
5171                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5172                 if (subName == "prefix" && prefix.size()>0)
5173                     {
5174                     attrVal.append(prefix);
5175                     //trace("prefix override:%s", prefix.c_str());
5176                     }
5177                 else
5178                     {
5179                     String subVal = attrs[subName];
5180                     //trace("subVal:%s", subVal.c_str());
5181                     attrVal.append(subVal);
5182                     }
5183                 }
5184             else
5185                 attrVal.push_back((char)ch);
5186             pos++;
5187             }
5189         attrVal = trim(attrVal);
5190         attrs[attrName] = attrVal;
5192         String attrNameL = toLower(attrName);
5194         if (attrNameL == "name")
5195             name = attrVal;
5196         else if (attrNameL == "description")
5197             description = attrVal;
5198         else if (attrNameL == "cflags")
5199             cflags = attrVal;
5200         else if (attrNameL == "libs")
5201             libs = attrVal;
5202         else if (attrNameL == "requires")
5203             requires = attrVal;
5204         else if (attrNameL == "version")
5205             version = attrVal;
5207         //trace("name:'%s'  value:'%s'",
5208         //      attrName.c_str(), attrVal.c_str());
5209         }
5211     return true;
5215 bool PkgConfig::parse(const String &buf)
5217     init();
5219     String line;
5220     int lineNr = 0;
5221     for (unsigned int p=0 ; p<buf.size() ; p++)
5222         {
5223         int ch = buf[p];
5224         if (ch == '\n' || ch == '\r')
5225             {
5226             if (!parseLine(line))
5227                 return false;
5228             line.clear();
5229             lineNr++;
5230             }
5231         else
5232             {
5233             line.push_back(ch);
5234             }
5235         }
5236     if (line.size()>0)
5237         {
5238         if (!parseLine(line))
5239             return false;
5240         }
5242     parseRequires();
5243     parseVersion();
5245     return true;
5251 void PkgConfig::dumpAttrs()
5253     //trace("### PkgConfig attributes for %s", fileName.c_str());
5254     std::map<String, String>::iterator iter;
5255     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5256         {
5257         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5258         }
5262 bool PkgConfig::readFile(const String &fname)
5264     fileName = getNativePath(fname);
5266     FILE *f = fopen(fileName.c_str(), "r");
5267     if (!f)
5268         {
5269         error("cannot open file '%s' for reading", fileName.c_str());
5270         return false;
5271         }
5272     String buf;
5273     while (true)
5274         {
5275         int ch = fgetc(f);
5276         if (ch < 0)
5277             break;
5278         buf.push_back((char)ch);
5279         }
5280     fclose(f);
5282     //trace("####### File:\n%s", buf.c_str());
5283     if (!parse(buf))
5284         {
5285         return false;
5286         }
5288     //dumpAttrs();
5290     return true;
5295 bool PkgConfig::query(const String &pkgName)
5297     name = pkgName;
5299     String fname = path;
5300     fname.append("/");
5301     fname.append(name);
5302     fname.append(".pc");
5304     if (!readFile(fname))
5305         return false;
5306     
5307     return true;
5314 //########################################################################
5315 //# D E P T O O L
5316 //########################################################################
5320 /**
5321  *  Class which holds information for each file.
5322  */
5323 class FileRec
5325 public:
5327     typedef enum
5328         {
5329         UNKNOWN,
5330         CFILE,
5331         HFILE,
5332         OFILE
5333         } FileType;
5335     /**
5336      *  Constructor
5337      */
5338     FileRec()
5339         { init(); type = UNKNOWN; }
5341     /**
5342      *  Copy constructor
5343      */
5344     FileRec(const FileRec &other)
5345         { init(); assign(other); }
5346     /**
5347      *  Constructor
5348      */
5349     FileRec(int typeVal)
5350         { init(); type = typeVal; }
5351     /**
5352      *  Assignment operator
5353      */
5354     FileRec &operator=(const FileRec &other)
5355         { init(); assign(other); return *this; }
5358     /**
5359      *  Destructor
5360      */
5361     ~FileRec()
5362         {}
5364     /**
5365      *  Directory part of the file name
5366      */
5367     String path;
5369     /**
5370      *  Base name, sans directory and suffix
5371      */
5372     String baseName;
5374     /**
5375      *  File extension, such as cpp or h
5376      */
5377     String suffix;
5379     /**
5380      *  Type of file: CFILE, HFILE, OFILE
5381      */
5382     int type;
5384     /**
5385      * Used to list files ref'd by this one
5386      */
5387     std::map<String, FileRec *> files;
5390 private:
5392     void init()
5393         {
5394         }
5396     void assign(const FileRec &other)
5397         {
5398         type     = other.type;
5399         baseName = other.baseName;
5400         suffix   = other.suffix;
5401         files    = other.files;
5402         }
5404 };
5408 /**
5409  *  Simpler dependency record
5410  */
5411 class DepRec
5413 public:
5415     /**
5416      *  Constructor
5417      */
5418     DepRec()
5419         {init();}
5421     /**
5422      *  Copy constructor
5423      */
5424     DepRec(const DepRec &other)
5425         {init(); assign(other);}
5426     /**
5427      *  Constructor
5428      */
5429     DepRec(const String &fname)
5430         {init(); name = fname; }
5431     /**
5432      *  Assignment operator
5433      */
5434     DepRec &operator=(const DepRec &other)
5435         {init(); assign(other); return *this;}
5438     /**
5439      *  Destructor
5440      */
5441     ~DepRec()
5442         {}
5444     /**
5445      *  Directory part of the file name
5446      */
5447     String path;
5449     /**
5450      *  Base name, without the path and suffix
5451      */
5452     String name;
5454     /**
5455      *  Suffix of the source
5456      */
5457     String suffix;
5460     /**
5461      * Used to list files ref'd by this one
5462      */
5463     std::vector<String> files;
5466 private:
5468     void init()
5469         {
5470         }
5472     void assign(const DepRec &other)
5473         {
5474         path     = other.path;
5475         name     = other.name;
5476         suffix   = other.suffix;
5477         files    = other.files; //avoid recursion
5478         }
5480 };
5483 class DepTool : public MakeBase
5485 public:
5487     /**
5488      *  Constructor
5489      */
5490     DepTool()
5491         { init(); }
5493     /**
5494      *  Copy constructor
5495      */
5496     DepTool(const DepTool &other)
5497         { init(); assign(other); }
5499     /**
5500      *  Assignment operator
5501      */
5502     DepTool &operator=(const DepTool &other)
5503         { init(); assign(other); return *this; }
5506     /**
5507      *  Destructor
5508      */
5509     ~DepTool()
5510         {}
5513     /**
5514      *  Reset this section of code
5515      */
5516     virtual void init();
5517     
5518     /**
5519      *  Reset this section of code
5520      */
5521     virtual void assign(const DepTool &other)
5522         {
5523         }
5524     
5525     /**
5526      *  Sets the source directory which will be scanned
5527      */
5528     virtual void setSourceDirectory(const String &val)
5529         { sourceDir = val; }
5531     /**
5532      *  Returns the source directory which will be scanned
5533      */
5534     virtual String getSourceDirectory()
5535         { return sourceDir; }
5537     /**
5538      *  Sets the list of files within the directory to analyze
5539      */
5540     virtual void setFileList(const std::vector<String> &list)
5541         { fileList = list; }
5543     /**
5544      * Creates the list of all file names which will be
5545      * candidates for further processing.  Reads make.exclude
5546      * to see which files for directories to leave out.
5547      */
5548     virtual bool createFileList();
5551     /**
5552      *  Generates the forward dependency list
5553      */
5554     virtual bool generateDependencies();
5557     /**
5558      *  Generates the forward dependency list, saving the file
5559      */
5560     virtual bool generateDependencies(const String &);
5563     /**
5564      *  Load a dependency file
5565      */
5566     std::vector<DepRec> loadDepFile(const String &fileName);
5568     /**
5569      *  Load a dependency file, generating one if necessary
5570      */
5571     std::vector<DepRec> getDepFile(const String &fileName,
5572               bool forceRefresh);
5574     /**
5575      *  Save a dependency file
5576      */
5577     bool saveDepFile(const String &fileName);
5580 private:
5583     /**
5584      *
5585      */
5586     void parseName(const String &fullname,
5587                    String &path,
5588                    String &basename,
5589                    String &suffix);
5591     /**
5592      *
5593      */
5594     int get(int pos);
5596     /**
5597      *
5598      */
5599     int skipwhite(int pos);
5601     /**
5602      *
5603      */
5604     int getword(int pos, String &ret);
5606     /**
5607      *
5608      */
5609     bool sequ(int pos, const char *key);
5611     /**
5612      *
5613      */
5614     bool addIncludeFile(FileRec *frec, const String &fname);
5616     /**
5617      *
5618      */
5619     bool scanFile(const String &fname, FileRec *frec);
5621     /**
5622      *
5623      */
5624     bool processDependency(FileRec *ofile, FileRec *include);
5626     /**
5627      *
5628      */
5629     String sourceDir;
5631     /**
5632      *
5633      */
5634     std::vector<String> fileList;
5636     /**
5637      *
5638      */
5639     std::vector<String> directories;
5641     /**
5642      * A list of all files which will be processed for
5643      * dependencies.
5644      */
5645     std::map<String, FileRec *> allFiles;
5647     /**
5648      * The list of .o files, and the
5649      * dependencies upon them.
5650      */
5651     std::map<String, FileRec *> oFiles;
5653     int depFileSize;
5654     char *depFileBuf;
5656     static const int readBufSize = 8192;
5657     char readBuf[8193];//byte larger
5659 };
5665 /**
5666  *  Clean up after processing.  Called by the destructor, but should
5667  *  also be called before the object is reused.
5668  */
5669 void DepTool::init()
5671     sourceDir = ".";
5673     fileList.clear();
5674     directories.clear();
5675     
5676     //clear output file list
5677     std::map<String, FileRec *>::iterator iter;
5678     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5679         delete iter->second;
5680     oFiles.clear();
5682     //allFiles actually contains the master copies. delete them
5683     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5684         delete iter->second;
5685     allFiles.clear(); 
5692 /**
5693  *  Parse a full path name into path, base name, and suffix
5694  */
5695 void DepTool::parseName(const String &fullname,
5696                         String &path,
5697                         String &basename,
5698                         String &suffix)
5700     if (fullname.size() < 2)
5701         return;
5703     unsigned int pos = fullname.find_last_of('/');
5704     if (pos != fullname.npos && pos<fullname.size()-1)
5705         {
5706         path = fullname.substr(0, pos);
5707         pos++;
5708         basename = fullname.substr(pos, fullname.size()-pos);
5709         }
5710     else
5711         {
5712         path = "";
5713         basename = fullname;
5714         }
5716     pos = basename.find_last_of('.');
5717     if (pos != basename.npos && pos<basename.size()-1)
5718         {
5719         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5720         basename = basename.substr(0, pos);
5721         }
5723     //trace("parsename:%s %s %s", path.c_str(),
5724     //        basename.c_str(), suffix.c_str()); 
5729 /**
5730  *  Generate our internal file list.
5731  */
5732 bool DepTool::createFileList()
5735     for (unsigned int i=0 ; i<fileList.size() ; i++)
5736         {
5737         String fileName = fileList[i];
5738         //trace("## FileName:%s", fileName.c_str());
5739         String path;
5740         String basename;
5741         String sfx;
5742         parseName(fileName, path, basename, sfx);
5743         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5744             sfx == "cc" || sfx == "CC")
5745             {
5746             FileRec *fe         = new FileRec(FileRec::CFILE);
5747             fe->path            = path;
5748             fe->baseName        = basename;
5749             fe->suffix          = sfx;
5750             allFiles[fileName]  = fe;
5751             }
5752         else if (sfx == "h"   ||  sfx == "hh"  ||
5753                  sfx == "hpp" ||  sfx == "hxx")
5754             {
5755             FileRec *fe         = new FileRec(FileRec::HFILE);
5756             fe->path            = path;
5757             fe->baseName        = basename;
5758             fe->suffix          = sfx;
5759             allFiles[fileName]  = fe;
5760             }
5761         }
5763     if (!listDirectories(sourceDir, "", directories))
5764         return false;
5765         
5766     return true;
5773 /**
5774  * Get a character from the buffer at pos.  If out of range,
5775  * return -1 for safety
5776  */
5777 int DepTool::get(int pos)
5779     if (pos>depFileSize)
5780         return -1;
5781     return depFileBuf[pos];
5786 /**
5787  *  Skip over all whitespace characters beginning at pos.  Return
5788  *  the position of the first non-whitespace character.
5789  */
5790 int DepTool::skipwhite(int pos)
5792     while (pos < depFileSize)
5793         {
5794         int ch = get(pos);
5795         if (ch < 0)
5796             break;
5797         if (!isspace(ch))
5798             break;
5799         pos++;
5800         }
5801     return pos;
5805 /**
5806  *  Parse the buffer beginning at pos, for a word.  Fill
5807  *  'ret' with the result.  Return the position after the
5808  *  word.
5809  */
5810 int DepTool::getword(int pos, String &ret)
5812     while (pos < depFileSize)
5813         {
5814         int ch = get(pos);
5815         if (ch < 0)
5816             break;
5817         if (isspace(ch))
5818             break;
5819         ret.push_back((char)ch);
5820         pos++;
5821         }
5822     return pos;
5825 /**
5826  * Return whether the sequence of characters in the buffer
5827  * beginning at pos match the key,  for the length of the key
5828  */
5829 bool DepTool::sequ(int pos, const char *key)
5831     while (*key)
5832         {
5833         if (*key != get(pos))
5834             return false;
5835         key++; pos++;
5836         }
5837     return true;
5842 /**
5843  *  Add an include file name to a file record.  If the name
5844  *  is not found in allFiles explicitly, try prepending include
5845  *  directory names to it and try again.
5846  */
5847 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5849     //# if the name is an exact match to a path name
5850     //# in allFiles, like "myinc.h"
5851     std::map<String, FileRec *>::iterator iter =
5852            allFiles.find(iname);
5853     if (iter != allFiles.end()) //already exists
5854         {
5855          //h file in same dir
5856         FileRec *other = iter->second;
5857         //trace("local: '%s'", iname.c_str());
5858         frec->files[iname] = other;
5859         return true;
5860         }
5861     else 
5862         {
5863         //## Ok, it was not found directly
5864         //look in other dirs
5865         std::vector<String>::iterator diter;
5866         for (diter=directories.begin() ;
5867              diter!=directories.end() ; diter++)
5868             {
5869             String dfname = *diter;
5870             dfname.append("/");
5871             dfname.append(iname);
5872             URI fullPathURI(dfname);  //normalize path name
5873             String fullPath = fullPathURI.getPath();
5874             if (fullPath[0] == '/')
5875                 fullPath = fullPath.substr(1);
5876             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5877             iter = allFiles.find(fullPath);
5878             if (iter != allFiles.end())
5879                 {
5880                 FileRec *other = iter->second;
5881                 //trace("other: '%s'", iname.c_str());
5882                 frec->files[fullPath] = other;
5883                 return true;
5884                 }
5885             }
5886         }
5887     return true;
5892 /**
5893  *  Lightly parse a file to find the #include directives.  Do
5894  *  a bit of state machine stuff to make sure that the directive
5895  *  is valid.  (Like not in a comment).
5896  */
5897 bool DepTool::scanFile(const String &fname, FileRec *frec)
5899     String fileName;
5900     if (sourceDir.size() > 0)
5901         {
5902         fileName.append(sourceDir);
5903         fileName.append("/");
5904         }
5905     fileName.append(fname);
5906     String nativeName = getNativePath(fileName);
5907     FILE *f = fopen(nativeName.c_str(), "r");
5908     if (!f)
5909         {
5910         error("Could not open '%s' for reading", fname.c_str());
5911         return false;
5912         }
5913     String buf;
5914     while (!feof(f))
5915         {
5916         int nrbytes = fread(readBuf, 1, readBufSize, f);
5917         readBuf[nrbytes] = '\0';
5918         buf.append(readBuf);
5919         }
5920     fclose(f);
5922     depFileSize = buf.size();
5923     depFileBuf  = (char *)buf.c_str();
5924     int pos = 0;
5927     while (pos < depFileSize)
5928         {
5929         //trace("p:%c", get(pos));
5931         //# Block comment
5932         if (get(pos) == '/' && get(pos+1) == '*')
5933             {
5934             pos += 2;
5935             while (pos < depFileSize)
5936                 {
5937                 if (get(pos) == '*' && get(pos+1) == '/')
5938                     {
5939                     pos += 2;
5940                     break;
5941                     }
5942                 else
5943                     pos++;
5944                 }
5945             }
5946         //# Line comment
5947         else if (get(pos) == '/' && get(pos+1) == '/')
5948             {
5949             pos += 2;
5950             while (pos < depFileSize)
5951                 {
5952                 if (get(pos) == '\n')
5953                     {
5954                     pos++;
5955                     break;
5956                     }
5957                 else
5958                     pos++;
5959                 }
5960             }
5961         //# #include! yaay
5962         else if (sequ(pos, "#include"))
5963             {
5964             pos += 8;
5965             pos = skipwhite(pos);
5966             String iname;
5967             pos = getword(pos, iname);
5968             if (iname.size()>2)
5969                 {
5970                 iname = iname.substr(1, iname.size()-2);
5971                 addIncludeFile(frec, iname);
5972                 }
5973             }
5974         else
5975             {
5976             pos++;
5977             }
5978         }
5980     return true;
5985 /**
5986  *  Recursively check include lists to find all files in allFiles to which
5987  *  a given file is dependent.
5988  */
5989 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5991     std::map<String, FileRec *>::iterator iter;
5992     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5993         {
5994         String fname  = iter->first;
5995         if (ofile->files.find(fname) != ofile->files.end())
5996             {
5997             //trace("file '%s' already seen", fname.c_str());
5998             continue;
5999             }
6000         FileRec *child  = iter->second;
6001         ofile->files[fname] = child;
6002       
6003         processDependency(ofile, child);
6004         }
6007     return true;
6014 /**
6015  *  Generate the file dependency list.
6016  */
6017 bool DepTool::generateDependencies()
6019     std::map<String, FileRec *>::iterator iter;
6020     //# First pass.  Scan for all includes
6021     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6022         {
6023         FileRec *frec = iter->second;
6024         if (!scanFile(iter->first, frec))
6025             {
6026             //quit?
6027             }
6028         }
6030     //# Second pass.  Scan for all includes
6031     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6032         {
6033         FileRec *include = iter->second;
6034         if (include->type == FileRec::CFILE)
6035             {
6036             //String cFileName   = iter->first;
6037             FileRec *ofile     = new FileRec(FileRec::OFILE);
6038             ofile->path        = include->path;
6039             ofile->baseName    = include->baseName;
6040             ofile->suffix      = include->suffix;
6041             String fname       = include->path;
6042             if (fname.size()>0)
6043                 fname.append("/");
6044             fname.append(include->baseName);
6045             fname.append(".o");
6046             oFiles[fname]    = ofile;
6047             //add the .c file first?   no, don't
6048             //ofile->files[cFileName] = include;
6049             
6050             //trace("ofile:%s", fname.c_str());
6052             processDependency(ofile, include);
6053             }
6054         }
6056       
6057     return true;
6062 /**
6063  *  High-level call to generate deps and optionally save them
6064  */
6065 bool DepTool::generateDependencies(const String &fileName)
6067     if (!createFileList())
6068         return false;
6069     if (!generateDependencies())
6070         return false;
6071     if (!saveDepFile(fileName))
6072         return false;
6073     return true;
6077 /**
6078  *   This saves the dependency cache.
6079  */
6080 bool DepTool::saveDepFile(const String &fileName)
6082     time_t tim;
6083     time(&tim);
6085     FILE *f = fopen(fileName.c_str(), "w");
6086     if (!f)
6087         {
6088         trace("cannot open '%s' for writing", fileName.c_str());
6089         }
6090     fprintf(f, "<?xml version='1.0'?>\n");
6091     fprintf(f, "<!--\n");
6092     fprintf(f, "########################################################\n");
6093     fprintf(f, "## File: build.dep\n");
6094     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6095     fprintf(f, "########################################################\n");
6096     fprintf(f, "-->\n");
6098     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6099     std::map<String, FileRec *>::iterator iter;
6100     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6101         {
6102         FileRec *frec = iter->second;
6103         if (frec->type == FileRec::OFILE)
6104             {
6105             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6106                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6107             std::map<String, FileRec *>::iterator citer;
6108             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6109                 {
6110                 String cfname = citer->first;
6111                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6112                 }
6113             fprintf(f, "</object>\n\n");
6114             }
6115         }
6117     fprintf(f, "</dependencies>\n");
6118     fprintf(f, "\n");
6119     fprintf(f, "<!--\n");
6120     fprintf(f, "########################################################\n");
6121     fprintf(f, "## E N D\n");
6122     fprintf(f, "########################################################\n");
6123     fprintf(f, "-->\n");
6125     fclose(f);
6127     return true;
6133 /**
6134  *   This loads the dependency cache.
6135  */
6136 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6138     std::vector<DepRec> result;
6139     
6140     Parser parser;
6141     Element *root = parser.parseFile(depFile.c_str());
6142     if (!root)
6143         {
6144         //error("Could not open %s for reading", depFile.c_str());
6145         return result;
6146         }
6148     if (root->getChildren().size()==0 ||
6149         root->getChildren()[0]->getName()!="dependencies")
6150         {
6151         error("loadDepFile: main xml element should be <dependencies>");
6152         delete root;
6153         return result;
6154         }
6156     //########## Start parsing
6157     Element *depList = root->getChildren()[0];
6159     std::vector<Element *> objects = depList->getChildren();
6160     for (unsigned int i=0 ; i<objects.size() ; i++)
6161         {
6162         Element *objectElem = objects[i];
6163         String tagName = objectElem->getName();
6164         if (tagName != "object")
6165             {
6166             error("loadDepFile: <dependencies> should have only <object> children");
6167             return result;
6168             }
6170         String objName   = objectElem->getAttribute("name");
6171          //trace("object:%s", objName.c_str());
6172         DepRec depObject(objName);
6173         depObject.path   = objectElem->getAttribute("path");
6174         depObject.suffix = objectElem->getAttribute("suffix");
6175         //########## DESCRIPTION
6176         std::vector<Element *> depElems = objectElem->getChildren();
6177         for (unsigned int i=0 ; i<depElems.size() ; i++)
6178             {
6179             Element *depElem = depElems[i];
6180             tagName = depElem->getName();
6181             if (tagName != "dep")
6182                 {
6183                 error("loadDepFile: <object> should have only <dep> children");
6184                 return result;
6185                 }
6186             String depName = depElem->getAttribute("name");
6187             //trace("    dep:%s", depName.c_str());
6188             depObject.files.push_back(depName);
6189             }
6191         //Insert into the result list, in a sorted manner
6192         bool inserted = false;
6193         std::vector<DepRec>::iterator iter;
6194         for (iter = result.begin() ; iter != result.end() ; iter++)
6195             {
6196             String vpath = iter->path;
6197             vpath.append("/");
6198             vpath.append(iter->name);
6199             String opath = depObject.path;
6200             opath.append("/");
6201             opath.append(depObject.name);
6202             if (vpath > opath)
6203                 {
6204                 inserted = true;
6205                 iter = result.insert(iter, depObject);
6206                 break;
6207                 }
6208             }
6209         if (!inserted)
6210             result.push_back(depObject);
6211         }
6213     delete root;
6215     return result;
6219 /**
6220  *   This loads the dependency cache.
6221  */
6222 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6223                    bool forceRefresh)
6225     std::vector<DepRec> result;
6226     if (forceRefresh)
6227         {
6228         generateDependencies(depFile);
6229         result = loadDepFile(depFile);
6230         }
6231     else
6232         {
6233         //try once
6234         result = loadDepFile(depFile);
6235         if (result.size() == 0)
6236             {
6237             //fail? try again
6238             generateDependencies(depFile);
6239             result = loadDepFile(depFile);
6240             }
6241         }
6242     return result;
6248 //########################################################################
6249 //# T A S K
6250 //########################################################################
6251 //forward decl
6252 class Target;
6253 class Make;
6255 /**
6256  *
6257  */
6258 class Task : public MakeBase
6261 public:
6263     typedef enum
6264         {
6265         TASK_NONE,
6266         TASK_CC,
6267         TASK_COPY,
6268         TASK_DELETE,
6269         TASK_ECHO,
6270         TASK_JAR,
6271         TASK_JAVAC,
6272         TASK_LINK,
6273         TASK_MAKEFILE,
6274         TASK_MKDIR,
6275         TASK_MSGFMT,
6276         TASK_PKG_CONFIG,
6277         TASK_RANLIB,
6278         TASK_RC,
6279         TASK_SHAREDLIB,
6280         TASK_STATICLIB,
6281         TASK_STRIP,
6282         TASK_TOUCH,
6283         TASK_TSTAMP
6284         } TaskType;
6285         
6287     /**
6288      *
6289      */
6290     Task(MakeBase &par) : parent(par)
6291         { init(); }
6293     /**
6294      *
6295      */
6296     Task(const Task &other) : parent(other.parent)
6297         { init(); assign(other); }
6299     /**
6300      *
6301      */
6302     Task &operator=(const Task &other)
6303         { assign(other); return *this; }
6305     /**
6306      *
6307      */
6308     virtual ~Task()
6309         { }
6312     /**
6313      *
6314      */
6315     virtual MakeBase &getParent()
6316         { return parent; }
6318      /**
6319      *
6320      */
6321     virtual int  getType()
6322         { return type; }
6324     /**
6325      *
6326      */
6327     virtual void setType(int val)
6328         { type = val; }
6330     /**
6331      *
6332      */
6333     virtual String getName()
6334         { return name; }
6336     /**
6337      *
6338      */
6339     virtual bool execute()
6340         { return true; }
6342     /**
6343      *
6344      */
6345     virtual bool parse(Element *elem)
6346         { return true; }
6348     /**
6349      *
6350      */
6351     Task *createTask(Element *elem, int lineNr);
6354 protected:
6356     void init()
6357         {
6358         type = TASK_NONE;
6359         name = "none";
6360         }
6362     void assign(const Task &other)
6363         {
6364         type = other.type;
6365         name = other.name;
6366         }
6367         
6368     /**
6369      *  Show task status
6370      */
6371     void taskstatus(const char *fmt, ...)
6372         {
6373         va_list args;
6374         va_start(args,fmt);
6375         fprintf(stdout, "    %s : ", name.c_str());
6376         vfprintf(stdout, fmt, args);
6377         fprintf(stdout, "\n");
6378         va_end(args) ;
6379         }
6381     String getAttribute(Element *elem, const String &attrName)
6382         {
6383         String str;
6384         return str;
6385         }
6387     MakeBase &parent;
6389     int type;
6391     String name;
6392 };
6396 /**
6397  * This task runs the C/C++ compiler.  The compiler is invoked
6398  * for all .c or .cpp files which are newer than their correcsponding
6399  * .o files.  
6400  */
6401 class TaskCC : public Task
6403 public:
6405     TaskCC(MakeBase &par) : Task(par)
6406         {
6407         type = TASK_CC;
6408         name = "cc";
6409         }
6411     virtual ~TaskCC()
6412         {}
6413         
6414     virtual bool isExcludedInc(const String &dirname)
6415         {
6416         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6417             {
6418             String fname = excludeInc[i];
6419             if (fname == dirname)
6420                 return true;
6421             }
6422         return false;
6423         }
6425     virtual bool execute()
6426         {
6427         //evaluate our parameters
6428         String command         = parent.eval(commandOpt, "gcc");
6429         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6430         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6431         String source          = parent.eval(sourceOpt, ".");
6432         String dest            = parent.eval(destOpt, ".");
6433         String flags           = parent.eval(flagsOpt, "");
6434         String defines         = parent.eval(definesOpt, "");
6435         String includes        = parent.eval(includesOpt, "");
6436         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6437         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6439         if (!listFiles(parent, fileSet))
6440             return false;
6441             
6442         FILE *f = NULL;
6443         f = fopen("compile.lst", "w");
6445         //refreshCache is probably false here, unless specified otherwise
6446         String fullName = parent.resolve("build.dep");
6447         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6448             {
6449             taskstatus("regenerating C/C++ dependency cache");
6450             refreshCache = true;
6451             }
6453         DepTool depTool;
6454         depTool.setSourceDirectory(source);
6455         depTool.setFileList(fileSet.getFiles());
6456         std::vector<DepRec> deps =
6457              depTool.getDepFile("build.dep", refreshCache);
6458         
6459         String incs;
6460         incs.append("-I");
6461         incs.append(parent.resolve("."));
6462         incs.append(" ");
6463         if (includes.size()>0)
6464             {
6465             incs.append(includes);
6466             incs.append(" ");
6467             }
6468         std::set<String> paths;
6469         std::vector<DepRec>::iterator viter;
6470         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6471             {
6472             DepRec dep = *viter;
6473             if (dep.path.size()>0)
6474                 paths.insert(dep.path);
6475             }
6476         if (source.size()>0)
6477             {
6478             incs.append(" -I");
6479             incs.append(parent.resolve(source));
6480             incs.append(" ");
6481             }
6482         std::set<String>::iterator setIter;
6483         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6484             {
6485             String dirName = *setIter;
6486             //check excludeInc to see if we dont want to include this dir
6487             if (isExcludedInc(dirName))
6488                 continue;
6489             incs.append(" -I");
6490             String dname;
6491             if (source.size()>0)
6492                 {
6493                 dname.append(source);
6494                 dname.append("/");
6495                 }
6496             dname.append(dirName);
6497             incs.append(parent.resolve(dname));
6498             }
6499             
6500         /**
6501          * Compile each of the C files that need it
6502          */
6503         bool errorOccurred = false;                 
6504         std::vector<String> cfiles;
6505         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6506             {
6507             DepRec dep = *viter;
6509             //## Select command
6510             String sfx = dep.suffix;
6511             String command = ccCommand;
6512             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6513                  sfx == "cc" || sfx == "CC")
6514                 command = cxxCommand;
6515  
6516             //## Make paths
6517             String destPath = dest;
6518             String srcPath  = source;
6519             if (dep.path.size()>0)
6520                 {
6521                 destPath.append("/");
6522                 destPath.append(dep.path);
6523                 srcPath.append("/");
6524                 srcPath.append(dep.path);
6525                 }
6526             //## Make sure destination directory exists
6527             if (!createDirectory(destPath))
6528                 return false;
6529                 
6530             //## Check whether it needs to be done
6531             String destName;
6532             if (destPath.size()>0)
6533                 {
6534                 destName.append(destPath);
6535                 destName.append("/");
6536                 }
6537             destName.append(dep.name);
6538             destName.append(".o");
6539             String destFullName = parent.resolve(destName);
6540             String srcName;
6541             if (srcPath.size()>0)
6542                 {
6543                 srcName.append(srcPath);
6544                 srcName.append("/");
6545                 }
6546             srcName.append(dep.name);
6547             srcName.append(".");
6548             srcName.append(dep.suffix);
6549             String srcFullName = parent.resolve(srcName);
6550             bool compileMe = false;
6551             //# First we check if the source is newer than the .o
6552             if (isNewerThan(srcFullName, destFullName))
6553                 {
6554                 taskstatus("compile of %s required by source: %s",
6555                         destFullName.c_str(), srcFullName.c_str());
6556                 compileMe = true;
6557                 }
6558             else
6559                 {
6560                 //# secondly, we check if any of the included dependencies
6561                 //# of the .c/.cpp is newer than the .o
6562                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6563                     {
6564                     String depName;
6565                     if (source.size()>0)
6566                         {
6567                         depName.append(source);
6568                         depName.append("/");
6569                         }
6570                     depName.append(dep.files[i]);
6571                     String depFullName = parent.resolve(depName);
6572                     bool depRequires = isNewerThan(depFullName, destFullName);
6573                     //trace("%d %s %s\n", depRequires,
6574                     //        destFullName.c_str(), depFullName.c_str());
6575                     if (depRequires)
6576                         {
6577                         taskstatus("compile of %s required by included: %s",
6578                                 destFullName.c_str(), depFullName.c_str());
6579                         compileMe = true;
6580                         break;
6581                         }
6582                     }
6583                 }
6584             if (!compileMe)
6585                 {
6586                 continue;
6587                 }
6589             //## Assemble the command
6590             String cmd = command;
6591             cmd.append(" -c ");
6592             cmd.append(flags);
6593             cmd.append(" ");
6594             cmd.append(defines);
6595             cmd.append(" ");
6596             cmd.append(incs);
6597             cmd.append(" ");
6598             cmd.append(srcFullName);
6599             cmd.append(" -o ");
6600             cmd.append(destFullName);
6602             //## Execute the command
6604             String outString, errString;
6605             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6607             if (f)
6608                 {
6609                 fprintf(f, "########################### File : %s\n",
6610                              srcFullName.c_str());
6611                 fprintf(f, "#### COMMAND ###\n");
6612                 int col = 0;
6613                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6614                     {
6615                     char ch = cmd[i];
6616                     if (isspace(ch)  && col > 63)
6617                         {
6618                         fputc('\n', f);
6619                         col = 0;
6620                         }
6621                     else
6622                         {
6623                         fputc(ch, f);
6624                         col++;
6625                         }
6626                     if (col > 76)
6627                         {
6628                         fputc('\n', f);
6629                         col = 0;
6630                         }
6631                     }
6632                 fprintf(f, "\n");
6633                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6634                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6635                 fflush(f);
6636                 }
6637             if (!ret)
6638                 {
6639                 error("problem compiling: %s", errString.c_str());
6640                 errorOccurred = true;
6641                 }
6642             if (errorOccurred && !continueOnError)
6643                 break;
6644             }
6646         if (f)
6647             {
6648             fclose(f);
6649             }
6650         
6651         return !errorOccurred;
6652         }
6655     virtual bool parse(Element *elem)
6656         {
6657         String s;
6658         if (!parent.getAttribute(elem, "command", commandOpt))
6659             return false;
6660         if (commandOpt.size()>0)
6661             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6662         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6663             return false;
6664         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6665             return false;
6666         if (!parent.getAttribute(elem, "destdir", destOpt))
6667             return false;
6668         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6669             return false;
6670         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6671             return false;
6673         std::vector<Element *> children = elem->getChildren();
6674         for (unsigned int i=0 ; i<children.size() ; i++)
6675             {
6676             Element *child = children[i];
6677             String tagName = child->getName();
6678             if (tagName == "flags")
6679                 {
6680                 if (!parent.getValue(child, flagsOpt))
6681                     return false;
6682                 flagsOpt = strip(flagsOpt);
6683                 }
6684             else if (tagName == "includes")
6685                 {
6686                 if (!parent.getValue(child, includesOpt))
6687                     return false;
6688                 includesOpt = strip(includesOpt);
6689                 }
6690             else if (tagName == "defines")
6691                 {
6692                 if (!parent.getValue(child, definesOpt))
6693                     return false;
6694                 definesOpt = strip(definesOpt);
6695                 }
6696             else if (tagName == "fileset")
6697                 {
6698                 if (!parseFileSet(child, parent, fileSet))
6699                     return false;
6700                 sourceOpt = fileSet.getDirectory();
6701                 }
6702             else if (tagName == "excludeinc")
6703                 {
6704                 if (!parseFileList(child, parent, excludeInc))
6705                     return false;
6706                 }
6707             }
6709         return true;
6710         }
6711         
6712 protected:
6714     String   commandOpt;
6715     String   ccCommandOpt;
6716     String   cxxCommandOpt;
6717     String   sourceOpt;
6718     String   destOpt;
6719     String   flagsOpt;
6720     String   definesOpt;
6721     String   includesOpt;
6722     String   continueOnErrorOpt;
6723     String   refreshCacheOpt;
6724     FileSet  fileSet;
6725     FileList excludeInc;
6726     
6727 };
6731 /**
6732  *
6733  */
6734 class TaskCopy : public Task
6736 public:
6738     typedef enum
6739         {
6740         CP_NONE,
6741         CP_TOFILE,
6742         CP_TODIR
6743         } CopyType;
6745     TaskCopy(MakeBase &par) : Task(par)
6746         {
6747         type        = TASK_COPY;
6748         name        = "copy";
6749         cptype      = CP_NONE;
6750         haveFileSet = false;
6751         }
6753     virtual ~TaskCopy()
6754         {}
6756     virtual bool execute()
6757         {
6758         String fileName   = parent.eval(fileNameOpt   , ".");
6759         String toFileName = parent.eval(toFileNameOpt , ".");
6760         String toDirName  = parent.eval(toDirNameOpt  , ".");
6761         bool   verbose    = parent.evalBool(verboseOpt, false);
6762         switch (cptype)
6763            {
6764            case CP_TOFILE:
6765                {
6766                if (fileName.size()>0)
6767                    {
6768                    taskstatus("%s to %s",
6769                         fileName.c_str(), toFileName.c_str());
6770                    String fullSource = parent.resolve(fileName);
6771                    String fullDest = parent.resolve(toFileName);
6772                    if (verbose)
6773                        taskstatus("copy %s to file %s", fullSource.c_str(),
6774                                           fullDest.c_str());
6775                    if (!isRegularFile(fullSource))
6776                        {
6777                        error("copy : file %s does not exist", fullSource.c_str());
6778                        return false;
6779                        }
6780                    if (!isNewerThan(fullSource, fullDest))
6781                        {
6782                        taskstatus("skipped");
6783                        return true;
6784                        }
6785                    if (!copyFile(fullSource, fullDest))
6786                        return false;
6787                    taskstatus("1 file copied");
6788                    }
6789                return true;
6790                }
6791            case CP_TODIR:
6792                {
6793                if (haveFileSet)
6794                    {
6795                    if (!listFiles(parent, fileSet))
6796                        return false;
6797                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6799                    taskstatus("%s to %s",
6800                        fileSetDir.c_str(), toDirName.c_str());
6802                    int nrFiles = 0;
6803                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6804                        {
6805                        String fileName = fileSet[i];
6807                        String sourcePath;
6808                        if (fileSetDir.size()>0)
6809                            {
6810                            sourcePath.append(fileSetDir);
6811                            sourcePath.append("/");
6812                            }
6813                        sourcePath.append(fileName);
6814                        String fullSource = parent.resolve(sourcePath);
6815                        
6816                        //Get the immediate parent directory's base name
6817                        String baseFileSetDir = fileSetDir;
6818                        unsigned int pos = baseFileSetDir.find_last_of('/');
6819                        if (pos!=baseFileSetDir.npos &&
6820                                   pos < baseFileSetDir.size()-1)
6821                            baseFileSetDir =
6822                               baseFileSetDir.substr(pos+1,
6823                                    baseFileSetDir.size());
6824                        //Now make the new path
6825                        String destPath;
6826                        if (toDirName.size()>0)
6827                            {
6828                            destPath.append(toDirName);
6829                            destPath.append("/");
6830                            }
6831                        if (baseFileSetDir.size()>0)
6832                            {
6833                            destPath.append(baseFileSetDir);
6834                            destPath.append("/");
6835                            }
6836                        destPath.append(fileName);
6837                        String fullDest = parent.resolve(destPath);
6838                        //trace("fileName:%s", fileName.c_str());
6839                        if (verbose)
6840                            taskstatus("copy %s to new dir : %s",
6841                                  fullSource.c_str(), fullDest.c_str());
6842                        if (!isNewerThan(fullSource, fullDest))
6843                            {
6844                            if (verbose)
6845                                taskstatus("copy skipping %s", fullSource.c_str());
6846                            continue;
6847                            }
6848                        if (!copyFile(fullSource, fullDest))
6849                            return false;
6850                        nrFiles++;
6851                        }
6852                    taskstatus("%d file(s) copied", nrFiles);
6853                    }
6854                else //file source
6855                    {
6856                    //For file->dir we want only the basename of
6857                    //the source appended to the dest dir
6858                    taskstatus("%s to %s", 
6859                        fileName.c_str(), toDirName.c_str());
6860                    String baseName = fileName;
6861                    unsigned int pos = baseName.find_last_of('/');
6862                    if (pos!=baseName.npos && pos<baseName.size()-1)
6863                        baseName = baseName.substr(pos+1, baseName.size());
6864                    String fullSource = parent.resolve(fileName);
6865                    String destPath;
6866                    if (toDirName.size()>0)
6867                        {
6868                        destPath.append(toDirName);
6869                        destPath.append("/");
6870                        }
6871                    destPath.append(baseName);
6872                    String fullDest = parent.resolve(destPath);
6873                    if (verbose)
6874                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6875                                           fullDest.c_str());
6876                    if (!isRegularFile(fullSource))
6877                        {
6878                        error("copy : file %s does not exist", fullSource.c_str());
6879                        return false;
6880                        }
6881                    if (!isNewerThan(fullSource, fullDest))
6882                        {
6883                        taskstatus("skipped");
6884                        return true;
6885                        }
6886                    if (!copyFile(fullSource, fullDest))
6887                        return false;
6888                    taskstatus("1 file copied");
6889                    }
6890                return true;
6891                }
6892            }
6893         return true;
6894         }
6897     virtual bool parse(Element *elem)
6898         {
6899         if (!parent.getAttribute(elem, "file", fileNameOpt))
6900             return false;
6901         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6902             return false;
6903         if (toFileNameOpt.size() > 0)
6904             cptype = CP_TOFILE;
6905         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6906             return false;
6907         if (toDirNameOpt.size() > 0)
6908             cptype = CP_TODIR;
6909         if (!parent.getAttribute(elem, "verbose", verboseOpt))
6910             return false;
6911             
6912         haveFileSet = false;
6913         
6914         std::vector<Element *> children = elem->getChildren();
6915         for (unsigned int i=0 ; i<children.size() ; i++)
6916             {
6917             Element *child = children[i];
6918             String tagName = child->getName();
6919             if (tagName == "fileset")
6920                 {
6921                 if (!parseFileSet(child, parent, fileSet))
6922                     {
6923                     error("problem getting fileset");
6924                     return false;
6925                     }
6926                 haveFileSet = true;
6927                 }
6928             }
6930         //Perform validity checks
6931         if (fileNameOpt.size()>0 && fileSet.size()>0)
6932             {
6933             error("<copy> can only have one of : file= and <fileset>");
6934             return false;
6935             }
6936         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6937             {
6938             error("<copy> can only have one of : tofile= or todir=");
6939             return false;
6940             }
6941         if (haveFileSet && toDirNameOpt.size()==0)
6942             {
6943             error("a <copy> task with a <fileset> must have : todir=");
6944             return false;
6945             }
6946         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6947             {
6948             error("<copy> tofile= must be associated with : file=");
6949             return false;
6950             }
6951         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6952             {
6953             error("<copy> todir= must be associated with : file= or <fileset>");
6954             return false;
6955             }
6957         return true;
6958         }
6959         
6960 private:
6962     int cptype;
6963     bool haveFileSet;
6965     FileSet fileSet;
6966     String  fileNameOpt;
6967     String  toFileNameOpt;
6968     String  toDirNameOpt;
6969     String  verboseOpt;
6970 };
6973 /**
6974  *
6975  */
6976 class TaskDelete : public Task
6978 public:
6980     typedef enum
6981         {
6982         DEL_FILE,
6983         DEL_DIR,
6984         DEL_FILESET
6985         } DeleteType;
6987     TaskDelete(MakeBase &par) : Task(par)
6988         { 
6989         type        = TASK_DELETE;
6990         name        = "delete";
6991         delType     = DEL_FILE;
6992         }
6994     virtual ~TaskDelete()
6995         {}
6997     virtual bool execute()
6998         {
6999         String dirName   = parent.eval(dirNameOpt, ".");
7000         String fileName  = parent.eval(fileNameOpt, ".");
7001         bool verbose     = parent.evalBool(verboseOpt, false);
7002         bool quiet       = parent.evalBool(quietOpt, false);
7003         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7004         struct stat finfo;
7005         switch (delType)
7006             {
7007             case DEL_FILE:
7008                 {
7009                 taskstatus("file: %s", fileName.c_str());
7010                 String fullName = parent.resolve(fileName);
7011                 char *fname = (char *)fullName.c_str();
7012                 if (!quiet && verbose)
7013                     taskstatus("path: %s", fname);
7014                 //does not exist
7015                 if (stat(fname, &finfo)<0)
7016                     {
7017                     if (failOnError)
7018                         return false;
7019                     else
7020                         return true;
7021                     }
7022                 //exists but is not a regular file
7023                 if (!S_ISREG(finfo.st_mode))
7024                     {
7025                     error("<delete> failed. '%s' exists and is not a regular file",
7026                           fname);
7027                     return false;
7028                     }
7029                 if (remove(fname)<0)
7030                     {
7031                     error("<delete> failed: %s", strerror(errno));
7032                     return false;
7033                     }
7034                 return true;
7035                 }
7036             case DEL_DIR:
7037                 {
7038                 taskstatus("dir: %s", dirName.c_str());
7039                 String fullDir = parent.resolve(dirName);
7040                 if (!quiet && verbose)
7041                     taskstatus("path: %s", fullDir.c_str());
7042                 if (!removeDirectory(fullDir))
7043                     return false;
7044                 return true;
7045                 }
7046             }
7047         return true;
7048         }
7050     virtual bool parse(Element *elem)
7051         {
7052         if (!parent.getAttribute(elem, "file", fileNameOpt))
7053             return false;
7054         if (fileNameOpt.size() > 0)
7055             delType = DEL_FILE;
7056         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7057             return false;
7058         if (dirNameOpt.size() > 0)
7059             delType = DEL_DIR;
7060         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7061             {
7062             error("<delete> can have one attribute of file= or dir=");
7063             return false;
7064             }
7065         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7066             {
7067             error("<delete> must have one attribute of file= or dir=");
7068             return false;
7069             }
7070         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7071             return false;
7072         if (!parent.getAttribute(elem, "quiet", quietOpt))
7073             return false;
7074         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7075             return false;
7076         return true;
7077         }
7079 private:
7081     int delType;
7082     String dirNameOpt;
7083     String fileNameOpt;
7084     String verboseOpt;
7085     String quietOpt;
7086     String failOnErrorOpt;
7087 };
7090 /**
7091  * Send a message to stdout
7092  */
7093 class TaskEcho : public Task
7095 public:
7097     TaskEcho(MakeBase &par) : Task(par)
7098         { type = TASK_ECHO; name = "echo"; }
7100     virtual ~TaskEcho()
7101         {}
7103     virtual bool execute()
7104         {
7105         //let message have priority over text
7106         String message = parent.eval(messageOpt, "");
7107         String text    = parent.eval(textOpt, "");
7108         if (message.size() > 0)
7109             {
7110             fprintf(stdout, "%s\n", message.c_str());
7111             }
7112         else if (text.size() > 0)
7113             {
7114             fprintf(stdout, "%s\n", text.c_str());
7115             }
7116         return true;
7117         }
7119     virtual bool parse(Element *elem)
7120         {
7121         if (!parent.getValue(elem, textOpt))
7122             return false;
7123         textOpt    = leftJustify(textOpt);
7124         if (!parent.getAttribute(elem, "message", messageOpt))
7125             return false;
7126         return true;
7127         }
7129 private:
7131     String messageOpt;
7132     String textOpt;
7133 };
7137 /**
7138  *
7139  */
7140 class TaskJar : public Task
7142 public:
7144     TaskJar(MakeBase &par) : Task(par)
7145         { type = TASK_JAR; name = "jar"; }
7147     virtual ~TaskJar()
7148         {}
7150     virtual bool execute()
7151         {
7152         String command  = parent.eval(commandOpt, "jar");
7153         String basedir  = parent.eval(basedirOpt, ".");
7154         String destfile = parent.eval(destfileOpt, ".");
7156         String cmd = command;
7157         cmd.append(" -cf ");
7158         cmd.append(destfile);
7159         cmd.append(" -C ");
7160         cmd.append(basedir);
7161         cmd.append(" .");
7163         String execCmd = cmd;
7165         String outString, errString;
7166         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7167         if (!ret)
7168             {
7169             error("<jar> command '%s' failed :\n %s",
7170                                       execCmd.c_str(), errString.c_str());
7171             return false;
7172             }
7173         return true;
7174         }
7176     virtual bool parse(Element *elem)
7177         {
7178         if (!parent.getAttribute(elem, "command", commandOpt))
7179             return false;
7180         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7181             return false;
7182         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7183             return false;
7184         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7185             {
7186             error("<jar> required both basedir and destfile attributes to be set");
7187             return false;
7188             }
7189         return true;
7190         }
7192 private:
7194     String commandOpt;
7195     String basedirOpt;
7196     String destfileOpt;
7197 };
7200 /**
7201  *
7202  */
7203 class TaskJavac : public Task
7205 public:
7207     TaskJavac(MakeBase &par) : Task(par)
7208         { 
7209         type = TASK_JAVAC; name = "javac";
7210         }
7212     virtual ~TaskJavac()
7213         {}
7215     virtual bool execute()
7216         {
7217         String command  = parent.eval(commandOpt, "javac");
7218         String srcdir   = parent.eval(srcdirOpt, ".");
7219         String destdir  = parent.eval(destdirOpt, ".");
7220         String target   = parent.eval(targetOpt, "");
7222         std::vector<String> fileList;
7223         if (!listFiles(srcdir, "", fileList))
7224             {
7225             return false;
7226             }
7227         String cmd = command;
7228         cmd.append(" -d ");
7229         cmd.append(destdir);
7230         cmd.append(" -classpath ");
7231         cmd.append(destdir);
7232         cmd.append(" -sourcepath ");
7233         cmd.append(srcdir);
7234         cmd.append(" ");
7235         if (target.size()>0)
7236             {
7237             cmd.append(" -target ");
7238             cmd.append(target);
7239             cmd.append(" ");
7240             }
7241         String fname = "javalist.btool";
7242         FILE *f = fopen(fname.c_str(), "w");
7243         int count = 0;
7244         for (unsigned int i=0 ; i<fileList.size() ; i++)
7245             {
7246             String fname = fileList[i];
7247             String srcName = fname;
7248             if (fname.size()<6) //x.java
7249                 continue;
7250             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7251                 continue;
7252             String baseName = fname.substr(0, fname.size()-5);
7253             String destName = baseName;
7254             destName.append(".class");
7256             String fullSrc = srcdir;
7257             fullSrc.append("/");
7258             fullSrc.append(fname);
7259             String fullDest = destdir;
7260             fullDest.append("/");
7261             fullDest.append(destName);
7262             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7263             if (!isNewerThan(fullSrc, fullDest))
7264                 continue;
7266             count++;
7267             fprintf(f, "%s\n", fullSrc.c_str());
7268             }
7269         fclose(f);
7270         if (!count)
7271             {
7272             taskstatus("nothing to do");
7273             return true;
7274             }
7276         taskstatus("compiling %d files", count);
7278         String execCmd = cmd;
7279         execCmd.append("@");
7280         execCmd.append(fname);
7282         String outString, errString;
7283         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7284         if (!ret)
7285             {
7286             error("<javac> command '%s' failed :\n %s",
7287                                       execCmd.c_str(), errString.c_str());
7288             return false;
7289             }
7290         return true;
7291         }
7293     virtual bool parse(Element *elem)
7294         {
7295         if (!parent.getAttribute(elem, "command", commandOpt))
7296             return false;
7297         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7298             return false;
7299         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7300             return false;
7301         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7302             {
7303             error("<javac> required both srcdir and destdir attributes to be set");
7304             return false;
7305             }
7306         if (!parent.getAttribute(elem, "target", targetOpt))
7307             return false;
7308         return true;
7309         }
7311 private:
7313     String commandOpt;
7314     String srcdirOpt;
7315     String destdirOpt;
7316     String targetOpt;
7318 };
7321 /**
7322  *
7323  */
7324 class TaskLink : public Task
7326 public:
7328     TaskLink(MakeBase &par) : Task(par)
7329         {
7330         type = TASK_LINK; name = "link";
7331         }
7333     virtual ~TaskLink()
7334         {}
7336     virtual bool execute()
7337         {
7338         String  command        = parent.eval(commandOpt, "g++");
7339         String  fileName       = parent.eval(fileNameOpt, "");
7340         String  flags          = parent.eval(flagsOpt, "");
7341         String  libs           = parent.eval(libsOpt, "");
7342         bool    doStrip        = parent.evalBool(doStripOpt, false);
7343         String  symFileName    = parent.eval(symFileNameOpt, "");
7344         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7345         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7347         if (!listFiles(parent, fileSet))
7348             return false;
7349         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7350         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7351         bool doit = false;
7352         String fullTarget = parent.resolve(fileName);
7353         String cmd = command;
7354         cmd.append(" -o ");
7355         cmd.append(fullTarget);
7356         cmd.append(" ");
7357         cmd.append(flags);
7358         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7359             {
7360             cmd.append(" ");
7361             String obj;
7362             if (fileSetDir.size()>0)
7363                 {
7364                 obj.append(fileSetDir);
7365                 obj.append("/");
7366                 }
7367             obj.append(fileSet[i]);
7368             String fullObj = parent.resolve(obj);
7369             String nativeFullObj = getNativePath(fullObj);
7370             cmd.append(nativeFullObj);
7371             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7372             //          fullObj.c_str());
7373             if (isNewerThan(fullObj, fullTarget))
7374                 doit = true;
7375             }
7376         cmd.append(" ");
7377         cmd.append(libs);
7378         if (!doit)
7379             {
7380             //trace("link not needed");
7381             return true;
7382             }
7383         //trace("LINK cmd:%s", cmd.c_str());
7386         String outbuf, errbuf;
7387         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7388             {
7389             error("LINK problem: %s", errbuf.c_str());
7390             return false;
7391             }
7393         if (symFileName.size()>0)
7394             {
7395             String symFullName = parent.resolve(symFileName);
7396             cmd = objcopyCommand;
7397             cmd.append(" --only-keep-debug ");
7398             cmd.append(getNativePath(fullTarget));
7399             cmd.append(" ");
7400             cmd.append(getNativePath(symFullName));
7401             if (!executeCommand(cmd, "", outbuf, errbuf))
7402                 {
7403                 error("<strip> symbol file failed : %s", errbuf.c_str());
7404                 return false;
7405                 }
7406             }
7407             
7408         if (doStrip)
7409             {
7410             cmd = stripCommand;
7411             cmd.append(" ");
7412             cmd.append(getNativePath(fullTarget));
7413             if (!executeCommand(cmd, "", outbuf, errbuf))
7414                {
7415                error("<strip> failed : %s", errbuf.c_str());
7416                return false;
7417                }
7418             }
7420         return true;
7421         }
7423     virtual bool parse(Element *elem)
7424         {
7425         if (!parent.getAttribute(elem, "command", commandOpt))
7426             return false;
7427         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7428             return false;
7429         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7430             return false;
7431         if (!parent.getAttribute(elem, "out", fileNameOpt))
7432             return false;
7433         if (!parent.getAttribute(elem, "strip", doStripOpt))
7434             return false;
7435         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7436             return false;
7437             
7438         std::vector<Element *> children = elem->getChildren();
7439         for (unsigned int i=0 ; i<children.size() ; i++)
7440             {
7441             Element *child = children[i];
7442             String tagName = child->getName();
7443             if (tagName == "fileset")
7444                 {
7445                 if (!parseFileSet(child, parent, fileSet))
7446                     return false;
7447                 }
7448             else if (tagName == "flags")
7449                 {
7450                 if (!parent.getValue(child, flagsOpt))
7451                     return false;
7452                 flagsOpt = strip(flagsOpt);
7453                 }
7454             else if (tagName == "libs")
7455                 {
7456                 if (!parent.getValue(child, libsOpt))
7457                     return false;
7458                 libsOpt = strip(libsOpt);
7459                 }
7460             }
7461         return true;
7462         }
7464 private:
7466     FileSet fileSet;
7468     String  commandOpt;
7469     String  fileNameOpt;
7470     String  flagsOpt;
7471     String  libsOpt;
7472     String  doStripOpt;
7473     String  symFileNameOpt;
7474     String  stripCommandOpt;
7475     String  objcopyCommandOpt;
7477 };
7481 /**
7482  * Create a named file
7483  */
7484 class TaskMakeFile : public Task
7486 public:
7488     TaskMakeFile(MakeBase &par) : Task(par)
7489         { type = TASK_MAKEFILE; name = "makefile"; }
7491     virtual ~TaskMakeFile()
7492         {}
7494     virtual bool execute()
7495         {
7496         String fileName = parent.eval(fileNameOpt, "");
7497         String text     = parent.eval(textOpt, "");
7499         taskstatus("%s", fileName.c_str());
7500         String fullName = parent.resolve(fileName);
7501         if (!isNewerThan(parent.getURI().getPath(), fullName))
7502             {
7503             //trace("skipped <makefile>");
7504             return true;
7505             }
7506         String fullNative = getNativePath(fullName);
7507         //trace("fullName:%s", fullName.c_str());
7508         FILE *f = fopen(fullNative.c_str(), "w");
7509         if (!f)
7510             {
7511             error("<makefile> could not open %s for writing : %s",
7512                 fullName.c_str(), strerror(errno));
7513             return false;
7514             }
7515         for (unsigned int i=0 ; i<text.size() ; i++)
7516             fputc(text[i], f);
7517         fputc('\n', f);
7518         fclose(f);
7519         return true;
7520         }
7522     virtual bool parse(Element *elem)
7523         {
7524         if (!parent.getAttribute(elem, "file", fileNameOpt))
7525             return false;
7526         if (fileNameOpt.size() == 0)
7527             {
7528             error("<makefile> requires 'file=\"filename\"' attribute");
7529             return false;
7530             }
7531         if (!parent.getValue(elem, textOpt))
7532             return false;
7533         textOpt = leftJustify(textOpt);
7534         //trace("dirname:%s", dirName.c_str());
7535         return true;
7536         }
7538 private:
7540     String fileNameOpt;
7541     String textOpt;
7542 };
7546 /**
7547  * Create a named directory
7548  */
7549 class TaskMkDir : public Task
7551 public:
7553     TaskMkDir(MakeBase &par) : Task(par)
7554         { type = TASK_MKDIR; name = "mkdir"; }
7556     virtual ~TaskMkDir()
7557         {}
7559     virtual bool execute()
7560         {
7561         String dirName = parent.eval(dirNameOpt, ".");
7562         
7563         taskstatus("%s", dirName.c_str());
7564         String fullDir = parent.resolve(dirName);
7565         //trace("fullDir:%s", fullDir.c_str());
7566         if (!createDirectory(fullDir))
7567             return false;
7568         return true;
7569         }
7571     virtual bool parse(Element *elem)
7572         {
7573         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7574             return false;
7575         if (dirNameOpt.size() == 0)
7576             {
7577             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7578             return false;
7579             }
7580         return true;
7581         }
7583 private:
7585     String dirNameOpt;
7586 };
7590 /**
7591  * Create a named directory
7592  */
7593 class TaskMsgFmt: public Task
7595 public:
7597     TaskMsgFmt(MakeBase &par) : Task(par)
7598          { type = TASK_MSGFMT;  name = "msgfmt"; }
7600     virtual ~TaskMsgFmt()
7601         {}
7603     virtual bool execute()
7604         {
7605         String  command   = parent.eval(commandOpt, "msgfmt");
7606         String  toDirName = parent.eval(toDirNameOpt, ".");
7607         String  outName   = parent.eval(outNameOpt, "");
7608         bool    owndir    = parent.evalBool(owndirOpt, false);
7610         if (!listFiles(parent, fileSet))
7611             return false;
7612         String fileSetDir = fileSet.getDirectory();
7614         //trace("msgfmt: %d", fileSet.size());
7615         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7616             {
7617             String fileName = fileSet[i];
7618             if (getSuffix(fileName) != "po")
7619                 continue;
7620             String sourcePath;
7621             if (fileSetDir.size()>0)
7622                 {
7623                 sourcePath.append(fileSetDir);
7624                 sourcePath.append("/");
7625                 }
7626             sourcePath.append(fileName);
7627             String fullSource = parent.resolve(sourcePath);
7629             String destPath;
7630             if (toDirName.size()>0)
7631                 {
7632                 destPath.append(toDirName);
7633                 destPath.append("/");
7634                 }
7635             if (owndir)
7636                 {
7637                 String subdir = fileName;
7638                 unsigned int pos = subdir.find_last_of('.');
7639                 if (pos != subdir.npos)
7640                     subdir = subdir.substr(0, pos);
7641                 destPath.append(subdir);
7642                 destPath.append("/");
7643                 }
7644             //Pick the output file name
7645             if (outName.size() > 0)
7646                 {
7647                 destPath.append(outName);
7648                 }
7649             else
7650                 {
7651                 destPath.append(fileName);
7652                 destPath[destPath.size()-2] = 'm';
7653                 }
7655             String fullDest = parent.resolve(destPath);
7657             if (!isNewerThan(fullSource, fullDest))
7658                 {
7659                 //trace("skip %s", fullSource.c_str());
7660                 continue;
7661                 }
7662                 
7663             String cmd = command;
7664             cmd.append(" ");
7665             cmd.append(fullSource);
7666             cmd.append(" -o ");
7667             cmd.append(fullDest);
7668             
7669             int pos = fullDest.find_last_of('/');
7670             if (pos>0)
7671                 {
7672                 String fullDestPath = fullDest.substr(0, pos);
7673                 if (!createDirectory(fullDestPath))
7674                     return false;
7675                 }
7679             String outString, errString;
7680             if (!executeCommand(cmd.c_str(), "", outString, errString))
7681                 {
7682                 error("<msgfmt> problem: %s", errString.c_str());
7683                 return false;
7684                 }
7685             }
7687         return true;
7688         }
7690     virtual bool parse(Element *elem)
7691         {
7692         if (!parent.getAttribute(elem, "command", commandOpt))
7693             return false;
7694         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7695             return false;
7696         if (!parent.getAttribute(elem, "out", outNameOpt))
7697             return false;
7698         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7699             return false;
7700             
7701         std::vector<Element *> children = elem->getChildren();
7702         for (unsigned int i=0 ; i<children.size() ; i++)
7703             {
7704             Element *child = children[i];
7705             String tagName = child->getName();
7706             if (tagName == "fileset")
7707                 {
7708                 if (!parseFileSet(child, parent, fileSet))
7709                     return false;
7710                 }
7711             }
7712         return true;
7713         }
7715 private:
7717     FileSet fileSet;
7719     String  commandOpt;
7720     String  toDirNameOpt;
7721     String  outNameOpt;
7722     String  owndirOpt;
7724 };
7728 /**
7729  *  Perform a Package-Config query similar to pkg-config
7730  */
7731 class TaskPkgConfig : public Task
7733 public:
7735     typedef enum
7736         {
7737         PKG_CONFIG_QUERY_CFLAGS,
7738         PKG_CONFIG_QUERY_LIBS,
7739         PKG_CONFIG_QUERY_ALL
7740         } QueryTypes;
7742     TaskPkgConfig(MakeBase &par) : Task(par)
7743         {
7744         type = TASK_PKG_CONFIG;
7745         name = "pkg-config";
7746         }
7748     virtual ~TaskPkgConfig()
7749         {}
7751     virtual bool execute()
7752         {
7753         String pkgName       = parent.eval(pkgNameOpt,      "");
7754         String prefix        = parent.eval(prefixOpt,       "");
7755         String propName      = parent.eval(propNameOpt,     "");
7756         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7757         String query         = parent.eval(queryOpt,        "all");
7759         String path = parent.resolve(pkgConfigPath);
7760         PkgConfig pkgconfig;
7761         pkgconfig.setPath(path);
7762         pkgconfig.setPrefix(prefix);
7763         if (!pkgconfig.query(pkgName))
7764             {
7765             error("<pkg-config> query failed for '%s", name.c_str());
7766             return false;
7767             }
7768             
7769         String val = "";
7770         if (query == "cflags")
7771             val = pkgconfig.getCflags();
7772         else if (query == "libs")
7773             val =pkgconfig.getLibs();
7774         else if (query == "all")
7775             val = pkgconfig.getAll();
7776         else
7777             {
7778             error("<pkg-config> unhandled query : %s", query.c_str());
7779             return false;
7780             }
7781         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7782         parent.setProperty(propName, val);
7783         return true;
7784         }
7786     virtual bool parse(Element *elem)
7787         {
7788         //# NAME
7789         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7790             return false;
7791         if (pkgNameOpt.size()==0)
7792             {
7793             error("<pkg-config> requires 'name=\"package\"' attribute");
7794             return false;
7795             }
7797         //# PROPERTY
7798         if (!parent.getAttribute(elem, "property", propNameOpt))
7799             return false;
7800         if (propNameOpt.size()==0)
7801             {
7802             error("<pkg-config> requires 'property=\"name\"' attribute");
7803             return false;
7804             }
7805         //# PATH
7806         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7807             return false;
7808         //# PREFIX
7809         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7810             return false;
7811         //# QUERY
7812         if (!parent.getAttribute(elem, "query", queryOpt))
7813             return false;
7815         return true;
7816         }
7818 private:
7820     String queryOpt;
7821     String pkgNameOpt;
7822     String prefixOpt;
7823     String propNameOpt;
7824     String pkgConfigPathOpt;
7826 };
7833 /**
7834  *  Process an archive to allow random access
7835  */
7836 class TaskRanlib : public Task
7838 public:
7840     TaskRanlib(MakeBase &par) : Task(par)
7841         { type = TASK_RANLIB; name = "ranlib"; }
7843     virtual ~TaskRanlib()
7844         {}
7846     virtual bool execute()
7847         {
7848         String fileName = parent.eval(fileNameOpt, "");
7849         String command  = parent.eval(commandOpt, "ranlib");
7851         String fullName = parent.resolve(fileName);
7852         //trace("fullDir:%s", fullDir.c_str());
7853         String cmd = command;
7854         cmd.append(" ");
7855         cmd.append(fullName);
7856         String outbuf, errbuf;
7857         if (!executeCommand(cmd, "", outbuf, errbuf))
7858             return false;
7859         return true;
7860         }
7862     virtual bool parse(Element *elem)
7863         {
7864         if (!parent.getAttribute(elem, "command", commandOpt))
7865             return false;
7866         if (!parent.getAttribute(elem, "file", fileNameOpt))
7867             return false;
7868         if (fileNameOpt.size() == 0)
7869             {
7870             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7871             return false;
7872             }
7873         return true;
7874         }
7876 private:
7878     String fileNameOpt;
7879     String commandOpt;
7880 };
7884 /**
7885  * Compile a resource file into a binary object
7886  */
7887 class TaskRC : public Task
7889 public:
7891     TaskRC(MakeBase &par) : Task(par)
7892         { type = TASK_RC; name = "rc"; }
7894     virtual ~TaskRC()
7895         {}
7897     virtual bool execute()
7898         {
7899         String command  = parent.eval(commandOpt,  "windres");
7900         String flags    = parent.eval(flagsOpt,    "");
7901         String fileName = parent.eval(fileNameOpt, "");
7902         String outName  = parent.eval(outNameOpt,  "");
7904         String fullFile = parent.resolve(fileName);
7905         String fullOut  = parent.resolve(outName);
7906         if (!isNewerThan(fullFile, fullOut))
7907             return true;
7908         String cmd = command;
7909         cmd.append(" -o ");
7910         cmd.append(fullOut);
7911         cmd.append(" ");
7912         cmd.append(flags);
7913         cmd.append(" ");
7914         cmd.append(fullFile);
7916         String outString, errString;
7917         if (!executeCommand(cmd.c_str(), "", outString, errString))
7918             {
7919             error("RC problem: %s", errString.c_str());
7920             return false;
7921             }
7922         return true;
7923         }
7925     virtual bool parse(Element *elem)
7926         {
7927         if (!parent.getAttribute(elem, "command", commandOpt))
7928             return false;
7929         if (!parent.getAttribute(elem, "file", fileNameOpt))
7930             return false;
7931         if (!parent.getAttribute(elem, "out", outNameOpt))
7932             return false;
7933         std::vector<Element *> children = elem->getChildren();
7934         for (unsigned int i=0 ; i<children.size() ; i++)
7935             {
7936             Element *child = children[i];
7937             String tagName = child->getName();
7938             if (tagName == "flags")
7939                 {
7940                 if (!parent.getValue(child, flagsOpt))
7941                     return false;
7942                 }
7943             }
7944         return true;
7945         }
7947 private:
7949     String commandOpt;
7950     String flagsOpt;
7951     String fileNameOpt;
7952     String outNameOpt;
7954 };
7958 /**
7959  *  Collect .o's into a .so or DLL
7960  */
7961 class TaskSharedLib : public Task
7963 public:
7965     TaskSharedLib(MakeBase &par) : Task(par)
7966         { type = TASK_SHAREDLIB; name = "dll"; }
7968     virtual ~TaskSharedLib()
7969         {}
7971     virtual bool execute()
7972         {
7973         String command     = parent.eval(commandOpt, "dllwrap");
7974         String fileName    = parent.eval(fileNameOpt, "");
7975         String defFileName = parent.eval(defFileNameOpt, "");
7976         String impFileName = parent.eval(impFileNameOpt, "");
7977         String libs        = parent.eval(libsOpt, "");
7979         //trace("###########HERE %d", fileSet.size());
7980         bool doit = false;
7981         
7982         String fullOut = parent.resolve(fileName);
7983         //trace("ar fullout: %s", fullOut.c_str());
7984         
7985         if (!listFiles(parent, fileSet))
7986             return false;
7987         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7989         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7990             {
7991             String fname;
7992             if (fileSetDir.size()>0)
7993                 {
7994                 fname.append(fileSetDir);
7995                 fname.append("/");
7996                 }
7997             fname.append(fileSet[i]);
7998             String fullName = parent.resolve(fname);
7999             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8000             if (isNewerThan(fullName, fullOut))
8001                 doit = true;
8002             }
8003         //trace("Needs it:%d", doit);
8004         if (!doit)
8005             {
8006             return true;
8007             }
8009         String cmd = "dllwrap";
8010         cmd.append(" -o ");
8011         cmd.append(fullOut);
8012         if (defFileName.size()>0)
8013             {
8014             cmd.append(" --def ");
8015             cmd.append(defFileName);
8016             cmd.append(" ");
8017             }
8018         if (impFileName.size()>0)
8019             {
8020             cmd.append(" --implib ");
8021             cmd.append(impFileName);
8022             cmd.append(" ");
8023             }
8024         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8025             {
8026             String fname;
8027             if (fileSetDir.size()>0)
8028                 {
8029                 fname.append(fileSetDir);
8030                 fname.append("/");
8031                 }
8032             fname.append(fileSet[i]);
8033             String fullName = parent.resolve(fname);
8035             cmd.append(" ");
8036             cmd.append(fullName);
8037             }
8038         cmd.append(" ");
8039         cmd.append(libs);
8041         String outString, errString;
8042         if (!executeCommand(cmd.c_str(), "", outString, errString))
8043             {
8044             error("<sharedlib> problem: %s", errString.c_str());
8045             return false;
8046             }
8048         return true;
8049         }
8051     virtual bool parse(Element *elem)
8052         {
8053         if (!parent.getAttribute(elem, "command", commandOpt))
8054             return false;
8055         if (!parent.getAttribute(elem, "file", fileNameOpt))
8056             return false;
8057         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8058             return false;
8059         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8060             return false;
8061             
8062         std::vector<Element *> children = elem->getChildren();
8063         for (unsigned int i=0 ; i<children.size() ; i++)
8064             {
8065             Element *child = children[i];
8066             String tagName = child->getName();
8067             if (tagName == "fileset")
8068                 {
8069                 if (!parseFileSet(child, parent, fileSet))
8070                     return false;
8071                 }
8072             else if (tagName == "libs")
8073                 {
8074                 if (!parent.getValue(child, libsOpt))
8075                     return false;
8076                 libsOpt = strip(libsOpt);
8077                 }
8078             }
8079         return true;
8080         }
8082 private:
8084     FileSet fileSet;
8086     String commandOpt;
8087     String fileNameOpt;
8088     String defFileNameOpt;
8089     String impFileNameOpt;
8090     String libsOpt;
8092 };
8096 /**
8097  * Run the "ar" command to archive .o's into a .a
8098  */
8099 class TaskStaticLib : public Task
8101 public:
8103     TaskStaticLib(MakeBase &par) : Task(par)
8104         { type = TASK_STATICLIB; name = "staticlib"; }
8106     virtual ~TaskStaticLib()
8107         {}
8109     virtual bool execute()
8110         {
8111         String command = parent.eval(commandOpt, "ar crv");
8112         String fileName = parent.eval(fileNameOpt, "");
8114         bool doit = false;
8115         
8116         String fullOut = parent.resolve(fileName);
8117         //trace("ar fullout: %s", fullOut.c_str());
8118         
8119         if (!listFiles(parent, fileSet))
8120             return false;
8121         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8122         //trace("###########HERE %s", fileSetDir.c_str());
8124         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8125             {
8126             String fname;
8127             if (fileSetDir.size()>0)
8128                 {
8129                 fname.append(fileSetDir);
8130                 fname.append("/");
8131                 }
8132             fname.append(fileSet[i]);
8133             String fullName = parent.resolve(fname);
8134             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8135             if (isNewerThan(fullName, fullOut))
8136                 doit = true;
8137             }
8138         //trace("Needs it:%d", doit);
8139         if (!doit)
8140             {
8141             return true;
8142             }
8144         String cmd = command;
8145         cmd.append(" ");
8146         cmd.append(fullOut);
8147         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8148             {
8149             String fname;
8150             if (fileSetDir.size()>0)
8151                 {
8152                 fname.append(fileSetDir);
8153                 fname.append("/");
8154                 }
8155             fname.append(fileSet[i]);
8156             String fullName = parent.resolve(fname);
8158             cmd.append(" ");
8159             cmd.append(fullName);
8160             }
8162         String outString, errString;
8163         if (!executeCommand(cmd.c_str(), "", outString, errString))
8164             {
8165             error("<staticlib> problem: %s", errString.c_str());
8166             return false;
8167             }
8169         return true;
8170         }
8173     virtual bool parse(Element *elem)
8174         {
8175         if (!parent.getAttribute(elem, "command", commandOpt))
8176             return false;
8177         if (!parent.getAttribute(elem, "file", fileNameOpt))
8178             return false;
8179             
8180         std::vector<Element *> children = elem->getChildren();
8181         for (unsigned int i=0 ; i<children.size() ; i++)
8182             {
8183             Element *child = children[i];
8184             String tagName = child->getName();
8185             if (tagName == "fileset")
8186                 {
8187                 if (!parseFileSet(child, parent, fileSet))
8188                     return false;
8189                 }
8190             }
8191         return true;
8192         }
8194 private:
8196     FileSet fileSet;
8198     String commandOpt;
8199     String fileNameOpt;
8201 };
8206 /**
8207  * Strip an executable
8208  */
8209 class TaskStrip : public Task
8211 public:
8213     TaskStrip(MakeBase &par) : Task(par)
8214         { type = TASK_STRIP; name = "strip"; }
8216     virtual ~TaskStrip()
8217         {}
8219     virtual bool execute()
8220         {
8221         String command     = parent.eval(commandOpt, "strip");
8222         String fileName    = parent.eval(fileNameOpt, "");
8223         String symFileName = parent.eval(symFileNameOpt, "");
8225         String fullName = parent.resolve(fileName);
8226         //trace("fullDir:%s", fullDir.c_str());
8227         String cmd;
8228         String outbuf, errbuf;
8230         if (symFileName.size()>0)
8231             {
8232             String symFullName = parent.resolve(symFileName);
8233             cmd = "objcopy --only-keep-debug ";
8234             cmd.append(getNativePath(fullName));
8235             cmd.append(" ");
8236             cmd.append(getNativePath(symFullName));
8237             if (!executeCommand(cmd, "", outbuf, errbuf))
8238                 {
8239                 error("<strip> symbol file failed : %s", errbuf.c_str());
8240                 return false;
8241                 }
8242             }
8243             
8244         cmd = command;
8245         cmd.append(getNativePath(fullName));
8246         if (!executeCommand(cmd, "", outbuf, errbuf))
8247             {
8248             error("<strip> failed : %s", errbuf.c_str());
8249             return false;
8250             }
8251         return true;
8252         }
8254     virtual bool parse(Element *elem)
8255         {
8256         if (!parent.getAttribute(elem, "command", commandOpt))
8257             return false;
8258         if (!parent.getAttribute(elem, "file", fileNameOpt))
8259             return false;
8260         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8261             return false;
8262         if (fileNameOpt.size() == 0)
8263             {
8264             error("<strip> requires 'file=\"fileName\"' attribute");
8265             return false;
8266             }
8267         return true;
8268         }
8270 private:
8272     String commandOpt;
8273     String fileNameOpt;
8274     String symFileNameOpt;
8275 };
8278 /**
8279  *
8280  */
8281 class TaskTouch : public Task
8283 public:
8285     TaskTouch(MakeBase &par) : Task(par)
8286         { type = TASK_TOUCH; name = "touch"; }
8288     virtual ~TaskTouch()
8289         {}
8291     virtual bool execute()
8292         {
8293         String fileName = parent.eval(fileNameOpt, "");
8295         String fullName = parent.resolve(fileName);
8296         String nativeFile = getNativePath(fullName);
8297         if (!isRegularFile(fullName) && !isDirectory(fullName))
8298             {            
8299             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8300             int ret = creat(nativeFile.c_str(), 0666);
8301             if (ret != 0) 
8302                 {
8303                 error("<touch> could not create '%s' : %s",
8304                     nativeFile.c_str(), strerror(ret));
8305                 return false;
8306                 }
8307             return true;
8308             }
8309         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8310         if (ret != 0)
8311             {
8312             error("<touch> could not update the modification time for '%s' : %s",
8313                 nativeFile.c_str(), strerror(ret));
8314             return false;
8315             }
8316         return true;
8317         }
8319     virtual bool parse(Element *elem)
8320         {
8321         //trace("touch parse");
8322         if (!parent.getAttribute(elem, "file", fileNameOpt))
8323             return false;
8324         if (fileNameOpt.size() == 0)
8325             {
8326             error("<touch> requires 'file=\"fileName\"' attribute");
8327             return false;
8328             }
8329         return true;
8330         }
8332     String fileNameOpt;
8333 };
8336 /**
8337  *
8338  */
8339 class TaskTstamp : public Task
8341 public:
8343     TaskTstamp(MakeBase &par) : Task(par)
8344         { type = TASK_TSTAMP; name = "tstamp"; }
8346     virtual ~TaskTstamp()
8347         {}
8349     virtual bool execute()
8350         {
8351         return true;
8352         }
8354     virtual bool parse(Element *elem)
8355         {
8356         //trace("tstamp parse");
8357         return true;
8358         }
8359 };
8363 /**
8364  *
8365  */
8366 Task *Task::createTask(Element *elem, int lineNr)
8368     String tagName = elem->getName();
8369     //trace("task:%s", tagName.c_str());
8370     Task *task = NULL;
8371     if (tagName == "cc")
8372         task = new TaskCC(parent);
8373     else if (tagName == "copy")
8374         task = new TaskCopy(parent);
8375     else if (tagName == "delete")
8376         task = new TaskDelete(parent);
8377     else if (tagName == "echo")
8378         task = new TaskEcho(parent);
8379     else if (tagName == "jar")
8380         task = new TaskJar(parent);
8381     else if (tagName == "javac")
8382         task = new TaskJavac(parent);
8383     else if (tagName == "link")
8384         task = new TaskLink(parent);
8385     else if (tagName == "makefile")
8386         task = new TaskMakeFile(parent);
8387     else if (tagName == "mkdir")
8388         task = new TaskMkDir(parent);
8389     else if (tagName == "msgfmt")
8390         task = new TaskMsgFmt(parent);
8391     else if (tagName == "pkg-config")
8392         task = new TaskPkgConfig(parent);
8393     else if (tagName == "ranlib")
8394         task = new TaskRanlib(parent);
8395     else if (tagName == "rc")
8396         task = new TaskRC(parent);
8397     else if (tagName == "sharedlib")
8398         task = new TaskSharedLib(parent);
8399     else if (tagName == "staticlib")
8400         task = new TaskStaticLib(parent);
8401     else if (tagName == "strip")
8402         task = new TaskStrip(parent);
8403     else if (tagName == "touch")
8404         task = new TaskTouch(parent);
8405     else if (tagName == "tstamp")
8406         task = new TaskTstamp(parent);
8407     else
8408         {
8409         error("Unknown task '%s'", tagName.c_str());
8410         return NULL;
8411         }
8413     task->setLine(lineNr);
8415     if (!task->parse(elem))
8416         {
8417         delete task;
8418         return NULL;
8419         }
8420     return task;
8425 //########################################################################
8426 //# T A R G E T
8427 //########################################################################
8429 /**
8430  *
8431  */
8432 class Target : public MakeBase
8435 public:
8437     /**
8438      *
8439      */
8440     Target(Make &par) : parent(par)
8441         { init(); }
8443     /**
8444      *
8445      */
8446     Target(const Target &other) : parent(other.parent)
8447         { init(); assign(other); }
8449     /**
8450      *
8451      */
8452     Target &operator=(const Target &other)
8453         { init(); assign(other); return *this; }
8455     /**
8456      *
8457      */
8458     virtual ~Target()
8459         { cleanup() ; }
8462     /**
8463      *
8464      */
8465     virtual Make &getParent()
8466         { return parent; }
8468     /**
8469      *
8470      */
8471     virtual String getName()
8472         { return name; }
8474     /**
8475      *
8476      */
8477     virtual void setName(const String &val)
8478         { name = val; }
8480     /**
8481      *
8482      */
8483     virtual String getDescription()
8484         { return description; }
8486     /**
8487      *
8488      */
8489     virtual void setDescription(const String &val)
8490         { description = val; }
8492     /**
8493      *
8494      */
8495     virtual void addDependency(const String &val)
8496         { deps.push_back(val); }
8498     /**
8499      *
8500      */
8501     virtual void parseDependencies(const String &val)
8502         { deps = tokenize(val, ", "); }
8504     /**
8505      *
8506      */
8507     virtual std::vector<String> &getDependencies()
8508         { return deps; }
8510     /**
8511      *
8512      */
8513     virtual String getIf()
8514         { return ifVar; }
8516     /**
8517      *
8518      */
8519     virtual void setIf(const String &val)
8520         { ifVar = val; }
8522     /**
8523      *
8524      */
8525     virtual String getUnless()
8526         { return unlessVar; }
8528     /**
8529      *
8530      */
8531     virtual void setUnless(const String &val)
8532         { unlessVar = val; }
8534     /**
8535      *
8536      */
8537     virtual void addTask(Task *val)
8538         { tasks.push_back(val); }
8540     /**
8541      *
8542      */
8543     virtual std::vector<Task *> &getTasks()
8544         { return tasks; }
8546 private:
8548     void init()
8549         {
8550         }
8552     void cleanup()
8553         {
8554         tasks.clear();
8555         }
8557     void assign(const Target &other)
8558         {
8559         //parent      = other.parent;
8560         name        = other.name;
8561         description = other.description;
8562         ifVar       = other.ifVar;
8563         unlessVar   = other.unlessVar;
8564         deps        = other.deps;
8565         tasks       = other.tasks;
8566         }
8568     Make &parent;
8570     String name;
8572     String description;
8574     String ifVar;
8576     String unlessVar;
8578     std::vector<String> deps;
8580     std::vector<Task *> tasks;
8582 };
8591 //########################################################################
8592 //# M A K E
8593 //########################################################################
8596 /**
8597  *
8598  */
8599 class Make : public MakeBase
8602 public:
8604     /**
8605      *
8606      */
8607     Make()
8608         { init(); }
8610     /**
8611      *
8612      */
8613     Make(const Make &other)
8614         { assign(other); }
8616     /**
8617      *
8618      */
8619     Make &operator=(const Make &other)
8620         { assign(other); return *this; }
8622     /**
8623      *
8624      */
8625     virtual ~Make()
8626         { cleanup(); }
8628     /**
8629      *
8630      */
8631     virtual std::map<String, Target> &getTargets()
8632         { return targets; }
8635     /**
8636      *
8637      */
8638     virtual String version()
8639         { return BUILDTOOL_VERSION; }
8641     /**
8642      * Overload a <property>
8643      */
8644     virtual bool specifyProperty(const String &name,
8645                                  const String &value);
8647     /**
8648      *
8649      */
8650     virtual bool run();
8652     /**
8653      *
8654      */
8655     virtual bool run(const String &target);
8659 private:
8661     /**
8662      *
8663      */
8664     void init();
8666     /**
8667      *
8668      */
8669     void cleanup();
8671     /**
8672      *
8673      */
8674     void assign(const Make &other);
8676     /**
8677      *
8678      */
8679     bool executeTask(Task &task);
8682     /**
8683      *
8684      */
8685     bool executeTarget(Target &target,
8686              std::set<String> &targetsCompleted);
8689     /**
8690      *
8691      */
8692     bool execute();
8694     /**
8695      *
8696      */
8697     bool checkTargetDependencies(Target &prop,
8698                     std::vector<String> &depList);
8700     /**
8701      *
8702      */
8703     bool parsePropertyFile(const String &fileName,
8704                            const String &prefix);
8706     /**
8707      *
8708      */
8709     bool parseProperty(Element *elem);
8711     /**
8712      *
8713      */
8714     bool parseFile();
8716     /**
8717      *
8718      */
8719     std::vector<String> glob(const String &pattern);
8722     //###############
8723     //# Fields
8724     //###############
8726     String projectName;
8728     String currentTarget;
8730     String defaultTarget;
8732     String specifiedTarget;
8734     String baseDir;
8736     String description;
8737     
8738     //std::vector<Property> properties;
8739     
8740     std::map<String, Target> targets;
8742     std::vector<Task *> allTasks;
8743     
8744     std::map<String, String> specifiedProperties;
8746 };
8749 //########################################################################
8750 //# C L A S S  M A I N T E N A N C E
8751 //########################################################################
8753 /**
8754  *
8755  */
8756 void Make::init()
8758     uri             = "build.xml";
8759     projectName     = "";
8760     currentTarget   = "";
8761     defaultTarget   = "";
8762     specifiedTarget = "";
8763     baseDir         = "";
8764     description     = "";
8765     envPrefix       = "env.";
8766     pcPrefix        = "pc.";
8767     pccPrefix       = "pcc.";
8768     pclPrefix       = "pcl.";
8769     properties.clear();
8770     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8771         delete allTasks[i];
8772     allTasks.clear();
8777 /**
8778  *
8779  */
8780 void Make::cleanup()
8782     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8783         delete allTasks[i];
8784     allTasks.clear();
8789 /**
8790  *
8791  */
8792 void Make::assign(const Make &other)
8794     uri              = other.uri;
8795     projectName      = other.projectName;
8796     currentTarget    = other.currentTarget;
8797     defaultTarget    = other.defaultTarget;
8798     specifiedTarget  = other.specifiedTarget;
8799     baseDir          = other.baseDir;
8800     description      = other.description;
8801     properties       = other.properties;
8806 //########################################################################
8807 //# U T I L I T Y    T A S K S
8808 //########################################################################
8810 /**
8811  *  Perform a file globbing
8812  */
8813 std::vector<String> Make::glob(const String &pattern)
8815     std::vector<String> res;
8816     return res;
8820 //########################################################################
8821 //# P U B L I C    A P I
8822 //########################################################################
8826 /**
8827  *
8828  */
8829 bool Make::executeTarget(Target &target,
8830              std::set<String> &targetsCompleted)
8833     String name = target.getName();
8835     //First get any dependencies for this target
8836     std::vector<String> deps = target.getDependencies();
8837     for (unsigned int i=0 ; i<deps.size() ; i++)
8838         {
8839         String dep = deps[i];
8840         //Did we do it already?  Skip
8841         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8842             continue;
8843             
8844         std::map<String, Target> &tgts =
8845                target.getParent().getTargets();
8846         std::map<String, Target>::iterator iter =
8847                tgts.find(dep);
8848         if (iter == tgts.end())
8849             {
8850             error("Target '%s' dependency '%s' not found",
8851                       name.c_str(),  dep.c_str());
8852             return false;
8853             }
8854         Target depTarget = iter->second;
8855         if (!executeTarget(depTarget, targetsCompleted))
8856             {
8857             return false;
8858             }
8859         }
8861     status("##### Target : %s\n##### %s", name.c_str(),
8862             target.getDescription().c_str());
8864     //Now let's do the tasks
8865     std::vector<Task *> &tasks = target.getTasks();
8866     for (unsigned int i=0 ; i<tasks.size() ; i++)
8867         {
8868         Task *task = tasks[i];
8869         status("--- %s / %s", name.c_str(), task->getName().c_str());
8870         if (!task->execute())
8871             {
8872             return false;
8873             }
8874         }
8875         
8876     targetsCompleted.insert(name);
8877     
8878     return true;
8883 /**
8884  *  Main execute() method.  Start here and work
8885  *  up the dependency tree 
8886  */
8887 bool Make::execute()
8889     status("######## EXECUTE");
8891     //Determine initial target
8892     if (specifiedTarget.size()>0)
8893         {
8894         currentTarget = specifiedTarget;
8895         }
8896     else if (defaultTarget.size()>0)
8897         {
8898         currentTarget = defaultTarget;
8899         }
8900     else
8901         {
8902         error("execute: no specified or default target requested");
8903         return false;
8904         }
8906     std::map<String, Target>::iterator iter =
8907                targets.find(currentTarget);
8908     if (iter == targets.end())
8909         {
8910         error("Initial target '%s' not found",
8911                  currentTarget.c_str());
8912         return false;
8913         }
8914         
8915     //Now run
8916     Target target = iter->second;
8917     std::set<String> targetsCompleted;
8918     if (!executeTarget(target, targetsCompleted))
8919         {
8920         return false;
8921         }
8923     status("######## EXECUTE COMPLETE");
8924     return true;
8930 /**
8931  *
8932  */
8933 bool Make::checkTargetDependencies(Target &target, 
8934                             std::vector<String> &depList)
8936     String tgtName = target.getName().c_str();
8937     depList.push_back(tgtName);
8939     std::vector<String> deps = target.getDependencies();
8940     for (unsigned int i=0 ; i<deps.size() ; i++)
8941         {
8942         String dep = deps[i];
8943         //First thing entered was the starting Target
8944         if (dep == depList[0])
8945             {
8946             error("Circular dependency '%s' found at '%s'",
8947                       dep.c_str(), tgtName.c_str());
8948             std::vector<String>::iterator diter;
8949             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8950                 {
8951                 error("  %s", diter->c_str());
8952                 }
8953             return false;
8954             }
8956         std::map<String, Target> &tgts =
8957                   target.getParent().getTargets();
8958         std::map<String, Target>::iterator titer = tgts.find(dep);
8959         if (titer == tgts.end())
8960             {
8961             error("Target '%s' dependency '%s' not found",
8962                       tgtName.c_str(), dep.c_str());
8963             return false;
8964             }
8965         if (!checkTargetDependencies(titer->second, depList))
8966             {
8967             return false;
8968             }
8969         }
8970     return true;
8977 static int getword(int pos, const String &inbuf, String &result)
8979     int p = pos;
8980     int len = (int)inbuf.size();
8981     String val;
8982     while (p < len)
8983         {
8984         char ch = inbuf[p];
8985         if (!isalnum(ch) && ch!='.' && ch!='_')
8986             break;
8987         val.push_back(ch);
8988         p++;
8989         }
8990     result = val;
8991     return p;
8997 /**
8998  *
8999  */
9000 bool Make::parsePropertyFile(const String &fileName,
9001                              const String &prefix)
9003     FILE *f = fopen(fileName.c_str(), "r");
9004     if (!f)
9005         {
9006         error("could not open property file %s", fileName.c_str());
9007         return false;
9008         }
9009     int linenr = 0;
9010     while (!feof(f))
9011         {
9012         char buf[256];
9013         if (!fgets(buf, 255, f))
9014             break;
9015         linenr++;
9016         String s = buf;
9017         s = trim(s);
9018         int len = s.size();
9019         if (len == 0)
9020             continue;
9021         if (s[0] == '#')
9022             continue;
9023         String key;
9024         String val;
9025         int p = 0;
9026         int p2 = getword(p, s, key);
9027         if (p2 <= p)
9028             {
9029             error("property file %s, line %d: expected keyword",
9030                     fileName.c_str(), linenr);
9031             return false;
9032             }
9033         if (prefix.size() > 0)
9034             {
9035             key.insert(0, prefix);
9036             }
9038         //skip whitespace
9039         for (p=p2 ; p<len ; p++)
9040             if (!isspace(s[p]))
9041                 break;
9043         if (p>=len || s[p]!='=')
9044             {
9045             error("property file %s, line %d: expected '='",
9046                     fileName.c_str(), linenr);
9047             return false;
9048             }
9049         p++;
9051         //skip whitespace
9052         for ( ; p<len ; p++)
9053             if (!isspace(s[p]))
9054                 break;
9056         /* This way expects a word after the =
9057         p2 = getword(p, s, val);
9058         if (p2 <= p)
9059             {
9060             error("property file %s, line %d: expected value",
9061                     fileName.c_str(), linenr);
9062             return false;
9063             }
9064         */
9065         // This way gets the rest of the line after the =
9066         if (p>=len)
9067             {
9068             error("property file %s, line %d: expected value",
9069                     fileName.c_str(), linenr);
9070             return false;
9071             }
9072         val = s.substr(p);
9073         if (key.size()==0)
9074             continue;
9075         //allow property to be set, even if val=""
9077         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9078         //See if we wanted to overload this property
9079         std::map<String, String>::iterator iter =
9080             specifiedProperties.find(key);
9081         if (iter!=specifiedProperties.end())
9082             {
9083             val = iter->second;
9084             status("overloading property '%s' = '%s'",
9085                    key.c_str(), val.c_str());
9086             }
9087         properties[key] = val;
9088         }
9089     fclose(f);
9090     return true;
9096 /**
9097  *
9098  */
9099 bool Make::parseProperty(Element *elem)
9101     std::vector<Attribute> &attrs = elem->getAttributes();
9102     for (unsigned int i=0 ; i<attrs.size() ; i++)
9103         {
9104         String attrName = attrs[i].getName();
9105         String attrVal  = attrs[i].getValue();
9107         if (attrName == "name")
9108             {
9109             String val;
9110             if (!getAttribute(elem, "value", val))
9111                 return false;
9112             if (val.size() > 0)
9113                 {
9114                 properties[attrVal] = val;
9115                 }
9116             else
9117                 {
9118                 if (!getAttribute(elem, "location", val))
9119                     return false;
9120                 //let the property exist, even if not defined
9121                 properties[attrVal] = val;
9122                 }
9123             //See if we wanted to overload this property
9124             std::map<String, String>::iterator iter =
9125                 specifiedProperties.find(attrVal);
9126             if (iter != specifiedProperties.end())
9127                 {
9128                 val = iter->second;
9129                 status("overloading property '%s' = '%s'",
9130                     attrVal.c_str(), val.c_str());
9131                 properties[attrVal] = val;
9132                 }
9133             }
9134         else if (attrName == "file")
9135             {
9136             String prefix;
9137             if (!getAttribute(elem, "prefix", prefix))
9138                 return false;
9139             if (prefix.size() > 0)
9140                 {
9141                 if (prefix[prefix.size()-1] != '.')
9142                     prefix.push_back('.');
9143                 }
9144             if (!parsePropertyFile(attrName, prefix))
9145                 return false;
9146             }
9147         else if (attrName == "environment")
9148             {
9149             if (attrVal.find('.') != attrVal.npos)
9150                 {
9151                 error("environment prefix cannot have a '.' in it");
9152                 return false;
9153                 }
9154             envPrefix = attrVal;
9155             envPrefix.push_back('.');
9156             }
9157         else if (attrName == "pkg-config")
9158             {
9159             if (attrVal.find('.') != attrVal.npos)
9160                 {
9161                 error("pkg-config prefix cannot have a '.' in it");
9162                 return false;
9163                 }
9164             pcPrefix = attrVal;
9165             pcPrefix.push_back('.');
9166             }
9167         else if (attrName == "pkg-config-cflags")
9168             {
9169             if (attrVal.find('.') != attrVal.npos)
9170                 {
9171                 error("pkg-config-cflags prefix cannot have a '.' in it");
9172                 return false;
9173                 }
9174             pccPrefix = attrVal;
9175             pccPrefix.push_back('.');
9176             }
9177         else if (attrName == "pkg-config-libs")
9178             {
9179             if (attrVal.find('.') != attrVal.npos)
9180                 {
9181                 error("pkg-config-libs prefix cannot have a '.' in it");
9182                 return false;
9183                 }
9184             pclPrefix = attrVal;
9185             pclPrefix.push_back('.');
9186             }
9187         }
9189     return true;
9195 /**
9196  *
9197  */
9198 bool Make::parseFile()
9200     status("######## PARSE : %s", uri.getPath().c_str());
9202     setLine(0);
9204     Parser parser;
9205     Element *root = parser.parseFile(uri.getNativePath());
9206     if (!root)
9207         {
9208         error("Could not open %s for reading",
9209               uri.getNativePath().c_str());
9210         return false;
9211         }
9212     
9213     setLine(root->getLine());
9215     if (root->getChildren().size()==0 ||
9216         root->getChildren()[0]->getName()!="project")
9217         {
9218         error("Main xml element should be <project>");
9219         delete root;
9220         return false;
9221         }
9223     //########## Project attributes
9224     Element *project = root->getChildren()[0];
9225     String s = project->getAttribute("name");
9226     if (s.size() > 0)
9227         projectName = s;
9228     s = project->getAttribute("default");
9229     if (s.size() > 0)
9230         defaultTarget = s;
9231     s = project->getAttribute("basedir");
9232     if (s.size() > 0)
9233         baseDir = s;
9235     //######### PARSE MEMBERS
9236     std::vector<Element *> children = project->getChildren();
9237     for (unsigned int i=0 ; i<children.size() ; i++)
9238         {
9239         Element *elem = children[i];
9240         setLine(elem->getLine());
9241         String tagName = elem->getName();
9243         //########## DESCRIPTION
9244         if (tagName == "description")
9245             {
9246             description = parser.trim(elem->getValue());
9247             }
9249         //######### PROPERTY
9250         else if (tagName == "property")
9251             {
9252             if (!parseProperty(elem))
9253                 return false;
9254             }
9256         //######### TARGET
9257         else if (tagName == "target")
9258             {
9259             String tname   = elem->getAttribute("name");
9260             String tdesc   = elem->getAttribute("description");
9261             String tdeps   = elem->getAttribute("depends");
9262             String tif     = elem->getAttribute("if");
9263             String tunless = elem->getAttribute("unless");
9264             Target target(*this);
9265             target.setName(tname);
9266             target.setDescription(tdesc);
9267             target.parseDependencies(tdeps);
9268             target.setIf(tif);
9269             target.setUnless(tunless);
9270             std::vector<Element *> telems = elem->getChildren();
9271             for (unsigned int i=0 ; i<telems.size() ; i++)
9272                 {
9273                 Element *telem = telems[i];
9274                 Task breeder(*this);
9275                 Task *task = breeder.createTask(telem, telem->getLine());
9276                 if (!task)
9277                     return false;
9278                 allTasks.push_back(task);
9279                 target.addTask(task);
9280                 }
9282             //Check name
9283             if (tname.size() == 0)
9284                 {
9285                 error("no name for target");
9286                 return false;
9287                 }
9288             //Check for duplicate name
9289             if (targets.find(tname) != targets.end())
9290                 {
9291                 error("target '%s' already defined", tname.c_str());
9292                 return false;
9293                 }
9294             //more work than targets[tname]=target, but avoids default allocator
9295             targets.insert(std::make_pair<String, Target>(tname, target));
9296             }
9297         //######### none of the above
9298         else
9299             {
9300             error("unknown toplevel tag: <%s>", tagName.c_str());
9301             return false;
9302             }
9304         }
9306     std::map<String, Target>::iterator iter;
9307     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9308         {
9309         Target tgt = iter->second;
9310         std::vector<String> depList;
9311         if (!checkTargetDependencies(tgt, depList))
9312             {
9313             return false;
9314             }
9315         }
9318     delete root;
9319     status("######## PARSE COMPLETE");
9320     return true;
9324 /**
9325  * Overload a <property>
9326  */
9327 bool Make::specifyProperty(const String &name, const String &value)
9329     if (specifiedProperties.find(name) != specifiedProperties.end())
9330         {
9331         error("Property %s already specified", name.c_str());
9332         return false;
9333         }
9334     specifiedProperties[name] = value;
9335     return true;
9340 /**
9341  *
9342  */
9343 bool Make::run()
9345     if (!parseFile())
9346         return false;
9347         
9348     if (!execute())
9349         return false;
9351     return true;
9357 /**
9358  * Get a formatted MM:SS.sss time elapsed string
9359  */ 
9360 static String
9361 timeDiffString(struct timeval &x, struct timeval &y)
9363     long microsX  = x.tv_usec;
9364     long secondsX = x.tv_sec;
9365     long microsY  = y.tv_usec;
9366     long secondsY = y.tv_sec;
9367     if (microsX < microsY)
9368         {
9369         microsX += 1000000;
9370         secondsX -= 1;
9371         }
9373     int seconds = (int)(secondsX - secondsY);
9374     int millis  = (int)((microsX - microsY)/1000);
9376     int minutes = seconds/60;
9377     seconds -= minutes*60;
9378     char buf[80];
9379     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9380     String ret = buf;
9381     return ret;
9382     
9385 /**
9386  *
9387  */
9388 bool Make::run(const String &target)
9390     status("####################################################");
9391     status("#   %s", version().c_str());
9392     status("####################################################");
9393     struct timeval timeStart, timeEnd;
9394     ::gettimeofday(&timeStart, NULL);
9395     specifiedTarget = target;
9396     if (!run())
9397         return false;
9398     ::gettimeofday(&timeEnd, NULL);
9399     String timeStr = timeDiffString(timeEnd, timeStart);
9400     status("####################################################");
9401     status("#   BuildTool Completed : %s", timeStr.c_str());
9402     status("####################################################");
9403     return true;
9412 }// namespace buildtool
9413 //########################################################################
9414 //# M A I N
9415 //########################################################################
9417 typedef buildtool::String String;
9419 /**
9420  *  Format an error message in printf() style
9421  */
9422 static void error(const char *fmt, ...)
9424     va_list ap;
9425     va_start(ap, fmt);
9426     fprintf(stderr, "BuildTool error: ");
9427     vfprintf(stderr, fmt, ap);
9428     fprintf(stderr, "\n");
9429     va_end(ap);
9433 static bool parseProperty(const String &s, String &name, String &val)
9435     int len = s.size();
9436     int i;
9437     for (i=0 ; i<len ; i++)
9438         {
9439         char ch = s[i];
9440         if (ch == '=')
9441             break;
9442         name.push_back(ch);
9443         }
9444     if (i>=len || s[i]!='=')
9445         {
9446         error("property requires -Dname=value");
9447         return false;
9448         }
9449     i++;
9450     for ( ; i<len ; i++)
9451         {
9452         char ch = s[i];
9453         val.push_back(ch);
9454         }
9455     return true;
9459 /**
9460  * Compare a buffer with a key, for the length of the key
9461  */
9462 static bool sequ(const String &buf, const char *key)
9464     int len = buf.size();
9465     for (int i=0 ; key[i] && i<len ; i++)
9466         {
9467         if (key[i] != buf[i])
9468             return false;
9469         }        
9470     return true;
9473 static void usage(int argc, char **argv)
9475     printf("usage:\n");
9476     printf("   %s [options] [target]\n", argv[0]);
9477     printf("Options:\n");
9478     printf("  -help, -h              print this message\n");
9479     printf("  -version               print the version information and exit\n");
9480     printf("  -file <file>           use given buildfile\n");
9481     printf("  -f <file>                 ''\n");
9482     printf("  -D<property>=<value>   use value for given property\n");
9488 /**
9489  * Parse the command-line args, get our options,
9490  * and run this thing
9491  */   
9492 static bool parseOptions(int argc, char **argv)
9494     if (argc < 1)
9495         {
9496         error("Cannot parse arguments");
9497         return false;
9498         }
9500     buildtool::Make make;
9502     String target;
9504     //char *progName = argv[0];
9505     for (int i=1 ; i<argc ; i++)
9506         {
9507         String arg = argv[i];
9508         if (arg.size()>1 && arg[0]=='-')
9509             {
9510             if (arg == "-h" || arg == "-help")
9511                 {
9512                 usage(argc,argv);
9513                 return true;
9514                 }
9515             else if (arg == "-version")
9516                 {
9517                 printf("%s", make.version().c_str());
9518                 return true;
9519                 }
9520             else if (arg == "-f" || arg == "-file")
9521                 {
9522                 if (i>=argc)
9523                    {
9524                    usage(argc, argv);
9525                    return false;
9526                    }
9527                 i++; //eat option
9528                 make.setURI(argv[i]);
9529                 }
9530             else if (arg.size()>2 && sequ(arg, "-D"))
9531                 {
9532                 String s = arg.substr(2, arg.size());
9533                 String name, value;
9534                 if (!parseProperty(s, name, value))
9535                    {
9536                    usage(argc, argv);
9537                    return false;
9538                    }
9539                 if (!make.specifyProperty(name, value))
9540                     return false;
9541                 }
9542             else
9543                 {
9544                 error("Unknown option:%s", arg.c_str());
9545                 return false;
9546                 }
9547             }
9548         else
9549             {
9550             if (target.size()>0)
9551                 {
9552                 error("only one initial target");
9553                 usage(argc, argv);
9554                 return false;
9555                 }
9556             target = arg;
9557             }
9558         }
9560     //We have the options.  Now execute them
9561     if (!make.run(target))
9562         return false;
9564     return true;
9570 /*
9571 static bool runMake()
9573     buildtool::Make make;
9574     if (!make.run())
9575         return false;
9576     return true;
9580 static bool pkgConfigTest()
9582     buildtool::PkgConfig pkgConfig;
9583     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9584         return false;
9585     return true;
9590 static bool depTest()
9592     buildtool::DepTool deptool;
9593     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9594     if (!deptool.generateDependencies("build.dep"))
9595         return false;
9596     std::vector<buildtool::FileRec> res =
9597            deptool.loadDepFile("build.dep");
9598     if (res.size() == 0)
9599         return false;
9600     return true;
9603 static bool popenTest()
9605     buildtool::Make make;
9606     buildtool::String out, err;
9607     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9608     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9609     return true;
9613 static bool propFileTest()
9615     buildtool::Make make;
9616     make.parsePropertyFile("test.prop", "test.");
9617     return true;
9619 */
9621 int main(int argc, char **argv)
9624     if (!parseOptions(argc, argv))
9625         return 1;
9626     /*
9627     if (!popenTest())
9628         return 1;
9630     if (!depTest())
9631         return 1;
9632     if (!propFileTest())
9633         return 1;
9634     if (runMake())
9635         return 1;
9636     */
9637     return 0;
9641 //########################################################################
9642 //# E N D 
9643 //########################################################################