Code

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