Code

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