Bug Summary

File:dmake/expand.c
Location:line 1183, column 9
Description:Null pointer passed as an argument to a 'nonnull' parameter

Annotated Source Code

1/*
2--
3-- SYNOPSIS
4-- Macro expansion code.
5--
6-- DESCRIPTION
7--
8-- This routine handles all the necessary junk that deals with macro
9-- expansion. It understands the following syntax. If a macro is
10-- not defined it expands to NULL, and {} are synonyms for ().
11--
12-- $$ - expands to $
13-- {{ - expands to {
14-- }} - expands to }
15-- $A - expands to whatever the macro A is defined as
16-- $(AA) - expands to whatever the macro AA is defined as
17-- $($(A)) - represents macro indirection
18-- <+...+> - get mapped to $(mktmp ...)
19--
20-- following macro is recognized
21--
22-- string1{ token_list }string2
23--
24-- and expands to string1 prepended to each element of token_list and
25-- string2 appended to each of the resulting tokens from the first
26-- operation. If string2 is of the form above then the result is
27-- the cross product of the specified (possibly modified) token_lists.
28--
29-- The folowing macro modifiers are defined and expanded:
30--
31-- $(macro:modifier_list:modifier_list:...)
32--
33-- where modifier_list a combination of:
34--
35-- D or d - Directory portion of token including separator
36-- F or f - File portion of token including suffix
37-- B or b - basename portion of token not including suffix
38-- E or e - Suffix portion of name
39-- L or l - translate to lower case
40-- U or u - translate to upper case
41-- I or i - return inferred names
42-- N or n - return normalized paths
43-- 1 - return the first white space separated token
44--
45-- or a single one of:
46-- M or m - map escape codes
47-- S or s - pattern substitution (simple)
48-- T or t - for tokenization
49-- ^ - prepend a prefix to each token
50-- + - append a suffix to each token
51--
52-- NOTE: Modifiers are applied once the macro value has been found.
53-- Thus the construct $($(test):s/joe/mary/) is defined and
54-- modifies the value of $($(test))
55--
56-- Also the construct $(m:d:f) is not the same as $(m:df)
57-- the first applies d to the value of $(m) and then
58-- applies f to the value of that whereas the second form
59-- applies df to the value of $(m).
60--
61-- AUTHOR
62-- Dennis Vadura, dvadura@dmake.wticorp.com
63--
64-- WWW
65-- http://dmake.wticorp.com/
66--
67-- COPYRIGHT
68-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
69--
70-- This program is NOT free software; you can redistribute it and/or
71-- modify it under the terms of the Software License Agreement Provided
72-- in the file <distribution-root>/readme/license.txt.
73--
74-- LOG
75-- Use cvs log to obtain detailed change logs.
76*/
77
78#include "extern.h"
79
80/* Microsoft BRAINDAMAGE ALERT!!!!
81 * This #ifdef is here only to satisfy stupid bugs in MSC5.0 and MSC5.1
82 * it isn't needed for anything else. It turns loop optimization off. */
83#if defined(_MSV_VER) && _MSC_VER < 600
84#include "optoff.h"
85#endif
86
87static char* _scan_macro ANSI((char*, char**, int))(char*, char**, int);
88static char* _scan_brace ANSI((char*, char**, int*))(char*, char**, int*);
89static char* _cross_prod ANSI((char*, char*))(char*, char*);
90
91#if !defined(__GNUC__4) && !defined(__IBMC__)
92static char* _scan_ballanced_parens ANSI((char*, char))(char*, char);
93#else
94static char* _scan_ballanced_parens ANSI((char*, int))(char*, int);
95#endif
96
97
98PUBLIC char *
99Expand( src )/*
100===============
101 This is the driver routine for the expansion, it identifies non-white
102 space tokens and gets the ScanToken routine to figure out if they should
103 be treated in a special way. */
104
105char *src; /* pointer to source string */
106{
107 char *tmp; /* pointer to temporary str */
108 char *res; /* pointer to result string */
109 char *start; /* pointer to start of token */
110
111 DB_ENTER( "Expand" );
112 DB_PRINT( "exp", ("Expanding [%s]", src) );
113
114 res = DmStrDup( "" );
115 if( src == NIL(char)((char*)((void*)0)) ) DB_RETURN( res )return (res);
116
117 while( *src ) {
118 char *ks, *ke;
119
120 /* Here we find the next non white space token in the string
121 * and find it's end, with respect to non-significant white space. */
122
123#if !defined( _MPW) && !defined(__EMX__)
124 start = DmStrSpn( src, " \t\n" );
125#else
126 start = DmStrSpn( src, " \t\r\n" );
127#endif
128
129 res = DmStrJoin( res, src, start-src, TRUE1 );
130 if( !(*start) ) break;
131
132 /* START <+...+> KLUDGE */
133 if( (ks=DmStrStr(start,"<+")) != NIL(char)((char*)((void*)0))
134 && (ke=DmStrStr(ks,"+>")) != NIL(char)((char*)((void*)0)) ) {
135 char *t1, *t2;
136
137 res = DmStrJoin( res, t2=Expand(t1=DmSubStr(start,ks)), -1, TRUE1);
138 FREE(t1)free((char*)(t1)); FREE(t2)free((char*)(t2));
139
140 t1 = DmSubStr(ks+2, ke+1); t1[ke-ks-2] = ')';
141 t2 = DmStrJoin( "$(mktmp ", t1, -1,FALSE0);
142 FREE(t1)free((char*)(t1));
143 res = DmStrJoin( res, t1=Expand(t2), -1, TRUE1);
144 FREE(t1)free((char*)(t1)); FREE(t2)free((char*)(t2));
145 src = ke+2;
146 }
147 /* END <+...+> KLUDGE */
148 else {
149 res = DmStrJoin( res, tmp = ScanToken(start,&src,TRUE1), -1, TRUE1 );
150 FREE( tmp )free((char*)(tmp));
151 }
152 }
153
154 DB_PRINT( "exp", ("Returning [%s]", res) );
155 DB_RETURN( res )return (res);
156}
157
158
159PUBLIC char *
160Apply_edit( src, pat, subst, fr, anchor )/*
161===========================================
162 Take the src string and apply the pattern substitution. ie. look for
163 occurrences of pat in src and replace each occurrence with subst. This is
164 NOT a regular expressions pattern substitution, it's just not worth it.
165
166 if anchor == TRUE then the src pattern match must be at the end of a token.
167 ie. this is for SYSV compatibility and is only used for substitutions of
168 the caused by $(macro:pat=sub). So if src = "fre.o.k june.o" then
169 $(src:.o=.a) results in "fre.o.k june.a", and $(src:s/.o/.a) results in
170 "fre.a.k june.a" */
171
172char *src; /* the source string */
173char *pat; /* pattern to find */
174char *subst; /* substitute string */
175int fr; /* if TRUE free src */
176int anchor; /* if TRUE anchor */
177{
178 char *res;
179 char *p;
180 char *s;
181 int l;
182
183 DB_ENTER( "Apply_edit" );
184
185 /* do nothing if pat is NULL or pat and subst are equal */
186 if( !*pat || !strcmp(pat,subst) ) DB_RETURN( src )return (src);
187
188 DB_PRINT( "mod", ("Source str: [%s]", src) );
189 DB_PRINT( "mod", ("Replacing [%s], with [%s]", pat, subst) );
190
191 /* FIXME: This routine is used frequently and has room for optimizations */
192 s = src;
193 l = strlen( pat );
194 if( (p = DmStrStr( s, pat )) != NIL(char)((char*)((void*)0)) ) {
195 res = DmStrDup( "" );
196 do {
197 if( anchor )
198 if( !*(p+l) || (strchr(" \t", *(p+l)) != NIL(char)((char*)((void*)0))) )
199 res = DmStrJoin( DmStrJoin(res,s,p-s,TRUE1), subst, -1, TRUE1 );
200 else
201 res = DmStrJoin( res, s, p+l-s, TRUE1 );
202 else
203 res = DmStrJoin( DmStrJoin(res,s,p-s,TRUE1), subst, -1, TRUE1 );
204
205 s = p + l;
206 }
207 while( (p = DmStrStr( s, pat )) != NIL(char)((char*)((void*)0)) );
208
209 res = DmStrJoin( res, s, -1, TRUE1 );
210 if( fr ) FREE( src )free((char*)(src));
211 }
212 else
213 res = src;
214
215
216 DB_PRINT( "mod", ("Result [%s]", res) );
217 DB_RETURN( res )return (res);
218}
219
220
221PUBLIC void
222Map_esc( tok )/*
223================
224 Map an escape sequence and replace it by it's corresponding character
225 value. It is assumed that tok points at the initial \, the esc
226 sequence in the original string is replaced and the value of tok
227 is not modified. */
228char *tok;
229{
230 if( strchr( "\"\\vantbrf01234567", tok[1] ) ) {
231 size_t len;
232 switch( tok[1] ) {
233 case 'a' : *tok = 0x07; break;
234 case 'b' : *tok = '\b'; break;
235 case 'f' : *tok = '\f'; break;
236 case 'n' : *tok = '\n'; break;
237 case 'r' : *tok = '\r'; break;
238 case 't' : *tok = '\t'; break;
239 case 'v' : *tok = 0x0b; break;
240 case '\\': *tok = '\\'; break;
241 case '\"': *tok = '\"'; break;
242
243 default: {
244 register int i = 0;
245 register int j = 0;
246 for( ; i<2 && isdigit(tok[2])((*__ctype_b_loc ())[(int) ((tok[2]))] & (unsigned short int
) _ISdigit)
; i++ ) {
247 j = (j << 3) + (tok[1] - '0');
248 len = strlen(tok+2)+1;
249 memmove( tok+1, tok+2, len );
250 }
251 j = (j << 3) + (tok[1] - '0');
252 *tok = j;
253 }
254 }
255 len = strlen(tok+2)+1;
256 memmove( tok+1, tok+2, len );
257 }
258}
259
260
261PUBLIC char*
262Apply_modifiers( mod, src )/*
263=============================
264 This routine applies the appropriate modifiers to the string src
265 and returns the proper result string */
266
267int mod;
268char *src;
269{
270 char *s;
271 char *e;
272 char *res;
273 TKSTR str;
274
275 DB_ENTER( "Apply_modifiers" );
276
277 if ( mod & INFNAME_FLAG32 ) {
278 SET_TOKEN( &str, src )(&str)->tk_str = (src); (&str)->tk_cchar = *(src
); (&str)->tk_quote = 1;
;
279 e = NIL(char)((char*)((void*)0));
280
281 while( *(s = Get_token( &str, "", FALSE0 )) != '\0' ) {
282 HASHPTR hp;
283
284 if ( (hp = Get_name(normalize_path(s), Defs, FALSE0)) != NIL(HASH)((HASH*)((void*)0))
285 && hp->CP_OWNRvar.val.ht.ht_owner
286 && hp->CP_OWNRvar.val.ht.ht_owner->ce_fname
287 ) {
288 res = hp->CP_OWNRvar.val.ht.ht_owner->ce_fname;
289 }
290 else
291 res = s;
292
293 if(str.tk_quote == 0) {
294 /* Add leading quote. */
295 e = DmStrApp(e, "\"");
296 e = DmStrJoin(e, res, -1, TRUE1);
297 /* Append the trailing quote. */
298 e = DmStrJoin(e, "\"", 1, TRUE1);
299 } else {
300 e = DmStrApp(e, res);
301 }
302
303 }
304
305 FREE(src)free((char*)(src));
306 src = e;
307 mod &= ~INFNAME_FLAG32;
308 }
309
310 if ( mod & NORMPATH_FLAG128 ) {
311 e = exec_normpath(src);
312
313 FREE(src)free((char*)(src));
314 src = e;
315 mod &= ~NORMPATH_FLAG128;
316 }
317
318 if(mod & (TOLOWER_FLAG8|TOUPPER_FLAG16) ) {
319 int lower;
320 lower = mod & TOLOWER_FLAG8;
321
322 for (s=src; *s; s++)
323 if ( isalpha(*s)((*__ctype_b_loc ())[(int) ((*s))] & (unsigned short int)
_ISalpha)
)
324 *s = ((lower) ? tolower(*s) : toupper(*s));
325
326 mod &= ~(TOLOWER_FLAG8|TOUPPER_FLAG16);
327 }
328
329 if (mod & JUST_FIRST_FLAG64) {
330 SET_TOKEN(&str, src)(&str)->tk_str = (src); (&str)->tk_cchar = *(src
); (&str)->tk_quote = 1;
;
331 if ((s = Get_token(&str,"",FALSE0)) != '\0') {
332 /* Recycle the quote at the beginning. */
333 if(str.tk_quote == 0) {
334 s--;
335 }
336 e = DmStrDup(s);
337 /* Add trailing quote. */
338 if(str.tk_quote == 0) {
339 e = DmStrJoin(e, "\"", 1, TRUE1);
340 }
341
342 CLEAR_TOKEN(&str)*(&str)->tk_str = (&str)->tk_cchar;
343 FREE(src)free((char*)(src));
344 src = e;
345 }
346 else {
347 CLEAR_TOKEN(&str)*(&str)->tk_str = (&str)->tk_cchar;
348 }
349 mod &= ~JUST_FIRST_FLAG64;
350 }
351
352 if( !mod || mod == (SUFFIX_FLAG1 | DIRECTORY_FLAG2 | FILE_FLAG4) )
353 DB_RETURN( src )return (src);
354
355 SET_TOKEN( &str, src )(&str)->tk_str = (src); (&str)->tk_cchar = *(src
); (&str)->tk_quote = 1;
;
356 DB_PRINT( "mod", ("Source string [%s]", src) );
357 res = DmStrDup("");
358
359 while( *(s = Get_token( &str, "", FALSE0 )) != '\0' ) {
360 char *tokstart = s;
361
362 /* search for the directory portion of the filename. If the
363 * DIRECTORY_FLAG is set, then we want to keep the directory portion
364 * othewise throw it away and blank out to the end of the token */
365
366 if( (e = Basename(s)) != s) {
367 if( !(mod & DIRECTORY_FLAG2) ) {
368 /* Move the basename to the start. */
369 size_t len = strlen(e)+1;
370 memmove(s, e, len);
371 }
372 else
373 s = e;
374 }
375 /* s now points to the start of the basename. */
376
377
378 /* search for the suffix, if there is none, treat it as a NULL suffix.
379 * if no file name treat it as a NULL file name. same copy op as
380 * for directory case above */
381
382 e = strrchr( s, '.' ); /* NULL suffix if e=0 */
383 if( e == NIL(char)((char*)((void*)0)) ) e = s+strlen(s);
384
385 if( !(mod & FILE_FLAG4) ) {
386 /* Move the suffix to the start. */
387 size_t len = strlen(e)+1;
388 memmove(s, e, len);
389 }
390 else
391 s = e;
392
393 /* s now points to the start of the suffix. */
394
395
396 /* The last and final part. This is the suffix case, if we don't want
397 * it then just erase it. */
398
399 if( s != NIL(char)((char*)((void*)0)) )
400 if( !(mod & SUFFIX_FLAG1) && s != str.tk_str )
401 *s = '\0';
402
403
404 /* only keep non-empty tokens. (This also discards empty quoted ""
405 * tokens.) */
406 if( strlen(tokstart) ) {
407 /* Recycle the quote at the beginning. */
408 if(str.tk_quote == 0) {
409 tokstart--;
410 }
411 res = DmStrApp(res, tokstart);
412 /* Add trailing quote. */
413 if(str.tk_quote == 0) {
414 res = DmStrJoin(res, "\"", 1, TRUE1);
415 }
416 }
417 }
418
419 FREE(src)free((char*)(src));
420 src = res;
421
422
423 DB_PRINT( "mod", ("Result string [%s]", src) );
424 DB_RETURN( src )return (src);
425}
426
427
428PUBLIC char*
429Tokenize( src, separator, op, mapesc )/*
430========================================
431 Tokenize the input of src and join each token found together with
432 the next token separated by the separator string.
433
434 When doing the tokenization, <sp>, <tab>, <nl>, and \<nl> all
435 constitute white space. */
436
437char *src;
438char *separator;
439char op;
440int mapesc;
441{
442 TKSTR tokens;
443 char *tok;
444 char *res;
445 int first = (op == 't' || op == 'T');
446
447 DB_ENTER( "Tokenize" );
448
449 /* map the escape codes in the separator string first */
450 if ( mapesc )
451 for(tok=separator; (tok = strchr(tok,ESCAPE_CHAR*Escape_char)) != NIL(char)((char*)((void*)0)); tok++)
452 Map_esc( tok );
453
454 DB_PRINT( "exp", ("Separator [%s]", separator) );
455
456 /* By default we return an empty string */
457 res = DmStrDup( "" );
458
459 /* Build the token list */
460 SET_TOKEN( &tokens, src )(&tokens)->tk_str = (src); (&tokens)->tk_cchar =
*(src); (&tokens)->tk_quote = 1;
;
461 while( *(tok = Get_token( &tokens, "", FALSE0 )) != '\0' ) {
462 char *x;
463
464 if( first ) {
465 FREE( res )free((char*)(res));
466 res = DmStrDup( tok );
467 first = FALSE0;
468 }
469 else if (op == '^') {
470 res = DmStrAdd(res, DmStrJoin(separator, tok, -1, FALSE0), TRUE1);
471 }
472 else if (op == '+') {
473 res = DmStrAdd(res, DmStrJoin(tok, separator, -1, FALSE0), TRUE1);
474 }
475 else {
476 res = DmStrJoin(res, x =DmStrJoin(separator, tok, -1, FALSE0),
477 -1, TRUE1);
478 FREE( x )free((char*)(x));
479 }
480
481 DB_PRINT( "exp", ("Tokenizing [%s] --> [%s]", tok, res) );
482 }
483
484 FREE( src )free((char*)(src));
485 DB_RETURN( res )return (res);
486}
487
488
489static char*
490_scan_ballanced_parens(p, delim)
491char *p;
492char delim;
493{
494 int pcount = 0;
495 int bcount = 0;
496
497 if ( p ) {
498 do {
499 if (delim)
500 if( !(bcount || pcount) && *p == delim) {
501 return(p);
502 }
503
504 if ( *p == '(' ) pcount++;
505 else if ( *p == '{' ) bcount++;
506 else if ( *p == ')' && pcount ) pcount--;
507 else if ( *p == '}' && bcount ) bcount--;
508
509 p++;
510 }
511 while (*p && (pcount || bcount || delim));
512 }
513
514 return(p);
515}
516
517
518PUBLIC char*
519ScanToken( s, ps, doexpand )/*
520==============================
521 This routine scans the token characters one at a time and identifies
522 macros starting with $( and ${ and calls _scan_macro to expand their
523 value. the string1{ token_list }string2 expansion is also handled.
524 In this case a temporary result is maintained so that we can take it's
525 cross product with any other token_lists that may possibly appear. */
526
527char *s; /* pointer to start of src string */
528char **ps; /* pointer to start pointer */
529int doexpand;
530{
531 char *res; /* pointer to result */
532 char *start; /* pointer to start of prefix */
533 int crossproduct = 0; /* if 1 then computing X-prod */
534
535 start = s;
536 res = DmStrDup( "" );
537 while( 1 ) {
538 switch( *s ) {
539 /* Termination, We halt at seeing a space or a tab or end of string.
540 * We return the value of the result with any new macro's we scanned
541 * or if we were computing cross_products then we return the new
542 * cross_product.
543 * NOTE: Once we start computing cross products it is impossible to
544 * stop. ie. the semantics are such that once a {} pair is
545 * seen we compute cross products until termination. */
546
547 case ' ':
548 case '\t':
549 case '\n':
550 case '\r':
551 case '\0':
552 {
553 char *tmp;
554
555 *ps = s;
556 if( !crossproduct )
557 tmp = DmStrJoin( res, start, (s-start), TRUE1 );
558 else
559 {
560 tmp = DmSubStr( start, s );
561 tmp = _cross_prod( res, tmp );
562 }
563 return( tmp );
564 }
565
566 case '$':
567 case '{':
568 {
569 /* Handle if it's a macro or if it's a {} construct.
570 * The results of a macro expansion are handled differently based
571 * on whether we have seen a {} beforehand. */
572
573 char *tmp;
574 tmp = DmSubStr( start, s ); /* save the prefix */
575
576 if( *s == '$' ) {
577 start = _scan_macro( s+1, &s, doexpand );
578
579 if( crossproduct ) {
580 res = _cross_prod( res, DmStrJoin( tmp, start, -1, TRUE1 ) );
581 }
582 else {
583 res = DmStrJoin(res,tmp=DmStrJoin(tmp,start,-1,TRUE1),-1,TRUE1);
584 FREE( tmp )free((char*)(tmp));
585 }
586 FREE( start )free((char*)(start));
587 }
588 else if( strchr("{ \t",s[1]) == NIL(char)((char*)((void*)0)) ){
589 int ok;
590 start = _scan_brace( s+1, &s, &ok );
591
592 if( ok ) {
593 if ( crossproduct ) {
594 res = _cross_prod(res,_cross_prod(tmp,start));
595 }
596 else {
597 char *freeres;
598 res = Tokenize(start,
599 freeres=DmStrJoin(res,tmp,-1,TRUE1),
600 '^', FALSE0);
601 FREE(freeres)free((char*)(freeres));
602 FREE(tmp)free((char*)(tmp));
603 }
604 crossproduct = TRUE1;
605 }
606 else {
607 res =DmStrJoin(res,tmp=DmStrJoin(tmp,start,-1,TRUE1),-1,TRUE1);
608 FREE( start )free((char*)(start));
609 FREE( tmp )free((char*)(tmp));
610 }
611 }
612 else { /* handle the {{ case */
613 res = DmStrJoin( res, start, (s-start+1), TRUE1 );
614 s += (s[1]=='{')?2:1;
615 FREE( tmp )free((char*)(tmp));
616 }
617
618 start = s;
619 }
620 break;
621
622 case '}':
623 if( s[1] != '}' ) {
624 /* error malformed macro expansion */
625 s++;
626 }
627 else { /* handle the }} case */
628 res = DmStrJoin( res, start, (s-start+1), TRUE1 );
629 s += 2;
630 start = s;
631 }
632 break;
633
634 default: s++;
635 }
636 }
637}
638
639
640static char*
641_scan_macro( s, ps, doexpand )/*
642================================
643 This routine scans a macro use and expands it to the value. It
644 returns the macro's expanded value and modifies the pointer into the
645 src string to point at the first character after the macro use.
646 The types of uses recognized are:
647
648 $$ and $<sp> - expands to $
649 $(name) - expands to value of name
650 ${name} - same as above
651 $($(name)) - recurses on macro names (any level)
652 and
653 $(func[,args ...] [data])
654 and
655 $(name:modifier_list:modifier_list:...)
656
657 see comment for Expand for description of valid modifiers.
658
659 NOTE that once a macro name bounded by ( or { is found only
660 the appropriate terminator (ie. ( or } is searched for. */
661
662char *s; /* pointer to start of src string */
663char **ps; /* pointer to start pointer */
664int doexpand; /* If TRUE enables macro expansion */
665{
666 char sdelim; /* start of macro delimiter */
667 char edelim; /* corresponding end macro delim */
668 char *start; /* start of prefix */
669 char *macro_name; /* temporary macro name */
670 char *recurse_name; /* recursive macro name */
671 char *result; /* result for macro expansion */
672 int bflag = 0; /* brace flag, ==0 => $A type macro */
673 int done = 0; /* != 0 => done macro search */
674 int lev = 0; /* brace level */
675 int mflag = 0; /* != 0 => modifiers present in mac */
676 int fflag = 0; /* != 0 => GNU style function */
677 HASHPTR hp; /* hash table pointer for macros */
678
679 DB_ENTER( "_scan_macro" );
680
681 /* Check for $ at end of line, or $ followed by white space */
682 /* FIXME: Shouldn't a single '$' be an error? */
683 if( !*s || strchr(" \t", *s) != NIL(char)((char*)((void*)0))) {
684 *ps = s;
685 DB_RETURN( DmStrDup("") )return (DmStrDup(""));
686 }
687
688 if( *s == '$' ) { /* Take care of the simple $$ case. */
689 *ps = s+1;
690 DB_RETURN( DmStrDup("$") )return (DmStrDup("$"));
691 }
692
693 sdelim = *s; /* set and remember start/end delim */
694 if( sdelim == '(' )
695 edelim = ')';
696 else
697 edelim = '}';
698
699 start = s; /* build up macro name, find its end */
700 while( !done ) {
701 switch( *s ) {
702 case '(': /* open macro brace */
703 case '{':
704 if( *s == sdelim ) {
705 lev++;
706 bflag++;
707 }
708 break;
709
710 case ':': /* halt at modifier */
711 if( lev == 1 && !fflag && doexpand ) {
712 done = TRUE1;
713 mflag = 1;
714 }
715 else if( !lev ) /* must be $: */
716 Fatal( "Syntax error in macro [$%s]. A colon [:] cannot be a macro name.\n", start );
717
718 /* continue if a colon is found but lev > 1 */
719 break;
720
721 case '\n': /* Not possible because of the
722 * following case. */
723 Fatal( "DEBUG: No standalone '\n' [%s].\n", start );
724 break;
725
726 case '\\': /* Transform \<nl> -> ' '. */
727 if( s[1] != '\n' ) {
728 done = !lev;
729 break;
730 } else {
731 size_t len;
732 s[1] = ' ';
733 len = strlen(s+1)+1;
734 memmove( s, s+1, len );
735 }
736 /*FALLTHRU*/
737 case ' ':
738 case '\t':
739 if ( lev == 1 ) fflag = 1;
740 break;
741
742 case '\0': /* check for null */
743 *ps = s;
744 done = TRUE1;
745 if( lev ) { /* catch $( or ${ without closing bracket */
746 Fatal( "Syntax error in macro [$%s]. The closing bracket [%c] is missing.\n", start, edelim );
747 } else
748 Fatal( "DEBUG: This cannot occur! [%s].\n", start );
749 break;
750
751 case ')': /* close macro brace */
752 case '}':
753 if( !lev ) /* A closing bracket without an .. */
754 Fatal("Syntax error in macro [$%s]. Closing bracket [%c] cannot be a macro name.\n", start, *s );
755 else if( *s == edelim ) --lev;
756 /*FALLTHRU*/
757
758 default: /* Done when lev == 0. This means either no */
759 done = !lev; /* opening bracket (single letter macro) or */
760 /* a fully enclosed $(..) or ${..} macro */
761 /* was found. */
762 }
763 s++;
764 }
765
766 /* Check if this is a $A type macro. If so then we have to
767 * handle it a little differently. */
768 if( bflag )
769 macro_name = DmSubStr( start+1, s-1 );
770 else
771 macro_name = DmSubStr( start, s );
772
773 /* If we don't have to expand the macro we're done. */
774 if (!doexpand) {
775 *ps = s;
776 DB_RETURN(macro_name)return (macro_name);
777 }
778
779 /* Check to see if the macro name contains spaces, if so then treat it
780 * as a GNU style function invocation and call the function mapper to
781 * deal with it. We do not call the function expander if the function
782 * invocation begins with a '$' */
783 if( fflag && *macro_name != '$' ) {
784 result = Exec_function(macro_name);
785 }
786 else {
787 /* Check if the macro is a recursive macro name, if so then
788 * EXPAND the name before expanding the value */
789 if( strchr( macro_name, '$' ) != NIL(char)((char*)((void*)0)) ) {
790 recurse_name = Expand( macro_name );
791 FREE( macro_name )free((char*)(macro_name));
792 macro_name = recurse_name;
793 }
794
795 /* Code to do value expansion goes here, NOTE: macros whose assign bit
796 is one have been evaluated and assigned, they contain no further
797 expansions and thus do not need their values expanded again. */
798
799 if( (hp = GET_MACRO( macro_name )Get_name(macro_name, Macs, 0)) != NIL(HASH)((HASH*)((void*)0)) ) {
800 if( hp->ht_flag & M_MARK0x0001 )
801 Fatal( "Detected circular macro [%s]", hp->ht_name );
802
803 if( !(hp->ht_flag & M_EXPANDED0x0008) ) {
804 hp->ht_flag |= M_MARK0x0001;
805 result = Expand( hp->ht_value );
806 hp->ht_flag ^= M_MARK0x0001;
807 }
808 else if( hp->ht_value != NIL(char)((char*)((void*)0)) )
809 result = DmStrDup( hp->ht_value );
810 else
811 result = DmStrDup( "" );
812
813 }
814 else {
815 /* The use of an undefined macro implicitly defines it but
816 * leaves its value to NIL(char). */
817 hp = Def_macro( macro_name, NIL(char)((char*)((void*)0)), M_EXPANDED0x0008 );
818 /* Setting M_INIT assures that this macro is treated unset like
819 * default internal macros. (Necessary for *= and *:=) */
820 hp->ht_flag |= M_INIT0x0200;
821
822 result = DmStrDup( "" );
823 }
824 /* Mark macros as used only if we are not expanding them for
825 * the purpose of a .IF test, so we can warn about redef after use*/
826 if( !If_expand ) hp->ht_flag |= M_USED0x0010;
827
828 }
829
830 if( mflag ) {
831 char separator;
832 int modifier_list = 0;
833 int aug_mod = FALSE0;
834 char *pat1;
835 char *pat2;
836 char *p;
837
838 /* We are inside of a macro expansion. The "build up macro name,
839 * find its while loop above should have caught all \<nl> and
840 * converted them to a real space. Let's verify this. */
841 for( p=s; *p && *p != edelim && *p; p++ ) {
842 if( p[0] == '\\' && p[1] == '\n' ) {
843 size_t len;
844 p[1] = ' ';
845 len = strlen(p+1)+1;
846 memmove( p, p+1, len );
847 }
848 }
849 if( !*p )
850 Fatal( "Syntax error in macro modifier pattern [$%s]. The closing bracket [%c] is missing.\n", start, edelim );
851
852 /* Yet another brain damaged AUGMAKE kludge. We should accept the
853 * AUGMAKE bullshit of $(f:pat=sub) form of macro expansion. In
854 * order to do this we will forgo the normal processing if the
855 * AUGMAKE solution pans out, otherwise we will try to process the
856 * modifiers ala dmake.
857 *
858 * So we look for = in modifier string.
859 * If found we process it and not do the normal stuff */
860
861 for( p=s; *p && *p != '=' && *p != edelim; p++ );
862
863 if( *p == '=' ) {
864 char *tmp;
865
866 pat1 = Expand(tmp = DmSubStr(s,p)); FREE(tmp)free((char*)(tmp));
867 s = p+1;
868 p = _scan_ballanced_parens(s+1, edelim);
869
870 if ( !*p ) {
871 Fatal( "Incomplete macro expression [%s]", s );
872 p = s+1;
873 }
874 pat2 = Expand(tmp = DmSubStr(s,p)); FREE(tmp)free((char*)(tmp));
875
876 result = Apply_edit( result, pat1, pat2, TRUE1, TRUE1 );
877 FREE( pat1 )free((char*)(pat1));
878 FREE( pat2 )free((char*)(pat2));
879 s = p;
880 aug_mod = TRUE1;
881 }
882
883 if( !aug_mod )
884 while( *s && *s != edelim ) { /* while not at end of macro */
885 char switch_char;
886
887 switch( switch_char = *s++ ) {
888 case '1': modifier_list |= JUST_FIRST_FLAG64; break;
889
890 case 'b':
891 case 'B': modifier_list |= FILE_FLAG4; break;
892
893 case 'd':
894 case 'D': modifier_list |= DIRECTORY_FLAG2; break;
895
896 case 'f':
897 case 'F': modifier_list |= FILE_FLAG4 | SUFFIX_FLAG1; break;
898
899 case 'e':
900 case 'E': modifier_list |= SUFFIX_FLAG1; break;
901
902 case 'l':
903 case 'L': modifier_list |= TOLOWER_FLAG8; break;
904
905 case 'i':
906 case 'I': modifier_list |= INFNAME_FLAG32; break;
907
908 case 'u':
909 case 'U': modifier_list |= TOUPPER_FLAG16; break;
910
911 case 'm':
912 case 'M':
913 if( modifier_list || ( (*s != edelim) && (*s != ':') ) ) {
914 Warning( "Map escape modifier must appear alone, ignored");
915 modifier_list = 0;
916 }
917 else {
918 /* map the escape codes in the separator string first */
919 for(p=result; (p = strchr(p,ESCAPE_CHAR*Escape_char)) != NIL(char)((char*)((void*)0)); p++)
920 Map_esc( p );
921 }
922 /* find the end of the macro spec, or the start of a new
923 * modifier list for further processing of the result */
924
925 for( ; (*s != edelim) && (*s != ':') && *s; s++ );
926 if( !*s )
927 Fatal( "Syntax error in macro. [$%s].\n", start );
928 if( *s == ':' ) s++;
929 break;
930
931 case 'n':
932 case 'N': modifier_list |= NORMPATH_FLAG128; break;
933
934 case 'S':
935 case 's':
936 if( modifier_list ) {
937 Warning( "Edit modifier must appear alone, ignored");
938 modifier_list = 0;
939 }
940 else {
941 separator = *s++;
942 for( p=s; *p != separator && *p; p++ );
943
944 if( !*p )
945 Fatal( "Syntax error in subst macro. [$%s].\n", start );
946 else {
947 char *t1, *t2;
948 pat1 = DmSubStr( s, p );
949 for(s=p=p+1; (*p != separator) && *p; p++ );
950 /* Before the parsing fixes in iz36027 the :s macro modifier
951 * erroneously worked with patterns with missing pattern
952 * separator, i.e. $(XXX:s#pat#sub). This is an error because
953 * it prohibits the use of following macro modifiers.
954 * I.e. $(XXX:s#pat#sub:u) falsely replaces with "sub:u".
955 * ??? Remove this special case once OOo compiles without
956 * any of this warnings. */
957 if( !*p ) {
958 if( *(p-1) == edelim ) {
959 p--;
960 Warning( "Syntax error in subst macro. Bracket found, but third delimiter [%c] missing in [$%s].\n", separator, start );
961 }
962 else {
963 Fatal( "Syntax error in subst macro. Third delimiter [%c] missing in [$%s].\n", separator, start );
964 }
965 }
966 pat2 = DmSubStr( s, p );
967 t1 = Expand(pat1); FREE(pat1)free((char*)(pat1));
968 t2 = Expand(pat2); FREE(pat2)free((char*)(pat2));
969 result = Apply_edit( result, t1, t2, TRUE1, FALSE0 );
970 FREE( t1 )free((char*)(t1));
971 FREE( t2 )free((char*)(t2));
972 }
973 s = p;
974 }
975 /* find the end of the macro spec, or the start of a new
976 * modifier list for further processing of the result */
977
978 for( ; (*s != edelim) && (*s != ':') && *s; s++ );
979 if( !*s )
980 Fatal( "Syntax error in macro. [$%s].\n", start );
981 if( *s == ':' ) s++;
982 break;
983
984 case 'T':
985 case 't':
986 case '^':
987 case '+':
988 if( modifier_list ) {
989 Warning( "Tokenize modifier must appear alone, ignored");
990 modifier_list = 0;
991 }
992 else {
993 separator = *s++;
994
995 if( separator == '$' ) {
996 p = _scan_ballanced_parens(s,'\0');
997
998 if ( *p ) {
999 char *tmp;
1000 pat1 = Expand(tmp = DmSubStr(s-1,p));
1001 FREE(tmp)free((char*)(tmp));
1002 result = Tokenize(result, pat1, switch_char, TRUE1);
1003 FREE(pat1)free((char*)(pat1));
1004 }
1005 else {
1006 Warning( "Incomplete macro expression [%s]", s );
1007 }
1008 s = p;
1009 }
1010 else if ( separator == '\"' ) {
1011 /* we change the semantics to allow $(v:t")") */
1012 for (p = s; *p && *p != separator; p++)
1013 if (*p == '\\')
1014 if (p[1] == '\\' || p[1] == '"')
1015 p++;
1016
1017 if( *p == 0 )
1018 Fatal( "Unterminated separator string" );
1019 else {
1020 pat1 = DmSubStr( s, p );
1021 result = Tokenize( result, pat1, switch_char, TRUE1);
1022 FREE( pat1 )free((char*)(pat1));
1023 }
1024 s = p;
1025 }
1026 else {
1027 Warning(
1028 "Separator must be a quoted string or macro expression");
1029 }
1030
1031 /* find the end of the macro spec, or the start of a new
1032 * modifier list for further processing of the result */
1033
1034 for( ; (*s != edelim) && (*s != ':'); s++ );
1035 if( *s == ':' ) s++;
1036 }
1037 break;
1038
1039 case ':':
1040 if( modifier_list ) {
1041 result = Apply_modifiers( modifier_list, result );
1042 modifier_list = 0;
1043 }
1044 break;
1045
1046 default:
1047 Warning( "Illegal modifier in macro, ignored" );
1048 break;
1049 }
1050 }
1051
1052 if( modifier_list ) /* apply modifier */
1053 result = Apply_modifiers( modifier_list, result );
1054
1055 s++;
1056 }
1057
1058 *ps = s;
1059 FREE( macro_name )free((char*)(macro_name));
1060 DB_RETURN( result )return (result);
1061}
1062
1063
1064static char*
1065_scan_brace( s, ps, flag )/*
1066============================
1067 This routine scans for { token_list } pairs. It expands the value of
1068 token_list by calling Expand on it. Token_list may be anything at all.
1069 Note that the routine count's ballanced parentheses. This means you
1070 cannot have something like { fred { joe }, if that is what you really
1071 need the write it as { fred {{ joe }, flag is set to 1 if all ok
1072 and to 0 if the braces were unballanced. */
1073
1074char *s;
1075char **ps;
1076int *flag;
1077{
1078 char *t;
1079 char *start;
1080 char *res;
1081 int lev = 1;
1082 int done = 0;
1083
1084 DB_ENTER( "_scan_brace" );
1085
1086 start = s;
1087 while( !done )
1088 switch( *s++ ) {
1089 case '{':
1090 if( *s == '{' ) break; /* ignore {{ */
1091 lev++;
1092 break;
1093
1094 case '}':
1095 if( *s == '}' ) break; /* ignore }} */
1096 if( lev )
1097 if( --lev == 0 ) done = TRUE1;
1098 break;
1099
1100 case '$':
1101 if( *s == '{' || *s == '}' ) {
1102 if( (t = strchr(s,'}')) != NIL(char)((char*)((void*)0)) )
1103 s = t;
1104 s++;
1105 }
1106 break;
1107
1108 case '\0':
1109 if( lev ) {
1110 done = TRUE1;
1111 s--;
1112 /* error malformed macro expansion */
1113 }
1114 break;
1115 }
1116
1117 start = DmSubStr( start, (lev) ? s : s-1 );
1118
1119 if( lev ) {
1120 /* Braces were not ballanced so just return the string.
1121 * Do not expand it. */
1122
1123 res = DmStrJoin( "{", start, -1, FALSE0 );
1124 *flag = 0;
1125 }
1126 else {
1127 *flag = 1;
1128 res = Expand( start );
1129
1130 if( (t = DmStrSpn( res, " \t" )) != res ) {
1131 size_t len = strlen(t)+1;
1132 memmove( res, t, len );
1133 }
1134 }
1135
1136 FREE( start )free((char*)(start)); /* this is ok! start is assigned a DmSubStr above */
1137 *ps = s;
1138
1139 DB_RETURN( res )return (res);
1140}
1141
1142
1143static char*
1144_cross_prod( x, y )/*
1145=====================
1146 Given two strings x and y compute the cross-product of the tokens found
1147 in each string. ie. if x = "a b" and y = "c d" return "ac ad bc bd".
1148
1149 NOTE: buf will continue to grow until it is big enough to handle
1150 all cross product requests. It is never freed! (maybe I
1151 will fix this someday) */
1152
1153char *x;
1154char *y;
1155{
1156 static char *buf = NULL((void*)0);
1
Variable 'buf' initialized to a null pointer value
1157 static int buf_siz = 0;
1158 char *brkx;
1159 char *brky;
1160 char *cy;
1161 char *cx;
1162 char *res;
1163 int i;
1164
1165 if( *x && *y ) {
2
Taking true branch
1166 res = DmStrDup( "" ); cx = x;
1167 while( *cx ) {
3
Loop condition is true. Entering loop body
1168 cy = y;
1169 brkx = DmStrPbrk( cx, " \t\n" );
1170 if( (brkx-cx == 2) && *cx == '\"' && *(cx+1) == '\"' ) cx = brkx;
1171
1172 while( *cy ) {
4
Loop condition is true. Entering loop body
1173 brky = DmStrPbrk( cy, " \t\n" );
1174 if( (brky-cy == 2) && *cy == '\"' && *(cy+1) == '\"' ) cy = brky;
1175 i = brkx-cx + brky-cy + 2;
1176
1177 if( i > buf_siz ) { /* grow buf to the correct size */
5
Taking false branch
1178 if( buf != NIL(char)((char*)((void*)0)) ) FREE( buf )free((char*)(buf));
1179 if( (buf = MALLOC( i, char )(char*) malloc((unsigned int)(i)*(size_t)sizeof(char))) == NIL(char)((char*)((void*)0))) No_ram();
1180 buf_siz = i;
1181 }
1182
1183 strncpy( buf, cx, (i = brkx-cx) );
6
Null pointer passed as an argument to a 'nonnull' parameter
1184 buf[i] = '\0';
1185 if (brky-cy > 0) strncat( buf, cy, brky-cy );
1186 buf[i+(brky-cy)] = '\0';
1187 strcat( buf, " " );
1188 res = DmStrJoin( res, buf, -1, TRUE1 );
1189 cy = DmStrSpn( brky, " \t\n" );
1190 }
1191 cx = DmStrSpn( brkx, " \t\n" );
1192 }
1193
1194 FREE( x )free((char*)(x));
1195 res[ strlen(res)-1 ] = '\0';
1196 }
1197 else
1198 res = DmStrJoin( x, y, -1, TRUE1 );
1199
1200 FREE( y )free((char*)(y));
1201 return( res );
1202}