Code

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