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