v19i030: dmake - dmake version 3.7, Part09/37

Dennis Vadura dvadura at watdragon.waterloo.edu
Sat May 11 04:58:04 AEST 1991


Submitted-by: Dennis Vadura <dvadura at 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 at 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 at 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 at 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 at 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 at uunet.uu.net.



More information about the Comp.sources.misc mailing list