Code

6c38eddb71775cb33b5582ea2dbb4bf6ec83677a
[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.0"
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      * replace variable refs in a sting like ${a} with their values
3282      */
3283     bool getSubstitutions(const String &s, String &result);
3285     int line;
3288 };
3292 /**
3293  * Define the pkg-config class here, since it will be used in MakeBase method
3294  * implementations. 
3295  */
3296 class PkgConfig : public MakeBase
3299 public:
3301     /**
3302      *
3303      */
3304     PkgConfig()
3305         {
3306                  path   = ".";
3307                  prefix = "/target";
3308                  init();
3309                  }
3311     /**
3312      *
3313      */
3314     PkgConfig(const PkgConfig &other)
3315         { assign(other); }
3317     /**
3318      *
3319      */
3320     PkgConfig &operator=(const PkgConfig &other)
3321         { assign(other); return *this; }
3323     /**
3324      *
3325      */
3326     virtual ~PkgConfig()
3327         { }
3329     /**
3330      *
3331      */
3332     virtual String getName()
3333         { return name; }
3335     /**
3336      *
3337      */
3338     virtual String getPath()
3339         { return path; }
3341     /**
3342      *
3343      */
3344     virtual void setPath(const String &val)
3345         { path = val; }
3347     /**
3348      *
3349      */
3350     virtual String getPrefix()
3351         { return prefix; }
3353     /**
3354      *  Allow the user to override the prefix in the file
3355      */
3356     virtual void setPrefix(const String &val)
3357         { prefix = val; }
3359     /**
3360      *
3361      */
3362     virtual String getDescription()
3363         { return description; }
3365     /**
3366      *
3367      */
3368     virtual String getCflags()
3369         { return cflags; }
3371     /**
3372      *
3373      */
3374     virtual String getLibs()
3375         { return libs; }
3377     /**
3378      *
3379      */
3380     virtual String getAll()
3381         {
3382          String ret = cflags;
3383          ret.append(" ");
3384          ret.append(libs);
3385          return ret;
3386         }
3388     /**
3389      *
3390      */
3391     virtual String getVersion()
3392         { return version; }
3394     /**
3395      *
3396      */
3397     virtual int getMajorVersion()
3398         { return majorVersion; }
3400     /**
3401      *
3402      */
3403     virtual int getMinorVersion()
3404         { return minorVersion; }
3406     /**
3407      *
3408      */
3409     virtual int getMicroVersion()
3410         { return microVersion; }
3412     /**
3413      *
3414      */
3415     virtual std::map<String, String> &getAttributes()
3416         { return attrs; }
3418     /**
3419      *
3420      */
3421     virtual std::vector<String> &getRequireList()
3422         { return requireList; }
3424     /**
3425      *  Read a file for its details
3426      */         
3427     virtual bool readFile(const String &fileName);
3429     /**
3430      *  Read a file for its details
3431      */         
3432     virtual bool query(const String &name);
3434 private:
3436     void init()
3437         {
3438         //do not set path and prefix here
3439         name         = "";
3440         description  = "";
3441         cflags       = "";
3442         libs         = "";
3443         requires     = "";
3444         version      = "";
3445         majorVersion = 0;
3446         minorVersion = 0;
3447         microVersion = 0;
3448         fileName     = "";
3449         attrs.clear();
3450         requireList.clear();
3451         }
3453     void assign(const PkgConfig &other)
3454         {
3455         name         = other.name;
3456         path         = other.path;
3457         prefix       = other.prefix;
3458         description  = other.description;
3459         cflags       = other.cflags;
3460         libs         = other.libs;
3461         requires     = other.requires;
3462         version      = other.version;
3463         majorVersion = other.majorVersion;
3464         minorVersion = other.minorVersion;
3465         microVersion = other.microVersion;
3466         fileName     = other.fileName;
3467         attrs        = other.attrs;
3468         requireList  = other.requireList;
3469         }
3473     int get(int pos);
3475     int skipwhite(int pos);
3477     int getword(int pos, String &ret);
3479     /**
3480      * Very important
3481      */         
3482     bool parseRequires();
3484     void parseVersion();
3486     bool parseLine(const String &lineBuf);
3488     bool parse(const String &buf);
3490     void dumpAttrs();
3492     String name;
3494     String path;
3496     String prefix;
3498     String description;
3500     String cflags;
3502     String libs;
3504     String requires;
3506     String version;
3508     int majorVersion;
3510     int minorVersion;
3512     int microVersion;
3514     String fileName;
3516     std::map<String, String> attrs;
3518     std::vector<String> requireList;
3520     char *parsebuf;
3521     int parselen;
3522 };
3527 /**
3528  *  Print a printf()-like formatted error message
3529  */
3530 void MakeBase::error(const char *fmt, ...)
3532     va_list args;
3533     va_start(args,fmt);
3534     fprintf(stderr, "Make error line %d: ", line);
3535     vfprintf(stderr, fmt, args);
3536     fprintf(stderr, "\n");
3537     va_end(args) ;
3542 /**
3543  *  Print a printf()-like formatted trace message
3544  */
3545 void MakeBase::status(const char *fmt, ...)
3547     va_list args;
3548     va_start(args,fmt);
3549     //fprintf(stdout, " ");
3550     vfprintf(stdout, fmt, args);
3551     fprintf(stdout, "\n");
3552     va_end(args) ;
3556 /**
3557  *  Resolve another path relative to this one
3558  */
3559 String MakeBase::resolve(const String &otherPath)
3561     URI otherURI(otherPath);
3562     URI fullURI = uri.resolve(otherURI);
3563     String ret = fullURI.toString();
3564     return ret;
3568 /**
3569  *  Print a printf()-like formatted trace message
3570  */
3571 void MakeBase::trace(const char *fmt, ...)
3573     va_list args;
3574     va_start(args,fmt);
3575     fprintf(stdout, "Make: ");
3576     vfprintf(stdout, fmt, args);
3577     fprintf(stdout, "\n");
3578     va_end(args) ;
3583 /**
3584  *  Check if a given string matches a given regex pattern
3585  */
3586 bool MakeBase::regexMatch(const String &str, const String &pattern)
3588     const TRexChar *terror = NULL;
3589     const TRexChar *cpat = pattern.c_str();
3590     TRex *expr = trex_compile(cpat, &terror);
3591     if (!expr)
3592         {
3593         if (!terror)
3594             terror = "undefined";
3595         error("compilation error [%s]!\n", terror);
3596         return false;
3597         } 
3599     bool ret = true;
3601     const TRexChar *cstr = str.c_str();
3602     if (trex_match(expr, cstr))
3603         {
3604         ret = true;
3605         }
3606     else
3607         {
3608         ret = false;
3609         }
3611     trex_free(expr);
3613     return ret;
3616 /**
3617  *  Return the suffix, if any, of a file name
3618  */
3619 String MakeBase::getSuffix(const String &fname)
3621     if (fname.size() < 2)
3622         return "";
3623     unsigned int pos = fname.find_last_of('.');
3624     if (pos == fname.npos)
3625         return "";
3626     pos++;
3627     String res = fname.substr(pos, fname.size()-pos);
3628     //trace("suffix:%s", res.c_str()); 
3629     return res;
3634 /**
3635  * Break up a string into substrings delimited the characters
3636  * in delimiters.  Null-length substrings are ignored
3637  */  
3638 std::vector<String> MakeBase::tokenize(const String &str,
3639                                 const String &delimiters)
3642     std::vector<String> res;
3643     char *del = (char *)delimiters.c_str();
3644     String dmp;
3645     for (unsigned int i=0 ; i<str.size() ; i++)
3646         {
3647         char ch = str[i];
3648         char *p = (char *)0;
3649         for (p=del ; *p ; p++)
3650             if (*p == ch)
3651                 break;
3652         if (*p)
3653             {
3654             if (dmp.size() > 0)
3655                 {
3656                 res.push_back(dmp);
3657                 dmp.clear();
3658                 }
3659             }
3660         else
3661             {
3662             dmp.push_back(ch);
3663             }
3664         }
3665     //Add tail
3666     if (dmp.size() > 0)
3667         {
3668         res.push_back(dmp);
3669         dmp.clear();
3670         }
3672     return res;
3677 /**
3678  *  replace runs of whitespace with a single space
3679  */
3680 String MakeBase::strip(const String &s)
3682     int len = s.size();
3683     String stripped;
3684     for (int i = 0 ; i<len ; i++)
3685         {
3686         char ch = s[i];
3687         if (isspace(ch))
3688             {
3689             stripped.push_back(' ');
3690             for ( ; i<len ; i++)
3691                 {
3692                 ch = s[i];
3693                 if (!isspace(ch))
3694                     {
3695                     stripped.push_back(ch);
3696                     break;
3697                     }
3698                 }
3699             }
3700         else
3701             {
3702             stripped.push_back(ch);
3703             }
3704         }
3705     return stripped;
3708 /**
3709  *  remove leading whitespace from each line
3710  */
3711 String MakeBase::leftJustify(const String &s)
3713     String out;
3714     int len = s.size();
3715     for (int i = 0 ; i<len ; )
3716         {
3717         char ch;
3718         //Skip to first visible character
3719         while (i<len)
3720             {
3721             ch = s[i];
3722             if (ch == '\n' || ch == '\r'
3723               || !isspace(ch))
3724                   break;
3725             i++;
3726             }
3727         //Copy the rest of the line
3728         while (i<len)
3729             {
3730             ch = s[i];
3731             if (ch == '\n' || ch == '\r')
3732                 {
3733                 if (ch != '\r')
3734                     out.push_back('\n');
3735                 i++;
3736                 break;
3737                 }
3738             else
3739                 {
3740                 out.push_back(ch);
3741                 }
3742             i++;
3743             }
3744         }
3745     return out;
3749 /**
3750  *  Removes whitespace from beginning and end of a string
3751  */
3752 String MakeBase::trim(const String &s)
3754     if (s.size() < 1)
3755         return s;
3756     
3757     //Find first non-ws char
3758     unsigned int begin = 0;
3759     for ( ; begin < s.size() ; begin++)
3760         {
3761         if (!isspace(s[begin]))
3762             break;
3763         }
3765     //Find first non-ws char, going in reverse
3766     unsigned int end = s.size() - 1;
3767     for ( ; end > begin ; end--)
3768         {
3769         if (!isspace(s[end]))
3770             break;
3771         }
3772     //trace("begin:%d  end:%d", begin, end);
3774     String res = s.substr(begin, end-begin+1);
3775     return res;
3779 /**
3780  *  Return a lower case version of the given string
3781  */
3782 String MakeBase::toLower(const String &s)
3784     if (s.size()==0)
3785         return s;
3787     String ret;
3788     for(unsigned int i=0; i<s.size() ; i++)
3789         {
3790         ret.push_back(tolower(s[i]));
3791         }
3792     return ret;
3796 /**
3797  * Return the native format of the canonical
3798  * path which we store
3799  */
3800 String MakeBase::getNativePath(const String &path)
3802 #ifdef __WIN32__
3803     String npath;
3804     unsigned int firstChar = 0;
3805     if (path.size() >= 3)
3806         {
3807         if (path[0] == '/' &&
3808             isalpha(path[1]) &&
3809             path[2] == ':')
3810             firstChar++;
3811         }
3812     for (unsigned int i=firstChar ; i<path.size() ; i++)
3813         {
3814         char ch = path[i];
3815         if (ch == '/')
3816             npath.push_back('\\');
3817         else
3818             npath.push_back(ch);
3819         }
3820     return npath;
3821 #else
3822     return path;
3823 #endif
3827 #ifdef __WIN32__
3828 #include <tchar.h>
3830 static String win32LastError()
3833     DWORD dw = GetLastError(); 
3835     LPVOID str;
3836     FormatMessage(
3837         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3838         FORMAT_MESSAGE_FROM_SYSTEM,
3839         NULL,
3840         dw,
3841         0,
3842         (LPTSTR) &str,
3843         0, NULL );
3844     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3845     if(p != NULL)
3846         { // lose CRLF
3847         *p = _T('\0');
3848         }
3849     String ret = (char *)str;
3850     LocalFree(str);
3852     return ret;
3854 #endif
3858 /**
3859  * Execute a system call, using pipes to send data to the
3860  * program's stdin,  and reading stdout and stderr.
3861  */
3862 bool MakeBase::executeCommand(const String &command,
3863                               const String &inbuf,
3864                               String &outbuf,
3865                               String &errbuf)
3868     status("============ cmd ============\n%s\n=============================",
3869                 command.c_str());
3871     outbuf.clear();
3872     errbuf.clear();
3873     
3874 #ifdef __WIN32__
3876     /*
3877     I really hate having win32 code in this program, but the
3878     read buffer in command.com and cmd.exe are just too small
3879     for the large commands we need for compiling and linking.
3880     */
3882     bool ret = true;
3884     //# Allocate a separate buffer for safety
3885     char *paramBuf = new char[command.size() + 1];
3886     if (!paramBuf)
3887        {
3888        error("executeCommand cannot allocate command buffer");
3889        return false;
3890        }
3891     strcpy(paramBuf, (char *)command.c_str());
3893     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3894     //# to see how Win32 pipes work
3896     //# Create pipes
3897     SECURITY_ATTRIBUTES saAttr; 
3898     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3899     saAttr.bInheritHandle = TRUE; 
3900     saAttr.lpSecurityDescriptor = NULL; 
3901     HANDLE stdinRead,  stdinWrite;
3902     HANDLE stdoutRead, stdoutWrite;
3903     HANDLE stderrRead, stderrWrite;
3904     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3905         {
3906         error("executeProgram: could not create pipe");
3907         delete[] paramBuf;
3908         return false;
3909         } 
3910     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3911     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3912         {
3913         error("executeProgram: could not create pipe");
3914         delete[] paramBuf;
3915         return false;
3916         } 
3917     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3918     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3919         {
3920         error("executeProgram: could not create pipe");
3921         delete[] paramBuf;
3922         return false;
3923         } 
3924     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3926     // Create the process
3927     STARTUPINFO siStartupInfo;
3928     PROCESS_INFORMATION piProcessInfo;
3929     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3930     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3931     siStartupInfo.cb = sizeof(siStartupInfo);
3932     siStartupInfo.hStdError   =  stderrWrite;
3933     siStartupInfo.hStdOutput  =  stdoutWrite;
3934     siStartupInfo.hStdInput   =  stdinRead;
3935     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3936    
3937     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3938                 0, NULL, NULL, &siStartupInfo,
3939                 &piProcessInfo))
3940         {
3941         error("executeCommand : could not create process : %s",
3942                     win32LastError().c_str());
3943         ret = false;
3944         }
3946     delete[] paramBuf;
3948     DWORD bytesWritten;
3949     if (inbuf.size()>0 &&
3950         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3951                &bytesWritten, NULL))
3952         {
3953         error("executeCommand: could not write to pipe");
3954         return false;
3955         }    
3956     if (!CloseHandle(stdinWrite))
3957         {          
3958         error("executeCommand: could not close write pipe");
3959         return false;
3960         }
3961     if (!CloseHandle(stdoutWrite))
3962         {
3963         error("executeCommand: could not close read pipe");
3964         return false;
3965         }
3966     if (!CloseHandle(stderrWrite))
3967         {
3968         error("executeCommand: could not close read pipe");
3969         return false;
3970         }
3972     bool lastLoop = false;
3973     while (true)
3974         {
3975         DWORD avail;
3976         DWORD bytesRead;
3977         char readBuf[4096];
3979         //trace("## stderr");
3980         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3981         if (avail > 0)
3982             {
3983             bytesRead = 0;
3984             if (avail>4096) avail = 4096;
3985             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3986             if (bytesRead > 0)
3987                 {
3988                 for (unsigned int i=0 ; i<bytesRead ; i++)
3989                     errbuf.push_back(readBuf[i]);
3990                 }
3991             }
3993         //trace("## stdout");
3994         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3995         if (avail > 0)
3996             {
3997             bytesRead = 0;
3998             if (avail>4096) avail = 4096;
3999             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4000             if (bytesRead > 0)
4001                 {
4002                 for (unsigned int i=0 ; i<bytesRead ; i++)
4003                     outbuf.push_back(readBuf[i]);
4004                 }
4005             }
4006             
4007         //Was this the final check after program done?
4008         if (lastLoop)
4009             break;
4011         DWORD exitCode;
4012         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4013         if (exitCode != STILL_ACTIVE)
4014             lastLoop = true;
4016         Sleep(10);
4017         }    
4018     //trace("outbuf:%s", outbuf.c_str());
4019     if (!CloseHandle(stdoutRead))
4020         {
4021         error("executeCommand: could not close read pipe");
4022         return false;
4023         }
4024     if (!CloseHandle(stderrRead))
4025         {
4026         error("executeCommand: could not close read pipe");
4027         return false;
4028         }
4030     DWORD exitCode;
4031     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4032     //trace("exit code:%d", exitCode);
4033     if (exitCode != 0)
4034         {
4035         ret = false;
4036         }
4037     
4038     CloseHandle(piProcessInfo.hProcess);
4039     CloseHandle(piProcessInfo.hThread);
4041     return ret;
4043 #else //do it unix-style
4045     String s;
4046     FILE *f = popen(command.c_str(), "r");
4047     int errnum = 0;
4048     if (f)
4049         {
4050         while (true)
4051             {
4052             int ch = fgetc(f);
4053             if (ch < 0)
4054                 break;
4055             s.push_back((char)ch);
4056             }
4057         errnum = pclose(f);
4058         }
4059     outbuf = s;
4060     if (errnum != 0)
4061         {
4062         error("exec of command '%s' failed : %s",
4063              command.c_str(), strerror(errno));
4064         return false;
4065         }
4066     else
4067         return true;
4069 #endif
4070
4075 bool MakeBase::listDirectories(const String &baseName,
4076                               const String &dirName,
4077                               std::vector<String> &res)
4079     res.push_back(dirName);
4080     String fullPath = baseName;
4081     if (dirName.size()>0)
4082         {
4083         fullPath.append("/");
4084         fullPath.append(dirName);
4085         }
4086     DIR *dir = opendir(fullPath.c_str());
4087     while (true)
4088         {
4089         struct dirent *de = readdir(dir);
4090         if (!de)
4091             break;
4093         //Get the directory member name
4094         String s = de->d_name;
4095         if (s.size() == 0 || s[0] == '.')
4096             continue;
4097         String childName = dirName;
4098         childName.append("/");
4099         childName.append(s);
4101         String fullChildPath = baseName;
4102         fullChildPath.append("/");
4103         fullChildPath.append(childName);
4104         struct stat finfo;
4105         String childNative = getNativePath(fullChildPath);
4106         if (stat(childNative.c_str(), &finfo)<0)
4107             {
4108             error("cannot stat file:%s", childNative.c_str());
4109             }
4110         else if (S_ISDIR(finfo.st_mode))
4111             {
4112             //trace("directory: %s", childName.c_str());
4113             if (!listDirectories(baseName, childName, res))
4114                 return false;
4115             }
4116         }
4117     closedir(dir);
4119     return true;
4123 bool MakeBase::listFiles(const String &baseDir,
4124                          const String &dirName,
4125                          std::vector<String> &res)
4127     String fullDir = baseDir;
4128     if (dirName.size()>0)
4129         {
4130         fullDir.append("/");
4131         fullDir.append(dirName);
4132         }
4133     String dirNative = getNativePath(fullDir);
4135     std::vector<String> subdirs;
4136     DIR *dir = opendir(dirNative.c_str());
4137     if (!dir)
4138         {
4139         error("Could not open directory %s : %s",
4140               dirNative.c_str(), strerror(errno));
4141         return false;
4142         }
4143     while (true)
4144         {
4145         struct dirent *de = readdir(dir);
4146         if (!de)
4147             break;
4149         //Get the directory member name
4150         String s = de->d_name;
4151         if (s.size() == 0 || s[0] == '.')
4152             continue;
4153         String childName;
4154         if (dirName.size()>0)
4155             {
4156             childName.append(dirName);
4157             childName.append("/");
4158             }
4159         childName.append(s);
4160         String fullChild = baseDir;
4161         fullChild.append("/");
4162         fullChild.append(childName);
4163         
4164         if (isDirectory(fullChild))
4165             {
4166             //trace("directory: %s", childName.c_str());
4167             if (!listFiles(baseDir, childName, res))
4168                 return false;
4169             continue;
4170             }
4171         else if (!isRegularFile(fullChild))
4172             {
4173             error("unknown file:%s", childName.c_str());
4174             return false;
4175             }
4177        //all done!
4178         res.push_back(childName);
4180         }
4181     closedir(dir);
4183     return true;
4187 /**
4188  * Several different classes extend MakeBase.  By "propRef", we mean
4189  * the one holding the properties.  Likely "Make" itself
4190  */
4191 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4193     //before doing the list,  resolve any property references
4194     //that might have been specified in the directory name, such as ${src}
4195     String fsDir = fileSet.getDirectory();
4196     String dir;
4197     if (!propRef.getSubstitutions(fsDir, dir))
4198         return false;
4199     String baseDir = propRef.resolve(dir);
4200     std::vector<String> fileList;
4201     if (!listFiles(baseDir, "", fileList))
4202         return false;
4204     std::vector<String> includes = fileSet.getIncludes();
4205     std::vector<String> excludes = fileSet.getExcludes();
4207     std::vector<String> incs;
4208     std::vector<String>::iterator iter;
4210     std::sort(fileList.begin(), fileList.end());
4212     //If there are <includes>, then add files to the output
4213     //in the order of the include list
4214     if (includes.size()==0)
4215         incs = fileList;
4216     else
4217         {
4218         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4219             {
4220             String &pattern = *iter;
4221             std::vector<String>::iterator siter;
4222             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4223                 {
4224                 String s = *siter;
4225                 if (regexMatch(s, pattern))
4226                     {
4227                     //trace("INCLUDED:%s", s.c_str());
4228                     incs.push_back(s);
4229                     }
4230                 }
4231             }
4232         }
4234     //Now trim off the <excludes>
4235     std::vector<String> res;
4236     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4237         {
4238         String s = *iter;
4239         bool skipme = false;
4240         std::vector<String>::iterator siter;
4241         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4242             {
4243             String &pattern = *siter;
4244             if (regexMatch(s, pattern))
4245                 {
4246                 //trace("EXCLUDED:%s", s.c_str());
4247                 skipme = true;
4248                 break;
4249                 }
4250             }
4251         if (!skipme)
4252             res.push_back(s);
4253         }
4254         
4255     fileSet.setFiles(res);
4257     return true;
4261 /**
4262  * 0 == all, 1 = cflags, 2 = libs
4263  */ 
4264 bool MakeBase::pkgConfigRecursive(const String packageName,
4265                                   const String &path, 
4266                                   const String &prefix, 
4267                                   int query,
4268                                   String &result,
4269                                   std::set<String> &deplist) 
4271     PkgConfig pkgConfig;
4272     if (path.size() > 0)
4273         pkgConfig.setPath(path);
4274     if (prefix.size() > 0)
4275             pkgConfig.setPrefix(prefix);
4276     if (!pkgConfig.query(packageName))
4277         return false;
4278     if (query == 0)
4279         result = pkgConfig.getAll();
4280     else if (query == 1)
4281         result = pkgConfig.getCflags();
4282     else
4283         result = pkgConfig.getLibs();
4284     deplist.insert(packageName);
4285     std::vector<String> list = pkgConfig.getRequireList();
4286     for (unsigned int i = 0 ; i<list.size() ; i++)
4287         {
4288         String depPkgName = list[i];
4289         if (deplist.find(depPkgName) != deplist.end())
4290             continue;
4291         String val;
4292         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4293             {
4294             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4295             return false;
4296             }
4297         result.append(" ");
4298         result.append(val);
4299         }
4301     return true;
4304 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4306     std::set<String> deplist;
4307     String path = getProperty("pkg-config-path");
4308     if (path.size()>0)
4309         path = resolve(path);
4310     String prefix = getProperty("pkg-config-prefix");
4311     String val;
4312     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4313         return false;
4314     result = val;
4315     return true;
4320 /**
4321  * replace a variable ref like ${a} with a value
4322  */
4323 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4325     String varname = propertyName;
4326     if (envPrefix.size() > 0 &&
4327         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4328         {
4329         varname = varname.substr(envPrefix.size());
4330         char *envstr = getenv(varname.c_str());
4331         if (!envstr)
4332             {
4333             error("environment variable '%s' not defined", varname.c_str());
4334             return false;
4335             }
4336         result = envstr;
4337         }
4338     else if (pcPrefix.size() > 0 &&
4339         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4340         {
4341         varname = varname.substr(pcPrefix.size());
4342         String val;
4343         if (!pkgConfigQuery(varname, 0, val))
4344             return false;
4345         result = val;
4346         }
4347     else if (pccPrefix.size() > 0 &&
4348         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4349         {
4350         varname = varname.substr(pccPrefix.size());
4351         String val;
4352         if (!pkgConfigQuery(varname, 1, val))
4353             return false;
4354         result = val;
4355         }
4356     else if (pclPrefix.size() > 0 &&
4357         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4358         {
4359         varname = varname.substr(pclPrefix.size());
4360         String val;
4361         if (!pkgConfigQuery(varname, 2, val))
4362             return false;
4363         result = val;
4364         }
4365     else
4366         {
4367         std::map<String, String>::iterator iter;
4368         iter = properties.find(varname);
4369         if (iter != properties.end())
4370             {
4371             result = iter->second;
4372             }
4373         else
4374             {
4375             error("property '%s' not found", varname.c_str());
4376             return false;
4377             }
4378         }
4379     return true;
4385 /**
4386  * Analyse a string, looking for any substitutions or other
4387  * things that need resilution 
4388  */
4389 bool MakeBase::getSubstitutions(const String &str, String &result)
4391     String s = trim(str);
4392     int len = (int)s.size();
4393     String val;
4394     for (int i=0 ; i<len ; i++)
4395         {
4396         char ch = s[i];
4397         if (ch == '$' && s[i+1] == '{')
4398             {
4399             String varname;
4400             int j = i+2;
4401             for ( ; j<len ; j++)
4402                 {
4403                 ch = s[j];
4404                 if (ch == '$' && s[j+1] == '{')
4405                     {
4406                     error("attribute %s cannot have nested variable references",
4407                            s.c_str());
4408                     return false;
4409                     }
4410                 else if (ch == '}')
4411                     {
4412                     varname = trim(varname);
4413                     String varval;
4414                     if (!lookupProperty(varname, varval))
4415                         return false;
4416                     val.append(varval);
4417                     break;
4418                     }
4419                 else
4420                     {
4421                     varname.push_back(ch);
4422                     }
4423                 }
4424             i = j;
4425             }
4426         else
4427             {
4428             val.push_back(ch);
4429             }
4430         }
4431     result = val;
4432     return true;
4437 /**
4438  * replace variable refs like ${a} with their values
4439  * Assume that the string has already been syntax validated
4440  */
4441 String MakeBase::eval(const String &s, const String &defaultVal)
4443     if (s.size()==0)
4444         return defaultVal;
4445     String ret;
4446     if (getSubstitutions(s, ret))
4447         return ret;
4448     else
4449         return defaultVal;
4453 /**
4454  * replace variable refs like ${a} with their values
4455  * return true or false
4456  * Assume that the string has already been syntax validated
4457  */
4458 bool MakeBase::evalBool(const String &s, bool defaultVal)
4460     if (s.size()==0)
4461         return defaultVal;
4462     String val = eval(s, "false");
4463     if (s == "true" || s == "TRUE")
4464         return true;
4465     else
4466         return defaultVal;
4470 /**
4471  * Get a string attribute, testing it for proper syntax and
4472  * property names.
4473  */
4474 bool MakeBase::getAttribute(Element *elem, const String &name,
4475                                     String &result)
4477     String s = elem->getAttribute(name);
4478     String tmp;
4479     bool ret = getSubstitutions(s, tmp);
4480     if (ret)
4481         result = s;  //assign -if- ok
4482     return ret;
4486 /**
4487  * Get a string value, testing it for proper syntax and
4488  * property names.
4489  */
4490 bool MakeBase::getValue(Element *elem, String &result)
4492     String s = elem->getValue();
4493     String tmp;
4494     bool ret = getSubstitutions(s, tmp);
4495     if (ret)
4496         result = s;  //assign -if- ok
4497     return ret;
4503 /**
4504  * Parse a <patternset> entry
4505  */  
4506 bool MakeBase::parsePatternSet(Element *elem,
4507                           MakeBase &propRef,
4508                           std::vector<String> &includes,
4509                           std::vector<String> &excludes
4510                           )
4512     std::vector<Element *> children  = elem->getChildren();
4513     for (unsigned int i=0 ; i<children.size() ; i++)
4514         {
4515         Element *child = children[i];
4516         String tagName = child->getName();
4517         if (tagName == "exclude")
4518             {
4519             String fname;
4520             if (!propRef.getAttribute(child, "name", fname))
4521                 return false;
4522             //trace("EXCLUDE: %s", fname.c_str());
4523             excludes.push_back(fname);
4524             }
4525         else if (tagName == "include")
4526             {
4527             String fname;
4528             if (!propRef.getAttribute(child, "name", fname))
4529                 return false;
4530             //trace("INCLUDE: %s", fname.c_str());
4531             includes.push_back(fname);
4532             }
4533         }
4535     return true;
4541 /**
4542  * Parse a <fileset> entry, and determine which files
4543  * should be included
4544  */  
4545 bool MakeBase::parseFileSet(Element *elem,
4546                           MakeBase &propRef,
4547                           FileSet &fileSet)
4549     String name = elem->getName();
4550     if (name != "fileset")
4551         {
4552         error("expected <fileset>");
4553         return false;
4554         }
4557     std::vector<String> includes;
4558     std::vector<String> excludes;
4560     //A fileset has one implied patternset
4561     if (!parsePatternSet(elem, propRef, includes, excludes))
4562         {
4563         return false;
4564         }
4565     //Look for child tags, including more patternsets
4566     std::vector<Element *> children  = elem->getChildren();
4567     for (unsigned int i=0 ; i<children.size() ; i++)
4568         {
4569         Element *child = children[i];
4570         String tagName = child->getName();
4571         if (tagName == "patternset")
4572             {
4573             if (!parsePatternSet(child, propRef, includes, excludes))
4574                 {
4575                 return false;
4576                 }
4577             }
4578         }
4580     String dir;
4581     //Now do the stuff
4582     //Get the base directory for reading file names
4583     if (!propRef.getAttribute(elem, "dir", dir))
4584         return false;
4586     fileSet.setDirectory(dir);
4587     fileSet.setIncludes(includes);
4588     fileSet.setExcludes(excludes);
4589     
4590     /*
4591     std::vector<String> fileList;
4592     if (dir.size() > 0)
4593         {
4594         String baseDir = propRef.resolve(dir);
4595         if (!listFiles(baseDir, "", includes, excludes, fileList))
4596             return false;
4597         }
4598     std::sort(fileList.begin(), fileList.end());
4599     result = fileList;
4600     */
4602     
4603     /*
4604     for (unsigned int i=0 ; i<result.size() ; i++)
4605         {
4606         trace("RES:%s", result[i].c_str());
4607         }
4608     */
4610     
4611     return true;
4614 /**
4615  * Parse a <filelist> entry.  This is far simpler than FileSet,
4616  * since no directory scanning is needed.  The file names are listed
4617  * explicitly.
4618  */  
4619 bool MakeBase::parseFileList(Element *elem,
4620                           MakeBase &propRef,
4621                           FileList &fileList)
4623     std::vector<String> fnames;
4624     //Look for child tags, namely "file"
4625     std::vector<Element *> children  = elem->getChildren();
4626     for (unsigned int i=0 ; i<children.size() ; i++)
4627         {
4628         Element *child = children[i];
4629         String tagName = child->getName();
4630         if (tagName == "file")
4631             {
4632             String fname = child->getAttribute("name");
4633             if (fname.size()==0)
4634                 {
4635                 error("<file> element requires name="" attribute");
4636                 return false;
4637                 }
4638             fnames.push_back(fname);
4639             }
4640         else
4641             {
4642             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4643             return false;
4644             }
4645         }
4647     String dir;
4648     //Get the base directory for reading file names
4649     if (!propRef.getAttribute(elem, "dir", dir))
4650         return false;
4651     fileList.setDirectory(dir);
4652     fileList.setFiles(fnames);
4654     return true;
4659 /**
4660  * Create a directory, making intermediate dirs
4661  * if necessary
4662  */                  
4663 bool MakeBase::createDirectory(const String &dirname)
4665     //trace("## createDirectory: %s", dirname.c_str());
4666     //## first check if it exists
4667     struct stat finfo;
4668     String nativeDir = getNativePath(dirname);
4669     char *cnative = (char *) nativeDir.c_str();
4670 #ifdef __WIN32__
4671     if (strlen(cnative)==2 && cnative[1]==':')
4672         return true;
4673 #endif
4674     if (stat(cnative, &finfo)==0)
4675         {
4676         if (!S_ISDIR(finfo.st_mode))
4677             {
4678             error("mkdir: file %s exists but is not a directory",
4679                   cnative);
4680             return false;
4681             }
4682         else //exists
4683             {
4684             return true;
4685             }
4686         }
4688     //## 2: pull off the last path segment, if any,
4689     //## to make the dir 'above' this one, if necessary
4690     unsigned int pos = dirname.find_last_of('/');
4691     if (pos>0 && pos != dirname.npos)
4692         {
4693         String subpath = dirname.substr(0, pos);
4694         //A letter root (c:) ?
4695         if (!createDirectory(subpath))
4696             return false;
4697         }
4698         
4699     //## 3: now make
4700 #ifdef __WIN32__
4701     if (mkdir(cnative)<0)
4702 #else
4703     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4704 #endif
4705         {
4706         error("cannot make directory '%s' : %s",
4707                  cnative, strerror(errno));
4708         return false;
4709         }
4710         
4711     return true;
4715 /**
4716  * Remove a directory recursively
4717  */ 
4718 bool MakeBase::removeDirectory(const String &dirName)
4720     char *dname = (char *)dirName.c_str();
4722     DIR *dir = opendir(dname);
4723     if (!dir)
4724         {
4725         //# Let this fail nicely.
4726         return true;
4727         //error("error opening directory %s : %s", dname, strerror(errno));
4728         //return false;
4729         }
4730     
4731     while (true)
4732         {
4733         struct dirent *de = readdir(dir);
4734         if (!de)
4735             break;
4737         //Get the directory member name
4738         String s = de->d_name;
4739         if (s.size() == 0 || s[0] == '.')
4740             continue;
4741         String childName;
4742         if (dirName.size() > 0)
4743             {
4744             childName.append(dirName);
4745             childName.append("/");
4746             }
4747         childName.append(s);
4750         struct stat finfo;
4751         String childNative = getNativePath(childName);
4752         char *cnative = (char *)childNative.c_str();
4753         if (stat(cnative, &finfo)<0)
4754             {
4755             error("cannot stat file:%s", cnative);
4756             }
4757         else if (S_ISDIR(finfo.st_mode))
4758             {
4759             //trace("DEL dir: %s", childName.c_str());
4760             if (!removeDirectory(childName))
4761                 {
4762                 return false;
4763                 }
4764             }
4765         else if (!S_ISREG(finfo.st_mode))
4766             {
4767             //trace("not regular: %s", cnative);
4768             }
4769         else
4770             {
4771             //trace("DEL file: %s", childName.c_str());
4772             if (remove(cnative)<0)
4773                 {
4774                 error("error deleting %s : %s",
4775                      cnative, strerror(errno));
4776                 return false;
4777                 }
4778             }
4779         }
4780     closedir(dir);
4782     //Now delete the directory
4783     String native = getNativePath(dirName);
4784     if (rmdir(native.c_str())<0)
4785         {
4786         error("could not delete directory %s : %s",
4787             native.c_str() , strerror(errno));
4788         return false;
4789         }
4791     return true;
4792     
4796 /**
4797  * Copy a file from one name to another. Perform only if needed
4798  */ 
4799 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4801     //# 1 Check up-to-date times
4802     String srcNative = getNativePath(srcFile);
4803     struct stat srcinfo;
4804     if (stat(srcNative.c_str(), &srcinfo)<0)
4805         {
4806         error("source file %s for copy does not exist",
4807                  srcNative.c_str());
4808         return false;
4809         }
4811     String destNative = getNativePath(destFile);
4812     struct stat destinfo;
4813     if (stat(destNative.c_str(), &destinfo)==0)
4814         {
4815         if (destinfo.st_mtime >= srcinfo.st_mtime)
4816             return true;
4817         }
4818         
4819     //# 2 prepare a destination directory if necessary
4820     unsigned int pos = destFile.find_last_of('/');
4821     if (pos != destFile.npos)
4822         {
4823         String subpath = destFile.substr(0, pos);
4824         if (!createDirectory(subpath))
4825             return false;
4826         }
4828     //# 3 do the data copy
4829 #ifndef __WIN32__
4831     FILE *srcf = fopen(srcNative.c_str(), "rb");
4832     if (!srcf)
4833         {
4834         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4835         return false;
4836         }
4837     FILE *destf = fopen(destNative.c_str(), "wb");
4838     if (!destf)
4839         {
4840         error("copyFile cannot open %s for writing", srcNative.c_str());
4841         return false;
4842         }
4844     while (!feof(srcf))
4845         {
4846         int ch = fgetc(srcf);
4847         if (ch<0)
4848             break;
4849         fputc(ch, destf);
4850         }
4852     fclose(destf);
4853     fclose(srcf);
4855 #else
4856     
4857     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4858         {
4859         error("copyFile from %s to %s failed",
4860              srcNative.c_str(), destNative.c_str());
4861         return false;
4862         }
4863         
4864 #endif /* __WIN32__ */
4867     return true;
4872 /**
4873  * Tests if the file exists and is a regular file
4874  */ 
4875 bool MakeBase::isRegularFile(const String &fileName)
4877     String native = getNativePath(fileName);
4878     struct stat finfo;
4879     
4880     //Exists?
4881     if (stat(native.c_str(), &finfo)<0)
4882         return false;
4885     //check the file mode
4886     if (!S_ISREG(finfo.st_mode))
4887         return false;
4889     return true;
4892 /**
4893  * Tests if the file exists and is a directory
4894  */ 
4895 bool MakeBase::isDirectory(const String &fileName)
4897     String native = getNativePath(fileName);
4898     struct stat finfo;
4899     
4900     //Exists?
4901     if (stat(native.c_str(), &finfo)<0)
4902         return false;
4905     //check the file mode
4906     if (!S_ISDIR(finfo.st_mode))
4907         return false;
4909     return true;
4914 /**
4915  * Tests is the modification of fileA is newer than fileB
4916  */ 
4917 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4919     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4920     String nativeA = getNativePath(fileA);
4921     struct stat infoA;
4922     //IF source does not exist, NOT newer
4923     if (stat(nativeA.c_str(), &infoA)<0)
4924         {
4925         return false;
4926         }
4928     String nativeB = getNativePath(fileB);
4929     struct stat infoB;
4930     //IF dest does not exist, YES, newer
4931     if (stat(nativeB.c_str(), &infoB)<0)
4932         {
4933         return true;
4934         }
4936     //check the actual times
4937     if (infoA.st_mtime > infoB.st_mtime)
4938         {
4939         return true;
4940         }
4942     return false;
4946 //########################################################################
4947 //# P K G    C O N F I G
4948 //########################################################################
4951 /**
4952  * Get a character from the buffer at pos.  If out of range,
4953  * return -1 for safety
4954  */
4955 int PkgConfig::get(int pos)
4957     if (pos>parselen)
4958         return -1;
4959     return parsebuf[pos];
4964 /**
4965  *  Skip over all whitespace characters beginning at pos.  Return
4966  *  the position of the first non-whitespace character.
4967  *  Pkg-config is line-oriented, so check for newline
4968  */
4969 int PkgConfig::skipwhite(int pos)
4971     while (pos < parselen)
4972         {
4973         int ch = get(pos);
4974         if (ch < 0)
4975             break;
4976         if (!isspace(ch))
4977             break;
4978         pos++;
4979         }
4980     return pos;
4984 /**
4985  *  Parse the buffer beginning at pos, for a word.  Fill
4986  *  'ret' with the result.  Return the position after the
4987  *  word.
4988  */
4989 int PkgConfig::getword(int pos, String &ret)
4991     while (pos < parselen)
4992         {
4993         int ch = get(pos);
4994         if (ch < 0)
4995             break;
4996         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
4997             break;
4998         ret.push_back((char)ch);
4999         pos++;
5000         }
5001     return pos;
5004 bool PkgConfig::parseRequires()
5006     if (requires.size() == 0)
5007         return true;
5008     parsebuf = (char *)requires.c_str();
5009     parselen = requires.size();
5010     int pos = 0;
5011     while (pos < parselen)
5012         {
5013         pos = skipwhite(pos);
5014         String val;
5015         int pos2 = getword(pos, val);
5016         if (pos2 == pos)
5017             break;
5018         pos = pos2;
5019         //trace("val %s", val.c_str());
5020         requireList.push_back(val);
5021         }
5022     return true;
5026 static int getint(const String str)
5028     char *s = (char *)str.c_str();
5029     char *ends = NULL;
5030     long val = strtol(s, &ends, 10);
5031     if (ends == s)
5032         return 0L;
5033     else
5034         return val;
5037 void PkgConfig::parseVersion()
5039     if (version.size() == 0)
5040         return;
5041     String s1, s2, s3;
5042     unsigned int pos = 0;
5043     unsigned int pos2 = version.find('.', pos);
5044     if (pos2 == version.npos)
5045         {
5046         s1 = version;
5047         }
5048     else
5049         {
5050         s1 = version.substr(pos, pos2-pos);
5051         pos = pos2;
5052         pos++;
5053         if (pos < version.size())
5054             {
5055             pos2 = version.find('.', pos);
5056             if (pos2 == version.npos)
5057                 {
5058                 s2 = version.substr(pos, version.size()-pos);
5059                 }
5060             else
5061                 {
5062                 s2 = version.substr(pos, pos2-pos);
5063                 pos = pos2;
5064                 pos++;
5065                 if (pos < version.size())
5066                     s3 = version.substr(pos, pos2-pos);
5067                 }
5068             }
5069         }
5071     majorVersion = getint(s1);
5072     minorVersion = getint(s2);
5073     microVersion = getint(s3);
5074     //trace("version:%d.%d.%d", majorVersion,
5075     //          minorVersion, microVersion );
5079 bool PkgConfig::parseLine(const String &lineBuf)
5081     parsebuf = (char *)lineBuf.c_str();
5082     parselen = lineBuf.size();
5083     int pos = 0;
5084     
5085     while (pos < parselen)
5086         {
5087         String attrName;
5088         pos = skipwhite(pos);
5089         int ch = get(pos);
5090         if (ch == '#')
5091             {
5092             //comment.  eat the rest of the line
5093             while (pos < parselen)
5094                 {
5095                 ch = get(pos);
5096                 if (ch == '\n' || ch < 0)
5097                     break;
5098                 pos++;
5099                 }
5100             continue;
5101             }
5102         pos = getword(pos, attrName);
5103         if (attrName.size() == 0)
5104             continue;
5105         
5106         pos = skipwhite(pos);
5107         ch = get(pos);
5108         if (ch != ':' && ch != '=')
5109             {
5110             error("expected ':' or '='");
5111             return false;
5112             }
5113         pos++;
5114         pos = skipwhite(pos);
5115         String attrVal;
5116         while (pos < parselen)
5117             {
5118             ch = get(pos);
5119             if (ch == '\n' || ch < 0)
5120                 break;
5121             else if (ch == '$' && get(pos+1) == '{')
5122                 {
5123                 //#  this is a ${substitution}
5124                 pos += 2;
5125                 String subName;
5126                 while (pos < parselen)
5127                     {
5128                     ch = get(pos);
5129                     if (ch < 0)
5130                         {
5131                         error("unterminated substitution");
5132                         return false;
5133                         }
5134                     else if (ch == '}')
5135                         break;
5136                     else
5137                         subName.push_back((char)ch);
5138                     pos++;
5139                     }
5140                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5141                 if (subName == "prefix" && prefix.size()>0)
5142                     {
5143                     attrVal.append(prefix);
5144                     //trace("prefix override:%s", prefix.c_str());
5145                     }
5146                 else
5147                     {
5148                     String subVal = attrs[subName];
5149                     //trace("subVal:%s", subVal.c_str());
5150                     attrVal.append(subVal);
5151                     }
5152                 }
5153             else
5154                 attrVal.push_back((char)ch);
5155             pos++;
5156             }
5158         attrVal = trim(attrVal);
5159         attrs[attrName] = attrVal;
5161         String attrNameL = toLower(attrName);
5163         if (attrNameL == "name")
5164             name = attrVal;
5165         else if (attrNameL == "description")
5166             description = attrVal;
5167         else if (attrNameL == "cflags")
5168             cflags = attrVal;
5169         else if (attrNameL == "libs")
5170             libs = attrVal;
5171         else if (attrNameL == "requires")
5172             requires = attrVal;
5173         else if (attrNameL == "version")
5174             version = attrVal;
5176         //trace("name:'%s'  value:'%s'",
5177         //      attrName.c_str(), attrVal.c_str());
5178         }
5180     return true;
5184 bool PkgConfig::parse(const String &buf)
5186     init();
5188     String line;
5189     int lineNr = 0;
5190     for (unsigned int p=0 ; p<buf.size() ; p++)
5191         {
5192         int ch = buf[p];
5193         if (ch == '\n' || ch == '\r')
5194             {
5195             if (!parseLine(line))
5196                 return false;
5197             line.clear();
5198             lineNr++;
5199             }
5200         else
5201             {
5202             line.push_back(ch);
5203             }
5204         }
5205     if (line.size()>0)
5206         {
5207         if (!parseLine(line))
5208             return false;
5209         }
5211     parseRequires();
5212     parseVersion();
5214     return true;
5220 void PkgConfig::dumpAttrs()
5222     //trace("### PkgConfig attributes for %s", fileName.c_str());
5223     std::map<String, String>::iterator iter;
5224     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5225         {
5226         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5227         }
5231 bool PkgConfig::readFile(const String &fname)
5233     fileName = getNativePath(fname);
5235     FILE *f = fopen(fileName.c_str(), "r");
5236     if (!f)
5237         {
5238         error("cannot open file '%s' for reading", fileName.c_str());
5239         return false;
5240         }
5241     String buf;
5242     while (true)
5243         {
5244         int ch = fgetc(f);
5245         if (ch < 0)
5246             break;
5247         buf.push_back((char)ch);
5248         }
5249     fclose(f);
5251     //trace("####### File:\n%s", buf.c_str());
5252     if (!parse(buf))
5253         {
5254         return false;
5255         }
5257     //dumpAttrs();
5259     return true;
5264 bool PkgConfig::query(const String &pkgName)
5266     name = pkgName;
5268     String fname = path;
5269     fname.append("/");
5270     fname.append(name);
5271     fname.append(".pc");
5273     if (!readFile(fname))
5274         return false;
5275     
5276     return true;
5283 //########################################################################
5284 //# D E P T O O L
5285 //########################################################################
5289 /**
5290  *  Class which holds information for each file.
5291  */
5292 class FileRec
5294 public:
5296     typedef enum
5297         {
5298         UNKNOWN,
5299         CFILE,
5300         HFILE,
5301         OFILE
5302         } FileType;
5304     /**
5305      *  Constructor
5306      */
5307     FileRec()
5308         { init(); type = UNKNOWN; }
5310     /**
5311      *  Copy constructor
5312      */
5313     FileRec(const FileRec &other)
5314         { init(); assign(other); }
5315     /**
5316      *  Constructor
5317      */
5318     FileRec(int typeVal)
5319         { init(); type = typeVal; }
5320     /**
5321      *  Assignment operator
5322      */
5323     FileRec &operator=(const FileRec &other)
5324         { init(); assign(other); return *this; }
5327     /**
5328      *  Destructor
5329      */
5330     ~FileRec()
5331         {}
5333     /**
5334      *  Directory part of the file name
5335      */
5336     String path;
5338     /**
5339      *  Base name, sans directory and suffix
5340      */
5341     String baseName;
5343     /**
5344      *  File extension, such as cpp or h
5345      */
5346     String suffix;
5348     /**
5349      *  Type of file: CFILE, HFILE, OFILE
5350      */
5351     int type;
5353     /**
5354      * Used to list files ref'd by this one
5355      */
5356     std::map<String, FileRec *> files;
5359 private:
5361     void init()
5362         {
5363         }
5365     void assign(const FileRec &other)
5366         {
5367         type     = other.type;
5368         baseName = other.baseName;
5369         suffix   = other.suffix;
5370         files    = other.files;
5371         }
5373 };
5377 /**
5378  *  Simpler dependency record
5379  */
5380 class DepRec
5382 public:
5384     /**
5385      *  Constructor
5386      */
5387     DepRec()
5388         {init();}
5390     /**
5391      *  Copy constructor
5392      */
5393     DepRec(const DepRec &other)
5394         {init(); assign(other);}
5395     /**
5396      *  Constructor
5397      */
5398     DepRec(const String &fname)
5399         {init(); name = fname; }
5400     /**
5401      *  Assignment operator
5402      */
5403     DepRec &operator=(const DepRec &other)
5404         {init(); assign(other); return *this;}
5407     /**
5408      *  Destructor
5409      */
5410     ~DepRec()
5411         {}
5413     /**
5414      *  Directory part of the file name
5415      */
5416     String path;
5418     /**
5419      *  Base name, without the path and suffix
5420      */
5421     String name;
5423     /**
5424      *  Suffix of the source
5425      */
5426     String suffix;
5429     /**
5430      * Used to list files ref'd by this one
5431      */
5432     std::vector<String> files;
5435 private:
5437     void init()
5438         {
5439         }
5441     void assign(const DepRec &other)
5442         {
5443         path     = other.path;
5444         name     = other.name;
5445         suffix   = other.suffix;
5446         files    = other.files; //avoid recursion
5447         }
5449 };
5452 class DepTool : public MakeBase
5454 public:
5456     /**
5457      *  Constructor
5458      */
5459     DepTool()
5460         { init(); }
5462     /**
5463      *  Copy constructor
5464      */
5465     DepTool(const DepTool &other)
5466         { init(); assign(other); }
5468     /**
5469      *  Assignment operator
5470      */
5471     DepTool &operator=(const DepTool &other)
5472         { init(); assign(other); return *this; }
5475     /**
5476      *  Destructor
5477      */
5478     ~DepTool()
5479         {}
5482     /**
5483      *  Reset this section of code
5484      */
5485     virtual void init();
5486     
5487     /**
5488      *  Reset this section of code
5489      */
5490     virtual void assign(const DepTool &other)
5491         {
5492         }
5493     
5494     /**
5495      *  Sets the source directory which will be scanned
5496      */
5497     virtual void setSourceDirectory(const String &val)
5498         { sourceDir = val; }
5500     /**
5501      *  Returns the source directory which will be scanned
5502      */
5503     virtual String getSourceDirectory()
5504         { return sourceDir; }
5506     /**
5507      *  Sets the list of files within the directory to analyze
5508      */
5509     virtual void setFileList(const std::vector<String> &list)
5510         { fileList = list; }
5512     /**
5513      * Creates the list of all file names which will be
5514      * candidates for further processing.  Reads make.exclude
5515      * to see which files for directories to leave out.
5516      */
5517     virtual bool createFileList();
5520     /**
5521      *  Generates the forward dependency list
5522      */
5523     virtual bool generateDependencies();
5526     /**
5527      *  Generates the forward dependency list, saving the file
5528      */
5529     virtual bool generateDependencies(const String &);
5532     /**
5533      *  Load a dependency file
5534      */
5535     std::vector<DepRec> loadDepFile(const String &fileName);
5537     /**
5538      *  Load a dependency file, generating one if necessary
5539      */
5540     std::vector<DepRec> getDepFile(const String &fileName,
5541               bool forceRefresh);
5543     /**
5544      *  Save a dependency file
5545      */
5546     bool saveDepFile(const String &fileName);
5549 private:
5552     /**
5553      *
5554      */
5555     void parseName(const String &fullname,
5556                    String &path,
5557                    String &basename,
5558                    String &suffix);
5560     /**
5561      *
5562      */
5563     int get(int pos);
5565     /**
5566      *
5567      */
5568     int skipwhite(int pos);
5570     /**
5571      *
5572      */
5573     int getword(int pos, String &ret);
5575     /**
5576      *
5577      */
5578     bool sequ(int pos, const char *key);
5580     /**
5581      *
5582      */
5583     bool addIncludeFile(FileRec *frec, const String &fname);
5585     /**
5586      *
5587      */
5588     bool scanFile(const String &fname, FileRec *frec);
5590     /**
5591      *
5592      */
5593     bool processDependency(FileRec *ofile, FileRec *include);
5595     /**
5596      *
5597      */
5598     String sourceDir;
5600     /**
5601      *
5602      */
5603     std::vector<String> fileList;
5605     /**
5606      *
5607      */
5608     std::vector<String> directories;
5610     /**
5611      * A list of all files which will be processed for
5612      * dependencies.
5613      */
5614     std::map<String, FileRec *> allFiles;
5616     /**
5617      * The list of .o files, and the
5618      * dependencies upon them.
5619      */
5620     std::map<String, FileRec *> oFiles;
5622     int depFileSize;
5623     char *depFileBuf;
5625     static const int readBufSize = 8192;
5626     char readBuf[8193];//byte larger
5628 };
5634 /**
5635  *  Clean up after processing.  Called by the destructor, but should
5636  *  also be called before the object is reused.
5637  */
5638 void DepTool::init()
5640     sourceDir = ".";
5642     fileList.clear();
5643     directories.clear();
5644     
5645     //clear output file list
5646     std::map<String, FileRec *>::iterator iter;
5647     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5648         delete iter->second;
5649     oFiles.clear();
5651     //allFiles actually contains the master copies. delete them
5652     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5653         delete iter->second;
5654     allFiles.clear(); 
5661 /**
5662  *  Parse a full path name into path, base name, and suffix
5663  */
5664 void DepTool::parseName(const String &fullname,
5665                         String &path,
5666                         String &basename,
5667                         String &suffix)
5669     if (fullname.size() < 2)
5670         return;
5672     unsigned int pos = fullname.find_last_of('/');
5673     if (pos != fullname.npos && pos<fullname.size()-1)
5674         {
5675         path = fullname.substr(0, pos);
5676         pos++;
5677         basename = fullname.substr(pos, fullname.size()-pos);
5678         }
5679     else
5680         {
5681         path = "";
5682         basename = fullname;
5683         }
5685     pos = basename.find_last_of('.');
5686     if (pos != basename.npos && pos<basename.size()-1)
5687         {
5688         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5689         basename = basename.substr(0, pos);
5690         }
5692     //trace("parsename:%s %s %s", path.c_str(),
5693     //        basename.c_str(), suffix.c_str()); 
5698 /**
5699  *  Generate our internal file list.
5700  */
5701 bool DepTool::createFileList()
5704     for (unsigned int i=0 ; i<fileList.size() ; i++)
5705         {
5706         String fileName = fileList[i];
5707         //trace("## FileName:%s", fileName.c_str());
5708         String path;
5709         String basename;
5710         String sfx;
5711         parseName(fileName, path, basename, sfx);
5712         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5713             sfx == "cc" || sfx == "CC")
5714             {
5715             FileRec *fe         = new FileRec(FileRec::CFILE);
5716             fe->path            = path;
5717             fe->baseName        = basename;
5718             fe->suffix          = sfx;
5719             allFiles[fileName]  = fe;
5720             }
5721         else if (sfx == "h"   ||  sfx == "hh"  ||
5722                  sfx == "hpp" ||  sfx == "hxx")
5723             {
5724             FileRec *fe         = new FileRec(FileRec::HFILE);
5725             fe->path            = path;
5726             fe->baseName        = basename;
5727             fe->suffix          = sfx;
5728             allFiles[fileName]  = fe;
5729             }
5730         }
5732     if (!listDirectories(sourceDir, "", directories))
5733         return false;
5734         
5735     return true;
5742 /**
5743  * Get a character from the buffer at pos.  If out of range,
5744  * return -1 for safety
5745  */
5746 int DepTool::get(int pos)
5748     if (pos>depFileSize)
5749         return -1;
5750     return depFileBuf[pos];
5755 /**
5756  *  Skip over all whitespace characters beginning at pos.  Return
5757  *  the position of the first non-whitespace character.
5758  */
5759 int DepTool::skipwhite(int pos)
5761     while (pos < depFileSize)
5762         {
5763         int ch = get(pos);
5764         if (ch < 0)
5765             break;
5766         if (!isspace(ch))
5767             break;
5768         pos++;
5769         }
5770     return pos;
5774 /**
5775  *  Parse the buffer beginning at pos, for a word.  Fill
5776  *  'ret' with the result.  Return the position after the
5777  *  word.
5778  */
5779 int DepTool::getword(int pos, String &ret)
5781     while (pos < depFileSize)
5782         {
5783         int ch = get(pos);
5784         if (ch < 0)
5785             break;
5786         if (isspace(ch))
5787             break;
5788         ret.push_back((char)ch);
5789         pos++;
5790         }
5791     return pos;
5794 /**
5795  * Return whether the sequence of characters in the buffer
5796  * beginning at pos match the key,  for the length of the key
5797  */
5798 bool DepTool::sequ(int pos, const char *key)
5800     while (*key)
5801         {
5802         if (*key != get(pos))
5803             return false;
5804         key++; pos++;
5805         }
5806     return true;
5811 /**
5812  *  Add an include file name to a file record.  If the name
5813  *  is not found in allFiles explicitly, try prepending include
5814  *  directory names to it and try again.
5815  */
5816 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5818     //# if the name is an exact match to a path name
5819     //# in allFiles, like "myinc.h"
5820     std::map<String, FileRec *>::iterator iter =
5821            allFiles.find(iname);
5822     if (iter != allFiles.end()) //already exists
5823         {
5824          //h file in same dir
5825         FileRec *other = iter->second;
5826         //trace("local: '%s'", iname.c_str());
5827         frec->files[iname] = other;
5828         return true;
5829         }
5830     else 
5831         {
5832         //## Ok, it was not found directly
5833         //look in other dirs
5834         std::vector<String>::iterator diter;
5835         for (diter=directories.begin() ;
5836              diter!=directories.end() ; diter++)
5837             {
5838             String dfname = *diter;
5839             dfname.append("/");
5840             dfname.append(iname);
5841             URI fullPathURI(dfname);  //normalize path name
5842             String fullPath = fullPathURI.getPath();
5843             if (fullPath[0] == '/')
5844                 fullPath = fullPath.substr(1);
5845             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5846             iter = allFiles.find(fullPath);
5847             if (iter != allFiles.end())
5848                 {
5849                 FileRec *other = iter->second;
5850                 //trace("other: '%s'", iname.c_str());
5851                 frec->files[fullPath] = other;
5852                 return true;
5853                 }
5854             }
5855         }
5856     return true;
5861 /**
5862  *  Lightly parse a file to find the #include directives.  Do
5863  *  a bit of state machine stuff to make sure that the directive
5864  *  is valid.  (Like not in a comment).
5865  */
5866 bool DepTool::scanFile(const String &fname, FileRec *frec)
5868     String fileName;
5869     if (sourceDir.size() > 0)
5870         {
5871         fileName.append(sourceDir);
5872         fileName.append("/");
5873         }
5874     fileName.append(fname);
5875     String nativeName = getNativePath(fileName);
5876     FILE *f = fopen(nativeName.c_str(), "r");
5877     if (!f)
5878         {
5879         error("Could not open '%s' for reading", fname.c_str());
5880         return false;
5881         }
5882     String buf;
5883     while (!feof(f))
5884         {
5885         int nrbytes = fread(readBuf, 1, readBufSize, f);
5886         readBuf[nrbytes] = '\0';
5887         buf.append(readBuf);
5888         }
5889     fclose(f);
5891     depFileSize = buf.size();
5892     depFileBuf  = (char *)buf.c_str();
5893     int pos = 0;
5896     while (pos < depFileSize)
5897         {
5898         //trace("p:%c", get(pos));
5900         //# Block comment
5901         if (get(pos) == '/' && get(pos+1) == '*')
5902             {
5903             pos += 2;
5904             while (pos < depFileSize)
5905                 {
5906                 if (get(pos) == '*' && get(pos+1) == '/')
5907                     {
5908                     pos += 2;
5909                     break;
5910                     }
5911                 else
5912                     pos++;
5913                 }
5914             }
5915         //# Line comment
5916         else if (get(pos) == '/' && get(pos+1) == '/')
5917             {
5918             pos += 2;
5919             while (pos < depFileSize)
5920                 {
5921                 if (get(pos) == '\n')
5922                     {
5923                     pos++;
5924                     break;
5925                     }
5926                 else
5927                     pos++;
5928                 }
5929             }
5930         //# #include! yaay
5931         else if (sequ(pos, "#include"))
5932             {
5933             pos += 8;
5934             pos = skipwhite(pos);
5935             String iname;
5936             pos = getword(pos, iname);
5937             if (iname.size()>2)
5938                 {
5939                 iname = iname.substr(1, iname.size()-2);
5940                 addIncludeFile(frec, iname);
5941                 }
5942             }
5943         else
5944             {
5945             pos++;
5946             }
5947         }
5949     return true;
5954 /**
5955  *  Recursively check include lists to find all files in allFiles to which
5956  *  a given file is dependent.
5957  */
5958 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5960     std::map<String, FileRec *>::iterator iter;
5961     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5962         {
5963         String fname  = iter->first;
5964         if (ofile->files.find(fname) != ofile->files.end())
5965             {
5966             //trace("file '%s' already seen", fname.c_str());
5967             continue;
5968             }
5969         FileRec *child  = iter->second;
5970         ofile->files[fname] = child;
5971       
5972         processDependency(ofile, child);
5973         }
5976     return true;
5983 /**
5984  *  Generate the file dependency list.
5985  */
5986 bool DepTool::generateDependencies()
5988     std::map<String, FileRec *>::iterator iter;
5989     //# First pass.  Scan for all includes
5990     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5991         {
5992         FileRec *frec = iter->second;
5993         if (!scanFile(iter->first, frec))
5994             {
5995             //quit?
5996             }
5997         }
5999     //# Second pass.  Scan for all includes
6000     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6001         {
6002         FileRec *include = iter->second;
6003         if (include->type == FileRec::CFILE)
6004             {
6005             //String cFileName   = iter->first;
6006             FileRec *ofile     = new FileRec(FileRec::OFILE);
6007             ofile->path        = include->path;
6008             ofile->baseName    = include->baseName;
6009             ofile->suffix      = include->suffix;
6010             String fname       = include->path;
6011             if (fname.size()>0)
6012                 fname.append("/");
6013             fname.append(include->baseName);
6014             fname.append(".o");
6015             oFiles[fname]    = ofile;
6016             //add the .c file first?   no, don't
6017             //ofile->files[cFileName] = include;
6018             
6019             //trace("ofile:%s", fname.c_str());
6021             processDependency(ofile, include);
6022             }
6023         }
6025       
6026     return true;
6031 /**
6032  *  High-level call to generate deps and optionally save them
6033  */
6034 bool DepTool::generateDependencies(const String &fileName)
6036     if (!createFileList())
6037         return false;
6038     if (!generateDependencies())
6039         return false;
6040     if (!saveDepFile(fileName))
6041         return false;
6042     return true;
6046 /**
6047  *   This saves the dependency cache.
6048  */
6049 bool DepTool::saveDepFile(const String &fileName)
6051     time_t tim;
6052     time(&tim);
6054     FILE *f = fopen(fileName.c_str(), "w");
6055     if (!f)
6056         {
6057         trace("cannot open '%s' for writing", fileName.c_str());
6058         }
6059     fprintf(f, "<?xml version='1.0'?>\n");
6060     fprintf(f, "<!--\n");
6061     fprintf(f, "########################################################\n");
6062     fprintf(f, "## File: build.dep\n");
6063     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6064     fprintf(f, "########################################################\n");
6065     fprintf(f, "-->\n");
6067     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6068     std::map<String, FileRec *>::iterator iter;
6069     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6070         {
6071         FileRec *frec = iter->second;
6072         if (frec->type == FileRec::OFILE)
6073             {
6074             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6075                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6076             std::map<String, FileRec *>::iterator citer;
6077             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6078                 {
6079                 String cfname = citer->first;
6080                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6081                 }
6082             fprintf(f, "</object>\n\n");
6083             }
6084         }
6086     fprintf(f, "</dependencies>\n");
6087     fprintf(f, "\n");
6088     fprintf(f, "<!--\n");
6089     fprintf(f, "########################################################\n");
6090     fprintf(f, "## E N D\n");
6091     fprintf(f, "########################################################\n");
6092     fprintf(f, "-->\n");
6094     fclose(f);
6096     return true;
6102 /**
6103  *   This loads the dependency cache.
6104  */
6105 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6107     std::vector<DepRec> result;
6108     
6109     Parser parser;
6110     Element *root = parser.parseFile(depFile.c_str());
6111     if (!root)
6112         {
6113         //error("Could not open %s for reading", depFile.c_str());
6114         return result;
6115         }
6117     if (root->getChildren().size()==0 ||
6118         root->getChildren()[0]->getName()!="dependencies")
6119         {
6120         error("loadDepFile: main xml element should be <dependencies>");
6121         delete root;
6122         return result;
6123         }
6125     //########## Start parsing
6126     Element *depList = root->getChildren()[0];
6128     std::vector<Element *> objects = depList->getChildren();
6129     for (unsigned int i=0 ; i<objects.size() ; i++)
6130         {
6131         Element *objectElem = objects[i];
6132         String tagName = objectElem->getName();
6133         if (tagName != "object")
6134             {
6135             error("loadDepFile: <dependencies> should have only <object> children");
6136             return result;
6137             }
6139         String objName   = objectElem->getAttribute("name");
6140          //trace("object:%s", objName.c_str());
6141         DepRec depObject(objName);
6142         depObject.path   = objectElem->getAttribute("path");
6143         depObject.suffix = objectElem->getAttribute("suffix");
6144         //########## DESCRIPTION
6145         std::vector<Element *> depElems = objectElem->getChildren();
6146         for (unsigned int i=0 ; i<depElems.size() ; i++)
6147             {
6148             Element *depElem = depElems[i];
6149             tagName = depElem->getName();
6150             if (tagName != "dep")
6151                 {
6152                 error("loadDepFile: <object> should have only <dep> children");
6153                 return result;
6154                 }
6155             String depName = depElem->getAttribute("name");
6156             //trace("    dep:%s", depName.c_str());
6157             depObject.files.push_back(depName);
6158             }
6160         //Insert into the result list, in a sorted manner
6161         bool inserted = false;
6162         std::vector<DepRec>::iterator iter;
6163         for (iter = result.begin() ; iter != result.end() ; iter++)
6164             {
6165             String vpath = iter->path;
6166             vpath.append("/");
6167             vpath.append(iter->name);
6168             String opath = depObject.path;
6169             opath.append("/");
6170             opath.append(depObject.name);
6171             if (vpath > opath)
6172                 {
6173                 inserted = true;
6174                 iter = result.insert(iter, depObject);
6175                 break;
6176                 }
6177             }
6178         if (!inserted)
6179             result.push_back(depObject);
6180         }
6182     delete root;
6184     return result;
6188 /**
6189  *   This loads the dependency cache.
6190  */
6191 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6192                    bool forceRefresh)
6194     std::vector<DepRec> result;
6195     if (forceRefresh)
6196         {
6197         generateDependencies(depFile);
6198         result = loadDepFile(depFile);
6199         }
6200     else
6201         {
6202         //try once
6203         result = loadDepFile(depFile);
6204         if (result.size() == 0)
6205             {
6206             //fail? try again
6207             generateDependencies(depFile);
6208             result = loadDepFile(depFile);
6209             }
6210         }
6211     return result;
6217 //########################################################################
6218 //# T A S K
6219 //########################################################################
6220 //forward decl
6221 class Target;
6222 class Make;
6224 /**
6225  *
6226  */
6227 class Task : public MakeBase
6230 public:
6232     typedef enum
6233         {
6234         TASK_NONE,
6235         TASK_CC,
6236         TASK_COPY,
6237         TASK_DELETE,
6238         TASK_ECHO,
6239         TASK_JAR,
6240         TASK_JAVAC,
6241         TASK_LINK,
6242         TASK_MAKEFILE,
6243         TASK_MKDIR,
6244         TASK_MSGFMT,
6245         TASK_PKG_CONFIG,
6246         TASK_RANLIB,
6247         TASK_RC,
6248         TASK_SHAREDLIB,
6249         TASK_STATICLIB,
6250         TASK_STRIP,
6251         TASK_TOUCH,
6252         TASK_TSTAMP
6253         } TaskType;
6254         
6256     /**
6257      *
6258      */
6259     Task(MakeBase &par) : parent(par)
6260         { init(); }
6262     /**
6263      *
6264      */
6265     Task(const Task &other) : parent(other.parent)
6266         { init(); assign(other); }
6268     /**
6269      *
6270      */
6271     Task &operator=(const Task &other)
6272         { assign(other); return *this; }
6274     /**
6275      *
6276      */
6277     virtual ~Task()
6278         { }
6281     /**
6282      *
6283      */
6284     virtual MakeBase &getParent()
6285         { return parent; }
6287      /**
6288      *
6289      */
6290     virtual int  getType()
6291         { return type; }
6293     /**
6294      *
6295      */
6296     virtual void setType(int val)
6297         { type = val; }
6299     /**
6300      *
6301      */
6302     virtual String getName()
6303         { return name; }
6305     /**
6306      *
6307      */
6308     virtual bool execute()
6309         { return true; }
6311     /**
6312      *
6313      */
6314     virtual bool parse(Element *elem)
6315         { return true; }
6317     /**
6318      *
6319      */
6320     Task *createTask(Element *elem, int lineNr);
6323 protected:
6325     void init()
6326         {
6327         type = TASK_NONE;
6328         name = "none";
6329         }
6331     void assign(const Task &other)
6332         {
6333         type = other.type;
6334         name = other.name;
6335         }
6336         
6337     /**
6338      *  Show task status
6339      */
6340     void taskstatus(const char *fmt, ...)
6341         {
6342         va_list args;
6343         va_start(args,fmt);
6344         fprintf(stdout, "    %s : ", name.c_str());
6345         vfprintf(stdout, fmt, args);
6346         fprintf(stdout, "\n");
6347         va_end(args) ;
6348         }
6350     String getAttribute(Element *elem, const String &attrName)
6351         {
6352         String str;
6353         return str;
6354         }
6356     MakeBase &parent;
6358     int type;
6360     String name;
6361 };
6365 /**
6366  * This task runs the C/C++ compiler.  The compiler is invoked
6367  * for all .c or .cpp files which are newer than their correcsponding
6368  * .o files.  
6369  */
6370 class TaskCC : public Task
6372 public:
6374     TaskCC(MakeBase &par) : Task(par)
6375         {
6376         type = TASK_CC;
6377         name = "cc";
6378         }
6380     virtual ~TaskCC()
6381         {}
6382         
6383     virtual bool isExcludedInc(const String &dirname)
6384         {
6385         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6386             {
6387             String fname = excludeInc[i];
6388             if (fname == dirname)
6389                 return true;
6390             }
6391         return false;
6392         }
6394     virtual bool execute()
6395         {
6396         //evaluate our parameters
6397         String command         = parent.eval(commandOpt, "gcc");
6398         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6399         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6400         String source          = parent.eval(sourceOpt, ".");
6401         String dest            = parent.eval(destOpt, ".");
6402         String flags           = parent.eval(flagsOpt, "");
6403         String defines         = parent.eval(definesOpt, "");
6404         String includes        = parent.eval(includesOpt, "");
6405         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6406         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6408         if (!listFiles(parent, fileSet))
6409             return false;
6410             
6411         FILE *f = NULL;
6412         f = fopen("compile.lst", "w");
6414         //refreshCache is probably false here, unless specified otherwise
6415         String fullName = parent.resolve("build.dep");
6416         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6417             {
6418             taskstatus("regenerating C/C++ dependency cache");
6419             refreshCache = true;
6420             }
6422         DepTool depTool;
6423         depTool.setSourceDirectory(source);
6424         depTool.setFileList(fileSet.getFiles());
6425         std::vector<DepRec> deps =
6426              depTool.getDepFile("build.dep", refreshCache);
6427         
6428         String incs;
6429         incs.append("-I");
6430         incs.append(parent.resolve("."));
6431         incs.append(" ");
6432         if (includes.size()>0)
6433             {
6434             incs.append(includes);
6435             incs.append(" ");
6436             }
6437         std::set<String> paths;
6438         std::vector<DepRec>::iterator viter;
6439         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6440             {
6441             DepRec dep = *viter;
6442             if (dep.path.size()>0)
6443                 paths.insert(dep.path);
6444             }
6445         if (source.size()>0)
6446             {
6447             incs.append(" -I");
6448             incs.append(parent.resolve(source));
6449             incs.append(" ");
6450             }
6451         std::set<String>::iterator setIter;
6452         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6453             {
6454             String dirName = *setIter;
6455             //check excludeInc to see if we dont want to include this dir
6456             if (isExcludedInc(dirName))
6457                 continue;
6458             incs.append(" -I");
6459             String dname;
6460             if (source.size()>0)
6461                 {
6462                 dname.append(source);
6463                 dname.append("/");
6464                 }
6465             dname.append(dirName);
6466             incs.append(parent.resolve(dname));
6467             }
6468             
6469         /**
6470          * Compile each of the C files that need it
6471          */
6472         bool errorOccurred = false;                 
6473         std::vector<String> cfiles;
6474         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6475             {
6476             DepRec dep = *viter;
6478             //## Select command
6479             String sfx = dep.suffix;
6480             String command = ccCommand;
6481             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6482                  sfx == "cc" || sfx == "CC")
6483                 command = cxxCommand;
6484  
6485             //## Make paths
6486             String destPath = dest;
6487             String srcPath  = source;
6488             if (dep.path.size()>0)
6489                 {
6490                 destPath.append("/");
6491                 destPath.append(dep.path);
6492                 srcPath.append("/");
6493                 srcPath.append(dep.path);
6494                 }
6495             //## Make sure destination directory exists
6496             if (!createDirectory(destPath))
6497                 return false;
6498                 
6499             //## Check whether it needs to be done
6500             String destName;
6501             if (destPath.size()>0)
6502                 {
6503                 destName.append(destPath);
6504                 destName.append("/");
6505                 }
6506             destName.append(dep.name);
6507             destName.append(".o");
6508             String destFullName = parent.resolve(destName);
6509             String srcName;
6510             if (srcPath.size()>0)
6511                 {
6512                 srcName.append(srcPath);
6513                 srcName.append("/");
6514                 }
6515             srcName.append(dep.name);
6516             srcName.append(".");
6517             srcName.append(dep.suffix);
6518             String srcFullName = parent.resolve(srcName);
6519             bool compileMe = false;
6520             //# First we check if the source is newer than the .o
6521             if (isNewerThan(srcFullName, destFullName))
6522                 {
6523                 taskstatus("compile of %s required by source: %s",
6524                         destFullName.c_str(), srcFullName.c_str());
6525                 compileMe = true;
6526                 }
6527             else
6528                 {
6529                 //# secondly, we check if any of the included dependencies
6530                 //# of the .c/.cpp is newer than the .o
6531                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6532                     {
6533                     String depName;
6534                     if (source.size()>0)
6535                         {
6536                         depName.append(source);
6537                         depName.append("/");
6538                         }
6539                     depName.append(dep.files[i]);
6540                     String depFullName = parent.resolve(depName);
6541                     bool depRequires = isNewerThan(depFullName, destFullName);
6542                     //trace("%d %s %s\n", depRequires,
6543                     //        destFullName.c_str(), depFullName.c_str());
6544                     if (depRequires)
6545                         {
6546                         taskstatus("compile of %s required by included: %s",
6547                                 destFullName.c_str(), depFullName.c_str());
6548                         compileMe = true;
6549                         break;
6550                         }
6551                     }
6552                 }
6553             if (!compileMe)
6554                 {
6555                 continue;
6556                 }
6558             //## Assemble the command
6559             String cmd = command;
6560             cmd.append(" -c ");
6561             cmd.append(flags);
6562             cmd.append(" ");
6563             cmd.append(defines);
6564             cmd.append(" ");
6565             cmd.append(incs);
6566             cmd.append(" ");
6567             cmd.append(srcFullName);
6568             cmd.append(" -o ");
6569             cmd.append(destFullName);
6571             //## Execute the command
6573             String outString, errString;
6574             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6576             if (f)
6577                 {
6578                 fprintf(f, "########################### File : %s\n",
6579                              srcFullName.c_str());
6580                 fprintf(f, "#### COMMAND ###\n");
6581                 int col = 0;
6582                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6583                     {
6584                     char ch = cmd[i];
6585                     if (isspace(ch)  && col > 63)
6586                         {
6587                         fputc('\n', f);
6588                         col = 0;
6589                         }
6590                     else
6591                         {
6592                         fputc(ch, f);
6593                         col++;
6594                         }
6595                     if (col > 76)
6596                         {
6597                         fputc('\n', f);
6598                         col = 0;
6599                         }
6600                     }
6601                 fprintf(f, "\n");
6602                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6603                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6604                 fflush(f);
6605                 }
6606             if (!ret)
6607                 {
6608                 error("problem compiling: %s", errString.c_str());
6609                 errorOccurred = true;
6610                 }
6611             if (errorOccurred && !continueOnError)
6612                 break;
6613             }
6615         if (f)
6616             {
6617             fclose(f);
6618             }
6619         
6620         return !errorOccurred;
6621         }
6624     virtual bool parse(Element *elem)
6625         {
6626         String s;
6627         if (!parent.getAttribute(elem, "command", commandOpt))
6628             return false;
6629         if (commandOpt.size()>0)
6630             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6631         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6632             return false;
6633         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6634             return false;
6635         if (!parent.getAttribute(elem, "destdir", destOpt))
6636             return false;
6637         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6638             return false;
6639         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6640             return false;
6642         std::vector<Element *> children = elem->getChildren();
6643         for (unsigned int i=0 ; i<children.size() ; i++)
6644             {
6645             Element *child = children[i];
6646             String tagName = child->getName();
6647             if (tagName == "flags")
6648                 {
6649                 if (!parent.getValue(child, flagsOpt))
6650                     return false;
6651                 flagsOpt = strip(flagsOpt);
6652                 }
6653             else if (tagName == "includes")
6654                 {
6655                 if (!parent.getValue(child, includesOpt))
6656                     return false;
6657                 includesOpt = strip(includesOpt);
6658                 }
6659             else if (tagName == "defines")
6660                 {
6661                 if (!parent.getValue(child, definesOpt))
6662                     return false;
6663                 definesOpt = strip(definesOpt);
6664                 }
6665             else if (tagName == "fileset")
6666                 {
6667                 if (!parseFileSet(child, parent, fileSet))
6668                     return false;
6669                 sourceOpt = fileSet.getDirectory();
6670                 }
6671             else if (tagName == "excludeinc")
6672                 {
6673                 if (!parseFileList(child, parent, excludeInc))
6674                     return false;
6675                 }
6676             }
6678         return true;
6679         }
6680         
6681 protected:
6683     String   commandOpt;
6684     String   ccCommandOpt;
6685     String   cxxCommandOpt;
6686     String   sourceOpt;
6687     String   destOpt;
6688     String   flagsOpt;
6689     String   definesOpt;
6690     String   includesOpt;
6691     String   continueOnErrorOpt;
6692     String   refreshCacheOpt;
6693     FileSet  fileSet;
6694     FileList excludeInc;
6695     
6696 };
6700 /**
6701  *
6702  */
6703 class TaskCopy : public Task
6705 public:
6707     typedef enum
6708         {
6709         CP_NONE,
6710         CP_TOFILE,
6711         CP_TODIR
6712         } CopyType;
6714     TaskCopy(MakeBase &par) : Task(par)
6715         {
6716         type        = TASK_COPY;
6717         name        = "copy";
6718         cptype      = CP_NONE;
6719         haveFileSet = false;
6720         }
6722     virtual ~TaskCopy()
6723         {}
6725     virtual bool execute()
6726         {
6727         String fileName   = parent.eval(fileNameOpt   , ".");
6728         String toFileName = parent.eval(toFileNameOpt , ".");
6729         String toDirName  = parent.eval(toDirNameOpt  , ".");
6730         bool   verbose    = parent.evalBool(verboseOpt, false);
6731         switch (cptype)
6732            {
6733            case CP_TOFILE:
6734                {
6735                if (fileName.size()>0)
6736                    {
6737                    taskstatus("%s to %s",
6738                         fileName.c_str(), toFileName.c_str());
6739                    String fullSource = parent.resolve(fileName);
6740                    String fullDest = parent.resolve(toFileName);
6741                    //trace("copy %s to file %s", fullSource.c_str(),
6742                    //                       fullDest.c_str());
6743                    if (!isRegularFile(fullSource))
6744                        {
6745                        error("copy : file %s does not exist", fullSource.c_str());
6746                        return false;
6747                        }
6748                    if (!isNewerThan(fullSource, fullDest))
6749                        {
6750                        taskstatus("skipped");
6751                        return true;
6752                        }
6753                    if (!copyFile(fullSource, fullDest))
6754                        return false;
6755                    taskstatus("1 file copied");
6756                    }
6757                return true;
6758                }
6759            case CP_TODIR:
6760                {
6761                if (haveFileSet)
6762                    {
6763                    if (!listFiles(parent, fileSet))
6764                        return false;
6765                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6767                    taskstatus("%s to %s",
6768                        fileSetDir.c_str(), toDirName.c_str());
6770                    int nrFiles = 0;
6771                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6772                        {
6773                        String fileName = fileSet[i];
6775                        String sourcePath;
6776                        if (fileSetDir.size()>0)
6777                            {
6778                            sourcePath.append(fileSetDir);
6779                            sourcePath.append("/");
6780                            }
6781                        sourcePath.append(fileName);
6782                        String fullSource = parent.resolve(sourcePath);
6783                        
6784                        //Get the immediate parent directory's base name
6785                        String baseFileSetDir = fileSetDir;
6786                        unsigned int pos = baseFileSetDir.find_last_of('/');
6787                        if (pos!=baseFileSetDir.npos &&
6788                                   pos < baseFileSetDir.size()-1)
6789                            baseFileSetDir =
6790                               baseFileSetDir.substr(pos+1,
6791                                    baseFileSetDir.size());
6792                        //Now make the new path
6793                        String destPath;
6794                        if (toDirName.size()>0)
6795                            {
6796                            destPath.append(toDirName);
6797                            destPath.append("/");
6798                            }
6799                        if (baseFileSetDir.size()>0)
6800                            {
6801                            destPath.append(baseFileSetDir);
6802                            destPath.append("/");
6803                            }
6804                        destPath.append(fileName);
6805                        String fullDest = parent.resolve(destPath);
6806                        //trace("fileName:%s", fileName.c_str());
6807                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6808                        //                   fullDest.c_str());
6809                        if (!isNewerThan(fullSource, fullDest))
6810                            {
6811                            //trace("copy skipping %s", fullSource.c_str());
6812                            continue;
6813                            }
6814                        if (!copyFile(fullSource, fullDest))
6815                            return false;
6816                        nrFiles++;
6817                        }
6818                    taskstatus("%d file(s) copied", nrFiles);
6819                    }
6820                else //file source
6821                    {
6822                    //For file->dir we want only the basename of
6823                    //the source appended to the dest dir
6824                    taskstatus("%s to %s", 
6825                        fileName.c_str(), toDirName.c_str());
6826                    String baseName = fileName;
6827                    unsigned int pos = baseName.find_last_of('/');
6828                    if (pos!=baseName.npos && pos<baseName.size()-1)
6829                        baseName = baseName.substr(pos+1, baseName.size());
6830                    String fullSource = parent.resolve(fileName);
6831                    String destPath;
6832                    if (toDirName.size()>0)
6833                        {
6834                        destPath.append(toDirName);
6835                        destPath.append("/");
6836                        }
6837                    destPath.append(baseName);
6838                    String fullDest = parent.resolve(destPath);
6839                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6840                    //                       fullDest.c_str());
6841                    if (!isRegularFile(fullSource))
6842                        {
6843                        error("copy : file %s does not exist", fullSource.c_str());
6844                        return false;
6845                        }
6846                    if (!isNewerThan(fullSource, fullDest))
6847                        {
6848                        taskstatus("skipped");
6849                        return true;
6850                        }
6851                    if (!copyFile(fullSource, fullDest))
6852                        return false;
6853                    taskstatus("1 file copied");
6854                    }
6855                return true;
6856                }
6857            }
6858         return true;
6859         }
6862     virtual bool parse(Element *elem)
6863         {
6864         if (!parent.getAttribute(elem, "file", fileNameOpt))
6865             return false;
6866         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6867             return false;
6868         if (toFileNameOpt.size() > 0)
6869             cptype = CP_TOFILE;
6870         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6871             return false;
6872         if (toDirNameOpt.size() > 0)
6873             cptype = CP_TODIR;
6874         if (!parent.getAttribute(elem, "verbose", verboseOpt))
6875             return false;
6876             
6877         haveFileSet = false;
6878         
6879         std::vector<Element *> children = elem->getChildren();
6880         for (unsigned int i=0 ; i<children.size() ; i++)
6881             {
6882             Element *child = children[i];
6883             String tagName = child->getName();
6884             if (tagName == "fileset")
6885                 {
6886                 if (!parseFileSet(child, parent, fileSet))
6887                     {
6888                     error("problem getting fileset");
6889                     return false;
6890                     }
6891                 haveFileSet = true;
6892                 }
6893             }
6895         //Perform validity checks
6896         if (fileNameOpt.size()>0 && fileSet.size()>0)
6897             {
6898             error("<copy> can only have one of : file= and <fileset>");
6899             return false;
6900             }
6901         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6902             {
6903             error("<copy> can only have one of : tofile= or todir=");
6904             return false;
6905             }
6906         if (haveFileSet && toDirNameOpt.size()==0)
6907             {
6908             error("a <copy> task with a <fileset> must have : todir=");
6909             return false;
6910             }
6911         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6912             {
6913             error("<copy> tofile= must be associated with : file=");
6914             return false;
6915             }
6916         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6917             {
6918             error("<copy> todir= must be associated with : file= or <fileset>");
6919             return false;
6920             }
6922         return true;
6923         }
6924         
6925 private:
6927     int cptype;
6928     bool haveFileSet;
6930     FileSet fileSet;
6931     String  fileNameOpt;
6932     String  toFileNameOpt;
6933     String  toDirNameOpt;
6934     String  verboseOpt;
6935 };
6938 /**
6939  *
6940  */
6941 class TaskDelete : public Task
6943 public:
6945     typedef enum
6946         {
6947         DEL_FILE,
6948         DEL_DIR,
6949         DEL_FILESET
6950         } DeleteType;
6952     TaskDelete(MakeBase &par) : Task(par)
6953         { 
6954         type        = TASK_DELETE;
6955         name        = "delete";
6956         delType     = DEL_FILE;
6957         }
6959     virtual ~TaskDelete()
6960         {}
6962     virtual bool execute()
6963         {
6964         String dirName   = parent.eval(dirNameOpt, ".");
6965         String fileName  = parent.eval(fileNameOpt, ".");
6966         bool verbose     = parent.evalBool(verboseOpt, false);
6967         bool quiet       = parent.evalBool(quietOpt, false);
6968         bool failOnError = parent.evalBool(failOnErrorOpt, true);
6969         struct stat finfo;
6970         switch (delType)
6971             {
6972             case DEL_FILE:
6973                 {
6974                 status("          : %s", fileName.c_str());
6975                 String fullName = parent.resolve(fileName);
6976                 char *fname = (char *)fullName.c_str();
6977                 //does not exist
6978                 if (stat(fname, &finfo)<0)
6979                     return true;
6980                 //exists but is not a regular file
6981                 if (!S_ISREG(finfo.st_mode))
6982                     {
6983                     error("<delete> failed. '%s' exists and is not a regular file",
6984                           fname);
6985                     return false;
6986                     }
6987                 if (remove(fname)<0)
6988                     {
6989                     error("<delete> failed: %s", strerror(errno));
6990                     return false;
6991                     }
6992                 return true;
6993                 }
6994             case DEL_DIR:
6995                 {
6996                 taskstatus("%s", dirName.c_str());
6997                 String fullDir = parent.resolve(dirName);
6998                 if (!removeDirectory(fullDir))
6999                     return false;
7000                 return true;
7001                 }
7002             }
7003         return true;
7004         }
7006     virtual bool parse(Element *elem)
7007         {
7008         if (!parent.getAttribute(elem, "file", fileNameOpt))
7009             return false;
7010         if (fileNameOpt.size() > 0)
7011             delType = DEL_FILE;
7012         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7013             return false;
7014         if (dirNameOpt.size() > 0)
7015             delType = DEL_DIR;
7016         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7017             {
7018             error("<delete> can have one attribute of file= or dir=");
7019             return false;
7020             }
7021         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7022             {
7023             error("<delete> must have one attribute of file= or dir=");
7024             return false;
7025             }
7026         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7027             return false;
7028         if (!parent.getAttribute(elem, "quiet", quietOpt))
7029             return false;
7030         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7031             return false;
7032         return true;
7033         }
7035 private:
7037     int delType;
7038     String dirNameOpt;
7039     String fileNameOpt;
7040     String verboseOpt;
7041     String quietOpt;
7042     String failOnErrorOpt;
7043 };
7046 /**
7047  * Send a message to stdout
7048  */
7049 class TaskEcho : public Task
7051 public:
7053     TaskEcho(MakeBase &par) : Task(par)
7054         { type = TASK_ECHO; name = "echo"; }
7056     virtual ~TaskEcho()
7057         {}
7059     virtual bool execute()
7060         {
7061         //let message have priority over text
7062         String message = parent.eval(messageOpt, "");
7063         String text    = parent.eval(textOpt, "");
7064         if (message.size() > 0)
7065             {
7066             fprintf(stdout, "%s\n", message.c_str());
7067             }
7068         else if (text.size() > 0)
7069             {
7070             fprintf(stdout, "%s\n", text.c_str());
7071             }
7072         return true;
7073         }
7075     virtual bool parse(Element *elem)
7076         {
7077         if (!parent.getValue(elem, textOpt))
7078             return false;
7079         textOpt    = leftJustify(textOpt);
7080         if (!parent.getAttribute(elem, "message", messageOpt))
7081             return false;
7082         return true;
7083         }
7085 private:
7087     String messageOpt;
7088     String textOpt;
7089 };
7093 /**
7094  *
7095  */
7096 class TaskJar : public Task
7098 public:
7100     TaskJar(MakeBase &par) : Task(par)
7101         { type = TASK_JAR; name = "jar"; }
7103     virtual ~TaskJar()
7104         {}
7106     virtual bool execute()
7107         {
7108         String command  = parent.eval(commandOpt, "jar");
7109         String basedir  = parent.eval(basedirOpt, ".");
7110         String destfile = parent.eval(destfileOpt, ".");
7112         String cmd = command;
7113         cmd.append(" -cf ");
7114         cmd.append(destfile);
7115         cmd.append(" -C ");
7116         cmd.append(basedir);
7117         cmd.append(" .");
7119         String execCmd = cmd;
7121         String outString, errString;
7122         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7123         if (!ret)
7124             {
7125             error("<jar> command '%s' failed :\n %s",
7126                                       execCmd.c_str(), errString.c_str());
7127             return false;
7128             }
7129         return true;
7130         }
7132     virtual bool parse(Element *elem)
7133         {
7134         if (!parent.getAttribute(elem, "command", commandOpt))
7135             return false;
7136         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7137             return false;
7138         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7139             return false;
7140         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7141             {
7142             error("<jar> required both basedir and destfile attributes to be set");
7143             return false;
7144             }
7145         return true;
7146         }
7148 private:
7150     String commandOpt;
7151     String basedirOpt;
7152     String destfileOpt;
7153 };
7156 /**
7157  *
7158  */
7159 class TaskJavac : public Task
7161 public:
7163     TaskJavac(MakeBase &par) : Task(par)
7164         { 
7165         type = TASK_JAVAC; name = "javac";
7166         }
7168     virtual ~TaskJavac()
7169         {}
7171     virtual bool execute()
7172         {
7173         String command  = parent.eval(commandOpt, "javac");
7174         String srcdir   = parent.eval(srcdirOpt, ".");
7175         String destdir  = parent.eval(destdirOpt, ".");
7176         String target   = parent.eval(targetOpt, "");
7178         std::vector<String> fileList;
7179         if (!listFiles(srcdir, "", fileList))
7180             {
7181             return false;
7182             }
7183         String cmd = command;
7184         cmd.append(" -d ");
7185         cmd.append(destdir);
7186         cmd.append(" -classpath ");
7187         cmd.append(destdir);
7188         cmd.append(" -sourcepath ");
7189         cmd.append(srcdir);
7190         cmd.append(" ");
7191         if (target.size()>0)
7192             {
7193             cmd.append(" -target ");
7194             cmd.append(target);
7195             cmd.append(" ");
7196             }
7197         String fname = "javalist.btool";
7198         FILE *f = fopen(fname.c_str(), "w");
7199         int count = 0;
7200         for (unsigned int i=0 ; i<fileList.size() ; i++)
7201             {
7202             String fname = fileList[i];
7203             String srcName = fname;
7204             if (fname.size()<6) //x.java
7205                 continue;
7206             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7207                 continue;
7208             String baseName = fname.substr(0, fname.size()-5);
7209             String destName = baseName;
7210             destName.append(".class");
7212             String fullSrc = srcdir;
7213             fullSrc.append("/");
7214             fullSrc.append(fname);
7215             String fullDest = destdir;
7216             fullDest.append("/");
7217             fullDest.append(destName);
7218             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7219             if (!isNewerThan(fullSrc, fullDest))
7220                 continue;
7222             count++;
7223             fprintf(f, "%s\n", fullSrc.c_str());
7224             }
7225         fclose(f);
7226         if (!count)
7227             {
7228             taskstatus("nothing to do");
7229             return true;
7230             }
7232         taskstatus("compiling %d files", count);
7234         String execCmd = cmd;
7235         execCmd.append("@");
7236         execCmd.append(fname);
7238         String outString, errString;
7239         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7240         if (!ret)
7241             {
7242             error("<javac> command '%s' failed :\n %s",
7243                                       execCmd.c_str(), errString.c_str());
7244             return false;
7245             }
7246         return true;
7247         }
7249     virtual bool parse(Element *elem)
7250         {
7251         if (!parent.getAttribute(elem, "command", commandOpt))
7252             return false;
7253         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7254             return false;
7255         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7256             return false;
7257         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7258             {
7259             error("<javac> required both srcdir and destdir attributes to be set");
7260             return false;
7261             }
7262         if (!parent.getAttribute(elem, "target", targetOpt))
7263             return false;
7264         return true;
7265         }
7267 private:
7269     String commandOpt;
7270     String srcdirOpt;
7271     String destdirOpt;
7272     String targetOpt;
7274 };
7277 /**
7278  *
7279  */
7280 class TaskLink : public Task
7282 public:
7284     TaskLink(MakeBase &par) : Task(par)
7285         {
7286         type = TASK_LINK; name = "link";
7287         }
7289     virtual ~TaskLink()
7290         {}
7292     virtual bool execute()
7293         {
7294         String  command        = parent.eval(commandOpt, "g++");
7295         String  fileName       = parent.eval(fileNameOpt, "");
7296         String  flags          = parent.eval(flagsOpt, "");
7297         String  libs           = parent.eval(libsOpt, "");
7298         bool    doStrip        = parent.evalBool(doStripOpt, false);
7299         String  symFileName    = parent.eval(symFileNameOpt, "");
7300         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7301         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7303         if (!listFiles(parent, fileSet))
7304             return false;
7305         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7306         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7307         bool doit = false;
7308         String fullTarget = parent.resolve(fileName);
7309         String cmd = command;
7310         cmd.append(" -o ");
7311         cmd.append(fullTarget);
7312         cmd.append(" ");
7313         cmd.append(flags);
7314         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7315             {
7316             cmd.append(" ");
7317             String obj;
7318             if (fileSetDir.size()>0)
7319                 {
7320                 obj.append(fileSetDir);
7321                 obj.append("/");
7322                 }
7323             obj.append(fileSet[i]);
7324             String fullObj = parent.resolve(obj);
7325             String nativeFullObj = getNativePath(fullObj);
7326             cmd.append(nativeFullObj);
7327             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7328             //          fullObj.c_str());
7329             if (isNewerThan(fullObj, fullTarget))
7330                 doit = true;
7331             }
7332         cmd.append(" ");
7333         cmd.append(libs);
7334         if (!doit)
7335             {
7336             //trace("link not needed");
7337             return true;
7338             }
7339         //trace("LINK cmd:%s", cmd.c_str());
7342         String outbuf, errbuf;
7343         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7344             {
7345             error("LINK problem: %s", errbuf.c_str());
7346             return false;
7347             }
7349         if (symFileName.size()>0)
7350             {
7351             String symFullName = parent.resolve(symFileName);
7352             cmd = objcopyCommand;
7353             cmd.append(" --only-keep-debug ");
7354             cmd.append(getNativePath(fullTarget));
7355             cmd.append(" ");
7356             cmd.append(getNativePath(symFullName));
7357             if (!executeCommand(cmd, "", outbuf, errbuf))
7358                 {
7359                 error("<strip> symbol file failed : %s", errbuf.c_str());
7360                 return false;
7361                 }
7362             }
7363             
7364         if (doStrip)
7365             {
7366             cmd = stripCommand;
7367             cmd.append(" ");
7368             cmd.append(getNativePath(fullTarget));
7369             if (!executeCommand(cmd, "", outbuf, errbuf))
7370                {
7371                error("<strip> failed : %s", errbuf.c_str());
7372                return false;
7373                }
7374             }
7376         return true;
7377         }
7379     virtual bool parse(Element *elem)
7380         {
7381         if (!parent.getAttribute(elem, "command", commandOpt))
7382             return false;
7383         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7384             return false;
7385         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7386             return false;
7387         if (!parent.getAttribute(elem, "out", fileNameOpt))
7388             return false;
7389         if (!parent.getAttribute(elem, "strip", doStripOpt))
7390             return false;
7391         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7392             return false;
7393             
7394         std::vector<Element *> children = elem->getChildren();
7395         for (unsigned int i=0 ; i<children.size() ; i++)
7396             {
7397             Element *child = children[i];
7398             String tagName = child->getName();
7399             if (tagName == "fileset")
7400                 {
7401                 if (!parseFileSet(child, parent, fileSet))
7402                     return false;
7403                 }
7404             else if (tagName == "flags")
7405                 {
7406                 if (!parent.getValue(child, flagsOpt))
7407                     return false;
7408                 flagsOpt = strip(flagsOpt);
7409                 }
7410             else if (tagName == "libs")
7411                 {
7412                 if (!parent.getValue(child, libsOpt))
7413                     return false;
7414                 libsOpt = strip(libsOpt);
7415                 }
7416             }
7417         return true;
7418         }
7420 private:
7422     FileSet fileSet;
7424     String  commandOpt;
7425     String  fileNameOpt;
7426     String  flagsOpt;
7427     String  libsOpt;
7428     String  doStripOpt;
7429     String  symFileNameOpt;
7430     String  stripCommandOpt;
7431     String  objcopyCommandOpt;
7433 };
7437 /**
7438  * Create a named file
7439  */
7440 class TaskMakeFile : public Task
7442 public:
7444     TaskMakeFile(MakeBase &par) : Task(par)
7445         { type = TASK_MAKEFILE; name = "makefile"; }
7447     virtual ~TaskMakeFile()
7448         {}
7450     virtual bool execute()
7451         {
7452         String fileName = parent.eval(fileNameOpt, "");
7453         String text     = parent.eval(textOpt, "");
7455         taskstatus("%s", fileName.c_str());
7456         String fullName = parent.resolve(fileName);
7457         if (!isNewerThan(parent.getURI().getPath(), fullName))
7458             {
7459             //trace("skipped <makefile>");
7460             return true;
7461             }
7462         String fullNative = getNativePath(fullName);
7463         //trace("fullName:%s", fullName.c_str());
7464         FILE *f = fopen(fullNative.c_str(), "w");
7465         if (!f)
7466             {
7467             error("<makefile> could not open %s for writing : %s",
7468                 fullName.c_str(), strerror(errno));
7469             return false;
7470             }
7471         for (unsigned int i=0 ; i<text.size() ; i++)
7472             fputc(text[i], f);
7473         fputc('\n', f);
7474         fclose(f);
7475         return true;
7476         }
7478     virtual bool parse(Element *elem)
7479         {
7480         if (!parent.getAttribute(elem, "file", fileNameOpt))
7481             return false;
7482         if (fileNameOpt.size() == 0)
7483             {
7484             error("<makefile> requires 'file=\"filename\"' attribute");
7485             return false;
7486             }
7487         if (!parent.getValue(elem, textOpt))
7488             return false;
7489         textOpt = leftJustify(textOpt);
7490         //trace("dirname:%s", dirName.c_str());
7491         return true;
7492         }
7494 private:
7496     String fileNameOpt;
7497     String textOpt;
7498 };
7502 /**
7503  * Create a named directory
7504  */
7505 class TaskMkDir : public Task
7507 public:
7509     TaskMkDir(MakeBase &par) : Task(par)
7510         { type = TASK_MKDIR; name = "mkdir"; }
7512     virtual ~TaskMkDir()
7513         {}
7515     virtual bool execute()
7516         {
7517         String dirName = parent.eval(dirNameOpt, ".");
7518         
7519         taskstatus("%s", dirName.c_str());
7520         String fullDir = parent.resolve(dirName);
7521         //trace("fullDir:%s", fullDir.c_str());
7522         if (!createDirectory(fullDir))
7523             return false;
7524         return true;
7525         }
7527     virtual bool parse(Element *elem)
7528         {
7529         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7530             return false;
7531         if (dirNameOpt.size() == 0)
7532             {
7533             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7534             return false;
7535             }
7536         return true;
7537         }
7539 private:
7541     String dirNameOpt;
7542 };
7546 /**
7547  * Create a named directory
7548  */
7549 class TaskMsgFmt: public Task
7551 public:
7553     TaskMsgFmt(MakeBase &par) : Task(par)
7554          { type = TASK_MSGFMT;  name = "msgfmt"; }
7556     virtual ~TaskMsgFmt()
7557         {}
7559     virtual bool execute()
7560         {
7561         String  command   = parent.eval(commandOpt, "msgfmt");
7562         String  toDirName = parent.eval(toDirNameOpt, ".");
7563         String  outName   = parent.eval(outNameOpt, "");
7564         bool    owndir    = parent.evalBool(owndirOpt, false);
7566         if (!listFiles(parent, fileSet))
7567             return false;
7568         String fileSetDir = fileSet.getDirectory();
7570         //trace("msgfmt: %d", fileSet.size());
7571         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7572             {
7573             String fileName = fileSet[i];
7574             if (getSuffix(fileName) != "po")
7575                 continue;
7576             String sourcePath;
7577             if (fileSetDir.size()>0)
7578                 {
7579                 sourcePath.append(fileSetDir);
7580                 sourcePath.append("/");
7581                 }
7582             sourcePath.append(fileName);
7583             String fullSource = parent.resolve(sourcePath);
7585             String destPath;
7586             if (toDirName.size()>0)
7587                 {
7588                 destPath.append(toDirName);
7589                 destPath.append("/");
7590                 }
7591             if (owndir)
7592                 {
7593                 String subdir = fileName;
7594                 unsigned int pos = subdir.find_last_of('.');
7595                 if (pos != subdir.npos)
7596                     subdir = subdir.substr(0, pos);
7597                 destPath.append(subdir);
7598                 destPath.append("/");
7599                 }
7600             //Pick the output file name
7601             if (outName.size() > 0)
7602                 {
7603                 destPath.append(outName);
7604                 }
7605             else
7606                 {
7607                 destPath.append(fileName);
7608                 destPath[destPath.size()-2] = 'm';
7609                 }
7611             String fullDest = parent.resolve(destPath);
7613             if (!isNewerThan(fullSource, fullDest))
7614                 {
7615                 //trace("skip %s", fullSource.c_str());
7616                 continue;
7617                 }
7618                 
7619             String cmd = command;
7620             cmd.append(" ");
7621             cmd.append(fullSource);
7622             cmd.append(" -o ");
7623             cmd.append(fullDest);
7624             
7625             int pos = fullDest.find_last_of('/');
7626             if (pos>0)
7627                 {
7628                 String fullDestPath = fullDest.substr(0, pos);
7629                 if (!createDirectory(fullDestPath))
7630                     return false;
7631                 }
7635             String outString, errString;
7636             if (!executeCommand(cmd.c_str(), "", outString, errString))
7637                 {
7638                 error("<msgfmt> problem: %s", errString.c_str());
7639                 return false;
7640                 }
7641             }
7643         return true;
7644         }
7646     virtual bool parse(Element *elem)
7647         {
7648         if (!parent.getAttribute(elem, "command", commandOpt))
7649             return false;
7650         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7651             return false;
7652         if (!parent.getAttribute(elem, "out", outNameOpt))
7653             return false;
7654         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7655             return false;
7656             
7657         std::vector<Element *> children = elem->getChildren();
7658         for (unsigned int i=0 ; i<children.size() ; i++)
7659             {
7660             Element *child = children[i];
7661             String tagName = child->getName();
7662             if (tagName == "fileset")
7663                 {
7664                 if (!parseFileSet(child, parent, fileSet))
7665                     return false;
7666                 }
7667             }
7668         return true;
7669         }
7671 private:
7673     FileSet fileSet;
7675     String  commandOpt;
7676     String  toDirNameOpt;
7677     String  outNameOpt;
7678     String  owndirOpt;
7680 };
7684 /**
7685  *  Perform a Package-Config query similar to pkg-config
7686  */
7687 class TaskPkgConfig : public Task
7689 public:
7691     typedef enum
7692         {
7693         PKG_CONFIG_QUERY_CFLAGS,
7694         PKG_CONFIG_QUERY_LIBS,
7695         PKG_CONFIG_QUERY_ALL
7696         } QueryTypes;
7698     TaskPkgConfig(MakeBase &par) : Task(par)
7699         {
7700         type = TASK_PKG_CONFIG;
7701         name = "pkg-config";
7702         }
7704     virtual ~TaskPkgConfig()
7705         {}
7707     virtual bool execute()
7708         {
7709         String pkgName       = parent.eval(pkgNameOpt,      "");
7710         String prefix        = parent.eval(prefixOpt,       "");
7711         String propName      = parent.eval(propNameOpt,     "");
7712         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7713         String query         = parent.eval(queryOpt,        "all");
7715         String path = parent.resolve(pkgConfigPath);
7716         PkgConfig pkgconfig;
7717         pkgconfig.setPath(path);
7718         pkgconfig.setPrefix(prefix);
7719         if (!pkgconfig.query(pkgName))
7720             {
7721             error("<pkg-config> query failed for '%s", name.c_str());
7722             return false;
7723             }
7724             
7725         String val = "";
7726         if (query == "cflags")
7727             val = pkgconfig.getCflags();
7728         else if (query == "libs")
7729             val =pkgconfig.getLibs();
7730         else if (query == "all")
7731             val = pkgconfig.getAll();
7732         else
7733             {
7734             error("<pkg-config> unhandled query : %s", query.c_str());
7735             return false;
7736             }
7737         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7738         parent.setProperty(propName, val);
7739         return true;
7740         }
7742     virtual bool parse(Element *elem)
7743         {
7744         //# NAME
7745         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7746             return false;
7747         if (pkgNameOpt.size()==0)
7748             {
7749             error("<pkg-config> requires 'name=\"package\"' attribute");
7750             return false;
7751             }
7753         //# PROPERTY
7754         if (!parent.getAttribute(elem, "property", propNameOpt))
7755             return false;
7756         if (propNameOpt.size()==0)
7757             {
7758             error("<pkg-config> requires 'property=\"name\"' attribute");
7759             return false;
7760             }
7761         //# PATH
7762         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7763             return false;
7764         //# PREFIX
7765         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7766             return false;
7767         //# QUERY
7768         if (!parent.getAttribute(elem, "query", queryOpt))
7769             return false;
7771         return true;
7772         }
7774 private:
7776     String queryOpt;
7777     String pkgNameOpt;
7778     String prefixOpt;
7779     String propNameOpt;
7780     String pkgConfigPathOpt;
7782 };
7789 /**
7790  *  Process an archive to allow random access
7791  */
7792 class TaskRanlib : public Task
7794 public:
7796     TaskRanlib(MakeBase &par) : Task(par)
7797         { type = TASK_RANLIB; name = "ranlib"; }
7799     virtual ~TaskRanlib()
7800         {}
7802     virtual bool execute()
7803         {
7804         String fileName = parent.eval(fileNameOpt, "");
7805         String command  = parent.eval(commandOpt, "ranlib");
7807         String fullName = parent.resolve(fileName);
7808         //trace("fullDir:%s", fullDir.c_str());
7809         String cmd = command;
7810         cmd.append(" ");
7811         cmd.append(fullName);
7812         String outbuf, errbuf;
7813         if (!executeCommand(cmd, "", outbuf, errbuf))
7814             return false;
7815         return true;
7816         }
7818     virtual bool parse(Element *elem)
7819         {
7820         if (!parent.getAttribute(elem, "command", commandOpt))
7821             return false;
7822         if (!parent.getAttribute(elem, "file", fileNameOpt))
7823             return false;
7824         if (fileNameOpt.size() == 0)
7825             {
7826             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7827             return false;
7828             }
7829         return true;
7830         }
7832 private:
7834     String fileNameOpt;
7835     String commandOpt;
7836 };
7840 /**
7841  * Compile a resource file into a .res
7842  */
7843 class TaskRC : public Task
7845 public:
7847     TaskRC(MakeBase &par) : Task(par)
7848         { type = TASK_RC; name = "rc"; }
7850     virtual ~TaskRC()
7851         {}
7853     virtual bool execute()
7854         {
7855         String command  = parent.eval(commandOpt,  "windres");
7856         String flags    = parent.eval(flagsOpt,    "");
7857         String fileName = parent.eval(fileNameOpt, "");
7858         String outName  = parent.eval(outNameOpt,  "");
7860         String fullFile = parent.resolve(fileName);
7861         String fullOut  = parent.resolve(outName);
7862         if (!isNewerThan(fullFile, fullOut))
7863             return true;
7864         String cmd = command;
7865         cmd.append(" -o ");
7866         cmd.append(fullOut);
7867         cmd.append(" ");
7868         cmd.append(flags);
7869         cmd.append(" ");
7870         cmd.append(fullFile);
7872         String outString, errString;
7873         if (!executeCommand(cmd.c_str(), "", outString, errString))
7874             {
7875             error("RC problem: %s", errString.c_str());
7876             return false;
7877             }
7878         return true;
7879         }
7881     virtual bool parse(Element *elem)
7882         {
7883         if (!parent.getAttribute(elem, "command", commandOpt))
7884             return false;
7885         if (!parent.getAttribute(elem, "file", fileNameOpt))
7886             return false;
7887         if (!parent.getAttribute(elem, "out", outNameOpt))
7888             return false;
7889         std::vector<Element *> children = elem->getChildren();
7890         for (unsigned int i=0 ; i<children.size() ; i++)
7891             {
7892             Element *child = children[i];
7893             String tagName = child->getName();
7894             if (tagName == "flags")
7895                 {
7896                 if (!parent.getValue(child, flagsOpt))
7897                     return false;
7898                 }
7899             }
7900         return true;
7901         }
7903 private:
7905     String commandOpt;
7906     String flagsOpt;
7907     String fileNameOpt;
7908     String outNameOpt;
7910 };
7914 /**
7915  *  Collect .o's into a .so or DLL
7916  */
7917 class TaskSharedLib : public Task
7919 public:
7921     TaskSharedLib(MakeBase &par) : Task(par)
7922         { type = TASK_SHAREDLIB; name = "dll"; }
7924     virtual ~TaskSharedLib()
7925         {}
7927     virtual bool execute()
7928         {
7929         String command     = parent.eval(commandOpt, "dllwrap");
7930         String fileName    = parent.eval(fileNameOpt, "");
7931         String defFileName = parent.eval(defFileNameOpt, "");
7932         String impFileName = parent.eval(impFileNameOpt, "");
7933         String libs        = parent.eval(libsOpt, "");
7935         //trace("###########HERE %d", fileSet.size());
7936         bool doit = false;
7937         
7938         String fullOut = parent.resolve(fileName);
7939         //trace("ar fullout: %s", fullOut.c_str());
7940         
7941         if (!listFiles(parent, fileSet))
7942             return false;
7943         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7945         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7946             {
7947             String fname;
7948             if (fileSetDir.size()>0)
7949                 {
7950                 fname.append(fileSetDir);
7951                 fname.append("/");
7952                 }
7953             fname.append(fileSet[i]);
7954             String fullName = parent.resolve(fname);
7955             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7956             if (isNewerThan(fullName, fullOut))
7957                 doit = true;
7958             }
7959         //trace("Needs it:%d", doit);
7960         if (!doit)
7961             {
7962             return true;
7963             }
7965         String cmd = "dllwrap";
7966         cmd.append(" -o ");
7967         cmd.append(fullOut);
7968         if (defFileName.size()>0)
7969             {
7970             cmd.append(" --def ");
7971             cmd.append(defFileName);
7972             cmd.append(" ");
7973             }
7974         if (impFileName.size()>0)
7975             {
7976             cmd.append(" --implib ");
7977             cmd.append(impFileName);
7978             cmd.append(" ");
7979             }
7980         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7981             {
7982             String fname;
7983             if (fileSetDir.size()>0)
7984                 {
7985                 fname.append(fileSetDir);
7986                 fname.append("/");
7987                 }
7988             fname.append(fileSet[i]);
7989             String fullName = parent.resolve(fname);
7991             cmd.append(" ");
7992             cmd.append(fullName);
7993             }
7994         cmd.append(" ");
7995         cmd.append(libs);
7997         String outString, errString;
7998         if (!executeCommand(cmd.c_str(), "", outString, errString))
7999             {
8000             error("<sharedlib> problem: %s", errString.c_str());
8001             return false;
8002             }
8004         return true;
8005         }
8007     virtual bool parse(Element *elem)
8008         {
8009         if (!parent.getAttribute(elem, "command", commandOpt))
8010             return false;
8011         if (!parent.getAttribute(elem, "file", fileNameOpt))
8012             return false;
8013         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8014             return false;
8015         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8016             return false;
8017             
8018         std::vector<Element *> children = elem->getChildren();
8019         for (unsigned int i=0 ; i<children.size() ; i++)
8020             {
8021             Element *child = children[i];
8022             String tagName = child->getName();
8023             if (tagName == "fileset")
8024                 {
8025                 if (!parseFileSet(child, parent, fileSet))
8026                     return false;
8027                 }
8028             else if (tagName == "libs")
8029                 {
8030                 if (!parent.getValue(child, libsOpt))
8031                     return false;
8032                 libsOpt = strip(libsOpt);
8033                 }
8034             }
8035         return true;
8036         }
8038 private:
8040     FileSet fileSet;
8042     String commandOpt;
8043     String fileNameOpt;
8044     String defFileNameOpt;
8045     String impFileNameOpt;
8046     String libsOpt;
8048 };
8052 /**
8053  * Run the "ar" command to archive .o's into a .a
8054  */
8055 class TaskStaticLib : public Task
8057 public:
8059     TaskStaticLib(MakeBase &par) : Task(par)
8060         { type = TASK_STATICLIB; name = "staticlib"; }
8062     virtual ~TaskStaticLib()
8063         {}
8065     virtual bool execute()
8066         {
8067         String command = parent.eval(commandOpt, "ar crv");
8068         String fileName = parent.eval(fileNameOpt, "");
8070         bool doit = false;
8071         
8072         String fullOut = parent.resolve(fileName);
8073         //trace("ar fullout: %s", fullOut.c_str());
8074         
8075         if (!listFiles(parent, fileSet))
8076             return false;
8077         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8078         //trace("###########HERE %s", fileSetDir.c_str());
8080         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8081             {
8082             String fname;
8083             if (fileSetDir.size()>0)
8084                 {
8085                 fname.append(fileSetDir);
8086                 fname.append("/");
8087                 }
8088             fname.append(fileSet[i]);
8089             String fullName = parent.resolve(fname);
8090             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8091             if (isNewerThan(fullName, fullOut))
8092                 doit = true;
8093             }
8094         //trace("Needs it:%d", doit);
8095         if (!doit)
8096             {
8097             return true;
8098             }
8100         String cmd = command;
8101         cmd.append(" ");
8102         cmd.append(fullOut);
8103         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8104             {
8105             String fname;
8106             if (fileSetDir.size()>0)
8107                 {
8108                 fname.append(fileSetDir);
8109                 fname.append("/");
8110                 }
8111             fname.append(fileSet[i]);
8112             String fullName = parent.resolve(fname);
8114             cmd.append(" ");
8115             cmd.append(fullName);
8116             }
8118         String outString, errString;
8119         if (!executeCommand(cmd.c_str(), "", outString, errString))
8120             {
8121             error("<staticlib> problem: %s", errString.c_str());
8122             return false;
8123             }
8125         return true;
8126         }
8129     virtual bool parse(Element *elem)
8130         {
8131         if (!parent.getAttribute(elem, "command", commandOpt))
8132             return false;
8133         if (!parent.getAttribute(elem, "file", fileNameOpt))
8134             return false;
8135             
8136         std::vector<Element *> children = elem->getChildren();
8137         for (unsigned int i=0 ; i<children.size() ; i++)
8138             {
8139             Element *child = children[i];
8140             String tagName = child->getName();
8141             if (tagName == "fileset")
8142                 {
8143                 if (!parseFileSet(child, parent, fileSet))
8144                     return false;
8145                 }
8146             }
8147         return true;
8148         }
8150 private:
8152     FileSet fileSet;
8154     String commandOpt;
8155     String fileNameOpt;
8157 };
8162 /**
8163  * Strip an executable
8164  */
8165 class TaskStrip : public Task
8167 public:
8169     TaskStrip(MakeBase &par) : Task(par)
8170         { type = TASK_STRIP; name = "strip"; }
8172     virtual ~TaskStrip()
8173         {}
8175     virtual bool execute()
8176         {
8177         String command     = parent.eval(commandOpt, "strip");
8178         String fileName    = parent.eval(fileNameOpt, "");
8179         String symFileName = parent.eval(symFileNameOpt, "");
8181         String fullName = parent.resolve(fileName);
8182         //trace("fullDir:%s", fullDir.c_str());
8183         String cmd;
8184         String outbuf, errbuf;
8186         if (symFileName.size()>0)
8187             {
8188             String symFullName = parent.resolve(symFileName);
8189             cmd = "objcopy --only-keep-debug ";
8190             cmd.append(getNativePath(fullName));
8191             cmd.append(" ");
8192             cmd.append(getNativePath(symFullName));
8193             if (!executeCommand(cmd, "", outbuf, errbuf))
8194                 {
8195                 error("<strip> symbol file failed : %s", errbuf.c_str());
8196                 return false;
8197                 }
8198             }
8199             
8200         cmd = command;
8201         cmd.append(getNativePath(fullName));
8202         if (!executeCommand(cmd, "", outbuf, errbuf))
8203             {
8204             error("<strip> failed : %s", errbuf.c_str());
8205             return false;
8206             }
8207         return true;
8208         }
8210     virtual bool parse(Element *elem)
8211         {
8212         if (!parent.getAttribute(elem, "command", commandOpt))
8213             return false;
8214         if (!parent.getAttribute(elem, "file", fileNameOpt))
8215             return false;
8216         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8217             return false;
8218         if (fileNameOpt.size() == 0)
8219             {
8220             error("<strip> requires 'file=\"fileName\"' attribute");
8221             return false;
8222             }
8223         return true;
8224         }
8226 private:
8228     String commandOpt;
8229     String fileNameOpt;
8230     String symFileNameOpt;
8231 };
8234 /**
8235  *
8236  */
8237 class TaskTouch : public Task
8239 public:
8241     TaskTouch(MakeBase &par) : Task(par)
8242         { type = TASK_TOUCH; name = "touch"; }
8244     virtual ~TaskTouch()
8245         {}
8247     virtual bool execute()
8248         {
8249         String fileName = parent.eval(fileNameOpt, "");
8251         String fullName = parent.resolve(fileName);
8252         String nativeFile = getNativePath(fullName);
8253         if (!isRegularFile(fullName) && !isDirectory(fullName))
8254             {            
8255             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8256             int ret = creat(nativeFile.c_str(), 0666);
8257             if (ret != 0) 
8258                 {
8259                 error("<touch> could not create '%s' : %s",
8260                     nativeFile.c_str(), strerror(ret));
8261                 return false;
8262                 }
8263             return true;
8264             }
8265         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8266         if (ret != 0)
8267             {
8268             error("<touch> could not update the modification time for '%s' : %s",
8269                 nativeFile.c_str(), strerror(ret));
8270             return false;
8271             }
8272         return true;
8273         }
8275     virtual bool parse(Element *elem)
8276         {
8277         //trace("touch parse");
8278         if (!parent.getAttribute(elem, "file", fileNameOpt))
8279             return false;
8280         if (fileNameOpt.size() == 0)
8281             {
8282             error("<touch> requires 'file=\"fileName\"' attribute");
8283             return false;
8284             }
8285         return true;
8286         }
8288     String fileNameOpt;
8289 };
8292 /**
8293  *
8294  */
8295 class TaskTstamp : public Task
8297 public:
8299     TaskTstamp(MakeBase &par) : Task(par)
8300         { type = TASK_TSTAMP; name = "tstamp"; }
8302     virtual ~TaskTstamp()
8303         {}
8305     virtual bool execute()
8306         {
8307         return true;
8308         }
8310     virtual bool parse(Element *elem)
8311         {
8312         //trace("tstamp parse");
8313         return true;
8314         }
8315 };
8319 /**
8320  *
8321  */
8322 Task *Task::createTask(Element *elem, int lineNr)
8324     String tagName = elem->getName();
8325     //trace("task:%s", tagName.c_str());
8326     Task *task = NULL;
8327     if (tagName == "cc")
8328         task = new TaskCC(parent);
8329     else if (tagName == "copy")
8330         task = new TaskCopy(parent);
8331     else if (tagName == "delete")
8332         task = new TaskDelete(parent);
8333     else if (tagName == "echo")
8334         task = new TaskEcho(parent);
8335     else if (tagName == "jar")
8336         task = new TaskJar(parent);
8337     else if (tagName == "javac")
8338         task = new TaskJavac(parent);
8339     else if (tagName == "link")
8340         task = new TaskLink(parent);
8341     else if (tagName == "makefile")
8342         task = new TaskMakeFile(parent);
8343     else if (tagName == "mkdir")
8344         task = new TaskMkDir(parent);
8345     else if (tagName == "msgfmt")
8346         task = new TaskMsgFmt(parent);
8347     else if (tagName == "pkg-config")
8348         task = new TaskPkgConfig(parent);
8349     else if (tagName == "ranlib")
8350         task = new TaskRanlib(parent);
8351     else if (tagName == "rc")
8352         task = new TaskRC(parent);
8353     else if (tagName == "sharedlib")
8354         task = new TaskSharedLib(parent);
8355     else if (tagName == "staticlib")
8356         task = new TaskStaticLib(parent);
8357     else if (tagName == "strip")
8358         task = new TaskStrip(parent);
8359     else if (tagName == "touch")
8360         task = new TaskTouch(parent);
8361     else if (tagName == "tstamp")
8362         task = new TaskTstamp(parent);
8363     else
8364         {
8365         error("Unknown task '%s'", tagName.c_str());
8366         return NULL;
8367         }
8369     task->setLine(lineNr);
8371     if (!task->parse(elem))
8372         {
8373         delete task;
8374         return NULL;
8375         }
8376     return task;
8381 //########################################################################
8382 //# T A R G E T
8383 //########################################################################
8385 /**
8386  *
8387  */
8388 class Target : public MakeBase
8391 public:
8393     /**
8394      *
8395      */
8396     Target(Make &par) : parent(par)
8397         { init(); }
8399     /**
8400      *
8401      */
8402     Target(const Target &other) : parent(other.parent)
8403         { init(); assign(other); }
8405     /**
8406      *
8407      */
8408     Target &operator=(const Target &other)
8409         { init(); assign(other); return *this; }
8411     /**
8412      *
8413      */
8414     virtual ~Target()
8415         { cleanup() ; }
8418     /**
8419      *
8420      */
8421     virtual Make &getParent()
8422         { return parent; }
8424     /**
8425      *
8426      */
8427     virtual String getName()
8428         { return name; }
8430     /**
8431      *
8432      */
8433     virtual void setName(const String &val)
8434         { name = val; }
8436     /**
8437      *
8438      */
8439     virtual String getDescription()
8440         { return description; }
8442     /**
8443      *
8444      */
8445     virtual void setDescription(const String &val)
8446         { description = val; }
8448     /**
8449      *
8450      */
8451     virtual void addDependency(const String &val)
8452         { deps.push_back(val); }
8454     /**
8455      *
8456      */
8457     virtual void parseDependencies(const String &val)
8458         { deps = tokenize(val, ", "); }
8460     /**
8461      *
8462      */
8463     virtual std::vector<String> &getDependencies()
8464         { return deps; }
8466     /**
8467      *
8468      */
8469     virtual String getIf()
8470         { return ifVar; }
8472     /**
8473      *
8474      */
8475     virtual void setIf(const String &val)
8476         { ifVar = val; }
8478     /**
8479      *
8480      */
8481     virtual String getUnless()
8482         { return unlessVar; }
8484     /**
8485      *
8486      */
8487     virtual void setUnless(const String &val)
8488         { unlessVar = val; }
8490     /**
8491      *
8492      */
8493     virtual void addTask(Task *val)
8494         { tasks.push_back(val); }
8496     /**
8497      *
8498      */
8499     virtual std::vector<Task *> &getTasks()
8500         { return tasks; }
8502 private:
8504     void init()
8505         {
8506         }
8508     void cleanup()
8509         {
8510         tasks.clear();
8511         }
8513     void assign(const Target &other)
8514         {
8515         //parent      = other.parent;
8516         name        = other.name;
8517         description = other.description;
8518         ifVar       = other.ifVar;
8519         unlessVar   = other.unlessVar;
8520         deps        = other.deps;
8521         tasks       = other.tasks;
8522         }
8524     Make &parent;
8526     String name;
8528     String description;
8530     String ifVar;
8532     String unlessVar;
8534     std::vector<String> deps;
8536     std::vector<Task *> tasks;
8538 };
8547 //########################################################################
8548 //# M A K E
8549 //########################################################################
8552 /**
8553  *
8554  */
8555 class Make : public MakeBase
8558 public:
8560     /**
8561      *
8562      */
8563     Make()
8564         { init(); }
8566     /**
8567      *
8568      */
8569     Make(const Make &other)
8570         { assign(other); }
8572     /**
8573      *
8574      */
8575     Make &operator=(const Make &other)
8576         { assign(other); return *this; }
8578     /**
8579      *
8580      */
8581     virtual ~Make()
8582         { cleanup(); }
8584     /**
8585      *
8586      */
8587     virtual std::map<String, Target> &getTargets()
8588         { return targets; }
8591     /**
8592      *
8593      */
8594     virtual String version()
8595         { return BUILDTOOL_VERSION; }
8597     /**
8598      * Overload a <property>
8599      */
8600     virtual bool specifyProperty(const String &name,
8601                                  const String &value);
8603     /**
8604      *
8605      */
8606     virtual bool run();
8608     /**
8609      *
8610      */
8611     virtual bool run(const String &target);
8615 private:
8617     /**
8618      *
8619      */
8620     void init();
8622     /**
8623      *
8624      */
8625     void cleanup();
8627     /**
8628      *
8629      */
8630     void assign(const Make &other);
8632     /**
8633      *
8634      */
8635     bool executeTask(Task &task);
8638     /**
8639      *
8640      */
8641     bool executeTarget(Target &target,
8642              std::set<String> &targetsCompleted);
8645     /**
8646      *
8647      */
8648     bool execute();
8650     /**
8651      *
8652      */
8653     bool checkTargetDependencies(Target &prop,
8654                     std::vector<String> &depList);
8656     /**
8657      *
8658      */
8659     bool parsePropertyFile(const String &fileName,
8660                            const String &prefix);
8662     /**
8663      *
8664      */
8665     bool parseProperty(Element *elem);
8667     /**
8668      *
8669      */
8670     bool parseFile();
8672     /**
8673      *
8674      */
8675     std::vector<String> glob(const String &pattern);
8678     //###############
8679     //# Fields
8680     //###############
8682     String projectName;
8684     String currentTarget;
8686     String defaultTarget;
8688     String specifiedTarget;
8690     String baseDir;
8692     String description;
8693     
8694     //std::vector<Property> properties;
8695     
8696     std::map<String, Target> targets;
8698     std::vector<Task *> allTasks;
8699     
8700     std::map<String, String> specifiedProperties;
8702 };
8705 //########################################################################
8706 //# C L A S S  M A I N T E N A N C E
8707 //########################################################################
8709 /**
8710  *
8711  */
8712 void Make::init()
8714     uri             = "build.xml";
8715     projectName     = "";
8716     currentTarget   = "";
8717     defaultTarget   = "";
8718     specifiedTarget = "";
8719     baseDir         = "";
8720     description     = "";
8721     envPrefix       = "env.";
8722     pcPrefix        = "pc.";
8723     pccPrefix       = "pcc.";
8724     pclPrefix       = "pcl.";
8725     properties.clear();
8726     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8727         delete allTasks[i];
8728     allTasks.clear();
8733 /**
8734  *
8735  */
8736 void Make::cleanup()
8738     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8739         delete allTasks[i];
8740     allTasks.clear();
8745 /**
8746  *
8747  */
8748 void Make::assign(const Make &other)
8750     uri              = other.uri;
8751     projectName      = other.projectName;
8752     currentTarget    = other.currentTarget;
8753     defaultTarget    = other.defaultTarget;
8754     specifiedTarget  = other.specifiedTarget;
8755     baseDir          = other.baseDir;
8756     description      = other.description;
8757     properties       = other.properties;
8762 //########################################################################
8763 //# U T I L I T Y    T A S K S
8764 //########################################################################
8766 /**
8767  *  Perform a file globbing
8768  */
8769 std::vector<String> Make::glob(const String &pattern)
8771     std::vector<String> res;
8772     return res;
8776 //########################################################################
8777 //# P U B L I C    A P I
8778 //########################################################################
8782 /**
8783  *
8784  */
8785 bool Make::executeTarget(Target &target,
8786              std::set<String> &targetsCompleted)
8789     String name = target.getName();
8791     //First get any dependencies for this target
8792     std::vector<String> deps = target.getDependencies();
8793     for (unsigned int i=0 ; i<deps.size() ; i++)
8794         {
8795         String dep = deps[i];
8796         //Did we do it already?  Skip
8797         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8798             continue;
8799             
8800         std::map<String, Target> &tgts =
8801                target.getParent().getTargets();
8802         std::map<String, Target>::iterator iter =
8803                tgts.find(dep);
8804         if (iter == tgts.end())
8805             {
8806             error("Target '%s' dependency '%s' not found",
8807                       name.c_str(),  dep.c_str());
8808             return false;
8809             }
8810         Target depTarget = iter->second;
8811         if (!executeTarget(depTarget, targetsCompleted))
8812             {
8813             return false;
8814             }
8815         }
8817     status("##### Target : %s\n##### %s", name.c_str(),
8818             target.getDescription().c_str());
8820     //Now let's do the tasks
8821     std::vector<Task *> &tasks = target.getTasks();
8822     for (unsigned int i=0 ; i<tasks.size() ; i++)
8823         {
8824         Task *task = tasks[i];
8825         status("--- %s / %s", name.c_str(), task->getName().c_str());
8826         if (!task->execute())
8827             {
8828             return false;
8829             }
8830         }
8831         
8832     targetsCompleted.insert(name);
8833     
8834     return true;
8839 /**
8840  *  Main execute() method.  Start here and work
8841  *  up the dependency tree 
8842  */
8843 bool Make::execute()
8845     status("######## EXECUTE");
8847     //Determine initial target
8848     if (specifiedTarget.size()>0)
8849         {
8850         currentTarget = specifiedTarget;
8851         }
8852     else if (defaultTarget.size()>0)
8853         {
8854         currentTarget = defaultTarget;
8855         }
8856     else
8857         {
8858         error("execute: no specified or default target requested");
8859         return false;
8860         }
8862     std::map<String, Target>::iterator iter =
8863                targets.find(currentTarget);
8864     if (iter == targets.end())
8865         {
8866         error("Initial target '%s' not found",
8867                  currentTarget.c_str());
8868         return false;
8869         }
8870         
8871     //Now run
8872     Target target = iter->second;
8873     std::set<String> targetsCompleted;
8874     if (!executeTarget(target, targetsCompleted))
8875         {
8876         return false;
8877         }
8879     status("######## EXECUTE COMPLETE");
8880     return true;
8886 /**
8887  *
8888  */
8889 bool Make::checkTargetDependencies(Target &target, 
8890                             std::vector<String> &depList)
8892     String tgtName = target.getName().c_str();
8893     depList.push_back(tgtName);
8895     std::vector<String> deps = target.getDependencies();
8896     for (unsigned int i=0 ; i<deps.size() ; i++)
8897         {
8898         String dep = deps[i];
8899         //First thing entered was the starting Target
8900         if (dep == depList[0])
8901             {
8902             error("Circular dependency '%s' found at '%s'",
8903                       dep.c_str(), tgtName.c_str());
8904             std::vector<String>::iterator diter;
8905             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8906                 {
8907                 error("  %s", diter->c_str());
8908                 }
8909             return false;
8910             }
8912         std::map<String, Target> &tgts =
8913                   target.getParent().getTargets();
8914         std::map<String, Target>::iterator titer = tgts.find(dep);
8915         if (titer == tgts.end())
8916             {
8917             error("Target '%s' dependency '%s' not found",
8918                       tgtName.c_str(), dep.c_str());
8919             return false;
8920             }
8921         if (!checkTargetDependencies(titer->second, depList))
8922             {
8923             return false;
8924             }
8925         }
8926     return true;
8933 static int getword(int pos, const String &inbuf, String &result)
8935     int p = pos;
8936     int len = (int)inbuf.size();
8937     String val;
8938     while (p < len)
8939         {
8940         char ch = inbuf[p];
8941         if (!isalnum(ch) && ch!='.' && ch!='_')
8942             break;
8943         val.push_back(ch);
8944         p++;
8945         }
8946     result = val;
8947     return p;
8953 /**
8954  *
8955  */
8956 bool Make::parsePropertyFile(const String &fileName,
8957                              const String &prefix)
8959     FILE *f = fopen(fileName.c_str(), "r");
8960     if (!f)
8961         {
8962         error("could not open property file %s", fileName.c_str());
8963         return false;
8964         }
8965     int linenr = 0;
8966     while (!feof(f))
8967         {
8968         char buf[256];
8969         if (!fgets(buf, 255, f))
8970             break;
8971         linenr++;
8972         String s = buf;
8973         s = trim(s);
8974         int len = s.size();
8975         if (len == 0)
8976             continue;
8977         if (s[0] == '#')
8978             continue;
8979         String key;
8980         String val;
8981         int p = 0;
8982         int p2 = getword(p, s, key);
8983         if (p2 <= p)
8984             {
8985             error("property file %s, line %d: expected keyword",
8986                     fileName.c_str(), linenr);
8987             return false;
8988             }
8989         if (prefix.size() > 0)
8990             {
8991             key.insert(0, prefix);
8992             }
8994         //skip whitespace
8995         for (p=p2 ; p<len ; p++)
8996             if (!isspace(s[p]))
8997                 break;
8999         if (p>=len || s[p]!='=')
9000             {
9001             error("property file %s, line %d: expected '='",
9002                     fileName.c_str(), linenr);
9003             return false;
9004             }
9005         p++;
9007         //skip whitespace
9008         for ( ; p<len ; p++)
9009             if (!isspace(s[p]))
9010                 break;
9012         /* This way expects a word after the =
9013         p2 = getword(p, s, val);
9014         if (p2 <= p)
9015             {
9016             error("property file %s, line %d: expected value",
9017                     fileName.c_str(), linenr);
9018             return false;
9019             }
9020         */
9021         // This way gets the rest of the line after the =
9022         if (p>=len)
9023             {
9024             error("property file %s, line %d: expected value",
9025                     fileName.c_str(), linenr);
9026             return false;
9027             }
9028         val = s.substr(p);
9029         if (key.size()==0)
9030             continue;
9031         //allow property to be set, even if val=""
9033         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9034         //See if we wanted to overload this property
9035         std::map<String, String>::iterator iter =
9036             specifiedProperties.find(key);
9037         if (iter!=specifiedProperties.end())
9038             {
9039             val = iter->second;
9040             status("overloading property '%s' = '%s'",
9041                    key.c_str(), val.c_str());
9042             }
9043         properties[key] = val;
9044         }
9045     fclose(f);
9046     return true;
9052 /**
9053  *
9054  */
9055 bool Make::parseProperty(Element *elem)
9057     std::vector<Attribute> &attrs = elem->getAttributes();
9058     for (unsigned int i=0 ; i<attrs.size() ; i++)
9059         {
9060         String attrName = attrs[i].getName();
9061         String attrVal  = attrs[i].getValue();
9063         if (attrName == "name")
9064             {
9065             String val;
9066             if (!getAttribute(elem, "value", val))
9067                 return false;
9068             if (val.size() > 0)
9069                 {
9070                 properties[attrVal] = val;
9071                 }
9072             else
9073                 {
9074                 if (!getAttribute(elem, "location", val))
9075                     return false;
9076                 //let the property exist, even if not defined
9077                 properties[attrVal] = val;
9078                 }
9079             //See if we wanted to overload this property
9080             std::map<String, String>::iterator iter =
9081                 specifiedProperties.find(attrVal);
9082             if (iter != specifiedProperties.end())
9083                 {
9084                 val = iter->second;
9085                 status("overloading property '%s' = '%s'",
9086                     attrVal.c_str(), val.c_str());
9087                 properties[attrVal] = val;
9088                 }
9089             }
9090         else if (attrName == "file")
9091             {
9092             String prefix;
9093             if (!getAttribute(elem, "prefix", prefix))
9094                 return false;
9095             if (prefix.size() > 0)
9096                 {
9097                 if (prefix[prefix.size()-1] != '.')
9098                     prefix.push_back('.');
9099                 }
9100             if (!parsePropertyFile(attrName, prefix))
9101                 return false;
9102             }
9103         else if (attrName == "environment")
9104             {
9105             if (attrVal.find('.') != attrVal.npos)
9106                 {
9107                 error("environment prefix cannot have a '.' in it");
9108                 return false;
9109                 }
9110             envPrefix = attrVal;
9111             envPrefix.push_back('.');
9112             }
9113         else if (attrName == "pkg-config")
9114             {
9115             if (attrVal.find('.') != attrVal.npos)
9116                 {
9117                 error("pkg-config prefix cannot have a '.' in it");
9118                 return false;
9119                 }
9120             pcPrefix = attrVal;
9121             pcPrefix.push_back('.');
9122             }
9123         else if (attrName == "pkg-config-cflags")
9124             {
9125             if (attrVal.find('.') != attrVal.npos)
9126                 {
9127                 error("pkg-config-cflags prefix cannot have a '.' in it");
9128                 return false;
9129                 }
9130             pccPrefix = attrVal;
9131             pccPrefix.push_back('.');
9132             }
9133         else if (attrName == "pkg-config-libs")
9134             {
9135             if (attrVal.find('.') != attrVal.npos)
9136                 {
9137                 error("pkg-config-libs prefix cannot have a '.' in it");
9138                 return false;
9139                 }
9140             pclPrefix = attrVal;
9141             pclPrefix.push_back('.');
9142             }
9143         }
9145     return true;
9151 /**
9152  *
9153  */
9154 bool Make::parseFile()
9156     status("######## PARSE : %s", uri.getPath().c_str());
9158     setLine(0);
9160     Parser parser;
9161     Element *root = parser.parseFile(uri.getNativePath());
9162     if (!root)
9163         {
9164         error("Could not open %s for reading",
9165               uri.getNativePath().c_str());
9166         return false;
9167         }
9168     
9169     setLine(root->getLine());
9171     if (root->getChildren().size()==0 ||
9172         root->getChildren()[0]->getName()!="project")
9173         {
9174         error("Main xml element should be <project>");
9175         delete root;
9176         return false;
9177         }
9179     //########## Project attributes
9180     Element *project = root->getChildren()[0];
9181     String s = project->getAttribute("name");
9182     if (s.size() > 0)
9183         projectName = s;
9184     s = project->getAttribute("default");
9185     if (s.size() > 0)
9186         defaultTarget = s;
9187     s = project->getAttribute("basedir");
9188     if (s.size() > 0)
9189         baseDir = s;
9191     //######### PARSE MEMBERS
9192     std::vector<Element *> children = project->getChildren();
9193     for (unsigned int i=0 ; i<children.size() ; i++)
9194         {
9195         Element *elem = children[i];
9196         setLine(elem->getLine());
9197         String tagName = elem->getName();
9199         //########## DESCRIPTION
9200         if (tagName == "description")
9201             {
9202             description = parser.trim(elem->getValue());
9203             }
9205         //######### PROPERTY
9206         else if (tagName == "property")
9207             {
9208             if (!parseProperty(elem))
9209                 return false;
9210             }
9212         //######### TARGET
9213         else if (tagName == "target")
9214             {
9215             String tname   = elem->getAttribute("name");
9216             String tdesc   = elem->getAttribute("description");
9217             String tdeps   = elem->getAttribute("depends");
9218             String tif     = elem->getAttribute("if");
9219             String tunless = elem->getAttribute("unless");
9220             Target target(*this);
9221             target.setName(tname);
9222             target.setDescription(tdesc);
9223             target.parseDependencies(tdeps);
9224             target.setIf(tif);
9225             target.setUnless(tunless);
9226             std::vector<Element *> telems = elem->getChildren();
9227             for (unsigned int i=0 ; i<telems.size() ; i++)
9228                 {
9229                 Element *telem = telems[i];
9230                 Task breeder(*this);
9231                 Task *task = breeder.createTask(telem, telem->getLine());
9232                 if (!task)
9233                     return false;
9234                 allTasks.push_back(task);
9235                 target.addTask(task);
9236                 }
9238             //Check name
9239             if (tname.size() == 0)
9240                 {
9241                 error("no name for target");
9242                 return false;
9243                 }
9244             //Check for duplicate name
9245             if (targets.find(tname) != targets.end())
9246                 {
9247                 error("target '%s' already defined", tname.c_str());
9248                 return false;
9249                 }
9250             //more work than targets[tname]=target, but avoids default allocator
9251             targets.insert(std::make_pair<String, Target>(tname, target));
9252             }
9253         //######### none of the above
9254         else
9255             {
9256             error("unknown toplevel tag: <%s>", tagName.c_str());
9257             return false;
9258             }
9260         }
9262     std::map<String, Target>::iterator iter;
9263     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9264         {
9265         Target tgt = iter->second;
9266         std::vector<String> depList;
9267         if (!checkTargetDependencies(tgt, depList))
9268             {
9269             return false;
9270             }
9271         }
9274     delete root;
9275     status("######## PARSE COMPLETE");
9276     return true;
9280 /**
9281  * Overload a <property>
9282  */
9283 bool Make::specifyProperty(const String &name, const String &value)
9285     if (specifiedProperties.find(name) != specifiedProperties.end())
9286         {
9287         error("Property %s already specified", name.c_str());
9288         return false;
9289         }
9290     specifiedProperties[name] = value;
9291     return true;
9296 /**
9297  *
9298  */
9299 bool Make::run()
9301     if (!parseFile())
9302         return false;
9303         
9304     if (!execute())
9305         return false;
9307     return true;
9313 /**
9314  * Get a formatted MM:SS.sss time elapsed string
9315  */ 
9316 static String
9317 timeDiffString(struct timeval &x, struct timeval &y)
9319     long microsX  = x.tv_usec;
9320     long secondsX = x.tv_sec;
9321     long microsY  = y.tv_usec;
9322     long secondsY = y.tv_sec;
9323     if (microsX < microsY)
9324         {
9325         microsX += 1000000;
9326         secondsX -= 1;
9327         }
9329     int seconds = (int)(secondsX - secondsY);
9330     int millis  = (int)((microsX - microsY)/1000);
9332     int minutes = seconds/60;
9333     seconds -= minutes*60;
9334     char buf[80];
9335     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9336     String ret = buf;
9337     return ret;
9338     
9341 /**
9342  *
9343  */
9344 bool Make::run(const String &target)
9346     status("####################################################");
9347     status("#   %s", version().c_str());
9348     status("####################################################");
9349     struct timeval timeStart, timeEnd;
9350     ::gettimeofday(&timeStart, NULL);
9351     specifiedTarget = target;
9352     if (!run())
9353         return false;
9354     ::gettimeofday(&timeEnd, NULL);
9355     String timeStr = timeDiffString(timeEnd, timeStart);
9356     status("####################################################");
9357     status("#   BuildTool Completed : %s", timeStr.c_str());
9358     status("####################################################");
9359     return true;
9368 }// namespace buildtool
9369 //########################################################################
9370 //# M A I N
9371 //########################################################################
9373 typedef buildtool::String String;
9375 /**
9376  *  Format an error message in printf() style
9377  */
9378 static void error(const char *fmt, ...)
9380     va_list ap;
9381     va_start(ap, fmt);
9382     fprintf(stderr, "BuildTool error: ");
9383     vfprintf(stderr, fmt, ap);
9384     fprintf(stderr, "\n");
9385     va_end(ap);
9389 static bool parseProperty(const String &s, String &name, String &val)
9391     int len = s.size();
9392     int i;
9393     for (i=0 ; i<len ; i++)
9394         {
9395         char ch = s[i];
9396         if (ch == '=')
9397             break;
9398         name.push_back(ch);
9399         }
9400     if (i>=len || s[i]!='=')
9401         {
9402         error("property requires -Dname=value");
9403         return false;
9404         }
9405     i++;
9406     for ( ; i<len ; i++)
9407         {
9408         char ch = s[i];
9409         val.push_back(ch);
9410         }
9411     return true;
9415 /**
9416  * Compare a buffer with a key, for the length of the key
9417  */
9418 static bool sequ(const String &buf, const char *key)
9420     int len = buf.size();
9421     for (int i=0 ; key[i] && i<len ; i++)
9422         {
9423         if (key[i] != buf[i])
9424             return false;
9425         }        
9426     return true;
9429 static void usage(int argc, char **argv)
9431     printf("usage:\n");
9432     printf("   %s [options] [target]\n", argv[0]);
9433     printf("Options:\n");
9434     printf("  -help, -h              print this message\n");
9435     printf("  -version               print the version information and exit\n");
9436     printf("  -file <file>           use given buildfile\n");
9437     printf("  -f <file>                 ''\n");
9438     printf("  -D<property>=<value>   use value for given property\n");
9444 /**
9445  * Parse the command-line args, get our options,
9446  * and run this thing
9447  */   
9448 static bool parseOptions(int argc, char **argv)
9450     if (argc < 1)
9451         {
9452         error("Cannot parse arguments");
9453         return false;
9454         }
9456     buildtool::Make make;
9458     String target;
9460     //char *progName = argv[0];
9461     for (int i=1 ; i<argc ; i++)
9462         {
9463         String arg = argv[i];
9464         if (arg.size()>1 && arg[0]=='-')
9465             {
9466             if (arg == "-h" || arg == "-help")
9467                 {
9468                 usage(argc,argv);
9469                 return true;
9470                 }
9471             else if (arg == "-version")
9472                 {
9473                 printf("%s", make.version().c_str());
9474                 return true;
9475                 }
9476             else if (arg == "-f" || arg == "-file")
9477                 {
9478                 if (i>=argc)
9479                    {
9480                    usage(argc, argv);
9481                    return false;
9482                    }
9483                 i++; //eat option
9484                 make.setURI(argv[i]);
9485                 }
9486             else if (arg.size()>2 && sequ(arg, "-D"))
9487                 {
9488                 String s = arg.substr(2, arg.size());
9489                 String name, value;
9490                 if (!parseProperty(s, name, value))
9491                    {
9492                    usage(argc, argv);
9493                    return false;
9494                    }
9495                 if (!make.specifyProperty(name, value))
9496                     return false;
9497                 }
9498             else
9499                 {
9500                 error("Unknown option:%s", arg.c_str());
9501                 return false;
9502                 }
9503             }
9504         else
9505             {
9506             if (target.size()>0)
9507                 {
9508                 error("only one initial target");
9509                 usage(argc, argv);
9510                 return false;
9511                 }
9512             target = arg;
9513             }
9514         }
9516     //We have the options.  Now execute them
9517     if (!make.run(target))
9518         return false;
9520     return true;
9526 /*
9527 static bool runMake()
9529     buildtool::Make make;
9530     if (!make.run())
9531         return false;
9532     return true;
9536 static bool pkgConfigTest()
9538     buildtool::PkgConfig pkgConfig;
9539     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9540         return false;
9541     return true;
9546 static bool depTest()
9548     buildtool::DepTool deptool;
9549     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9550     if (!deptool.generateDependencies("build.dep"))
9551         return false;
9552     std::vector<buildtool::FileRec> res =
9553            deptool.loadDepFile("build.dep");
9554     if (res.size() == 0)
9555         return false;
9556     return true;
9559 static bool popenTest()
9561     buildtool::Make make;
9562     buildtool::String out, err;
9563     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9564     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9565     return true;
9569 static bool propFileTest()
9571     buildtool::Make make;
9572     make.parsePropertyFile("test.prop", "test.");
9573     return true;
9575 */
9577 int main(int argc, char **argv)
9580     if (!parseOptions(argc, argv))
9581         return 1;
9582     /*
9583     if (!popenTest())
9584         return 1;
9586     if (!depTest())
9587         return 1;
9588     if (!propFileTest())
9589         return 1;
9590     if (runMake())
9591         return 1;
9592     */
9593     return 0;
9597 //########################################################################
9598 //# E N D 
9599 //########################################################################