allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (09/24/89)
Posting-number: Volume 8, Issue 44 Submitted-by: davecb@nexus.yorku.ca (David Collier-Brown) Archive-name: explode Well, this is a little program to pull chunks of source code out of LaTeX files and put them into one or more output files. As the man page says, I did this as a simple tool to allow better documentation of quite simple programs, ones that could be explained easily in a fairly sequential discussion. Since it hasn't broken on the few machines I've used it on, I'm contributing it to the net. I believe it portable and reliable, but it's whole life has been in the Berzerkly universe... --dave #! /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 shell archive." # Contents: Makefile RCS README bar.tex explode.1 explode.c foo.tex # unexplode # Wrapped by davecb@yunexus on Mon Sep 18 21:50:59 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(508 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# X# A somewhat generic makefile, for explode X# XCC=/usr/local/gnu/gcc # Try another compiler XSRCS= explode.c XOBJS= explode.o XCFLAGS= -g XDESTDIR= /usr/local/bin XMANDIR= /usr/local/man/man1 X Xexplode: ${OBJS} X ${CC} -o explode ${CFLAGS} ${OBJS} X Xinstall: X install -s -m 755 explode ${DESTDIR} X install -m 755 unexplode ${DESTDIR} X cp explode.1 ${MANDIR}/explode.1 X Xdeinstall: X rm -f ${DESTDIR}/explode ${DESTDIR}/unexplode X rm -f ${MANDIR}/explode.1 X Xclean: X rm -f explode *.o core a.out Xlint: X lint explode.c END_OF_FILE if test 508 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test ! -d 'RCS' ; then echo shar: Creating directory \"'RCS'\" mkdir 'RCS' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(154 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XThis is new, incomplet and inefficent. X 1) no state in table X 2) no error checking X 3) no line-splitting when directive in mid-line X 4) underdocumented. X END_OF_FILE if test 154 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'bar.tex' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'bar.tex'\" else echo shar: Extracting \"'bar.tex'\" \(136 characters\) sed "s/^X//" >'bar.tex' <<'END_OF_FILE' X\file{beginning} X\begin{verbatim} Xbeginning X\end{verbatim} X X\begin{verbatim} Xmiddle X\end{verbatim} X X\begin{verbatim} Xend X\end{verbatim} END_OF_FILE if test 136 -ne `wc -c <'bar.tex'`; then echo shar: \"'bar.tex'\" unpacked with wrong size! fi # end of 'bar.tex' fi if test -f 'explode.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'explode.1'\" else echo shar: Extracting \"'explode.1'\" \(1981 characters\) sed "s/^X//" >'explode.1' <<'END_OF_FILE' X.TH EXPLODE 1 X.SH NAME Xexplode \- file exploder (LaTeX source-code extractor) X.SH SYNOPSIS X.B explode X.I pathname[.tex] X.sp X.B unexplode X.I pathname[.tex] X X.SH DESCRIPTION X.I explode Xtakes source code intertwined with LaTeX and emits Xjust the code, to however many files as are needed. X.I unexplode Xdeletes the files created that way. X.PP XThe syntax of the LaTeX file is very simple: X.I \\file{filename} Xnames a file to which material will be written. X.I \\begin{verbatim} Xstarts the copy to the file, and X.I \\end{verbatim} Xends it. X.PP XThe verbatim sections can be interleaved with any other Xlegal LaTeX construct, and multiple verbatims which Xfollow a single \\file directive will be written to the Xsame file. X.PP XMultiple files can be in use at any one time, and Xyou can switch between them by inserting a X\\file line. X X.SH EXAMPLE XThe following is the simplest plausible explode-file. X.nf X.nj X X\\file{foo.h} X ... X\\begin{verbatim} Xtypedef void * FOO; X\\end{verbatim} X ... X\\file{foo.c} X\\begin{verbatim} X ... X.nf X.nj X X.SHISTORY XThis was written one afternoon when it became obvious that pr Xrewriting my Web subset was going to take a looooong time. XThis was written instead, to allow simple, multi-file programs Xto be generated from a single LaTeX source file. X.PP XThe program is extremely simple, has no options and probably never will Xhave. The problem of multi-input, multi-output LaTeX -> Xprogramming-language coding is not something that's going to succumb Xto simple v7 filters. In the meantime, here's the filter. X XNone at present. X X.SH SEE ALSO XLaTeX(1), tangle(1), weave(1), Donald Knuth's Web system. X X.SH AUTHORS X.nf XDave Collier-Brown (davecb@nexus.yorku.ca). X.fi X X.SH DIAGNOSTICS X``Too many output files'', if the system runs out of Xfile descriptors. X X.SH BUGS XThe limit on output files. An earlier version Xre-used file descriptors... X.PP XThe directives need to be on a line by themselves, as Xthe whole line gets discarded after the directive Xis interpreted. END_OF_FILE if test 1981 -ne `wc -c <'explode.1'`; then echo shar: \"'explode.1'\" unpacked with wrong size! fi # end of 'explode.1' fi if test -f 'explode.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'explode.c'\" else echo shar: Extracting \"'explode.c'\" \(7384 characters\) sed "s/^X//" >'explode.c' <<'END_OF_FILE' X/* X * explode -- an extact function for a latex-flavored tangle. X */ X#include <stdio.h> X#include <strings.h> X X#define MAXLINE 256 X#define OK 0 Xchar *ProgName = NULL; Xtypedef enum {NO=0,FILENAME,BEGIN_VERBATIM,END_VERBATIM} KEY; X X void Xmain(argc,argv) int argc; char *argv[]; { X int rc = 0, /* Return code from explode. */ X i; X FILE *fp, *lopen(); X X ProgName = argv[0]; X if (argc < 2 && isatty(0)) { X (void) fprintf(stderr,"Usage: %s file[.tex]\n", X ProgName); X exit(1); X } X if (!isatty(0)) { X rc = explode(stdin); X } X else { X for (i=1; i < argc; i++) { X /* printf("argv[%d] = '%s'\n",i,argv[i]); */ X if ((fp= lopen(argv[i],"r",".tex")) == NULL) { X (void) fprintf(stderr, X "%s: %s \"%s\", ignored.\n", X ProgName, "can't open input file", X argv[i]); X continue; X } X else { X rc = explode(fp); X (void) fclose(fp); X } X } X } X exit(rc); X} X X/* X * explode -- the main function, which supervises parsing X * and output-file bookkeeping. X */ X int Xexplode(fp) FILE *fp; { X char line[MAXLINE], /* A place to put the line. */ X operand[MAXLINE]; /* The keyword's parameter. */ X FILE *outFp, /* Current output file. */ X *tFp, /* Temporary fp. */ X *initialFile(), /* Initial output file (/dev/null) */ X *beginFile(), /* Start writing to taht file. */ X *endFile(); /* Stop writing to it. */ X int initiateFile(); /* Associate a name with an fp. */ X X KEY hasKeyWord(); X X if ((outFp= initialFile()) == NULL) { X /* Assumed infallible. */ X (void) fprintf(stderr,"%s: unable to open \"%s\", %s.\n", X ProgName,"/dev/null","aborting"); X exit(3); X } X while (fgets(line,sizeof(line),fp) != NULL) { X switch (hasKeyWord(line,operand)) { X case NO: /* Just write the line. */ X /* printf("body line: %s",line); */ X (void) fputs(line,outFp); X break; X case FILENAME: /* Collect a new filename to use. */ X /* printf("file line: %s, %s",operand,line); */ X if ( !initiateFile(operand)) { X (void) fprintf(stderr, X "%s: can't open output file \"%s\", %s.\n", X ProgName, operand, "aborting"); X exit(3); X } X break; X case BEGIN_VERBATIM: /* Start writing to named file. */ X /* printf("begin line: %s, %s",operand,line); */ X if ((tFp= beginFile()) != NULL) { X outFp = tFp; X } X else { X /* It's "\begin{verbatim}" with no current file. */ X (void) fputs(line,outFp); X } X break; X case END_VERBATIM: /* Stop writing to that file. */ X /* printf("end line %s, %s",operand,line); */ X if ((tFp= endFile()) != NULL) { X outFp = tFp; X } X else { X /* It's "\end{verbatim} with no current file". */ X (void) fputs(line,outFp); X } X break; X } X } X return OK; X} X X/* X * lopen -- local open, sees if there's a file with an optional suffix. X */ X FILE * Xlopen(name,mode,ext) char *name, *mode, *ext; { X FILE *fp, *fopen(); X char buffer[MAXLINE], X *strcat(), *strcpy(); X X if ((fp= fopen(name,mode)) != NULL) { X return fp; X } X (void) strcat(strcpy(buffer,name),ext); X fp = fopen(buffer,mode); X return fp; X} X X/* X * hasKeyWord -- see if the keyword appears in the line, X * return either NO, FILENAME, BEGIN_VERBATIM or END_VERBATIM and X * a pointer to a null-terminated string containing X * the parameter. Also produces error messages if X * the keyword-phrase is misparsed. X */ X#define strchr(s,c) index(s,c) X#define streq(s1,s2) (strncmp(s1,s2,strlen(s2))==0) X KEY XhasKeyWord(line,operand) char *line; char *operand; { X register char *p; /* Pointer to current position in string. */ X KEY rc; /* Result. */ X char *skipTo(); X void terminate(), unTerminate(); X X if ((p= strchr(line,'\\')) == NULL) { X return NO; X } X else if (streq(p,"\\file{")) { X rc = FILENAME; X } X else if (streq(line,"\\begin{verbatim}")) { X rc = BEGIN_VERBATIM; X } X else if (streq(line,"\\end{verbatim}")) { X rc = END_VERBATIM; X } X else { X return NO; X } X /* It found one! Stuff "operand" with the parameter value. */ X p = skipTo(p,'{') + 1; X terminate(p,'}'); X (void) strcpy(operand,p); X unTerminate(p,'}'); X return rc; X} X X/* X** string utilities X*/ X char * XskipTo(p,c) register char *p; char c; { X while (*p && *p != c) X p++; X return p; X } X X void Xterminate(from,at) char *from, at; { X while (*from && *from != at) X from++; X if (!*from) X return; X *from = '\0'; X return; X} X X void XunTerminate(from,with) char *from, with; { X while (*from) X from++; X *from = with; X return; X} X X/* X * strsave -- allocate enopugh space for a string & its terminating null. X */ X char * Xstrsave(s) char *s; { X char *p, *malloc(), *strcpy(); X X if ((p= malloc((unsigned)strlen(s)+1)) != NULL) { X return strcpy(p,s); X } X else { X return NULL; X } X} X X/* X** File -- output-file managment package. X** Depends on table package. X*/ X#define CURRENT "The current file" Xtypedef struct { X char *name; X FILE *fp; X} TABLE; X X X/* X * initialFile -- create the initial output file (/dev/null). X */ X FILE * XinitialFile() { X FILE *fp, *fopen(); X TABLE *addToTable(); X X if ((fp= fopen("/dev/null","w")) != NULL X && addToTable("/dev/null",fp) != NULL) { X return fp; X } X else { X return (FILE *) NULL; X } X} X X/* X * initiateFile -- get a file pointer for the named file & save it. X */ X int XinitiateFile(name) char *name; { X TABLE *getFromTable(), *addToTable(); X FILE *fp, *fopen(); X X if (getFromTable(name) != NULL) { X return 1; X } X else if ((fp= fopen(name,"w")) == NULL) { X return 0; X } X else { X return addToTable(name,fp)? 1: 0; X } X} X X/* X * beginFile -- start writing to that file. X */ X FILE * XbeginFile() { X TABLE *t, *getFromTable(); X X if ((t= getFromTable(CURRENT)) != NULL) { X return t->fp; X } X else { X return NULL; X } X} X X/* X * endFile -- stop writing to it. X */ X FILE * XendFile() { X TABLE *getFromTable(); X X if (getFromTable(CURRENT) != NULL) { X /* Return the replacement. */ X return getFromTable("/dev/null")->fp; X } X else { X /* Otherwise don't stop using it. */ X return (FILE *) NULL; X } X} X X X/* X * X */ X#define NFILES 30 XTABLE Table[NFILES]; Xstatic int CurrentFile = -1; X X TABLE * XaddToTable(name,fp) char *name; FILE *fp; { X static int lastLookedAt = 0; X int i; X char *strsave(); X TABLE *t, *getFromTable(); X X if ((t= getFromTable(name)) != NULL) { X return (TABLE *) t; X } X else { X for (i=lastLookedAt; i < NFILES; i++) { X if (Table[i].name == NULL) { X Table[i].name = strsave(name); X Table[i].fp = fp; X CurrentFile = i; X return &Table[i]; X } X } X return (TABLE *) NULL; X } X} X X TABLE * XgetFromTable(name) char *name; { X int i; X X if (streq(name,CURRENT)) { X return (CurrentFile == -1)? (TABLE *) NULL: &Table[CurrentFile]; X } X for (i=0; i < NFILES && Table[i].name != NULL; i++) { X if (streq(Table[i].name,name) != NULL) { X return &Table[i]; X } X } X return (TABLE *) NULL; X} END_OF_FILE if test 7384 -ne `wc -c <'explode.c'`; then echo shar: \"'explode.c'\" unpacked with wrong size! fi # end of 'explode.c' fi if test -f 'foo.tex' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'foo.tex'\" else echo shar: Extracting \"'foo.tex'\" \(339 characters\) sed "s/^X//" >'foo.tex' <<'END_OF_FILE' Xmake explode blow up (run out of file pointers) X\file{1} X\file{2} X\file{3} X\file{4} X\file{5} X\file{6} X\file{7} X\file{8} X\file{9} X\file{10} X\file{11} X\file{12} X\file{13} X\file{14} X\file{15} X\file{16} X\file{17} X\file{18} X\file{19} X\file{20} X\file{21} X\file{22} X\file{23} X\file{24} X\file{25} X\file{26} X\file{27} X\file{28} X\file{29} X\file{30} END_OF_FILE if test 339 -ne `wc -c <'foo.tex'`; then echo shar: \"'foo.tex'\" unpacked with wrong size! fi # end of 'foo.tex' fi if test -f 'unexplode' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'unexplode'\" else echo shar: Extracting \"'unexplode'\" \(341 characters\) sed "s/^X//" >'unexplode' <<'END_OF_FILE' X#!/bin/sh X# X# unexplode -- delete the files cerated by explode. X# Xif [ $# -lt 1 ] ; then X echo "unexplode -- delete files created by explode." X echo "Usage: unexplode file" X exit 1 Xfi Xfor i in $*; do X if [ -f $i ] ; then X : X else X if [ -f $i.tex ] ; then X i=$i.tex X fi X fi X rm -f `grep '\\file{' $i | sed ' X s/.file{// X s/}//'` Xdone END_OF_FILE if test 341 -ne `wc -c <'unexplode'`; then echo shar: \"'unexplode'\" unpacked with wrong size! fi chmod +x 'unexplode' # end of 'unexplode' fi echo shar: End of shell archive. exit 0 -- David Collier-Brown, | davecb@yunexus, ...!yunexus!davecb or 72 Abitibi Ave., | {toronto area...}lethe!dave Willowdale, Ontario, | Joyce C-B: CANADA. 416-223-8968 | He's so smart he's dumb.