Code

rewrite pipe reading to avoid deadlock
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2008 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 /**
25  * To use this file, compile with:
26  * <pre>
27  * g++ -O3 buildtool.cpp -o btool.exe
28  * (or whatever your compiler might be)
29  * Then
30  * btool
31  * or
32  * btool {target}
33  *
34  * Note: if you are using MinGW, and a not very recent version of it,
35  * gettimeofday() might be missing.  If so, just build this file with
36  * this command:
37  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38  *
39  */
41 #define BUILDTOOL_VERSION  "BuildTool v0.9.4"
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>
57 #include <algorithm>
60 #ifdef __WIN32__
61 #include <windows.h>
62 #endif
65 #include <errno.h>
68 //########################################################################
69 //# Definition of gettimeofday() for those who don't have it
70 //########################################################################
71 #ifdef NEED_GETTIMEOFDAY
72 #include <sys/timeb.h>
74 struct timezone {
75       int tz_minuteswest; /* minutes west of Greenwich */
76       int tz_dsttime;     /* type of dst correction */
77     };
79 static int gettimeofday (struct timeval *tv, struct timezone *tz)
80 {
81    struct _timeb tb;
83    if (!tv)
84       return (-1);
86     _ftime (&tb);
87     tv->tv_sec  = tb.time;
88     tv->tv_usec = tb.millitm * 1000 + 500;
89     if (tz)
90         {
91         tz->tz_minuteswest = -60 * _timezone;
92         tz->tz_dsttime = _daylight;
93         }
94     return 0;
95 }
97 #endif
105 namespace buildtool
111 //########################################################################
112 //########################################################################
113 //##  R E G E X P
114 //########################################################################
115 //########################################################################
117 /**
118  * This is the T-Rex regular expression library, which we
119  * gratefully acknowledge.  It's clean code and small size allow
120  * us to embed it in BuildTool without adding a dependency
121  *
122  */    
124 //begin trex.h
126 #ifndef _TREX_H_
127 #define _TREX_H_
128 /***************************************************************
129     T-Rex a tiny regular expression library
131     Copyright (C) 2003-2006 Alberto Demichelis
133     This software is provided 'as-is', without any express 
134     or implied warranty. In no event will the authors be held 
135     liable for any damages arising from the use of this software.
137     Permission is granted to anyone to use this software for 
138     any purpose, including commercial applications, and to alter
139     it and redistribute it freely, subject to the following restrictions:
141         1. The origin of this software must not be misrepresented;
142         you must not claim that you wrote the original software.
143         If you use this software in a product, an acknowledgment
144         in the product documentation would be appreciated but
145         is not required.
147         2. Altered source versions must be plainly marked as such,
148         and must not be misrepresented as being the original software.
150         3. This notice may not be removed or altered from any
151         source distribution.
153 ****************************************************************/
155 #ifdef _UNICODE
156 #define TRexChar unsigned short
157 #define MAX_CHAR 0xFFFF
158 #define _TREXC(c) L##c 
159 #define trex_strlen wcslen
160 #define trex_printf wprintf
161 #else
162 #define TRexChar char
163 #define MAX_CHAR 0xFF
164 #define _TREXC(c) (c) 
165 #define trex_strlen strlen
166 #define trex_printf printf
167 #endif
169 #ifndef TREX_API
170 #define TREX_API extern
171 #endif
173 #define TRex_True 1
174 #define TRex_False 0
176 typedef unsigned int TRexBool;
177 typedef struct TRex TRex;
179 typedef struct {
180     const TRexChar *begin;
181     int len;
182 } TRexMatch;
184 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
185 TREX_API void trex_free(TRex *exp);
186 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
187 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
188 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API int trex_getsubexpcount(TRex* exp);
190 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
192 #endif
194 //end trex.h
196 //start trex.c
199 #include <stdio.h>
200 #include <string>
202 /* see copyright notice in trex.h */
203 #include <string.h>
204 #include <stdlib.h>
205 #include <ctype.h>
206 #include <setjmp.h>
207 //#include "trex.h"
209 #ifdef _UINCODE
210 #define scisprint iswprint
211 #define scstrlen wcslen
212 #define scprintf wprintf
213 #define _SC(x) L(x)
214 #else
215 #define scisprint isprint
216 #define scstrlen strlen
217 #define scprintf printf
218 #define _SC(x) (x)
219 #endif
221 #ifdef _DEBUG
222 #include <stdio.h>
224 static const TRexChar *g_nnames[] =
226     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
227     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
228     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
229     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
230 };
232 #endif
233 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
234 #define OP_OR            (MAX_CHAR+2)
235 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
236 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
237 #define OP_DOT            (MAX_CHAR+5)
238 #define OP_CLASS        (MAX_CHAR+6)
239 #define OP_CCLASS        (MAX_CHAR+7)
240 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
241 #define OP_RANGE        (MAX_CHAR+9)
242 #define OP_CHAR            (MAX_CHAR+10)
243 #define OP_EOL            (MAX_CHAR+11)
244 #define OP_BOL            (MAX_CHAR+12)
245 #define OP_WB            (MAX_CHAR+13)
247 #define TREX_SYMBOL_ANY_CHAR ('.')
248 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
249 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
251 #define TREX_SYMBOL_BRANCH ('|')
252 #define TREX_SYMBOL_END_OF_STRING ('$')
253 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
254 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
257 typedef int TRexNodeType;
259 typedef struct tagTRexNode{
260     TRexNodeType type;
261     int left;
262     int right;
263     int next;
264 }TRexNode;
266 struct TRex{
267     const TRexChar *_eol;
268     const TRexChar *_bol;
269     const TRexChar *_p;
270     int _first;
271     int _op;
272     TRexNode *_nodes;
273     int _nallocated;
274     int _nsize;
275     int _nsubexpr;
276     TRexMatch *_matches;
277     int _currsubexp;
278     void *_jmpbuf;
279     const TRexChar **_error;
280 };
282 static int trex_list(TRex *exp);
284 static int trex_newnode(TRex *exp, TRexNodeType type)
286     TRexNode n;
287     int newid;
288     n.type = type;
289     n.next = n.right = n.left = -1;
290     if(type == OP_EXPR)
291         n.right = exp->_nsubexpr++;
292     if(exp->_nallocated < (exp->_nsize + 1)) {
293         //int oldsize = exp->_nallocated;
294         exp->_nallocated *= 2;
295         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
296     }
297     exp->_nodes[exp->_nsize++] = n;
298     newid = exp->_nsize - 1;
299     return (int)newid;
302 static void trex_error(TRex *exp,const TRexChar *error)
304     if(exp->_error) *exp->_error = error;
305     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
308 static void trex_expect(TRex *exp, int n){
309     if((*exp->_p) != n) 
310         trex_error(exp, _SC("expected paren"));
311     exp->_p++;
314 static TRexChar trex_escapechar(TRex *exp)
316     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
317         exp->_p++;
318         switch(*exp->_p) {
319         case 'v': exp->_p++; return '\v';
320         case 'n': exp->_p++; return '\n';
321         case 't': exp->_p++; return '\t';
322         case 'r': exp->_p++; return '\r';
323         case 'f': exp->_p++; return '\f';
324         default: return (*exp->_p++);
325         }
326     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
327     return (*exp->_p++);
330 static int trex_charclass(TRex *exp,int classid)
332     int n = trex_newnode(exp,OP_CCLASS);
333     exp->_nodes[n].left = classid;
334     return n;
337 static int trex_charnode(TRex *exp,TRexBool isclass)
339     TRexChar t;
340     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
341         exp->_p++;
342         switch(*exp->_p) {
343             case 'n': exp->_p++; return trex_newnode(exp,'\n');
344             case 't': exp->_p++; return trex_newnode(exp,'\t');
345             case 'r': exp->_p++; return trex_newnode(exp,'\r');
346             case 'f': exp->_p++; return trex_newnode(exp,'\f');
347             case 'v': exp->_p++; return trex_newnode(exp,'\v');
348             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
349             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
350             case 'p': case 'P': case 'l': case 'u': 
351                 {
352                 t = *exp->_p; exp->_p++; 
353                 return trex_charclass(exp,t);
354                 }
355             case 'b': 
356             case 'B':
357                 if(!isclass) {
358                     int node = trex_newnode(exp,OP_WB);
359                     exp->_nodes[node].left = *exp->_p;
360                     exp->_p++; 
361                     return node;
362                 } //else default
363             default: 
364                 t = *exp->_p; exp->_p++; 
365                 return trex_newnode(exp,t);
366         }
367     }
368     else if(!scisprint(*exp->_p)) {
369         
370         trex_error(exp,_SC("letter expected"));
371     }
372     t = *exp->_p; exp->_p++; 
373     return trex_newnode(exp,t);
375 static int trex_class(TRex *exp)
377     int ret = -1;
378     int first = -1,chain;
379     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
380         ret = trex_newnode(exp,OP_NCLASS);
381         exp->_p++;
382     }else ret = trex_newnode(exp,OP_CLASS);
383     
384     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
385     chain = ret;
386     while(*exp->_p != ']' && exp->_p != exp->_eol) {
387         if(*exp->_p == '-' && first != -1){ 
388             int r,t;
389             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
390             r = trex_newnode(exp,OP_RANGE);
391             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
392             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
393             exp->_nodes[r].left = exp->_nodes[first].type;
394             t = trex_escapechar(exp);
395             exp->_nodes[r].right = t;
396             exp->_nodes[chain].next = r;
397             chain = r;
398             first = -1;
399         }
400         else{
401             if(first!=-1){
402                 int c = first;
403                 exp->_nodes[chain].next = c;
404                 chain = c;
405                 first = trex_charnode(exp,TRex_True);
406             }
407             else{
408                 first = trex_charnode(exp,TRex_True);
409             }
410         }
411     }
412     if(first!=-1){
413         int c = first;
414         exp->_nodes[chain].next = c;
415         chain = c;
416         first = -1;
417     }
418     /* hack? */
419     exp->_nodes[ret].left = exp->_nodes[ret].next;
420     exp->_nodes[ret].next = -1;
421     return ret;
424 static int trex_parsenumber(TRex *exp)
426     int ret = *exp->_p-'0';
427     int positions = 10;
428     exp->_p++;
429     while(isdigit(*exp->_p)) {
430         ret = ret*10+(*exp->_p++-'0');
431         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
432         positions *= 10;
433     };
434     return ret;
437 static int trex_element(TRex *exp)
439     int ret = -1;
440     switch(*exp->_p)
441     {
442     case '(': {
443         int expr,newn;
444         exp->_p++;
447         if(*exp->_p =='?') {
448             exp->_p++;
449             trex_expect(exp,':');
450             expr = trex_newnode(exp,OP_NOCAPEXPR);
451         }
452         else
453             expr = trex_newnode(exp,OP_EXPR);
454         newn = trex_list(exp);
455         exp->_nodes[expr].left = newn;
456         ret = expr;
457         trex_expect(exp,')');
458               }
459               break;
460     case '[':
461         exp->_p++;
462         ret = trex_class(exp);
463         trex_expect(exp,']');
464         break;
465     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
466     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
467     default:
468         ret = trex_charnode(exp,TRex_False);
469         break;
470     }
472     {
473         int op;
474         TRexBool isgreedy = TRex_False;
475         unsigned short p0 = 0, p1 = 0;
476         switch(*exp->_p){
477             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
478             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
480             case '{':
481                 exp->_p++;
482                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
483                 p0 = (unsigned short)trex_parsenumber(exp);
484                 /*******************************/
485                 switch(*exp->_p) {
486             case '}':
487                 p1 = p0; exp->_p++;
488                 break;
489             case ',':
490                 exp->_p++;
491                 p1 = 0xFFFF;
492                 if(isdigit(*exp->_p)){
493                     p1 = (unsigned short)trex_parsenumber(exp);
494                 }
495                 trex_expect(exp,'}');
496                 break;
497             default:
498                 trex_error(exp,_SC(", or } expected"));
499         }
500         /*******************************/
501         isgreedy = TRex_True; 
502         break;
504         }
505         if(isgreedy) {
506             int nnode = trex_newnode(exp,OP_GREEDY);
507             op = OP_GREEDY;
508             exp->_nodes[nnode].left = ret;
509             exp->_nodes[nnode].right = ((p0)<<16)|p1;
510             ret = nnode;
511         }
512     }
513     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')) {
514         int nnode = trex_element(exp);
515         exp->_nodes[ret].next = nnode;
516     }
518     return ret;
521 static int trex_list(TRex *exp)
523     int ret=-1,e;
524     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
525         exp->_p++;
526         ret = trex_newnode(exp,OP_BOL);
527     }
528     e = trex_element(exp);
529     if(ret != -1) {
530         exp->_nodes[ret].next = e;
531     }
532     else ret = e;
534     if(*exp->_p == TREX_SYMBOL_BRANCH) {
535         int temp,tright;
536         exp->_p++;
537         temp = trex_newnode(exp,OP_OR);
538         exp->_nodes[temp].left = ret;
539         tright = trex_list(exp);
540         exp->_nodes[temp].right = tright;
541         ret = temp;
542     }
543     return ret;
546 static TRexBool trex_matchcclass(int cclass,TRexChar c)
548     switch(cclass) {
549     case 'a': return isalpha(c)?TRex_True:TRex_False;
550     case 'A': return !isalpha(c)?TRex_True:TRex_False;
551     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
552     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
553     case 's': return isspace(c)?TRex_True:TRex_False;
554     case 'S': return !isspace(c)?TRex_True:TRex_False;
555     case 'd': return isdigit(c)?TRex_True:TRex_False;
556     case 'D': return !isdigit(c)?TRex_True:TRex_False;
557     case 'x': return isxdigit(c)?TRex_True:TRex_False;
558     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
559     case 'c': return iscntrl(c)?TRex_True:TRex_False;
560     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
561     case 'p': return ispunct(c)?TRex_True:TRex_False;
562     case 'P': return !ispunct(c)?TRex_True:TRex_False;
563     case 'l': return islower(c)?TRex_True:TRex_False;
564     case 'u': return isupper(c)?TRex_True:TRex_False;
565     }
566     return TRex_False; /*cannot happen*/
569 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
571     do {
572         switch(node->type) {
573             case OP_RANGE:
574                 if(c >= node->left && c <= node->right) return TRex_True;
575                 break;
576             case OP_CCLASS:
577                 if(trex_matchcclass(node->left,c)) return TRex_True;
578                 break;
579             default:
580                 if(c == node->type)return TRex_True;
581         }
582     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
583     return TRex_False;
586 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
588     
589     TRexNodeType type = node->type;
590     switch(type) {
591     case OP_GREEDY: {
592         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
593         TRexNode *greedystop = NULL;
594         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
595         const TRexChar *s=str, *good = str;
597         if(node->next != -1) {
598             greedystop = &exp->_nodes[node->next];
599         }
600         else {
601             greedystop = next;
602         }
604         while((nmaches == 0xFFFF || nmaches < p1)) {
606             const TRexChar *stop;
607             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
608                 break;
609             nmaches++;
610             good=s;
611             if(greedystop) {
612                 //checks that 0 matches satisfy the expression(if so skips)
613                 //if not would always stop(for instance if is a '?')
614                 if(greedystop->type != OP_GREEDY ||
615                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
616                 {
617                     TRexNode *gnext = NULL;
618                     if(greedystop->next != -1) {
619                         gnext = &exp->_nodes[greedystop->next];
620                     }else if(next && next->next != -1){
621                         gnext = &exp->_nodes[next->next];
622                     }
623                     stop = trex_matchnode(exp,greedystop,s,gnext);
624                     if(stop) {
625                         //if satisfied stop it
626                         if(p0 == p1 && p0 == nmaches) break;
627                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
628                         else if(nmaches >= p0 && nmaches <= p1) break;
629                     }
630                 }
631             }
632             
633             if(s >= exp->_eol)
634                 break;
635         }
636         if(p0 == p1 && p0 == nmaches) return good;
637         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
638         else if(nmaches >= p0 && nmaches <= p1) return good;
639         return NULL;
640     }
641     case OP_OR: {
642             const TRexChar *asd = str;
643             TRexNode *temp=&exp->_nodes[node->left];
644             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
645                 if(temp->next != -1)
646                     temp = &exp->_nodes[temp->next];
647                 else
648                     return asd;
649             }
650             asd = str;
651             temp = &exp->_nodes[node->right];
652             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
653                 if(temp->next != -1)
654                     temp = &exp->_nodes[temp->next];
655                 else
656                     return asd;
657             }
658             return NULL;
659             break;
660     }
661     case OP_EXPR:
662     case OP_NOCAPEXPR:{
663             TRexNode *n = &exp->_nodes[node->left];
664             const TRexChar *cur = str;
665             int capture = -1;
666             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
667                 capture = exp->_currsubexp;
668                 exp->_matches[capture].begin = cur;
669                 exp->_currsubexp++;
670             }
671             
672             do {
673                 TRexNode *subnext = NULL;
674                 if(n->next != -1) {
675                     subnext = &exp->_nodes[n->next];
676                 }else {
677                     subnext = next;
678                 }
679                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
680                     if(capture != -1){
681                         exp->_matches[capture].begin = 0;
682                         exp->_matches[capture].len = 0;
683                     }
684                     return NULL;
685                 }
686             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
688             if(capture != -1) 
689                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
690             return cur;
691     }                 
692     case OP_WB:
693         if((str == exp->_bol && !isspace(*str))
694          || (str == exp->_eol && !isspace(*(str-1)))
695          || (!isspace(*str) && isspace(*(str+1)))
696          || (isspace(*str) && !isspace(*(str+1))) ) {
697             return (node->left == 'b')?str:NULL;
698         }
699         return (node->left == 'b')?NULL:str;
700     case OP_BOL:
701         if(str == exp->_bol) return str;
702         return NULL;
703     case OP_EOL:
704         if(str == exp->_eol) return str;
705         return NULL;
706     case OP_DOT:{
707         *str++;
708                 }
709         return str;
710     case OP_NCLASS:
711     case OP_CLASS:
712         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
713             *str++;
714             return str;
715         }
716         return NULL;
717     case OP_CCLASS:
718         if(trex_matchcclass(node->left,*str)) {
719             *str++;
720             return str;
721         }
722         return NULL;
723     default: /* char */
724         if(*str != node->type) return NULL;
725         *str++;
726         return str;
727     }
728     return NULL;
731 /* public api */
732 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
734     TRex *exp = (TRex *)malloc(sizeof(TRex));
735     exp->_eol = exp->_bol = NULL;
736     exp->_p = pattern;
737     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
738     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
739     exp->_nsize = 0;
740     exp->_matches = 0;
741     exp->_nsubexpr = 0;
742     exp->_first = trex_newnode(exp,OP_EXPR);
743     exp->_error = error;
744     exp->_jmpbuf = malloc(sizeof(jmp_buf));
745     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
746         int res = trex_list(exp);
747         exp->_nodes[exp->_first].left = res;
748         if(*exp->_p!='\0')
749             trex_error(exp,_SC("unexpected character"));
750 #ifdef _DEBUG
751         {
752             int nsize,i;
753             TRexNode *t;
754             nsize = exp->_nsize;
755             t = &exp->_nodes[0];
756             scprintf(_SC("\n"));
757             for(i = 0;i < nsize; i++) {
758                 if(exp->_nodes[i].type>MAX_CHAR)
759                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
760                 else
761                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
762                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
763             }
764             scprintf(_SC("\n"));
765         }
766 #endif
767         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
768         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
769     }
770     else{
771         trex_free(exp);
772         return NULL;
773     }
774     return exp;
777 void trex_free(TRex *exp)
779     if(exp)    {
780         if(exp->_nodes) free(exp->_nodes);
781         if(exp->_jmpbuf) free(exp->_jmpbuf);
782         if(exp->_matches) free(exp->_matches);
783         free(exp);
784     }
787 TRexBool trex_match(TRex* exp,const TRexChar* text)
789     const TRexChar* res = NULL;
790     exp->_bol = text;
791     exp->_eol = text + scstrlen(text);
792     exp->_currsubexp = 0;
793     res = trex_matchnode(exp,exp->_nodes,text,NULL);
794     if(res == NULL || res != exp->_eol)
795         return TRex_False;
796     return TRex_True;
799 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
801     const TRexChar *cur = NULL;
802     int node = exp->_first;
803     if(text_begin >= text_end) return TRex_False;
804     exp->_bol = text_begin;
805     exp->_eol = text_end;
806     do {
807         cur = text_begin;
808         while(node != -1) {
809             exp->_currsubexp = 0;
810             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
811             if(!cur)
812                 break;
813             node = exp->_nodes[node].next;
814         }
815         *text_begin++;
816     } while(cur == NULL && text_begin != text_end);
818     if(cur == NULL)
819         return TRex_False;
821     --text_begin;
823     if(out_begin) *out_begin = text_begin;
824     if(out_end) *out_end = cur;
825     return TRex_True;
828 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
830     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
833 int trex_getsubexpcount(TRex* exp)
835     return exp->_nsubexpr;
838 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
840     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
841     *subexp = exp->_matches[n];
842     return TRex_True;
846 //########################################################################
847 //########################################################################
848 //##  E N D    R E G E X P
849 //########################################################################
850 //########################################################################
856 //########################################################################
857 //########################################################################
858 //##  X M L
859 //########################################################################
860 //########################################################################
862 // Note:  This mini-dom library comes from Pedro, another little project
863 // of mine.
865 typedef std::string String;
866 typedef unsigned int XMLCh;
869 class Namespace
871 public:
872     Namespace()
873         {}
875     Namespace(const String &prefixArg, const String &namespaceURIArg)
876         {
877         prefix       = prefixArg;
878         namespaceURI = namespaceURIArg;
879         }
881     Namespace(const Namespace &other)
882         {
883         assign(other);
884         }
886     Namespace &operator=(const Namespace &other)
887         {
888         assign(other);
889         return *this;
890         }
892     virtual ~Namespace()
893         {}
895     virtual String getPrefix()
896         { return prefix; }
898     virtual String getNamespaceURI()
899         { return namespaceURI; }
901 protected:
903     void assign(const Namespace &other)
904         {
905         prefix       = other.prefix;
906         namespaceURI = other.namespaceURI;
907         }
909     String prefix;
910     String namespaceURI;
912 };
914 class Attribute
916 public:
917     Attribute()
918         {}
920     Attribute(const String &nameArg, const String &valueArg)
921         {
922         name  = nameArg;
923         value = valueArg;
924         }
926     Attribute(const Attribute &other)
927         {
928         assign(other);
929         }
931     Attribute &operator=(const Attribute &other)
932         {
933         assign(other);
934         return *this;
935         }
937     virtual ~Attribute()
938         {}
940     virtual String getName()
941         { return name; }
943     virtual String getValue()
944         { return value; }
946 protected:
948     void assign(const Attribute &other)
949         {
950         name  = other.name;
951         value = other.value;
952         }
954     String name;
955     String value;
957 };
960 class Element
962 friend class Parser;
964 public:
965     Element()
966         {
967         init();
968         }
970     Element(const String &nameArg)
971         {
972         init();
973         name   = nameArg;
974         }
976     Element(const String &nameArg, const String &valueArg)
977         {
978         init();
979         name   = nameArg;
980         value  = valueArg;
981         }
983     Element(const Element &other)
984         {
985         assign(other);
986         }
988     Element &operator=(const Element &other)
989         {
990         assign(other);
991         return *this;
992         }
994     virtual Element *clone();
996     virtual ~Element()
997         {
998         for (unsigned int i=0 ; i<children.size() ; i++)
999             delete children[i];
1000         }
1002     virtual String getName()
1003         { return name; }
1005     virtual String getValue()
1006         { return value; }
1008     Element *getParent()
1009         { return parent; }
1011     std::vector<Element *> getChildren()
1012         { return children; }
1014     std::vector<Element *> findElements(const String &name);
1016     String getAttribute(const String &name);
1018     std::vector<Attribute> &getAttributes()
1019         { return attributes; } 
1021     String getTagAttribute(const String &tagName, const String &attrName);
1023     String getTagValue(const String &tagName);
1025     void addChild(Element *child);
1027     void addAttribute(const String &name, const String &value);
1029     void addNamespace(const String &prefix, const String &namespaceURI);
1032     /**
1033      * Prettyprint an XML tree to an output stream.  Elements are indented
1034      * according to element hierarchy.
1035      * @param f a stream to receive the output
1036      * @param elem the element to output
1037      */
1038     void writeIndented(FILE *f);
1040     /**
1041      * Prettyprint an XML tree to standard output.  This is the equivalent of
1042      * writeIndented(stdout).
1043      * @param elem the element to output
1044      */
1045     void print();
1046     
1047     int getLine()
1048         { return line; }
1050 protected:
1052     void init()
1053         {
1054         parent = NULL;
1055         line   = 0;
1056         }
1058     void assign(const Element &other)
1059         {
1060         parent     = other.parent;
1061         children   = other.children;
1062         attributes = other.attributes;
1063         namespaces = other.namespaces;
1064         name       = other.name;
1065         value      = other.value;
1066         line       = other.line;
1067         }
1069     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1071     void writeIndentedRecursive(FILE *f, int indent);
1073     Element *parent;
1075     std::vector<Element *>children;
1077     std::vector<Attribute> attributes;
1078     std::vector<Namespace> namespaces;
1080     String name;
1081     String value;
1082     
1083     int line;
1084 };
1090 class Parser
1092 public:
1093     /**
1094      * Constructor
1095      */
1096     Parser()
1097         { init(); }
1099     virtual ~Parser()
1100         {}
1102     /**
1103      * Parse XML in a char buffer.
1104      * @param buf a character buffer to parse
1105      * @param pos position to start parsing
1106      * @param len number of chars, from pos, to parse.
1107      * @return a pointer to the root of the XML document;
1108      */
1109     Element *parse(const char *buf,int pos,int len);
1111     /**
1112      * Parse XML in a char buffer.
1113      * @param buf a character buffer to parse
1114      * @param pos position to start parsing
1115      * @param len number of chars, from pos, to parse.
1116      * @return a pointer to the root of the XML document;
1117      */
1118     Element *parse(const String &buf);
1120     /**
1121      * Parse a named XML file.  The file is loaded like a data file;
1122      * the original format is not preserved.
1123      * @param fileName the name of the file to read
1124      * @return a pointer to the root of the XML document;
1125      */
1126     Element *parseFile(const String &fileName);
1128     /**
1129      * Utility method to preprocess a string for XML
1130      * output, escaping its entities.
1131      * @param str the string to encode
1132      */
1133     static String encode(const String &str);
1135     /**
1136      *  Removes whitespace from beginning and end of a string
1137      */
1138     String trim(const String &s);
1140 private:
1142     void init()
1143         {
1144         keepGoing       = true;
1145         currentNode     = NULL;
1146         parselen        = 0;
1147         parsebuf        = NULL;
1148         currentPosition = 0;
1149         }
1151     int countLines(int begin, int end);
1153     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1155     void error(const char *fmt, ...);
1157     int peek(int pos);
1159     int match(int pos, const char *text);
1161     int skipwhite(int p);
1163     int getWord(int p0, String &buf);
1165     int getQuoted(int p0, String &buf, int do_i_parse);
1167     int parseVersion(int p0);
1169     int parseDoctype(int p0);
1171     int parseElement(int p0, Element *par,int depth);
1173     Element *parse(XMLCh *buf,int pos,int len);
1175     bool       keepGoing;
1176     Element    *currentNode;
1177     int        parselen;
1178     XMLCh      *parsebuf;
1179     String     cdatabuf;
1180     int        currentPosition;
1181 };
1186 //########################################################################
1187 //# E L E M E N T
1188 //########################################################################
1190 Element *Element::clone()
1192     Element *elem = new Element(name, value);
1193     elem->parent     = parent;
1194     elem->attributes = attributes;
1195     elem->namespaces = namespaces;
1196     elem->line       = line;
1198     std::vector<Element *>::iterator iter;
1199     for (iter = children.begin(); iter != children.end() ; iter++)
1200         {
1201         elem->addChild((*iter)->clone());
1202         }
1203     return elem;
1207 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1209     if (getName() == name)
1210         {
1211         res.push_back(this);
1212         }
1213     for (unsigned int i=0; i<children.size() ; i++)
1214         children[i]->findElementsRecursive(res, name);
1217 std::vector<Element *> Element::findElements(const String &name)
1219     std::vector<Element *> res;
1220     findElementsRecursive(res, name);
1221     return res;
1224 String Element::getAttribute(const String &name)
1226     for (unsigned int i=0 ; i<attributes.size() ; i++)
1227         if (attributes[i].getName() ==name)
1228             return attributes[i].getValue();
1229     return "";
1232 String Element::getTagAttribute(const String &tagName, const String &attrName)
1234     std::vector<Element *>elems = findElements(tagName);
1235     if (elems.size() <1)
1236         return "";
1237     String res = elems[0]->getAttribute(attrName);
1238     return res;
1241 String Element::getTagValue(const String &tagName)
1243     std::vector<Element *>elems = findElements(tagName);
1244     if (elems.size() <1)
1245         return "";
1246     String res = elems[0]->getValue();
1247     return res;
1250 void Element::addChild(Element *child)
1252     if (!child)
1253         return;
1254     child->parent = this;
1255     children.push_back(child);
1259 void Element::addAttribute(const String &name, const String &value)
1261     Attribute attr(name, value);
1262     attributes.push_back(attr);
1265 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1267     Namespace ns(prefix, namespaceURI);
1268     namespaces.push_back(ns);
1271 void Element::writeIndentedRecursive(FILE *f, int indent)
1273     int i;
1274     if (!f)
1275         return;
1276     //Opening tag, and attributes
1277     for (i=0;i<indent;i++)
1278         fputc(' ',f);
1279     fprintf(f,"<%s",name.c_str());
1280     for (unsigned int i=0 ; i<attributes.size() ; i++)
1281         {
1282         fprintf(f," %s=\"%s\"",
1283               attributes[i].getName().c_str(),
1284               attributes[i].getValue().c_str());
1285         }
1286     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1287         {
1288         fprintf(f," xmlns:%s=\"%s\"",
1289               namespaces[i].getPrefix().c_str(),
1290               namespaces[i].getNamespaceURI().c_str());
1291         }
1292     fprintf(f,">\n");
1294     //Between the tags
1295     if (value.size() > 0)
1296         {
1297         for (int i=0;i<indent;i++)
1298             fputc(' ', f);
1299         fprintf(f," %s\n", value.c_str());
1300         }
1302     for (unsigned int i=0 ; i<children.size() ; i++)
1303         children[i]->writeIndentedRecursive(f, indent+2);
1305     //Closing tag
1306     for (int i=0; i<indent; i++)
1307         fputc(' ',f);
1308     fprintf(f,"</%s>\n", name.c_str());
1311 void Element::writeIndented(FILE *f)
1313     writeIndentedRecursive(f, 0);
1316 void Element::print()
1318     writeIndented(stdout);
1322 //########################################################################
1323 //# P A R S E R
1324 //########################################################################
1328 typedef struct
1329     {
1330     const char *escaped;
1331     char value;
1332     } EntityEntry;
1334 static EntityEntry entities[] =
1336     { "&amp;" , '&'  },
1337     { "&lt;"  , '<'  },
1338     { "&gt;"  , '>'  },
1339     { "&apos;", '\'' },
1340     { "&quot;", '"'  },
1341     { NULL    , '\0' }
1342 };
1346 /**
1347  *  Removes whitespace from beginning and end of a string
1348  */
1349 String Parser::trim(const String &s)
1351     if (s.size() < 1)
1352         return s;
1353     
1354     //Find first non-ws char
1355     unsigned int begin = 0;
1356     for ( ; begin < s.size() ; begin++)
1357         {
1358         if (!isspace(s[begin]))
1359             break;
1360         }
1362     //Find first non-ws char, going in reverse
1363     unsigned int end = s.size() - 1;
1364     for ( ; end > begin ; end--)
1365         {
1366         if (!isspace(s[end]))
1367             break;
1368         }
1369     //trace("begin:%d  end:%d", begin, end);
1371     String res = s.substr(begin, end-begin+1);
1372     return res;
1376 int Parser::countLines(int begin, int end)
1378     int count = 0;
1379     for (int i=begin ; i<end ; i++)
1380         {
1381         XMLCh ch = parsebuf[i];
1382         if (ch == '\n' || ch == '\r')
1383             count++;
1384         }
1385     return count;
1389 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1391     int line = 1;
1392     int col  = 1;
1393     for (long i=0 ; i<pos ; i++)
1394         {
1395         XMLCh ch = parsebuf[i];
1396         if (ch == '\n' || ch == '\r')
1397             {
1398             col = 0;
1399             line ++;
1400             }
1401         else
1402             col++;
1403         }
1404     *lineNr = line;
1405     *colNr  = col;
1410 void Parser::error(const char *fmt, ...)
1412     int lineNr;
1413     int colNr;
1414     getLineAndColumn(currentPosition, &lineNr, &colNr);
1415     va_list args;
1416     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1417     va_start(args,fmt);
1418     vfprintf(stderr,fmt,args);
1419     va_end(args) ;
1420     fprintf(stderr, "\n");
1425 int Parser::peek(int pos)
1427     if (pos >= parselen)
1428         return -1;
1429     currentPosition = pos;
1430     int ch = parsebuf[pos];
1431     //printf("ch:%c\n", ch);
1432     return ch;
1437 String Parser::encode(const String &str)
1439     String ret;
1440     for (unsigned int i=0 ; i<str.size() ; i++)
1441         {
1442         XMLCh ch = (XMLCh)str[i];
1443         if (ch == '&')
1444             ret.append("&amp;");
1445         else if (ch == '<')
1446             ret.append("&lt;");
1447         else if (ch == '>')
1448             ret.append("&gt;");
1449         else if (ch == '\'')
1450             ret.append("&apos;");
1451         else if (ch == '"')
1452             ret.append("&quot;");
1453         else
1454             ret.push_back(ch);
1456         }
1457     return ret;
1461 int Parser::match(int p0, const char *text)
1463     int p = p0;
1464     while (*text)
1465         {
1466         if (peek(p) != *text)
1467             return p0;
1468         p++; text++;
1469         }
1470     return p;
1475 int Parser::skipwhite(int p)
1478     while (p<parselen)
1479         {
1480         int p2 = match(p, "<!--");
1481         if (p2 > p)
1482             {
1483             p = p2;
1484             while (p<parselen)
1485               {
1486               p2 = match(p, "-->");
1487               if (p2 > p)
1488                   {
1489                   p = p2;
1490                   break;
1491                   }
1492               p++;
1493               }
1494           }
1495       XMLCh b = peek(p);
1496       if (!isspace(b))
1497           break;
1498       p++;
1499       }
1500   return p;
1503 /* modify this to allow all chars for an element or attribute name*/
1504 int Parser::getWord(int p0, String &buf)
1506     int p = p0;
1507     while (p<parselen)
1508         {
1509         XMLCh b = peek(p);
1510         if (b<=' ' || b=='/' || b=='>' || b=='=')
1511             break;
1512         buf.push_back(b);
1513         p++;
1514         }
1515     return p;
1518 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1521     int p = p0;
1522     if (peek(p) != '"' && peek(p) != '\'')
1523         return p0;
1524     p++;
1526     while ( p<parselen )
1527         {
1528         XMLCh b = peek(p);
1529         if (b=='"' || b=='\'')
1530             break;
1531         if (b=='&' && do_i_parse)
1532             {
1533             bool found = false;
1534             for (EntityEntry *ee = entities ; ee->value ; ee++)
1535                 {
1536                 int p2 = match(p, ee->escaped);
1537                 if (p2>p)
1538                     {
1539                     buf.push_back(ee->value);
1540                     p = p2;
1541                     found = true;
1542                     break;
1543                     }
1544                 }
1545             if (!found)
1546                 {
1547                 error("unterminated entity");
1548                 return false;
1549                 }
1550             }
1551         else
1552             {
1553             buf.push_back(b);
1554             p++;
1555             }
1556         }
1557     return p;
1560 int Parser::parseVersion(int p0)
1562     //printf("### parseVersion: %d\n", p0);
1564     int p = p0;
1566     p = skipwhite(p0);
1568     if (peek(p) != '<')
1569         return p0;
1571     p++;
1572     if (p>=parselen || peek(p)!='?')
1573         return p0;
1575     p++;
1577     String buf;
1579     while (p<parselen)
1580         {
1581         XMLCh ch = peek(p);
1582         if (ch=='?')
1583             {
1584             p++;
1585             break;
1586             }
1587         buf.push_back(ch);
1588         p++;
1589         }
1591     if (peek(p) != '>')
1592         return p0;
1593     p++;
1595     //printf("Got version:%s\n",buf.c_str());
1596     return p;
1599 int Parser::parseDoctype(int p0)
1601     //printf("### parseDoctype: %d\n", p0);
1603     int p = p0;
1604     p = skipwhite(p);
1606     if (p>=parselen || peek(p)!='<')
1607         return p0;
1609     p++;
1611     if (peek(p)!='!' || peek(p+1)=='-')
1612         return p0;
1613     p++;
1615     String buf;
1616     while (p<parselen)
1617         {
1618         XMLCh ch = peek(p);
1619         if (ch=='>')
1620             {
1621             p++;
1622             break;
1623             }
1624         buf.push_back(ch);
1625         p++;
1626         }
1628     //printf("Got doctype:%s\n",buf.c_str());
1629     return p;
1634 int Parser::parseElement(int p0, Element *par,int lineNr)
1637     int p = p0;
1639     int p2 = p;
1641     p = skipwhite(p);
1643     //## Get open tag
1644     XMLCh ch = peek(p);
1645     if (ch!='<')
1646         return p0;
1648     //int line, col;
1649     //getLineAndColumn(p, &line, &col);
1651     p++;
1653     String openTagName;
1654     p = skipwhite(p);
1655     p = getWord(p, openTagName);
1656     //printf("####tag :%s\n", openTagName.c_str());
1657     p = skipwhite(p);
1659     //Add element to tree
1660     Element *n = new Element(openTagName);
1661     n->line = lineNr + countLines(p0, p);
1662     n->parent = par;
1663     par->addChild(n);
1665     // Get attributes
1666     if (peek(p) != '>')
1667         {
1668         while (p<parselen)
1669             {
1670             p = skipwhite(p);
1671             ch = peek(p);
1672             //printf("ch:%c\n",ch);
1673             if (ch=='>')
1674                 break;
1675             else if (ch=='/' && p<parselen+1)
1676                 {
1677                 p++;
1678                 p = skipwhite(p);
1679                 ch = peek(p);
1680                 if (ch=='>')
1681                     {
1682                     p++;
1683                     //printf("quick close\n");
1684                     return p;
1685                     }
1686                 }
1687             String attrName;
1688             p2 = getWord(p, attrName);
1689             if (p2==p)
1690                 break;
1691             //printf("name:%s",buf);
1692             p=p2;
1693             p = skipwhite(p);
1694             ch = peek(p);
1695             //printf("ch:%c\n",ch);
1696             if (ch!='=')
1697                 break;
1698             p++;
1699             p = skipwhite(p);
1700             // ch = parsebuf[p];
1701             // printf("ch:%c\n",ch);
1702             String attrVal;
1703             p2 = getQuoted(p, attrVal, true);
1704             p=p2+1;
1705             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1706             char *namestr = (char *)attrName.c_str();
1707             if (strncmp(namestr, "xmlns:", 6)==0)
1708                 n->addNamespace(attrName, attrVal);
1709             else
1710                 n->addAttribute(attrName, attrVal);
1711             }
1712         }
1714     bool cdata = false;
1716     p++;
1717     // ### Get intervening data ### */
1718     String data;
1719     while (p<parselen)
1720         {
1721         //# COMMENT
1722         p2 = match(p, "<!--");
1723         if (!cdata && p2>p)
1724             {
1725             p = p2;
1726             while (p<parselen)
1727                 {
1728                 p2 = match(p, "-->");
1729                 if (p2 > p)
1730                     {
1731                     p = p2;
1732                     break;
1733                     }
1734                 p++;
1735                 }
1736             }
1738         ch = peek(p);
1739         //# END TAG
1740         if (ch=='<' && !cdata && peek(p+1)=='/')
1741             {
1742             break;
1743             }
1744         //# CDATA
1745         p2 = match(p, "<![CDATA[");
1746         if (p2 > p)
1747             {
1748             cdata = true;
1749             p = p2;
1750             continue;
1751             }
1753         //# CHILD ELEMENT
1754         if (ch == '<')
1755             {
1756             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1757             if (p2 == p)
1758                 {
1759                 /*
1760                 printf("problem on element:%s.  p2:%d p:%d\n",
1761                       openTagName.c_str(), p2, p);
1762                 */
1763                 return p0;
1764                 }
1765             p = p2;
1766             continue;
1767             }
1768         //# ENTITY
1769         if (ch=='&' && !cdata)
1770             {
1771             bool found = false;
1772             for (EntityEntry *ee = entities ; ee->value ; ee++)
1773                 {
1774                 int p2 = match(p, ee->escaped);
1775                 if (p2>p)
1776                     {
1777                     data.push_back(ee->value);
1778                     p = p2;
1779                     found = true;
1780                     break;
1781                     }
1782                 }
1783             if (!found)
1784                 {
1785                 error("unterminated entity");
1786                 return -1;
1787                 }
1788             continue;
1789             }
1791         //# NONE OF THE ABOVE
1792         data.push_back(ch);
1793         p++;
1794         }/*while*/
1797     n->value = data;
1798     //printf("%d : data:%s\n",p,data.c_str());
1800     //## Get close tag
1801     p = skipwhite(p);
1802     ch = peek(p);
1803     if (ch != '<')
1804         {
1805         error("no < for end tag\n");
1806         return p0;
1807         }
1808     p++;
1809     ch = peek(p);
1810     if (ch != '/')
1811         {
1812         error("no / on end tag");
1813         return p0;
1814         }
1815     p++;
1816     ch = peek(p);
1817     p = skipwhite(p);
1818     String closeTagName;
1819     p = getWord(p, closeTagName);
1820     if (openTagName != closeTagName)
1821         {
1822         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1823                 openTagName.c_str(), closeTagName.c_str());
1824         return p0;
1825         }
1826     p = skipwhite(p);
1827     if (peek(p) != '>')
1828         {
1829         error("no > on end tag for '%s'", closeTagName.c_str());
1830         return p0;
1831         }
1832     p++;
1833     // printf("close element:%s\n",closeTagName.c_str());
1834     p = skipwhite(p);
1835     return p;
1841 Element *Parser::parse(XMLCh *buf,int pos,int len)
1843     parselen = len;
1844     parsebuf = buf;
1845     Element *rootNode = new Element("root");
1846     pos = parseVersion(pos);
1847     pos = parseDoctype(pos);
1848     pos = parseElement(pos, rootNode, 1);
1849     return rootNode;
1853 Element *Parser::parse(const char *buf, int pos, int len)
1855     XMLCh *charbuf = new XMLCh[len + 1];
1856     long i = 0;
1857     for ( ; i < len ; i++)
1858         charbuf[i] = (XMLCh)buf[i];
1859     charbuf[i] = '\0';
1861     Element *n = parse(charbuf, pos, len);
1862     delete[] charbuf;
1863     return n;
1866 Element *Parser::parse(const String &buf)
1868     long len = (long)buf.size();
1869     XMLCh *charbuf = new XMLCh[len + 1];
1870     long i = 0;
1871     for ( ; i < len ; i++)
1872         charbuf[i] = (XMLCh)buf[i];
1873     charbuf[i] = '\0';
1875     Element *n = parse(charbuf, 0, len);
1876     delete[] charbuf;
1877     return n;
1880 Element *Parser::parseFile(const String &fileName)
1883     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1884     FILE *f = fopen(fileName.c_str(), "rb");
1885     if (!f)
1886         return NULL;
1888     struct stat  statBuf;
1889     if (fstat(fileno(f),&statBuf)<0)
1890         {
1891         fclose(f);
1892         return NULL;
1893         }
1894     long filelen = statBuf.st_size;
1896     //printf("length:%d\n",filelen);
1897     XMLCh *charbuf = new XMLCh[filelen + 1];
1898     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1899         {
1900         *p = (XMLCh)fgetc(f);
1901         }
1902     fclose(f);
1903     charbuf[filelen] = '\0';
1906     /*
1907     printf("nrbytes:%d\n",wc_count);
1908     printf("buf:%ls\n======\n",charbuf);
1909     */
1910     Element *n = parse(charbuf, 0, filelen);
1911     delete[] charbuf;
1912     return n;
1915 //########################################################################
1916 //########################################################################
1917 //##  E N D    X M L
1918 //########################################################################
1919 //########################################################################
1926 //########################################################################
1927 //########################################################################
1928 //##  U R I
1929 //########################################################################
1930 //########################################################################
1932 //This would normally be a call to a UNICODE function
1933 #define isLetter(x) isalpha(x)
1935 /**
1936  *  A class that implements the W3C URI resource reference.
1937  */
1938 class URI
1940 public:
1942     typedef enum
1943         {
1944         SCHEME_NONE =0,
1945         SCHEME_DATA,
1946         SCHEME_HTTP,
1947         SCHEME_HTTPS,
1948         SCHEME_FTP,
1949         SCHEME_FILE,
1950         SCHEME_LDAP,
1951         SCHEME_MAILTO,
1952         SCHEME_NEWS,
1953         SCHEME_TELNET
1954         } SchemeTypes;
1956     /**
1957      *
1958      */
1959     URI()
1960         {
1961         init();
1962         }
1964     /**
1965      *
1966      */
1967     URI(const String &str)
1968         {
1969         init();
1970         parse(str);
1971         }
1974     /**
1975      *
1976      */
1977     URI(const char *str)
1978         {
1979         init();
1980         String domStr = str;
1981         parse(domStr);
1982         }
1985     /**
1986      *
1987      */
1988     URI(const URI &other)
1989         {
1990         init();
1991         assign(other);
1992         }
1995     /**
1996      *
1997      */
1998     URI &operator=(const URI &other)
1999         {
2000         init();
2001         assign(other);
2002         return *this;
2003         }
2006     /**
2007      *
2008      */
2009     virtual ~URI()
2010         {}
2014     /**
2015      *
2016      */
2017     virtual bool parse(const String &str);
2019     /**
2020      *
2021      */
2022     virtual String toString() const;
2024     /**
2025      *
2026      */
2027     virtual int getScheme() const;
2029     /**
2030      *
2031      */
2032     virtual String getSchemeStr() const;
2034     /**
2035      *
2036      */
2037     virtual String getAuthority() const;
2039     /**
2040      *  Same as getAuthority, but if the port has been specified
2041      *  as host:port , the port will not be included
2042      */
2043     virtual String getHost() const;
2045     /**
2046      *
2047      */
2048     virtual int getPort() const;
2050     /**
2051      *
2052      */
2053     virtual String getPath() const;
2055     /**
2056      *
2057      */
2058     virtual String getNativePath() const;
2060     /**
2061      *
2062      */
2063     virtual bool isAbsolute() const;
2065     /**
2066      *
2067      */
2068     virtual bool isOpaque() const;
2070     /**
2071      *
2072      */
2073     virtual String getQuery() const;
2075     /**
2076      *
2077      */
2078     virtual String getFragment() const;
2080     /**
2081      *
2082      */
2083     virtual URI resolve(const URI &other) const;
2085     /**
2086      *
2087      */
2088     virtual void normalize();
2090 private:
2092     /**
2093      *
2094      */
2095     void init()
2096         {
2097         parsebuf  = NULL;
2098         parselen  = 0;
2099         scheme    = SCHEME_NONE;
2100         schemeStr = "";
2101         port      = 0;
2102         authority = "";
2103         path      = "";
2104         absolute  = false;
2105         opaque    = false;
2106         query     = "";
2107         fragment  = "";
2108         }
2111     /**
2112      *
2113      */
2114     void assign(const URI &other)
2115         {
2116         scheme    = other.scheme;
2117         schemeStr = other.schemeStr;
2118         authority = other.authority;
2119         port      = other.port;
2120         path      = other.path;
2121         absolute  = other.absolute;
2122         opaque    = other.opaque;
2123         query     = other.query;
2124         fragment  = other.fragment;
2125         }
2127     int scheme;
2129     String schemeStr;
2131     String authority;
2133     bool portSpecified;
2135     int port;
2137     String path;
2139     bool absolute;
2141     bool opaque;
2143     String query;
2145     String fragment;
2147     void error(const char *fmt, ...);
2149     void trace(const char *fmt, ...);
2152     int peek(int p);
2154     int match(int p, const char *key);
2156     int parseScheme(int p);
2158     int parseHierarchicalPart(int p0);
2160     int parseQuery(int p0);
2162     int parseFragment(int p0);
2164     int parse(int p);
2166     char *parsebuf;
2168     int parselen;
2170 };
2174 typedef struct
2176     int         ival;
2177     const char *sval;
2178     int         port;
2179 } LookupEntry;
2181 LookupEntry schemes[] =
2183     { URI::SCHEME_DATA,   "data:",    0 },
2184     { URI::SCHEME_HTTP,   "http:",   80 },
2185     { URI::SCHEME_HTTPS,  "https:", 443 },
2186     { URI::SCHEME_FTP,    "ftp",     12 },
2187     { URI::SCHEME_FILE,   "file:",    0 },
2188     { URI::SCHEME_LDAP,   "ldap:",  123 },
2189     { URI::SCHEME_MAILTO, "mailto:", 25 },
2190     { URI::SCHEME_NEWS,   "news:",  117 },
2191     { URI::SCHEME_TELNET, "telnet:", 23 },
2192     { 0,                  NULL,       0 }
2193 };
2196 String URI::toString() const
2198     String str = schemeStr;
2199     if (authority.size() > 0)
2200         {
2201         str.append("//");
2202         str.append(authority);
2203         }
2204     str.append(path);
2205     if (query.size() > 0)
2206         {
2207         str.append("?");
2208         str.append(query);
2209         }
2210     if (fragment.size() > 0)
2211         {
2212         str.append("#");
2213         str.append(fragment);
2214         }
2215     return str;
2219 int URI::getScheme() const
2221     return scheme;
2224 String URI::getSchemeStr() const
2226     return schemeStr;
2230 String URI::getAuthority() const
2232     String ret = authority;
2233     if (portSpecified && port>=0)
2234         {
2235         char buf[7];
2236         snprintf(buf, 6, ":%6d", port);
2237         ret.append(buf);
2238         }
2239     return ret;
2242 String URI::getHost() const
2244     return authority;
2247 int URI::getPort() const
2249     return port;
2253 String URI::getPath() const
2255     return path;
2258 String URI::getNativePath() const
2260     String npath;
2261 #ifdef __WIN32__
2262     unsigned int firstChar = 0;
2263     if (path.size() >= 3)
2264         {
2265         if (path[0] == '/' &&
2266             isLetter(path[1]) &&
2267             path[2] == ':')
2268             firstChar++;
2269          }
2270     for (unsigned int i=firstChar ; i<path.size() ; i++)
2271         {
2272         XMLCh ch = (XMLCh) path[i];
2273         if (ch == '/')
2274             npath.push_back((XMLCh)'\\');
2275         else
2276             npath.push_back(ch);
2277         }
2278 #else
2279     npath = path;
2280 #endif
2281     return npath;
2285 bool URI::isAbsolute() const
2287     return absolute;
2290 bool URI::isOpaque() const
2292     return opaque;
2296 String URI::getQuery() const
2298     return query;
2302 String URI::getFragment() const
2304     return fragment;
2308 URI URI::resolve(const URI &other) const
2310     //### According to w3c, this is handled in 3 cases
2312     //## 1
2313     if (opaque || other.isAbsolute())
2314         return other;
2316     //## 2
2317     if (other.fragment.size()  >  0 &&
2318         other.path.size()      == 0 &&
2319         other.scheme           == SCHEME_NONE &&
2320         other.authority.size() == 0 &&
2321         other.query.size()     == 0 )
2322         {
2323         URI fragUri = *this;
2324         fragUri.fragment = other.fragment;
2325         return fragUri;
2326         }
2328     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2329     URI newUri;
2330     //# 3.1
2331     newUri.scheme    = scheme;
2332     newUri.schemeStr = schemeStr;
2333     newUri.query     = other.query;
2334     newUri.fragment  = other.fragment;
2335     if (other.authority.size() > 0)
2336         {
2337         //# 3.2
2338         if (absolute || other.absolute)
2339             newUri.absolute = true;
2340         newUri.authority = other.authority;
2341         newUri.port      = other.port;//part of authority
2342         newUri.path      = other.path;
2343         }
2344     else
2345         {
2346         //# 3.3
2347         if (other.absolute)
2348             {
2349             newUri.absolute = true;
2350             newUri.path     = other.path;
2351             }
2352         else
2353             {
2354             unsigned int pos = path.find_last_of('/');
2355             if (pos != path.npos)
2356                 {
2357                 String tpath = path.substr(0, pos+1);
2358                 tpath.append(other.path);
2359                 newUri.path = tpath;
2360                 }
2361             else
2362                 newUri.path = other.path;
2363             }
2364         }
2366     newUri.normalize();
2367     return newUri;
2372 /**
2373  *  This follows the Java URI algorithm:
2374  *   1. All "." segments are removed.
2375  *   2. If a ".." segment is preceded by a non-".." segment
2376  *          then both of these segments are removed. This step
2377  *          is repeated until it is no longer applicable.
2378  *   3. If the path is relative, and if its first segment
2379  *          contains a colon character (':'), then a "." segment
2380  *          is prepended. This prevents a relative URI with a path
2381  *          such as "a:b/c/d" from later being re-parsed as an
2382  *          opaque URI with a scheme of "a" and a scheme-specific
2383  *          part of "b/c/d". (Deviation from RFC 2396)
2384  */
2385 void URI::normalize()
2387     std::vector<String> segments;
2389     //## Collect segments
2390     if (path.size()<2)
2391         return;
2392     bool abs = false;
2393     unsigned int pos=0;
2394     if (path[0]=='/')
2395         {
2396         abs = true;
2397         pos++;
2398         }
2399     while (pos < path.size())
2400         {
2401         unsigned int pos2 = path.find('/', pos);
2402         if (pos2==path.npos)
2403             {
2404             String seg = path.substr(pos);
2405             //printf("last segment:%s\n", seg.c_str());
2406             segments.push_back(seg);
2407             break;
2408             }
2409         if (pos2>pos)
2410             {
2411             String seg = path.substr(pos, pos2-pos);
2412             //printf("segment:%s\n", seg.c_str());
2413             segments.push_back(seg);
2414             }
2415         pos = pos2;
2416         pos++;
2417         }
2419     //## Clean up (normalize) segments
2420     bool edited = false;
2421     std::vector<String>::iterator iter;
2422     for (iter=segments.begin() ; iter!=segments.end() ; )
2423         {
2424         String s = *iter;
2425         if (s == ".")
2426             {
2427             iter = segments.erase(iter);
2428             edited = true;
2429             }
2430         else if (s == ".." &&
2431                  iter != segments.begin() &&
2432                  *(iter-1) != "..")
2433             {
2434             iter--; //back up, then erase two entries
2435             iter = segments.erase(iter);
2436             iter = segments.erase(iter);
2437             edited = true;
2438             }
2439         else
2440             iter++;
2441         }
2443     //## Rebuild path, if necessary
2444     if (edited)
2445         {
2446         path.clear();
2447         if (abs)
2448             {
2449             path.append("/");
2450             }
2451         std::vector<String>::iterator iter;
2452         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2453             {
2454             if (iter != segments.begin())
2455                 path.append("/");
2456             path.append(*iter);
2457             }
2458         }
2464 //#########################################################################
2465 //# M E S S A G E S
2466 //#########################################################################
2468 void URI::error(const char *fmt, ...)
2470     va_list args;
2471     fprintf(stderr, "URI error: ");
2472     va_start(args, fmt);
2473     vfprintf(stderr, fmt, args);
2474     va_end(args);
2475     fprintf(stderr, "\n");
2478 void URI::trace(const char *fmt, ...)
2480     va_list args;
2481     fprintf(stdout, "URI: ");
2482     va_start(args, fmt);
2483     vfprintf(stdout, fmt, args);
2484     va_end(args);
2485     fprintf(stdout, "\n");
2491 //#########################################################################
2492 //# P A R S I N G
2493 //#########################################################################
2497 int URI::peek(int p)
2499     if (p<0 || p>=parselen)
2500         return -1;
2501     return parsebuf[p];
2506 int URI::match(int p0, const char *key)
2508     int p = p0;
2509     while (p < parselen)
2510         {
2511         if (*key == '\0')
2512             return p;
2513         else if (*key != parsebuf[p])
2514             break;
2515         p++; key++;
2516         }
2517     return p0;
2520 //#########################################################################
2521 //#  Parsing is performed according to:
2522 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2523 //#########################################################################
2525 int URI::parseScheme(int p0)
2527     int p = p0;
2528     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2529         {
2530         int p2 = match(p, entry->sval);
2531         if (p2 > p)
2532             {
2533             schemeStr = entry->sval;
2534             scheme    = entry->ival;
2535             port      = entry->port;
2536             p = p2;
2537             return p;
2538             }
2539         }
2541     return p;
2545 int URI::parseHierarchicalPart(int p0)
2547     int p = p0;
2548     int ch;
2550     //# Authority field (host and port, for example)
2551     int p2 = match(p, "//");
2552     if (p2 > p)
2553         {
2554         p = p2;
2555         portSpecified = false;
2556         String portStr;
2557         while (p < parselen)
2558             {
2559             ch = peek(p);
2560             if (ch == '/')
2561                 break;
2562             else if (ch == ':')
2563                 portSpecified = true;
2564             else if (portSpecified)
2565                 portStr.push_back((XMLCh)ch);
2566             else
2567                 authority.push_back((XMLCh)ch);
2568             p++;
2569             }
2570         if (portStr.size() > 0)
2571             {
2572             char *pstr = (char *)portStr.c_str();
2573             char *endStr;
2574             long val = strtol(pstr, &endStr, 10);
2575             if (endStr > pstr) //successful parse?
2576                 port = val;
2577             }
2578         }
2580     //# Are we absolute?
2581     ch = peek(p);
2582     if (isLetter(ch) && peek(p+1)==':')
2583         {
2584         absolute = true;
2585         path.push_back((XMLCh)'/');
2586         }
2587     else if (ch == '/')
2588         {
2589         absolute = true;
2590         if (p>p0) //in other words, if '/' is not the first char
2591             opaque = true;
2592         path.push_back((XMLCh)ch);
2593         p++;
2594         }
2596     while (p < parselen)
2597         {
2598         ch = peek(p);
2599         if (ch == '?' || ch == '#')
2600             break;
2601         path.push_back((XMLCh)ch);
2602         p++;
2603         }
2605     return p;
2608 int URI::parseQuery(int p0)
2610     int p = p0;
2611     int ch = peek(p);
2612     if (ch != '?')
2613         return p0;
2615     p++;
2616     while (p < parselen)
2617         {
2618         ch = peek(p);
2619         if (ch == '#')
2620             break;
2621         query.push_back((XMLCh)ch);
2622         p++;
2623         }
2626     return p;
2629 int URI::parseFragment(int p0)
2632     int p = p0;
2633     int ch = peek(p);
2634     if (ch != '#')
2635         return p0;
2637     p++;
2638     while (p < parselen)
2639         {
2640         ch = peek(p);
2641         if (ch == '?')
2642             break;
2643         fragment.push_back((XMLCh)ch);
2644         p++;
2645         }
2648     return p;
2652 int URI::parse(int p0)
2655     int p = p0;
2657     int p2 = parseScheme(p);
2658     if (p2 < 0)
2659         {
2660         error("Scheme");
2661         return -1;
2662         }
2663     p = p2;
2666     p2 = parseHierarchicalPart(p);
2667     if (p2 < 0)
2668         {
2669         error("Hierarchical part");
2670         return -1;
2671         }
2672     p = p2;
2674     p2 = parseQuery(p);
2675     if (p2 < 0)
2676         {
2677         error("Query");
2678         return -1;
2679         }
2680     p = p2;
2683     p2 = parseFragment(p);
2684     if (p2 < 0)
2685         {
2686         error("Fragment");
2687         return -1;
2688         }
2689     p = p2;
2691     return p;
2697 bool URI::parse(const String &str)
2699     init();
2700     
2701     parselen = str.size();
2703     String tmp;
2704     for (unsigned int i=0 ; i<str.size() ; i++)
2705         {
2706         XMLCh ch = (XMLCh) str[i];
2707         if (ch == '\\')
2708             tmp.push_back((XMLCh)'/');
2709         else
2710             tmp.push_back(ch);
2711         }
2712     parsebuf = (char *) tmp.c_str();
2715     int p = parse(0);
2716     normalize();
2718     if (p < 0)
2719         {
2720         error("Syntax error");
2721         return false;
2722         }
2724     //printf("uri:%s\n", toString().c_str());
2725     //printf("path:%s\n", path.c_str());
2727     return true;
2738 //########################################################################
2739 //########################################################################
2740 //##  M A K E
2741 //########################################################################
2742 //########################################################################
2744 //########################################################################
2745 //# F I L E S E T
2746 //########################################################################
2747 /**
2748  * This is the descriptor for a <fileset> item
2749  */
2750 class FileSet
2752 public:
2754     /**
2755      *
2756      */
2757     FileSet()
2758         {}
2760     /**
2761      *
2762      */
2763     FileSet(const FileSet &other)
2764         { assign(other); }
2766     /**
2767      *
2768      */
2769     FileSet &operator=(const FileSet &other)
2770         { assign(other); return *this; }
2772     /**
2773      *
2774      */
2775     virtual ~FileSet()
2776         {}
2778     /**
2779      *
2780      */
2781     String getDirectory()
2782         { return directory; }
2783         
2784     /**
2785      *
2786      */
2787     void setDirectory(const String &val)
2788         { directory = val; }
2790     /**
2791      *
2792      */
2793     void setFiles(const std::vector<String> &val)
2794         { files = val; }
2796     /**
2797      *
2798      */
2799     std::vector<String> getFiles()
2800         { return files; }
2801         
2802     /**
2803      *
2804      */
2805     void setIncludes(const std::vector<String> &val)
2806         { includes = val; }
2808     /**
2809      *
2810      */
2811     std::vector<String> getIncludes()
2812         { return includes; }
2813         
2814     /**
2815      *
2816      */
2817     void setExcludes(const std::vector<String> &val)
2818         { excludes = val; }
2820     /**
2821      *
2822      */
2823     std::vector<String> getExcludes()
2824         { return excludes; }
2825         
2826     /**
2827      *
2828      */
2829     unsigned int size()
2830         { return files.size(); }
2831         
2832     /**
2833      *
2834      */
2835     String operator[](int index)
2836         { return files[index]; }
2837         
2838     /**
2839      *
2840      */
2841     void clear()
2842         {
2843         directory = "";
2844         files.clear();
2845         includes.clear();
2846         excludes.clear();
2847         }
2848         
2850 private:
2852     void assign(const FileSet &other)
2853         {
2854         directory = other.directory;
2855         files     = other.files;
2856         includes  = other.includes;
2857         excludes  = other.excludes;
2858         }
2860     String directory;
2861     std::vector<String> files;
2862     std::vector<String> includes;
2863     std::vector<String> excludes;
2864 };
2867 //########################################################################
2868 //# F I L E L I S T
2869 //########################################################################
2870 /**
2871  * This is a simpler, explicitly-named list of files
2872  */
2873 class FileList
2875 public:
2877     /**
2878      *
2879      */
2880     FileList()
2881         {}
2883     /**
2884      *
2885      */
2886     FileList(const FileList &other)
2887         { assign(other); }
2889     /**
2890      *
2891      */
2892     FileList &operator=(const FileList &other)
2893         { assign(other); return *this; }
2895     /**
2896      *
2897      */
2898     virtual ~FileList()
2899         {}
2901     /**
2902      *
2903      */
2904     String getDirectory()
2905         { return directory; }
2906         
2907     /**
2908      *
2909      */
2910     void setDirectory(const String &val)
2911         { directory = val; }
2913     /**
2914      *
2915      */
2916     void setFiles(const std::vector<String> &val)
2917         { files = val; }
2919     /**
2920      *
2921      */
2922     std::vector<String> getFiles()
2923         { return files; }
2924         
2925     /**
2926      *
2927      */
2928     unsigned int size()
2929         { return files.size(); }
2930         
2931     /**
2932      *
2933      */
2934     String operator[](int index)
2935         { return files[index]; }
2936         
2937     /**
2938      *
2939      */
2940     void clear()
2941         {
2942         directory = "";
2943         files.clear();
2944         }
2945         
2947 private:
2949     void assign(const FileList &other)
2950         {
2951         directory = other.directory;
2952         files     = other.files;
2953         }
2955     String directory;
2956     std::vector<String> files;
2957 };
2962 //########################################################################
2963 //# M A K E    B A S E
2964 //########################################################################
2965 /**
2966  * Base class for all classes in this file
2967  */
2968 class MakeBase
2970 public:
2972     MakeBase()
2973         { line = 0; }
2974     virtual ~MakeBase()
2975         {}
2977     /**
2978      *     Return the URI of the file associated with this object 
2979      */     
2980     URI getURI()
2981         { return uri; }
2983     /**
2984      * Set the uri to the given string
2985      */
2986     void setURI(const String &uristr)
2987         { uri.parse(uristr); }
2989     /**
2990      *  Resolve another path relative to this one
2991      */
2992     String resolve(const String &otherPath);
2994     /**
2995      * replace variable refs like ${a} with their values
2996      * Assume that the string has already been syntax validated
2997      */
2998     String eval(const String &s, const String &defaultVal);
3000     /**
3001      * replace variable refs like ${a} with their values
3002      * return true or false
3003      * Assume that the string has already been syntax validated
3004      */
3005     bool evalBool(const String &s, bool defaultVal);
3007     /**
3008      *  Get an element attribute, performing substitutions if necessary
3009      */
3010     bool getAttribute(Element *elem, const String &name, String &result);
3012     /**
3013      * Get an element value, performing substitutions if necessary
3014      */
3015     bool getValue(Element *elem, String &result);
3016     
3017     /**
3018      * Set the current line number in the file
3019      */         
3020     void setLine(int val)
3021         { line = val; }
3022         
3023     /**
3024      * Get the current line number in the file
3025      */         
3026     int getLine()
3027         { return line; }
3030     /**
3031      * Set a property to a given value
3032      */
3033     virtual void setProperty(const String &name, const String &val)
3034         {
3035         properties[name] = val;
3036         }
3038     /**
3039      * Return a named property is found, else a null string
3040      */
3041     virtual String getProperty(const String &name)
3042         {
3043         String val;
3044         std::map<String, String>::iterator iter = properties.find(name);
3045         if (iter != properties.end())
3046             val = iter->second;
3047         String sval;
3048         if (!getSubstitutions(val, sval))
3049             return false;
3050         return sval;
3051         }
3053     /**
3054      * Return true if a named property is found, else false
3055      */
3056     virtual bool hasProperty(const String &name)
3057         {
3058         std::map<String, String>::iterator iter = properties.find(name);
3059         if (iter == properties.end())
3060             return false;
3061         return true;
3062         }
3065 protected:
3067     /**
3068      *    The path to the file associated with this object
3069      */     
3070     URI uri;
3071     
3072     /**
3073      *    If this prefix is seen in a substitution, use an environment
3074      *    variable.
3075      *             example:  <property environment="env"/>
3076      *             ${env.JAVA_HOME}
3077      */
3078     String envPrefix;
3080     /**
3081      *    If this prefix is seen in a substitution, use as a
3082      *    pkg-config 'all' query
3083      *             example:  <property pkg-config="pc"/>
3084      *             ${pc.gtkmm}
3085      */
3086     String pcPrefix;
3088     /**
3089      *    If this prefix is seen in a substitution, use as a
3090      *    pkg-config 'cflags' query
3091      *             example:  <property pkg-config="pcc"/>
3092      *             ${pcc.gtkmm}
3093      */
3094     String pccPrefix;
3096     /**
3097      *    If this prefix is seen in a substitution, use as a
3098      *    pkg-config 'libs' query
3099      *             example:  <property pkg-config="pcl"/>
3100      *             ${pcl.gtkmm}
3101      */
3102     String pclPrefix;
3108     /**
3109      *  Print a printf()-like formatted error message
3110      */
3111     void error(const char *fmt, ...);
3113     /**
3114      *  Print a printf()-like formatted trace message
3115      */
3116     void status(const char *fmt, ...);
3118     /**
3119      *  Show target status
3120      */
3121     void targetstatus(const char *fmt, ...);
3123     /**
3124      *  Print a printf()-like formatted trace message
3125      */
3126     void trace(const char *fmt, ...);
3128     /**
3129      *  Check if a given string matches a given regex pattern
3130      */
3131     bool regexMatch(const String &str, const String &pattern);
3133     /**
3134      *
3135      */
3136     String getSuffix(const String &fname);
3138     /**
3139      * Break up a string into substrings delimited the characters
3140      * in delimiters.  Null-length substrings are ignored
3141      */  
3142     std::vector<String> tokenize(const String &val,
3143                           const String &delimiters);
3145     /**
3146      *  replace runs of whitespace with a space
3147      */
3148     String strip(const String &s);
3150     /**
3151      *  remove leading whitespace from each line
3152      */
3153     String leftJustify(const String &s);
3155     /**
3156      *  remove leading and trailing whitespace from string
3157      */
3158     String trim(const String &s);
3160     /**
3161      *  Return a lower case version of the given string
3162      */
3163     String toLower(const String &s);
3165     /**
3166      * Return the native format of the canonical
3167      * path which we store
3168      */
3169     String getNativePath(const String &path);
3171     /**
3172      * Execute a shell command.  Outbuf is a ref to a string
3173      * to catch the result.     
3174      */         
3175     bool executeCommand(const String &call,
3176                         const String &inbuf,
3177                         String &outbuf,
3178                         String &errbuf);
3179     /**
3180      * List all directories in a given base and starting directory
3181      * It is usually called like:
3182      *        bool ret = listDirectories("src", "", result);    
3183      */         
3184     bool listDirectories(const String &baseName,
3185                          const String &dirname,
3186                          std::vector<String> &res);
3188     /**
3189      * Find all files in the named directory 
3190      */         
3191     bool listFiles(const String &baseName,
3192                    const String &dirname,
3193                    std::vector<String> &result);
3195     /**
3196      * Perform a listing for a fileset 
3197      */         
3198     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3200     /**
3201      * Parse a <patternset>
3202      */  
3203     bool parsePatternSet(Element *elem,
3204                        MakeBase &propRef,
3205                        std::vector<String> &includes,
3206                        std::vector<String> &excludes);
3208     /**
3209      * Parse a <fileset> entry, and determine which files
3210      * should be included
3211      */  
3212     bool parseFileSet(Element *elem,
3213                     MakeBase &propRef,
3214                     FileSet &fileSet);
3215     /**
3216      * Parse a <filelist> entry
3217      */  
3218     bool parseFileList(Element *elem,
3219                     MakeBase &propRef,
3220                     FileList &fileList);
3222     /**
3223      * Return this object's property list
3224      */
3225     virtual std::map<String, String> &getProperties()
3226         { return properties; }
3229     std::map<String, String> properties;
3231     /**
3232      * Create a directory, making intermediate dirs
3233      * if necessary
3234      */                  
3235     bool createDirectory(const String &dirname);
3237     /**
3238      * Delete a directory and its children if desired
3239      */
3240     bool removeDirectory(const String &dirName);
3242     /**
3243      * Copy a file from one name to another. Perform only if needed
3244      */ 
3245     bool copyFile(const String &srcFile, const String &destFile);
3247     /**
3248      * Tests if the file exists and is a regular file
3249      */ 
3250     bool isRegularFile(const String &fileName);
3252     /**
3253      * Tests if the file exists and is a directory
3254      */ 
3255     bool isDirectory(const String &fileName);
3257     /**
3258      * Tests is the modification date of fileA is newer than fileB
3259      */ 
3260     bool isNewerThan(const String &fileA, const String &fileB);
3262 private:
3264     bool pkgConfigRecursive(const String packageName,
3265                             const String &path, 
3266                             const String &prefix, 
3267                             int query,
3268                             String &result,
3269                             std::set<String> &deplist);
3271     /**
3272      * utility method to query for "all", "cflags", or "libs" for this package and its
3273      * dependencies.  0, 1, 2
3274      */          
3275     bool pkgConfigQuery(const String &packageName, int query, String &result);
3277     /**
3278      * replace a variable ref like ${a} with a value
3279      */
3280     bool lookupProperty(const String &s, String &result);
3281     
3282     /**
3283      * called by getSubstitutions().  This is in case a looked-up string
3284      * has substitutions also.     
3285      */
3286     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3288     /**
3289      * replace variable refs in a string like ${a} with their values
3290      */
3291     bool getSubstitutions(const String &s, String &result);
3293     int line;
3296 };
3300 /**
3301  * Define the pkg-config class here, since it will be used in MakeBase method
3302  * implementations. 
3303  */
3304 class PkgConfig : public MakeBase
3307 public:
3309     /**
3310      *
3311      */
3312     PkgConfig()
3313         {
3314          path   = ".";
3315          prefix = "/target";
3316          init();
3317          }
3319     /**
3320      *
3321      */
3322     PkgConfig(const PkgConfig &other)
3323         { assign(other); }
3325     /**
3326      *
3327      */
3328     PkgConfig &operator=(const PkgConfig &other)
3329         { assign(other); return *this; }
3331     /**
3332      *
3333      */
3334     virtual ~PkgConfig()
3335         { }
3337     /**
3338      *
3339      */
3340     virtual String getName()
3341         { return name; }
3343     /**
3344      *
3345      */
3346     virtual String getPath()
3347         { return path; }
3349     /**
3350      *
3351      */
3352     virtual void setPath(const String &val)
3353         { path = val; }
3355     /**
3356      *
3357      */
3358     virtual String getPrefix()
3359         { return prefix; }
3361     /**
3362      *  Allow the user to override the prefix in the file
3363      */
3364     virtual void setPrefix(const String &val)
3365         { prefix = val; }
3367     /**
3368      *
3369      */
3370     virtual String getDescription()
3371         { return description; }
3373     /**
3374      *
3375      */
3376     virtual String getCflags()
3377         { return cflags; }
3379     /**
3380      *
3381      */
3382     virtual String getLibs()
3383         { return libs; }
3385     /**
3386      *
3387      */
3388     virtual String getAll()
3389         {
3390          String ret = cflags;
3391          ret.append(" ");
3392          ret.append(libs);
3393          return ret;
3394         }
3396     /**
3397      *
3398      */
3399     virtual String getVersion()
3400         { return version; }
3402     /**
3403      *
3404      */
3405     virtual int getMajorVersion()
3406         { return majorVersion; }
3408     /**
3409      *
3410      */
3411     virtual int getMinorVersion()
3412         { return minorVersion; }
3414     /**
3415      *
3416      */
3417     virtual int getMicroVersion()
3418         { return microVersion; }
3420     /**
3421      *
3422      */
3423     virtual std::map<String, String> &getAttributes()
3424         { return attrs; }
3426     /**
3427      *
3428      */
3429     virtual std::vector<String> &getRequireList()
3430         { return requireList; }
3432     /**
3433      *  Read a file for its details
3434      */         
3435     virtual bool readFile(const String &fileName);
3437     /**
3438      *  Read a file for its details
3439      */         
3440     virtual bool query(const String &name);
3442 private:
3444     void init()
3445         {
3446         //do not set path and prefix here
3447         name         = "";
3448         description  = "";
3449         cflags       = "";
3450         libs         = "";
3451         requires     = "";
3452         version      = "";
3453         majorVersion = 0;
3454         minorVersion = 0;
3455         microVersion = 0;
3456         fileName     = "";
3457         attrs.clear();
3458         requireList.clear();
3459         }
3461     void assign(const PkgConfig &other)
3462         {
3463         name         = other.name;
3464         path         = other.path;
3465         prefix       = other.prefix;
3466         description  = other.description;
3467         cflags       = other.cflags;
3468         libs         = other.libs;
3469         requires     = other.requires;
3470         version      = other.version;
3471         majorVersion = other.majorVersion;
3472         minorVersion = other.minorVersion;
3473         microVersion = other.microVersion;
3474         fileName     = other.fileName;
3475         attrs        = other.attrs;
3476         requireList  = other.requireList;
3477         }
3481     int get(int pos);
3483     int skipwhite(int pos);
3485     int getword(int pos, String &ret);
3487     /**
3488      * Very important
3489      */         
3490     bool parseRequires();
3492     void parseVersion();
3494     bool parseLine(const String &lineBuf);
3496     bool parse(const String &buf);
3498     void dumpAttrs();
3500     String name;
3502     String path;
3504     String prefix;
3506     String description;
3508     String cflags;
3510     String libs;
3512     String requires;
3514     String version;
3516     int majorVersion;
3518     int minorVersion;
3520     int microVersion;
3522     String fileName;
3524     std::map<String, String> attrs;
3526     std::vector<String> requireList;
3528     char *parsebuf;
3529     int parselen;
3530 };
3535 /**
3536  *  Print a printf()-like formatted error message
3537  */
3538 void MakeBase::error(const char *fmt, ...)
3540     va_list args;
3541     va_start(args,fmt);
3542     fprintf(stderr, "Make error line %d: ", line);
3543     vfprintf(stderr, fmt, args);
3544     fprintf(stderr, "\n");
3545     va_end(args) ;
3550 /**
3551  *  Print a printf()-like formatted trace message
3552  */
3553 void MakeBase::status(const char *fmt, ...)
3555     va_list args;
3556     //fprintf(stdout, " ");
3557     va_start(args,fmt);
3558     vfprintf(stdout, fmt, args);
3559     va_end(args);
3560     fprintf(stdout, "\n");
3561     fflush(stdout);
3565 /**
3566  *  Print a printf()-like formatted trace message
3567  */
3568 void MakeBase::trace(const char *fmt, ...)
3570     va_list args;
3571     fprintf(stdout, "Make: ");
3572     va_start(args,fmt);
3573     vfprintf(stdout, fmt, args);
3574     va_end(args) ;
3575     fprintf(stdout, "\n");
3576     fflush(stdout);
3581 /**
3582  *  Resolve another path relative to this one
3583  */
3584 String MakeBase::resolve(const String &otherPath)
3586     URI otherURI(otherPath);
3587     URI fullURI = uri.resolve(otherURI);
3588     String ret = fullURI.toString();
3589     return ret;
3594 /**
3595  *  Check if a given string matches a given regex pattern
3596  */
3597 bool MakeBase::regexMatch(const String &str, const String &pattern)
3599     const TRexChar *terror = NULL;
3600     const TRexChar *cpat = pattern.c_str();
3601     TRex *expr = trex_compile(cpat, &terror);
3602     if (!expr)
3603         {
3604         if (!terror)
3605             terror = "undefined";
3606         error("compilation error [%s]!\n", terror);
3607         return false;
3608         } 
3610     bool ret = true;
3612     const TRexChar *cstr = str.c_str();
3613     if (trex_match(expr, cstr))
3614         {
3615         ret = true;
3616         }
3617     else
3618         {
3619         ret = false;
3620         }
3622     trex_free(expr);
3624     return ret;
3627 /**
3628  *  Return the suffix, if any, of a file name
3629  */
3630 String MakeBase::getSuffix(const String &fname)
3632     if (fname.size() < 2)
3633         return "";
3634     unsigned int pos = fname.find_last_of('.');
3635     if (pos == fname.npos)
3636         return "";
3637     pos++;
3638     String res = fname.substr(pos, fname.size()-pos);
3639     //trace("suffix:%s", res.c_str()); 
3640     return res;
3645 /**
3646  * Break up a string into substrings delimited the characters
3647  * in delimiters.  Null-length substrings are ignored
3648  */  
3649 std::vector<String> MakeBase::tokenize(const String &str,
3650                                 const String &delimiters)
3653     std::vector<String> res;
3654     char *del = (char *)delimiters.c_str();
3655     String dmp;
3656     for (unsigned int i=0 ; i<str.size() ; i++)
3657         {
3658         char ch = str[i];
3659         char *p = (char *)0;
3660         for (p=del ; *p ; p++)
3661             if (*p == ch)
3662                 break;
3663         if (*p)
3664             {
3665             if (dmp.size() > 0)
3666                 {
3667                 res.push_back(dmp);
3668                 dmp.clear();
3669                 }
3670             }
3671         else
3672             {
3673             dmp.push_back(ch);
3674             }
3675         }
3676     //Add tail
3677     if (dmp.size() > 0)
3678         {
3679         res.push_back(dmp);
3680         dmp.clear();
3681         }
3683     return res;
3688 /**
3689  *  replace runs of whitespace with a single space
3690  */
3691 String MakeBase::strip(const String &s)
3693     int len = s.size();
3694     String stripped;
3695     for (int i = 0 ; i<len ; i++)
3696         {
3697         char ch = s[i];
3698         if (isspace(ch))
3699             {
3700             stripped.push_back(' ');
3701             for ( ; i<len ; i++)
3702                 {
3703                 ch = s[i];
3704                 if (!isspace(ch))
3705                     {
3706                     stripped.push_back(ch);
3707                     break;
3708                     }
3709                 }
3710             }
3711         else
3712             {
3713             stripped.push_back(ch);
3714             }
3715         }
3716     return stripped;
3719 /**
3720  *  remove leading whitespace from each line
3721  */
3722 String MakeBase::leftJustify(const String &s)
3724     String out;
3725     int len = s.size();
3726     for (int i = 0 ; i<len ; )
3727         {
3728         char ch;
3729         //Skip to first visible character
3730         while (i<len)
3731             {
3732             ch = s[i];
3733             if (ch == '\n' || ch == '\r'
3734               || !isspace(ch))
3735                   break;
3736             i++;
3737             }
3738         //Copy the rest of the line
3739         while (i<len)
3740             {
3741             ch = s[i];
3742             if (ch == '\n' || ch == '\r')
3743                 {
3744                 if (ch != '\r')
3745                     out.push_back('\n');
3746                 i++;
3747                 break;
3748                 }
3749             else
3750                 {
3751                 out.push_back(ch);
3752                 }
3753             i++;
3754             }
3755         }
3756     return out;
3760 /**
3761  *  Removes whitespace from beginning and end of a string
3762  */
3763 String MakeBase::trim(const String &s)
3765     if (s.size() < 1)
3766         return s;
3767     
3768     //Find first non-ws char
3769     unsigned int begin = 0;
3770     for ( ; begin < s.size() ; begin++)
3771         {
3772         if (!isspace(s[begin]))
3773             break;
3774         }
3776     //Find first non-ws char, going in reverse
3777     unsigned int end = s.size() - 1;
3778     for ( ; end > begin ; end--)
3779         {
3780         if (!isspace(s[end]))
3781             break;
3782         }
3783     //trace("begin:%d  end:%d", begin, end);
3785     String res = s.substr(begin, end-begin+1);
3786     return res;
3790 /**
3791  *  Return a lower case version of the given string
3792  */
3793 String MakeBase::toLower(const String &s)
3795     if (s.size()==0)
3796         return s;
3798     String ret;
3799     for(unsigned int i=0; i<s.size() ; i++)
3800         {
3801         ret.push_back(tolower(s[i]));
3802         }
3803     return ret;
3807 /**
3808  * Return the native format of the canonical
3809  * path which we store
3810  */
3811 String MakeBase::getNativePath(const String &path)
3813 #ifdef __WIN32__
3814     String npath;
3815     unsigned int firstChar = 0;
3816     if (path.size() >= 3)
3817         {
3818         if (path[0] == '/' &&
3819             isalpha(path[1]) &&
3820             path[2] == ':')
3821             firstChar++;
3822         }
3823     for (unsigned int i=firstChar ; i<path.size() ; i++)
3824         {
3825         char ch = path[i];
3826         if (ch == '/')
3827             npath.push_back('\\');
3828         else
3829             npath.push_back(ch);
3830         }
3831     return npath;
3832 #else
3833     return path;
3834 #endif
3838 #ifdef __WIN32__
3839 #include <tchar.h>
3841 static String win32LastError()
3844     DWORD dw = GetLastError(); 
3846     LPVOID str;
3847     FormatMessage(
3848         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3849         FORMAT_MESSAGE_FROM_SYSTEM,
3850         NULL,
3851         dw,
3852         0,
3853         (LPTSTR) &str,
3854         0, NULL );
3855     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3856     if(p != NULL)
3857         { // lose CRLF
3858         *p = _T('\0');
3859         }
3860     String ret = (char *)str;
3861     LocalFree(str);
3863     return ret;
3865 #endif
3870 #ifdef __WIN32__
3872 /**
3873  * Execute a system call, using pipes to send data to the
3874  * program's stdin,  and reading stdout and stderr.
3875  */
3876 bool MakeBase::executeCommand(const String &command,
3877                               const String &inbuf,
3878                               String &outbuf,
3879                               String &errbuf)
3882     status("============ cmd ============\n%s\n=============================",
3883                 command.c_str());
3885     outbuf.clear();
3886     errbuf.clear();
3887     
3889     /*
3890     I really hate having win32 code in this program, but the
3891     read buffer in command.com and cmd.exe are just too small
3892     for the large commands we need for compiling and linking.
3893     */
3895     bool ret = true;
3897     //# Allocate a separate buffer for safety
3898     char *paramBuf = new char[command.size() + 1];
3899     if (!paramBuf)
3900        {
3901        error("executeCommand cannot allocate command buffer");
3902        return false;
3903        }
3904     strcpy(paramBuf, (char *)command.c_str());
3906     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3907     //# to see how Win32 pipes work
3909     //# Create pipes
3910     SECURITY_ATTRIBUTES saAttr; 
3911     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3912     saAttr.bInheritHandle = TRUE; 
3913     saAttr.lpSecurityDescriptor = NULL; 
3914     HANDLE stdinRead,  stdinWrite;
3915     HANDLE stdoutRead, stdoutWrite;
3916     HANDLE stderrRead, stderrWrite;
3917     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3918         {
3919         error("executeProgram: could not create pipe");
3920         delete[] paramBuf;
3921         return false;
3922         } 
3923     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3924     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3925         {
3926         error("executeProgram: could not create pipe");
3927         delete[] paramBuf;
3928         return false;
3929         } 
3930     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3931     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3932         {
3933         error("executeProgram: could not create pipe");
3934         delete[] paramBuf;
3935         return false;
3936         } 
3937     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3939     // Create the process
3940     STARTUPINFO siStartupInfo;
3941     PROCESS_INFORMATION piProcessInfo;
3942     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3943     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3944     siStartupInfo.cb = sizeof(siStartupInfo);
3945     siStartupInfo.hStdError   =  stderrWrite;
3946     siStartupInfo.hStdOutput  =  stdoutWrite;
3947     siStartupInfo.hStdInput   =  stdinRead;
3948     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3949    
3950     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3951                 0, NULL, NULL, &siStartupInfo,
3952                 &piProcessInfo))
3953         {
3954         error("executeCommand : could not create process : %s",
3955                     win32LastError().c_str());
3956         ret = false;
3957         }
3959     delete[] paramBuf;
3961     DWORD bytesWritten;
3962     if (inbuf.size()>0 &&
3963         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3964                &bytesWritten, NULL))
3965         {
3966         error("executeCommand: could not write to pipe");
3967         return false;
3968         }    
3969     if (!CloseHandle(stdinWrite))
3970         {          
3971         error("executeCommand: could not close write pipe");
3972         return false;
3973         }
3974     if (!CloseHandle(stdoutWrite))
3975         {
3976         error("executeCommand: could not close read pipe");
3977         return false;
3978         }
3979     if (!CloseHandle(stderrWrite))
3980         {
3981         error("executeCommand: could not close read pipe");
3982         return false;
3983         }
3985     bool lastLoop = false;
3986     while (true)
3987         {
3988         DWORD avail;
3989         DWORD bytesRead;
3990         char readBuf[4096];
3992         //trace("## stderr");
3993         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3994         if (avail > 0)
3995             {
3996             bytesRead = 0;
3997             if (avail>4096) avail = 4096;
3998             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3999             if (bytesRead > 0)
4000                 {
4001                 for (unsigned int i=0 ; i<bytesRead ; i++)
4002                     errbuf.push_back(readBuf[i]);
4003                 }
4004             }
4006         //trace("## stdout");
4007         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4008         if (avail > 0)
4009             {
4010             bytesRead = 0;
4011             if (avail>4096) avail = 4096;
4012             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4013             if (bytesRead > 0)
4014                 {
4015                 for (unsigned int i=0 ; i<bytesRead ; i++)
4016                     outbuf.push_back(readBuf[i]);
4017                 }
4018             }
4019             
4020         //Was this the final check after program done?
4021         if (lastLoop)
4022             break;
4024         DWORD exitCode;
4025         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4026         if (exitCode != STILL_ACTIVE)
4027             lastLoop = true;
4029         Sleep(10);
4030         }    
4031     //trace("outbuf:%s", outbuf.c_str());
4032     if (!CloseHandle(stdoutRead))
4033         {
4034         error("executeCommand: could not close read pipe");
4035         return false;
4036         }
4037     if (!CloseHandle(stderrRead))
4038         {
4039         error("executeCommand: could not close read pipe");
4040         return false;
4041         }
4043     DWORD exitCode;
4044     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4045     //trace("exit code:%d", exitCode);
4046     if (exitCode != 0)
4047         {
4048         ret = false;
4049         }
4050     
4051     CloseHandle(piProcessInfo.hProcess);
4052     CloseHandle(piProcessInfo.hThread);
4054     return ret;
4056
4058 #else  /*do it unix style*/
4060 #include <sys/wait.h>
4062 /**
4063  * Execute a system call, using pipes to send data to the
4064  * program's stdin,  and reading stdout and stderr.
4065  */
4066 bool MakeBase::executeCommand(const String &command,
4067                               const String &inbuf,
4068                               String &outbuf,
4069                               String &errbuf)
4072     status("============ cmd ============\n%s\n=============================",
4073                 command.c_str());
4075     outbuf.clear();
4076     errbuf.clear();
4077     
4079     int outfds[2];
4080     if (pipe(outfds) < 0)
4081         return false;
4082     int errfds[2];
4083     if (pipe(errfds) < 0)
4084         return false;
4085     int pid = fork();
4086     if (pid < 0)
4087         {
4088         close(outfds[0]);
4089         close(outfds[1]);
4090         close(errfds[0]);
4091         close(errfds[1]);
4092         error("launch of command '%s' failed : %s",
4093              command.c_str(), strerror(errno));
4094         return false;
4095         }
4096     else if (pid > 0) // parent
4097         {
4098         close(outfds[1]);
4099         close(errfds[1]);
4100         }
4101     else // == 0, child
4102         {
4103         close(outfds[0]);
4104         dup2(outfds[1], STDOUT_FILENO);
4105         close(outfds[1]);
4106         close(errfds[0]);
4107         dup2(errfds[1], STDERR_FILENO);
4108         close(errfds[1]);
4110         char *args[4];
4111         args[0] = (char *)"sh";
4112         args[1] = (char *)"-c";
4113         args[2] = (char *)command.c_str();
4114         args[3] = NULL;
4115         execv("/bin/sh", args);
4116         _exit(EXIT_FAILURE);
4117         }
4119     String outb;
4120     String errb;
4122     FILE *stdOutRead = fdopen(outfds[0], "r");
4123     while (!feof(stdOutRead))
4124         {
4125         char ch = fgetc(stdOutRead);
4126         if (ch<0)
4127             break;
4128         outb.push_back(ch);
4129         }
4130     FILE *stdErrRead = fdopen(errfds[0], "r");
4131     while (!feof(stdErrRead))
4132         {
4133         char ch = fgetc(stdErrRead);
4134         if (ch<0)
4135             break;
4136         errb.push_back(ch);
4137         }
4139     int childReturnValue;
4140     wait(&childReturnValue);
4142     fclose(stdOutRead);
4143     fclose(stdErrRead);
4145     outbuf = outb;
4146     errbuf = errb;
4148     if (childReturnValue != 0)
4149         {
4150         error("exec of command '%s' failed : %s",
4151              command.c_str(), strerror(childReturnValue));
4152         return false;
4153         }
4155     return true;
4156
4158 #endif
4163 bool MakeBase::listDirectories(const String &baseName,
4164                               const String &dirName,
4165                               std::vector<String> &res)
4167     res.push_back(dirName);
4168     String fullPath = baseName;
4169     if (dirName.size()>0)
4170         {
4171         fullPath.append("/");
4172         fullPath.append(dirName);
4173         }
4174     DIR *dir = opendir(fullPath.c_str());
4175     while (true)
4176         {
4177         struct dirent *de = readdir(dir);
4178         if (!de)
4179             break;
4181         //Get the directory member name
4182         String s = de->d_name;
4183         if (s.size() == 0 || s[0] == '.')
4184             continue;
4185         String childName = dirName;
4186         childName.append("/");
4187         childName.append(s);
4189         String fullChildPath = baseName;
4190         fullChildPath.append("/");
4191         fullChildPath.append(childName);
4192         struct stat finfo;
4193         String childNative = getNativePath(fullChildPath);
4194         if (stat(childNative.c_str(), &finfo)<0)
4195             {
4196             error("cannot stat file:%s", childNative.c_str());
4197             }
4198         else if (S_ISDIR(finfo.st_mode))
4199             {
4200             //trace("directory: %s", childName.c_str());
4201             if (!listDirectories(baseName, childName, res))
4202                 return false;
4203             }
4204         }
4205     closedir(dir);
4207     return true;
4211 bool MakeBase::listFiles(const String &baseDir,
4212                          const String &dirName,
4213                          std::vector<String> &res)
4215     String fullDir = baseDir;
4216     if (dirName.size()>0)
4217         {
4218         fullDir.append("/");
4219         fullDir.append(dirName);
4220         }
4221     String dirNative = getNativePath(fullDir);
4223     std::vector<String> subdirs;
4224     DIR *dir = opendir(dirNative.c_str());
4225     if (!dir)
4226         {
4227         error("Could not open directory %s : %s",
4228               dirNative.c_str(), strerror(errno));
4229         return false;
4230         }
4231     while (true)
4232         {
4233         struct dirent *de = readdir(dir);
4234         if (!de)
4235             break;
4237         //Get the directory member name
4238         String s = de->d_name;
4239         if (s.size() == 0 || s[0] == '.')
4240             continue;
4241         String childName;
4242         if (dirName.size()>0)
4243             {
4244             childName.append(dirName);
4245             childName.append("/");
4246             }
4247         childName.append(s);
4248         String fullChild = baseDir;
4249         fullChild.append("/");
4250         fullChild.append(childName);
4251         
4252         if (isDirectory(fullChild))
4253             {
4254             //trace("directory: %s", childName.c_str());
4255             if (!listFiles(baseDir, childName, res))
4256                 return false;
4257             continue;
4258             }
4259         else if (!isRegularFile(fullChild))
4260             {
4261             error("unknown file:%s", childName.c_str());
4262             return false;
4263             }
4265        //all done!
4266         res.push_back(childName);
4268         }
4269     closedir(dir);
4271     return true;
4275 /**
4276  * Several different classes extend MakeBase.  By "propRef", we mean
4277  * the one holding the properties.  Likely "Make" itself
4278  */
4279 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4281     //before doing the list,  resolve any property references
4282     //that might have been specified in the directory name, such as ${src}
4283     String fsDir = fileSet.getDirectory();
4284     String dir;
4285     if (!propRef.getSubstitutions(fsDir, dir))
4286         return false;
4287     String baseDir = propRef.resolve(dir);
4288     std::vector<String> fileList;
4289     if (!listFiles(baseDir, "", fileList))
4290         return false;
4292     std::vector<String> includes = fileSet.getIncludes();
4293     std::vector<String> excludes = fileSet.getExcludes();
4295     std::vector<String> incs;
4296     std::vector<String>::iterator iter;
4298     std::sort(fileList.begin(), fileList.end());
4300     //If there are <includes>, then add files to the output
4301     //in the order of the include list
4302     if (includes.size()==0)
4303         incs = fileList;
4304     else
4305         {
4306         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4307             {
4308             String &pattern = *iter;
4309             std::vector<String>::iterator siter;
4310             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4311                 {
4312                 String s = *siter;
4313                 if (regexMatch(s, pattern))
4314                     {
4315                     //trace("INCLUDED:%s", s.c_str());
4316                     incs.push_back(s);
4317                     }
4318                 }
4319             }
4320         }
4322     //Now trim off the <excludes>
4323     std::vector<String> res;
4324     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4325         {
4326         String s = *iter;
4327         bool skipme = false;
4328         std::vector<String>::iterator siter;
4329         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4330             {
4331             String &pattern = *siter;
4332             if (regexMatch(s, pattern))
4333                 {
4334                 //trace("EXCLUDED:%s", s.c_str());
4335                 skipme = true;
4336                 break;
4337                 }
4338             }
4339         if (!skipme)
4340             res.push_back(s);
4341         }
4342         
4343     fileSet.setFiles(res);
4345     return true;
4349 /**
4350  * 0 == all, 1 = cflags, 2 = libs
4351  */ 
4352 bool MakeBase::pkgConfigRecursive(const String packageName,
4353                                   const String &path, 
4354                                   const String &prefix, 
4355                                   int query,
4356                                   String &result,
4357                                   std::set<String> &deplist) 
4359     PkgConfig pkgConfig;
4360     if (path.size() > 0)
4361         pkgConfig.setPath(path);
4362     if (prefix.size() > 0)
4363         pkgConfig.setPrefix(prefix);
4364     if (!pkgConfig.query(packageName))
4365         return false;
4366     if (query == 0)
4367         result = pkgConfig.getAll();
4368     else if (query == 1)
4369         result = pkgConfig.getCflags();
4370     else
4371         result = pkgConfig.getLibs();
4372     deplist.insert(packageName);
4373     std::vector<String> list = pkgConfig.getRequireList();
4374     for (unsigned int i = 0 ; i<list.size() ; i++)
4375         {
4376         String depPkgName = list[i];
4377         if (deplist.find(depPkgName) != deplist.end())
4378             continue;
4379         String val;
4380         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4381             {
4382             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4383             return false;
4384             }
4385         result.append(" ");
4386         result.append(val);
4387         }
4389     return true;
4392 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4394     std::set<String> deplist;
4395     String path = getProperty("pkg-config-path");
4396     if (path.size()>0)
4397         path = resolve(path);
4398     String prefix = getProperty("pkg-config-prefix");
4399     String val;
4400     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4401         return false;
4402     result = val;
4403     return true;
4408 /**
4409  * replace a variable ref like ${a} with a value
4410  */
4411 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4413     String varname = propertyName;
4414     if (envPrefix.size() > 0 &&
4415         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4416         {
4417         varname = varname.substr(envPrefix.size());
4418         char *envstr = getenv(varname.c_str());
4419         if (!envstr)
4420             {
4421             error("environment variable '%s' not defined", varname.c_str());
4422             return false;
4423             }
4424         result = envstr;
4425         }
4426     else if (pcPrefix.size() > 0 &&
4427         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4428         {
4429         varname = varname.substr(pcPrefix.size());
4430         String val;
4431         if (!pkgConfigQuery(varname, 0, val))
4432             return false;
4433         result = val;
4434         }
4435     else if (pccPrefix.size() > 0 &&
4436         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4437         {
4438         varname = varname.substr(pccPrefix.size());
4439         String val;
4440         if (!pkgConfigQuery(varname, 1, val))
4441             return false;
4442         result = val;
4443         }
4444     else if (pclPrefix.size() > 0 &&
4445         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4446         {
4447         varname = varname.substr(pclPrefix.size());
4448         String val;
4449         if (!pkgConfigQuery(varname, 2, val))
4450             return false;
4451         result = val;
4452         }
4453     else
4454         {
4455         std::map<String, String>::iterator iter;
4456         iter = properties.find(varname);
4457         if (iter != properties.end())
4458             {
4459             result = iter->second;
4460             }
4461         else
4462             {
4463             error("property '%s' not found", varname.c_str());
4464             return false;
4465             }
4466         }
4467     return true;
4473 /**
4474  * Analyse a string, looking for any substitutions or other
4475  * things that need resolution 
4476  */
4477 bool MakeBase::getSubstitutionsRecursive(const String &str,
4478                                          String &result, int depth)
4480     if (depth > 10)
4481         {
4482         error("nesting of substitutions too deep (>10) for '%s'",
4483                         str.c_str());
4484         return false;
4485         }
4486     String s = trim(str);
4487     int len = (int)s.size();
4488     String val;
4489     for (int i=0 ; i<len ; i++)
4490         {
4491         char ch = s[i];
4492         if (ch == '$' && s[i+1] == '{')
4493             {
4494             String varname;
4495             int j = i+2;
4496             for ( ; j<len ; j++)
4497                 {
4498                 ch = s[j];
4499                 if (ch == '$' && s[j+1] == '{')
4500                     {
4501                     error("attribute %s cannot have nested variable references",
4502                            s.c_str());
4503                     return false;
4504                     }
4505                 else if (ch == '}')
4506                     {
4507                     varname = trim(varname);
4508                     String varval;
4509                     if (!lookupProperty(varname, varval))
4510                         return false;
4511                     String varval2;
4512                     //Now see if the answer has ${} in it, too
4513                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4514                         return false;
4515                     val.append(varval2);
4516                     break;
4517                     }
4518                 else
4519                     {
4520                     varname.push_back(ch);
4521                     }
4522                 }
4523             i = j;
4524             }
4525         else
4526             {
4527             val.push_back(ch);
4528             }
4529         }
4530     result = val;
4531     return true;
4534 /**
4535  * Analyse a string, looking for any substitutions or other
4536  * things that need resilution 
4537  */
4538 bool MakeBase::getSubstitutions(const String &str, String &result)
4540     return getSubstitutionsRecursive(str, result, 0);
4545 /**
4546  * replace variable refs like ${a} with their values
4547  * Assume that the string has already been syntax validated
4548  */
4549 String MakeBase::eval(const String &s, const String &defaultVal)
4551     if (s.size()==0)
4552         return defaultVal;
4553     String ret;
4554     if (getSubstitutions(s, ret))
4555         return ret;
4556     else
4557         return defaultVal;
4561 /**
4562  * replace variable refs like ${a} with their values
4563  * return true or false
4564  * Assume that the string has already been syntax validated
4565  */
4566 bool MakeBase::evalBool(const String &s, bool defaultVal)
4568     if (s.size()==0)
4569         return defaultVal;
4570     String val = eval(s, "false");
4571     if (val.size()==0)
4572         return defaultVal;
4573     if (val == "true" || val == "TRUE")
4574         return true;
4575     else
4576         return false;
4580 /**
4581  * Get a string attribute, testing it for proper syntax and
4582  * property names.
4583  */
4584 bool MakeBase::getAttribute(Element *elem, const String &name,
4585                                     String &result)
4587     String s = elem->getAttribute(name);
4588     String tmp;
4589     bool ret = getSubstitutions(s, tmp);
4590     if (ret)
4591         result = s;  //assign -if- ok
4592     return ret;
4596 /**
4597  * Get a string value, testing it for proper syntax and
4598  * property names.
4599  */
4600 bool MakeBase::getValue(Element *elem, String &result)
4602     String s = elem->getValue();
4603     String tmp;
4604     bool ret = getSubstitutions(s, tmp);
4605     if (ret)
4606         result = s;  //assign -if- ok
4607     return ret;
4613 /**
4614  * Parse a <patternset> entry
4615  */  
4616 bool MakeBase::parsePatternSet(Element *elem,
4617                           MakeBase &propRef,
4618                           std::vector<String> &includes,
4619                           std::vector<String> &excludes
4620                           )
4622     std::vector<Element *> children  = elem->getChildren();
4623     for (unsigned int i=0 ; i<children.size() ; i++)
4624         {
4625         Element *child = children[i];
4626         String tagName = child->getName();
4627         if (tagName == "exclude")
4628             {
4629             String fname;
4630             if (!propRef.getAttribute(child, "name", fname))
4631                 return false;
4632             //trace("EXCLUDE: %s", fname.c_str());
4633             excludes.push_back(fname);
4634             }
4635         else if (tagName == "include")
4636             {
4637             String fname;
4638             if (!propRef.getAttribute(child, "name", fname))
4639                 return false;
4640             //trace("INCLUDE: %s", fname.c_str());
4641             includes.push_back(fname);
4642             }
4643         }
4645     return true;
4651 /**
4652  * Parse a <fileset> entry, and determine which files
4653  * should be included
4654  */  
4655 bool MakeBase::parseFileSet(Element *elem,
4656                           MakeBase &propRef,
4657                           FileSet &fileSet)
4659     String name = elem->getName();
4660     if (name != "fileset")
4661         {
4662         error("expected <fileset>");
4663         return false;
4664         }
4667     std::vector<String> includes;
4668     std::vector<String> excludes;
4670     //A fileset has one implied patternset
4671     if (!parsePatternSet(elem, propRef, includes, excludes))
4672         {
4673         return false;
4674         }
4675     //Look for child tags, including more patternsets
4676     std::vector<Element *> children  = elem->getChildren();
4677     for (unsigned int i=0 ; i<children.size() ; i++)
4678         {
4679         Element *child = children[i];
4680         String tagName = child->getName();
4681         if (tagName == "patternset")
4682             {
4683             if (!parsePatternSet(child, propRef, includes, excludes))
4684                 {
4685                 return false;
4686                 }
4687             }
4688         }
4690     String dir;
4691     //Now do the stuff
4692     //Get the base directory for reading file names
4693     if (!propRef.getAttribute(elem, "dir", dir))
4694         return false;
4696     fileSet.setDirectory(dir);
4697     fileSet.setIncludes(includes);
4698     fileSet.setExcludes(excludes);
4699     
4700     /*
4701     std::vector<String> fileList;
4702     if (dir.size() > 0)
4703         {
4704         String baseDir = propRef.resolve(dir);
4705         if (!listFiles(baseDir, "", includes, excludes, fileList))
4706             return false;
4707         }
4708     std::sort(fileList.begin(), fileList.end());
4709     result = fileList;
4710     */
4712     
4713     /*
4714     for (unsigned int i=0 ; i<result.size() ; i++)
4715         {
4716         trace("RES:%s", result[i].c_str());
4717         }
4718     */
4720     
4721     return true;
4724 /**
4725  * Parse a <filelist> entry.  This is far simpler than FileSet,
4726  * since no directory scanning is needed.  The file names are listed
4727  * explicitly.
4728  */  
4729 bool MakeBase::parseFileList(Element *elem,
4730                           MakeBase &propRef,
4731                           FileList &fileList)
4733     std::vector<String> fnames;
4734     //Look for child tags, namely "file"
4735     std::vector<Element *> children  = elem->getChildren();
4736     for (unsigned int i=0 ; i<children.size() ; i++)
4737         {
4738         Element *child = children[i];
4739         String tagName = child->getName();
4740         if (tagName == "file")
4741             {
4742             String fname = child->getAttribute("name");
4743             if (fname.size()==0)
4744                 {
4745                 error("<file> element requires name="" attribute");
4746                 return false;
4747                 }
4748             fnames.push_back(fname);
4749             }
4750         else
4751             {
4752             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4753             return false;
4754             }
4755         }
4757     String dir;
4758     //Get the base directory for reading file names
4759     if (!propRef.getAttribute(elem, "dir", dir))
4760         return false;
4761     fileList.setDirectory(dir);
4762     fileList.setFiles(fnames);
4764     return true;
4769 /**
4770  * Create a directory, making intermediate dirs
4771  * if necessary
4772  */                  
4773 bool MakeBase::createDirectory(const String &dirname)
4775     //trace("## createDirectory: %s", dirname.c_str());
4776     //## first check if it exists
4777     struct stat finfo;
4778     String nativeDir = getNativePath(dirname);
4779     char *cnative = (char *) nativeDir.c_str();
4780 #ifdef __WIN32__
4781     if (strlen(cnative)==2 && cnative[1]==':')
4782         return true;
4783 #endif
4784     if (stat(cnative, &finfo)==0)
4785         {
4786         if (!S_ISDIR(finfo.st_mode))
4787             {
4788             error("mkdir: file %s exists but is not a directory",
4789                   cnative);
4790             return false;
4791             }
4792         else //exists
4793             {
4794             return true;
4795             }
4796         }
4798     //## 2: pull off the last path segment, if any,
4799     //## to make the dir 'above' this one, if necessary
4800     unsigned int pos = dirname.find_last_of('/');
4801     if (pos>0 && pos != dirname.npos)
4802         {
4803         String subpath = dirname.substr(0, pos);
4804         //A letter root (c:) ?
4805         if (!createDirectory(subpath))
4806             return false;
4807         }
4808         
4809     //## 3: now make
4810 #ifdef __WIN32__
4811     if (mkdir(cnative)<0)
4812 #else
4813     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4814 #endif
4815         {
4816         error("cannot make directory '%s' : %s",
4817                  cnative, strerror(errno));
4818         return false;
4819         }
4820         
4821     return true;
4825 /**
4826  * Remove a directory recursively
4827  */ 
4828 bool MakeBase::removeDirectory(const String &dirName)
4830     char *dname = (char *)dirName.c_str();
4832     DIR *dir = opendir(dname);
4833     if (!dir)
4834         {
4835         //# Let this fail nicely.
4836         return true;
4837         //error("error opening directory %s : %s", dname, strerror(errno));
4838         //return false;
4839         }
4840     
4841     while (true)
4842         {
4843         struct dirent *de = readdir(dir);
4844         if (!de)
4845             break;
4847         //Get the directory member name
4848         String s = de->d_name;
4849         if (s.size() == 0 || s[0] == '.')
4850             continue;
4851         String childName;
4852         if (dirName.size() > 0)
4853             {
4854             childName.append(dirName);
4855             childName.append("/");
4856             }
4857         childName.append(s);
4860         struct stat finfo;
4861         String childNative = getNativePath(childName);
4862         char *cnative = (char *)childNative.c_str();
4863         if (stat(cnative, &finfo)<0)
4864             {
4865             error("cannot stat file:%s", cnative);
4866             }
4867         else if (S_ISDIR(finfo.st_mode))
4868             {
4869             //trace("DEL dir: %s", childName.c_str());
4870             if (!removeDirectory(childName))
4871                 {
4872                 return false;
4873                 }
4874             }
4875         else if (!S_ISREG(finfo.st_mode))
4876             {
4877             //trace("not regular: %s", cnative);
4878             }
4879         else
4880             {
4881             //trace("DEL file: %s", childName.c_str());
4882             if (remove(cnative)<0)
4883                 {
4884                 error("error deleting %s : %s",
4885                      cnative, strerror(errno));
4886                 return false;
4887                 }
4888             }
4889         }
4890     closedir(dir);
4892     //Now delete the directory
4893     String native = getNativePath(dirName);
4894     if (rmdir(native.c_str())<0)
4895         {
4896         error("could not delete directory %s : %s",
4897             native.c_str() , strerror(errno));
4898         return false;
4899         }
4901     return true;
4902     
4906 /**
4907  * Copy a file from one name to another. Perform only if needed
4908  */ 
4909 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4911     //# 1 Check up-to-date times
4912     String srcNative = getNativePath(srcFile);
4913     struct stat srcinfo;
4914     if (stat(srcNative.c_str(), &srcinfo)<0)
4915         {
4916         error("source file %s for copy does not exist",
4917                  srcNative.c_str());
4918         return false;
4919         }
4921     String destNative = getNativePath(destFile);
4922     struct stat destinfo;
4923     if (stat(destNative.c_str(), &destinfo)==0)
4924         {
4925         if (destinfo.st_mtime >= srcinfo.st_mtime)
4926             return true;
4927         }
4928         
4929     //# 2 prepare a destination directory if necessary
4930     unsigned int pos = destFile.find_last_of('/');
4931     if (pos != destFile.npos)
4932         {
4933         String subpath = destFile.substr(0, pos);
4934         if (!createDirectory(subpath))
4935             return false;
4936         }
4938     //# 3 do the data copy
4939 #ifndef __WIN32__
4941     FILE *srcf = fopen(srcNative.c_str(), "rb");
4942     if (!srcf)
4943         {
4944         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4945         return false;
4946         }
4947     FILE *destf = fopen(destNative.c_str(), "wb");
4948     if (!destf)
4949         {
4950         error("copyFile cannot open %s for writing", srcNative.c_str());
4951         return false;
4952         }
4954     while (!feof(srcf))
4955         {
4956         int ch = fgetc(srcf);
4957         if (ch<0)
4958             break;
4959         fputc(ch, destf);
4960         }
4962     fclose(destf);
4963     fclose(srcf);
4965 #else
4966     
4967     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4968         {
4969         error("copyFile from %s to %s failed",
4970              srcNative.c_str(), destNative.c_str());
4971         return false;
4972         }
4973         
4974 #endif /* __WIN32__ */
4977     return true;
4982 /**
4983  * Tests if the file exists and is a regular file
4984  */ 
4985 bool MakeBase::isRegularFile(const String &fileName)
4987     String native = getNativePath(fileName);
4988     struct stat finfo;
4989     
4990     //Exists?
4991     if (stat(native.c_str(), &finfo)<0)
4992         return false;
4995     //check the file mode
4996     if (!S_ISREG(finfo.st_mode))
4997         return false;
4999     return true;
5002 /**
5003  * Tests if the file exists and is a directory
5004  */ 
5005 bool MakeBase::isDirectory(const String &fileName)
5007     String native = getNativePath(fileName);
5008     struct stat finfo;
5009     
5010     //Exists?
5011     if (stat(native.c_str(), &finfo)<0)
5012         return false;
5015     //check the file mode
5016     if (!S_ISDIR(finfo.st_mode))
5017         return false;
5019     return true;
5024 /**
5025  * Tests is the modification of fileA is newer than fileB
5026  */ 
5027 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5029     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5030     String nativeA = getNativePath(fileA);
5031     struct stat infoA;
5032     //IF source does not exist, NOT newer
5033     if (stat(nativeA.c_str(), &infoA)<0)
5034         {
5035         return false;
5036         }
5038     String nativeB = getNativePath(fileB);
5039     struct stat infoB;
5040     //IF dest does not exist, YES, newer
5041     if (stat(nativeB.c_str(), &infoB)<0)
5042         {
5043         return true;
5044         }
5046     //check the actual times
5047     if (infoA.st_mtime > infoB.st_mtime)
5048         {
5049         return true;
5050         }
5052     return false;
5056 //########################################################################
5057 //# P K G    C O N F I G
5058 //########################################################################
5061 /**
5062  * Get a character from the buffer at pos.  If out of range,
5063  * return -1 for safety
5064  */
5065 int PkgConfig::get(int pos)
5067     if (pos>parselen)
5068         return -1;
5069     return parsebuf[pos];
5074 /**
5075  *  Skip over all whitespace characters beginning at pos.  Return
5076  *  the position of the first non-whitespace character.
5077  *  Pkg-config is line-oriented, so check for newline
5078  */
5079 int PkgConfig::skipwhite(int pos)
5081     while (pos < parselen)
5082         {
5083         int ch = get(pos);
5084         if (ch < 0)
5085             break;
5086         if (!isspace(ch))
5087             break;
5088         pos++;
5089         }
5090     return pos;
5094 /**
5095  *  Parse the buffer beginning at pos, for a word.  Fill
5096  *  'ret' with the result.  Return the position after the
5097  *  word.
5098  */
5099 int PkgConfig::getword(int pos, String &ret)
5101     while (pos < parselen)
5102         {
5103         int ch = get(pos);
5104         if (ch < 0)
5105             break;
5106         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5107             break;
5108         ret.push_back((char)ch);
5109         pos++;
5110         }
5111     return pos;
5114 bool PkgConfig::parseRequires()
5116     if (requires.size() == 0)
5117         return true;
5118     parsebuf = (char *)requires.c_str();
5119     parselen = requires.size();
5120     int pos = 0;
5121     while (pos < parselen)
5122         {
5123         pos = skipwhite(pos);
5124         String val;
5125         int pos2 = getword(pos, val);
5126         if (pos2 == pos)
5127             break;
5128         pos = pos2;
5129         //trace("val %s", val.c_str());
5130         requireList.push_back(val);
5131         }
5132     return true;
5136 static int getint(const String str)
5138     char *s = (char *)str.c_str();
5139     char *ends = NULL;
5140     long val = strtol(s, &ends, 10);
5141     if (ends == s)
5142         return 0L;
5143     else
5144         return val;
5147 void PkgConfig::parseVersion()
5149     if (version.size() == 0)
5150         return;
5151     String s1, s2, s3;
5152     unsigned int pos = 0;
5153     unsigned int pos2 = version.find('.', pos);
5154     if (pos2 == version.npos)
5155         {
5156         s1 = version;
5157         }
5158     else
5159         {
5160         s1 = version.substr(pos, pos2-pos);
5161         pos = pos2;
5162         pos++;
5163         if (pos < version.size())
5164             {
5165             pos2 = version.find('.', pos);
5166             if (pos2 == version.npos)
5167                 {
5168                 s2 = version.substr(pos, version.size()-pos);
5169                 }
5170             else
5171                 {
5172                 s2 = version.substr(pos, pos2-pos);
5173                 pos = pos2;
5174                 pos++;
5175                 if (pos < version.size())
5176                     s3 = version.substr(pos, pos2-pos);
5177                 }
5178             }
5179         }
5181     majorVersion = getint(s1);
5182     minorVersion = getint(s2);
5183     microVersion = getint(s3);
5184     //trace("version:%d.%d.%d", majorVersion,
5185     //          minorVersion, microVersion );
5189 bool PkgConfig::parseLine(const String &lineBuf)
5191     parsebuf = (char *)lineBuf.c_str();
5192     parselen = lineBuf.size();
5193     int pos = 0;
5194     
5195     while (pos < parselen)
5196         {
5197         String attrName;
5198         pos = skipwhite(pos);
5199         int ch = get(pos);
5200         if (ch == '#')
5201             {
5202             //comment.  eat the rest of the line
5203             while (pos < parselen)
5204                 {
5205                 ch = get(pos);
5206                 if (ch == '\n' || ch < 0)
5207                     break;
5208                 pos++;
5209                 }
5210             continue;
5211             }
5212         pos = getword(pos, attrName);
5213         if (attrName.size() == 0)
5214             continue;
5215         
5216         pos = skipwhite(pos);
5217         ch = get(pos);
5218         if (ch != ':' && ch != '=')
5219             {
5220             error("expected ':' or '='");
5221             return false;
5222             }
5223         pos++;
5224         pos = skipwhite(pos);
5225         String attrVal;
5226         while (pos < parselen)
5227             {
5228             ch = get(pos);
5229             if (ch == '\n' || ch < 0)
5230                 break;
5231             else if (ch == '$' && get(pos+1) == '{')
5232                 {
5233                 //#  this is a ${substitution}
5234                 pos += 2;
5235                 String subName;
5236                 while (pos < parselen)
5237                     {
5238                     ch = get(pos);
5239                     if (ch < 0)
5240                         {
5241                         error("unterminated substitution");
5242                         return false;
5243                         }
5244                     else if (ch == '}')
5245                         break;
5246                     else
5247                         subName.push_back((char)ch);
5248                     pos++;
5249                     }
5250                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5251                 if (subName == "prefix" && prefix.size()>0)
5252                     {
5253                     attrVal.append(prefix);
5254                     //trace("prefix override:%s", prefix.c_str());
5255                     }
5256                 else
5257                     {
5258                     String subVal = attrs[subName];
5259                     //trace("subVal:%s", subVal.c_str());
5260                     attrVal.append(subVal);
5261                     }
5262                 }
5263             else
5264                 attrVal.push_back((char)ch);
5265             pos++;
5266             }
5268         attrVal = trim(attrVal);
5269         attrs[attrName] = attrVal;
5271         String attrNameL = toLower(attrName);
5273         if (attrNameL == "name")
5274             name = attrVal;
5275         else if (attrNameL == "description")
5276             description = attrVal;
5277         else if (attrNameL == "cflags")
5278             cflags = attrVal;
5279         else if (attrNameL == "libs")
5280             libs = attrVal;
5281         else if (attrNameL == "requires")
5282             requires = attrVal;
5283         else if (attrNameL == "version")
5284             version = attrVal;
5286         //trace("name:'%s'  value:'%s'",
5287         //      attrName.c_str(), attrVal.c_str());
5288         }
5290     return true;
5294 bool PkgConfig::parse(const String &buf)
5296     init();
5298     String line;
5299     int lineNr = 0;
5300     for (unsigned int p=0 ; p<buf.size() ; p++)
5301         {
5302         int ch = buf[p];
5303         if (ch == '\n' || ch == '\r')
5304             {
5305             if (!parseLine(line))
5306                 return false;
5307             line.clear();
5308             lineNr++;
5309             }
5310         else
5311             {
5312             line.push_back(ch);
5313             }
5314         }
5315     if (line.size()>0)
5316         {
5317         if (!parseLine(line))
5318             return false;
5319         }
5321     parseRequires();
5322     parseVersion();
5324     return true;
5330 void PkgConfig::dumpAttrs()
5332     //trace("### PkgConfig attributes for %s", fileName.c_str());
5333     std::map<String, String>::iterator iter;
5334     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5335         {
5336         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5337         }
5341 bool PkgConfig::readFile(const String &fname)
5343     fileName = getNativePath(fname);
5345     FILE *f = fopen(fileName.c_str(), "r");
5346     if (!f)
5347         {
5348         error("cannot open file '%s' for reading", fileName.c_str());
5349         return false;
5350         }
5351     String buf;
5352     while (true)
5353         {
5354         int ch = fgetc(f);
5355         if (ch < 0)
5356             break;
5357         buf.push_back((char)ch);
5358         }
5359     fclose(f);
5361     //trace("####### File:\n%s", buf.c_str());
5362     if (!parse(buf))
5363         {
5364         return false;
5365         }
5367     //dumpAttrs();
5369     return true;
5374 bool PkgConfig::query(const String &pkgName)
5376     name = pkgName;
5378     String fname = path;
5379     fname.append("/");
5380     fname.append(name);
5381     fname.append(".pc");
5383     if (!readFile(fname))
5384         return false;
5385     
5386     return true;
5393 //########################################################################
5394 //# D E P T O O L
5395 //########################################################################
5399 /**
5400  *  Class which holds information for each file.
5401  */
5402 class FileRec
5404 public:
5406     typedef enum
5407         {
5408         UNKNOWN,
5409         CFILE,
5410         HFILE,
5411         OFILE
5412         } FileType;
5414     /**
5415      *  Constructor
5416      */
5417     FileRec()
5418         { init(); type = UNKNOWN; }
5420     /**
5421      *  Copy constructor
5422      */
5423     FileRec(const FileRec &other)
5424         { init(); assign(other); }
5425     /**
5426      *  Constructor
5427      */
5428     FileRec(int typeVal)
5429         { init(); type = typeVal; }
5430     /**
5431      *  Assignment operator
5432      */
5433     FileRec &operator=(const FileRec &other)
5434         { init(); assign(other); return *this; }
5437     /**
5438      *  Destructor
5439      */
5440     ~FileRec()
5441         {}
5443     /**
5444      *  Directory part of the file name
5445      */
5446     String path;
5448     /**
5449      *  Base name, sans directory and suffix
5450      */
5451     String baseName;
5453     /**
5454      *  File extension, such as cpp or h
5455      */
5456     String suffix;
5458     /**
5459      *  Type of file: CFILE, HFILE, OFILE
5460      */
5461     int type;
5463     /**
5464      * Used to list files ref'd by this one
5465      */
5466     std::map<String, FileRec *> files;
5469 private:
5471     void init()
5472         {
5473         }
5475     void assign(const FileRec &other)
5476         {
5477         type     = other.type;
5478         baseName = other.baseName;
5479         suffix   = other.suffix;
5480         files    = other.files;
5481         }
5483 };
5487 /**
5488  *  Simpler dependency record
5489  */
5490 class DepRec
5492 public:
5494     /**
5495      *  Constructor
5496      */
5497     DepRec()
5498         {init();}
5500     /**
5501      *  Copy constructor
5502      */
5503     DepRec(const DepRec &other)
5504         {init(); assign(other);}
5505     /**
5506      *  Constructor
5507      */
5508     DepRec(const String &fname)
5509         {init(); name = fname; }
5510     /**
5511      *  Assignment operator
5512      */
5513     DepRec &operator=(const DepRec &other)
5514         {init(); assign(other); return *this;}
5517     /**
5518      *  Destructor
5519      */
5520     ~DepRec()
5521         {}
5523     /**
5524      *  Directory part of the file name
5525      */
5526     String path;
5528     /**
5529      *  Base name, without the path and suffix
5530      */
5531     String name;
5533     /**
5534      *  Suffix of the source
5535      */
5536     String suffix;
5539     /**
5540      * Used to list files ref'd by this one
5541      */
5542     std::vector<String> files;
5545 private:
5547     void init()
5548         {
5549         }
5551     void assign(const DepRec &other)
5552         {
5553         path     = other.path;
5554         name     = other.name;
5555         suffix   = other.suffix;
5556         files    = other.files; //avoid recursion
5557         }
5559 };
5562 class DepTool : public MakeBase
5564 public:
5566     /**
5567      *  Constructor
5568      */
5569     DepTool()
5570         { init(); }
5572     /**
5573      *  Copy constructor
5574      */
5575     DepTool(const DepTool &other)
5576         { init(); assign(other); }
5578     /**
5579      *  Assignment operator
5580      */
5581     DepTool &operator=(const DepTool &other)
5582         { init(); assign(other); return *this; }
5585     /**
5586      *  Destructor
5587      */
5588     ~DepTool()
5589         {}
5592     /**
5593      *  Reset this section of code
5594      */
5595     virtual void init();
5596     
5597     /**
5598      *  Reset this section of code
5599      */
5600     virtual void assign(const DepTool &other)
5601         {
5602         }
5603     
5604     /**
5605      *  Sets the source directory which will be scanned
5606      */
5607     virtual void setSourceDirectory(const String &val)
5608         { sourceDir = val; }
5610     /**
5611      *  Returns the source directory which will be scanned
5612      */
5613     virtual String getSourceDirectory()
5614         { return sourceDir; }
5616     /**
5617      *  Sets the list of files within the directory to analyze
5618      */
5619     virtual void setFileList(const std::vector<String> &list)
5620         { fileList = list; }
5622     /**
5623      * Creates the list of all file names which will be
5624      * candidates for further processing.  Reads make.exclude
5625      * to see which files for directories to leave out.
5626      */
5627     virtual bool createFileList();
5630     /**
5631      *  Generates the forward dependency list
5632      */
5633     virtual bool generateDependencies();
5636     /**
5637      *  Generates the forward dependency list, saving the file
5638      */
5639     virtual bool generateDependencies(const String &);
5642     /**
5643      *  Load a dependency file
5644      */
5645     std::vector<DepRec> loadDepFile(const String &fileName);
5647     /**
5648      *  Load a dependency file, generating one if necessary
5649      */
5650     std::vector<DepRec> getDepFile(const String &fileName,
5651               bool forceRefresh);
5653     /**
5654      *  Save a dependency file
5655      */
5656     bool saveDepFile(const String &fileName);
5659 private:
5662     /**
5663      *
5664      */
5665     void parseName(const String &fullname,
5666                    String &path,
5667                    String &basename,
5668                    String &suffix);
5670     /**
5671      *
5672      */
5673     int get(int pos);
5675     /**
5676      *
5677      */
5678     int skipwhite(int pos);
5680     /**
5681      *
5682      */
5683     int getword(int pos, String &ret);
5685     /**
5686      *
5687      */
5688     bool sequ(int pos, const char *key);
5690     /**
5691      *
5692      */
5693     bool addIncludeFile(FileRec *frec, const String &fname);
5695     /**
5696      *
5697      */
5698     bool scanFile(const String &fname, FileRec *frec);
5700     /**
5701      *
5702      */
5703     bool processDependency(FileRec *ofile, FileRec *include);
5705     /**
5706      *
5707      */
5708     String sourceDir;
5710     /**
5711      *
5712      */
5713     std::vector<String> fileList;
5715     /**
5716      *
5717      */
5718     std::vector<String> directories;
5720     /**
5721      * A list of all files which will be processed for
5722      * dependencies.
5723      */
5724     std::map<String, FileRec *> allFiles;
5726     /**
5727      * The list of .o files, and the
5728      * dependencies upon them.
5729      */
5730     std::map<String, FileRec *> oFiles;
5732     int depFileSize;
5733     char *depFileBuf;
5735     static const int readBufSize = 8192;
5736     char readBuf[8193];//byte larger
5738 };
5744 /**
5745  *  Clean up after processing.  Called by the destructor, but should
5746  *  also be called before the object is reused.
5747  */
5748 void DepTool::init()
5750     sourceDir = ".";
5752     fileList.clear();
5753     directories.clear();
5754     
5755     //clear output file list
5756     std::map<String, FileRec *>::iterator iter;
5757     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5758         delete iter->second;
5759     oFiles.clear();
5761     //allFiles actually contains the master copies. delete them
5762     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5763         delete iter->second;
5764     allFiles.clear(); 
5771 /**
5772  *  Parse a full path name into path, base name, and suffix
5773  */
5774 void DepTool::parseName(const String &fullname,
5775                         String &path,
5776                         String &basename,
5777                         String &suffix)
5779     if (fullname.size() < 2)
5780         return;
5782     unsigned int pos = fullname.find_last_of('/');
5783     if (pos != fullname.npos && pos<fullname.size()-1)
5784         {
5785         path = fullname.substr(0, pos);
5786         pos++;
5787         basename = fullname.substr(pos, fullname.size()-pos);
5788         }
5789     else
5790         {
5791         path = "";
5792         basename = fullname;
5793         }
5795     pos = basename.find_last_of('.');
5796     if (pos != basename.npos && pos<basename.size()-1)
5797         {
5798         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5799         basename = basename.substr(0, pos);
5800         }
5802     //trace("parsename:%s %s %s", path.c_str(),
5803     //        basename.c_str(), suffix.c_str()); 
5808 /**
5809  *  Generate our internal file list.
5810  */
5811 bool DepTool::createFileList()
5814     for (unsigned int i=0 ; i<fileList.size() ; i++)
5815         {
5816         String fileName = fileList[i];
5817         //trace("## FileName:%s", fileName.c_str());
5818         String path;
5819         String basename;
5820         String sfx;
5821         parseName(fileName, path, basename, sfx);
5822         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5823             sfx == "cc" || sfx == "CC")
5824             {
5825             FileRec *fe         = new FileRec(FileRec::CFILE);
5826             fe->path            = path;
5827             fe->baseName        = basename;
5828             fe->suffix          = sfx;
5829             allFiles[fileName]  = fe;
5830             }
5831         else if (sfx == "h"   ||  sfx == "hh"  ||
5832                  sfx == "hpp" ||  sfx == "hxx")
5833             {
5834             FileRec *fe         = new FileRec(FileRec::HFILE);
5835             fe->path            = path;
5836             fe->baseName        = basename;
5837             fe->suffix          = sfx;
5838             allFiles[fileName]  = fe;
5839             }
5840         }
5842     if (!listDirectories(sourceDir, "", directories))
5843         return false;
5844         
5845     return true;
5852 /**
5853  * Get a character from the buffer at pos.  If out of range,
5854  * return -1 for safety
5855  */
5856 int DepTool::get(int pos)
5858     if (pos>depFileSize)
5859         return -1;
5860     return depFileBuf[pos];
5865 /**
5866  *  Skip over all whitespace characters beginning at pos.  Return
5867  *  the position of the first non-whitespace character.
5868  */
5869 int DepTool::skipwhite(int pos)
5871     while (pos < depFileSize)
5872         {
5873         int ch = get(pos);
5874         if (ch < 0)
5875             break;
5876         if (!isspace(ch))
5877             break;
5878         pos++;
5879         }
5880     return pos;
5884 /**
5885  *  Parse the buffer beginning at pos, for a word.  Fill
5886  *  'ret' with the result.  Return the position after the
5887  *  word.
5888  */
5889 int DepTool::getword(int pos, String &ret)
5891     while (pos < depFileSize)
5892         {
5893         int ch = get(pos);
5894         if (ch < 0)
5895             break;
5896         if (isspace(ch))
5897             break;
5898         ret.push_back((char)ch);
5899         pos++;
5900         }
5901     return pos;
5904 /**
5905  * Return whether the sequence of characters in the buffer
5906  * beginning at pos match the key,  for the length of the key
5907  */
5908 bool DepTool::sequ(int pos, const char *key)
5910     while (*key)
5911         {
5912         if (*key != get(pos))
5913             return false;
5914         key++; pos++;
5915         }
5916     return true;
5921 /**
5922  *  Add an include file name to a file record.  If the name
5923  *  is not found in allFiles explicitly, try prepending include
5924  *  directory names to it and try again.
5925  */
5926 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5928     //# if the name is an exact match to a path name
5929     //# in allFiles, like "myinc.h"
5930     std::map<String, FileRec *>::iterator iter =
5931            allFiles.find(iname);
5932     if (iter != allFiles.end()) //already exists
5933         {
5934          //h file in same dir
5935         FileRec *other = iter->second;
5936         //trace("local: '%s'", iname.c_str());
5937         frec->files[iname] = other;
5938         return true;
5939         }
5940     else 
5941         {
5942         //## Ok, it was not found directly
5943         //look in other dirs
5944         std::vector<String>::iterator diter;
5945         for (diter=directories.begin() ;
5946              diter!=directories.end() ; diter++)
5947             {
5948             String dfname = *diter;
5949             dfname.append("/");
5950             dfname.append(iname);
5951             URI fullPathURI(dfname);  //normalize path name
5952             String fullPath = fullPathURI.getPath();
5953             if (fullPath[0] == '/')
5954                 fullPath = fullPath.substr(1);
5955             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5956             iter = allFiles.find(fullPath);
5957             if (iter != allFiles.end())
5958                 {
5959                 FileRec *other = iter->second;
5960                 //trace("other: '%s'", iname.c_str());
5961                 frec->files[fullPath] = other;
5962                 return true;
5963                 }
5964             }
5965         }
5966     return true;
5971 /**
5972  *  Lightly parse a file to find the #include directives.  Do
5973  *  a bit of state machine stuff to make sure that the directive
5974  *  is valid.  (Like not in a comment).
5975  */
5976 bool DepTool::scanFile(const String &fname, FileRec *frec)
5978     String fileName;
5979     if (sourceDir.size() > 0)
5980         {
5981         fileName.append(sourceDir);
5982         fileName.append("/");
5983         }
5984     fileName.append(fname);
5985     String nativeName = getNativePath(fileName);
5986     FILE *f = fopen(nativeName.c_str(), "r");
5987     if (!f)
5988         {
5989         error("Could not open '%s' for reading", fname.c_str());
5990         return false;
5991         }
5992     String buf;
5993     while (!feof(f))
5994         {
5995         int nrbytes = fread(readBuf, 1, readBufSize, f);
5996         readBuf[nrbytes] = '\0';
5997         buf.append(readBuf);
5998         }
5999     fclose(f);
6001     depFileSize = buf.size();
6002     depFileBuf  = (char *)buf.c_str();
6003     int pos = 0;
6006     while (pos < depFileSize)
6007         {
6008         //trace("p:%c", get(pos));
6010         //# Block comment
6011         if (get(pos) == '/' && get(pos+1) == '*')
6012             {
6013             pos += 2;
6014             while (pos < depFileSize)
6015                 {
6016                 if (get(pos) == '*' && get(pos+1) == '/')
6017                     {
6018                     pos += 2;
6019                     break;
6020                     }
6021                 else
6022                     pos++;
6023                 }
6024             }
6025         //# Line comment
6026         else if (get(pos) == '/' && get(pos+1) == '/')
6027             {
6028             pos += 2;
6029             while (pos < depFileSize)
6030                 {
6031                 if (get(pos) == '\n')
6032                     {
6033                     pos++;
6034                     break;
6035                     }
6036                 else
6037                     pos++;
6038                 }
6039             }
6040         //# #include! yaay
6041         else if (sequ(pos, "#include"))
6042             {
6043             pos += 8;
6044             pos = skipwhite(pos);
6045             String iname;
6046             pos = getword(pos, iname);
6047             if (iname.size()>2)
6048                 {
6049                 iname = iname.substr(1, iname.size()-2);
6050                 addIncludeFile(frec, iname);
6051                 }
6052             }
6053         else
6054             {
6055             pos++;
6056             }
6057         }
6059     return true;
6064 /**
6065  *  Recursively check include lists to find all files in allFiles to which
6066  *  a given file is dependent.
6067  */
6068 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6070     std::map<String, FileRec *>::iterator iter;
6071     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6072         {
6073         String fname  = iter->first;
6074         if (ofile->files.find(fname) != ofile->files.end())
6075             {
6076             //trace("file '%s' already seen", fname.c_str());
6077             continue;
6078             }
6079         FileRec *child  = iter->second;
6080         ofile->files[fname] = child;
6081       
6082         processDependency(ofile, child);
6083         }
6086     return true;
6093 /**
6094  *  Generate the file dependency list.
6095  */
6096 bool DepTool::generateDependencies()
6098     std::map<String, FileRec *>::iterator iter;
6099     //# First pass.  Scan for all includes
6100     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6101         {
6102         FileRec *frec = iter->second;
6103         if (!scanFile(iter->first, frec))
6104             {
6105             //quit?
6106             }
6107         }
6109     //# Second pass.  Scan for all includes
6110     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6111         {
6112         FileRec *include = iter->second;
6113         if (include->type == FileRec::CFILE)
6114             {
6115             //String cFileName   = iter->first;
6116             FileRec *ofile     = new FileRec(FileRec::OFILE);
6117             ofile->path        = include->path;
6118             ofile->baseName    = include->baseName;
6119             ofile->suffix      = include->suffix;
6120             String fname       = include->path;
6121             if (fname.size()>0)
6122                 fname.append("/");
6123             fname.append(include->baseName);
6124             fname.append(".o");
6125             oFiles[fname]    = ofile;
6126             //add the .c file first?   no, don't
6127             //ofile->files[cFileName] = include;
6128             
6129             //trace("ofile:%s", fname.c_str());
6131             processDependency(ofile, include);
6132             }
6133         }
6135       
6136     return true;
6141 /**
6142  *  High-level call to generate deps and optionally save them
6143  */
6144 bool DepTool::generateDependencies(const String &fileName)
6146     if (!createFileList())
6147         return false;
6148     if (!generateDependencies())
6149         return false;
6150     if (!saveDepFile(fileName))
6151         return false;
6152     return true;
6156 /**
6157  *   This saves the dependency cache.
6158  */
6159 bool DepTool::saveDepFile(const String &fileName)
6161     time_t tim;
6162     time(&tim);
6164     FILE *f = fopen(fileName.c_str(), "w");
6165     if (!f)
6166         {
6167         trace("cannot open '%s' for writing", fileName.c_str());
6168         }
6169     fprintf(f, "<?xml version='1.0'?>\n");
6170     fprintf(f, "<!--\n");
6171     fprintf(f, "########################################################\n");
6172     fprintf(f, "## File: build.dep\n");
6173     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6174     fprintf(f, "########################################################\n");
6175     fprintf(f, "-->\n");
6177     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6178     std::map<String, FileRec *>::iterator iter;
6179     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6180         {
6181         FileRec *frec = iter->second;
6182         if (frec->type == FileRec::OFILE)
6183             {
6184             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6185                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6186             std::map<String, FileRec *>::iterator citer;
6187             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6188                 {
6189                 String cfname = citer->first;
6190                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6191                 }
6192             fprintf(f, "</object>\n\n");
6193             }
6194         }
6196     fprintf(f, "</dependencies>\n");
6197     fprintf(f, "\n");
6198     fprintf(f, "<!--\n");
6199     fprintf(f, "########################################################\n");
6200     fprintf(f, "## E N D\n");
6201     fprintf(f, "########################################################\n");
6202     fprintf(f, "-->\n");
6204     fclose(f);
6206     return true;
6212 /**
6213  *   This loads the dependency cache.
6214  */
6215 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6217     std::vector<DepRec> result;
6218     
6219     Parser parser;
6220     Element *root = parser.parseFile(depFile.c_str());
6221     if (!root)
6222         {
6223         //error("Could not open %s for reading", depFile.c_str());
6224         return result;
6225         }
6227     if (root->getChildren().size()==0 ||
6228         root->getChildren()[0]->getName()!="dependencies")
6229         {
6230         error("loadDepFile: main xml element should be <dependencies>");
6231         delete root;
6232         return result;
6233         }
6235     //########## Start parsing
6236     Element *depList = root->getChildren()[0];
6238     std::vector<Element *> objects = depList->getChildren();
6239     for (unsigned int i=0 ; i<objects.size() ; i++)
6240         {
6241         Element *objectElem = objects[i];
6242         String tagName = objectElem->getName();
6243         if (tagName != "object")
6244             {
6245             error("loadDepFile: <dependencies> should have only <object> children");
6246             return result;
6247             }
6249         String objName   = objectElem->getAttribute("name");
6250          //trace("object:%s", objName.c_str());
6251         DepRec depObject(objName);
6252         depObject.path   = objectElem->getAttribute("path");
6253         depObject.suffix = objectElem->getAttribute("suffix");
6254         //########## DESCRIPTION
6255         std::vector<Element *> depElems = objectElem->getChildren();
6256         for (unsigned int i=0 ; i<depElems.size() ; i++)
6257             {
6258             Element *depElem = depElems[i];
6259             tagName = depElem->getName();
6260             if (tagName != "dep")
6261                 {
6262                 error("loadDepFile: <object> should have only <dep> children");
6263                 return result;
6264                 }
6265             String depName = depElem->getAttribute("name");
6266             //trace("    dep:%s", depName.c_str());
6267             depObject.files.push_back(depName);
6268             }
6270         //Insert into the result list, in a sorted manner
6271         bool inserted = false;
6272         std::vector<DepRec>::iterator iter;
6273         for (iter = result.begin() ; iter != result.end() ; iter++)
6274             {
6275             String vpath = iter->path;
6276             vpath.append("/");
6277             vpath.append(iter->name);
6278             String opath = depObject.path;
6279             opath.append("/");
6280             opath.append(depObject.name);
6281             if (vpath > opath)
6282                 {
6283                 inserted = true;
6284                 iter = result.insert(iter, depObject);
6285                 break;
6286                 }
6287             }
6288         if (!inserted)
6289             result.push_back(depObject);
6290         }
6292     delete root;
6294     return result;
6298 /**
6299  *   This loads the dependency cache.
6300  */
6301 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6302                    bool forceRefresh)
6304     std::vector<DepRec> result;
6305     if (forceRefresh)
6306         {
6307         generateDependencies(depFile);
6308         result = loadDepFile(depFile);
6309         }
6310     else
6311         {
6312         //try once
6313         result = loadDepFile(depFile);
6314         if (result.size() == 0)
6315             {
6316             //fail? try again
6317             generateDependencies(depFile);
6318             result = loadDepFile(depFile);
6319             }
6320         }
6321     return result;
6327 //########################################################################
6328 //# T A S K
6329 //########################################################################
6330 //forward decl
6331 class Target;
6332 class Make;
6334 /**
6335  *
6336  */
6337 class Task : public MakeBase
6340 public:
6342     typedef enum
6343         {
6344         TASK_NONE,
6345         TASK_CC,
6346         TASK_COPY,
6347         TASK_DELETE,
6348         TASK_ECHO,
6349         TASK_JAR,
6350         TASK_JAVAC,
6351         TASK_LINK,
6352         TASK_MAKEFILE,
6353         TASK_MKDIR,
6354         TASK_MSGFMT,
6355         TASK_PKG_CONFIG,
6356         TASK_RANLIB,
6357         TASK_RC,
6358         TASK_SHAREDLIB,
6359         TASK_STATICLIB,
6360         TASK_STRIP,
6361         TASK_TOUCH,
6362         TASK_TSTAMP
6363         } TaskType;
6364         
6366     /**
6367      *
6368      */
6369     Task(MakeBase &par) : parent(par)
6370         { init(); }
6372     /**
6373      *
6374      */
6375     Task(const Task &other) : parent(other.parent)
6376         { init(); assign(other); }
6378     /**
6379      *
6380      */
6381     Task &operator=(const Task &other)
6382         { assign(other); return *this; }
6384     /**
6385      *
6386      */
6387     virtual ~Task()
6388         { }
6391     /**
6392      *
6393      */
6394     virtual MakeBase &getParent()
6395         { return parent; }
6397      /**
6398      *
6399      */
6400     virtual int  getType()
6401         { return type; }
6403     /**
6404      *
6405      */
6406     virtual void setType(int val)
6407         { type = val; }
6409     /**
6410      *
6411      */
6412     virtual String getName()
6413         { return name; }
6415     /**
6416      *
6417      */
6418     virtual bool execute()
6419         { return true; }
6421     /**
6422      *
6423      */
6424     virtual bool parse(Element *elem)
6425         { return true; }
6427     /**
6428      *
6429      */
6430     Task *createTask(Element *elem, int lineNr);
6433 protected:
6435     void init()
6436         {
6437         type = TASK_NONE;
6438         name = "none";
6439         }
6441     void assign(const Task &other)
6442         {
6443         type = other.type;
6444         name = other.name;
6445         }
6446         
6447     /**
6448      *  Show task status
6449      */
6450     void taskstatus(const char *fmt, ...)
6451         {
6452         va_list args;
6453         va_start(args,fmt);
6454         fprintf(stdout, "    %s : ", name.c_str());
6455         vfprintf(stdout, fmt, args);
6456         fprintf(stdout, "\n");
6457         va_end(args) ;
6458         }
6460     String getAttribute(Element *elem, const String &attrName)
6461         {
6462         String str;
6463         return str;
6464         }
6466     MakeBase &parent;
6468     int type;
6470     String name;
6471 };
6475 /**
6476  * This task runs the C/C++ compiler.  The compiler is invoked
6477  * for all .c or .cpp files which are newer than their correcsponding
6478  * .o files.  
6479  */
6480 class TaskCC : public Task
6482 public:
6484     TaskCC(MakeBase &par) : Task(par)
6485         {
6486         type = TASK_CC;
6487         name = "cc";
6488         }
6490     virtual ~TaskCC()
6491         {}
6492         
6493     virtual bool isExcludedInc(const String &dirname)
6494         {
6495         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6496             {
6497             String fname = excludeInc[i];
6498             if (fname == dirname)
6499                 return true;
6500             }
6501         return false;
6502         }
6504     virtual bool execute()
6505         {
6506         //evaluate our parameters
6507         String command         = parent.eval(commandOpt, "gcc");
6508         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6509         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6510         String source          = parent.eval(sourceOpt, ".");
6511         String dest            = parent.eval(destOpt, ".");
6512         String flags           = parent.eval(flagsOpt, "");
6513         String defines         = parent.eval(definesOpt, "");
6514         String includes        = parent.eval(includesOpt, "");
6515         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6516         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6518         if (!listFiles(parent, fileSet))
6519             return false;
6520             
6521         FILE *f = NULL;
6522         f = fopen("compile.lst", "w");
6524         //refreshCache is probably false here, unless specified otherwise
6525         String fullName = parent.resolve("build.dep");
6526         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6527             {
6528             taskstatus("regenerating C/C++ dependency cache");
6529             refreshCache = true;
6530             }
6532         DepTool depTool;
6533         depTool.setSourceDirectory(source);
6534         depTool.setFileList(fileSet.getFiles());
6535         std::vector<DepRec> deps =
6536              depTool.getDepFile("build.dep", refreshCache);
6537         
6538         String incs;
6539         incs.append("-I");
6540         incs.append(parent.resolve("."));
6541         incs.append(" ");
6542         if (includes.size()>0)
6543             {
6544             incs.append(includes);
6545             incs.append(" ");
6546             }
6547         std::set<String> paths;
6548         std::vector<DepRec>::iterator viter;
6549         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6550             {
6551             DepRec dep = *viter;
6552             if (dep.path.size()>0)
6553                 paths.insert(dep.path);
6554             }
6555         if (source.size()>0)
6556             {
6557             incs.append(" -I");
6558             incs.append(parent.resolve(source));
6559             incs.append(" ");
6560             }
6561         std::set<String>::iterator setIter;
6562         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6563             {
6564             String dirName = *setIter;
6565             //check excludeInc to see if we dont want to include this dir
6566             if (isExcludedInc(dirName))
6567                 continue;
6568             incs.append(" -I");
6569             String dname;
6570             if (source.size()>0)
6571                 {
6572                 dname.append(source);
6573                 dname.append("/");
6574                 }
6575             dname.append(dirName);
6576             incs.append(parent.resolve(dname));
6577             }
6578             
6579         /**
6580          * Compile each of the C files that need it
6581          */
6582         bool errorOccurred = false;                 
6583         std::vector<String> cfiles;
6584         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6585             {
6586             DepRec dep = *viter;
6588             //## Select command
6589             String sfx = dep.suffix;
6590             String command = ccCommand;
6591             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6592                  sfx == "cc" || sfx == "CC")
6593                 command = cxxCommand;
6594  
6595             //## Make paths
6596             String destPath = dest;
6597             String srcPath  = source;
6598             if (dep.path.size()>0)
6599                 {
6600                 destPath.append("/");
6601                 destPath.append(dep.path);
6602                 srcPath.append("/");
6603                 srcPath.append(dep.path);
6604                 }
6605             //## Make sure destination directory exists
6606             if (!createDirectory(destPath))
6607                 return false;
6608                 
6609             //## Check whether it needs to be done
6610             String destName;
6611             if (destPath.size()>0)
6612                 {
6613                 destName.append(destPath);
6614                 destName.append("/");
6615                 }
6616             destName.append(dep.name);
6617             destName.append(".o");
6618             String destFullName = parent.resolve(destName);
6619             String srcName;
6620             if (srcPath.size()>0)
6621                 {
6622                 srcName.append(srcPath);
6623                 srcName.append("/");
6624                 }
6625             srcName.append(dep.name);
6626             srcName.append(".");
6627             srcName.append(dep.suffix);
6628             String srcFullName = parent.resolve(srcName);
6629             bool compileMe = false;
6630             //# First we check if the source is newer than the .o
6631             if (isNewerThan(srcFullName, destFullName))
6632                 {
6633                 taskstatus("compile of %s required by source: %s",
6634                         destFullName.c_str(), srcFullName.c_str());
6635                 compileMe = true;
6636                 }
6637             else
6638                 {
6639                 //# secondly, we check if any of the included dependencies
6640                 //# of the .c/.cpp is newer than the .o
6641                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6642                     {
6643                     String depName;
6644                     if (source.size()>0)
6645                         {
6646                         depName.append(source);
6647                         depName.append("/");
6648                         }
6649                     depName.append(dep.files[i]);
6650                     String depFullName = parent.resolve(depName);
6651                     bool depRequires = isNewerThan(depFullName, destFullName);
6652                     //trace("%d %s %s\n", depRequires,
6653                     //        destFullName.c_str(), depFullName.c_str());
6654                     if (depRequires)
6655                         {
6656                         taskstatus("compile of %s required by included: %s",
6657                                 destFullName.c_str(), depFullName.c_str());
6658                         compileMe = true;
6659                         break;
6660                         }
6661                     }
6662                 }
6663             if (!compileMe)
6664                 {
6665                 continue;
6666                 }
6668             //## Assemble the command
6669             String cmd = command;
6670             cmd.append(" -c ");
6671             cmd.append(flags);
6672             cmd.append(" ");
6673             cmd.append(defines);
6674             cmd.append(" ");
6675             cmd.append(incs);
6676             cmd.append(" ");
6677             cmd.append(srcFullName);
6678             cmd.append(" -o ");
6679             cmd.append(destFullName);
6681             //## Execute the command
6683             String outString, errString;
6684             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6686             if (f)
6687                 {
6688                 fprintf(f, "########################### File : %s\n",
6689                              srcFullName.c_str());
6690                 fprintf(f, "#### COMMAND ###\n");
6691                 int col = 0;
6692                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6693                     {
6694                     char ch = cmd[i];
6695                     if (isspace(ch)  && col > 63)
6696                         {
6697                         fputc('\n', f);
6698                         col = 0;
6699                         }
6700                     else
6701                         {
6702                         fputc(ch, f);
6703                         col++;
6704                         }
6705                     if (col > 76)
6706                         {
6707                         fputc('\n', f);
6708                         col = 0;
6709                         }
6710                     }
6711                 fprintf(f, "\n");
6712                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6713                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6714                 fflush(f);
6715                 }
6716             if (!ret)
6717                 {
6718                 error("problem compiling: %s", errString.c_str());
6719                 errorOccurred = true;
6720                 }
6721             if (errorOccurred && !continueOnError)
6722                 break;
6723             }
6725         if (f)
6726             {
6727             fclose(f);
6728             }
6729         
6730         return !errorOccurred;
6731         }
6734     virtual bool parse(Element *elem)
6735         {
6736         String s;
6737         if (!parent.getAttribute(elem, "command", commandOpt))
6738             return false;
6739         if (commandOpt.size()>0)
6740             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6741         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6742             return false;
6743         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6744             return false;
6745         if (!parent.getAttribute(elem, "destdir", destOpt))
6746             return false;
6747         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6748             return false;
6749         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6750             return false;
6752         std::vector<Element *> children = elem->getChildren();
6753         for (unsigned int i=0 ; i<children.size() ; i++)
6754             {
6755             Element *child = children[i];
6756             String tagName = child->getName();
6757             if (tagName == "flags")
6758                 {
6759                 if (!parent.getValue(child, flagsOpt))
6760                     return false;
6761                 flagsOpt = strip(flagsOpt);
6762                 }
6763             else if (tagName == "includes")
6764                 {
6765                 if (!parent.getValue(child, includesOpt))
6766                     return false;
6767                 includesOpt = strip(includesOpt);
6768                 }
6769             else if (tagName == "defines")
6770                 {
6771                 if (!parent.getValue(child, definesOpt))
6772                     return false;
6773                 definesOpt = strip(definesOpt);
6774                 }
6775             else if (tagName == "fileset")
6776                 {
6777                 if (!parseFileSet(child, parent, fileSet))
6778                     return false;
6779                 sourceOpt = fileSet.getDirectory();
6780                 }
6781             else if (tagName == "excludeinc")
6782                 {
6783                 if (!parseFileList(child, parent, excludeInc))
6784                     return false;
6785                 }
6786             }
6788         return true;
6789         }
6790         
6791 protected:
6793     String   commandOpt;
6794     String   ccCommandOpt;
6795     String   cxxCommandOpt;
6796     String   sourceOpt;
6797     String   destOpt;
6798     String   flagsOpt;
6799     String   definesOpt;
6800     String   includesOpt;
6801     String   continueOnErrorOpt;
6802     String   refreshCacheOpt;
6803     FileSet  fileSet;
6804     FileList excludeInc;
6805     
6806 };
6810 /**
6811  *
6812  */
6813 class TaskCopy : public Task
6815 public:
6817     typedef enum
6818         {
6819         CP_NONE,
6820         CP_TOFILE,
6821         CP_TODIR
6822         } CopyType;
6824     TaskCopy(MakeBase &par) : Task(par)
6825         {
6826         type        = TASK_COPY;
6827         name        = "copy";
6828         cptype      = CP_NONE;
6829         haveFileSet = false;
6830         }
6832     virtual ~TaskCopy()
6833         {}
6835     virtual bool execute()
6836         {
6837         String fileName   = parent.eval(fileNameOpt   , ".");
6838         String toFileName = parent.eval(toFileNameOpt , ".");
6839         String toDirName  = parent.eval(toDirNameOpt  , ".");
6840         bool   verbose    = parent.evalBool(verboseOpt, false);
6841         switch (cptype)
6842            {
6843            case CP_TOFILE:
6844                {
6845                if (fileName.size()>0)
6846                    {
6847                    taskstatus("%s to %s",
6848                         fileName.c_str(), toFileName.c_str());
6849                    String fullSource = parent.resolve(fileName);
6850                    String fullDest = parent.resolve(toFileName);
6851                    if (verbose)
6852                        taskstatus("copy %s to file %s", fullSource.c_str(),
6853                                           fullDest.c_str());
6854                    if (!isRegularFile(fullSource))
6855                        {
6856                        error("copy : file %s does not exist", fullSource.c_str());
6857                        return false;
6858                        }
6859                    if (!isNewerThan(fullSource, fullDest))
6860                        {
6861                        taskstatus("skipped");
6862                        return true;
6863                        }
6864                    if (!copyFile(fullSource, fullDest))
6865                        return false;
6866                    taskstatus("1 file copied");
6867                    }
6868                return true;
6869                }
6870            case CP_TODIR:
6871                {
6872                if (haveFileSet)
6873                    {
6874                    if (!listFiles(parent, fileSet))
6875                        return false;
6876                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6878                    taskstatus("%s to %s",
6879                        fileSetDir.c_str(), toDirName.c_str());
6881                    int nrFiles = 0;
6882                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6883                        {
6884                        String fileName = fileSet[i];
6886                        String sourcePath;
6887                        if (fileSetDir.size()>0)
6888                            {
6889                            sourcePath.append(fileSetDir);
6890                            sourcePath.append("/");
6891                            }
6892                        sourcePath.append(fileName);
6893                        String fullSource = parent.resolve(sourcePath);
6894                        
6895                        //Get the immediate parent directory's base name
6896                        String baseFileSetDir = fileSetDir;
6897                        unsigned int pos = baseFileSetDir.find_last_of('/');
6898                        if (pos!=baseFileSetDir.npos &&
6899                                   pos < baseFileSetDir.size()-1)
6900                            baseFileSetDir =
6901                               baseFileSetDir.substr(pos+1,
6902                                    baseFileSetDir.size());
6903                        //Now make the new path
6904                        String destPath;
6905                        if (toDirName.size()>0)
6906                            {
6907                            destPath.append(toDirName);
6908                            destPath.append("/");
6909                            }
6910                        if (baseFileSetDir.size()>0)
6911                            {
6912                            destPath.append(baseFileSetDir);
6913                            destPath.append("/");
6914                            }
6915                        destPath.append(fileName);
6916                        String fullDest = parent.resolve(destPath);
6917                        //trace("fileName:%s", fileName.c_str());
6918                        if (verbose)
6919                            taskstatus("copy %s to new dir : %s",
6920                                  fullSource.c_str(), fullDest.c_str());
6921                        if (!isNewerThan(fullSource, fullDest))
6922                            {
6923                            if (verbose)
6924                                taskstatus("copy skipping %s", fullSource.c_str());
6925                            continue;
6926                            }
6927                        if (!copyFile(fullSource, fullDest))
6928                            return false;
6929                        nrFiles++;
6930                        }
6931                    taskstatus("%d file(s) copied", nrFiles);
6932                    }
6933                else //file source
6934                    {
6935                    //For file->dir we want only the basename of
6936                    //the source appended to the dest dir
6937                    taskstatus("%s to %s", 
6938                        fileName.c_str(), toDirName.c_str());
6939                    String baseName = fileName;
6940                    unsigned int pos = baseName.find_last_of('/');
6941                    if (pos!=baseName.npos && pos<baseName.size()-1)
6942                        baseName = baseName.substr(pos+1, baseName.size());
6943                    String fullSource = parent.resolve(fileName);
6944                    String destPath;
6945                    if (toDirName.size()>0)
6946                        {
6947                        destPath.append(toDirName);
6948                        destPath.append("/");
6949                        }
6950                    destPath.append(baseName);
6951                    String fullDest = parent.resolve(destPath);
6952                    if (verbose)
6953                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6954                                           fullDest.c_str());
6955                    if (!isRegularFile(fullSource))
6956                        {
6957                        error("copy : file %s does not exist", fullSource.c_str());
6958                        return false;
6959                        }
6960                    if (!isNewerThan(fullSource, fullDest))
6961                        {
6962                        taskstatus("skipped");
6963                        return true;
6964                        }
6965                    if (!copyFile(fullSource, fullDest))
6966                        return false;
6967                    taskstatus("1 file copied");
6968                    }
6969                return true;
6970                }
6971            }
6972         return true;
6973         }
6976     virtual bool parse(Element *elem)
6977         {
6978         if (!parent.getAttribute(elem, "file", fileNameOpt))
6979             return false;
6980         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6981             return false;
6982         if (toFileNameOpt.size() > 0)
6983             cptype = CP_TOFILE;
6984         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6985             return false;
6986         if (toDirNameOpt.size() > 0)
6987             cptype = CP_TODIR;
6988         if (!parent.getAttribute(elem, "verbose", verboseOpt))
6989             return false;
6990             
6991         haveFileSet = false;
6992         
6993         std::vector<Element *> children = elem->getChildren();
6994         for (unsigned int i=0 ; i<children.size() ; i++)
6995             {
6996             Element *child = children[i];
6997             String tagName = child->getName();
6998             if (tagName == "fileset")
6999                 {
7000                 if (!parseFileSet(child, parent, fileSet))
7001                     {
7002                     error("problem getting fileset");
7003                     return false;
7004                     }
7005                 haveFileSet = true;
7006                 }
7007             }
7009         //Perform validity checks
7010         if (fileNameOpt.size()>0 && fileSet.size()>0)
7011             {
7012             error("<copy> can only have one of : file= and <fileset>");
7013             return false;
7014             }
7015         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7016             {
7017             error("<copy> can only have one of : tofile= or todir=");
7018             return false;
7019             }
7020         if (haveFileSet && toDirNameOpt.size()==0)
7021             {
7022             error("a <copy> task with a <fileset> must have : todir=");
7023             return false;
7024             }
7025         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7026             {
7027             error("<copy> tofile= must be associated with : file=");
7028             return false;
7029             }
7030         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7031             {
7032             error("<copy> todir= must be associated with : file= or <fileset>");
7033             return false;
7034             }
7036         return true;
7037         }
7038         
7039 private:
7041     int cptype;
7042     bool haveFileSet;
7044     FileSet fileSet;
7045     String  fileNameOpt;
7046     String  toFileNameOpt;
7047     String  toDirNameOpt;
7048     String  verboseOpt;
7049 };
7052 /**
7053  *
7054  */
7055 class TaskDelete : public Task
7057 public:
7059     typedef enum
7060         {
7061         DEL_FILE,
7062         DEL_DIR,
7063         DEL_FILESET
7064         } DeleteType;
7066     TaskDelete(MakeBase &par) : Task(par)
7067         { 
7068         type        = TASK_DELETE;
7069         name        = "delete";
7070         delType     = DEL_FILE;
7071         }
7073     virtual ~TaskDelete()
7074         {}
7076     virtual bool execute()
7077         {
7078         String dirName   = parent.eval(dirNameOpt, ".");
7079         String fileName  = parent.eval(fileNameOpt, ".");
7080         bool verbose     = parent.evalBool(verboseOpt, false);
7081         bool quiet       = parent.evalBool(quietOpt, false);
7082         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7083         struct stat finfo;
7084         switch (delType)
7085             {
7086             case DEL_FILE:
7087                 {
7088                 taskstatus("file: %s", fileName.c_str());
7089                 String fullName = parent.resolve(fileName);
7090                 char *fname = (char *)fullName.c_str();
7091                 if (!quiet && verbose)
7092                     taskstatus("path: %s", fname);
7093                 //does not exist
7094                 if (stat(fname, &finfo)<0)
7095                     {
7096                     if (failOnError)
7097                         return false;
7098                     else
7099                         return true;
7100                     }
7101                 //exists but is not a regular file
7102                 if (!S_ISREG(finfo.st_mode))
7103                     {
7104                     error("<delete> failed. '%s' exists and is not a regular file",
7105                           fname);
7106                     return false;
7107                     }
7108                 if (remove(fname)<0)
7109                     {
7110                     error("<delete> failed: %s", strerror(errno));
7111                     return false;
7112                     }
7113                 return true;
7114                 }
7115             case DEL_DIR:
7116                 {
7117                 taskstatus("dir: %s", dirName.c_str());
7118                 String fullDir = parent.resolve(dirName);
7119                 if (!quiet && verbose)
7120                     taskstatus("path: %s", fullDir.c_str());
7121                 if (!removeDirectory(fullDir))
7122                     return false;
7123                 return true;
7124                 }
7125             }
7126         return true;
7127         }
7129     virtual bool parse(Element *elem)
7130         {
7131         if (!parent.getAttribute(elem, "file", fileNameOpt))
7132             return false;
7133         if (fileNameOpt.size() > 0)
7134             delType = DEL_FILE;
7135         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7136             return false;
7137         if (dirNameOpt.size() > 0)
7138             delType = DEL_DIR;
7139         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7140             {
7141             error("<delete> can have one attribute of file= or dir=");
7142             return false;
7143             }
7144         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7145             {
7146             error("<delete> must have one attribute of file= or dir=");
7147             return false;
7148             }
7149         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7150             return false;
7151         if (!parent.getAttribute(elem, "quiet", quietOpt))
7152             return false;
7153         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7154             return false;
7155         return true;
7156         }
7158 private:
7160     int delType;
7161     String dirNameOpt;
7162     String fileNameOpt;
7163     String verboseOpt;
7164     String quietOpt;
7165     String failOnErrorOpt;
7166 };
7169 /**
7170  * Send a message to stdout
7171  */
7172 class TaskEcho : public Task
7174 public:
7176     TaskEcho(MakeBase &par) : Task(par)
7177         { type = TASK_ECHO; name = "echo"; }
7179     virtual ~TaskEcho()
7180         {}
7182     virtual bool execute()
7183         {
7184         //let message have priority over text
7185         String message = parent.eval(messageOpt, "");
7186         String text    = parent.eval(textOpt, "");
7187         if (message.size() > 0)
7188             {
7189             fprintf(stdout, "%s\n", message.c_str());
7190             }
7191         else if (text.size() > 0)
7192             {
7193             fprintf(stdout, "%s\n", text.c_str());
7194             }
7195         return true;
7196         }
7198     virtual bool parse(Element *elem)
7199         {
7200         if (!parent.getValue(elem, textOpt))
7201             return false;
7202         textOpt    = leftJustify(textOpt);
7203         if (!parent.getAttribute(elem, "message", messageOpt))
7204             return false;
7205         return true;
7206         }
7208 private:
7210     String messageOpt;
7211     String textOpt;
7212 };
7216 /**
7217  *
7218  */
7219 class TaskJar : public Task
7221 public:
7223     TaskJar(MakeBase &par) : Task(par)
7224         { type = TASK_JAR; name = "jar"; }
7226     virtual ~TaskJar()
7227         {}
7229     virtual bool execute()
7230         {
7231         String command  = parent.eval(commandOpt, "jar");
7232         String basedir  = parent.eval(basedirOpt, ".");
7233         String destfile = parent.eval(destfileOpt, ".");
7235         String cmd = command;
7236         cmd.append(" -cf ");
7237         cmd.append(destfile);
7238         cmd.append(" -C ");
7239         cmd.append(basedir);
7240         cmd.append(" .");
7242         String execCmd = cmd;
7244         String outString, errString;
7245         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7246         if (!ret)
7247             {
7248             error("<jar> command '%s' failed :\n %s",
7249                                       execCmd.c_str(), errString.c_str());
7250             return false;
7251             }
7252         return true;
7253         }
7255     virtual bool parse(Element *elem)
7256         {
7257         if (!parent.getAttribute(elem, "command", commandOpt))
7258             return false;
7259         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7260             return false;
7261         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7262             return false;
7263         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7264             {
7265             error("<jar> required both basedir and destfile attributes to be set");
7266             return false;
7267             }
7268         return true;
7269         }
7271 private:
7273     String commandOpt;
7274     String basedirOpt;
7275     String destfileOpt;
7276 };
7279 /**
7280  *
7281  */
7282 class TaskJavac : public Task
7284 public:
7286     TaskJavac(MakeBase &par) : Task(par)
7287         { 
7288         type = TASK_JAVAC; name = "javac";
7289         }
7291     virtual ~TaskJavac()
7292         {}
7294     virtual bool execute()
7295         {
7296         String command  = parent.eval(commandOpt, "javac");
7297         String srcdir   = parent.eval(srcdirOpt, ".");
7298         String destdir  = parent.eval(destdirOpt, ".");
7299         String target   = parent.eval(targetOpt, "");
7301         std::vector<String> fileList;
7302         if (!listFiles(srcdir, "", fileList))
7303             {
7304             return false;
7305             }
7306         String cmd = command;
7307         cmd.append(" -d ");
7308         cmd.append(destdir);
7309         cmd.append(" -classpath ");
7310         cmd.append(destdir);
7311         cmd.append(" -sourcepath ");
7312         cmd.append(srcdir);
7313         cmd.append(" ");
7314         if (target.size()>0)
7315             {
7316             cmd.append(" -target ");
7317             cmd.append(target);
7318             cmd.append(" ");
7319             }
7320         String fname = "javalist.btool";
7321         FILE *f = fopen(fname.c_str(), "w");
7322         int count = 0;
7323         for (unsigned int i=0 ; i<fileList.size() ; i++)
7324             {
7325             String fname = fileList[i];
7326             String srcName = fname;
7327             if (fname.size()<6) //x.java
7328                 continue;
7329             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7330                 continue;
7331             String baseName = fname.substr(0, fname.size()-5);
7332             String destName = baseName;
7333             destName.append(".class");
7335             String fullSrc = srcdir;
7336             fullSrc.append("/");
7337             fullSrc.append(fname);
7338             String fullDest = destdir;
7339             fullDest.append("/");
7340             fullDest.append(destName);
7341             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7342             if (!isNewerThan(fullSrc, fullDest))
7343                 continue;
7345             count++;
7346             fprintf(f, "%s\n", fullSrc.c_str());
7347             }
7348         fclose(f);
7349         if (!count)
7350             {
7351             taskstatus("nothing to do");
7352             return true;
7353             }
7355         taskstatus("compiling %d files", count);
7357         String execCmd = cmd;
7358         execCmd.append("@");
7359         execCmd.append(fname);
7361         String outString, errString;
7362         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7363         if (!ret)
7364             {
7365             error("<javac> command '%s' failed :\n %s",
7366                                       execCmd.c_str(), errString.c_str());
7367             return false;
7368             }
7369         return true;
7370         }
7372     virtual bool parse(Element *elem)
7373         {
7374         if (!parent.getAttribute(elem, "command", commandOpt))
7375             return false;
7376         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7377             return false;
7378         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7379             return false;
7380         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7381             {
7382             error("<javac> required both srcdir and destdir attributes to be set");
7383             return false;
7384             }
7385         if (!parent.getAttribute(elem, "target", targetOpt))
7386             return false;
7387         return true;
7388         }
7390 private:
7392     String commandOpt;
7393     String srcdirOpt;
7394     String destdirOpt;
7395     String targetOpt;
7397 };
7400 /**
7401  *
7402  */
7403 class TaskLink : public Task
7405 public:
7407     TaskLink(MakeBase &par) : Task(par)
7408         {
7409         type = TASK_LINK; name = "link";
7410         }
7412     virtual ~TaskLink()
7413         {}
7415     virtual bool execute()
7416         {
7417         String  command        = parent.eval(commandOpt, "g++");
7418         String  fileName       = parent.eval(fileNameOpt, "");
7419         String  flags          = parent.eval(flagsOpt, "");
7420         String  libs           = parent.eval(libsOpt, "");
7421         bool    doStrip        = parent.evalBool(doStripOpt, false);
7422         String  symFileName    = parent.eval(symFileNameOpt, "");
7423         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7424         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7426         if (!listFiles(parent, fileSet))
7427             return false;
7428         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7429         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7430         bool doit = false;
7431         String fullTarget = parent.resolve(fileName);
7432         String cmd = command;
7433         cmd.append(" -o ");
7434         cmd.append(fullTarget);
7435         cmd.append(" ");
7436         cmd.append(flags);
7437         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7438             {
7439             cmd.append(" ");
7440             String obj;
7441             if (fileSetDir.size()>0)
7442                 {
7443                 obj.append(fileSetDir);
7444                 obj.append("/");
7445                 }
7446             obj.append(fileSet[i]);
7447             String fullObj = parent.resolve(obj);
7448             String nativeFullObj = getNativePath(fullObj);
7449             cmd.append(nativeFullObj);
7450             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7451             //          fullObj.c_str());
7452             if (isNewerThan(fullObj, fullTarget))
7453                 doit = true;
7454             }
7455         cmd.append(" ");
7456         cmd.append(libs);
7457         if (!doit)
7458             {
7459             //trace("link not needed");
7460             return true;
7461             }
7462         //trace("LINK cmd:%s", cmd.c_str());
7465         String outbuf, errbuf;
7466         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7467             {
7468             error("LINK problem: %s", errbuf.c_str());
7469             return false;
7470             }
7472         if (symFileName.size()>0)
7473             {
7474             String symFullName = parent.resolve(symFileName);
7475             cmd = objcopyCommand;
7476             cmd.append(" --only-keep-debug ");
7477             cmd.append(getNativePath(fullTarget));
7478             cmd.append(" ");
7479             cmd.append(getNativePath(symFullName));
7480             if (!executeCommand(cmd, "", outbuf, errbuf))
7481                 {
7482                 error("<strip> symbol file failed : %s", errbuf.c_str());
7483                 return false;
7484                 }
7485             }
7486             
7487         if (doStrip)
7488             {
7489             cmd = stripCommand;
7490             cmd.append(" ");
7491             cmd.append(getNativePath(fullTarget));
7492             if (!executeCommand(cmd, "", outbuf, errbuf))
7493                {
7494                error("<strip> failed : %s", errbuf.c_str());
7495                return false;
7496                }
7497             }
7499         return true;
7500         }
7502     virtual bool parse(Element *elem)
7503         {
7504         if (!parent.getAttribute(elem, "command", commandOpt))
7505             return false;
7506         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7507             return false;
7508         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7509             return false;
7510         if (!parent.getAttribute(elem, "out", fileNameOpt))
7511             return false;
7512         if (!parent.getAttribute(elem, "strip", doStripOpt))
7513             return false;
7514         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7515             return false;
7516             
7517         std::vector<Element *> children = elem->getChildren();
7518         for (unsigned int i=0 ; i<children.size() ; i++)
7519             {
7520             Element *child = children[i];
7521             String tagName = child->getName();
7522             if (tagName == "fileset")
7523                 {
7524                 if (!parseFileSet(child, parent, fileSet))
7525                     return false;
7526                 }
7527             else if (tagName == "flags")
7528                 {
7529                 if (!parent.getValue(child, flagsOpt))
7530                     return false;
7531                 flagsOpt = strip(flagsOpt);
7532                 }
7533             else if (tagName == "libs")
7534                 {
7535                 if (!parent.getValue(child, libsOpt))
7536                     return false;
7537                 libsOpt = strip(libsOpt);
7538                 }
7539             }
7540         return true;
7541         }
7543 private:
7545     FileSet fileSet;
7547     String  commandOpt;
7548     String  fileNameOpt;
7549     String  flagsOpt;
7550     String  libsOpt;
7551     String  doStripOpt;
7552     String  symFileNameOpt;
7553     String  stripCommandOpt;
7554     String  objcopyCommandOpt;
7556 };
7560 /**
7561  * Create a named file
7562  */
7563 class TaskMakeFile : public Task
7565 public:
7567     TaskMakeFile(MakeBase &par) : Task(par)
7568         { type = TASK_MAKEFILE; name = "makefile"; }
7570     virtual ~TaskMakeFile()
7571         {}
7573     virtual bool execute()
7574         {
7575         String fileName = parent.eval(fileNameOpt, "");
7576         String text     = parent.eval(textOpt, "");
7578         taskstatus("%s", fileName.c_str());
7579         String fullName = parent.resolve(fileName);
7580         if (!isNewerThan(parent.getURI().getPath(), fullName))
7581             {
7582             //trace("skipped <makefile>");
7583             return true;
7584             }
7585         String fullNative = getNativePath(fullName);
7586         //trace("fullName:%s", fullName.c_str());
7587         FILE *f = fopen(fullNative.c_str(), "w");
7588         if (!f)
7589             {
7590             error("<makefile> could not open %s for writing : %s",
7591                 fullName.c_str(), strerror(errno));
7592             return false;
7593             }
7594         for (unsigned int i=0 ; i<text.size() ; i++)
7595             fputc(text[i], f);
7596         fputc('\n', f);
7597         fclose(f);
7598         return true;
7599         }
7601     virtual bool parse(Element *elem)
7602         {
7603         if (!parent.getAttribute(elem, "file", fileNameOpt))
7604             return false;
7605         if (fileNameOpt.size() == 0)
7606             {
7607             error("<makefile> requires 'file=\"filename\"' attribute");
7608             return false;
7609             }
7610         if (!parent.getValue(elem, textOpt))
7611             return false;
7612         textOpt = leftJustify(textOpt);
7613         //trace("dirname:%s", dirName.c_str());
7614         return true;
7615         }
7617 private:
7619     String fileNameOpt;
7620     String textOpt;
7621 };
7625 /**
7626  * Create a named directory
7627  */
7628 class TaskMkDir : public Task
7630 public:
7632     TaskMkDir(MakeBase &par) : Task(par)
7633         { type = TASK_MKDIR; name = "mkdir"; }
7635     virtual ~TaskMkDir()
7636         {}
7638     virtual bool execute()
7639         {
7640         String dirName = parent.eval(dirNameOpt, ".");
7641         
7642         taskstatus("%s", dirName.c_str());
7643         String fullDir = parent.resolve(dirName);
7644         //trace("fullDir:%s", fullDir.c_str());
7645         if (!createDirectory(fullDir))
7646             return false;
7647         return true;
7648         }
7650     virtual bool parse(Element *elem)
7651         {
7652         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7653             return false;
7654         if (dirNameOpt.size() == 0)
7655             {
7656             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7657             return false;
7658             }
7659         return true;
7660         }
7662 private:
7664     String dirNameOpt;
7665 };
7669 /**
7670  * Create a named directory
7671  */
7672 class TaskMsgFmt: public Task
7674 public:
7676     TaskMsgFmt(MakeBase &par) : Task(par)
7677          { type = TASK_MSGFMT;  name = "msgfmt"; }
7679     virtual ~TaskMsgFmt()
7680         {}
7682     virtual bool execute()
7683         {
7684         String  command   = parent.eval(commandOpt, "msgfmt");
7685         String  toDirName = parent.eval(toDirNameOpt, ".");
7686         String  outName   = parent.eval(outNameOpt, "");
7687         bool    owndir    = parent.evalBool(owndirOpt, false);
7689         if (!listFiles(parent, fileSet))
7690             return false;
7691         String fileSetDir = fileSet.getDirectory();
7693         //trace("msgfmt: %d", fileSet.size());
7694         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7695             {
7696             String fileName = fileSet[i];
7697             if (getSuffix(fileName) != "po")
7698                 continue;
7699             String sourcePath;
7700             if (fileSetDir.size()>0)
7701                 {
7702                 sourcePath.append(fileSetDir);
7703                 sourcePath.append("/");
7704                 }
7705             sourcePath.append(fileName);
7706             String fullSource = parent.resolve(sourcePath);
7708             String destPath;
7709             if (toDirName.size()>0)
7710                 {
7711                 destPath.append(toDirName);
7712                 destPath.append("/");
7713                 }
7714             if (owndir)
7715                 {
7716                 String subdir = fileName;
7717                 unsigned int pos = subdir.find_last_of('.');
7718                 if (pos != subdir.npos)
7719                     subdir = subdir.substr(0, pos);
7720                 destPath.append(subdir);
7721                 destPath.append("/");
7722                 }
7723             //Pick the output file name
7724             if (outName.size() > 0)
7725                 {
7726                 destPath.append(outName);
7727                 }
7728             else
7729                 {
7730                 destPath.append(fileName);
7731                 destPath[destPath.size()-2] = 'm';
7732                 }
7734             String fullDest = parent.resolve(destPath);
7736             if (!isNewerThan(fullSource, fullDest))
7737                 {
7738                 //trace("skip %s", fullSource.c_str());
7739                 continue;
7740                 }
7741                 
7742             String cmd = command;
7743             cmd.append(" ");
7744             cmd.append(fullSource);
7745             cmd.append(" -o ");
7746             cmd.append(fullDest);
7747             
7748             int pos = fullDest.find_last_of('/');
7749             if (pos>0)
7750                 {
7751                 String fullDestPath = fullDest.substr(0, pos);
7752                 if (!createDirectory(fullDestPath))
7753                     return false;
7754                 }
7758             String outString, errString;
7759             if (!executeCommand(cmd.c_str(), "", outString, errString))
7760                 {
7761                 error("<msgfmt> problem: %s", errString.c_str());
7762                 return false;
7763                 }
7764             }
7766         return true;
7767         }
7769     virtual bool parse(Element *elem)
7770         {
7771         if (!parent.getAttribute(elem, "command", commandOpt))
7772             return false;
7773         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7774             return false;
7775         if (!parent.getAttribute(elem, "out", outNameOpt))
7776             return false;
7777         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7778             return false;
7779             
7780         std::vector<Element *> children = elem->getChildren();
7781         for (unsigned int i=0 ; i<children.size() ; i++)
7782             {
7783             Element *child = children[i];
7784             String tagName = child->getName();
7785             if (tagName == "fileset")
7786                 {
7787                 if (!parseFileSet(child, parent, fileSet))
7788                     return false;
7789                 }
7790             }
7791         return true;
7792         }
7794 private:
7796     FileSet fileSet;
7798     String  commandOpt;
7799     String  toDirNameOpt;
7800     String  outNameOpt;
7801     String  owndirOpt;
7803 };
7807 /**
7808  *  Perform a Package-Config query similar to pkg-config
7809  */
7810 class TaskPkgConfig : public Task
7812 public:
7814     typedef enum
7815         {
7816         PKG_CONFIG_QUERY_CFLAGS,
7817         PKG_CONFIG_QUERY_LIBS,
7818         PKG_CONFIG_QUERY_ALL
7819         } QueryTypes;
7821     TaskPkgConfig(MakeBase &par) : Task(par)
7822         {
7823         type = TASK_PKG_CONFIG;
7824         name = "pkg-config";
7825         }
7827     virtual ~TaskPkgConfig()
7828         {}
7830     virtual bool execute()
7831         {
7832         String pkgName       = parent.eval(pkgNameOpt,      "");
7833         String prefix        = parent.eval(prefixOpt,       "");
7834         String propName      = parent.eval(propNameOpt,     "");
7835         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7836         String query         = parent.eval(queryOpt,        "all");
7838         String path = parent.resolve(pkgConfigPath);
7839         PkgConfig pkgconfig;
7840         pkgconfig.setPath(path);
7841         pkgconfig.setPrefix(prefix);
7842         if (!pkgconfig.query(pkgName))
7843             {
7844             error("<pkg-config> query failed for '%s", name.c_str());
7845             return false;
7846             }
7847             
7848         String val = "";
7849         if (query == "cflags")
7850             val = pkgconfig.getCflags();
7851         else if (query == "libs")
7852             val =pkgconfig.getLibs();
7853         else if (query == "all")
7854             val = pkgconfig.getAll();
7855         else
7856             {
7857             error("<pkg-config> unhandled query : %s", query.c_str());
7858             return false;
7859             }
7860         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7861         parent.setProperty(propName, val);
7862         return true;
7863         }
7865     virtual bool parse(Element *elem)
7866         {
7867         //# NAME
7868         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7869             return false;
7870         if (pkgNameOpt.size()==0)
7871             {
7872             error("<pkg-config> requires 'name=\"package\"' attribute");
7873             return false;
7874             }
7876         //# PROPERTY
7877         if (!parent.getAttribute(elem, "property", propNameOpt))
7878             return false;
7879         if (propNameOpt.size()==0)
7880             {
7881             error("<pkg-config> requires 'property=\"name\"' attribute");
7882             return false;
7883             }
7884         //# PATH
7885         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7886             return false;
7887         //# PREFIX
7888         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7889             return false;
7890         //# QUERY
7891         if (!parent.getAttribute(elem, "query", queryOpt))
7892             return false;
7894         return true;
7895         }
7897 private:
7899     String queryOpt;
7900     String pkgNameOpt;
7901     String prefixOpt;
7902     String propNameOpt;
7903     String pkgConfigPathOpt;
7905 };
7912 /**
7913  *  Process an archive to allow random access
7914  */
7915 class TaskRanlib : public Task
7917 public:
7919     TaskRanlib(MakeBase &par) : Task(par)
7920         { type = TASK_RANLIB; name = "ranlib"; }
7922     virtual ~TaskRanlib()
7923         {}
7925     virtual bool execute()
7926         {
7927         String fileName = parent.eval(fileNameOpt, "");
7928         String command  = parent.eval(commandOpt, "ranlib");
7930         String fullName = parent.resolve(fileName);
7931         //trace("fullDir:%s", fullDir.c_str());
7932         String cmd = command;
7933         cmd.append(" ");
7934         cmd.append(fullName);
7935         String outbuf, errbuf;
7936         if (!executeCommand(cmd, "", outbuf, errbuf))
7937             return false;
7938         return true;
7939         }
7941     virtual bool parse(Element *elem)
7942         {
7943         if (!parent.getAttribute(elem, "command", commandOpt))
7944             return false;
7945         if (!parent.getAttribute(elem, "file", fileNameOpt))
7946             return false;
7947         if (fileNameOpt.size() == 0)
7948             {
7949             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7950             return false;
7951             }
7952         return true;
7953         }
7955 private:
7957     String fileNameOpt;
7958     String commandOpt;
7959 };
7963 /**
7964  * Compile a resource file into a binary object
7965  */
7966 class TaskRC : public Task
7968 public:
7970     TaskRC(MakeBase &par) : Task(par)
7971         { type = TASK_RC; name = "rc"; }
7973     virtual ~TaskRC()
7974         {}
7976     virtual bool execute()
7977         {
7978         String command  = parent.eval(commandOpt,  "windres");
7979         String flags    = parent.eval(flagsOpt,    "");
7980         String fileName = parent.eval(fileNameOpt, "");
7981         String outName  = parent.eval(outNameOpt,  "");
7983         String fullFile = parent.resolve(fileName);
7984         String fullOut  = parent.resolve(outName);
7985         if (!isNewerThan(fullFile, fullOut))
7986             return true;
7987         String cmd = command;
7988         cmd.append(" -o ");
7989         cmd.append(fullOut);
7990         cmd.append(" ");
7991         cmd.append(flags);
7992         cmd.append(" ");
7993         cmd.append(fullFile);
7995         String outString, errString;
7996         if (!executeCommand(cmd.c_str(), "", outString, errString))
7997             {
7998             error("RC problem: %s", errString.c_str());
7999             return false;
8000             }
8001         return true;
8002         }
8004     virtual bool parse(Element *elem)
8005         {
8006         if (!parent.getAttribute(elem, "command", commandOpt))
8007             return false;
8008         if (!parent.getAttribute(elem, "file", fileNameOpt))
8009             return false;
8010         if (!parent.getAttribute(elem, "out", outNameOpt))
8011             return false;
8012         std::vector<Element *> children = elem->getChildren();
8013         for (unsigned int i=0 ; i<children.size() ; i++)
8014             {
8015             Element *child = children[i];
8016             String tagName = child->getName();
8017             if (tagName == "flags")
8018                 {
8019                 if (!parent.getValue(child, flagsOpt))
8020                     return false;
8021                 }
8022             }
8023         return true;
8024         }
8026 private:
8028     String commandOpt;
8029     String flagsOpt;
8030     String fileNameOpt;
8031     String outNameOpt;
8033 };
8037 /**
8038  *  Collect .o's into a .so or DLL
8039  */
8040 class TaskSharedLib : public Task
8042 public:
8044     TaskSharedLib(MakeBase &par) : Task(par)
8045         { type = TASK_SHAREDLIB; name = "dll"; }
8047     virtual ~TaskSharedLib()
8048         {}
8050     virtual bool execute()
8051         {
8052         String command     = parent.eval(commandOpt, "dllwrap");
8053         String fileName    = parent.eval(fileNameOpt, "");
8054         String defFileName = parent.eval(defFileNameOpt, "");
8055         String impFileName = parent.eval(impFileNameOpt, "");
8056         String libs        = parent.eval(libsOpt, "");
8058         //trace("###########HERE %d", fileSet.size());
8059         bool doit = false;
8060         
8061         String fullOut = parent.resolve(fileName);
8062         //trace("ar fullout: %s", fullOut.c_str());
8063         
8064         if (!listFiles(parent, fileSet))
8065             return false;
8066         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8068         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8069             {
8070             String fname;
8071             if (fileSetDir.size()>0)
8072                 {
8073                 fname.append(fileSetDir);
8074                 fname.append("/");
8075                 }
8076             fname.append(fileSet[i]);
8077             String fullName = parent.resolve(fname);
8078             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8079             if (isNewerThan(fullName, fullOut))
8080                 doit = true;
8081             }
8082         //trace("Needs it:%d", doit);
8083         if (!doit)
8084             {
8085             return true;
8086             }
8088         String cmd = "dllwrap";
8089         cmd.append(" -o ");
8090         cmd.append(fullOut);
8091         if (defFileName.size()>0)
8092             {
8093             cmd.append(" --def ");
8094             cmd.append(defFileName);
8095             cmd.append(" ");
8096             }
8097         if (impFileName.size()>0)
8098             {
8099             cmd.append(" --implib ");
8100             cmd.append(impFileName);
8101             cmd.append(" ");
8102             }
8103         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8104             {
8105             String fname;
8106             if (fileSetDir.size()>0)
8107                 {
8108                 fname.append(fileSetDir);
8109                 fname.append("/");
8110                 }
8111             fname.append(fileSet[i]);
8112             String fullName = parent.resolve(fname);
8114             cmd.append(" ");
8115             cmd.append(fullName);
8116             }
8117         cmd.append(" ");
8118         cmd.append(libs);
8120         String outString, errString;
8121         if (!executeCommand(cmd.c_str(), "", outString, errString))
8122             {
8123             error("<sharedlib> problem: %s", errString.c_str());
8124             return false;
8125             }
8127         return true;
8128         }
8130     virtual bool parse(Element *elem)
8131         {
8132         if (!parent.getAttribute(elem, "command", commandOpt))
8133             return false;
8134         if (!parent.getAttribute(elem, "file", fileNameOpt))
8135             return false;
8136         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8137             return false;
8138         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8139             return false;
8140             
8141         std::vector<Element *> children = elem->getChildren();
8142         for (unsigned int i=0 ; i<children.size() ; i++)
8143             {
8144             Element *child = children[i];
8145             String tagName = child->getName();
8146             if (tagName == "fileset")
8147                 {
8148                 if (!parseFileSet(child, parent, fileSet))
8149                     return false;
8150                 }
8151             else if (tagName == "libs")
8152                 {
8153                 if (!parent.getValue(child, libsOpt))
8154                     return false;
8155                 libsOpt = strip(libsOpt);
8156                 }
8157             }
8158         return true;
8159         }
8161 private:
8163     FileSet fileSet;
8165     String commandOpt;
8166     String fileNameOpt;
8167     String defFileNameOpt;
8168     String impFileNameOpt;
8169     String libsOpt;
8171 };
8175 /**
8176  * Run the "ar" command to archive .o's into a .a
8177  */
8178 class TaskStaticLib : public Task
8180 public:
8182     TaskStaticLib(MakeBase &par) : Task(par)
8183         { type = TASK_STATICLIB; name = "staticlib"; }
8185     virtual ~TaskStaticLib()
8186         {}
8188     virtual bool execute()
8189         {
8190         String command = parent.eval(commandOpt, "ar crv");
8191         String fileName = parent.eval(fileNameOpt, "");
8193         bool doit = false;
8194         
8195         String fullOut = parent.resolve(fileName);
8196         //trace("ar fullout: %s", fullOut.c_str());
8197         
8198         if (!listFiles(parent, fileSet))
8199             return false;
8200         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8201         //trace("###########HERE %s", fileSetDir.c_str());
8203         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8204             {
8205             String fname;
8206             if (fileSetDir.size()>0)
8207                 {
8208                 fname.append(fileSetDir);
8209                 fname.append("/");
8210                 }
8211             fname.append(fileSet[i]);
8212             String fullName = parent.resolve(fname);
8213             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8214             if (isNewerThan(fullName, fullOut))
8215                 doit = true;
8216             }
8217         //trace("Needs it:%d", doit);
8218         if (!doit)
8219             {
8220             return true;
8221             }
8223         String cmd = command;
8224         cmd.append(" ");
8225         cmd.append(fullOut);
8226         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8227             {
8228             String fname;
8229             if (fileSetDir.size()>0)
8230                 {
8231                 fname.append(fileSetDir);
8232                 fname.append("/");
8233                 }
8234             fname.append(fileSet[i]);
8235             String fullName = parent.resolve(fname);
8237             cmd.append(" ");
8238             cmd.append(fullName);
8239             }
8241         String outString, errString;
8242         if (!executeCommand(cmd.c_str(), "", outString, errString))
8243             {
8244             error("<staticlib> problem: %s", errString.c_str());
8245             return false;
8246             }
8248         return true;
8249         }
8252     virtual bool parse(Element *elem)
8253         {
8254         if (!parent.getAttribute(elem, "command", commandOpt))
8255             return false;
8256         if (!parent.getAttribute(elem, "file", fileNameOpt))
8257             return false;
8258             
8259         std::vector<Element *> children = elem->getChildren();
8260         for (unsigned int i=0 ; i<children.size() ; i++)
8261             {
8262             Element *child = children[i];
8263             String tagName = child->getName();
8264             if (tagName == "fileset")
8265                 {
8266                 if (!parseFileSet(child, parent, fileSet))
8267                     return false;
8268                 }
8269             }
8270         return true;
8271         }
8273 private:
8275     FileSet fileSet;
8277     String commandOpt;
8278     String fileNameOpt;
8280 };
8285 /**
8286  * Strip an executable
8287  */
8288 class TaskStrip : public Task
8290 public:
8292     TaskStrip(MakeBase &par) : Task(par)
8293         { type = TASK_STRIP; name = "strip"; }
8295     virtual ~TaskStrip()
8296         {}
8298     virtual bool execute()
8299         {
8300         String command     = parent.eval(commandOpt, "strip");
8301         String fileName    = parent.eval(fileNameOpt, "");
8302         String symFileName = parent.eval(symFileNameOpt, "");
8304         String fullName = parent.resolve(fileName);
8305         //trace("fullDir:%s", fullDir.c_str());
8306         String cmd;
8307         String outbuf, errbuf;
8309         if (symFileName.size()>0)
8310             {
8311             String symFullName = parent.resolve(symFileName);
8312             cmd = "objcopy --only-keep-debug ";
8313             cmd.append(getNativePath(fullName));
8314             cmd.append(" ");
8315             cmd.append(getNativePath(symFullName));
8316             if (!executeCommand(cmd, "", outbuf, errbuf))
8317                 {
8318                 error("<strip> symbol file failed : %s", errbuf.c_str());
8319                 return false;
8320                 }
8321             }
8322             
8323         cmd = command;
8324         cmd.append(getNativePath(fullName));
8325         if (!executeCommand(cmd, "", outbuf, errbuf))
8326             {
8327             error("<strip> failed : %s", errbuf.c_str());
8328             return false;
8329             }
8330         return true;
8331         }
8333     virtual bool parse(Element *elem)
8334         {
8335         if (!parent.getAttribute(elem, "command", commandOpt))
8336             return false;
8337         if (!parent.getAttribute(elem, "file", fileNameOpt))
8338             return false;
8339         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8340             return false;
8341         if (fileNameOpt.size() == 0)
8342             {
8343             error("<strip> requires 'file=\"fileName\"' attribute");
8344             return false;
8345             }
8346         return true;
8347         }
8349 private:
8351     String commandOpt;
8352     String fileNameOpt;
8353     String symFileNameOpt;
8354 };
8357 /**
8358  *
8359  */
8360 class TaskTouch : public Task
8362 public:
8364     TaskTouch(MakeBase &par) : Task(par)
8365         { type = TASK_TOUCH; name = "touch"; }
8367     virtual ~TaskTouch()
8368         {}
8370     virtual bool execute()
8371         {
8372         String fileName = parent.eval(fileNameOpt, "");
8374         String fullName = parent.resolve(fileName);
8375         String nativeFile = getNativePath(fullName);
8376         if (!isRegularFile(fullName) && !isDirectory(fullName))
8377             {            
8378             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8379             int ret = creat(nativeFile.c_str(), 0666);
8380             if (ret != 0) 
8381                 {
8382                 error("<touch> could not create '%s' : %s",
8383                     nativeFile.c_str(), strerror(ret));
8384                 return false;
8385                 }
8386             return true;
8387             }
8388         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8389         if (ret != 0)
8390             {
8391             error("<touch> could not update the modification time for '%s' : %s",
8392                 nativeFile.c_str(), strerror(ret));
8393             return false;
8394             }
8395         return true;
8396         }
8398     virtual bool parse(Element *elem)
8399         {
8400         //trace("touch parse");
8401         if (!parent.getAttribute(elem, "file", fileNameOpt))
8402             return false;
8403         if (fileNameOpt.size() == 0)
8404             {
8405             error("<touch> requires 'file=\"fileName\"' attribute");
8406             return false;
8407             }
8408         return true;
8409         }
8411     String fileNameOpt;
8412 };
8415 /**
8416  *
8417  */
8418 class TaskTstamp : public Task
8420 public:
8422     TaskTstamp(MakeBase &par) : Task(par)
8423         { type = TASK_TSTAMP; name = "tstamp"; }
8425     virtual ~TaskTstamp()
8426         {}
8428     virtual bool execute()
8429         {
8430         return true;
8431         }
8433     virtual bool parse(Element *elem)
8434         {
8435         //trace("tstamp parse");
8436         return true;
8437         }
8438 };
8442 /**
8443  *
8444  */
8445 Task *Task::createTask(Element *elem, int lineNr)
8447     String tagName = elem->getName();
8448     //trace("task:%s", tagName.c_str());
8449     Task *task = NULL;
8450     if (tagName == "cc")
8451         task = new TaskCC(parent);
8452     else if (tagName == "copy")
8453         task = new TaskCopy(parent);
8454     else if (tagName == "delete")
8455         task = new TaskDelete(parent);
8456     else if (tagName == "echo")
8457         task = new TaskEcho(parent);
8458     else if (tagName == "jar")
8459         task = new TaskJar(parent);
8460     else if (tagName == "javac")
8461         task = new TaskJavac(parent);
8462     else if (tagName == "link")
8463         task = new TaskLink(parent);
8464     else if (tagName == "makefile")
8465         task = new TaskMakeFile(parent);
8466     else if (tagName == "mkdir")
8467         task = new TaskMkDir(parent);
8468     else if (tagName == "msgfmt")
8469         task = new TaskMsgFmt(parent);
8470     else if (tagName == "pkg-config")
8471         task = new TaskPkgConfig(parent);
8472     else if (tagName == "ranlib")
8473         task = new TaskRanlib(parent);
8474     else if (tagName == "rc")
8475         task = new TaskRC(parent);
8476     else if (tagName == "sharedlib")
8477         task = new TaskSharedLib(parent);
8478     else if (tagName == "staticlib")
8479         task = new TaskStaticLib(parent);
8480     else if (tagName == "strip")
8481         task = new TaskStrip(parent);
8482     else if (tagName == "touch")
8483         task = new TaskTouch(parent);
8484     else if (tagName == "tstamp")
8485         task = new TaskTstamp(parent);
8486     else
8487         {
8488         error("Unknown task '%s'", tagName.c_str());
8489         return NULL;
8490         }
8492     task->setLine(lineNr);
8494     if (!task->parse(elem))
8495         {
8496         delete task;
8497         return NULL;
8498         }
8499     return task;
8504 //########################################################################
8505 //# T A R G E T
8506 //########################################################################
8508 /**
8509  *
8510  */
8511 class Target : public MakeBase
8514 public:
8516     /**
8517      *
8518      */
8519     Target(Make &par) : parent(par)
8520         { init(); }
8522     /**
8523      *
8524      */
8525     Target(const Target &other) : parent(other.parent)
8526         { init(); assign(other); }
8528     /**
8529      *
8530      */
8531     Target &operator=(const Target &other)
8532         { init(); assign(other); return *this; }
8534     /**
8535      *
8536      */
8537     virtual ~Target()
8538         { cleanup() ; }
8541     /**
8542      *
8543      */
8544     virtual Make &getParent()
8545         { return parent; }
8547     /**
8548      *
8549      */
8550     virtual String getName()
8551         { return name; }
8553     /**
8554      *
8555      */
8556     virtual void setName(const String &val)
8557         { name = val; }
8559     /**
8560      *
8561      */
8562     virtual String getDescription()
8563         { return description; }
8565     /**
8566      *
8567      */
8568     virtual void setDescription(const String &val)
8569         { description = val; }
8571     /**
8572      *
8573      */
8574     virtual void addDependency(const String &val)
8575         { deps.push_back(val); }
8577     /**
8578      *
8579      */
8580     virtual void parseDependencies(const String &val)
8581         { deps = tokenize(val, ", "); }
8583     /**
8584      *
8585      */
8586     virtual std::vector<String> &getDependencies()
8587         { return deps; }
8589     /**
8590      *
8591      */
8592     virtual String getIf()
8593         { return ifVar; }
8595     /**
8596      *
8597      */
8598     virtual void setIf(const String &val)
8599         { ifVar = val; }
8601     /**
8602      *
8603      */
8604     virtual String getUnless()
8605         { return unlessVar; }
8607     /**
8608      *
8609      */
8610     virtual void setUnless(const String &val)
8611         { unlessVar = val; }
8613     /**
8614      *
8615      */
8616     virtual void addTask(Task *val)
8617         { tasks.push_back(val); }
8619     /**
8620      *
8621      */
8622     virtual std::vector<Task *> &getTasks()
8623         { return tasks; }
8625 private:
8627     void init()
8628         {
8629         }
8631     void cleanup()
8632         {
8633         tasks.clear();
8634         }
8636     void assign(const Target &other)
8637         {
8638         //parent      = other.parent;
8639         name        = other.name;
8640         description = other.description;
8641         ifVar       = other.ifVar;
8642         unlessVar   = other.unlessVar;
8643         deps        = other.deps;
8644         tasks       = other.tasks;
8645         }
8647     Make &parent;
8649     String name;
8651     String description;
8653     String ifVar;
8655     String unlessVar;
8657     std::vector<String> deps;
8659     std::vector<Task *> tasks;
8661 };
8670 //########################################################################
8671 //# M A K E
8672 //########################################################################
8675 /**
8676  *
8677  */
8678 class Make : public MakeBase
8681 public:
8683     /**
8684      *
8685      */
8686     Make()
8687         { init(); }
8689     /**
8690      *
8691      */
8692     Make(const Make &other)
8693         { assign(other); }
8695     /**
8696      *
8697      */
8698     Make &operator=(const Make &other)
8699         { assign(other); return *this; }
8701     /**
8702      *
8703      */
8704     virtual ~Make()
8705         { cleanup(); }
8707     /**
8708      *
8709      */
8710     virtual std::map<String, Target> &getTargets()
8711         { return targets; }
8714     /**
8715      *
8716      */
8717     virtual String version()
8718         { return BUILDTOOL_VERSION; }
8720     /**
8721      * Overload a <property>
8722      */
8723     virtual bool specifyProperty(const String &name,
8724                                  const String &value);
8726     /**
8727      *
8728      */
8729     virtual bool run();
8731     /**
8732      *
8733      */
8734     virtual bool run(const String &target);
8738 private:
8740     /**
8741      *
8742      */
8743     void init();
8745     /**
8746      *
8747      */
8748     void cleanup();
8750     /**
8751      *
8752      */
8753     void assign(const Make &other);
8755     /**
8756      *
8757      */
8758     bool executeTask(Task &task);
8761     /**
8762      *
8763      */
8764     bool executeTarget(Target &target,
8765              std::set<String> &targetsCompleted);
8768     /**
8769      *
8770      */
8771     bool execute();
8773     /**
8774      *
8775      */
8776     bool checkTargetDependencies(Target &prop,
8777                     std::vector<String> &depList);
8779     /**
8780      *
8781      */
8782     bool parsePropertyFile(const String &fileName,
8783                            const String &prefix);
8785     /**
8786      *
8787      */
8788     bool parseProperty(Element *elem);
8790     /**
8791      *
8792      */
8793     bool parseFile();
8795     /**
8796      *
8797      */
8798     std::vector<String> glob(const String &pattern);
8801     //###############
8802     //# Fields
8803     //###############
8805     String projectName;
8807     String currentTarget;
8809     String defaultTarget;
8811     String specifiedTarget;
8813     String baseDir;
8815     String description;
8816     
8817     //std::vector<Property> properties;
8818     
8819     std::map<String, Target> targets;
8821     std::vector<Task *> allTasks;
8822     
8823     std::map<String, String> specifiedProperties;
8825 };
8828 //########################################################################
8829 //# C L A S S  M A I N T E N A N C E
8830 //########################################################################
8832 /**
8833  *
8834  */
8835 void Make::init()
8837     uri             = "build.xml";
8838     projectName     = "";
8839     currentTarget   = "";
8840     defaultTarget   = "";
8841     specifiedTarget = "";
8842     baseDir         = "";
8843     description     = "";
8844     envPrefix       = "env.";
8845     pcPrefix        = "pc.";
8846     pccPrefix       = "pcc.";
8847     pclPrefix       = "pcl.";
8848     properties.clear();
8849     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8850         delete allTasks[i];
8851     allTasks.clear();
8856 /**
8857  *
8858  */
8859 void Make::cleanup()
8861     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8862         delete allTasks[i];
8863     allTasks.clear();
8868 /**
8869  *
8870  */
8871 void Make::assign(const Make &other)
8873     uri              = other.uri;
8874     projectName      = other.projectName;
8875     currentTarget    = other.currentTarget;
8876     defaultTarget    = other.defaultTarget;
8877     specifiedTarget  = other.specifiedTarget;
8878     baseDir          = other.baseDir;
8879     description      = other.description;
8880     properties       = other.properties;
8885 //########################################################################
8886 //# U T I L I T Y    T A S K S
8887 //########################################################################
8889 /**
8890  *  Perform a file globbing
8891  */
8892 std::vector<String> Make::glob(const String &pattern)
8894     std::vector<String> res;
8895     return res;
8899 //########################################################################
8900 //# P U B L I C    A P I
8901 //########################################################################
8905 /**
8906  *
8907  */
8908 bool Make::executeTarget(Target &target,
8909              std::set<String> &targetsCompleted)
8912     String name = target.getName();
8914     //First get any dependencies for this target
8915     std::vector<String> deps = target.getDependencies();
8916     for (unsigned int i=0 ; i<deps.size() ; i++)
8917         {
8918         String dep = deps[i];
8919         //Did we do it already?  Skip
8920         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8921             continue;
8922             
8923         std::map<String, Target> &tgts =
8924                target.getParent().getTargets();
8925         std::map<String, Target>::iterator iter =
8926                tgts.find(dep);
8927         if (iter == tgts.end())
8928             {
8929             error("Target '%s' dependency '%s' not found",
8930                       name.c_str(),  dep.c_str());
8931             return false;
8932             }
8933         Target depTarget = iter->second;
8934         if (!executeTarget(depTarget, targetsCompleted))
8935             {
8936             return false;
8937             }
8938         }
8940     status("##### Target : %s\n##### %s", name.c_str(),
8941             target.getDescription().c_str());
8943     //Now let's do the tasks
8944     std::vector<Task *> &tasks = target.getTasks();
8945     for (unsigned int i=0 ; i<tasks.size() ; i++)
8946         {
8947         Task *task = tasks[i];
8948         status("--- %s / %s", name.c_str(), task->getName().c_str());
8949         if (!task->execute())
8950             {
8951             return false;
8952             }
8953         }
8954         
8955     targetsCompleted.insert(name);
8956     
8957     return true;
8962 /**
8963  *  Main execute() method.  Start here and work
8964  *  up the dependency tree 
8965  */
8966 bool Make::execute()
8968     status("######## EXECUTE");
8970     //Determine initial target
8971     if (specifiedTarget.size()>0)
8972         {
8973         currentTarget = specifiedTarget;
8974         }
8975     else if (defaultTarget.size()>0)
8976         {
8977         currentTarget = defaultTarget;
8978         }
8979     else
8980         {
8981         error("execute: no specified or default target requested");
8982         return false;
8983         }
8985     std::map<String, Target>::iterator iter =
8986                targets.find(currentTarget);
8987     if (iter == targets.end())
8988         {
8989         error("Initial target '%s' not found",
8990                  currentTarget.c_str());
8991         return false;
8992         }
8993         
8994     //Now run
8995     Target target = iter->second;
8996     std::set<String> targetsCompleted;
8997     if (!executeTarget(target, targetsCompleted))
8998         {
8999         return false;
9000         }
9002     status("######## EXECUTE COMPLETE");
9003     return true;
9009 /**
9010  *
9011  */
9012 bool Make::checkTargetDependencies(Target &target, 
9013                             std::vector<String> &depList)
9015     String tgtName = target.getName().c_str();
9016     depList.push_back(tgtName);
9018     std::vector<String> deps = target.getDependencies();
9019     for (unsigned int i=0 ; i<deps.size() ; i++)
9020         {
9021         String dep = deps[i];
9022         //First thing entered was the starting Target
9023         if (dep == depList[0])
9024             {
9025             error("Circular dependency '%s' found at '%s'",
9026                       dep.c_str(), tgtName.c_str());
9027             std::vector<String>::iterator diter;
9028             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9029                 {
9030                 error("  %s", diter->c_str());
9031                 }
9032             return false;
9033             }
9035         std::map<String, Target> &tgts =
9036                   target.getParent().getTargets();
9037         std::map<String, Target>::iterator titer = tgts.find(dep);
9038         if (titer == tgts.end())
9039             {
9040             error("Target '%s' dependency '%s' not found",
9041                       tgtName.c_str(), dep.c_str());
9042             return false;
9043             }
9044         if (!checkTargetDependencies(titer->second, depList))
9045             {
9046             return false;
9047             }
9048         }
9049     return true;
9056 static int getword(int pos, const String &inbuf, String &result)
9058     int p = pos;
9059     int len = (int)inbuf.size();
9060     String val;
9061     while (p < len)
9062         {
9063         char ch = inbuf[p];
9064         if (!isalnum(ch) && ch!='.' && ch!='_')
9065             break;
9066         val.push_back(ch);
9067         p++;
9068         }
9069     result = val;
9070     return p;
9076 /**
9077  *
9078  */
9079 bool Make::parsePropertyFile(const String &fileName,
9080                              const String &prefix)
9082     FILE *f = fopen(fileName.c_str(), "r");
9083     if (!f)
9084         {
9085         error("could not open property file %s", fileName.c_str());
9086         return false;
9087         }
9088     int linenr = 0;
9089     while (!feof(f))
9090         {
9091         char buf[256];
9092         if (!fgets(buf, 255, f))
9093             break;
9094         linenr++;
9095         String s = buf;
9096         s = trim(s);
9097         int len = s.size();
9098         if (len == 0)
9099             continue;
9100         if (s[0] == '#')
9101             continue;
9102         String key;
9103         String val;
9104         int p = 0;
9105         int p2 = getword(p, s, key);
9106         if (p2 <= p)
9107             {
9108             error("property file %s, line %d: expected keyword",
9109                     fileName.c_str(), linenr);
9110             return false;
9111             }
9112         if (prefix.size() > 0)
9113             {
9114             key.insert(0, prefix);
9115             }
9117         //skip whitespace
9118         for (p=p2 ; p<len ; p++)
9119             if (!isspace(s[p]))
9120                 break;
9122         if (p>=len || s[p]!='=')
9123             {
9124             error("property file %s, line %d: expected '='",
9125                     fileName.c_str(), linenr);
9126             return false;
9127             }
9128         p++;
9130         //skip whitespace
9131         for ( ; p<len ; p++)
9132             if (!isspace(s[p]))
9133                 break;
9135         /* This way expects a word after the =
9136         p2 = getword(p, s, val);
9137         if (p2 <= p)
9138             {
9139             error("property file %s, line %d: expected value",
9140                     fileName.c_str(), linenr);
9141             return false;
9142             }
9143         */
9144         // This way gets the rest of the line after the =
9145         if (p>=len)
9146             {
9147             error("property file %s, line %d: expected value",
9148                     fileName.c_str(), linenr);
9149             return false;
9150             }
9151         val = s.substr(p);
9152         if (key.size()==0)
9153             continue;
9154         //allow property to be set, even if val=""
9156         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9157         //See if we wanted to overload this property
9158         std::map<String, String>::iterator iter =
9159             specifiedProperties.find(key);
9160         if (iter!=specifiedProperties.end())
9161             {
9162             val = iter->second;
9163             status("overloading property '%s' = '%s'",
9164                    key.c_str(), val.c_str());
9165             }
9166         properties[key] = val;
9167         }
9168     fclose(f);
9169     return true;
9175 /**
9176  *
9177  */
9178 bool Make::parseProperty(Element *elem)
9180     std::vector<Attribute> &attrs = elem->getAttributes();
9181     for (unsigned int i=0 ; i<attrs.size() ; i++)
9182         {
9183         String attrName = attrs[i].getName();
9184         String attrVal  = attrs[i].getValue();
9186         if (attrName == "name")
9187             {
9188             String val;
9189             if (!getAttribute(elem, "value", val))
9190                 return false;
9191             if (val.size() > 0)
9192                 {
9193                 properties[attrVal] = val;
9194                 }
9195             else
9196                 {
9197                 if (!getAttribute(elem, "location", val))
9198                     return false;
9199                 //let the property exist, even if not defined
9200                 properties[attrVal] = val;
9201                 }
9202             //See if we wanted to overload this property
9203             std::map<String, String>::iterator iter =
9204                 specifiedProperties.find(attrVal);
9205             if (iter != specifiedProperties.end())
9206                 {
9207                 val = iter->second;
9208                 status("overloading property '%s' = '%s'",
9209                     attrVal.c_str(), val.c_str());
9210                 properties[attrVal] = val;
9211                 }
9212             }
9213         else if (attrName == "file")
9214             {
9215             String prefix;
9216             if (!getAttribute(elem, "prefix", prefix))
9217                 return false;
9218             if (prefix.size() > 0)
9219                 {
9220                 if (prefix[prefix.size()-1] != '.')
9221                     prefix.push_back('.');
9222                 }
9223             if (!parsePropertyFile(attrName, prefix))
9224                 return false;
9225             }
9226         else if (attrName == "environment")
9227             {
9228             if (attrVal.find('.') != attrVal.npos)
9229                 {
9230                 error("environment prefix cannot have a '.' in it");
9231                 return false;
9232                 }
9233             envPrefix = attrVal;
9234             envPrefix.push_back('.');
9235             }
9236         else if (attrName == "pkg-config")
9237             {
9238             if (attrVal.find('.') != attrVal.npos)
9239                 {
9240                 error("pkg-config prefix cannot have a '.' in it");
9241                 return false;
9242                 }
9243             pcPrefix = attrVal;
9244             pcPrefix.push_back('.');
9245             }
9246         else if (attrName == "pkg-config-cflags")
9247             {
9248             if (attrVal.find('.') != attrVal.npos)
9249                 {
9250                 error("pkg-config-cflags prefix cannot have a '.' in it");
9251                 return false;
9252                 }
9253             pccPrefix = attrVal;
9254             pccPrefix.push_back('.');
9255             }
9256         else if (attrName == "pkg-config-libs")
9257             {
9258             if (attrVal.find('.') != attrVal.npos)
9259                 {
9260                 error("pkg-config-libs prefix cannot have a '.' in it");
9261                 return false;
9262                 }
9263             pclPrefix = attrVal;
9264             pclPrefix.push_back('.');
9265             }
9266         }
9268     return true;
9274 /**
9275  *
9276  */
9277 bool Make::parseFile()
9279     status("######## PARSE : %s", uri.getPath().c_str());
9281     setLine(0);
9283     Parser parser;
9284     Element *root = parser.parseFile(uri.getNativePath());
9285     if (!root)
9286         {
9287         error("Could not open %s for reading",
9288               uri.getNativePath().c_str());
9289         return false;
9290         }
9291     
9292     setLine(root->getLine());
9294     if (root->getChildren().size()==0 ||
9295         root->getChildren()[0]->getName()!="project")
9296         {
9297         error("Main xml element should be <project>");
9298         delete root;
9299         return false;
9300         }
9302     //########## Project attributes
9303     Element *project = root->getChildren()[0];
9304     String s = project->getAttribute("name");
9305     if (s.size() > 0)
9306         projectName = s;
9307     s = project->getAttribute("default");
9308     if (s.size() > 0)
9309         defaultTarget = s;
9310     s = project->getAttribute("basedir");
9311     if (s.size() > 0)
9312         baseDir = s;
9314     //######### PARSE MEMBERS
9315     std::vector<Element *> children = project->getChildren();
9316     for (unsigned int i=0 ; i<children.size() ; i++)
9317         {
9318         Element *elem = children[i];
9319         setLine(elem->getLine());
9320         String tagName = elem->getName();
9322         //########## DESCRIPTION
9323         if (tagName == "description")
9324             {
9325             description = parser.trim(elem->getValue());
9326             }
9328         //######### PROPERTY
9329         else if (tagName == "property")
9330             {
9331             if (!parseProperty(elem))
9332                 return false;
9333             }
9335         //######### TARGET
9336         else if (tagName == "target")
9337             {
9338             String tname   = elem->getAttribute("name");
9339             String tdesc   = elem->getAttribute("description");
9340             String tdeps   = elem->getAttribute("depends");
9341             String tif     = elem->getAttribute("if");
9342             String tunless = elem->getAttribute("unless");
9343             Target target(*this);
9344             target.setName(tname);
9345             target.setDescription(tdesc);
9346             target.parseDependencies(tdeps);
9347             target.setIf(tif);
9348             target.setUnless(tunless);
9349             std::vector<Element *> telems = elem->getChildren();
9350             for (unsigned int i=0 ; i<telems.size() ; i++)
9351                 {
9352                 Element *telem = telems[i];
9353                 Task breeder(*this);
9354                 Task *task = breeder.createTask(telem, telem->getLine());
9355                 if (!task)
9356                     return false;
9357                 allTasks.push_back(task);
9358                 target.addTask(task);
9359                 }
9361             //Check name
9362             if (tname.size() == 0)
9363                 {
9364                 error("no name for target");
9365                 return false;
9366                 }
9367             //Check for duplicate name
9368             if (targets.find(tname) != targets.end())
9369                 {
9370                 error("target '%s' already defined", tname.c_str());
9371                 return false;
9372                 }
9373             //more work than targets[tname]=target, but avoids default allocator
9374             targets.insert(std::make_pair<String, Target>(tname, target));
9375             }
9376         //######### none of the above
9377         else
9378             {
9379             error("unknown toplevel tag: <%s>", tagName.c_str());
9380             return false;
9381             }
9383         }
9385     std::map<String, Target>::iterator iter;
9386     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9387         {
9388         Target tgt = iter->second;
9389         std::vector<String> depList;
9390         if (!checkTargetDependencies(tgt, depList))
9391             {
9392             return false;
9393             }
9394         }
9397     delete root;
9398     status("######## PARSE COMPLETE");
9399     return true;
9403 /**
9404  * Overload a <property>
9405  */
9406 bool Make::specifyProperty(const String &name, const String &value)
9408     if (specifiedProperties.find(name) != specifiedProperties.end())
9409         {
9410         error("Property %s already specified", name.c_str());
9411         return false;
9412         }
9413     specifiedProperties[name] = value;
9414     return true;
9419 /**
9420  *
9421  */
9422 bool Make::run()
9424     if (!parseFile())
9425         return false;
9426         
9427     if (!execute())
9428         return false;
9430     return true;
9436 /**
9437  * Get a formatted MM:SS.sss time elapsed string
9438  */ 
9439 static String
9440 timeDiffString(struct timeval &x, struct timeval &y)
9442     long microsX  = x.tv_usec;
9443     long secondsX = x.tv_sec;
9444     long microsY  = y.tv_usec;
9445     long secondsY = y.tv_sec;
9446     if (microsX < microsY)
9447         {
9448         microsX += 1000000;
9449         secondsX -= 1;
9450         }
9452     int seconds = (int)(secondsX - secondsY);
9453     int millis  = (int)((microsX - microsY)/1000);
9455     int minutes = seconds/60;
9456     seconds -= minutes*60;
9457     char buf[80];
9458     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9459     String ret = buf;
9460     return ret;
9461     
9464 /**
9465  *
9466  */
9467 bool Make::run(const String &target)
9469     status("####################################################");
9470     status("#   %s", version().c_str());
9471     status("####################################################");
9472     struct timeval timeStart, timeEnd;
9473     ::gettimeofday(&timeStart, NULL);
9474     specifiedTarget = target;
9475     if (!run())
9476         return false;
9477     ::gettimeofday(&timeEnd, NULL);
9478     String timeStr = timeDiffString(timeEnd, timeStart);
9479     status("####################################################");
9480     status("#   BuildTool Completed : %s", timeStr.c_str());
9481     status("####################################################");
9482     return true;
9491 }// namespace buildtool
9492 //########################################################################
9493 //# M A I N
9494 //########################################################################
9496 typedef buildtool::String String;
9498 /**
9499  *  Format an error message in printf() style
9500  */
9501 static void error(const char *fmt, ...)
9503     va_list ap;
9504     va_start(ap, fmt);
9505     fprintf(stderr, "BuildTool error: ");
9506     vfprintf(stderr, fmt, ap);
9507     fprintf(stderr, "\n");
9508     va_end(ap);
9512 static bool parseProperty(const String &s, String &name, String &val)
9514     int len = s.size();
9515     int i;
9516     for (i=0 ; i<len ; i++)
9517         {
9518         char ch = s[i];
9519         if (ch == '=')
9520             break;
9521         name.push_back(ch);
9522         }
9523     if (i>=len || s[i]!='=')
9524         {
9525         error("property requires -Dname=value");
9526         return false;
9527         }
9528     i++;
9529     for ( ; i<len ; i++)
9530         {
9531         char ch = s[i];
9532         val.push_back(ch);
9533         }
9534     return true;
9538 /**
9539  * Compare a buffer with a key, for the length of the key
9540  */
9541 static bool sequ(const String &buf, const char *key)
9543     int len = buf.size();
9544     for (int i=0 ; key[i] && i<len ; i++)
9545         {
9546         if (key[i] != buf[i])
9547             return false;
9548         }        
9549     return true;
9552 static void usage(int argc, char **argv)
9554     printf("usage:\n");
9555     printf("   %s [options] [target]\n", argv[0]);
9556     printf("Options:\n");
9557     printf("  -help, -h              print this message\n");
9558     printf("  -version               print the version information and exit\n");
9559     printf("  -file <file>           use given buildfile\n");
9560     printf("  -f <file>                 ''\n");
9561     printf("  -D<property>=<value>   use value for given property\n");
9567 /**
9568  * Parse the command-line args, get our options,
9569  * and run this thing
9570  */   
9571 static bool parseOptions(int argc, char **argv)
9573     if (argc < 1)
9574         {
9575         error("Cannot parse arguments");
9576         return false;
9577         }
9579     buildtool::Make make;
9581     String target;
9583     //char *progName = argv[0];
9584     for (int i=1 ; i<argc ; i++)
9585         {
9586         String arg = argv[i];
9587         if (arg.size()>1 && arg[0]=='-')
9588             {
9589             if (arg == "-h" || arg == "-help")
9590                 {
9591                 usage(argc,argv);
9592                 return true;
9593                 }
9594             else if (arg == "-version")
9595                 {
9596                 printf("%s", make.version().c_str());
9597                 return true;
9598                 }
9599             else if (arg == "-f" || arg == "-file")
9600                 {
9601                 if (i>=argc)
9602                    {
9603                    usage(argc, argv);
9604                    return false;
9605                    }
9606                 i++; //eat option
9607                 make.setURI(argv[i]);
9608                 }
9609             else if (arg.size()>2 && sequ(arg, "-D"))
9610                 {
9611                 String s = arg.substr(2, arg.size());
9612                 String name, value;
9613                 if (!parseProperty(s, name, value))
9614                    {
9615                    usage(argc, argv);
9616                    return false;
9617                    }
9618                 if (!make.specifyProperty(name, value))
9619                     return false;
9620                 }
9621             else
9622                 {
9623                 error("Unknown option:%s", arg.c_str());
9624                 return false;
9625                 }
9626             }
9627         else
9628             {
9629             if (target.size()>0)
9630                 {
9631                 error("only one initial target");
9632                 usage(argc, argv);
9633                 return false;
9634                 }
9635             target = arg;
9636             }
9637         }
9639     //We have the options.  Now execute them
9640     if (!make.run(target))
9641         return false;
9643     return true;
9649 /*
9650 static bool runMake()
9652     buildtool::Make make;
9653     if (!make.run())
9654         return false;
9655     return true;
9659 static bool pkgConfigTest()
9661     buildtool::PkgConfig pkgConfig;
9662     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9663         return false;
9664     return true;
9669 static bool depTest()
9671     buildtool::DepTool deptool;
9672     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9673     if (!deptool.generateDependencies("build.dep"))
9674         return false;
9675     std::vector<buildtool::FileRec> res =
9676            deptool.loadDepFile("build.dep");
9677     if (res.size() == 0)
9678         return false;
9679     return true;
9682 static bool popenTest()
9684     buildtool::Make make;
9685     buildtool::String out, err;
9686     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9687     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9688     return true;
9692 static bool propFileTest()
9694     buildtool::Make make;
9695     make.parsePropertyFile("test.prop", "test.");
9696     return true;
9698 */
9700 int main(int argc, char **argv)
9703     if (!parseOptions(argc, argv))
9704         return 1;
9705     /*
9706     if (!popenTest())
9707         return 1;
9709     if (!depTest())
9710         return 1;
9711     if (!propFileTest())
9712         return 1;
9713     if (runMake())
9714         return 1;
9715     */
9716     return 0;
9720 //########################################################################
9721 //# E N D 
9722 //########################################################################