Code

prevent file_save_remote to break build on system without GNOME_VFS
[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.1, 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         String fullNative = getNativePath(fullName);
6856         //trace("fullName:%s", fullName.c_str());
6857         FILE *f = fopen(fullNative.c_str(), "w");
6858         if (!f)
6859             {
6860             error("<makefile> could not open %s for writing : %s",
6861                 fullName.c_str(), strerror(errno));
6862             return false;
6863             }
6864         for (unsigned int i=0 ; i<text.size() ; i++)
6865             fputc(text[i], f);
6866         fputc('\n', f);
6867         fclose(f);
6868         return true;
6869         }
6871     virtual bool parse(Element *elem)
6872         {
6873         if (!parent.getAttribute(elem, "file", fileName))
6874             return false;
6875         if (fileName.size() == 0)
6876             {
6877             error("<makefile> requires 'file=\"filename\"' attribute");
6878             return false;
6879             }
6880         if (!parent.getValue(elem, text))
6881             return false;
6882         text = leftJustify(text);
6883         //trace("dirname:%s", dirName.c_str());
6884         return true;
6885         }
6887 private:
6889     String fileName;
6890     String text;
6891 };
6895 /**
6896  * Create a named directory
6897  */
6898 class TaskMkDir : public Task
6900 public:
6902     TaskMkDir(MakeBase &par) : Task(par)
6903         { type = TASK_MKDIR; name = "mkdir"; }
6905     virtual ~TaskMkDir()
6906         {}
6908     virtual bool execute()
6909         {
6910         status("          : %s", dirName.c_str());
6911         String fullDir = parent.resolve(dirName);
6912         //trace("fullDir:%s", fullDir.c_str());
6913         if (!createDirectory(fullDir))
6914             return false;
6915         return true;
6916         }
6918     virtual bool parse(Element *elem)
6919         {
6920         if (!parent.getAttribute(elem, "dir", dirName))
6921             return false;
6922         if (dirName.size() == 0)
6923             {
6924             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6925             return false;
6926             }
6927         return true;
6928         }
6930 private:
6932     String dirName;
6933 };
6937 /**
6938  * Create a named directory
6939  */
6940 class TaskMsgFmt: public Task
6942 public:
6944     TaskMsgFmt(MakeBase &par) : Task(par)
6945          {
6946          type    = TASK_MSGFMT;
6947          name    = "msgfmt";
6948          command = "msgfmt";
6949          owndir  = false;
6950          outName = "";
6951          }
6953     virtual ~TaskMsgFmt()
6954         {}
6956     virtual bool execute()
6957         {
6958         if (!listFiles(parent, fileSet))
6959             return false;
6960         String fileSetDir = fileSet.getDirectory();
6962         //trace("msgfmt: %d", fileSet.size());
6963         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6964             {
6965             String fileName = fileSet[i];
6966             if (getSuffix(fileName) != "po")
6967                 continue;
6968             String sourcePath;
6969             if (fileSetDir.size()>0)
6970                 {
6971                 sourcePath.append(fileSetDir);
6972                 sourcePath.append("/");
6973                 }
6974             sourcePath.append(fileName);
6975             String fullSource = parent.resolve(sourcePath);
6977             String destPath;
6978             if (toDirName.size()>0)
6979                 {
6980                 destPath.append(toDirName);
6981                 destPath.append("/");
6982                 }
6983             if (owndir)
6984                 {
6985                 String subdir = fileName;
6986                 unsigned int pos = subdir.find_last_of('.');
6987                 if (pos != subdir.npos)
6988                     subdir = subdir.substr(0, pos);
6989                 destPath.append(subdir);
6990                 destPath.append("/");
6991                 }
6992             //Pick the output file name
6993             if (outName.size() > 0)
6994                 {
6995                 destPath.append(outName);
6996                 }
6997             else
6998                 {
6999                 destPath.append(fileName);
7000                 destPath[destPath.size()-2] = 'm';
7001                 }
7003             String fullDest = parent.resolve(destPath);
7005             if (!isNewerThan(fullSource, fullDest))
7006                 {
7007                 //trace("skip %s", fullSource.c_str());
7008                 continue;
7009                 }
7010                 
7011             String cmd = command;
7012             cmd.append(" ");
7013             cmd.append(fullSource);
7014             cmd.append(" -o ");
7015             cmd.append(fullDest);
7016             
7017             int pos = fullDest.find_last_of('/');
7018             if (pos>0)
7019                 {
7020                 String fullDestPath = fullDest.substr(0, pos);
7021                 if (!createDirectory(fullDestPath))
7022                     return false;
7023                 }
7027             String outString, errString;
7028             if (!executeCommand(cmd.c_str(), "", outString, errString))
7029                 {
7030                 error("<msgfmt> problem: %s", errString.c_str());
7031                 return false;
7032                 }
7033             }
7035         return true;
7036         }
7038     virtual bool parse(Element *elem)
7039         {
7040         String s;
7041         if (!parent.getAttribute(elem, "command", s))
7042             return false;
7043         if (s.size()>0)
7044             command = s;
7045         if (!parent.getAttribute(elem, "todir", toDirName))
7046             return false;
7047         if (!parent.getAttribute(elem, "out", outName))
7048             return false;
7049         if (!parent.getAttribute(elem, "owndir", s))
7050             return false;
7051         if (s.size()>0 && !getBool(s, owndir))
7052             return false;
7053             
7054         std::vector<Element *> children = elem->getChildren();
7055         for (unsigned int i=0 ; i<children.size() ; i++)
7056             {
7057             Element *child = children[i];
7058             String tagName = child->getName();
7059             if (tagName == "fileset")
7060                 {
7061                 if (!parseFileSet(child, parent, fileSet))
7062                     return false;
7063                 }
7064             }
7065         return true;
7066         }
7068 private:
7070     String  command;
7071     String  toDirName;
7072     String  outName;
7073     FileSet fileSet;
7074     bool    owndir;
7076 };
7080 /**
7081  *  Perform a Package-Config query similar to pkg-config
7082  */
7083 class TaskPkgConfig : public Task
7085 public:
7087     typedef enum
7088         {
7089         PKG_CONFIG_QUERY_CFLAGS,
7090         PKG_CONFIG_QUERY_LIBS,
7091         PKG_CONFIG_QUERY_ALL
7092         } QueryTypes;
7094     TaskPkgConfig(MakeBase &par) : Task(par)
7095         {
7096         type = TASK_PKG_CONFIG;
7097         name = "pkg-config";
7098         }
7100     virtual ~TaskPkgConfig()
7101         {}
7103     virtual bool execute()
7104         {
7105         String path = parent.resolve(pkg_config_path);
7106         PkgConfig pkgconfig;
7107         pkgconfig.setPath(path);
7108         pkgconfig.setPrefix(prefix);
7109         if (!pkgconfig.query(pkgName))
7110             {
7111             error("<pkg-config> query failed for '%s", name.c_str());
7112             return false;
7113             }
7114         String ret;
7115         switch (query)
7116             {
7117             case PKG_CONFIG_QUERY_CFLAGS:
7118                 {
7119                 ret = pkgconfig.getCflags();
7120                 break;
7121                 }
7122             case PKG_CONFIG_QUERY_LIBS:
7123                 {
7124                 ret = pkgconfig.getLibs();
7125                 break;
7126                 }
7127             case PKG_CONFIG_QUERY_ALL:
7128                 {
7129                 ret = pkgconfig.getAll();
7130                 break;
7131                 }
7132             default:
7133                 {
7134                 error("<pkg-config> unhandled query : %d", query);
7135                 return false;
7136                 }
7137             
7138             }
7139         status("          : %s", ret.c_str());
7140         parent.setProperty(propName, ret);
7141         return true;
7142         }
7144     virtual bool parse(Element *elem)
7145         {
7146         String s;
7147         //# NAME
7148         if (!parent.getAttribute(elem, "name", s))
7149             return false;
7150         if (s.size()>0)
7151            pkgName = s;
7152         else
7153             {
7154             error("<pkg-config> requires 'name=\"package\"' attribute");
7155             return false;
7156             }
7158         //# PROPERTY
7159         if (!parent.getAttribute(elem, "property", s))
7160             return false;
7161         if (s.size()>0)
7162            propName = s;
7163         else
7164             {
7165             error("<pkg-config> requires 'property=\"name\"' attribute");
7166             return false;
7167             }
7168         if (parent.hasProperty(propName))
7169             {
7170             error("<pkg-config> property '%s' is already defined",
7171                           propName.c_str());
7172             return false;
7173             }
7174         parent.setProperty(propName, "undefined");
7176         //# PATH
7177         if (!parent.getAttribute(elem, "path", s))
7178             return false;
7179         if (s.size()>0)
7180            pkg_config_path = s;
7182         //# PREFIX
7183         if (!parent.getAttribute(elem, "prefix", s))
7184             return false;
7185         if (s.size()>0)
7186            prefix = s;
7188         //# QUERY
7189         if (!parent.getAttribute(elem, "query", s))
7190             return false;
7191         if (s == "cflags")
7192             query = PKG_CONFIG_QUERY_CFLAGS;
7193         else if (s == "libs")
7194             query = PKG_CONFIG_QUERY_LIBS;
7195         else if (s == "both")
7196             query = PKG_CONFIG_QUERY_ALL;
7197         else
7198             {
7199             error("<pkg-config> requires 'query=\"type\"' attribute");
7200             error("where type = cflags, libs, or both");
7201             return false;
7202             }
7203         return true;
7204         }
7206 private:
7208     String pkgName;
7209     String prefix;
7210     String propName;
7211     String pkg_config_path;
7212     int query;
7214 };
7221 /**
7222  *  Process an archive to allow random access
7223  */
7224 class TaskRanlib : public Task
7226 public:
7228     TaskRanlib(MakeBase &par) : Task(par)
7229         {
7230         type = TASK_RANLIB; name = "ranlib";
7231         command = "ranlib";
7232         }
7234     virtual ~TaskRanlib()
7235         {}
7237     virtual bool execute()
7238         {
7239         String fullName = parent.resolve(fileName);
7240         //trace("fullDir:%s", fullDir.c_str());
7241         String cmd = command;
7242         cmd.append(" ");
7243         cmd.append(fullName);
7244         String outbuf, errbuf;
7245         if (!executeCommand(cmd, "", outbuf, errbuf))
7246             return false;
7247         return true;
7248         }
7250     virtual bool parse(Element *elem)
7251         {
7252         String s;
7253         if (!parent.getAttribute(elem, "command", s))
7254             return false;
7255         if (s.size()>0)
7256            command = s;
7257         if (!parent.getAttribute(elem, "file", fileName))
7258             return false;
7259         if (fileName.size() == 0)
7260             {
7261             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7262             return false;
7263             }
7264         return true;
7265         }
7267 private:
7269     String fileName;
7270     String command;
7271 };
7275 /**
7276  * Run the "ar" command to archive .o's into a .a
7277  */
7278 class TaskRC : public Task
7280 public:
7282     TaskRC(MakeBase &par) : Task(par)
7283         {
7284         type = TASK_RC; name = "rc";
7285         command = "windres";
7286         }
7288     virtual ~TaskRC()
7289         {}
7291     virtual bool execute()
7292         {
7293         String fullFile = parent.resolve(fileName);
7294         String fullOut  = parent.resolve(outName);
7295         if (!isNewerThan(fullFile, fullOut))
7296             return true;
7297         String cmd = command;
7298         cmd.append(" -o ");
7299         cmd.append(fullOut);
7300         cmd.append(" ");
7301         cmd.append(flags);
7302         cmd.append(" ");
7303         cmd.append(fullFile);
7305         String outString, errString;
7306         if (!executeCommand(cmd.c_str(), "", outString, errString))
7307             {
7308             error("RC problem: %s", errString.c_str());
7309             return false;
7310             }
7311         return true;
7312         }
7314     virtual bool parse(Element *elem)
7315         {
7316         if (!parent.getAttribute(elem, "command", command))
7317             return false;
7318         if (!parent.getAttribute(elem, "file", fileName))
7319             return false;
7320         if (!parent.getAttribute(elem, "out", outName))
7321             return false;
7322         std::vector<Element *> children = elem->getChildren();
7323         for (unsigned int i=0 ; i<children.size() ; i++)
7324             {
7325             Element *child = children[i];
7326             String tagName = child->getName();
7327             if (tagName == "flags")
7328                 {
7329                 if (!parent.getValue(child, flags))
7330                     return false;
7331                 }
7332             }
7333         return true;
7334         }
7336 private:
7338     String command;
7339     String flags;
7340     String fileName;
7341     String outName;
7343 };
7347 /**
7348  *  Collect .o's into a .so or DLL
7349  */
7350 class TaskSharedLib : public Task
7352 public:
7354     TaskSharedLib(MakeBase &par) : Task(par)
7355         {
7356         type = TASK_SHAREDLIB; name = "dll";
7357         command = "dllwrap";
7358         }
7360     virtual ~TaskSharedLib()
7361         {}
7363     virtual bool execute()
7364         {
7365         //trace("###########HERE %d", fileSet.size());
7366         bool doit = false;
7367         
7368         String fullOut = parent.resolve(fileName);
7369         //trace("ar fullout: %s", fullOut.c_str());
7370         
7371         if (!listFiles(parent, fileSet))
7372             return false;
7373         String fileSetDir = fileSet.getDirectory();
7375         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7376             {
7377             String fname;
7378             if (fileSetDir.size()>0)
7379                 {
7380                 fname.append(fileSetDir);
7381                 fname.append("/");
7382                 }
7383             fname.append(fileSet[i]);
7384             String fullName = parent.resolve(fname);
7385             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7386             if (isNewerThan(fullName, fullOut))
7387                 doit = true;
7388             }
7389         //trace("Needs it:%d", doit);
7390         if (!doit)
7391             {
7392             return true;
7393             }
7395         String cmd = "dllwrap";
7396         cmd.append(" -o ");
7397         cmd.append(fullOut);
7398         if (defFileName.size()>0)
7399             {
7400             cmd.append(" --def ");
7401             cmd.append(defFileName);
7402             cmd.append(" ");
7403             }
7404         if (impFileName.size()>0)
7405             {
7406             cmd.append(" --implib ");
7407             cmd.append(impFileName);
7408             cmd.append(" ");
7409             }
7410         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7411             {
7412             String fname;
7413             if (fileSetDir.size()>0)
7414                 {
7415                 fname.append(fileSetDir);
7416                 fname.append("/");
7417                 }
7418             fname.append(fileSet[i]);
7419             String fullName = parent.resolve(fname);
7421             cmd.append(" ");
7422             cmd.append(fullName);
7423             }
7424         cmd.append(" ");
7425         cmd.append(libs);
7427         String outString, errString;
7428         if (!executeCommand(cmd.c_str(), "", outString, errString))
7429             {
7430             error("<sharedlib> problem: %s", errString.c_str());
7431             return false;
7432             }
7434         return true;
7435         }
7437     virtual bool parse(Element *elem)
7438         {
7439         if (!parent.getAttribute(elem, "file", fileName))
7440             return false;
7441         if (!parent.getAttribute(elem, "import", impFileName))
7442             return false;
7443         if (!parent.getAttribute(elem, "def", defFileName))
7444             return false;
7445             
7446         std::vector<Element *> children = elem->getChildren();
7447         for (unsigned int i=0 ; i<children.size() ; i++)
7448             {
7449             Element *child = children[i];
7450             String tagName = child->getName();
7451             if (tagName == "fileset")
7452                 {
7453                 if (!parseFileSet(child, parent, fileSet))
7454                     return false;
7455                 }
7456             else if (tagName == "libs")
7457                 {
7458                 if (!parent.getValue(child, libs))
7459                     return false;
7460                 libs = strip(libs);
7461                 }
7462             }
7463         return true;
7464         }
7466 private:
7468     String command;
7469     String fileName;
7470     String defFileName;
7471     String impFileName;
7472     FileSet fileSet;
7473     String libs;
7475 };
7479 /**
7480  * Run the "ar" command to archive .o's into a .a
7481  */
7482 class TaskStaticLib : public Task
7484 public:
7486     TaskStaticLib(MakeBase &par) : Task(par)
7487         {
7488         type = TASK_STATICLIB; name = "staticlib";
7489         command = "ar crv";
7490         }
7492     virtual ~TaskStaticLib()
7493         {}
7495     virtual bool execute()
7496         {
7497         //trace("###########HERE %d", fileSet.size());
7498         bool doit = false;
7499         
7500         String fullOut = parent.resolve(fileName);
7501         //trace("ar fullout: %s", fullOut.c_str());
7502         
7503         if (!listFiles(parent, fileSet))
7504             return false;
7505         String fileSetDir = fileSet.getDirectory();
7507         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7508             {
7509             String fname;
7510             if (fileSetDir.size()>0)
7511                 {
7512                 fname.append(fileSetDir);
7513                 fname.append("/");
7514                 }
7515             fname.append(fileSet[i]);
7516             String fullName = parent.resolve(fname);
7517             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7518             if (isNewerThan(fullName, fullOut))
7519                 doit = true;
7520             }
7521         //trace("Needs it:%d", doit);
7522         if (!doit)
7523             {
7524             return true;
7525             }
7527         String cmd = command;
7528         cmd.append(" ");
7529         cmd.append(fullOut);
7530         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7531             {
7532             String fname;
7533             if (fileSetDir.size()>0)
7534                 {
7535                 fname.append(fileSetDir);
7536                 fname.append("/");
7537                 }
7538             fname.append(fileSet[i]);
7539             String fullName = parent.resolve(fname);
7541             cmd.append(" ");
7542             cmd.append(fullName);
7543             }
7545         String outString, errString;
7546         if (!executeCommand(cmd.c_str(), "", outString, errString))
7547             {
7548             error("<staticlib> problem: %s", errString.c_str());
7549             return false;
7550             }
7552         return true;
7553         }
7556     virtual bool parse(Element *elem)
7557         {
7558         String s;
7559         if (!parent.getAttribute(elem, "command", s))
7560             return false;
7561         if (s.size()>0)
7562             command = s;
7563         if (!parent.getAttribute(elem, "file", fileName))
7564             return false;
7565             
7566         std::vector<Element *> children = elem->getChildren();
7567         for (unsigned int i=0 ; i<children.size() ; i++)
7568             {
7569             Element *child = children[i];
7570             String tagName = child->getName();
7571             if (tagName == "fileset")
7572                 {
7573                 if (!parseFileSet(child, parent, fileSet))
7574                     return false;
7575                 }
7576             }
7577         return true;
7578         }
7580 private:
7582     String command;
7583     String fileName;
7584     FileSet fileSet;
7586 };
7591 /**
7592  * Strip an executable
7593  */
7594 class TaskStrip : public Task
7596 public:
7598     TaskStrip(MakeBase &par) : Task(par)
7599         { type = TASK_STRIP; name = "strip"; }
7601     virtual ~TaskStrip()
7602         {}
7604     virtual bool execute()
7605         {
7606         String fullName = parent.resolve(fileName);
7607         //trace("fullDir:%s", fullDir.c_str());
7608         String cmd;
7609         String outbuf, errbuf;
7611         if (symFileName.size()>0)
7612             {
7613             String symFullName = parent.resolve(symFileName);
7614             cmd = "objcopy --only-keep-debug ";
7615             cmd.append(getNativePath(fullName));
7616             cmd.append(" ");
7617             cmd.append(getNativePath(symFullName));
7618             if (!executeCommand(cmd, "", outbuf, errbuf))
7619                 {
7620                 error("<strip> symbol file failed : %s", errbuf.c_str());
7621                 return false;
7622                 }
7623             }
7624             
7625         cmd = "strip ";
7626         cmd.append(getNativePath(fullName));
7627         if (!executeCommand(cmd, "", outbuf, errbuf))
7628             {
7629             error("<strip> failed : %s", errbuf.c_str());
7630             return false;
7631             }
7632         return true;
7633         }
7635     virtual bool parse(Element *elem)
7636         {
7637         if (!parent.getAttribute(elem, "file", fileName))
7638             return false;
7639         if (!parent.getAttribute(elem, "symfile", symFileName))
7640             return false;
7641         if (fileName.size() == 0)
7642             {
7643             error("<strip> requires 'file=\"fileName\"' attribute");
7644             return false;
7645             }
7646         return true;
7647         }
7649 private:
7651     String fileName;
7652     String symFileName;
7653 };
7656 /**
7657  *
7658  */
7659 class TaskTouch : public Task
7661 public:
7663     TaskTouch(MakeBase &par) : Task(par)
7664         { type = TASK_TOUCH; name = "touch"; }
7666     virtual ~TaskTouch()
7667         {}
7669     virtual bool execute()
7670         {
7671         String fullName = parent.resolve(fileName);
7672         String nativeFile = getNativePath(fullName);
7673         if (!isRegularFile(fullName) && !isDirectory(fullName))
7674             {            
7675             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7676             int ret = creat(nativeFile.c_str(), 0666);
7677             if (ret != 0) 
7678                 {
7679                 error("<touch> could not create '%s' : %s",
7680                     nativeFile.c_str(), strerror(ret));
7681                 return false;
7682                 }
7683             return true;
7684             }
7685         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7686         if (ret != 0)
7687             {
7688             error("<touch> could not update the modification time for '%s' : %s",
7689                 nativeFile.c_str(), strerror(ret));
7690             return false;
7691             }
7692         return true;
7693         }
7695     virtual bool parse(Element *elem)
7696         {
7697         //trace("touch parse");
7698         if (!parent.getAttribute(elem, "file", fileName))
7699             return false;
7700         if (fileName.size() == 0)
7701             {
7702             error("<touch> requires 'file=\"fileName\"' attribute");
7703             return false;
7704             }
7705         return true;
7706         }
7708     String fileName;
7709 };
7712 /**
7713  *
7714  */
7715 class TaskTstamp : public Task
7717 public:
7719     TaskTstamp(MakeBase &par) : Task(par)
7720         { type = TASK_TSTAMP; name = "tstamp"; }
7722     virtual ~TaskTstamp()
7723         {}
7725     virtual bool execute()
7726         {
7727         return true;
7728         }
7730     virtual bool parse(Element *elem)
7731         {
7732         //trace("tstamp parse");
7733         return true;
7734         }
7735 };
7739 /**
7740  *
7741  */
7742 Task *Task::createTask(Element *elem, int lineNr)
7744     String tagName = elem->getName();
7745     //trace("task:%s", tagName.c_str());
7746     Task *task = NULL;
7747     if (tagName == "cc")
7748         task = new TaskCC(parent);
7749     else if (tagName == "copy")
7750         task = new TaskCopy(parent);
7751     else if (tagName == "delete")
7752         task = new TaskDelete(parent);
7753     else if (tagName == "jar")
7754         task = new TaskJar(parent);
7755     else if (tagName == "javac")
7756         task = new TaskJavac(parent);
7757     else if (tagName == "link")
7758         task = new TaskLink(parent);
7759     else if (tagName == "makefile")
7760         task = new TaskMakeFile(parent);
7761     else if (tagName == "mkdir")
7762         task = new TaskMkDir(parent);
7763     else if (tagName == "msgfmt")
7764         task = new TaskMsgFmt(parent);
7765     else if (tagName == "pkg-config")
7766         task = new TaskPkgConfig(parent);
7767     else if (tagName == "ranlib")
7768         task = new TaskRanlib(parent);
7769     else if (tagName == "rc")
7770         task = new TaskRC(parent);
7771     else if (tagName == "sharedlib")
7772         task = new TaskSharedLib(parent);
7773     else if (tagName == "staticlib")
7774         task = new TaskStaticLib(parent);
7775     else if (tagName == "strip")
7776         task = new TaskStrip(parent);
7777     else if (tagName == "touch")
7778         task = new TaskTouch(parent);
7779     else if (tagName == "tstamp")
7780         task = new TaskTstamp(parent);
7781     else
7782         {
7783         error("Unknown task '%s'", tagName.c_str());
7784         return NULL;
7785         }
7787     task->setLine(lineNr);
7789     if (!task->parse(elem))
7790         {
7791         delete task;
7792         return NULL;
7793         }
7794     return task;
7799 //########################################################################
7800 //# T A R G E T
7801 //########################################################################
7803 /**
7804  *
7805  */
7806 class Target : public MakeBase
7809 public:
7811     /**
7812      *
7813      */
7814     Target(Make &par) : parent(par)
7815         { init(); }
7817     /**
7818      *
7819      */
7820     Target(const Target &other) : parent(other.parent)
7821         { init(); assign(other); }
7823     /**
7824      *
7825      */
7826     Target &operator=(const Target &other)
7827         { init(); assign(other); return *this; }
7829     /**
7830      *
7831      */
7832     virtual ~Target()
7833         { cleanup() ; }
7836     /**
7837      *
7838      */
7839     virtual Make &getParent()
7840         { return parent; }
7842     /**
7843      *
7844      */
7845     virtual String getName()
7846         { return name; }
7848     /**
7849      *
7850      */
7851     virtual void setName(const String &val)
7852         { name = val; }
7854     /**
7855      *
7856      */
7857     virtual String getDescription()
7858         { return description; }
7860     /**
7861      *
7862      */
7863     virtual void setDescription(const String &val)
7864         { description = val; }
7866     /**
7867      *
7868      */
7869     virtual void addDependency(const String &val)
7870         { deps.push_back(val); }
7872     /**
7873      *
7874      */
7875     virtual void parseDependencies(const String &val)
7876         { deps = tokenize(val, ", "); }
7878     /**
7879      *
7880      */
7881     virtual std::vector<String> &getDependencies()
7882         { return deps; }
7884     /**
7885      *
7886      */
7887     virtual String getIf()
7888         { return ifVar; }
7890     /**
7891      *
7892      */
7893     virtual void setIf(const String &val)
7894         { ifVar = val; }
7896     /**
7897      *
7898      */
7899     virtual String getUnless()
7900         { return unlessVar; }
7902     /**
7903      *
7904      */
7905     virtual void setUnless(const String &val)
7906         { unlessVar = val; }
7908     /**
7909      *
7910      */
7911     virtual void addTask(Task *val)
7912         { tasks.push_back(val); }
7914     /**
7915      *
7916      */
7917     virtual std::vector<Task *> &getTasks()
7918         { return tasks; }
7920 private:
7922     void init()
7923         {
7924         }
7926     void cleanup()
7927         {
7928         tasks.clear();
7929         }
7931     void assign(const Target &other)
7932         {
7933         //parent      = other.parent;
7934         name        = other.name;
7935         description = other.description;
7936         ifVar       = other.ifVar;
7937         unlessVar   = other.unlessVar;
7938         deps        = other.deps;
7939         tasks       = other.tasks;
7940         }
7942     Make &parent;
7944     String name;
7946     String description;
7948     String ifVar;
7950     String unlessVar;
7952     std::vector<String> deps;
7954     std::vector<Task *> tasks;
7956 };
7965 //########################################################################
7966 //# M A K E
7967 //########################################################################
7970 /**
7971  *
7972  */
7973 class Make : public MakeBase
7976 public:
7978     /**
7979      *
7980      */
7981     Make()
7982         { init(); }
7984     /**
7985      *
7986      */
7987     Make(const Make &other)
7988         { assign(other); }
7990     /**
7991      *
7992      */
7993     Make &operator=(const Make &other)
7994         { assign(other); return *this; }
7996     /**
7997      *
7998      */
7999     virtual ~Make()
8000         { cleanup(); }
8002     /**
8003      *
8004      */
8005     virtual std::map<String, Target> &getTargets()
8006         { return targets; }
8009     /**
8010      *
8011      */
8012     virtual String version()
8013         { return BUILDTOOL_VERSION; }
8015     /**
8016      * Overload a <property>
8017      */
8018     virtual bool specifyProperty(const String &name,
8019                                  const String &value);
8021     /**
8022      *
8023      */
8024     virtual bool run();
8026     /**
8027      *
8028      */
8029     virtual bool run(const String &target);
8033 private:
8035     /**
8036      *
8037      */
8038     void init();
8040     /**
8041      *
8042      */
8043     void cleanup();
8045     /**
8046      *
8047      */
8048     void assign(const Make &other);
8050     /**
8051      *
8052      */
8053     bool executeTask(Task &task);
8056     /**
8057      *
8058      */
8059     bool executeTarget(Target &target,
8060              std::set<String> &targetsCompleted);
8063     /**
8064      *
8065      */
8066     bool execute();
8068     /**
8069      *
8070      */
8071     bool checkTargetDependencies(Target &prop,
8072                     std::vector<String> &depList);
8074     /**
8075      *
8076      */
8077     bool parsePropertyFile(const String &fileName,
8078                            const String &prefix);
8080     /**
8081      *
8082      */
8083     bool parseProperty(Element *elem);
8085     /**
8086      *
8087      */
8088     bool parseFile();
8090     /**
8091      *
8092      */
8093     std::vector<String> glob(const String &pattern);
8096     //###############
8097     //# Fields
8098     //###############
8100     String projectName;
8102     String currentTarget;
8104     String defaultTarget;
8106     String specifiedTarget;
8108     String baseDir;
8110     String description;
8111     
8112     String envAlias;
8114     //std::vector<Property> properties;
8115     
8116     std::map<String, Target> targets;
8118     std::vector<Task *> allTasks;
8119     
8120     std::map<String, String> specifiedProperties;
8122 };
8125 //########################################################################
8126 //# C L A S S  M A I N T E N A N C E
8127 //########################################################################
8129 /**
8130  *
8131  */
8132 void Make::init()
8134     uri             = "build.xml";
8135     projectName     = "";
8136     currentTarget   = "";
8137     defaultTarget   = "";
8138     specifiedTarget = "";
8139     baseDir         = "";
8140     description     = "";
8141     envAlias        = "";
8142     properties.clear();
8143     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8144         delete allTasks[i];
8145     allTasks.clear();
8150 /**
8151  *
8152  */
8153 void Make::cleanup()
8155     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8156         delete allTasks[i];
8157     allTasks.clear();
8162 /**
8163  *
8164  */
8165 void Make::assign(const Make &other)
8167     uri              = other.uri;
8168     projectName      = other.projectName;
8169     currentTarget    = other.currentTarget;
8170     defaultTarget    = other.defaultTarget;
8171     specifiedTarget  = other.specifiedTarget;
8172     baseDir          = other.baseDir;
8173     description      = other.description;
8174     properties       = other.properties;
8179 //########################################################################
8180 //# U T I L I T Y    T A S K S
8181 //########################################################################
8183 /**
8184  *  Perform a file globbing
8185  */
8186 std::vector<String> Make::glob(const String &pattern)
8188     std::vector<String> res;
8189     return res;
8193 //########################################################################
8194 //# P U B L I C    A P I
8195 //########################################################################
8199 /**
8200  *
8201  */
8202 bool Make::executeTarget(Target &target,
8203              std::set<String> &targetsCompleted)
8206     String name = target.getName();
8208     //First get any dependencies for this target
8209     std::vector<String> deps = target.getDependencies();
8210     for (unsigned int i=0 ; i<deps.size() ; i++)
8211         {
8212         String dep = deps[i];
8213         //Did we do it already?  Skip
8214         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8215             continue;
8216             
8217         std::map<String, Target> &tgts =
8218                target.getParent().getTargets();
8219         std::map<String, Target>::iterator iter =
8220                tgts.find(dep);
8221         if (iter == tgts.end())
8222             {
8223             error("Target '%s' dependency '%s' not found",
8224                       name.c_str(),  dep.c_str());
8225             return false;
8226             }
8227         Target depTarget = iter->second;
8228         if (!executeTarget(depTarget, targetsCompleted))
8229             {
8230             return false;
8231             }
8232         }
8234     status("## Target : %s : %s", name.c_str(),
8235             target.getDescription().c_str());
8237     //Now let's do the tasks
8238     std::vector<Task *> &tasks = target.getTasks();
8239     for (unsigned int i=0 ; i<tasks.size() ; i++)
8240         {
8241         Task *task = tasks[i];
8242         status("---- task : %s", task->getName().c_str());
8243         if (!task->execute())
8244             {
8245             return false;
8246             }
8247         }
8248         
8249     targetsCompleted.insert(name);
8250     
8251     return true;
8256 /**
8257  *  Main execute() method.  Start here and work
8258  *  up the dependency tree 
8259  */
8260 bool Make::execute()
8262     status("######## EXECUTE");
8264     //Determine initial target
8265     if (specifiedTarget.size()>0)
8266         {
8267         currentTarget = specifiedTarget;
8268         }
8269     else if (defaultTarget.size()>0)
8270         {
8271         currentTarget = defaultTarget;
8272         }
8273     else
8274         {
8275         error("execute: no specified or default target requested");
8276         return false;
8277         }
8279     std::map<String, Target>::iterator iter =
8280                targets.find(currentTarget);
8281     if (iter == targets.end())
8282         {
8283         error("Initial target '%s' not found",
8284                  currentTarget.c_str());
8285         return false;
8286         }
8287         
8288     //Now run
8289     Target target = iter->second;
8290     std::set<String> targetsCompleted;
8291     if (!executeTarget(target, targetsCompleted))
8292         {
8293         return false;
8294         }
8296     status("######## EXECUTE COMPLETE");
8297     return true;
8303 /**
8304  *
8305  */
8306 bool Make::checkTargetDependencies(Target &target, 
8307                             std::vector<String> &depList)
8309     String tgtName = target.getName().c_str();
8310     depList.push_back(tgtName);
8312     std::vector<String> deps = target.getDependencies();
8313     for (unsigned int i=0 ; i<deps.size() ; i++)
8314         {
8315         String dep = deps[i];
8316         //First thing entered was the starting Target
8317         if (dep == depList[0])
8318             {
8319             error("Circular dependency '%s' found at '%s'",
8320                       dep.c_str(), tgtName.c_str());
8321             std::vector<String>::iterator diter;
8322             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8323                 {
8324                 error("  %s", diter->c_str());
8325                 }
8326             return false;
8327             }
8329         std::map<String, Target> &tgts =
8330                   target.getParent().getTargets();
8331         std::map<String, Target>::iterator titer = tgts.find(dep);
8332         if (titer == tgts.end())
8333             {
8334             error("Target '%s' dependency '%s' not found",
8335                       tgtName.c_str(), dep.c_str());
8336             return false;
8337             }
8338         if (!checkTargetDependencies(titer->second, depList))
8339             {
8340             return false;
8341             }
8342         }
8343     return true;
8350 static int getword(int pos, const String &inbuf, String &result)
8352     int p = pos;
8353     int len = (int)inbuf.size();
8354     String val;
8355     while (p < len)
8356         {
8357         char ch = inbuf[p];
8358         if (!isalnum(ch) && ch!='.' && ch!='_')
8359             break;
8360         val.push_back(ch);
8361         p++;
8362         }
8363     result = val;
8364     return p;
8370 /**
8371  *
8372  */
8373 bool Make::parsePropertyFile(const String &fileName,
8374                              const String &prefix)
8376     FILE *f = fopen(fileName.c_str(), "r");
8377     if (!f)
8378         {
8379         error("could not open property file %s", fileName.c_str());
8380         return false;
8381         }
8382     int linenr = 0;
8383     while (!feof(f))
8384         {
8385         char buf[256];
8386         if (!fgets(buf, 255, f))
8387             break;
8388         linenr++;
8389         String s = buf;
8390         s = trim(s);
8391         int len = s.size();
8392         if (len == 0)
8393             continue;
8394         if (s[0] == '#')
8395             continue;
8396         String key;
8397         String val;
8398         int p = 0;
8399         int p2 = getword(p, s, key);
8400         if (p2 <= p)
8401             {
8402             error("property file %s, line %d: expected keyword",
8403                     fileName.c_str(), linenr);
8404             return false;
8405             }
8406         if (prefix.size() > 0)
8407             {
8408             key.insert(0, prefix);
8409             }
8411         //skip whitespace
8412         for (p=p2 ; p<len ; p++)
8413             if (!isspace(s[p]))
8414                 break;
8416         if (p>=len || s[p]!='=')
8417             {
8418             error("property file %s, line %d: expected '='",
8419                     fileName.c_str(), linenr);
8420             return false;
8421             }
8422         p++;
8424         //skip whitespace
8425         for ( ; p<len ; p++)
8426             if (!isspace(s[p]))
8427                 break;
8429         /* This way expects a word after the =
8430         p2 = getword(p, s, val);
8431         if (p2 <= p)
8432             {
8433             error("property file %s, line %d: expected value",
8434                     fileName.c_str(), linenr);
8435             return false;
8436             }
8437         */
8438         // This way gets the rest of the line after the =
8439         if (p>=len)
8440             {
8441             error("property file %s, line %d: expected value",
8442                     fileName.c_str(), linenr);
8443             return false;
8444             }
8445         val = s.substr(p);
8446         if (key.size()==0)
8447             continue;
8448         //allow property to be set, even if val=""
8450         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8451         //See if we wanted to overload this property
8452         std::map<String, String>::iterator iter =
8453             specifiedProperties.find(key);
8454         if (iter!=specifiedProperties.end())
8455             {
8456             val = iter->second;
8457             status("overloading property '%s' = '%s'",
8458                    key.c_str(), val.c_str());
8459             }
8460         properties[key] = val;
8461         }
8462     fclose(f);
8463     return true;
8469 /**
8470  *
8471  */
8472 bool Make::parseProperty(Element *elem)
8474     std::vector<Attribute> &attrs = elem->getAttributes();
8475     for (unsigned int i=0 ; i<attrs.size() ; i++)
8476         {
8477         String attrName = attrs[i].getName();
8478         String attrVal  = attrs[i].getValue();
8480         if (attrName == "name")
8481             {
8482             String val;
8483             if (!getAttribute(elem, "value", val))
8484                 return false;
8485             if (val.size() > 0)
8486                 {
8487                 properties[attrVal] = val;
8488                 }
8489             else
8490                 {
8491                 if (!getAttribute(elem, "location", val))
8492                     return false;
8493                 //let the property exist, even if not defined
8494                 properties[attrVal] = val;
8495                 }
8496             //See if we wanted to overload this property
8497             std::map<String, String>::iterator iter =
8498                 specifiedProperties.find(attrVal);
8499             if (iter != specifiedProperties.end())
8500                 {
8501                 val = iter->second;
8502                 status("overloading property '%s' = '%s'",
8503                     attrVal.c_str(), val.c_str());
8504                 properties[attrVal] = val;
8505                 }
8506             }
8507         else if (attrName == "file")
8508             {
8509             String prefix;
8510             if (!getAttribute(elem, "prefix", prefix))
8511                 return false;
8512             if (prefix.size() > 0)
8513                 {
8514                 if (prefix[prefix.size()-1] != '.')
8515                     prefix.push_back('.');
8516                 }
8517             if (!parsePropertyFile(attrName, prefix))
8518                 return false;
8519             }
8520         else if (attrName == "environment")
8521             {
8522             if (envAlias.size() > 0)
8523                 {
8524                 error("environment property can only be set once");
8525                 return false;
8526                 }
8527             envAlias = attrVal;
8528             }
8529         }
8531     return true;
8537 /**
8538  *
8539  */
8540 bool Make::parseFile()
8542     status("######## PARSE : %s", uri.getPath().c_str());
8544     setLine(0);
8546     Parser parser;
8547     Element *root = parser.parseFile(uri.getNativePath());
8548     if (!root)
8549         {
8550         error("Could not open %s for reading",
8551               uri.getNativePath().c_str());
8552         return false;
8553         }
8554     
8555     setLine(root->getLine());
8557     if (root->getChildren().size()==0 ||
8558         root->getChildren()[0]->getName()!="project")
8559         {
8560         error("Main xml element should be <project>");
8561         delete root;
8562         return false;
8563         }
8565     //########## Project attributes
8566     Element *project = root->getChildren()[0];
8567     String s = project->getAttribute("name");
8568     if (s.size() > 0)
8569         projectName = s;
8570     s = project->getAttribute("default");
8571     if (s.size() > 0)
8572         defaultTarget = s;
8573     s = project->getAttribute("basedir");
8574     if (s.size() > 0)
8575         baseDir = s;
8577     //######### PARSE MEMBERS
8578     std::vector<Element *> children = project->getChildren();
8579     for (unsigned int i=0 ; i<children.size() ; i++)
8580         {
8581         Element *elem = children[i];
8582         setLine(elem->getLine());
8583         String tagName = elem->getName();
8585         //########## DESCRIPTION
8586         if (tagName == "description")
8587             {
8588             description = parser.trim(elem->getValue());
8589             }
8591         //######### PROPERTY
8592         else if (tagName == "property")
8593             {
8594             if (!parseProperty(elem))
8595                 return false;
8596             }
8598         //######### TARGET
8599         else if (tagName == "target")
8600             {
8601             String tname   = elem->getAttribute("name");
8602             String tdesc   = elem->getAttribute("description");
8603             String tdeps   = elem->getAttribute("depends");
8604             String tif     = elem->getAttribute("if");
8605             String tunless = elem->getAttribute("unless");
8606             Target target(*this);
8607             target.setName(tname);
8608             target.setDescription(tdesc);
8609             target.parseDependencies(tdeps);
8610             target.setIf(tif);
8611             target.setUnless(tunless);
8612             std::vector<Element *> telems = elem->getChildren();
8613             for (unsigned int i=0 ; i<telems.size() ; i++)
8614                 {
8615                 Element *telem = telems[i];
8616                 Task breeder(*this);
8617                 Task *task = breeder.createTask(telem, telem->getLine());
8618                 if (!task)
8619                     return false;
8620                 allTasks.push_back(task);
8621                 target.addTask(task);
8622                 }
8624             //Check name
8625             if (tname.size() == 0)
8626                 {
8627                 error("no name for target");
8628                 return false;
8629                 }
8630             //Check for duplicate name
8631             if (targets.find(tname) != targets.end())
8632                 {
8633                 error("target '%s' already defined", tname.c_str());
8634                 return false;
8635                 }
8636             //more work than targets[tname]=target, but avoids default allocator
8637             targets.insert(std::make_pair<String, Target>(tname, target));
8638             }
8639         //######### none of the above
8640         else
8641             {
8642             error("unknown toplevel tag: <%s>", tagName.c_str());
8643             return false;
8644             }
8646         }
8648     std::map<String, Target>::iterator iter;
8649     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8650         {
8651         Target tgt = iter->second;
8652         std::vector<String> depList;
8653         if (!checkTargetDependencies(tgt, depList))
8654             {
8655             return false;
8656             }
8657         }
8660     delete root;
8661     status("######## PARSE COMPLETE");
8662     return true;
8666 /**
8667  * Overload a <property>
8668  */
8669 bool Make::specifyProperty(const String &name, const String &value)
8671     if (specifiedProperties.find(name) != specifiedProperties.end())
8672         {
8673         error("Property %s already specified", name.c_str());
8674         return false;
8675         }
8676     specifiedProperties[name] = value;
8677     return true;
8682 /**
8683  *
8684  */
8685 bool Make::run()
8687     if (!parseFile())
8688         return false;
8689         
8690     if (!execute())
8691         return false;
8693     return true;
8699 /**
8700  * Get a formatted MM:SS.sss time elapsed string
8701  */ 
8702 static String
8703 timeDiffString(struct timeval &x, struct timeval &y)
8705     long microsX  = x.tv_usec;
8706     long secondsX = x.tv_sec;
8707     long microsY  = y.tv_usec;
8708     long secondsY = y.tv_sec;
8709     if (microsX < microsY)
8710         {
8711         microsX += 1000000;
8712         secondsX -= 1;
8713         }
8715     int seconds = (int)(secondsX - secondsY);
8716     int millis  = (int)((microsX - microsY)/1000);
8718     int minutes = seconds/60;
8719     seconds -= minutes*60;
8720     char buf[80];
8721     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8722     String ret = buf;
8723     return ret;
8724     
8727 /**
8728  *
8729  */
8730 bool Make::run(const String &target)
8732     status("####################################################");
8733     status("#   %s", version().c_str());
8734     status("####################################################");
8735     struct timeval timeStart, timeEnd;
8736     ::gettimeofday(&timeStart, NULL);
8737     specifiedTarget = target;
8738     if (!run())
8739         return false;
8740     ::gettimeofday(&timeEnd, NULL);
8741     String timeStr = timeDiffString(timeEnd, timeStart);
8742     status("####################################################");
8743     status("#   BuildTool Completed : %s", timeStr.c_str());
8744     status("####################################################");
8745     return true;
8754 }// namespace buildtool
8755 //########################################################################
8756 //# M A I N
8757 //########################################################################
8759 typedef buildtool::String String;
8761 /**
8762  *  Format an error message in printf() style
8763  */
8764 static void error(const char *fmt, ...)
8766     va_list ap;
8767     va_start(ap, fmt);
8768     fprintf(stderr, "BuildTool error: ");
8769     vfprintf(stderr, fmt, ap);
8770     fprintf(stderr, "\n");
8771     va_end(ap);
8775 static bool parseProperty(const String &s, String &name, String &val)
8777     int len = s.size();
8778     int i;
8779     for (i=0 ; i<len ; i++)
8780         {
8781         char ch = s[i];
8782         if (ch == '=')
8783             break;
8784         name.push_back(ch);
8785         }
8786     if (i>=len || s[i]!='=')
8787         {
8788         error("property requires -Dname=value");
8789         return false;
8790         }
8791     i++;
8792     for ( ; i<len ; i++)
8793         {
8794         char ch = s[i];
8795         val.push_back(ch);
8796         }
8797     return true;
8801 /**
8802  * Compare a buffer with a key, for the length of the key
8803  */
8804 static bool sequ(const String &buf, const char *key)
8806     int len = buf.size();
8807     for (int i=0 ; key[i] && i<len ; i++)
8808         {
8809         if (key[i] != buf[i])
8810             return false;
8811         }        
8812     return true;
8815 static void usage(int argc, char **argv)
8817     printf("usage:\n");
8818     printf("   %s [options] [target]\n", argv[0]);
8819     printf("Options:\n");
8820     printf("  -help, -h              print this message\n");
8821     printf("  -version               print the version information and exit\n");
8822     printf("  -file <file>           use given buildfile\n");
8823     printf("  -f <file>                 ''\n");
8824     printf("  -D<property>=<value>   use value for given property\n");
8830 /**
8831  * Parse the command-line args, get our options,
8832  * and run this thing
8833  */   
8834 static bool parseOptions(int argc, char **argv)
8836     if (argc < 1)
8837         {
8838         error("Cannot parse arguments");
8839         return false;
8840         }
8842     buildtool::Make make;
8844     String target;
8846     //char *progName = argv[0];
8847     for (int i=1 ; i<argc ; i++)
8848         {
8849         String arg = argv[i];
8850         if (arg.size()>1 && arg[0]=='-')
8851             {
8852             if (arg == "-h" || arg == "-help")
8853                 {
8854                 usage(argc,argv);
8855                 return true;
8856                 }
8857             else if (arg == "-version")
8858                 {
8859                 printf("%s", make.version().c_str());
8860                 return true;
8861                 }
8862             else if (arg == "-f" || arg == "-file")
8863                 {
8864                 if (i>=argc)
8865                    {
8866                    usage(argc, argv);
8867                    return false;
8868                    }
8869                 i++; //eat option
8870                 make.setURI(argv[i]);
8871                 }
8872             else if (arg.size()>2 && sequ(arg, "-D"))
8873                 {
8874                 String s = arg.substr(2, s.size());
8875                 String name, value;
8876                 if (!parseProperty(s, name, value))
8877                    {
8878                    usage(argc, argv);
8879                    return false;
8880                    }
8881                 if (!make.specifyProperty(name, value))
8882                     return false;
8883                 }
8884             else
8885                 {
8886                 error("Unknown option:%s", arg.c_str());
8887                 return false;
8888                 }
8889             }
8890         else
8891             {
8892             if (target.size()>0)
8893                 {
8894                 error("only one initial target");
8895                 usage(argc, argv);
8896                 return false;
8897                 }
8898             target = arg;
8899             }
8900         }
8902     //We have the options.  Now execute them
8903     if (!make.run(target))
8904         return false;
8906     return true;
8912 /*
8913 static bool runMake()
8915     buildtool::Make make;
8916     if (!make.run())
8917         return false;
8918     return true;
8922 static bool pkgConfigTest()
8924     buildtool::PkgConfig pkgConfig;
8925     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8926         return false;
8927     return true;
8932 static bool depTest()
8934     buildtool::DepTool deptool;
8935     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8936     if (!deptool.generateDependencies("build.dep"))
8937         return false;
8938     std::vector<buildtool::FileRec> res =
8939            deptool.loadDepFile("build.dep");
8940     if (res.size() == 0)
8941         return false;
8942     return true;
8945 static bool popenTest()
8947     buildtool::Make make;
8948     buildtool::String out, err;
8949     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8950     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8951     return true;
8955 static bool propFileTest()
8957     buildtool::Make make;
8958     make.parsePropertyFile("test.prop", "test.");
8959     return true;
8961 */
8963 int main(int argc, char **argv)
8966     if (!parseOptions(argc, argv))
8967         return 1;
8968     /*
8969     if (!popenTest())
8970         return 1;
8972     if (!depTest())
8973         return 1;
8974     if (!propFileTest())
8975         return 1;
8976     if (runMake())
8977         return 1;
8978     */
8979     return 0;
8983 //########################################################################
8984 //# E N D 
8985 //########################################################################