rsalz@uunet.UU.NET (Rich Salz) (10/16/87)
Submitted-by: Zoltan Somogyi <zs@munnari.oz>
Posting-number: Volume 12, Issue 9
Archive-name: cake/part03
#! /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:
# list.c
# main.c
# make.c
# mem.c
# pat.c
# print.c
# proc.c
# sym.c
# sys.c
# table.c
# test.c
# trail.c
# This archive created: Wed Oct 14 21:10:02 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'list.c'" '(2669 characters)'
if test -f 'list.c'
then
echo shar: "will not over-write existing file 'list.c'"
else
sed 's/^X//' << \SHAR_EOF > 'list.c'
X/*
X** Linked list module.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/list.c,v 1.14 86/07/19 12:23:15 zs Exp $";
X
X#include "cake.h"
X
X/*
X** Make an empty list.
X*/
X
XList *
Xmakelist0()
X{
X reg List *list;
X
X list = make(List);
X ldata(list) = (Cast) 0;
X next(list) = list;
X prev(list) = list;
X
X return list;
X}
X
X/*
X** Make a list with the argument is its only element.
X*/
X
XList *
X_makelist(data)
Xreg Cast data;
X{
X return addhead(makelist0(), data);
X}
X
X/*
X** Add some data to the head of a list.
X*/
X
XList *
X_addhead(list, data)
Xreg List *list;
Xreg Cast data;
X{
X reg List *item;
X
X if (list == NULL)
X list = makelist0();
X
X item = make(List);
X ldata(item) = data;
X ldata(list) = (Cast) ((int) ldata(list) + 1);
X
X /* item's pointers */
X next(item) = next(list);
X prev(item) = list;
X /* neighbours' pointers */
X next(prev(item)) = item;
X prev(next(item)) = item;
X
X return list;
X}
X
X/*
X** Add some data to the tail of a list.
X*/
X
XList *
X_addtail(list, data)
Xreg List *list;
Xreg Cast data;
X{
X reg List *item;
X
X if (list == NULL)
X list = makelist0();
X
X item = make(List);
X ldata(item) = data;
X ldata(list) = (Cast) ((int) ldata(list) + 1);
X
X /* item's pointers */
X next(item) = list;
X prev(item) = prev(list);
X /* neighbours' pointers */
X next(prev(item)) = item;
X prev(next(item)) = item;
X
X return list;
X}
X
X/*
X** Destructively append list2 to list1. Since the header of
X** list2 is not meaningful after the operation, it is freed.
X*/
X
XList *
Xaddlist(list1, list2)
Xreg List *list1;
Xreg List *list2;
X{
X if (list1 == NULL)
X list1 = makelist0();
X
X if (list2 == NULL)
X list2 = makelist0();
X
X if (length(list2) > 0)
X {
X if (length(list1) == 0)
X {
X ldata(list1) = ldata(list2);
X /* pointers from header */
X next(list1) = next(list2);
X prev(list1) = prev(list2);
X /* pointers to header */
X prev(next(list1)) = list1;
X next(prev(list1)) = list1;
X }
X else
X {
X ldata(list1) = (Cast) ((int) ldata(list1) + (int) ldata(list2));
X /* end of list 1 to start of list 2 */
X next(prev(list1)) = next(list2);
X prev(next(list2)) = prev(list1);
X /* end of list 2 to start of list 1 */
X next(prev(list2)) = list1;
X prev(list1) = prev(list2);
X }
X }
X
X oldmem((Cast) list2);
X return list1;
X}
X
X/*
X** Return the length of a given list.
X*/
X
Xint
Xlength(list)
Xreg List *list;
X{
X if (list == NULL)
X return 0;
X
X return (int) ldata(list);
X}
X
X/*
X** Delete an item from its linked list, and free the node.
X*/
X
Xdelete(list, item)
Xreg List *list;
Xreg List *item;
X{
X if (list == NULL)
X return;
X
X if (item == NULL)
X return;
X
X ldata(list) = (Cast) ((int) ldata(list) - 1);
X next(prev(item)) = next(item);
X prev(next(item)) = prev(item);
X
X oldmem((Cast) item);
X}
SHAR_EOF
if test 2669 -ne "`wc -c < 'list.c'`"
then
echo shar: "error transmitting 'list.c'" '(should have been 2669 characters)'
fi
fi
echo shar: "extracting 'main.c'" '(9780 characters)'
if test -f 'main.c'
then
echo shar: "will not over-write existing file 'main.c'"
else
sed 's/^X//' << \SHAR_EOF > 'main.c'
X/*
X** Cake main file.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/main.c,v 1.15 87/10/05 20:14:53 zs Exp $";
X
X#include "cake.h"
X#include <pwd.h>
X#include <signal.h>
X#include <sys/stat.h>
X
Xtypedef struct passwd Pwent;
Xtypedef struct stat Stat;
X
Xint Gflag = FALSE;
Xint Lflag = FALSE;
Xint Rflag = FALSE;
Xint Xflag = FALSE;
Xint Zflag = FALSE;
Xint bflag = FALSE;
Xint cflag = FALSE;
Xint dflag = FALSE;
Xint gflag = FALSE;
Xint iflag = FALSE;
Xint kflag = TRUE;
Xint nflag = FALSE;
Xint qflag = FALSE;
Xint rflag = FALSE;
Xint sflag = FALSE;
Xint tflag = FALSE;
Xint vflag = FALSE;
Xint wflag = FALSE;
Xint xflag = FALSE;
Xint zflag = FALSE;
X
Xchar *cakefile = NULL;
Xchar *shellfile[2] = { SYSTEM_CMD, SCRIPT_CMD };
Xchar *metachars = METACHARS;
Xint maxprocs = 1;
XList *active_procs;
Xchar scratchbuf[128];
X
Xint cakedebug = FALSE;
Xint entrydebug = FALSE;
Xint patdebug = FALSE;
Xint lexdebug = FALSE;
X
Xchar cakeflagbuf[MAXSIZE];
Xchar *cppargv[MAXARGS];
Xint cppargc = 0;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X extern cake_abort(), cake_finish();
X extern int parse_args(), process_args();
X extern Node *chase();
X extern char *getenv();
X extern char *dir_setup();
X extern Pwent *getpwuid();
X extern int geteuid();
X extern FILE *cake_popen();
X extern FILE *yyin;
X extern int yydebug;
X Stat statbuf;
X int envc;
X char *envv[MAXARGS];
X reg Pwent *pwent;
X reg char *envstr;
X reg int status;
X reg Node *rootnode;
X
X signal(SIGINT, cake_finish);
X signal(SIGQUIT, cake_finish);
X signal(SIGILL, cake_abort);
X signal(SIGTRAP, cake_abort);
X signal(SIGIOT, cake_abort);
X signal(SIGEMT, cake_abort);
X signal(SIGFPE, cake_abort);
X signal(SIGBUS, cake_abort);
X signal(SIGSEGV, cake_abort);
X signal(SIGSYS, cake_abort);
X signal(SIGPIPE, cake_abort);
X signal(SIGALRM, cake_abort);
X
X yydebug = FALSE;
X active_procs = makelist0();
X
X if (rindex(argv[0], 'f') != NULL
X && streq(rindex(argv[0], 'f'), "fake"))
X cakedebug = TRUE;
X
X init_sym();
X cppargv[cppargc++] = new_name(CPP);
X strcpy(cakeflagbuf, "-DCAKEFLAGS=");
X
X if ((envstr = getenv("CAKE")) != NULL)
X {
X envc = parse_args(envstr, envv);
X process_args(envv, &envc, 0);
X if (envc > 0)
X fprintf(stderr, "cake: non-options in environment ignored\n");
X }
X
X argv += process_args(argv, &argc, 1);
X
X#ifdef CAKEDEBUG
X if (cakedebug || entrydebug || patdebug || lexdebug)
X setlinebuf(stdout);
X#endif
X
X if (cakefile == NULL)
X {
X if (stat("cakefile", &statbuf) == 0)
X cakefile = "cakefile";
X or (stat("Cakefile", &statbuf) == 0)
X cakefile = "Cakefile";
X or (stat("recipe", &statbuf) == 0)
X cakefile = "recipe";
X or (stat("Recipe", &statbuf) == 0)
X cakefile = "Recipe";
X else
X {
X fprintf(stderr, "cake: cannot locate a cakefile\n");
X exit(1);
X }
X }
X
X if (gflag)
X cakefile = dir_setup(cakefile);
X
X pwent = getpwuid(geteuid());
X strcpy(scratchbuf, "-I");
X strcat(scratchbuf, pwent->pw_dir);
X strcat(scratchbuf, ULIB);
X cppargv[cppargc++] = new_name(scratchbuf);
X strcpy(scratchbuf, "-I");
X strcat(scratchbuf, SLIB);
X cppargv[cppargc++] = new_name(scratchbuf);
X cppargv[cppargc++] = cakeflagbuf;
X cppargv[cppargc++] = cakefile;
X cppargv[cppargc] = NULL;
X
X if (cakedebug)
X {
X reg int i;
X
X for (i = 0; i < cppargc; i++)
X printf("%s\n", cppargv[i]);
X }
X
X if ((yyin = cake_popen(cppargv, "r")) == NULL)
X {
X fprintf(stderr, "cake: cannot open cpp filter\n");
X exit(1);
X }
X
X if (Zflag)
X {
X reg int c;
X
X while ((c = getc(yyin)) != EOF)
X putchar(c);
X
X cake_pclose(yyin);
X exit(0);
X }
X
X yyinit();
X init_entry();
X if (yyparse())
X {
X fprintf(stderr, "cake: cannot parse %s\n", cakefile);
X exit(1);
X }
X
X shell_setup(shellfile[0], 0);
X shell_setup(shellfile[1], 1);
X meta_setup(metachars);
X
X cake_pclose(yyin);
X dir_start();
X prep_entries();
X final_entry(argc, argv);
X
X rootnode = chase(CHASEROOT, 0, (Entry *) NULL);
X
X if (! qflag)
X execute(rootnode);
X
X dir_finish();
X cleanup();
X#ifdef STATS_FILE
X statistics();
X#endif
X
X status = (off_node(rootnode, nf_ERR) && is_ok(rootnode))? 0: 1;
X cdebug("exit status %d\n", status);
X exit(status);
X}
X
Xint
Xprocess_args(vector, count, base)
Xreg char **vector;
Xreg int *count;
Xreg int base;
X{
X reg int i, j;
X
X j = 0;
X cdebug("process args:");
X while (*count > base && vector[base][0] == '-')
X {
X putflag(base, vector[base]);
X
X for (i = 1; vector[base][i] != '\0'; i++)
X {
X switch (vector[base][i])
X {
X
X#ifdef CAKEDEBUG
X when 'C': cdebug(" -C");
X cakedebug = ! cakedebug;
X
X when 'E': cdebug(" -E");
X entrydebug = ! entrydebug;
X
X when 'P': cdebug(" -P");
X patdebug = ! patdebug;
X
X when 'W': cdebug(" -W");
X lexdebug = TRUE;
X
X when 'Y': cdebug(" -Y");
X yydebug = TRUE;
X#endif
X when 'G': cdebug(" -G");
X Gflag = TRUE;
X
X when 'L': cdebug(" -L");
X Lflag = TRUE;
X
X when 'R': cdebug(" -R");
X Rflag = TRUE;
X
X when 'X': cdebug(" -X");
X Xflag = TRUE;
X
X when 'Z': cdebug(" -Z");
X Zflag = TRUE;
X
X when 'a': cdebug(" -a");
X kflag = FALSE;
X
X when 'b': cdebug(" -b");
X bflag = TRUE;
X
X when 'c': cdebug(" -c");
X cflag = TRUE;
X
X when 'd': cdebug(" -d");
X dflag = TRUE;
X
X when 'g': cdebug(" -g");
X gflag = TRUE;
X
X when 'i': cdebug(" -i");
X iflag = TRUE;
X
X when 'k': cdebug(" -k");
X kflag = TRUE;
X
X when 'n': cdebug(" -n");
X nflag = TRUE;
X tflag = FALSE;
X qflag = FALSE;
X
X when 'q': cdebug(" -q");
X qflag = TRUE;
X nflag = FALSE;
X tflag = FALSE;
X
X when 'r': cdebug(" -r");
X rflag = TRUE;
X
X when 's': cdebug(" -s");
X sflag = TRUE;
X
X when 't': cdebug(" -t");
X tflag = TRUE;
X nflag = FALSE;
X qflag = FALSE;
X
X when 'v': cdebug(" -v");
X vflag = TRUE;
X
X when 'w': cdebug(" -w");
X wflag = TRUE;
X
X when 'x': cdebug(" -x");
X xflag = TRUE;
X
X when 'z': cdebug(" -z");
X zflag = TRUE;
X
X when 'D':
X case 'I':
X case 'U': if (i != 1)
X usage();
X
X cdebug(" %s", vector[base]);
X cppargv[cppargc++] = new_name(vector[base]);
X goto nextword;
X
X when 'N': putflag(base, vector[base+1]);
X sscanf(vector[base+1], "%d", &maxprocs);
X if (vector[base][i+1] != '\0')
X usage();
X
X cdebug(" -N %d", maxprocs);
X (*count)--;
X vector++, j++;
X goto nextword;
X
X when 'S': putflag(base, vector[base+1]);
X if (vector[base][i+1] == '1')
X shellfile[0] = new_name(vector[base+1]);
X or (vector[base][i+1] == '2')
X shellfile[1] = new_name(vector[base+1]);
X else
X usage();
X
X if (vector[base][i+2] != '\0')
X usage();
X
X cdebug(" -S%c %s", vector[base][i+1], vector[base+1]);
X (*count)--;
X vector++, j++;
X goto nextword;
X
X when 'T': putflag(base, vector[base+1]);
X metachars = new_name(vector[base+1]);
X if (vector[base][i+1] != '\0')
X usage();
X
X cdebug(" -T %s", metachars);
X (*count)--;
X vector++, j++;
X goto nextword;
X
X when 'f': putflag(base, vector[base+1]);
X cakefile = new_name(vector[base+1]);
X if (vector[base][i+1] != '\0')
X usage();
X
X cdebug(" -f %s", cakefile);
X (*count)--;
X vector++, j++;
X goto nextword;
X
X otherwise: usage();
X }
X }
X
Xnextword:
X (*count)--;
X vector++, j++;
X }
X
X cdebug(" \n%return %d\n", j);
X return j;
X}
X
X/*
X** Put a flag into the CAKEFLAGS definition.
X*/
X
Xputflag(base, flag)
Xreg int base;
Xreg char *flag;
X{
X if (base == 1)
X {
X strcat(cakeflagbuf, " ");
X strcat(cakeflagbuf, flag);
X if (strlen(cakeflagbuf) >= MAXSIZE)
X {
X fprintf(stderr, "cake: CAKEFLAGS too long\n");
X exit(1);
X }
X }
X}
X
X/*
X** Tell the unfortunate user how to use cake.
X*/
X
Xusage()
X{
X fprintf(stderr, "Usage: cake [-abcdgiknqrstvwxzGLRXZ] [-ffile]\n");
X fprintf(stderr, " [-Ddefn] [-Idir] [-Uname] [-S shell] [-T metachars] [file ...]\n");
X exit(1);
X}
X
Xexit_cake(needtrail)
Xreg int needtrail;
X{
X if (cakedebug && needtrail)
X get_trail(stdout);
X else
X dir_finish();
X
X exit(1);
X}
X
X/*
X** Handle bus errors and segmentation violations.
X*/
X
Xcake_abort()
X{
X
X signal(SIGINT, SIG_IGN);
X signal(SIGQUIT, SIG_IGN);
X
X printf("Abort on signal\n");
X if (cakedebug)
X get_trail(stdout);
X
X signal(SIGQUIT, SIG_DFL);
X kill(getpid(), SIGQUIT);
X}
X
X/*
X** Handle user interrupts.
X*/
X
Xcake_finish()
X{
X reg List *ptr;
X reg Proc *proc;
X
X signal(SIGINT, SIG_IGN);
X signal(SIGQUIT, SIG_IGN);
X
X printf("*** Interrupt\n");
X fflush(stdout);
X for_list (ptr, active_procs)
X {
X proc = (Proc *) ldata(ptr);
X if (proc->pr_node != NULL)
X cake_error(proc->pr_node);
X }
X
X exit_cake(FALSE);
X}
X
X#ifdef STATS_FILE
X#ifdef ATT
X#include <sys/times.h>
X
Xtypedef struct tms Tms;
X#else
X#include <sys/time.h>
X#include <sys/resource.h>
X
Xtypedef struct rusage Rusage;
X#endif
X
Xstatistics()
X{
X extern char *getlogin();
X extern getpw();
X extern int out_tried, out_found;
X extern int stat_tried, stat_found;
X FILE *sfp;
X
X if ((sfp = fopen(STATS_FILE, "a")) != NULL)
X {
X#ifdef ATT
X Tms tbuf;
X#else
X Rusage s, c;
X#endif
X long su, ss, cu, cs;
X char *usr;
X
X if ((usr = getlogin()) == NULL)
X {
X char buf[256];
X char *usr_end;
X
X if (getpw(getuid(), buf) != 0)
X usr = "NULL";
X else
X {
X usr = buf;
X if ((usr_end = index(usr, ':')) != NULL)
X *usr_end = '\0';
X else
X usr = "NULL";
X }
X
X usr = new_name(usr);
X }
X
X#ifdef ATT
X if (times(&tbuf) == -1)
X {
X fclose(sfp);
X return;
X }
X
X su = tbuf.tms_utime*100/TIMERES;
X ss = tbuf.tms_stime*100/TIMERES;
X cu = tbuf.tms_cutime*100/TIMERES;
X cs = tbuf.tms_cstime*100/TIMERES;
X#else
X getrusage(RUSAGE_SELF, &s);
X getrusage(RUSAGE_CHILDREN, &c);
X
X su = s.ru_utime.tv_sec*100 + s.ru_utime.tv_usec/10000;
X ss = s.ru_stime.tv_sec*100 + s.ru_stime.tv_usec/10000;
X cu = c.ru_utime.tv_sec*100 + c.ru_utime.tv_usec/10000;
X cs = c.ru_stime.tv_sec*100 + c.ru_stime.tv_usec/10000;
X#endif
X fprintf(sfp, "%s %ld %ld %ld %ld ", usr, su, ss, cu, cs);
X fprintf(sfp, "%d %d %d %d %d\n", sbrk(0),
X out_tried, out_found, stat_tried, stat_found);
X
X fclose(sfp);
X }
X}
X#endif
SHAR_EOF
if test 9780 -ne "`wc -c < 'main.c'`"
then
echo shar: "error transmitting 'main.c'" '(should have been 9780 characters)'
fi
fi
echo shar: "extracting 'make.c'" '(2967 characters)'
if test -f 'make.c'
then
echo shar: "will not over-write existing file 'make.c'"
else
sed 's/^X//' << \SHAR_EOF > 'make.c'
X/*
X** Module to make Cake data structures.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/make.c,v 1.15 87/10/05 20:15:02 zs Exp $";
X
X#include "cake.h"
X
XNode *
Xmake_node(name)
Xreg char *name;
X{
X reg Node *node;
X
X node = make(Node);
X node->n_name = name;
X
X if (name == NULL)
X return node;
X
X node->n_kind = n_NOWAY; /* not yet */
X node->n_flag = 0;
X node->n_old = makelist0();
X node->n_when = NULL;
X node->n_act = NULL;
X node->n_new = makelist(node);
X#if 0 /* N must be 1 for the time being */
X node->n_pid = 0;
X node->n_file = NULL;
X#endif
X node_stat(node);
X node->n_utime = 0;
X node->n_stime = node->n_rtime;
X node->n_badguys = NULL;
X node->n_msg = NULL;
X
X return node;
X}
X
XEntry *
Xmake_dep(new, old, cond, file, colon)
Xreg List *new; /* of Pat */
Xreg List *old; /* of Pat */
Xreg Test *cond;
Xreg char *file;
Xreg char *colon;
X{
X reg Entry *entry;
X
X entry = make(Entry);
X entry->e_new = new;
X entry->e_old = old;
X entry->e_cond = cond;
X entry->e_file = file;
X entry->e_dblc = streq(colon, "::");
X entry->e_when = makelist0();
X
X return entry;
X}
X
XTest *
Xmake_test_mm(name)
Xreg Pat *name;
X{
X reg Test *test;
X
X test = make(Test);
X test->t_kind = t_MATCH;
X test->t_pat = name;
X test->t_list = makelist0();
X
X return test;
X}
X
XTest *
Xmake_test_m(name, varpat, pattern)
Xreg Pat *name;
Xreg Pat *varpat;
Xreg Pat *pattern;
X{
X reg Test *test;
X
X test = make(Test);
X test->t_kind = t_MATCH;
X test->t_pat = name;
X test->t_list = makelist0();
X
X if (varpat == NULL)
X addtail(test->t_list, make_pat("-vX", FALSE, 0));
X else
X addtail(test->t_list, varpat); /* no assignment */
X
X addtail(test->t_list, pattern); /* no assignment */
X return test;
X}
X
XTest *
Xmake_test_c(cmd)
Xreg char *cmd;
X{
X reg Test *test;
X
X test = make(Test);
X test->t_kind = t_CMD;
X test->t_cmd = cmd;
X
X return test;
X}
X
XTest *
Xmake_test_s(tkind, pat)
Xreg T_kind tkind;
Xreg Pat *pat;
X{
X reg Test *test;
X
X test = make(Test);
X test->t_kind = tkind;
X test->t_pat = pat;
X
X return test;
X}
X
XTest *
Xmake_test_l(pat, list)
Xreg Pat *pat;
Xreg List *list; /* of Pat */
X{
X reg Test *test;
X
X test = make(Test);
X test->t_kind = t_LIST;
X test->t_pat = pat;
X test->t_list = list;
X
X return test;
X}
X
XTest *
Xmake_test_b(tkind, left, right)
Xreg T_kind tkind;
Xreg Test *left;
Xreg Test *right;
X{
X reg Test *test;
X
X test = make(Test);
X test->t_kind = tkind;
X test->t_left = left;
X test->t_right = right;
X
X return test;
X}
X
XTest *
Xmake_test_u(tkind, left)
Xreg T_kind tkind;
Xreg Test *left;
X{
X reg Test *test;
X
X test = make(Test);
X test->t_kind = tkind;
X test->t_left = left;
X test->t_right = (Test *) NULL;
X
X return test;
X}
X
XPat *
Xmake_pat(text, iscmd, flags)
Xreg char *text;
Xreg bool iscmd;
Xreg int flags;
X{
X reg Pat *pat;
X
X pat = make(Pat);
X pat->p_str = text;
X pat->p_cmd = iscmd;
X pat->p_flag = flags;
X
X checkpatlen(text);
X return pat;
X}
X
XAct *
Xmake_act(str, flag)
Xreg char *str;
Xreg int flag;
X{
X reg Act *act;
X
X act = make(Act);
X act->a_str = str;
X act->a_flag = flag;
X
X return act;
X}
SHAR_EOF
if test 2967 -ne "`wc -c < 'make.c'`"
then
echo shar: "error transmitting 'make.c'" '(should have been 2967 characters)'
fi
fi
echo shar: "extracting 'mem.c'" '(822 characters)'
if test -f 'mem.c'
then
echo shar: "will not over-write existing file 'mem.c'"
else
sed 's/^X//' << \SHAR_EOF > 'mem.c'
X/*
X** Memory management module.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/mem.c,v 1.15 87/10/05 20:15:10 zs Exp $";
X
X#include "cake.h"
X
X/*
X** Allocate space if possible. In the future it may record
X** all pointers returned, so that old can do some checks.
X*/
X
XCast
Xnewmem(size)
Xint size;
X{
X reg char *space;
X
X if ((space = malloc((unsigned) size)) == NULL)
X {
X fprintf(stderr, "cake system error: no more malloc\n");
X exit_cake(FALSE);
X }
X
X#ifdef EXTRACHECK
X if (((int) space & 03) != 0)
X {
X fprintf(stderr, "cake system error: malloc not aligned\n");
X exit_cake(FALSE);
X }
X#endif
X
X return (Cast) space;
X}
X
X/*
X** Return some storage to the free list. This storage must
X** have been obtained from new and malloc.
X*/
X
X/*ARGSUSED*/
Xoldmem(ptr)
XCast ptr;
X{
X#ifdef MEMUSED
X free((char *) ptr);
X#endif
X}
SHAR_EOF
if test 822 -ne "`wc -c < 'mem.c'`"
then
echo shar: "error transmitting 'mem.c'" '(should have been 822 characters)'
fi
fi
echo shar: "extracting 'pat.c'" '(6360 characters)'
if test -f 'pat.c'
then
echo shar: "will not over-write existing file 'pat.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pat.c'
X/*
X** Module to manipulate Cake patterns.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/pat.c,v 1.15 87/10/05 20:15:15 zs Exp $";
X
X#include "cake.h"
X#include <ctype.h>
X
X/*
X** This function serves as an interface to the rest of the system
X** for domatch, looking after its environment.
X*/
X
Xbool
Xmatch(env, str, pat)
XEnv env;
Xreg char *str;
Xreg Pat *pat;
X{
X extern bool domatch();
X reg bool result;
X reg int i;
X reg char *s, *p;
X
X if (patdebug)
X printf("Match %s vs %s\n", str, pat->p_str);
X
X if (pat->p_cmd)
X {
X fprintf(stderr, "cake internal error: undereferenced pattern %s in match\n",
X pat->p_str);
X exit_cake(TRUE);
X }
X
X if (streq(str, CHASEROOT))
X result = streq(pat->p_str, CHASEROOT);
X else
X {
X result = TRUE; /* assume so for the moment */
X p = pat->p_str+strlen(pat->p_str)-1;
X if (*p != '%' && !isdigit(*p)) /* not part of a var */
X {
X s = str+strlen(str)-1;
X if (*s != *p) /* last chars differ */
X result = FALSE;
X }
X
X if (result) /* if last-char test inconclusive */
X {
X for (i = 0; i < MAXVAR; i++)
X env[i].v_bound = FALSE;
X
X result = domatch(env, str, pat->p_str);
X }
X }
X
X if (patdebug)
X {
X if (result == FALSE)
X printf("Match failed\n");
X else
X {
X printf("Match succeeded\n");
X for (i = 0; i < MAXVAR; i++)
X if (env[i].v_bound)
X printf("X%d: %s\n", i, env[i].v_val);
X }
X }
X
X return result;
X}
X
X/*
X** Match a string against a pattern.
X** The pattern is expected to have been dereferenced.
X** To handle nondeterminism, a brute force recursion approach
X** is taken.
X*/
X
Xbool
Xdomatch(env, str, patstr)
XEnv env;
Xreg char *str;
Xreg char *patstr;
X{
X char buf[MAXPATSIZE];
X reg char *follow;
X reg char *s, *t;
X reg int varno;
X reg int i;
X reg bool more;
X
X put_trail("domatch", "start");
X if (patstr[0] == '%')
X {
X if (isdigit(patstr[1]))
X {
X varno = patstr[1] - '0';
X follow = patstr + 2;
X }
X else
X {
X varno = NOVAR;
X follow = patstr + 1;
X }
X
X if (env[varno].v_bound)
X {
X /* lifetime of buf is local */
X strcpy(buf, env[varno].v_val);
X strcat(buf, follow);
X checkpatlen(buf);
X put_trail("domatch", "recurse");
X return domatch(env, str, buf);
X }
X
X i = 0;
X buf[0] = '\0';
X env[varno].v_bound = TRUE;
X env[varno].v_val = buf;
X
X /* keep invariant: buf = tentative value of var */
X /* the value of a variable may never contain a % */
X /* must consider *s == \0, but do not overshoot */
X for (s = str, more = TRUE; more && *s != '%'; s++)
X {
X if (patdebug)
X printf("trying X%d = '%s'\n", varno, buf);
X
X if (domatch(env, s, follow))
X {
X checkpatlen(buf);
X env[varno].v_val = new_name(buf);
X /* lifetime of buf is now global */
X put_trail("domatch", "finish");
X return TRUE;
X }
X
X /* maintain invariant */
X buf[i++] = *s;
X buf[i] = '\0';
X
X more = (*s != '\0');
X }
X
X /* no luck, match failed */
X env[varno].v_bound = FALSE;
X put_trail("domatch", "finish");
X return FALSE;
X }
X
X /* here we have something other than a variable first off */
X for (s = str, t = patstr; *t != '\0' && *t != '%'; s++, t++)
X {
X if (*t == '\\')
X t++; /* the new *t is not checked for % */
X
X if (*s != *t)
X {
X put_trail("domatch", "finish");
X return FALSE;
X }
X }
X
X /* see if we have come to the end of the pattern */
X if (*t == '\0')
X {
X put_trail("domatch", "finish");
X return *s == '\0';
X }
X
X /* if not, recurse on the next variable */
X put_trail("domatch", "recurse");
X return domatch(env, s, t);
X}
X
X/*
X** Ground the argument string, i.e. replace all occurrences
X** of variables in it. It is fatal error for the string to
X** contain a variable which has no value.
X*/
X
Xchar *
Xground(env, str)
XEnv env;
Xreg char *str;
X{
X reg char *s, *t;
X reg int i, var;
X char buf[MAXSIZE];
X
X put_trail("ground", "start");
X i = 0;
X for (s = str; *s != '\0'; s++)
X {
X if (*s == '%')
X {
X if (isdigit(s[1]))
X var = *++s - '0';
X else
X var = NOVAR;
X
X if (! env[var].v_bound)
X {
X if (var == NOVAR)
X fprintf(stderr, "cake: %s is undefined in %s\n", "%", str);
X else
X fprintf(stderr, "cake: %s%1d is undefined in %s\n", "%", var, str);
X exit_cake(FALSE);
X }
X
X for (t = env[var].v_val; *t != '\0'; t++)
X buf[i++] = *t;
X }
X or (*s == '\\')
X {
X if (s[1] != '\0')
X buf[i++] = *++s;
X }
X else
X buf[i++] = *s;
X }
X
X buf[i] = '\0';
X if (i >= MAXSIZE)
X {
X fprintf(stderr, "cake internal error: pattern buffer overflow for %s\n", buf);
X exit_cake(FALSE);
X }
X
X put_trail("ground", "new_name finish");
X return new_name(buf);
X}
X
X/*
X** See if the argument contains any variebles.
X*/
X
Xbool
Xhasvars(str)
Xreg char *str;
X{
X reg char *s;
X
X for (s = str; *s != '\0'; s++)
X {
X if (*s == '%')
X return TRUE;
X or (*s == '\\')
X {
X if (s[1] != '\0')
X s++;
X }
X }
X
X return FALSE;
X}
X
X/*
X** Dereference the pattern; i.e. if it a command pattern
X** replace it with the output of the command. It is the task
X** of the caller to break this up if necessary.
X** Note that the pattern will never have to be dereferenced again.
X**
X** The second arg says whether the pattern is to be broken after
X** dereferencing: this is needed purely for debugging purposes.
X*/
X
Xderef(pat, broken)
Xreg Pat *pat;
Xreg bool broken;
X{
X extern char *expand_cmds();
X
X if (! pat->p_cmd)
X return;
X
X pat->p_cmd = FALSE;
X pat->p_str = expand_cmds(pat->p_str);
X
X if (! broken)
X checkpatlen(pat->p_str);
X}
X
X/*
X** Break the given pattern up into a (possibly empty) list
X** of smaller patterns at white space positions.
X*/
X
XList *
Xbreak_pat(pat)
Xreg Pat *pat;
X{
X reg Pat *newpat;
X reg List *list;
X reg char *s;
X
X put_trail("break_pat", "start");
X if (pat->p_cmd)
X {
X fprintf(stderr, "cake internal error: trying to break command pattern %s\n", pat->p_str);
X exit_cake(TRUE);
X }
X
X list = makelist0();
X s = pat->p_str;
X while (*s != '\0')
X {
X char buf[MAXSIZE];
X reg int i;
X
X for (; *s != '\0' && isspace(*s); s++)
X ;
X
X i = 0;
X for (; *s != '\0' && !isspace(*s); s++)
X buf[i++] = *s;
X
X buf[i] = '\0';
X if (i > 0)
X {
X newpat = make_pat(new_name(buf), FALSE, pat->p_flag);
X addtail(list, newpat); /* no assignment */
X }
X }
X
X put_trail("break_pat", "finish");
X return list;
X}
X
X/*
X** Add flags to a list of patterns.
X*/
X
XList *
Xset_flag(patlist, flags)
Xreg List *patlist;
Xreg int flags;
X{
X reg List *ptr;
X reg Pat *pat;
X
X for_list (ptr, patlist)
X {
X pat = (Pat *) ldata(ptr);
X pat->p_flag |= flags;
X }
X
X return patlist;
X}
SHAR_EOF
if test 6360 -ne "`wc -c < 'pat.c'`"
then
echo shar: "error transmitting 'pat.c'" '(should have been 6360 characters)'
fi
fi
echo shar: "extracting 'print.c'" '(4954 characters)'
if test -f 'print.c'
then
echo shar: "will not over-write existing file 'print.c'"
else
sed 's/^X//' << \SHAR_EOF > 'print.c'
X/*
X** Printout routines for Cake data structures.
X*/
X
X#ifdef CAKEDEBUG
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/print.c,v 1.15 87/10/05 20:15:33 zs Exp $";
X
X#include "cake.h"
X
Xchar *
Xstr_pflag(flag)
Xreg int flag;
X{
X char buf[256];
X
X strcpy(buf, "[");
X if (flag & nf_NONVOL)
X strcat(buf, "nonvol ");
X if (flag & nf_PRECIOUS)
X strcat(buf, "precious ");
X if (flag & nf_PSEUDO)
X strcat(buf, "pseudo ");
X if (flag & nf_REDUNDANT)
X strcat(buf, "redundant ");
X if (flag & nf_WHEN)
X strcat(buf, "when ");
X if (flag & nf_DEPNONVOL)
X strcat(buf, "depnonvol ");
X if (flag & nf_NODELETE)
X strcat(buf, "nodelete ");
X if (flag & nf_NEWFILE)
X strcat(buf, "newfile ");
X if (flag & nf_EXIST)
X strcat(buf, "exist ");
X if (flag & nf_BUSY)
X strcat(buf, "busy ");
X if (flag & nf_ERR)
X strcat(buf, "err ");
X if (flag & nf_TRACED)
X strcat(buf, "traced ");
X if (flag & nf_WARNED)
X strcat(buf, "warned ");
X if (flag & nf_ORIG)
X strcat(buf, "orig ");
X
X if (strlen(buf) > 1)
X buf[strlen(buf)-1] = '\0';
X
X strcat(buf, "]");
X return new_name(buf);
X}
X
Xchar *
Xstr_aflag(flag)
Xreg int flag;
X{
X char buf[128];
X
X strcpy(buf, "[");
X if (flag & af_SILENT)
X strcat(buf, "silent ");
X if (flag & af_IGNORE)
X strcat(buf, "ignore ");
X if (flag & af_MINUSN)
X strcat(buf, "minusn ");
X if (flag & af_SYSTEM)
X strcat(buf, "system ");
X if (flag & af_SCRIPT)
X strcat(buf, "script ");
X
X if (strlen(buf) > 1)
X buf[strlen(buf)-1] = '\0';
X
X strcat(buf, "]");
X return new_name(buf);
X}
X
Xprint_pat(pat)
Xreg Pat *pat;
X{
X if (pat->p_cmd)
X printf("`%s`", pat->p_str);
X else
X printf("%s", pat->p_str);
X
X printf(str_pflag(pat->p_flag));
X}
X
Xprint_act(act)
Xreg Act *act;
X{
X printf(str_aflag(act->a_flag));
X printf("%s", act->a_str);
X}
X
Xprint_test(test)
Xreg Test *test;
X{
X reg List *ptr;
X reg char *pre;
X reg Pat *pat;
X
X if (test == NULL)
X {
X printf("null");
X return;
X }
X
X switch (test->t_kind)
X {
X
Xwhen t_TRUE: printf("true");
Xwhen t_FALSE: printf("false");
X
Xwhen t_AND: print_test(test->t_left);
X printf(" and ");
X print_test(test->t_right);
X
Xwhen t_OR: print_test(test->t_left);
X printf(" or ");
X print_test(test->t_right);
X
Xwhen t_NOT: printf("not ");
X print_test(test->t_left);
X
Xwhen t_CMD: printf("cmd `%s`", test->t_cmd);
X
Xwhen t_MATCH: printf("match ");
X print_pat(test->t_pat);
X printf(" against");
X printf(" (");
X print_pat((Pat *) first(test->t_list));
X printf(") ");
X print_pat((Pat *) last(test->t_list));
X
Xwhen t_LIST: printf("list ");
X print_pat(test->t_pat);
X
X printf(" in (");
X pre = "";
X for_list (ptr, test->t_list)
X {
X pat = (Pat *) ldata(ptr);
X printf(pre);
X print_pat(pat);
X pre = ", ";
X }
X
X printf(")");
X
Xwhen t_EXIST: printf("exist ");
X print_pat(test->t_pat);
X
Xwhen t_CANDO: printf("cando ");
X print_pat(test->t_pat);
X
Xwhen t_OK: printf("ok ");
X print_pat(test->t_pat);
X
Xotherwise: printf("Bad type kind %d in print_test\n", test->t_kind);
X
X }
X}
X
Xprint_entry(entry)
Xreg Entry *entry;
X{
X reg List *ptr;
X reg Pat *pat;
X reg Act *act;
X reg char *pre;
X
X printf("ENTRY\nnew: ");
X pre = "";
X for_list (ptr, entry->e_new)
X {
X pat = (Pat *) ldata(ptr);
X printf(pre);
X print_pat(pat);
X pre = ", ";
X }
X
X printf("\nold: ");
X pre = "";
X for_list (ptr, entry->e_old)
X {
X pat = (Pat *) ldata(ptr);
X printf(pre);
X print_pat(pat);
X pre = ", ";
X }
X
X printf("\nwhen: ");
X pre = "";
X for_list (ptr, entry->e_when)
X {
X pat = (Pat *) ldata(ptr);
X printf(pre);
X print_pat(pat);
X pre = ", ";
X }
X
X printf("\ntest: ");
X print_test(entry->e_cond);
X
X printf("\nactions:\n");
X for_list (ptr, entry->e_act)
X {
X act = (Act *) ldata(ptr);
X print_act(act);
X }
X
X printf("\n");
X}
X
Xchar *
Xstr_nkind(nkind)
Xreg N_kind nkind;
X{
X switch (nkind)
X {
Xwhen n_OK: return "ok";
Xwhen n_NOWAY: return "noway";
Xwhen n_CANDO: return "cando";
X }
X
X return "bizarre";
X}
X
Xprint_time(str, ntime)
Xreg char *str;
Xtime_t ntime;
X{
X extern char *ctime();
X
X printf("%s time: %d, %s", str, ntime, ctime(&ntime));
X}
X
Xprint_node(node)
Xreg Node *node;
X{
X reg List *ptr;
X reg char *pre;
X reg Node *bnode;
X reg Act *act;
X
X printf("\nNODE\n%s: kind %s flag %s\n", node->n_name,
X str_nkind(node->n_kind), str_pflag(node->n_flag));
X print_time("real", node->n_rtime);
X print_time("used", node->n_utime);
X print_time("saved", node->n_stime);
X
X printf("new: ");
X pre = "";
X for_list (ptr, node->n_new)
X {
X bnode = (Node *) ldata(ptr);
X printf(pre);
X printf(bnode->n_name);
X pre = ", ";
X }
X
X printf("\nold: ");
X pre = "";
X for_list (ptr, node->n_old)
X {
X bnode = (Node *) ldata(ptr);
X printf(pre);
X printf(bnode->n_name);
X pre = ", ";
X }
X
X printf("\naction:\n");
X for_list (ptr, node->n_act)
X {
X act = (Act *) ldata(ptr);
X print_act(act);
X }
X
X if (node->n_badguys != (List *) NULL)
X {
X printf("bad guys: ");
X pre = "";
X for_list (ptr, node->n_badguys)
X {
X bnode = (Node *) ldata(ptr);
X printf(pre);
X printf(bnode->n_name);
X pre = ", ";
X }
X
X printf("\n");
X }
X
X if (node->n_msg != NULL)
X printf("msg: %s", node->n_msg);
X
X printf("\n");
X}
X
X#endif
SHAR_EOF
if test 4954 -ne "`wc -c < 'print.c'`"
then
echo shar: "error transmitting 'print.c'" '(should have been 4954 characters)'
fi
fi
echo shar: "extracting 'proc.c'" '(8939 characters)'
if test -f 'proc.c'
then
echo shar: "will not over-write existing file 'proc.c'"
else
sed 's/^X//' << \SHAR_EOF > 'proc.c'
X/*
X** Cake interface to the other processes.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/proc.c,v 1.15 87/10/05 20:15:45 zs Exp $";
X
X#include "cake.h"
X#include <ctype.h>
X#include <errno.h>
X#include <signal.h>
X#include <fcntl.h>
X#include <sys/param.h>
X
Xextern List *active_procs; /* of Proc */
X
X/*
X** Process the given command in the manner described by the args.
X*/
X
Xint
Xcake_proc(cmd, type, file, node, func, args)
Xreg char *cmd;
Xreg A_kind type;
Xreg char *file;
Xreg Node *node;
Xreg int (*func)();
Xreg List *args;
X{
X extern bool has_meta();
X extern char *strip_backslash();
X extern char *shell_path[2];
X extern char *shell_cmd[2];
X extern char *shell_opt[2];
X char *argv[MAXARGS];
X reg Proc *proc;
X reg int pid;
X reg char *script_filename;
X
X cmd = strip_backslash(cmd);
X
X#ifdef CAKEDEBUG
X if (file != NULL)
X cdebug("file is %s\n", file);
X else
X cdebug("file is NULL\n");
X#endif
X
X if (type == Exec && has_meta(cmd, TRUE))
X type = System;
X
X cdebug("cake_proc, type %d: %s\n", type, cmd);
X fflush(stdout);
X
X if (type == Exec)
X parse_args(cmd, argv);
X or (type == Script)
X {
X reg FILE *script_fp;
X
X script_filename = get_newname();
X if ((script_fp = fopen(script_filename, "w")) == NULL)
X {
X sprintf(scratchbuf, "cake system error, fopen %s", script_filename);
X perror(scratchbuf);
X exit_cake(FALSE);
X }
X
X fprintf(script_fp, "%s", cmd);
X fclose(script_fp);
X }
X
X mutex_lock();
X if ((pid = vfork()) == 0)
X {
X reg int fd;
X
X#ifdef CLOSE_ALL
X for (fd = 3; fd < NOFILE; fd++)
X close(fd);
X#endif
X
X if (file != NULL)
X {
X if (close(1) != 0)
X {
X perror("cake system error, close stdout");
X _exit(127);
X }
X
X if ((fd = open(file, O_WRONLY|O_CREAT, 0600)) < 0)
X {
X perror("cake system error, reopen stdout");
X _exit(127);
X }
X
X if (fd != 1)
X {
X fprintf(stderr, "cake system error: reopen stdout gives fd %d\n", fd);
X _exit(127);
X }
X }
X
X switch (type)
X {
X
X when Script: if (shell_opt[1] != NULL)
X execl(shell_path[1], shell_cmd[1], shell_opt[1], script_filename, 0);
X else
X execl(shell_path[1], shell_cmd[1], script_filename, 0);
X
X perror("cake system error, shell script exec");
X _exit(127);
X
X when System: if (shell_opt[0] != NULL)
X execl(shell_path[0], shell_cmd[0], shell_opt[0], cmd, 0);
X else
X execl(shell_path[0], shell_cmd[0], cmd, 0);
X
X perror("cake system error, shell exec");
X _exit(127);
X
X when Exec: execvp(argv[0], argv);
X sprintf(scratchbuf, "cake system error, %s exec", argv[0]);
X perror(scratchbuf);
X _exit(127);
X
X otherwise: fprintf(stderr, "cake internal error: type = %x in cake_proc\n", type);
X _exit(127);
X }
X }
X or (pid == -1)
X {
X perror("cake system error, fork");
X exit_cake(FALSE);
X }
X
X proc = make(Proc);
X proc->pr_pid = pid;
X proc->pr_node = node;
X proc->pr_func = func;
X proc->pr_args = args;
X proc->pr_run = TRUE;
X addtail(active_procs, proc);
X cdebug("cake_proc pid = %d\n", pid);
X
X mutex_unlock();
X if (type == Script && ! cakedebug)
X {
X cdebug("cake_proc unlink script_filename %s\n", script_filename);
X if (unlink(script_filename) != 0)
X {
X sprintf(scratchbuf, "cake system error, unlink %s", script_filename);
X perror(scratchbuf);
X exit_cake(FALSE);
X }
X }
X
X return pid;
X}
X
X/*
X** The cake code allows many child processes to be running
X** at any given time. Cake waits for these processes
X** when their results are required. However, there is no
X** necessary relationship between between when a process exits
X** and when its product is needed.
X**
X** The list of active processes contains all running processes
X** and all exited processes that haven't been waited for yet.
X** Cake_wait maintains this list. Whenever cake_wait gets
X** an exit report from wait, it invokes the function (if any)
X** associated with the exited process. If the exited process
X** is the one cake_wait is looking for, it returns; otherwise
X** it calls wait again and again until it finds that process.
X*/
X
XWait
Xcake_wait(pid)
Xreg int pid;
X{
X extern List *find_process();
X reg int exitpid;
X reg List *ptr;
X reg Proc *proc;
X Wait status;
X
X mutex_lock();
X ptr = find_process(pid);
X proc = (Proc *) ldata(ptr);
X if (! proc->pr_run)
X {
X status = proc->pr_stat;
X delete(active_procs, ptr);
X mutex_unlock();
X return status;
X }
X
X while ((exitpid = wait(&status)) != -1)
X {
X mutex_lock();
X ptr = find_process(exitpid);
X proc = (Proc *) ldata(ptr);
X proc->pr_run = FALSE;
X proc->pr_stat = status;
X cdebug("cake_wait pid = %d, status = %d\n", exitpid, status.w_status);
X fflush(stdout);
X
X if (proc->pr_func != NULL)
X {
X cdebug("cake_wait calling function at %x\n", proc->pr_func);
X fflush(stdout);
X (*proc->pr_func)(status, proc->pr_args);
X }
X
X if (exitpid == pid)
X {
X delete(active_procs, ptr);
X mutex_unlock();
X return status; /* normal return */
X }
X
X mutex_unlock();
X }
X
X fprintf(stderr, "cake internal error: waiting for nonactive process %s\n", pid);
X exit_cake(TRUE);
X return status; /* to shut up lint */
X}
X
X/*
X** Find an active process in the active process list.
X*/
X
XList *
Xfind_process(pid)
Xreg int pid;
X{
X reg List *ptr;
X reg Proc *proc;
X reg bool found;
X
X found = FALSE;
X for_list (ptr, active_procs)
X {
X proc = (Proc *) ldata(ptr);
X if (proc->pr_pid == pid)
X {
X found = TRUE;
X break;
X }
X }
X
X if (! found)
X {
X fprintf(stderr, "cake internal error: cannot find active process %d\n", pid);
X exit_cake(TRUE);
X }
X
X return ptr;
X}
X
X/*
X** Open a filter the cake way, with an execv instead of an execl.
X*/
X
X#define READSIDE 0
X#define WRITESIDE 1
X
Xstatic int popen_pid[NOFILE];
X
XFILE *
Xcake_popen(argv, mode)
Xreg char *argv[MAXARGS];
Xreg char *mode;
X{
X int pdesc[2];
X reg int parent_end, child_end;
X reg int replaced, pid;
X reg Proc *proc;
X
X if (pipe(pdesc) < 0)
X return NULL;
X
X if (mode[0] == 'r')
X {
X parent_end = pdesc[READSIDE];
X child_end = pdesc[WRITESIDE];
X replaced = 1;
X }
X else
X {
X parent_end = pdesc[WRITESIDE];
X child_end = pdesc[READSIDE];
X replaced = 0;
X }
X
X fflush(stdout);
X mutex_lock();
X if ((pid = vfork()) == 0)
X {
X close(parent_end);
X close(replaced);
X if (dup(child_end) != replaced)
X _exit(127);
X
X close(child_end);
X execv(argv[0], argv);
X sprintf(scratchbuf, "cake system error, %s exec", argv[0]);
X perror(scratchbuf);
X _exit(127);
X }
X or (pid == -1)
X {
X close(parent_end);
X close(child_end);
X perror("cake system error, fork");
X exit_cake(FALSE);
X }
X
X proc = make(Proc);
X proc->pr_pid = pid;
X proc->pr_func = NULL;
X proc->pr_args = NULL;
X proc->pr_run = TRUE;
X addtail(active_procs, proc);
X
X close(child_end);
X popen_pid[parent_end] = pid;
X mutex_unlock();
X
X return fdopen(parent_end, mode);
X}
X
Xint
Xcake_pclose(fp)
Xreg FILE *fp;
X{
X Wait code;
X reg int f;
X
X f = fileno(fp);
X fclose(fp);
X code = cake_wait(popen_pid[f]);
X return code.w_status;
X}
X
X/*
X** Parse the given command into argv, argc.
X*/
X
Xint
Xparse_args(cmd, vector)
Xreg char *cmd;
Xreg char **vector;
X{
X char buf[MAXARGSIZE];
X reg int i, count;
X reg char *s;
X reg bool instring;
X
X cdebug("parse_args: ");
X
X s = cmd;
X for (count = 0; *s != '\0'; count++)
X {
X while (*s != '\0' && isspace(*s))
X s++;
X
X instring = FALSE;
X for (i = 0; *s != '\0' && (! isspace(*s) || instring); s++)
X {
X if (*s == '\\')
X {
X if (s[1] != '\0')
X buf[i++] = *++s;
X }
X or (*s == '"')
X instring = ! instring;
X else
X buf[i++] = *s;
X }
X
X buf[i] = '\0';
X if (i >= MAXARGSIZE)
X {
X fprintf(stderr, "cake: argument '%s' too long\n", buf);
X exit_cake(FALSE);
X }
X
X if (count >= MAXARGS)
X {
X fprintf(stderr, "cake: '%s' has too many arguments\n", cmd);
X exit_cake(FALSE);
X }
X
X if (i == 0)
X count--;
X else
X {
X cdebug("<%s>", buf);
X vector[count] = new_name(buf);
X }
X }
X
X vector[count] = NULL;
X cdebug("/%d\n", count);
X return count;
X}
X
X/*
X** Strip one level of backslashes from the given string.
X*/
X
Xchar *
Xstrip_backslash(str)
Xreg char *str;
X{
X char buf[MAXSIZE];
X reg char *s;
X reg int i;
X
X if (index(str, '\\') == NULL)
X return str;
X
X for (i = 0, s = str; *s != '\0'; s++)
X {
X if (*s != '\\')
X buf[i++] = *s;
X or (s[1] != '\0')
X buf[i++] = *++s;
X }
X
X buf[i] = '\0';
X if (i >= MAXSIZE)
X {
X fprintf(stderr, "cake: command '%s' too long.\n", str);
X exit_cake(FALSE);
X }
X
X return new_name(buf);
X}
X
X/*
X** These functions implement mutual exclusion.
X** They prevent cake from being interrupted
X** between calls to lock and unlock.
X** This is used to preserve the consistency
X** of the active_procs data structure in the presence
X** of multiple executing children.
X*/
X
X#if 0 /* mutual exclusion is not necessary & has bug */
X#ifdef ATT
Xint (*signalint)();
Xint (*signalquit)();
X#else
Xint signalmask;
X#endif
X#endif
X
Xmutex_lock()
X{
X#if 0
X#ifdef ATT
X signalint = signal(SIGINT, SIG_IGN);
X signalquit = signal(SIGQUIT, SIG_IGN);
X#else
X signalmask = sigblock(mask(SIGINT)|mask(SIGQUIT));
X#endif
X#endif
X}
X
Xmutex_unlock()
X{
X#if 0
X#ifdef ATT
X signal(SIGINT, signalint);
X signal(SIGQUIT, signalquit);
X#else
X sigsetmask(signalmask);
X#endif
X#endif
X}
SHAR_EOF
if test 8939 -ne "`wc -c < 'proc.c'`"
then
echo shar: "error transmitting 'proc.c'" '(should have been 8939 characters)'
fi
fi
echo shar: "extracting 'sym.c'" '(4553 characters)'
if test -f 'sym.c'
then
echo shar: "will not over-write existing file 'sym.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sym.c'
X/*
X** Symbol table module
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/sym.c,v 1.15 87/10/05 20:16:08 zs Exp $";
X
X#include "cake.h"
X
Xtypedef struct s_out
X{
X char *o_cmd;
X char *o_out;
X} Out;
X
Xtypedef struct s_stat
X{
X char *s_cmd;
X int s_stat;
X} Stat;
X
Xextern int hash();
Xextern Cast name_key(); extern bool name_equal();
Xextern Cast node_key(); extern bool node_equal();
Xextern Cast out_key(); extern bool out_equal();
Xextern Cast stat_key(); extern bool stat_equal();
XTable name_tab = { SIZE, NULL, name_key, hash, name_equal };
XTable node_tab = { SIZE, NULL, node_key, hash, node_equal };
XTable out_tab = { SIZE, NULL, out_key, hash, out_equal };
XTable stat_tab = { SIZE, NULL, stat_key, hash, stat_equal };
X
X/*
X** Initialize the name and the command tables.
X*/
X
Xinit_sym()
X{
X init_table(name_tab);
X init_table(node_tab);
X init_table(out_tab);
X init_table(stat_tab);
X}
X
X/**********************************************************************/
X/* Name table */
X
X/*
X** Save the given string in the name table if not already there;
X** return its new address. This address is unique, so comparing
X** two identifiers for equality can be done by comparing their
X** addresses.
X*/
X
Xchar *
Xnew_name(str)
Xreg char *str;
X{
X reg Cast old;
X reg char *copy;
X
X if ((old = lookup_table(name_tab, str)) != NULL)
X return (char *) old;
X
X copy = (char *) newmem(strlen(str) + 1);
X strcpy(copy, str);
X insert_table(name_tab, copy);
X
X return copy;
X}
X
XCast
Xname_key(entry)
Xreg Cast entry;
X{
X return entry;
X}
X
Xbool
Xname_equal(key1, key2)
Xreg Cast key1, key2;
X{
X return streq((char *) key1, (char *) key2);
X}
X
X/**********************************************************************/
X/* Node table */
X
X/*
X** Th insertion function for the node table is chase(),
X** which is in chase.c with the rest of the chase stuff.
X*/
X
X/*
X** This function merely reports on the results
X** of past calls to chase.
X*/
X
XNode *
Xchase_node(name)
Xreg char *name;
X{
X return (Node *) lookup_table(node_tab, name);
X}
X
X/*
X** Return a list of all the nodes of this run.
X*/
X
XList *
Xget_allnodes()
X{
X return contents_table(node_tab);
X}
X
XCast
Xnode_key(entry)
Xreg Cast entry;
X{
X return (Cast) ((Node *) entry)->n_name;
X}
X
Xbool
Xnode_equal(key1, key2)
Xreg Cast key1, key2;
X{
X#ifdef EXTRACHECK
X if (key1 != key2 && streq((char *) key1, (char *) key2))
X {
X fprintf(stderr, "cake internal error: inconsistency in node_equal\n");
X exit_cake(TRUE);
X }
X#endif
X
X return key1 == key2;
X}
X
X/**********************************************************************/
X/* Command output table */
X
X#ifdef STATS_FILE
Xint out_tried = 0;
Xint out_found = 0;
X#endif
X
X/*
X** Save a command and its output.
X*/
X
Xnew_out(cmd, output)
Xreg char *cmd;
Xreg char *output;
X{
X reg Out *out;
X
X out = make(Out);
X out->o_cmd = cmd;
X out->o_out = output;
X insert_table(out_tab, out);
X}
X
X/*
X** Return the output if any associated with a given command.
X*/
X
Xchar *
Xget_out(cmd)
Xreg char *cmd;
X{
X reg Out *out;
X
X#ifdef STATS_FILE
X out_tried++;
X#endif
X
X if ((out = (Out *) lookup_table(out_tab, cmd)) == NULL)
X return NULL;
X
X#ifdef STATS_FILE
X out_found++;
X#endif
X
X return out->o_out;
X}
X
XCast
Xout_key(entry)
Xreg Cast entry;
X{
X return (Cast) ((Out *) entry)->o_cmd;
X}
X
Xbool
Xout_equal(key1, key2)
Xreg Cast key1, key2;
X{
X#ifdef EXTRACHECK
X if (key1 != key2 && streq((char *) key1, (char *) key2))
X {
X fprintf(stderr, "cake internal error: inconsistency in out_equal\n");
X exit_cake(TRUE);
X }
X#endif
X
X return key1 == key2;
X}
X
X/**********************************************************************/
X/* Command status table */
X
X#ifdef STATS_FILE
Xint stat_tried = 0;
Xint stat_found = 0;
X#endif
X
X/*
X** Save a command and its status.
X*/
X
Xnew_stat(cmd, status)
Xreg char *cmd;
Xreg int status;
X{
X reg Stat *stat;
X
X stat = make(Stat);
X stat->s_cmd = cmd;
X stat->s_stat = status;
X insert_table(stat_tab, stat);
X}
X
X/*
X** Return the status if any associated with a given command.
X*/
X
Xbool
Xget_stat(cmd, code)
Xreg char *cmd;
Xreg int *code;
X{
X reg Stat *stat;
X
X#ifdef STATS_FILE
X stat_tried++;
X#endif
X
X if ((stat = (Stat *) lookup_table(stat_tab, cmd)) == NULL)
X return FALSE;
X
X#ifdef STATS_FILE
X stat_found++;
X#endif
X
X *code = stat->s_stat;
X return TRUE;
X}
X
XCast
Xstat_key(entry)
Xreg Cast entry;
X{
X return (Cast) ((Stat *) entry)->s_cmd;
X}
X
Xbool
Xstat_equal(key1, key2)
Xreg Cast key1, key2;
X{
X#ifdef EXTRACHECK
X if (key1 != key2 && streq((char *) key1, (char *) key2))
X {
X fprintf(stderr, "cake internal error: inconsistency in stat_equal\n");
X exit_cake(TRUE);
X }
X#endif
X
X return key1 == key2;
X}
SHAR_EOF
if test 4553 -ne "`wc -c < 'sym.c'`"
then
echo shar: "error transmitting 'sym.c'" '(should have been 4553 characters)'
fi
fi
echo shar: "extracting 'sys.c'" '(2672 characters)'
if test -f 'sys.c'
then
echo shar: "will not over-write existing file 'sys.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sys.c'
X/*
X** Cake system interface.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/sys.c,v 1.15 87/10/05 20:16:20 zs Exp $";
X
X#include "cake.h"
X
X/*
X** Change to the directory of the cakefile.
X** Return the shortened name of the cakefile.
X*/
X
Xchar *
Xdir_setup(name)
Xreg char *name;
X{
X char buf[MAXSIZE];
X reg int i, l; /* l is the subscript of the last / */
X
X l = -1;
X for (i = 0; name[i] != '\0'; i++)
X if (name[i] == '/')
X l = i;
X
X if (l < 0)
X return name;
X else
X {
X for (i = 0; i < l; i++)
X buf[i] = name[i];
X
X if (i == 0)
X buf[i++] = '/';
X
X buf[i] = '\0';
X if (chdir(buf) != 0)
X {
X sprintf(scratchbuf, "cake system error, chdir %s", buf);
X perror(scratchbuf);
X exit(1);
X }
X
X return name+l+1;
X }
X}
X
X/*
X** Set up the arguments to shell exec's.
X** pattern: execl("/bin/sh", "sh", "-c", s, 0);
X** pattern: execl("/bin/csh", "csh", "-cf", s, 0);
X*/
X
Xchar *shell_path[2];
Xchar *shell_cmd[2];
Xchar *shell_opt[2];
X
Xshell_setup(shell, which)
Xreg char *shell;
Xreg int which;
X{
X char buf[MAXSIZE];
X reg char *s;
X reg int i, l;
X
X /* find the shell path */
X i = 0;
X for (s = shell; *s != '\0' && *s != ' ' && *s != '\t'; s++)
X buf[i++] = *s;
X
X buf[i] = '\0';
X shell_path[which] = new_name(buf);
X
X for (; *s != '\0' && *s != '-'; s++)
X ;
X
X /* find the options */
X i = 0;
X for (; *s != '\0' && *s != ' ' && *s != '\t'; s++)
X buf[i++] = *s;
X
X buf[i] = '\0';
X if (i != 0)
X shell_opt[which] = new_name(buf);
X else
X shell_opt[which] = NULL;
X
X if (*s != NULL)
X {
X fprintf(stderr, "cake: cannot parse shell command '%s'\n", shell);
X exit_cake(FALSE);
X }
X
X /* find the shell command itself */
X s = shell_path[which];
X l = -1;
X for (i = 0; s[i] != '\0'; i++)
X if (s[i] == '/')
X l = i;
X
X shell_cmd[which] = new_name(s+l+1);
X
X if (cakedebug)
X {
X printf("shell path%d: %s\n", which, shell_path[which]);
X printf("shell cmd%d: %s\n", which, shell_cmd[which]);
X if (shell_opt[which] != NULL)
X printf("shell opt%d: %s\n", which, shell_opt[which]);
X else
X printf("shell opt%d: NULL\n", which);
X }
X}
X
Xbool metatab[CHARSETSIZE];
X
X/*
X** Set up the metacharacter table.
X*/
X
Xmeta_setup(metachars)
Xreg char *metachars;
X{
X reg int i;
X reg char *s;
X
X for (i = 0; i < CHARSETSIZE; i++)
X metatab[i] = FALSE;
X
X for (s = metachars; *s != '\0'; s++)
X metatab[*s] = TRUE;
X}
X
X/*
X** Decide whether the given string has metacharacters or not.
X** The second arg tells whether to honour backslash escapes.
X*/
X
Xbool
Xhas_meta(str, allow_esc)
Xreg char *str;
Xreg bool allow_esc;
X{
X reg char *s;
X
X for (s = str; *s != '\0'; s++)
X if (metatab[(unsigned) *s])
X return TRUE;
X or (allow_esc && (*s == '\\'))
X {
X if (s[1] != '\0')
X s++;
X }
X
X return FALSE;
X}
SHAR_EOF
if test 2672 -ne "`wc -c < 'sys.c'`"
then
echo shar: "error transmitting 'sys.c'" '(should have been 2672 characters)'
fi
fi
echo shar: "extracting 'table.c'" '(2269 characters)'
if test -f 'table.c'
then
echo shar: "will not over-write existing file 'table.c'"
else
sed 's/^X//' << \SHAR_EOF > 'table.c'
X/*
X** Table handling module.
X**
X** This file supplies data manipulation routines to other modules;
X** it does not store any data itself. Its routines are generic,
X** applicable to the storage of any kind of data structure with
X** primary key and a hash function on it.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/table.c,v 1.15 87/10/05 20:16:28 zs Exp $";
X
X#include "cake.h"
X
X/*
X** Initialize a table.
X*/
X
X_init_table(table)
Xreg Table *table;
X{
X reg int i;
X
X table->ta_store = make_many(List *, table->ta_size);
X for (i = 0; i < table->ta_size; i++)
X table->ta_store[i] = NULL;
X}
X
X/*
X** Look up and return the entry corresponding to the key
X** in a table.
X*/
X
XCast
X_lookup_table(table, key)
Xreg Table *table;
Xreg Cast key;
X{
X reg List *ptr;
X reg int h;
X
X put_trail("lookup_table", "start");
X h = tablehash(table)(key);
X for_list (ptr, table->ta_store[h])
X {
X if (tableequal(table)(key, tablekey(table)(ldata(ptr))))
X {
X#ifdef EXTRACHECK
X if (ldata(ptr) == NULL)
X {
X fprintf(stderr, "cake internal error: returning null in lookup_table\n");
X exit_cake(TRUE);
X }
X#endif
X put_trail("lookup_table", "finish");
X return ldata(ptr);
X }
X }
X
X put_trail("lookup_table", "finish");
X return NULL;
X}
X
X/*
X** Insert a new entry into the table.
X** Return whether it was there before.
X*/
X
Xbool
X_insert_table(table, entry)
Xreg Table *table;
Xreg Cast entry;
X{
X reg List *ptr;
X reg Cast key;
X reg int h;
X
X put_trail("insert_table", "start");
X key = tablekey(table)(entry);
X h = tablehash(table)(key);
X for_list (ptr, table->ta_store[h])
X if (tableequal(table)(key, tablekey(table)(ldata(ptr))))
X {
X put_trail("insert_table", "finish");
X return TRUE;
X }
X
X table->ta_store[h] = addhead(table->ta_store[h], entry);
X put_trail("insert_table", "finish");
X return FALSE;
X}
X
X/*
X** Hash str into the range 0 to SIZE-1.
X*/
X
Xint
Xhash(s)
Xreg char *s;
X{
X reg int h;
X
X for (h = 0; *s != '\0'; s++)
X h = (h << 1) + *s;
X
X return (h >= 0? h: -h) % SIZE;
X}
X
X/*
X** Return a list of all the entries in a table.
X*/
X
XList *
X_contents_table(table)
Xreg Table *table;
X{
X reg List *all;
X reg List *ptr;
X reg int i;
X
X all = makelist0();
X for (i = 0; i < table->ta_size; i++)
X for_list (ptr, table->ta_store[i])
X addtail(all, ldata(ptr)); /* na */
X
X return all;
X}
SHAR_EOF
if test 2269 -ne "`wc -c < 'table.c'`"
then
echo shar: "error transmitting 'table.c'" '(should have been 2269 characters)'
fi
fi
echo shar: "extracting 'test.c'" '(3874 characters)'
if test -f 'test.c'
then
echo shar: "will not over-write existing file 'test.c'"
else
sed 's/^X//' << \SHAR_EOF > 'test.c'
X/*
X** Module to handle Cake's tests.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/test.c,v 1.15 87/10/05 20:16:34 zs Exp $";
X
X#include "cake.h"
X
Xbool
Xeval(node, test, env)
Xreg Node *node;
Xreg Test *test;
XEnv env;
X{
X extern char *expand_cmds();
X extern int cake_proc();
X extern Wait cake_wait();
X extern Node *chase();
X extern bool get_stat();
X extern bool exist();
X extern char *ground();
X char buf[256];
X Wait status;
X reg List *ptr;
X reg Pat *pat;
X reg char *text1, *text2;
X reg char *cmd;
X reg Node *chasenode;
X reg int result;
X reg int pid;
X
X if (test == (Test *) NULL)
X return TRUE;
X
X#ifdef CAKEDEBUG
X if (cakedebug)
X {
X printf("testing ");
X print_test(test);
X printf("\n");
X }
X#endif
X
X switch (test->t_kind)
X {
X
Xwhen t_TRUE: return TRUE;
Xwhen t_FALSE: return FALSE;
Xwhen t_AND: return eval(node, test->t_left, env) && eval(node, test->t_right, env);
Xwhen t_OR: return eval(node, test->t_left, env) || eval(node, test->t_right, env);
Xwhen t_NOT: return ! eval(node, test->t_left, env);
X
Xwhen t_CMD: if (get_stat(test->t_cmd, &status.w_status))
X {
X test->t_kind = (status.w_status == 0)? t_TRUE: t_FALSE;
X cdebug("test cmd cache %s: %s\n", test->t_cmd,
X (status.w_status == 0)? "True": "False");
X return (status.w_status == 0)? TRUE: FALSE;
X }
X
X cmd = expand_cmds(ground(env, test->t_cmd));
X pid = cake_proc(cmd, Exec, "/dev/null", (Node *) NULL,
X (int (*)()) NULL, (List *) NULL);
X status = cake_wait(pid);
X new_stat(test->t_cmd, status.w_status);
X test->t_kind = (status.w_status == 0)? t_TRUE: t_FALSE;
X cdebug("test cmd %s: %s\n", test->t_cmd,
X (status.w_status == 0)? "True": "False");
X return (status.w_status == 0)? TRUE: FALSE;
X
Xwhen t_MATCH: text1 = (char *) first(test->t_list); /* -vX */
X text2 = (char *) last(test->t_list); /* file */
X /* e.g. sub -vX X.c NULL file.c */
X sprintf(buf, "sub %s %s NULL %s > /dev/null",
X text1, text2, test->t_pat->p_str);
X
X cmd = new_name(buf);
X cdebug("matching command: %s\n", cmd);
X if (get_stat(cmd, &status.w_status))
X {
X test->t_kind = (status.w_status == 0)? t_TRUE: t_FALSE;
X cdebug("test cmd cache %s: %s\n", test->t_cmd,
X (status.w_status == 0)? "True": "False");
X return (status.w_status == 0)? TRUE: FALSE;
X }
X
X pid = cake_proc(cmd, Exec, "/dev/null", (Node *) NULL,
X (int (*)()) NULL, (List *) NULL);
X status = cake_wait(pid);
X new_stat(test->t_cmd, status.w_status);
X test->t_kind = (status.w_status == 0)? t_TRUE: t_FALSE;
X cdebug("test cmd %s: %s\n", test->t_cmd,
X (status.w_status == 0)? "True": "False");
X return (status.w_status == 0)? TRUE: FALSE;
X
Xwhen t_LIST: for_list (ptr, test->t_list)
X {
X pat = (Pat *) ldata(ptr);
X if (streq(test->t_pat->p_str, pat->p_str))
X return TRUE;
X }
X
X return FALSE;
X
Xwhen t_EXIST: result = exist(test->t_pat->p_str);
X cdebug("test exist %s: %s\n", test->t_pat->p_str,
X result? "True": "False");
X return result;
X
Xwhen t_CANDO: chasenode = chase(test->t_pat->p_str, 0, (Entry *) NULL);
X if (on_node(chasenode, nf_ERR))
X {
X sprintf(scratchbuf, "cannot evaluate 'cando %s' test for %s",
X chasenode->n_name, node->n_name);
X add_error(node, new_name(scratchbuf), LNULL, TRUE);
X }
X
X result = is_ok(chasenode) || is_cando(chasenode);
X cdebug("test cando %s: %s\n", test->t_pat->p_str,
X result? "True": "False");
X return result;
X
Xwhen t_OK: chasenode = chase(test->t_pat->p_str, 0, (Entry *) NULL);
X if (on_node(chasenode, nf_ERR))
X {
X sprintf(scratchbuf, "cannot evaluate 'ok %s' test for %s",
X chasenode->n_name, node->n_name);
X add_error(node, new_name(scratchbuf), LNULL, TRUE);
X }
X
X result = is_ok(chasenode);
X cdebug("test ok %s: %s\n", test->t_pat->p_str,
X result? "True": "False");
X return result;
X
Xotherwise: fprintf(stderr, "cake internal error: invalid test type %x in eval\n",
X test->t_kind);
X exit_cake(TRUE);
X }
X
X /*NOTREACHED*/
X return FALSE;
X}
SHAR_EOF
if test 3874 -ne "`wc -c < 'test.c'`"
then
echo shar: "error transmitting 'test.c'" '(should have been 3874 characters)'
fi
fi
echo shar: "extracting 'trail.c'" '(1045 characters)'
if test -f 'trail.c'
then
echo shar: "will not over-write existing file 'trail.c'"
else
sed 's/^X//' << \SHAR_EOF > 'trail.c'
X/*
X** Trail module.
X*/
X
Xstatic char
Xrcs_id[] = "$Header: /mip/zs/src/sys/cake/RCS/trail.c,v 1.14 86/07/19 12:24:19 zs Exp $";
X
X#ifdef CAKEDEBUG
X#include "cake.h"
X
X#define TRAILSIZE 100
X
Xstatic int trail_entries = 0; /* no. of calls to put_tr */
Xstatic int slot = 0; /* next slot */
Xstatic char *trailfunc[TRAILSIZE]; /* functions and events */
Xstatic char *trailevent[TRAILSIZE]; /* init to NULL by C */
X
X/*
X** Register this function on the trail.
X*/
X
Xput_trail(func, event)
Xreg char *func;
Xreg char *event;
X{
X trailfunc[slot] = func;
X trailevent[slot] = event;
X
X trail_entries++;
X if (++slot == TRAILSIZE)
X slot = 0;
X}
X
X/*
X** Print the trail on the given file.
X*/
X
Xget_trail(fp)
Xreg FILE *fp;
X{
X reg int maxent;
X
X fprintf(fp, "^^^^^^^^^^^^^^^^^^^ TRAIL ^^^^^^^^^^^^^^^^^^^\n");
X fprintf(fp, "%d entries\n", trail_entries);
X
X maxent = (trail_entries < TRAILSIZE)? trail_entries: TRAILSIZE;
X while (maxent-- > 0)
X {
X if (--slot == -1)
X slot = TRAILSIZE - 1;
X
X fprintf(fp, "%s %s\n", trailfunc[slot], trailevent[slot]);
X }
X}
X#endif
SHAR_EOF
if test 1045 -ne "`wc -c < 'trail.c'`"
then
echo shar: "error transmitting 'trail.c'" '(should have been 1045 characters)'
fi
fi
exit 0
# End of shell archive