Code

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