[comp.sources.misc] v14i014: dmake version 3.5 part 4/21

dvadura@watdragon.waterloo.edu (Dennis Vadura) (07/27/90)

Posting-number: Volume 14, Issue 14
Submitted-by: dvadura@watdragon.waterloo.edu (Dennis Vadura)
Archive-name: dmake/part04

#!/bin/sh
# this is part 4 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file struct.h continued
#
CurArch=4
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 struct.h"
sed 's/^X//' << 'SHAR_EOF' >> struct.h
X * It gets unlinked when Quit is called due to an execution error */
Xtypedef struct flst {
X   char		*fl_name;	/* file name 		*/
X   FILE		*fl_file;	/* the open file	*/
X   struct flst  *fl_next;	/* pointer to next file */
X} FILELIST, *FILELISTPTR;
X
X
X
X/* This is the structure of a target cell in the dag which represents the
X * graph of dependencies.  Each possible target is represented as a cell.
X * 
X * Each cell contains a pointer to the hash table entry for this cell.
X * The hash table entry records the name of the cell. */
X
Xtypedef struct tcell {
X	struct tcell	*ce_link;	/* link for temporary list making   */
X	struct tcell	*ce_all;	/* link for grouping UPDATEALL cells*/
X	struct hcell	*ce_name;	/* name of this cell                */
X	struct tcell	*ce_setdir;	/* .SETDIR root for this cell       */
X	union {
X		struct hwcell	*ce_how;/* tell all we need to make cell    */
X		struct edge	*ce_edges;/* tell what we can infer */
X	} how;
X	char		*ce_lib;	/* archive name, if A_LIBRARYM      */
X	char		*ce_fname;	/* file name associated with target */
X	char		*ce_dir;	/* dir to set if A_SETDIR set       */
X	int		ce_flag;	/* all kinds of goodies		    */
X	int		ce_attr;	/* attributes for this target	    */
X	time_t		ce_time;	/* time stamp value of target if any*/
X} CELL, *CELLPTR;
X
X#define CE_NAME		ce_name->ht_name
X#define CE_RECIPE       how.ce_how->hw_recipe
X#define CE_PRQ		how.ce_how->hw_prq
X#define CE_EDGES	how.ce_edges
X#define CE_HOW		how.ce_how
X
X
X
X/* This struct represents that used by Get_token to return and control
X * access to a token list inside a particular string.  This gives the
X * ability to access non overlapping tokens simultaneously from
X * multiple strings. */
X	
Xtypedef struct {
X	char *tk_str;              /* the string to search for tokens  */
X	char tk_cchar;             /* current char under *str          */
X	int  tk_quote;	           /* if we are scanning a quoted str  */
X}  TKSTR, *TKSTRPTR;
X
X
X
X
X/* This structure defines the necessary fields for handling the :: rules,
X * any flags, and the fact that the target has been made using this
X * set of rules.  All attributes active when making this target are
X * defined in this struct. */
X
Xtypedef struct hwcell {
X	struct hwcell	*hw_next;	/* next set of rules (::)	*/
X	struct lcell	*hw_prq;	/* the list of prerequisites	*/
X	struct lcell	*hw_indprq;	/* the list of glob prereqs	*/
X	struct str	*hw_recipe;	/* list of rules for making tg	*/
X	char		*hw_per;	/* value of % in % meta rules	*/
X	FILELISTPTR	hw_files;	/* temp files associated with tg*/
X	int		hw_flag;	/* flags for using these rules  */
X	int		hw_attr;	/* attrs for using these rules  */
X} HOW, *HOWPTR;
X
X#define HW_FLAG		CE_HOW->hw_flag
X#define HW_ATTR		CE_HOW->hw_attr
X
X
X
X
X/* Below is the struct used to represent a string.  It points at possibly
X * another string, since the set of rules for making a target is a collection
X * of strings. */
X
X
Xtypedef struct str {
X	char		*st_string;	/* the string value */
X	struct str	*st_next;	/* pointer to the next string */
X	int		st_attr;	/* attr for rule operations */
X} STRING, *STRINGPTR;
X
X
X/* The next struct is used to link together prerequisite lists */
X
Xtypedef struct lcell {
X	struct tcell	*cl_prq;	/* link to a prerequisite 	*/
X	struct lcell	*cl_next;	/* next cell on dependency list */
X	int		cl_flag;	/* flags for link cell		*/
X} LINK, *LINKPTR;
X
X
X
X/* This struct is used to define an edge in the graph of % rules */
X
Xtypedef struct edge {
X	struct tcell	*ed_tg;		/* the % target			*/
X	struct tcell	*ed_prq;	/* the prerequisite for target	*/
X	struct hwcell	*ed_how;	/* chain of how pointers	*/
X	struct edge	*ed_next;	/* next edge of graph		*/
X	struct edge	*ed_link;	/* work list pointer 		*/
X} EDGE, *EDGEPTR;
X
X
X
X
X/* These structs are used in processing of the % rules, and in building
X * the DFA machines that are used to match an arbitrary target string to
X * one of the % rules that is represented by each DFA */
X
Xtypedef int16 statecnt;		/* limits the max number of dfa states	*/
X
X
X/* This struct stores a single state for the DFA machine */
X
Xtypedef struct {
X	statecnt	no_match;	/* state to go to if no match */
X	statecnt	next;		/* state to go to if we do match */
X	char		symbol;		/* the symbol matched by this state */
X	char		action;		/* action to perform if we match */
X} STATE, *STATEPTR;
X
X
X/* This struct stores the DFA machine, it's current state, and pointers
X * indicating the extent of the % substring match */
X
Xtypedef struct {
X	char		*pstart;	/* start of % string match */
X	char		*pend;		/* end of % string match */
X	statecnt	c_state;	/* current DFA state */
X	statecnt	n_states;	/* number of states in this dfa */
X	CELLPTR		node;		/* % target represented by this DFA */
X	STATEPTR	states;		/* table of states for the DFA */
X} DFA, *DFAPTR;
X
X
X/* This struct represents the linked together DFA's which form one large
X * NFA machine.  When a pattern match takes place all DFA's making up this
X * NFA are run in parallel. */
X
Xtypedef struct nfa_machine {
X	DFAPTR		dfa;	/* The DFA pointed at by epsilon trans */
X	char		status;	/* DFA state:  -1 ==> fail, 4 ==> ACCEPT */
X	struct nfa_machine *next; /* the next DFA in list */
X} NFA, *NFAPTR;
X
X
X
X/* The next struct is used to link together DFA nodes for inference. */
X
Xtypedef struct dfal {
X	struct tcell	*dl_meta;	/* link to %-meta cell		*/
X	struct dfal	*dl_next;	/* next cell on matched DFA list*/
X	struct dfal	*dl_prev;	/* prev cell on matched DFA list*/
X	struct dfal	*dl_member;	/* used during subset calc	*/
X	char		dl_delete;	/* used during subset calc	*/
X	char   		*dl_per;	/* value of % for matched DFA   */
X	statecnt        dl_state;	/* matched state of the DFA	*/
X	int		dl_prep;	/* repetion count for the cell	*/
X} DFALINK, *DFALINKPTR;
X
X
X/* This struct is used to store the stack of DFA sets during inference */
Xtypedef struct dfst {
X   DFALINKPTR	df_set;			/* pointer to the set		*/
X   struct dfst *df_next;		/* next element in the stack	*/
X} DFASET, *DFASETPTR;
X
X
X#endif
SHAR_EOF
echo "File struct.h is complete"
chmod 0440 struct.h || echo "restore of struct.h fails"
echo "x - extracting string.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > string.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/string.c,v 1.1 90/07/19 13:53:35 dvadura Exp $
X-- SYNOPSIS -- string handling code
X-- 
X-- DESCRIPTION
X--	Routines to handle string manipulation.  This code is not specific
X--	to dmake and has/and will be used in other programs.  The string
X--	"" is considered the NULL string, if NIL(char) is received instead
X--	undefined results may occurr.  (In reality NIL(char) is checked for
X--	but in general it is not safe to assume NIL(char) ==  NULL)
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:	string.c,v $
X * Revision 1.1  90/07/19  13:53:35  dvadura
X * Initial Revision of Version 3.5
X * 
X*/
X
X#include "extern.h"
X#include "alloc.h"
X#include "db.h"
X
Xchar *
X_strjoin( src, data, n, fr )/*
X==============================
X   Join data to src according to value of n.
X
X      n = -1   - return strcat( src, data )
X      n >= 0   - return strncat( src, data, n )
X
X   FREE original src if fr == TRUE, else leave it alone */
X
Xchar *src;
Xchar *data;
Xint  n;
Xint  fr;
X{
X   char *t;
X   int  l;
X   int  flag = FALSE;
X
X   DB_ENTER( "_strjoin" );
X   
X   if( src  == NIL( char ) ) { src = ""; flag = TRUE; }
X   if( data == NIL( char ) ) data = "";
X   DB_PRINT( "str", ("Joining [%s] [%s] %d", src, data, n) );
X
X   if( n == -1 )  n = strlen( data );
X
X   l = strlen( src ) + n + 1;
X   if( (t = MALLOC( l, char )) == NIL( char ) ) No_ram();
X
X   strcpy( t, src );
X   if (n) strncat( t, data, n );
X   t[ l-1 ] = '\0';
X
X   if( !flag && fr ) FREE( src );
X
X   DB_PRINT( "str", ("Result  [%s]", t) );
X   DB_RETURN( t );
X}
X
X
X
X
Xchar *
X_stradd( src, data, fr )/*
X==========================
X   append data to src with space in between if src is not NIL( char ) or ""
X   and free both src and data if fr == TRUE, otherwise leave them be */
X
Xchar *src;
Xchar *data;
Xint  fr;
X{
X   char *t;
X   int  l;
X   int  sflag;
X   int  dflag;
X
X   DB_ENTER( "_stradd" );
X
X   sflag = dflag = fr;
X
X   if( src  == NIL( char ) ) { src  = ""; sflag = FALSE; }
X   if( data == NIL( char ) ) { data = ""; dflag = FALSE; }
X   DB_PRINT( "str", ("Adding [%s] [%s] %d", src, data, fr) );
X
X   l = strlen(src) + strlen(data) + 1;
X   if( *src ) l++;
X
X   if( (t = MALLOC( l, char )) == NIL( char ) ) No_ram();
X
X   strcpy( t, src );
X   
X   if( *data )
X   {
X      if( *src ) strcat( t,  " " );
X      strcat( t, data );
X   }
X
X   if( sflag )  FREE( src  );
X   if( dflag )  FREE( data );
X
X   DB_PRINT( "str", ("Result  [%s]", t) );
X   DB_RETURN( t );
X}
X
X
X
Xchar *
X_strapp( src1, src2 )/*
X=======================
X   Append two strings together, and return the result with a space between
X   the two strings.  FREE the first string if it is not NIL and always
X   leave the second string be. */
Xchar *src1;
Xchar *src2;
X{
X   src2 = _stradd( src1, src2, FALSE );
X   if( src1 != NIL( char ) ) FREE( src1 );
X   return( src2 );
X}
X
X
X
Xchar *
X_strdup( str )/*
X================  Duplicate the contents of a string, by using malloc */
Xchar *str;
X{
X   char *t;
X
X   if( str == NIL( char ) ) return( NIL( char ) );
X   
X   if( (t = MALLOC( strlen( str )+1, char )) == NIL( char ) ) No_ram();
X   strcpy( t, str );
X
X   return( t );
X}
X
X
X
X
Xchar *
X_strpbrk( s1, s2 )/*
X====================
X   find first occurence of char in s2 in string s1.
X   Returns a pointer to the first occurrence.  NOTE '\0' is considered part
X   of s2 and a pointer to it is returned if no other chars match. */
X
Xchar *s1;
Xchar *s2;
X{
X   register char *t;
X
X   if( s1 == NIL( char ) ) return( "" );
X
X   for( t=s1; *t && (strchr( s2, *t ) == NIL( char )); t++ );
X   return( t );
X}
X
X
X
X
Xchar *
X_strspn( s1, s2 )/*
X===================
X   return pointer to first char in s1 that does not belong to s2.
X   Returns the pointer if match found, else returns pointer to null char
X   in s1. (ie. "" ) */
X   
Xchar *s1;
Xchar *s2;
X{
X   register char *t;
X
X   if( s1 == NIL( char ) ) return( "" );
X
X   for( t=s1; *t && (strchr( s2, *t ) != NIL( char )); t++ );
X   return( t );
X}
X
X
X
X
Xchar *
X_strstr( s1, s2 )/*
X==================  find first occurrence in s1 of s2 */
Xchar *s1;
Xchar *s2;
X{
X   register char *s;
X   register char *p;
X   register char *r;
X
X   if( s1 != NIL(char) && s2 != NIL(char) )
X      for( s=s1; *s; s++ )
X	 if( *s == *s2 )
X	 {
X	    for( r=s+1, p = s2+1; *p && (*r == *p); r++, p++ );
X	    if( !*p ) return( s );
X	 }
X   
X   return( NIL( char ) );
X}
X
X
X
Xchar *
X_substr( s, e )/*
X=================
X      Return the string between the two pointers s and e, not including the
X      char that e points to.  NOTE:  This routine assumes that s and e point
X      into the same string. */
X
Xchar *s;
Xchar *e;
X{
X   char save;
X
X   save = *e;
X   *e   = '\0';
X   s    = _strdup( s );
X   *e   = save;
X
X   return( s );
X}
SHAR_EOF
chmod 0440 string.c || echo "restore of string.c fails"
echo "x - extracting stat.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > stat.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/stat.c,v 1.1 90/07/19 13:53:34 dvadura Exp $
X-- SYNOPSIS -- bind a target name to a file.
X-- 
X-- DESCRIPTION
X--	This file contains the code to go and stat a target.  The stat rules
X--	follow a predefined order defined in the comment for Stat_target.
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:	stat.c,v $
X * Revision 1.1  90/07/19  13:53:34  dvadura
X * Initial Revision of Version 3.5
X * 
X*/
X
X#include "extern.h"
X#include "alloc.h"
X#include "db.h"
X
X
Xstatic	int	_check_dir_list ANSI((CELLPTR, CELLPTR, int));
X
X#ifdef DBUG
X   /* Just a little ditty for debugging this thing */
X   static time_t
X   _do_stat( name, lib, sym )
X   char *name;
X   char *lib;
X   char **sym;
X   {
X      time_t res;
X      DB_ENTER( "_do_stat" );
X
X      res = Do_stat(name, lib, sym);
X      DB_PRINT( "stat", ("Statted [%s,%s,%d,%ld]", name, lib, sym, res) );
X
X      DB_RETURN( res );
X   }   
X#define DO_STAT(A,B,C)  _do_stat(A,B,C)
X#else
X#define DO_STAT(A,B,C)  Do_stat(A,B,C)
X#endif
X
Xstatic char *_first;	/* local storage of first attempted path */
X
Xvoid
XStat_target( cp, setfname )/*
X=============================
X	Stat a target.  When doing so follow the following rules, suppose
X	that cp->CE_NAME points at a target called fred.o:
X
X		0.      If A_SYMBOL attribute set look into the library
X			then do the steps 1 thru 4 on the resulting name.
X		1.	Try path's obtained by prepending any dirs found as
X			prerequisites for .SOURCE.o.
X		2.	If not found, do same as 2 but use .SOURCE
X		3.	If not found and .LIBRARYM attribute for the target is
X			set then look for it in the corresponding library.
X	        4.	If found in step 0 thru 3, then ce_fname points at
X			file name associate with target, else ce_fname points
X			at a file name built by the first .SOURCE* dir that
X			applied. */
X
XCELLPTR cp;
Xint     setfname;
X{
X   register HASHPTR hp;
X   static   HASHPTR srchp = NIL(HASH);
X   char		    *name;
X   char		    *tmp;
X   int		    res = 0;
X
X   DB_ENTER( "Stat_target" );
X
X   name = cp->CE_NAME;
X   if( srchp == NIL(HASH) ) srchp = Get_name(".SOURCE",Defs,FALSE,NIL(CELL));
X
X   /* Look for a symbol of the form lib((symbol)) the name of the symbol
X    * as entered in the hash table is (symbol) so pull out symbol and try
X    * to find it's module.  If successful DO_STAT will return the module
X    * as well as the archive member name (pointed at by tmp).  We then
X    * replace the symbol name with the archive member name so that we
X    * have the proper name for any future refrences. */
X
X   if( cp->ce_attr & A_SYMBOL ) {
X      DB_PRINT( "stat", ("Binding lib symbol [%s]", name) );
X      cp->ce_time = DO_STAT( name, cp->ce_lib, &tmp );
X
X      if( cp->ce_time != (time_t) 0L ) {
X	 /* stat the new member name below  note tmp must point at a string
X	  * returned by MALLOC... ie. the Do_stat code should use _strdup */
X
X	 if( Verbose )
X	    printf( "%s:  Mapped ((%s)) to %s(%s)\n", Pname,
X		     name, cp->ce_lib, tmp );
X
X         FREE( name );		
X	 name = cp->CE_NAME = tmp;		
X	 cp->ce_attr &= ~(A_FFNAME | A_SYMBOL);
X      }
X      else
X         { DB_VOID_RETURN; }
X   }
X
X   _first = NIL(char);
X   tmp = _strjoin( ".SOURCE", Get_suffix( name ), -1, FALSE);
X
X   /* Check .SOURCE.xxx target */
X   if( (hp = Get_name(tmp, Defs, FALSE, NIL(CELL))) != NIL(HASH) )
X      res = _check_dir_list( cp, hp->CP_OWNR, setfname );
X
X   /* Check just .SOURCE */
X   if( !res && (srchp != NIL(HASH)) )
X      res = _check_dir_list( cp, srchp->CP_OWNR, setfname );
X
X   /* If libmember and we haven't found it check the library */
X   if( !res && (cp->ce_attr & A_LIBRARYM) )
X       cp->ce_time = DO_STAT(name, cp->ce_lib, NIL(char *));
X
X   FREE( tmp );
X
X   if( (cp->ce_attr & A_FFNAME) && (cp->ce_fname != NIL(char)) )
X      FREE( cp->ce_fname );
X
X   if( setfname )
X      if( _first != NIL(char) ) {
X	 cp->ce_fname = _first;
X	 cp->ce_attr |= A_FFNAME;
X      }
X      else {
X	 cp->ce_fname = cp->CE_NAME;
X	 cp->ce_attr &= ~A_FFNAME;
X      }
X
X   /* set it as stated only if successful, this way, we shall try again
X    * later. */
X   if( cp->ce_time != (time_t)0L ) cp->ce_flag |= F_STAT;
X
X   DB_VOID_RETURN;
X}
X
X
X
Xstatic int
X_check_dir_list( cp, sp, setfname )/*
X=====================================
X	Check the list of dir's given by the prerequisite list of sp, for a
X	file pointed at by cp.  Returns 0 if path not bound, else returns
X	1 and replaces old name for cell with new cell name. */
X
XCELLPTR cp;
XCELLPTR sp;
Xint     setfname;
X{
X   register LINKPTR lp;
X   char *dir;
X   char *path;
X   char *name;
X   int  res  = 0;
X   int  fset = 0;
X
X   DB_ENTER( "_check_dir_list" );
X   DB_PRINT( "mem", ("%s:-> mem %ld", cp->CE_NAME, (long) coreleft()) );
X
X   if( sp->CE_HOW != NIL(HOW) )		/* check prerequisites if any */
X   {
X      /* Use the real name instead of basename, this prevents silly
X       * loops in inference code, and is consistent with man page */
X      name = cp->CE_NAME;
X
X      /* Here we loop through each directory on the list, and try to stat
X       * the target.  We always save the first pathname we try and stat in
X       * _first.  If we subsequently get a match we then replace the value of
X       * _first by the matched path name.  */
X
X      for( lp=sp->CE_PRQ; lp != NIL(LINK) && !res; lp=lp->cl_next ) {
X	 int  nodup = 0;
X	 dir  = lp->cl_prq->CE_NAME;
X
X	 if( strchr( dir, '$' ) ) dir = Expand(dir);
X	 if( strcmp( dir, ".NULL" ) == 0 ) {
X	    nodup = 1;
X	    path = cp->CE_NAME;
X	 }
X	 else
X	    path = Build_path( dir, name );
X
X	 res = ((cp->ce_time = DO_STAT(path,NIL(char),NIL(char *))) != (time_t)0L);
X
X	 /* Have to use _strdup to set _first since Build_path, builds it's
X	  * path names inside a static buffer. */
X	 if( setfname )
X	    if( (_first == NIL(char) && !fset) || res ) {
X	       if( _first != NIL(char) ) FREE( _first );
X	       _first = nodup ? NIL(char) : _strdup(path);
X	       fset = 1;
X	    }
X
X	 DB_PRINT( "stat", ("_first [%s], path [%s]", _first, path) );
X	 if( dir != lp->cl_prq->CE_NAME )  FREE(dir);
X      }
X   }
X
X   DB_PRINT( "mem", ("%s:-< mem %ld", cp->CE_NAME, (long) coreleft()) );
X   DB_RETURN( res );
X}
SHAR_EOF
chmod 0440 stat.c || echo "restore of stat.c fails"
echo "x - extracting rulparse.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > rulparse.c &&
X
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/rulparse.c,v 1.1 90/07/21 11:06:33 dvadura Exp $
X-- SYNOPSIS -- perform semantic analysis on input
X-- 
X-- DESCRIPTION
X--	This code performs semantic analysis on the input, and builds
X--	the complex internal datastructure that is used to represent
X--	the user makefile.
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:	rulparse.c,v $
X * Revision 1.1  90/07/21  11:06:33  dvadura
X * Initial Revision Version 3.5
X * 
X*/
X
X#include <ctype.h>
X#include "extern.h"
X#include "alloc.h"
X#include "db.h"
X
X/* prototypes for local functions */
Xstatic  void    _add_global_prereq ANSI((CELLPTR));
Xstatic	void	_build_graph ANSI((int, CELLPTR, CELLPTR));
Xstatic	char*	_build_meta ANSI((char*));
Xstatic	int	_do_magic ANSI((int, char*, CELLPTR, CELLPTR, int, char*));
Xstatic	void	_do_special ANSI((int, int, int, char*, CELLPTR, CELLPTR,int*));
Xstatic	int	_do_targets ANSI((int, int, char*, CELLPTR, CELLPTR));
Xstatic	int	_is_attribute ANSI((char*));
Xstatic	int	_is_special ANSI((char*));
Xstatic	char*	_is_magic ANSI((char*));
Xstatic	int	_is_percent ANSI((char*));
Xstatic	void	_set_attributes ANSI((int, char*, CELLPTR));
Xstatic	void	_stick_at_head ANSI((HOWPTR, CELLPTR));
Xstatic	void	_set_global_attr ANSI((int, char*));
X
X/* static variables that must persist across invocation of Parse_rule_def */
Xstatic CELLPTR    _sv_targets = NIL(CELL);
Xstatic STRINGPTR  _sv_rules   = NIL(STRING);
Xstatic STRINGPTR  _sv_crule   = NIL(STRING);
Xstatic EDGEPTR    _sv_edgel   = NIL(EDGE);
Xstatic LINKPTR    _sv_glb_prq = NIL(LINK);
Xstatic int	  _sp_target  = FALSE;
Xstatic int        _sv_attr;
Xstatic int        _sv_attro;
Xstatic int        _sv_flag;
Xstatic int	  _sv_op;
Xstatic char      *_sv_setdir;
Xstatic char	  _sv_globprq_only = 0;
X
X
Xint
XParse_rule_def( state )/*
X=========================
X   Parse the rule definition contained in Buffer, and modify the state 
X   if appropriate.  The function returns 0, if the definition is found to
X   be an illegal rule definition, and it returns 1 if it is a rule definition.
X   */
Xint *state;
X{
X   TKSTR 	input;		/* input string struct for token search	  */
X   CELLPTR	targets;	/* list of targets if any		  */
X   CELLPTR	prereq;		/* list of prereq if any		  */
X   CELLPTR	prereqtail;	/* tail of prerequisite list		  */
X   CELLPTR	cp;		/* temporary cell pointer for list making */
X   char 	*result;	/* temporary storage for result	  	  */
X   char		*tok;		/* temporary pointer for tokens		  */
X   char         *set_dir;       /* value of setdir attribute              */
X   char		*brk;		/* break char list for Get_token	  */
X   char         *firstrcp;      /* first recipe line, from ; in rule line */
X   int          attr;           /* sum of attribute flags for current tgts*/
X   int		op;		/* rule operator			  */
X   int		at;		/* temp place to keep an attribute code	  */
X   int		special;	/* indicate special targets in rule	  */
X   int		percent;	/* indicate percent rule target		  */
X   int		mixed_glob_prq; /* indicate mixed %-rule prereq possible  */
X
X   DB_ENTER( "Parse_rule_def" );
X
X   op	      = 0;
X   attr       = 0;
X   special    = 0;
X   percent    = 0;
X   set_dir    = NIL( char );
X   targets    = NIL(CELL);
X   prereq     = NIL(CELL);
X   prereqtail = NIL(CELL);
X   mixed_glob_prq = 0;
X
X   /* Check to see if the line is of the form:
X    *    targets : prerequisites; first recipe line
X    * If so remember the first_recipe part of the line. */
X
X   firstrcp = strchr( Buffer, ';' );
X   if( firstrcp != NIL( char ) ) {
X      *firstrcp++ = 0;
X      firstrcp = _strspn( firstrcp, " \t" );
X   }
X
X   result = Expand( Buffer );
X   for( brk=strchr(result,'\\'); brk != NIL(char); brk=strchr(brk,'\\') )
X      if( brk[1] == '\n' )
X	 *brk = ' ';
X      else
X         brk++;
X
X   DB_PRINT( "par", ("Scanning: [%s]", result) );
X
X   SET_TOKEN( &input, result );
X   brk = ":-^!";
X   Def_targets = TRUE;
X   
X   /* Scan the input rule line collecting targets, the operator, and any
X    * prerequisites.  Stop when we run out of targets and prerequisites. */
X
X   while( *(tok = Get_token( &input, brk, TRUE )) != '\0' )
X      if( !op ) {
X	 /* we are scanning targets and attributes
X	  * check to see if token is an operator.  */
X
X	 op = Rule_op( tok );
X
X	 if( !op ) {
X	    /* define a new cell, or get old cell  */
X	    cp = Def_cell(tok, NIL(CELL));
X	    DB_PRINT( "par", ("tg_cell [%s]", tok) );
X	    
X	    if( at = _is_attribute( tok ) ) {
X	       /* Logically OR the attributes specified into one main
X	        * ATTRIBUTE mask. */
X
X	       if( at == A_SETDIR )
X	          if( set_dir != NIL( char ) )
X	             Fatal( "Only one .SETDIR attribute allowed in rule line" );
X	          else
X	             set_dir = _strdup( tok );
X
X	       attr |= at;
X	    }
X	    else {
X	       int tmp;
X	       
X	       tmp = _is_special( tok );
X	       if( _is_percent( tok ) ) percent++;
X
X	       if( percent )
X	          if( targets != NIL(CELL) )
X		     Fatal( "Multiple targets are not allowed in %% rules" );
X		  else
X		     cp->ce_flag |= F_PERCENT;
X
X	       if( special )
X	          Fatal( "Special target must appear alone", tok );
X	       else if( !(cp->ce_flag & F_MARK) ) {
X		  cp->ce_link  = targets;  /* targets are stacked in this list*/
X		  cp->ce_flag |= F_MARK | F_EXPLICIT;
X		  targets      = cp;
X
X		  special = tmp;
X	       }
X	       else if( !(cp->ce_attr & A_LIBRARY) )
X		  Warning("Duplicate entry [%s] in target list",cp->CE_NAME);
X	    }
X	 }
X	 else {
X	    /* found an operator so empty out break list
X	     * and clear mark bits on target list, setting them all to F_USED */
X
X	    brk  = "";
X	    for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) {
X	       cp->ce_flag ^= F_MARK;
X	       cp->ce_flag |= F_USED;
X	    }
X
X	    Def_targets = FALSE;
X	 }
X      }
X      else {
X         /* Scanning prerequisites so build the prerequisite list.  We use
X          * F_MARK flag to make certain we have only a single copy of the
X          * prerequisite in the list */
X
X	 cp = Def_cell( tok, NIL(CELL) );
X
X	 if( _is_percent( tok ) ) {
X	    if( !percent && !attr )
X	       Fatal( "Syntax error in %% rule, missing %% target");
X	    mixed_glob_prq = 1;
X	 }
X
X	 if( cp->ce_flag & F_USED ) {
X	    if( cp->ce_attr & A_COMPOSITE )
X	       continue;
X	    else
X	       Fatal( "Detected circular dependency in graph at [%s]",
X		      cp->CE_NAME );
X	 }
X         else if( !(cp->ce_flag & F_MARK) ) {
X	    DB_PRINT( "par", ("pq_cell [%s]", tok) );
X	    cp->ce_flag |= F_MARK;
X
X	    if( prereqtail == NIL(CELL) )	/* keep prereq's in order */
X	       prereq = cp;
X	    else
X	       prereqtail->ce_link = cp;
X
X	    prereqtail = cp;
X	 }
X	 else if( !(cp->ce_attr & A_LIBRARY) )
X	    Warning("Duplicate entry [%s] in prerequisite list",cp->CE_NAME);
X      }
X      
X   /* Check to see if we have a percent rule that has only global
X    * prerequisites.  If so then set the flag so that later on, we don't issue
X    * an error if such targets supply an empty set of rules. */
X
X   if( percent && !mixed_glob_prq && (prereq != NIL(CELL)) )
X      _sv_globprq_only = 1;
X
X   /* It's ok to have targets with attributes, and no prerequisites, but it's
X    * not ok to have no targets and no attributes, or no operator */
X
X   if( !op ) {
X      CLEAR_TOKEN( &input );
X      DB_PRINT( "par", ("Not a rule [%s]", Buffer) );
X      DB_RETURN( 0 );
X   }
X
X   if( !attr && targets == NIL(CELL) ) {
X      Fatal( "Missing targets or attributes in rule" );
X      if( set_dir != NIL( char )) FREE( set_dir );
X      DB_RETURN( 0 );
X   }
X
X   /* We have established we have a legal rules line, so we must process it.
X    * In doing so we must handle any special targets.  Special targets must
X    * appear alone possibly accompanied by attributes.
X    * NOTE:  special != 0  ==> targets != NIL(CELL) */
X    
X   if( prereqtail != NIL(CELL) ) prereqtail->ce_link = NIL(CELL);
X
X   /* Clear out MARK bits used in duplicate checking.  I originally wanted
X    * to do this as the lists get processed but that got too error prone
X    * so I bit the bullit and added these two loops. */
X
X   for( cp=prereq;  cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_MARK;
X   for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_USED;
X
X   /* Check to see if the previous rule line was bound if, not the call
X    * Bind_rules_to_targets to go and bind the line */
X
X   if( _sv_rules != NIL(STRING) ) Bind_rules_to_targets( F_DEFAULT );
X
X   /* Add the first recipe line to the list */
X   if( firstrcp != NIL( char ) )
X      Add_recipe_to_list( firstrcp, TRUE, FALSE );
X
X   if( special )
X      _do_special( special, op, attr, set_dir, targets, prereq, state );
X   else
X      *state = _do_targets( op, attr, set_dir, targets, prereq );
X
X   _sv_op     = op;
X   _sv_setdir = set_dir;
X   DB_RETURN( 1 );
X}
X
X
X
X
Xint
XRule_op( op )/*
X================
X   Check the passed in op string and map it to one of the rule operators */
Xchar *op;
X{
X   int ret = 0;
X
X   DB_ENTER( "rule_op" );
X   
X   if( *op == TGT_DEP_SEP ) {
X      ret = R_OP_CL;
X      op++;
X
X      /* All rule operations begin with a :, but may include any one of the
X       * four modifiers.  In order for the rule to be properly mapped we must
X       * check for each of the modifiers in turn, building up our return bit
X       * string. */
X
X      while( *op && ret )
X         switch( *op ) {
X	    case ':': ret |= R_OP_DCL; op++; break;
X	    case '!': ret |= R_OP_BG;  op++; break;
X	    case '^': ret |= R_OP_UP;  op++; break;
X	    case '-': ret |= R_OP_MI;  op++; break;
X
X	    default : ret  = 0;  /* an invalid modifier, chuck whole string */
X         }
X
X      if( *op != '\0' ) ret = 0;
X   }
X
X   DB_RETURN( ret );
X}
X
X
X
X
Xvoid
XAdd_recipe_to_list( rule, white_too, no_check )/*
X=================================================
X        Take the provided string and add it to the list of recipe lines
X	we are saving to be added to the list of targets we have built
X	previously.  If white_too == TRUE add the rule EVEN IF it contains only
X        whitespace. */
Xchar *rule;
Xint  white_too;
Xint  no_check;
X{
X   DB_ENTER( "Add_recipe_to_list" );
X
X   if( rule != NIL( char ) && (*rule != '\0' || white_too) ) {
X      DB_PRINT( "par", ("Adding recipe [%s]", rule) );
X      _sv_crule = Def_recipe( rule, _sv_crule, white_too, no_check );
X
X      if( _sv_rules == NIL(STRING) )
X         _sv_rules = _sv_crule;
X   }
X
X   DB_VOID_RETURN;
X}
X
X
X
Xvoid
XBind_rules_to_targets( flag )/*
X===============================
X        Take the rules we have defined and bind them with proper attributes
X        to the targets that were previously defined in the parse.  The
X        attributes that get passed here are merged with those that are were
X        previously defined.  (namely F_SINGLE) */
Xint flag;
X{
X   CELLPTR tg;             /* pointer to current target in list */
X   LINKPTR lp;		   /* pointer to link cell		*/
X   HOWPTR  how;		   /* pointer to targets main HOW cell	*/
X   int     magic;          /* TRUE if target is .xxx.yyy form   */
X   int     tflag;          /* TRUE if we assigned targets here  */
X
X   DB_ENTER( "Bind_rules_to_targets" );
X
X   /* This line is needed since Parse may call us twice when the last
X    * GROUP rule appears at the end of file.  In this case the rules
X    * have already been bound and we want to ignore them. */
X
X   if( _sv_targets == NIL(CELL) ) { DB_VOID_RETURN; }
X
X   tflag  = FALSE;
X   flag  |= (_sv_flag & F_SINGLE);
X
X   for( tg = _sv_targets; tg != NIL(CELL); tg = tg->ce_link ) {
X      DB_PRINT( "par", ("Binding to %s, %04x", tg->CE_NAME, tg->ce_flag) );
X      magic = tg->ce_flag & F_PERCENT;
X
X      /* Check to see if we had a rule of the form '%.o : a.h b.h ; xxx'
X       * In which case we must build a NULL prq node to hold the recipe */
X
X      if( _sv_globprq_only && (_sv_rules != NIL(STRING)) )
X	 _build_graph( _sv_op, tg, NIL(CELL) );
X
X      /* NOTE:  For targets that are magic we ignore any previously defined
X       *        rules.  ie. We throw away the old definition and use the new. */
X
X      if( !(tg->ce_flag & F_MULTI) && !magic && (tg->CE_RECIPE != NIL(STRING))
X	  && !_sp_target && (_sv_rules != NIL(STRING)) )
X         Fatal( "Multiply defined recipe for target %s", tg->CE_NAME );
X
X      if( (magic || _sp_target) && (_sv_rules == NIL(STRING)) &&
X	  !(tg->ce_flag & F_SPECIAL) && !_sv_globprq_only )
X         Warning( "Empty recipe for special target %s", tg->CE_NAME );
X
X      if( magic ) {
X	 EDGEPTR el;
X
X	 for( el=_sv_edgel; el != NIL(EDGE); el=el->ed_link ) {
X	    how = el->ed_how;
X	    how->hw_flag |= flag;
X
X	    _set_attributes( _sv_attro, _sv_setdir, el->ed_tg );
X
X	    if( _sv_rules != NIL(STRING) ) {
X	       how->hw_recipe  = _sv_rules;
X	       how->hw_attr   |= _sv_attr & (A_SILENT | A_IGNORE);
X	       how->hw_indprq = _sv_glb_prq;
X	    }
X	 }
X      }
X      else {
X	 how = tg->CE_HOW;
X	 how->hw_flag |= flag;
X
X	 if( _sv_rules != NIL(STRING) ) {
X	    how->hw_recipe  = _sv_rules;
X	    how->hw_attr   |= _sv_attr & (A_SILENT | A_IGNORE);
X	    tg->ce_flag    |= F_RULES | F_TARGET;
X
X	    /* Bind the current set of prerequisites as belonging to the
X	     * original recipe given for the target */
X
X	    for( lp=how->hw_prq; lp != NIL(LINK); lp = lp->cl_next )
X		  if( !(lp->cl_flag & F_USED) ) lp->cl_flag |= F_TARGET;
X         }
X	 else
X	    for( lp=how->hw_prq; lp != NIL(LINK); lp = lp->cl_next )
X		  lp->cl_flag |= F_USED;
X      }
X
X      if( !Target && !magic && !(tg->ce_flag & F_SPECIAL) ) {
X	 Add_fringe( tg );
X
X	 tg->ce_flag |= F_TARGET;
X	 tflag        = TRUE;
X      }
X
X      /* Break since all prerequisites are attached and all targets in the
X       * .UPDATEALL list point at the same HOW cell. */
X      if( tg->ce_all != NIL(CELL) ) {
X	 CELLPTR tcp = tg;
X
X	 /* Make sure all people participating in a .UPDATEALL prerequisite
X	  * get marked as having rules and being a target if appropriate. */
X	 do {
X	    tcp->ce_flag |= tg->ce_flag & (F_RULES|F_TARGET);
X	    tcp = tcp->ce_all;
X	 }
X	 while( tcp != tg );
X	 break;
X      }
X   }
X
X   if( tflag ) Target = TRUE;
X   _sv_rules   = NIL(STRING);
X   _sv_crule   = NIL(STRING);
X   _sv_targets = NIL(CELL);
X   _sv_glb_prq = NIL(LINK);
X   _sv_edgel   = NIL(EDGE);
X   _sp_target  = FALSE;
X   _sv_globprq_only = 0;
X
X   DB_VOID_RETURN;
X}
X
X
X
Xint
XSet_group_attributes( list )/*
X==============================
X	Scan list looking for the standard @ and - (as in recipe line defs)
X	and set the flags accordingly so that they apply when we bind the
X	rules to the appropriate targets. */
Xchar *list;
X{
X   int done = FALSE;
X   int res  = FALSE;
X
X   DB_ENTER( "Set_group_attributes" );
X
X   while( !done )
X      switch( *list++ ) {
X	 case '@' : _sv_attr |= A_SILENT; res = TRUE; break;
X	 case '-' : _sv_attr |= A_IGNORE; res = TRUE; break;
X	 case '[' : 			  res = TRUE; break;
X
X         case ' ' :
X	 case '\t': break;
X
X	 case '\0': done = TRUE; break;
X
X	 default  : done = TRUE; res = FALSE; break;
X      }
X
X   DB_RETURN( res );
X}
X
X
X
Xstatic void
X_do_special( special, op, attr, set_dir, target, prereq, state )/*
X==================================================================
X   Process a special target.  So far the only special targets we have
X   are those recognized by the _is_special function.
X
X   target is always only a single special target.
X   
X   NOTE:  For the cases of .IMPORT, and .INCLUDE, the cells created by the
X   	  parser are never freed.  This is due to the fact that it is too much
X	  trouble to get them out of the hash table once they are defined, and
X	  if they are per chance used again it will be ok, anyway, since the
X	  cell is not really used by the code below.  */
X
Xint	special;
Xint	op;
Xint	attr;
Xchar	*set_dir;
XCELLPTR target;
XCELLPTR prereq;
Xint     *state;
X{
X   HASHPTR	hp;		/* pointer to macro def cell		*/
X   CELLPTR	cp;		/* temporary pointer into cells list	*/
X   CELLPTR 	dp;		/* pointer to directory dir cell	*/
X   LINKPTR 	lp;		/* pointer at prerequisite list 	*/
X   char    	*dir;		/* current dir to prepend		*/
X   char    	*path;		/* resulting path to try to read	*/
X   char 	*name;		/* File name for processing a .INCLUDE	*/
X   char		*tmp;		/* temporary string pointer		*/
X   FILE 	*fil;		/* File descriptor returned by Openfile	*/
X
X   DB_ENTER( "_do_special" );
X
X   target->ce_flag = F_SPECIAL;	/* mark the target as special */
X
X   switch( special ) {
X      case ST_EXPORT:
X	 for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
X	    DB_PRINT( "par", ("Exporting [%s]", prereq->CE_NAME) );
X	    hp = GET_MACRO( prereq->CE_NAME );
X
X	    if( hp != NIL(HASH) ) {
X	       char *tmpstr = hp->ht_value;
X
X	       if( tmpstr == NIL(char) ) tmpstr = "";
X
X	       if( Write_env_string( prereq->CE_NAME, tmpstr ) != 0 )
X		  Warning( "Could not export %s", prereq->CE_NAME );
X	    }
X	 }
X	 break;
X
X      case ST_IMPORT:
X	 if( prereq != NIL(CELL) && prereq->ce_link == NIL(CELL) &&
X	     strcmp(prereq->CE_NAME, ".EVERYTHING") == 0 )
X	 {
X	    ReadEnvironment();
X	 }
X	 else {
X	    char *tmpstr;
X
X	    for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
X	       DB_PRINT( "par", ("Importing [%s]", prereq->CE_NAME) );
X
X	       tmpstr = Read_env_string( prereq->CE_NAME );
X
X	       if( tmpstr != NIL(char) )
X		  Def_macro( prereq->CE_NAME, tmpstr, M_EXPANDED | M_LITERAL );
X	       else
X	          if( !((Glob_attr | attr) & A_IGNORE) )
X		     Fatal( "Imported macro `%s' not found", prereq->CE_NAME );
X	    }
X
X	    attr &= ~A_IGNORE;
X	 }
X	 break;
X
X      case ST_INCLUDE:
X      {
X	 int ignore = (Glob_attr | attr) & A_IGNORE;
X	 int pushed = FALSE;
X	 CELL inc;
X	 HASH hcell;
X
X	 if( prereq == NIL(CELL) )  Fatal( "No .INCLUDE file(s) specified" );
X
X	 dp = Def_cell( ".INCLUDEDIRS", NIL(CELL) );
X
X	 if( (attr & A_SETDIR) && *(dir = strchr(set_dir, '=')+1) ) {
X	    hcell.ht_name = ".INCLUDE";
X	    inc.ce_name   = &hcell;
X	    inc.ce_dir    = dir;
X	    pushed = Push_dir( &inc, ignore );
X	 }
X
SHAR_EOF
echo "End of part 4"
echo "File rulparse.c is continued in part 5"
echo "5" > s2_seq_.tmp
exit 0