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(cnative, &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>0 && pos != dirname.npos)
3924 {
3925 String subpath = dirname.substr(0, pos);
3926 //A letter root (c:) ?
3927 if (!createDirectory(subpath))
3928 return false;
3929 }
3931 //## 3: now make
3932 if (mkdir(cnative)<0)
3933 {
3934 error("cannot make directory '%s'", cnative);
3935 return false;
3936 }
3938 return true;
3939 }
3942 /**
3943 * Remove a directory recursively
3944 */
3945 bool MakeBase::removeDirectory(const String &dirName)
3946 {
3947 char *dname = (char *)dirName.c_str();
3949 DIR *dir = opendir(dname);
3950 if (!dir)
3951 {
3952 //# Let this fail nicely.
3953 return true;
3954 //error("error opening directory %s : %s", dname, strerror(errno));
3955 //return false;
3956 }
3958 while (true)
3959 {
3960 struct dirent *de = readdir(dir);
3961 if (!de)
3962 break;
3964 //Get the directory member name
3965 String s = de->d_name;
3966 if (s.size() == 0 || s[0] == '.')
3967 continue;
3968 String childName;
3969 if (dirName.size() > 0)
3970 {
3971 childName.append(dirName);
3972 childName.append("/");
3973 }
3974 childName.append(s);
3977 struct stat finfo;
3978 String childNative = getNativePath(childName);
3979 char *cnative = (char *)childNative.c_str();
3980 if (stat(cnative, &finfo)<0)
3981 {
3982 error("cannot stat file:%s", cnative);
3983 }
3984 else if (S_ISDIR(finfo.st_mode))
3985 {
3986 //trace("DEL dir: %s", childName.c_str());
3987 if (!removeDirectory(childName))
3988 {
3989 return false;
3990 }
3991 }
3992 else if (!S_ISREG(finfo.st_mode))
3993 {
3994 //trace("not regular: %s", cnative);
3995 }
3996 else
3997 {
3998 //trace("DEL file: %s", childName.c_str());
3999 if (remove(cnative)<0)
4000 {
4001 error("error deleting %s : %s",
4002 cnative, strerror(errno));
4003 return false;
4004 }
4005 }
4006 }
4007 closedir(dir);
4009 //Now delete the directory
4010 String native = getNativePath(dirName);
4011 if (rmdir(native.c_str())<0)
4012 {
4013 error("could not delete directory %s : %s",
4014 native.c_str() , strerror(errno));
4015 return false;
4016 }
4018 return true;
4020 }
4023 /**
4024 * Copy a file from one name to another. Perform only if needed
4025 */
4026 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4027 {
4028 //# 1 Check up-to-date times
4029 String srcNative = getNativePath(srcFile);
4030 struct stat srcinfo;
4031 if (stat(srcNative.c_str(), &srcinfo)<0)
4032 {
4033 error("source file %s for copy does not exist",
4034 srcNative.c_str());
4035 return false;
4036 }
4038 String destNative = getNativePath(destFile);
4039 struct stat destinfo;
4040 if (stat(destNative.c_str(), &destinfo)==0)
4041 {
4042 if (destinfo.st_mtime >= srcinfo.st_mtime)
4043 return true;
4044 }
4046 //# 2 prepare a destination directory if necessary
4047 unsigned int pos = destFile.find_last_of('/');
4048 if (pos != destFile.npos)
4049 {
4050 String subpath = destFile.substr(0, pos);
4051 if (!createDirectory(subpath))
4052 return false;
4053 }
4055 //# 3 do the data copy
4056 #ifndef __WIN32__
4058 FILE *srcf = fopen(srcNative.c_str(), "rb");
4059 if (!srcf)
4060 {
4061 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4062 return false;
4063 }
4064 FILE *destf = fopen(destNative.c_str(), "wb");
4065 if (!destf)
4066 {
4067 error("copyFile cannot open %s for writing", srcNative.c_str());
4068 return false;
4069 }
4071 while (!feof(srcf))
4072 {
4073 int ch = fgetc(srcf);
4074 if (ch<0)
4075 break;
4076 fputc(ch, destf);
4077 }
4079 fclose(destf);
4080 fclose(srcf);
4082 #else
4084 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4085 {
4086 error("copyFile from %s to %s failed",
4087 srcNative.c_str(), destNative.c_str());
4088 return false;
4089 }
4091 #endif /* __WIN32__ */
4094 return true;
4095 }
4099 /**
4100 * Tests if the file exists and is a regular file
4101 */
4102 bool MakeBase::isRegularFile(const String &fileName)
4103 {
4104 String native = getNativePath(fileName);
4105 struct stat finfo;
4107 //Exists?
4108 if (stat(native.c_str(), &finfo)<0)
4109 return false;
4112 //check the file mode
4113 if (!S_ISREG(finfo.st_mode))
4114 return false;
4116 return true;
4117 }
4119 /**
4120 * Tests if the file exists and is a directory
4121 */
4122 bool MakeBase::isDirectory(const String &fileName)
4123 {
4124 String native = getNativePath(fileName);
4125 struct stat finfo;
4127 //Exists?
4128 if (stat(native.c_str(), &finfo)<0)
4129 return false;
4132 //check the file mode
4133 if (!S_ISDIR(finfo.st_mode))
4134 return false;
4136 return true;
4137 }
4141 /**
4142 * Tests is the modification of fileA is newer than fileB
4143 */
4144 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4145 {
4146 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4147 String nativeA = getNativePath(fileA);
4148 struct stat infoA;
4149 //IF source does not exist, NOT newer
4150 if (stat(nativeA.c_str(), &infoA)<0)
4151 {
4152 return false;
4153 }
4155 String nativeB = getNativePath(fileB);
4156 struct stat infoB;
4157 //IF dest does not exist, YES, newer
4158 if (stat(nativeB.c_str(), &infoB)<0)
4159 {
4160 return true;
4161 }
4163 //check the actual times
4164 if (infoA.st_mtime > infoB.st_mtime)
4165 {
4166 return true;
4167 }
4169 return false;
4170 }
4173 //########################################################################
4174 //# P K G C O N F I G
4175 //########################################################################
4177 /**
4178 *
4179 */
4180 class PkgConfig : public MakeBase
4181 {
4183 public:
4185 /**
4186 *
4187 */
4188 PkgConfig()
4189 { init(); }
4191 /**
4192 *
4193 */
4194 PkgConfig(const String &namearg)
4195 { init(); name = namearg; }
4197 /**
4198 *
4199 */
4200 PkgConfig(const PkgConfig &other)
4201 { assign(other); }
4203 /**
4204 *
4205 */
4206 PkgConfig &operator=(const PkgConfig &other)
4207 { assign(other); return *this; }
4209 /**
4210 *
4211 */
4212 virtual ~PkgConfig()
4213 { }
4215 /**
4216 *
4217 */
4218 virtual String getName()
4219 { return name; }
4221 /**
4222 *
4223 */
4224 virtual String getDescription()
4225 { return description; }
4227 /**
4228 *
4229 */
4230 virtual String getCflags()
4231 { return cflags; }
4233 /**
4234 *
4235 */
4236 virtual String getLibs()
4237 { return libs; }
4239 /**
4240 *
4241 */
4242 virtual String getVersion()
4243 { return version; }
4245 /**
4246 *
4247 */
4248 virtual int getMajorVersion()
4249 { return majorVersion; }
4251 /**
4252 *
4253 */
4254 virtual int getMinorVersion()
4255 { return minorVersion; }
4257 /**
4258 *
4259 */
4260 virtual int getMicroVersion()
4261 { return microVersion; }
4263 /**
4264 *
4265 */
4266 virtual std::map<String, String> &getAttributes()
4267 { return attrs; }
4269 /**
4270 *
4271 */
4272 virtual std::vector<String> &getRequireList()
4273 { return requireList; }
4275 virtual bool readFile(const String &fileName);
4277 private:
4279 void init()
4280 {
4281 name = "";
4282 description = "";
4283 cflags = "";
4284 libs = "";
4285 requires = "";
4286 version = "";
4287 majorVersion = 0;
4288 minorVersion = 0;
4289 microVersion = 0;
4290 fileName = "";
4291 attrs.clear();
4292 requireList.clear();
4293 }
4295 void assign(const PkgConfig &other)
4296 {
4297 name = other.name;
4298 description = other.description;
4299 cflags = other.cflags;
4300 libs = other.libs;
4301 requires = other.requires;
4302 version = other.version;
4303 majorVersion = other.majorVersion;
4304 minorVersion = other.minorVersion;
4305 microVersion = other.microVersion;
4306 fileName = other.fileName;
4307 attrs = other.attrs;
4308 requireList = other.requireList;
4309 }
4313 int get(int pos);
4315 int skipwhite(int pos);
4317 int getword(int pos, String &ret);
4319 void parseRequires();
4321 void parseVersion();
4323 bool parse(const String &buf);
4325 void dumpAttrs();
4327 String name;
4329 String description;
4331 String cflags;
4333 String libs;
4335 String requires;
4337 String version;
4339 int majorVersion;
4341 int minorVersion;
4343 int microVersion;
4345 String fileName;
4347 std::map<String, String> attrs;
4349 std::vector<String> requireList;
4351 char *parsebuf;
4352 int parselen;
4353 };
4356 /**
4357 * Get a character from the buffer at pos. If out of range,
4358 * return -1 for safety
4359 */
4360 int PkgConfig::get(int pos)
4361 {
4362 if (pos>parselen)
4363 return -1;
4364 return parsebuf[pos];
4365 }
4369 /**
4370 * Skip over all whitespace characters beginning at pos. Return
4371 * the position of the first non-whitespace character.
4372 */
4373 int PkgConfig::skipwhite(int pos)
4374 {
4375 while (pos < parselen)
4376 {
4377 int ch = get(pos);
4378 if (ch < 0)
4379 break;
4380 if (!isspace(ch))
4381 break;
4382 pos++;
4383 }
4384 return pos;
4385 }
4388 /**
4389 * Parse the buffer beginning at pos, for a word. Fill
4390 * 'ret' with the result. Return the position after the
4391 * word.
4392 */
4393 int PkgConfig::getword(int pos, String &ret)
4394 {
4395 while (pos < parselen)
4396 {
4397 int ch = get(pos);
4398 if (ch < 0)
4399 break;
4400 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4401 break;
4402 ret.push_back((char)ch);
4403 pos++;
4404 }
4405 return pos;
4406 }
4408 void PkgConfig::parseRequires()
4409 {
4410 if (requires.size() == 0)
4411 return;
4412 parsebuf = (char *)requires.c_str();
4413 parselen = requires.size();
4414 int pos = 0;
4415 while (pos < parselen)
4416 {
4417 pos = skipwhite(pos);
4418 String val;
4419 int pos2 = getword(pos, val);
4420 if (pos2 == pos)
4421 break;
4422 pos = pos2;
4423 //trace("val %s", val.c_str());
4424 requireList.push_back(val);
4425 }
4426 }
4428 static int getint(const String str)
4429 {
4430 char *s = (char *)str.c_str();
4431 char *ends = NULL;
4432 long val = strtol(s, &ends, 10);
4433 if (ends == s)
4434 return 0L;
4435 else
4436 return val;
4437 }
4439 void PkgConfig::parseVersion()
4440 {
4441 if (version.size() == 0)
4442 return;
4443 String s1, s2, s3;
4444 unsigned int pos = 0;
4445 unsigned int pos2 = version.find('.', pos);
4446 if (pos2 == version.npos)
4447 {
4448 s1 = version;
4449 }
4450 else
4451 {
4452 s1 = version.substr(pos, pos2-pos);
4453 pos = pos2;
4454 pos++;
4455 if (pos < version.size())
4456 {
4457 pos2 = version.find('.', pos);
4458 if (pos2 == version.npos)
4459 {
4460 s2 = version.substr(pos, version.size()-pos);
4461 }
4462 else
4463 {
4464 s2 = version.substr(pos, pos2-pos);
4465 pos = pos2;
4466 pos++;
4467 if (pos < version.size())
4468 s3 = version.substr(pos, pos2-pos);
4469 }
4470 }
4471 }
4473 majorVersion = getint(s1);
4474 minorVersion = getint(s2);
4475 microVersion = getint(s3);
4476 //trace("version:%d.%d.%d", majorVersion,
4477 // minorVersion, microVersion );
4478 }
4481 bool PkgConfig::parse(const String &buf)
4482 {
4483 init();
4485 parsebuf = (char *)buf.c_str();
4486 parselen = buf.size();
4487 int pos = 0;
4490 while (pos < parselen)
4491 {
4492 String attrName;
4493 pos = skipwhite(pos);
4494 int ch = get(pos);
4495 if (ch == '#')
4496 {
4497 //comment. eat the rest of the line
4498 while (pos < parselen)
4499 {
4500 ch = get(pos);
4501 if (ch == '\n' || ch < 0)
4502 break;
4503 pos++;
4504 }
4505 continue;
4506 }
4507 pos = getword(pos, attrName);
4508 if (attrName.size() == 0)
4509 continue;
4510 pos = skipwhite(pos);
4511 ch = get(pos);
4512 if (ch != ':' && ch != '=')
4513 {
4514 error("expected ':' or '='");
4515 return false;
4516 }
4517 pos++;
4518 pos = skipwhite(pos);
4519 String attrVal;
4520 while (pos < parselen)
4521 {
4522 ch = get(pos);
4523 if (ch == '\n' || ch < 0)
4524 break;
4525 else if (ch == '$' && get(pos+1) == '{')
4526 {
4527 //# this is a ${substitution}
4528 pos += 2;
4529 String subName;
4530 while (pos < parselen)
4531 {
4532 ch = get(pos);
4533 if (ch < 0)
4534 {
4535 error("unterminated substitution");
4536 return false;
4537 }
4538 else if (ch == '}')
4539 break;
4540 else
4541 subName.push_back((char)ch);
4542 pos++;
4543 }
4544 //trace("subName:%s", subName.c_str());
4545 String subVal = attrs[subName];
4546 //trace("subVal:%s", subVal.c_str());
4547 attrVal.append(subVal);
4548 }
4549 else
4550 attrVal.push_back((char)ch);
4551 pos++;
4552 }
4554 attrVal = trim(attrVal);
4555 attrs[attrName] = attrVal;
4557 if (attrName == "Name")
4558 name = attrVal;
4559 else if (attrName == "Description")
4560 description = attrVal;
4561 else if (attrName == "Cflags")
4562 cflags = attrVal;
4563 else if (attrName == "Libs")
4564 libs = attrVal;
4565 else if (attrName == "Requires")
4566 requires = attrVal;
4567 else if (attrName == "Version")
4568 version = attrVal;
4570 //trace("name:'%s' value:'%s'",
4571 // attrName.c_str(), attrVal.c_str());
4572 }
4575 parseRequires();
4576 parseVersion();
4578 return true;
4579 }
4581 void PkgConfig::dumpAttrs()
4582 {
4583 //trace("### PkgConfig attributes for %s", fileName.c_str());
4584 std::map<String, String>::iterator iter;
4585 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4586 {
4587 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4588 }
4589 }
4592 bool PkgConfig::readFile(const String &fileNameArg)
4593 {
4594 fileName = fileNameArg;
4596 FILE *f = fopen(fileName.c_str(), "r");
4597 if (!f)
4598 {
4599 error("cannot open file '%s' for reading", fileName.c_str());
4600 return false;
4601 }
4602 String buf;
4603 while (true)
4604 {
4605 int ch = fgetc(f);
4606 if (ch < 0)
4607 break;
4608 buf.push_back((char)ch);
4609 }
4610 fclose(f);
4612 //trace("####### File:\n%s", buf.c_str());
4613 if (!parse(buf))
4614 {
4615 return false;
4616 }
4618 dumpAttrs();
4620 return true;
4621 }
4627 //########################################################################
4628 //# D E P T O O L
4629 //########################################################################
4633 /**
4634 * Class which holds information for each file.
4635 */
4636 class FileRec
4637 {
4638 public:
4640 typedef enum
4641 {
4642 UNKNOWN,
4643 CFILE,
4644 HFILE,
4645 OFILE
4646 } FileType;
4648 /**
4649 * Constructor
4650 */
4651 FileRec()
4652 {init(); type = UNKNOWN;}
4654 /**
4655 * Copy constructor
4656 */
4657 FileRec(const FileRec &other)
4658 {init(); assign(other);}
4659 /**
4660 * Constructor
4661 */
4662 FileRec(int typeVal)
4663 {init(); type = typeVal;}
4664 /**
4665 * Assignment operator
4666 */
4667 FileRec &operator=(const FileRec &other)
4668 {init(); assign(other); return *this;}
4671 /**
4672 * Destructor
4673 */
4674 ~FileRec()
4675 {}
4677 /**
4678 * Directory part of the file name
4679 */
4680 String path;
4682 /**
4683 * Base name, sans directory and suffix
4684 */
4685 String baseName;
4687 /**
4688 * File extension, such as cpp or h
4689 */
4690 String suffix;
4692 /**
4693 * Type of file: CFILE, HFILE, OFILE
4694 */
4695 int type;
4697 /**
4698 * Used to list files ref'd by this one
4699 */
4700 std::map<String, FileRec *> files;
4703 private:
4705 void init()
4706 {
4707 }
4709 void assign(const FileRec &other)
4710 {
4711 type = other.type;
4712 baseName = other.baseName;
4713 suffix = other.suffix;
4714 files = other.files;
4715 }
4717 };
4721 /**
4722 * Simpler dependency record
4723 */
4724 class DepRec
4725 {
4726 public:
4728 /**
4729 * Constructor
4730 */
4731 DepRec()
4732 {init();}
4734 /**
4735 * Copy constructor
4736 */
4737 DepRec(const DepRec &other)
4738 {init(); assign(other);}
4739 /**
4740 * Constructor
4741 */
4742 DepRec(const String &fname)
4743 {init(); name = fname; }
4744 /**
4745 * Assignment operator
4746 */
4747 DepRec &operator=(const DepRec &other)
4748 {init(); assign(other); return *this;}
4751 /**
4752 * Destructor
4753 */
4754 ~DepRec()
4755 {}
4757 /**
4758 * Directory part of the file name
4759 */
4760 String path;
4762 /**
4763 * Base name, without the path and suffix
4764 */
4765 String name;
4767 /**
4768 * Suffix of the source
4769 */
4770 String suffix;
4773 /**
4774 * Used to list files ref'd by this one
4775 */
4776 std::vector<String> files;
4779 private:
4781 void init()
4782 {
4783 }
4785 void assign(const DepRec &other)
4786 {
4787 path = other.path;
4788 name = other.name;
4789 suffix = other.suffix;
4790 files = other.files;
4791 }
4793 };
4796 class DepTool : public MakeBase
4797 {
4798 public:
4800 /**
4801 * Constructor
4802 */
4803 DepTool()
4804 {init();}
4806 /**
4807 * Copy constructor
4808 */
4809 DepTool(const DepTool &other)
4810 {init(); assign(other);}
4812 /**
4813 * Assignment operator
4814 */
4815 DepTool &operator=(const DepTool &other)
4816 {init(); assign(other); return *this;}
4819 /**
4820 * Destructor
4821 */
4822 ~DepTool()
4823 {}
4826 /**
4827 * Reset this section of code
4828 */
4829 virtual void init();
4831 /**
4832 * Reset this section of code
4833 */
4834 virtual void assign(const DepTool &other)
4835 {
4836 }
4838 /**
4839 * Sets the source directory which will be scanned
4840 */
4841 virtual void setSourceDirectory(const String &val)
4842 { sourceDir = val; }
4844 /**
4845 * Returns the source directory which will be scanned
4846 */
4847 virtual String getSourceDirectory()
4848 { return sourceDir; }
4850 /**
4851 * Sets the list of files within the directory to analyze
4852 */
4853 virtual void setFileList(const std::vector<String> &list)
4854 { fileList = list; }
4856 /**
4857 * Creates the list of all file names which will be
4858 * candidates for further processing. Reads make.exclude
4859 * to see which files for directories to leave out.
4860 */
4861 virtual bool createFileList();
4864 /**
4865 * Generates the forward dependency list
4866 */
4867 virtual bool generateDependencies();
4870 /**
4871 * Generates the forward dependency list, saving the file
4872 */
4873 virtual bool generateDependencies(const String &);
4876 /**
4877 * Load a dependency file
4878 */
4879 std::vector<DepRec> loadDepFile(const String &fileName);
4881 /**
4882 * Load a dependency file, generating one if necessary
4883 */
4884 std::vector<DepRec> getDepFile(const String &fileName);
4886 /**
4887 * Save a dependency file
4888 */
4889 bool saveDepFile(const String &fileName);
4892 private:
4895 /**
4896 *
4897 */
4898 void parseName(const String &fullname,
4899 String &path,
4900 String &basename,
4901 String &suffix);
4903 /**
4904 *
4905 */
4906 int get(int pos);
4908 /**
4909 *
4910 */
4911 int skipwhite(int pos);
4913 /**
4914 *
4915 */
4916 int getword(int pos, String &ret);
4918 /**
4919 *
4920 */
4921 bool sequ(int pos, char *key);
4923 /**
4924 *
4925 */
4926 bool addIncludeFile(FileRec *frec, const String &fname);
4928 /**
4929 *
4930 */
4931 bool scanFile(const String &fname, FileRec *frec);
4933 /**
4934 *
4935 */
4936 bool processDependency(FileRec *ofile,
4937 FileRec *include,
4938 int depth);
4940 /**
4941 *
4942 */
4943 String sourceDir;
4945 /**
4946 *
4947 */
4948 std::vector<String> fileList;
4950 /**
4951 *
4952 */
4953 std::vector<String> directories;
4955 /**
4956 * A list of all files which will be processed for
4957 * dependencies. This is the only list that has the actual
4958 * records. All other lists have pointers to these records.
4959 */
4960 std::map<String, FileRec *> allFiles;
4962 /**
4963 * The list of .o files, and the
4964 * dependencies upon them.
4965 */
4966 std::map<String, FileRec *> depFiles;
4968 int depFileSize;
4969 char *depFileBuf;
4972 };
4978 /**
4979 * Clean up after processing. Called by the destructor, but should
4980 * also be called before the object is reused.
4981 */
4982 void DepTool::init()
4983 {
4984 sourceDir = ".";
4986 fileList.clear();
4987 directories.clear();
4989 //clear refs
4990 depFiles.clear();
4991 //clear records
4992 std::map<String, FileRec *>::iterator iter;
4993 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4994 delete iter->second;
4996 allFiles.clear();
4998 }
5003 /**
5004 * Parse a full path name into path, base name, and suffix
5005 */
5006 void DepTool::parseName(const String &fullname,
5007 String &path,
5008 String &basename,
5009 String &suffix)
5010 {
5011 if (fullname.size() < 2)
5012 return;
5014 unsigned int pos = fullname.find_last_of('/');
5015 if (pos != fullname.npos && pos<fullname.size()-1)
5016 {
5017 path = fullname.substr(0, pos);
5018 pos++;
5019 basename = fullname.substr(pos, fullname.size()-pos);
5020 }
5021 else
5022 {
5023 path = "";
5024 basename = fullname;
5025 }
5027 pos = basename.find_last_of('.');
5028 if (pos != basename.npos && pos<basename.size()-1)
5029 {
5030 suffix = basename.substr(pos+1, basename.size()-pos-1);
5031 basename = basename.substr(0, pos);
5032 }
5034 //trace("parsename:%s %s %s", path.c_str(),
5035 // basename.c_str(), suffix.c_str());
5036 }
5040 /**
5041 * Generate our internal file list.
5042 */
5043 bool DepTool::createFileList()
5044 {
5046 for (unsigned int i=0 ; i<fileList.size() ; i++)
5047 {
5048 String fileName = fileList[i];
5049 //trace("## FileName:%s", fileName.c_str());
5050 String path;
5051 String basename;
5052 String sfx;
5053 parseName(fileName, path, basename, sfx);
5054 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5055 sfx == "cc" || sfx == "CC")
5056 {
5057 FileRec *fe = new FileRec(FileRec::CFILE);
5058 fe->path = path;
5059 fe->baseName = basename;
5060 fe->suffix = sfx;
5061 allFiles[fileName] = fe;
5062 }
5063 else if (sfx == "h" || sfx == "hh" ||
5064 sfx == "hpp" || sfx == "hxx")
5065 {
5066 FileRec *fe = new FileRec(FileRec::HFILE);
5067 fe->path = path;
5068 fe->baseName = basename;
5069 fe->suffix = sfx;
5070 allFiles[fileName] = fe;
5071 }
5072 }
5074 if (!listDirectories(sourceDir, "", directories))
5075 return false;
5077 return true;
5078 }
5084 /**
5085 * Get a character from the buffer at pos. If out of range,
5086 * return -1 for safety
5087 */
5088 int DepTool::get(int pos)
5089 {
5090 if (pos>depFileSize)
5091 return -1;
5092 return depFileBuf[pos];
5093 }
5097 /**
5098 * Skip over all whitespace characters beginning at pos. Return
5099 * the position of the first non-whitespace character.
5100 */
5101 int DepTool::skipwhite(int pos)
5102 {
5103 while (pos < depFileSize)
5104 {
5105 int ch = get(pos);
5106 if (ch < 0)
5107 break;
5108 if (!isspace(ch))
5109 break;
5110 pos++;
5111 }
5112 return pos;
5113 }
5116 /**
5117 * Parse the buffer beginning at pos, for a word. Fill
5118 * 'ret' with the result. Return the position after the
5119 * word.
5120 */
5121 int DepTool::getword(int pos, String &ret)
5122 {
5123 while (pos < depFileSize)
5124 {
5125 int ch = get(pos);
5126 if (ch < 0)
5127 break;
5128 if (isspace(ch))
5129 break;
5130 ret.push_back((char)ch);
5131 pos++;
5132 }
5133 return pos;
5134 }
5136 /**
5137 * Return whether the sequence of characters in the buffer
5138 * beginning at pos match the key, for the length of the key
5139 */
5140 bool DepTool::sequ(int pos, char *key)
5141 {
5142 while (*key)
5143 {
5144 if (*key != get(pos))
5145 return false;
5146 key++; pos++;
5147 }
5148 return true;
5149 }
5153 /**
5154 * Add an include file name to a file record. If the name
5155 * is not found in allFiles explicitly, try prepending include
5156 * directory names to it and try again.
5157 */
5158 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5159 {
5161 std::map<String, FileRec *>::iterator iter =
5162 allFiles.find(iname);
5163 if (iter != allFiles.end()) //already exists
5164 {
5165 //h file in same dir
5166 FileRec *other = iter->second;
5167 //trace("local: '%s'", iname.c_str());
5168 frec->files[iname] = other;
5169 return true;
5170 }
5171 else
5172 {
5173 //look in other dirs
5174 std::vector<String>::iterator diter;
5175 for (diter=directories.begin() ;
5176 diter!=directories.end() ; diter++)
5177 {
5178 String dfname = *diter;
5179 dfname.append("/");
5180 dfname.append(iname);
5181 iter = allFiles.find(dfname);
5182 if (iter != allFiles.end())
5183 {
5184 FileRec *other = iter->second;
5185 //trace("other: '%s'", iname.c_str());
5186 frec->files[dfname] = other;
5187 return true;
5188 }
5189 }
5190 }
5191 return true;
5192 }
5196 /**
5197 * Lightly parse a file to find the #include directives. Do
5198 * a bit of state machine stuff to make sure that the directive
5199 * is valid. (Like not in a comment).
5200 */
5201 bool DepTool::scanFile(const String &fname, FileRec *frec)
5202 {
5203 String fileName;
5204 if (sourceDir.size() > 0)
5205 {
5206 fileName.append(sourceDir);
5207 fileName.append("/");
5208 }
5209 fileName.append(fname);
5210 String nativeName = getNativePath(fileName);
5211 FILE *f = fopen(nativeName.c_str(), "r");
5212 if (!f)
5213 {
5214 error("Could not open '%s' for reading", fname.c_str());
5215 return false;
5216 }
5217 String buf;
5218 while (true)
5219 {
5220 int ch = fgetc(f);
5221 if (ch < 0)
5222 break;
5223 buf.push_back((char)ch);
5224 }
5225 fclose(f);
5227 depFileSize = buf.size();
5228 depFileBuf = (char *)buf.c_str();
5229 int pos = 0;
5232 while (pos < depFileSize)
5233 {
5234 //trace("p:%c", get(pos));
5236 //# Block comment
5237 if (get(pos) == '/' && get(pos+1) == '*')
5238 {
5239 pos += 2;
5240 while (pos < depFileSize)
5241 {
5242 if (get(pos) == '*' && get(pos+1) == '/')
5243 {
5244 pos += 2;
5245 break;
5246 }
5247 else
5248 pos++;
5249 }
5250 }
5251 //# Line comment
5252 else if (get(pos) == '/' && get(pos+1) == '/')
5253 {
5254 pos += 2;
5255 while (pos < depFileSize)
5256 {
5257 if (get(pos) == '\n')
5258 {
5259 pos++;
5260 break;
5261 }
5262 else
5263 pos++;
5264 }
5265 }
5266 //# #include! yaay
5267 else if (sequ(pos, "#include"))
5268 {
5269 pos += 8;
5270 pos = skipwhite(pos);
5271 String iname;
5272 pos = getword(pos, iname);
5273 if (iname.size()>2)
5274 {
5275 iname = iname.substr(1, iname.size()-2);
5276 addIncludeFile(frec, iname);
5277 }
5278 }
5279 else
5280 {
5281 pos++;
5282 }
5283 }
5285 return true;
5286 }
5290 /**
5291 * Recursively check include lists to find all files in allFiles to which
5292 * a given file is dependent.
5293 */
5294 bool DepTool::processDependency(FileRec *ofile,
5295 FileRec *include,
5296 int depth)
5297 {
5298 std::map<String, FileRec *>::iterator iter;
5299 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5300 {
5301 String fname = iter->first;
5302 if (ofile->files.find(fname) != ofile->files.end())
5303 {
5304 //trace("file '%s' already seen", fname.c_str());
5305 continue;
5306 }
5307 FileRec *child = iter->second;
5308 ofile->files[fname] = child;
5310 processDependency(ofile, child, depth+1);
5311 }
5314 return true;
5315 }
5321 /**
5322 * Generate the file dependency list.
5323 */
5324 bool DepTool::generateDependencies()
5325 {
5326 std::map<String, FileRec *>::iterator iter;
5327 //# First pass. Scan for all includes
5328 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5329 {
5330 FileRec *frec = iter->second;
5331 if (!scanFile(iter->first, frec))
5332 {
5333 //quit?
5334 }
5335 }
5337 //# Second pass. Scan for all includes
5338 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5339 {
5340 FileRec *include = iter->second;
5341 if (include->type == FileRec::CFILE)
5342 {
5343 String cFileName = iter->first;
5344 FileRec *ofile = new FileRec(FileRec::OFILE);
5345 ofile->path = include->path;
5346 ofile->baseName = include->baseName;
5347 ofile->suffix = include->suffix;
5348 String fname = include->path;
5349 if (fname.size()>0)
5350 fname.append("/");
5351 fname.append(include->baseName);
5352 fname.append(".o");
5353 depFiles[fname] = ofile;
5354 //add the .c file first? no, don't
5355 //ofile->files[cFileName] = include;
5357 //trace("ofile:%s", fname.c_str());
5359 processDependency(ofile, include, 0);
5360 }
5361 }
5364 return true;
5365 }
5369 /**
5370 * High-level call to generate deps and optionally save them
5371 */
5372 bool DepTool::generateDependencies(const String &fileName)
5373 {
5374 if (!createFileList())
5375 return false;
5376 if (!generateDependencies())
5377 return false;
5378 if (!saveDepFile(fileName))
5379 return false;
5380 return true;
5381 }
5384 /**
5385 * This saves the dependency cache.
5386 */
5387 bool DepTool::saveDepFile(const String &fileName)
5388 {
5389 time_t tim;
5390 time(&tim);
5392 FILE *f = fopen(fileName.c_str(), "w");
5393 if (!f)
5394 {
5395 trace("cannot open '%s' for writing", fileName.c_str());
5396 }
5397 fprintf(f, "<?xml version='1.0'?>\n");
5398 fprintf(f, "<!--\n");
5399 fprintf(f, "########################################################\n");
5400 fprintf(f, "## File: build.dep\n");
5401 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5402 fprintf(f, "########################################################\n");
5403 fprintf(f, "-->\n");
5405 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5406 std::map<String, FileRec *>::iterator iter;
5407 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5408 {
5409 FileRec *frec = iter->second;
5410 if (frec->type == FileRec::OFILE)
5411 {
5412 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5413 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5414 std::map<String, FileRec *>::iterator citer;
5415 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5416 {
5417 String cfname = citer->first;
5418 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5419 }
5420 fprintf(f, "</object>\n\n");
5421 }
5422 }
5424 fprintf(f, "</dependencies>\n");
5425 fprintf(f, "\n");
5426 fprintf(f, "<!--\n");
5427 fprintf(f, "########################################################\n");
5428 fprintf(f, "## E N D\n");
5429 fprintf(f, "########################################################\n");
5430 fprintf(f, "-->\n");
5432 fclose(f);
5434 return true;
5435 }
5440 /**
5441 * This loads the dependency cache.
5442 */
5443 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5444 {
5445 std::vector<DepRec> result;
5447 Parser parser;
5448 Element *root = parser.parseFile(depFile.c_str());
5449 if (!root)
5450 {
5451 error("Could not open %s for reading", depFile.c_str());
5452 return result;
5453 }
5455 if (root->getChildren().size()==0 ||
5456 root->getChildren()[0]->getName()!="dependencies")
5457 {
5458 error("Main xml element should be <dependencies>");
5459 delete root;
5460 return result;
5461 }
5463 //########## Start parsing
5464 Element *depList = root->getChildren()[0];
5466 std::vector<Element *> objects = depList->getChildren();
5467 for (unsigned int i=0 ; i<objects.size() ; i++)
5468 {
5469 Element *objectElem = objects[i];
5470 String tagName = objectElem->getName();
5471 if (tagName == "object")
5472 {
5473 String objName = objectElem->getAttribute("name");
5474 //trace("object:%s", objName.c_str());
5475 DepRec depObject(objName);
5476 depObject.path = objectElem->getAttribute("path");
5477 depObject.suffix = objectElem->getAttribute("suffix");
5478 //########## DESCRIPTION
5479 std::vector<Element *> depElems = objectElem->getChildren();
5480 for (unsigned int i=0 ; i<depElems.size() ; i++)
5481 {
5482 Element *depElem = depElems[i];
5483 tagName = depElem->getName();
5484 if (tagName == "dep")
5485 {
5486 String depName = depElem->getAttribute("name");
5487 //trace(" dep:%s", depName.c_str());
5488 depObject.files.push_back(depName);
5489 }
5490 }
5491 //Insert into the result list, in a sorted manner
5492 bool inserted = false;
5493 std::vector<DepRec>::iterator iter;
5494 for (iter = result.begin() ; iter != result.end() ; iter++)
5495 {
5496 String vpath = iter->path;
5497 vpath.append("/");
5498 vpath.append(iter->name);
5499 String opath = depObject.path;
5500 opath.append("/");
5501 opath.append(depObject.name);
5502 if (vpath > opath)
5503 {
5504 inserted = true;
5505 iter = result.insert(iter, depObject);
5506 break;
5507 }
5508 }
5509 if (!inserted)
5510 result.push_back(depObject);
5511 }
5512 }
5514 delete root;
5516 return result;
5517 }
5520 /**
5521 * This loads the dependency cache.
5522 */
5523 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
5524 {
5525 std::vector<DepRec> result = loadDepFile(depFile);
5526 if (result.size() == 0)
5527 {
5528 generateDependencies(depFile);
5529 result = loadDepFile(depFile);
5530 }
5531 return result;
5532 }
5537 //########################################################################
5538 //# T A S K
5539 //########################################################################
5540 //forward decl
5541 class Target;
5542 class Make;
5544 /**
5545 *
5546 */
5547 class Task : public MakeBase
5548 {
5550 public:
5552 typedef enum
5553 {
5554 TASK_NONE,
5555 TASK_AR,
5556 TASK_CC,
5557 TASK_COPY,
5558 TASK_DELETE,
5559 TASK_JAR,
5560 TASK_JAVAC,
5561 TASK_LINK,
5562 TASK_MAKEFILE,
5563 TASK_MKDIR,
5564 TASK_MSGFMT,
5565 TASK_RANLIB,
5566 TASK_RC,
5567 TASK_STRIP,
5568 TASK_TSTAMP
5569 } TaskType;
5572 /**
5573 *
5574 */
5575 Task(MakeBase &par) : parent(par)
5576 { init(); }
5578 /**
5579 *
5580 */
5581 Task(const Task &other) : parent(other.parent)
5582 { init(); assign(other); }
5584 /**
5585 *
5586 */
5587 Task &operator=(const Task &other)
5588 { assign(other); return *this; }
5590 /**
5591 *
5592 */
5593 virtual ~Task()
5594 { }
5597 /**
5598 *
5599 */
5600 virtual MakeBase &getParent()
5601 { return parent; }
5603 /**
5604 *
5605 */
5606 virtual int getType()
5607 { return type; }
5609 /**
5610 *
5611 */
5612 virtual void setType(int val)
5613 { type = val; }
5615 /**
5616 *
5617 */
5618 virtual String getName()
5619 { return name; }
5621 /**
5622 *
5623 */
5624 virtual bool execute()
5625 { return true; }
5627 /**
5628 *
5629 */
5630 virtual bool parse(Element *elem)
5631 { return true; }
5633 /**
5634 *
5635 */
5636 Task *createTask(Element *elem);
5639 protected:
5641 void init()
5642 {
5643 type = TASK_NONE;
5644 name = "none";
5645 }
5647 void assign(const Task &other)
5648 {
5649 type = other.type;
5650 name = other.name;
5651 }
5653 String getAttribute(Element *elem, const String &attrName)
5654 {
5655 String str;
5656 return str;
5657 }
5659 MakeBase &parent;
5661 int type;
5663 String name;
5664 };
5669 /**
5670 * Run the "ar" command to archive .o's into a .a
5671 */
5672 class TaskAr : public Task
5673 {
5674 public:
5676 TaskAr(MakeBase &par) : Task(par)
5677 {
5678 type = TASK_AR; name = "ar";
5679 command = "ar crv";
5680 }
5682 virtual ~TaskAr()
5683 {}
5685 virtual bool execute()
5686 {
5687 //trace("###########HERE %d", fileSet.size());
5688 bool doit = false;
5690 String fullOut = parent.resolve(fileName);
5691 //trace("ar fullout: %s", fullOut.c_str());
5693 if (!listFiles(parent, fileSet))
5694 return false;
5695 String fileSetDir = fileSet.getDirectory();
5697 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5698 {
5699 String fname;
5700 if (fileSetDir.size()>0)
5701 {
5702 fname.append(fileSetDir);
5703 fname.append("/");
5704 }
5705 fname.append(fileSet[i]);
5706 String fullName = parent.resolve(fname);
5707 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
5708 if (isNewerThan(fullName, fullOut))
5709 doit = true;
5710 }
5711 //trace("Needs it:%d", doit);
5712 if (!doit)
5713 {
5714 return true;
5715 }
5717 String cmd = command;
5718 cmd.append(" ");
5719 cmd.append(fullOut);
5720 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5721 {
5722 String fname;
5723 if (fileSetDir.size()>0)
5724 {
5725 fname.append(fileSetDir);
5726 fname.append("/");
5727 }
5728 fname.append(fileSet[i]);
5729 String fullName = parent.resolve(fname);
5731 cmd.append(" ");
5732 cmd.append(fullName);
5733 }
5735 String outString, errString;
5736 if (!executeCommand(cmd.c_str(), "", outString, errString))
5737 {
5738 error("AR problem: %s", errString.c_str());
5739 return false;
5740 }
5742 return true;
5743 }
5745 virtual bool parse(Element *elem)
5746 {
5747 if (!parent.getAttribute(elem, "file", fileName))
5748 return false;
5750 std::vector<Element *> children = elem->getChildren();
5751 for (unsigned int i=0 ; i<children.size() ; i++)
5752 {
5753 Element *child = children[i];
5754 String tagName = child->getName();
5755 if (tagName == "fileset")
5756 {
5757 if (!parseFileSet(child, parent, fileSet))
5758 return false;
5759 }
5760 }
5761 return true;
5762 }
5764 private:
5766 String command;
5767 String fileName;
5768 FileSet fileSet;
5770 };
5773 /**
5774 * This task runs the C/C++ compiler. The compiler is invoked
5775 * for all .c or .cpp files which are newer than their correcsponding
5776 * .o files.
5777 */
5778 class TaskCC : public Task
5779 {
5780 public:
5782 TaskCC(MakeBase &par) : Task(par)
5783 {
5784 type = TASK_CC; name = "cc";
5785 ccCommand = "gcc";
5786 cxxCommand = "g++";
5787 source = ".";
5788 dest = ".";
5789 flags = "";
5790 defines = "";
5791 includes = "";
5792 fileSet.clear();
5793 }
5795 virtual ~TaskCC()
5796 {}
5798 virtual bool execute()
5799 {
5800 if (!listFiles(parent, fileSet))
5801 return false;
5803 DepTool depTool;
5804 depTool.setSourceDirectory(source);
5805 depTool.setFileList(fileSet.getFiles());
5806 std::vector<DepRec> deps = depTool.getDepFile("build.dep");
5808 String incs;
5809 incs.append("-I");
5810 incs.append(parent.resolve("."));
5811 incs.append(" ");
5812 if (includes.size()>0)
5813 {
5814 incs.append(includes);
5815 incs.append(" ");
5816 }
5817 std::set<String> paths;
5818 std::vector<DepRec>::iterator viter;
5819 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5820 {
5821 DepRec dep = *viter;
5822 if (dep.path.size()>0)
5823 paths.insert(dep.path);
5824 }
5825 if (source.size()>0)
5826 {
5827 incs.append(" -I");
5828 incs.append(parent.resolve(source));
5829 incs.append(" ");
5830 }
5831 std::set<String>::iterator setIter;
5832 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5833 {
5834 incs.append(" -I");
5835 String dname;
5836 if (source.size()>0)
5837 {
5838 dname.append(source);
5839 dname.append("/");
5840 }
5841 dname.append(*setIter);
5842 incs.append(parent.resolve(dname));
5843 }
5844 std::vector<String> cfiles;
5845 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5846 {
5847 DepRec dep = *viter;
5849 //## Select command
5850 String sfx = dep.suffix;
5851 String command = ccCommand;
5852 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5853 || sfx == "CC")
5854 command = cxxCommand;
5856 //## Make paths
5857 String destPath = dest;
5858 String srcPath = source;
5859 if (dep.path.size()>0)
5860 {
5861 destPath.append("/");
5862 destPath.append(dep.path);
5863 srcPath.append("/");
5864 srcPath.append(dep.path);
5865 }
5866 //## Make sure destination directory exists
5867 if (!createDirectory(destPath))
5868 return false;
5870 //## Check whether it needs to be done
5871 String destFullName = destPath;
5872 destFullName.append("/");
5873 destFullName.append(dep.name);
5874 destFullName.append(".o");
5875 String srcFullName = srcPath;
5876 srcFullName.append("/");
5877 srcFullName.append(dep.name);
5878 srcFullName.append(".");
5879 srcFullName.append(dep.suffix);
5880 if (!isNewerThan(srcFullName, destFullName))
5881 {
5882 //trace("%s skipped", srcFullName.c_str());
5883 continue;
5884 }
5886 //## Assemble the command
5887 String cmd = command;
5888 cmd.append(" -c ");
5889 cmd.append(flags);
5890 cmd.append(" ");
5891 cmd.append(defines);
5892 cmd.append(" ");
5893 cmd.append(incs);
5894 cmd.append(" ");
5895 cmd.append(srcFullName);
5896 cmd.append(" -o ");
5897 cmd.append(destFullName);
5899 //## Execute the command
5901 String outString, errString;
5902 if (!executeCommand(cmd.c_str(), "", outString, errString))
5903 {
5904 error("problem compiling: %s", errString.c_str());
5905 return false;
5906 }
5907 }
5909 return true;
5910 }
5912 virtual bool parse(Element *elem)
5913 {
5914 String s;
5915 if (!parent.getAttribute(elem, "command", s))
5916 return false;
5917 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5918 if (!parent.getAttribute(elem, "cc", s))
5919 return false;
5920 if (s.size()>0) ccCommand = s;
5921 if (!parent.getAttribute(elem, "cxx", s))
5922 return false;
5923 if (s.size()>0) cxxCommand = s;
5924 if (!parent.getAttribute(elem, "destdir", s))
5925 return false;
5926 if (s.size()>0) dest = s;
5928 std::vector<Element *> children = elem->getChildren();
5929 for (unsigned int i=0 ; i<children.size() ; i++)
5930 {
5931 Element *child = children[i];
5932 String tagName = child->getName();
5933 if (tagName == "flags")
5934 {
5935 if (!parent.getValue(child, flags))
5936 return false;
5937 flags = strip(flags);
5938 }
5939 else if (tagName == "includes")
5940 {
5941 if (!parent.getValue(child, includes))
5942 return false;
5943 includes = strip(includes);
5944 }
5945 else if (tagName == "defines")
5946 {
5947 if (!parent.getValue(child, defines))
5948 return false;
5949 defines = strip(defines);
5950 }
5951 else if (tagName == "fileset")
5952 {
5953 if (!parseFileSet(child, parent, fileSet))
5954 return false;
5955 source = fileSet.getDirectory();
5956 }
5957 }
5959 return true;
5960 }
5962 protected:
5964 String ccCommand;
5965 String cxxCommand;
5966 String source;
5967 String dest;
5968 String flags;
5969 String defines;
5970 String includes;
5971 FileSet fileSet;
5973 };
5977 /**
5978 *
5979 */
5980 class TaskCopy : public Task
5981 {
5982 public:
5984 typedef enum
5985 {
5986 CP_NONE,
5987 CP_TOFILE,
5988 CP_TODIR
5989 } CopyType;
5991 TaskCopy(MakeBase &par) : Task(par)
5992 {
5993 type = TASK_COPY; name = "copy";
5994 cptype = CP_NONE;
5995 verbose = false;
5996 haveFileSet = false;
5997 }
5999 virtual ~TaskCopy()
6000 {}
6002 virtual bool execute()
6003 {
6004 switch (cptype)
6005 {
6006 case CP_TOFILE:
6007 {
6008 if (fileName.size()>0)
6009 {
6010 status(" : %s", fileName.c_str());
6011 String fullSource = parent.resolve(fileName);
6012 String fullDest = parent.resolve(toFileName);
6013 //trace("copy %s to file %s", fullSource.c_str(),
6014 // fullDest.c_str());
6015 if (!isRegularFile(fullSource))
6016 {
6017 error("copy : file %s does not exist", fullSource.c_str());
6018 return false;
6019 }
6020 if (!isNewerThan(fullSource, fullDest))
6021 {
6022 return true;
6023 }
6024 if (!copyFile(fullSource, fullDest))
6025 return false;
6026 status(" : 1 file copied");
6027 }
6028 return true;
6029 }
6030 case CP_TODIR:
6031 {
6032 if (haveFileSet)
6033 {
6034 if (!listFiles(parent, fileSet))
6035 return false;
6036 String fileSetDir = fileSet.getDirectory();
6038 int nrFiles = 0;
6039 status(" : %s", fileSetDir.c_str());
6040 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6041 {
6042 String fileName = fileSet[i];
6044 String sourcePath;
6045 if (fileSetDir.size()>0)
6046 {
6047 sourcePath.append(fileSetDir);
6048 sourcePath.append("/");
6049 }
6050 sourcePath.append(fileName);
6051 String fullSource = parent.resolve(sourcePath);
6053 //Get the immediate parent directory's base name
6054 String baseFileSetDir = fileSetDir;
6055 unsigned int pos = baseFileSetDir.find_last_of('/');
6056 if (pos!=baseFileSetDir.npos &&
6057 pos < baseFileSetDir.size()-1)
6058 baseFileSetDir =
6059 baseFileSetDir.substr(pos+1,
6060 baseFileSetDir.size());
6061 //Now make the new path
6062 String destPath;
6063 if (toDirName.size()>0)
6064 {
6065 destPath.append(toDirName);
6066 destPath.append("/");
6067 }
6068 if (baseFileSetDir.size()>0)
6069 {
6070 destPath.append(baseFileSetDir);
6071 destPath.append("/");
6072 }
6073 destPath.append(fileName);
6074 String fullDest = parent.resolve(destPath);
6075 //trace("fileName:%s", fileName.c_str());
6076 //trace("copy %s to new dir : %s", fullSource.c_str(),
6077 // fullDest.c_str());
6078 if (!isNewerThan(fullSource, fullDest))
6079 {
6080 //trace("copy skipping %s", fullSource.c_str());
6081 continue;
6082 }
6083 if (!copyFile(fullSource, fullDest))
6084 return false;
6085 nrFiles++;
6086 }
6087 status(" : %d file(s) copied", nrFiles);
6088 }
6089 else //file source
6090 {
6091 //For file->dir we want only the basename of
6092 //the source appended to the dest dir
6093 status(" : %s", fileName.c_str());
6094 String baseName = fileName;
6095 unsigned int pos = baseName.find_last_of('/');
6096 if (pos!=baseName.npos && pos<baseName.size()-1)
6097 baseName = baseName.substr(pos+1, baseName.size());
6098 String fullSource = parent.resolve(fileName);
6099 String destPath;
6100 if (toDirName.size()>0)
6101 {
6102 destPath.append(toDirName);
6103 destPath.append("/");
6104 }
6105 destPath.append(baseName);
6106 String fullDest = parent.resolve(destPath);
6107 //trace("copy %s to new dir : %s", fullSource.c_str(),
6108 // fullDest.c_str());
6109 if (!isRegularFile(fullSource))
6110 {
6111 error("copy : file %s does not exist", fullSource.c_str());
6112 return false;
6113 }
6114 if (!isNewerThan(fullSource, fullDest))
6115 {
6116 return true;
6117 }
6118 if (!copyFile(fullSource, fullDest))
6119 return false;
6120 status(" : 1 file copied");
6121 }
6122 return true;
6123 }
6124 }
6125 return true;
6126 }
6129 virtual bool parse(Element *elem)
6130 {
6131 if (!parent.getAttribute(elem, "file", fileName))
6132 return false;
6133 if (!parent.getAttribute(elem, "tofile", toFileName))
6134 return false;
6135 if (toFileName.size() > 0)
6136 cptype = CP_TOFILE;
6137 if (!parent.getAttribute(elem, "todir", toDirName))
6138 return false;
6139 if (toDirName.size() > 0)
6140 cptype = CP_TODIR;
6141 String ret;
6142 if (!parent.getAttribute(elem, "verbose", ret))
6143 return false;
6144 if (ret.size()>0 && !getBool(ret, verbose))
6145 return false;
6147 haveFileSet = false;
6149 std::vector<Element *> children = elem->getChildren();
6150 for (unsigned int i=0 ; i<children.size() ; i++)
6151 {
6152 Element *child = children[i];
6153 String tagName = child->getName();
6154 if (tagName == "fileset")
6155 {
6156 if (!parseFileSet(child, parent, fileSet))
6157 {
6158 error("problem getting fileset");
6159 return false;
6160 }
6161 haveFileSet = true;
6162 }
6163 }
6165 //Perform validity checks
6166 if (fileName.size()>0 && fileSet.size()>0)
6167 {
6168 error("<copy> can only have one of : file= and <fileset>");
6169 return false;
6170 }
6171 if (toFileName.size()>0 && toDirName.size()>0)
6172 {
6173 error("<copy> can only have one of : tofile= or todir=");
6174 return false;
6175 }
6176 if (haveFileSet && toDirName.size()==0)
6177 {
6178 error("a <copy> task with a <fileset> must have : todir=");
6179 return false;
6180 }
6181 if (cptype == CP_TOFILE && fileName.size()==0)
6182 {
6183 error("<copy> tofile= must be associated with : file=");
6184 return false;
6185 }
6186 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6187 {
6188 error("<copy> todir= must be associated with : file= or <fileset>");
6189 return false;
6190 }
6192 return true;
6193 }
6195 private:
6197 int cptype;
6198 String fileName;
6199 FileSet fileSet;
6200 String toFileName;
6201 String toDirName;
6202 bool verbose;
6203 bool haveFileSet;
6204 };
6207 /**
6208 *
6209 */
6210 class TaskDelete : public Task
6211 {
6212 public:
6214 typedef enum
6215 {
6216 DEL_FILE,
6217 DEL_DIR,
6218 DEL_FILESET
6219 } DeleteType;
6221 TaskDelete(MakeBase &par) : Task(par)
6222 {
6223 type = TASK_DELETE;
6224 name = "delete";
6225 delType = DEL_FILE;
6226 verbose = false;
6227 quiet = false;
6228 failOnError = true;
6229 }
6231 virtual ~TaskDelete()
6232 {}
6234 virtual bool execute()
6235 {
6236 struct stat finfo;
6237 switch (delType)
6238 {
6239 case DEL_FILE:
6240 {
6241 status(" : %s", fileName.c_str());
6242 String fullName = parent.resolve(fileName);
6243 char *fname = (char *)fullName.c_str();
6244 //does not exist
6245 if (stat(fname, &finfo)<0)
6246 return true;
6247 //exists but is not a regular file
6248 if (!S_ISREG(finfo.st_mode))
6249 {
6250 error("<delete> failed. '%s' exists and is not a regular file",
6251 fname);
6252 return false;
6253 }
6254 if (remove(fname)<0)
6255 {
6256 error("<delete> failed: %s", strerror(errno));
6257 return false;
6258 }
6259 return true;
6260 }
6261 case DEL_DIR:
6262 {
6263 status(" : %s", dirName.c_str());
6264 String fullDir = parent.resolve(dirName);
6265 if (!removeDirectory(fullDir))
6266 return false;
6267 return true;
6268 }
6269 }
6270 return true;
6271 }
6273 virtual bool parse(Element *elem)
6274 {
6275 if (!parent.getAttribute(elem, "file", fileName))
6276 return false;
6277 if (fileName.size() > 0)
6278 delType = DEL_FILE;
6279 if (!parent.getAttribute(elem, "dir", dirName))
6280 return false;
6281 if (dirName.size() > 0)
6282 delType = DEL_DIR;
6283 if (fileName.size()>0 && dirName.size()>0)
6284 {
6285 error("<delete> can only have one attribute of file= or dir=");
6286 return false;
6287 }
6288 String ret;
6289 if (!parent.getAttribute(elem, "verbose", ret))
6290 return false;
6291 if (ret.size()>0 && !getBool(ret, verbose))
6292 return false;
6293 if (!parent.getAttribute(elem, "quiet", ret))
6294 return false;
6295 if (ret.size()>0 && !getBool(ret, quiet))
6296 return false;
6297 if (!parent.getAttribute(elem, "failonerror", ret))
6298 return false;
6299 if (ret.size()>0 && !getBool(ret, failOnError))
6300 return false;
6301 return true;
6302 }
6304 private:
6306 int delType;
6307 String dirName;
6308 String fileName;
6309 bool verbose;
6310 bool quiet;
6311 bool failOnError;
6312 };
6315 /**
6316 *
6317 */
6318 class TaskJar : public Task
6319 {
6320 public:
6322 TaskJar(MakeBase &par) : Task(par)
6323 { type = TASK_JAR; name = "jar"; }
6325 virtual ~TaskJar()
6326 {}
6328 virtual bool execute()
6329 {
6330 return true;
6331 }
6333 virtual bool parse(Element *elem)
6334 {
6335 return true;
6336 }
6337 };
6340 /**
6341 *
6342 */
6343 class TaskJavac : public Task
6344 {
6345 public:
6347 TaskJavac(MakeBase &par) : Task(par)
6348 { type = TASK_JAVAC; name = "javac"; }
6350 virtual ~TaskJavac()
6351 {}
6353 virtual bool execute()
6354 {
6355 return true;
6356 }
6358 virtual bool parse(Element *elem)
6359 {
6360 return true;
6361 }
6362 };
6365 /**
6366 *
6367 */
6368 class TaskLink : public Task
6369 {
6370 public:
6372 TaskLink(MakeBase &par) : Task(par)
6373 {
6374 type = TASK_LINK; name = "link";
6375 command = "g++";
6376 }
6378 virtual ~TaskLink()
6379 {}
6381 virtual bool execute()
6382 {
6383 if (!listFiles(parent, fileSet))
6384 return false;
6385 String fileSetDir = fileSet.getDirectory();
6386 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6387 bool doit = false;
6388 String fullTarget = parent.resolve(fileName);
6389 String cmd = command;
6390 cmd.append(" -o ");
6391 cmd.append(fullTarget);
6392 cmd.append(" ");
6393 cmd.append(flags);
6394 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6395 {
6396 cmd.append(" ");
6397 String obj;
6398 if (fileSetDir.size()>0)
6399 {
6400 obj.append(fileSetDir);
6401 obj.append("/");
6402 }
6403 obj.append(fileSet[i]);
6404 String fullObj = parent.resolve(obj);
6405 cmd.append(fullObj);
6406 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6407 // fullObj.c_str());
6408 if (isNewerThan(fullObj, fullTarget))
6409 doit = true;
6410 }
6411 cmd.append(" ");
6412 cmd.append(libs);
6413 if (!doit)
6414 {
6415 //trace("link not needed");
6416 return true;
6417 }
6418 //trace("LINK cmd:%s", cmd.c_str());
6421 String outString, errString;
6422 if (!executeCommand(cmd.c_str(), "", outString, errString))
6423 {
6424 error("LINK problem: %s", errString.c_str());
6425 return false;
6426 }
6427 return true;
6428 }
6430 virtual bool parse(Element *elem)
6431 {
6432 if (!parent.getAttribute(elem, "command", command))
6433 return false;
6434 if (!parent.getAttribute(elem, "out", fileName))
6435 return false;
6437 std::vector<Element *> children = elem->getChildren();
6438 for (unsigned int i=0 ; i<children.size() ; i++)
6439 {
6440 Element *child = children[i];
6441 String tagName = child->getName();
6442 if (tagName == "fileset")
6443 {
6444 if (!parseFileSet(child, parent, fileSet))
6445 return false;
6446 }
6447 else if (tagName == "flags")
6448 {
6449 if (!parent.getValue(child, flags))
6450 return false;
6451 flags = strip(flags);
6452 }
6453 else if (tagName == "libs")
6454 {
6455 if (!parent.getValue(child, libs))
6456 return false;
6457 libs = strip(libs);
6458 }
6459 }
6460 return true;
6461 }
6463 private:
6465 String command;
6466 String fileName;
6467 String flags;
6468 String libs;
6469 FileSet fileSet;
6471 };
6475 /**
6476 * Create a named directory
6477 */
6478 class TaskMakeFile : public Task
6479 {
6480 public:
6482 TaskMakeFile(MakeBase &par) : Task(par)
6483 { type = TASK_MAKEFILE; name = "makefile"; }
6485 virtual ~TaskMakeFile()
6486 {}
6488 virtual bool execute()
6489 {
6490 status(" : %s", fileName.c_str());
6491 String fullName = parent.resolve(fileName);
6492 if (!isNewerThan(parent.getURI().getPath(), fullName))
6493 {
6494 //trace("skipped <makefile>");
6495 return true;
6496 }
6497 //trace("fullName:%s", fullName.c_str());
6498 FILE *f = fopen(fullName.c_str(), "w");
6499 if (!f)
6500 {
6501 error("<makefile> could not open %s for writing : %s",
6502 fullName.c_str(), strerror(errno));
6503 return false;
6504 }
6505 for (unsigned int i=0 ; i<text.size() ; i++)
6506 fputc(text[i], f);
6507 fclose(f);
6508 return true;
6509 }
6511 virtual bool parse(Element *elem)
6512 {
6513 if (!parent.getAttribute(elem, "file", fileName))
6514 return false;
6515 if (fileName.size() == 0)
6516 {
6517 error("<makefile> requires 'file=\"filename\"' attribute");
6518 return false;
6519 }
6520 if (!parent.getValue(elem, text))
6521 return false;
6522 text = leftJustify(text);
6523 //trace("dirname:%s", dirName.c_str());
6524 return true;
6525 }
6527 private:
6529 String fileName;
6530 String text;
6531 };
6535 /**
6536 * Create a named directory
6537 */
6538 class TaskMkDir : public Task
6539 {
6540 public:
6542 TaskMkDir(MakeBase &par) : Task(par)
6543 { type = TASK_MKDIR; name = "mkdir"; }
6545 virtual ~TaskMkDir()
6546 {}
6548 virtual bool execute()
6549 {
6550 status(" : %s", dirName.c_str());
6551 String fullDir = parent.resolve(dirName);
6552 //trace("fullDir:%s", fullDir.c_str());
6553 if (!createDirectory(fullDir))
6554 return false;
6555 return true;
6556 }
6558 virtual bool parse(Element *elem)
6559 {
6560 if (!parent.getAttribute(elem, "dir", dirName))
6561 return false;
6562 if (dirName.size() == 0)
6563 {
6564 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6565 return false;
6566 }
6567 trace("dirname:%s", dirName.c_str());
6568 return true;
6569 }
6571 private:
6573 String dirName;
6574 };
6578 /**
6579 * Create a named directory
6580 */
6581 class TaskMsgFmt: public Task
6582 {
6583 public:
6585 TaskMsgFmt(MakeBase &par) : Task(par)
6586 {
6587 type = TASK_MSGFMT;
6588 name = "msgfmt";
6589 command = "msgfmt";
6590 }
6592 virtual ~TaskMsgFmt()
6593 {}
6595 virtual bool execute()
6596 {
6597 if (!listFiles(parent, fileSet))
6598 return false;
6599 String fileSetDir = fileSet.getDirectory();
6601 //trace("msgfmt: %d", fileSet.size());
6602 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6603 {
6604 String fileName = fileSet[i];
6605 if (getSuffix(fileName) != "po")
6606 continue;
6607 String sourcePath;
6608 if (fileSetDir.size()>0)
6609 {
6610 sourcePath.append(fileSetDir);
6611 sourcePath.append("/");
6612 }
6613 sourcePath.append(fileName);
6614 String fullSource = parent.resolve(sourcePath);
6616 String destPath;
6617 if (toDirName.size()>0)
6618 {
6619 destPath.append(toDirName);
6620 destPath.append("/");
6621 }
6622 destPath.append(fileName);
6623 destPath[destPath.size()-2] = 'm';
6624 String fullDest = parent.resolve(destPath);
6626 if (!isNewerThan(fullSource, fullDest))
6627 {
6628 //trace("skip %s", fullSource.c_str());
6629 continue;
6630 }
6632 String cmd = command;
6633 cmd.append(" ");
6634 cmd.append(fullSource);
6635 cmd.append(" -o ");
6636 cmd.append(fullDest);
6638 int pos = fullDest.find_last_of('/');
6639 if (pos>0)
6640 {
6641 String fullDestPath = fullDest.substr(0, pos);
6642 if (!createDirectory(fullDestPath))
6643 return false;
6644 }
6648 String outString, errString;
6649 if (!executeCommand(cmd.c_str(), "", outString, errString))
6650 {
6651 error("<msgfmt> problem: %s", errString.c_str());
6652 return false;
6653 }
6654 }
6656 return true;
6657 }
6659 virtual bool parse(Element *elem)
6660 {
6661 if (!parent.getAttribute(elem, "todir", toDirName))
6662 return false;
6664 std::vector<Element *> children = elem->getChildren();
6665 for (unsigned int i=0 ; i<children.size() ; i++)
6666 {
6667 Element *child = children[i];
6668 String tagName = child->getName();
6669 if (tagName == "fileset")
6670 {
6671 if (!parseFileSet(child, parent, fileSet))
6672 return false;
6673 }
6674 }
6675 return true;
6676 }
6678 private:
6680 String command;
6681 String toDirName;
6682 FileSet fileSet;
6684 };
6690 /**
6691 * Process an archive to allow random access
6692 */
6693 class TaskRanlib : public Task
6694 {
6695 public:
6697 TaskRanlib(MakeBase &par) : Task(par)
6698 { type = TASK_RANLIB; name = "ranlib"; }
6700 virtual ~TaskRanlib()
6701 {}
6703 virtual bool execute()
6704 {
6705 String fullName = parent.resolve(fileName);
6706 //trace("fullDir:%s", fullDir.c_str());
6707 String cmd = "ranlib ";
6708 cmd.append(fullName);
6709 String outbuf, errbuf;
6710 if (!executeCommand(cmd, "", outbuf, errbuf))
6711 return false;
6712 return true;
6713 }
6715 virtual bool parse(Element *elem)
6716 {
6717 if (!parent.getAttribute(elem, "file", fileName))
6718 return false;
6719 if (fileName.size() == 0)
6720 {
6721 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6722 return false;
6723 }
6724 return true;
6725 }
6727 private:
6729 String fileName;
6730 };
6734 /**
6735 * Run the "ar" command to archive .o's into a .a
6736 */
6737 class TaskRC : public Task
6738 {
6739 public:
6741 TaskRC(MakeBase &par) : Task(par)
6742 {
6743 type = TASK_RC; name = "rc";
6744 command = "windres -o";
6745 }
6747 virtual ~TaskRC()
6748 {}
6750 virtual bool execute()
6751 {
6752 String fullFile = parent.resolve(fileName);
6753 String fullOut = parent.resolve(outName);
6754 if (!isNewerThan(fullFile, fullOut))
6755 return true;
6756 String cmd = command;
6757 cmd.append(" ");
6758 cmd.append(fullOut);
6759 cmd.append(" ");
6760 cmd.append(flags);
6761 cmd.append(" ");
6762 cmd.append(fullFile);
6764 String outString, errString;
6765 if (!executeCommand(cmd.c_str(), "", outString, errString))
6766 {
6767 error("RC problem: %s", errString.c_str());
6768 return false;
6769 }
6770 return true;
6771 }
6773 virtual bool parse(Element *elem)
6774 {
6775 if (!parent.getAttribute(elem, "command", command))
6776 return false;
6777 if (!parent.getAttribute(elem, "file", fileName))
6778 return false;
6779 if (!parent.getAttribute(elem, "out", outName))
6780 return false;
6781 std::vector<Element *> children = elem->getChildren();
6782 for (unsigned int i=0 ; i<children.size() ; i++)
6783 {
6784 Element *child = children[i];
6785 String tagName = child->getName();
6786 if (tagName == "flags")
6787 {
6788 if (!parent.getValue(child, flags))
6789 return false;
6790 }
6791 }
6792 return true;
6793 }
6795 private:
6797 String command;
6798 String flags;
6799 String fileName;
6800 String outName;
6802 };
6806 /**
6807 * Strip an executable
6808 */
6809 class TaskStrip : public Task
6810 {
6811 public:
6813 TaskStrip(MakeBase &par) : Task(par)
6814 { type = TASK_STRIP; name = "strip"; }
6816 virtual ~TaskStrip()
6817 {}
6819 virtual bool execute()
6820 {
6821 String fullName = parent.resolve(fileName);
6822 //trace("fullDir:%s", fullDir.c_str());
6823 String cmd = "strip ";
6824 cmd.append(fullName);
6826 String outbuf, errbuf;
6827 if (!executeCommand(cmd, "", outbuf, errbuf))
6828 return false;
6829 return true;
6830 }
6832 virtual bool parse(Element *elem)
6833 {
6834 if (!parent.getAttribute(elem, "file", fileName))
6835 return false;
6836 if (fileName.size() == 0)
6837 {
6838 error("<strip> requires 'file=\"fileNname\"' attribute");
6839 return false;
6840 }
6841 return true;
6842 }
6844 private:
6846 String fileName;
6847 };
6850 /**
6851 *
6852 */
6853 class TaskTstamp : public Task
6854 {
6855 public:
6857 TaskTstamp(MakeBase &par) : Task(par)
6858 { type = TASK_TSTAMP; name = "tstamp"; }
6860 virtual ~TaskTstamp()
6861 {}
6863 virtual bool execute()
6864 {
6865 return true;
6866 }
6868 virtual bool parse(Element *elem)
6869 {
6870 //trace("tstamp parse");
6871 return true;
6872 }
6873 };
6877 /**
6878 *
6879 */
6880 Task *Task::createTask(Element *elem)
6881 {
6882 String tagName = elem->getName();
6883 //trace("task:%s", tagName.c_str());
6884 Task *task = NULL;
6885 if (tagName == "ar")
6886 task = new TaskAr(parent);
6887 else if (tagName == "cc")
6888 task = new TaskCC(parent);
6889 else if (tagName == "copy")
6890 task = new TaskCopy(parent);
6891 else if (tagName == "delete")
6892 task = new TaskDelete(parent);
6893 else if (tagName == "jar")
6894 task = new TaskJar(parent);
6895 else if (tagName == "javac")
6896 task = new TaskJavac(parent);
6897 else if (tagName == "link")
6898 task = new TaskLink(parent);
6899 else if (tagName == "makefile")
6900 task = new TaskMakeFile(parent);
6901 else if (tagName == "mkdir")
6902 task = new TaskMkDir(parent);
6903 else if (tagName == "msgfmt")
6904 task = new TaskMsgFmt(parent);
6905 else if (tagName == "ranlib")
6906 task = new TaskRanlib(parent);
6907 else if (tagName == "rc")
6908 task = new TaskRC(parent);
6909 else if (tagName == "strip")
6910 task = new TaskStrip(parent);
6911 else if (tagName == "tstamp")
6912 task = new TaskTstamp(parent);
6913 else
6914 {
6915 error("Unknown task '%s'", tagName.c_str());
6916 return NULL;
6917 }
6919 if (!task->parse(elem))
6920 {
6921 delete task;
6922 return NULL;
6923 }
6924 return task;
6925 }
6929 //########################################################################
6930 //# T A R G E T
6931 //########################################################################
6933 /**
6934 *
6935 */
6936 class Target : public MakeBase
6937 {
6939 public:
6941 /**
6942 *
6943 */
6944 Target(Make &par) : parent(par)
6945 { init(); }
6947 /**
6948 *
6949 */
6950 Target(const Target &other) : parent(other.parent)
6951 { init(); assign(other); }
6953 /**
6954 *
6955 */
6956 Target &operator=(const Target &other)
6957 { init(); assign(other); return *this; }
6959 /**
6960 *
6961 */
6962 virtual ~Target()
6963 { cleanup() ; }
6966 /**
6967 *
6968 */
6969 virtual Make &getParent()
6970 { return parent; }
6972 /**
6973 *
6974 */
6975 virtual String getName()
6976 { return name; }
6978 /**
6979 *
6980 */
6981 virtual void setName(const String &val)
6982 { name = val; }
6984 /**
6985 *
6986 */
6987 virtual String getDescription()
6988 { return description; }
6990 /**
6991 *
6992 */
6993 virtual void setDescription(const String &val)
6994 { description = val; }
6996 /**
6997 *
6998 */
6999 virtual void addDependency(const String &val)
7000 { deps.push_back(val); }
7002 /**
7003 *
7004 */
7005 virtual void parseDependencies(const String &val)
7006 { deps = tokenize(val, ", "); }
7008 /**
7009 *
7010 */
7011 virtual std::vector<String> &getDependencies()
7012 { return deps; }
7014 /**
7015 *
7016 */
7017 virtual String getIf()
7018 { return ifVar; }
7020 /**
7021 *
7022 */
7023 virtual void setIf(const String &val)
7024 { ifVar = val; }
7026 /**
7027 *
7028 */
7029 virtual String getUnless()
7030 { return unlessVar; }
7032 /**
7033 *
7034 */
7035 virtual void setUnless(const String &val)
7036 { unlessVar = val; }
7038 /**
7039 *
7040 */
7041 virtual void addTask(Task *val)
7042 { tasks.push_back(val); }
7044 /**
7045 *
7046 */
7047 virtual std::vector<Task *> &getTasks()
7048 { return tasks; }
7050 private:
7052 void init()
7053 {
7054 }
7056 void cleanup()
7057 {
7058 tasks.clear();
7059 }
7061 void assign(const Target &other)
7062 {
7063 //parent = other.parent;
7064 name = other.name;
7065 description = other.description;
7066 ifVar = other.ifVar;
7067 unlessVar = other.unlessVar;
7068 deps = other.deps;
7069 tasks = other.tasks;
7070 }
7072 Make &parent;
7074 String name;
7076 String description;
7078 String ifVar;
7080 String unlessVar;
7082 std::vector<String> deps;
7084 std::vector<Task *> tasks;
7086 };
7095 //########################################################################
7096 //# M A K E
7097 //########################################################################
7100 /**
7101 *
7102 */
7103 class Make : public MakeBase
7104 {
7106 public:
7108 /**
7109 *
7110 */
7111 Make()
7112 { init(); }
7114 /**
7115 *
7116 */
7117 Make(const Make &other)
7118 { assign(other); }
7120 /**
7121 *
7122 */
7123 Make &operator=(const Make &other)
7124 { assign(other); return *this; }
7126 /**
7127 *
7128 */
7129 virtual ~Make()
7130 { cleanup(); }
7132 /**
7133 *
7134 */
7135 virtual std::map<String, Target> &getTargets()
7136 { return targets; }
7139 /**
7140 *
7141 */
7142 bool run();
7144 /**
7145 *
7146 */
7147 bool run(const String &target);
7151 private:
7153 /**
7154 *
7155 */
7156 void init();
7158 /**
7159 *
7160 */
7161 void cleanup();
7163 /**
7164 *
7165 */
7166 void assign(const Make &other);
7168 /**
7169 *
7170 */
7171 bool executeTask(Task &task);
7174 /**
7175 *
7176 */
7177 bool executeTarget(Target &target,
7178 std::set<String> &targetsCompleted);
7181 /**
7182 *
7183 */
7184 bool execute();
7186 /**
7187 *
7188 */
7189 bool checkTargetDependencies(Target &prop,
7190 std::vector<String> &depList);
7192 /**
7193 *
7194 */
7195 bool parsePropertyFile(const String &fileName,
7196 const String &prefix);
7198 /**
7199 *
7200 */
7201 bool parseProperty(Element *elem);
7203 /**
7204 *
7205 */
7206 bool parseTask(Task &task, Element *elem);
7208 /**
7209 *
7210 */
7211 bool parseFile();
7213 /**
7214 *
7215 */
7216 std::vector<String> glob(const String &pattern);
7219 //###############
7220 //# Fields
7221 //###############
7223 String projectName;
7225 String currentTarget;
7227 String defaultTarget;
7229 String specifiedTarget;
7231 String baseDir;
7233 String description;
7235 String envAlias;
7237 //std::vector<Property> properties;
7239 std::map<String, Target> targets;
7241 std::vector<Task *> allTasks;
7244 };
7247 //########################################################################
7248 //# C L A S S M A I N T E N A N C E
7249 //########################################################################
7251 /**
7252 *
7253 */
7254 void Make::init()
7255 {
7256 uri = "build.xml";
7257 projectName = "";
7258 currentTarget = "";
7259 defaultTarget = "";
7260 specifiedTarget = "";
7261 baseDir = "";
7262 description = "";
7263 envAlias = "";
7264 properties.clear();
7265 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7266 delete allTasks[i];
7267 allTasks.clear();
7268 }
7272 /**
7273 *
7274 */
7275 void Make::cleanup()
7276 {
7277 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7278 delete allTasks[i];
7279 allTasks.clear();
7280 }
7284 /**
7285 *
7286 */
7287 void Make::assign(const Make &other)
7288 {
7289 uri = other.uri;
7290 projectName = other.projectName;
7291 currentTarget = other.currentTarget;
7292 defaultTarget = other.defaultTarget;
7293 specifiedTarget = other.specifiedTarget;
7294 baseDir = other.baseDir;
7295 description = other.description;
7296 properties = other.properties;
7297 }
7301 //########################################################################
7302 //# U T I L I T Y T A S K S
7303 //########################################################################
7305 /**
7306 * Perform a file globbing
7307 */
7308 std::vector<String> Make::glob(const String &pattern)
7309 {
7310 std::vector<String> res;
7311 return res;
7312 }
7315 //########################################################################
7316 //# P U B L I C A P I
7317 //########################################################################
7321 /**
7322 *
7323 */
7324 bool Make::executeTarget(Target &target,
7325 std::set<String> &targetsCompleted)
7326 {
7328 String name = target.getName();
7330 //First get any dependencies for this target
7331 std::vector<String> deps = target.getDependencies();
7332 for (unsigned int i=0 ; i<deps.size() ; i++)
7333 {
7334 String dep = deps[i];
7335 //Did we do it already? Skip
7336 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7337 continue;
7339 std::map<String, Target> &tgts =
7340 target.getParent().getTargets();
7341 std::map<String, Target>::iterator iter =
7342 tgts.find(dep);
7343 if (iter == tgts.end())
7344 {
7345 error("Target '%s' dependency '%s' not found",
7346 name.c_str(), dep.c_str());
7347 return false;
7348 }
7349 Target depTarget = iter->second;
7350 if (!executeTarget(depTarget, targetsCompleted))
7351 {
7352 return false;
7353 }
7354 }
7356 status("## Target : %s", name.c_str());
7358 //Now let's do the tasks
7359 std::vector<Task *> &tasks = target.getTasks();
7360 for (unsigned int i=0 ; i<tasks.size() ; i++)
7361 {
7362 Task *task = tasks[i];
7363 status("---- task : %s", task->getName().c_str());
7364 if (!task->execute())
7365 {
7366 return false;
7367 }
7368 }
7370 targetsCompleted.insert(name);
7372 return true;
7373 }
7377 /**
7378 * Main execute() method. Start here and work
7379 * up the dependency tree
7380 */
7381 bool Make::execute()
7382 {
7383 status("######## EXECUTE");
7385 //Determine initial target
7386 if (specifiedTarget.size()>0)
7387 {
7388 currentTarget = specifiedTarget;
7389 }
7390 else if (defaultTarget.size()>0)
7391 {
7392 currentTarget = defaultTarget;
7393 }
7394 else
7395 {
7396 error("execute: no specified or default target requested");
7397 return false;
7398 }
7400 std::map<String, Target>::iterator iter =
7401 targets.find(currentTarget);
7402 if (iter == targets.end())
7403 {
7404 error("Initial target '%s' not found",
7405 currentTarget.c_str());
7406 return false;
7407 }
7409 //Now run
7410 Target target = iter->second;
7411 std::set<String> targetsCompleted;
7412 if (!executeTarget(target, targetsCompleted))
7413 {
7414 return false;
7415 }
7417 status("######## EXECUTE COMPLETE");
7418 return true;
7419 }
7424 /**
7425 *
7426 */
7427 bool Make::checkTargetDependencies(Target &target,
7428 std::vector<String> &depList)
7429 {
7430 String tgtName = target.getName().c_str();
7431 depList.push_back(tgtName);
7433 std::vector<String> deps = target.getDependencies();
7434 for (unsigned int i=0 ; i<deps.size() ; i++)
7435 {
7436 String dep = deps[i];
7437 //First thing entered was the starting Target
7438 if (dep == depList[0])
7439 {
7440 error("Circular dependency '%s' found at '%s'",
7441 dep.c_str(), tgtName.c_str());
7442 std::vector<String>::iterator diter;
7443 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7444 {
7445 error(" %s", diter->c_str());
7446 }
7447 return false;
7448 }
7450 std::map<String, Target> &tgts =
7451 target.getParent().getTargets();
7452 std::map<String, Target>::iterator titer = tgts.find(dep);
7453 if (titer == tgts.end())
7454 {
7455 error("Target '%s' dependency '%s' not found",
7456 tgtName.c_str(), dep.c_str());
7457 return false;
7458 }
7459 if (!checkTargetDependencies(titer->second, depList))
7460 {
7461 return false;
7462 }
7463 }
7464 return true;
7465 }
7471 static int getword(int pos, const String &inbuf, String &result)
7472 {
7473 int p = pos;
7474 int len = (int)inbuf.size();
7475 String val;
7476 while (p < len)
7477 {
7478 char ch = inbuf[p];
7479 if (!isalnum(ch) && ch!='.' && ch!='_')
7480 break;
7481 val.push_back(ch);
7482 p++;
7483 }
7484 result = val;
7485 return p;
7486 }
7491 /**
7492 *
7493 */
7494 bool Make::parsePropertyFile(const String &fileName,
7495 const String &prefix)
7496 {
7497 FILE *f = fopen(fileName.c_str(), "r");
7498 if (!f)
7499 {
7500 error("could not open property file %s", fileName.c_str());
7501 return false;
7502 }
7503 int linenr = 0;
7504 while (!feof(f))
7505 {
7506 char buf[256];
7507 if (!fgets(buf, 255, f))
7508 break;
7509 linenr++;
7510 String s = buf;
7511 s = trim(s);
7512 int len = s.size();
7513 if (len == 0)
7514 continue;
7515 if (s[0] == '#')
7516 continue;
7517 String key;
7518 String val;
7519 int p = 0;
7520 int p2 = getword(p, s, key);
7521 if (p2 <= p)
7522 {
7523 error("property file %s, line %d: expected keyword",
7524 fileName.c_str(), linenr);
7525 return false;
7526 }
7527 if (prefix.size() > 0)
7528 {
7529 key.insert(0, prefix);
7530 }
7532 //skip whitespace
7533 for (p=p2 ; p<len ; p++)
7534 if (!isspace(s[p]))
7535 break;
7537 if (p>=len || s[p]!='=')
7538 {
7539 error("property file %s, line %d: expected '='",
7540 fileName.c_str(), linenr);
7541 return false;
7542 }
7543 p++;
7545 //skip whitespace
7546 for ( ; p<len ; p++)
7547 if (!isspace(s[p]))
7548 break;
7550 /* This way expects a word after the =
7551 p2 = getword(p, s, val);
7552 if (p2 <= p)
7553 {
7554 error("property file %s, line %d: expected value",
7555 fileName.c_str(), linenr);
7556 return false;
7557 }
7558 */
7559 // This way gets the rest of the line after the =
7560 if (p>=len)
7561 {
7562 error("property file %s, line %d: expected value",
7563 fileName.c_str(), linenr);
7564 return false;
7565 }
7566 val = s.substr(p);
7567 if (key.size()==0 || val.size()==0)
7568 continue;
7570 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7571 properties[key] = val;
7572 }
7573 fclose(f);
7574 return true;
7575 }
7580 /**
7581 *
7582 */
7583 bool Make::parseProperty(Element *elem)
7584 {
7585 std::vector<Attribute> &attrs = elem->getAttributes();
7586 for (unsigned int i=0 ; i<attrs.size() ; i++)
7587 {
7588 String attrName = attrs[i].getName();
7589 String attrVal = attrs[i].getValue();
7591 if (attrName == "name")
7592 {
7593 String val;
7594 if (!getAttribute(elem, "value", val))
7595 return false;
7596 if (val.size() > 0)
7597 {
7598 properties[attrVal] = val;
7599 continue;
7600 }
7601 if (!getAttribute(elem, "location", val))
7602 return false;
7603 if (val.size() > 0)
7604 {
7605 //TODO: process a path relative to build.xml
7606 properties[attrVal] = val;
7607 continue;
7608 }
7609 }
7610 else if (attrName == "file")
7611 {
7612 String prefix;
7613 if (!getAttribute(elem, "prefix", prefix))
7614 return false;
7615 if (prefix.size() > 0)
7616 {
7617 if (prefix[prefix.size()-1] != '.')
7618 prefix.push_back('.');
7619 }
7620 if (!parsePropertyFile(attrName, prefix))
7621 return false;
7622 }
7623 else if (attrName == "environment")
7624 {
7625 if (envAlias.size() > 0)
7626 {
7627 error("environment property can only be set once");
7628 return false;
7629 }
7630 envAlias = attrVal;
7631 }
7632 }
7634 return true;
7635 }
7640 /**
7641 *
7642 */
7643 bool Make::parseFile()
7644 {
7645 status("######## PARSE");
7647 Parser parser;
7648 Element *root = parser.parseFile(uri.getNativePath());
7649 if (!root)
7650 {
7651 error("Could not open %s for reading",
7652 uri.getNativePath().c_str());
7653 return false;
7654 }
7656 if (root->getChildren().size()==0 ||
7657 root->getChildren()[0]->getName()!="project")
7658 {
7659 error("Main xml element should be <project>");
7660 delete root;
7661 return false;
7662 }
7664 //########## Project attributes
7665 Element *project = root->getChildren()[0];
7666 String s = project->getAttribute("name");
7667 if (s.size() > 0)
7668 projectName = s;
7669 s = project->getAttribute("default");
7670 if (s.size() > 0)
7671 defaultTarget = s;
7672 s = project->getAttribute("basedir");
7673 if (s.size() > 0)
7674 baseDir = s;
7676 //######### PARSE MEMBERS
7677 std::vector<Element *> children = project->getChildren();
7678 for (unsigned int i=0 ; i<children.size() ; i++)
7679 {
7680 Element *elem = children[i];
7681 String tagName = elem->getName();
7683 //########## DESCRIPTION
7684 if (tagName == "description")
7685 {
7686 description = parser.trim(elem->getValue());
7687 }
7689 //######### PROPERTY
7690 else if (tagName == "property")
7691 {
7692 if (!parseProperty(elem))
7693 return false;
7694 }
7696 //######### TARGET
7697 else if (tagName == "target")
7698 {
7699 String tname = elem->getAttribute("name");
7700 String tdesc = elem->getAttribute("description");
7701 String tdeps = elem->getAttribute("depends");
7702 String tif = elem->getAttribute("if");
7703 String tunless = elem->getAttribute("unless");
7704 Target target(*this);
7705 target.setName(tname);
7706 target.setDescription(tdesc);
7707 target.parseDependencies(tdeps);
7708 target.setIf(tif);
7709 target.setUnless(tunless);
7710 std::vector<Element *> telems = elem->getChildren();
7711 for (unsigned int i=0 ; i<telems.size() ; i++)
7712 {
7713 Element *telem = telems[i];
7714 Task breeder(*this);
7715 Task *task = breeder.createTask(telem);
7716 if (!task)
7717 return false;
7718 allTasks.push_back(task);
7719 target.addTask(task);
7720 }
7722 //Check name
7723 if (tname.size() == 0)
7724 {
7725 error("no name for target");
7726 return false;
7727 }
7728 //Check for duplicate name
7729 if (targets.find(tname) != targets.end())
7730 {
7731 error("target '%s' already defined", tname.c_str());
7732 return false;
7733 }
7734 //more work than targets[tname]=target, but avoids default allocator
7735 targets.insert(std::make_pair<String, Target>(tname, target));
7736 }
7738 }
7740 std::map<String, Target>::iterator iter;
7741 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
7742 {
7743 Target tgt = iter->second;
7744 std::vector<String> depList;
7745 if (!checkTargetDependencies(tgt, depList))
7746 {
7747 return false;
7748 }
7749 }
7752 delete root;
7753 status("######## PARSE COMPLETE");
7754 return true;
7755 }
7758 /**
7759 *
7760 */
7761 bool Make::run()
7762 {
7763 if (!parseFile())
7764 return false;
7765 if (!execute())
7766 return false;
7767 return true;
7768 }
7771 /**
7772 *
7773 */
7774 bool Make::run(const String &target)
7775 {
7776 status("##################################");
7777 status("# BuildTool");
7778 status("# version 0.3");
7779 status("# 16 Nov 06");
7780 status("##################################");
7781 specifiedTarget = target;
7782 if (!run())
7783 return false;
7784 status("##################################");
7785 status("# BuildTool Completed");
7786 status("##################################");
7787 return true;
7788 }
7796 }// namespace buildtool
7797 //########################################################################
7798 //# M A I N
7799 //########################################################################
7801 /**
7802 * Format an error message in printf() style
7803 */
7804 static void error(char *fmt, ...)
7805 {
7806 va_list ap;
7807 va_start(ap, fmt);
7808 fprintf(stderr, "BuildTool error: ");
7809 vfprintf(stderr, fmt, ap);
7810 fprintf(stderr, "\n");
7811 va_end(ap);
7812 }
7815 /**
7816 * Compare a buffer with a key, for the length of the key
7817 */
7818 static bool sequ(const buildtool::String &buf, char *key)
7819 {
7820 for (int i=0 ; key[i] ; i++)
7821 {
7822 if (key[i] != buf[i])
7823 return false;
7824 }
7825 return true;
7826 }
7828 /**
7829 * Parse the command-line args, get our options,
7830 * and run this thing
7831 */
7832 static bool parseOptions(int argc, char **argv)
7833 {
7834 if (argc < 1)
7835 {
7836 error("Cannot parse arguments");
7837 return false;
7838 }
7840 buildtool::String buildFile;
7841 buildtool::String target;
7843 //char *progName = argv[0];
7844 for (int i=1 ; i<argc ; i++)
7845 {
7846 buildtool::String arg = argv[i];
7847 if (sequ(arg, "--"))
7848 {
7849 if (sequ(arg, "--file=") && arg.size()>7)
7850 {
7851 buildFile = arg.substr(7, arg.size()-7);
7852 }
7853 else
7854 {
7855 error("Unknown option:%s", arg.c_str());
7856 return false;
7857 }
7858 }
7859 else if (sequ(arg, "-"))
7860 {
7861 for (unsigned int p=1 ; p<arg.size() ; p++)
7862 {
7863 int ch = arg[p];
7864 if (0)//put options here
7865 {
7866 }
7867 else
7868 {
7869 error("Unknown option '%c'", ch);
7870 return false;
7871 }
7872 }
7873 }
7874 else
7875 {
7876 target = arg;
7877 }
7878 }
7880 //We have the options. Now execute them
7881 buildtool::Make make;
7882 if (buildFile.size() > 0)
7883 {
7884 make.setURI(buildFile);
7885 }
7886 if (!make.run(target))
7887 return false;
7889 return true;
7890 }
7895 /*
7896 static bool runMake()
7897 {
7898 buildtool::Make make;
7899 if (!make.run())
7900 return false;
7901 return true;
7902 }
7905 static bool pkgConfigTest()
7906 {
7907 buildtool::PkgConfig pkgConfig;
7908 if (!pkgConfig.readFile("gtk+-2.0.pc"))
7909 return false;
7910 return true;
7911 }
7915 static bool depTest()
7916 {
7917 buildtool::DepTool deptool;
7918 deptool.setSourceDirectory("/dev/ink/inkscape/src");
7919 if (!deptool.generateDependencies("build.dep"))
7920 return false;
7921 std::vector<buildtool::DepRec> res =
7922 deptool.loadDepFile("build.dep");
7923 if (res.size() == 0)
7924 return false;
7925 return true;
7926 }
7928 static bool popenTest()
7929 {
7930 buildtool::Make make;
7931 buildtool::String out, err;
7932 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
7933 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
7934 return true;
7935 }
7938 static bool propFileTest()
7939 {
7940 buildtool::Make make;
7941 make.parsePropertyFile("test.prop", "test.");
7942 return true;
7943 }
7944 */
7946 int main(int argc, char **argv)
7947 {
7949 if (!parseOptions(argc, argv))
7950 return 1;
7951 /*
7952 if (!popenTest())
7953 return 1;
7955 if (!depTest())
7956 return 1;
7957 if (!propFileTest())
7958 return 1;
7959 if (runMake())
7960 return 1;
7961 */
7962 return 0;
7963 }
7966 //########################################################################
7967 //# E N D
7968 //########################################################################