1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006 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 build.exe
28 * (or whatever your compiler might be)
29 * Then
30 * build
31 * or
32 * build {target}
33 */
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <dirent.h>
45 #include <string>
46 #include <map>
47 #include <set>
48 #include <vector>
50 #ifdef __WIN32__
51 #include <windows.h>
52 #endif
56 namespace buildtool
57 {
61 //########################################################################
62 //########################################################################
63 //## R E G E X P
64 //########################################################################
65 //########################################################################
67 /**
68 * This is the T-Rex regular expression library, which we
69 * gratefully acknowledge. It's clean code and small size allow
70 * us to embed it in BuildTool without adding a dependency
71 *
72 */
74 //begin trex.h
76 #ifndef _TREX_H_
77 #define _TREX_H_
78 /***************************************************************
79 T-Rex a tiny regular expression library
81 Copyright (C) 2003-2006 Alberto Demichelis
83 This software is provided 'as-is', without any express
84 or implied warranty. In no event will the authors be held
85 liable for any damages arising from the use of this software.
87 Permission is granted to anyone to use this software for
88 any purpose, including commercial applications, and to alter
89 it and redistribute it freely, subject to the following restrictions:
91 1. The origin of this software must not be misrepresented;
92 you must not claim that you wrote the original software.
93 If you use this software in a product, an acknowledgment
94 in the product documentation would be appreciated but
95 is not required.
97 2. Altered source versions must be plainly marked as such,
98 and must not be misrepresented as being the original software.
100 3. This notice may not be removed or altered from any
101 source distribution.
103 ****************************************************************/
105 #ifdef _UNICODE
106 #define TRexChar unsigned short
107 #define MAX_CHAR 0xFFFF
108 #define _TREXC(c) L##c
109 #define trex_strlen wcslen
110 #define trex_printf wprintf
111 #else
112 #define TRexChar char
113 #define MAX_CHAR 0xFF
114 #define _TREXC(c) (c)
115 #define trex_strlen strlen
116 #define trex_printf printf
117 #endif
119 #ifndef TREX_API
120 #define TREX_API extern
121 #endif
123 #define TRex_True 1
124 #define TRex_False 0
126 typedef unsigned int TRexBool;
127 typedef struct TRex TRex;
129 typedef struct {
130 const TRexChar *begin;
131 int len;
132 } TRexMatch;
134 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
135 TREX_API void trex_free(TRex *exp);
136 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
137 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
138 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
139 TREX_API int trex_getsubexpcount(TRex* exp);
140 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
142 #endif
144 //end trex.h
146 //start trex.c
149 #include <stdio.h>
150 #include <string>
152 /* see copyright notice in trex.h */
153 #include <string.h>
154 #include <stdlib.h>
155 #include <ctype.h>
156 #include <setjmp.h>
157 //#include "trex.h"
159 #ifdef _UINCODE
160 #define scisprint iswprint
161 #define scstrlen wcslen
162 #define scprintf wprintf
163 #define _SC(x) L(x)
164 #else
165 #define scisprint isprint
166 #define scstrlen strlen
167 #define scprintf printf
168 #define _SC(x) (x)
169 #endif
171 #ifdef _DEBUG
172 #include <stdio.h>
174 static const TRexChar *g_nnames[] =
175 {
176 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
177 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
178 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
179 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
180 };
182 #endif
183 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
184 #define OP_OR (MAX_CHAR+2)
185 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
186 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
187 #define OP_DOT (MAX_CHAR+5)
188 #define OP_CLASS (MAX_CHAR+6)
189 #define OP_CCLASS (MAX_CHAR+7)
190 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
191 #define OP_RANGE (MAX_CHAR+9)
192 #define OP_CHAR (MAX_CHAR+10)
193 #define OP_EOL (MAX_CHAR+11)
194 #define OP_BOL (MAX_CHAR+12)
195 #define OP_WB (MAX_CHAR+13)
197 #define TREX_SYMBOL_ANY_CHAR ('.')
198 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
199 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
200 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
201 #define TREX_SYMBOL_BRANCH ('|')
202 #define TREX_SYMBOL_END_OF_STRING ('$')
203 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
204 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
207 typedef int TRexNodeType;
209 typedef struct tagTRexNode{
210 TRexNodeType type;
211 int left;
212 int right;
213 int next;
214 }TRexNode;
216 struct TRex{
217 const TRexChar *_eol;
218 const TRexChar *_bol;
219 const TRexChar *_p;
220 int _first;
221 int _op;
222 TRexNode *_nodes;
223 int _nallocated;
224 int _nsize;
225 int _nsubexpr;
226 TRexMatch *_matches;
227 int _currsubexp;
228 void *_jmpbuf;
229 const TRexChar **_error;
230 };
232 static int trex_list(TRex *exp);
234 static int trex_newnode(TRex *exp, TRexNodeType type)
235 {
236 TRexNode n;
237 int newid;
238 n.type = type;
239 n.next = n.right = n.left = -1;
240 if(type == OP_EXPR)
241 n.right = exp->_nsubexpr++;
242 if(exp->_nallocated < (exp->_nsize + 1)) {
243 int oldsize = exp->_nallocated;
244 exp->_nallocated *= 2;
245 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
246 }
247 exp->_nodes[exp->_nsize++] = n;
248 newid = exp->_nsize - 1;
249 return (int)newid;
250 }
252 static void trex_error(TRex *exp,const TRexChar *error)
253 {
254 if(exp->_error) *exp->_error = error;
255 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
256 }
258 static void trex_expect(TRex *exp, int n){
259 if((*exp->_p) != n)
260 trex_error(exp, _SC("expected paren"));
261 exp->_p++;
262 }
264 static TRexChar trex_escapechar(TRex *exp)
265 {
266 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
267 exp->_p++;
268 switch(*exp->_p) {
269 case 'v': exp->_p++; return '\v';
270 case 'n': exp->_p++; return '\n';
271 case 't': exp->_p++; return '\t';
272 case 'r': exp->_p++; return '\r';
273 case 'f': exp->_p++; return '\f';
274 default: return (*exp->_p++);
275 }
276 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
277 return (*exp->_p++);
278 }
280 static int trex_charclass(TRex *exp,int classid)
281 {
282 int n = trex_newnode(exp,OP_CCLASS);
283 exp->_nodes[n].left = classid;
284 return n;
285 }
287 static int trex_charnode(TRex *exp,TRexBool isclass)
288 {
289 TRexChar t;
290 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
291 exp->_p++;
292 switch(*exp->_p) {
293 case 'n': exp->_p++; return trex_newnode(exp,'\n');
294 case 't': exp->_p++; return trex_newnode(exp,'\t');
295 case 'r': exp->_p++; return trex_newnode(exp,'\r');
296 case 'f': exp->_p++; return trex_newnode(exp,'\f');
297 case 'v': exp->_p++; return trex_newnode(exp,'\v');
298 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
299 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
300 case 'p': case 'P': case 'l': case 'u':
301 {
302 t = *exp->_p; exp->_p++;
303 return trex_charclass(exp,t);
304 }
305 case 'b':
306 case 'B':
307 if(!isclass) {
308 int node = trex_newnode(exp,OP_WB);
309 exp->_nodes[node].left = *exp->_p;
310 exp->_p++;
311 return node;
312 } //else default
313 default:
314 t = *exp->_p; exp->_p++;
315 return trex_newnode(exp,t);
316 }
317 }
318 else if(!scisprint(*exp->_p)) {
320 trex_error(exp,_SC("letter expected"));
321 }
322 t = *exp->_p; exp->_p++;
323 return trex_newnode(exp,t);
324 }
325 static int trex_class(TRex *exp)
326 {
327 int ret = -1;
328 int first = -1,chain;
329 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
330 ret = trex_newnode(exp,OP_NCLASS);
331 exp->_p++;
332 }else ret = trex_newnode(exp,OP_CLASS);
334 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
335 chain = ret;
336 while(*exp->_p != ']' && exp->_p != exp->_eol) {
337 if(*exp->_p == '-' && first != -1){
338 int r,t;
339 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
340 r = trex_newnode(exp,OP_RANGE);
341 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
342 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
343 exp->_nodes[r].left = exp->_nodes[first].type;
344 t = trex_escapechar(exp);
345 exp->_nodes[r].right = t;
346 exp->_nodes[chain].next = r;
347 chain = r;
348 first = -1;
349 }
350 else{
351 if(first!=-1){
352 int c = first;
353 exp->_nodes[chain].next = c;
354 chain = c;
355 first = trex_charnode(exp,TRex_True);
356 }
357 else{
358 first = trex_charnode(exp,TRex_True);
359 }
360 }
361 }
362 if(first!=-1){
363 int c = first;
364 exp->_nodes[chain].next = c;
365 chain = c;
366 first = -1;
367 }
368 /* hack? */
369 exp->_nodes[ret].left = exp->_nodes[ret].next;
370 exp->_nodes[ret].next = -1;
371 return ret;
372 }
374 static int trex_parsenumber(TRex *exp)
375 {
376 int ret = *exp->_p-'0';
377 int positions = 10;
378 exp->_p++;
379 while(isdigit(*exp->_p)) {
380 ret = ret*10+(*exp->_p++-'0');
381 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
382 positions *= 10;
383 };
384 return ret;
385 }
387 static int trex_element(TRex *exp)
388 {
389 int ret = -1;
390 switch(*exp->_p)
391 {
392 case '(': {
393 int expr,newn;
394 exp->_p++;
397 if(*exp->_p =='?') {
398 exp->_p++;
399 trex_expect(exp,':');
400 expr = trex_newnode(exp,OP_NOCAPEXPR);
401 }
402 else
403 expr = trex_newnode(exp,OP_EXPR);
404 newn = trex_list(exp);
405 exp->_nodes[expr].left = newn;
406 ret = expr;
407 trex_expect(exp,')');
408 }
409 break;
410 case '[':
411 exp->_p++;
412 ret = trex_class(exp);
413 trex_expect(exp,']');
414 break;
415 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
416 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
417 default:
418 ret = trex_charnode(exp,TRex_False);
419 break;
420 }
422 {
423 int op;
424 TRexBool isgreedy = TRex_False;
425 unsigned short p0 = 0, p1 = 0;
426 switch(*exp->_p){
427 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
428 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
429 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
430 case '{':
431 exp->_p++;
432 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
433 p0 = (unsigned short)trex_parsenumber(exp);
434 /*******************************/
435 switch(*exp->_p) {
436 case '}':
437 p1 = p0; exp->_p++;
438 break;
439 case ',':
440 exp->_p++;
441 p1 = 0xFFFF;
442 if(isdigit(*exp->_p)){
443 p1 = (unsigned short)trex_parsenumber(exp);
444 }
445 trex_expect(exp,'}');
446 break;
447 default:
448 trex_error(exp,_SC(", or } expected"));
449 }
450 /*******************************/
451 isgreedy = TRex_True;
452 break;
454 }
455 if(isgreedy) {
456 int nnode = trex_newnode(exp,OP_GREEDY);
457 op = OP_GREEDY;
458 exp->_nodes[nnode].left = ret;
459 exp->_nodes[nnode].right = ((p0)<<16)|p1;
460 ret = nnode;
461 }
462 }
463 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')) {
464 int nnode = trex_element(exp);
465 exp->_nodes[ret].next = nnode;
466 }
468 return ret;
469 }
471 static int trex_list(TRex *exp)
472 {
473 int ret=-1,e;
474 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
475 exp->_p++;
476 ret = trex_newnode(exp,OP_BOL);
477 }
478 e = trex_element(exp);
479 if(ret != -1) {
480 exp->_nodes[ret].next = e;
481 }
482 else ret = e;
484 if(*exp->_p == TREX_SYMBOL_BRANCH) {
485 int temp,tright;
486 exp->_p++;
487 temp = trex_newnode(exp,OP_OR);
488 exp->_nodes[temp].left = ret;
489 tright = trex_list(exp);
490 exp->_nodes[temp].right = tright;
491 ret = temp;
492 }
493 return ret;
494 }
496 static TRexBool trex_matchcclass(int cclass,TRexChar c)
497 {
498 switch(cclass) {
499 case 'a': return isalpha(c)?TRex_True:TRex_False;
500 case 'A': return !isalpha(c)?TRex_True:TRex_False;
501 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
502 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
503 case 's': return isspace(c)?TRex_True:TRex_False;
504 case 'S': return !isspace(c)?TRex_True:TRex_False;
505 case 'd': return isdigit(c)?TRex_True:TRex_False;
506 case 'D': return !isdigit(c)?TRex_True:TRex_False;
507 case 'x': return isxdigit(c)?TRex_True:TRex_False;
508 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
509 case 'c': return iscntrl(c)?TRex_True:TRex_False;
510 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
511 case 'p': return ispunct(c)?TRex_True:TRex_False;
512 case 'P': return !ispunct(c)?TRex_True:TRex_False;
513 case 'l': return islower(c)?TRex_True:TRex_False;
514 case 'u': return isupper(c)?TRex_True:TRex_False;
515 }
516 return TRex_False; /*cannot happen*/
517 }
519 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
520 {
521 do {
522 switch(node->type) {
523 case OP_RANGE:
524 if(c >= node->left && c <= node->right) return TRex_True;
525 break;
526 case OP_CCLASS:
527 if(trex_matchcclass(node->left,c)) return TRex_True;
528 break;
529 default:
530 if(c == node->type)return TRex_True;
531 }
532 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
533 return TRex_False;
534 }
536 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
537 {
539 TRexNodeType type = node->type;
540 switch(type) {
541 case OP_GREEDY: {
542 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
543 TRexNode *greedystop = NULL;
544 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
545 const TRexChar *s=str, *good = str;
547 if(node->next != -1) {
548 greedystop = &exp->_nodes[node->next];
549 }
550 else {
551 greedystop = next;
552 }
554 while((nmaches == 0xFFFF || nmaches < p1)) {
556 const TRexChar *stop;
557 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
558 break;
559 nmaches++;
560 good=s;
561 if(greedystop) {
562 //checks that 0 matches satisfy the expression(if so skips)
563 //if not would always stop(for instance if is a '?')
564 if(greedystop->type != OP_GREEDY ||
565 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
566 {
567 TRexNode *gnext = NULL;
568 if(greedystop->next != -1) {
569 gnext = &exp->_nodes[greedystop->next];
570 }else if(next && next->next != -1){
571 gnext = &exp->_nodes[next->next];
572 }
573 stop = trex_matchnode(exp,greedystop,s,gnext);
574 if(stop) {
575 //if satisfied stop it
576 if(p0 == p1 && p0 == nmaches) break;
577 else if(nmaches >= p0 && p1 == 0xFFFF) break;
578 else if(nmaches >= p0 && nmaches <= p1) break;
579 }
580 }
581 }
583 if(s >= exp->_eol)
584 break;
585 }
586 if(p0 == p1 && p0 == nmaches) return good;
587 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
588 else if(nmaches >= p0 && nmaches <= p1) return good;
589 return NULL;
590 }
591 case OP_OR: {
592 const TRexChar *asd = str;
593 TRexNode *temp=&exp->_nodes[node->left];
594 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
595 if(temp->next != -1)
596 temp = &exp->_nodes[temp->next];
597 else
598 return asd;
599 }
600 asd = str;
601 temp = &exp->_nodes[node->right];
602 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
603 if(temp->next != -1)
604 temp = &exp->_nodes[temp->next];
605 else
606 return asd;
607 }
608 return NULL;
609 break;
610 }
611 case OP_EXPR:
612 case OP_NOCAPEXPR:{
613 TRexNode *n = &exp->_nodes[node->left];
614 const TRexChar *cur = str;
615 int capture = -1;
616 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
617 capture = exp->_currsubexp;
618 exp->_matches[capture].begin = cur;
619 exp->_currsubexp++;
620 }
622 do {
623 TRexNode *subnext = NULL;
624 if(n->next != -1) {
625 subnext = &exp->_nodes[n->next];
626 }else {
627 subnext = next;
628 }
629 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
630 if(capture != -1){
631 exp->_matches[capture].begin = 0;
632 exp->_matches[capture].len = 0;
633 }
634 return NULL;
635 }
636 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
638 if(capture != -1)
639 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
640 return cur;
641 }
642 case OP_WB:
643 if(str == exp->_bol && !isspace(*str)
644 || (str == exp->_eol && !isspace(*(str-1)))
645 || (!isspace(*str) && isspace(*(str+1)))
646 || (isspace(*str) && !isspace(*(str+1))) ) {
647 return (node->left == 'b')?str:NULL;
648 }
649 return (node->left == 'b')?NULL:str;
650 case OP_BOL:
651 if(str == exp->_bol) return str;
652 return NULL;
653 case OP_EOL:
654 if(str == exp->_eol) return str;
655 return NULL;
656 case OP_DOT:{
657 *str++;
658 }
659 return str;
660 case OP_NCLASS:
661 case OP_CLASS:
662 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
663 *str++;
664 return str;
665 }
666 return NULL;
667 case OP_CCLASS:
668 if(trex_matchcclass(node->left,*str)) {
669 *str++;
670 return str;
671 }
672 return NULL;
673 default: /* char */
674 if(*str != node->type) return NULL;
675 *str++;
676 return str;
677 }
678 return NULL;
679 }
681 /* public api */
682 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
683 {
684 TRex *exp = (TRex *)malloc(sizeof(TRex));
685 exp->_eol = exp->_bol = NULL;
686 exp->_p = pattern;
687 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
688 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
689 exp->_nsize = 0;
690 exp->_matches = 0;
691 exp->_nsubexpr = 0;
692 exp->_first = trex_newnode(exp,OP_EXPR);
693 exp->_error = error;
694 exp->_jmpbuf = malloc(sizeof(jmp_buf));
695 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
696 int res = trex_list(exp);
697 exp->_nodes[exp->_first].left = res;
698 if(*exp->_p!='\0')
699 trex_error(exp,_SC("unexpected character"));
700 #ifdef _DEBUG
701 {
702 int nsize,i;
703 TRexNode *t;
704 nsize = exp->_nsize;
705 t = &exp->_nodes[0];
706 scprintf(_SC("\n"));
707 for(i = 0;i < nsize; i++) {
708 if(exp->_nodes[i].type>MAX_CHAR)
709 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
710 else
711 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
712 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
713 }
714 scprintf(_SC("\n"));
715 }
716 #endif
717 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
718 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
719 }
720 else{
721 trex_free(exp);
722 return NULL;
723 }
724 return exp;
725 }
727 void trex_free(TRex *exp)
728 {
729 if(exp) {
730 if(exp->_nodes) free(exp->_nodes);
731 if(exp->_jmpbuf) free(exp->_jmpbuf);
732 if(exp->_matches) free(exp->_matches);
733 free(exp);
734 }
735 }
737 TRexBool trex_match(TRex* exp,const TRexChar* text)
738 {
739 const TRexChar* res = NULL;
740 exp->_bol = text;
741 exp->_eol = text + scstrlen(text);
742 exp->_currsubexp = 0;
743 res = trex_matchnode(exp,exp->_nodes,text,NULL);
744 if(res == NULL || res != exp->_eol)
745 return TRex_False;
746 return TRex_True;
747 }
749 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
750 {
751 const TRexChar *cur = NULL;
752 int node = exp->_first;
753 if(text_begin >= text_end) return TRex_False;
754 exp->_bol = text_begin;
755 exp->_eol = text_end;
756 do {
757 cur = text_begin;
758 while(node != -1) {
759 exp->_currsubexp = 0;
760 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
761 if(!cur)
762 break;
763 node = exp->_nodes[node].next;
764 }
765 *text_begin++;
766 } while(cur == NULL && text_begin != text_end);
768 if(cur == NULL)
769 return TRex_False;
771 --text_begin;
773 if(out_begin) *out_begin = text_begin;
774 if(out_end) *out_end = cur;
775 return TRex_True;
776 }
778 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
779 {
780 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
781 }
783 int trex_getsubexpcount(TRex* exp)
784 {
785 return exp->_nsubexpr;
786 }
788 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
789 {
790 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
791 *subexp = exp->_matches[n];
792 return TRex_True;
793 }
796 //########################################################################
797 //########################################################################
798 //## E N D R E G E X P
799 //########################################################################
800 //########################################################################
806 //########################################################################
807 //########################################################################
808 //## X M L
809 //########################################################################
810 //########################################################################
812 // Note: This mini-dom library comes from Pedro, another little project
813 // of mine.
815 typedef std::string String;
816 typedef unsigned int XMLCh;
819 class Namespace
820 {
821 public:
822 Namespace()
823 {}
825 Namespace(const String &prefixArg, const String &namespaceURIArg)
826 {
827 prefix = prefixArg;
828 namespaceURI = namespaceURIArg;
829 }
831 Namespace(const Namespace &other)
832 {
833 assign(other);
834 }
836 Namespace &operator=(const Namespace &other)
837 {
838 assign(other);
839 return *this;
840 }
842 virtual ~Namespace()
843 {}
845 virtual String getPrefix()
846 { return prefix; }
848 virtual String getNamespaceURI()
849 { return namespaceURI; }
851 protected:
853 void assign(const Namespace &other)
854 {
855 prefix = other.prefix;
856 namespaceURI = other.namespaceURI;
857 }
859 String prefix;
860 String namespaceURI;
862 };
864 class Attribute
865 {
866 public:
867 Attribute()
868 {}
870 Attribute(const String &nameArg, const String &valueArg)
871 {
872 name = nameArg;
873 value = valueArg;
874 }
876 Attribute(const Attribute &other)
877 {
878 assign(other);
879 }
881 Attribute &operator=(const Attribute &other)
882 {
883 assign(other);
884 return *this;
885 }
887 virtual ~Attribute()
888 {}
890 virtual String getName()
891 { return name; }
893 virtual String getValue()
894 { return value; }
896 protected:
898 void assign(const Attribute &other)
899 {
900 name = other.name;
901 value = other.value;
902 }
904 String name;
905 String value;
907 };
910 class Element
911 {
912 friend class Parser;
914 public:
915 Element()
916 {
917 parent = NULL;
918 }
920 Element(const String &nameArg)
921 {
922 parent = NULL;
923 name = nameArg;
924 }
926 Element(const String &nameArg, const String &valueArg)
927 {
928 parent = NULL;
929 name = nameArg;
930 value = valueArg;
931 }
933 Element(const Element &other)
934 {
935 assign(other);
936 }
938 Element &operator=(const Element &other)
939 {
940 assign(other);
941 return *this;
942 }
944 virtual Element *clone();
946 virtual ~Element()
947 {
948 for (unsigned int i=0 ; i<children.size() ; i++)
949 delete children[i];
950 }
952 virtual String getName()
953 { return name; }
955 virtual String getValue()
956 { return value; }
958 Element *getParent()
959 { return parent; }
961 std::vector<Element *> getChildren()
962 { return children; }
964 std::vector<Element *> findElements(const String &name);
966 String getAttribute(const String &name);
968 std::vector<Attribute> &getAttributes()
969 { return attributes; }
971 String getTagAttribute(const String &tagName, const String &attrName);
973 String getTagValue(const String &tagName);
975 void addChild(Element *child);
977 void addAttribute(const String &name, const String &value);
979 void addNamespace(const String &prefix, const String &namespaceURI);
982 /**
983 * Prettyprint an XML tree to an output stream. Elements are indented
984 * according to element hierarchy.
985 * @param f a stream to receive the output
986 * @param elem the element to output
987 */
988 void writeIndented(FILE *f);
990 /**
991 * Prettyprint an XML tree to standard output. This is the equivalent of
992 * writeIndented(stdout).
993 * @param elem the element to output
994 */
995 void print();
997 protected:
999 void assign(const Element &other)
1000 {
1001 parent = other.parent;
1002 children = other.children;
1003 attributes = other.attributes;
1004 namespaces = other.namespaces;
1005 name = other.name;
1006 value = other.value;
1007 }
1009 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1011 void writeIndentedRecursive(FILE *f, int indent);
1013 Element *parent;
1015 std::vector<Element *>children;
1017 std::vector<Attribute> attributes;
1018 std::vector<Namespace> namespaces;
1020 String name;
1021 String value;
1023 };
1029 class Parser
1030 {
1031 public:
1032 /**
1033 * Constructor
1034 */
1035 Parser()
1036 { init(); }
1038 virtual ~Parser()
1039 {}
1041 /**
1042 * Parse XML in a char buffer.
1043 * @param buf a character buffer to parse
1044 * @param pos position to start parsing
1045 * @param len number of chars, from pos, to parse.
1046 * @return a pointer to the root of the XML document;
1047 */
1048 Element *parse(const char *buf,int pos,int len);
1050 /**
1051 * Parse XML in a char buffer.
1052 * @param buf a character buffer to parse
1053 * @param pos position to start parsing
1054 * @param len number of chars, from pos, to parse.
1055 * @return a pointer to the root of the XML document;
1056 */
1057 Element *parse(const String &buf);
1059 /**
1060 * Parse a named XML file. The file is loaded like a data file;
1061 * the original format is not preserved.
1062 * @param fileName the name of the file to read
1063 * @return a pointer to the root of the XML document;
1064 */
1065 Element *parseFile(const String &fileName);
1067 /**
1068 * Utility method to preprocess a string for XML
1069 * output, escaping its entities.
1070 * @param str the string to encode
1071 */
1072 static String encode(const String &str);
1074 /**
1075 * Removes whitespace from beginning and end of a string
1076 */
1077 String trim(const String &s);
1079 private:
1081 void init()
1082 {
1083 keepGoing = true;
1084 currentNode = NULL;
1085 parselen = 0;
1086 parsebuf = NULL;
1087 currentPosition = 0;
1088 }
1090 void getLineAndColumn(long pos, long *lineNr, long *colNr);
1092 void error(char *fmt, ...);
1094 int peek(long pos);
1096 int match(long pos, const char *text);
1098 int skipwhite(long p);
1100 int getWord(int p0, String &buf);
1102 int getQuoted(int p0, String &buf, int do_i_parse);
1104 int parseVersion(int p0);
1106 int parseDoctype(int p0);
1108 int parseElement(int p0, Element *par,int depth);
1110 Element *parse(XMLCh *buf,int pos,int len);
1112 bool keepGoing;
1113 Element *currentNode;
1114 long parselen;
1115 XMLCh *parsebuf;
1116 String cdatabuf;
1117 long currentPosition;
1118 int colNr;
1120 };
1125 //########################################################################
1126 //# E L E M E N T
1127 //########################################################################
1129 Element *Element::clone()
1130 {
1131 Element *elem = new Element(name, value);
1132 elem->parent = parent;
1133 elem->attributes = attributes;
1134 elem->namespaces = namespaces;
1136 std::vector<Element *>::iterator iter;
1137 for (iter = children.begin(); iter != children.end() ; iter++)
1138 {
1139 elem->addChild((*iter)->clone());
1140 }
1141 return elem;
1142 }
1145 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1146 {
1147 if (getName() == name)
1148 {
1149 res.push_back(this);
1150 }
1151 for (unsigned int i=0; i<children.size() ; i++)
1152 children[i]->findElementsRecursive(res, name);
1153 }
1155 std::vector<Element *> Element::findElements(const String &name)
1156 {
1157 std::vector<Element *> res;
1158 findElementsRecursive(res, name);
1159 return res;
1160 }
1162 String Element::getAttribute(const String &name)
1163 {
1164 for (unsigned int i=0 ; i<attributes.size() ; i++)
1165 if (attributes[i].getName() ==name)
1166 return attributes[i].getValue();
1167 return "";
1168 }
1170 String Element::getTagAttribute(const String &tagName, const String &attrName)
1171 {
1172 std::vector<Element *>elems = findElements(tagName);
1173 if (elems.size() <1)
1174 return "";
1175 String res = elems[0]->getAttribute(attrName);
1176 return res;
1177 }
1179 String Element::getTagValue(const String &tagName)
1180 {
1181 std::vector<Element *>elems = findElements(tagName);
1182 if (elems.size() <1)
1183 return "";
1184 String res = elems[0]->getValue();
1185 return res;
1186 }
1188 void Element::addChild(Element *child)
1189 {
1190 if (!child)
1191 return;
1192 child->parent = this;
1193 children.push_back(child);
1194 }
1197 void Element::addAttribute(const String &name, const String &value)
1198 {
1199 Attribute attr(name, value);
1200 attributes.push_back(attr);
1201 }
1203 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1204 {
1205 Namespace ns(prefix, namespaceURI);
1206 namespaces.push_back(ns);
1207 }
1209 void Element::writeIndentedRecursive(FILE *f, int indent)
1210 {
1211 int i;
1212 if (!f)
1213 return;
1214 //Opening tag, and attributes
1215 for (i=0;i<indent;i++)
1216 fputc(' ',f);
1217 fprintf(f,"<%s",name.c_str());
1218 for (unsigned int i=0 ; i<attributes.size() ; i++)
1219 {
1220 fprintf(f," %s=\"%s\"",
1221 attributes[i].getName().c_str(),
1222 attributes[i].getValue().c_str());
1223 }
1224 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1225 {
1226 fprintf(f," xmlns:%s=\"%s\"",
1227 namespaces[i].getPrefix().c_str(),
1228 namespaces[i].getNamespaceURI().c_str());
1229 }
1230 fprintf(f,">\n");
1232 //Between the tags
1233 if (value.size() > 0)
1234 {
1235 for (int i=0;i<indent;i++)
1236 fputc(' ', f);
1237 fprintf(f," %s\n", value.c_str());
1238 }
1240 for (unsigned int i=0 ; i<children.size() ; i++)
1241 children[i]->writeIndentedRecursive(f, indent+2);
1243 //Closing tag
1244 for (int i=0; i<indent; i++)
1245 fputc(' ',f);
1246 fprintf(f,"</%s>\n", name.c_str());
1247 }
1249 void Element::writeIndented(FILE *f)
1250 {
1251 writeIndentedRecursive(f, 0);
1252 }
1254 void Element::print()
1255 {
1256 writeIndented(stdout);
1257 }
1260 //########################################################################
1261 //# P A R S E R
1262 //########################################################################
1266 typedef struct
1267 {
1268 char *escaped;
1269 char value;
1270 } EntityEntry;
1272 static EntityEntry entities[] =
1273 {
1274 { "&" , '&' },
1275 { "<" , '<' },
1276 { ">" , '>' },
1277 { "'", '\'' },
1278 { """, '"' },
1279 { NULL , '\0' }
1280 };
1284 /**
1285 * Removes whitespace from beginning and end of a string
1286 */
1287 String Parser::trim(const String &s)
1288 {
1289 if (s.size() < 1)
1290 return s;
1292 //Find first non-ws char
1293 unsigned int begin = 0;
1294 for ( ; begin < s.size() ; begin++)
1295 {
1296 if (!isspace(s[begin]))
1297 break;
1298 }
1300 //Find first non-ws char, going in reverse
1301 unsigned int end = s.size() - 1;
1302 for ( ; end > begin ; end--)
1303 {
1304 if (!isspace(s[end]))
1305 break;
1306 }
1307 //trace("begin:%d end:%d", begin, end);
1309 String res = s.substr(begin, end-begin+1);
1310 return res;
1311 }
1313 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1314 {
1315 long line = 1;
1316 long col = 1;
1317 for (long i=0 ; i<pos ; i++)
1318 {
1319 XMLCh ch = parsebuf[i];
1320 if (ch == '\n' || ch == '\r')
1321 {
1322 col = 0;
1323 line ++;
1324 }
1325 else
1326 col++;
1327 }
1328 *lineNr = line;
1329 *colNr = col;
1331 }
1334 void Parser::error(char *fmt, ...)
1335 {
1336 long lineNr;
1337 long colNr;
1338 getLineAndColumn(currentPosition, &lineNr, &colNr);
1339 va_list args;
1340 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1341 va_start(args,fmt);
1342 vfprintf(stderr,fmt,args);
1343 va_end(args) ;
1344 fprintf(stderr, "\n");
1345 }
1349 int Parser::peek(long pos)
1350 {
1351 if (pos >= parselen)
1352 return -1;
1353 currentPosition = pos;
1354 int ch = parsebuf[pos];
1355 //printf("ch:%c\n", ch);
1356 return ch;
1357 }
1361 String Parser::encode(const String &str)
1362 {
1363 String ret;
1364 for (unsigned int i=0 ; i<str.size() ; i++)
1365 {
1366 XMLCh ch = (XMLCh)str[i];
1367 if (ch == '&')
1368 ret.append("&");
1369 else if (ch == '<')
1370 ret.append("<");
1371 else if (ch == '>')
1372 ret.append(">");
1373 else if (ch == '\'')
1374 ret.append("'");
1375 else if (ch == '"')
1376 ret.append(""");
1377 else
1378 ret.push_back(ch);
1380 }
1381 return ret;
1382 }
1385 int Parser::match(long p0, const char *text)
1386 {
1387 int p = p0;
1388 while (*text)
1389 {
1390 if (peek(p) != *text)
1391 return p0;
1392 p++; text++;
1393 }
1394 return p;
1395 }
1399 int Parser::skipwhite(long p)
1400 {
1402 while (p<parselen)
1403 {
1404 int p2 = match(p, "<!--");
1405 if (p2 > p)
1406 {
1407 p = p2;
1408 while (p<parselen)
1409 {
1410 p2 = match(p, "-->");
1411 if (p2 > p)
1412 {
1413 p = p2;
1414 break;
1415 }
1416 p++;
1417 }
1418 }
1419 XMLCh b = peek(p);
1420 if (!isspace(b))
1421 break;
1422 p++;
1423 }
1424 return p;
1425 }
1427 /* modify this to allow all chars for an element or attribute name*/
1428 int Parser::getWord(int p0, String &buf)
1429 {
1430 int p = p0;
1431 while (p<parselen)
1432 {
1433 XMLCh b = peek(p);
1434 if (b<=' ' || b=='/' || b=='>' || b=='=')
1435 break;
1436 buf.push_back(b);
1437 p++;
1438 }
1439 return p;
1440 }
1442 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1443 {
1445 int p = p0;
1446 if (peek(p) != '"' && peek(p) != '\'')
1447 return p0;
1448 p++;
1450 while ( p<parselen )
1451 {
1452 XMLCh b = peek(p);
1453 if (b=='"' || b=='\'')
1454 break;
1455 if (b=='&' && do_i_parse)
1456 {
1457 bool found = false;
1458 for (EntityEntry *ee = entities ; ee->value ; ee++)
1459 {
1460 int p2 = match(p, ee->escaped);
1461 if (p2>p)
1462 {
1463 buf.push_back(ee->value);
1464 p = p2;
1465 found = true;
1466 break;
1467 }
1468 }
1469 if (!found)
1470 {
1471 error("unterminated entity");
1472 return false;
1473 }
1474 }
1475 else
1476 {
1477 buf.push_back(b);
1478 p++;
1479 }
1480 }
1481 return p;
1482 }
1484 int Parser::parseVersion(int p0)
1485 {
1486 //printf("### parseVersion: %d\n", p0);
1488 int p = p0;
1490 p = skipwhite(p0);
1492 if (peek(p) != '<')
1493 return p0;
1495 p++;
1496 if (p>=parselen || peek(p)!='?')
1497 return p0;
1499 p++;
1501 String buf;
1503 while (p<parselen)
1504 {
1505 XMLCh ch = peek(p);
1506 if (ch=='?')
1507 {
1508 p++;
1509 break;
1510 }
1511 buf.push_back(ch);
1512 p++;
1513 }
1515 if (peek(p) != '>')
1516 return p0;
1517 p++;
1519 //printf("Got version:%s\n",buf.c_str());
1520 return p;
1521 }
1523 int Parser::parseDoctype(int p0)
1524 {
1525 //printf("### parseDoctype: %d\n", p0);
1527 int p = p0;
1528 p = skipwhite(p);
1530 if (p>=parselen || peek(p)!='<')
1531 return p0;
1533 p++;
1535 if (peek(p)!='!' || peek(p+1)=='-')
1536 return p0;
1537 p++;
1539 String buf;
1540 while (p<parselen)
1541 {
1542 XMLCh ch = peek(p);
1543 if (ch=='>')
1544 {
1545 p++;
1546 break;
1547 }
1548 buf.push_back(ch);
1549 p++;
1550 }
1552 //printf("Got doctype:%s\n",buf.c_str());
1553 return p;
1554 }
1556 int Parser::parseElement(int p0, Element *par,int depth)
1557 {
1559 int p = p0;
1561 int p2 = p;
1563 p = skipwhite(p);
1565 //## Get open tag
1566 XMLCh ch = peek(p);
1567 if (ch!='<')
1568 return p0;
1570 p++;
1572 String openTagName;
1573 p = skipwhite(p);
1574 p = getWord(p, openTagName);
1575 //printf("####tag :%s\n", openTagName.c_str());
1576 p = skipwhite(p);
1578 //Add element to tree
1579 Element *n = new Element(openTagName);
1580 n->parent = par;
1581 par->addChild(n);
1583 // Get attributes
1584 if (peek(p) != '>')
1585 {
1586 while (p<parselen)
1587 {
1588 p = skipwhite(p);
1589 ch = peek(p);
1590 //printf("ch:%c\n",ch);
1591 if (ch=='>')
1592 break;
1593 else if (ch=='/' && p<parselen+1)
1594 {
1595 p++;
1596 p = skipwhite(p);
1597 ch = peek(p);
1598 if (ch=='>')
1599 {
1600 p++;
1601 //printf("quick close\n");
1602 return p;
1603 }
1604 }
1605 String attrName;
1606 p2 = getWord(p, attrName);
1607 if (p2==p)
1608 break;
1609 //printf("name:%s",buf);
1610 p=p2;
1611 p = skipwhite(p);
1612 ch = peek(p);
1613 //printf("ch:%c\n",ch);
1614 if (ch!='=')
1615 break;
1616 p++;
1617 p = skipwhite(p);
1618 // ch = parsebuf[p];
1619 // printf("ch:%c\n",ch);
1620 String attrVal;
1621 p2 = getQuoted(p, attrVal, true);
1622 p=p2+1;
1623 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1624 char *namestr = (char *)attrName.c_str();
1625 if (strncmp(namestr, "xmlns:", 6)==0)
1626 n->addNamespace(attrName, attrVal);
1627 else
1628 n->addAttribute(attrName, attrVal);
1629 }
1630 }
1632 bool cdata = false;
1634 p++;
1635 // ### Get intervening data ### */
1636 String data;
1637 while (p<parselen)
1638 {
1639 //# COMMENT
1640 p2 = match(p, "<!--");
1641 if (!cdata && p2>p)
1642 {
1643 p = p2;
1644 while (p<parselen)
1645 {
1646 p2 = match(p, "-->");
1647 if (p2 > p)
1648 {
1649 p = p2;
1650 break;
1651 }
1652 p++;
1653 }
1654 }
1656 ch = peek(p);
1657 //# END TAG
1658 if (ch=='<' && !cdata && peek(p+1)=='/')
1659 {
1660 break;
1661 }
1662 //# CDATA
1663 p2 = match(p, "<![CDATA[");
1664 if (p2 > p)
1665 {
1666 cdata = true;
1667 p = p2;
1668 continue;
1669 }
1671 //# CHILD ELEMENT
1672 if (ch == '<')
1673 {
1674 p2 = parseElement(p, n, depth+1);
1675 if (p2 == p)
1676 {
1677 /*
1678 printf("problem on element:%s. p2:%d p:%d\n",
1679 openTagName.c_str(), p2, p);
1680 */
1681 return p0;
1682 }
1683 p = p2;
1684 continue;
1685 }
1686 //# ENTITY
1687 if (ch=='&' && !cdata)
1688 {
1689 bool found = false;
1690 for (EntityEntry *ee = entities ; ee->value ; ee++)
1691 {
1692 int p2 = match(p, ee->escaped);
1693 if (p2>p)
1694 {
1695 data.push_back(ee->value);
1696 p = p2;
1697 found = true;
1698 break;
1699 }
1700 }
1701 if (!found)
1702 {
1703 error("unterminated entity");
1704 return -1;
1705 }
1706 continue;
1707 }
1709 //# NONE OF THE ABOVE
1710 data.push_back(ch);
1711 p++;
1712 }/*while*/
1715 n->value = data;
1716 //printf("%d : data:%s\n",p,data.c_str());
1718 //## Get close tag
1719 p = skipwhite(p);
1720 ch = peek(p);
1721 if (ch != '<')
1722 {
1723 error("no < for end tag\n");
1724 return p0;
1725 }
1726 p++;
1727 ch = peek(p);
1728 if (ch != '/')
1729 {
1730 error("no / on end tag");
1731 return p0;
1732 }
1733 p++;
1734 ch = peek(p);
1735 p = skipwhite(p);
1736 String closeTagName;
1737 p = getWord(p, closeTagName);
1738 if (openTagName != closeTagName)
1739 {
1740 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1741 openTagName.c_str(), closeTagName.c_str());
1742 return p0;
1743 }
1744 p = skipwhite(p);
1745 if (peek(p) != '>')
1746 {
1747 error("no > on end tag for '%s'", closeTagName.c_str());
1748 return p0;
1749 }
1750 p++;
1751 // printf("close element:%s\n",closeTagName.c_str());
1752 p = skipwhite(p);
1753 return p;
1754 }
1759 Element *Parser::parse(XMLCh *buf,int pos,int len)
1760 {
1761 parselen = len;
1762 parsebuf = buf;
1763 Element *rootNode = new Element("root");
1764 pos = parseVersion(pos);
1765 pos = parseDoctype(pos);
1766 pos = parseElement(pos, rootNode, 0);
1767 return rootNode;
1768 }
1771 Element *Parser::parse(const char *buf, int pos, int len)
1772 {
1773 XMLCh *charbuf = new XMLCh[len + 1];
1774 long i = 0;
1775 for ( ; i < len ; i++)
1776 charbuf[i] = (XMLCh)buf[i];
1777 charbuf[i] = '\0';
1779 Element *n = parse(charbuf, pos, len);
1780 delete[] charbuf;
1781 return n;
1782 }
1784 Element *Parser::parse(const String &buf)
1785 {
1786 long len = (long)buf.size();
1787 XMLCh *charbuf = new XMLCh[len + 1];
1788 long i = 0;
1789 for ( ; i < len ; i++)
1790 charbuf[i] = (XMLCh)buf[i];
1791 charbuf[i] = '\0';
1793 Element *n = parse(charbuf, 0, len);
1794 delete[] charbuf;
1795 return n;
1796 }
1798 Element *Parser::parseFile(const String &fileName)
1799 {
1801 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1802 FILE *f = fopen(fileName.c_str(), "rb");
1803 if (!f)
1804 return NULL;
1806 struct stat statBuf;
1807 if (fstat(fileno(f),&statBuf)<0)
1808 {
1809 fclose(f);
1810 return NULL;
1811 }
1812 long filelen = statBuf.st_size;
1814 //printf("length:%d\n",filelen);
1815 XMLCh *charbuf = new XMLCh[filelen + 1];
1816 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1817 {
1818 *p = (XMLCh)fgetc(f);
1819 }
1820 fclose(f);
1821 charbuf[filelen] = '\0';
1824 /*
1825 printf("nrbytes:%d\n",wc_count);
1826 printf("buf:%ls\n======\n",charbuf);
1827 */
1828 Element *n = parse(charbuf, 0, filelen);
1829 delete[] charbuf;
1830 return n;
1831 }
1835 //########################################################################
1836 //########################################################################
1837 //## E N D X M L
1838 //########################################################################
1839 //########################################################################
1842 //########################################################################
1843 //########################################################################
1844 //## U R I
1845 //########################################################################
1846 //########################################################################
1848 //This would normally be a call to a UNICODE function
1849 #define isLetter(x) isalpha(x)
1851 /**
1852 * A class that implements the W3C URI resource reference.
1853 */
1854 class URI
1855 {
1856 public:
1858 typedef enum
1859 {
1860 SCHEME_NONE =0,
1861 SCHEME_DATA,
1862 SCHEME_HTTP,
1863 SCHEME_HTTPS,
1864 SCHEME_FTP,
1865 SCHEME_FILE,
1866 SCHEME_LDAP,
1867 SCHEME_MAILTO,
1868 SCHEME_NEWS,
1869 SCHEME_TELNET
1870 } SchemeTypes;
1872 /**
1873 *
1874 */
1875 URI()
1876 {
1877 init();
1878 }
1880 /**
1881 *
1882 */
1883 URI(const String &str)
1884 {
1885 init();
1886 parse(str);
1887 }
1890 /**
1891 *
1892 */
1893 URI(const char *str)
1894 {
1895 init();
1896 String domStr = str;
1897 parse(domStr);
1898 }
1901 /**
1902 *
1903 */
1904 URI(const URI &other)
1905 {
1906 init();
1907 assign(other);
1908 }
1911 /**
1912 *
1913 */
1914 URI &operator=(const URI &other)
1915 {
1916 init();
1917 assign(other);
1918 return *this;
1919 }
1922 /**
1923 *
1924 */
1925 virtual ~URI()
1926 {}
1930 /**
1931 *
1932 */
1933 virtual bool parse(const String &str);
1935 /**
1936 *
1937 */
1938 virtual String toString() const;
1940 /**
1941 *
1942 */
1943 virtual int getScheme() const;
1945 /**
1946 *
1947 */
1948 virtual String getSchemeStr() const;
1950 /**
1951 *
1952 */
1953 virtual String getAuthority() const;
1955 /**
1956 * Same as getAuthority, but if the port has been specified
1957 * as host:port , the port will not be included
1958 */
1959 virtual String getHost() const;
1961 /**
1962 *
1963 */
1964 virtual int getPort() const;
1966 /**
1967 *
1968 */
1969 virtual String getPath() const;
1971 /**
1972 *
1973 */
1974 virtual String getNativePath() const;
1976 /**
1977 *
1978 */
1979 virtual bool isAbsolute() const;
1981 /**
1982 *
1983 */
1984 virtual bool isOpaque() const;
1986 /**
1987 *
1988 */
1989 virtual String getQuery() const;
1991 /**
1992 *
1993 */
1994 virtual String getFragment() const;
1996 /**
1997 *
1998 */
1999 virtual URI resolve(const URI &other) const;
2001 /**
2002 *
2003 */
2004 virtual void normalize();
2006 private:
2008 /**
2009 *
2010 */
2011 void init()
2012 {
2013 parsebuf = NULL;
2014 parselen = 0;
2015 scheme = SCHEME_NONE;
2016 schemeStr = "";
2017 port = 0;
2018 authority = "";
2019 path = "";
2020 absolute = false;
2021 opaque = false;
2022 query = "";
2023 fragment = "";
2024 }
2027 /**
2028 *
2029 */
2030 void assign(const URI &other)
2031 {
2032 scheme = other.scheme;
2033 schemeStr = other.schemeStr;
2034 authority = other.authority;
2035 port = other.port;
2036 path = other.path;
2037 absolute = other.absolute;
2038 opaque = other.opaque;
2039 query = other.query;
2040 fragment = other.fragment;
2041 }
2043 int scheme;
2045 String schemeStr;
2047 String authority;
2049 bool portSpecified;
2051 int port;
2053 String path;
2055 bool absolute;
2057 bool opaque;
2059 String query;
2061 String fragment;
2063 void error(const char *fmt, ...);
2065 void trace(const char *fmt, ...);
2068 int peek(int p);
2070 int match(int p, char *key);
2072 int parseScheme(int p);
2074 int parseHierarchicalPart(int p0);
2076 int parseQuery(int p0);
2078 int parseFragment(int p0);
2080 int parse(int p);
2082 char *parsebuf;
2084 int parselen;
2086 };
2090 typedef struct
2091 {
2092 int ival;
2093 char *sval;
2094 int port;
2095 } LookupEntry;
2097 LookupEntry schemes[] =
2098 {
2099 { URI::SCHEME_DATA, "data:", 0 },
2100 { URI::SCHEME_HTTP, "http:", 80 },
2101 { URI::SCHEME_HTTPS, "https:", 443 },
2102 { URI::SCHEME_FTP, "ftp", 12 },
2103 { URI::SCHEME_FILE, "file:", 0 },
2104 { URI::SCHEME_LDAP, "ldap:", 123 },
2105 { URI::SCHEME_MAILTO, "mailto:", 25 },
2106 { URI::SCHEME_NEWS, "news:", 117 },
2107 { URI::SCHEME_TELNET, "telnet:", 23 },
2108 { 0, NULL, 0 }
2109 };
2112 String URI::toString() const
2113 {
2114 String str = schemeStr;
2115 if (authority.size() > 0)
2116 {
2117 str.append("//");
2118 str.append(authority);
2119 }
2120 str.append(path);
2121 if (query.size() > 0)
2122 {
2123 str.append("?");
2124 str.append(query);
2125 }
2126 if (fragment.size() > 0)
2127 {
2128 str.append("#");
2129 str.append(fragment);
2130 }
2131 return str;
2132 }
2135 int URI::getScheme() const
2136 {
2137 return scheme;
2138 }
2140 String URI::getSchemeStr() const
2141 {
2142 return schemeStr;
2143 }
2146 String URI::getAuthority() const
2147 {
2148 String ret = authority;
2149 if (portSpecified && port>=0)
2150 {
2151 char buf[7];
2152 snprintf(buf, 6, ":%6d", port);
2153 ret.append(buf);
2154 }
2155 return ret;
2156 }
2158 String URI::getHost() const
2159 {
2160 return authority;
2161 }
2163 int URI::getPort() const
2164 {
2165 return port;
2166 }
2169 String URI::getPath() const
2170 {
2171 return path;
2172 }
2174 String URI::getNativePath() const
2175 {
2176 String npath;
2177 #ifdef __WIN32__
2178 unsigned int firstChar = 0;
2179 if (path.size() >= 3)
2180 {
2181 if (path[0] == '/' &&
2182 isLetter(path[1]) &&
2183 path[2] == ':')
2184 firstChar++;
2185 }
2186 for (unsigned int i=firstChar ; i<path.size() ; i++)
2187 {
2188 XMLCh ch = (XMLCh) path[i];
2189 if (ch == '/')
2190 npath.push_back((XMLCh)'\\');
2191 else
2192 npath.push_back(ch);
2193 }
2194 #else
2195 npath = path;
2196 #endif
2197 return npath;
2198 }
2201 bool URI::isAbsolute() const
2202 {
2203 return absolute;
2204 }
2206 bool URI::isOpaque() const
2207 {
2208 return opaque;
2209 }
2212 String URI::getQuery() const
2213 {
2214 return query;
2215 }
2218 String URI::getFragment() const
2219 {
2220 return fragment;
2221 }
2224 URI URI::resolve(const URI &other) const
2225 {
2226 //### According to w3c, this is handled in 3 cases
2228 //## 1
2229 if (opaque || other.isAbsolute())
2230 return other;
2232 //## 2
2233 if (other.fragment.size() > 0 &&
2234 other.path.size() == 0 &&
2235 other.scheme == SCHEME_NONE &&
2236 other.authority.size() == 0 &&
2237 other.query.size() == 0 )
2238 {
2239 URI fragUri = *this;
2240 fragUri.fragment = other.fragment;
2241 return fragUri;
2242 }
2244 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2245 URI newUri;
2246 //# 3.1
2247 newUri.scheme = scheme;
2248 newUri.schemeStr = schemeStr;
2249 newUri.query = other.query;
2250 newUri.fragment = other.fragment;
2251 if (other.authority.size() > 0)
2252 {
2253 //# 3.2
2254 if (absolute || other.absolute)
2255 newUri.absolute = true;
2256 newUri.authority = other.authority;
2257 newUri.port = other.port;//part of authority
2258 newUri.path = other.path;
2259 }
2260 else
2261 {
2262 //# 3.3
2263 if (other.absolute)
2264 {
2265 newUri.absolute = true;
2266 newUri.path = other.path;
2267 }
2268 else
2269 {
2270 unsigned int pos = path.find_last_of('/');
2271 if (pos != path.npos)
2272 {
2273 String tpath = path.substr(0, pos+1);
2274 tpath.append(other.path);
2275 newUri.path = tpath;
2276 }
2277 else
2278 newUri.path = other.path;
2279 }
2280 }
2282 newUri.normalize();
2283 return newUri;
2284 }
2287 /**
2288 * This follows the Java URI algorithm:
2289 * 1. All "." segments are removed.
2290 * 2. If a ".." segment is preceded by a non-".." segment
2291 * then both of these segments are removed. This step
2292 * is repeated until it is no longer applicable.
2293 * 3. If the path is relative, and if its first segment
2294 * contains a colon character (':'), then a "." segment
2295 * is prepended. This prevents a relative URI with a path
2296 * such as "a:b/c/d" from later being re-parsed as an
2297 * opaque URI with a scheme of "a" and a scheme-specific
2298 * part of "b/c/d". (Deviation from RFC 2396)
2299 */
2300 void URI::normalize()
2301 {
2302 std::vector<String> segments;
2304 //## Collect segments
2305 if (path.size()<2)
2306 return;
2307 bool abs = false;
2308 unsigned int pos=0;
2309 if (path[0]=='/')
2310 {
2311 abs = true;
2312 pos++;
2313 }
2314 while (pos < path.size())
2315 {
2316 unsigned int pos2 = path.find('/', pos);
2317 if (pos2==path.npos)
2318 {
2319 String seg = path.substr(pos);
2320 //printf("last segment:%s\n", seg.c_str());
2321 segments.push_back(seg);
2322 break;
2323 }
2324 if (pos2>pos)
2325 {
2326 String seg = path.substr(pos, pos2-pos);
2327 //printf("segment:%s\n", seg.c_str());
2328 segments.push_back(seg);
2329 }
2330 pos = pos2;
2331 pos++;
2332 }
2334 //## Clean up (normalize) segments
2335 bool edited = false;
2336 std::vector<String>::iterator iter;
2337 for (iter=segments.begin() ; iter!=segments.end() ; )
2338 {
2339 String s = *iter;
2340 if (s == ".")
2341 {
2342 iter = segments.erase(iter);
2343 edited = true;
2344 }
2345 else if (s == ".." &&
2346 iter != segments.begin() &&
2347 *(iter-1) != "..")
2348 {
2349 iter--; //back up, then erase two entries
2350 iter = segments.erase(iter);
2351 iter = segments.erase(iter);
2352 edited = true;
2353 }
2354 else
2355 iter++;
2356 }
2358 //## Rebuild path, if necessary
2359 if (edited)
2360 {
2361 path.clear();
2362 if (abs)
2363 {
2364 path.append("/");
2365 }
2366 std::vector<String>::iterator iter;
2367 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2368 {
2369 if (iter != segments.begin())
2370 path.append("/");
2371 path.append(*iter);
2372 }
2373 }
2375 }
2379 //#########################################################################
2380 //# M E S S A G E S
2381 //#########################################################################
2383 void URI::error(const char *fmt, ...)
2384 {
2385 va_list args;
2386 fprintf(stderr, "URI error: ");
2387 va_start(args, fmt);
2388 vfprintf(stderr, fmt, args);
2389 va_end(args);
2390 fprintf(stderr, "\n");
2391 }
2393 void URI::trace(const char *fmt, ...)
2394 {
2395 va_list args;
2396 fprintf(stdout, "URI: ");
2397 va_start(args, fmt);
2398 vfprintf(stdout, fmt, args);
2399 va_end(args);
2400 fprintf(stdout, "\n");
2401 }
2405 //#########################################################################
2406 //# P A R S I N G
2407 //#########################################################################
2411 int URI::peek(int p)
2412 {
2413 if (p<0 || p>=parselen)
2414 return -1;
2415 return parsebuf[p];
2416 }
2420 int URI::match(int p0, char *key)
2421 {
2422 int p = p0;
2423 while (p < parselen)
2424 {
2425 if (*key == '\0')
2426 return p;
2427 else if (*key != parsebuf[p])
2428 break;
2429 p++; key++;
2430 }
2431 return p0;
2432 }
2434 //#########################################################################
2435 //# Parsing is performed according to:
2436 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2437 //#########################################################################
2439 int URI::parseScheme(int p0)
2440 {
2441 int p = p0;
2442 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2443 {
2444 int p2 = match(p, entry->sval);
2445 if (p2 > p)
2446 {
2447 schemeStr = entry->sval;
2448 scheme = entry->ival;
2449 port = entry->port;
2450 p = p2;
2451 return p;
2452 }
2453 }
2455 return p;
2456 }
2459 int URI::parseHierarchicalPart(int p0)
2460 {
2461 int p = p0;
2462 int ch;
2464 //# Authority field (host and port, for example)
2465 int p2 = match(p, "//");
2466 if (p2 > p)
2467 {
2468 p = p2;
2469 portSpecified = false;
2470 String portStr;
2471 while (p < parselen)
2472 {
2473 ch = peek(p);
2474 if (ch == '/')
2475 break;
2476 else if (ch == ':')
2477 portSpecified = true;
2478 else if (portSpecified)
2479 portStr.push_back((XMLCh)ch);
2480 else
2481 authority.push_back((XMLCh)ch);
2482 p++;
2483 }
2484 if (portStr.size() > 0)
2485 {
2486 char *pstr = (char *)portStr.c_str();
2487 char *endStr;
2488 long val = strtol(pstr, &endStr, 10);
2489 if (endStr > pstr) //successful parse?
2490 port = val;
2491 }
2492 }
2494 //# Are we absolute?
2495 ch = peek(p);
2496 if (isLetter(ch) && peek(p+1)==':')
2497 {
2498 absolute = true;
2499 path.push_back((XMLCh)'/');
2500 }
2501 else if (ch == '/')
2502 {
2503 absolute = true;
2504 if (p>p0) //in other words, if '/' is not the first char
2505 opaque = true;
2506 path.push_back((XMLCh)ch);
2507 p++;
2508 }
2510 while (p < parselen)
2511 {
2512 ch = peek(p);
2513 if (ch == '?' || ch == '#')
2514 break;
2515 path.push_back((XMLCh)ch);
2516 p++;
2517 }
2519 return p;
2520 }
2522 int URI::parseQuery(int p0)
2523 {
2524 int p = p0;
2525 int ch = peek(p);
2526 if (ch != '?')
2527 return p0;
2529 p++;
2530 while (p < parselen)
2531 {
2532 ch = peek(p);
2533 if (ch == '#')
2534 break;
2535 query.push_back((XMLCh)ch);
2536 p++;
2537 }
2540 return p;
2541 }
2543 int URI::parseFragment(int p0)
2544 {
2546 int p = p0;
2547 int ch = peek(p);
2548 if (ch != '#')
2549 return p0;
2551 p++;
2552 while (p < parselen)
2553 {
2554 ch = peek(p);
2555 if (ch == '?')
2556 break;
2557 fragment.push_back((XMLCh)ch);
2558 p++;
2559 }
2562 return p;
2563 }
2566 int URI::parse(int p0)
2567 {
2569 int p = p0;
2571 int p2 = parseScheme(p);
2572 if (p2 < 0)
2573 {
2574 error("Scheme");
2575 return -1;
2576 }
2577 p = p2;
2580 p2 = parseHierarchicalPart(p);
2581 if (p2 < 0)
2582 {
2583 error("Hierarchical part");
2584 return -1;
2585 }
2586 p = p2;
2588 p2 = parseQuery(p);
2589 if (p2 < 0)
2590 {
2591 error("Query");
2592 return -1;
2593 }
2594 p = p2;
2597 p2 = parseFragment(p);
2598 if (p2 < 0)
2599 {
2600 error("Fragment");
2601 return -1;
2602 }
2603 p = p2;
2605 return p;
2607 }
2611 bool URI::parse(const String &str)
2612 {
2614 parselen = str.size();
2616 String tmp;
2617 for (unsigned int i=0 ; i<str.size() ; i++)
2618 {
2619 XMLCh ch = (XMLCh) str[i];
2620 if (ch == '\\')
2621 tmp.push_back((XMLCh)'/');
2622 else
2623 tmp.push_back(ch);
2624 }
2625 parsebuf = (char *) tmp.c_str();
2628 int p = parse(0);
2629 normalize();
2631 if (p < 0)
2632 {
2633 error("Syntax error");
2634 return false;
2635 }
2637 //printf("uri:%s\n", toString().c_str());
2638 //printf("path:%s\n", path.c_str());
2640 return true;
2642 }
2651 //########################################################################
2652 //########################################################################
2653 //## M A K E
2654 //########################################################################
2655 //########################################################################
2657 //########################################################################
2658 //# F I L E S E T
2659 //########################################################################
2660 /**
2661 * This is the descriptor for a <fileset> item
2662 */
2663 class FileSet
2664 {
2665 public:
2667 /**
2668 *
2669 */
2670 FileSet()
2671 {}
2673 /**
2674 *
2675 */
2676 FileSet(const FileSet &other)
2677 { assign(other); }
2679 /**
2680 *
2681 */
2682 FileSet &operator=(const FileSet &other)
2683 { assign(other); return *this; }
2685 /**
2686 *
2687 */
2688 virtual ~FileSet()
2689 {}
2691 /**
2692 *
2693 */
2694 String getDirectory()
2695 { return directory; }
2697 /**
2698 *
2699 */
2700 void setDirectory(const String &val)
2701 { directory = val; }
2703 /**
2704 *
2705 */
2706 void setFiles(const std::vector<String> &val)
2707 { files = val; }
2709 /**
2710 *
2711 */
2712 std::vector<String> getFiles()
2713 { return files; }
2715 /**
2716 *
2717 */
2718 void setIncludes(const std::vector<String> &val)
2719 { includes = val; }
2721 /**
2722 *
2723 */
2724 std::vector<String> getIncludes()
2725 { return includes; }
2727 /**
2728 *
2729 */
2730 void setExcludes(const std::vector<String> &val)
2731 { excludes = val; }
2733 /**
2734 *
2735 */
2736 std::vector<String> getExcludes()
2737 { return excludes; }
2739 /**
2740 *
2741 */
2742 unsigned int size()
2743 { return files.size(); }
2745 /**
2746 *
2747 */
2748 String operator[](int index)
2749 { return files[index]; }
2751 /**
2752 *
2753 */
2754 void clear()
2755 {
2756 directory = "";
2757 files.clear();
2758 includes.clear();
2759 excludes.clear();
2760 }
2763 private:
2765 void assign(const FileSet &other)
2766 {
2767 directory = other.directory;
2768 files = other.files;
2769 includes = other.includes;
2770 excludes = other.excludes;
2771 }
2773 String directory;
2774 std::vector<String> files;
2775 std::vector<String> includes;
2776 std::vector<String> excludes;
2777 };
2782 //########################################################################
2783 //# M A K E B A S E
2784 //########################################################################
2785 /**
2786 * Base class for all classes in this file
2787 */
2788 class MakeBase
2789 {
2790 public:
2791 MakeBase()
2792 {}
2793 virtual ~MakeBase()
2794 {}
2796 /**
2797 * Return the URI of the file associated with this object
2798 */
2799 URI getURI()
2800 { return uri; }
2802 /**
2803 * Set the uri to the given string
2804 */
2805 void setURI(const String &uristr)
2806 { uri.parse(uristr); }
2808 /**
2809 * Resolve another path relative to this one
2810 */
2811 String resolve(const String &otherPath);
2813 /**
2814 * Get an element attribute, performing substitutions if necessary
2815 */
2816 bool getAttribute(Element *elem, const String &name, String &result);
2818 /**
2819 * Get an element value, performing substitutions if necessary
2820 */
2821 bool getValue(Element *elem, String &result);
2823 protected:
2825 /**
2826 * The path to the file associated with this object
2827 */
2828 URI uri;
2831 /**
2832 * Print a printf()-like formatted error message
2833 */
2834 void error(char *fmt, ...);
2836 /**
2837 * Print a printf()-like formatted trace message
2838 */
2839 void status(char *fmt, ...);
2841 /**
2842 * Print a printf()-like formatted trace message
2843 */
2844 void trace(char *fmt, ...);
2846 /**
2847 * Check if a given string matches a given regex pattern
2848 */
2849 bool regexMatch(const String &str, const String &pattern);
2851 /**
2852 *
2853 */
2854 String getSuffix(const String &fname);
2856 /**
2857 * Break up a string into substrings delimited the characters
2858 * in delimiters. Null-length substrings are ignored
2859 */
2860 std::vector<String> tokenize(const String &val,
2861 const String &delimiters);
2863 /**
2864 * replace runs of whitespace with a space
2865 */
2866 String strip(const String &s);
2868 /**
2869 * remove leading whitespace from each line
2870 */
2871 String leftJustify(const String &s);
2873 /**
2874 * remove leading and trailing whitespace from string
2875 */
2876 String trim(const String &s);
2878 /**
2879 * Return the native format of the canonical
2880 * path which we store
2881 */
2882 String getNativePath(const String &path);
2884 /**
2885 * Execute a shell command. Outbuf is a ref to a string
2886 * to catch the result.
2887 */
2888 bool executeCommand(const String &call,
2889 const String &inbuf,
2890 String &outbuf,
2891 String &errbuf);
2892 /**
2893 * List all directories in a given base and starting directory
2894 * It is usually called like:
2895 * bool ret = listDirectories("src", "", result);
2896 */
2897 bool listDirectories(const String &baseName,
2898 const String &dirname,
2899 std::vector<String> &res);
2901 /**
2902 * Find all files in the named directory
2903 */
2904 bool listFiles(const String &baseName,
2905 const String &dirname,
2906 std::vector<String> &result);
2908 /**
2909 * Perform a listing for a fileset
2910 */
2911 bool listFiles(MakeBase &propRef, FileSet &fileSet);
2913 /**
2914 * Parse a <patternset>
2915 */
2916 bool parsePatternSet(Element *elem,
2917 MakeBase &propRef,
2918 std::vector<String> &includes,
2919 std::vector<String> &excludes);
2921 /**
2922 * Parse a <fileset> entry, and determine which files
2923 * should be included
2924 */
2925 bool parseFileSet(Element *elem,
2926 MakeBase &propRef,
2927 FileSet &fileSet);
2929 /**
2930 * Return this object's property list
2931 */
2932 virtual std::map<String, String> &getProperties()
2933 { return properties; }
2935 /**
2936 * Return a named property if found, else a null string
2937 */
2938 virtual String getProperty(const String &name)
2939 {
2940 String val;
2941 std::map<String, String>::iterator iter;
2942 iter = properties.find(name);
2943 if (iter != properties.end())
2944 val = iter->second;
2945 return val;
2946 }
2949 std::map<String, String> properties;
2951 /**
2952 * Turn 'true' and 'false' into boolean values
2953 */
2954 bool getBool(const String &str, bool &val);
2956 /**
2957 * Create a directory, making intermediate dirs
2958 * if necessary
2959 */
2960 bool createDirectory(const String &dirname);
2962 /**
2963 * Delete a directory and its children if desired
2964 */
2965 bool removeDirectory(const String &dirName);
2967 /**
2968 * Copy a file from one name to another. Perform only if needed
2969 */
2970 bool copyFile(const String &srcFile, const String &destFile);
2972 /**
2973 * Tests if the file exists and is a regular file
2974 */
2975 bool isRegularFile(const String &fileName);
2977 /**
2978 * Tests if the file exists and is a directory
2979 */
2980 bool isDirectory(const String &fileName);
2982 /**
2983 * Tests is the modification date of fileA is newer than fileB
2984 */
2985 bool isNewerThan(const String &fileA, const String &fileB);
2987 private:
2989 /**
2990 * replace variable refs like ${a} with their values
2991 */
2992 bool getSubstitutions(const String &s, String &result);
2996 };
3001 /**
3002 * Print a printf()-like formatted error message
3003 */
3004 void MakeBase::error(char *fmt, ...)
3005 {
3006 va_list args;
3007 va_start(args,fmt);
3008 fprintf(stderr, "Make error: ");
3009 vfprintf(stderr, fmt, args);
3010 fprintf(stderr, "\n");
3011 va_end(args) ;
3012 }
3016 /**
3017 * Print a printf()-like formatted trace message
3018 */
3019 void MakeBase::status(char *fmt, ...)
3020 {
3021 va_list args;
3022 va_start(args,fmt);
3023 //fprintf(stdout, " ");
3024 vfprintf(stdout, fmt, args);
3025 fprintf(stdout, "\n");
3026 va_end(args) ;
3027 }
3031 /**
3032 * Resolve another path relative to this one
3033 */
3034 String MakeBase::resolve(const String &otherPath)
3035 {
3036 URI otherURI(otherPath);
3037 URI fullURI = uri.resolve(otherURI);
3038 String ret = fullURI.toString();
3039 return ret;
3040 }
3043 /**
3044 * Print a printf()-like formatted trace message
3045 */
3046 void MakeBase::trace(char *fmt, ...)
3047 {
3048 va_list args;
3049 va_start(args,fmt);
3050 fprintf(stdout, "Make: ");
3051 vfprintf(stdout, fmt, args);
3052 fprintf(stdout, "\n");
3053 va_end(args) ;
3054 }
3058 /**
3059 * Check if a given string matches a given regex pattern
3060 */
3061 bool MakeBase::regexMatch(const String &str, const String &pattern)
3062 {
3063 const TRexChar *terror = NULL;
3064 const TRexChar *cpat = pattern.c_str();
3065 TRex *expr = trex_compile(cpat, &terror);
3066 if (!expr)
3067 {
3068 if (!terror)
3069 terror = "undefined";
3070 error("compilation error [%s]!\n", terror);
3071 return false;
3072 }
3074 bool ret = true;
3076 const TRexChar *cstr = str.c_str();
3077 if (trex_match(expr, cstr))
3078 {
3079 ret = true;
3080 }
3081 else
3082 {
3083 ret = false;
3084 }
3086 trex_free(expr);
3088 return ret;
3089 }
3091 /**
3092 * Return the suffix, if any, of a file name
3093 */
3094 String MakeBase::getSuffix(const String &fname)
3095 {
3096 if (fname.size() < 2)
3097 return "";
3098 unsigned int pos = fname.find_last_of('.');
3099 if (pos == fname.npos)
3100 return "";
3101 pos++;
3102 String res = fname.substr(pos, fname.size()-pos);
3103 //trace("suffix:%s", res.c_str());
3104 return res;
3105 }
3109 /**
3110 * Break up a string into substrings delimited the characters
3111 * in delimiters. Null-length substrings are ignored
3112 */
3113 std::vector<String> MakeBase::tokenize(const String &str,
3114 const String &delimiters)
3115 {
3117 std::vector<String> res;
3118 char *del = (char *)delimiters.c_str();
3119 String dmp;
3120 for (unsigned int i=0 ; i<str.size() ; i++)
3121 {
3122 char ch = str[i];
3123 char *p = (char *)0;
3124 for (p=del ; *p ; p++)
3125 if (*p == ch)
3126 break;
3127 if (*p)
3128 {
3129 if (dmp.size() > 0)
3130 {
3131 res.push_back(dmp);
3132 dmp.clear();
3133 }
3134 }
3135 else
3136 {
3137 dmp.push_back(ch);
3138 }
3139 }
3140 //Add tail
3141 if (dmp.size() > 0)
3142 {
3143 res.push_back(dmp);
3144 dmp.clear();
3145 }
3147 return res;
3148 }
3152 /**
3153 * replace runs of whitespace with a single space
3154 */
3155 String MakeBase::strip(const String &s)
3156 {
3157 int len = s.size();
3158 String stripped;
3159 for (int i = 0 ; i<len ; i++)
3160 {
3161 char ch = s[i];
3162 if (isspace(ch))
3163 {
3164 stripped.push_back(' ');
3165 for ( ; i<len ; i++)
3166 {
3167 ch = s[i];
3168 if (!isspace(ch))
3169 {
3170 stripped.push_back(ch);
3171 break;
3172 }
3173 }
3174 }
3175 else
3176 {
3177 stripped.push_back(ch);
3178 }
3179 }
3180 return stripped;
3181 }
3183 /**
3184 * remove leading whitespace from each line
3185 */
3186 String MakeBase::leftJustify(const String &s)
3187 {
3188 String out;
3189 int len = s.size();
3190 for (int i = 0 ; i<len ; )
3191 {
3192 char ch;
3193 //Skip to first visible character
3194 while (i<len)
3195 {
3196 ch = s[i];
3197 if (ch == '\n' || ch == '\r'
3198 || !isspace(ch))
3199 break;
3200 i++;
3201 }
3202 //Copy the rest of the line
3203 while (i<len)
3204 {
3205 ch = s[i];
3206 if (ch == '\n' || ch == '\r')
3207 {
3208 if (ch != '\r')
3209 out.push_back('\n');
3210 i++;
3211 break;
3212 }
3213 else
3214 {
3215 out.push_back(ch);
3216 }
3217 i++;
3218 }
3219 }
3220 return out;
3221 }
3224 /**
3225 * Removes whitespace from beginning and end of a string
3226 */
3227 String MakeBase::trim(const String &s)
3228 {
3229 if (s.size() < 1)
3230 return s;
3232 //Find first non-ws char
3233 unsigned int begin = 0;
3234 for ( ; begin < s.size() ; begin++)
3235 {
3236 if (!isspace(s[begin]))
3237 break;
3238 }
3240 //Find first non-ws char, going in reverse
3241 unsigned int end = s.size() - 1;
3242 for ( ; end > begin ; end--)
3243 {
3244 if (!isspace(s[end]))
3245 break;
3246 }
3247 //trace("begin:%d end:%d", begin, end);
3249 String res = s.substr(begin, end-begin+1);
3250 return res;
3251 }
3253 /**
3254 * Return the native format of the canonical
3255 * path which we store
3256 */
3257 String MakeBase::getNativePath(const String &path)
3258 {
3259 #ifdef __WIN32__
3260 String npath;
3261 unsigned int firstChar = 0;
3262 if (path.size() >= 3)
3263 {
3264 if (path[0] == '/' &&
3265 isalpha(path[1]) &&
3266 path[2] == ':')
3267 firstChar++;
3268 }
3269 for (unsigned int i=firstChar ; i<path.size() ; i++)
3270 {
3271 char ch = path[i];
3272 if (ch == '/')
3273 npath.push_back('\\');
3274 else
3275 npath.push_back(ch);
3276 }
3277 return npath;
3278 #else
3279 return path;
3280 #endif
3281 }
3284 #ifdef __WIN32__
3285 #include <tchar.h>
3287 static String win32LastError()
3288 {
3290 DWORD dw = GetLastError();
3292 LPVOID str;
3293 FormatMessage(
3294 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3295 FORMAT_MESSAGE_FROM_SYSTEM,
3296 NULL,
3297 dw,
3298 0,
3299 (LPTSTR) &str,
3300 0, NULL );
3301 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3302 if(p != NULL)
3303 { // lose CRLF
3304 *p = _T('\0');
3305 }
3306 String ret = (char *)str;
3307 LocalFree(str);
3309 return ret;
3310 }
3311 #endif
3315 /**
3316 * Execute a system call, using pipes to send data to the
3317 * program's stdin, and reading stdout and stderr.
3318 */
3319 bool MakeBase::executeCommand(const String &command,
3320 const String &inbuf,
3321 String &outbuf,
3322 String &errbuf)
3323 {
3325 status("============ cmd ============\n%s\n=============================",
3326 command.c_str());
3328 #ifdef __WIN32__
3330 /*
3331 I really hate having win32 code in this program, but the
3332 read buffer in command.com and cmd.exe are just too small
3333 for the large commands we need for compiling and linking.
3334 */
3336 bool ret = true;
3338 //# Allocate a separate buffer for safety
3339 char *paramBuf = new char[command.size() + 1];
3340 if (!paramBuf)
3341 {
3342 error("executeCommand cannot allocate command buffer");
3343 return false;
3344 }
3345 strcpy(paramBuf, (char *)command.c_str());
3347 //# Create pipes
3348 SECURITY_ATTRIBUTES saAttr;
3349 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3350 saAttr.bInheritHandle = TRUE;
3351 saAttr.lpSecurityDescriptor = NULL;
3352 HANDLE stdinRead, stdinWrite;
3353 HANDLE stdoutRead, stdoutWrite;
3354 HANDLE stderrRead, stderrWrite;
3355 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3356 {
3357 error("executeProgram: could not create pipe");
3358 delete[] paramBuf;
3359 return false;
3360 }
3361 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3362 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3363 {
3364 error("executeProgram: could not create pipe");
3365 delete[] paramBuf;
3366 return false;
3367 }
3368 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3369 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3370 {
3371 error("executeProgram: could not create pipe");
3372 delete[] paramBuf;
3373 return false;
3374 }
3375 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3377 // Create the process
3378 STARTUPINFO siStartupInfo;
3379 PROCESS_INFORMATION piProcessInfo;
3380 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3381 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3382 siStartupInfo.cb = sizeof(siStartupInfo);
3383 siStartupInfo.hStdError = stderrWrite;
3384 siStartupInfo.hStdOutput = stdoutWrite;
3385 siStartupInfo.hStdInput = stdinRead;
3386 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3388 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3389 0, NULL, NULL, &siStartupInfo,
3390 &piProcessInfo))
3391 {
3392 error("executeCommand : could not create process : %s",
3393 win32LastError().c_str());
3394 ret = false;
3395 }
3397 DWORD bytesWritten;
3398 if (inbuf.size()>0 &&
3399 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3400 &bytesWritten, NULL))
3401 {
3402 error("executeCommand: could not write to pipe");
3403 return false;
3404 }
3405 if (!CloseHandle(stdinWrite))
3406 {
3407 error("executeCommand: could not close write pipe");
3408 return false;
3409 }
3410 if (!CloseHandle(stdoutWrite))
3411 {
3412 error("executeCommand: could not close read pipe");
3413 return false;
3414 }
3415 if (!CloseHandle(stderrWrite))
3416 {
3417 error("executeCommand: could not close read pipe");
3418 return false;
3419 }
3420 while (true)
3421 {
3422 //trace("## stderr");
3423 DWORD avail;
3424 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3425 break;
3426 if (avail > 0)
3427 {
3428 DWORD bytesRead = 0;
3429 char readBuf[1025];
3430 if (avail>1024) avail = 1024;
3431 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3432 || bytesRead == 0)
3433 {
3434 break;
3435 }
3436 for (unsigned int i=0 ; i<bytesRead ; i++)
3437 errbuf.push_back(readBuf[i]);
3438 }
3439 //trace("## stdout");
3440 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3441 break;
3442 if (avail > 0)
3443 {
3444 DWORD bytesRead = 0;
3445 char readBuf[1025];
3446 if (avail>1024) avail = 1024;
3447 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3448 || bytesRead==0)
3449 {
3450 break;
3451 }
3452 for (unsigned int i=0 ; i<bytesRead ; i++)
3453 outbuf.push_back(readBuf[i]);
3454 }
3455 DWORD exitCode;
3456 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3457 if (exitCode != STILL_ACTIVE)
3458 break;
3459 Sleep(100);
3460 }
3461 //trace("outbuf:%s", outbuf.c_str());
3462 if (!CloseHandle(stdoutRead))
3463 {
3464 error("executeCommand: could not close read pipe");
3465 return false;
3466 }
3467 if (!CloseHandle(stderrRead))
3468 {
3469 error("executeCommand: could not close read pipe");
3470 return false;
3471 }
3473 DWORD exitCode;
3474 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3475 //trace("exit code:%d", exitCode);
3476 if (exitCode != 0)
3477 {
3478 ret = false;
3479 }
3481 // Clean up
3482 CloseHandle(piProcessInfo.hProcess);
3483 CloseHandle(piProcessInfo.hThread);
3486 return ret;
3488 #else //do it unix-style
3490 String s;
3491 FILE *f = popen(command.c_str(), "r");
3492 int errnum = 0;
3493 if (f)
3494 {
3495 while (true)
3496 {
3497 int ch = fgetc(f);
3498 if (ch < 0)
3499 break;
3500 s.push_back((char)ch);
3501 }
3502 errnum = pclose(f);
3503 }
3504 outbuf = s;
3505 if (errnum < 0)
3506 {
3507 error("exec of command '%s' failed : %s",
3508 command.c_str(), strerror(errno));
3509 return false;
3510 }
3511 else
3512 return true;
3514 #endif
3515 }
3520 bool MakeBase::listDirectories(const String &baseName,
3521 const String &dirName,
3522 std::vector<String> &res)
3523 {
3524 res.push_back(dirName);
3525 String fullPath = baseName;
3526 if (dirName.size()>0)
3527 {
3528 fullPath.append("/");
3529 fullPath.append(dirName);
3530 }
3531 DIR *dir = opendir(fullPath.c_str());
3532 while (true)
3533 {
3534 struct dirent *de = readdir(dir);
3535 if (!de)
3536 break;
3538 //Get the directory member name
3539 String s = de->d_name;
3540 if (s.size() == 0 || s[0] == '.')
3541 continue;
3542 String childName = dirName;
3543 childName.append("/");
3544 childName.append(s);
3546 String fullChildPath = baseName;
3547 fullChildPath.append("/");
3548 fullChildPath.append(childName);
3549 struct stat finfo;
3550 String childNative = getNativePath(fullChildPath);
3551 if (stat(childNative.c_str(), &finfo)<0)
3552 {
3553 error("cannot stat file:%s", childNative.c_str());
3554 }
3555 else if (S_ISDIR(finfo.st_mode))
3556 {
3557 //trace("directory: %s", childName.c_str());
3558 if (!listDirectories(baseName, childName, res))
3559 return false;
3560 }
3561 }
3562 closedir(dir);
3564 return true;
3565 }
3568 bool MakeBase::listFiles(const String &baseDir,
3569 const String &dirName,
3570 std::vector<String> &res)
3571 {
3572 String fullDir = baseDir;
3573 if (dirName.size()>0)
3574 {
3575 fullDir.append("/");
3576 fullDir.append(dirName);
3577 }
3578 String dirNative = getNativePath(fullDir);
3580 std::vector<String> subdirs;
3581 DIR *dir = opendir(dirNative.c_str());
3582 while (true)
3583 {
3584 struct dirent *de = readdir(dir);
3585 if (!de)
3586 break;
3588 //Get the directory member name
3589 String s = de->d_name;
3590 if (s.size() == 0 || s[0] == '.')
3591 continue;
3592 String childName;
3593 if (dirName.size()>0)
3594 {
3595 childName.append(dirName);
3596 childName.append("/");
3597 }
3598 childName.append(s);
3599 String fullChild = baseDir;
3600 fullChild.append("/");
3601 fullChild.append(childName);
3603 if (isDirectory(fullChild))
3604 {
3605 //trace("directory: %s", childName.c_str());
3606 if (!listFiles(baseDir, childName, res))
3607 return false;
3608 continue;
3609 }
3610 else if (!isRegularFile(fullChild))
3611 {
3612 error("unknown file:%s", childName.c_str());
3613 return false;
3614 }
3616 //all done!
3617 res.push_back(childName);
3619 }
3620 closedir(dir);
3622 return true;
3623 }
3626 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3627 {
3628 String baseDir = propRef.resolve(fileSet.getDirectory());
3629 std::vector<String> fileList;
3630 if (!listFiles(baseDir, "", fileList))
3631 return false;
3633 std::vector<String> includes = fileSet.getIncludes();
3634 std::vector<String> excludes = fileSet.getExcludes();
3636 std::vector<String> incs;
3637 std::vector<String>::iterator iter;
3639 std::sort(fileList.begin(), fileList.end());
3641 //If there are <includes>, then add files to the output
3642 //in the order of the include list
3643 if (includes.size()==0)
3644 incs = fileList;
3645 else
3646 {
3647 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3648 {
3649 String pattern = *iter;
3650 std::vector<String>::iterator siter;
3651 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3652 {
3653 String s = *siter;
3654 if (regexMatch(s, pattern))
3655 {
3656 //trace("INCLUDED:%s", s.c_str());
3657 incs.push_back(s);
3658 }
3659 }
3660 }
3661 }
3663 //Now trim off the <excludes>
3664 std::vector<String> res;
3665 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3666 {
3667 String s = *iter;
3668 bool skipme = false;
3669 std::vector<String>::iterator siter;
3670 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3671 {
3672 String pattern = *siter;
3673 if (regexMatch(s, pattern))
3674 {
3675 //trace("EXCLUDED:%s", s.c_str());
3676 skipme = true;
3677 break;
3678 }
3679 }
3680 if (!skipme)
3681 res.push_back(s);
3682 }
3684 fileSet.setFiles(res);
3686 return true;
3687 }
3693 bool MakeBase::getSubstitutions(const String &str, String &result)
3694 {
3695 String s = trim(str);
3696 int len = (int)s.size();
3697 String val;
3698 for (int i=0 ; i<len ; i++)
3699 {
3700 char ch = s[i];
3701 if (ch == '$' && s[i+1] == '{')
3702 {
3703 String varname;
3704 int j = i+2;
3705 for ( ; j<len ; j++)
3706 {
3707 ch = s[j];
3708 if (ch == '$' && s[j+1] == '{')
3709 {
3710 error("attribute %s cannot have nested variable references",
3711 s.c_str());
3712 return false;
3713 }
3714 else if (ch == '}')
3715 {
3716 std::map<String, String>::iterator iter;
3717 iter = properties.find(trim(varname));
3718 if (iter != properties.end())
3719 {
3720 val.append(iter->second);
3721 }
3722 else
3723 {
3724 error("property ${%s} not found", varname.c_str());
3725 return false;
3726 }
3727 break;
3728 }
3729 else
3730 {
3731 varname.push_back(ch);
3732 }
3733 }
3734 i = j;
3735 }
3736 else
3737 {
3738 val.push_back(ch);
3739 }
3740 }
3741 result = val;
3742 return true;
3743 }
3746 bool MakeBase::getAttribute(Element *elem, const String &name,
3747 String &result)
3748 {
3749 String s = elem->getAttribute(name);
3750 return getSubstitutions(s, result);
3751 }
3754 bool MakeBase::getValue(Element *elem, String &result)
3755 {
3756 String s = elem->getValue();
3757 //Replace all runs of whitespace with a single space
3758 return getSubstitutions(s, result);
3759 }
3762 /**
3763 * Turn 'true' and 'false' into boolean values
3764 */
3765 bool MakeBase::getBool(const String &str, bool &val)
3766 {
3767 if (str == "true")
3768 val = true;
3769 else if (str == "false")
3770 val = false;
3771 else
3772 {
3773 error("expected 'true' or 'false'. found '%s'", str.c_str());
3774 return false;
3775 }
3776 return true;
3777 }
3782 /**
3783 * Parse a <patternset> entry
3784 */
3785 bool MakeBase::parsePatternSet(Element *elem,
3786 MakeBase &propRef,
3787 std::vector<String> &includes,
3788 std::vector<String> &excludes
3789 )
3790 {
3791 std::vector<Element *> children = elem->getChildren();
3792 for (unsigned int i=0 ; i<children.size() ; i++)
3793 {
3794 Element *child = children[i];
3795 String tagName = child->getName();
3796 if (tagName == "exclude")
3797 {
3798 String fname;
3799 if (!propRef.getAttribute(child, "name", fname))
3800 return false;
3801 //trace("EXCLUDE: %s", fname.c_str());
3802 excludes.push_back(fname);
3803 }
3804 else if (tagName == "include")
3805 {
3806 String fname;
3807 if (!propRef.getAttribute(child, "name", fname))
3808 return false;
3809 //trace("INCLUDE: %s", fname.c_str());
3810 includes.push_back(fname);
3811 }
3812 }
3814 return true;
3815 }
3820 /**
3821 * Parse a <fileset> entry, and determine which files
3822 * should be included
3823 */
3824 bool MakeBase::parseFileSet(Element *elem,
3825 MakeBase &propRef,
3826 FileSet &fileSet)
3827 {
3828 String name = elem->getName();
3829 if (name != "fileset")
3830 {
3831 error("expected <fileset>");
3832 return false;
3833 }
3836 std::vector<String> includes;
3837 std::vector<String> excludes;
3839 //A fileset has one implied patternset
3840 if (!parsePatternSet(elem, propRef, includes, excludes))
3841 {
3842 return false;
3843 }
3844 //Look for child tags, including more patternsets
3845 std::vector<Element *> children = elem->getChildren();
3846 for (unsigned int i=0 ; i<children.size() ; i++)
3847 {
3848 Element *child = children[i];
3849 String tagName = child->getName();
3850 if (tagName == "patternset")
3851 {
3852 if (!parsePatternSet(child, propRef, includes, excludes))
3853 {
3854 return false;
3855 }
3856 }
3857 }
3859 String dir;
3860 //Now do the stuff
3861 //Get the base directory for reading file names
3862 if (!propRef.getAttribute(elem, "dir", dir))
3863 return false;
3865 fileSet.setDirectory(dir);
3866 fileSet.setIncludes(includes);
3867 fileSet.setExcludes(excludes);
3869 /*
3870 std::vector<String> fileList;
3871 if (dir.size() > 0)
3872 {
3873 String baseDir = propRef.resolve(dir);
3874 if (!listFiles(baseDir, "", includes, excludes, fileList))
3875 return false;
3876 }
3877 std::sort(fileList.begin(), fileList.end());
3878 result = fileList;
3879 */
3882 /*
3883 for (unsigned int i=0 ; i<result.size() ; i++)
3884 {
3885 trace("RES:%s", result[i].c_str());
3886 }
3887 */
3890 return true;
3891 }
3895 /**
3896 * Create a directory, making intermediate dirs
3897 * if necessary
3898 */
3899 bool MakeBase::createDirectory(const String &dirname)
3900 {
3901 //trace("## createDirectory: %s", dirname.c_str());
3902 //## first check if it exists
3903 struct stat finfo;
3904 String nativeDir = getNativePath(dirname);
3905 char *cnative = (char *) nativeDir.c_str();
3906 if (stat(dirname.c_str(), &finfo)==0)
3907 {
3908 if (!S_ISDIR(finfo.st_mode))
3909 {
3910 error("mkdir: file %s exists but is not a directory",
3911 cnative);
3912 return false;
3913 }
3914 else //exists
3915 {
3916 return true;
3917 }
3918 }
3920 //## 2: pull off the last path segment, if any,
3921 //## to make the dir 'above' this one, if necessary
3922 unsigned int pos = dirname.find_last_of('/');
3923 if (pos != dirname.npos)
3924 {
3925 String subpath = dirname.substr(0, pos);
3926 if (!createDirectory(subpath))
3927 return false;
3928 }
3930 //## 3: now make
3931 if (mkdir(cnative)<0)
3932 {
3933 error("cannot make directory %s", cnative);
3934 return false;
3935 }
3937 return true;
3938 }
3941 /**
3942 * Remove a directory recursively
3943 */
3944 bool MakeBase::removeDirectory(const String &dirName)
3945 {
3946 char *dname = (char *)dirName.c_str();
3948 DIR *dir = opendir(dname);
3949 if (!dir)
3950 {
3951 //# Let this fail nicely.
3952 return true;
3953 //error("error opening directory %s : %s", dname, strerror(errno));
3954 //return false;
3955 }
3957 while (true)
3958 {
3959 struct dirent *de = readdir(dir);
3960 if (!de)
3961 break;
3963 //Get the directory member name
3964 String s = de->d_name;
3965 if (s.size() == 0 || s[0] == '.')
3966 continue;
3967 String childName;
3968 if (dirName.size() > 0)
3969 {
3970 childName.append(dirName);
3971 childName.append("/");
3972 }
3973 childName.append(s);
3976 struct stat finfo;
3977 String childNative = getNativePath(childName);
3978 char *cnative = (char *)childNative.c_str();
3979 if (stat(cnative, &finfo)<0)
3980 {
3981 error("cannot stat file:%s", cnative);
3982 }
3983 else if (S_ISDIR(finfo.st_mode))
3984 {
3985 //trace("DEL dir: %s", childName.c_str());
3986 if (!removeDirectory(childName))
3987 {
3988 return false;
3989 }
3990 }
3991 else if (!S_ISREG(finfo.st_mode))
3992 {
3993 //trace("not regular: %s", cnative);
3994 }
3995 else
3996 {
3997 //trace("DEL file: %s", childName.c_str());
3998 if (remove(cnative)<0)
3999 {
4000 error("error deleting %s : %s",
4001 cnative, strerror(errno));
4002 return false;
4003 }
4004 }
4005 }
4006 closedir(dir);
4008 //Now delete the directory
4009 String native = getNativePath(dirName);
4010 if (rmdir(native.c_str())<0)
4011 {
4012 error("could not delete directory %s : %s",
4013 native.c_str() , strerror(errno));
4014 return false;
4015 }
4017 return true;
4019 }
4022 /**
4023 * Copy a file from one name to another. Perform only if needed
4024 */
4025 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4026 {
4027 //# 1 Check up-to-date times
4028 String srcNative = getNativePath(srcFile);
4029 struct stat srcinfo;
4030 if (stat(srcNative.c_str(), &srcinfo)<0)
4031 {
4032 error("source file %s for copy does not exist",
4033 srcNative.c_str());
4034 return false;
4035 }
4037 String destNative = getNativePath(destFile);
4038 struct stat destinfo;
4039 if (stat(destNative.c_str(), &destinfo)==0)
4040 {
4041 if (destinfo.st_mtime >= srcinfo.st_mtime)
4042 return true;
4043 }
4045 //# 2 prepare a destination directory if necessary
4046 unsigned int pos = destFile.find_last_of('/');
4047 if (pos != destFile.npos)
4048 {
4049 String subpath = destFile.substr(0, pos);
4050 if (!createDirectory(subpath))
4051 return false;
4052 }
4054 //# 3 do the data copy
4055 #ifndef __WIN32__
4057 FILE *srcf = fopen(srcNative.c_str(), "rb");
4058 if (!srcf)
4059 {
4060 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4061 return false;
4062 }
4063 FILE *destf = fopen(destNative.c_str(), "wb");
4064 if (!destf)
4065 {
4066 error("copyFile cannot open %s for writing", srcNative.c_str());
4067 return false;
4068 }
4070 while (!feof(srcf))
4071 {
4072 int ch = fgetc(srcf);
4073 if (ch<0)
4074 break;
4075 fputc(ch, destf);
4076 }
4078 fclose(destf);
4079 fclose(srcf);
4081 #else
4083 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4084 {
4085 error("copyFile from %s to %s failed",
4086 srcNative.c_str(), destNative.c_str());
4087 return false;
4088 }
4090 #endif /* __WIN32__ */
4093 return true;
4094 }
4098 /**
4099 * Tests if the file exists and is a regular file
4100 */
4101 bool MakeBase::isRegularFile(const String &fileName)
4102 {
4103 String native = getNativePath(fileName);
4104 struct stat finfo;
4106 //Exists?
4107 if (stat(native.c_str(), &finfo)<0)
4108 return false;
4111 //check the file mode
4112 if (!S_ISREG(finfo.st_mode))
4113 return false;
4115 return true;
4116 }
4118 /**
4119 * Tests if the file exists and is a directory
4120 */
4121 bool MakeBase::isDirectory(const String &fileName)
4122 {
4123 String native = getNativePath(fileName);
4124 struct stat finfo;
4126 //Exists?
4127 if (stat(native.c_str(), &finfo)<0)
4128 return false;
4131 //check the file mode
4132 if (!S_ISDIR(finfo.st_mode))
4133 return false;
4135 return true;
4136 }
4140 /**
4141 * Tests is the modification of fileA is newer than fileB
4142 */
4143 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4144 {
4145 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4146 String nativeA = getNativePath(fileA);
4147 struct stat infoA;
4148 //IF source does not exist, NOT newer
4149 if (stat(nativeA.c_str(), &infoA)<0)
4150 {
4151 return false;
4152 }
4154 String nativeB = getNativePath(fileB);
4155 struct stat infoB;
4156 //IF dest does not exist, YES, newer
4157 if (stat(nativeB.c_str(), &infoB)<0)
4158 {
4159 return true;
4160 }
4162 //check the actual times
4163 if (infoA.st_mtime > infoB.st_mtime)
4164 {
4165 return true;
4166 }
4168 return false;
4169 }
4172 //########################################################################
4173 //# P K G C O N F I G
4174 //########################################################################
4176 /**
4177 *
4178 */
4179 class PkgConfig : public MakeBase
4180 {
4182 public:
4184 /**
4185 *
4186 */
4187 PkgConfig()
4188 { init(); }
4190 /**
4191 *
4192 */
4193 PkgConfig(const String &namearg)
4194 { init(); name = namearg; }
4196 /**
4197 *
4198 */
4199 PkgConfig(const PkgConfig &other)
4200 { assign(other); }
4202 /**
4203 *
4204 */
4205 PkgConfig &operator=(const PkgConfig &other)
4206 { assign(other); return *this; }
4208 /**
4209 *
4210 */
4211 virtual ~PkgConfig()
4212 { }
4214 /**
4215 *
4216 */
4217 virtual String getName()
4218 { return name; }
4220 /**
4221 *
4222 */
4223 virtual String getDescription()
4224 { return description; }
4226 /**
4227 *
4228 */
4229 virtual String getCflags()
4230 { return cflags; }
4232 /**
4233 *
4234 */
4235 virtual String getLibs()
4236 { return libs; }
4238 /**
4239 *
4240 */
4241 virtual String getVersion()
4242 { return version; }
4244 /**
4245 *
4246 */
4247 virtual int getMajorVersion()
4248 { return majorVersion; }
4250 /**
4251 *
4252 */
4253 virtual int getMinorVersion()
4254 { return minorVersion; }
4256 /**
4257 *
4258 */
4259 virtual int getMicroVersion()
4260 { return microVersion; }
4262 /**
4263 *
4264 */
4265 virtual std::map<String, String> &getAttributes()
4266 { return attrs; }
4268 /**
4269 *
4270 */
4271 virtual std::vector<String> &getRequireList()
4272 { return requireList; }
4274 virtual bool readFile(const String &fileName);
4276 private:
4278 void init()
4279 {
4280 name = "";
4281 description = "";
4282 cflags = "";
4283 libs = "";
4284 requires = "";
4285 version = "";
4286 majorVersion = 0;
4287 minorVersion = 0;
4288 microVersion = 0;
4289 fileName = "";
4290 attrs.clear();
4291 requireList.clear();
4292 }
4294 void assign(const PkgConfig &other)
4295 {
4296 name = other.name;
4297 description = other.description;
4298 cflags = other.cflags;
4299 libs = other.libs;
4300 requires = other.requires;
4301 version = other.version;
4302 majorVersion = other.majorVersion;
4303 minorVersion = other.minorVersion;
4304 microVersion = other.microVersion;
4305 fileName = other.fileName;
4306 attrs = other.attrs;
4307 requireList = other.requireList;
4308 }
4312 int get(int pos);
4314 int skipwhite(int pos);
4316 int getword(int pos, String &ret);
4318 void parseRequires();
4320 void parseVersion();
4322 bool parse(const String &buf);
4324 void dumpAttrs();
4326 String name;
4328 String description;
4330 String cflags;
4332 String libs;
4334 String requires;
4336 String version;
4338 int majorVersion;
4340 int minorVersion;
4342 int microVersion;
4344 String fileName;
4346 std::map<String, String> attrs;
4348 std::vector<String> requireList;
4350 char *parsebuf;
4351 int parselen;
4352 };
4355 /**
4356 * Get a character from the buffer at pos. If out of range,
4357 * return -1 for safety
4358 */
4359 int PkgConfig::get(int pos)
4360 {
4361 if (pos>parselen)
4362 return -1;
4363 return parsebuf[pos];
4364 }
4368 /**
4369 * Skip over all whitespace characters beginning at pos. Return
4370 * the position of the first non-whitespace character.
4371 */
4372 int PkgConfig::skipwhite(int pos)
4373 {
4374 while (pos < parselen)
4375 {
4376 int ch = get(pos);
4377 if (ch < 0)
4378 break;
4379 if (!isspace(ch))
4380 break;
4381 pos++;
4382 }
4383 return pos;
4384 }
4387 /**
4388 * Parse the buffer beginning at pos, for a word. Fill
4389 * 'ret' with the result. Return the position after the
4390 * word.
4391 */
4392 int PkgConfig::getword(int pos, String &ret)
4393 {
4394 while (pos < parselen)
4395 {
4396 int ch = get(pos);
4397 if (ch < 0)
4398 break;
4399 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4400 break;
4401 ret.push_back((char)ch);
4402 pos++;
4403 }
4404 return pos;
4405 }
4407 void PkgConfig::parseRequires()
4408 {
4409 if (requires.size() == 0)
4410 return;
4411 parsebuf = (char *)requires.c_str();
4412 parselen = requires.size();
4413 int pos = 0;
4414 while (pos < parselen)
4415 {
4416 pos = skipwhite(pos);
4417 String val;
4418 int pos2 = getword(pos, val);
4419 if (pos2 == pos)
4420 break;
4421 pos = pos2;
4422 //trace("val %s", val.c_str());
4423 requireList.push_back(val);
4424 }
4425 }
4427 static int getint(const String str)
4428 {
4429 char *s = (char *)str.c_str();
4430 char *ends = NULL;
4431 long val = strtol(s, &ends, 10);
4432 if (ends == s)
4433 return 0L;
4434 else
4435 return val;
4436 }
4438 void PkgConfig::parseVersion()
4439 {
4440 if (version.size() == 0)
4441 return;
4442 String s1, s2, s3;
4443 unsigned int pos = 0;
4444 unsigned int pos2 = version.find('.', pos);
4445 if (pos2 == version.npos)
4446 {
4447 s1 = version;
4448 }
4449 else
4450 {
4451 s1 = version.substr(pos, pos2-pos);
4452 pos = pos2;
4453 pos++;
4454 if (pos < version.size())
4455 {
4456 pos2 = version.find('.', pos);
4457 if (pos2 == version.npos)
4458 {
4459 s2 = version.substr(pos, version.size()-pos);
4460 }
4461 else
4462 {
4463 s2 = version.substr(pos, pos2-pos);
4464 pos = pos2;
4465 pos++;
4466 if (pos < version.size())
4467 s3 = version.substr(pos, pos2-pos);
4468 }
4469 }
4470 }
4472 majorVersion = getint(s1);
4473 minorVersion = getint(s2);
4474 microVersion = getint(s3);
4475 //trace("version:%d.%d.%d", majorVersion,
4476 // minorVersion, microVersion );
4477 }
4480 bool PkgConfig::parse(const String &buf)
4481 {
4482 init();
4484 parsebuf = (char *)buf.c_str();
4485 parselen = buf.size();
4486 int pos = 0;
4489 while (pos < parselen)
4490 {
4491 String attrName;
4492 pos = skipwhite(pos);
4493 int ch = get(pos);
4494 if (ch == '#')
4495 {
4496 //comment. eat the rest of the line
4497 while (pos < parselen)
4498 {
4499 ch = get(pos);
4500 if (ch == '\n' || ch < 0)
4501 break;
4502 pos++;
4503 }
4504 continue;
4505 }
4506 pos = getword(pos, attrName);
4507 if (attrName.size() == 0)
4508 continue;
4509 pos = skipwhite(pos);
4510 ch = get(pos);
4511 if (ch != ':' && ch != '=')
4512 {
4513 error("expected ':' or '='");
4514 return false;
4515 }
4516 pos++;
4517 pos = skipwhite(pos);
4518 String attrVal;
4519 while (pos < parselen)
4520 {
4521 ch = get(pos);
4522 if (ch == '\n' || ch < 0)
4523 break;
4524 else if (ch == '$' && get(pos+1) == '{')
4525 {
4526 //# this is a ${substitution}
4527 pos += 2;
4528 String subName;
4529 while (pos < parselen)
4530 {
4531 ch = get(pos);
4532 if (ch < 0)
4533 {
4534 error("unterminated substitution");
4535 return false;
4536 }
4537 else if (ch == '}')
4538 break;
4539 else
4540 subName.push_back((char)ch);
4541 pos++;
4542 }
4543 //trace("subName:%s", subName.c_str());
4544 String subVal = attrs[subName];
4545 //trace("subVal:%s", subVal.c_str());
4546 attrVal.append(subVal);
4547 }
4548 else
4549 attrVal.push_back((char)ch);
4550 pos++;
4551 }
4553 attrVal = trim(attrVal);
4554 attrs[attrName] = attrVal;
4556 if (attrName == "Name")
4557 name = attrVal;
4558 else if (attrName == "Description")
4559 description = attrVal;
4560 else if (attrName == "Cflags")
4561 cflags = attrVal;
4562 else if (attrName == "Libs")
4563 libs = attrVal;
4564 else if (attrName == "Requires")
4565 requires = attrVal;
4566 else if (attrName == "Version")
4567 version = attrVal;
4569 //trace("name:'%s' value:'%s'",
4570 // attrName.c_str(), attrVal.c_str());
4571 }
4574 parseRequires();
4575 parseVersion();
4577 return true;
4578 }
4580 void PkgConfig::dumpAttrs()
4581 {
4582 //trace("### PkgConfig attributes for %s", fileName.c_str());
4583 std::map<String, String>::iterator iter;
4584 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4585 {
4586 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4587 }
4588 }
4591 bool PkgConfig::readFile(const String &fileNameArg)
4592 {
4593 fileName = fileNameArg;
4595 FILE *f = fopen(fileName.c_str(), "r");
4596 if (!f)
4597 {
4598 error("cannot open file '%s' for reading", fileName.c_str());
4599 return false;
4600 }
4601 String buf;
4602 while (true)
4603 {
4604 int ch = fgetc(f);
4605 if (ch < 0)
4606 break;
4607 buf.push_back((char)ch);
4608 }
4609 fclose(f);
4611 //trace("####### File:\n%s", buf.c_str());
4612 if (!parse(buf))
4613 {
4614 return false;
4615 }
4617 dumpAttrs();
4619 return true;
4620 }
4626 //########################################################################
4627 //# D E P T O O L
4628 //########################################################################
4632 /**
4633 * Class which holds information for each file.
4634 */
4635 class FileRec
4636 {
4637 public:
4639 typedef enum
4640 {
4641 UNKNOWN,
4642 CFILE,
4643 HFILE,
4644 OFILE
4645 } FileType;
4647 /**
4648 * Constructor
4649 */
4650 FileRec()
4651 {init(); type = UNKNOWN;}
4653 /**
4654 * Copy constructor
4655 */
4656 FileRec(const FileRec &other)
4657 {init(); assign(other);}
4658 /**
4659 * Constructor
4660 */
4661 FileRec(int typeVal)
4662 {init(); type = typeVal;}
4663 /**
4664 * Assignment operator
4665 */
4666 FileRec &operator=(const FileRec &other)
4667 {init(); assign(other); return *this;}
4670 /**
4671 * Destructor
4672 */
4673 ~FileRec()
4674 {}
4676 /**
4677 * Directory part of the file name
4678 */
4679 String path;
4681 /**
4682 * Base name, sans directory and suffix
4683 */
4684 String baseName;
4686 /**
4687 * File extension, such as cpp or h
4688 */
4689 String suffix;
4691 /**
4692 * Type of file: CFILE, HFILE, OFILE
4693 */
4694 int type;
4696 /**
4697 * Used to list files ref'd by this one
4698 */
4699 std::map<String, FileRec *> files;
4702 private:
4704 void init()
4705 {
4706 }
4708 void assign(const FileRec &other)
4709 {
4710 type = other.type;
4711 baseName = other.baseName;
4712 suffix = other.suffix;
4713 files = other.files;
4714 }
4716 };
4720 /**
4721 * Simpler dependency record
4722 */
4723 class DepRec
4724 {
4725 public:
4727 /**
4728 * Constructor
4729 */
4730 DepRec()
4731 {init();}
4733 /**
4734 * Copy constructor
4735 */
4736 DepRec(const DepRec &other)
4737 {init(); assign(other);}
4738 /**
4739 * Constructor
4740 */
4741 DepRec(const String &fname)
4742 {init(); name = fname; }
4743 /**
4744 * Assignment operator
4745 */
4746 DepRec &operator=(const DepRec &other)
4747 {init(); assign(other); return *this;}
4750 /**
4751 * Destructor
4752 */
4753 ~DepRec()
4754 {}
4756 /**
4757 * Directory part of the file name
4758 */
4759 String path;
4761 /**
4762 * Base name, without the path and suffix
4763 */
4764 String name;
4766 /**
4767 * Suffix of the source
4768 */
4769 String suffix;
4772 /**
4773 * Used to list files ref'd by this one
4774 */
4775 std::vector<String> files;
4778 private:
4780 void init()
4781 {
4782 }
4784 void assign(const DepRec &other)
4785 {
4786 path = other.path;
4787 name = other.name;
4788 suffix = other.suffix;
4789 files = other.files;
4790 }
4792 };
4795 class DepTool : public MakeBase
4796 {
4797 public:
4799 /**
4800 * Constructor
4801 */
4802 DepTool()
4803 {init();}
4805 /**
4806 * Copy constructor
4807 */
4808 DepTool(const DepTool &other)
4809 {init(); assign(other);}
4811 /**
4812 * Assignment operator
4813 */
4814 DepTool &operator=(const DepTool &other)
4815 {init(); assign(other); return *this;}
4818 /**
4819 * Destructor
4820 */
4821 ~DepTool()
4822 {}
4825 /**
4826 * Reset this section of code
4827 */
4828 virtual void init();
4830 /**
4831 * Reset this section of code
4832 */
4833 virtual void assign(const DepTool &other)
4834 {
4835 }
4837 /**
4838 * Sets the source directory which will be scanned
4839 */
4840 virtual void setSourceDirectory(const String &val)
4841 { sourceDir = val; }
4843 /**
4844 * Returns the source directory which will be scanned
4845 */
4846 virtual String getSourceDirectory()
4847 { return sourceDir; }
4849 /**
4850 * Sets the list of files within the directory to analyze
4851 */
4852 virtual void setFileList(const std::vector<String> &list)
4853 { fileList = list; }
4855 /**
4856 * Creates the list of all file names which will be
4857 * candidates for further processing. Reads make.exclude
4858 * to see which files for directories to leave out.
4859 */
4860 virtual bool createFileList();
4863 /**
4864 * Generates the forward dependency list
4865 */
4866 virtual bool generateDependencies();
4869 /**
4870 * Generates the forward dependency list, saving the file
4871 */
4872 virtual bool generateDependencies(const String &);
4875 /**
4876 * Load a dependency file
4877 */
4878 std::vector<DepRec> loadDepFile(const String &fileName);
4880 /**
4881 * Load a dependency file, generating one if necessary
4882 */
4883 std::vector<DepRec> getDepFile(const String &fileName);
4885 /**
4886 * Save a dependency file
4887 */
4888 bool saveDepFile(const String &fileName);
4891 private:
4894 /**
4895 *
4896 */
4897 void parseName(const String &fullname,
4898 String &path,
4899 String &basename,
4900 String &suffix);
4902 /**
4903 *
4904 */
4905 int get(int pos);
4907 /**
4908 *
4909 */
4910 int skipwhite(int pos);
4912 /**
4913 *
4914 */
4915 int getword(int pos, String &ret);
4917 /**
4918 *
4919 */
4920 bool sequ(int pos, char *key);
4922 /**
4923 *
4924 */
4925 bool addIncludeFile(FileRec *frec, const String &fname);
4927 /**
4928 *
4929 */
4930 bool scanFile(const String &fname, FileRec *frec);
4932 /**
4933 *
4934 */
4935 bool processDependency(FileRec *ofile,
4936 FileRec *include,
4937 int depth);
4939 /**
4940 *
4941 */
4942 String sourceDir;
4944 /**
4945 *
4946 */
4947 std::vector<String> fileList;
4949 /**
4950 *
4951 */
4952 std::vector<String> directories;
4954 /**
4955 * A list of all files which will be processed for
4956 * dependencies. This is the only list that has the actual
4957 * records. All other lists have pointers to these records.
4958 */
4959 std::map<String, FileRec *> allFiles;
4961 /**
4962 * The list of .o files, and the
4963 * dependencies upon them.
4964 */
4965 std::map<String, FileRec *> depFiles;
4967 int depFileSize;
4968 char *depFileBuf;
4971 };
4977 /**
4978 * Clean up after processing. Called by the destructor, but should
4979 * also be called before the object is reused.
4980 */
4981 void DepTool::init()
4982 {
4983 sourceDir = ".";
4985 fileList.clear();
4986 directories.clear();
4988 //clear refs
4989 depFiles.clear();
4990 //clear records
4991 std::map<String, FileRec *>::iterator iter;
4992 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4993 delete iter->second;
4995 allFiles.clear();
4997 }
5002 /**
5003 * Parse a full path name into path, base name, and suffix
5004 */
5005 void DepTool::parseName(const String &fullname,
5006 String &path,
5007 String &basename,
5008 String &suffix)
5009 {
5010 if (fullname.size() < 2)
5011 return;
5013 unsigned int pos = fullname.find_last_of('/');
5014 if (pos != fullname.npos && pos<fullname.size()-1)
5015 {
5016 path = fullname.substr(0, pos);
5017 pos++;
5018 basename = fullname.substr(pos, fullname.size()-pos);
5019 }
5020 else
5021 {
5022 path = "";
5023 basename = fullname;
5024 }
5026 pos = basename.find_last_of('.');
5027 if (pos != basename.npos && pos<basename.size()-1)
5028 {
5029 suffix = basename.substr(pos+1, basename.size()-pos-1);
5030 basename = basename.substr(0, pos);
5031 }
5033 //trace("parsename:%s %s %s", path.c_str(),
5034 // basename.c_str(), suffix.c_str());
5035 }
5039 /**
5040 * Generate our internal file list.
5041 */
5042 bool DepTool::createFileList()
5043 {
5045 for (unsigned int i=0 ; i<fileList.size() ; i++)
5046 {
5047 String fileName = fileList[i];
5048 //trace("## FileName:%s", fileName.c_str());
5049 String path;
5050 String basename;
5051 String sfx;
5052 parseName(fileName, path, basename, sfx);
5053 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5054 sfx == "cc" || sfx == "CC")
5055 {
5056 FileRec *fe = new FileRec(FileRec::CFILE);
5057 fe->path = path;
5058 fe->baseName = basename;
5059 fe->suffix = sfx;
5060 allFiles[fileName] = fe;
5061 }
5062 else if (sfx == "h" || sfx == "hh" ||
5063 sfx == "hpp" || sfx == "hxx")
5064 {
5065 FileRec *fe = new FileRec(FileRec::HFILE);
5066 fe->path = path;
5067 fe->baseName = basename;
5068 fe->suffix = sfx;
5069 allFiles[fileName] = fe;
5070 }
5071 }
5073 if (!listDirectories(sourceDir, "", directories))
5074 return false;
5076 return true;
5077 }
5083 /**
5084 * Get a character from the buffer at pos. If out of range,
5085 * return -1 for safety
5086 */
5087 int DepTool::get(int pos)
5088 {
5089 if (pos>depFileSize)
5090 return -1;
5091 return depFileBuf[pos];
5092 }
5096 /**
5097 * Skip over all whitespace characters beginning at pos. Return
5098 * the position of the first non-whitespace character.
5099 */
5100 int DepTool::skipwhite(int pos)
5101 {
5102 while (pos < depFileSize)
5103 {
5104 int ch = get(pos);
5105 if (ch < 0)
5106 break;
5107 if (!isspace(ch))
5108 break;
5109 pos++;
5110 }
5111 return pos;
5112 }
5115 /**
5116 * Parse the buffer beginning at pos, for a word. Fill
5117 * 'ret' with the result. Return the position after the
5118 * word.
5119 */
5120 int DepTool::getword(int pos, String &ret)
5121 {
5122 while (pos < depFileSize)
5123 {
5124 int ch = get(pos);
5125 if (ch < 0)
5126 break;
5127 if (isspace(ch))
5128 break;
5129 ret.push_back((char)ch);
5130 pos++;
5131 }
5132 return pos;
5133 }
5135 /**
5136 * Return whether the sequence of characters in the buffer
5137 * beginning at pos match the key, for the length of the key
5138 */
5139 bool DepTool::sequ(int pos, char *key)
5140 {
5141 while (*key)
5142 {
5143 if (*key != get(pos))
5144 return false;
5145 key++; pos++;
5146 }
5147 return true;
5148 }
5152 /**
5153 * Add an include file name to a file record. If the name
5154 * is not found in allFiles explicitly, try prepending include
5155 * directory names to it and try again.
5156 */
5157 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5158 {
5160 std::map<String, FileRec *>::iterator iter =
5161 allFiles.find(iname);
5162 if (iter != allFiles.end()) //already exists
5163 {
5164 //h file in same dir
5165 FileRec *other = iter->second;
5166 //trace("local: '%s'", iname.c_str());
5167 frec->files[iname] = other;
5168 return true;
5169 }
5170 else
5171 {
5172 //look in other dirs
5173 std::vector<String>::iterator diter;
5174 for (diter=directories.begin() ;
5175 diter!=directories.end() ; diter++)
5176 {
5177 String dfname = *diter;
5178 dfname.append("/");
5179 dfname.append(iname);
5180 iter = allFiles.find(dfname);
5181 if (iter != allFiles.end())
5182 {
5183 FileRec *other = iter->second;
5184 //trace("other: '%s'", iname.c_str());
5185 frec->files[dfname] = other;
5186 return true;
5187 }
5188 }
5189 }
5190 return true;
5191 }
5195 /**
5196 * Lightly parse a file to find the #include directives. Do
5197 * a bit of state machine stuff to make sure that the directive
5198 * is valid. (Like not in a comment).
5199 */
5200 bool DepTool::scanFile(const String &fname, FileRec *frec)
5201 {
5202 String fileName;
5203 if (sourceDir.size() > 0)
5204 {
5205 fileName.append(sourceDir);
5206 fileName.append("/");
5207 }
5208 fileName.append(fname);
5209 String nativeName = getNativePath(fileName);
5210 FILE *f = fopen(nativeName.c_str(), "r");
5211 if (!f)
5212 {
5213 error("Could not open '%s' for reading", fname.c_str());
5214 return false;
5215 }
5216 String buf;
5217 while (true)
5218 {
5219 int ch = fgetc(f);
5220 if (ch < 0)
5221 break;
5222 buf.push_back((char)ch);
5223 }
5224 fclose(f);
5226 depFileSize = buf.size();
5227 depFileBuf = (char *)buf.c_str();
5228 int pos = 0;
5231 while (pos < depFileSize)
5232 {
5233 //trace("p:%c", get(pos));
5235 //# Block comment
5236 if (get(pos) == '/' && get(pos+1) == '*')
5237 {
5238 pos += 2;
5239 while (pos < depFileSize)
5240 {
5241 if (get(pos) == '*' && get(pos+1) == '/')
5242 {
5243 pos += 2;
5244 break;
5245 }
5246 else
5247 pos++;
5248 }
5249 }
5250 //# Line comment
5251 else if (get(pos) == '/' && get(pos+1) == '/')
5252 {
5253 pos += 2;
5254 while (pos < depFileSize)
5255 {
5256 if (get(pos) == '\n')
5257 {
5258 pos++;
5259 break;
5260 }
5261 else
5262 pos++;
5263 }
5264 }
5265 //# #include! yaay
5266 else if (sequ(pos, "#include"))
5267 {
5268 pos += 8;
5269 pos = skipwhite(pos);
5270 String iname;
5271 pos = getword(pos, iname);
5272 if (iname.size()>2)
5273 {
5274 iname = iname.substr(1, iname.size()-2);
5275 addIncludeFile(frec, iname);
5276 }
5277 }
5278 else
5279 {
5280 pos++;
5281 }
5282 }
5284 return true;
5285 }
5289 /**
5290 * Recursively check include lists to find all files in allFiles to which
5291 * a given file is dependent.
5292 */
5293 bool DepTool::processDependency(FileRec *ofile,
5294 FileRec *include,
5295 int depth)
5296 {
5297 std::map<String, FileRec *>::iterator iter;
5298 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5299 {
5300 String fname = iter->first;
5301 if (ofile->files.find(fname) != ofile->files.end())
5302 {
5303 //trace("file '%s' already seen", fname.c_str());
5304 continue;
5305 }
5306 FileRec *child = iter->second;
5307 ofile->files[fname] = child;
5309 processDependency(ofile, child, depth+1);
5310 }
5313 return true;
5314 }
5320 /**
5321 * Generate the file dependency list.
5322 */
5323 bool DepTool::generateDependencies()
5324 {
5325 std::map<String, FileRec *>::iterator iter;
5326 //# First pass. Scan for all includes
5327 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5328 {
5329 FileRec *frec = iter->second;
5330 if (!scanFile(iter->first, frec))
5331 {
5332 //quit?
5333 }
5334 }
5336 //# Second pass. Scan for all includes
5337 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5338 {
5339 FileRec *include = iter->second;
5340 if (include->type == FileRec::CFILE)
5341 {
5342 String cFileName = iter->first;
5343 FileRec *ofile = new FileRec(FileRec::OFILE);
5344 ofile->path = include->path;
5345 ofile->baseName = include->baseName;
5346 ofile->suffix = include->suffix;
5347 String fname = include->path;
5348 if (fname.size()>0)
5349 fname.append("/");
5350 fname.append(include->baseName);
5351 fname.append(".o");
5352 depFiles[fname] = ofile;
5353 //add the .c file first? no, don't
5354 //ofile->files[cFileName] = include;
5356 //trace("ofile:%s", fname.c_str());
5358 processDependency(ofile, include, 0);
5359 }
5360 }
5363 return true;
5364 }
5368 /**
5369 * High-level call to generate deps and optionally save them
5370 */
5371 bool DepTool::generateDependencies(const String &fileName)
5372 {
5373 if (!createFileList())
5374 return false;
5375 if (!generateDependencies())
5376 return false;
5377 if (!saveDepFile(fileName))
5378 return false;
5379 return true;
5380 }
5383 /**
5384 * This saves the dependency cache.
5385 */
5386 bool DepTool::saveDepFile(const String &fileName)
5387 {
5388 time_t tim;
5389 time(&tim);
5391 FILE *f = fopen(fileName.c_str(), "w");
5392 if (!f)
5393 {
5394 trace("cannot open '%s' for writing", fileName.c_str());
5395 }
5396 fprintf(f, "<?xml version='1.0'?>\n");
5397 fprintf(f, "<!--\n");
5398 fprintf(f, "########################################################\n");
5399 fprintf(f, "## File: build.dep\n");
5400 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5401 fprintf(f, "########################################################\n");
5402 fprintf(f, "-->\n");
5404 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5405 std::map<String, FileRec *>::iterator iter;
5406 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5407 {
5408 FileRec *frec = iter->second;
5409 if (frec->type == FileRec::OFILE)
5410 {
5411 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5412 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5413 std::map<String, FileRec *>::iterator citer;
5414 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5415 {
5416 String cfname = citer->first;
5417 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5418 }
5419 fprintf(f, "</object>\n\n");
5420 }
5421 }
5423 fprintf(f, "</dependencies>\n");
5424 fprintf(f, "\n");
5425 fprintf(f, "<!--\n");
5426 fprintf(f, "########################################################\n");
5427 fprintf(f, "## E N D\n");
5428 fprintf(f, "########################################################\n");
5429 fprintf(f, "-->\n");
5431 fclose(f);
5433 return true;
5434 }
5439 /**
5440 * This loads the dependency cache.
5441 */
5442 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5443 {
5444 std::vector<DepRec> result;
5446 Parser parser;
5447 Element *root = parser.parseFile(depFile.c_str());
5448 if (!root)
5449 {
5450 error("Could not open %s for reading", depFile.c_str());
5451 return result;
5452 }
5454 if (root->getChildren().size()==0 ||
5455 root->getChildren()[0]->getName()!="dependencies")
5456 {
5457 error("Main xml element should be <dependencies>");
5458 delete root;
5459 return result;
5460 }
5462 //########## Start parsing
5463 Element *depList = root->getChildren()[0];
5465 std::vector<Element *> objects = depList->getChildren();
5466 for (unsigned int i=0 ; i<objects.size() ; i++)
5467 {
5468 Element *objectElem = objects[i];
5469 String tagName = objectElem->getName();
5470 if (tagName == "object")
5471 {
5472 String objName = objectElem->getAttribute("name");
5473 //trace("object:%s", objName.c_str());
5474 DepRec depObject(objName);
5475 depObject.path = objectElem->getAttribute("path");
5476 depObject.suffix = objectElem->getAttribute("suffix");
5477 //########## DESCRIPTION
5478 std::vector<Element *> depElems = objectElem->getChildren();
5479 for (unsigned int i=0 ; i<depElems.size() ; i++)
5480 {
5481 Element *depElem = depElems[i];
5482 tagName = depElem->getName();
5483 if (tagName == "dep")
5484 {
5485 String depName = depElem->getAttribute("name");
5486 //trace(" dep:%s", depName.c_str());
5487 depObject.files.push_back(depName);
5488 }
5489 }
5490 //Insert into the result list, in a sorted manner
5491 bool inserted = false;
5492 std::vector<DepRec>::iterator iter;
5493 for (iter = result.begin() ; iter != result.end() ; iter++)
5494 {
5495 String vpath = iter->path;
5496 vpath.append("/");
5497 vpath.append(iter->name);
5498 String opath = depObject.path;
5499 opath.append("/");
5500 opath.append(depObject.name);
5501 if (vpath > opath)
5502 {
5503 inserted = true;
5504 iter = result.insert(iter, depObject);
5505 break;
5506 }
5507 }
5508 if (!inserted)
5509 result.push_back(depObject);
5510 }
5511 }
5513 delete root;
5515 return result;
5516 }
5519 /**
5520 * This loads the dependency cache.
5521 */
5522 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
5523 {
5524 std::vector<DepRec> result = loadDepFile(depFile);
5525 if (result.size() == 0)
5526 {
5527 generateDependencies(depFile);
5528 result = loadDepFile(depFile);
5529 }
5530 return result;
5531 }
5536 //########################################################################
5537 //# T A S K
5538 //########################################################################
5539 //forward decl
5540 class Target;
5541 class Make;
5543 /**
5544 *
5545 */
5546 class Task : public MakeBase
5547 {
5549 public:
5551 typedef enum
5552 {
5553 TASK_NONE,
5554 TASK_AR,
5555 TASK_CC,
5556 TASK_COPY,
5557 TASK_DELETE,
5558 TASK_JAR,
5559 TASK_JAVAC,
5560 TASK_LINK,
5561 TASK_MAKEFILE,
5562 TASK_MKDIR,
5563 TASK_MSGFMT,
5564 TASK_RANLIB,
5565 TASK_RC,
5566 TASK_STRIP,
5567 TASK_TSTAMP
5568 } TaskType;
5571 /**
5572 *
5573 */
5574 Task(MakeBase &par) : parent(par)
5575 { init(); }
5577 /**
5578 *
5579 */
5580 Task(const Task &other) : parent(other.parent)
5581 { init(); assign(other); }
5583 /**
5584 *
5585 */
5586 Task &operator=(const Task &other)
5587 { assign(other); return *this; }
5589 /**
5590 *
5591 */
5592 virtual ~Task()
5593 { }
5596 /**
5597 *
5598 */
5599 virtual MakeBase &getParent()
5600 { return parent; }
5602 /**
5603 *
5604 */
5605 virtual int getType()
5606 { return type; }
5608 /**
5609 *
5610 */
5611 virtual void setType(int val)
5612 { type = val; }
5614 /**
5615 *
5616 */
5617 virtual String getName()
5618 { return name; }
5620 /**
5621 *
5622 */
5623 virtual bool execute()
5624 { return true; }
5626 /**
5627 *
5628 */
5629 virtual bool parse(Element *elem)
5630 { return true; }
5632 /**
5633 *
5634 */
5635 Task *createTask(Element *elem);
5638 protected:
5640 void init()
5641 {
5642 type = TASK_NONE;
5643 name = "none";
5644 }
5646 void assign(const Task &other)
5647 {
5648 type = other.type;
5649 name = other.name;
5650 }
5652 String getAttribute(Element *elem, const String &attrName)
5653 {
5654 String str;
5655 return str;
5656 }
5658 MakeBase &parent;
5660 int type;
5662 String name;
5663 };
5668 /**
5669 * Run the "ar" command to archive .o's into a .a
5670 */
5671 class TaskAr : public Task
5672 {
5673 public:
5675 TaskAr(MakeBase &par) : Task(par)
5676 {
5677 type = TASK_AR; name = "ar";
5678 command = "ar crv";
5679 }
5681 virtual ~TaskAr()
5682 {}
5684 virtual bool execute()
5685 {
5686 //trace("###########HERE %d", fileSet.size());
5687 bool doit = false;
5689 String fullOut = parent.resolve(fileName);
5690 //trace("ar fullout: %s", fullOut.c_str());
5692 if (!listFiles(parent, fileSet))
5693 return false;
5694 String fileSetDir = fileSet.getDirectory();
5696 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5697 {
5698 String fname;
5699 if (fileSetDir.size()>0)
5700 {
5701 fname.append(fileSetDir);
5702 fname.append("/");
5703 }
5704 fname.append(fileSet[i]);
5705 String fullName = parent.resolve(fname);
5706 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
5707 if (isNewerThan(fullName, fullOut))
5708 doit = true;
5709 }
5710 //trace("Needs it:%d", doit);
5711 if (!doit)
5712 {
5713 return true;
5714 }
5716 String cmd = command;
5717 cmd.append(" ");
5718 cmd.append(fullOut);
5719 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5720 {
5721 String fname;
5722 if (fileSetDir.size()>0)
5723 {
5724 fname.append(fileSetDir);
5725 fname.append("/");
5726 }
5727 fname.append(fileSet[i]);
5728 String fullName = parent.resolve(fname);
5730 cmd.append(" ");
5731 cmd.append(fullName);
5732 }
5734 String outString, errString;
5735 if (!executeCommand(cmd.c_str(), "", outString, errString))
5736 {
5737 error("AR problem: %s", errString.c_str());
5738 return false;
5739 }
5741 return true;
5742 }
5744 virtual bool parse(Element *elem)
5745 {
5746 if (!parent.getAttribute(elem, "file", fileName))
5747 return false;
5749 std::vector<Element *> children = elem->getChildren();
5750 for (unsigned int i=0 ; i<children.size() ; i++)
5751 {
5752 Element *child = children[i];
5753 String tagName = child->getName();
5754 if (tagName == "fileset")
5755 {
5756 if (!parseFileSet(child, parent, fileSet))
5757 return false;
5758 }
5759 }
5760 return true;
5761 }
5763 private:
5765 String command;
5766 String fileName;
5767 FileSet fileSet;
5769 };
5772 /**
5773 * This task runs the C/C++ compiler. The compiler is invoked
5774 * for all .c or .cpp files which are newer than their correcsponding
5775 * .o files.
5776 */
5777 class TaskCC : public Task
5778 {
5779 public:
5781 TaskCC(MakeBase &par) : Task(par)
5782 {
5783 type = TASK_CC; name = "cc";
5784 ccCommand = "gcc";
5785 cxxCommand = "g++";
5786 source = ".";
5787 dest = ".";
5788 flags = "";
5789 defines = "";
5790 includes = "";
5791 fileSet.clear();
5792 }
5794 virtual ~TaskCC()
5795 {}
5797 virtual bool execute()
5798 {
5799 if (!listFiles(parent, fileSet))
5800 return false;
5802 DepTool depTool;
5803 depTool.setSourceDirectory(source);
5804 depTool.setFileList(fileSet.getFiles());
5805 std::vector<DepRec> deps = depTool.getDepFile("build.dep");
5807 String incs;
5808 incs.append("-I");
5809 incs.append(parent.resolve("."));
5810 incs.append(" ");
5811 if (includes.size()>0)
5812 {
5813 incs.append(includes);
5814 incs.append(" ");
5815 }
5816 std::set<String> paths;
5817 std::vector<DepRec>::iterator viter;
5818 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5819 {
5820 DepRec dep = *viter;
5821 if (dep.path.size()>0)
5822 paths.insert(dep.path);
5823 }
5824 if (source.size()>0)
5825 {
5826 incs.append(" -I");
5827 incs.append(parent.resolve(source));
5828 incs.append(" ");
5829 }
5830 std::set<String>::iterator setIter;
5831 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5832 {
5833 incs.append(" -I");
5834 String dname;
5835 if (source.size()>0)
5836 {
5837 dname.append(source);
5838 dname.append("/");
5839 }
5840 dname.append(*setIter);
5841 incs.append(parent.resolve(dname));
5842 }
5843 std::vector<String> cfiles;
5844 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5845 {
5846 DepRec dep = *viter;
5848 //## Select command
5849 String sfx = dep.suffix;
5850 String command = ccCommand;
5851 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5852 || sfx == "CC")
5853 command = cxxCommand;
5855 //## Make paths
5856 String destPath = dest;
5857 String srcPath = source;
5858 if (dep.path.size()>0)
5859 {
5860 destPath.append("/");
5861 destPath.append(dep.path);
5862 srcPath.append("/");
5863 srcPath.append(dep.path);
5864 }
5865 //## Make sure destination directory exists
5866 if (!createDirectory(destPath))
5867 return false;
5869 //## Check whether it needs to be done
5870 String destFullName = destPath;
5871 destFullName.append("/");
5872 destFullName.append(dep.name);
5873 destFullName.append(".o");
5874 String srcFullName = srcPath;
5875 srcFullName.append("/");
5876 srcFullName.append(dep.name);
5877 srcFullName.append(".");
5878 srcFullName.append(dep.suffix);
5879 if (!isNewerThan(srcFullName, destFullName))
5880 {
5881 //trace("%s skipped", srcFullName.c_str());
5882 continue;
5883 }
5885 //## Assemble the command
5886 String cmd = command;
5887 cmd.append(" -c ");
5888 cmd.append(flags);
5889 cmd.append(" ");
5890 cmd.append(defines);
5891 cmd.append(" ");
5892 cmd.append(incs);
5893 cmd.append(" ");
5894 cmd.append(srcFullName);
5895 cmd.append(" -o ");
5896 cmd.append(destFullName);
5898 //## Execute the command
5900 String outString, errString;
5901 if (!executeCommand(cmd.c_str(), "", outString, errString))
5902 {
5903 error("problem compiling: %s", errString.c_str());
5904 return false;
5905 }
5906 }
5908 return true;
5909 }
5911 virtual bool parse(Element *elem)
5912 {
5913 String s;
5914 if (!parent.getAttribute(elem, "command", s))
5915 return false;
5916 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5917 if (!parent.getAttribute(elem, "cc", s))
5918 return false;
5919 if (s.size()>0) ccCommand = s;
5920 if (!parent.getAttribute(elem, "cxx", s))
5921 return false;
5922 if (s.size()>0) cxxCommand = s;
5923 if (!parent.getAttribute(elem, "destdir", s))
5924 return false;
5925 if (s.size()>0) dest = s;
5927 std::vector<Element *> children = elem->getChildren();
5928 for (unsigned int i=0 ; i<children.size() ; i++)
5929 {
5930 Element *child = children[i];
5931 String tagName = child->getName();
5932 if (tagName == "flags")
5933 {
5934 if (!parent.getValue(child, flags))
5935 return false;
5936 flags = strip(flags);
5937 }
5938 else if (tagName == "includes")
5939 {
5940 if (!parent.getValue(child, includes))
5941 return false;
5942 includes = strip(includes);
5943 }
5944 else if (tagName == "defines")
5945 {
5946 if (!parent.getValue(child, defines))
5947 return false;
5948 defines = strip(defines);
5949 }
5950 else if (tagName == "fileset")
5951 {
5952 if (!parseFileSet(child, parent, fileSet))
5953 return false;
5954 source = fileSet.getDirectory();
5955 }
5956 }
5958 return true;
5959 }
5961 protected:
5963 String ccCommand;
5964 String cxxCommand;
5965 String source;
5966 String dest;
5967 String flags;
5968 String defines;
5969 String includes;
5970 FileSet fileSet;
5972 };
5976 /**
5977 *
5978 */
5979 class TaskCopy : public Task
5980 {
5981 public:
5983 typedef enum
5984 {
5985 CP_NONE,
5986 CP_TOFILE,
5987 CP_TODIR
5988 } CopyType;
5990 TaskCopy(MakeBase &par) : Task(par)
5991 {
5992 type = TASK_COPY; name = "copy";
5993 cptype = CP_NONE;
5994 verbose = false;
5995 haveFileSet = false;
5996 }
5998 virtual ~TaskCopy()
5999 {}
6001 virtual bool execute()
6002 {
6003 switch (cptype)
6004 {
6005 case CP_TOFILE:
6006 {
6007 if (fileName.size()>0)
6008 {
6009 status(" : %s", fileName.c_str());
6010 String fullSource = parent.resolve(fileName);
6011 String fullDest = parent.resolve(toFileName);
6012 //trace("copy %s to file %s", fullSource.c_str(),
6013 // fullDest.c_str());
6014 if (!isRegularFile(fullSource))
6015 {
6016 error("copy : file %s does not exist", fullSource.c_str());
6017 return false;
6018 }
6019 if (!isNewerThan(fullSource, fullDest))
6020 {
6021 return true;
6022 }
6023 if (!copyFile(fullSource, fullDest))
6024 return false;
6025 status(" : 1 file copied");
6026 }
6027 return true;
6028 }
6029 case CP_TODIR:
6030 {
6031 if (haveFileSet)
6032 {
6033 if (!listFiles(parent, fileSet))
6034 return false;
6035 String fileSetDir = fileSet.getDirectory();
6037 int nrFiles = 0;
6038 status(" : %s", fileSetDir.c_str());
6039 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6040 {
6041 String fileName = fileSet[i];
6043 String sourcePath;
6044 if (fileSetDir.size()>0)
6045 {
6046 sourcePath.append(fileSetDir);
6047 sourcePath.append("/");
6048 }
6049 sourcePath.append(fileName);
6050 String fullSource = parent.resolve(sourcePath);
6052 //Get the immediate parent directory's base name
6053 String baseFileSetDir = fileSetDir;
6054 unsigned int pos = baseFileSetDir.find_last_of('/');
6055 if (pos!=baseFileSetDir.npos &&
6056 pos < baseFileSetDir.size()-1)
6057 baseFileSetDir =
6058 baseFileSetDir.substr(pos+1,
6059 baseFileSetDir.size());
6060 //Now make the new path
6061 String destPath;
6062 if (toDirName.size()>0)
6063 {
6064 destPath.append(toDirName);
6065 destPath.append("/");
6066 }
6067 if (baseFileSetDir.size()>0)
6068 {
6069 destPath.append(baseFileSetDir);
6070 destPath.append("/");
6071 }
6072 destPath.append(fileName);
6073 String fullDest = parent.resolve(destPath);
6074 //trace("fileName:%s", fileName.c_str());
6075 //trace("copy %s to new dir : %s", fullSource.c_str(),
6076 // fullDest.c_str());
6077 if (!isNewerThan(fullSource, fullDest))
6078 {
6079 //trace("copy skipping %s", fullSource.c_str());
6080 continue;
6081 }
6082 if (!copyFile(fullSource, fullDest))
6083 return false;
6084 nrFiles++;
6085 }
6086 status(" : %d file(s) copied", nrFiles);
6087 }
6088 else //file source
6089 {
6090 //For file->dir we want only the basename of
6091 //the source appended to the dest dir
6092 status(" : %s", fileName.c_str());
6093 String baseName = fileName;
6094 unsigned int pos = baseName.find_last_of('/');
6095 if (pos!=baseName.npos && pos<baseName.size()-1)
6096 baseName = baseName.substr(pos+1, baseName.size());
6097 String fullSource = parent.resolve(fileName);
6098 String destPath;
6099 if (toDirName.size()>0)
6100 {
6101 destPath.append(toDirName);
6102 destPath.append("/");
6103 }
6104 destPath.append(baseName);
6105 String fullDest = parent.resolve(destPath);
6106 //trace("copy %s to new dir : %s", fullSource.c_str(),
6107 // fullDest.c_str());
6108 if (!isRegularFile(fullSource))
6109 {
6110 error("copy : file %s does not exist", fullSource.c_str());
6111 return false;
6112 }
6113 if (!isNewerThan(fullSource, fullDest))
6114 {
6115 return true;
6116 }
6117 if (!copyFile(fullSource, fullDest))
6118 return false;
6119 status(" : 1 file copied");
6120 }
6121 return true;
6122 }
6123 }
6124 return true;
6125 }
6128 virtual bool parse(Element *elem)
6129 {
6130 if (!parent.getAttribute(elem, "file", fileName))
6131 return false;
6132 if (!parent.getAttribute(elem, "tofile", toFileName))
6133 return false;
6134 if (toFileName.size() > 0)
6135 cptype = CP_TOFILE;
6136 if (!parent.getAttribute(elem, "todir", toDirName))
6137 return false;
6138 if (toDirName.size() > 0)
6139 cptype = CP_TODIR;
6140 String ret;
6141 if (!parent.getAttribute(elem, "verbose", ret))
6142 return false;
6143 if (ret.size()>0 && !getBool(ret, verbose))
6144 return false;
6146 haveFileSet = false;
6148 std::vector<Element *> children = elem->getChildren();
6149 for (unsigned int i=0 ; i<children.size() ; i++)
6150 {
6151 Element *child = children[i];
6152 String tagName = child->getName();
6153 if (tagName == "fileset")
6154 {
6155 if (!parseFileSet(child, parent, fileSet))
6156 {
6157 error("problem getting fileset");
6158 return false;
6159 }
6160 haveFileSet = true;
6161 }
6162 }
6164 //Perform validity checks
6165 if (fileName.size()>0 && fileSet.size()>0)
6166 {
6167 error("<copy> can only have one of : file= and <fileset>");
6168 return false;
6169 }
6170 if (toFileName.size()>0 && toDirName.size()>0)
6171 {
6172 error("<copy> can only have one of : tofile= or todir=");
6173 return false;
6174 }
6175 if (haveFileSet && toDirName.size()==0)
6176 {
6177 error("a <copy> task with a <fileset> must have : todir=");
6178 return false;
6179 }
6180 if (cptype == CP_TOFILE && fileName.size()==0)
6181 {
6182 error("<copy> tofile= must be associated with : file=");
6183 return false;
6184 }
6185 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6186 {
6187 error("<copy> todir= must be associated with : file= or <fileset>");
6188 return false;
6189 }
6191 return true;
6192 }
6194 private:
6196 int cptype;
6197 String fileName;
6198 FileSet fileSet;
6199 String toFileName;
6200 String toDirName;
6201 bool verbose;
6202 bool haveFileSet;
6203 };
6206 /**
6207 *
6208 */
6209 class TaskDelete : public Task
6210 {
6211 public:
6213 typedef enum
6214 {
6215 DEL_FILE,
6216 DEL_DIR,
6217 DEL_FILESET
6218 } DeleteType;
6220 TaskDelete(MakeBase &par) : Task(par)
6221 {
6222 type = TASK_DELETE;
6223 name = "delete";
6224 delType = DEL_FILE;
6225 verbose = false;
6226 quiet = false;
6227 failOnError = true;
6228 }
6230 virtual ~TaskDelete()
6231 {}
6233 virtual bool execute()
6234 {
6235 struct stat finfo;
6236 switch (delType)
6237 {
6238 case DEL_FILE:
6239 {
6240 status(" : %s", fileName.c_str());
6241 String fullName = parent.resolve(fileName);
6242 char *fname = (char *)fullName.c_str();
6243 //does not exist
6244 if (stat(fname, &finfo)<0)
6245 return true;
6246 //exists but is not a regular file
6247 if (!S_ISREG(finfo.st_mode))
6248 {
6249 error("<delete> failed. '%s' exists and is not a regular file",
6250 fname);
6251 return false;
6252 }
6253 if (remove(fname)<0)
6254 {
6255 error("<delete> failed: %s", strerror(errno));
6256 return false;
6257 }
6258 return true;
6259 }
6260 case DEL_DIR:
6261 {
6262 status(" : %s", dirName.c_str());
6263 String fullDir = parent.resolve(dirName);
6264 if (!removeDirectory(fullDir))
6265 return false;
6266 return true;
6267 }
6268 }
6269 return true;
6270 }
6272 virtual bool parse(Element *elem)
6273 {
6274 if (!parent.getAttribute(elem, "file", fileName))
6275 return false;
6276 if (fileName.size() > 0)
6277 delType = DEL_FILE;
6278 if (!parent.getAttribute(elem, "dir", dirName))
6279 return false;
6280 if (dirName.size() > 0)
6281 delType = DEL_DIR;
6282 if (fileName.size()>0 && dirName.size()>0)
6283 {
6284 error("<delete> can only have one attribute of file= or dir=");
6285 return false;
6286 }
6287 String ret;
6288 if (!parent.getAttribute(elem, "verbose", ret))
6289 return false;
6290 if (ret.size()>0 && !getBool(ret, verbose))
6291 return false;
6292 if (!parent.getAttribute(elem, "quiet", ret))
6293 return false;
6294 if (ret.size()>0 && !getBool(ret, quiet))
6295 return false;
6296 if (!parent.getAttribute(elem, "failonerror", ret))
6297 return false;
6298 if (ret.size()>0 && !getBool(ret, failOnError))
6299 return false;
6300 return true;
6301 }
6303 private:
6305 int delType;
6306 String dirName;
6307 String fileName;
6308 bool verbose;
6309 bool quiet;
6310 bool failOnError;
6311 };
6314 /**
6315 *
6316 */
6317 class TaskJar : public Task
6318 {
6319 public:
6321 TaskJar(MakeBase &par) : Task(par)
6322 { type = TASK_JAR; name = "jar"; }
6324 virtual ~TaskJar()
6325 {}
6327 virtual bool execute()
6328 {
6329 return true;
6330 }
6332 virtual bool parse(Element *elem)
6333 {
6334 return true;
6335 }
6336 };
6339 /**
6340 *
6341 */
6342 class TaskJavac : public Task
6343 {
6344 public:
6346 TaskJavac(MakeBase &par) : Task(par)
6347 { type = TASK_JAVAC; name = "javac"; }
6349 virtual ~TaskJavac()
6350 {}
6352 virtual bool execute()
6353 {
6354 return true;
6355 }
6357 virtual bool parse(Element *elem)
6358 {
6359 return true;
6360 }
6361 };
6364 /**
6365 *
6366 */
6367 class TaskLink : public Task
6368 {
6369 public:
6371 TaskLink(MakeBase &par) : Task(par)
6372 {
6373 type = TASK_LINK; name = "link";
6374 command = "g++";
6375 }
6377 virtual ~TaskLink()
6378 {}
6380 virtual bool execute()
6381 {
6382 if (!listFiles(parent, fileSet))
6383 return false;
6384 String fileSetDir = fileSet.getDirectory();
6385 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6386 bool doit = false;
6387 String fullTarget = parent.resolve(fileName);
6388 String cmd = command;
6389 cmd.append(" -o ");
6390 cmd.append(fullTarget);
6391 cmd.append(" ");
6392 cmd.append(flags);
6393 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6394 {
6395 cmd.append(" ");
6396 String obj;
6397 if (fileSetDir.size()>0)
6398 {
6399 obj.append(fileSetDir);
6400 obj.append("/");
6401 }
6402 obj.append(fileSet[i]);
6403 String fullObj = parent.resolve(obj);
6404 cmd.append(fullObj);
6405 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6406 // fullObj.c_str());
6407 if (isNewerThan(fullObj, fullTarget))
6408 doit = true;
6409 }
6410 cmd.append(" ");
6411 cmd.append(libs);
6412 if (!doit)
6413 {
6414 //trace("link not needed");
6415 return true;
6416 }
6417 //trace("LINK cmd:%s", cmd.c_str());
6420 String outString, errString;
6421 if (!executeCommand(cmd.c_str(), "", outString, errString))
6422 {
6423 error("LINK problem: %s", errString.c_str());
6424 return false;
6425 }
6426 return true;
6427 }
6429 virtual bool parse(Element *elem)
6430 {
6431 if (!parent.getAttribute(elem, "command", command))
6432 return false;
6433 if (!parent.getAttribute(elem, "out", fileName))
6434 return false;
6436 std::vector<Element *> children = elem->getChildren();
6437 for (unsigned int i=0 ; i<children.size() ; i++)
6438 {
6439 Element *child = children[i];
6440 String tagName = child->getName();
6441 if (tagName == "fileset")
6442 {
6443 if (!parseFileSet(child, parent, fileSet))
6444 return false;
6445 }
6446 else if (tagName == "flags")
6447 {
6448 if (!parent.getValue(child, flags))
6449 return false;
6450 flags = strip(flags);
6451 }
6452 else if (tagName == "libs")
6453 {
6454 if (!parent.getValue(child, libs))
6455 return false;
6456 libs = strip(libs);
6457 }
6458 }
6459 return true;
6460 }
6462 private:
6464 String command;
6465 String fileName;
6466 String flags;
6467 String libs;
6468 FileSet fileSet;
6470 };
6474 /**
6475 * Create a named directory
6476 */
6477 class TaskMakeFile : public Task
6478 {
6479 public:
6481 TaskMakeFile(MakeBase &par) : Task(par)
6482 { type = TASK_MAKEFILE; name = "makefile"; }
6484 virtual ~TaskMakeFile()
6485 {}
6487 virtual bool execute()
6488 {
6489 status(" : %s", fileName.c_str());
6490 String fullName = parent.resolve(fileName);
6491 if (!isNewerThan(parent.getURI().getPath(), fullName))
6492 {
6493 //trace("skipped <makefile>");
6494 return true;
6495 }
6496 //trace("fullName:%s", fullName.c_str());
6497 FILE *f = fopen(fullName.c_str(), "w");
6498 if (!f)
6499 {
6500 error("<makefile> could not open %s for writing : %s",
6501 fullName.c_str(), strerror(errno));
6502 return false;
6503 }
6504 for (unsigned int i=0 ; i<text.size() ; i++)
6505 fputc(text[i], f);
6506 fclose(f);
6507 return true;
6508 }
6510 virtual bool parse(Element *elem)
6511 {
6512 if (!parent.getAttribute(elem, "file", fileName))
6513 return false;
6514 if (fileName.size() == 0)
6515 {
6516 error("<makefile> requires 'file=\"filename\"' attribute");
6517 return false;
6518 }
6519 if (!parent.getValue(elem, text))
6520 return false;
6521 text = leftJustify(text);
6522 //trace("dirname:%s", dirName.c_str());
6523 return true;
6524 }
6526 private:
6528 String fileName;
6529 String text;
6530 };
6534 /**
6535 * Create a named directory
6536 */
6537 class TaskMkDir : public Task
6538 {
6539 public:
6541 TaskMkDir(MakeBase &par) : Task(par)
6542 { type = TASK_MKDIR; name = "mkdir"; }
6544 virtual ~TaskMkDir()
6545 {}
6547 virtual bool execute()
6548 {
6549 status(" : %s", dirName.c_str());
6550 String fullDir = parent.resolve(dirName);
6551 //trace("fullDir:%s", fullDir.c_str());
6552 if (!createDirectory(fullDir))
6553 return false;
6554 return true;
6555 }
6557 virtual bool parse(Element *elem)
6558 {
6559 if (!parent.getAttribute(elem, "dir", dirName))
6560 return false;
6561 if (dirName.size() == 0)
6562 {
6563 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6564 return false;
6565 }
6566 //trace("dirname:%s", dirName.c_str());
6567 return true;
6568 }
6570 private:
6572 String dirName;
6573 };
6577 /**
6578 * Create a named directory
6579 */
6580 class TaskMsgFmt: public Task
6581 {
6582 public:
6584 TaskMsgFmt(MakeBase &par) : Task(par)
6585 {
6586 type = TASK_MSGFMT;
6587 name = "msgfmt";
6588 command = "msgfmt";
6589 }
6591 virtual ~TaskMsgFmt()
6592 {}
6594 virtual bool execute()
6595 {
6596 if (!listFiles(parent, fileSet))
6597 return false;
6598 String fileSetDir = fileSet.getDirectory();
6600 //trace("msgfmt: %d", fileSet.size());
6601 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6602 {
6603 String fileName = fileSet[i];
6604 if (getSuffix(fileName) != "po")
6605 continue;
6606 String sourcePath;
6607 if (fileSetDir.size()>0)
6608 {
6609 sourcePath.append(fileSetDir);
6610 sourcePath.append("/");
6611 }
6612 sourcePath.append(fileName);
6613 String fullSource = parent.resolve(sourcePath);
6615 String destPath;
6616 if (toDirName.size()>0)
6617 {
6618 destPath.append(toDirName);
6619 destPath.append("/");
6620 }
6621 destPath.append(fileName);
6622 destPath[destPath.size()-2] = 'm';
6623 String fullDest = parent.resolve(destPath);
6625 if (!isNewerThan(fullSource, fullDest))
6626 {
6627 //trace("skip %s", fullSource.c_str());
6628 continue;
6629 }
6631 String cmd = command;
6632 cmd.append(" ");
6633 cmd.append(fullSource);
6634 cmd.append(" -o ");
6635 cmd.append(fullDest);
6637 int pos = fullDest.find_last_of('/');
6638 if (pos>0)
6639 {
6640 String fullDestPath = fullDest.substr(0, pos);
6641 if (!createDirectory(fullDestPath))
6642 return false;
6643 }
6647 String outString, errString;
6648 if (!executeCommand(cmd.c_str(), "", outString, errString))
6649 {
6650 error("<msgfmt> problem: %s", errString.c_str());
6651 return false;
6652 }
6653 }
6655 return true;
6656 }
6658 virtual bool parse(Element *elem)
6659 {
6660 if (!parent.getAttribute(elem, "todir", toDirName))
6661 return false;
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 return false;
6672 }
6673 }
6674 return true;
6675 }
6677 private:
6679 String command;
6680 String toDirName;
6681 FileSet fileSet;
6683 };
6689 /**
6690 * Process an archive to allow random access
6691 */
6692 class TaskRanlib : public Task
6693 {
6694 public:
6696 TaskRanlib(MakeBase &par) : Task(par)
6697 { type = TASK_RANLIB; name = "ranlib"; }
6699 virtual ~TaskRanlib()
6700 {}
6702 virtual bool execute()
6703 {
6704 String fullName = parent.resolve(fileName);
6705 //trace("fullDir:%s", fullDir.c_str());
6706 String cmd = "ranlib ";
6707 cmd.append(fullName);
6708 String outbuf, errbuf;
6709 if (!executeCommand(cmd, "", outbuf, errbuf))
6710 return false;
6711 return true;
6712 }
6714 virtual bool parse(Element *elem)
6715 {
6716 if (!parent.getAttribute(elem, "file", fileName))
6717 return false;
6718 if (fileName.size() == 0)
6719 {
6720 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6721 return false;
6722 }
6723 return true;
6724 }
6726 private:
6728 String fileName;
6729 };
6733 /**
6734 * Run the "ar" command to archive .o's into a .a
6735 */
6736 class TaskRC : public Task
6737 {
6738 public:
6740 TaskRC(MakeBase &par) : Task(par)
6741 {
6742 type = TASK_RC; name = "rc";
6743 command = "windres -o";
6744 }
6746 virtual ~TaskRC()
6747 {}
6749 virtual bool execute()
6750 {
6751 String fullFile = parent.resolve(fileName);
6752 String fullOut = parent.resolve(outName);
6753 if (!isNewerThan(fullFile, fullOut))
6754 return true;
6755 String cmd = command;
6756 cmd.append(" ");
6757 cmd.append(fullOut);
6758 cmd.append(" ");
6759 cmd.append(flags);
6760 cmd.append(" ");
6761 cmd.append(fullFile);
6763 String outString, errString;
6764 if (!executeCommand(cmd.c_str(), "", outString, errString))
6765 {
6766 error("RC problem: %s", errString.c_str());
6767 return false;
6768 }
6769 return true;
6770 }
6772 virtual bool parse(Element *elem)
6773 {
6774 if (!parent.getAttribute(elem, "command", command))
6775 return false;
6776 if (!parent.getAttribute(elem, "file", fileName))
6777 return false;
6778 if (!parent.getAttribute(elem, "out", outName))
6779 return false;
6780 std::vector<Element *> children = elem->getChildren();
6781 for (unsigned int i=0 ; i<children.size() ; i++)
6782 {
6783 Element *child = children[i];
6784 String tagName = child->getName();
6785 if (tagName == "flags")
6786 {
6787 if (!parent.getValue(child, flags))
6788 return false;
6789 }
6790 }
6791 return true;
6792 }
6794 private:
6796 String command;
6797 String flags;
6798 String fileName;
6799 String outName;
6801 };
6805 /**
6806 * Strip an executable
6807 */
6808 class TaskStrip : public Task
6809 {
6810 public:
6812 TaskStrip(MakeBase &par) : Task(par)
6813 { type = TASK_STRIP; name = "strip"; }
6815 virtual ~TaskStrip()
6816 {}
6818 virtual bool execute()
6819 {
6820 String fullName = parent.resolve(fileName);
6821 //trace("fullDir:%s", fullDir.c_str());
6822 String cmd = "strip ";
6823 cmd.append(fullName);
6825 String outbuf, errbuf;
6826 if (!executeCommand(cmd, "", outbuf, errbuf))
6827 return false;
6828 return true;
6829 }
6831 virtual bool parse(Element *elem)
6832 {
6833 if (!parent.getAttribute(elem, "file", fileName))
6834 return false;
6835 if (fileName.size() == 0)
6836 {
6837 error("<strip> requires 'file=\"fileNname\"' attribute");
6838 return false;
6839 }
6840 return true;
6841 }
6843 private:
6845 String fileName;
6846 };
6849 /**
6850 *
6851 */
6852 class TaskTstamp : public Task
6853 {
6854 public:
6856 TaskTstamp(MakeBase &par) : Task(par)
6857 { type = TASK_TSTAMP; name = "tstamp"; }
6859 virtual ~TaskTstamp()
6860 {}
6862 virtual bool execute()
6863 {
6864 return true;
6865 }
6867 virtual bool parse(Element *elem)
6868 {
6869 //trace("tstamp parse");
6870 return true;
6871 }
6872 };
6876 /**
6877 *
6878 */
6879 Task *Task::createTask(Element *elem)
6880 {
6881 String tagName = elem->getName();
6882 //trace("task:%s", tagName.c_str());
6883 Task *task = NULL;
6884 if (tagName == "ar")
6885 task = new TaskAr(parent);
6886 else if (tagName == "cc")
6887 task = new TaskCC(parent);
6888 else if (tagName == "copy")
6889 task = new TaskCopy(parent);
6890 else if (tagName == "delete")
6891 task = new TaskDelete(parent);
6892 else if (tagName == "jar")
6893 task = new TaskJar(parent);
6894 else if (tagName == "javac")
6895 task = new TaskJavac(parent);
6896 else if (tagName == "link")
6897 task = new TaskLink(parent);
6898 else if (tagName == "makefile")
6899 task = new TaskMakeFile(parent);
6900 else if (tagName == "mkdir")
6901 task = new TaskMkDir(parent);
6902 else if (tagName == "msgfmt")
6903 task = new TaskMsgFmt(parent);
6904 else if (tagName == "ranlib")
6905 task = new TaskRanlib(parent);
6906 else if (tagName == "rc")
6907 task = new TaskRC(parent);
6908 else if (tagName == "strip")
6909 task = new TaskStrip(parent);
6910 else if (tagName == "tstamp")
6911 task = new TaskTstamp(parent);
6912 else
6913 {
6914 error("Unknown task '%s'", tagName.c_str());
6915 return NULL;
6916 }
6918 if (!task->parse(elem))
6919 {
6920 delete task;
6921 return NULL;
6922 }
6923 return task;
6924 }
6928 //########################################################################
6929 //# T A R G E T
6930 //########################################################################
6932 /**
6933 *
6934 */
6935 class Target : public MakeBase
6936 {
6938 public:
6940 /**
6941 *
6942 */
6943 Target(Make &par) : parent(par)
6944 { init(); }
6946 /**
6947 *
6948 */
6949 Target(const Target &other) : parent(other.parent)
6950 { init(); assign(other); }
6952 /**
6953 *
6954 */
6955 Target &operator=(const Target &other)
6956 { init(); assign(other); return *this; }
6958 /**
6959 *
6960 */
6961 virtual ~Target()
6962 { cleanup() ; }
6965 /**
6966 *
6967 */
6968 virtual Make &getParent()
6969 { return parent; }
6971 /**
6972 *
6973 */
6974 virtual String getName()
6975 { return name; }
6977 /**
6978 *
6979 */
6980 virtual void setName(const String &val)
6981 { name = val; }
6983 /**
6984 *
6985 */
6986 virtual String getDescription()
6987 { return description; }
6989 /**
6990 *
6991 */
6992 virtual void setDescription(const String &val)
6993 { description = val; }
6995 /**
6996 *
6997 */
6998 virtual void addDependency(const String &val)
6999 { deps.push_back(val); }
7001 /**
7002 *
7003 */
7004 virtual void parseDependencies(const String &val)
7005 { deps = tokenize(val, ", "); }
7007 /**
7008 *
7009 */
7010 virtual std::vector<String> &getDependencies()
7011 { return deps; }
7013 /**
7014 *
7015 */
7016 virtual String getIf()
7017 { return ifVar; }
7019 /**
7020 *
7021 */
7022 virtual void setIf(const String &val)
7023 { ifVar = val; }
7025 /**
7026 *
7027 */
7028 virtual String getUnless()
7029 { return unlessVar; }
7031 /**
7032 *
7033 */
7034 virtual void setUnless(const String &val)
7035 { unlessVar = val; }
7037 /**
7038 *
7039 */
7040 virtual void addTask(Task *val)
7041 { tasks.push_back(val); }
7043 /**
7044 *
7045 */
7046 virtual std::vector<Task *> &getTasks()
7047 { return tasks; }
7049 private:
7051 void init()
7052 {
7053 }
7055 void cleanup()
7056 {
7057 tasks.clear();
7058 }
7060 void assign(const Target &other)
7061 {
7062 //parent = other.parent;
7063 name = other.name;
7064 description = other.description;
7065 ifVar = other.ifVar;
7066 unlessVar = other.unlessVar;
7067 deps = other.deps;
7068 tasks = other.tasks;
7069 }
7071 Make &parent;
7073 String name;
7075 String description;
7077 String ifVar;
7079 String unlessVar;
7081 std::vector<String> deps;
7083 std::vector<Task *> tasks;
7085 };
7094 //########################################################################
7095 //# M A K E
7096 //########################################################################
7099 /**
7100 *
7101 */
7102 class Make : public MakeBase
7103 {
7105 public:
7107 /**
7108 *
7109 */
7110 Make()
7111 { init(); }
7113 /**
7114 *
7115 */
7116 Make(const Make &other)
7117 { assign(other); }
7119 /**
7120 *
7121 */
7122 Make &operator=(const Make &other)
7123 { assign(other); return *this; }
7125 /**
7126 *
7127 */
7128 virtual ~Make()
7129 { cleanup(); }
7131 /**
7132 *
7133 */
7134 virtual std::map<String, Target> &getTargets()
7135 { return targets; }
7138 /**
7139 *
7140 */
7141 bool run();
7143 /**
7144 *
7145 */
7146 bool run(const String &target);
7150 private:
7152 /**
7153 *
7154 */
7155 void init();
7157 /**
7158 *
7159 */
7160 void cleanup();
7162 /**
7163 *
7164 */
7165 void assign(const Make &other);
7167 /**
7168 *
7169 */
7170 bool executeTask(Task &task);
7173 /**
7174 *
7175 */
7176 bool executeTarget(Target &target,
7177 std::set<String> &targetsCompleted);
7180 /**
7181 *
7182 */
7183 bool execute();
7185 /**
7186 *
7187 */
7188 bool checkTargetDependencies(Target &prop,
7189 std::vector<String> &depList);
7191 /**
7192 *
7193 */
7194 bool parsePropertyFile(const String &fileName,
7195 const String &prefix);
7197 /**
7198 *
7199 */
7200 bool parseProperty(Element *elem);
7202 /**
7203 *
7204 */
7205 bool parseTask(Task &task, Element *elem);
7207 /**
7208 *
7209 */
7210 bool parseFile();
7212 /**
7213 *
7214 */
7215 std::vector<String> glob(const String &pattern);
7218 //###############
7219 //# Fields
7220 //###############
7222 String projectName;
7224 String currentTarget;
7226 String defaultTarget;
7228 String specifiedTarget;
7230 String baseDir;
7232 String description;
7234 String envAlias;
7236 //std::vector<Property> properties;
7238 std::map<String, Target> targets;
7240 std::vector<Task *> allTasks;
7243 };
7246 //########################################################################
7247 //# C L A S S M A I N T E N A N C E
7248 //########################################################################
7250 /**
7251 *
7252 */
7253 void Make::init()
7254 {
7255 uri = "build.xml";
7256 projectName = "";
7257 currentTarget = "";
7258 defaultTarget = "";
7259 specifiedTarget = "";
7260 baseDir = "";
7261 description = "";
7262 envAlias = "";
7263 properties.clear();
7264 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7265 delete allTasks[i];
7266 allTasks.clear();
7267 }
7271 /**
7272 *
7273 */
7274 void Make::cleanup()
7275 {
7276 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7277 delete allTasks[i];
7278 allTasks.clear();
7279 }
7283 /**
7284 *
7285 */
7286 void Make::assign(const Make &other)
7287 {
7288 uri = other.uri;
7289 projectName = other.projectName;
7290 currentTarget = other.currentTarget;
7291 defaultTarget = other.defaultTarget;
7292 specifiedTarget = other.specifiedTarget;
7293 baseDir = other.baseDir;
7294 description = other.description;
7295 properties = other.properties;
7296 }
7300 //########################################################################
7301 //# U T I L I T Y T A S K S
7302 //########################################################################
7304 /**
7305 * Perform a file globbing
7306 */
7307 std::vector<String> Make::glob(const String &pattern)
7308 {
7309 std::vector<String> res;
7310 return res;
7311 }
7314 //########################################################################
7315 //# P U B L I C A P I
7316 //########################################################################
7320 /**
7321 *
7322 */
7323 bool Make::executeTarget(Target &target,
7324 std::set<String> &targetsCompleted)
7325 {
7327 String name = target.getName();
7329 //First get any dependencies for this target
7330 std::vector<String> deps = target.getDependencies();
7331 for (unsigned int i=0 ; i<deps.size() ; i++)
7332 {
7333 String dep = deps[i];
7334 //Did we do it already? Skip
7335 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7336 continue;
7338 std::map<String, Target> &tgts =
7339 target.getParent().getTargets();
7340 std::map<String, Target>::iterator iter =
7341 tgts.find(dep);
7342 if (iter == tgts.end())
7343 {
7344 error("Target '%s' dependency '%s' not found",
7345 name.c_str(), dep.c_str());
7346 return false;
7347 }
7348 Target depTarget = iter->second;
7349 if (!executeTarget(depTarget, targetsCompleted))
7350 {
7351 return false;
7352 }
7353 }
7355 status("## Target : %s", name.c_str());
7357 //Now let's do the tasks
7358 std::vector<Task *> &tasks = target.getTasks();
7359 for (unsigned int i=0 ; i<tasks.size() ; i++)
7360 {
7361 Task *task = tasks[i];
7362 status("---- task : %s", task->getName().c_str());
7363 if (!task->execute())
7364 {
7365 return false;
7366 }
7367 }
7369 targetsCompleted.insert(name);
7371 return true;
7372 }
7376 /**
7377 * Main execute() method. Start here and work
7378 * up the dependency tree
7379 */
7380 bool Make::execute()
7381 {
7382 status("######## EXECUTE");
7384 //Determine initial target
7385 if (specifiedTarget.size()>0)
7386 {
7387 currentTarget = specifiedTarget;
7388 }
7389 else if (defaultTarget.size()>0)
7390 {
7391 currentTarget = defaultTarget;
7392 }
7393 else
7394 {
7395 error("execute: no specified or default target requested");
7396 return false;
7397 }
7399 std::map<String, Target>::iterator iter =
7400 targets.find(currentTarget);
7401 if (iter == targets.end())
7402 {
7403 error("Initial target '%s' not found",
7404 currentTarget.c_str());
7405 return false;
7406 }
7408 //Now run
7409 Target target = iter->second;
7410 std::set<String> targetsCompleted;
7411 if (!executeTarget(target, targetsCompleted))
7412 {
7413 return false;
7414 }
7416 status("######## EXECUTE COMPLETE");
7417 return true;
7418 }
7423 /**
7424 *
7425 */
7426 bool Make::checkTargetDependencies(Target &target,
7427 std::vector<String> &depList)
7428 {
7429 String tgtName = target.getName().c_str();
7430 depList.push_back(tgtName);
7432 std::vector<String> deps = target.getDependencies();
7433 for (unsigned int i=0 ; i<deps.size() ; i++)
7434 {
7435 String dep = deps[i];
7436 //First thing entered was the starting Target
7437 if (dep == depList[0])
7438 {
7439 error("Circular dependency '%s' found at '%s'",
7440 dep.c_str(), tgtName.c_str());
7441 std::vector<String>::iterator diter;
7442 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7443 {
7444 error(" %s", diter->c_str());
7445 }
7446 return false;
7447 }
7449 std::map<String, Target> &tgts =
7450 target.getParent().getTargets();
7451 std::map<String, Target>::iterator titer = tgts.find(dep);
7452 if (titer == tgts.end())
7453 {
7454 error("Target '%s' dependency '%s' not found",
7455 tgtName.c_str(), dep.c_str());
7456 return false;
7457 }
7458 if (!checkTargetDependencies(titer->second, depList))
7459 {
7460 return false;
7461 }
7462 }
7463 return true;
7464 }
7470 static int getword(int pos, const String &inbuf, String &result)
7471 {
7472 int p = pos;
7473 int len = (int)inbuf.size();
7474 String val;
7475 while (p < len)
7476 {
7477 char ch = inbuf[p];
7478 if (!isalnum(ch) && ch!='.' && ch!='_')
7479 break;
7480 val.push_back(ch);
7481 p++;
7482 }
7483 result = val;
7484 return p;
7485 }
7490 /**
7491 *
7492 */
7493 bool Make::parsePropertyFile(const String &fileName,
7494 const String &prefix)
7495 {
7496 FILE *f = fopen(fileName.c_str(), "r");
7497 if (!f)
7498 {
7499 error("could not open property file %s", fileName.c_str());
7500 return false;
7501 }
7502 int linenr = 0;
7503 while (!feof(f))
7504 {
7505 char buf[256];
7506 if (!fgets(buf, 255, f))
7507 break;
7508 linenr++;
7509 String s = buf;
7510 s = trim(s);
7511 int len = s.size();
7512 if (len == 0)
7513 continue;
7514 if (s[0] == '#')
7515 continue;
7516 String key;
7517 String val;
7518 int p = 0;
7519 int p2 = getword(p, s, key);
7520 if (p2 <= p)
7521 {
7522 error("property file %s, line %d: expected keyword",
7523 fileName.c_str(), linenr);
7524 return false;
7525 }
7526 if (prefix.size() > 0)
7527 {
7528 key.insert(0, prefix);
7529 }
7531 //skip whitespace
7532 for (p=p2 ; p<len ; p++)
7533 if (!isspace(s[p]))
7534 break;
7536 if (p>=len || s[p]!='=')
7537 {
7538 error("property file %s, line %d: expected '='",
7539 fileName.c_str(), linenr);
7540 return false;
7541 }
7542 p++;
7544 //skip whitespace
7545 for ( ; p<len ; p++)
7546 if (!isspace(s[p]))
7547 break;
7549 /* This way expects a word after the =
7550 p2 = getword(p, s, val);
7551 if (p2 <= p)
7552 {
7553 error("property file %s, line %d: expected value",
7554 fileName.c_str(), linenr);
7555 return false;
7556 }
7557 */
7558 // This way gets the rest of the line after the =
7559 if (p>=len)
7560 {
7561 error("property file %s, line %d: expected value",
7562 fileName.c_str(), linenr);
7563 return false;
7564 }
7565 val = s.substr(p);
7566 if (key.size()==0 || val.size()==0)
7567 continue;
7569 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7570 properties[key] = val;
7571 }
7572 fclose(f);
7573 return true;
7574 }
7579 /**
7580 *
7581 */
7582 bool Make::parseProperty(Element *elem)
7583 {
7584 std::vector<Attribute> &attrs = elem->getAttributes();
7585 for (unsigned int i=0 ; i<attrs.size() ; i++)
7586 {
7587 String attrName = attrs[i].getName();
7588 String attrVal = attrs[i].getValue();
7590 if (attrName == "name")
7591 {
7592 String val;
7593 if (!getAttribute(elem, "value", val))
7594 return false;
7595 if (val.size() > 0)
7596 {
7597 properties[attrVal] = val;
7598 continue;
7599 }
7600 if (!getAttribute(elem, "location", val))
7601 return false;
7602 if (val.size() > 0)
7603 {
7604 //TODO: process a path relative to build.xml
7605 properties[attrVal] = val;
7606 continue;
7607 }
7608 }
7609 else if (attrName == "file")
7610 {
7611 String prefix;
7612 if (!getAttribute(elem, "prefix", prefix))
7613 return false;
7614 if (prefix.size() > 0)
7615 {
7616 if (prefix[prefix.size()-1] != '.')
7617 prefix.push_back('.');
7618 }
7619 if (!parsePropertyFile(attrName, prefix))
7620 return false;
7621 }
7622 else if (attrName == "environment")
7623 {
7624 if (envAlias.size() > 0)
7625 {
7626 error("environment property can only be set once");
7627 return false;
7628 }
7629 envAlias = attrVal;
7630 }
7631 }
7633 return true;
7634 }
7639 /**
7640 *
7641 */
7642 bool Make::parseFile()
7643 {
7644 status("######## PARSE");
7646 Parser parser;
7647 Element *root = parser.parseFile(uri.getNativePath());
7648 if (!root)
7649 {
7650 error("Could not open %s for reading",
7651 uri.getNativePath().c_str());
7652 return false;
7653 }
7655 if (root->getChildren().size()==0 ||
7656 root->getChildren()[0]->getName()!="project")
7657 {
7658 error("Main xml element should be <project>");
7659 delete root;
7660 return false;
7661 }
7663 //########## Project attributes
7664 Element *project = root->getChildren()[0];
7665 String s = project->getAttribute("name");
7666 if (s.size() > 0)
7667 projectName = s;
7668 s = project->getAttribute("default");
7669 if (s.size() > 0)
7670 defaultTarget = s;
7671 s = project->getAttribute("basedir");
7672 if (s.size() > 0)
7673 baseDir = s;
7675 //######### PARSE MEMBERS
7676 std::vector<Element *> children = project->getChildren();
7677 for (unsigned int i=0 ; i<children.size() ; i++)
7678 {
7679 Element *elem = children[i];
7680 String tagName = elem->getName();
7682 //########## DESCRIPTION
7683 if (tagName == "description")
7684 {
7685 description = parser.trim(elem->getValue());
7686 }
7688 //######### PROPERTY
7689 else if (tagName == "property")
7690 {
7691 if (!parseProperty(elem))
7692 return false;
7693 }
7695 //######### TARGET
7696 else if (tagName == "target")
7697 {
7698 String tname = elem->getAttribute("name");
7699 String tdesc = elem->getAttribute("description");
7700 String tdeps = elem->getAttribute("depends");
7701 String tif = elem->getAttribute("if");
7702 String tunless = elem->getAttribute("unless");
7703 Target target(*this);
7704 target.setName(tname);
7705 target.setDescription(tdesc);
7706 target.parseDependencies(tdeps);
7707 target.setIf(tif);
7708 target.setUnless(tunless);
7709 std::vector<Element *> telems = elem->getChildren();
7710 for (unsigned int i=0 ; i<telems.size() ; i++)
7711 {
7712 Element *telem = telems[i];
7713 Task breeder(*this);
7714 Task *task = breeder.createTask(telem);
7715 if (!task)
7716 return false;
7717 allTasks.push_back(task);
7718 target.addTask(task);
7719 }
7721 //Check name
7722 if (tname.size() == 0)
7723 {
7724 error("no name for target");
7725 return false;
7726 }
7727 //Check for duplicate name
7728 if (targets.find(tname) != targets.end())
7729 {
7730 error("target '%s' already defined", tname.c_str());
7731 return false;
7732 }
7733 //more work than targets[tname]=target, but avoids default allocator
7734 targets.insert(std::make_pair<String, Target>(tname, target));
7735 }
7737 }
7739 std::map<String, Target>::iterator iter;
7740 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
7741 {
7742 Target tgt = iter->second;
7743 std::vector<String> depList;
7744 if (!checkTargetDependencies(tgt, depList))
7745 {
7746 return false;
7747 }
7748 }
7751 delete root;
7752 status("######## PARSE COMPLETE");
7753 return true;
7754 }
7757 /**
7758 *
7759 */
7760 bool Make::run()
7761 {
7762 if (!parseFile())
7763 return false;
7764 if (!execute())
7765 return false;
7766 return true;
7767 }
7770 /**
7771 *
7772 */
7773 bool Make::run(const String &target)
7774 {
7775 status("##################################");
7776 status("# BuildTool");
7777 status("# version 0.3");
7778 status("# 16 Nov 06");
7779 status("##################################");
7780 specifiedTarget = target;
7781 if (!run())
7782 return false;
7783 status("##################################");
7784 status("# BuildTool Completed");
7785 status("##################################");
7786 return true;
7787 }
7795 }// namespace buildtool
7796 //########################################################################
7797 //# M A I N
7798 //########################################################################
7800 /**
7801 * Format an error message in printf() style
7802 */
7803 static void error(char *fmt, ...)
7804 {
7805 va_list ap;
7806 va_start(ap, fmt);
7807 fprintf(stderr, "BuildTool error: ");
7808 vfprintf(stderr, fmt, ap);
7809 fprintf(stderr, "\n");
7810 va_end(ap);
7811 }
7814 /**
7815 * Compare a buffer with a key, for the length of the key
7816 */
7817 static bool sequ(const buildtool::String &buf, char *key)
7818 {
7819 for (int i=0 ; key[i] ; i++)
7820 {
7821 if (key[i] != buf[i])
7822 return false;
7823 }
7824 return true;
7825 }
7827 /**
7828 * Parse the command-line args, get our options,
7829 * and run this thing
7830 */
7831 static bool parseOptions(int argc, char **argv)
7832 {
7833 if (argc < 1)
7834 {
7835 error("Cannot parse arguments");
7836 return false;
7837 }
7839 buildtool::String buildFile;
7840 buildtool::String target;
7842 //char *progName = argv[0];
7843 for (int i=1 ; i<argc ; i++)
7844 {
7845 buildtool::String arg = argv[i];
7846 if (sequ(arg, "--"))
7847 {
7848 if (sequ(arg, "--file=") && arg.size()>7)
7849 {
7850 buildFile = arg.substr(7, arg.size()-7);
7851 }
7852 else
7853 {
7854 error("Unknown option:%s", arg.c_str());
7855 return false;
7856 }
7857 }
7858 else if (sequ(arg, "-"))
7859 {
7860 for (unsigned int p=1 ; p<arg.size() ; p++)
7861 {
7862 int ch = arg[p];
7863 if (0)//put options here
7864 {
7865 }
7866 else
7867 {
7868 error("Unknown option '%c'", ch);
7869 return false;
7870 }
7871 }
7872 }
7873 else
7874 {
7875 target = arg;
7876 }
7877 }
7879 //We have the options. Now execute them
7880 buildtool::Make make;
7881 if (buildFile.size() > 0)
7882 {
7883 make.setURI(buildFile);
7884 }
7885 if (!make.run(target))
7886 return false;
7888 return true;
7889 }
7894 /*
7895 static bool runMake()
7896 {
7897 buildtool::Make make;
7898 if (!make.run())
7899 return false;
7900 return true;
7901 }
7904 static bool pkgConfigTest()
7905 {
7906 buildtool::PkgConfig pkgConfig;
7907 if (!pkgConfig.readFile("gtk+-2.0.pc"))
7908 return false;
7909 return true;
7910 }
7914 static bool depTest()
7915 {
7916 buildtool::DepTool deptool;
7917 deptool.setSourceDirectory("/dev/ink/inkscape/src");
7918 if (!deptool.generateDependencies("build.dep"))
7919 return false;
7920 std::vector<buildtool::DepRec> res =
7921 deptool.loadDepFile("build.dep");
7922 if (res.size() == 0)
7923 return false;
7924 return true;
7925 }
7927 static bool popenTest()
7928 {
7929 buildtool::Make make;
7930 buildtool::String out, err;
7931 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
7932 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
7933 return true;
7934 }
7937 static bool propFileTest()
7938 {
7939 buildtool::Make make;
7940 make.parsePropertyFile("test.prop", "test.");
7941 return true;
7942 }
7943 */
7945 int main(int argc, char **argv)
7946 {
7948 if (!parseOptions(argc, argv))
7949 return 1;
7950 /*
7951 if (!popenTest())
7952 return 1;
7954 if (!depTest())
7955 return 1;
7956 if (!propFileTest())
7957 return 1;
7958 if (runMake())
7959 return 1;
7960 */
7961 return 0;
7962 }
7965 //########################################################################
7966 //# E N D
7967 //########################################################################