Code

add additional needed files for win32 NSIS installer during make dist
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2007 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.4, 2007 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(" -sourcepath ");
6697         cmd.append(srcdir);
6698         cmd.append(" ");
6699         for (unsigned int i=0 ; i<fileList.size() ; i++)
6700             {
6701             String fname = fileList[i];
6702             String srcName = fname;
6703             if (fname.size()<6) //x.java
6704                 continue;
6705             if (fname.compare(fname.size()-5, 5, ".java") != 0)
6706                 continue;
6707             String baseName = fname.substr(0, fname.size()-5);
6708             String destName = baseName;
6709             destName.append(".class");
6711             String fullSrc = srcdir;
6712             fullSrc.append("/");
6713             fullSrc.append(fname);
6714             String fullDest = destdir;
6715             fullDest.append("/");
6716             fullDest.append(destName);
6717             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6718             if (!isNewerThan(fullSrc, fullDest))
6719                 continue;
6721             String execCmd = cmd;
6722             execCmd.append(fullSrc);
6724             String outString, errString;
6725             bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6726             if (!ret)
6727                 {
6728                 error("<javac> command '%s' failed :\n %s",
6729                                           execCmd.c_str(), errString.c_str());
6730                 return false;
6731                 }
6732             }
6733         return true;
6734         }
6736     virtual bool parse(Element *elem)
6737         {
6738         String s;
6739         if (!parent.getAttribute(elem, "command", s))
6740             return false;
6741         if (s.size() > 0)
6742             command = s;
6743         if (!parent.getAttribute(elem, "srcdir", srcdir))
6744             return false;
6745         if (!parent.getAttribute(elem, "destdir", destdir))
6746             return false;
6747         if (srcdir.size() == 0 || destdir.size() == 0)
6748             {
6749             error("<javac> required both srcdir and destdir attributes to be set");
6750             return false;
6751             }
6752         return true;
6753         }
6755 private:
6757     String command;
6758     String srcdir;
6759     String destdir;
6761 };
6764 /**
6765  *
6766  */
6767 class TaskLink : public Task
6769 public:
6771     TaskLink(MakeBase &par) : Task(par)
6772         {
6773         type = TASK_LINK; name = "link";
6774         command = "g++";
6775         doStrip = false;
6776         stripCommand = "strip";
6777         objcopyCommand = "objcopy";
6778         }
6780     virtual ~TaskLink()
6781         {}
6783     virtual bool execute()
6784         {
6785         if (!listFiles(parent, fileSet))
6786             return false;
6787         String fileSetDir = fileSet.getDirectory();
6788         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6789         bool doit = false;
6790         String fullTarget = parent.resolve(fileName);
6791         String cmd = command;
6792         cmd.append(" -o ");
6793         cmd.append(fullTarget);
6794         cmd.append(" ");
6795         cmd.append(flags);
6796         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6797             {
6798             cmd.append(" ");
6799             String obj;
6800             if (fileSetDir.size()>0)
6801                 {
6802                 obj.append(fileSetDir);
6803                 obj.append("/");
6804                 }
6805             obj.append(fileSet[i]);
6806             String fullObj = parent.resolve(obj);
6807             String nativeFullObj = getNativePath(fullObj);
6808             cmd.append(nativeFullObj);
6809             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6810             //          fullObj.c_str());
6811             if (isNewerThan(fullObj, fullTarget))
6812                 doit = true;
6813             }
6814         cmd.append(" ");
6815         cmd.append(libs);
6816         if (!doit)
6817             {
6818             //trace("link not needed");
6819             return true;
6820             }
6821         //trace("LINK cmd:%s", cmd.c_str());
6824         String outbuf, errbuf;
6825         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6826             {
6827             error("LINK problem: %s", errbuf.c_str());
6828             return false;
6829             }
6831         if (symFileName.size()>0)
6832             {
6833             String symFullName = parent.resolve(symFileName);
6834             cmd = objcopyCommand;
6835             cmd.append(" --only-keep-debug ");
6836             cmd.append(getNativePath(fullTarget));
6837             cmd.append(" ");
6838             cmd.append(getNativePath(symFullName));
6839             if (!executeCommand(cmd, "", outbuf, errbuf))
6840                 {
6841                 error("<strip> symbol file failed : %s", errbuf.c_str());
6842                 return false;
6843                 }
6844             }
6845             
6846         if (doStrip)
6847             {
6848             cmd = stripCommand;
6849             cmd.append(" ");
6850             cmd.append(getNativePath(fullTarget));
6851             if (!executeCommand(cmd, "", outbuf, errbuf))
6852                {
6853                error("<strip> failed : %s", errbuf.c_str());
6854                return false;
6855                }
6856             }
6858         return true;
6859         }
6861     virtual bool parse(Element *elem)
6862         {
6863         String s;
6864         if (!parent.getAttribute(elem, "command", s))
6865             return false;
6866         if (s.size()>0)
6867             command = s;
6868         if (!parent.getAttribute(elem, "objcopycommand", s))
6869             return false;
6870         if (s.size()>0)
6871             objcopyCommand = s;
6872         if (!parent.getAttribute(elem, "stripcommand", s))
6873             return false;
6874         if (s.size()>0)
6875             stripCommand = s;
6876         if (!parent.getAttribute(elem, "out", fileName))
6877             return false;
6878         if (!parent.getAttribute(elem, "strip", s))
6879             return false;
6880         if (s.size()>0 && !getBool(s, doStrip))
6881             return false;
6882         if (!parent.getAttribute(elem, "symfile", symFileName))
6883             return false;
6884             
6885         std::vector<Element *> children = elem->getChildren();
6886         for (unsigned int i=0 ; i<children.size() ; i++)
6887             {
6888             Element *child = children[i];
6889             String tagName = child->getName();
6890             if (tagName == "fileset")
6891                 {
6892                 if (!parseFileSet(child, parent, fileSet))
6893                     return false;
6894                 }
6895             else if (tagName == "flags")
6896                 {
6897                 if (!parent.getValue(child, flags))
6898                     return false;
6899                 flags = strip(flags);
6900                 }
6901             else if (tagName == "libs")
6902                 {
6903                 if (!parent.getValue(child, libs))
6904                     return false;
6905                 libs = strip(libs);
6906                 }
6907             }
6908         return true;
6909         }
6911 private:
6913     String  command;
6914     String  fileName;
6915     String  flags;
6916     String  libs;
6917     FileSet fileSet;
6918     bool    doStrip;
6919     String  symFileName;
6920     String  stripCommand;
6921     String  objcopyCommand;
6923 };
6927 /**
6928  * Create a named directory
6929  */
6930 class TaskMakeFile : public Task
6932 public:
6934     TaskMakeFile(MakeBase &par) : Task(par)
6935         { type = TASK_MAKEFILE; name = "makefile"; }
6937     virtual ~TaskMakeFile()
6938         {}
6940     virtual bool execute()
6941         {
6942         status("          : %s", fileName.c_str());
6943         String fullName = parent.resolve(fileName);
6944         if (!isNewerThan(parent.getURI().getPath(), fullName))
6945             {
6946             //trace("skipped <makefile>");
6947             return true;
6948             }
6949         String fullNative = getNativePath(fullName);
6950         //trace("fullName:%s", fullName.c_str());
6951         FILE *f = fopen(fullNative.c_str(), "w");
6952         if (!f)
6953             {
6954             error("<makefile> could not open %s for writing : %s",
6955                 fullName.c_str(), strerror(errno));
6956             return false;
6957             }
6958         for (unsigned int i=0 ; i<text.size() ; i++)
6959             fputc(text[i], f);
6960         fputc('\n', f);
6961         fclose(f);
6962         return true;
6963         }
6965     virtual bool parse(Element *elem)
6966         {
6967         if (!parent.getAttribute(elem, "file", fileName))
6968             return false;
6969         if (fileName.size() == 0)
6970             {
6971             error("<makefile> requires 'file=\"filename\"' attribute");
6972             return false;
6973             }
6974         if (!parent.getValue(elem, text))
6975             return false;
6976         text = leftJustify(text);
6977         //trace("dirname:%s", dirName.c_str());
6978         return true;
6979         }
6981 private:
6983     String fileName;
6984     String text;
6985 };
6989 /**
6990  * Create a named directory
6991  */
6992 class TaskMkDir : public Task
6994 public:
6996     TaskMkDir(MakeBase &par) : Task(par)
6997         { type = TASK_MKDIR; name = "mkdir"; }
6999     virtual ~TaskMkDir()
7000         {}
7002     virtual bool execute()
7003         {
7004         status("          : %s", dirName.c_str());
7005         String fullDir = parent.resolve(dirName);
7006         //trace("fullDir:%s", fullDir.c_str());
7007         if (!createDirectory(fullDir))
7008             return false;
7009         return true;
7010         }
7012     virtual bool parse(Element *elem)
7013         {
7014         if (!parent.getAttribute(elem, "dir", dirName))
7015             return false;
7016         if (dirName.size() == 0)
7017             {
7018             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7019             return false;
7020             }
7021         return true;
7022         }
7024 private:
7026     String dirName;
7027 };
7031 /**
7032  * Create a named directory
7033  */
7034 class TaskMsgFmt: public Task
7036 public:
7038     TaskMsgFmt(MakeBase &par) : Task(par)
7039          {
7040          type    = TASK_MSGFMT;
7041          name    = "msgfmt";
7042          command = "msgfmt";
7043          owndir  = false;
7044          outName = "";
7045          }
7047     virtual ~TaskMsgFmt()
7048         {}
7050     virtual bool execute()
7051         {
7052         if (!listFiles(parent, fileSet))
7053             return false;
7054         String fileSetDir = fileSet.getDirectory();
7056         //trace("msgfmt: %d", fileSet.size());
7057         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7058             {
7059             String fileName = fileSet[i];
7060             if (getSuffix(fileName) != "po")
7061                 continue;
7062             String sourcePath;
7063             if (fileSetDir.size()>0)
7064                 {
7065                 sourcePath.append(fileSetDir);
7066                 sourcePath.append("/");
7067                 }
7068             sourcePath.append(fileName);
7069             String fullSource = parent.resolve(sourcePath);
7071             String destPath;
7072             if (toDirName.size()>0)
7073                 {
7074                 destPath.append(toDirName);
7075                 destPath.append("/");
7076                 }
7077             if (owndir)
7078                 {
7079                 String subdir = fileName;
7080                 unsigned int pos = subdir.find_last_of('.');
7081                 if (pos != subdir.npos)
7082                     subdir = subdir.substr(0, pos);
7083                 destPath.append(subdir);
7084                 destPath.append("/");
7085                 }
7086             //Pick the output file name
7087             if (outName.size() > 0)
7088                 {
7089                 destPath.append(outName);
7090                 }
7091             else
7092                 {
7093                 destPath.append(fileName);
7094                 destPath[destPath.size()-2] = 'm';
7095                 }
7097             String fullDest = parent.resolve(destPath);
7099             if (!isNewerThan(fullSource, fullDest))
7100                 {
7101                 //trace("skip %s", fullSource.c_str());
7102                 continue;
7103                 }
7104                 
7105             String cmd = command;
7106             cmd.append(" ");
7107             cmd.append(fullSource);
7108             cmd.append(" -o ");
7109             cmd.append(fullDest);
7110             
7111             int pos = fullDest.find_last_of('/');
7112             if (pos>0)
7113                 {
7114                 String fullDestPath = fullDest.substr(0, pos);
7115                 if (!createDirectory(fullDestPath))
7116                     return false;
7117                 }
7121             String outString, errString;
7122             if (!executeCommand(cmd.c_str(), "", outString, errString))
7123                 {
7124                 error("<msgfmt> problem: %s", errString.c_str());
7125                 return false;
7126                 }
7127             }
7129         return true;
7130         }
7132     virtual bool parse(Element *elem)
7133         {
7134         String s;
7135         if (!parent.getAttribute(elem, "command", s))
7136             return false;
7137         if (s.size()>0)
7138             command = s;
7139         if (!parent.getAttribute(elem, "todir", toDirName))
7140             return false;
7141         if (!parent.getAttribute(elem, "out", outName))
7142             return false;
7143         if (!parent.getAttribute(elem, "owndir", s))
7144             return false;
7145         if (s.size()>0 && !getBool(s, owndir))
7146             return false;
7147             
7148         std::vector<Element *> children = elem->getChildren();
7149         for (unsigned int i=0 ; i<children.size() ; i++)
7150             {
7151             Element *child = children[i];
7152             String tagName = child->getName();
7153             if (tagName == "fileset")
7154                 {
7155                 if (!parseFileSet(child, parent, fileSet))
7156                     return false;
7157                 }
7158             }
7159         return true;
7160         }
7162 private:
7164     String  command;
7165     String  toDirName;
7166     String  outName;
7167     FileSet fileSet;
7168     bool    owndir;
7170 };
7174 /**
7175  *  Perform a Package-Config query similar to pkg-config
7176  */
7177 class TaskPkgConfig : public Task
7179 public:
7181     typedef enum
7182         {
7183         PKG_CONFIG_QUERY_CFLAGS,
7184         PKG_CONFIG_QUERY_LIBS,
7185         PKG_CONFIG_QUERY_ALL
7186         } QueryTypes;
7188     TaskPkgConfig(MakeBase &par) : Task(par)
7189         {
7190         type = TASK_PKG_CONFIG;
7191         name = "pkg-config";
7192         }
7194     virtual ~TaskPkgConfig()
7195         {}
7197     virtual bool execute()
7198         {
7199         String path = parent.resolve(pkg_config_path);
7200         PkgConfig pkgconfig;
7201         pkgconfig.setPath(path);
7202         pkgconfig.setPrefix(prefix);
7203         if (!pkgconfig.query(pkgName))
7204             {
7205             error("<pkg-config> query failed for '%s", name.c_str());
7206             return false;
7207             }
7208         String ret;
7209         switch (query)
7210             {
7211             case PKG_CONFIG_QUERY_CFLAGS:
7212                 {
7213                 ret = pkgconfig.getCflags();
7214                 break;
7215                 }
7216             case PKG_CONFIG_QUERY_LIBS:
7217                 {
7218                 ret = pkgconfig.getLibs();
7219                 break;
7220                 }
7221             case PKG_CONFIG_QUERY_ALL:
7222                 {
7223                 ret = pkgconfig.getAll();
7224                 break;
7225                 }
7226             default:
7227                 {
7228                 error("<pkg-config> unhandled query : %d", query);
7229                 return false;
7230                 }
7231             
7232             }
7233         status("          : %s", ret.c_str());
7234         parent.setProperty(propName, ret);
7235         return true;
7236         }
7238     virtual bool parse(Element *elem)
7239         {
7240         String s;
7241         //# NAME
7242         if (!parent.getAttribute(elem, "name", s))
7243             return false;
7244         if (s.size()>0)
7245            pkgName = s;
7246         else
7247             {
7248             error("<pkg-config> requires 'name=\"package\"' attribute");
7249             return false;
7250             }
7252         //# PROPERTY
7253         if (!parent.getAttribute(elem, "property", s))
7254             return false;
7255         if (s.size()>0)
7256            propName = s;
7257         else
7258             {
7259             error("<pkg-config> requires 'property=\"name\"' attribute");
7260             return false;
7261             }
7262         if (parent.hasProperty(propName))
7263             {
7264             error("<pkg-config> property '%s' is already defined",
7265                           propName.c_str());
7266             return false;
7267             }
7268         parent.setProperty(propName, "undefined");
7270         //# PATH
7271         if (!parent.getAttribute(elem, "path", s))
7272             return false;
7273         if (s.size()>0)
7274            pkg_config_path = s;
7276         //# PREFIX
7277         if (!parent.getAttribute(elem, "prefix", s))
7278             return false;
7279         if (s.size()>0)
7280            prefix = s;
7282         //# QUERY
7283         if (!parent.getAttribute(elem, "query", s))
7284             return false;
7285         if (s == "cflags")
7286             query = PKG_CONFIG_QUERY_CFLAGS;
7287         else if (s == "libs")
7288             query = PKG_CONFIG_QUERY_LIBS;
7289         else if (s == "both")
7290             query = PKG_CONFIG_QUERY_ALL;
7291         else
7292             {
7293             error("<pkg-config> requires 'query=\"type\"' attribute");
7294             error("where type = cflags, libs, or both");
7295             return false;
7296             }
7297         return true;
7298         }
7300 private:
7302     String pkgName;
7303     String prefix;
7304     String propName;
7305     String pkg_config_path;
7306     int query;
7308 };
7315 /**
7316  *  Process an archive to allow random access
7317  */
7318 class TaskRanlib : public Task
7320 public:
7322     TaskRanlib(MakeBase &par) : Task(par)
7323         {
7324         type = TASK_RANLIB; name = "ranlib";
7325         command = "ranlib";
7326         }
7328     virtual ~TaskRanlib()
7329         {}
7331     virtual bool execute()
7332         {
7333         String fullName = parent.resolve(fileName);
7334         //trace("fullDir:%s", fullDir.c_str());
7335         String cmd = command;
7336         cmd.append(" ");
7337         cmd.append(fullName);
7338         String outbuf, errbuf;
7339         if (!executeCommand(cmd, "", outbuf, errbuf))
7340             return false;
7341         return true;
7342         }
7344     virtual bool parse(Element *elem)
7345         {
7346         String s;
7347         if (!parent.getAttribute(elem, "command", s))
7348             return false;
7349         if (s.size()>0)
7350            command = s;
7351         if (!parent.getAttribute(elem, "file", fileName))
7352             return false;
7353         if (fileName.size() == 0)
7354             {
7355             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7356             return false;
7357             }
7358         return true;
7359         }
7361 private:
7363     String fileName;
7364     String command;
7365 };
7369 /**
7370  * Run the "ar" command to archive .o's into a .a
7371  */
7372 class TaskRC : public Task
7374 public:
7376     TaskRC(MakeBase &par) : Task(par)
7377         {
7378         type = TASK_RC; name = "rc";
7379         command = "windres";
7380         }
7382     virtual ~TaskRC()
7383         {}
7385     virtual bool execute()
7386         {
7387         String fullFile = parent.resolve(fileName);
7388         String fullOut  = parent.resolve(outName);
7389         if (!isNewerThan(fullFile, fullOut))
7390             return true;
7391         String cmd = command;
7392         cmd.append(" -o ");
7393         cmd.append(fullOut);
7394         cmd.append(" ");
7395         cmd.append(flags);
7396         cmd.append(" ");
7397         cmd.append(fullFile);
7399         String outString, errString;
7400         if (!executeCommand(cmd.c_str(), "", outString, errString))
7401             {
7402             error("RC problem: %s", errString.c_str());
7403             return false;
7404             }
7405         return true;
7406         }
7408     virtual bool parse(Element *elem)
7409         {
7410         if (!parent.getAttribute(elem, "command", command))
7411             return false;
7412         if (!parent.getAttribute(elem, "file", fileName))
7413             return false;
7414         if (!parent.getAttribute(elem, "out", outName))
7415             return false;
7416         std::vector<Element *> children = elem->getChildren();
7417         for (unsigned int i=0 ; i<children.size() ; i++)
7418             {
7419             Element *child = children[i];
7420             String tagName = child->getName();
7421             if (tagName == "flags")
7422                 {
7423                 if (!parent.getValue(child, flags))
7424                     return false;
7425                 }
7426             }
7427         return true;
7428         }
7430 private:
7432     String command;
7433     String flags;
7434     String fileName;
7435     String outName;
7437 };
7441 /**
7442  *  Collect .o's into a .so or DLL
7443  */
7444 class TaskSharedLib : public Task
7446 public:
7448     TaskSharedLib(MakeBase &par) : Task(par)
7449         {
7450         type = TASK_SHAREDLIB; name = "dll";
7451         command = "dllwrap";
7452         }
7454     virtual ~TaskSharedLib()
7455         {}
7457     virtual bool execute()
7458         {
7459         //trace("###########HERE %d", fileSet.size());
7460         bool doit = false;
7461         
7462         String fullOut = parent.resolve(fileName);
7463         //trace("ar fullout: %s", fullOut.c_str());
7464         
7465         if (!listFiles(parent, fileSet))
7466             return false;
7467         String fileSetDir = fileSet.getDirectory();
7469         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7470             {
7471             String fname;
7472             if (fileSetDir.size()>0)
7473                 {
7474                 fname.append(fileSetDir);
7475                 fname.append("/");
7476                 }
7477             fname.append(fileSet[i]);
7478             String fullName = parent.resolve(fname);
7479             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7480             if (isNewerThan(fullName, fullOut))
7481                 doit = true;
7482             }
7483         //trace("Needs it:%d", doit);
7484         if (!doit)
7485             {
7486             return true;
7487             }
7489         String cmd = "dllwrap";
7490         cmd.append(" -o ");
7491         cmd.append(fullOut);
7492         if (defFileName.size()>0)
7493             {
7494             cmd.append(" --def ");
7495             cmd.append(defFileName);
7496             cmd.append(" ");
7497             }
7498         if (impFileName.size()>0)
7499             {
7500             cmd.append(" --implib ");
7501             cmd.append(impFileName);
7502             cmd.append(" ");
7503             }
7504         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7505             {
7506             String fname;
7507             if (fileSetDir.size()>0)
7508                 {
7509                 fname.append(fileSetDir);
7510                 fname.append("/");
7511                 }
7512             fname.append(fileSet[i]);
7513             String fullName = parent.resolve(fname);
7515             cmd.append(" ");
7516             cmd.append(fullName);
7517             }
7518         cmd.append(" ");
7519         cmd.append(libs);
7521         String outString, errString;
7522         if (!executeCommand(cmd.c_str(), "", outString, errString))
7523             {
7524             error("<sharedlib> problem: %s", errString.c_str());
7525             return false;
7526             }
7528         return true;
7529         }
7531     virtual bool parse(Element *elem)
7532         {
7533         if (!parent.getAttribute(elem, "file", fileName))
7534             return false;
7535         if (!parent.getAttribute(elem, "import", impFileName))
7536             return false;
7537         if (!parent.getAttribute(elem, "def", defFileName))
7538             return false;
7539             
7540         std::vector<Element *> children = elem->getChildren();
7541         for (unsigned int i=0 ; i<children.size() ; i++)
7542             {
7543             Element *child = children[i];
7544             String tagName = child->getName();
7545             if (tagName == "fileset")
7546                 {
7547                 if (!parseFileSet(child, parent, fileSet))
7548                     return false;
7549                 }
7550             else if (tagName == "libs")
7551                 {
7552                 if (!parent.getValue(child, libs))
7553                     return false;
7554                 libs = strip(libs);
7555                 }
7556             }
7557         return true;
7558         }
7560 private:
7562     String command;
7563     String fileName;
7564     String defFileName;
7565     String impFileName;
7566     FileSet fileSet;
7567     String libs;
7569 };
7573 /**
7574  * Run the "ar" command to archive .o's into a .a
7575  */
7576 class TaskStaticLib : public Task
7578 public:
7580     TaskStaticLib(MakeBase &par) : Task(par)
7581         {
7582         type = TASK_STATICLIB; name = "staticlib";
7583         command = "ar crv";
7584         }
7586     virtual ~TaskStaticLib()
7587         {}
7589     virtual bool execute()
7590         {
7591         //trace("###########HERE %d", fileSet.size());
7592         bool doit = false;
7593         
7594         String fullOut = parent.resolve(fileName);
7595         //trace("ar fullout: %s", fullOut.c_str());
7596         
7597         if (!listFiles(parent, fileSet))
7598             return false;
7599         String fileSetDir = fileSet.getDirectory();
7601         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7602             {
7603             String fname;
7604             if (fileSetDir.size()>0)
7605                 {
7606                 fname.append(fileSetDir);
7607                 fname.append("/");
7608                 }
7609             fname.append(fileSet[i]);
7610             String fullName = parent.resolve(fname);
7611             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7612             if (isNewerThan(fullName, fullOut))
7613                 doit = true;
7614             }
7615         //trace("Needs it:%d", doit);
7616         if (!doit)
7617             {
7618             return true;
7619             }
7621         String cmd = command;
7622         cmd.append(" ");
7623         cmd.append(fullOut);
7624         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7625             {
7626             String fname;
7627             if (fileSetDir.size()>0)
7628                 {
7629                 fname.append(fileSetDir);
7630                 fname.append("/");
7631                 }
7632             fname.append(fileSet[i]);
7633             String fullName = parent.resolve(fname);
7635             cmd.append(" ");
7636             cmd.append(fullName);
7637             }
7639         String outString, errString;
7640         if (!executeCommand(cmd.c_str(), "", outString, errString))
7641             {
7642             error("<staticlib> problem: %s", errString.c_str());
7643             return false;
7644             }
7646         return true;
7647         }
7650     virtual bool parse(Element *elem)
7651         {
7652         String s;
7653         if (!parent.getAttribute(elem, "command", s))
7654             return false;
7655         if (s.size()>0)
7656             command = s;
7657         if (!parent.getAttribute(elem, "file", fileName))
7658             return false;
7659             
7660         std::vector<Element *> children = elem->getChildren();
7661         for (unsigned int i=0 ; i<children.size() ; i++)
7662             {
7663             Element *child = children[i];
7664             String tagName = child->getName();
7665             if (tagName == "fileset")
7666                 {
7667                 if (!parseFileSet(child, parent, fileSet))
7668                     return false;
7669                 }
7670             }
7671         return true;
7672         }
7674 private:
7676     String command;
7677     String fileName;
7678     FileSet fileSet;
7680 };
7685 /**
7686  * Strip an executable
7687  */
7688 class TaskStrip : public Task
7690 public:
7692     TaskStrip(MakeBase &par) : Task(par)
7693         { type = TASK_STRIP; name = "strip"; }
7695     virtual ~TaskStrip()
7696         {}
7698     virtual bool execute()
7699         {
7700         String fullName = parent.resolve(fileName);
7701         //trace("fullDir:%s", fullDir.c_str());
7702         String cmd;
7703         String outbuf, errbuf;
7705         if (symFileName.size()>0)
7706             {
7707             String symFullName = parent.resolve(symFileName);
7708             cmd = "objcopy --only-keep-debug ";
7709             cmd.append(getNativePath(fullName));
7710             cmd.append(" ");
7711             cmd.append(getNativePath(symFullName));
7712             if (!executeCommand(cmd, "", outbuf, errbuf))
7713                 {
7714                 error("<strip> symbol file failed : %s", errbuf.c_str());
7715                 return false;
7716                 }
7717             }
7718             
7719         cmd = "strip ";
7720         cmd.append(getNativePath(fullName));
7721         if (!executeCommand(cmd, "", outbuf, errbuf))
7722             {
7723             error("<strip> failed : %s", errbuf.c_str());
7724             return false;
7725             }
7726         return true;
7727         }
7729     virtual bool parse(Element *elem)
7730         {
7731         if (!parent.getAttribute(elem, "file", fileName))
7732             return false;
7733         if (!parent.getAttribute(elem, "symfile", symFileName))
7734             return false;
7735         if (fileName.size() == 0)
7736             {
7737             error("<strip> requires 'file=\"fileName\"' attribute");
7738             return false;
7739             }
7740         return true;
7741         }
7743 private:
7745     String fileName;
7746     String symFileName;
7747 };
7750 /**
7751  *
7752  */
7753 class TaskTouch : public Task
7755 public:
7757     TaskTouch(MakeBase &par) : Task(par)
7758         { type = TASK_TOUCH; name = "touch"; }
7760     virtual ~TaskTouch()
7761         {}
7763     virtual bool execute()
7764         {
7765         String fullName = parent.resolve(fileName);
7766         String nativeFile = getNativePath(fullName);
7767         if (!isRegularFile(fullName) && !isDirectory(fullName))
7768             {            
7769             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7770             int ret = creat(nativeFile.c_str(), 0666);
7771             if (ret != 0) 
7772                 {
7773                 error("<touch> could not create '%s' : %s",
7774                     nativeFile.c_str(), strerror(ret));
7775                 return false;
7776                 }
7777             return true;
7778             }
7779         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7780         if (ret != 0)
7781             {
7782             error("<touch> could not update the modification time for '%s' : %s",
7783                 nativeFile.c_str(), strerror(ret));
7784             return false;
7785             }
7786         return true;
7787         }
7789     virtual bool parse(Element *elem)
7790         {
7791         //trace("touch parse");
7792         if (!parent.getAttribute(elem, "file", fileName))
7793             return false;
7794         if (fileName.size() == 0)
7795             {
7796             error("<touch> requires 'file=\"fileName\"' attribute");
7797             return false;
7798             }
7799         return true;
7800         }
7802     String fileName;
7803 };
7806 /**
7807  *
7808  */
7809 class TaskTstamp : public Task
7811 public:
7813     TaskTstamp(MakeBase &par) : Task(par)
7814         { type = TASK_TSTAMP; name = "tstamp"; }
7816     virtual ~TaskTstamp()
7817         {}
7819     virtual bool execute()
7820         {
7821         return true;
7822         }
7824     virtual bool parse(Element *elem)
7825         {
7826         //trace("tstamp parse");
7827         return true;
7828         }
7829 };
7833 /**
7834  *
7835  */
7836 Task *Task::createTask(Element *elem, int lineNr)
7838     String tagName = elem->getName();
7839     //trace("task:%s", tagName.c_str());
7840     Task *task = NULL;
7841     if (tagName == "cc")
7842         task = new TaskCC(parent);
7843     else if (tagName == "copy")
7844         task = new TaskCopy(parent);
7845     else if (tagName == "delete")
7846         task = new TaskDelete(parent);
7847     else if (tagName == "jar")
7848         task = new TaskJar(parent);
7849     else if (tagName == "javac")
7850         task = new TaskJavac(parent);
7851     else if (tagName == "link")
7852         task = new TaskLink(parent);
7853     else if (tagName == "makefile")
7854         task = new TaskMakeFile(parent);
7855     else if (tagName == "mkdir")
7856         task = new TaskMkDir(parent);
7857     else if (tagName == "msgfmt")
7858         task = new TaskMsgFmt(parent);
7859     else if (tagName == "pkg-config")
7860         task = new TaskPkgConfig(parent);
7861     else if (tagName == "ranlib")
7862         task = new TaskRanlib(parent);
7863     else if (tagName == "rc")
7864         task = new TaskRC(parent);
7865     else if (tagName == "sharedlib")
7866         task = new TaskSharedLib(parent);
7867     else if (tagName == "staticlib")
7868         task = new TaskStaticLib(parent);
7869     else if (tagName == "strip")
7870         task = new TaskStrip(parent);
7871     else if (tagName == "touch")
7872         task = new TaskTouch(parent);
7873     else if (tagName == "tstamp")
7874         task = new TaskTstamp(parent);
7875     else
7876         {
7877         error("Unknown task '%s'", tagName.c_str());
7878         return NULL;
7879         }
7881     task->setLine(lineNr);
7883     if (!task->parse(elem))
7884         {
7885         delete task;
7886         return NULL;
7887         }
7888     return task;
7893 //########################################################################
7894 //# T A R G E T
7895 //########################################################################
7897 /**
7898  *
7899  */
7900 class Target : public MakeBase
7903 public:
7905     /**
7906      *
7907      */
7908     Target(Make &par) : parent(par)
7909         { init(); }
7911     /**
7912      *
7913      */
7914     Target(const Target &other) : parent(other.parent)
7915         { init(); assign(other); }
7917     /**
7918      *
7919      */
7920     Target &operator=(const Target &other)
7921         { init(); assign(other); return *this; }
7923     /**
7924      *
7925      */
7926     virtual ~Target()
7927         { cleanup() ; }
7930     /**
7931      *
7932      */
7933     virtual Make &getParent()
7934         { return parent; }
7936     /**
7937      *
7938      */
7939     virtual String getName()
7940         { return name; }
7942     /**
7943      *
7944      */
7945     virtual void setName(const String &val)
7946         { name = val; }
7948     /**
7949      *
7950      */
7951     virtual String getDescription()
7952         { return description; }
7954     /**
7955      *
7956      */
7957     virtual void setDescription(const String &val)
7958         { description = val; }
7960     /**
7961      *
7962      */
7963     virtual void addDependency(const String &val)
7964         { deps.push_back(val); }
7966     /**
7967      *
7968      */
7969     virtual void parseDependencies(const String &val)
7970         { deps = tokenize(val, ", "); }
7972     /**
7973      *
7974      */
7975     virtual std::vector<String> &getDependencies()
7976         { return deps; }
7978     /**
7979      *
7980      */
7981     virtual String getIf()
7982         { return ifVar; }
7984     /**
7985      *
7986      */
7987     virtual void setIf(const String &val)
7988         { ifVar = val; }
7990     /**
7991      *
7992      */
7993     virtual String getUnless()
7994         { return unlessVar; }
7996     /**
7997      *
7998      */
7999     virtual void setUnless(const String &val)
8000         { unlessVar = val; }
8002     /**
8003      *
8004      */
8005     virtual void addTask(Task *val)
8006         { tasks.push_back(val); }
8008     /**
8009      *
8010      */
8011     virtual std::vector<Task *> &getTasks()
8012         { return tasks; }
8014 private:
8016     void init()
8017         {
8018         }
8020     void cleanup()
8021         {
8022         tasks.clear();
8023         }
8025     void assign(const Target &other)
8026         {
8027         //parent      = other.parent;
8028         name        = other.name;
8029         description = other.description;
8030         ifVar       = other.ifVar;
8031         unlessVar   = other.unlessVar;
8032         deps        = other.deps;
8033         tasks       = other.tasks;
8034         }
8036     Make &parent;
8038     String name;
8040     String description;
8042     String ifVar;
8044     String unlessVar;
8046     std::vector<String> deps;
8048     std::vector<Task *> tasks;
8050 };
8059 //########################################################################
8060 //# M A K E
8061 //########################################################################
8064 /**
8065  *
8066  */
8067 class Make : public MakeBase
8070 public:
8072     /**
8073      *
8074      */
8075     Make()
8076         { init(); }
8078     /**
8079      *
8080      */
8081     Make(const Make &other)
8082         { assign(other); }
8084     /**
8085      *
8086      */
8087     Make &operator=(const Make &other)
8088         { assign(other); return *this; }
8090     /**
8091      *
8092      */
8093     virtual ~Make()
8094         { cleanup(); }
8096     /**
8097      *
8098      */
8099     virtual std::map<String, Target> &getTargets()
8100         { return targets; }
8103     /**
8104      *
8105      */
8106     virtual String version()
8107         { return BUILDTOOL_VERSION; }
8109     /**
8110      * Overload a <property>
8111      */
8112     virtual bool specifyProperty(const String &name,
8113                                  const String &value);
8115     /**
8116      *
8117      */
8118     virtual bool run();
8120     /**
8121      *
8122      */
8123     virtual bool run(const String &target);
8127 private:
8129     /**
8130      *
8131      */
8132     void init();
8134     /**
8135      *
8136      */
8137     void cleanup();
8139     /**
8140      *
8141      */
8142     void assign(const Make &other);
8144     /**
8145      *
8146      */
8147     bool executeTask(Task &task);
8150     /**
8151      *
8152      */
8153     bool executeTarget(Target &target,
8154              std::set<String> &targetsCompleted);
8157     /**
8158      *
8159      */
8160     bool execute();
8162     /**
8163      *
8164      */
8165     bool checkTargetDependencies(Target &prop,
8166                     std::vector<String> &depList);
8168     /**
8169      *
8170      */
8171     bool parsePropertyFile(const String &fileName,
8172                            const String &prefix);
8174     /**
8175      *
8176      */
8177     bool parseProperty(Element *elem);
8179     /**
8180      *
8181      */
8182     bool parseFile();
8184     /**
8185      *
8186      */
8187     std::vector<String> glob(const String &pattern);
8190     //###############
8191     //# Fields
8192     //###############
8194     String projectName;
8196     String currentTarget;
8198     String defaultTarget;
8200     String specifiedTarget;
8202     String baseDir;
8204     String description;
8205     
8206     //std::vector<Property> properties;
8207     
8208     std::map<String, Target> targets;
8210     std::vector<Task *> allTasks;
8211     
8212     std::map<String, String> specifiedProperties;
8214 };
8217 //########################################################################
8218 //# C L A S S  M A I N T E N A N C E
8219 //########################################################################
8221 /**
8222  *
8223  */
8224 void Make::init()
8226     uri             = "build.xml";
8227     projectName     = "";
8228     currentTarget   = "";
8229     defaultTarget   = "";
8230     specifiedTarget = "";
8231     baseDir         = "";
8232     description     = "";
8233     envPrefix       = "";
8234     properties.clear();
8235     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8236         delete allTasks[i];
8237     allTasks.clear();
8242 /**
8243  *
8244  */
8245 void Make::cleanup()
8247     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8248         delete allTasks[i];
8249     allTasks.clear();
8254 /**
8255  *
8256  */
8257 void Make::assign(const Make &other)
8259     uri              = other.uri;
8260     projectName      = other.projectName;
8261     currentTarget    = other.currentTarget;
8262     defaultTarget    = other.defaultTarget;
8263     specifiedTarget  = other.specifiedTarget;
8264     baseDir          = other.baseDir;
8265     description      = other.description;
8266     properties       = other.properties;
8271 //########################################################################
8272 //# U T I L I T Y    T A S K S
8273 //########################################################################
8275 /**
8276  *  Perform a file globbing
8277  */
8278 std::vector<String> Make::glob(const String &pattern)
8280     std::vector<String> res;
8281     return res;
8285 //########################################################################
8286 //# P U B L I C    A P I
8287 //########################################################################
8291 /**
8292  *
8293  */
8294 bool Make::executeTarget(Target &target,
8295              std::set<String> &targetsCompleted)
8298     String name = target.getName();
8300     //First get any dependencies for this target
8301     std::vector<String> deps = target.getDependencies();
8302     for (unsigned int i=0 ; i<deps.size() ; i++)
8303         {
8304         String dep = deps[i];
8305         //Did we do it already?  Skip
8306         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8307             continue;
8308             
8309         std::map<String, Target> &tgts =
8310                target.getParent().getTargets();
8311         std::map<String, Target>::iterator iter =
8312                tgts.find(dep);
8313         if (iter == tgts.end())
8314             {
8315             error("Target '%s' dependency '%s' not found",
8316                       name.c_str(),  dep.c_str());
8317             return false;
8318             }
8319         Target depTarget = iter->second;
8320         if (!executeTarget(depTarget, targetsCompleted))
8321             {
8322             return false;
8323             }
8324         }
8326     status("## Target : %s : %s", name.c_str(),
8327             target.getDescription().c_str());
8329     //Now let's do the tasks
8330     std::vector<Task *> &tasks = target.getTasks();
8331     for (unsigned int i=0 ; i<tasks.size() ; i++)
8332         {
8333         Task *task = tasks[i];
8334         status("---- task : %s", task->getName().c_str());
8335         if (!task->execute())
8336             {
8337             return false;
8338             }
8339         }
8340         
8341     targetsCompleted.insert(name);
8342     
8343     return true;
8348 /**
8349  *  Main execute() method.  Start here and work
8350  *  up the dependency tree 
8351  */
8352 bool Make::execute()
8354     status("######## EXECUTE");
8356     //Determine initial target
8357     if (specifiedTarget.size()>0)
8358         {
8359         currentTarget = specifiedTarget;
8360         }
8361     else if (defaultTarget.size()>0)
8362         {
8363         currentTarget = defaultTarget;
8364         }
8365     else
8366         {
8367         error("execute: no specified or default target requested");
8368         return false;
8369         }
8371     std::map<String, Target>::iterator iter =
8372                targets.find(currentTarget);
8373     if (iter == targets.end())
8374         {
8375         error("Initial target '%s' not found",
8376                  currentTarget.c_str());
8377         return false;
8378         }
8379         
8380     //Now run
8381     Target target = iter->second;
8382     std::set<String> targetsCompleted;
8383     if (!executeTarget(target, targetsCompleted))
8384         {
8385         return false;
8386         }
8388     status("######## EXECUTE COMPLETE");
8389     return true;
8395 /**
8396  *
8397  */
8398 bool Make::checkTargetDependencies(Target &target, 
8399                             std::vector<String> &depList)
8401     String tgtName = target.getName().c_str();
8402     depList.push_back(tgtName);
8404     std::vector<String> deps = target.getDependencies();
8405     for (unsigned int i=0 ; i<deps.size() ; i++)
8406         {
8407         String dep = deps[i];
8408         //First thing entered was the starting Target
8409         if (dep == depList[0])
8410             {
8411             error("Circular dependency '%s' found at '%s'",
8412                       dep.c_str(), tgtName.c_str());
8413             std::vector<String>::iterator diter;
8414             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8415                 {
8416                 error("  %s", diter->c_str());
8417                 }
8418             return false;
8419             }
8421         std::map<String, Target> &tgts =
8422                   target.getParent().getTargets();
8423         std::map<String, Target>::iterator titer = tgts.find(dep);
8424         if (titer == tgts.end())
8425             {
8426             error("Target '%s' dependency '%s' not found",
8427                       tgtName.c_str(), dep.c_str());
8428             return false;
8429             }
8430         if (!checkTargetDependencies(titer->second, depList))
8431             {
8432             return false;
8433             }
8434         }
8435     return true;
8442 static int getword(int pos, const String &inbuf, String &result)
8444     int p = pos;
8445     int len = (int)inbuf.size();
8446     String val;
8447     while (p < len)
8448         {
8449         char ch = inbuf[p];
8450         if (!isalnum(ch) && ch!='.' && ch!='_')
8451             break;
8452         val.push_back(ch);
8453         p++;
8454         }
8455     result = val;
8456     return p;
8462 /**
8463  *
8464  */
8465 bool Make::parsePropertyFile(const String &fileName,
8466                              const String &prefix)
8468     FILE *f = fopen(fileName.c_str(), "r");
8469     if (!f)
8470         {
8471         error("could not open property file %s", fileName.c_str());
8472         return false;
8473         }
8474     int linenr = 0;
8475     while (!feof(f))
8476         {
8477         char buf[256];
8478         if (!fgets(buf, 255, f))
8479             break;
8480         linenr++;
8481         String s = buf;
8482         s = trim(s);
8483         int len = s.size();
8484         if (len == 0)
8485             continue;
8486         if (s[0] == '#')
8487             continue;
8488         String key;
8489         String val;
8490         int p = 0;
8491         int p2 = getword(p, s, key);
8492         if (p2 <= p)
8493             {
8494             error("property file %s, line %d: expected keyword",
8495                     fileName.c_str(), linenr);
8496             return false;
8497             }
8498         if (prefix.size() > 0)
8499             {
8500             key.insert(0, prefix);
8501             }
8503         //skip whitespace
8504         for (p=p2 ; p<len ; p++)
8505             if (!isspace(s[p]))
8506                 break;
8508         if (p>=len || s[p]!='=')
8509             {
8510             error("property file %s, line %d: expected '='",
8511                     fileName.c_str(), linenr);
8512             return false;
8513             }
8514         p++;
8516         //skip whitespace
8517         for ( ; p<len ; p++)
8518             if (!isspace(s[p]))
8519                 break;
8521         /* This way expects a word after the =
8522         p2 = getword(p, s, val);
8523         if (p2 <= p)
8524             {
8525             error("property file %s, line %d: expected value",
8526                     fileName.c_str(), linenr);
8527             return false;
8528             }
8529         */
8530         // This way gets the rest of the line after the =
8531         if (p>=len)
8532             {
8533             error("property file %s, line %d: expected value",
8534                     fileName.c_str(), linenr);
8535             return false;
8536             }
8537         val = s.substr(p);
8538         if (key.size()==0)
8539             continue;
8540         //allow property to be set, even if val=""
8542         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8543         //See if we wanted to overload this property
8544         std::map<String, String>::iterator iter =
8545             specifiedProperties.find(key);
8546         if (iter!=specifiedProperties.end())
8547             {
8548             val = iter->second;
8549             status("overloading property '%s' = '%s'",
8550                    key.c_str(), val.c_str());
8551             }
8552         properties[key] = val;
8553         }
8554     fclose(f);
8555     return true;
8561 /**
8562  *
8563  */
8564 bool Make::parseProperty(Element *elem)
8566     std::vector<Attribute> &attrs = elem->getAttributes();
8567     for (unsigned int i=0 ; i<attrs.size() ; i++)
8568         {
8569         String attrName = attrs[i].getName();
8570         String attrVal  = attrs[i].getValue();
8572         if (attrName == "name")
8573             {
8574             String val;
8575             if (!getAttribute(elem, "value", val))
8576                 return false;
8577             if (val.size() > 0)
8578                 {
8579                 properties[attrVal] = val;
8580                 }
8581             else
8582                 {
8583                 if (!getAttribute(elem, "location", val))
8584                     return false;
8585                 //let the property exist, even if not defined
8586                 properties[attrVal] = val;
8587                 }
8588             //See if we wanted to overload this property
8589             std::map<String, String>::iterator iter =
8590                 specifiedProperties.find(attrVal);
8591             if (iter != specifiedProperties.end())
8592                 {
8593                 val = iter->second;
8594                 status("overloading property '%s' = '%s'",
8595                     attrVal.c_str(), val.c_str());
8596                 properties[attrVal] = val;
8597                 }
8598             }
8599         else if (attrName == "file")
8600             {
8601             String prefix;
8602             if (!getAttribute(elem, "prefix", prefix))
8603                 return false;
8604             if (prefix.size() > 0)
8605                 {
8606                 if (prefix[prefix.size()-1] != '.')
8607                     prefix.push_back('.');
8608                 }
8609             if (!parsePropertyFile(attrName, prefix))
8610                 return false;
8611             }
8612         else if (attrName == "environment")
8613             {
8614             if (envPrefix.size() > 0)
8615                 {
8616                 error("environment prefix can only be set once");
8617                 return false;
8618                 }
8619             if (attrVal.find('.') != attrVal.npos)
8620                 {
8621                 error("environment prefix cannot have a '.' in it");
8622                 return false;
8623                 }
8624             envPrefix = attrVal;
8625             envPrefix.push_back('.');
8626             }
8627         }
8629     return true;
8635 /**
8636  *
8637  */
8638 bool Make::parseFile()
8640     status("######## PARSE : %s", uri.getPath().c_str());
8642     setLine(0);
8644     Parser parser;
8645     Element *root = parser.parseFile(uri.getNativePath());
8646     if (!root)
8647         {
8648         error("Could not open %s for reading",
8649               uri.getNativePath().c_str());
8650         return false;
8651         }
8652     
8653     setLine(root->getLine());
8655     if (root->getChildren().size()==0 ||
8656         root->getChildren()[0]->getName()!="project")
8657         {
8658         error("Main xml element should be <project>");
8659         delete root;
8660         return false;
8661         }
8663     //########## Project attributes
8664     Element *project = root->getChildren()[0];
8665     String s = project->getAttribute("name");
8666     if (s.size() > 0)
8667         projectName = s;
8668     s = project->getAttribute("default");
8669     if (s.size() > 0)
8670         defaultTarget = s;
8671     s = project->getAttribute("basedir");
8672     if (s.size() > 0)
8673         baseDir = s;
8675     //######### PARSE MEMBERS
8676     std::vector<Element *> children = project->getChildren();
8677     for (unsigned int i=0 ; i<children.size() ; i++)
8678         {
8679         Element *elem = children[i];
8680         setLine(elem->getLine());
8681         String tagName = elem->getName();
8683         //########## DESCRIPTION
8684         if (tagName == "description")
8685             {
8686             description = parser.trim(elem->getValue());
8687             }
8689         //######### PROPERTY
8690         else if (tagName == "property")
8691             {
8692             if (!parseProperty(elem))
8693                 return false;
8694             }
8696         //######### TARGET
8697         else if (tagName == "target")
8698             {
8699             String tname   = elem->getAttribute("name");
8700             String tdesc   = elem->getAttribute("description");
8701             String tdeps   = elem->getAttribute("depends");
8702             String tif     = elem->getAttribute("if");
8703             String tunless = elem->getAttribute("unless");
8704             Target target(*this);
8705             target.setName(tname);
8706             target.setDescription(tdesc);
8707             target.parseDependencies(tdeps);
8708             target.setIf(tif);
8709             target.setUnless(tunless);
8710             std::vector<Element *> telems = elem->getChildren();
8711             for (unsigned int i=0 ; i<telems.size() ; i++)
8712                 {
8713                 Element *telem = telems[i];
8714                 Task breeder(*this);
8715                 Task *task = breeder.createTask(telem, telem->getLine());
8716                 if (!task)
8717                     return false;
8718                 allTasks.push_back(task);
8719                 target.addTask(task);
8720                 }
8722             //Check name
8723             if (tname.size() == 0)
8724                 {
8725                 error("no name for target");
8726                 return false;
8727                 }
8728             //Check for duplicate name
8729             if (targets.find(tname) != targets.end())
8730                 {
8731                 error("target '%s' already defined", tname.c_str());
8732                 return false;
8733                 }
8734             //more work than targets[tname]=target, but avoids default allocator
8735             targets.insert(std::make_pair<String, Target>(tname, target));
8736             }
8737         //######### none of the above
8738         else
8739             {
8740             error("unknown toplevel tag: <%s>", tagName.c_str());
8741             return false;
8742             }
8744         }
8746     std::map<String, Target>::iterator iter;
8747     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8748         {
8749         Target tgt = iter->second;
8750         std::vector<String> depList;
8751         if (!checkTargetDependencies(tgt, depList))
8752             {
8753             return false;
8754             }
8755         }
8758     delete root;
8759     status("######## PARSE COMPLETE");
8760     return true;
8764 /**
8765  * Overload a <property>
8766  */
8767 bool Make::specifyProperty(const String &name, const String &value)
8769     if (specifiedProperties.find(name) != specifiedProperties.end())
8770         {
8771         error("Property %s already specified", name.c_str());
8772         return false;
8773         }
8774     specifiedProperties[name] = value;
8775     return true;
8780 /**
8781  *
8782  */
8783 bool Make::run()
8785     if (!parseFile())
8786         return false;
8787         
8788     if (!execute())
8789         return false;
8791     return true;
8797 /**
8798  * Get a formatted MM:SS.sss time elapsed string
8799  */ 
8800 static String
8801 timeDiffString(struct timeval &x, struct timeval &y)
8803     long microsX  = x.tv_usec;
8804     long secondsX = x.tv_sec;
8805     long microsY  = y.tv_usec;
8806     long secondsY = y.tv_sec;
8807     if (microsX < microsY)
8808         {
8809         microsX += 1000000;
8810         secondsX -= 1;
8811         }
8813     int seconds = (int)(secondsX - secondsY);
8814     int millis  = (int)((microsX - microsY)/1000);
8816     int minutes = seconds/60;
8817     seconds -= minutes*60;
8818     char buf[80];
8819     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8820     String ret = buf;
8821     return ret;
8822     
8825 /**
8826  *
8827  */
8828 bool Make::run(const String &target)
8830     status("####################################################");
8831     status("#   %s", version().c_str());
8832     status("####################################################");
8833     struct timeval timeStart, timeEnd;
8834     ::gettimeofday(&timeStart, NULL);
8835     specifiedTarget = target;
8836     if (!run())
8837         return false;
8838     ::gettimeofday(&timeEnd, NULL);
8839     String timeStr = timeDiffString(timeEnd, timeStart);
8840     status("####################################################");
8841     status("#   BuildTool Completed : %s", timeStr.c_str());
8842     status("####################################################");
8843     return true;
8852 }// namespace buildtool
8853 //########################################################################
8854 //# M A I N
8855 //########################################################################
8857 typedef buildtool::String String;
8859 /**
8860  *  Format an error message in printf() style
8861  */
8862 static void error(const char *fmt, ...)
8864     va_list ap;
8865     va_start(ap, fmt);
8866     fprintf(stderr, "BuildTool error: ");
8867     vfprintf(stderr, fmt, ap);
8868     fprintf(stderr, "\n");
8869     va_end(ap);
8873 static bool parseProperty(const String &s, String &name, String &val)
8875     int len = s.size();
8876     int i;
8877     for (i=0 ; i<len ; i++)
8878         {
8879         char ch = s[i];
8880         if (ch == '=')
8881             break;
8882         name.push_back(ch);
8883         }
8884     if (i>=len || s[i]!='=')
8885         {
8886         error("property requires -Dname=value");
8887         return false;
8888         }
8889     i++;
8890     for ( ; i<len ; i++)
8891         {
8892         char ch = s[i];
8893         val.push_back(ch);
8894         }
8895     return true;
8899 /**
8900  * Compare a buffer with a key, for the length of the key
8901  */
8902 static bool sequ(const String &buf, const char *key)
8904     int len = buf.size();
8905     for (int i=0 ; key[i] && i<len ; i++)
8906         {
8907         if (key[i] != buf[i])
8908             return false;
8909         }        
8910     return true;
8913 static void usage(int argc, char **argv)
8915     printf("usage:\n");
8916     printf("   %s [options] [target]\n", argv[0]);
8917     printf("Options:\n");
8918     printf("  -help, -h              print this message\n");
8919     printf("  -version               print the version information and exit\n");
8920     printf("  -file <file>           use given buildfile\n");
8921     printf("  -f <file>                 ''\n");
8922     printf("  -D<property>=<value>   use value for given property\n");
8928 /**
8929  * Parse the command-line args, get our options,
8930  * and run this thing
8931  */   
8932 static bool parseOptions(int argc, char **argv)
8934     if (argc < 1)
8935         {
8936         error("Cannot parse arguments");
8937         return false;
8938         }
8940     buildtool::Make make;
8942     String target;
8944     //char *progName = argv[0];
8945     for (int i=1 ; i<argc ; i++)
8946         {
8947         String arg = argv[i];
8948         if (arg.size()>1 && arg[0]=='-')
8949             {
8950             if (arg == "-h" || arg == "-help")
8951                 {
8952                 usage(argc,argv);
8953                 return true;
8954                 }
8955             else if (arg == "-version")
8956                 {
8957                 printf("%s", make.version().c_str());
8958                 return true;
8959                 }
8960             else if (arg == "-f" || arg == "-file")
8961                 {
8962                 if (i>=argc)
8963                    {
8964                    usage(argc, argv);
8965                    return false;
8966                    }
8967                 i++; //eat option
8968                 make.setURI(argv[i]);
8969                 }
8970             else if (arg.size()>2 && sequ(arg, "-D"))
8971                 {
8972                 String s = arg.substr(2, arg.size());
8973                 String name, value;
8974                 if (!parseProperty(s, name, value))
8975                    {
8976                    usage(argc, argv);
8977                    return false;
8978                    }
8979                 if (!make.specifyProperty(name, value))
8980                     return false;
8981                 }
8982             else
8983                 {
8984                 error("Unknown option:%s", arg.c_str());
8985                 return false;
8986                 }
8987             }
8988         else
8989             {
8990             if (target.size()>0)
8991                 {
8992                 error("only one initial target");
8993                 usage(argc, argv);
8994                 return false;
8995                 }
8996             target = arg;
8997             }
8998         }
9000     //We have the options.  Now execute them
9001     if (!make.run(target))
9002         return false;
9004     return true;
9010 /*
9011 static bool runMake()
9013     buildtool::Make make;
9014     if (!make.run())
9015         return false;
9016     return true;
9020 static bool pkgConfigTest()
9022     buildtool::PkgConfig pkgConfig;
9023     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9024         return false;
9025     return true;
9030 static bool depTest()
9032     buildtool::DepTool deptool;
9033     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9034     if (!deptool.generateDependencies("build.dep"))
9035         return false;
9036     std::vector<buildtool::FileRec> res =
9037            deptool.loadDepFile("build.dep");
9038     if (res.size() == 0)
9039         return false;
9040     return true;
9043 static bool popenTest()
9045     buildtool::Make make;
9046     buildtool::String out, err;
9047     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9048     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9049     return true;
9053 static bool propFileTest()
9055     buildtool::Make make;
9056     make.parsePropertyFile("test.prop", "test.");
9057     return true;
9059 */
9061 int main(int argc, char **argv)
9064     if (!parseOptions(argc, argv))
9065         return 1;
9066     /*
9067     if (!popenTest())
9068         return 1;
9070     if (!depTest())
9071         return 1;
9072     if (!propFileTest())
9073         return 1;
9074     if (runMake())
9075         return 1;
9076     */
9077     return 0;
9081 //########################################################################
9082 //# E N D 
9083 //########################################################################