[comp.sources.unix] v12i008: Cake, a make replacement, Part02/09

rsalz@uunet.UU.NET (Rich Salz) (10/16/87)

Submitted-by: Zoltan Somogyi <zs@munnari.oz>
Posting-number: Volume 12, Issue 8
Archive-name: cake/part02

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	act.c
#	chase.c
#	entry.c
#	error.c
#	expand.c
#	file.c
# This archive created: Wed Oct 14 21:08:43 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'act.c'" '(12578 characters)'
if test -f 'act.c'
then
	echo shar: "will not over-write existing file 'act.c'"
else
sed 's/^X//' << \SHAR_EOF > 'act.c'
X/*
X**	Module to execute Cake actions.
X*/
X
Xstatic	char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/act.c,v 1.14 86/07/19 12:22:18 zs Exp $";
X
X#include	"cake.h"
X
X/*
X**	Update the node by executing the attached actions.
X**	Actually, all the buddies will be updated, and
X**	no action will be executed unless it is necessary.
X**
X**	The level of a file is one greater than the level of its
X**	parent in the chase graph; the level of !MAINCAKE! is 0.
X**	This controls the printing of messages about the primary
X**	targets.
X*/
X
Xupdate(node, level, force_exec)
Xreg	Node	*node;
Xreg	int	level;
Xreg	bool	force_exec;
X{
X	extern		get_utime();
X	extern		save_novol();
X	extern	bool	diff_novol();
X	extern	Wait	carry_out();
X	extern	char	*list_names();
X	reg	Node	*bnode, *onode;
X	reg	List	*ptr, *ptr1;
X	reg	bool	oksofar, needact, mayskip;
X	reg	List	*errnodes;
X	Wait		code;
X
X	put_trail("update", "start");
X	cdebug("hunting %s at level %d\n", node->n_name, level);
X
X	/* force printing of "up-to-date" messages */
X	if (level == 0 && is_ok(node))
X		node->n_kind = n_CANDO;
X		
X	if (off_node(node, nf_ERR) && is_ok(node))
X	{
X		cdebug("everything a-ok, nothing to do\n");
X		if (level == 1)
X		{
X			if (on_node(node, nf_ORIG))
X			{
X				printf("cake: target %s has no ancestors\n", node->n_name);
X				node->n_kind = n_NOWAY;
X			}
X			or (! xflag)
X				printf("cake: %s is up to date\n", node->n_name);
X		}
X
X		put_trail("update", "finish");
X		return;
X	}
X
X#ifdef	CAKEDEBUG
X	if (cakedebug)
X	{
X		printf("starting update of\n");
X		print_node(node);
X	}
X#endif
X
X	if (on_node(node, nf_ERR))
X	{
X		trace_errs(node);
X		return;
X	}
X
X	errnodes = NULL;
X
X	/* skip the actions if they are useless or not needed */
X	oksofar = TRUE;
X	needact = FALSE;
X	mayskip = FALSE;
X	for_list (ptr, node->n_new)
X	{
X		bnode  = (Node *) ldata(ptr);
X
X		if (on_node(bnode, nf_ERR))
X		{
X			oksofar = FALSE;
X			errnodes = addtail(errnodes, bnode);
X		}
X		else
X		{
X			if (off_node(bnode, nf_EXIST) || bnode->n_rtime < bnode->n_utime)
X				needact = TRUE;
X			
X			if (on_node(bnode, nf_DEPNONVOL))
X				mayskip = TRUE;
X		}
X	}
X
X	if (! oksofar)
X	{
X		cdebug("error in buddies\n");
X		if (vflag && strdiff(node->n_name, CHASEROOT))
X		{
X			sprintf(scratchbuf, "cannot proceed with update of %s because of problems with %s %s",
X				node->n_name, (length(errnodes) == 1)? "buddy": "buddies", list_names(errnodes));
X			add_error(node, new_name(scratchbuf), errnodes, TRUE);
X			trace_errs(node);
X		}
X
X		goto endit;
X	}
X
X	if (! needact)
X	{
X		cdebug("no need for actions\n");
X		if (strdiff(node->n_name, CHASEROOT))
X			printf("cake: %s is up to date\n", node->n_name);
X
X		goto endit;
X	}
X
X	/* update all ancestors of all buddies */
X	for_list (ptr, node->n_new)
X	{
X		bnode  = (Node *) ldata(ptr);
X		for_list (ptr1, bnode->n_old)
X		{
X			onode = (Node *) ldata(ptr1);
X			update(onode, level+1, force_exec);
X			if (on_node(onode, nf_ERR) || ! is_ok(onode))
X			{
X				oksofar = FALSE;
X				errnodes = addtail(errnodes, onode);
X			}
X		}
X	}
X
X	/* skip the actions if any ancestors are missing */
X	if (! oksofar)
X	{
X		cdebug("error in prerequisites\n");
X		if (vflag && strdiff(node->n_name, CHASEROOT))
X		{
X			sprintf(scratchbuf, "cannot proceed with update of %s because of problems with %s %s",
X				node->n_name, (length(errnodes) == 1)? "ancestor": "ancestors", list_names(errnodes));
X			add_error(node, new_name(scratchbuf), errnodes, TRUE);
X			trace_errs(node);
X		}
X
X		goto endit;
X	}
X
X	/* skip the actions; node is a missing base file */
X	if (is_noway(node))
X	{
X		cdebug("error in planning\n");
X		trace_errs(node);
X		goto endit;
X	}
X
X	/* find any NOWAY buddies; these may have n_old == [] */
X	for_list (ptr, node->n_new)
X	{
X		bnode  = (Node *) ldata(ptr);
X		if (is_noway(bnode))
X		{
X			oksofar = FALSE;
X			errnodes = addtail(errnodes, bnode);
X		}
X	}
X
X	/* skip the actions if there are any NOWAY buddies */
X	if (! oksofar)
X	{
X		cdebug("error in buddy planning\n");
X		sprintf(scratchbuf, "dare not update %s because of possible effects on %s %s", node->n_name,
X			(length(errnodes) == 1)? "buddy": "buddies", list_names(errnodes));
X		add_error(node, new_name(scratchbuf), LNULL, TRUE);
X		trace_errs(node);
X		goto endit;
X	}
X
X	/* reevaluate the necessity to act in the light of the */
X	/* actions taken to update possibly nonvolatile ancestors */
X	if (mayskip)
X	{
X		needact = FALSE; /* assume so for the moment */
X		cdebug("deciding whether to skip\n");
X		for_list (ptr, node->n_new)
X		{
X			bnode  = (Node *) ldata(ptr);
X
X			if (on_node(bnode, nf_DEPNONVOL))
X			{
X				cdebug("considering %s\n", bnode->n_name);
X				get_utime(bnode, FALSE);
X			}
X
X			if (off_node(bnode, nf_EXIST) || bnode->n_rtime < bnode->n_utime)
X			{
X				cdebug("%s needs action\n", bnode->n_name);
X				needact = TRUE;
X			}
X		}
X	}
X
X	if (! needact)
X	{
X		cdebug("no need for actions after all\n");
X		if (strdiff(node->n_name, CHASEROOT))
X			printf("cake: %s is up to date\n", node->n_name);
X
X		goto endit;
X	}
X
X	if (tflag)
X	{
X		/* instead of action just touch the targets */
X		for_list (ptr, node->n_new)
X		{
X			bnode = (Node *) ldata(ptr);
X			if (on_node(bnode, nf_PSEUDO))
X				continue;
X
X			if (rflag)
X				cake_utimes(bnode, bnode->n_utime);
X			else
X				cake_utimes(bnode, GENESIS);
X			
X			printf("touch %s\n", bnode->n_name);
X		}
X	}
X	else
X	{
X		/* prepare for actions */
X		for_list (ptr, node->n_new)
X		{
X			bnode = (Node *) ldata(ptr);
X			if (on_node(bnode, nf_NONVOL))
X				save_novol(bnode);
X		}
X
X		/* execute actions */
X		code = carry_out(node, force_exec);
X		if (code.w_status != 0)
X		{
X			oksofar = FALSE;
X			sprintf(scratchbuf, "error in actions for %s", node->n_name);
X			for_list (ptr, node->n_new)
X			{
X				bnode = (Node *) ldata(ptr);
X				add_error(bnode, new_name(scratchbuf), LNULL, TRUE);
X			}
X
X			if (! xflag)
X			{
X				if (code.w_termsig == 0)
X					printf("*** Error code %d\n", code.w_retcode);
X				else
X					printf("*** Termination code %d\n", code.w_termsig);
X			}
X			
X			cake_error(node);
X			goto endit;
X		}
X
X		/* clean up after actions */
X		for_list (ptr, node->n_new)
X		{
X			bnode = (Node *) ldata(ptr);
X			if (on_node(bnode, nf_NONVOL) && ! diff_novol(bnode))
X				node_resetstat(bnode);
X			or (rflag)
X				node_setstat(bnode);
X			else
X				node_stat(bnode);
X		}
X	}
X
Xendit:
X
X	cdebug("update finished\n");
X	for_list (ptr, node->n_new)
X	{
X		bnode = (Node *) ldata(ptr);
X		if (oksofar)
X		{
X			if (on_node(bnode, nf_EXIST) || on_node(bnode, nf_PSEUDO) || nflag)
X				bnode->n_kind = n_OK;
X			else
X			{
X				set_node(bnode, nf_ERR);
X				if (length(node->n_act) == 0)
X					sprintf(scratchbuf, "no actions to make %s with", bnode->n_name);
X				else
X					sprintf(scratchbuf, "action did not create %s", bnode->n_name);
X
X				add_error(bnode, new_name(scratchbuf), LNULL, TRUE);
X				trace_errs(bnode);
X			}
X		}
X
X#ifdef	CAKEDEBUG
X		if (cakedebug)
X			print_node(bnode);
X#endif
X	}
X
X	put_trail("update", "finish");
X}
X
X/*
X**	Execute the plan prepared by chase.c
X**	Prevent the deletion of the primary targets by cleanup.
X*/
X
Xexecute(root)
Xreg	Node	*root;
X{
X	reg	List	*ptr;
X	reg	Node	*target;
X
X	for_list (ptr, root->n_old)
X	{
X		target = (Node *) ldata(ptr);
X		set_node(target, nf_NODELETE);
X	}
X
X	update(root, 0, FALSE);
X}
X
X/*
X**	Carry out the specified actions and return the exit code.
X**	Note that an empty list of actions is perfectly acceptable.
X*/
X
X#define	START_SCRIPT	"{"
X#define	FINISH_SCRIPT	"}"
X
XWait
Xcarry_out(node, force_exec)
Xreg	Node	*node;
Xreg	bool	force_exec;
X{
X	extern	char	*expand_cmds();
X	extern	Wait	action();
X	reg	List	*ptr;
X	reg	Act	*act;
X	reg	Node	*bnode;
X	Wait		code;
X
X	if (Gflag && ! nflag)
X	{
X		for_list (ptr, node->n_new)
X		{
X			bnode = (Node *) ldata(ptr);
X			if (on_node(bnode, nf_EXIST))
X			{
X				cdebug("removing %s for -G\n", bnode->n_name);
X				cake_remove(bnode->n_name);
X			}
X		}
X	}
X
X	for_list (ptr, node->n_act)
X	{
X		act = (Act *) ldata(ptr);
X
X		if (nflag)
X		{
X			if (off_act(act, af_MINUSN) && ! force_exec)
X				show_act(act->a_str, (char *) NULL);
X			else
X			{
X				reset_act(act, af_SILENT);
X				printf("executing ...\n");
X				code = action(act, node);
X				printf("... done\n");
X				if (code.w_status != 0)
X					return code;
X			}
X
X			continue;
X		}
X
X		code = action(act, node);
X		if (code.w_status != 0)
X			return code;
X	}
X
X	code.w_status = 0;
X	return code;
X}
X
X/*
X**	Execute the given command and return its status,
X**	modified by flags and prefixes.
X*/
X
XWait
Xaction(act, node)
Xreg	Act	*act;
Xreg	Node	*node;
X{
X	extern	int	cake_proc();
X	extern	Wait	cake_wait();
X	Wait		code;
X	reg	A_kind	type;
X	reg	int	pid;
X	reg	char	*after;
X
X	put_trail("action", "start");
X	after = expand_cmds(act->a_str);
X	if (! (on_act(act, af_SILENT) || sflag))
X		show_act(act->a_str, after);
X	act->a_str = after;
X
X	if (on_act(act, af_SCRIPT))
X		type = Script;
X	or (on_act(act, af_SYSTEM))
X		type = System;
X	else
X		type = Exec;
X
X	pid = cake_proc(act->a_str, type, (char *) NULL, node, (int (*)()) NULL, (List *) NULL);
X	code = cake_wait(pid);
X
X	if (on_act(act, af_IGNORE) || iflag)
X		code.w_status = 0;
X
X	if (code.w_status != 0 && ! kflag)
X		exit_cake(FALSE);
X
X	put_trail("action", "finish");
X	return code;
X}
X
X/*
X**	Print an action in the format specified by the options.
X**	The two args are the action before and after expansion.
X**	The second may be NULL, in which case show_act does the
X**	expansion itself.
X*/
X
Xshow_act(before, after)
Xreg	char	*before;
Xreg	char	*after;
X{
X	extern	char	*squeeze();
X	reg	char	*form;
X
X	if (bflag)
X		form = before;
X	or (after != NULL)
X		form = after;
X	else
X		form = expand_cmds(before);
X
X	if (wflag)
X		printf("%s", form);
X	else
X		printf("%s", squeeze(form));
X}
X
X/*
X**	Reduce the width of the given command string
X**	by squeezing out extra spaces and tabs.
X*/
X
Xchar *
Xsqueeze(cmd)
Xreg	char	*cmd;
X{
X	char		buf[MAXSIZE];
X	reg	char	*s;
X	reg	int	i, oldi;
X	reg	bool	inquotes;
X	reg	bool	insingle;
X	reg	bool	indouble;
X	reg	bool	lastblank;
X
X	s = cmd;
X	i = 0;
X	lastblank = FALSE;
X	while (*s != '\0')
X	{
X		while (*s != '\0' && (*s == ' ' || *s == '\t'))
X			s++;
X
X		if (lastblank && (*s == '\n' || *s == '\r' || *s == '\f'))
X			i--;
X
X		oldi = i;
X		inquotes = FALSE;
X		for (; *s != '\0' && ((*s != ' ' && *s != '\t') || inquotes); s++)
X		{
X			if (*s == '\\')
X			{
X				buf[i++] = *s;
X				if (s[1] != '\0')
X					buf[i++] = *++s;
X			}
X			else
X			{
X				if (*s == '"' && ! (inquotes && insingle))
X				{
X					inquotes = ! inquotes;
X					indouble = TRUE;
X					insingle = FALSE;
X				}
X				or (*s == '\'' && ! (inquotes && indouble))
X				{
X					inquotes = ! inquotes;
X					insingle = TRUE;
X					indouble = FALSE;
X				}
X
X				buf[i++] = *s;
X			}
X		}
X
X		if (i == oldi || buf[i-1] == '\n' || buf[i-1] == '\r' || buf[i-1] == '\f')
X			lastblank = FALSE;
X		else
X		{
X			lastblank = TRUE;
X			buf[i++] = ' ';
X		}
X	}
X
X	buf[i] = '\0';
X	if (i >= MAXSIZE)
X	{
X		fprintf(stderr, "cake internal error: command '%s' too long\n", buf);
X		exit_cake(FALSE);
X	}
X
X	while (buf[i-1] == ' ' || buf[i-1] == '\t')
X		buf[--i] = '\0';
X
X	return new_name(buf);
X}
X
X/*
X**	Clean up after an error or interrupt.
X*/
X
Xcake_error(node)
Xreg	Node	*node;
X{
X	reg	List	*ptr, *ptr1;
X	reg	Node	*bnode, *onode;
X
X	for_list (ptr, node->n_new)
X	{
X		bnode = (Node *) ldata(ptr);
X		for_list (ptr1, bnode->n_old)
X		{
X			onode = (Node *) ldata(ptr1);
X			set_node(onode, nf_NODELETE);
X		}
X
X		if (on_node(bnode, nf_EXIST) && off_node(bnode, nf_PRECIOUS))
X			cake_remove(bnode->n_name);
X	}
X}
X
X/*
X**	Clean up after all the fuss.
X*/
X
Xcleanup()
X{
X	extern	List	*get_allnodes();
X	reg	List	*ptr, *ptr1;
X	reg	List	*nodes;
X	reg	Node	*node, *onode;
X
X	cdebug("cleanup:\n");
X
X	if (nflag)
X		return;
X
X	nodes = get_allnodes();
X	for_list (ptr, nodes)
X	{
X		node = (Node *) ldata(ptr);
X		cdebug("considering %s: ", node->n_name);
X
X		if (off_node(node, nf_EXIST))
X		{
X			cdebug("nonexistent\n");
X			continue;
X		}
X
X		if (on_node(node, nf_ERR) || is_noway(node))
X		{
X			cdebug("errors or noway\n");
X			continue;
X		}
X
X		if (! dflag && off_node(node, nf_REDUNDANT))
X		{
X			cdebug("no flag\n");
X			continue;
X		}
X
X		/* file exists and OK, shall we delete it ? */
X
X		/* not if we thought earlier it need it */
X		if (on_node(node, nf_NODELETE))
X		{
X			cdebug("nodelete flag\n");
X			continue;
X		}
X
X		/* not if it cannot be regenerated at all */
X		if (length(node->n_act) == 0)
X		{
X			cdebug("nonregenerable\n");
X			continue;
X		}
X
X		/* not if it cannot be regenerated as is */
X		if ((node->n_utime < node->n_rtime) && off_node(node, nf_NEWFILE))
X		{
X			cdebug("nonregenerable as is\n");
X			continue;
X		}
X
X		/* or if its ancestors are not all OK */
X		for_list (ptr1, node->n_old)
X		{
X			onode = (Node *) ldata(ptr1);
X			if (! is_ok(onode))
X			{
X				cdebug("ancestor not ok\n");
X				goto nextnode;
X			}
X		}
X
X		cdebug("DELETED\n");
X		cake_remove(node->n_name);
X	
X	nextnode:	;
X	}
X}
SHAR_EOF
if test 12578 -ne "`wc -c < 'act.c'`"
then
	echo shar: "error transmitting 'act.c'" '(should have been 12578 characters)'
fi
fi
echo shar: "extracting 'chase.c'" '(13609 characters)'
if test -f 'chase.c'
then
	echo shar: "will not over-write existing file 'chase.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chase.c'
X/*
X**	Module to chase Cake dependencies.
X*/
X
Xstatic	char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/chase.c,v 1.14 86/07/19 12:22:39 zs Exp $";
X
X#include	"cake.h"
X
X/*
X**	The main chasing function. It checks all entries to see
X**	if they describe a dependency of the name. If the second arg
X**	is not NULL, it is taken as a fixed choice (by another do_chase)
X**	as to which entry will actually be used to generate the name.
X**
X**	The kinds of nodes returned by do_chase are as follows.
X**	OK means that no action is needed to update the name.
X**	CANDO means that the action is known and there is no reason
X**	why it shouldn't succeed. NOWAY is set when neither of those
X**	conditions is satisfied. nf_ERR means that some error has
X**	occurred; nf_ERR nodes are never touched again; except for
X**	printing the error messages themselves. (NOWAY nodes *are*
X**	touched by act.c to update as many ancestors as possible.)
X**
X**	Whenever nf_ERR is not set, do_chase sets the utime field,
X**	and the rtime field as well if the file exists.
X*/
X
Xdo_chase(node, picked)
Xreg	Node	*node;
Xreg	Entry	*picked;
X{
X	extern		get_utime();
X	extern	time_t	cake_gettime();
X	extern	Node	*chase();
X	extern	Node	*chase_node();
X	extern	time_t	pick_time();
X	extern	bool	match();
X	extern	bool	eval();
X	extern	Entry	*ground_entry();
X	extern	Test	*deref_test();
X	extern		deref_entry();
X	extern	List	*entries;
X	extern	char	*list_names();
X	Env		env;
X	reg	List	*ptr, *ptr1, *ptr2;
X	reg	Node	*newnode, *onode;
X	reg	Entry	*entry, *newentry;
X	reg	Pat	*pat;
X	reg	List	*entry_anay, *entry_ayea;	/* of Entry	*/
X	reg	List	*miss_anay, *miss_ayea;		/* of Node	*/
X	reg	List	*pot_entry;		/* of Entry	*/
X	reg	List	*pot_pat;		/* of Pat	*/
X	reg	List	*probe_old;		/* of Node	*/
X	reg	bool	found, missing;
X	reg	bool	singleton, orphan;
X
X	put_trail("do_chase", "start");
X	cdebug("chasing %s\n", node->n_name);
X
X	if (off_node(node, nf_EXIST))
X		set_node(node, nf_NEWFILE);
X
X	/* find all entries which could generate this name */
X	pot_entry = makelist0();
X	pot_pat   = makelist0();
X	for_list (ptr, entries)
X	{
X		entry = (Entry *) ldata(ptr);
X		/* try everything on the left side of the entry */
X		for_list (ptr1, entry->e_new)
X		{
X			pat = (Pat *) ldata(ptr1);
X			if (match(env, node->n_name, pat))
X			{
X				if ((newentry = ground_entry(env, entry)) == (Entry *) NULL)
X				{
X					sprintf(scratchbuf, "cannot perform substitutions on entry for %s",
X						node->n_name);
X					add_error(node, new_name(scratchbuf), LNULL, TRUE);
X					goto end;
X				}
X
X#ifdef	CAKEDEBUG
X				if (entrydebug)
X				{
X					printf("Applicable entry\n");
X					print_entry(newentry);
X				}
X#endif
X
X				addtail(pot_entry, newentry);	/* na */
X				addtail(pot_pat, pat);		/* na */
X				break;
X			}
X		}
X	}
X
X	cdebug("%s: %d potentials\n", node->n_name, length(pot_entry));
X
X	/* find out which tests are satisfied */
X	entry_anay = makelist0();
X	entry_ayea = makelist0();
X	for_2list (ptr1, ptr2, pot_entry, pot_pat)
X	{
X		entry = (Entry *) ldata(ptr1);
X		pat = (Pat *) ldata(ptr2);
X
X		for_list (ptr, entry->e_when)
X		{
X			reg	Node	*whennode;
X			reg	Pat	*whenpat;
X
X			whenpat  = (Pat *) ldata(ptr);
X			whennode = chase(whenpat->p_str, whenpat->p_flag, (Entry *) NULL);
X			if (off_node(whennode, nf_ERR))
X			{
X				if (is_ok(whennode))
X					continue;
X
X				if (nflag && off_node(whennode, nf_WARNED))
X				{
X					printf("cake -n: warning, need to make %s to find dependencies\n",
X						whenpat->p_str);
X					set_node(whennode, nf_WARNED);
X				}
X
X				update(whennode, 100, TRUE);
X			}
X
X			if (on_node(whennode, nf_ERR) || ! is_ok(whennode))
X			{
X				sprintf(scratchbuf, "cannot find out if entry applies to %s",
X					node->n_name);
X				add_error(node, new_name(scratchbuf), makelist(whennode), TRUE);
X				return;
X			}
X		}
X
X#ifdef	CAKEDEBUG
X		if (entrydebug)
X		{
X			printf("about to test condition\n");
X			print_entry(entry);
X		}
X#endif
X
X		entry->e_cond = deref_test(entry->e_cond);
X		if (! eval(node, entry->e_cond, env))
X		{
X			cdebug("condition FALSE\n");
X			continue;
X		}
X
X		/* here we know that this entry applies */
X		deref_entry(env, entry);
X
X		if (Lflag)
X		{
X			for_list (ptr, entry->e_old)
X			{
X				reg	Pat	*opat;
X
X				opat = (Pat *) ldata(ptr);
X				if (streq(node->n_name, opat->p_str))
X				{
X					cdebug("found loop in entry at %s\n", node->n_name);
X					continue;
X				}
X			}
X		}
X
X		node->n_flag |= pat->p_flag;
X		if (length(entry->e_act) == 0)
X			addtail(entry_anay, entry);	/* no assigment */
X		else
X			addtail(entry_ayea, entry);	/* no assigment */
X	}
X
X	if (on_node(node, nf_ERR))
X		return;
X
X	cdebug("%s: no action: %d, with actions: %d\n",
X		node->n_name, length(entry_anay), length(entry_ayea));
X
X	/* All entries considered from now on are ground */
X
X	/* see if the file is an original */
X	if (length(entry_anay) == 0 && length(entry_ayea) == 0)
X	{
X		if (off_node(node, nf_EXIST))
X		{
X			cdebug("%s: noway\n", node->n_name);
X			node->n_kind  = n_NOWAY;
X			node->n_utime = GENESIS;
X			sprintf(scratchbuf, "base file %s does not exist", node->n_name);
X			add_error(node, new_name(scratchbuf), LNULL, FALSE);
X			goto end;
X		}
X
X		set_node(node, nf_ORIG);
X		cdebug("%s: orig ok\n", node->n_name);
X		node->n_kind  = n_OK;
X		node->n_utime = node->n_rtime;
X		goto end;
X	}
X
X	/* see to actionless dependencies */
X	missing = FALSE;
X	miss_anay = NULL;
X	for_list (ptr, entry_anay)
X	{
X		entry = (Entry *) ldata(ptr);
X		for_list (ptr1, entry->e_old)
X		{
X			pat = (Pat *) ldata(ptr1);
X			newnode = chase(pat->p_str, pat->p_flag, (Entry *) NULL);
X			if (on_node(newnode, nf_ERR) || is_noway(newnode))
X			{
X				missing = TRUE;
X				miss_anay = addtail(miss_anay, newnode);
X			}
X
X			addtail(node->n_old, newnode);	/* na */
X		}
X	}
X
X	cdebug("%s: unconditional dependencies, %s\n",
X		node->n_name, missing? "some missing": "all there");
X
X	/*
X	**	If necessary, choose a feasible action dependency.
X	**	However, if there is only one, select it even if
X	**	it is not feasible.
X	*/
X
X	found = FALSE;
X	miss_ayea = NULL;
X	if (picked == (Entry *) NULL)
X	{
X		singleton = (length(entry_ayea) == 1);
X		orphan    = (length(entry_ayea) == 0);
X
X		for_list (ptr, entry_ayea)
X		{
X			entry = (Entry *) ldata(ptr);
X			/* first copy of this code, with chase */
X			found = TRUE; /* assume it for the moment */
X			probe_old = makelist0();
X			for_list (ptr1, entry->e_old)
X			{
X				pat = (Pat *) ldata(ptr1);
X				newnode = chase(pat->p_str, pat->p_flag, (Entry *) NULL);
X				if (on_node(newnode, nf_ERR) || is_noway(newnode))
X				{
X					found = FALSE;
X					miss_ayea = addtail(miss_ayea, newnode);
X					if (! singleton)
X						break;
X				}
X
X				addtail(probe_old, newnode);	/* na */
X			}
X
X			if (found)
X				break;
X		}
X	}
X	else
X	{
X		singleton = TRUE;
X		orphan    = FALSE;
X
X		cdebug("%s: was given generator\n", node->n_name);
X		entry = picked;
X
X		/* second copy of this code, with chase_node */
X		found = TRUE; /* assume it for the moment */
X		probe_old = makelist0();
X		for_list (ptr1, entry->e_old)
X		{
X			pat = (Pat *) ldata(ptr1);
X			newnode = chase_node(pat->p_str);
X			if (on_node(newnode, nf_ERR) || is_noway(newnode))
X			{
X				found = FALSE;
X				miss_ayea = addtail(miss_ayea, newnode);
X				if (! singleton)
X					break;
X			}
X
X			addtail(probe_old, newnode);	/* na */
X		}
X	}
X
X	if (found)
X	{
X		/* we have found an applicable entry */
X		cdebug("%s: found feasible generator\n", node->n_name);
X		addlist(node->n_old, probe_old);	/* na */
X		node->n_act = entry->e_act;
X		if (picked == (Entry *) NULL)
X			set_buddies(node, entry);
X		picked = entry;
X	}
X	or (singleton)
X	{
X		/* we have only one (inapplicable) entry */
X		/* so we shall make do as best we can */
X		cdebug("%s: found single infeasible generator\n", node->n_name);
X		addlist(node->n_old, probe_old);	/* na */
X		node->n_act = entry->e_act;
X		if (picked == (Entry *) NULL)
X			set_buddies(node, entry);
X		picked = entry;
X	}
X	or (orphan && on_node(node, nf_PSEUDO))
X	{
X		/* no entry is needed, act as we had one */
X		found = TRUE;
X		cdebug("%s: no generator, no problem\n", node->n_name);
X		node->n_act = makelist0();
X	}
X	else
X	{
X		cdebug("%s: no feasible generator\n", node->n_name);
X		node->n_act = makelist0();
X	}
X
X	if (length(node->n_act) > 0 && length(picked->e_old) == 0)
X		node->n_utime = cake_gettime();
X	else
X		get_utime(node, TRUE);
X
X	for_list (ptr, node->n_old)
X	{
X		newnode = (Node *) ldata(ptr);
X		if (on_node(newnode, nf_NONVOL))
X			set_node(node, nf_DEPNONVOL);
X	}
X
X	if (missing)
X	{
X		node->n_kind = n_NOWAY;
X		sprintf(scratchbuf, "%s is missing the prerequisite%s %s",
X			node->n_name, length(miss_anay)==1? "": "s", list_names(miss_anay));
X		add_error(node, new_name(scratchbuf), miss_anay, FALSE);
X	}
X	or (! found)
X	{
X		node->n_kind = n_NOWAY;
X		if (miss_ayea == (List *) NULL)
X			sprintf(scratchbuf, "%s has no applicable entries with actions\n",
X				node->n_name);
X		else
X			sprintf(scratchbuf, "%s is missing the ancestor%s %s",
X				node->n_name, length(miss_ayea)==1? "": "s", list_names(miss_ayea));
X
X		add_error(node, new_name(scratchbuf), miss_ayea, FALSE);
X	}
X	or (on_node(node, nf_EXIST) && node->n_utime <= node->n_rtime)
X		node->n_kind = n_OK;
X	or (on_node(node, nf_PSEUDO) && length(node->n_act) == 0)
X	{
X		node->n_kind = n_OK;
X		for_list (ptr, node->n_old)
X		{
X			onode = (Node *) ldata(ptr);
X			if (is_cando(onode))
X				node->n_kind = n_CANDO;
X		}
X	}
X	else
X		node->n_kind = n_CANDO;
X	
Xend:
X#ifdef	CAKEDEBUG
X	if (cakedebug)
X	{
X		printf("chase exit\n");
X		print_node(node);
X	}
X#endif
X
X	put_trail("do_chase", "finish");
X}
X
X/*
X**	Calculate the "update time" of the given node. The second arg
X**	tells us whether we should set a flag for nonvolatile ancestors.
X*/
X
Xget_utime(node, planning)
Xreg	Node	*node;
Xreg	bool	planning;
X{
X	extern	time_t	get_youngest(), cake_gettime();
X	reg	time_t	youngest;
X
X	if ((youngest = get_youngest(node, planning)) == GENESIS)
X		node->n_utime = cake_gettime();
X	else
X		node->n_utime = youngest;
X}
X
X/*
X**	Return the time of update of the youngest dependent.
X*/
X
Xtime_t
Xget_youngest(node, planning)
Xreg	Node	*node;
Xreg	bool	planning;
X{
X	reg	List	*ptr;
X	reg	Node	*onode;
X	reg	time_t	youngest, picked_time;
X	reg	List	*errnodes;
X
X	youngest = GENESIS;
X	errnodes = NULL;
X	for_list (ptr, node->n_old)
X	{
X		onode = (Node *) ldata(ptr);
X		if (on_node(onode, nf_ERR))
X			errnodes = addtail(errnodes, onode);
X		else
X		{
X			if ((picked_time = pick_time(onode, planning)) > youngest)
X				youngest = picked_time;
X		}
X	}
X
X	if (errnodes != (List *) NULL)
X	{
X		sprintf(scratchbuf, "don't know what to do with %s because of problems with %s",
X			node->n_name, list_names(errnodes));
X		add_error(node, new_name(scratchbuf), errnodes, TRUE);
X	}
X
X	return youngest;
X}
X
X/*
X**	Take care of the names mentioned alongside node's
X**	on the left hand side of the given ground entry.
X**
X**	If a node exists for one of these names we know that
X**	a producer must have been chosen for it, and we know that
X**	it is not this rule (if it were, then the caller would not
X**	have done anything about buddies). This is an inconsistency.
X*/
X
Xset_buddies(node, entry)
Xreg	Node	*node;
Xreg	Entry	*entry;
X{
X	extern	Node	*chase_node();
X	reg	List	*list;
X	reg	List	*ptr;
X	reg	Node	*newnode;
X	reg	Pat	*pat;
X	reg	List	*errnodes;
X
X	put_trail("set_buddies", "start");
X	list = makelist(node);
X	errnodes = NULL;
X	for_list (ptr, entry->e_new)
X	{
X		pat = (Pat *) ldata(ptr);
X		if (streq(pat->p_str, node->n_name))
X			continue;
X		
X		if ((newnode = chase_node(pat->p_str)) != (Node *) NULL)
X			errnodes = addtail(errnodes, newnode);
X		else
X		{
X			newnode = chase(pat->p_str, pat->p_flag, entry);
X			addtail(list, newnode);	/* no assigment */
X		}
X	}
X
X	for_list (ptr, list)
X	{
X		newnode = (Node *) ldata(ptr);
X		newnode->n_new = list;
X
X		if (errnodes != (List *) NULL)
X		{
X			sprintf(scratchbuf, "cannot update %s because of interference with %s %s",
X				newnode->n_name, (length(errnodes) == 1)? "buddy": "buddies",
X				list_names(errnodes));
X			add_error(node, new_name(scratchbuf), errnodes, TRUE);
X		}
X	}
X
X	put_trail("set_buddies", "finish");
X}
X
X/*
X**	Pick the appropriate time to use in decisions
X**	about dependencies. The algorithms differ in the
X**	twp phases: in the execution phase real times
X**	override any calculated times.
X*/
X
Xtime_t
Xpick_time(node, planning)
Xreg	Node	*node;
Xreg	bool	planning;
X{
X	if (off_node(node, nf_EXIST))
X		return node->n_utime;
X	
X	if (planning)
X		return max(node->n_rtime, node->n_utime);
X
X	return node->n_rtime;
X}
X
X/*
X**	Chase down the ancestors of the given name.
X**	Return the node describing the dependency graph.
X**
X**	Note that this is merely an interface function,
X**	serving to cache previous results and to detect cycles.
X*/
X
Xchar	*goal_stack[MAXGSTACK];
Xint	goal_stackp = 0;
X
XNode *
Xchase(name, flag, picked)
Xreg	char	*name;
Xreg	bool	flag;
Xreg	Entry	*picked;
X{
X	extern	Table	node_tab;
X	extern		do_chase();
X	extern	char	*find_circle();
X	reg	Node	*node;
X
X	if ((node = (Node *) lookup_table(node_tab, name)) == NULL)
X	{
X		node = make_node(name);
X		if (goal_stackp >= MAXGSTACK)
X		{
X			printf("cake: dependency nesting level %d too deep\n", goal_stackp);
X			exit_cake(FALSE);
X		}
X
X		goal_stack[goal_stackp++] = name;
X#ifdef	CAKEDEBUG
X		cdebug("stack[%d] = %s\n", goal_stackp-1, goal_stack[goal_stackp-1]);
X#endif
X		set_node(node, nf_BUSY);
X		insert_table(node_tab, node);
X		do_chase(node, picked);
X		--goal_stackp;
X		reset_node(node, nf_BUSY);
X	}
X	else
X	{
X		cdebug("Using cache for %s\n", name);
X		if (on_node(node, nf_BUSY))
X		{
X			node->n_flag |= flag;
X			sprintf(scratchbuf, "%s depends upon itself %s", node->n_name,
X				find_circle(node->n_name));
X			add_error(node, new_name(scratchbuf), LNULL, TRUE);
X			put_trail("chase", "finish");
X			return node;
X		}
X	}
X
X	node->n_flag |= flag;
X	put_trail("chase", "finish");
X	return node;
X}
SHAR_EOF
if test 13609 -ne "`wc -c < 'chase.c'`"
then
	echo shar: "error transmitting 'chase.c'" '(should have been 13609 characters)'
fi
fi
echo shar: "extracting 'entry.c'" '(11376 characters)'
if test -f 'entry.c'
then
	echo shar: "will not over-write existing file 'entry.c'"
else
sed 's/^X//' << \SHAR_EOF > 'entry.c'
X/*
X**	Module to record and process Cake dependency entries.
X*/
X
Xstatic	char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/entry.c,v 1.14 86/07/19 12:22:52 zs Exp $";
X
X#include	"cake.h"
X#include	<ctype.h>
X
XList	*entries;
XEntry	*main_entry;
X
Xextern	Test	*ground_test();
X
X#define	ground_pat(env, pat)	make_pat(ground(env, pat->p_str), pat->p_cmd, pat->p_flag);
X#define	ground_act(env, act)	make_act(ground(env, act->a_str), act->a_flag);
X
X/*
X**	Initialise ALL data structures about entries.
X*/
X
Xinit_entry()
X{
X	entries = makelist0();
X	main_entry = (Entry *) NULL;
X}
X
X/*
X**	Deposit an entry just read in.
X*/
X
Xnew_entry(deps, acts)
Xreg	List	*deps;				/* of Entry	*/
Xreg	List	*acts;				/* of Pat	*/
X{
X	reg	List	*ptr;
X	reg	Entry	*entry;
X
X	for_list (ptr, deps)
X	{
X		entry = (Entry *) ldata(ptr);
X		entry->e_act  = acts;
X		addtail(entries, entry);	/* no assignment */
X	}
X}
X
X/*
X**	Prepare entries for matches against their left hand sides.
X**	This essentially means dereferencing any command patterns.
X**	Another minor correction is also done here: if -n is given
X**	then we set nf_PRECIOUS so no file is deleted.
X*/
X
Xprep_entries()
X{
X	extern	List	*break_pat();
X	reg	List	*ptr, *ptr1;
X	reg	Entry	*entry;
X	reg	Pat	*pat;
X	reg	List	*newnew;
X
X	for_list (ptr, entries)
X	{
X		entry = (Entry *) ldata(ptr);
X
X		newnew = makelist0();
X		for_list (ptr1, entry->e_new)
X		{
X			pat = (Pat *) ldata(ptr1);
X			if (on_pat(pat, nf_WHEN))
X			{
X				printf("cake: inappropriate flag '*' after target %s ignored\n",
X					pat->p_str);
X				reset_pat(pat, nf_WHEN);
X			}
X
X			if (nflag)
X				set_pat(pat, nf_PRECIOUS);
X
X			if (! pat->p_cmd)
X				addtail(newnew, pat);		/* na */
X			else
X			{
X				deref(pat, TRUE);
X				addlist(newnew, break_pat(pat));/* na */
X			}
X		}
X
X		entry->e_new = newnew;
X
X		for_list (ptr1, entry->e_old)
X		{
X			pat = (Pat *) ldata(ptr1);
X			if (pat->p_cmd && on_pat(pat, nf_WHEN))
X			{
X				printf("cake: inappropriate flag '*' after command source %s ignored\n",
X					pat->p_str);
X				reset_pat(pat, nf_WHEN);
X			}
X		}
X
X#ifdef	CAKEDEBUG
X		if (entrydebug)
X		{
X			printf("prepared entry:\n");
X			print_entry(entry);
X		}
X#endif
X	}
X}
X
X/*
X**	Enter the last (main) entry. This entry has a distinguished
X**	left hand side consisting of the sole pattern CHASEROOT.
X**	This is the pattern which cake tries to make up-to-date.
X**	If there were nonoption arguments on the command line,
X**	CHASEROOT is made to depend on them; otherwise the left hand
X**	side of the first concrete entry is used, as in make.
X**	However, if the first concrete entry is in an included file,
X**	we keep looking, unless it had a double colon.
X*/
X
Xfinal_entry(argc, argv)
Xreg	int	argc;
Xreg	char	*argv[];
X{
X	extern	bool	hasvars();
X	reg	List	*ptr, *ptr1;
X	reg	Entry	*entry, *foundentry;
X	reg	Pat	*pat;
X	reg	bool	found_main, found_incl;
X	reg	int	i;
X
X	main_entry = make(Entry);
X	main_entry->e_new  = makelist(make_pat(CHASEROOT, FALSE, nf_PSEUDO|nf_NODELETE));
X	main_entry->e_old  = makelist0();
X	main_entry->e_cond = (Test *) NULL;
X	main_entry->e_act  = makelist0();
X
X	cdebug("target count %d\n", argc-1);
X
X	if (argc > 1)
X	{
X		for (i = 1; i < argc; i++)
X		{
X			cdebug("target %s\n", argv[i]);
X			addtail(main_entry->e_old, make_pat(argv[i], FALSE, 0));	/* na */
X		}
X	}
X	else
X	{
X		/* find the first entry without variables */
X		found_main = FALSE;
X		found_incl = FALSE;
X		for_list (ptr, entries)
X		{
X			entry = (Entry *) ldata(ptr);
X			for_list (ptr1, entry->e_new)
X			{
X				pat = (Pat *) ldata(ptr1);
X				if (hasvars(pat->p_str))
X					goto nextentry;
X			}
X
X			if (streq(entry->e_file, cakefile) || entry->e_dblc)
X			{
X				found_main = TRUE;
X				foundentry = entry;
X				break;
X			}
X			or (! found_incl)
X			{
X				found_incl = TRUE;
X				foundentry = entry;
X			}
X
X		nextentry:	;
X		}
X
X		if (! found_main && ! found_incl)
X		{
X			printf("cake: no entries without variables\n");
X			printf("cake: don't know what cake to bake\n");
X			exit(1);
X		}
X
X		/* update everything on its left side */
X		for_list (ptr, foundentry->e_new)
X		{
X			pat = (Pat *) ldata(ptr);
X			addtail(main_entry->e_old, pat);	/* na */
X		}
X	}
X
X	addtail(entries, main_entry);	/* no assigment */
X}
X
X
X/*
X**	Return a new entry which is a ground version of the one passed.
X**	Process '*' flags and set up e_when accordingly.
X*/
X
XEntry *
Xground_entry(env, entry)
XEnv		env;
Xreg	Entry	*entry;
X{
X	extern		do_when();
X	extern	List	*break_pat();
X	extern	char	*ground();
X	reg	List	*ptr;
X	reg	Entry	*newentry;
X	reg	Pat	*pat, *newpat;
X	reg	Act	*act, *newact;
X
X	put_trail("ground_entry", "start");
X	newentry = make(Entry);
X	newentry->e_new  = makelist0();
X	newentry->e_old  = makelist0();
X	newentry->e_act  = makelist0();
X	newentry->e_when = makelist0();
X
X	for_list (ptr, entry->e_new)
X	{
X		/* prep_entries made these patterns command free */
X		pat = (Pat *) ldata(ptr);
X		newpat = ground_pat(env, pat);
X		do_when(newentry, newpat);
X		addtail(newentry->e_new, newpat);	/* na */
X	}
X
X	for_list (ptr, entry->e_old)
X	{
X		pat = (Pat *) ldata(ptr);
X		newpat = ground_pat(env, pat);
X		do_when(newentry, newpat);
X		addtail(newentry->e_old, newpat);	/* na */
X	}
X
X	for_list (ptr, entry->e_act)
X	{
X		act = (Act *) ldata(ptr);
X		newact = ground_act(env, act);
X		addtail(newentry->e_act, newact);	/* na */
X	}
X
X	newentry->e_cond = ground_test(env, entry->e_cond);
X
X	put_trail("ground_entry", "finish");
X	return newentry;
X}
X
X/*
X**	Look after '*' (when) flags.
X*/
X
Xdo_when(entry, pat)
Xreg	Entry	*entry;
Xreg	Pat	*pat;
X{
X	if (on_pat(pat, nf_WHEN))
X	{
X		reset_pat(pat, nf_WHEN);
X		entry->e_when = addtail(entry->e_when, pat);
X	}
X}
X
X/*
X**	Ground a test.
X*/
X
XTest *
Xground_test(env, test)
XEnv		env;
Xreg	Test	*test;
X{
X	extern	char	*ground();
X	reg	List	*ptr;
X	reg	Pat	*pat, *newpat;
X	reg	Test	*newtest;
X
X	if (test == (Test *) NULL)
X		return test;
X
X	put_trail("ground_test", "start");
X	switch (test->t_kind)
X	{
X
Xwhen t_TRUE:
Xcase t_FALSE:	put_trail("ground_test", "finish");
X		return test;
X
Xwhen t_AND:
Xcase t_OR:	put_trail("ground_test", "finish");
X		return make_test_b(test->t_kind,
X			ground_test(env, test->t_left), ground_test(env, test->t_right));
X
Xwhen t_NOT:	put_trail("ground_test", "finish");
X		return make_test_u(test->t_kind, ground_test(env, test->t_left));
X
Xwhen t_CMD:	put_trail("ground_test", "finish");
X		return make_test_c(ground(env, test->t_cmd));
X
Xwhen t_MATCH:	pat = ground_pat(env, test->t_pat);
X		newtest = make_test_mm(pat);
X		for_list (ptr, test->t_list)
X		{
X			pat = (Pat *) ldata(ptr);
X			newpat = ground_pat(env, pat);
X			addtail(newtest->t_list, newpat);	/* na */
X		}
X
X		put_trail("ground_test", "finish");
X		return newtest;
X
Xwhen t_LIST:	pat = ground_pat(env, test->t_pat);
X		newtest = make_test_l(pat, makelist0());
X		for_list (ptr, test->t_list)
X		{
X			pat = (Pat *) ldata(ptr);
X			newpat = ground_pat(env, pat);
X			addtail(newtest->t_list, newpat);	/* na */
X		}
X
X		put_trail("ground_test", "finish");
X		return newtest;
X
Xwhen t_EXIST:	newpat = ground_pat(env, test->t_pat);
X		put_trail("ground_test", "finish");
X		return make_test_s(t_EXIST, newpat);
X
Xwhen t_CANDO:	newpat = ground_pat(env, test->t_pat);
X		put_trail("ground_test", "finish");
X		return make_test_s(t_CANDO, newpat);
X
Xwhen t_OK:	newpat = ground_pat(env, test->t_pat);
X		put_trail("ground_test", "finish");
X		return make_test_s(t_OK, newpat);
X
Xotherwise:	printf("cake internal error: bad test type %x in ground_test\n", test->t_kind);
X		put_trail("ground_test", "finish");
X		return (Test *) NULL;
X	}
X
X	put_trail("ground_test", "finish");
X	return (Test *) NULL;
X}
X
X/*
X**	Dereference all command patterns in the given entry.
X*/
X
Xderef_entry(env, entry)
XEnv		env;
Xreg	Entry	*entry;
X{
X	reg	List	*ptr, *ptr1;
X	reg	List	*newlist;
X	reg	List	*patlist;
X	reg	Pat	*pat, *oldpat, *newpat;
X
X	newlist = makelist0();
X	for_list (ptr, entry->e_old)
X	{
X		pat = (Pat *) ldata(ptr);
X		if (! pat->p_cmd)
X			addtail(newlist, pat);	/* na */
X		else
X		{
X			deref(pat, TRUE);
X			patlist = break_pat(pat);
X			for_list (ptr1, patlist)
X			{
X				oldpat = (Pat *) ldata(ptr1);
X				newpat = make_pat(ground(env, oldpat->p_str), FALSE, 0);
X				do_when(entry, newpat);
X				addtail(newlist, newpat);	/* na */
X			}
X		}
X	}
X
X	entry->e_old  = newlist;
X}
X
X/*
X**	Dereference a test.
X*/
X
XTest *
Xderef_test(test)
Xreg	Test	*test;
X{
X	extern	char	*expand_cmds();
X	reg	List	*ptr;
X	reg	List	*newlist;
X	reg	Pat	*pat;
X
X	if (test == (Test *) NULL)
X		return test;
X
X	put_trail("deref_test", "start");
X	switch (test->t_kind)
X	{
X
Xwhen t_TRUE:
Xcase t_FALSE:	put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_AND:
Xcase t_OR:	test->t_left  = deref_test(test->t_left);
X		test->t_right = deref_test(test->t_right);
X		put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_NOT:	test->t_left  = deref_test(test->t_left);
X		put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_CMD:	test->t_cmd = expand_cmds(test->t_cmd);
X		put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_MATCH:	deref(test->t_pat, FALSE);
X		for_list (ptr, test->t_list)
X		{
X			pat = (Pat *) ldata(ptr);
X			deref(pat, FALSE);
X		}
X
X		put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_LIST:	deref(test->t_pat, FALSE);
X		newlist = makelist0();
X		for_list (ptr, test->t_list)
X		{
X			pat = (Pat *) ldata(ptr);
X			if (! pat->p_cmd)
X				addtail(newlist, pat);			/* na */
X			else
X			{
X				deref(pat, TRUE);
X				addlist(newlist, break_pat(pat));	/* na */
X			}
X		}
X
X		test->t_list = newlist;
X		put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_EXIST:	deref(test->t_pat, FALSE);
X		put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_CANDO:	deref(test->t_pat, FALSE);
X		put_trail("deref_test", "finish");
X		return test;
X
Xwhen t_OK:	deref(test->t_pat, FALSE);
X		put_trail("deref_test", "finish");
X		return test;
X
Xotherwise:	printf("cake internal error: bad test type %x in deref_test\n", test->t_kind);
X		put_trail("deref_test", "finish");
X		return (Test *) NULL;
X	}
X
X	put_trail("deref_test", "finish");
X	return (Test *) NULL;
X}
X
XAct *
Xprep_act(text)
Xreg	char	*text;
X{
X	reg	Act	*act;
X	reg	char	*s;
X
X	act = make(Act);
X	act->a_flag = 0;
X
X	/* strip trailing space - there is at least the newline */
X	for (s = text+strlen(text)-1; isspace(*s); s--)
X		*s = '\0';
X
X	/* put just the newline back */
X	strcat(s, "\n");
X
X	/* strip spaces before flags */
X	for (s = text; isspace(*s); s++)
X		;
X
X	/* process the flags */
X	for (; *s != '\0'; s++)
X		if (*s == '@')
X			set_act(act, af_SILENT);
X		or (*s == '!')
X			set_act(act, af_SYSTEM);
X		or (*s == '-')
X			set_act(act, af_IGNORE);
X		or (*s == '+')
X			set_act(act, af_MINUSN);
X		else
X			break;
X
X	/* strip spaces after flags */
X	for (; isspace(*s); s++)
X		;
X
X	act->a_str = new_name(s);
X	return act;
X}
X
XAct *
Xprep_script(first_act, middle_act, last_act)
Xreg	Act	*first_act, *last_act;
Xreg	List	*middle_act;
X{
X	reg	List	*ptr;
X	reg	Act	*act;
X	reg	char	*s;
X	char		buf[MAXSCRIPT];
X
X	buf[0] = '\0';
X	/* handle the first action */
X	for (s = first_act->a_str+1; *s == ' ' || *s == '\t'; s++)
X		;
X
X	if (strdiff(s, "\n"))
X		strcat(buf, s);
X
X	/* handle the middle actions, if any */
X	for_list (ptr, middle_act)
X	{
X		act = (Act *) ldata(ptr);
X		strcat(buf, act->a_str);
X	}
X
X	/* handle the last action as the first */
X	for (s = last_act->a_str+1; *s == ' ' || *s == '\t'; s++)
X		;
X
X	if (strdiff(s, "\n"))
X		strcat(buf, s);
X
X	/* check for overflow */
X	if (strlen(buf) > MAXSCRIPT)
X	{
X		printf("cake: script too long\n");
X		printf("%s", buf);
X		exit_cake(FALSE);
X	}
X
X	first_act->a_str = new_name(buf);
X	set_act(first_act, af_SCRIPT);
X	return first_act;
X}
SHAR_EOF
if test 11376 -ne "`wc -c < 'entry.c'`"
then
	echo shar: "error transmitting 'entry.c'" '(should have been 11376 characters)'
fi
fi
echo shar: "extracting 'error.c'" '(2454 characters)'
if test -f 'error.c'
then
	echo shar: "will not over-write existing file 'error.c'"
else
sed 's/^X//' << \SHAR_EOF > 'error.c'
X/*
X**	Error handling module.
X*/
X
Xstatic	char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/error.c,v 1.15 87/10/05 20:14:20 zs Exp $";
X
X#include	"cake.h"
X
X/*
X**	This function prints out the history of errors
X**	that prevent the proper update of the given node.
X*/
X
Xtrace_errs(node)
Xreg	Node	*node;
X{
X	reg	List	*ptr;
X	reg	Node	*bnode;
X
X	if (on_node(node, nf_TRACED))
X		return;
X
X	set_node(node, nf_TRACED);
X	for_list (ptr, node->n_badguys)
X	{
X		bnode = (Node *) ldata(ptr);
X		trace_errs(bnode);
X	}
X
X	if (strdiff(node->n_name, CHASEROOT))
X		printf("%s", node->n_msg);
X}
X
X/*
X**	This function adds msg to the list of error messages
X**	to be printed by trace_errs for the given node.
X*/
X
Xadd_error(node, msg, badguys, iserror)
Xreg	Node	*node;
Xreg	char	*msg;
Xreg	List	*badguys;
Xreg	int	iserror;
X{
X	char	buf[MAXSIZE];
X
X	if (node->n_msg == NULL)
X		node->n_msg = "";
X
X	if (iserror)
X		set_node(node, nf_ERR);
X
X	sprintf(buf, "%scake: %s\n", node->n_msg, msg);
X	if (strlen(buf) >= MAXSIZE)
X	{
X		fprintf(stderr, "cake internal error: buffer overflow in add_error\n");
X		exit_cake(FALSE);
X	}
X
X	node->n_msg = new_name(buf);
X	node->n_badguys = addlist(node->n_badguys, badguys);
X}
X
Xchar *
Xfind_circle(name)
Xreg	char	*name;
X{
X	extern	int	goal_stackp;
X	extern	char	*goal_stack[];
X	char		buf[MAXSIZE];
X	reg	int	i;
X
X	cdebug("stackp = %d\n", goal_stackp);
X	cdebug("looking for %s\n", goal_stack[goal_stackp-1]);
X
X	if (goal_stack[goal_stackp-1] == name)
X		return "directly";
X
X	for (i = 0; i < goal_stackp; i++)
X	{
X		cdebug("checking %d: %s\n", i, goal_stack[i]);
X		if (goal_stack[i] == name)
X		{
X			cdebug("hit\n");
X			strcpy(buf, "through");
X			for (; i < goal_stackp; i++)
X			{
X				strcat(buf, " ");
X				strcat(buf, goal_stack[i]);
X			}
X
X			if (strlen(buf) >= MAXSIZE)
X			{
X				fprintf(stderr, "cake internal error: buffer overflow in find_circle\n");
X				exit_cake(FALSE);
X			}
X
X			return new_name(buf);
X		}
X	}
X
X	fprintf(stderr, "cake internal error: no circularity in find_circle\n");
X	exit_cake(TRUE);
X	return "";		/* to satisfy lint */
X}
X
Xchar *
Xlist_names(list)
Xreg	List	*list;		/* of Node	*/
X{
X	reg	List	*ptr;
X	reg	Node	*node;
X	reg	char	*sep;
X	char		buf[MAXSIZE];
X
X	buf[0] = '\0';
X	sep = "";
X	for_list (ptr, list)
X	{
X		node = (Node *) ldata(ptr);
X		strcat(buf, sep);
X		strcat(buf, node->n_name);
X		sep = " ";
X	}
X
X	if (strlen(buf) >= MAXSIZE)
X	{
X		fprintf(stderr, "cake internal error: buffer overflow in list_names\n");
X		exit_cake(FALSE);
X	}
X
X	return new_name(buf);
X}
SHAR_EOF
if test 2454 -ne "`wc -c < 'error.c'`"
then
	echo shar: "error transmitting 'error.c'" '(should have been 2454 characters)'
fi
fi
echo shar: "extracting 'expand.c'" '(5774 characters)'
if test -f 'expand.c'
then
	echo shar: "will not over-write existing file 'expand.c'"
else
sed 's/^X//' << \SHAR_EOF > 'expand.c'
X/*
X**	Module to expand out commands in patterns and actions.
X*/
X
Xstatic	char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/expand.c,v 1.14 86/07/19 12:23:01 zs Exp $";
X
X#include	"cake.h"
X#include	<ctype.h>
X
X/*
X**	Expand any commands in actions. Doing this here instead of
X**	in the shell saves time because of cake's command cache.
X*/
X
Xchar *
Xexpand_cmds(str)
Xreg	char	*str;
X{
X	extern	char	*get_output();
X	reg	char	*s;
X	reg	int	n, cmds, offset;
X	reg	int	stackp;
X	reg	char	*rightbr;
X	char		*leftbr_stack[MAXNEST];
X	char		*nextbr_stack[MAXNEST];
X	char		copybuf[MAXSIZE], buf[MAXSIZE];
X	reg	char	*result;
X
X	if (index(str, '[') == NULL)
X		return str;
X
X	put_trail("expand_cmds", "start");
X	cmds = 0;
X	stackp = 0;
X	if (strlen(str) >= MAXSIZE)
X	{
X		fprintf(stderr, "cake internal error: command too long %s\n", str);
X		exit_cake(FALSE);
X	}
X
X	strcpy(copybuf, str);
X	leftbr_stack[stackp] = copybuf;
X	nextbr_stack[stackp] = NULL;
X
X	/* Leftbr_stack[stackp] and rightbr enclose commands	   */
X	/* for stackp > 0; for stackp == 0 they enclose the string */
X	/* Nextbr_stack gives the beginning of the next segment	   */
X	/* after the corresponding leftbr; == 0 if not known (yet) */
X	/* The top entry on the stack always has nextbr == NULL	   */
X
X	for (s = copybuf; s[0] != '\0'; s++)
X	{
X		if (s[0] == '[' && s[1] == '[')
X		{
X			nextbr_stack[stackp] = s;
X			if (++stackp >= MAXNEST)
X			{
X				fprintf(stderr, "cake internal error: command nesting level too deep\n");
X				exit_cake(FALSE);
X			}
X
X			leftbr_stack[stackp] = s;
X			nextbr_stack[stackp] = NULL;
X			s++;	/* avoid problems with [[[cmd]] ... */
X		}
X		or (s[0] == ']' && s[1] == ']')
X		{
X			if (stackp <= 0)
X				continue;
X
X			cmds++;
X			rightbr = s;
X
X			leftbr_stack[stackp][0] = '\0';
X			leftbr_stack[stackp][1] = '\0';
X			rightbr[0] = '\0';
X			rightbr[1] = '\0';
X
X			result = get_output(new_name(leftbr_stack[stackp]+2));
X			cdebug("expansion [[%s]] => [[%s]]\n", leftbr_stack[stackp]+2, result);
X
X			segcpy(buf, leftbr_stack[stackp-1], nextbr_stack[stackp-1]);
X			strcat(buf, result);
X			offset = strlen(buf) - 1;
X			strcat(buf, rightbr+2);
X
X			if (strlen(buf) >= MAXSIZE)
X			{
X				fprintf(stderr, "cake: expansion result '%s' is too long\n", buf);
X				exit_cake(FALSE);
X			}
X
X			stackp--;
X			leftbr_stack[stackp] = new_name(buf);
X			nextbr_stack[stackp] = NULL;
X
X			/* start next iteration with the */
X			/* first character after the ]]  */
X			s = leftbr_stack[stackp] + offset;
X		}
X		or (s[0] == '\\')
X		{
X			/* don't check next char */
X			if (s[1] != '\0')
X				s++; 
X		}
X	}
X
X#ifdef	CAKEDEBUG
X	if (entrydebug)
X		printf("after expand_cmds: [[%s]]\n", str);
X#endif
X
X	if (cmds <= 0)
X	{
X		put_trail("expand_cmds", "finish");
X		return str;
X	}
X	or (stackp == 0)
X	{
X		put_trail("expand_cmds", "finish");
X		return leftbr_stack[0];
X	}
X
X	segcpy(buf, leftbr_stack[0], nextbr_stack[0]);
X	for (n = 1; n <= stackp; n++)
X		segcat(buf, leftbr_stack[n], nextbr_stack[n]);
X
X	if (strlen(buf) >= MAXSIZE)
X	{
X		fprintf(stderr, "cake: expansion result '%s' is too long\n", buf);
X		exit_cake(FALSE);
X	}
X
X	put_trail("expand_cmds", "finish");
X	return new_name(buf);
X}
X
Xsegcpy(target, left, right)
Xreg	char	*target, *left, *right;
X{
X	reg	int	i;
X	reg	char	*s;
X
X	if (right == NULL)
X		strcpy(target, left);
X	else
X	{
X		for (s = left, i = 0; s != right; s++, i++)
X			target[i] = *s;
X
X		target[i] = '\0';
X	}
X}
X
Xsegcat(target, left, right)
Xreg	char	*target, *left, *right;
X{
X	reg	int	i;
X	reg	char	*s;
X
X	if (right == NULL)
X		strcat(target, left);
X	else
X	{
X		for (s = left, i = strlen(target); s != right; s++, i++)
X			target[i] = *s;
X
X		target[i] = '\0';
X	}
X}
X
X/*
X**	Execute the given command and return its output.
X**	It is a fatal error for the command to terminate abnormally.
X*/
X
Xchar *
Xget_output(cmd)
Xreg	char	*cmd;
X{
X	extern	char	*mktemp();
X	extern	char	*get_out();
X	extern	char	*flatten();
X	extern	int	cake_proc();
X	extern	Wait	cake_wait();
X	char		buf[MAXSIZE];
X	Wait		code;
X	reg	char	*out_filename;
X	reg	FILE	*fp;
X	reg	int	i, c;
X	reg	int	pid;
X	reg	char	*s, *result;
X
X	put_trail("get_output", "start");
X	cdebug("get_output of [%s]\n", cmd);
X
X	/* see if we have tried this command before */
X	if ((result = get_out(cmd)) != NULL)
X	{
X		cdebug("cache yields [%s]\n", result);
X		put_trail("get_output", "early finish");
X		return result;
X	}
X
X	out_filename = get_newname();
X	pid = cake_proc(cmd, Exec, out_filename, (Node *) NULL, (int (*)()) NULL, (List *) NULL);
X	code = cake_wait(pid);
X	if (code.w_status != 0 && ! zflag)
X	{
X		printf("cake, %s: nonzero exit status\n", cmd);
X		exit_cake(FALSE);
X	}
X
X	if ((fp = fopen(out_filename, "r")) == NULL)
X	{
X		sprintf(scratchbuf, "cake system error, fopen %s", out_filename);
X		perror(scratchbuf);
X		exit_cake(FALSE);
X	}
X
X	/* convert the chars in file fp to a string */
X	i = 0;
X	while ((c = getc(fp)) != EOF)
X		buf[i++] = c;
X	
X	buf[i] = '\0';
X	fclose(fp);
X	if (MAXSIZE <= i)
X	{
X		printf("cake, %s: output too long\n", cmd);
X		exit_cake(FALSE);
X	}
X
X#ifndef	LEAVE_DIR
X	cdebug("get_output unlink out_filename %s\n", out_filename);
X	if (unlink(out_filename) != 0)
X	{
X		sprintf(scratchbuf, "cake system error, unlink %s", out_filename);
X		perror(scratchbuf);
X	}
X#endif
X
X	/* save the result for posterity */
X	s = new_name(flatten(buf));
X	new_out(cmd, s);
X	cdebug("put result [%s] into cache\n", s);
X	put_trail("get_output", "finish");
X	return s;
X}
X
X/*
X**	Flatten the output of commands by converting newlines to spaces
X**	and by removing blanks around the edges.
X*/
X
Xchar *
Xflatten(str)
Xreg	char	*str;
X{
X	reg	char	*s;
X
X	/* convert newlines (and formfeeds) to spaces */
X	for (s = str; *s != '\0'; s++)
X		if (*s == '\n' || *s == '\f')
X			*s = ' ';
X
X	/* remove blanks around the edges */
X	for (s = str+strlen(str)-1; str <= s && isspace(*s); s--)
X		*s = '\0';
X	for (s = str; *s != '\0' && isspace(*s); s++)
X		;
X
X	return s;
X}
SHAR_EOF
if test 5774 -ne "`wc -c < 'expand.c'`"
then
	echo shar: "error transmitting 'expand.c'" '(should have been 5774 characters)'
fi
fi
echo shar: "extracting 'file.c'" '(12147 characters)'
if test -f 'file.c'
then
	echo shar: "will not over-write existing file 'file.c'"
else
sed 's/^X//' << \SHAR_EOF > 'file.c'
X/*
X**	File system interface module.
X*/
X
Xstatic	char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/file.c,v 1.14 86/07/19 12:23:08 zs Exp $";
X
X#include	"cake.h"
X#include	<errno.h>
X#include	<sys/dir.h>
X#include	<sys/stat.h>
X#ifndef		ATT
X#include	<sys/time.h>
X#endif
X
Xtypedef	struct	timeval		Tval;
Xtypedef	struct	timezone	Tzone;
Xtypedef	struct	stat		Stat;
Xtypedef	struct	direct		Dirent;
X
Xextern	int	errno;
X
Xstatic	char	dir_name[30] = "/tmp/CakeXXXXXX";
X
X/*
X**	Create the directory to be used for all temporary storage.
X*/
X
Xdir_start()
X{
X	reg	bool	successful;
X
X	mktemp(dir_name);
X
X#ifdef	ATT
X	{
X		extern	int	cake_proc();
X		extern	Wait	cake_wait();
X		reg	int	pid;
X		Wait		code;
X
X		sprintf(scratchbuf, "mkdir %s", dir_name);
X		pid = cake_proc(new_name(scratchbuf), Exec, (char *) NULL,
X			(Node *) NULL, (int (*)()) NULL, (List *) NULL);
X		code = cake_wait(pid);
X		successful = code.w_status == 0;
X	}
X#else
X	successful = mkdir(dir_name, 0700) == 0;
X#endif
X
X	if (! successful)
X	{
X		sprintf(scratchbuf, "cake system error, mkdir %s", dir_name);
X		perror(scratchbuf);
X		exit_cake(FALSE);
X	}
X
X#ifdef	ATT
X	if (chmod(dir_name, 0700) != 0)
X	{
X		sprintf(scratchbuf, "cake system error, chmod 700 %s", dir_name);
X		perror(scratchbuf);
X		exit_cake(FALSE);
X	}
X#endif
X}
X
X/*
X**	Clean up /tmp when we are finished
X*/
X
Xdir_finish()
X{
X#ifndef	LEAVE_DIR
X#ifdef	ATT
X	extern	int	cake_proc();
X	extern	Wait	cake_wait();
X	reg	int	pid;
X	Wait		code;
X
X	sprintf(scratchbuf, "/bin/rm -fr %s", dir_name);
X	pid = cake_proc(new_name(scratchbuf), Exec, (char *) NULL,
X		(Node *) NULL, (int (*)()) NULL, (List *) NULL);
X	code = cake_wait(pid);
X	if (code.w_status != 0)
X	{
X		fprintf(stderr, "cake system error: cannot remove %s\n", dir_name);
X		return;
X	}
X#else
X	char		buf[1024];
X	reg	DIR	*dirp;
X	reg	Dirent	*dp;
X	reg	int	nameoffset;
X
X	if ((dirp = opendir(dir_name)) == NULL)
X	{
X		sprintf(scratchbuf, "cake system error, opendir %s", dir_name);
X		perror(scratchbuf);
X		return;
X	}
X
X	strcpy(buf, dir_name);
X	strcat(buf, "/");
X	nameoffset = strlen(buf);
X
X	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
X		if (strdiff(dp->d_name, ".") && strdiff(dp->d_name, ".."))
X		{
X			buf[nameoffset] = '\0';
X			strcat(buf, dp->d_name);
X			cdebug("dir_finish unlink %s\n", buf);
X			if (unlink(buf) != 0)
X			{
X				sprintf(scratchbuf, "cake system error, unlink %s", buf);
X				perror(scratchbuf);
X			}
X		}
X
X	closedir(dirp);
X
X	cdebug("rmdir %s\n", dir_name);
X	if (rmdir(dir_name) != 0)
X	{
X		sprintf(scratchbuf, "cake system error, rmdir %s", dir_name);
X		perror(scratchbuf);
X	}
X#endif
X#endif
X}
X
X/*
X**	Get a new filename in our private directory.
X**	These files are used to store shell scripts
X**	and the output of actions.
X**
X**	These names MUST be different from those generated
X**	by get_savename, so they have different prefixes.
X*/
X
Xchar *
Xget_newname()
X{
X	char		buf[100];
X	static	int	count = 0;
X
X	++count;
X	sprintf(buf, "%s/gen_%d", dir_name, count);
X	return new_name(buf);
X}
X
X/*
X**	Save a nonvolatile file for later comparison.
X**	Any slashes in the filename are replaced by colons
X**	to avoid reference to nonexistent directories.
X*/
X
Xsave_novol(node)
Xreg	Node	*node;
X{
X	char		buf[MAXSIZE];
X	reg	FILE	*ifp, *ofp;
X	reg	int	c;
X
X	if (nflag)
X	{
X		node->n_stime = node->n_rtime;
X		return;
X	}
X
X	put_trail("save_novol", "start");
X	cdebug("saving %s\n", node->n_name);
X	node->n_stime = node->n_rtime;
X
X	get_savename(node->n_name, buf);
X	if ((ifp = fopen(node->n_name, "r")) == NULL)
X	{
X		if (errno == ENOENT)
X		{
X			cdebug("%s does not exist, considered volatile\n", node->n_name);
X			reset_node(node, nf_NONVOL);
X			put_trail("save_novol", "finish");
X			return;
X		}
X
X		sprintf(scratchbuf, "cake system error, fopen (r) %s", node->n_name);
X		perror(scratchbuf);
X		printf("cake: considering %s volatile\n", node->n_name);
X		reset_node(node, nf_NONVOL);
X		put_trail("save_novol", "finish");
X		return;
X	}
X
X	if ((ofp = fopen(buf, "w")) == NULL)
X	{
X		sprintf(scratchbuf, "cake system error, fopen (w) %s", buf);
X		perror(scratchbuf);
X		printf("cake: considering %s volatile\n", node->n_name);
X		reset_node(node, nf_NONVOL);
X		put_trail("save_novol", "finish");
X		return;
X	}
X
X	while ((c = getc(ifp)) != EOF)
X		putc(c, ofp);
X	
X	fclose(ifp);
X	fclose(ofp);
X	put_trail("save_novol", "finish");
X}
X
X/*
X**	See if the given file has been changed since saved.
X*/
X
Xbool
Xdiff_novol(node)
Xreg	Node	*node;
X{
X	char		buf[MAXSIZE];
X	reg	FILE	*i1fp, *i2fp;
X	reg	int	c;
X	reg	bool	diff;
X
X	if (nflag)
X		return FALSE;
X
X	put_trail("diff_novol", "start");
X	cdebug("comparing saved copy of %s: ", node->n_name);
X
X	get_savename(node->n_name, buf);
X	if ((i1fp = fopen(node->n_name, "r")) == NULL)
X	{
X		sprintf(scratchbuf, "cake system error, fopen (r) %s", node->n_name);
X		perror(scratchbuf);
X		printf("cake: considering %s different\n", node->n_name);
X		reset_node(node, nf_NONVOL);
X		put_trail("diff_novol", "finish");
X		return TRUE;;
X	}
X
X	if ((i2fp = fopen(buf, "r")) == NULL)
X	{
X		sprintf(scratchbuf, "cake system error, fopen (r) %s", buf);
X		perror(scratchbuf);
X		printf("cake: considering %s different\n", node->n_name);
X		reset_node(node, nf_NONVOL);
X		put_trail("diff_novol", "finish");
X		return TRUE;;
X	}
X
X	diff = FALSE;
X	while ((c = getc(i1fp)) != EOF)
X		if (getc(i2fp) != c)
X		{
X			diff = TRUE;
X			break;
X		}
X	
X	if (getc(i2fp) != EOF)
X		diff = TRUE;
X	
X	fclose(i1fp);
X	fclose(i2fp);
X	cdebug("diff_novol unlink %s\n", buf);
X	if (unlink(buf) != 0)
X	{
X		sprintf(scratchbuf, "cake system error, unlink %s", buf);
X		perror(scratchbuf);
X		put_trail("diff_novol", "finish");
X		return TRUE;
X	}
X
X	cdebug("%s\n", diff? "different": "same");
X	put_trail("diff_novol", "finish");
X	return diff;
X}
X
X/*
X**	Get the the name of the saved copy of the given file.
X*/
X
Xget_savename(name, buf)
Xreg	char	*name;
Xreg	char	buf[];
X{
X	extern	char	*noslash();
X
X	strcpy(buf, dir_name);
X	strcat(buf, "/save_");
X	strcat(buf, noslash(name));
X}
X
X/*
X**	Remove any slashes from a filename.
X*/
X
Xchar *
Xnoslash(name)
Xreg	char	*name;
X{
X	char		buf[MAXSIZE];
X	reg	int	i;
X	reg	char	*s;
X
X	i = 0;
X	for (s = name; *s != '\0'; s++)
X		if (*s == '/')
X			buf[i++] = ':';
X		else
X			buf[i++] = *s;
X	
X	buf[i] = '\0';
X	return new_name(buf);
X}
X
X/*
X**	Perform a stat on the given file. The only relevant info
X**	is the return code, indicating whether the file exists or not
X**	and its last modify (or status change) date. Note that if a
X**	file does not exist, it is by definition volatile. An aside
X**	considers all directories precious.
X*/
X
Xnode_stat(node)
Xreg	Node	*node;
X{
X	extern	int	stat();
X	Stat		statbuf;
X
X	if (node->n_name != (char *) NULL && stat(node->n_name, &statbuf) == 0)
X	{
X		set_node(node, nf_EXIST);
X		if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
X			set_node(node, nf_PRECIOUS);
X
X		node->n_rtime = cflag? statbuf.st_ctime: statbuf.st_mtime;
X	}
X	else
X	{
X		reset_node(node, nf_EXIST);
X		reset_node(node, nf_NONVOL);
X		node->n_rtime = GENESIS;
X	}
X}
X
X/*
X**	Restore the previous stat of a nonvol file,
X**	both in the filesystem and in cake's memory.
X**	However, make sure that it will not need to be
X**	remade again and again to discover this anew
X**	while its ancestors remain the same.
X*/
X
Xnode_resetstat(node)
Xreg	Node	*node;
X{
X	node_stat(node);
X	if (off_node(node, nf_EXIST))
X		return;
X
X	cake_utimes(node, max(node->n_stime, node->n_utime));
X	node->n_utime = node->n_rtime;
X}
X
X/*
X**	Set up the stat of a file as the latest ancestor's,
X**	both in the filesystem and in cake's memory.
X*/
X
Xnode_setstat(node)
Xreg	Node	*node;
X{
X	extern	time_t	get_youngest();
X	reg	time_t	youngest;
X
X	node_stat(node);
X	if (off_node(node, nf_EXIST))
X		return;
X
X	youngest = get_youngest(node, FALSE);
X	if (youngest != node->n_utime && youngest != GENESIS)
X	{
X		fprintf(stderr, "cake internal error: youngest is mismatched for %s\n",
X			node->n_name);
X		exit_cake(TRUE);
X	}
X
X	cake_utimes(node, node->n_utime);
X}
X
X/*
X**	Return the time, converting formats.
X*/
X
Xtime_t
Xcake_gettime()
X{
X#ifdef	ATT
X	extern	long	time();
X	long		val;
X
X	if (time(&val) == -1)
X	{
X		sprintf(scratchbuf, "cake system error, time");
X		perror(scratchbuf);
X		exit_cake(FALSE);
X	}
X
X	return (time_t) val;
X#else
X	Tzone		tzone;
X	Tval		tval;
X
X	if (gettimeofday(&tval, &tzone) != 0)
X	{
X		sprintf(scratchbuf, "cake system error, gettimeofday");
X		perror(scratchbuf);
X		exit_cake(FALSE);
X	}
X
X	return (time_t) tval.tv_sec;
X#endif
X}
X
X/*
X**	Issue the system call utimes after expanding its arguments
X**	from time_t to Tval. If the time given is GENESIS, use the
X**	current time instead.
X*/
X
X#ifdef	ATT
Xtypedef	struct	utimbuf
X{
X	time_t	actime;
X	time_t	modtime;
X} Utimbuf;
X#endif
X
Xcake_utimes(node, newtime)
Xreg	Node	*node;
Xtime_t		newtime;
X{
X#ifdef	ATT
X	extern	int	utime();
X#else
X	extern	int	utimes();
X#endif
X	extern	int	stat();
X	Stat		statbuf;
X#ifdef	ATT
X	Utimbuf		timbuf;
X	long		vals[2];
X#else
X	Tzone		tzone;
X	Tval		tvals[2];
X#endif
X
X	cdebug("resetting time for %s to %d, %s",
X		node->n_name, newtime, ctime(&newtime));
X
X#ifdef	ATT
X	if (time(&vals[0]) == -1)
X	{
X		sprintf(scratchbuf, "cake system error, time");
X		perror(scratchbuf);
X		exit_cake(FALSE);
X	}
X#else
X	if (gettimeofday(&tvals[0], &tzone) != 0)
X	{
X		sprintf(scratchbuf, "cake system error, gettimeofday");
X		perror(scratchbuf);
X		exit_cake(FALSE);
X	}
X#endif
X
X	if (newtime == GENESIS)
X	{
X		cdebug("reinterpreting GENESIS\n");
X#ifdef	ATT
X		if (time(&vals[1]) == -1)
X		{
X			sprintf(scratchbuf, "cake system error, time");
X			perror(scratchbuf);
X			exit_cake(FALSE);
X		}
X#else
X		if (gettimeofday(&tvals[1], &tzone) != 0)
X		{
X			sprintf(scratchbuf, "cake system error, gettimeofday");
X			perror(scratchbuf);
X			exit_cake(FALSE);
X		}
X#endif
X	}
X	else
X	{
X#ifdef	ATT
X		vals[1] = (long) newtime;
X#else
X		tvals[1].tv_sec  = newtime;
X		tvals[1].tv_usec = 0;
X#endif
X	}
X
X#ifdef	ATT
X	timbuf.actime  = vals[0];
X	timbuf.modtime = vals[1];
X	node->n_rtime  = vals[1];
X#else
X	node->n_rtime  = (time_t) tvals[1].tv_sec;
X#endif
X
X	if (nflag)
X		return;
X
X#ifdef	ATT
X	if (utime(node->n_name, &timbuf) != 0)
X#else
X	if (utimes(node->n_name, tvals) != 0)
X#endif
X	{
X		sprintf(scratchbuf, "cake system error, utime(s) %s", node->n_name);
X		perror(scratchbuf);
X		fprintf(stderr, "cake: continuing\n");
X	}
X
X#ifdef	CAKEDEBUG
X	if (cakedebug)
X	{
X		if (stat(node->n_name, &statbuf) != 0)
X			printf("cannot verify utimes\n");
X		else
X		{
X			print_time("accessed", statbuf.st_atime);
X			print_time("modified", statbuf.st_mtime);
X			print_time("changed ", statbuf.st_ctime);
X		}
X	}
X#endif
X}
X
X/*
X**	See if the given file exists.
X*/
X
Xbool
Xexist(name)
Xreg	char	*name;
X{
X	Stat	statbuf;
X
X	if (stat(name, &statbuf) == 0)
X		return TRUE;
X	else
X		return FALSE;
X}
X
X/*
X**	Remove the given file.
X*/
X
Xcake_remove(name)
Xreg	char	*name;
X{
X#ifdef	CAREFUL
X	if (! Xflag)
X	{
X		extern	int	getpid();
X		extern	int	cake_proc();
X		extern	Wait	cake_wait();
X		extern	char	*noslash();
X		reg	int	pid, cakepid;
X		Stat		statbuf;
X		Wait		code;
X		char		buf[80];
X
X#ifndef	ATT
X		if (lstat(name, &statbuf) != 0)
X		{
X			sprintf(scratchbuf, "cake system error, lstat %s", name);
X			perror(scratchbuf);
X			fprintf(stderr, "cake: continuing\n");
X			return;
X		}
X
X		if ((statbuf.st_mode & S_IFMT) == S_IFLNK)
X		{
X			printf("removing the symbolic link %s\n", name);
X			cdebug("cake_remove unlink %s\n", name);
X			if (unlink(name) != 0)
X			{
X				sprintf(scratchbuf, "cake system error, unlink %s", name);
X				perror(scratchbuf);
X				fprintf(stderr, "cake: continuing\n");
X			}
X
X			return;
X		}
X#endif
X
X		cakepid = getpid();
X		printf("moving %s to /tmp\n", name);
X		sprintf(buf, "mv %s /tmp/%s.%d", name, noslash(name), cakepid);
X		pid = cake_proc(buf, Exec, (char *) NULL, (Node *) NULL,
X			(int (*)()) NULL, (List *) NULL);
X		code = cake_wait(pid);
X		if (code.w_status != 0)
X		{
X			fprintf(stderr, "cake system error: '%s' failed\n", buf);
X			fprintf(stderr, "cake: continuing\n");
X			return;
X		}
X
X		sprintf(buf, "/tmp/%s.%d", noslash(name), cakepid);
X		if (chmod(buf, 0600) != 0)
X		{
X			sprintf(scratchbuf, "cake system error, chmod %s", buf);
X			perror(scratchbuf);
X			fprintf(stderr, "cake: continuing\n");
X			return;
X		}
X
X		return;
X	}
X#endif
X
X	cdebug("cake_remove unlink %s\n", name);
X	if (unlink(name) != 0)
X	{
X		sprintf(scratchbuf, "cake system error, unlink %s", name);
X		perror(scratchbuf);
X		fprintf(stderr, "cake: continuing\n");
X	}
X}
SHAR_EOF
if test 12147 -ne "`wc -c < 'file.c'`"
then
	echo shar: "error transmitting 'file.c'" '(should have been 12147 characters)'
fi
fi
exit 0
#	End of shell archivec