[comp.sources.misc] v15i073: dmake version 3.6

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