From bacchus.pa.dec.com!decwrl!elroy.jpl.nasa.gov!usc!cs.utexas.edu!uunet!allbery Sun Jun 24 17:37:02 PDT 1990 Article 1664 of comp.sources.misc: Path: bacchus.pa.dec.com!decwrl!elroy.jpl.nasa.gov!usc!cs.utexas.edu!uunet!allbery From: mike@milhow2.UUCP (Mike Howard) Newsgroups: comp.sources.misc Subject: v13i069: generalized t/nroff pre-processor Message-ID: <94762@uunet.UU.NET> Date: 25 Jun 90 00:01:46 GMT Sender: allbery@uunet.UU.NET Lines: 1227 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 13, Issue 69 Submitted-by: mike@milhow2.UUCP (Mike Howard) Archive-name: preroff/part01 This is an extension of ``soelim''. It reads an input file and interpolates all: 1. file inclusions specified by '.so' commands 2. the output of any shell scripts bounded by .shell-start/.shell-end pairs 3. the output of any program executions specified by .exec-cmd I wrote it in order to manage a document I was writing which contained may tables and graphs derived from a variety of simulation runs. My purpose was to extract the tables and graphs directly simulation output reports 'on the fly' as well as computing summary statistics by combining reports. The obvious benefits (to me) were accuracy of tables, charts, and summaries; ensuring that the report is generated from the 'latest and most correct' simulation results; reducing (read eliminating) a potentially unbounded number of cryptic files containing nothing but numbers. The input commands are modelled after t/nroff commands, because that is what I use, but, since 'preroff' doesn't know anything about the structure of the document it is processing, that isn't much of a limitation. Mike Howard how%milhow1@uunet.uu.net ...!uunet!milhow1!how #! /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 'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 X Makefile 1 X README 1 X preroff.1 1 X preroff.c 1 X sofile-1 1 X sofile-2 1 X sofile-3 1 X test.exec 1 X test.file 1 END_OF_FILE if test 406 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(299 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X XSYS_FLAG = -DSYSV X# SYS_FLAG = -DBSD X X# CFLAGS = -g $(SYS_FLAG) XCFLAGS = -O $(SYS_FLAG) X Xpreroff : preroff.c X $(CC) $(CFLAGS) preroff.c -o preroff X Xtransfer : preroff X sh transfer.sh X Xkit : X makekit -m MANIFEST README preroff.1 preroff.c Makefile \ X test.file sofile-1 sofile-2 sofile-3 test.exec END_OF_FILE if test 299 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(1125 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XThis is an extension of ``soelim''. It reads an input file and Xinterpolates all: X 1. file inclusions specified by '.so' commands X 2. the output of any shell scripts bounded by .shell-start/.shell-end X pairs X 3. the output of any program executions specified by .exec-cmd X XI wrote it in order to manage a document I was writing which contained Xmay tables and graphs derived from a variety of simulation runs. My Xpurpose was to extract the tables and graphs directly simulation Xoutput reports 'on the fly' as well as computing summary statistics by Xcombining reports. The obvious benefits (to me) were accuracy of Xtables, charts, and summaries; ensuring that the report is generated Xfrom the 'latest and most correct' simulation results; reducing (read Xeliminating) a potentially unbounded number of cryptic files Xcontaining nothing but numbers. X XThe input commands are modelled after t/nroff commands, because that Xis what I use, but, since 'preroff' doesn't know anything about the Xstructure of the document it is processing, that isn't much of a Xlimitation. X XMike Howard Xhow%milhow1@uunet.uu.net X...!uunet!milhow1!how END_OF_FILE if test 1125 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'preroff.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'preroff.1'\" else echo shar: Extracting \"'preroff.1'\" \(3633 characters\) sed "s/^X//" >'preroff.1' <<'END_OF_FILE' X. \" -*- nroff -*- X.TH PREROFF 1 "local" X.SH NAME Xpreroff \- an extension of soelim which runs also runs processes X.SH SYNOPSIS X.B preroff X[-v(vv)] [-t timeout] [-d] [file ...] X.SH DESCRIPTION X.PP X.B preroff Xpre-processes one or more files (standard input if no files are given Xor if the file name is X.B \- ) Xinterpolating the results of file inclusions and processing Xdirectives. X.HP X.B -v[v..] X\- increments verbosity. After more than one -v, it gets pretty Xobnoxious. X.HP X.B -t timeout-in-seconds X\- sets the timeout value to the specified value. Default is 5 Xminutes. The timeout value is used to trap sub-processes which do not Xproduce output and are suspected of being infinite loops. X.PP XFor example: X.PP X.nf X\&.shell-start Xwhile true ; do X sleep 100 Xdone X\&.shell-stop X.fi X.HP X.B -d X\- causes X.B preroff Xto discard X.I .so Xdirectives which fail. X.B preroff Xattempts to interpolate the contents of any X.I .so Xdirectives \- if it fails, then it normally passes the directive Xupstream. This is done in order to handle the case where the sourced Xfile is created dynamically, by say a X.I .sy Xcommand, during later processing. The X.B -d Xflag turns this feature off. X.PP XFiles and commands may be nested to a depth of _NFILE - 5, or 15 if X_NFILE is not defined in stdio.h. X.PP XCommands are recognized by beginning in the leftmost column and being Xencoded exactly as shown. XThe following commands are interpreted: X.HP X.B .so X.I file X\- interpolates the output of X.B preroff Xapplied to X.I file. X.HP X.B .shell-start/.shell-end X\- interpolates the execution of X.B /bin/sh Xon the text between .shell-start and .shell-end commands. X.HP X.B .exec-cmd X\- performs an X.B execp(3) Xon the named command, passing the parameters on the line. XParameters may be continued onto successive lines by escaping the Xnewline (\\n) with a backslash (\\). XParameters are separated by white space \- so parameters which contain Xwhite space must consist of or contain quoted strings.. X.IP XQuoted strings are sequence of characters which are bracketed by Xmatching double quotes (") or single quotes('). Quoted strings may Xinclude white space (blanks or tabs) and may span more than one line. XWhen a quoted string contains a new-line, the new-line will be Xnormally be included in the parameter unless it is escaped by a Xbackslash (\\). With the exception of new-lines, any character may be Xincluded in a quoted string by escaping it with a backslash (\\) \- in Xparticular, the active quote mark and the backslash. X.IP X.B .exec-cmd Xcommands are performed using the X.B execp() Xsystem call, so, with the exception of quoting strings, the arguments Xare passed to the exec'ed process literally, i.e. there is no macro Xsubstitution mechanism available - use X.B .shell-start/.shell-end Xfor that. X.IP XQuoting is a little strange in that quoted strings may occur within Xparameters: e.g. parameter="foo bar plotz". This is done to make Xwriting the commands more familiar, although it should lead to Xinteresting errors. X.HP X.B .exit-preroff X\- kicks you our of the file (not necessarily the run). X.SH "SEE ALSO" Xsoelim X.SH DIAGNOSTICS X.PP XSome self explanatory diagnostics are written on X.B stderr. X.PP XIf a command or B .so fails, a stack backtrace is printed identifying Xthe applicable document(s) and line(s). X.SH BUGS X.PP XThis is not an especially forgiving program. X.PP XThere should be much more comprehensive in the input syntax. It should Xincorporate some sort of escape mechanism so that preroff commands (or Xconflicts with other processors) could be included in the input Xfile(s)/scripts and be passed on to a second invocation ... END_OF_FILE if test 3633 -ne `wc -c <'preroff.1'`; then echo shar: \"'preroff.1'\" unpacked with wrong size! fi # end of 'preroff.1' fi if test -f 'preroff.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'preroff.c'\" else echo shar: Extracting \"'preroff.c'\" \(17872 characters\) sed "s/^X//" >'preroff.c' <<'END_OF_FILE' X/* @(#)preroff.c 1.6 90/06/11 */ X/* copyright Mike Howard, 1990 - all rights reserved */ X X#ifdef BSD X# ifdef SYSV Xthis is an error - cannot define both BSD and SYSV X# endif /* SYSV */ X#endif /* BSD */ X X#include X#include X#include X#include X#include X#include X X#ifdef BSD X# include X# include X#endif /* BSD */ X Xchar *shell_path = "/bin/sh"; Xchar *shell_name = "sh"; X#define SO ".so " X#define SHELL_START ".shell-start" X#define SHELL_END ".shell-end" X#define EXIT_PREROFF ".exit-preroff" X#define EXEC_CMD ".exec-cmd" X#define SO_LEN 4 X#define SHELL_START_LEN 12 X#define SHELL_END_LEN 10 X#define EXIT_PREROFF_LEN 13 X#define EXEC_CMD_LEN 9 X X X#ifdef _NFILE X# define STACK_SIZE _NFILE - 5 X#else X# define STACK_SIZE 15 X#endif X Xint stack_top; Xstruct stack_item { X struct stack_item *next; X char *file_name; X int line_number; X int fd; X int pid; X char *buffer; X char *next_cp; X char *last_cp; X} stack[STACK_SIZE]; X Xint save_fd; X#define current_fname stack[stack_top].file_name X#define current_line stack[stack_top].line_number X X/* pid of current child process */ X#define child_pid stack[stack_top].pid X#define current_pid stack[stack_top - 1].pid X X#define current_buffer stack[stack_top].buffer X#define current_next_cp stack[stack_top].next_cp X#define current_last_cp stack[stack_top].last_cp X X#define BUF_SIZE 1024 Xchar buf[BUF_SIZE]; X Xint verbose; Xint delete_failed_so_flag; Xint timeout = 300; /* everything should send some input within 5 minutes*/ X X/* dirty.c */ X#define VOID void /* */ X#define VOIDARG void /* */ X/* #define VOID int /* */ X/* #define VOIDARG /* */ X X#ifdef __STDC__ Xint main(int /* argc */ , char ** /* argv */ ); Xint open_a_file(char * /* fname */ ); XVOID doit_dirty(VOIDARG); XVOID gather_command(VOIDARG); XVOID do_shell_command(VOIDARG); XVOID do_exec_command(VOIDARG); Xchar *collect_args(char * /* bp */ ); XVOID clean_up(int /* sig */ ); Xint get_line(VOIDARG); XVOID read_alarm(VOIDARG); XVOID kill_current_child(VOIDARG); XVOID push_name(char * /* fname */ , int /* fd */ ); XVOID pop_name(VOIDARG); Xchar *Malloc(unsigned /* size */ ); Xchar *Realloc(char * /* ptr */ , unsigned /* size */ ); XVOID wait_for_child(VOIDARG); XVOID err_msg(char * /* s */ ); X#else /* __STDC__ */ Xint main( /* int argc, char **argv */ ); Xint open_a_file( /* char *fname */ ); XVOID doit_dirty( /* void */ ); XVOID gather_command( /* void */ ); XVOID do_shell_command( /* void */ ); XVOID do_exec_command( /* void */ ); Xchar *collect_args( /* char *bp */ ); XVOID clean_up( /* int sig */ ); Xint get_line( /* void */ ); XVOID read_alarm( /* void */ ); XVOID kill_current_child( /* void */ ); XVOID push_name( /* char *fname, int fd */ ); XVOID pop_name( /* void */ ); Xchar *Malloc( /* unsigned size */ ); Xchar *Realloc( /* char *ptr, unsigned size */ ); XVOID wait_for_child( /* void */ ); XVOID err_msg( /* char *s */ ); X#endif /* __STDC__ */ X Xchar *hlp[] = { X"Option Effect", X"-v(vvv) increment verbosity - for debugging documents", X"-t timeout change timeout value from default (5 minutes)", X"-d discard failed .so directives rather than passing", X" them thru (default)", X(char *)0}; X X#define USE_MSG "usage: %s(1.6) [-h | option(s)] [file or '-' ...]\n" X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X int i; X extern int optind, opterr; X extern char *optarg; X X while ((i = getopt(argc, argv, "hdvt:")) != EOF) { X switch (i) { X case 'h': X for (i=0;hlp[i];i++) X fprintf(stderr, "%s\n", hlp[i]); X fprintf(stderr, USE_MSG, argv[0]); X exit(0); X case 'd': X delete_failed_so_flag++; X break; X case 't': X if ((timeout = atoi(optarg)) <= 0) X timeout = 0; X break; X case 'v': X verbose++; X break; X case '?': X fprintf(stderr, "Illegal argument: %c\n", i); X fprintf(stderr, USE_MSG, argv[0]); X exit(1); X } X } X X X signal(SIGHUP, clean_up); X signal(SIGINT, clean_up); X signal(SIGQUIT, clean_up); X signal(SIGTERM, clean_up); X stack_top = 0; X save_fd = dup(0); X X if (argc == optind) { X push_name("stdin", save_fd); X doit_dirty(); X X /* return to the prior file */ X pop_name(); X exit(0); X } X X for (i=optind;i= 0) { X push_name("stdin", save_fd); X save_fd = -1; X return 0; X } X else { X err_msg("Attempt to re-scan stdin"); X dup(stack[stack_top].fd); X close(stack[stack_top].fd); X return -1; X } X } X else if ((fd = open(fname, O_RDONLY)) < 0) { X char err_buf[256]; X sprintf(err_buf, "unable to open %s", fname); X err_msg(err_buf); X return -1; X } X X push_name(fname, fd); X X return 0; X} X XVOID doit_dirty() X{ X if (verbose) X fprintf(stderr, "Starting to process %s\n", current_fname); X X fflush(stdout); X while (get_line() > 0) { X if (verbose > 1) X fprintf(stderr, "%s:%d: %s", current_fname, current_line, X buf); X if (!strncmp(buf, SO, SO_LEN)) { X char *fname = &buf[4]; X char *cp; X X /* find and bound file name */ X while (isspace(*fname)) X fname++; X for (cp=fname;!isspace(*cp);cp++) X ; X *cp = '\0'; X X if (!open_a_file(fname)) { X doit_dirty(); X pop_name(); X } X else if (!delete_failed_so_flag) X fputs(buf, stdout); X } X else if (!strncmp(buf, SHELL_START, SHELL_START_LEN)) { X gather_command(); X do_shell_command(); X } X else if (!strncmp(buf, EXEC_CMD, EXEC_CMD_LEN)) { X do_exec_command(); X } X else if (!strncmp(buf, EXIT_PREROFF, EXIT_PREROFF_LEN)) { X break; X } X else X fputs(buf, stdout); X } X X if (verbose) X fprintf(stderr, "Finished processing %s\n", current_fname); X} X Xchar *tmp_fname; X XVOID gather_command() X{ X int fd; X X /* open a temp file */ X do { X tmp_fname = tmpnam((char *)0); X } while ((fd = open(tmp_fname, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0); X X if (verbose > 1) X write(fd, "set -v\n", 7); X if (verbose > 2) X write(fd, "set -x\n", 7); X X while (get_line() > 0) { X if (!strncmp(buf, SHELL_END, SHELL_END_LEN)) X break; X X write(fd, buf, strlen(buf)); X } X X close(fd); X} X XVOID do_shell_command() X{ X int pipe_fd[2]; X int fd; X X if (pipe(pipe_fd) < 0) { X err_msg("unable to open pipe to command"); X unlink(tmp_fname); X tmp_fname = (char *)0; X return; X } X X if (child_pid = fork()) { X if (verbose) X fprintf(stderr, "forking child process: %d\n", child_pid); X X close(pipe_fd[1]); X push_name("shell-process", pipe_fd[0]); X close(pipe_fd[0]); X X doit_dirty(); X wait_for_child(); X pop_name(); X } X else { X /* direct output to pipe */ X close(1); X dup(pipe_fd[1]); X close(pipe_fd[0]); X close(pipe_fd[1]); X X close(0); X open("/dev/null", O_RDONLY); X X execl(shell_path, shell_name, tmp_fname, (char *)0); X err_msg("exec of shell failed"); X } X X unlink(tmp_fname); X tmp_fname = (char *)0; X} X X/* the command line(s) are scanned and arguments are found. X the scanner recognizes '...' and "..." strings - the enclosing X quote marks are stripped, but no /bin/sh interpretations are performed X \x escapes are processed by stripping the \ character X and recognizes \{white-space}*\n as a continuation to the next line X*/ X XVOID do_exec_command() X{ X int pipe_fd[2]; X char *args[100]; X int arg_count = 0; X X while (args[arg_count] = X collect_args(arg_count ? (char *)0 : &buf[EXEC_CMD_LEN])) X arg_count++; X X if (!arg_count) { X err_msg("No arguments for system command"); X return; X } X X if (verbose > 1) { X int i; X X fprintf(stderr, "%s arg dump:\n", EXEC_CMD); X for (i=0;i 2) Xfprintf(stderr, "skipping %lx: %c %2x\n", next_char, *next_char, *next_char); X while (isspace(*next_char)) X next_char++; X /* if there is no next token then we are done */ X if (!*next_char) X return (char *)0; X X /* if we see \ \n seqence, then this is a continuation line */ X if (next_char[0] == '\\' && next_char[1] == '\n') { X if (get_line() <= 0) X return (char *)0; X next_char = buf; X } X } while (isspace(*next_char)); X X /* allocate sufficient room */ X base = X cp = Malloc(strlen(next_char)); X X /* detect quoted string */ X if ((c = *next_char) == '\'' || c == '"') { X next_char++; X in_quoted_string = c; X } X else X in_quoted_string = 0; X X /* collect this argument */ X while (1) { Xif (verbose > 2) Xfprintf(stderr, "scanning %lx: %c %2x\n", next_char, *next_char, *next_char); X switch (c = *next_char++) { X case '\'': X case '"': X if (in_quoted_string) { X if (c == in_quoted_string) X in_quoted_string = 0; X else X *cp++ = c; X } X else X in_quoted_string = c; X break; X case '\\': X if ((*cp++ = *next_char++) == '\n') { X cp--; X if (get_line() <= 0) { X *cp = '\0'; X return Realloc(base, strlen(base)); X } X len = cp - base; X base = Realloc(base, strlen(base) + strlen(buf)); X cp = base + len; X next_char = buf; X } X break; X case ' ': X case '\t': X if (in_quoted_string) { X *cp++ = c; X } X else { X *cp = '\0'; X return Realloc(base, strlen(base)); X } X break; X case '\n': X if (!in_quoted_string) { X *cp = '\0'; X return Realloc(base, strlen(base)); X } X *cp++ = '\n'; X if (get_line() <= 0) { X *cp = '\0'; X return Realloc(base, strlen(base)); X } X len = cp - base; X base = Realloc(base, strlen(base) + strlen(buf)); X cp = base + len; X next_char = buf; X break; X case '\0': X err_msg("null encountered in .exec-cmd arg list"); X *cp = '\0'; X return Realloc(base, strlen(base)); X default: X *cp++ = c; X break; X } X } X} X XVOID clean_up(sig) Xint sig; X{ X if (tmp_fname) X unlink(tmp_fname); X X switch (sig) { X case SIGQUIT: X abort(); X default: X exit(sig); X } X} X X#ifdef BSD Xint get_line() X{ X char *cp = buf; X char *ncp = current_next_cp; X char *lcp = current_last_cp; X int len; X int local_errno; X fd_set read_fdset; X struct timeval select_timeout; X X while (1) { X while (ncp < lcp) { X if ((*cp++ = *ncp++) == '\n') { X current_next_cp = ncp; X current_line++; X *cp = '\0'; X return cp - buf; X } X } X X /* test for ready-to-read */ X FD_ZERO(&read_fdset); X FD_SET(0, &read_fdset); X select_timeout.tv_sec = timeout; X select_timeout.tv_usec = 0; X if (select(1, &read_fdset, (fd_set *)0, (fd_set *)0, &timeout) <= 0) { X int old_timeout = timeout; X X err_msg("Input timeout - probable infinite loop"); X timeout = 5; X signal(SIGALRM, kill_current_child); X alarm(timeout); X kill_current_child(); X wait_for_child(); X timeout = old_timeout; X X local_errno = errno; X alarm(0); X signal(SIGALRM, SIG_IGN); X } X X /* input ready - so read stdin */ X len = read(0, current_buffer, BUF_SIZE); X X /* initialize input buffer pointers */ X ncp = X current_next_cp = current_buffer; X lcp = X current_last_cp = current_buffer + len; X X if (len < 0 && local_errno == EINTR) { X fprintf(stderr, "errno: %d: %s, %d\n", local_errno, X current_fname, current_line); X errno = 0; X } X X if (len <= 0) { X *cp = '\0'; X return cp - buf; X } X } X} X X#else /* BSD */ X Xint get_line() X{ X char *cp = buf; X char *ncp = current_next_cp; X char *lcp = current_last_cp; X int len; X int local_errno; X X while (1) { X while (ncp < lcp) { X if ((*cp++ = *ncp++) == '\n') { X current_next_cp = ncp; X current_line++; X *cp = '\0'; X return cp - buf; X } X } X X signal(SIGALRM, read_alarm); X alarm(timeout); X len = read(0, current_buffer, BUF_SIZE); X local_errno = errno; X alarm(0); X signal(SIGALRM, SIG_IGN); X X ncp = X current_next_cp = current_buffer; X lcp = X current_last_cp = current_buffer + len; X X if (len < 0 && local_errno == EINTR) { X int old_timeout = timeout; X X err_msg("Input timeout - probable infinite loop"); X timeout = 5; X signal(SIGALRM, kill_current_child); X alarm(timeout); X kill_current_child(); X wait_for_child(); X timeout = old_timeout; X } X if (verbose > 1 && local_errno) { X fprintf(stderr, "errno: %d: %s, %d\n", local_errno, X current_fname, current_line); X errno = 0; X } X X if (len <= 0) { X *cp = '\0'; X return cp - buf; X } X } X} X XVOID read_alarm() X{ X return; X} X#endif /* BSD */ X X/* this routine attempts to kill `current_pid' with a SIGTERM. If X that fails,(causing an alarm signal) it sends a SIGKILL */ X XVOID kill_current_child() X{ X static int last_pid_killed = 0; X X if (verbose) X fprintf(stderr, "Alarm Signal - sending %d to %d\n", X last_pid_killed == current_pid ? SIGKILL : SIGTERM, current_pid); X if (current_pid) { X kill(current_pid, last_pid_killed == current_pid ? SIGKILL : SIGTERM); X last_pid_killed = current_pid; X } X signal(SIGALRM, kill_current_child); X alarm(timeout); X X return; X} X XVOID push_name(fname, fd) Xchar *fname; Xint fd; X{ X if (stack_top >= STACK_SIZE) { X char err_buf[80]; X X sprintf(err_buf, "attempt to open more than %d inputs\n", STACK_SIZE -1); X err_msg(err_buf); X return; X } X X /* redirect stdin - saving old file descriptor */ X stack[stack_top].fd = dup(0); X close(0); X dup(fd); X close(fd); X X /* push new file name and initialize line counter */ X stack[++stack_top].file_name = strcpy(Malloc(strlen(fname)), fname); X stack[stack_top].line_number = 0; X if (!stack[stack_top].buffer) X stack[stack_top].buffer = Malloc(BUF_SIZE); X stack[stack_top].next_cp = X stack[stack_top].last_cp = stack[stack_top].buffer; X} X X/* pops stack and restores stdin */ X XVOID pop_name() X{ X if (stack_top < 0) { X err_msg("fatal error - pop_name()\n"); X abort(); X } X free(stack[stack_top--].file_name); X close(0); X if (stack_top >= 0) { X dup(stack[stack_top].fd); X close(stack[stack_top].fd); X } X} X X/* like malloc EXCEPT that it abort()s if malloc() fails and it allocates X size+1 bytes */ X Xchar *Malloc(size) Xunsigned size; X{ X char *malloc(); X char *ret; X X if (ret = malloc(++size)) X return ret; X X err_msg("Memory Allocation Failure in Malloc"); X abort(); X} X X/* like realloc EXCEPT that it abort()s if malloc() fails and it allocates X size+1 bytes */ X Xchar *Realloc(ptr, size) Xchar *ptr; Xunsigned size; X{ X char *realloc(); X char *ret; X X if (ret = realloc(ptr, ++size)) X return ret; X X err_msg("Memory Allocation Failure in Realloc"); X abort(); X} X X/* This is messy - I'm not all that sure I trust it. X X The hair comes in when we have a couple of child processes running. X Exprience says that they may return their pid's to the wait() call X out of order, so we have to test all valid wait returns to see X if they were some of our children and clear their slot in the stack. X Also, wait_for_child() has to make sure that the child it is waiting X for has not already gone away. X X for the curious: this sort of thing happens when a child process X produces output which contains .shell-start/end or .exec-cmd directives: X the first child spits out the output and then dies. X preroff reads the stream and then fires up a new child to process X the directive - the first child's output is still in the pipe. X the second child croaks and the doit_dirty() exits - which starts X the wait_for_child() loop - which receives the pid's of the dead X processes in the order they died, but not in the order of completing X processing their output. X*/ X XVOID wait_for_child() X{ X int wait_ret; X int status; X int counter = 0; X int i; X X if (!current_pid) X return; X X signal(SIGALRM, kill_current_child); X alarm(timeout); X while (1) { X wait_ret = wait(&status); X if (verbose) X fprintf(stderr, "wait_for_child: wait returned %d\n", wait_ret); X if (wait_ret == current_pid) { X current_pid = 0; X break; X } X if (wait_ret > 0) X for (i=0;i 2 && errno == EINTR) X break; X X if (counter > 4) X break; X } X X signal(SIGALRM, SIG_IGN); X alarm(0); X} X XVOID err_msg(s) Xchar *s; X{ X int i; X X fprintf(stderr, "%s\nStack Trace\n", s ? s : "Error Detected"); X X for (i=stack_top;i>0;i--) X fprintf(stderr, "line %4d: %s \n", stack[i].line_number, X stack[i].file_name); X} END_OF_FILE if test 17872 -ne `wc -c <'preroff.c'`; then echo shar: \"'preroff.c'\" unpacked with wrong size! fi # end of 'preroff.c' fi if test -f 'sofile-1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sofile-1'\" else echo shar: Extracting \"'sofile-1'\" \(95 characters\) sed "s/^X//" >'sofile-1' <<'END_OF_FILE' X This is the beginning of file 1 X.so sofile-2 X --------------------this is the end of file 1 END_OF_FILE if test 95 -ne `wc -c <'sofile-1'`; then echo shar: \"'sofile-1'\" unpacked with wrong size! fi # end of 'sofile-1' fi if test -f 'sofile-2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sofile-2'\" else echo shar: Extracting \"'sofile-2'\" \(259 characters\) sed "s/^X//" >'sofile-2' <<'END_OF_FILE' X this is sofile 2 X this is an so which should fail X.so foobar X this is the line after the failing line X.shell-start Xecho "this is a shell command" Xecho "this is a second shell command" X.shell-end X X --------------------this is the end of so file 2 END_OF_FILE if test 259 -ne `wc -c <'sofile-2'`; then echo shar: \"'sofile-2'\" unpacked with wrong size! fi # end of 'sofile-2' fi if test -f 'sofile-3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sofile-3'\" else echo shar: Extracting \"'sofile-3'\" \(69 characters\) sed "s/^X//" >'sofile-3' <<'END_OF_FILE' X>this is so file 3 X>--------------------this is the end of so file 3 END_OF_FILE if test 69 -ne `wc -c <'sofile-3'`; then echo shar: \"'sofile-3'\" unpacked with wrong size! fi # end of 'sofile-3' fi if test -f 'test.exec' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'test.exec'\" else echo shar: Extracting \"'test.exec'\" \(121 characters\) sed "s/^X//" >'test.exec' <<'END_OF_FILE' X.exec-cmd echo one X.exec-cmd echo \ X two X.exec-cmd echo "three Xfour" X.exec-cmd awk ' \ XBEGIN { print "Beginning" } \ X' - END_OF_FILE if test 121 -ne `wc -c <'test.exec'`; then echo shar: \"'test.exec'\" unpacked with wrong size! fi # end of 'test.exec' fi if test -f 'test.file' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'test.file'\" else echo shar: Extracting \"'test.file'\" \(673 characters\) sed "s/^X//" >'test.file' <<'END_OF_FILE' XThis is some text - including sofile-3 X.so sofile-3 Xsome more text - after including sofile-3 X Xincluding sofile-1 X.so sofile-1 Xafter including sofile-1 X Xincluding sofile-3 again X.so sofile-3 XThis is some more text X XThe next line does an ls -C of 'pwd' X.shell-start Xls -C X.shell-end X X".exec-cmd echo "exec command"" X.exec-cmd echo "exec command" X X".exec-cmd foobar - which will Fail" X.exec-cmd foobar X X.exec-cmd echo " a multi line Xline two Xline three Xecho " \ X"foo" \ X"Last Line" X Xa script which will not generate output X.shell-start X# a script which will not generate output Xecho "foo" Xwhile true ; do X sleep 10 Xdone X.shell-end X--------------------This is the final text END_OF_FILE if test 673 -ne `wc -c <'test.file'`; then echo shar: \"'test.file'\" unpacked with wrong size! fi # end of 'test.file' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0