home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume20 / inserts < prev    next >
Encoding:
Text File  |  1989-10-27  |  10.6 KB  |  444 lines

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