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