Newsgroups: comp.sources.unix From: pmiller@bmr.gov.au (Peter Miller) Subject: v28i104: cook - a file construction tool, V1.6, Part11/19 References: <1.775008949.28543@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: pmiller@bmr.gov.au (Peter Miller) Posting-Number: Volume 28, Issue 104 Archive-Name: cook-1.6/part11 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'c_incl/sniff.c' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1991, 1992, 1993, 1994 Peter Miller. X * All rights reserved. X * X * This program is free software; you can redistribute it and/or modify X * it under the terms of the GNU General Public License as published by X * the Free Software Foundation; either version 2 of the License, or X * (at your option) any later version. X * X * This program is distributed in the hope that it will be useful, X * but WITHOUT ANY WARRANTY; without even the implied warranty of X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X * GNU General Public License for more details. X * X * You should have received a copy of the GNU General Public License X * along with this program; if not, write to the Free Software X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X * X * MANIFEST: functions to scan source files looking for include files X */ X X#include X#include X#include X#include X X#include X#include X#include X#include X#include X#include X#include X Xoption_ty option; Xchar *prefix; Xchar *suffix; Xstatic long pcount; Xstatic wlist srl; Xstatic wlist visited; Xstatic sniff_ty *lang; X X Xvoid Xsniff_language(lp) X sniff_ty *lp; X{ X trace(("sniff_language(lp = %08lX)\n{\n"/*}*/, lp)); X assert(lp); X lang = lp; X trace((/*{*/"}\n")); X} X X X/* X * NAME X * sniff_include X * X * SYNOPSIS X * void sniff_include(string_ty *path); X * X * DESCRIPTION X * The sniff_include function is used to add to X * the standard include paths. X * X * ARGUMENTS X * path - path to add X */ X Xvoid Xsniff_include(path) X char *path; X{ X string_ty *s; X X assert(path); X trace(("sniff_include(path = \"%s\")\n{\n"/*}*/, path)); X s = str_from_c(path); X wl_append_unique(&srl, s); X str_free(s); X trace((/*{*/"}\n")); X} X X Xlong Xsniff_include_count() X{ X trace(("sniff_include_count()\n{\n"/*}*/)); X trace(("return %ld;\n", (long)srl.wl_nwords)); X trace((/*{*/"}\n")); X return srl.wl_nwords; X} X X X/* X * NAME X * sniff_prepare X * X * SYNOPSIS X * void sniff_prepare(void); X * X * DESCRIPTION X * The sniff_prepare function is used to append the standard X * search paths after the user-supplied search paths. X */ X Xvoid Xsniff_prepare() X{ X trace(("sniff_prepare()\n{\n"/*}*/)); X assert(lang); X lang->prepare(); X trace((/*{*/"}\n")); X} X X X/* X * NAME X * flatten - remove kinks from paths X * X * SYNOPSIS X * void flatten(char *); X * X * DESCRIPTION X * The flatten function is used to remove kinks X * from unix path named. X * X * ARGUMENTS X * path - path to flatten X * X * CAVEATS X * It doesn't understand symbolic links. X */ X Xstatic string_ty *flatten _((string_ty *)); X Xstatic string_ty * Xflatten(s) X string_ty *s; X{ X static size_t tmplen; X static char *tmp; X char *in; X char *out; X X /* X * make sure tmp area is big enough X */ X trace(("flatten(s = \"%s\")\n{\n"/*}*/, s->str_text)); X if (s->str_length > tmplen) X { X tmplen = s->str_length; X tmp = mem_change_size(tmp, tmplen + 1); X } X X /* X * remove multiple '/'s X */ X out = tmp; X for (in = s->str_text; *in; ++in) X { X if (in[0] != '/' || in[1] != '/') X *out++ = *in; X } X *out = 0; X X /* X * remove . references X */ X in = out = tmp; X if (*in == '/') X *out++ = *in++; X while (*in) X { X if (in[0] == '.' && !in[1]) X break; X if (in[0] == '.' && in[1] == '/') X { X in += 2; X continue; X } X while (*in && (*out++ = *in++) != '/') X ; X } X *out = 0; X X /* X * remove .. references X */ X in = out = tmp; X if (*in == '/') X *out++ = *in++; X while (*in) X { X if (in[0] == '.' && in[1] == '.' && !in[2]) X { X if (out == tmp) X { X /* ".." -> ".." */ X *out++ = *in++; X *out++ = *in++; X break; X } X if (out == tmp + 1) X { X /* "/.." -> "/" */ X break; X } X --out; X while (out > tmp && out[-1] != '/') X --out; X break; X } X if (in[0] == '.' && in[1] == '.' && in[2] == '/') X { X if (out == tmp) X { X /* "../" -> "../" */ X *out++ = *in++; X *out++ = *in++; X *out++ = *in++; X continue; X } X if (out == tmp + 1) X { X /* "/../" -> "/" */ X in += 3; X continue; X } X in += 3; X --out; X while (out > tmp && out[-1] != '/') X --out; X continue; X } X while (*in && (*out++ = *in++) != '/') X ; X } X *out = 0; X X /* X * remove trailing '/' X */ X if (out > tmp + 1 && out[-1] == '/') X *--out = 0; X X /* X * "" -> "." X */ X if (!*tmp) X { X tmp[0] = '.'; X tmp[1] = 0; X } X X /* X * return a string X */ X trace(("return \"%s\";\n", tmp)); X trace((/*{*/"}\n")); X return str_from_c(tmp); X} X X X/* X * NAME X * resolve X * X * SYNOPSIS X * string_ty *resolve(string_ty *filename, string_ty *extra); X * X * DESCRIPTION X * The resolve function is used to resolve an include X * filename into the path of an existing file. X * X * ARGUMENTS X * filename - name to be resolved X * extra - extra first search element, if not NULL X * X * RETURNS X * string_ty *; name of path, or NULL if unmentionable X */ X Xstatic string_ty *resolve _((string_ty *filename, string_ty *extra)); X Xstatic string_ty * Xresolve(filename, extra) X string_ty *filename; X string_ty *extra; X{ X string_ty *s; X size_t j; X string_ty *result; X X /* X * If the name is absolute, irrespecitive of X * which style, we need look no further. X */ X trace(("resolve(filename = \"%s\", extra = \"%s\")\n{\n"/*}*/, X filename->str_text, extra ? extra->str_text : "NULL")); X if (filename->str_text[0] == '/') X { X result = flatten(filename); X if (os_exists(result->str_text)) X goto done; X goto dilema; X } X X /* X * Includes of the form "filename" look in the directory X * of the parent file, an then the places. X */ X if (extra) X { X s = str_format("%S/%S", extra, filename); X result = flatten(s); X str_free(s); X if (option.o_verbose) X error("may need to look at \"%s\"", result->str_text); X if (os_exists(result->str_text)) X goto done; X str_free(result); X } X X /* X * look in all the standard places X */ X for (j = 0; j < srl.wl_nwords; ++j) X { X s = str_format("%S/%S", srl.wl_word[j], filename); X result = flatten(s); X str_free(s); X if (option.o_verbose) X error("may need to look at \"%s\"", result->str_text); X if (os_exists(result->str_text)) X goto done; X str_free(result); X } X X /* X * not found, must have been ifdef'ed out X * or needs to be built X */ X dilema: X switch (extra ? option.o_absent_local : option.o_absent_system) X { X default: X fatal("include file \"%s\" not found", filename->str_text); X X case absent_ignore: X result = 0; X break; X X case absent_mention: X if (filename->str_text[0] == '/') X result = str_copy(filename); X else if (extra) X { X s = str_format("%S/%S", extra, filename); X result = flatten(s); X str_free(s); X } X else if (srl.wl_nwords) X { X s = str_format("%S/%S", srl.wl_word[0], filename); X result = flatten(s); X str_free(s); X } X else X result = flatten(filename); X break; X } X X /* X * here for all exits X */ X done: X trace(("return \"%s\";\n", result ? result->str_text : "NULL")); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * stat_equal - compare stat structures X * X * SYNOPSIS X * vint stat_equal(struct stat *, struct stat *); X * X * DESCRIPTION X * The stat_equal function is sued to compare two stat structures X * for equality. Only this fields which would change if X * the file changed are examined. X */ X Xstatic int stat_equal _((struct stat *, struct stat *)); X Xstatic int Xstat_equal(st1, st2) X struct stat *st1; X struct stat *st2; X{ X return X ( X st1->st_dev == st2->st_dev X && X st1->st_ino == st2->st_ino X && X st1->st_size == st2->st_size X && X st1->st_mtime == st2->st_mtime X && X st1->st_ctime == st2->st_ctime X ); X} X X X/* X * NAME X * sniffer - search file for include dependencies X * X * SYNOPSIS X * void sniffer(string_ty *pathname); X * X * DESCRIPTION X * The sniffer function is used to walk a file looking X * for any files which it includes, and walking then also. X * The names of any include files encountered are printed onto X * the standard output. X * X * ARGUMENTS X * pathname - pathname to read X * X * CAVEATS X * Uses the cache where possible to speed things up. X */ X Xstatic void sniffer _((string_ty *, int)); X Xstatic void Xsniffer(filename, prnam) X string_ty *filename; X int prnam; X{ X FILE *fp; X cache_ty *cp; X struct stat st; X size_t j; X X trace(("sniffer(filename = \"%s\")\n{\n"/*}*/, filename->str_text)); X if (prnam) X { X if (!pcount && prefix) X printf("%s\n", prefix); X printf("%s\n", filename->str_text); X ++pcount; X } X X /* X * find the file in the cache X * (will be created if not already there) X */ X cp = cache_search(filename); X assert(cp); X if (stat(filename->str_text, &st) < 0) X { X /* X * here for failure to open/find a file X */ X absent: X switch (errno) X { X case ENOENT: X break; X X case ENOTDIR: X case EACCES: X nerror("warning: %s", filename->str_text); X break; X X default: X nfatal("%s", filename->str_text); X } X X /* X * zap the stat info, X * and pretend the file was empty X */ X memset(&cp->st, 0, sizeof(cp->st)); X wl_free(&cp->ingredients); X cache_update_notify(); X if (option.o_verbose) X error("bogus empty \"%s\" file", filename->str_text); X goto done; X } X X /* X * if the stat in the cache is not the same X * as the state just obtained, reread the file. X */ X if (!stat_equal(&st, &cp->st)) X { X wlist type1; X wlist type2; X X cp->st = st; X wl_free(&cp->ingredients); X cache_update_notify(); X if (option.o_verbose) X error("cache miss for \"%s\" file", filename->str_text); X X fp = fopen(filename->str_text, "r"); X if (!fp) X { X /* X * probably "permission denied" error, X * but file could have suddenly vanished X */ X goto absent; X } X wl_zero(&type1); X wl_zero(&type2); X if (lang->scan(fp, &type1, &type2) || fclose(fp)) X nfatal("%s", filename->str_text); X X /* X * type2 names have an implicit first element of the search path X * which is the directory of the including file X */ X if (type2.wl_nwords) X { X string_ty *parent; X char *ep; X char *sp; X X sp = filename->str_text; X ep = strrchr(sp, '/'); X if (ep) X parent = str_n_from_c(sp, ep - sp); X else X parent = str_from_c("."); X for (j = 0; j < type2.wl_nwords; ++j) X { X string_ty *path; X X path = resolve(type2.wl_word[j], parent); X if (path) X { X wl_append_unique(&cp->ingredients, path); X str_free(path); X } X } X str_free(parent); X } X X /* X * type1 names scan the search path X */ X for (j = 0; j < type1.wl_nwords; ++j) X { X string_ty *path; X X path = resolve(type1.wl_word[j], (string_ty *)0); X if (path) X { X wl_append_unique(&cp->ingredients, path); X str_free(path); X } X } X X /* X * let the lists go X */ X wl_free(&type1); X wl_free(&type2); X } X X /* X * work down the ingredients list X * to see if there are more dependencies X */ X wl_append_unique(&visited, filename); X for (j = 0; j < cp->ingredients.wl_nwords; ++j) X { X string_ty *s; X X s = cp->ingredients.wl_word[j]; X if (!wl_member(&visited, s)) X sniffer(s, 1); X } X X /* X * here for all exits X */ X done: X trace((/*{*/"}\n")); X} X X X/* X * NAME X * sniff - search file for include dependencies X * X * SYNOPSIS X * void sniff(char *pathname); X * X * DESCRIPTION X * The sniff function is used to walk a file looking X * for any files which it includes, and walking then also. X * The names of any include files encountered are printed onto X * the standard output. X * X * ARGUMENTS X * pathname - pathname to read X */ X Xvoid Xsniff(filename) X char *filename; X{ X string_ty *s; X X assert(filename); X trace(("sniff(filename = \"%s\")\n{\n"/*}*/, filename)); X if (!os_exists(filename)) X { X switch (option.o_absent_program) X { X case absent_error: X fatal("%s: no such file", filename); X break; X X case absent_mention: X error("warning: %s: no such file", filename); X break; X X default: X break; X } X goto done; X } X s = str_from_c(filename); X sniffer(s, 0); X str_free(s); X if (pcount && suffix) X printf("%s\n", suffix); X done: X trace((/*{*/"}\n")); X} END_OF_FILE if test 11865 -ne `wc -c <'c_incl/sniff.c'`; then echo shar: \"'c_incl/sniff.c'\" unpacked with wrong size! fi # end of 'c_incl/sniff.c' fi if test -f 'common/arglex.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'common/arglex.c'\" else echo shar: Extracting \"'common/arglex.c'\" \(12038 characters\) sed "s/^X//" >'common/arglex.c' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1990, 1991, 1992, 1993, 1994 Peter Miller. X * All rights reserved. X * X * This program is free software; you can redistribute it and/or modify X * it under the terms of the GNU General Public License as published by X * the Free Software Foundation; either version 2 of the License, or X * (at your option) any later version. X * X * This program is distributed in the hope that it will be useful, X * but WITHOUT ANY WARRANTY; without even the implied warranty of X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X * GNU General Public License for more details. X * X * You should have received a copy of the GNU General Public License X * along with this program; if not, write to the Free Software X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X * X * MANIFEST: functions to perform lexical analysis on command line arguments X */ X X#include X#include X#include X#include X X#include X#include X#include X#include X#include X#include X Xstatic arglex_table_ty table[] = X{ X { "-", arglex_token_stdio, }, X { "-Help", arglex_token_help, }, X { "-TRACIng", arglex_token_tracing, }, X { "-VERSion", arglex_token_version, }, X}; X Xstatic int argc; Xstatic char **argv; Xarglex_value_ty arglex_value; Xarglex_token_ty arglex_token; Xchar *progname; Xstatic arglex_table_ty *utable; Xstatic char *partial; X X Xstatic char *basename _((char *)); X Xstatic char * Xbasename(s) X char *s; X{ X char *bp; X char *ep; X X bp = s; X ep = 0; X while (*s) X { X if (s[0] == '/' && s[1] && s[1] != '/') X bp = s + 1; X if (s > bp && s[0] == '/' && s[-1] != '/') X ep = s; X ++s; X } X if (!ep) X ep = s; X *s = 0; X return bp; X} X X X/* X * NAME X * arglex_init X * X * SYNOPSIS X * void arglex_init(int ac, char **av, arglex_table-t *tp); X * X * DESCRIPTION X * The arglex_init function is used to initialize the X * command line processing. X * X * ARGUMENTS X * ac - aergument count, from main X * av - argument values, from main X * tp - pointer to table of options X * X * CAVEAT X * Must be called before the arglex() function. X */ X Xvoid Xarglex_init(ac, av, tp) X int ac; X char **av; X arglex_table_ty *tp; X{ X progname = basename(av[0]); X X argc = ac - 1; X argv = av + 1; X utable = tp; X} X X X/* X * NAME X * arglex_compare X * X * SYNOPSIS X * int arglex_compare(char *formal, char *actual); X * X * DESCRIPTION X * The arglex_compare function is used to compare X * a command line string with a formal spec of the option, X * to see if they compare equal. X * X * The actual is case-insensitive. Uppercase in the formal X * means a mandatory character, while lower case means optional. X * Any number of consecutive optional characters may be supplied X * by actual, but none may be skipped, unless all are skipped to X * the next non-lower-case letter. X * X * The underscore (_) is like a lower-case minus, X * it matches "", "-" and "_". X * X * The "*" in a pattern matches everything to the end of the line, X * anything after the "*" is ignored. The rest of the line is pointed X * to by the "partial" variable as a side-effect (else it will be 0). X * This rather ugly feature is to support "-I./dir" type options. X * X * A backslash in a pattern nominates an exact match required, X * case must matche excatly here. X * This rather ugly feature is to support "-I./dir" type options. X * X * For example: "-project" and "-P' both match "-Project", X * as does "-proJ", but "-prj" does not. X * X * For example: "-devDir" and "-d_d' both match "-Development_Directory", X * but "-dvlpmnt_drctry" does not. X * X * For example: to match include path specifications, use a pattern X * such as "-\\I*", and the partial global variable will have the X * path in it on return. X * X * ARGUMENTS X * formal - the "pattern" for the option X * actual - what the user supplied X * X * RETURNS X * int; zero if no match, X * non-zero if they do match. X */ X Xint Xarglex_compare(formal, actual) X char *formal; X char *actual; X{ X char fc; X char ac; X int result; X X trace(("arglex_compare(formal = \"%s\", actual = \"%s\")\n{\n", X formal, actual)); X for (;;) X { X trace_string(formal); X trace_string(actual); X ac = *actual++; X if (isupper(ac)) X ac = tolower(ac); X fc = *formal++; X switch (fc) X { X case 0: X result = !ac; X goto done; X X case '_': X if (ac == '-') X break; X /* fall through... */ X X case 'a': case 'b': case 'c': case 'd': case 'e': X case 'f': case 'g': case 'h': case 'i': case 'j': X case 'k': case 'l': case 'm': case 'n': case 'o': X case 'p': case 'q': case 'r': case 's': case 't': X case 'u': case 'v': case 'w': case 'x': case 'y': X case 'z': X /* X * optional characters X */ X if (ac == fc && arglex_compare(formal, actual)) X { X result = 1; X goto done; X } X /* X * skip forward to next X * mandatory character, or after '_' X */ X while (islower(*formal)) X ++formal; X if (*formal == '_') X { X ++formal; X if (ac == '_' || ac == '-') X ++actual; X } X --actual; X break; X X case '*': X /* X * This is a hack, it should really X * check for a match match the stuff after X * the '*', too, a la glob. X */ X if (!ac) X { X result = 0; X goto done; X } X partial = actual - 1; X result = 1; X goto done; X X case '\\': X if (actual[-1] != *formal++) X { X result = 0; X goto done; X } X break; X X case 'A': case 'B': case 'C': case 'D': case 'E': X case 'F': case 'G': case 'H': case 'I': case 'J': X case 'K': case 'L': case 'M': case 'N': case 'O': X case 'P': case 'Q': case 'R': case 'S': case 'T': X case 'U': case 'V': case 'W': case 'X': case 'Y': X case 'Z': X fc = tolower(fc); X /* fall through... */ X X default: X /* X * mandatory characters X */ X if (fc != ac) X { X result = 0; X goto done; X } X break; X } X } X done: X trace(("return %d;\n}\n", result)); X return result; X} X X X/* X * NAME X * is_a_number X * X * SYNOPSIS X * int is_a_number(char *s); X * X * DESCRIPTION X * The is_a_number function is used to determine if the X * argument is a number. X * X * The value is placed in arglex_value.alv_number as X * a side effect. X * X * Negative and positive signs are accepted. X * The C conventions for decimal, octal and hexadecimal are understood. X * X * There may be no white space anywhere in the string, X * and the string must end after the last digit. X * Trailing garbage will be interpreted to mean it is not a string. X * X * ARGUMENTS X * s - string to be tested and evaluated X * X * RETURNS X * int; zero if not a number, X * non-zero if is a number. X */ X Xstatic int is_a_number _((char *)); X Xstatic int Xis_a_number(s) X char *s; X{ X long n; X int sign; X X n = 0; X switch (*s) X { X case '-': X ++s; X sign = -1; X break; X X case '+': X ++s; X sign = 1; X break; X X default: X sign = 1; X break; X } X switch (*s) X { X case '0': X if ((s[1] == 'x' || s[1] == 'X') && s[2]) X { X s += 2; X for (;;) X { X switch (*s) X { X case '0': case '1': case '2': case '3': X case '4': case '5': case '6': case '7': X case '8': case '9': X n = n * 16 + *s++ - '0'; X continue; X X case 'A': case 'B': case 'C': X case 'D': case 'E': case 'F': X n = n * 16 + *s++ - 'A' + 10; X continue; X X case 'a': case 'b': case 'c': X case 'd': case 'e': case 'f': X n = n * 16 + *s++ - 'a' + 10; X continue; X } X break; X } X } X else X { X for (;;) X { X switch (*s) X { X case '0': case '1': case '2': case '3': X case '4': case '5': case '6': case '7': X n = n * 8 + *s++ - '0'; X continue; X } X break; X } X } X break; X X case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X for (;;) X { X switch (*s) X { X case '0': case '1': case '2': case '3': X case '4': case '5': case '6': case '7': X case '8': case '9': X n = n * 10 + *s++ - '0'; X continue; X } X break; X } X break; X X default: X return 0; X } X if (*s) X return 0; X arglex_value.alv_number = n * sign; X return 1; X} X X X/* X * NAME X * arglex X * X * SYNOPSIS X * arglex_token_ty arglex(void); X * X * DESCRIPTION X * The arglex function is used to perfom lexical analysis X * on the command line arguments. X * X * Unrecognised options are returned as arglex_token_option X * for anything starting with a '-', or X * arglex_token_string otherwise. X * X * RETURNS X * The next token in the token stream. X * When the end is reached, arglex_token_eoln is returned forever. X * X * CAVEAT X * Must call arglex_init befor this function is called. X */ X Xarglex_token_ty Xarglex() X{ X arglex_table_ty *tp; X int j; X arglex_table_ty *hit[20]; X int nhit; X char *arg; X static char *pushback[3]; X static int pushback_depth; X X trace(("arglex()\n{\n"/*}*/)); X if (pushback_depth) X { X /* X * the second half of a "-foo=bar" style argument. X */ X arg = pushback[--pushback_depth]; X } X else X { X if (argc <= 0) X { X arglex_token = arglex_token_eoln; X arg = ""; X goto done; X } X arg = argv[0]; X argc--; X argv++; X X /* X * See if it looks like a GNU "-foo=bar" option. X * Split it at the '=' to make it something the X * rest of the code understands. X */ X if (arg[0] == '-' && arg[1] != '=') X { X char *eqp; X X eqp = strchr(arg, '='); X if (eqp) X { X pushback[pushback_depth++] = eqp + 1; X *eqp = 0; X } X } X X /* X * Turn the GNU-style leading "--" X * into "-" if necessary. X */ X if X ( X arg[0] == '-' X && X arg[1] == '-' X && X arg[2] X && X !is_a_number(arg + 1) X ) X ++arg; X } X X /* X * see if it is a number X */ X if (is_a_number(arg)) X { X arglex_token = arglex_token_number; X goto done; X } X X /* X * scan the tables to see what it matches X */ X nhit = 0; X partial = 0; X for (tp = table; tp < ENDOF(table); tp++) X { X if (arglex_compare(tp->name, arg)) X hit[nhit++] = tp; X } X if (utable) X { X for (tp = utable; tp->name; tp++) X { X if (arglex_compare(tp->name, arg)) X hit[nhit++] = tp; X } X } X X /* X * deal with unknown or ambiguous options X */ X switch (nhit) X { X case 0: X /* X * not found in the tables X */ X if (*arg == '-') X arglex_token = arglex_token_option; X else X arglex_token = arglex_token_string; X break; X X case 1: X if (partial) X pushback[pushback_depth++] = partial; X arg = hit[0]->name; X arglex_token = hit[0]->token; X break; X X default: X { X string_ty *s1; X string_ty *s2; X X s1 = str_from_c(hit[0]->name); X for (j = 1; j < nhit; ++j) X { X s2 = str_format("%S, %s", s1, hit[j]->name); X str_free(s1); X s1 = s2; X } X fatal X ( X "option \"%s\" ambiguous (%s)", X arg, X s1->str_text X ); X } X } X X /* X * here for all exits X */ X done: X arglex_value.alv_string = arg; X trace(("return %d; /* \"%s\" */\n", arglex_token, arg)); X trace((/*{*/"}\n")); X return arglex_token; X} X X X/* X * NAME X * arglex_init_from_env - initialize analyzer X * X * SYNOPSIS X * void arglex_init_from_env(arglex_table_ty *); X * X * DESCRIPTION X * The arglex_init_from_env function is used to initialize the command X * line lexical analyzer form the COOK environment variable. X * X * RETURNS X * void X * X * CAVEAT X * Must be called befor the arglex function. X */ X Xvoid Xarglex_init_from_env(av0, table) X char *av0; X arglex_table_ty *table; X{ X char *cp1; X char *cp2; X size_t ac; X char **av; X X trace(("arglex_init_from_env()\n{\n")); X av0 = basename(av0); X cp1 = mem_alloc(strlen(av0) + 1); X strcpy(cp1, av0); X for (cp2 = cp1; *cp2; ++cp2) X { X if (islower(*cp2)) X *cp2 = toupper(*cp2); X } X X ac = 1; X av = mem_alloc(sizeof(char *)); X av[0] = av0; X X cp2 = getenv(cp1); X free(cp1); X if (!cp2) X goto done; X X /* make a copy so we don't damage the environment copy */ X cp1 = mem_alloc(strlen(cp2) + 1); X strcpy(cp1, cp2); X X for (;;) X { X size_t nbytes; X X while (*cp1 == ' ') X ++cp1; X if (!*cp1) X break; X cp2 = cp1; X while (*++cp1 != ' ' && *cp1) X ; X if (*cp1) X *cp1++ = 0; X nbytes = (ac + 1) * sizeof(char *); X av = mem_change_size(av, nbytes); X av[ac++] = cp2; X } X done: X arglex_init(ac, av, table); X trace((/*{*/"}\n")); X} X Xvoid Xarglex_set_progname(argv0) X char *argv0; X{ X progname = basename(argv0); X} END_OF_FILE if test 12038 -ne `wc -c <'common/arglex.c'`; then echo shar: \"'common/arglex.c'\" unpacked with wrong size! fi # end of 'common/arglex.c' fi if test -f 'common/fp/md5.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'common/fp/md5.c'\" else echo shar: Extracting \"'common/fp/md5.c'\" \(11871 characters\) sed "s/^X//" >'common/fp/md5.c' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1994 Peter Miller. X * All rights reserved. X * X * This program is free software; you can redistribute it and/or modify X * it under the terms of the GNU General Public License as published by X * the Free Software Foundation; either version 2 of the License, or X * (at your option) any later version. X * X * This program is distributed in the hope that it will be useful, X * but WITHOUT ANY WARRANTY; without even the implied warranty of X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X * GNU General Public License for more details. X * X * You should have received a copy of the GNU General Public License X * along with this program; if not, write to the Free Software X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X * X * MANIFEST: functions to manipulate md5 fingerprints X * X * Derived from code marked X * derived from RSADSI MD5 Message-Digest Algorithm X * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. X * All rights reserved. X * X * License to copy and use this software is granted provided that it X * is identified as the "RSA Data Security, Inc. MD5 Message-Digest X * Algorithm" in all material mentioning or referencing this software X * or this function. X * X * License is also granted to make and use derivative works provided X * that such works are identified as "derived from the RSA Data X * Security, Inc. MD5 Message-Digest Algorithm" in all material X * mentioning or referencing the derived work. X * X * RSA Data Security, Inc. makes no representations concerning either X * the merchantability of this software or the suitability of this X * software for any particular purpose. It is provided "as is" X * without express or implied warranty of any kind. X * X * These notices must be retained in any copies of any part of this X * documentation and/or software. X */ X X#include X#include X X#include X X#define MD5_HASH_LEN 16 X Xtypedef struct md5_ty md5_ty; Xstruct md5_ty X{ X FINGERPRINT_BASE_CLASS X unsigned long state[4]; X unsigned long count[2]; X unsigned char buffer[64]; X}; X X X/* X * Constants for MD5Transform routine. X */ X#define S11 7 X#define S12 12 X#define S13 17 X#define S14 22 X#define S21 5 X#define S22 9 X#define S23 14 X#define S24 20 X#define S31 4 X#define S32 11 X#define S33 16 X#define S34 23 X#define S41 6 X#define S42 10 X#define S43 15 X#define S44 21 X Xstatic unsigned char PADDING[64] = X{ X 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 X}; X X/* X * F, G, H and I are basic MD5 functions. X */ X#define F(x, y, z) (((x)&(y)) | ((~x)&(z))) X#define G(x, y, z) (((x)&(z)) | ((y)&(~z))) X#define H(x, y, z) ((x) ^ (y) ^ (z)) X#define I(x, y, z) ((y) ^ ((x) | (~z))) X X/* X * ROTATE_LEFT rotates x left n bits. X */ X#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) X X/* X * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. X * Rotation is separate from addition to prevent recomputation. X */ X#define FF(a, b, c, d, x, s, ac) \ X do { \ X (a) += F((b), (c), (d)) + (x) + (unsigned long)(ac); \ X (a) = ROTATE_LEFT((a), (s)); \ X (a) += (b); \ X } while(0) X#define GG(a, b, c, d, x, s, ac) \ X do { \ X (a) += G((b), (c), (d)) + (x) + (unsigned long)(ac); \ X (a) = ROTATE_LEFT((a), (s)); \ X (a) += (b); \ X } while(0) X#define HH(a, b, c, d, x, s, ac) \ X do { \ X (a) += H((b), (c), (d)) + (x) + (unsigned long)(ac); \ X (a) = ROTATE_LEFT((a), (s)); \ X (a) += (b); \ X } while(0) X#define II(a, b, c, d, x, s, ac) \ X do { \ X (a) += I((b), (c), (d)) + (x) + (unsigned long)(ac); \ X (a) = ROTATE_LEFT((a), (s)); \ X (a) += (b); \ X } while(0) X X X/* X * MD5 initialization. Begins an MD5 operation, writing a new context. X */ X Xstatic void reset _((md5_ty *)); X Xstatic void Xreset(context) X md5_ty *context; X{ X context->count[0] = 0; X context->count[1] = 0; X X /* X * Load magic initialization constants. X */ X context->state[0] = 0x67452301; X context->state[1] = 0xefcdab89; X context->state[2] = 0x98badcfe; X context->state[3] = 0x10325476; X} X Xstatic void md5_constructor _((fingerprint_ty *)); X Xstatic void Xmd5_constructor(p) X fingerprint_ty *p; X{ X md5_ty *context; X X context = (md5_ty *)p; X reset(context); X} X X Xstatic void md5_destructor _((fingerprint_ty *)); X Xstatic void Xmd5_destructor(p) X fingerprint_ty *p; X{ X} X X X/* X * Encodes input (unsigned long) into output (unsigned char). X * Assumes len is a multiple of 4. X */ X Xstatic void Encode _((unsigned char *, unsigned long *, unsigned int)); X Xstatic void XEncode(output, input, len) X unsigned char *output; X unsigned long *input; X unsigned int len; X{ X unsigned int i, X j; X X for (i = 0, j = 0; j < len; i++, j += 4) X { X output[j] = (unsigned char)(input[i] & 0xff); X output[j + 1] = (unsigned char)((input[i] >> 8) & 0xff); X output[j + 2] = (unsigned char)((input[i] >> 16) & 0xff); X output[j + 3] = (unsigned char)((input[i] >> 24) & 0xff); X } X} X X X/* X * Decodes input (unsigned char) into output (unsigned long). X * Assumes len is a multiple of 4. X */ X Xstatic void Decode _((unsigned long *, unsigned char *, unsigned int)); X Xstatic void XDecode(output, input, len) X unsigned long *output; X unsigned char *input; X unsigned int len; X{ X unsigned int i, X j; X X for (i = 0, j = 0; j < len; i++, j += 4) X output[i] = X ((unsigned long)input[j]) X | X (((unsigned long)input[j + 1]) << 8) X | X (((unsigned long)input[j + 2]) << 16) X | X (((unsigned long)input[j + 3]) << 24); X} X X X/* X * MD5 basic transformation. Transforms state based on block. X */ X Xstatic void MD5Transform _((unsigned long state[4], unsigned char block[64])); X Xstatic void XMD5Transform(state, block) X unsigned long state[4]; X unsigned char block[64]; X{ X unsigned long a = state[0], X b = state[1], X c = state[2], X d = state[3], X x[16]; X X Decode(x, block, 64); X /* Round 1 */ X FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ X FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ X FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ X FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ X FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ X FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ X FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ X FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ X FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ X FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ X FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ X FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ X FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ X FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ X FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ X FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ X /* Round 2 */ X GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ X GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ X GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ X GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ X GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ X GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ X GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ X GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ X GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ X GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ X GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ X GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ X GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ X GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ X GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ X GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ X /* Round 3 */ X HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ X HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ X HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ X HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ X HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ X HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ X HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ X HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ X HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ X HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ X HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ X HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ X HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ X HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ X HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ X HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ X /* Round 4 */ X II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ X II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ X II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ X II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ X II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ X II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ X II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ X II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ X II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ X II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ X II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ X II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ X II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ X II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ X II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ X II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ X state[0] += a; X state[1] += b; X state[2] += c; X state[3] += d; X X /* Reset sensitive information. */ X memset(x, 0, sizeof(x)); X} X X X/* X * MD5 block update operation. Continues an MD5 message-digest X * operation, processing another message block, and updating the X * context. X */ X Xstatic void md5_addn _((fingerprint_ty *, unsigned char *, int)); X Xstatic void Xmd5_addn(p, input, inputLen) X fingerprint_ty *p; X unsigned char *input; /* input block */ X int inputLen; /* length of input block */ X{ X md5_ty *context; X unsigned int i; X unsigned int index; X unsigned int partLen; X X context = (md5_ty *)p; X /* Compute number of bytes mod 64 */ X index = (unsigned int)((context->count[0] >> 3) & 0x3F); X /* Update number of bits */ X if X ( X (context->count[0] += ((unsigned long)inputLen << 3)) X < X ((unsigned long)inputLen << 3) X ) X context->count[1]++; X context->count[1] += ((unsigned long)inputLen >> 29); X partLen = 64 - index; X X /* X * Transform as many times as possible. X */ X if (inputLen >= partLen) X { X memcpy(&context->buffer[index], input, partLen); X MD5Transform(context->state, context->buffer); X for (i = partLen; i + 63 < inputLen; i += 64) X MD5Transform(context->state, &input[i]); X index = 0; X } X else X i = 0; X /* Buffer remaining input */ X memcpy(&context->buffer[index], &input[i], inputLen - i); X} X X X X/* X * MD5 finalization. X * Ends an MD5 message-digest operation, writing the message digest X * and resetting the context. X */ X Xstatic int md5_hash _((fingerprint_ty *, unsigned char *)); X Xstatic int Xmd5_hash(p, digest) X fingerprint_ty *p; X unsigned char *digest; X{ X md5_ty *context; X unsigned char bits[8]; X unsigned int index; X unsigned int padLen; X X /* X * Save number of bits X */ X context = (md5_ty *)p; X Encode(bits, context->count, 8); X X /* X * Pad out to 56 mod 64. X */ X index = (unsigned int)((context->count[0] >> 3) & 0x3f); X padLen = (index < 56) ? (56 - index) : (120 - index); X md5_addn(p, PADDING, padLen); X X /* X * Append length (before padding) X */ X md5_addn(p, bits, 8); X X /* X * Store state in digest X */ X Encode(digest, context->state, 16); X X /* X * reset sensitive information. X */ X reset(context); X X /* X * return length of hash X */ X return MD5_HASH_LEN; X} X X Xstatic void md5_sum _((fingerprint_ty *, char *)); X Xstatic void Xmd5_sum(p, obuf) X fingerprint_ty *p; X char *obuf; X{ X md5_ty *context; X unsigned char h[MD5_HASH_LEN]; X char *cp; X int i; X X context = (md5_ty *)p; X md5_hash(p, h); X cp = obuf; X for (i = 0; i < MD5_HASH_LEN; ++i) X { X sprintf(cp, "%2.2X", h[i]); X cp += 2; X } X *cp = 0; X} X X Xfingerprint_methods_ty fp_md5 = X{ X sizeof(md5_ty), X "md5", X md5_constructor, X md5_destructor, X md5_addn, X md5_hash, X md5_sum X}; END_OF_FILE if test 11871 -ne `wc -c <'common/fp/md5.c'`; then echo shar: \"'common/fp/md5.c'\" unpacked with wrong size! fi # end of 'common/fp/md5.c' fi if test -f 'cook/expr.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cook/expr.c'\" else echo shar: Extracting \"'cook/expr.c'\" \(12344 characters\) sed "s/^X//" >'cook/expr.c' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1990, 1991, 1992, 1993, 1994 Peter Miller. X * All rights reserved. X * X * This program is free software; you can redistribute it and/or modify X * it under the terms of the GNU General Public License as published by X * the Free Software Foundation; either version 2 of the License, or X * (at your option) any later version. X * X * This program is distributed in the hope that it will be useful, X * but WITHOUT ANY WARRANTY; without even the implied warranty of X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X * GNU General Public License for more details. X * X * You should have received a copy of the GNU General Public License X * along with this program; if not, write to the Free Software X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X * X * MANIFEST: functions to manipulate expression trees X * X * This file contains the functions for manipulating expression X * trees; building, interpreting and freeing them. X */ X X#include X#include X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X Xstatic position *expr_context; X X X/* X * NAME X * expr_alloc - allocate a pointer structure X * X * SYNOPSIS X * expr *expr_alloc(void); X * X * DESCRIPTION X * The expr_alloc function is used to allocate an expression node X * structure in dynamic memory. It will initially be filled with zeros. X * The e_op field is defined as being non-zero to allow detection X * of some classes of missuse of the expression nodes. X * X * RETURNS X * A pointer to a "expr" in dynamic memory. X * X * CAVEAT X * The expression node is allocated in dynamic memory, X * it is the callers responsibility to ensure that it is freed X * when it is finished with, by a call to expr_free(). X */ X Xexpr * Xexpr_alloc() X{ X expr *ep; X X trace(("expr_alloc()\n{\n"/*}*/)); X ep = mem_alloc_clear(sizeof(expr)); X ep->e_references = 1; X ep->e_position.pos_name = str_copy(lex_cur_file()); X ep->e_position.pos_line = lex_cur_line(); X trace(("return %08lX;\n", ep)); X trace((/*{*/"}\n")); X return ep; X} X X X/* X * NAME X * expr_copy - copy and expression X * X * SYNOPSIS X * expr *expr_copy(expr *); X * X * DESCRIPTION X * The expr_copy function is used to make a copy of an expression tree. X * X * RETURNS X * The expr_copy function returns a pointer to the root of the copied X * expression tree. X * X * CAVEAT X * The result is in dynamic memory, used expr_free to dispose of it when X * finished with. X */ X Xexpr * Xexpr_copy(ep) X expr *ep; X{ X trace(("expr_copy(ep = %08X)\n{\n"/*}*/, ep)); X ep->e_references++; X trace(("return %08lX;\n", ep)); X trace((/*{*/"}\n")); X return ep; X} X X X/* X * NAME X * expr_free - free expression tree X * X * SYNOPSIS X * void expr_free(expr *ep); X * X * DESCRIPTION X * The expr_free function is used to free expression trees. X * X * CAVEAT X * It is assumed that the expression trees are all X * dynamically allocated. Use expr_alloc() to allocate them. X */ X Xvoid Xexpr_free(ep) X expr *ep; X{ X trace(("expr_free(ep = %08X)\n{\n"/*}*/, ep)); X assert(ep); X ep->e_references--; X if (ep->e_references > 0) X goto ret; X str_free(ep->e_position.pos_name); X switch ((int)ep->e_op) X { X default: X fatal("illegal expression opcode %d (bug)", ep->e_op); X X case OP_CAT: X expr_free(ep->e_left); X expr_free(ep->e_right); X break; X X case OP_FUNC: X el_free(&ep->e_list); X break; X X case OP_WORD: X str_free(ep->e_word); X break; X } X mem_free(ep); Xret: X trace((/*{*/"}\n")); X} X X X/* X * NAME X * el_free - free expression lists X * X * SYNOPSIS X * void el_free(elist *elp); X * X * DESCRIPTION X * The el_free function is used to free expression lists, X * it calls expr_free for each expression in the list. X * X * CAVEAT X * It is assumed that the expressions are dynamically allocated, X * and that the expression list was grown using el_append(). X * The actual structure pointed to is NOT assumed to be in dynamic memory X * and should not be passed to free(). X */ X Xvoid Xel_free(elp) X elist *elp; X{ X int j; X X trace(("el_free(elp = %08X)\n{\n"/*}*/, elp)); X for (j = 0; j < elp->el_nexprs; j++) X expr_free(elp->el_expr[j]); X if (elp->el_nexprs) X mem_free(elp->el_expr); X elp->el_nexprs = 0; X elp->el_expr = 0; X trace((/*{*/"}\n")); X} X X X/* X * NAME X * el_copy - copy expression list X * X * SYNOPSIS X * void el_copy(elist *to, elist *from); X * X * DESCRIPTION X * The el_copy function is used to copy the list of expression trees X * pointed to by `from' into the expression list pointed to by `to'. X * X * RETURNS X * void X * X * CAVEAT X * The el_free function must be used to dispose of the list when X * finished with. X */ X Xvoid Xel_copy(to, from) X elist *to; X elist *from; X{ X int j; X X trace(("el_copy(to = %08X, from = %08X)\n{\n"/*}*/, to, from)); X el_zero(to); X if (!from->el_nexprs) X return; X for (j = 0; j < from->el_nexprs; j++) X el_append(to, from->el_expr[j]); X trace((/*{*/"}\n")); X} X X X/* X * NAME X * expr_eval - evaluate an expression X * X * SYNOPSIS X * int expr_eval(wlist *result, expr *ep); X * X * DESCRIPTION X * The expr_eval function is used to evaluate an expression. X * X * RETURNS X * The results of the expression evaluation are appended to X * the word list pointed to by 'results' argument using wl_append. X * X * The functiuon result is -1 for evaluation errors, 0 for no error. X * X * CAVEAT X * It is assumed that the results wordlist has been initialised X * before it was passed to expr_eval(). X * X * The result returned from this function are allocated in dynamic memory. X * It is the responsibility of the caller to ensure that they are freed X * when they are finished with, using wl_free(). X */ X Xint Xexpr_eval(result, ep) X wlist *result; X expr *ep; X{ X int retval; X X trace(("expr_eval(result = %08X, ep = %08X)\n{\n"/*}*/, result, ep)); X assert(ep); X switch ((int)ep->e_op) X { X default: X error X ( X "%s: %d: illegal expression opcode %d (bug)", X ep->e_position.pos_name->str_text, X ep->e_position.pos_line, X ep->e_op X ); X expr_eval_fails: X option_set_errors(); X retval = -1; X goto ret; X X case OP_WORD: X { X match_ty *field; X X /* X * If a wildcard mapping is in force (we are performing X * actions bound to an implicit recipe) the word will be X * mapped before it is returned. X */ X field = match_top(); X if (field) X { X string_ty *s; X X s = reconstruct(ep->e_word, field); X wl_append(result, s); X str_free(s); X } X else X wl_append(result, ep->e_word); X } X break; X X case OP_FUNC: X { X wlist wl; X X if (el2wl(&wl, &ep->e_list)) X { X wl_free(&wl); X goto expr_eval_fails; X } X switch (wl.wl_nwords) X { X case 0: X break; X X case 1: X { X wlist value; X X if (id_search(wl.wl_word[0], &value)) X { X int j; X X for (j = 0; j < value.wl_nwords; j++) X wl_append(result, value.wl_word[j]); X wl_free(&value); X break; X } X X /* X * If the variable is not found, X * fall through into the function case X */ X } X X default: X { X bifp code; X X code = builtin_search(wl.wl_word[0]); X if (!code) X { X error X ( X "%s: %d: undefined %s \"%s\"", X ep->e_position.pos_name->str_text, X ep->e_position.pos_line, X wl.wl_nwords >= 2 ? "function" : "variable", X wl.wl_word[0]->str_text X ); X goto expr_eval_fails; X } X expr_context = &ep->e_position; X if (code(result, &wl)) X goto expr_eval_fails; X } X break; X } X wl_free(&wl); X } X break; X X case OP_CAT: X { X wlist left; X wlist right; X int j; X X /* X * Form the two word lists. X * Tack the last word of the left list X * onto the first word of the right list. X * X * There are other conceivable ways to do this, X * but this definition gives the fewest surprises. X */ X wl_zero(&left); X if (expr_eval(&left, ep->e_left)) X { X wl_free(&left); X goto expr_eval_fails; X } X wl_zero(&right); X if (expr_eval(&right, ep->e_right)) X { X wl_free(&left); X wl_free(&right); X goto expr_eval_fails; X } X switch ((left.wl_nwords ? 1 : 0) | (right.wl_nwords ? 2 : 0)) X { X case 0: X /* both lists empty */ X break; X X case 1: X /* right list empty */ X for (j = 0; j < left.wl_nwords; j++) X wl_append(result, left.wl_word[j]); X break; X X case 2: X /* left list empty */ X for (j = 0; j < right.wl_nwords; j++) X wl_append(result, right.wl_word[j]); X break; X X case 3: X { X string_ty *s; X X /* at least one word in each list */ X for (j = 0; j < left.wl_nwords - 1; j++) X wl_append(result, left.wl_word[j]); X s = str_catenate(left.wl_word[j], right.wl_word[0]); X wl_append(result, s); X str_free(s); X for (j = 1; j < right.wl_nwords; j++) X wl_append(result, right.wl_word[j]); X } X break; X } X wl_free(&left); X wl_free(&right); X } X break; X } X retval = 0; Xret: X trace(("return %d;\n", retval)); X trace((/*{*/"}\n")); X return retval; X} X X X/* X * NAME X * el_append - append to an expression list X * X * SYNOPSIS X * void el_append(elist *el, expr *e); X * X * DESCRIPTION X * The el_append function is used to append an expression to an expression X * list. X * X * RETURNS X * void X * X * CAVEAT X * The expression has not been copied, so do not hand it X * to expr_free after you append it. X * X * It is assumed that the elist has been previously initialised by a X * elist el; X * el_zero(&el); X * statement (or similar) before this function is called. X */ X Xvoid Xel_append(el, e) X elist *el; X expr *e; X{ X size_t nbytes; X X trace(("el_append(el = %08X, e = %08X)\n{\n"/*}*/, el, e)); X assert(el); X assert(e); X assert(!el->el_nexprs || !!el->el_expr); X nbytes = (el->el_nexprs + 1 ) * sizeof(expr *); X el->el_expr = mem_change_size(el->el_expr, nbytes); X el->el_expr[el->el_nexprs++] = expr_copy(e); X trace((/*{*/"}\n")); X} X X X/* X * NAME X * el2wl - expression list to word list X * X * SYNOPSIS X * int el2wl(wlist *wl, elist *el); X * X * DESCRIPTION X * The el2wl function is used to turn an expression list into a word list. X * X * RETURNS X * The word list is initialised before it is used to X * store the results of evaluating the expressions. X * X * The function return value is -1 for errors and 0 for success. X * X * CAVEAT X * The results returned by this function are allocated in dynamic memory. X * It is the responsibility of the caller to free them when finished with, X * by a call to wl_free(). X */ X Xint Xel2wl(wlp, elp) X wlist *wlp; X elist *elp; X{ X int j; X int retval; X X trace(("el2wl(wlp = %08X, elp = %08X)\n{\n"/*}*/, wlp, elp)); X retval = 0; X wl_zero(wlp); X for (j = 0; j < elp->el_nexprs; j++) X { X if (expr_eval(wlp, elp->el_expr[j])) X { X retval = -1; X break; X } X } X trace(("return %d;\n", retval)); X trace((/*{*/"}\n")); X return retval; X} X X X/* X * NAME X * expr_eveal_condition - evaluate condition X * X * SYNOPSIS X * int expr_eval_condition(expr *); X * X * DESCRIPTION X * The expr_eval_condition function is used to evaluate an expression to X * yeild a true/false result. The expression is evaluated into a word X * list. A false result is if all of the resulting strings are empty or X * 0, true otherwise. X * X * RETURNS X * The expr_eval_condition function returns 0 if the condition is false, X * and nonzero if it is true. The value -1 is returned on error. X * X * CAVEAT X * The str_bool function is used to test the booean value of a string; X * changeing the behaviour of that function will change the behaviour of X * this one. X */ X Xint Xexpr_eval_condition(ep) X expr *ep; X{ X wlist wl; X int j; X int result; X X trace(("expr_eval_condition(ep = %08X)\n{\n"/*}*/, ep)); X assert(ep); X wl_zero(&wl); X if (expr_eval(&wl, ep)) X { X result = -1; X goto ret; X } X for (j = 0; j < wl.wl_nwords; ++j) X { X if (str_bool(wl.wl_word[j])) X { X wl_free(&wl); X result = 1; X goto ret; X } X } X wl_free(&wl); X result = 0; Xret: X trace(("return %d;\n", result)); X trace((/*{*/"}\n")); X return result; X} X X X/*VARARGS1*/ Xvoid Xexpr_error(s sva_last) X char *s; X sva_last_decl X{ X va_list ap; X string_ty *buffer; X X sva_init(ap, s); X buffer = str_vformat(s, ap); X va_end(ap); X error X ( X "%S: %d: %S", X expr_context->pos_name, X expr_context->pos_line, X buffer X ); X str_free(buffer); X} X X Xvoid Xel_zero(elp) X elist *elp; X{ X elp->el_nexprs = 0; X elp->el_expr = 0; X} END_OF_FILE if test 12344 -ne `wc -c <'cook/expr.c'`; then echo shar: \"'cook/expr.c'\" unpacked with wrong size! fi # end of 'cook/expr.c' fi if test -f 'cook/main.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cook/main.c'\" else echo shar: Extracting \"'cook/main.c'\" \(12629 characters\) sed "s/^X//" >'cook/main.c' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1991, 1992, 1993, 1994 Peter Miller. X * All rights reserved. X * X * This program is free software; you can redistribute it and/or modify X * it under the terms of the GNU General Public License as published by X * the Free Software Foundation; either version 2 of the License, or X * (at your option) any later version. X * X * This program is distributed in the hope that it will be useful, X * but WITHOUT ANY WARRANTY; without even the implied warranty of X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X * GNU General Public License for more details. X * X * You should have received a copy of the GNU General Public License X * along with this program; if not, write to the Free Software X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X * X * MANIFEST: operating system start point, and command line argument parsing X */ X X#include X#include X#include X#include X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X Xenum X{ X arglex_token_action, X arglex_token_no_action, X arglex_token_book, X arglex_token_no_book, X arglex_token_errok, X arglex_token_no_errok, X arglex_token_fingerprint, X arglex_token_no_fingerprint, X arglex_token_force, X arglex_token_no_force, X arglex_token_include, X arglex_token_log, X arglex_token_no_log, X arglex_token_metering, X arglex_token_no_metering, X arglex_token_persevere, X arglex_token_no_persevere, X arglex_token_precious, X arglex_token_no_precious, X arglex_token_silent, X arglex_token_no_silent, X arglex_token_star, X arglex_token_no_star, X arglex_token_strip_dot, X arglex_token_no_strip_dot, X arglex_token_touch, X arglex_token_no_touch, X arglex_token_trace, X arglex_token_no_trace, X arglex_token_tty, X arglex_token_no_tty, X arglex_token_update, X arglex_token_no_update X}; X Xstatic arglex_table_ty argtab[] = X{ X { "-Action", (arglex_token_ty)arglex_token_action, }, X { "-No_Action", (arglex_token_ty)arglex_token_no_action, }, X { "-Book", (arglex_token_ty)arglex_token_book, }, X { "-No_Book", (arglex_token_ty)arglex_token_no_book, }, X { "-Continue", (arglex_token_ty)arglex_token_persevere, }, X { "-No_Continue", (arglex_token_ty)arglex_token_no_persevere, }, X { "-Errok", (arglex_token_ty)arglex_token_errok, }, X { "-No_Errok", (arglex_token_ty)arglex_token_no_errok, }, X { "-FingerPrint", (arglex_token_ty)arglex_token_fingerprint, }, X { "-No_FingerPrint", (arglex_token_ty)arglex_token_no_fingerprint, }, X { "-Forced", (arglex_token_ty)arglex_token_force, }, X { "-No_Forced", (arglex_token_ty)arglex_token_no_force, }, X { "-Include", (arglex_token_ty)arglex_token_include, }, X { "-\\I*", (arglex_token_ty)arglex_token_include, }, X { "-LOg", (arglex_token_ty)arglex_token_log, }, X { "-List", (arglex_token_ty)arglex_token_log, }, X { "-No_LOg", (arglex_token_ty)arglex_token_no_log, }, X { "-No_List", (arglex_token_ty)arglex_token_no_log, }, X { "-Meter", (arglex_token_ty)arglex_token_metering, }, X { "-No_Meter", (arglex_token_ty)arglex_token_no_metering, }, X { "-Precious", (arglex_token_ty)arglex_token_precious, }, X { "-No_Precious", (arglex_token_ty)arglex_token_no_precious, }, X { "-Silent", (arglex_token_ty)arglex_token_silent, }, X { "-No_Silent", (arglex_token_ty)arglex_token_no_silent, }, X { "-STar", (arglex_token_ty)arglex_token_star, }, X { "-No_STar", (arglex_token_ty)arglex_token_no_star, }, X { "-Strip_Dot", (arglex_token_ty)arglex_token_strip_dot, }, X { "-No_Strip_Dot", (arglex_token_ty)arglex_token_no_strip_dot, }, X { "-TErminal", (arglex_token_ty)arglex_token_tty, }, X { "-No_TErminal", (arglex_token_ty)arglex_token_no_tty, }, X { "-Touch", (arglex_token_ty)arglex_token_touch, }, X { "-No_Touch", (arglex_token_ty)arglex_token_no_touch, }, X { "-TRace", (arglex_token_ty)arglex_token_trace, }, X { "-No_TRace", (arglex_token_ty)arglex_token_no_trace, }, X { "-Update", (arglex_token_ty)arglex_token_update, }, X { "-No_Update", (arglex_token_ty)arglex_token_no_update, }, X { 0, (arglex_token_ty)0, }, /* end marker */ X}; X X X/* X * NAME X * usage - options diagnostic X * X * SYNOPSIS X * void usage(void); X * X * DESCRIPTION X * Usage is called when the user has made a syntactic or semantic error X * on the command line. X * X * CAVEAT X * This function does NOT return. X */ X Xstatic void usage _((void)); X Xstatic void Xusage() X{ X fprintf(stderr, "usage: %s [