Code

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