home *** CD-ROM | disk | FTP | other *** search
- Path: wuarchive!gem.mps.ohio-state.edu!uwm.edu!cs.utexas.edu!uunet!bbn.com!rsalz
- From: rsalz@uunet.uu.net (Rich Salz)
- Newsgroups: comp.sources.unix,comp.text
- Subject: v20i079: Do insertions into a document
- Message-ID: <2095@papaya.bbn.com>
- Date: 27 Oct 89 17:16:21 GMT
- Lines: 434
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Henry Spencer <attcan!utzoo!henry>
- Posting-number: Volume 20, Issue 79
- Archive-name: inserts
-
- I alluded to this in comp.text a while ago, and got a number of requests
- for it. It's a program for things like form letters, where one wants to
- insert chunks of text into repetitions of a master document. It does much
- the same things as troff's .rd request, but it is much faster, does better
- error checking, and cuts the troff input down to a single stream (which is
- much easier to use in an environment where troff jobs get batched up and
- fed to a server).
-
-
- echo 'README':
- sed 's/^X//' >'README' <<'!'
- XI alluded to this in comp.text a while ago, and got a number of requests
- Xfor it. It's a program for things like form letters, where one wants to
- Xinsert chunks of text into repetitions of a master document. It does much
- Xthe same things as troff's .rd request, but it is much faster, does better
- Xerror checking, and cuts the troff input down to a single stream (which is
- Xmuch easier to use in an environment where troff jobs get batched up and
- Xfed to a server).
- X
- X Henry Spencer at U of Toronto Zoology
- X uunet!attcan!utzoo!henry henry@zoo.toronto.edu
- !
- echo 'inserts.1':
- sed 's/^X//' >'inserts.1' <<'!'
- X.TH INSERTS 1 local
- X.DA 14 April 1988
- X.SH NAME
- Xinserts \- do insertions into document
- X.SH SYNOPSIS
- X.B inserts
- X[
- X.B \-p
- Xinspt
- X] [
- X.B \-e
- Xend
- X] [
- X.B \-s
- Xoutsep
- X] [
- X.B \-w
- X] [
- X.B \-i
- Xinsfile
- X] [ file ] ...
- X.SH DESCRIPTION
- X.I Inserts
- Xaccepts input from the named \fIfile\fR(s) (standard input default;
- X`\-' as a name means standard input also)
- Xconsisting of a document containing \fIinsertion points\fR,
- Xfollowed by a sequence of \fIinserts\fR, and produces
- X(on standard output) copies of the document with each insertion point
- Xreplaced by an insert.
- XInserts are used in succession until there are no more;
- Xthe number of copies of the document produced
- Xis the number needed to use up all the inserts.
- XIf the inserts end midway through a copy of the document,
- Xempty insertions are supplied until that copy is complete.
- X.PP
- XAn insertion point in the input
- Xis a line consisting entirely of the \fIinspt\fR string,
- X(default \fB.RD\fR).
- XIn the input, the document is separated from the inserts by
- Xa line
- Xconsisting entirely
- Xof the \fIend\fR string (default \fB.INSERTS\fR).
- XSuccessive inserts are separated from each other in the input by a
- Xsingle empty line.
- X.I Inserts
- Xseparates
- Xsuccessive copies of the document in the output by a line
- Xconsisting entirely of the \fIoutsep\fR string (default \fB.END\fR).
- X.PP
- XIf the \fB\-i\fR option is given, the effect is as if
- Xthe contents of \fIinsfile\fR were appended to the input, with an
- X\fIend\fR line in between.
- X.SH SEE ALSO
- Xtroff(1)
- X.SH DIAGNOSTICS
- XIt is an error for there to be no insertion points in the document.
- XIn the absence of the \fB\-w\fR option, warnings are produced if there
- Xare no inserts or if the supply of inserts is exhausted midway through
- Xa copy of the document.
- X.SH HISTORY
- XWritten at U of Toronto by Henry Spencer.
- X.SH BUGS
- XA finite in-core buffer is used to hold the document, so a very large
- Xdocument will fail.
- !
- echo 'inserts.c':
- sed 's/^X//' >'inserts.c' <<'!'
- X/*
- X * inserts - process form-letter-ish insertions
- X *
- X * $Log$
- X */
- X
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <string.h>
- X
- X#define MAXSTR 500 /* For sizing strings -- DON'T use BUFSIZ! */
- X#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
- X
- X#ifndef lint
- Xstatic char RCSid[] = "$Header$";
- X#endif
- X
- X/* storage for document body */
- X#define MAXBODY 30000
- X#define MAXSEGS 100
- Xchar body[MAXBODY]; /* relies on initialization to 0 */
- Xint nbody = 0; /* chars in body */
- Xchar *segs[MAXSEGS] = { body }; /* ptrs to NUL-terminated body segments */
- Xint nsegs = 1;
- X
- X/* markers in text */
- Xchar *inspoint = ".RD"; /* insertion point in body */
- Xchar *endbody = ".INSERTS"; /* end of body */
- Xchar *separator = ".END"; /* separator between copies in output */
- Xint dowarn = 1; /* supply warnings? */
- X
- Xint debug = 0;
- Xchar *progname;
- X
- Xchar **argvp; /* scan pointer for nextfile() */
- Xchar *nullargv[] = { "-", NULL }; /* dummy argv for case of no args */
- Xchar *inname; /* filename for messages etc. */
- Xchar *filename = NULL; /* -f overrides inname for messages */
- Xlong lineno; /* line number for messages etc. */
- XFILE *in = NULL; /* current input file */
- X
- Xchar *ifile = NULL; /* -i file, if any */
- Xint istart = 0; /* just starting ifile? */
- X
- Xextern void error(), exit();
- X#ifdef UTZOOERR
- Xextern char *mkprogname();
- X#else
- X#define mkprogname(a) (a)
- X#endif
- X
- Xchar *nextfile();
- Xvoid fail();
- Xvoid getfirst();
- Xvoid getins();
- X
- X/*
- X - main - parse arguments and handle options
- X */
- Xmain(argc, argv)
- Xint argc;
- Xchar *argv[];
- X{
- X int c;
- X int errflg = 0;
- X extern int optind;
- X extern char *optarg;
- X void process();
- X
- X progname = mkprogname(argv[0]);
- X
- X while ((c = getopt(argc, argv, "p:e:s:wi:d")) != EOF)
- X switch (c) {
- X case 'p': /* insertion-point marker */
- X inspoint = optarg;
- X break;
- X case 'e': /* end-of-body marker */
- X endbody = optarg;
- X break;
- X case 's': /* output separator */
- X separator = optarg;
- X break;
- X case 'w': /* suppress warnings */
- X dowarn = 0;
- X break;
- X case 'i': /* this is file of inserts */
- X ifile = optarg;
- X break;
- X case 'd': /* Debugging. */
- X debug++;
- X break;
- X case '?':
- X default:
- X errflg++;
- X break;
- X }
- X if (errflg) {
- X fprintf(stderr, "usage: %s ", progname);
- X fprintf(stderr, "[-i inspt] [-e end] [-s outsep] [file] ...\n");
- X exit(2);
- X }
- X
- X if (optind >= argc)
- X argvp = nullargv;
- X else
- X argvp = &argv[optind];
- X inname = nextfile();
- X if (inname != NULL)
- X process();
- X exit(0);
- X}
- X
- X/*
- X - getline - get next line (internal version of fgets)
- X */
- Xchar *
- Xgetline(ptr, size)
- Xchar *ptr;
- Xint size;
- X{
- X register char *namep;
- X
- X while (fgets(ptr, size, in) == NULL) { /* while no more */
- X namep = nextfile(); /* try next source */
- X if (namep == NULL)
- X return(NULL); /* really no more */
- X inname = namep;
- X if (istart) { /* transition to ifile */
- X sprintf(ptr, "%s\n", endbody);
- X return(ptr);
- X }
- X }
- X lineno++;
- X return(ptr);
- X}
- X
- X/*
- X - nextfile - switch files
- X */
- Xchar * /* filename */
- Xnextfile()
- X{
- X register char *namep;
- X struct stat statbuf;
- X extern FILE *efopen();
- X
- X namep = *argvp;
- X if (namep == NULL) { /* no more files */
- X if (ifile == NULL)
- X return(NULL);
- X else if (istart) { /* already done the end line */
- X istart = 0;
- X namep = ifile;
- X ifile = NULL;
- X } else { /* supply end line before ifile */
- X istart = 1;
- X return(inname);
- X }
- X } else
- X argvp++;
- X
- X if (in != NULL)
- X (void) fclose(in);
- X
- X if (STREQ(namep, "-")) {
- X in = stdin;
- X namep = "stdin";
- X } else {
- X in = efopen(namep, "r");
- X if (fstat(fileno(in), &statbuf) < 0)
- X error("can't fstat `%s'", namep);
- X if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
- X error("`%s' is directory!", namep);
- X }
- X
- X lineno = 0;
- X return(namep);
- X}
- X
- X/*
- X - fail - complain and die
- X */
- Xvoid
- Xfail(s1, s2)
- Xchar *s1;
- Xchar *s2;
- X{
- X fprintf(stderr, "%s: (file `%s', line %ld) ", progname,
- X (filename != NULL) ? filename : inname, lineno);
- X fprintf(stderr, s1, s2);
- X fprintf(stderr, "\n");
- X exit(1);
- X}
- X
- X/*
- X - warn - just complain
- X */
- Xvoid
- Xwarn(s1, s2)
- Xchar *s1;
- Xchar *s2;
- X{
- X if (!dowarn)
- X return;
- X
- X fprintf(stderr, "%s: (file `%s', line %ld) (warning) ", progname,
- X (filename != NULL) ? filename : inname, lineno);
- X fprintf(stderr, s1, s2);
- X fprintf(stderr, "\n");
- X}
- X
- X/*
- X - process - process input data
- X *
- X * The MAXBODY check is over-conservative in that it will blow up a few
- X * characters too early on an insertion-point or body-end line; big deal.
- X */
- Xvoid
- Xprocess()
- X{
- X char line[MAXSTR];
- X# define LINSIZ ((int)sizeof(line))
- X register int len;
- X register int seg;
- X int firstbody; /* first time body has been output? */
- X int firstins; /* first line of first insertion in body? */
- X int firstline; /* first line of an insertion? */
- X
- X /* pick up the body */
- X while ((len = getbody(line, LINSIZ)) > 0) {
- X if (nbody + len >= MAXBODY-1) /* -1 for final NUL */
- X fail("document too large", "");
- X line[len-1] = '\0'; /* get rid of newline */
- X if (STREQ(line, inspoint)) {
- X nbody++; /* NUL ends that segment */
- X body[nbody] = '\0'; /* start new segment */
- X segs[nsegs++] = body + nbody;
- X } else {
- X line[len-1] = '\n'; /* put newline back */
- X (void) strcpy(body+nbody, line);
- X nbody += len;
- X }
- X }
- X if (nsegs <= 1)
- X fail("no insertion points (%s's) found!", inspoint);
- X
- X /* insertions */
- X firstbody = 1;
- X while (getline(line, LINSIZ) != NULL) {
- X if (!firstbody) { /* first body doesn't need separator */
- X (void) fputs(separator, stdout);
- X (void) fputs("\n", stdout);
- X }
- X firstbody = 0;
- X firstins = 1;
- X (void) fputs(segs[0], stdout);
- X for (seg = 1; seg < nsegs; seg++) {
- X firstline = 1;
- X for (;;) {
- X if (!firstins) { /* first line read */
- X if (firstline)
- X getfirst(line, LINSIZ);
- X else
- X getins(line, LINSIZ);
- X }
- X firstins = 0;
- X firstline = 0;
- X if (STREQ(line, "\n"))
- X break;
- X (void) fputs(line, stdout);
- X }
- X (void) fputs(segs[seg], stdout);
- X }
- X }
- X if (firstbody)
- X warn("no inserts supplied!", "");
- X}
- X
- X/*
- X - getbody - get a body line
- X */
- Xint /* length of line; 0 means end of body */
- Xgetbody(buf, nbuf)
- Xchar *buf;
- Xint nbuf;
- X{
- X register int len;
- X
- X if (getline(buf, nbuf) == NULL)
- X return(0);
- X len = strlen(buf);
- X if (buf[len-1] != '\n')
- X fail("line too long", "");
- X buf[len-1] = '\0';
- X if (STREQ(buf, endbody))
- X return(0);
- X buf[len-1] = '\n';
- X return(len);
- X}
- X
- X/*
- X - getfirst - get first line of insert, EOF turning into empty line
- X */
- Xvoid
- Xgetfirst(buf, nbuf)
- Xchar *buf;
- Xint nbuf;
- X{
- X static int grumped = 0;
- X
- X if (getline(buf, nbuf) == NULL) {
- X if (!grumped)
- X warn("ran out of inserts in mid-document!", "");
- X grumped = 1;
- X (void) strcpy(buf, "\n");
- X }
- X}
- X
- X/*
- X - getins - get an insert line, EOF turning into empty line
- X *
- X * like getfirst except that EOF just ends the insert
- X */
- Xvoid
- Xgetins(buf, nbuf)
- Xchar *buf;
- Xint nbuf;
- X{
- X if (getline(buf, nbuf) == NULL)
- X (void) strcpy(buf, "\n");
- X}
- !
- echo done
-
-
- --
- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
- Use a domain-based address or give alternate paths, or you may lose out.
-