dvadura@watdragon.waterloo.edu (Dennis Vadura) (10/15/90)
Posting-number: Volume 15, Issue 73 Submitted-by: Dennis Vadura <dvadura@watdragon.waterloo.edu> Archive-name: dmake-3.6/part21 #!/bin/sh # this is part 21 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file expand.c continued # CurArch=21 if test ! -r s2_seq_.tmp then echo "Please unpack part 1 first!" exit 1; fi ( read Scheck if test "$Scheck" != $CurArch then echo "Please unpack part $Scheck next!" exit 1; else exit 0; fi ) < s2_seq_.tmp || exit 1 echo "x - Continuing file expand.c" sed 's/^X//' << 'SHAR_EOF' >> expand.c X-- $($(A)) - represents macro indirection X-- <+...+> - get mapped to $(mktmp ...) X-- X-- following macro is recognized X-- X-- string1{ token_list }string2 X-- X-- and expands to string1 prepended to each element of token_list and X-- string2 appended to each of the resulting tokens from the first X-- operation. If string2 is of the form above then the result is X-- the cross product of the specified (possibly modified) token_lists. X-- X-- The folowing macro modifiers are defined and expanded: X-- X-- $(macro:modifier_list:modifier_list:...) X-- X-- where modifier_list a combination of: X-- X-- D or d - Directory portion of token including separator X-- F or f - File portion of token including suffix X-- B or b - basename portion of token not including suffix X-- T or t - for tokenization X-- X-- or a single X-- S or s - pattern substitution (simple) X-- X-- NOTE: Modifiers are applied once the macro value has been found. X-- Thus the construct $($(test):s/joe/mary/) is defined and X-- modifies the value of $($(test)) X-- X-- Also the construct $(m:d:f) is not the same as $(m:df) X-- the first applies d to the value of $(m) and then X-- applies f to the value of that whereas the second form X-- applies df to the value of $(m). X-- X-- AUTHOR X-- Dennis Vadura, dvadura@watdragon.uwaterloo.ca X-- CS DEPT, University of Waterloo, Waterloo, Ont., Canada X-- X-- COPYRIGHT X-- Copyright (c) 1990 by Dennis Vadura. All rights reserved. X-- X-- This program is free software; you can redistribute it and/or X-- modify it under the terms of the GNU General Public License X-- (version 1), as published by the Free Software Foundation, and X-- found in the file 'LICENSE' included with this distribution. X-- X-- This program is distributed in the hope that it will be useful, X-- but WITHOUT ANY WARRANTY; without even the implied warrant of X-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X-- GNU General Public License for more details. X-- X-- You should have received a copy of the GNU General Public License X-- along with this program; if not, write to the Free Software X-- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X-- X-- LOG X-- $Log: expand.c,v $ X * Revision 1.1 90/10/06 12:03:40 dvadura X * dmake Release, Version 3.6 X * X*/ X X#include <ctype.h> X#include "extern.h" X#include "alloc.h" X#include "db.h" X Xstatic char* _scan_token ANSI((char*, char**)); Xstatic char* _scan_macro ANSI((char*, char**)); Xstatic char* _scan_brace ANSI((char*, char**, int*)); Xstatic char* _cross_prod ANSI((char*, char*)); Xstatic char* _apply_modifiers ANSI((int, char*)); Xstatic char* _tokenize ANSI((char*, char*)); X X#define SUFFIX_FLAG 1 /* defines for macro modifier code */ X#define DIRECTORY_FLAG 2 X#define FILE_FLAG 4 X X Xchar * XExpand( src )/* X=============== X This is the driver routine for the expansion, it identifies non-white X space tokens and gets the _scan_token routine to figure out if they should X be treated in a special way. */ X Xchar *src; /* pointer to source string */ X{ X char *tmp; /* pointer to temporary str */ X char *res; /* pointer to result string */ X char *start; /* pointer to start of token */ X X DB_ENTER( "Expand" ); X DB_PRINT( "exp", ("Expanding [%s]", src) ); X X res = _strdup( "" ); X if( src == NIL(char) ) DB_RETURN( res ); X X while( *src ) { X char *ks, *ke; X X /* Here we find the next non white space token in the string X * and find it's end, with respect to non-significant white space. */ X X start = _strspn( src, " \t\n" ); X res = _strjoin( res, src, start-src, TRUE ); X if( !(*start) ) break; X X /* START <+...+> KLUDGE */ X if( (ks=_strstr(start,"<+")) && (ke=_strstr(ks,"+>")) ){ X char *t1, *t2; X X res = _strjoin( res, t2=Expand(t1=_substr(start,ks)), -1, TRUE); X FREE(t1); FREE(t2); X X t1 = _substr(ks+2, ke+1); t1[ke-ks-2] = ')'; X t2 = _strjoin( "$(mktmp ", t1, -1,FALSE); X FREE(t1); X res = _strjoin( res, t2=Expand(t2), -1, TRUE); X FREE(t2); X src = ke+2; X } X /* END <+...+> KLUDGE */ X else { X res = _strjoin( res, tmp = _scan_token( start, &src ), -1, TRUE ); X FREE( tmp ); X } X } X X DB_PRINT( "exp", ("Returning [%s]", res) ); X DB_RETURN( res ); X} X X X Xchar * XApply_edit( src, pat, subst, fr, anchor )/* X=========================================== X Take the src string and apply the pattern substitution. ie. look for X occurrences of pat in src and replace each occurrence with subst. This is X NOT a regular expressions pattern substitution, it's just not worth it. X X if anchor == TRUE then the src pattern match must be at the end of a token. X ie. this is for SYSV compatibility and is only used for substitutions of X the caused by $(macro:pat=sub). So if src = "fre.o.k june.o" then X $(src:.o=.a) results in "fre.o.k june.a", and $(src:s/.o/.a) results in X "fre.a.k june.a" */ X Xchar *src; /* the source string */ Xchar *pat; /* pattern to find */ Xchar *subst; /* substitute string */ Xint fr; /* if TRUE free src */ Xint anchor; /* if TRUE anchor */ X{ X char *res; X char *p; X char *s; X int l; X X DB_ENTER( "Apply_edit" ); X X if( !*pat ) DB_RETURN( src ); /* do nothing if pat is NULL */ X X DB_PRINT( "mod", ("Source str: [%s]", src) ); X DB_PRINT( "mod", ("Replacing [%s], with [%s]", pat, subst) ); X X s = src; X l = strlen( pat ); X if( (p = _strstr( s, pat )) != NIL(char) ) { X res = _strdup( "" ); X do { X if( anchor ) X if( !*(p+l) || (strchr(" \t", *(p+l)) != NIL(char)) ) X res = _strjoin( _strjoin( res, s, p-s, TRUE ), subst, -1, TRUE ); X else X res = _strjoin( res, s, p+l-s, TRUE ); X else X res = _strjoin( _strjoin( res, s, p-s, TRUE ), subst, -1, TRUE ); X X s = p + l; X } X while( (p = _strstr( s, pat )) != NIL(char) ); X X res = _strjoin( res, s, -1, TRUE ); X if( fr ) FREE( src ); X } X else X res = src; X X X DB_PRINT( "mod", ("Result [%s]", res) ); X DB_RETURN( res ); X} X X X Xvoid XMap_esc( tok )/* X================ X Map an escape sequence and replace it by it's corresponding character X value. It is assumed that tok points at the initial \, the esc X sequence in the original string is replaced and the value of tok X is not modified. */ Xchar *tok; X{ X if( strchr( "\"\\vantbrf01234567", tok[1] ) ) { X switch( tok[1] ) { X case 'a' : *tok = 0x07; break; X case 'b' : *tok = '\b'; break; X case 'f' : *tok = '\f'; break; X case 'n' : *tok = '\n'; break; X case 'r' : *tok = '\r'; break; X case 't' : *tok = '\t'; break; X case 'v' : *tok = 0x0b; break; X case '\\': *tok = '\\'; break; X case '\"': *tok = '\"'; break; X X default: { X register int i = 0; X register int j = 0; X for( ; i<2 && isdigit(tok[2]); i++ ) { X j = (j << 3) + (tok[1] - '0'); X strcpy( tok+1, tok+2 ); X } X j = (j << 3) + (tok[1] - '0'); X *tok = j; X } X } X strcpy( tok+1, tok+2 ); X } X} X X X Xstatic char* X_scan_token( s, ps )/* X====================== X This routine scans the token characters one at a time and identifies X macros starting with $( and ${ and calls _scan_macro to expand their X value. the string1{ token_list }string2 expansion is also handled. X In this case a temporary result is maintained so that we can take it's X cross product with any other token_lists that may possibly appear. */ X Xchar *s; /* pointer to start of src string */ Xchar **ps; /* pointer to start pointer */ X{ X char *res; /* pointer to result */ X char *start; /* pointer to start of prefix */ X int crossproduct = 0; /* if 1 then computing X-prod */ X X start = s; X res = _strdup( "" ); X while( 1 ) X switch( *s ) { X /* Termination, We halt at seeing a space or a tab or end of string. X * We return the value of the result with any new macro's we scanned X * or if we were computing cross_products then we return the new X * cross_product. X * NOTE: Once we start computing cross products it is impossible to X * stop. ie. the semantics are such that once a {} pair is X * seen we compute cross products until termination. */ X X case ' ': X case '\t': X case '\n': X case '\0': X { X char *tmp; X X *ps = s; X if( !crossproduct ) X tmp = _strjoin( res, start, (s-start), TRUE ); X else X { X tmp = _substr( start, s ); X tmp = _cross_prod( res, tmp ); X } X return( tmp ); X } X X case '$': X case '{': X { X /* Handle if it's a macro or if it's a {} construct. X * The results of a macro expansion are handled differently based X * on whether we have seen a {} beforehand. */ X X char *tmp; X tmp = _substr( start, s ); /* save the prefix */ X X if( *s == '$' ) { X start = _scan_macro( s+1, &s ); X X if( crossproduct ) X res = _cross_prod( res, _strjoin( tmp, start, -1, TRUE ) ); X else { X res = _strjoin(res,tmp = _strjoin(tmp,start,-1,TRUE),-1,TRUE); X FREE( tmp ); X } X FREE( start ); X } X else if( s[1] != '{' ) { X int ok; X start = _scan_brace( s+1, &s, &ok ); X X if( ok ) { X res = _cross_prod( res, _cross_prod(tmp, start) ); X crossproduct = TRUE; X } X else { X res =_strjoin(res,tmp=_strjoin(tmp,start,-1,TRUE),-1,TRUE); X FREE( start ); X FREE( tmp ); X } X } X else { /* handle the {{ case */ X res = _strjoin( res, start, (s-start+1), TRUE ); X s += 2; X FREE( tmp ); X } X X start = s; X } X break; X X case '}': X if( s[1] != '}' ) { X /* error malformed macro expansion */ X s++; X } X else { /* handle the }} case */ X res = _strjoin( res, start, (s-start+1), TRUE ); X s += 2; X start = s; X } X break; X X default: s++; X } X} X X X Xstatic char* X_scan_macro( s, ps )/* X====================== X This routine scans a macro use and expands it to the value. It X returns the macro's expanded value and modifies the pointer into the X src string to point at the first character after the macro use. X The types of uses recognized are: X X $$ - expands to $ X $(name) - expands to value of name X ${name} - same as above X $($(name)) - recurses on macro names (any level) X and X $(func[,args ...] [data]) X and X $(name:modifier_list:modifier_list:...) X X see comment for Expand for description of valid modifiers. X X NOTE that once a macro name bounded by ( or { is found only X the appropriate terminator (ie. ( or } is searched for. */ X Xchar *s; /* pointer to start of src string */ Xchar **ps; /* pointer to start pointer */ X{ X char sdelim; /* start of macro delimiter */ X char edelim; /* corresponding end macro delim */ X char *start; /* start of prefix */ X char *macro_name; /* temporary macro name */ X char *recurse_name; /* recursive macro name */ X char *result; /* result for macro expansion */ X int bflag = 0; /* brace flag, ==0 => $A type macro */ X int done = 0; /* != 0 => done macro search */ X int lev = 0; /* brace level */ X int mflag = 0; /* != 0 => modifiers present in mac */ X int fflag = 0; /* != 0 => GNU style function */ X HASHPTR hp; /* hash table pointer for macros */ X X DB_ENTER( "_scan_macro" ); X X /* Check for the simple $ at end of line case */ X if( !*s ) { X *ps = s; X DB_RETURN( _strdup("") ); X } X X if( *s == '$' ) { /* Take care of the simple $$ case. */ X *ps = s+1; X DB_RETURN( _strdup("$") ); X } X X sdelim = *s; /* set and remember start/end delim */ X if( sdelim == '(' ) X edelim = ')'; X else X edelim = '}'; X X start = s; /* build up macro name, find its end*/ X while( !done ) { X switch( *s ) { X case '(': /* open macro brace */ X case '{': X if( *s == sdelim ) { X lev++; X bflag++; X } X break; X X case ':': /* halt at modifier */ X if( lev == 1 ) { X done = TRUE; X mflag = 1; X } X break; X X case ' ': X case '\t': X case '\n': X fflag = 1; X break; X X case '\0': /* check for null */ X *ps = s; X if( lev ) { X done = TRUE; X bflag = 0; X s = start; X } X break; X X case ')': /* close macro brace */ X case '}': X if( *s == edelim && lev ) --lev; X /*FALLTHROUGH*/ X X default: X done = !lev; X } X s++; X } X X /* Check if this is a $A type macro. If so then we have to X * handle it a little differently. */ X if( bflag ) X macro_name = _substr( start+1, s-1 ); X else X macro_name = _substr( start, s ); X X /* Check to see if the macro name contains spaces, if so then treat it X * as a GNU style function invocation and call the function mapper to X * deal with it. */ X if( fflag ) X result = Exec_function(macro_name); X else { X /* Check if the macro is a recursive macro name, if so then X * EXPAND the name before expanding the value */ X if( strchr( macro_name, '$' ) != NIL(char) ) { X recurse_name = Expand( macro_name ); X FREE( macro_name ); X macro_name = recurse_name; X } X X /* Code to do value expansion goes here, NOTE: macros whose assign bit X is one have been evaluated and assigned, they contain no further X expansions and thus do not need their values expanded again. */ X X if( (hp = GET_MACRO( macro_name )) != NIL(HASH) ) { X if( hp->ht_flag & M_MARK ) X Fatal( "Detected circular macro [%s]", hp->ht_name ); X X /* for M_MULTI macro variable assignments */ X If_multi = hp->ht_flag & M_MULTI; X X if( !(hp->ht_flag & M_EXPANDED) ) { X hp->ht_flag |= M_MARK; X result = Expand( hp->ht_value ); X hp->ht_flag ^= M_MARK; X } X else if( hp->ht_value != NIL(char) ) X result = _strdup( hp->ht_value ); X else X result = _strdup( "" ); X X /* X * Mark macros as used only if we are not expanding them for X * the purpose of a .IF test, so we can warn about redef after use*/ X X if( !If_expand ) hp->ht_flag |= M_USED; X } X else X result = _strdup( "" ); X } X X if( mflag ) { X char separator; X int modifier_list = 0; X int aug_mod = FALSE; X char *pat1; X char *pat2; X char *p; X X /* Yet another brain damaged AUGMAKE kludge. We should accept the X * AUGMAKE bullshit of $(f:pat=sub) form of macro expansion. In X * order to do this we will forgo the normal processing if the X * AUGMAKE solution pans out, otherwise we will try to process the X * modifiers ala dmake. X * X * So we look for = in modifier string. X * If found we process it and not do the normal stuff */ X X for( p=s; *p && *p != '=' && *p != edelim; p++ ); X X if( *p == '=' ) { X pat1 = _substr( s, p ); X for( s=p=p+1; (*p != edelim); p++ ); X X pat2 = _substr( s, p ); X result = Apply_edit( result, pat1, pat2, TRUE, TRUE ); X FREE( pat1 ); X FREE( pat2 ); X s = p; X aug_mod = TRUE; X } X X if( !aug_mod ) X while( *s && *s != edelim ) { /* while not at end of macro */ X switch( *s++ ) { X case 'b': X case 'B': modifier_list |= FILE_FLAG; break; X X case 'd': X case 'D': modifier_list |= DIRECTORY_FLAG; break; X X case 'f': X case 'F': modifier_list |= FILE_FLAG | SUFFIX_FLAG; break; X X case 'S': X case 's': X if( modifier_list ) { X Warning( "Edit modifier must appear alone, ignored"); X modifier_list = 0; X } X else { X separator = *s++; X for( p=s; *p != separator && *p != edelim; p++ ); X X if( *p == edelim ) X Warning("Syntax error in edit pattern, ignored"); X else { X char *t1, *t2; X pat1 = _substr( s, p ); X for(s=p=p+1; (*p != separator) && (*p != edelim); p++ ); X pat2 = _substr( s, p ); X t1 = Expand(pat1); FREE(pat1); X t2 = Expand(pat2); FREE(pat2); X result = Apply_edit( result, t1, t2, TRUE, FALSE ); X FREE( t1 ); X FREE( t2 ); X } X s = p; X } X /* find the end of the macro spec, or the start of a new X * modifier list for further processing of the result */ X X for( ; (*s != edelim) && (*s != ':'); s++ ); X if( *s == ':' ) s++; X break; X X case 'T': X case 't': X if( modifier_list ) { X Warning( "Tokenize modifier must appear alone, ignored"); X modifier_list = 0; X } X else { X char *msg = "Separator string must be quoted"; X X separator = *s++; X X if( separator != '\"' ) X Warning( msg ); X else { X /* we change the semantics to allow $(v:t")") */ X for (p = s; *p && *p != separator; p++) X if (*p == '\\') X if (p[1] == '\\' || p[1] == '"') X p++; X if( *p == 0 ) X Fatal( "Unterminated separator string" ); X else { X pat1 = _substr( s, p ); X result = _tokenize( result, pat1 ); X FREE( pat1 ); X } X s = p; X } X X /* find the end of the macro spec, or the start of a new X * modifier list for further processing of the result */ X X for( ; (*s != edelim) && (*s != ':'); s++ ); X if( *s == ':' ) s++; X } X break; X X case ':': X if( modifier_list ) { X result = _apply_modifiers( modifier_list, result ); X modifier_list = 0; X } X break; X X default: X Warning( "Illegal modifier in macro, ignored" ); X break; X } X } X X if( modifier_list ) /* apply modifier */ X result = _apply_modifiers( modifier_list, result ); X X s++; X } X X *ps = s; X FREE( macro_name ); X DB_RETURN( result ); X} X X X Xstatic char* X_scan_brace( s, ps, flag )/* X============================ X This routine scans for { token_list } pairs. It expands the value of X token_list by calling Expand on it. Token_list may be anything at all. X Note that the routine count's ballanced parentheses. This means you X cannot have something like { fred { joe }, if that is what you really X need the write it as { fred {{ joe }, flag is set to 1 if all ok X and to 0 if the braces were unballanced. */ X Xchar *s; Xchar **ps; Xint *flag; X{ X char *t; X char *start; X char *res; X int lev = 1; X int done = 0; X X DB_ENTER( "_scan_brace" ); X X start = s; X while( !done ) X switch( *s++ ) { X case '{': X if( *s == '{' ) break; /* ignore {{ */ X lev++; X break; X X case '}': X if( *s == '}' ) break; /* ignore }} */ X if( lev ) X if( --lev == 0 ) done = TRUE; X break; X X case '$': X if( *s == '{' || *s == '}' ) { X if( (t = strchr(s,'}')) != NIL(char) ) X s = t; X s++; X } X break; X X case '\0': X if( lev ) { X done = TRUE; X s--; X /* error malformed macro expansion */ X } X break; X } X X start = _substr( start, (lev) ? s : s-1 ); X X if( lev ) { X /* Braces were not ballanced so just return the string. X * Do not expand it. */ X X res = _strjoin( "{", start, -1, FALSE ); X *flag = 0; X } X else { X *flag = 1; X res = Expand( start ); X X if( (t = _strspn( res, " \t" )) != res ) strcpy( res, t ); X } X X FREE( start ); /* this is ok! start is assigned a _substr above */ X *ps = s; X X DB_RETURN( res ); X} X X X Xstatic char* X_cross_prod( x, y )/* X===================== X Given two strings x and y compute the cross-product of the tokens found X in each string. ie. if x = "a b" and y = "c d" return "ac ad bc bd". X X NOTE: buf will continue to grow until it is big enough to handle X all cross product requests. It is never freed! (maybe I X will fix this someday) */ X Xchar *x; Xchar *y; X{ X static char *buf; X static int buf_siz = 0; X char *brkx; X char *brky; X char *cy; X char *cx; X char *res; X int i; X X if( *x && *y ) { X res = _strdup( "" ); cx = x; X while( *cx ) { X cy = y; X brkx = _strpbrk( cx, " \t\n" ); X if( (brkx-cx == 2) && *cx == '\"' && *(cx+1) == '\"' ) cx = brkx; X X while( *cy ) { X brky = _strpbrk( cy, " \t\n" ); X if( (brky-cy == 2) && *cy == '\"' && *(cy+1) == '\"' ) cy = brky; X i = brkx-cx + brky-cy + 2; X X if( i > buf_siz ) { /* grow buf to the correct size */ X if( buf != NIL(char) ) FREE( buf ); X if( (buf = MALLOC( i, char )) == NIL(char)) No_ram(); X buf_siz = i; X } X X strncpy( buf, cx, (i = brkx-cx) ); X buf[i] = '\0'; X if (brky-cy > 0) strncat( buf, cy, brky-cy ); X strcat( buf, " " ); X res = _strjoin( res, buf, -1, TRUE ); X cy = _strspn( brky, " \t\n" ); X } X cx = _strspn( brkx, " \t\n" ); X } X X FREE( x ); X res[ strlen(res)-1 ] = '\0'; X } X else X res = _strjoin( x, y, -1, TRUE ); X X FREE( y ); X return( res ); X} X X X Xstatic char* X_apply_modifiers( mod, src )/* X============================== X This routine applies the appropriate modifiers to the string src X and returns the proper result string */ X Xint mod; Xchar *src; X{ X char *s; X char *e; X TKSTR str; X X DB_ENTER( "_apply_modifiers" ); X X if( mod == (SUFFIX_FLAG | DIRECTORY_FLAG | FILE_FLAG) ) X DB_RETURN( src ); X X SET_TOKEN( &str, src ); X DB_PRINT( "mod", ("Source string [%s]", src) ); X X while( *(s = Get_token( &str, "", FALSE )) != '\0' ) { X /* search for the directory portion of the filename. If the X * DIRECTORY_FLAG is set, then we want to keep the directory portion X * othewise throw it away and blank out to the end of the token */ X X if( (e = basename(s)) != s) X if( !(mod & DIRECTORY_FLAG) ) { X strcpy(s, e); X e = s+(str.tk_str-e); X for(; e != str.tk_str; e++) X *e = ' '; X } X else X s = e; X X /* search for the suffix, if there is none, treat it as a NULL suffix. X * if no file name treat it as a NULL file name. same copy op as X * for directory case above */ X X e = strrchr( s, '.' ); /* NULL suffix if e=0 */ X if( e == NIL(char) ) e = s+strlen(s); X X if( !(mod & FILE_FLAG) ) { X strcpy( s, e ); X e = s+(str.tk_str-e); X for( ; e != str.tk_str; e++ ) *e = ' '; X } X else X s = e; X X /* The last and final part. This is the suffix case, if we don't want X * it then just erase to the end of the token. */ X X if( s != NIL(char) ) X if( !(mod & SUFFIX_FLAG) ) X for( ; s != str.tk_str; s++ ) *s = ' '; X } X X /* delete the extra white space, it looks ugly */ X for( s = src, e = NIL(char); *s; s++ ) X if( *s == ' ' || *s == '\t' || *s == '\n' ) { X if( e == NIL(char) ) X e = s; X } X else { X if( e != NIL(char) ) { X if( e+1 < s ) { X strcpy( e+1, s ); X s = e+1; X *e = ' '; X } X e = NIL(char); X } X } X X if( e != NIL(char) ) X if( e < s ) X strcpy( e, s ); X X DB_PRINT( "mod", ("Result string [%s]", src) ); X DB_RETURN( src ); X} X X X Xstatic char* X_tokenize( src, separator )/* X============================= X Tokenize the input of src and join each token found together with X the next token separated by the separator string. X X When doing the tokenization, <sp>, <tab>, <nl>, and \<nl> all X constitute white space. */ X Xchar *src; Xchar *separator; X{ X TKSTR tokens; X char *tok; X char *res; X int first = TRUE; X X DB_ENTER( "_tokenize" ); X X SET_TOKEN( &tokens, src ); X X X /* map the escape codes in the separator string first */ X X for(tok=separator; (tok = strchr(tok,CONTINUATION_CHAR)) != NIL(char); tok++) X Map_esc( tok ); X X DB_PRINT( "exp", ("Separator [%s]", separator) ); X X /* Build the token list */ X res = _strdup( "" ); X while( *(tok = Get_token( &tokens, "", FALSE )) != '\0' ) { X DB_PRINT( "exp", ("Tokenizing [%s]", tok) ); X X if( first ) { X FREE( res ); X res = _strdup( tok ); X first = FALSE; X } X else { X char *x; X res = _strjoin(res, x =_strjoin(separator, tok, -1, FALSE), -1, TRUE); X FREE( x ); X } X } X X FREE( src ); X DB_RETURN( res ); X} SHAR_EOF echo "File expand.c is complete" chmod 0440 expand.c || echo "restore of expand.c fails" echo "x - extracting dump.c (Text)" sed 's/^X//' << 'SHAR_EOF' > dump.c && X/* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/dump.c,v 1.1 90/10/06 12:03:37 dvadura Exp $ X-- SYNOPSIS -- dump the internal dag to stdout. X-- X-- DESCRIPTION X-- This file contains the routine that is called to dump a version of X-- the digested makefile to the standard output. May be useful perhaps X-- to the ordinary user, and invaluable for debugging make. X-- X-- AUTHOR X-- Dennis Vadura, dvadura@watdragon.uwaterloo.ca X-- CS DEPT, University of Waterloo, Waterloo, Ont., Canada X-- X-- COPYRIGHT X-- Copyright (c) 1990 by Dennis Vadura. All rights reserved. X-- X-- This program is free software; you can redistribute it and/or X-- modify it under the terms of the GNU General Public License X-- (version 1), as published by the Free Software Foundation, and X-- found in the file 'LICENSE' included with this distribution. X-- X-- This program is distributed in the hope that it will be useful, X-- but WITHOUT ANY WARRANTY; without even the implied warrant of X-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X-- GNU General Public License for more details. X-- X-- You should have received a copy of the GNU General Public License X-- along with this program; if not, write to the Free Software X-- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X-- X-- LOG X-- $Log: dump.c,v $ X * Revision 1.1 90/10/06 12:03:37 dvadura X * dmake Release, Version 3.6 X * X*/ X X#include "db.h" X#include "extern.h" X X#define M_TEST (M_PRECIOUS | M_VAR_MASK) X Xstatic void dump_normal_target ANSI((CELLPTR, HOWPTR, CELLPTR)); Xstatic void dump_graph_node ANSI((CELLPTR)); Xstatic void dump_name ANSI((HASHPTR, int)); X X Xvoid XDump()/* X======== Dump onto standard output the digested makefile. Note that X the form of the dump is not representative of the contents X of the original makefile contents at all */ X{ X HASHPTR hp; X int i; X X DB_ENTER( "Dump" ); X X puts( "# Dump of dmake macro variables:" ); X for( i=0; i<HASH_TABLE_SIZE; i++) X for( hp=Macs[i]; hp != NIL(HASH); hp = hp->ht_next ) { X int flag = hp->ht_flag; X X printf( "%s ", hp->ht_name ); X if( flag & M_EXPANDED ) putchar( ':' ); X printf( "= " ); X if( hp->ht_value != NIL(char) ) printf( hp->ht_value ); X if( flag & M_PRECIOUS ) X printf( "\t # PRECIOUS " ); X putchar( '\n' ); X } X X puts( "\n#====================================" ); X puts( "# Dump of targets:\n" ); X X for( i=0; i<HASH_TABLE_SIZE; i++ ) X for( hp = Defs[i]; hp != NIL(HASH); hp = hp->ht_next ) X if( !(hp->CP_OWNR->ce_flag & F_PERCENT) ) { X if( hp->CP_OWNR == Fringe_hd->cl_prq ) X puts( "# ******* FIRST TARGET ********" ); X dump_normal_target( hp->CP_OWNR, hp->CP_OWNR->CE_HOW, NIL(CELL) ); X } X X puts( "\n#====================================" ); X puts( "# Dump of inference graph\n" ); X X for( i=0; i<HASH_TABLE_SIZE; i++ ) X for( hp = Defs[i]; hp != NIL(HASH); hp = hp->ht_next ) X if( (hp->CP_OWNR->ce_flag & F_PERCENT) && X !(hp->CP_OWNR->ce_flag & F_MAGIC) ) X dump_graph_node( hp->CP_OWNR ); X X DB_VOID_RETURN; X} X X X Xvoid XDump_recipe( sp )/* X=================== X Given a string pointer print the recipe line out */ XSTRINGPTR sp; X{ X char *st; X char *nl; X X if( sp == NIL(STRING) ) return; X X putchar( '\t' ); X if( sp->st_attr & A_SILENT ) putchar( '@' ); X if( sp->st_attr & A_IGNORE ) putchar( '-' ); X if( sp->st_attr & A_SHELL ) putchar( '+' ); X X st = sp->st_string; X for( nl=strchr(st,'\n'); nl != NIL( char); nl=strchr(st,'\n') ) { X *nl = '\0'; X printf( "%s\\\n", st ); X *nl = '\n'; X st = nl+1; X } X printf( "%s\n", st ); X} X X Xstatic char *_attrs[] = { ".PRECIOUS", ".SILENT", ".LIBRARY", X ".EPILOG", ".PROLOG", ".IGNORE", ".SYMBOL", ".NOINFER", X ".UPDATEALL", ".SEQUENTIAL", ".SETDIR=", ".USESHELL", ".SWAP", ".MKSARGS" }; X Xstatic void Xdump_normal_target( cp, hw, prq )/* X=================================== X Dump in makefile like format the dag information */ XCELLPTR cp; XHOWPTR hw; XCELLPTR prq; X{ X register LINKPTR lp; X register STRINGPTR sp; X t_attr attr; X unsigned int k; X X DB_ENTER( "dump_normal_target" ); X X if( hw == NIL(HOW) ) { DB_VOID_RETURN; } X if( hw->hw_next != NIL(HOW) ) dump_normal_target( cp, hw->hw_next, prq ); X X dump_name( cp->ce_name, FALSE ); X X for( k=0, attr=1; attr <= MAX_ATTR; attr <<= 1, k++ ) X if( (cp->ce_attr & attr) || ((hw->hw_attr & A_SHELL)&attr) ) { X printf( "%s%s ", _attrs[k], X (attr != A_SETDIR) ? "" : (cp->ce_dir?cp->ce_dir:"") ); X } X X putchar( ':' ); X X if( cp->ce_flag & F_MULTI ) putchar( ':' ); X if( hw->hw_flag & F_SINGLE ) putchar( '!' ); X putchar( ' ' ); X X if( prq != NIL(CELL) ) dump_name( prq->ce_name, FALSE ); X for( lp = hw->hw_prq; lp != NIL(LINK); lp = lp->cl_next ) X dump_name( lp->cl_prq->ce_name, FALSE ); X X if( (lp = hw->hw_indprq) != NIL(LINK) ) { X for( ; lp != NIL(LINK); lp = lp->cl_next ) X dump_name( lp->cl_prq->ce_name, TRUE ); X } X X putchar( '\n' ); X if( hw->hw_flag & F_GROUP ) { X if( hw->hw_attr & A_IGNORE ) putchar( '-' ); X if( hw->hw_attr & A_SILENT ) putchar( '@' ); X puts( "[" ); X } X X for( sp = hw->hw_recipe; sp != NIL(STRING); sp = sp->st_next ) X Dump_recipe( sp ); X if( hw->hw_flag & F_GROUP ) puts( "]" ); X X putchar( '\n' ); X DB_VOID_RETURN; X} X X Xstatic void Xdump_name( hp, quote )/* X======================== X print out a name */ XHASHPTR hp; Xint quote; X{ X if( quote ) putchar('\''); X printf( "%s", hp->ht_name ); X if( quote ) putchar('\''); X putchar(' '); X} X X X Xstatic void Xdump_graph_node( cp )/* X=======================*/ XCELLPTR cp; X{ X EDGEPTR pe; X X pe = cp->CE_EDGES; X X if( pe != NIL(EDGE) ) X do { X dump_normal_target( cp, pe->ed_how, pe->ed_prq ); X pe = pe->ed_next; X } X while( pe != cp->CE_EDGES ); X} SHAR_EOF chmod 0440 dump.c || echo "restore of dump.c fails" echo "x - extracting dmake.h (Text)" sed 's/^X//' << 'SHAR_EOF' > dmake.h && X/* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/dmake.h,v 1.1 90/10/06 12:04:20 dvadura Exp $ X-- SYNOPSIS -- global defines for dmake. X-- X-- DESCRIPTION X-- All the interesting bits and flags that dmake uses are defined here. X-- X-- AUTHOR X-- Dennis Vadura, dvadura@watdragon.uwaterloo.ca X-- CS DEPT, University of Waterloo, Waterloo, Ont., Canada X-- X-- COPYRIGHT X-- Copyright (c) 1990 by Dennis Vadura. All rights reserved. X-- X-- This program is free software; you can redistribute it and/or X-- modify it under the terms of the GNU General Public License X-- (version 1), as published by the Free Software Foundation, and X-- found in the file 'LICENSE' included with this distribution. X-- X-- This program is distributed in the hope that it will be useful, X-- but WITHOUT ANY WARRANTY; without even the implied warrant of X-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X-- GNU General Public License for more details. X-- X-- You should have received a copy of the GNU General Public License X-- along with this program; if not, write to the Free Software X-- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X-- X-- LOG X-- $Log: dmake.h,v $ X * Revision 1.1 90/10/06 12:04:20 dvadura X * dmake Release, Version 3.6 X * X*/ X X#ifndef _DMAKE_INCLUDED_ X#define _DMAKE_INCLUDED_ X X#define MAX_INC_DEPTH 10 /* max of ten nested include files */ X#define MAX_COND_DEPTH 20 /* max nesting level of conditionals */ X#define ERROR_EXIT_VALUE 255 /* return code of aborted make */ X#define CONTINUATION_CHAR '\\' /* line continuation \<nl> */ X#define ESCAPE_CHAR '\\' /* escape char for used chars */ X#define COMMENT_CHAR '#' /* start of comment chars */ X#define TGT_DEP_SEP ':' /* separator for targets and dependents */ X#define CONDSTART '.' /* start of conditional token eg .IF */ X#define DEF_MAKE_PNAME "dmake"/* default name to use as name of make */ X X X/* ............... Hashing function constants ......................... */ X#define HASH_TABLE_SIZE 200 /* See hash.c for description */ X X X/* Bit flags for cells and macro definitions. */ X#define M_DEFAULT 0x0000 /* default flag value */ X#define M_MARK 0x0001 /* mark for circularity checks */ X#define M_PRECIOUS 0x0002 /* keep macro, same as A_PRE... */ X#define M_MULTI 0x0004 /* multiple redefinitions ok! */ X#define M_EXPANDED 0x0008 /* macro has been assigned */ X#define M_USED 0x0010 /* macro has been expanded */ X#define M_LITERAL 0x0020 /* don't strip w/s on macro def */ X#define M_NOEXPORT 0x0040 /* don't export macro for -x */ X#define M_FORCE 0x0080 /* Force a macro redefinition */ X#define M_VAR_BIT 0x1000 /* macro bit variable */ X#define M_VAR_CHAR 0x2000 /* macro char variable */ X#define M_VAR_STRING 0x4000 /* macro string variable */ X#define M_VAR_INT 0x8000 /* macro integer variable */ X X#define M_VAR_MASK 0xf000 /* macro variable mask */ X X X X/* Global and target attribute flag definitions. X * If you change the values of these or re-order them make appropriate changes X * in dump.c so that the output of dmake -p matches the attribute info for a X * target. */ X X#define A_DEFAULT 0x00000 /* default flag value */ X#define A_PRECIOUS 0x00001 /* object is precious */ X#define A_SILENT 0x00002 /* don't echo commands */ X#define A_LIBRARY 0x00004 /* target is an archive */ X#define A_EPILOG 0x00008 /* insert shell epilog code */ X#define A_PROLOG 0x00010 /* insert shell prolog code */ X#define A_IGNORE 0x00020 /* ignore errors */ X#define A_SYMBOL 0x00040 /* lib member is a symbol */ X#define A_NOINFER 0x00080 /* no trans closure from cell */ X#define A_UPDATEALL 0x00100 /* all targets of rule modified */ X#define A_SEQ 0x00200 /* sequential make attribute */ X#define A_SETDIR 0x00400 /* cd to dir when making target */ X#define A_SHELL 0x00800 /* appears only on HOW nodes */ X#define A_SWAP 0x01000 /* swap on exec. */ X#define A_MKSARGS 0x02000 /* use MKS argument swapping */ X#define MAX_ATTR A_MKSARGS /* highest valid attribute */ X#define A_LIBRARYM 0x04000 /* target is an archive member */ X#define A_FRINGE 0x08000 /* cell is on the fringe */ X#define A_COMPOSITE 0x10000 /* member of lib(targ) name */ X#define A_FFNAME 0x20000 /* if set, free ce_fname in stat*/ X#define A_UPDATED 0x40000 /* Used to mark cell as updated */ X X X/* Global and target bit flag definitions */ X X#define F_DEFAULT 0x0000 /* default flag value */ X#define F_MARK 0x0001 /* circularity check mark */ X#define F_MULTI 0x0002 /* multiple rules for target */ X#define F_SINGLE 0x0004 /* exec rules one/prerequisite */ X#define F_TARGET 0x0008 /* marks a target */ X#define F_RULES 0x0010 /* indicates target has rules */ X#define F_GROUP 0x0020 /* indicates that rules are to */ X /* fed to the shell as a group */ X X#define F_TRANS 0x0040 /* same as F_STAT not used tgthr*/ X#define F_STAT 0x0040 /* target already stated */ X#define F_VISITED 0x0080 /* target scheduled for make */ X#define F_USED 0x0080 /* used in releparse.c */ X#define F_SPECIAL 0x0100 /* marks a special target */ X#define F_DFA 0x0200 /* bit for marking added DFA */ X#define F_EXPLICIT 0x0400 /* explicit target in makefile */ X#define F_PERCENT 0x0800 /* marks a target as a % rule */ X#define F_REMOVE 0x1000 /* marks an intermediate target */ X#define F_MAGIC 0x2000 /* marks a magic target */ X#define F_INFER 0x4000 /* target is result of inference*/ X#define F_MADE 0x8000 /* target is manufactured */ X X X/* Definitions for the Parser states */ X#define NORMAL_SCAN 0 /* normal processing state */ X#define RULE_SCAN 1 /* scan of rule text */ X X/* definitions for macro operator types */ X#define M_OP_EQ 1 /* macro operation is '=' */ X#define M_OP_CL 2 /* macro operation is ':=' */ X#define M_OP_PL 3 /* macro operation is '+=' */ X#define M_OP_PLCL 4 /* macro operation is '+:='*/ X#define M_OP_DF 5 /* macro operation is '*=' */ X#define M_OP_DFCL 6 /* macro operation is '*:='*/ X X/* definitions for rule operator types */ X#define R_OP_CL 1 /* rule operation is ':' */ X#define R_OP_DCL 2 /* rule operation is '::' */ X#define R_OP_BG 4 /* rule operation is ':!' */ X#define R_OP_UP 8 /* rule operation is ':^' */ X#define R_OP_MI 16 /* rule operation is ':-' */ X X X/* special target definitions for use inside dmake */ X#define ST_IF 1 X#define ST_ELSE 2 X#define ST_END 3 X#define ST_REST 4 /* remaining special targets */ X#define ST_INCLUDE 5 X#define ST_SOURCE 7 X#define ST_EXPORT 8 SHAR_EOF echo "End of part 21" echo "File dmake.h is continued in part 22" echo "22" > s2_seq_.tmp exit 0