dvadura@watdragon.waterloo.edu (Dennis Vadura) (05/11/91)
Submitted-by: Dennis Vadura <dvadura@watdragon.waterloo.edu> Posting-number: Volume 19, Issue 30 Archive-name: dmake/part09 Supersedes: dmake-3.6: Volume 15, Issue 52-77 ---- Cut Here and feed the following to sh ---- #!/bin/sh # this is dmake.shar.09 (part 9 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file dmake/expand.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 9; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test -f _shar_wnt_.tmp; then sed 's/^X//' << 'SHAR_EOF' >> 'dmake/expand.c' && 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 X static char* _scan_token( s, ps )/* ====================== 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 char *s; /* pointer to start of src string */ char **ps; /* pointer to start pointer */ { 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( strchr("{ \t",s[1]) == NIL(char) ){ 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 += (s[1]=='{')?2:1; 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 static char* _scan_macro( s, ps )/* ====================== 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 char *s; /* pointer to start of src string */ char **ps; /* pointer to start pointer */ { 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 static char* _scan_brace( s, ps, flag )/* ============================ 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 char *s; char **ps; int *flag; { 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 static char* _cross_prod( x, y )/* ===================== 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 char *x; char *y; { 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 buf[i+(brky-cy)] = '\0'; 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 ); } SHAR_EOF chmod 0640 dmake/expand.c || echo 'restore of dmake/expand.c failed' Wc_c="`wc -c < 'dmake/expand.c'`" test 25368 -eq "$Wc_c" || echo 'dmake/expand.c: original size 25368, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= dmake/extern.h ============== if test -f 'dmake/extern.h' -a X"$1" != X"-c"; then echo 'x - skipping dmake/extern.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp sed 's/^X//' << 'SHAR_EOF' > 'dmake/extern.h' && /* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/extern.h,v 1.1 91/05/06 15:23:13 dvadura Exp $ -- SYNOPSIS -- external declarations for dmake functions. -- -- DESCRIPTION -- ANSI is a macro that allows the proper handling of ANSI style -- function declarations. -- -- AUTHOR -- Dennis Vadura, dvadura@watdragon.uwaterloo.ca -- CS DEPT, University of Waterloo, Waterloo, Ont., Canada -- -- COPYRIGHT -- Copyright (c) 1990 by Dennis Vadura. All rights reserved. -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License -- (version 1), as published by the Free Software Foundation, and -- found in the file 'LICENSE' included with this distribution. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warrant of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; if not, write to the Free Software -- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -- -- LOG -- $Log: extern.h,v $ X * Revision 1.1 91/05/06 15:23:13 dvadura X * dmake Release Version 3.7 X * */ X #ifndef EXTERN_h #define EXTERN_h X /* Define this for the RS/6000 if it breaks something then we have to put a X * #ifdef around it. */ #if defined(rs6000) #define _POSIX_SOURCE #endif X #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <stdarg.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include "itypes.h" #include "stdmacs.h" #include "alloc.h" #include "db.h" #include "dmake.h" #include "struct.h" #include "vextern.h" #include "public.h" X /* Include this last as it invalidates some functions that are defined X * externally above and turns them into no-ops. Have to do this after X * the extern declarations however. */ #include "config.h" X #endif X SHAR_EOF chmod 0640 dmake/extern.h || echo 'restore of dmake/extern.h failed' Wc_c="`wc -c < 'dmake/extern.h'`" test 2089 -eq "$Wc_c" || echo 'dmake/extern.h: original size 2089, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= dmake/function.c ============== if test -f 'dmake/function.c' -a X"$1" != X"-c"; then echo 'x - skipping dmake/function.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp sed 's/^X//' << 'SHAR_EOF' > 'dmake/function.c' && /* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/function.c,v 1.1 91/05/06 15:23:14 dvadura Exp $ -- SYNOPSIS -- GNU style functions for dmake. -- -- DESCRIPTION -- All GNU stule functions understood by dmake are implemented in this -- file. Currently the only such function is $(mktmp ...) which is -- not part of GNU-make is an extension provided by dmake. -- -- AUTHOR -- Dennis Vadura, dvadura@watdragon.uwaterloo.ca -- CS DEPT, University of Waterloo, Waterloo, Ont., Canada -- -- COPYRIGHT -- Copyright (c) 1990 by Dennis Vadura. All rights reserved. -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License -- (version 1), as published by the Free Software Foundation, and -- found in the file 'LICENSE' included with this distribution. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warrant of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; if not, write to the Free Software -- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -- -- LOG -- $Log: function.c,v $ X * Revision 1.1 91/05/06 15:23:14 dvadura X * dmake Release Version 3.7 X * */ X #include "extern.h" X static char *_exec_mktmp ANSI((char *, char *, char *)); static char *_exec_subst ANSI((char *, char *, char *)); static char *_exec_iseq ANSI((char *, char *, char *, int)); static char *_exec_sort ANSI((char *)); static char *_exec_shell ANSI((char *)); static int _mystrcmp ANSI((CONST void *, CONST void *)); X X PUBLIC char * Exec_function(buf)/* ==================== X Execute the function given by the value of args. X X So far mktmp is the only valid function, anything else elicits and error X message. It is my hope to support the GNU style functions in this portion X of the code at some time in the future. */ char *buf; { X char *fname; X char *args; X char *mod1; X char *mod2 = NIL(char); X char *res = NIL(char); X X /* This must succeed since the presence of ' ', \t or \n is what X * determines if this functions is called in the first place. */ X fname = _substr(buf, args=_strpbrk(buf," \t\n")); X X if( (mod1 = strchr(fname,',')) != NIL(char) ){ X *mod1 = '\0'; X mod1++; X X if( (mod2 = strchr(mod1,',')) != NIL(char) ){ X *mod2 = '\0'; X mod2++; X } X } X X switch( *fname ) { X case 'e': X if(strncmp(fname,"eq",2) == 0) res = _exec_iseq(mod1,mod2,args,TRUE); X break; X X case 'm': X if( strncmp(fname,"mktmp", 5) == 0 ) res = _exec_mktmp(mod1,mod2,args); X break; X X case 'n': X if( strncmp(fname,"null", 4) == 0 ) X res = _exec_iseq(mod1,NIL(char),args,TRUE); X break; X X case '!': X if(strncmp(fname,"!null",5) == 0) X res = _exec_iseq(mod1,NIL(char),args,FALSE); X if(strncmp(fname,"!eq",3) == 0) res = _exec_iseq(mod1,mod2,args,FALSE); X break; X X case 's': X if(strncmp(fname,"sort",4) == 0) res = _exec_sort(args); X else if(strncmp(fname,"shell",5)==0) res = _exec_shell(args); X else if(strncmp(fname,"strip",5)==0) res = Tokenize(Expand(args)," "); X else if(strncmp(fname,"subst",5)==0) res = _exec_subst(mod1,mod2,args); X break; X X default: X Warning( "Function '%s' not implemented at this time", fname ); X } X X if( res == NIL(char) ) res = _strdup(""); X X FREE(fname); X return(res); } X X static char * _exec_mktmp( file, text, data ) char *file; char *text; char *data; { X register char *p; X char *tmpname; X char *name; X FILE *tmpfile = NIL(FILE); X X /* This is only a test of the recipe line so prevent the tempfile side X * effects. */ X if( Suppress_temp_file ) return(NIL(char)); X X name = Current_target ? Current_target->CE_NAME:"makefile text"; X X if( file && *file ) { X char *newtmp; X X /* This call to Get_temp sets TMPFILE for subsequent expansion of file. X * DO NOT DELETE IT! */ X Get_temp( &newtmp, "", FALSE ); FREE(newtmp); X tmpname = Expand(file); X X if( *tmpname ) { X if( (tmpfile = fopen(tmpname, "w")) == NIL(FILE) ) X Open_temp_error( tmpname, name ); X X Def_macro("TMPFILE", tmpname, M_EXPANDED|M_MULTI); X Link_temp( Current_target, tmpfile, tmpname ); X } X else X FREE(tmpname); X } X X if( !tmpfile ) X tmpfile = Start_temp( "", Current_target, &tmpname ); X X if( !text || !*text ) text = tmpname; X data = Expand(_strspn(data, " \t\n")); X X for(p=strchr(data,'\n'); p; p=strchr(p,'\n')) { X char *q = _strspn(++p," \t"); X strcpy(p,q); X } X X Append_line( data, FALSE, tmpfile, name, FALSE, TRUE ); X Close_temp( Current_target, tmpfile ); X FREE(data); X X return( Expand(text) ); } X X static char * _exec_iseq( lhs, rhs, data, eq ) char *lhs; char *rhs; char *data; int eq; { X char *l = Expand(lhs); X char *r = Expand(rhs); X char *i = _strspn(data, " \t\n"); X char *e = strchr(i, ' '); X char *res = NIL(char); X int val = strcmp(l,r); X X if( (!val && eq) || (val && !eq) ) { X if( e != NIL(char) ) *e = '\0'; X res = Expand(i); X } X else if( e != NIL(char) ) { X e = _strspn(e," \t\n"); X if( *e ) res = Expand(e); X } X X FREE(l); X FREE(r); X return(res); } X X static char * _exec_sort( args ) char *args; { X char *res = NIL(char); X char *data = Expand(args); X char **tokens = NIL(char *); X char *p; X char *white = " \t\n"; X int j; X int i = 0; X X for( i=0,p=_strspn(data,white); *p; p=_strspn(_strpbrk(p,white),white),i++); X X if( i != 0 ) { X TALLOC(tokens, i, char *); X X for( i=0,p=_strspn(data,white); *p; p=_strspn(p,white),i++){ X tokens[i] = p; X p = _strpbrk(p,white); X if( *p ) *p++ = '\0'; X } X X qsort( tokens, i-1, sizeof(char *), _mystrcmp ); X X for( j=0; j<i; j++ ) res = _strapp(res, tokens[j]); X FREE(data); X FREE(tokens); X } X X return(res); } X X static int _mystrcmp( p, q ) CONST void *p; CONST void *q; { X return(strcmp(*((CONST char **)p),*((CONST char **)q))); } X X static char * _exec_subst( pat, subst, data ) char *pat; char *subst; char *data; { X char *res; X X pat = Expand(pat); X subst = Expand(subst); X res = Apply_edit( Expand(data), pat, subst, TRUE, FALSE ); X FREE(pat); X FREE(subst); X X return(res); } X X static char * _exec_shell( data ) char *data; { X extern char *tempnam(); X int wait = Wait_for_completion; X char *res = NIL(char); X int free_buf = FALSE; X char *buffer; X int out; X int err; X FILE *tmp; X char *tmpnm; X CELL cell; X STRING rcp; X HASH cname; X X if( Suppress_temp_file ) return(NIL(char)); X X /* Set the temp CELL used for building prerequisite candidates to X * all zero so that we don't have to keep initializing all the X * fields. */ X { X register char *s = (char *) &cell; X register int n = sizeof(CELL); X while( n ) { *s++ = '\0'; n--; } X } X rcp.st_string = _strspn(data, " \t+-%@"); X rcp.st_attr = Rcp_attribute( data ); X rcp.st_next = NIL(STRING); X cname.ht_name = "Shell escape"; X cell.ce_name = &cname; X cell.ce_fname = cname.ht_name; X cell.ce_recipe = &rcp; X cell.ce_flag = F_TARGET|F_RULES; X cell.ce_attr = A_PHONY|A_SILENT; X X tmpnm = tempnam(NIL(char),"mk"); X out = dup( 1 ); X if( (tmp = fopen(tmpnm, "w")) == NIL(FILE) ) X Open_temp_error( tmpnm, cname.ht_name ); X X Wait_for_completion = TRUE; X close( 1 ); X dup( fileno(tmp) ); X fclose( tmp ); X Exec_commands( &cell ); X close(1); X dup(out); X close(out); X X /* Now we have to read the temporary file, get the tokens and return them X * as a string. */ X if( (tmp = fopen(tmpnm, "r")) == NIL(FILE) ) X Open_temp_error( tmpnm, cname.ht_name ); X X if( Buffer == NIL(char) ) { X Buffer_size = BUFSIZ-2; X buffer = MALLOC(BUFSIZ-2,char); X free_buf = TRUE; X } X else X buffer = Buffer; X X while( fgets(buffer, Buffer_size, tmp) ) { X char *p = strchr(buffer, '\n'); X X if( p == NIL(char) ) X res = _strjoin(res,buffer,-1,TRUE); X else { X *p = '\0'; X res = _strapp(res,buffer); X } X } X X fclose(tmp); X unlink(tmpnm); X FREE(tmpnm); X if( free_buf ) FREE(buffer); X X Wait_for_completion = wait; X return(res); } SHAR_EOF chmod 0640 dmake/function.c || echo 'restore of dmake/function.c failed' Wc_c="`wc -c < 'dmake/function.c'`" test 8398 -eq "$Wc_c" || echo 'dmake/function.c: original size 8398, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= dmake/getinp.c ============== if test -f 'dmake/getinp.c' -a X"$1" != X"-c"; then echo 'x - skipping dmake/getinp.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp sed 's/^X//' << 'SHAR_EOF' > 'dmake/getinp.c' && /* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/getinp.c,v 1.1 91/05/06 15:23:15 dvadura Exp $ -- SYNOPSIS -- handle reading of input. -- -- DESCRIPTION -- The code in this file reads the input from the specified stream -- into the provided buffer of size Buffer_size. In doing so it deletes -- comments. Comments are delimited by the #, and -- <nl> character sequences. An exception is \# which -- is replaced by # in the input. Line continuations are signalled -- at the end of a line and are recognized inside comments. -- The line continuation is always <\><nl>. -- -- If the file to read is NIL(FILE) then the Get_line routine returns the -- next rule from the builtin rule table if there is one. -- -- AUTHOR -- Dennis Vadura, dvadura@watdragon.uwaterloo.ca -- CS DEPT, University of Waterloo, Waterloo, Ont., Canada -- -- COPYRIGHT -- Copyright (c) 1990 by Dennis Vadura. All rights reserved. -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License -- (version 1), as published by the Free Software Foundation, and -- found in the file 'LICENSE' included with this distribution. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warrant of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; if not, write to the Free Software -- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -- -- LOG -- $Log: getinp.c,v $ X * Revision 1.1 91/05/06 15:23:15 dvadura X * dmake Release Version 3.7 X * */ X #include "extern.h" X #define IS_WHITE(A) ((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r')) #define SCAN_WHITE(A) \ X while( IS_WHITE(*A) ) A++; X static int _is_conditional ANSI((char*)); static int _handle_conditional ANSI((int, TKSTRPTR)); X static int rule_ind = 0; /* index of rule when reading Rule_tab */ static int skip = FALSE; /* if true the skip input */ X X PUBLIC int Get_line( buf, fil )/* ====================== X Read a line of input from the file stripping X off comments. The routine returns TRUE if EOF */ char *buf; FILE *fil; { X extern char **Rule_tab; X register char *p; X register char *c; X char *q; X char *buf_org; X static int ignore = FALSE; X int cont = FALSE; X int pos = 0; X int res; X X DB_ENTER( "Get_line" ); X X if( fil == NIL(FILE) ) { X /* Reading the internal rule table. Set the rule_index to zero. X * This way ReadEnvironment works as expected every time. */ X X while( (p = Rule_tab[ rule_ind++ ]) != NIL(char) ) X /* The last test in this if '*p != '~', handles the environment X * passing conventions used by MKS to pass arguments. We want to X * skip those environment entries. */ X if( !Readenv || (Readenv && (strchr(p,'=') != NIL(char)) && *p!='~')){ X strcpy( buf, p ); X X DB_PRINT( "io", ("Returning [%s]", buf) ); X DB_RETURN( FALSE ); X } X X rule_ind = 0; X X DB_PRINT( "io", ("Done Ruletab") ); X DB_RETURN( TRUE ); X } X X buf_org = buf; X do_again: X do { X p = buf+pos; X if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char))) X DB_RETURN( TRUE ); X X Line_number++; X X /* ignore input if ignore flag set and line ends in a continuation X character. */ X X q = p+strlen(p)-2; X /* ignore each RETURN at the end of a line before any further X * processing */ X if( q[0] == '\r' && q[1] == '\n' ) { X q[0] = '\n'; X q[1] = '\0'; X q--; X } X /* you also have to deal with END_OF_FILE chars to process raw X * DOS-Files. Normally they are the last chars in file, but after X * working on these file with vi, there is an additional NEWLINE X * after the last END_OF_FILE. So if the second last char in the X * actual line is END_OF_FILE, you can skip the last char. Then X * you can search the line back until you find no more END_OF_FILE X * and nuke each you found by string termination. */ X if( q[0] == '\032' ) X q--; X while( q[1] == '\032' ) { X q[1] = '\0'; X q--; X } X X if( ignore ) { X if( q[0] != CONTINUATION_CHAR || q[1] != '\n' ) ignore = FALSE; X *p = '\0'; X continue; X } X X c = Do_comment(p, &q, Group || (*buf == '\t')); X X /* Does the end of the line end in a continuation sequence? */ X X if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) { X /* If the continuation was at the end of a comment then ignore the X * next input line, (or lines until we get one ending in just <nl>) X * else it's a continuation, so build the input line from several X * text lines on input. The maximum size of this is governened by X * Buffer_size */ X if( q != p && q[-1] == CONTINUATION_CHAR ) { X strcpy( q, q+1 ); X q--; X cont = FALSE; X } X else if( c != NIL(char) ) X ignore = TRUE; X else X cont = TRUE; X } X else { X cont = FALSE; X } X X q = ( c == NIL(char) ) ? q+2 : c; X pos += q-p; X } X while( (cont || !*buf) && (pos <= Buffer_size) ); X X if( buf[ pos-1 ] == '\n' ) X buf[ --pos ] = '\0'; X else X if( pos == Buffer_size-1 ) X Fatal( "Input line too long, increase MAXLINELENGTH" ); X X X /* Now that we have the next line of input to make, we should check to X * see if it is a conditional expression. If it is then process it, X * otherwise pass it on to the parser. */ X X if( *(p = _strspn(buf, " \t\r\n")) == CONDSTART ) { X TKSTR token; X X SET_TOKEN( &token, p ); X X p = Get_token( &token, "", FALSE ); X X if( (res = _is_conditional( p )) ) /* ignore non control special */ X { /* targets */ X res = _handle_conditional( res, &token ); X skip = TRUE; X } X else { X CLEAR_TOKEN( &token ); X res = TRUE; X } X } X X if( skip ) { X buf = buf_org; /* ignore line just read in */ X pos = 0; X skip = res; X goto do_again; X } X X DB_PRINT( "io", ("Returning [%s]", buf) ); X DB_RETURN( FALSE ); } X X PUBLIC char * Do_comment(str, pend, keep)/* ============================= X Search the input string looking for comment chars. If it contains X comment chars then NUKE the remainder of the line, if the comment X char is preceeded by \ then shift the remainder of the line left X by one char. */ char *str; char **pend; int keep; { X char *c = str; X X while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) { X if( Comment || State == NORMAL_SCAN ) X if( c != str && c[-1] == ESCAPE_CHAR ) { X strcpy( c-1, c ); /* copy it left, due to \# */ X if( pend ) (*pend)--; /* shift tail pointer left */ X } X else { X *c = '\0'; /* a true comment so break */ X break; X } X else { X if( keep ) X c = NIL(char); X else X *c = '\0'; X X break; X } X } X X return(c); } X X PUBLIC char * Get_token( string, brk, anchor )/* ================================== X Return the next token in string. X Returns empty string when no more tokens in string. X brk is a list of chars that also cause breaks in addition to space and X tab, but are themselves returned as tokens. if brk is NULL then the X remainder of the line is returned as a single token. X X anchor if TRUE, says break on chars in the brk list, but only if X the entire token begins with the first char of the brk list, if X FALSE then any char of brk will cause a break to occurr. */ X TKSTRPTR string; char *brk; int anchor; { X register char *s; X register char *curp; X register char *t; X int done = FALSE; X char space[10]; X X DB_ENTER( "Get_token" ); X X s = string->tk_str; /* Get string parameters */ X *s = string->tk_cchar; /* ... and strip leading w/s */ X X SCAN_WHITE( s ); X X DB_PRINT( "tok", ("What's left [%s]", s) ); X X if( !*s ) { X DB_PRINT( "tok", ("Returning NULL token") ); X DB_RETURN( "" ); X } X X X /* Build the space list. space contains all those chars that may possibly X * cause breaks. This includes the brk list as well as white space. */ X X if( brk != NIL(char) ) { X strcpy( space, " \t\r\n" ); X strcat( space, brk ); X } X else { X space[0] = 0xff; /* a char we know will not show up */ X space[1] = 0; X } X X X /* Handle processing of quoted tokens. Note that this is disabled if X * brk is equal to NIL */ X X while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) { X s++; X if( string->tk_quote ) { X curp = s-1; X do { curp = strchr( curp+1, '\"' ); } X while( (curp != NIL(char)) && (*(curp+1) == '\"')); X X if( curp == NIL(char) ) Fatal( "Unmatched quote in token" ); X string->tk_quote = !string->tk_quote; X X /* Check for "" case, and if found ignore it */ X if( curp == s ) continue; X goto found_token; X } X else X SCAN_WHITE( s ); X X string->tk_quote = !string->tk_quote; X } X X X /* Check for a token break character at the beginning of the token. X * If found return the next set of break chars as a token. */ X X if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) { X curp = _strspn( s, brk ); X done = (anchor == 0) ? TRUE : X ((anchor == 1)?(*s == *brk) : (*brk == curp[-1])); X } X X X /* Scan for the next token in the list and return it less the break char X * that was used to terminate the token. It will possibly be returned in X * the next call to Get_token */ X X if( !done ) { X SCAN_WHITE( s ); X X t = s; X do { X done = TRUE; X curp = _strpbrk(t, space); X X if( anchor && *curp && !IS_WHITE( *curp ) ) X if( ((anchor == 1)?*curp:_strspn(curp,brk)[-1]) != *brk ) { X t++; X done = FALSE; X } X } X while( !done ); X X if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++; X } X found_token: X string->tk_str = curp; X string->tk_cchar = *curp; X *curp = '\0'; X X DB_PRINT( "tok", ("Returning [%s]", s) ); X DB_RETURN( s ); } X X static int _is_conditional( tg )/* ======================= X Look at tg and return it's value if it is a conditional identifier X otherwise return 0. */ char *tg; { X DB_ENTER( "_is_conditional" ); X X tg++; X switch( *tg ) { X case 'I': if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF ); break; X X case 'E': X if( !strcmp( tg, "END" )) DB_RETURN( ST_END ); X else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END ); X else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE ); X else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF ); X break; X } X X DB_RETURN( 0 ); } X X X #define SEEN_END 0x00 #define SEEN_IF 0x01 #define SEEN_ELSE 0x02 X static int _handle_conditional( opcode, tg )/* =================================== X Perform the necessary processing for .IF conditinal targets. X Someday this should be modified to do bracketted expressions ala X CPP... sigh */ int opcode; TKSTRPTR tg; { X static short action[MAX_COND_DEPTH]; X static char ifcntl[MAX_COND_DEPTH]; X char *tok, *lhs, *rhs, *op, *expr; X int result; X X DB_ENTER( "_handle_conditional" ); X X switch( opcode ) { X case ST_ELIF: X if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) ) X Fatal(".ELIF without a preceeding .IF" ); X /* FALLTHROUGH */ X X case ST_IF: X if( opcode == ST_IF && (Nest_level+1) == MAX_COND_DEPTH ) X Fatal( ".IF .ELSE ... .END nesting too deep" ); X X If_expand = TRUE; X expr = Expand( Get_token( tg, NIL(char), FALSE )); X If_expand = FALSE; X lhs = _strspn( expr, " \t" ); X if( !*lhs ) lhs = NIL(char); X X if( (op = _strstr( lhs, "==" )) == NIL(char) ) X op = _strstr( lhs, "!=" ); X X if( op == NIL(char) ) X result = (lhs != NIL(char)); X else { X op[1] = op[0]; X if( lhs != op ) { X for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); SHAR_EOF true || echo 'restore of dmake/getinp.c failed' fi echo 'End of part 9, continue with part 10' echo 10 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.