home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1993-05-03 | 82.9 KB | 4,419 lines
Newsgroups: comp.sources.unix From: pmiller@bmr.gov.au (Peter Miller) Subject: v26i217: cook-1.4 - a file construction tool (like "make"), Part09/11 Sender: unix-sources-moderator@efficacy.home.vix.com Approved: WhoAmI@efficacy.home.vix.com Submitted-By: pmiller@bmr.gov.au (Peter Miller) Posting-Number: Volume 26, Issue 217 Archive-Name: cook-1.4/part09 #! /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 <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 9 (of 11)." # Contents: cook/lex.c cook/os.c cooktime/date.y # Wrapped by vixie@efficacy.home.vix.com on Tue May 4 01:36:42 1993 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'cook/lex.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cook/lex.c'\" else echo shar: Extracting \"'cook/lex.c'\" \(26740 characters\) sed "s/^X//" >'cook/lex.c' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1990, 1991, 1992, 1993 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 cookbooks X * X * This file contains the lexical analyser for the cookbook parser. X * A known bug is that it processes # control lines even within comments. X * Another bug is that comments vanish: they should be replaced by a single X * space. X */ X X#include <stddef.h> X#include <string.h> X#include <stdlib.h> X#include <stdio.h> X X#include <error.h> X#include <expr.h> X#include <hashline.h> X#include <id.h> X#include <lex.h> X#include <mem.h> X#include <option.h> X#include <s-v-arg.h> X#include <stmt.h> X#include <trace.h> X#include <parse.gen.h> /* must be last */ X#include <hashline.gen.h> /* must be last */ X X X/* X * Meta types X */ X#define NORMAL 1 X#define SLOSHED 2 X#define WHITE 3 X X/* X */ X#define BEFORE (1<<0) X#define AFTER (1<<1) X#define SINGLE (1<<2) X X#define HASHLINE_ESCAPE ('#' ^ 0x80) X#define UNQUOTED_WORD 32767 X#define EOLN 32766 X X Xtypedef struct meta_ty meta_ty; Xstruct meta_ty X{ X int m_type; /* meta character "type" info */ X int m_char; /* meta character actual value */ X int m_flag; /* meta character flags */ X}; X Xtypedef struct lex_ty lex_ty; Xstruct lex_ty X{ X string_ty *l_name; /* name of file being analysed */ X FILE *l_file; /* open file structure of the file */ X int l_line; /* the line number we are up to */ X meta_ty l_mback; /* backup for meta chars */ X lex_ty *l_chain; /* file this one is an insert from */ X short l_bol; X}; X Xtypedef struct stracc stracc; Xstruct stracc X{ X size_t sa_max; /* size of string accum buffer */ X size_t sa_len; /* number of chars accumulated */ X char *sa_buf; /* the string accum buffer */ X int sa_inuse; X}; X Xstatic lex_ty *root; /* root of insert list */ Xstatic stracc saroot; /* root of string accum list */ Xstatic int errcnt; /* count of errors to date */ Xstatic int mode; /* what lex mode we are in */ Xstatic int state; Xstatic int passing; Xstatic int catted; X X X/* X * NAME X * lex_initialize - look for keywords X * X * SYNOPSIS X * int lex_initialize(void); X * X * DESCRIPTION X * The lex_initialize function adds all the keywords to the symbol table. X * X * CAVEAT X * The keywords are intentionally case sensitive. X * Assumes that str_initialize has already been called. X */ X Xvoid Xlex_initialize() X{ X typedef struct keyword_ty keyword_ty; X struct keyword_ty X { X char *k_name; X int k_token; X id_class_ty k_class; X }; X X static keyword_ty keyword[] = X { X { ":", COLON, ID_CLASS_PARSE_KEYWORD, }, X { ";", SEMICOLON, ID_CLASS_PARSE_KEYWORD, }, X { "=", EQUALS, ID_CLASS_PARSE_KEYWORD, }, X { "[", LBRAK, ID_CLASS_HASH_KEYWORD, }, X { "[", LBRAK, ID_CLASS_PARSE_KEYWORD, }, X { "]", RBRAK, ID_CLASS_HASH_KEYWORD, }, X { "]", RBRAK, ID_CLASS_PARSE_KEYWORD, }, X { "data", DATA, ID_CLASS_PARSE_KEYWORD, }, X { "elif", HASH_ELIF, ID_CLASS_HASH_KEYWORD, }, X { "else", ELSE, ID_CLASS_PARSE_KEYWORD, }, X { "else", HASH_ELSE, ID_CLASS_HASH_KEYWORD, }, X { "endif", HASH_ENDIF, ID_CLASS_HASH_KEYWORD, }, X { "fail", FAIL, ID_CLASS_PARSE_KEYWORD, }, X { "if", HASH_IF, ID_CLASS_HASH_KEYWORD, }, X { "if", IF, ID_CLASS_PARSE_KEYWORD, }, X { "ifdef", HASH_IFDEF, ID_CLASS_HASH_KEYWORD, }, X { "ifndef", HASH_IFNDEF, ID_CLASS_HASH_KEYWORD, }, X { "include", HASH_INCLUDE, ID_CLASS_HASH_KEYWORD, }, X { "loop", LOOP, ID_CLASS_PARSE_KEYWORD, }, X { "loopstop", LOOPSTOP, ID_CLASS_PARSE_KEYWORD, }, X { "pragma", HASH_PRAGMA, ID_CLASS_HASH_KEYWORD, }, X { "set", SET, ID_CLASS_PARSE_KEYWORD, }, X { "then", THEN, ID_CLASS_PARSE_KEYWORD, }, X { "unsetenv", UNSETENV, ID_CLASS_PARSE_KEYWORD, }, X { "{"/*}*/, LBRACE, ID_CLASS_PARSE_KEYWORD, }, X { /*{*/"}", RBRACE, ID_CLASS_PARSE_KEYWORD, }, X }; X keyword_ty *kp; X X trace(("init_reserved()\n{\n"/*}*/)); X passing = 1; X for (kp = keyword; kp < ENDOF(keyword); ++kp) X { X string_ty *s; X X s = str_from_c(kp->k_name); X id_assign(s, kp->k_class, &kp->k_token); X str_free(s); X } X trace((/*{*/"}\n")); X} X X X/* X * NAME X * lex_open - open a file for lexical analysis X * X * SYNOPSIS X * void lex_open(string_ty *filename); X * X * DESCRIPTION X * Lex_open opens a file for lexical analysis X * (it is also used to open include files). X * X * RETURNS X * void X */ X Xvoid Xlex_open(filename) X string_ty *filename; X{ X lex_ty *new; X X trace(("lex_open(filename = %08lX)\n{\n"/*}*/, filename)); X trace_string(filename->str_text); X for (new = root; new; new = new->l_chain) X { X if (str_equal(filename, new->l_name)) X { X lex_error("%s: recursive include", filename->str_text); X return; X } X } X new = (lex_ty *)mem_alloc_clear(sizeof(lex_ty)); X new->l_name = str_copy(filename); X new->l_file = fopen(filename->str_text, "r"); X if (!new->l_file) X nfatal("cookbook \"%s\"", filename->str_text); X new->l_line = 1; X new->l_bol = 1; X new->l_chain = root; X root = new; X trace((/*{*/"}\n")); X} X X X/* X * NAME X * lex_close - closes a file which was open for lexical analysis X * X * SYNOPSIS X * void lex_close(void); X * X * DESCRIPTION X * Lex_close closes a file previously opened for lexical analysis. X * X * CAVEAT X * Lex_open must have been previously used to open the file. X */ X Xvoid Xlex_close() X{ X lex_ty *old; X X trace(("lex_close()\n{\n"/*}*/)); X assert(root); X old = root; X fclose(root->l_file); X root = old->l_chain; X if (!root && errcnt) X exit(1); X str_free(old->l_name); X free(old); X trace((/*{*/"}\n")); X} X X X/* X * NAME X * illeof - conplain about an illegal end-of-file X * X * SYNOPSIS X * void illeof(void); X * X * DESCRIPTION X * Illeof is used to complain about illegal end-of-file. X * This may occu in several places. X * X * CAVEAT X * It does not return. X */ X Xstatic void illeof _((void)); X Xstatic void Xilleof() X{ X lex_error("unexpected end of file"); X exit(1); X} X X X/* X * NAME X * byte - get a byte from the input stream X * X * SYNOPSIS X * int byte(void); X * X * DESCRIPTION X * Byte return the next byte from the input stream, or EOF X * If the end of the outermost file is reached. X * It detechs the end of include files and closes them transparently. X * X * CAVEAT X * Lex_open must have been called previously. X */ X Xstatic int byte _((void)); X Xstatic int Xbyte() X{ X int c; X X trace(("byte()\n{\n"/*}*/)); X for (;;) X { X assert(root); X c = fgetc(root->l_file); X if (c != EOF) X break; X if (ferror(root->l_file)) X nfatal("%s", root->l_name->str_text); X if (!root->l_chain) X break; X lex_close(); X } X if ((c < ' ' || c > '~') && c != EOF && !strchr("\t\n\f", c)) X { X lex_error("illegal '\\%o' character", (unsigned char)c); X c = ' '; X } X if (mode != LM_DATA && root->l_bol && c == '#') X c = HASHLINE_ESCAPE; X if (c == '\n') X { X root->l_bol = 1; X root->l_line++; X } X else X { X if (!strchr(" \t\f", c)) X root->l_bol = 0; X } X trace_short(root->l_bol); X trace(("return '%c';\n", c)); X trace((/*{*/"}\n")); X return c; X} X X X/* X * NAME X * byte_undo - push back a character X * X * SYNOPSIS X * void byte_undo(int c); X * X * Description X * Byte_undo pushes a byte back onto the input stream. X * X * CAVEAT X * Only one byte may be pushed back at any one time. X */ X Xstatic void byte_undo _((int)); X Xstatic void Xbyte_undo(c) X int c; X{ X trace(("byte_undo(c = '%c')\n{\n"/*}*/, c)); X assert(root); X switch (c) X { X case EOF: X break; X X case '\n': X root->l_line--; X default: X ungetc(c, root->l_file); X break; X } X trace((/*{*/"}\n")); X} X X X/* X * NAME X * sa_open - start accumulating a string. X * X * SYNOPSIS X * void sa_open(void); X * X * DESCRIPTION X * Sa_open begins accumulating a string for the lexical analyser. X * This allows virtually infinite length constructs within the X * lexical analyser. X */ X Xstatic void sa_open _((void)); X Xstatic void Xsa_open() X{ X trace(("sa_open()\n{\n"/*}*/)); X assert(!saroot.sa_inuse); X saroot.sa_inuse = 1; X saroot.sa_len = 0; X trace((/*{*/"}\n")); X} X X X/* X * NAME X * sa_char - add a character to the accumulating string X * X * SYNOPSIS X * void sa_char(int c); X * X * DESCRIPTION X * Sa_char adds a character to the accumulating string. X * X * CAVEAT X * Sa_open must have been called previously. X */ X Xstatic void sa_char _((int)); X Xstatic void Xsa_char(c) X int c; X{ X trace(("sa_char(c = '%c')\n{\n"/*}*/, c)); X assert(saroot.sa_inuse); X if (saroot.sa_len >= saroot.sa_max) X { X saroot.sa_max += 128; X if (!saroot.sa_buf) X saroot.sa_buf = (char *)mem_alloc(saroot.sa_max); X else X mem_change_size((char **)&saroot.sa_buf, saroot.sa_max); X } X saroot.sa_buf[saroot.sa_len++] = c; X trace((/*{*/"}\n")); X} X X X/* X * NAME X * sa_close - finish accumulating a string X * X * SYNOPSIS X * string_ty *sa_close(void); X * X * DESCRIPTION X * Sa_close finished accumulating a string and X * returns a pointer to the string in dynamic memory. X * X * CAVEAT X * Sa_open must have been called previously. X * X * The value returned by this function is allocated in dynamic memory. X * It is the responsibility of the caller to ensure that it is freed when X * finished with, by a call to str_free(). X */ X Xstatic string_ty *sa_close _((void)); X Xstatic string_ty * Xsa_close() X{ X string_ty *val; X X trace(("sa_close()\n{\n"/*}*/)); X assert(saroot.sa_inuse); X val = str_n_from_c(saroot.sa_buf, saroot.sa_len); X saroot.sa_inuse = 0; X trace(("return %08lX;\n", val)); X trace((/*{*/"}\n")); X return val; X} X X X/* X * NAME X * sa_mark - mark place when accumulating a string X * X * SYNOPSIS X * size_t sa_mark(void); X * X * DESACRIPTION X * Sa_mark returns an indicator as to where the string being accumulated X * is up to. X * X * CAVEAT X * Sa_open must have been called previously. X * Do not use the returned value for anything other than passing to X * sa_goto as an argument. X */ X Xstatic size_t sa_mark _((void)); X Xstatic size_t Xsa_mark() X{ X assert(saroot.sa_inuse); X return (saroot.sa_len); X} X X X/* X * NAME X * sa_goto - shorten the accumulated string X * X * SYNOPSIS X * void sa_goto(size_t mark); X * X * DESACRIPTION X * Sa_goto shotens the accumulating string to a point X * previously determined by sa_mark. X * X * CAVEAT X * Sa_open must have been called previously. X * The mark argument must have previously been returned by sa_mark, X * and sa_close not called in the interim. X */ X Xstatic void sa_goto _((size_t)); X Xstatic void Xsa_goto(n) X size_t n; X{ X assert(saroot.sa_inuse); X assert(n <= saroot.sa_len); X saroot.sa_len = n; X} X X X/* X * NAME X * meta_repn - mata_ty representation X * X * SYNOPSIS X * char *meta_repn(meta_ty*); X * X * DESCRIPTION X * The meta_repn function is used to produce a readable representation of X * a mata_ty value. X * X * RETURNS X * char* - a pointer to ta C string. X * X * CAVEAT X * This function is only available when the DEBUG symbol is #define'd. X */ X X#ifdef DEBUG X Xstatic char *meta_repn _((meta_ty *)); X Xstatic char * Xmeta_repn(val) X meta_ty *val; X{ X static char *type[] = X { X "0", X "NORMAL", X "SLOSHED", X "WHITE", X }; X static char *flag[] = X { X "0", X "BEFORE", X "AFTER", X "AFTER | BEFORE", X "SINGLE", X "SINGLE | BEFORE", X "SINGLE | AFTER", X "SINGLE | AFTER | BEFORE", X }; X static char buffer[100]; X char buf2[20]; X char buf3[10]; X X if (val->m_type >= 0 && val->m_type < SIZEOF(type)) X strcpy(buf2, type[val->m_type]); X else X sprintf(buf2, "%d", val->m_type); X switch (val->m_char) X { X default: X if (val->m_char >= ' ' && val->m_char <= '~') X { X buf3[0] = val->m_char; X buf3[1] = 0; X } X else X sprintf(buf3, "\\%03o", (unsigned char)val->m_char); X break; X X case '\'': X case '\\': X buf3[0] = '\\'; X buf3[1] = val->m_char; X buf3[2] = 0; X break; X } X sprintf X ( X buffer, X "{m_char = '%s', m_type = %s, m_flag = %s}", X buf3, X buf2, X flag[val->m_flag] X ); X return buffer; X} X X#endif X X X/* X * NAME X * meta - fetch and classify a character X * X * SYNOPSIS X * void meta(meta_ty*); X * X * DESCRIPTION X * Meta fetches and classifies a character from the input stream. X * The classification may be 'mode' dependent. X * X * This is the point at which escaped newlines and comments are replaced X * by a single space. X * X * CAVEAT X * Lex_open must have been called previously. X */ X Xstatic void meta _((meta_ty *)); X Xstatic void Xmeta(val) X meta_ty *val; X{ X trace(("meta(val = %08lX)\n{\n"/*}*/, val)); X assert(root); X if (root->l_mback.m_type) X { X *val = root->l_mback; X root->l_mback.m_type = 0; X } X else X { X switch (val->m_char = byte()) X { X default: X val->m_type = NORMAL; X val->m_flag = BEFORE | AFTER; X break; X X case EOF: X val->m_type = EOF; X val->m_flag = SINGLE; X break; X X case '=': X case ';': X case ':': X case '{'/*}*/: X case /*{*/'}': X switch (mode) X { X default: X val->m_type = NORMAL; X val->m_flag = SINGLE; X break; X X case LM_DATA: X case LM_SQUOTE: X case LM_DQUOTE: X val->m_type = NORMAL; X val->m_flag = BEFORE | AFTER; X break; X } X break; X X case '['/*]*/: X switch (mode) X { X default: X val->m_flag = BEFORE | SINGLE; X break; X X case LM_SQUOTE: X case LM_DQUOTE: X val->m_flag = BEFORE | AFTER; X } X val->m_type = NORMAL; X break; X X case /*[*/']': X val->m_type = NORMAL; X switch (mode) X { X default: X val->m_flag = AFTER | SINGLE; X break; X X case LM_DATA: X case LM_SQUOTE: X case LM_DQUOTE: X val->m_flag = BEFORE | AFTER; X break; X } X break; X X case ' ': X case '\t': X case '\n': X case '\f': X switch (mode) X { X default: X val->m_type = WHITE; X val->m_flag = SINGLE; X break; X X case LM_SQUOTE: X case LM_DQUOTE: X case LM_DATA: X val->m_type = NORMAL; X val->m_flag = BEFORE | AFTER; X break; X } X break; X X case '\\': X switch (val->m_char = byte()) X { X case EOF: X illeof(); X X case '\n': X val->m_char = ' '; X val->m_type = WHITE; X val->m_flag = SINGLE; X break; X X case '0': X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X { X int count; X int c; X X c = val->m_char; X val->m_char = 0; X for (count = 0; count < 3; count++) X { X val->m_char = val->m_char * 8 + c - '0'; X c = byte(); X if (c < '0' || c > '7') X { X byte_undo(c); X break; X } X } X val->m_type = SLOSHED; X val->m_flag = BEFORE | AFTER; X } X break; X X default: X { X char *cp; X X cp = strchr("b\bf\fn\nr\rt\t", val->m_char); X if (cp) X val->m_char = cp[1]; X val->m_type = SLOSHED; X val->m_flag = BEFORE | AFTER; X } X break; X } X break; X X case '/': X switch (mode) X { X default: X { X int c; X int gate; X int count; X X if ((c = byte()) != '*') X { X byte_undo(c); X val->m_type = NORMAL; X val->m_flag = BEFORE | AFTER; X break; X } X gate = 0; X count = 1; X while (count) X { X switch (c = byte()) X { X default: X case '\n': X case '\t': X case '\f': X gate = 0; X break; X X case EOF: X illeof(); X X case '*': X if (gate == 1) X { X /* X * nested comment start X * X * should a warning be issued? X */ X count++; X gate = 0; X } X else X gate = 2; X break; X X case '/': X if (gate == 2) X { X count--; X gate = 0; X } X else X gate = 1; X break; X } X } X val->m_char = ' '; X val->m_type = WHITE; X val->m_flag = SINGLE; X } X break; X X case LM_DATA: X case LM_SQUOTE: X case LM_DQUOTE: X val->m_type = NORMAL; X val->m_flag = BEFORE | AFTER; X break; X } X break; X } X } X trace(("*val = %s;\n", meta_repn(val))); X trace((/*{*/"}\n")); X} X X X/* X * NAME X * meta_undo - push back a character X * X * SYNOPSIS X * void meta_undo(meta_ty*); X * X * DESCRIPTION X * Meta_undo is used to temporarily push back a character X * from the input stream. X * X * CAVEAT X * Only one character may be pushed back at any one time. X */ X Xstatic void meta_undo _((meta_ty *)); X Xstatic void Xmeta_undo(val) X meta_ty *val; X{ X assert(root); X assert(!root->l_mback.m_type); X root->l_mback = *val; X} X X X/* X * NAME X * lex_error - print a file location specific error message X * X * SYNOPSIS X * void lex_error(char *s, ...); X * X * DESCRIPTION X * Lex_error prints a file location specific error message. X * If too many errors pass through here, cook will give up. X */ X X/*VARARGS1*/ Xvoid Xlex_error(s sva_last) X char *s; X sva_last_decl X{ X char buffer[1024]; X va_list ap; X X sva_init(ap, s); X assert(root); X vsprintf(buffer, s, ap); X va_end(ap); X error("%s: %d: %s", root->l_name->str_text, root->l_line, buffer); X if (++errcnt > 20) X fatal("too many errors"); X option_set_errors(); X} X X X/* X * NAME X * lex_mode - change the mode of lexical analysis X * X * SYNOPSIS X * int lex_mode(int n); X * X * DESCRIPTION X * Lex_mode changes the mode of lexical analysis to the one given, X * and returns the previous mode. X * X * CAVEAT X * Use the #define's in lex.h. X */ X Xint Xlex_mode(n) X int n; X{ X int old; X X old = mode; X mode = n; X return (old); X} X X X/* X * NAME X * lex_trace - debug output X * X * SYNOPSIS X * void lex_trace(char *s, ...); X * X * DESCRIPTION X * The lex_trace function is used to format and output yyparse's trace X * output. The printf's in yyparse are #define'd into lex_trace calls. X * Unfortunately yacc's designer did not take trace output redirection X * into account, to this function is a little tedious. X * X * RETURNS X * void X * X * CAVEAT X * This function is only available when the DEBUG symbol is #define'd. X */ X X#ifdef DEBUG X X/*VARARGS1*/ Xvoid Xlex_trace(s sva_last) X char *s; X sva_last_decl X{ X va_list ap; X static char line[1024]; X char buffer[512]; X char *cp; X X sva_init(ap, s); X vsprintf(buffer, s, ap); X va_end(ap); X strcat(line, buffer); X cp = line + strlen(line) - 1; X if (cp > line && *cp == '\n') X { X *cp = 0; X trace_printf X ( X "%s: %d: %s\n", X root->l_name->str_text, X root->l_line, X line X ); X line[0] = 0; X } X} X X Xstatic char *mode_repn _((int)); X Xstatic char * Xmode_repn(n) X int n; X{ X static char *name[] = X { X "NORMAL", X "DATA", X "SQUOTE", X "DQUOTE", X }; X static char buffer[12]; X X if (n >= 0 && n < SIZEOF(name)) X return name[n]; X sprintf(buffer, "%d", n); X return buffer; X} X X#endif X X X/* X * NAME X * tokenize - lexical analyser X * X * SYNOPSIS X * int tokenize(void); X * X * DESCRIPTION X * The tokenize functionm is used to partition the input stream into X * discreet tokens. X * X * CAVEAT X * Lex_open must have been called previously. X */ X Xstatic int tokenize _((void)); X Xstatic int Xtokenize() X{ X meta_ty c; X int tok; X static meta_ty last; X static int dataended; X X trace(("tokenize()\n{\n"/*}*/)); X if (dataended) X { X dataended = 0; X tok = DATAEND; X goto ret; X } X X if (!catted && (last.m_flag & AFTER)) X { X /* X * For parts of words in expressions which are abutted X * together, the user is implying catenation of the strings. X * This implied zero length token happens here. X */ X meta(&c); X meta_undo(&c); X if (c.m_flag & BEFORE) X { X catted = 1; X tok = CATENATE; X goto ret; X } X } X else X catted = 0; X X for (;;) X { X int mark; X X mark = 0; X meta(&c); X if (c.m_char == HASHLINE_ESCAPE) X { X state = 0; X last.m_flag = 0; X hashline(); X state = 0; X last.m_flag = 0; X continue; X } X switch (c.m_type) X { X default: X fatal("illegal m_type %d (bug %d)", c.m_type, __LINE__); X X case EOF: X tok = 0; /* by definition of yacc */ X goto ret; X X case NORMAL: X case SLOSHED: X { X int sloshed; X X sloshed = 0; X sa_open(); X if (state == 0) X mark = sa_mark(); X for (;;) X { X trace X (( X "%s: %d: state %d, mode %s;\n", X root->l_name->str_text, X root->l_line, X state, X mode_repn(mode) X )); X sa_char(c.m_char); X if (c.m_type == SLOSHED) X { X sloshed = 1; X switch (state) X { X default: X state = 1; X break; X X case 11: X case 12: X /* don't change state */ X break; X } X } X else X { X switch (c.m_char) X { X default: X switch (state) X { X default: X state = 1; X X case 11: X case 12: X break; X } X break; X X case '\n': X switch (state) X { X case 9: X case 10: X sa_goto(mark); X parse_lval.lv_word = sa_close(); X state = 0; X last.m_flag = 0; /* don't CAT after this */ X dataended = DATAEND; X tok = WORD; X goto ret; X X case 11: X case 12: X /* don't change state */ X lex_error("newline in quote"); X break; X X default: X state = 0; X mark = sa_mark(); X break; X } X break; X X case '\'': X if (state != 12) X { X static int oldmode = LM_SQUOTE; X X oldmode = lex_mode(oldmode); X sa_goto(sa_mark()-1); X state = ((state == 11) ? 1 : 11); X sloshed = 1; X } X break; X X case '"': X if (state != 11) X { X static int oldmode = LM_DQUOTE; X X oldmode = lex_mode(oldmode); X sa_goto(sa_mark()-1); X state = ((state == 12) ? 1 : 12); X sloshed = 1; X } X break; X X case ' ': X case '\t': X switch (state) X { X default: X state = 1; X break; X X case 0: X case 2: X state = 2; X break; X X case 9: X case 10: X state = 10; X break; X X case 11: X case 12: X break; X } X break; X X case 'a': X switch (state) X { X default: X state = 1; X break; X X case 3: X case 5: X state++; X break; X X case 11: X case 12: X break; X } X break; X X case 'd': X switch (state) X { X default: X state = 1; X break; X X case 0: X case 2: X state = 3; X break; X X case 8: X state = 9; X break; X X case 11: X case 12: X break; X } X break; X X case 'e': X switch (state) X { X default: X state = 1; X break; X X case 6: X state = 7; X break; X X case 11: X case 12: X break; X } X break; X X case 'n': X switch (state) X { X default: X state = 1; X break; X X case 7: X state = 8; X break; X X case 11: X case 12: X break; X } X break; X X case 't': X switch (state) X { X default: X state = 1; X break; X X case 4: X state = 5; X break; X X case 11: X case 12: X break; X } X break; X } X } X last = c; X if (last.m_flag & SINGLE) X break; X meta(&c); X if (c.m_flag & SINGLE) X { X meta_undo(&c); X break; X } X } X parse_lval.lv_word = sa_close(); X if (sloshed) X tok = WORD; X else X tok = UNQUOTED_WORD; X } X goto ret; X X case WHITE: X if (c.m_char == '\n') X { X state = 0; X tok = EOLN; X goto ret; X } X state = 1; X break; X } X } X X ret: X trace(("return %d;\n", tok)); X trace((/*{*/"}\n")); X return tok; X} X X X/* X * NAME X * lex_cur_file - name current file X * X * SYNOPSIS X * string_ty *lex_cur_file(void); X * X * DESCRIPTION X * The lex_cur_file function is used to get the name of the current X * file being analyzed by lex. X * X * RETURNS X * string_ty* - pointer to string with name in it X * X * CAVEAT X * Does not take a copy, so don't use str_free() X */ X Xstring_ty * Xlex_cur_file() X{ X assert(root); X return root->l_name; X} X Xint Xlex_cur_line() X{ X assert(root); X return root->l_line; X} X X X/* X * NAME X * parse_lex - lexer for parse.y X * X * SYNOPSIS X * int parse_lex(void); X * X * DESCRIPTION X * The parse_lex function is used to turn the tokens generated X * by tokenize into parse.y tokens. X * X * RETURNS X * int - the token number X * X * CAVEAT X * Intended solely for use by the yacc-generated compiler X */ X Xint Xparse_lex() X{ X int tok; X X trace(("parse_lex()\n{\n"/*}*/)); X for (;;) X { X tok = tokenize(); X if (!passing) X { X if (!tok) X { X lex_error("unterminated conditional"); X break; X } X if (tok == WORD || tok == UNQUOTED_WORD) X str_free(parse_lval.lv_word); X continue; X } X switch (tok) X { X case EOLN: X continue; X X case UNQUOTED_WORD: X if (id_search(parse_lval.lv_word, ID_CLASS_PARSE_KEYWORD, &tok)) X { X str_free(parse_lval.lv_word); X parse_lval.lv_word = 0; X if (tok == COLON) X { X parse_lval.lv_position.pos_name = str_copy(root->l_name); X parse_lval.lv_position.pos_line = root->l_line; X } X if (tok == DATA) X { X int winged; X meta_ty last; X meta_ty c; X X /* X * This is a special. Gobble the rest X * of the line before he starts sucking X * in the program data. X */ X winged = 0; X for (;;) X { X meta(&c); X last = c; X switch (c.m_type) X { X default: X fatal("illegal m_type %d (bug %d)", c.m_type, __LINE__); X X case EOF: X illeof(); X X case NORMAL: X case SLOSHED: X if (!winged) X { X lex_error("the 'data' keyword must be the last on the line"); X winged = 1; X } X continue; X X case WHITE: X if (c.m_char != '\n') X continue; X state = 0; X break; X } X break; X } X catted = 1; X } X } X else X tok = WORD; X break; X } X break; X } X trace(("return %d;\n", tok)); X trace((/*{*/"}\n")); X return tok; X} X X X/* X * NAME X * hashline_lex - lexer for hashline.y X * X * SYNOPSIS X * int hashline_lex(void); X * X * DESCRIPTION X * The hashline_lex function is used to turn the tokens generated X * by tokenize into hashline.y tokens. X * X * RETURNS X * int - the token number X * X * CAVEAT X * Intended solely for use by the yacc-generated compiler X */ X Xint Xhashline_lex() X{ X int tok; X X trace(("hashline_lex()\n{\n"/*}*/)); X tok = tokenize(); X switch (tok) X { X case EOLN: X tok = 0; X break; X X case UNQUOTED_WORD: X if (id_search(parse_lval.lv_word, ID_CLASS_HASH_KEYWORD, &tok)) X { X str_free(parse_lval.lv_word); X parse_lval.lv_word = 0; X } X else X tok = WORD; X break; X } X memcpy(&hashline_lval, &parse_lval, sizeof(hashline_lval)); X trace(("return %d;\n", tok)); X trace((/*{*/"}\n")); X return tok; X} X X Xvoid Xlex_passing(n) X int n; X{ X passing = n; X} END_OF_FILE if test 26740 -ne `wc -c <'cook/lex.c'`; then echo shar: \"'cook/lex.c'\" unpacked with wrong size! fi # end of 'cook/lex.c' fi if test -f 'cook/os.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cook/os.c'\" else echo shar: Extracting \"'cook/os.c'\" \(24336 characters\) sed "s/^X//" >'cook/os.c' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1991, 1992, 1993 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 isolate operating system interface X */ X X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/times.h> X#include <sys/param.h> X X#include <stddef.h> X#include <string.h> X#include <stdlib.h> X#include <limits.h> X#include <errno.h> X#include <stdio.h> X#include <signal.h> X#include <unistd.h> X#include <utime.h> X X#include <conf.h> X#include <error.h> X#include <id.h> X#include <mem.h> X#include <option.h> X#include <os.h> X#include <trace.h> X#include <word.h> X X#ifdef BSD X#include <sys/time.h> X#include <sys/resource.h> X#endif X X X/* X * get rid if this real soon X */ X#ifndef PATH_MAX X# define PATH_MAX 1024 X#endif X#ifndef NAME_MAX X# ifdef BSD X# define NAME_MAX 255 X# else X# define NAME_MAX 14 X# endif X#endif X X X/* X * NAME X * stat_cache - stat() with caching X * X * SYNOPSIS X * int stat_cache(string_ty *path, struct stat *result); X * X * DESCRIPTION X * The stat_cache function is used to perform the same as the stat() X * system function, but the results are cached to avoid too many probes X * into the file system. Files which do not exist are indicated by X * filling the result structure with zeros. X * X * RETURNS X * int; -1 on error, 0 on success X * X * CAVEAT X * Errors, other than ENOENT, result in a fatal diagnostic. X */ X Xstatic int stat_cache _((string_ty *, struct stat *)); X Xstatic int Xstat_cache(path, result) X string_ty *path; X struct stat *result; X{ X if (id_search(path, ID_CLASS_STAT, result)) X return 0; X if (stat(path->str_text, result)) X { X switch (errno) X { X case ENOENT: X case ENOTDIR: X /* X * ENOENT occurs when a path element does not exist X * ENOTDIR occurs when a path element (except the last) X * is not a directory. X * Either way, the file being "stat"ed does not exist. X */ X break; X X default: X nerror("stat(\"%s\")", path->str_text); X option_set_errors(); X return -1; X } X memset(result, 0, sizeof(*result)); X } X else X { X if (result->st_mtime < 1) X result->st_mtime = 1; X } X id_assign(path, ID_CLASS_STAT, result); X if (option_test(OPTION_TRACE)) X { X if (!result->st_mtime) X { X error X ( X "mtime(\"%s\") == ENOENT (trace)", X path->str_text X ); X } X else X { X struct tm *tm; X X tm = localtime(&result->st_mtime); X error X ( X "mtime(\"%s\") == %4d/%02d/%02d.%02d:%02d:%02d (trace)", X path->str_text, X 1900 + tm->tm_year, X tm->tm_mon + 1, X tm->tm_mday, X tm->tm_hour, X tm->tm_min, X tm->tm_sec X ); X } X } X return 0; X} X X X/* X * NAME X * os_mtime - return the last-modified time of a file X * X * SYNOPSIS X * time_t os_mtime(string_ty *path); X * X * DESCRIPTION X * Os_mtime returns the time the named file was last modified. X * It returns -1L if the file does not exist. X * X * CAVEAT X * Assumes time will be the UNIX format. X */ X Xtime_t Xos_mtime(path) X string_ty *path; X{ X struct stat buf; X X if (stat_cache(path, &buf)) X return -1; X return buf.st_mtime; X} X X X X/* X * NAME X * os_mtime_adjust - indicate change X * X * SYNOPSIS X * int os_mtime_adjust(string_ty *path); X * X * DESCRIPTION X * The os_mtime_adjust function is used to adjust the value in the stat X * cache to indicate that a recipe has constructed a file, and thus X * changed it last-modified time. No change to the actual file system X * occurs. X * X * RETURNS X * int; -1 on error, 0 on success X */ X Xint Xos_mtime_adjust(path) X string_ty *path; X{ X struct stat buf; X struct tm *tm; X X if (stat_cache(path, &buf)) X return -1; X time(&buf.st_mtime); X if (buf.st_mtime < 1) X buf.st_mtime = 1; X id_assign(path, ID_CLASS_STAT, &buf); X if (option_test(OPTION_TRACE)) X { X tm = localtime(&buf.st_mtime); X error X ( X "mtime(\"%s\") = %4d/%02d/%02d.%02d:%02d:%02d (trace)", X path->str_text, X 1900 + tm->tm_year, X tm->tm_mon + 1, X tm->tm_mday, X tm->tm_hour, X tm->tm_min, X tm->tm_sec X ); X } X return 0; X} X X X/* X * NAME X * os_clear_stat - invalidate cache X * X * SYNOPSIS X * int os_clear_stat(string_ty *path); X * X * DESCRIPTION X * The os_clear_stat function is used to invalidate the the stat X * cache to indicate that a recipe has constructed a file, and thus X * changed it last-modified time. No change to the actual file system X * occurs. X * X * RETURNS X * int; 0 on success, -1 on error X * X * CAVEAT X * This is used in situations where the recipe changes a file not named X * in the targets of the recipe. This usually occurs around mkdir, rm X * and mv commands, used in conjunction with the [exists] builtin function. X */ X Xint Xos_clear_stat(path) X string_ty *path; X{ X id_delete(path, ID_CLASS_STAT); X return 0; X} X X X/* X * NAME X * os_touch - update the modify time of the file X * X * SYNOPSIS X * int os_touch(string_ty *path); X * X * DESCRIPTION X * Os_touch updates the last-modified time of the file to the present. X * If the named file does not exist, then nothing is done. X * X * RETURNS X * int; 0 on success, -1 on error X */ X Xint Xos_touch(path) X string_ty *path; X{ X struct stat buf; X struct utimbuf ut; X X if (stat_cache(path, &buf)) X return -1; X if (!buf.st_ino) X /* file does not exist */ X return 0; X time(&ut.modtime); X if (ut.modtime < 0) X ut.modtime = 0; X ut.actime = buf.st_atime; X if (utime(path->str_text, &ut)) X { X nerror("utime(\"%s\")", path->str_text); X option_set_errors(); X return -1; X } X buf.st_mtime = ut.modtime; X id_assign(path, ID_CLASS_STAT, &buf); X return 0; X} X X X/* X * NAME X * os_delete - delete a file X * X * SYNOPSIS X * int os_delete(string_ty *path); X * X * DESCRIPTION X * Os_delete deletes the named file. X * If it does not exist, no error is given. X * X * RETURNS X * int; -1 on error, 0 on success X */ X Xint Xos_delete(path) X string_ty *path; X{ X struct stat buf; X X if (unlink(path->str_text) && errno != ENOENT) X { X nerror("unlink(\"%s\")", path->str_text); X option_set_errors(); X return -1; X } X X /* X * if the knew about the existence of the file before we deleted X * it, then we will have to adjust the stat cache. X */ X if (stat_cache(path, &buf)) X return -1; X if (buf.st_ino) X { X memset(&buf, 0, sizeof(buf)); X id_assign(path, ID_CLASS_STAT, &buf); X } X return 0; X} X X X/* X * NAME X * signal_name - find it X * X * SYNOPSIS X * char *signal_name(int n); X * X * DESCRIPTION X * The signal_name function is used to find the name of a signal from its X * number. X * X * RETURNS X * char *: pointer to the signal name. X * X * CAVEAT X * The signal name may not be written on. Subsequent calls may alter the X * area pointed to. X */ X Xchar * Xsignal_name(n) X int n; X{ X static char buffer[16]; X X switch (n) X { X#ifdef SIGHUP X case SIGHUP: X return "hang up [SIGHUP]"; X#endif /* SIGHUP */ X X#ifdef SIGINT X case SIGINT: X return "user interrupt [SIGINT]"; X#endif /* SIGINT */ X X#ifdef SIGQUIT X case SIGQUIT: X return "user quit [SIGQUIT]"; X#endif /* SIGQUIT */ X X#ifdef SIGILL X case SIGILL: X return "illegal instruction [SIGILL]"; X#endif /* SIGILL */ X X#ifdef SIGTRAP X case SIGTRAP: X return "trace trap [SIGTRAP]"; X#endif /* SIGTRAP */ X X#ifdef SIGIOT X case SIGIOT: X return "abort [SIGIOT]"; X#endif /* SIGIOT */ X X#ifdef SIGEMT X case SIGEMT: X return "EMT instruction [SIGEMT]"; X#endif /* SIGEMT */ X X#ifdef SIGFPE X case SIGFPE: X return "floating point exception [SIGFPE]"; X#endif /* SIGFPE */ X X#ifdef SIGKILL X case SIGKILL: X return "kill [SIGKILL]"; X#endif /* SIGKILL */ X X#ifdef SIGBUS X case SIGBUS: X return "bus error [SIGBUS]"; X#endif /* SIGBUS */ X X#ifdef SIGSEGV X case SIGSEGV: X return "segmentation violation [SIGSEGV]"; X#endif /* SIGSEGV */ X X#ifdef SIGSYS X case SIGSYS: X return "bad argument to system call [SIGSYS]"; X#endif /* SIGSYS */ X X#ifdef SIGPIPE X case SIGPIPE: X return "write on a pipe with no one to read it [SIGPIPE]"; X#endif /* SIGPIPE */ X X#ifdef SIGALRM X case SIGALRM: X return "alarm clock [SIGALRM]"; X#endif /* SIGALRM */ X X#ifdef SIGTERM X case SIGTERM: X return "software termination [SIGTERM]"; X#endif /* SIGTERM */ X X#ifdef SIGUSR1 X case SIGUSR1: X return "user defined signal one [SIGUSR1]"; X#endif /* SIGUSR1 */ X X#ifdef SIGUSR2 X case SIGUSR2: X return "user defined signal two [SIGUSR2]"; X#endif /* SIGUSR2 */ X X#ifdef SIGCLD X case SIGCLD: X return "death of child [SIGCLD]"; X#endif /* SIGCLD */ X X#ifdef SIGPWR X case SIGPWR: X return "power failure [SIGPWR]"; X#endif /* SIGPWR */ X X default: X sprintf(buffer, "signal %d", n); X return buffer; X } X} X X X/* X * NAME X * exit_status - pretty print X * X * SYNOPSIS X * int exit_status(char *cmd, int status); X * X * DESCRIPTION X * The exit_status function is used to pretty print the meaning of the X * exit status of the given command. No output is generated for normal X * (0) termination. X * X * RETURNS X * int: zero if the command succeeded, 1 if it failed. X * X * CAVEAT X * This function should be static, but func_collect (builtin.c) uses it. X */ X Xint Xexit_status(cmd, status) X char *cmd; X int status; X{ X int a, b, c; X X a = (status >> 8) & 0xFF; X b = (status >> 7) & 1; X c = status & 0x7F; X switch (c) X { X case 0x7F: X /* X * process was stopped, X * since we didn't do it, treat it as an error X */ X error("%s: stopped", cmd); X return 1; X X case 0: X /* normal termination */ X if (a) X { X error("%s: exit status: %d", cmd, a); X return 1; X } X return 0; X X default: X /* X * process dies from unhandled condition X */ X error X ( X "%s: terminated by %s%s", X cmd, X signal_name(c), X (b ? " (core dumped)" : "") X ); X return 1; X } X} X X X/* X * NAME X * execute - do a command X * X * SYNOPSIS X * int execute(wlist *cmd, int fd); X * X * DESCRIPTION X * The execute function is used to execute the command in the word list. X * If the file descriptor is >= 0, it indicates a file to use as stdin to X * the command. X * X * RETURNS X * int: zero if the commands succeeds, nonzero if it fails. X * X * CAVEAT X */ X Xstatic int execute _((wlist *, int)); X Xstatic int Xexecute(cmd, fd) X wlist *cmd; X int fd; X{ X int child; X int pid; X int status; X static char **argv; X static size_t argvlen; X int j; X X if (!argv) X { X argvlen = cmd->wl_nwords + 1; X argv = (char **)mem_alloc(argvlen * sizeof(char*)); X } X else X { X if (argvlen < cmd->wl_nwords + 1) X { X argvlen = cmd->wl_nwords + 1; X mem_change_size((char **)&argv, argvlen * sizeof(char*)); X } X } X for (j = 0; j < cmd->wl_nwords; ++j) X argv[j] = cmd->wl_word[j]->str_text; X argv[cmd->wl_nwords] = 0; X switch (child = fork()) X { X case -1: X nerror("fork()"); X return -1; X X case 0: X if (fd >= 0) X { X if (close(0) && errno != EBADF) X nfatal("close(0)"); X if (dup(fd) < 0) X nfatal("dup()"); X } X if (argv[0][0] == '/') X execv(argv[0], argv); X else X execvp(argv[0], argv); X nfatal("exec(\"%s\")", argv[0]); X X default: X for (;;) X { X pid = wait(&status); X if (pid == child) X return exit_status(argv[0], status); X if (pid < 0 && errno != EINTR) X { X nerror("wait()"); X option_set_errors(); X return -1; X } X } X } X} X X X/* X * NAME X * os_execute - execute a command X * X * SYNOPSIS X * int os_execute(wlist *args, string_ty *input); X * X * DESCRIPTION X * Os_execute performs the given command. X * If the command succeeds 0 is returned. X * If the command fails a diagnostic message is given X * and 1 is returned. X */ X Xint Xos_execute(args, input) X wlist *args; X string_ty *input; X{ X int j; X FILE *fp; X char iname[L_tmpnam]; X int retval; X X assert(args); X assert(args->wl_nwords > 0); X X fp = 0; X if (input) X { X /* X * He has given a string to be used as input to the command, X * so write it out to a file, and then redirect the input. X */ X tmpnam(iname); X fp = fopen(iname, "w"); X if (!fp) X { X nerror("create %s", iname); X exec_fails: X option_set_errors(); X retval = -1; X goto ret; X } X if (unlink(iname)) X { X nerror("unlink(\"%s\")", iname); X goto exec_fails; X } X fputs(input->str_text, fp); X if (ferror(fp) || fseek(fp, 0L, 0)) X { X nerror("write %s", iname); X goto exec_fails; X } X } X X for (j = 1; j < args->wl_nwords; j++) X { X char *s; X X s = args->wl_word[j]->str_text; X while (*s) X { X if (strchr("#|=^();&<>*?[]:$`'\"\\\n", *s)) X { X string_ty *str; X wlist newcmd; X char *cp; X X cp = getenv("SHELL"); X if (!cp || !*cp) X cp = CONF_SHELL; X wl_zero(&newcmd); X str = str_from_c(cp); X wl_append(&newcmd, str); X str_free(str); X if (option_test(OPTION_ERROK)) X str = str_from_c("-c"); X else X str = str_from_c("-ce"); X wl_append(&newcmd, str); X str_free(str); X str = wl2str(args, 0, 32767); X wl_append(&newcmd, str); X str_free(str); X retval = execute(&newcmd, input ? fileno(fp) : -1); X wl_free(&newcmd); X if (input) X fclose(fp); X return retval; X } X ++s; X } X } X retval = execute(args, input ? fileno(fp) : -1); X if (input) X fclose(fp); Xret: X return retval; X} X X X/* X * NAME X * os_exists - tests for the existence of a file X * X * SYNOPSIS X * int os_exists(string_ty *path); X * X * DESCRIPTION X * The os_exists function is used to determine the existence of a file. X * X * RETURNS X * int; 1 if the file exists, 0 if it does not. -1 on error X */ X Xint Xos_exists(path) X string_ty *path; X{ X struct stat buf; X X if (stat_cache(path, &buf)) X return -1; X return (buf.st_ino != 0); X} X X X/* X * NAME X * os_accdir - return the directory path of the users account X * X * SYNOPSIS X * string_ty *os_accdir(void); X * X * DESCRIPTION X * The full pathname of the user's account is returned. X * The string will have been dynamically allocated. X * X * RETURNS X * A pointer to a string in dynamic memory is returned. X * A null pointer is returned on error. X * X * CAVEAT X * Use str_free() when you are done with the value returned. X */ X Xstring_ty * Xos_accdir() X{ X static char home[] = "HOME"; X static string_ty *s; X X if (!s) X { X char *cp; X X cp = getenv(home); X if (!cp) X { X error("%s: environment variable not defined", home); X option_set_errors(); X return 0; X } X s = str_from_c(cp); X } X return str_copy(s); X} X X X/* X * NAME X * os_curdir - full current directory path X * X * SYNOPSIS X * string_ty *os_curdir(void); X * X * DESCRIPTION X * Os_curdir is used to determine the full pathname X * of the current directory. X * X * RETURNS X * A pointer to a string in dynamic memory is returned. X * A null pointer is returned on error. X * X * CAVEAT X * Use str_free() when you are done with the value returned. X */ X Xstring_ty * Xos_curdir() X{ X static string_ty *s; X X if (!s) X { X char buffer[PATH_MAX + 1]; X X if (!getcwd(buffer, sizeof(buffer))) X { X nerror("getcwd"); X option_set_errors(); X return 0; X } X s = str_from_c(buffer); X } X return str_copy(s); X} X X X/* X * NAME X * os_pathname - determine full file name X * X * SYNOPSIS X * string_ty *os_pathname(string_ty *path); X * X * DESCRIPTION X * Os_pathname is used to determine the full path name X * of a partial path given. X * X * RETURNS X * pointer to dynamically allocated string. X * X * CAVEAT X * Use str_free() when you are done with the value returned. X */ X Xstring_ty * Xos_pathname(path) X string_ty *path; X{ X static char *tmp; X static size_t tmplen; X static size_t ipos; X static size_t opos; X int c; X int found; X#ifdef S_IFLNK X char link[2000]; X int nbytes; X wlist loop; X string_ty *s; X#endif X X /* X * Change relative pathnames to absolute X */ X trace(("os_pathname(path = %08lX)\n{\n"/*}*/, path)); X trace_string(path->str_text); X if (path->str_text[0] != '/') X { X string_ty *cwd; X X cwd = os_curdir(); X assert(cwd); X path = str_format("%S/%S", cwd, path); X str_free(cwd); X } X else X path = str_copy(path); X if (!tmp) X { X tmplen = 200; X tmp = mem_alloc(tmplen); X } X X /* X * Take kinks out of the pathname X */ X ipos = 0; X opos = 0; X found = 0; X#ifdef S_IFLNK X wl_zero(&loop); X#endif X while (!found) X { X /* X * get the next character X */ X c = path->str_text[ipos]; X if (c) X ipos++; X else X { X found = 1; X c = '/'; X } X X /* X * remember the normal characters X * until get to slash X */ X if (c != '/') X goto remember; X X /* X * leave root alone X */ X if (!opos) X goto remember; X X /* X * "/.." -> "/" X */ X if (opos == 3 && tmp[1] == '.' && tmp[2] == '.') X { X opos = 1; X continue; X } X X /* X * "a//" -> "a/" X */ X if (tmp[opos - 1] == '/') X continue; X X /* X * "a/./" -> "a/" X */ X if (opos >= 2 && tmp[opos - 1] == '.' && tmp[opos - 2] == '/') X { X opos--; X continue; X } X X /* X * "a/b/../" -> "a/" X */ X if X ( X opos > 3 X && X tmp[opos - 1] == '.' X && X tmp[opos - 2] == '.' X && X tmp[opos - 3] == '/' X ) X { X opos -= 4; X assert(opos > 0); X while (tmp[opos - 1] != '/') X opos--; X continue; X } X X /* X * see if the path so far is a symbolic link X */ X#ifdef S_IFLNK X s = str_n_from_c(tmp, opos); X if (wl_member(&loop, s)) X { X fatal X ( X "symbolic link loop \"%s\" detected", X s->str_text X ); X } X nbytes = readlink(s->str_text, link, sizeof(link) - 1); X if (nbytes < 0) X { X /* X * probably not a symbolic link X */ X if (errno != EINVAL && errno != ENOENT) X nfatal("readlink(\"%s\")", s->str_text); X not_a_symlink: X str_free(s); X } X else X { X string_ty *newpath; X X if (nbytes == 0) X { X fatal X ( X "readlink(\"%s\") returned \"\"", X s->str_text X ); X } X link[nbytes] = 0; X X /* X * ignore auto-mounter temporary mount links, X * they will change with each mount. X */ X if (!strncmp(link, "/tmp_mnt/", 9)) X goto not_a_symlink; X X wl_append(&loop, s); X str_free(s); X if (link[0] == '/') X tmp[1] = 0; X else X { X while (tmp[opos - 1] != '/') X opos--; X tmp[opos] = 0; X } X newpath = X str_format X ( X "%s/%s/%s", X tmp, X link, X path->str_text + ipos X ); X str_free(path); X path = newpath; X ipos = 0; X opos = 0; X found = 0; X continue; X } X#endif X X /* X * keep the slash X */ X remember: X if (opos >= tmplen) X { X tmplen += 100; X mem_change_size(&tmp, tmplen); X } X tmp[opos++] = c; X } X#ifdef S_IFLNK X wl_free(&loop); X#endif X str_free(path); X assert(opos >= 1); X assert(tmp[0] == '/'); X assert(tmp[opos - 1] == '/'); X if (opos >= 2) X opos--; X path = str_n_from_c(tmp, opos); X trace_string(path->str_text); X trace((/*{*/"}\n")); X return path; X} X X X/* X * NAME X * os_entryname - take path apart X * X * SYNOPSIS X * string_ty *os_entryname(string_ty *path); X * X * DESCRIPTION X * Os_entryname is used to extract the entry part X * from a pathname. X * X * RETURNS X * pointer to dynamically allocated string. X * X * CAVEAT X * Use str_free() when you are done with the return value. X */ X Xstring_ty * Xos_entryname(path) X string_ty *path; X{ X char *cp; X X trace(("os_entryname(path = %08lX) entry", path)); X trace_string(path->str_text); X cp = strrchr(path->str_text, '/'); X if (cp) X path = str_from_c(cp + 1); X else X path = str_copy(path); X trace_string(path->str_text); X trace(("return %08lX;\n", path)); X trace((/*{*/"}\n")); X return path; X} X X X/* X * NAME X * os_dirname - take path apart X * X * SYNOPSIS X * string_ty *os_dirname(string_ty *path); X * X * DESCRIPTION X * Os_dirname is used to extract the directory part X * of a pathname. X * X * RETURNS X * pointer to dynamically allocated string. X * A null pointer is returned on error. X * X * CAVEAT X * Use str_free() when you are done with the value returned. X */ X Xstring_ty * Xos_dirname(path) X string_ty *path; X{ X char *cp; X X trace(("os_dirname(path = %08lX)\n{\n"/*}*/, path)); X trace_string(path->str_text); X cp = strrchr(path->str_text, '/'); X if (cp) X { X if (cp > path->str_text) X path = str_n_from_c(path->str_text, cp - path->str_text); X else X path = str_from_c("/"); X } X else X path = os_curdir(); X trace_string(path->str_text); X trace(("return %08lX;\n", path)); X trace((/*{*/"}\n")); X return path; X} X X X/* X * NAME X * os_meter_grab - get metering snapshot X * X * SYNOPSIS X * void os_meter_grab(grab*); X * X * DESCRIPTION X * The os_meter_gram function is used to take a snapshot of metering X * information. X * X * RETURNS X * void X */ X Xtypedef struct grab grab; Xstruct grab X{ X double g_elapsed; X double g_cpu; X double g_io; X}; X Xstatic void os_meter_grab _((grab *)); X Xstatic void Xos_meter_grab(gp) X grab *gp; X{ X#ifdef BSD X struct rusage ru; X struct timeval tv; X struct timezone tz; X X int getrusage _((int, struct rusage *)); X int gettimeofday _((struct timeval *, struct timezone *)); X X if (getrusage(RUSAGE_CHILDREN, &ru)) X nfatal("getrusage"); X if (gettimeofday(&tv, &tz)) X nfatal("gettimeofday"); X gp->g_cpu = ru.ru_utime.tv_sec + ru.ru_utime.tv_usec * 1.0e-6; X gp->g_io = ru.ru_stime.tv_sec + ru.ru_stime.tv_usec * 1.0e-6; X gp->g_elapsed = tv.tv_sec + tv.tv_usec * 1.0e-6; X#else X struct tms buffer; X time_t n; X X n = times(&buffer); X gp->g_elapsed = n / (double)HZ; X gp->g_cpu = buffer.tms_cutime / (double)HZ; X gp->g_io = buffer.tms_cstime / (double)HZ; X#endif X} X X X/* X * NAME X * os_meter_ptime - print timing info X * X * SYNOPSIS X * void os_meter_ptime(double t, char *s, double elapsed); X * X * DESCRIPTION X * The os_meter_ptime function is used to print e representation of the X * time, in seconds, given in the `t' argument. If `elapsed' is >0, it is X * the elapsed time taken, for percentage figures. The `s' argument is a X * title string. X * X * RETURNS X * void X */ X Xstatic void os_meter_ptime _((double, char *, double)); X Xstatic void Xos_meter_ptime(t,s,e) X double t; X char *s; X double e; X{ X char buffer[32]; X char temp[32]; X long hour, min, sec, frac; X X frac = t * 1000 + 0.5; X sec = frac / 1000; X frac %= 1000; X min = sec / 60; X sec %= 60; X hour = min / 60; X min %= 60; X if (e > 0.0) X { X sprintf(temp, "(%.2f%%)", 100. * t / e); X sprintf(buffer, "%-4s%9s", s, temp); X s = buffer; X } X fprintf(stderr, "%2ld:%02ld:%02ld.%03ld %s\n", hour, min, sec, frac, s); X} X X X/* X * NAME X * os_meter_begin - start metering interval X * X * SYNOPSIS X * void os_meter_begin(void); X * X * DESCRIPTION X * The os_meter_begin function is used to start a metring interval. X * X * RETURNS X * void X */ X Xstatic grab os_meter_start; X Xvoid Xos_meter_begin() X{ X os_meter_grab(&os_meter_start); X} X X X/* X * NAME X * os_meter_end - end a metering interval X * X * SYNOPSIS X * void os_meter_end(void); X * X * DESCRIPTION X * The os_meter_end function is used to end a metering interval and print X * the metered information. X * X * RETURNS X * void X */ X Xvoid Xos_meter_end() X{ X grab end; X double elapsed; X double cpu; X double io; X X os_meter_grab(&end); X X elapsed = end.g_elapsed - os_meter_start.g_elapsed; X os_meter_ptime(elapsed, "elapsed", 0.0); X cpu = end.g_cpu - os_meter_start.g_cpu; X os_meter_ptime(cpu, "usr", elapsed); X io = end.g_io - os_meter_start.g_io; X os_meter_ptime(io, "sys", elapsed); X} X X X/* X * NAME X * os_legal_path - test if path is legal X * X * SYNOPSIS X * int os_legal_path(string_ty *path); X * X * DESCRIPTION X * The os_legal_path function is used to test if each of the components of X * the given path are legal to the underlying operating system. X * X * RETURNS X * int: zero if it is an illegal path, nonzero it is a legal path. X */ X Xint Xos_legal_path(str) X string_ty *str; X{ X char *s; X char *ep; X X if (str->str_length < 1 || str->str_length > PATH_MAX) X return 0; X s = str->str_text; X for (;;) X { X ep = strchr(s, '/'); X if (!ep) X return (strlen(s) <= NAME_MAX); X if (ep - s > NAME_MAX) X return 0; X s = ep + 1; X } X} X X X#ifdef TIMING X Xstatic long os_totals_baseline; X Xvoid Xos_totals_start() X{ X struct tms buffer; X X os_totals_baseline = times(&buffer); X} X Xvoid Xos_totals() X{ X struct tms buffer; X time_t n; X double elapsed; X double t; X X error("timing totals..."); X n = times(&buffer); X elapsed = (n - os_totals_baseline) / (double)HZ; X os_meter_ptime(elapsed, "elapsed", 0.0); X t = buffer.tms_utime / (double)HZ; X os_meter_ptime(t, "usr", elapsed); X t = buffer.tms_stime / (double)HZ; X os_meter_ptime(t, "sys", elapsed); X t = buffer.tms_cutime / (double)HZ; X os_meter_ptime(t, "cusr", elapsed); X t = buffer.tms_cstime / (double)HZ; X os_meter_ptime(t, "csys", elapsed); X} X X#endif END_OF_FILE if test 24336 -ne `wc -c <'cook/os.c'`; then echo shar: \"'cook/os.c'\" unpacked with wrong size! fi # end of 'cook/os.c' fi if test -f 'cooktime/date.y' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cooktime/date.y'\" else echo shar: Extracting \"'cooktime/date.y'\" \(27036 characters\) sed "s/^X//" >'cooktime/date.y' <<'END_OF_FILE' X/* X * cook - file construction tool X * Copyright (C) 1991, 1992, 1993 Peter Miller X * All rights reserved. X * Peter Miller <pmiller@bmr.gov.au> X * X * This code is derived from code which is X * Copyright (C) 1986 Steven M. Bellovin X * Steven Bellovin <smb@cs.unc.edu> X * X * MANIFEST: functions to parse dates X */ X X%token AGO X%token COLON X%token COMMA X%token DAY X%token DAYZONE X%token ID X%token JUNK X%token MERIDIAN X%token MONTH X%token MUNIT X%token NUMBER X%token SLASH X%token SUNIT X%token UNIT X%token ZONE X X%{ X X#include <stdio.h> X#include <stdlib.h> X#include <time.h> X#include <sys/timeb.h> X#include <ctype.h> X#include <time.h> X#include <string.h> X X#include <date.h> X#include <s-v-arg.h> X#include <trace.h> X X#define daysec (24L * 60L * 60L) X X#define AM 1 X#define PM 2 X X#define DAYLIGHT 1 X#define STANDARD 2 X#define MAYBE 3 X X#define MAX_ID_LENGTH 20 X Xstatic int timeflag; Xstatic int zoneflag; Xstatic int dateflag; Xstatic int dayflag; Xstatic int relflag; Xstatic time_t relsec; Xstatic time_t relmonth; Xstatic int hh; Xstatic int mm; Xstatic int ss; Xstatic int merid; Xstatic int day_light_flag; Xstatic int dayord; Xstatic int dayreq; Xstatic int month; Xstatic int day; Xstatic int year; Xstatic int ourzone; Xstatic char *lptr; Xextern int yylval; Xextern int yydebug; X X Xstatic int mdays[12] = X{ X 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 X}; X X#define epoch 1970 X Xint yyparse _((void)); /* forward */ X X X/* X * NAME X * table - list of known names X * X * SYNOPSIS X * table_t table[]; X * X * DESCRIPTION X * The table is used to hold the list of known names. X * This includes time zone names and days of the week, etc. X * X * CAVEAT X * It is in English. X * It is impossible to have a full list of time zones. X */ X Xtypedef struct table_t table_t; Xstruct table_t X{ X char *name; X int type; X int value; X}; X X#define HRMIN(a, b) ((a) * 60 + (b)) X Xstatic table_t table[] = X{ X { "a", ZONE, HRMIN(1, 0), }, X { "a.c.s.s.t.", DAYZONE, -HRMIN(9, 30), }, X { "a.c.s.t.", ZONE, -HRMIN(9, 30), }, X { "a.d.t.", DAYZONE, HRMIN(4, 0), }, X { "a.e.s.s.t.", DAYZONE, -HRMIN(10, 0), }, X { "a.e.s.t.", ZONE, -HRMIN(10, 0), }, X { "a.m.", MERIDIAN, AM, }, X { "a.s.t.", ZONE, HRMIN(4, 0), }, X { "a.w.s.t.", ZONE, -HRMIN(8, 0), }, /* (no daylight time there, I'm told */ X { "acsst", DAYZONE, -HRMIN(9, 30), }, /* Australian Central Summer */ X { "acst", ZONE, -HRMIN(9, 30), }, /* Australian Central Time */ X { "adt", DAYZONE, HRMIN(4, 0), }, X { "aesst", DAYZONE, -HRMIN(10, 0), }, /* Australian Eastern Summer Time */ X { "aest", ZONE, -HRMIN(10, 0), }, /* Australian Eastern Time */ X { "ago", AGO, 1, }, X { "am", MERIDIAN, AM, }, X { "apr", MONTH, 4, }, X { "apr.", MONTH, 4, }, X { "april", MONTH, 4, }, X { "ast", ZONE, HRMIN(4, 0), }, /* Atlantic */ X { "aug", MONTH, 8, }, X { "aug.", MONTH, 8, }, X { "august", MONTH, 8, }, X { "awst", ZONE, -HRMIN(8, 0), }, /* Australian Western Time */ X { "b", ZONE, HRMIN(2, 0), }, X { "b.s.t.", DAYZONE, HRMIN(0, 0), }, X { "bst", DAYZONE, HRMIN(0, 0), }, /* British Summer Time */ X { "c", ZONE, HRMIN(3, 0), }, X { "c.d.t.", DAYZONE, HRMIN(6, 0), }, X { "c.s.t.", ZONE, HRMIN(6, 0), }, X { "cdt", DAYZONE, HRMIN(6, 0), }, X { "cst", ZONE, HRMIN(6, 0), }, /* Central */ X { "d", ZONE, HRMIN(4, 0), }, X { "day", UNIT, 1 * 24 * 60, }, X { "days", UNIT, 1 * 24 * 60, }, X { "dec", MONTH, 12, }, X { "dec.", MONTH, 12, }, X { "december", MONTH, 12, }, X { "e", ZONE, HRMIN(5, 0), }, X { "e.d.t.", DAYZONE, HRMIN(5, 0), }, X { "e.e.s.t.", DAYZONE, HRMIN(0, 0), }, X { "e.e.t.", ZONE, HRMIN(0, 0), }, X { "e.s.t.", ZONE, HRMIN(5, 0), }, X { "edt", DAYZONE, HRMIN(5, 0), }, X { "eest", DAYZONE, HRMIN(0, 0), }, /* European Eastern Summer Time */ X { "eet", ZONE, HRMIN(0, 0), }, /* European Eastern Time */ X { "eigth", NUMBER, 8, }, X { "eleventh", NUMBER, 11, }, X { "est", ZONE, HRMIN(5, 0), }, /* Eastern */ X { "f", ZONE, HRMIN(6, 0), }, X { "feb", MONTH, 2, }, X { "feb.", MONTH, 2, }, X { "february", MONTH, 2, }, X { "fifth", NUMBER, 5, }, X { "first", NUMBER, 1, }, X { "fortnight", UNIT, 14 * 24 * 60, }, X { "fortnights", UNIT, 14 * 24 * 60, }, X { "fourth", NUMBER, 4, }, X { "fri", DAY, 5, }, X { "fri.", DAY, 5, }, X { "friday", DAY, 5, }, X { "g", ZONE, HRMIN(7, 0), }, X { "g.m.t.", ZONE, HRMIN(0, 0), }, X { "gmt", ZONE, HRMIN(0, 0), }, X { "h", ZONE, HRMIN(8, 0), }, X { "h.d.t.", DAYZONE, HRMIN(10, 0), }, X { "h.s.t.", ZONE, HRMIN(10, 0), }, X { "hdt", DAYZONE, HRMIN(10, 0), }, X { "hour", UNIT, 60, }, X { "hours", UNIT, 60, }, X { "hr", UNIT, 60, }, X { "hrs", UNIT, 60, }, X { "hst", ZONE, HRMIN(10, 0), }, /* Hawaii */ X { "i", ZONE, HRMIN(9, 0), }, X { "j.s.t.", ZONE, -HRMIN(9, 0), }, /* Japan Standard Time */ X { "jan", MONTH, 1, }, X { "jan.", MONTH, 1, }, X { "january", MONTH, 1, }, X { "jst", ZONE, -HRMIN(9, 0), }, /* Japan Standard Time */ X { "jul", MONTH, 7, }, X { "jul.", MONTH, 7, }, X { "july", MONTH, 7, }, X { "jun", MONTH, 6, }, X { "jun.", MONTH, 6, }, X { "june", MONTH, 6, }, X { "k", ZONE, HRMIN(10, 0), }, X { "l", ZONE, HRMIN(11, 0), }, X { "last", NUMBER, -1, }, X { "m", ZONE, HRMIN(12, 0), }, X { "m.d.t.", DAYZONE, HRMIN(7, 0), }, X { "m.e.s.t.", DAYZONE, -HRMIN(1, 0), }, X { "m.e.t.", ZONE, -HRMIN(1, 0), }, X { "m.s.t.", ZONE, HRMIN(7, 0), }, X { "mar", MONTH, 3, }, X { "mar.", MONTH, 3, }, X { "march", MONTH, 3, }, X { "may", MONTH, 5, }, X { "mdt", DAYZONE, HRMIN(7, 0), }, X { "mest", DAYZONE, -HRMIN(1, 0), }, /* Middle European Summer Time */ X { "met", ZONE, -HRMIN(1, 0), }, /* Middle European Time */ X { "min", UNIT, 1, }, X { "mins", UNIT, 1, }, X { "minute", UNIT, 1, }, X { "minutes", UNIT, 1, }, X { "mon", DAY, 1, }, X { "mon.", DAY, 1, }, X { "monday", DAY, 1, }, X { "month", MUNIT, 1, }, X { "months", MUNIT, 1, }, X { "mst", ZONE, HRMIN(7, 0), }, /* Mountain */ X { "n", ZONE, -HRMIN(1, 0), }, X { "n.s.t.", ZONE, HRMIN(3, 30), }, X { "next", NUMBER, 2, }, X { "ninth", NUMBER, 9, }, X { "nov", MONTH, 11, }, X { "nov.", MONTH, 11, }, X { "november", MONTH, 11, }, X { "now", UNIT, 0, }, X { "nst", ZONE, HRMIN(3, 30), }, /* Newfoundland */ X { "o", ZONE, -HRMIN(2, 0), }, X { "oct", MONTH, 10, }, X { "oct.", MONTH, 10, }, X { "october", MONTH, 10, }, X { "p", ZONE, -HRMIN(3, 0), }, X { "p.d.t.", DAYZONE, HRMIN(8, 0), }, X { "p.m.", MERIDIAN, PM, }, X { "p.s.t.", ZONE, HRMIN(8, 0), }, X { "pdt", DAYZONE, HRMIN(8, 0), }, X { "pm", MERIDIAN, PM, }, X { "pst", ZONE, HRMIN(8, 0), }, /* Pacific */ X { "q", ZONE, -HRMIN(4, 0), }, X { "r", ZONE, -HRMIN(5, 0), }, X { "s", ZONE, -HRMIN(6, 0), }, X { "sat", DAY, 6, }, X { "sat.", DAY, 6, }, X { "saturday", DAY, 6, }, X { "sec", SUNIT, 1, }, X { "second", SUNIT, 1, }, X { "seconds", SUNIT, 1, }, X { "secs", SUNIT, 1, }, X { "sep", MONTH, 9, }, X { "sep.", MONTH, 9, }, X { "sept", MONTH, 9, }, X { "sept.", MONTH, 9, }, X { "september", MONTH, 9, }, X { "seventh", NUMBER, 7, }, X { "sixth", NUMBER, 6, }, X { "sun", DAY, 0, }, X { "sun.", DAY, 0, }, X { "sunday", DAY, 0, }, X { "t", ZONE, -HRMIN(7, 0), }, X { "tenth", NUMBER, 10, }, X { "third", NUMBER, 3, }, X { "this", UNIT, 0, }, X { "thu", DAY, 4, }, X { "thu.", DAY, 4, }, X { "thur", DAY, 4, }, X { "thur.", DAY, 4, }, X { "thurs", DAY, 4, }, X { "thurs.", DAY, 4, }, X { "thursday", DAY, 4, }, X { "today", UNIT, 0, }, X { "tomorrow", UNIT, 1 * 24 * 60, }, X { "tue", DAY, 2, }, X { "tue.", DAY, 2, }, X { "tues", DAY, 2, }, X { "tues.", DAY, 2, }, X { "tuesday", DAY, 2, }, X { "twelfth", NUMBER, 12, }, X { "u", ZONE, -HRMIN(8, 0), }, X { "u.t.", ZONE, HRMIN(0, 0), }, X { "ut", ZONE, HRMIN(0, 0), }, X { "v", ZONE, -HRMIN(9, 0), }, X { "w", ZONE, -HRMIN(10, 0), }, X { "w.e.s.t.", DAYZONE, -HRMIN(2, 0), }, X { "w.e.t.", ZONE, -HRMIN(2, 0), }, X { "wed", DAY, 3, }, X { "wed.", DAY, 3, }, X { "wednes", DAY, 3, }, X { "wednes.", DAY, 3, }, X { "wednesday", DAY, 3, }, X { "week", UNIT, 7 * 24 * 60, }, X { "weeks", UNIT, 7 * 24 * 60, }, X { "west", DAYZONE, -HRMIN(2, 0), }, /* Western European Summer Time */ X { "wet", ZONE, -HRMIN(2, 0), }, /* Western European Time */ X { "x", ZONE, -HRMIN(11, 0), }, X { "y", ZONE, -HRMIN(12, 0), }, X { "y.d.t.", DAYZONE, HRMIN(9, 0), }, X { "y.s.t.", ZONE, HRMIN(9, 0), }, X { "ydt", DAYZONE, HRMIN(9, 0), }, X { "year", MUNIT, 12, }, X { "years", MUNIT, 12, }, X { "yesterday", UNIT, -1*24*60, }, X { "yst", ZONE, HRMIN(9, 0), }, /* Yukon */ X { "z", ZONE, HRMIN(0, 0), }, X}; X X X/* X * NAME X * timeconv - convert a time X * X * SYNOPSIS X * time_t timeconv(int hh, int mm, int ss, int mer); X * X * DESCRIPTION X * The timeconv function is used to convert a time X * specified in hours minutes and seconds, into seconds past midnight. X * X * ARGUMENTS X * hh hours, range depends on the meridian X * mm minutes, 0..59 X * ss seconds, 0..59 X * mer meridian to use: AM, PM or 24 X * X * RETURNS X * time_t; seconds past midnight; -1 on any error. X */ X Xstatic time_t timeconv _((int hh, int mm, int ss, int mer)); X Xstatic time_t Xtimeconv(hh, mm, ss, mer) X int hh; X int mm; X int ss; X int mer; X{ X time_t result; X X /* X * perform sanity checks on input X */ X trace(("timeconv(hh = %d, mm = %d, ss = %d, mer = %d)\n{\n"/*}*/, hh, mm, ss, mer)); X result = -1; X if (mm < 0 || mm > 59 || ss < 0 || ss > 59) X goto done; X X /* X * perform range checks depending on the meridian X */ X switch (mer) X { X case AM: X if (hh < 1 || hh > 12) X goto done; X if (hh == 12) X hh = 0; X break; X X case PM: X if (hh < 1 || hh > 12) X goto done; X if (hh == 12) X hh = 0; X hh += 12; X break; X X case 24: X if (hh < 0 || hh > 23) X goto done; X break; X X default: X goto done; X } X result = ((hh * 60L + mm) * 60L + ss); Xdone: X trace(("return %ld;\n", (long)result)); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * dateconv - convert a date X * X * SYNOPSIS X * time_t dateconv(int mm, int dd, int year, int h, int m, int s, X * int mer, int zone, int dayflag); X * X * DESCRIPTION X * The dateconv function may be used to convert a date after the X * date string has been taken apart by yyparse. X * X * ARGUMENTS X * mm month number, in the range 1..12 X * year year number, in several ranges: X * 0..37 means 2000..2037 X * 70..99 means 1970..1999 X * 1970..2037 mean themselves. X * dd day of month, in the range 1..max, where max varies for X * each month, as per the catchy jingle (except February, X * which is a monster). X * h hours since midnight or meridian X * m minutes past hour X * s seconds past minute X * mer meridian, AM or PM. X * zone minutes correction for the time zone. X * dayflag whether to use daylight savings: STANDARD, DAYLIGHT or MAYBE. X * X * RETURNS X * time_t; the time in seconds past Jan 1 0:00:00 1970 GMT, this will X * always be positive or zero; -1 is returned for any error. X * X * CAVEAT X * The date functions only work between 1970 and 2037, X * because 0 is Jan 1 00:00:00 1970 GMT X * and (2^31-1) is Jan 19 03:14:07 2038 GMT X * hence some if the weir magic number below. X * X * Because -1 is used to represent errors, times before noon Jan 1 1970 X * in places east of GMT can't always be represented. X */ X Xstatic time_t dateconv _((int mm, int dd, int year, int h, int m, int s, X int mer, int zone, int dayflag)); X Xstatic time_t Xdateconv(mm, dd, year, h, m, s, mer, zone, dayflag) X int mm; X int dd; X int year; X int h; X int m; X int s; X int mer; X int zone; X int dayflag; X{ X time_t result; X time_t tod; X time_t jdate; X int i; X X /* X * make corrections for the year X * X * If it is 0..99, RFC822 says pick closest century. X */ X trace(("dateconv(mm = %d, dd = %d, year = %d, h = %d, m = %d, s = %d, mer = %d, zone = %d, dayflag = %d)\n{\n"/*}*/, mm, dd, year, h, m, s, mer, zone, dayflag)); X result = -1; X if (year < 0) X year = -year; X if (year < 38) X year += 2000; X else if (year < 100) X year += 1900; X X /* X * correct February length once we know the year X */ X mdays[1] = 28 + (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); X X /* X * perform some sanity checks on the input X */ X if X ( X year < epoch X || X year >= 2038 X || X mm < 1 X || X mm > 12 X || X dd < 1 X || X dd > mdays[--mm] X ) X goto done; X X /* X * Determine the julian day number * of the dd-mm-yy given. X * Turn it into seconds, and add in the time zone correction. X */ X jdate = dd - 1; X for (i = 0; i < mm; i++) X jdate += mdays[i]; X for (i = epoch; i < year; i++) X jdate += 365 + (i % 4 == 0); X jdate *= daysec; X jdate += zone * 60L; X X /* X * Determine the time of day. X * that is, seconds from midnight. X * Add it into the julian date. X */ X tod = timeconv(h, m, s, mer); X if (tod < 0) X goto done; X jdate += tod; X X /* X * Perform daylight savings correction if necessary. X * (This assumes 1 hour daylite savings, which is probably wrong.) X */ X if X ( X dayflag == DAYLIGHT X || X (dayflag == MAYBE && localtime(&jdate)->tm_isdst) X ) X jdate += -1 * 60 * 60; X X /* X * there you have it. X */ X result = jdate; Xdone: X trace(("return %ld;\n", (long)result)); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * daylcorr X * X * SYNOPSIS X * time_t daylcorr(time_t future, time_t now); X * X * DESCRIPTION X * The daylcorr function is used to determine the difference in seconds X * between two times, taking daylight savings into account. X * X * ARGUMENTS X * future - a later time X * now - an earlier time X * X * RETURNS X * time_t; the difference in seconds X * X * CAVEAT X * Assumes daylight savings is alays an integral number of hours. X * This is wrong is Saudi Arabia (time zone changes during the day), X * and South Australia (half hour DLS). X */ X Xstatic time_t daylcorr _((time_t future, time_t now)); X Xstatic time_t Xdaylcorr(future, now) X time_t future; X time_t now; X{ X int fdayl; X int nowdayl; X time_t result; X X trace(("daylcorr(future = %ld, now = %ld)\n{\n"/*}*/, (long)future, (long)now)); X nowdayl = (localtime(&now)->tm_hour + 1) % 24; X fdayl = (localtime(&future)->tm_hour + 1) % 24; X result = ((future - now) + 60L * 60L * (nowdayl - fdayl)); X trace(("return %ld;\n", (long)result)); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * dayconv X * X * SYNOPSIS X * time_t dayconv(int ord, int day, time_t now); X * X * DESCRIPTION X * The dayconv function is used to convert a day-of-the-week into X * a meaningful time. X * X * ARGUMENTS X * ord - the ord'th day from now X * day - which day of the week X * now - relative to this X * X * RETURNS X * time_t; time in seconds from epoch X */ X Xstatic time_t dayconv _((int ord, int day, time_t now)); X Xstatic time_t Xdayconv(ord, day, now) X int ord; X int day; X time_t now; X{ X time_t tod; X time_t result; X X trace(("dayconv(ord = %d, day = %d, now = %ld)\n{\n"/*}*/, ord, day, (long)now)); X tod = now; X tod += daysec * ((day - localtime(&tod)->tm_wday + 7) % 7); X tod += 7 * daysec * (ord <= 0 ? ord : ord - 1); X result = daylcorr(tod, now); X trace(("return %ld;\n", (long)result)); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * monthadd X * X * SYNOPSIS X * time_t monthadd(time_t sdate, time_t relmonth); X * X * DESCRIPTION X * The monthadd function is used to add a given number of X * months to a specified time. X * X * ARGUMENTS X * sdate - add the months to this X * relmonth - add this many months X * X * RETURNS X * time_t; seconds since the epoch X */ X Xstatic time_t monthadd _((time_t sdate, time_t relmonth)); X Xstatic time_t Xmonthadd(sdate, relmonth) X time_t sdate; X time_t relmonth; X{ X struct tm *ltime; X int mm; X int year; X time_t result; X X trace(("monthadd(sdate = %ld, relmonth = %ld)\n{\n"/*}*/, (long)sdate, (long)relmonth)); X if (relmonth == 0) X result = 0; X else X { X ltime = localtime(&sdate); X mm = 12 * ltime->tm_year + ltime->tm_mon + relmonth; X year = mm / 12; X mm = mm % 12 + 1; X result = X dateconv X ( X mm, X ltime->tm_mday, X year, X ltime->tm_hour, X ltime->tm_min, X ltime->tm_sec, X 24, X ourzone, X MAYBE X ); X if (result >= 0) X result = daylcorr(result, sdate); X } X trace(("return %ld;\n", (long)result)); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * lookup - find name X * X * SYNOPSIS X * int lookup(char *id); X * X * DESCRIPTION X * The lookup function is used to find a token corresponding to X * a given name. X * X * ARGUMENTS X * id - name to search for. Assumes already downcased. X * X * RETURNS X * int; yacc token, ID if not found. X */ X Xstatic int lookup _((char *id)); X Xstatic int Xlookup(id) X char *id; X{ X table_t *tp; X int min; X int max; X int mid; X int cmp; X int result; X X /* X * binary chop the table X */ X trace(("lookup(id = \"%s\")\n{\n"/*}*/, id)); X result = ID; X min = 0; X max = SIZEOF(table) - 1; X while (min <= max) X { X mid = (min + max) / 2; X tp = table + mid; X cmp = strcmp(id, tp->name); X if (!cmp) X { X yylval = tp->value; X result = tp->type; X break; X } X if (cmp < 0) X max = mid - 1; X else X min = mid + 1; X } X trace(("return %d;\n", result)); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * yylex - lexical analyser X * X * SYNOPSIS X * int yylex(void); X * X * DESCRIPTION X * The yylex function is used to scan the input string X * and break it into discrete tokens. X * X * RETURNS X * int; the yacc token, 0 means the-end. X */ X Xstatic int yylex _((void)); X Xstatic int Xyylex() X{ X int sign; X int c; X char *p; X char idbuf[MAX_ID_LENGTH]; X int pcnt; X int token; X X trace(("yylex()\n{\n"/*}*/)); X yylval = 0; X for (;;) X { X /* X * get the next input character X */ X c = *lptr++; X X /* X * action depends on the character X */ X switch (c) X { X case 0: X token = 0; X lptr--; X break; X X case ' ': X case '\t': X /* X * ignore white space X */ X continue; X X case ':': X token = COLON; X break; X X case ',': X token = COMMA; X break; X X case '/': X token = SLASH; X break; X X case '-': X if (!isdigit(*lptr)) X { X /* X * ignore lonely '-'s X */ X continue; X } X sign = -1; X c = *lptr++; X goto number; X X case '+': X if (!isdigit(*lptr)) X { X token = c; X break; X } X sign = 1; X c = *lptr++; X goto number; X X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X /* X * numbers X */ X sign = 1; X number: X for (;;) X { X yylval = yylval * 10 + c - '0'; X c = *lptr++; X switch (c) 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 continue; X } X break; X } X yylval *= sign; X lptr--; X token = NUMBER; 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': case 'z': 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': case 'Z': X /* X * name X */ X p = idbuf; X for (;;) X { X if (isupper(c)) X c = tolower(c); X if (p < idbuf + sizeof(idbuf) - 1) X *p++ = c; X c = *lptr++; X switch (c) X { X case 'a': case 'b': case 'c': case 'd': X case 'e': case 'f': case 'g': case 'h': X case 'i': case 'j': case 'k': case 'l': X case 'm': case 'n': case 'o': case 'p': X case 'q': case 'r': case 's': case 't': X case 'u': case 'v': case 'w': case 'x': X case 'y': case 'z': X case 'A': case 'B': case 'C': case 'D': X case 'E': case 'F': case 'G': case 'H': X case 'I': case 'J': case 'K': case 'L': X case 'M': case 'N': case 'O': case 'P': X case 'Q': case 'R': case 'S': case 'T': X case 'U': case 'V': case 'W': case 'X': X case 'Y': case 'Z': X case '.': X continue; X } X break; X } X *p = 0; X lptr--; X token = lookup(idbuf); X break; X X case '('/*)*/: X /* X * comment X */ X for (pcnt = 1; pcnt > 0; ) X { X c = *lptr++; X switch (c) X { X case 0: X --lptr; X pcnt = 0; X break; X X case '('/*)*/: X pcnt++; X break; X X case /*(*/')': X pcnt--; X break; X } X } X continue; X X default: X /* X * unrecognosed X */ X token = JUNK; X break; X } X break; X } X trace(("yylval = %d;\n", yylval)); X trace(("return %d;\n", token)); X trace((/*{*/"}\n")); X return token; X} X X X/* X * NAME X * date_scan X * X * SYNOPSIS X * time_t date_scan(char *s); X * X * DESCRIPTION X * The date_scan function is used to scan a string and X * return a number of seconds since epoch. X * X * ARGUMENTS X * s - string to scan X * X * RETURNS X * time_t; seconds to epoch, -1 on error. X * X * CAVEAT X * it isn't psychic X */ X Xtime_t Xdate_scan(p) X char *p; X{ X struct timeb *now; X struct tm *lt; X struct timeb ftz; X time_t result; X time_t tod; X X /* X * find time zone info, if not given X */ X trace(("date_scan(p = \"%s\")\n{\n"/*}*/, p)); X lptr = p; X now = &ftz; X ftime(&ftz); X X /* X * initialize things X */ X lt = localtime(&now->time); X year = lt->tm_year; X month = lt->tm_mon + 1; X day = lt->tm_mday; X relsec = 0; X relmonth = 0; X timeflag = 0; X zoneflag = 0; X dateflag = 0; X dayflag = 0; X relflag = 0; X#ifdef CONF_pyramid_broken_ftime X ourzone = now->timezone / 60; X#else X ourzone = now->timezone; X#endif X day_light_flag = MAYBE; X hh = 0; X mm = 0; X ss = 0; X merid = 24; X X /* X * parse the string X */ X#ifdef DEBUG X yydebug = trace_pretest_; X#endif X trace(("yyparse()\n{\n"/*}*/)); X result = yyparse(); X trace((/*{*/"}\n")); X if (result) X { X result = -1; X goto done; X } X X /* X * sanity checks X */ X result = -1; X if (timeflag > 1 || zoneflag > 1 || dateflag > 1 || dayflag > 1) X goto done; X X if (dateflag || timeflag || dayflag) X { X result = X dateconv X ( X month, X day, X year, X hh, X mm, X ss, X merid, X ourzone, X day_light_flag X ); X if (result < 0) X goto done; X } X else X { X result = now->time; X if (!relflag) X { X result -= X ( X (lt->tm_hour * 60L + lt->tm_min * 60) X + X lt->tm_sec X ); X } X } X X result += relsec; X relsec = monthadd(result, relmonth); X if (relsec < 0) X { X result = -1; X goto done; X } X result += relsec; X X if (dayflag && !dateflag) X { X tod = dayconv(dayord, dayreq, result); X result += tod; X } X X /* X * here for all exits X */ Xdone: X trace(("return %ld;\n", (long)result)); X trace((/*{*/"}\n")); X return result; X} X X X/* X * NAME X * date_string - build one X * X * SYNOPSIS X * char *date_string(time_t when); X * X * DESCRIPTION X * The date_string function may be used to construct a X * string from a given time in seconds. X * X * The string will conform to the RFC822 standard, X * which states a definite preference for GMT dates. X * X * ARGUMENTS X * when the time to be rendered. X * X * RETURNS X * Pointer to string containing rendered time. X * The contents of this string will remain undisturbed X * only until the next call to date_string. X */ X Xchar * Xdate_string(when) X time_t when; X{ X struct tm *tm; X static char buffer[32]; X X static char *weekday_name[] = X { X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", X }; X X static char *month_name[] = X { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", X }; X X /* X * break the given time down into components X * (RFC1036 likes GMT, remember) X */ X trace(("date_string(when = %ld)\n{\n"/*}*/, (long)when)); X tm = gmtime(&when); X X /* X * build the date string X */ X sprintf X ( X buffer, X "%s,%3d %s %2.2d %2.2d:%2.2d:%2.2d GMT", X weekday_name[tm->tm_wday], X tm->tm_mday, X month_name[tm->tm_mon], X tm->tm_year % 100, X tm->tm_hour, X tm->tm_min, X tm->tm_sec X ); X trace(("return \"%s\";\n", buffer)); X trace((/*{*/"}\n")); X return buffer; X} X X X/* X * NAME X * yyerror X * X * SYNOPSIS X * void yyerror(char *); X * X * DESCRIPTION X * The yyerror function is invoked by yacc to report X * errors, but we just throw it away. X * X * ARGUMENTS X * s - error to report X */ X Xstatic void yyerror _((char *)); X Xstatic void Xyyerror(s) X char *s; X{ X trace(("yyerror(s = \"%s\")\n{\n"/*}*/, s)); X trace((/*{*/"}\n")); X} X X X/* X * NAME X * yytrace - follow parser actions X * X * SYNOPSIS X * void yytrace(char *, ...); X * X * DESCRIPTION X * The yytrace function is used to print the various shifts X * and reductions, etc, done by the yacc-generated parser. X * lines are accumulated and printed whole, X * so as to avoid confusing the trace output. X * X * ARGUMENTS X * as for printf X * X * CAVEAT X * only available when DEBUG is defined X */ X X#ifdef DEBUG X#define YYDEBUG 1 X X#define printf yytrace X Xstatic void yytrace _((char *, ...)); X Xstatic void Xyytrace(s sva_last) X char *s; X sva_last_decl X{ X va_list ap; X static char line[1024]; X char buffer[512]; X char *cp; X X sva_init(ap, s); X vsprintf(buffer, s, ap); X va_end(ap); X strcat(line, buffer); X cp = line + strlen(line) - 1; X if (cp > line && *cp == '\n') X { X *cp = 0; X trace_printf("%s\n", line); X line[0] = 0; X } X} X X#endif /* DEBUG */ X X%} X X%% X Xtimedate X : /* empty */ X | timedate item X ; X Xitem X : TimeSpecification X { timeflag++; } X | TimeZone X { zoneflag++; } X | DateSpecification X { dateflag++; } X | DayOfWeekSpecification X { dayflag++; } X | RelativeSpecification X { relflag++; } X | NumberSpecification X ; X XNumberSpecification X : NUMBER X { X if (timeflag && dateflag && !relflag) X year = $1; X else X { X timeflag++; X hh = $1 / 100; X mm = $1 % 100; X ss = 0; X merid = 24; X } X } X ; X XTimeSpecification X : NUMBER MERIDIAN X { X hh = $1; X mm = 0; X ss = 0; X merid = $2; X } X | NUMBER COLON NUMBER X { X hh = $1; X mm = $3; X merid = 24; X } X | NUMBER COLON NUMBER MERIDIAN X { X hh = $1; X mm = $3; X merid = $4; X } X | NUMBER COLON NUMBER NUMBER X { X hh = $1; X mm = $3; X merid = 24; X day_light_flag = STANDARD; X $4 = -$4; X ourzone = $4 % 100 + 60 * $4 / 100; X } X | NUMBER COLON NUMBER COLON NUMBER X { X hh = $1; X mm = $3; X ss = $5; X merid = 24; X } X | NUMBER COLON NUMBER COLON NUMBER MERIDIAN X { X hh = $1; X mm = $3; X ss = $5; X merid = $6; X } X | NUMBER COLON NUMBER COLON NUMBER NUMBER X { X hh = $1; X mm = $3; X ss = $5; X merid = 24; X day_light_flag = STANDARD; X $6 = -$6; X ourzone = $6 % 100 + 60 * $6 / 100; X } X ; X XTimeZone X : ZONE X { X ourzone = $1; X day_light_flag = STANDARD; X } X | DAYZONE X { X ourzone = $1; X day_light_flag = DAYLIGHT; X } X ; X XDayOfWeekSpecification X : DAY X { X dayord = 1; X dayreq = $1; X } X | DAY COMMA X { X dayord = 1; X dayreq = $1; X } X | NUMBER DAY X { X dayord = $1; X dayreq = $2; X } X ; X XDateSpecification X : NUMBER SLASH NUMBER X { X month = $1; X day = $3; X } X | NUMBER SLASH NUMBER SLASH NUMBER X { X month = $1; X day = $3; X year = $5; X } X | MONTH NUMBER X { X month = $1; X day = $2; X } X | MONTH NUMBER COMMA NUMBER X { X month = $1; X day = $2; X year = $4; X } X | NUMBER MONTH X { X month = $2; X day = $1; X } X | NUMBER MONTH NUMBER X { X month = $2; X day = $1; X year = $3; X } X ; X XRelativeSpecification X : NUMBER UNIT X { relsec += 60L * $1 * $2; } X | NUMBER MUNIT X { relmonth += $1 * $2; } X | NUMBER SUNIT X { relsec += $1; } X | UNIT X { relsec += 60L * $1; } X | MUNIT X { relmonth += $1; } X | SUNIT X { relsec++; } X | RelativeSpecification AGO X { X relsec = -relsec; X relmonth = -relmonth; X } X ; END_OF_FILE if test 27036 -ne `wc -c <'cooktime/date.y'`; then echo shar: \"'cooktime/date.y'\" unpacked with wrong size! fi # end of 'cooktime/date.y' fi echo shar: End of archive 9 \(of 11\). cp /dev/null ark9isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 11 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0