home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume20 / inserts < prev    next >
Text File  |  1989-10-26  |  11KB  |  437 lines

  1. Subject:  v20i079:  Do insertions into a document
  2. Newsgroups: comp.sources.unix,comp.text
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Henry Spencer <attcan!utzoo!henry>
  7. Posting-number: Volume 20, Issue 79
  8. Archive-name: inserts
  9.  
  10. I alluded to this in comp.text a while ago, and got a number of requests
  11. for it.  It's a program for things like form letters, where one wants to
  12. insert chunks of text into repetitions of a master document.  It does much
  13. the same things as troff's .rd request, but it is much faster, does better
  14. error checking, and cuts the troff input down to a single stream (which is
  15. much easier to use in an environment where troff jobs get batched up and
  16. fed to a server).
  17.  
  18.  
  19. echo 'README':
  20. sed 's/^X//' >'README' <<'!'
  21. XI alluded to this in comp.text a while ago, and got a number of requests
  22. Xfor it.  It's a program for things like form letters, where one wants to
  23. Xinsert chunks of text into repetitions of a master document.  It does much
  24. Xthe same things as troff's .rd request, but it is much faster, does better
  25. Xerror checking, and cuts the troff input down to a single stream (which is
  26. Xmuch easier to use in an environment where troff jobs get batched up and
  27. Xfed to a server).
  28. X
  29. X                                     Henry Spencer at U of Toronto Zoology
  30. X                                 uunet!attcan!utzoo!henry henry@zoo.toronto.edu
  31. !
  32. echo 'inserts.1':
  33. sed 's/^X//' >'inserts.1' <<'!'
  34. X.TH INSERTS 1 local
  35. X.DA 14 April 1988
  36. X.SH NAME
  37. Xinserts \- do insertions into document
  38. X.SH SYNOPSIS
  39. X.B inserts
  40. X[
  41. X.B \-p
  42. Xinspt
  43. X] [
  44. X.B \-e
  45. Xend
  46. X] [
  47. X.B \-s
  48. Xoutsep
  49. X] [
  50. X.B \-w
  51. X] [
  52. X.B \-i
  53. Xinsfile
  54. X] [ file ] ...
  55. X.SH DESCRIPTION
  56. X.I Inserts
  57. Xaccepts input from the named \fIfile\fR(s) (standard input default;
  58. X`\-' as a name means standard input also)
  59. Xconsisting of a document containing \fIinsertion points\fR,
  60. Xfollowed by a sequence of \fIinserts\fR, and produces
  61. X(on standard output) copies of the document with each insertion point
  62. Xreplaced by an insert.
  63. XInserts are used in succession until there are no more;
  64. Xthe number of copies of the document produced
  65. Xis the number needed to use up all the inserts.
  66. XIf the inserts end midway through a copy of the document,
  67. Xempty insertions are supplied until that copy is complete.
  68. X.PP
  69. XAn insertion point in the input
  70. Xis a line consisting entirely of the \fIinspt\fR string,
  71. X(default \fB.RD\fR).
  72. XIn the input, the document is separated from the inserts by
  73. Xa line
  74. Xconsisting entirely
  75. Xof the \fIend\fR string (default \fB.INSERTS\fR).
  76. XSuccessive inserts are separated from each other in the input by a
  77. Xsingle empty line.
  78. X.I Inserts
  79. Xseparates
  80. Xsuccessive copies of the document in the output by a line
  81. Xconsisting entirely of the \fIoutsep\fR string (default \fB.END\fR).
  82. X.PP
  83. XIf the \fB\-i\fR option is given, the effect is as if
  84. Xthe contents of \fIinsfile\fR were appended to the input, with an
  85. X\fIend\fR line in between.
  86. X.SH SEE ALSO
  87. Xtroff(1)
  88. X.SH DIAGNOSTICS
  89. XIt is an error for there to be no insertion points in the document.
  90. XIn the absence of the \fB\-w\fR option, warnings are produced if there
  91. Xare no inserts or if the supply of inserts is exhausted midway through
  92. Xa copy of the document.
  93. X.SH HISTORY
  94. XWritten at U of Toronto by Henry Spencer.
  95. X.SH BUGS
  96. XA finite in-core buffer is used to hold the document, so a very large
  97. Xdocument will fail.
  98. !
  99. echo 'inserts.c':
  100. sed 's/^X//' >'inserts.c' <<'!'
  101. X/*
  102. X * inserts - process form-letter-ish insertions
  103. X *
  104. X * $Log$
  105. X */
  106. X
  107. X#include <stdio.h>
  108. X#include <sys/types.h>
  109. X#include <sys/stat.h>
  110. X#include <string.h>
  111. X
  112. X#define    MAXSTR    500        /* For sizing strings -- DON'T use BUFSIZ! */
  113. X#define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
  114. X
  115. X#ifndef lint
  116. Xstatic char RCSid[] = "$Header$";
  117. X#endif
  118. X
  119. X/* storage for document body */
  120. X#define    MAXBODY    30000
  121. X#define    MAXSEGS    100
  122. Xchar body[MAXBODY];        /* relies on initialization to 0 */
  123. Xint nbody = 0;            /* chars in body */
  124. Xchar *segs[MAXSEGS] = { body };    /* ptrs to NUL-terminated body segments */
  125. Xint nsegs = 1;
  126. X
  127. X/* markers in text */
  128. Xchar *inspoint = ".RD";        /* insertion point in body */
  129. Xchar *endbody = ".INSERTS";    /* end of body */
  130. Xchar *separator = ".END";    /* separator between copies in output */
  131. Xint dowarn = 1;            /* supply warnings? */
  132. X
  133. Xint debug = 0;
  134. Xchar *progname;
  135. X
  136. Xchar **argvp;                /* scan pointer for nextfile() */
  137. Xchar *nullargv[] = { "-", NULL };    /* dummy argv for case of no args */
  138. Xchar *inname;                /* filename for messages etc. */
  139. Xchar *filename = NULL;            /* -f overrides inname for messages */
  140. Xlong lineno;                /* line number for messages etc. */
  141. XFILE *in = NULL;            /* current input file */
  142. X
  143. Xchar *ifile = NULL;            /* -i file, if any */
  144. Xint istart = 0;                /* just starting ifile? */
  145. X
  146. Xextern void error(), exit();
  147. X#ifdef UTZOOERR
  148. Xextern char *mkprogname();
  149. X#else
  150. X#define    mkprogname(a)    (a)
  151. X#endif
  152. X
  153. Xchar *nextfile();
  154. Xvoid fail();
  155. Xvoid getfirst();
  156. Xvoid getins();
  157. X
  158. X/*
  159. X - main - parse arguments and handle options
  160. X */
  161. Xmain(argc, argv)
  162. Xint argc;
  163. Xchar *argv[];
  164. X{
  165. X    int c;
  166. X    int errflg = 0;
  167. X    extern int optind;
  168. X    extern char *optarg;
  169. X    void process();
  170. X
  171. X    progname = mkprogname(argv[0]);
  172. X
  173. X    while ((c = getopt(argc, argv, "p:e:s:wi:d")) != EOF)
  174. X        switch (c) {
  175. X        case 'p':    /* insertion-point marker */
  176. X            inspoint = optarg;
  177. X            break;
  178. X        case 'e':    /* end-of-body marker */
  179. X            endbody = optarg;
  180. X            break;
  181. X        case 's':    /* output separator */
  182. X            separator = optarg;
  183. X            break;
  184. X        case 'w':    /* suppress warnings */
  185. X            dowarn = 0;
  186. X            break;
  187. X        case 'i':    /* this is file of inserts */
  188. X            ifile = optarg;
  189. X            break;
  190. X        case 'd':    /* Debugging. */
  191. X            debug++;
  192. X            break;
  193. X        case '?':
  194. X        default:
  195. X            errflg++;
  196. X            break;
  197. X        }
  198. X    if (errflg) {
  199. X        fprintf(stderr, "usage: %s ", progname);
  200. X        fprintf(stderr, "[-i inspt] [-e end] [-s outsep] [file] ...\n");
  201. X        exit(2);
  202. X    }
  203. X
  204. X    if (optind >= argc)
  205. X        argvp = nullargv;
  206. X    else
  207. X        argvp = &argv[optind];
  208. X    inname = nextfile();
  209. X    if (inname != NULL)
  210. X        process();
  211. X    exit(0);
  212. X}
  213. X
  214. X/*
  215. X - getline - get next line (internal version of fgets)
  216. X */
  217. Xchar *
  218. Xgetline(ptr, size)
  219. Xchar *ptr;
  220. Xint size;
  221. X{
  222. X    register char *namep;
  223. X
  224. X    while (fgets(ptr, size, in) == NULL) {    /* while no more */
  225. X        namep = nextfile();        /* try next source */
  226. X        if (namep == NULL)
  227. X            return(NULL);        /* really no more */
  228. X        inname = namep;
  229. X        if (istart) {            /* transition to ifile */
  230. X            sprintf(ptr, "%s\n", endbody);
  231. X            return(ptr);
  232. X        }
  233. X    }
  234. X    lineno++;
  235. X    return(ptr);
  236. X}
  237. X
  238. X/*
  239. X - nextfile - switch files
  240. X */
  241. Xchar *                /* filename */
  242. Xnextfile()
  243. X{
  244. X    register char *namep;
  245. X    struct stat statbuf;
  246. X    extern FILE *efopen();
  247. X
  248. X    namep = *argvp;
  249. X    if (namep == NULL) {    /* no more files */
  250. X        if (ifile == NULL)
  251. X            return(NULL);
  252. X        else if (istart) {    /* already done the end line */
  253. X            istart = 0;
  254. X            namep = ifile;
  255. X            ifile = NULL;
  256. X        } else {        /* supply end line before ifile */
  257. X            istart = 1;
  258. X            return(inname);
  259. X        }
  260. X    } else
  261. X        argvp++;
  262. X
  263. X    if (in != NULL)
  264. X        (void) fclose(in);
  265. X
  266. X    if (STREQ(namep, "-")) {
  267. X        in = stdin;
  268. X        namep = "stdin";
  269. X    } else {
  270. X        in = efopen(namep, "r");
  271. X        if (fstat(fileno(in), &statbuf) < 0)
  272. X            error("can't fstat `%s'", namep);
  273. X        if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
  274. X            error("`%s' is directory!", namep);
  275. X    }
  276. X
  277. X    lineno = 0;
  278. X    return(namep);
  279. X}
  280. X
  281. X/*
  282. X - fail - complain and die
  283. X */
  284. Xvoid
  285. Xfail(s1, s2)
  286. Xchar *s1;
  287. Xchar *s2;
  288. X{
  289. X    fprintf(stderr, "%s: (file `%s', line %ld) ", progname,
  290. X            (filename != NULL) ? filename : inname, lineno);
  291. X    fprintf(stderr, s1, s2);
  292. X    fprintf(stderr, "\n");
  293. X    exit(1);
  294. X}
  295. X
  296. X/*
  297. X - warn - just complain
  298. X */
  299. Xvoid
  300. Xwarn(s1, s2)
  301. Xchar *s1;
  302. Xchar *s2;
  303. X{
  304. X    if (!dowarn)
  305. X        return;
  306. X
  307. X    fprintf(stderr, "%s: (file `%s', line %ld) (warning) ", progname,
  308. X            (filename != NULL) ? filename : inname, lineno);
  309. X    fprintf(stderr, s1, s2);
  310. X    fprintf(stderr, "\n");
  311. X}
  312. X
  313. X/*
  314. X - process - process input data
  315. X *
  316. X * The MAXBODY check is over-conservative in that it will blow up a few
  317. X * characters too early on an insertion-point or body-end line; big deal.
  318. X */
  319. Xvoid
  320. Xprocess()
  321. X{
  322. X    char line[MAXSTR];
  323. X#    define    LINSIZ    ((int)sizeof(line))
  324. X    register int len;
  325. X    register int seg;
  326. X    int firstbody;        /* first time body has been output? */
  327. X    int firstins;        /* first line of first insertion in body? */
  328. X    int firstline;        /* first line of an insertion? */
  329. X
  330. X    /* pick up the body */
  331. X    while ((len = getbody(line, LINSIZ)) > 0) {
  332. X        if (nbody + len >= MAXBODY-1)    /* -1 for final NUL */
  333. X            fail("document too large", "");
  334. X        line[len-1] = '\0';        /* get rid of newline */
  335. X        if (STREQ(line, inspoint)) {
  336. X            nbody++;        /* NUL ends that segment */
  337. X            body[nbody] = '\0';    /* start new segment */
  338. X            segs[nsegs++] = body + nbody;
  339. X        } else {
  340. X            line[len-1] = '\n';    /* put newline back */
  341. X            (void) strcpy(body+nbody, line);
  342. X            nbody += len;
  343. X        }
  344. X    }
  345. X    if (nsegs <= 1)
  346. X        fail("no insertion points (%s's) found!", inspoint);
  347. X
  348. X    /* insertions */
  349. X    firstbody = 1;
  350. X    while (getline(line, LINSIZ) != NULL) {
  351. X        if (!firstbody) {    /* first body doesn't need separator */
  352. X            (void) fputs(separator, stdout);
  353. X            (void) fputs("\n", stdout);
  354. X        }
  355. X        firstbody = 0;
  356. X        firstins = 1;
  357. X        (void) fputs(segs[0], stdout);
  358. X        for (seg = 1; seg < nsegs; seg++) {
  359. X            firstline = 1;
  360. X            for (;;) {
  361. X                if (!firstins) {    /* first line read */
  362. X                    if (firstline)
  363. X                        getfirst(line, LINSIZ);
  364. X                    else
  365. X                        getins(line, LINSIZ);
  366. X                }
  367. X                firstins = 0;
  368. X                firstline = 0;
  369. X                if (STREQ(line, "\n"))
  370. X                    break;
  371. X                (void) fputs(line, stdout);
  372. X            }
  373. X            (void) fputs(segs[seg], stdout);
  374. X        }
  375. X    }
  376. X    if (firstbody)
  377. X        warn("no inserts supplied!", "");
  378. X}
  379. X
  380. X/*
  381. X - getbody - get a body line
  382. X */
  383. Xint                /* length of line; 0 means end of body */
  384. Xgetbody(buf, nbuf)
  385. Xchar *buf;
  386. Xint nbuf;
  387. X{
  388. X    register int len;
  389. X
  390. X    if (getline(buf, nbuf) == NULL)
  391. X        return(0);
  392. X    len = strlen(buf);
  393. X    if (buf[len-1] != '\n')
  394. X        fail("line too long", "");
  395. X    buf[len-1] = '\0';
  396. X    if (STREQ(buf, endbody))
  397. X        return(0);
  398. X    buf[len-1] = '\n';
  399. X    return(len);
  400. X}
  401. X
  402. X/*
  403. X - getfirst - get first line of insert, EOF turning into empty line
  404. X */
  405. Xvoid
  406. Xgetfirst(buf, nbuf)
  407. Xchar *buf;
  408. Xint nbuf;
  409. X{
  410. X    static int grumped = 0;
  411. X
  412. X    if (getline(buf, nbuf) == NULL) {
  413. X        if (!grumped)
  414. X            warn("ran out of inserts in mid-document!", "");
  415. X        grumped = 1;
  416. X        (void) strcpy(buf, "\n");
  417. X    }
  418. X}
  419. X
  420. X/*
  421. X - getins - get an insert line, EOF turning into empty line
  422. X *
  423. X * like getfirst except that EOF just ends the insert
  424. X */
  425. Xvoid
  426. Xgetins(buf, nbuf)
  427. Xchar *buf;
  428. Xint nbuf;
  429. X{
  430. X    if (getline(buf, nbuf) == NULL)
  431. X        (void) strcpy(buf, "\n");
  432. X}
  433. !
  434. echo done
  435.  
  436.  
  437.