v14i014: dmake version 3.5 part 4/21
Dennis Vadura
dvadura at watdragon.waterloo.edu
Fri Jul 27 09:43:43 AEST 1990
Posting-number: Volume 14, Issue 14
Submitted-by: dvadura at 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 at 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 at 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 at 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
More information about the Comp.sources.misc
mailing list