update dev300-m58
[ooovba.git] / dmake / expand.c
bloba2aaa8b7ecb1fde9b8d1ec2fe9d936ad9dc494f3
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 switch( tok[1] ) {
234 case 'a' : *tok = 0x07; break;
235 case 'b' : *tok = '\b'; break;
236 case 'f' : *tok = '\f'; break;
237 case 'n' : *tok = '\n'; break;
238 case 'r' : *tok = '\r'; break;
239 case 't' : *tok = '\t'; break;
240 case 'v' : *tok = 0x0b; break;
241 case '\\': *tok = '\\'; break;
242 case '\"': *tok = '\"'; break;
244 default: {
245 register int i = 0;
246 register int j = 0;
247 for( ; i<2 && isdigit(tok[2]); i++ ) {
248 j = (j << 3) + (tok[1] - '0');
249 strcpy( tok+1, tok+2 );
251 j = (j << 3) + (tok[1] - '0');
252 *tok = j;
255 strcpy( tok+1, tok+2 );
260 PUBLIC char*
261 Apply_modifiers( mod, src )/*
262 =============================
263 This routine applies the appropriate modifiers to the string src
264 and returns the proper result string */
266 int mod;
267 char *src;
269 char *s;
270 char *e;
271 char *res;
272 TKSTR str;
274 DB_ENTER( "Apply_modifiers" );
276 if ( mod & INFNAME_FLAG ) {
277 SET_TOKEN( &str, src );
278 e = NIL(char);
280 while( *(s = Get_token( &str, "", FALSE )) != '\0' ) {
281 HASHPTR hp;
283 if ( (hp = Get_name(normalize_path(s), Defs, FALSE)) != NIL(HASH)
284 && hp->CP_OWNR
285 && hp->CP_OWNR->ce_fname
287 res = hp->CP_OWNR->ce_fname;
289 else
290 res = s;
292 if(str.tk_quote == 0) {
293 /* Add leading quote. */
294 e = DmStrApp(e, "\"");
295 e = DmStrJoin(e, res, -1, TRUE);
296 /* Append the trailing quote. */
297 e = DmStrJoin(e, "\"", 1, TRUE);
298 } else {
299 e = DmStrApp(e, res);
304 FREE(src);
305 src = e;
306 mod &= ~INFNAME_FLAG;
309 if ( mod & NORMPATH_FLAG ) {
310 e = exec_normpath(src);
312 FREE(src);
313 src = e;
314 mod &= ~NORMPATH_FLAG;
317 if(mod & (TOLOWER_FLAG|TOUPPER_FLAG) ) {
318 int lower;
319 lower = mod & TOLOWER_FLAG;
321 for (s=src; *s; s++)
322 if ( isalpha(*s) )
323 *s = ((lower) ? tolower(*s) : toupper(*s));
325 mod &= ~(TOLOWER_FLAG|TOUPPER_FLAG);
328 if (mod & JUST_FIRST_FLAG) {
329 SET_TOKEN(&str, src);
330 if ((s = Get_token(&str,"",FALSE)) != '\0') {
331 /* Recycle the quote at the beginning. */
332 if(str.tk_quote == 0) {
333 s--;
335 e = DmStrDup(s);
336 /* Add trailing quote. */
337 if(str.tk_quote == 0) {
338 e = DmStrJoin(e, "\"", 1, TRUE);
341 CLEAR_TOKEN(&str);
342 FREE(src);
343 src = e;
345 else {
346 CLEAR_TOKEN(&str);
348 mod &= ~JUST_FIRST_FLAG;
351 if( !mod || mod == (SUFFIX_FLAG | DIRECTORY_FLAG | FILE_FLAG) )
352 DB_RETURN( src );
354 SET_TOKEN( &str, src );
355 DB_PRINT( "mod", ("Source string [%s]", src) );
356 res = DmStrDup("");
358 while( *(s = Get_token( &str, "", FALSE )) != '\0' ) {
359 char *tokstart = s;
361 /* search for the directory portion of the filename. If the
362 * DIRECTORY_FLAG is set, then we want to keep the directory portion
363 * othewise throw it away and blank out to the end of the token */
365 if( (e = Basename(s)) != s) {
366 if( !(mod & DIRECTORY_FLAG) ) {
367 /* Move the basename to the start. */
368 strcpy(s, e);
370 else
371 s = e;
373 /* s now points to the start of the basename. */
376 /* search for the suffix, if there is none, treat it as a NULL suffix.
377 * if no file name treat it as a NULL file name. same copy op as
378 * for directory case above */
380 e = strrchr( s, '.' ); /* NULL suffix if e=0 */
381 if( e == NIL(char) ) e = s+strlen(s);
383 if( !(mod & FILE_FLAG) ) {
384 /* Move the suffix to the start. */
385 strcpy( s, e );
387 else
388 s = e;
390 /* s now points to the start of the suffix. */
393 /* The last and final part. This is the suffix case, if we don't want
394 * it then just erase it. */
396 if( s != NIL(char) )
397 if( !(mod & SUFFIX_FLAG) && s != str.tk_str )
398 *s = '\0';
401 /* only keep non-empty tokens. (This also discards empty quoted ""
402 * tokens.) */
403 if( strlen(tokstart) ) {
404 /* Recycle the quote at the beginning. */
405 if(str.tk_quote == 0) {
406 tokstart--;
408 res = DmStrApp(res, tokstart);
409 /* Add trailing quote. */
410 if(str.tk_quote == 0) {
411 res = DmStrJoin(res, "\"", 1, TRUE);
416 FREE(src);
417 src = res;
420 DB_PRINT( "mod", ("Result string [%s]", src) );
421 DB_RETURN( src );
425 PUBLIC char*
426 Tokenize( src, separator, op, mapesc )/*
427 ========================================
428 Tokenize the input of src and join each token found together with
429 the next token separated by the separator string.
431 When doing the tokenization, <sp>, <tab>, <nl>, and \<nl> all
432 constitute white space. */
434 char *src;
435 char *separator;
436 char op;
437 int mapesc;
439 TKSTR tokens;
440 char *tok;
441 char *res;
442 int first = (op == 't' || op == 'T');
444 DB_ENTER( "Tokenize" );
446 /* map the escape codes in the separator string first */
447 if ( mapesc )
448 for(tok=separator; (tok = strchr(tok,ESCAPE_CHAR)) != NIL(char); tok++)
449 Map_esc( tok );
451 DB_PRINT( "exp", ("Separator [%s]", separator) );
453 /* By default we return an empty string */
454 res = DmStrDup( "" );
456 /* Build the token list */
457 SET_TOKEN( &tokens, src );
458 while( *(tok = Get_token( &tokens, "", FALSE )) != '\0' ) {
459 char *x;
461 if( first ) {
462 FREE( res );
463 res = DmStrDup( tok );
464 first = FALSE;
466 else if (op == '^') {
467 res = DmStrAdd(res, DmStrJoin(separator, tok, -1, FALSE), TRUE);
469 else if (op == '+') {
470 res = DmStrAdd(res, DmStrJoin(tok, separator, -1, FALSE), TRUE);
472 else {
473 res = DmStrJoin(res, x =DmStrJoin(separator, tok, -1, FALSE),
474 -1, TRUE);
475 FREE( x );
478 DB_PRINT( "exp", ("Tokenizing [%s] --> [%s]", tok, res) );
481 FREE( src );
482 DB_RETURN( res );
486 static char*
487 _scan_ballanced_parens(p, delim)
488 char *p;
489 char delim;
491 int pcount = 0;
492 int bcount = 0;
494 if ( p ) {
495 do {
496 if (delim)
497 if( !(bcount || pcount) && *p == delim) {
498 return(p);
501 if ( *p == '(' ) pcount++;
502 else if ( *p == '{' ) bcount++;
503 else if ( *p == ')' && pcount ) pcount--;
504 else if ( *p == '}' && bcount ) bcount--;
506 p++;
508 while (*p && (pcount || bcount || delim));
511 return(p);
515 PUBLIC char*
516 ScanToken( s, ps, doexpand )/*
517 ==============================
518 This routine scans the token characters one at a time and identifies
519 macros starting with $( and ${ and calls _scan_macro to expand their
520 value. the string1{ token_list }string2 expansion is also handled.
521 In this case a temporary result is maintained so that we can take it's
522 cross product with any other token_lists that may possibly appear. */
524 char *s; /* pointer to start of src string */
525 char **ps; /* pointer to start pointer */
526 int doexpand;
528 char *res; /* pointer to result */
529 char *start; /* pointer to start of prefix */
530 int crossproduct = 0; /* if 1 then computing X-prod */
532 start = s;
533 res = DmStrDup( "" );
534 while( 1 ) {
535 switch( *s ) {
536 /* Termination, We halt at seeing a space or a tab or end of string.
537 * We return the value of the result with any new macro's we scanned
538 * or if we were computing cross_products then we return the new
539 * cross_product.
540 * NOTE: Once we start computing cross products it is impossible to
541 * stop. ie. the semantics are such that once a {} pair is
542 * seen we compute cross products until termination. */
544 case ' ':
545 case '\t':
546 case '\n':
547 case '\r':
548 case '\0':
550 char *tmp;
552 *ps = s;
553 if( !crossproduct )
554 tmp = DmStrJoin( res, start, (s-start), TRUE );
555 else
557 tmp = DmSubStr( start, s );
558 tmp = _cross_prod( res, tmp );
560 return( tmp );
563 case '$':
564 case '{':
566 /* Handle if it's a macro or if it's a {} construct.
567 * The results of a macro expansion are handled differently based
568 * on whether we have seen a {} beforehand. */
570 char *tmp;
571 tmp = DmSubStr( start, s ); /* save the prefix */
573 if( *s == '$' ) {
574 start = _scan_macro( s+1, &s, doexpand );
576 if( crossproduct ) {
577 res = _cross_prod( res, DmStrJoin( tmp, start, -1, TRUE ) );
579 else {
580 res = DmStrJoin(res,tmp=DmStrJoin(tmp,start,-1,TRUE),-1,TRUE);
581 FREE( tmp );
583 FREE( start );
585 else if( strchr("{ \t",s[1]) == NIL(char) ){
586 int ok;
587 start = _scan_brace( s+1, &s, &ok );
589 if( ok ) {
590 if ( crossproduct ) {
591 res = _cross_prod(res,_cross_prod(tmp,start));
593 else {
594 char *freeres;
595 res = Tokenize(start,
596 freeres=DmStrJoin(res,tmp,-1,TRUE),
597 '^', FALSE);
598 FREE(freeres);
599 FREE(tmp);
601 crossproduct = TRUE;
603 else {
604 res =DmStrJoin(res,tmp=DmStrJoin(tmp,start,-1,TRUE),-1,TRUE);
605 FREE( start );
606 FREE( tmp );
609 else { /* handle the {{ case */
610 res = DmStrJoin( res, start, (s-start+1), TRUE );
611 s += (s[1]=='{')?2:1;
612 FREE( tmp );
615 start = s;
617 break;
619 case '}':
620 if( s[1] != '}' ) {
621 /* error malformed macro expansion */
622 s++;
624 else { /* handle the }} case */
625 res = DmStrJoin( res, start, (s-start+1), TRUE );
626 s += 2;
627 start = s;
629 break;
631 default: s++;
637 static char*
638 _scan_macro( s, ps, doexpand )/*
639 ================================
640 This routine scans a macro use and expands it to the value. It
641 returns the macro's expanded value and modifies the pointer into the
642 src string to point at the first character after the macro use.
643 The types of uses recognized are:
645 $$ and $<sp> - expands to $
646 $(name) - expands to value of name
647 ${name} - same as above
648 $($(name)) - recurses on macro names (any level)
650 $(func[,args ...] [data])
651 and
652 $(name:modifier_list:modifier_list:...)
654 see comment for Expand for description of valid modifiers.
656 NOTE that once a macro name bounded by ( or { is found only
657 the appropriate terminator (ie. ( or } is searched for. */
659 char *s; /* pointer to start of src string */
660 char **ps; /* pointer to start pointer */
661 int doexpand; /* If TRUE enables macro expansion */
663 char sdelim; /* start of macro delimiter */
664 char edelim; /* corresponding end macro delim */
665 char *start; /* start of prefix */
666 char *macro_name; /* temporary macro name */
667 char *recurse_name; /* recursive macro name */
668 char *result; /* result for macro expansion */
669 int bflag = 0; /* brace flag, ==0 => $A type macro */
670 int done = 0; /* != 0 => done macro search */
671 int lev = 0; /* brace level */
672 int mflag = 0; /* != 0 => modifiers present in mac */
673 int fflag = 0; /* != 0 => GNU style function */
674 HASHPTR hp; /* hash table pointer for macros */
676 DB_ENTER( "_scan_macro" );
678 /* Check for $ at end of line, or $ followed by white space */
679 /* FIXME: Shouldn't a single '$' be an error? */
680 if( !*s || strchr(" \t", *s) != NIL(char)) {
681 *ps = s;
682 DB_RETURN( DmStrDup("") );
685 if( *s == '$' ) { /* Take care of the simple $$ case. */
686 *ps = s+1;
687 DB_RETURN( DmStrDup("$") );
690 sdelim = *s; /* set and remember start/end delim */
691 if( sdelim == '(' )
692 edelim = ')';
693 else
694 edelim = '}';
696 start = s; /* build up macro name, find its end */
697 while( !done ) {
698 switch( *s ) {
699 case '(': /* open macro brace */
700 case '{':
701 if( *s == sdelim ) {
702 lev++;
703 bflag++;
705 break;
707 case ':': /* halt at modifier */
708 if( lev == 1 && !fflag && doexpand ) {
709 done = TRUE;
710 mflag = 1;
712 else if( !lev ) /* must be $: */
713 Fatal( "Syntax error in macro [$%s]. A colon [:] cannot be a macro name.\n", start );
715 /* continue if a colon is found but lev > 1 */
716 break;
718 case '\n': /* Not possible because of the
719 * following case. */
720 Fatal( "DEBUG: No standalone '\n' [%s].\n", start );
721 break;
723 case '\\': /* Transform \<nl> -> ' '. */
724 if( s[1] != '\n' ) {
725 done = !lev;
726 break;
727 } else {
728 s[1] = ' ';
729 strcpy( s, s+1 );
731 /*FALLTHRU*/
732 case ' ':
733 case '\t':
734 if ( lev == 1 ) fflag = 1;
735 break;
737 case '\0': /* check for null */
738 *ps = s;
739 done = TRUE;
740 if( lev ) { /* catch $( or ${ without closing bracket */
741 Fatal( "Syntax error in macro [$%s]. The closing bracket [%c] is missing.\n", start, edelim );
742 } else
743 Fatal( "DEBUG: This cannot occur! [%s].\n", start );
744 break;
746 case ')': /* close macro brace */
747 case '}':
748 if( !lev ) /* A closing bracket without an .. */
749 Fatal("Syntax error in macro [$%s]. Closing bracket [%c] cannot be a macro name.\n", start, *s );
750 else if( *s == edelim ) --lev;
751 /*FALLTHRU*/
753 default: /* Done when lev == 0. This means either no */
754 done = !lev; /* opening bracket (single letter macro) or */
755 /* a fully enclosed $(..) or ${..} macro */
756 /* was found. */
758 s++;
761 /* Check if this is a $A type macro. If so then we have to
762 * handle it a little differently. */
763 if( bflag )
764 macro_name = DmSubStr( start+1, s-1 );
765 else
766 macro_name = DmSubStr( start, s );
768 /* If we don't have to expand the macro we're done. */
769 if (!doexpand) {
770 *ps = s;
771 DB_RETURN(macro_name);
774 /* Check to see if the macro name contains spaces, if so then treat it
775 * as a GNU style function invocation and call the function mapper to
776 * deal with it. We do not call the function expander if the function
777 * invocation begins with a '$' */
778 if( fflag && *macro_name != '$' ) {
779 result = Exec_function(macro_name);
781 else {
782 /* Check if the macro is a recursive macro name, if so then
783 * EXPAND the name before expanding the value */
784 if( strchr( macro_name, '$' ) != NIL(char) ) {
785 recurse_name = Expand( macro_name );
786 FREE( macro_name );
787 macro_name = recurse_name;
790 /* Code to do value expansion goes here, NOTE: macros whose assign bit
791 is one have been evaluated and assigned, they contain no further
792 expansions and thus do not need their values expanded again. */
794 if( (hp = GET_MACRO( macro_name )) != NIL(HASH) ) {
795 if( hp->ht_flag & M_MARK )
796 Fatal( "Detected circular macro [%s]", hp->ht_name );
798 if( !(hp->ht_flag & M_EXPANDED) ) {
799 hp->ht_flag |= M_MARK;
800 result = Expand( hp->ht_value );
801 hp->ht_flag ^= M_MARK;
803 else if( hp->ht_value != NIL(char) )
804 result = DmStrDup( hp->ht_value );
805 else
806 result = DmStrDup( "" );
809 else {
810 /* The use of an undefined macro implicitly defines it but
811 * leaves its value to NIL(char). */
812 hp = Def_macro( macro_name, NIL(char), M_EXPANDED );
813 /* Setting M_INIT assures that this macro is treated unset like
814 * default internal macros. (Necessary for *= and *:=) */
815 hp->ht_flag |= M_INIT;
817 result = DmStrDup( "" );
819 /* Mark macros as used only if we are not expanding them for
820 * the purpose of a .IF test, so we can warn about redef after use*/
821 if( !If_expand ) hp->ht_flag |= M_USED;
825 if( mflag ) {
826 char separator;
827 int modifier_list = 0;
828 int aug_mod = FALSE;
829 char *pat1;
830 char *pat2;
831 char *p;
833 /* We are inside of a macro expansion. The "build up macro name,
834 * find its while loop above should have caught all \<nl> and
835 * converted them to a real space. Let's verify this. */
836 for( p=s; *p && *p != edelim && *p; p++ ) {
837 if( p[0] == '\\' && p[1] == '\n' ) {
838 p[1] = ' ';
839 strcpy( p, p+1 );
842 if( !*p )
843 Fatal( "Syntax error in macro modifier pattern [$%s]. The closing bracket [%c] is missing.\n", start, edelim );
845 /* Yet another brain damaged AUGMAKE kludge. We should accept the
846 * AUGMAKE bullshit of $(f:pat=sub) form of macro expansion. In
847 * order to do this we will forgo the normal processing if the
848 * AUGMAKE solution pans out, otherwise we will try to process the
849 * modifiers ala dmake.
851 * So we look for = in modifier string.
852 * If found we process it and not do the normal stuff */
854 for( p=s; *p && *p != '=' && *p != edelim; p++ );
856 if( *p == '=' ) {
857 char *tmp;
859 pat1 = Expand(tmp = DmSubStr(s,p)); FREE(tmp);
860 s = p+1;
861 p = _scan_ballanced_parens(s+1, edelim);
863 if ( !*p ) {
864 Fatal( "Incomplete macro expression [%s]", s );
865 p = s+1;
867 pat2 = Expand(tmp = DmSubStr(s,p)); FREE(tmp);
869 result = Apply_edit( result, pat1, pat2, TRUE, TRUE );
870 FREE( pat1 );
871 FREE( pat2 );
872 s = p;
873 aug_mod = TRUE;
876 if( !aug_mod )
877 while( *s && *s != edelim ) { /* while not at end of macro */
878 char switch_char;
880 switch( switch_char = *s++ ) {
881 case '1': modifier_list |= JUST_FIRST_FLAG; break;
883 case 'b':
884 case 'B': modifier_list |= FILE_FLAG; break;
886 case 'd':
887 case 'D': modifier_list |= DIRECTORY_FLAG; break;
889 case 'f':
890 case 'F': modifier_list |= FILE_FLAG | SUFFIX_FLAG; break;
892 case 'e':
893 case 'E': modifier_list |= SUFFIX_FLAG; break;
895 case 'l':
896 case 'L': modifier_list |= TOLOWER_FLAG; break;
898 case 'i':
899 case 'I': modifier_list |= INFNAME_FLAG; break;
901 case 'u':
902 case 'U': modifier_list |= TOUPPER_FLAG; break;
904 case 'm':
905 case 'M':
906 if( modifier_list || ( (*s != edelim) && (*s != ':') ) ) {
907 Warning( "Map escape modifier must appear alone, ignored");
908 modifier_list = 0;
910 else {
911 /* map the escape codes in the separator string first */
912 for(p=result; (p = strchr(p,ESCAPE_CHAR)) != NIL(char); p++)
913 Map_esc( p );
915 /* find the end of the macro spec, or the start of a new
916 * modifier list for further processing of the result */
918 for( ; (*s != edelim) && (*s != ':') && *s; s++ );
919 if( !*s )
920 Fatal( "Syntax error in macro. [$%s].\n", start );
921 if( *s == ':' ) s++;
922 break;
924 case 'n':
925 case 'N': modifier_list |= NORMPATH_FLAG; break;
927 case 'S':
928 case 's':
929 if( modifier_list ) {
930 Warning( "Edit modifier must appear alone, ignored");
931 modifier_list = 0;
933 else {
934 separator = *s++;
935 for( p=s; *p != separator && *p; p++ );
937 if( !*p )
938 Fatal( "Syntax error in subst macro. [$%s].\n", start );
939 else {
940 char *t1, *t2;
941 pat1 = DmSubStr( s, p );
942 for(s=p=p+1; (*p != separator) && *p; p++ );
943 /* Before the parsing fixes in iz36027 the :s macro modifier
944 * erroneously worked with patterns with missing pattern
945 * separator, i.e. $(XXX:s#pat#sub). This is an error because
946 * it prohibits the use of following macro modifiers.
947 * I.e. $(XXX:s#pat#sub:u) falsely replaces with "sub:u".
948 * ??? Remove this special case once OOo compiles without
949 * any of this warnings. */
950 if( !*p ) {
951 if( *(p-1) == edelim ) {
952 p--;
953 Warning( "Syntax error in subst macro. Bracket found, but third delimiter [%c] missing in [$%s].\n", separator, start );
955 else {
956 Fatal( "Syntax error in subst macro. Third delimiter [%c] missing in [$%s].\n", separator, start );
959 pat2 = DmSubStr( s, p );
960 t1 = Expand(pat1); FREE(pat1);
961 t2 = Expand(pat2); FREE(pat2);
962 result = Apply_edit( result, t1, t2, TRUE, FALSE );
963 FREE( t1 );
964 FREE( t2 );
966 s = p;
968 /* find the end of the macro spec, or the start of a new
969 * modifier list for further processing of the result */
971 for( ; (*s != edelim) && (*s != ':') && *s; s++ );
972 if( !*s )
973 Fatal( "Syntax error in macro. [$%s].\n", start );
974 if( *s == ':' ) s++;
975 break;
977 case 'T':
978 case 't':
979 case '^':
980 case '+':
981 if( modifier_list ) {
982 Warning( "Tokenize modifier must appear alone, ignored");
983 modifier_list = 0;
985 else {
986 separator = *s++;
988 if( separator == '$' ) {
989 p = _scan_ballanced_parens(s,'\0');
991 if ( *p ) {
992 char *tmp;
993 pat1 = Expand(tmp = DmSubStr(s-1,p));
994 FREE(tmp);
995 result = Tokenize(result, pat1, switch_char, TRUE);
996 FREE(pat1);
998 else {
999 Warning( "Incomplete macro expression [%s]", s );
1001 s = p;
1003 else if ( separator == '\"' ) {
1004 /* we change the semantics to allow $(v:t")") */
1005 for (p = s; *p && *p != separator; p++)
1006 if (*p == '\\')
1007 if (p[1] == '\\' || p[1] == '"')
1008 p++;
1010 if( *p == 0 )
1011 Fatal( "Unterminated separator string" );
1012 else {
1013 pat1 = DmSubStr( s, p );
1014 result = Tokenize( result, pat1, switch_char, TRUE);
1015 FREE( pat1 );
1017 s = p;
1019 else {
1020 Warning(
1021 "Separator must be a quoted string or macro expression");
1024 /* find the end of the macro spec, or the start of a new
1025 * modifier list for further processing of the result */
1027 for( ; (*s != edelim) && (*s != ':'); s++ );
1028 if( *s == ':' ) s++;
1030 break;
1032 case ':':
1033 if( modifier_list ) {
1034 result = Apply_modifiers( modifier_list, result );
1035 modifier_list = 0;
1037 break;
1039 default:
1040 Warning( "Illegal modifier in macro, ignored" );
1041 break;
1045 if( modifier_list ) /* apply modifier */
1046 result = Apply_modifiers( modifier_list, result );
1048 s++;
1051 *ps = s;
1052 FREE( macro_name );
1053 DB_RETURN( result );
1057 static char*
1058 _scan_brace( s, ps, flag )/*
1059 ============================
1060 This routine scans for { token_list } pairs. It expands the value of
1061 token_list by calling Expand on it. Token_list may be anything at all.
1062 Note that the routine count's ballanced parentheses. This means you
1063 cannot have something like { fred { joe }, if that is what you really
1064 need the write it as { fred {{ joe }, flag is set to 1 if all ok
1065 and to 0 if the braces were unballanced. */
1067 char *s;
1068 char **ps;
1069 int *flag;
1071 char *t;
1072 char *start;
1073 char *res;
1074 int lev = 1;
1075 int done = 0;
1077 DB_ENTER( "_scan_brace" );
1079 start = s;
1080 while( !done )
1081 switch( *s++ ) {
1082 case '{':
1083 if( *s == '{' ) break; /* ignore {{ */
1084 lev++;
1085 break;
1087 case '}':
1088 if( *s == '}' ) break; /* ignore }} */
1089 if( lev )
1090 if( --lev == 0 ) done = TRUE;
1091 break;
1093 case '$':
1094 if( *s == '{' || *s == '}' ) {
1095 if( (t = strchr(s,'}')) != NIL(char) )
1096 s = t;
1097 s++;
1099 break;
1101 case '\0':
1102 if( lev ) {
1103 done = TRUE;
1104 s--;
1105 /* error malformed macro expansion */
1107 break;
1110 start = DmSubStr( start, (lev) ? s : s-1 );
1112 if( lev ) {
1113 /* Braces were not ballanced so just return the string.
1114 * Do not expand it. */
1116 res = DmStrJoin( "{", start, -1, FALSE );
1117 *flag = 0;
1119 else {
1120 *flag = 1;
1121 res = Expand( start );
1123 if( (t = DmStrSpn( res, " \t" )) != res ) strcpy( res, t );
1126 FREE( start ); /* this is ok! start is assigned a DmSubStr above */
1127 *ps = s;
1129 DB_RETURN( res );
1133 static char*
1134 _cross_prod( x, y )/*
1135 =====================
1136 Given two strings x and y compute the cross-product of the tokens found
1137 in each string. ie. if x = "a b" and y = "c d" return "ac ad bc bd".
1139 NOTE: buf will continue to grow until it is big enough to handle
1140 all cross product requests. It is never freed! (maybe I
1141 will fix this someday) */
1143 char *x;
1144 char *y;
1146 static char *buf = NULL;
1147 static int buf_siz = 0;
1148 char *brkx;
1149 char *brky;
1150 char *cy;
1151 char *cx;
1152 char *res;
1153 int i;
1155 if( *x && *y ) {
1156 res = DmStrDup( "" ); cx = x;
1157 while( *cx ) {
1158 cy = y;
1159 brkx = DmStrPbrk( cx, " \t\n" );
1160 if( (brkx-cx == 2) && *cx == '\"' && *(cx+1) == '\"' ) cx = brkx;
1162 while( *cy ) {
1163 brky = DmStrPbrk( cy, " \t\n" );
1164 if( (brky-cy == 2) && *cy == '\"' && *(cy+1) == '\"' ) cy = brky;
1165 i = brkx-cx + brky-cy + 2;
1167 if( i > buf_siz ) { /* grow buf to the correct size */
1168 if( buf != NIL(char) ) FREE( buf );
1169 if( (buf = MALLOC( i, char )) == NIL(char)) No_ram();
1170 buf_siz = i;
1173 strncpy( buf, cx, (i = brkx-cx) );
1174 buf[i] = '\0';
1175 if (brky-cy > 0) strncat( buf, cy, brky-cy );
1176 buf[i+(brky-cy)] = '\0';
1177 strcat( buf, " " );
1178 res = DmStrJoin( res, buf, -1, TRUE );
1179 cy = DmStrSpn( brky, " \t\n" );
1181 cx = DmStrSpn( brkx, " \t\n" );
1184 FREE( x );
1185 res[ strlen(res)-1 ] = '\0';
1187 else
1188 res = DmStrJoin( x, y, -1, TRUE );
1190 FREE( y );
1191 return( res );