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