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