[net.sources] Quicky "mail-merge" program

rs@mirror.UUCP (04/02/87)

We needed to generate a whole bunch of form letters, and it was quicker
and easier to throw together his general solution, than for me to write
a general-purpose troff .rd macro-caller.  Besides, sometimes we just
quick something straight to the lineprinter.

You should be able to specify the number of lines per field on the command
line, I suppose...
	/r$

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
#		"End of shell archive."
# Contents:  mmerge.1 mmerge.l Makefile Test.list Test.template
# Wrapped by rs@mirror on Thu Apr  2 14:19:28 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"mmerge.1\" \(2456 characters\)
if test -f mmerge.1 ; then 
  echo shar: Will not over-write existing file \"mmerge.1\"
else
sed "s/^X//" >mmerge.1 <<'END_OF_mmerge.1'
X.TH MMERGE 1 LOCAL
X\" $Header:$
X.SH NAME
Xmmerge \- ``quick and dirty'' mail-merge program.
X.SH SYNOPSIS
X.B mmerge
Xtemplate_file list_file
X.SH DESCRIPTION
X.I Mmerge
Xis a short little program designed to help generate form letters and
Xthe like.
XIt should be given two arguments:  the name of the template file, and
Xthe name of the list file.
XIf either name is a dash,
X.BR \- ,
Xstandard input is used.
XOutput is sent to standard output, presumably to be piped into
X.IR lpr (1)
Xor
X.IR ptroff (1).
X.PP
XThe template file is a normal text file, that contains special ``markers''
Xindicating where you want the input from the address list to appear.
XFor example:
X.RS
X.nf
X\&^L
X$$1$
X$$3$, $$4$
XApril 1, 1986
X
XDear $$1$:
X
XYou may already be a winner!  That's right, $$2$, you could be
Xthe first in the town of $$3$, $$4$, to win $1000.
X	.
X.sp -.5
X	.
X.sp -.5
X	.
X	Sincerely,
X
X	Ed McMahon
X.fi
X.RE
X(You would probably want to start or end the template with a formfeed if
Xyou are sending your output to a lineprinter, hence the ``^L'' as the
Xfirst line.)
X.PP
XWithin the template, there can be any number of markers of the form
X.BI $$ digits $
Xmeaning that the contents of field number
X.I digits
Xfrom the list file should be inserted at that point.
X.PP
XContinuing this example, here is what a sample list file might look
Xlike:
X.RS
X.nf
XLINES: 4
X
XRichard Salz
XRich
XCambridge
XMassachusetts
X
XJohnny Carson
XJoohhhny!
XBurbank
XCalifornia
X
X.fi
X.RE
XThe first line of the file contains the word
X.I LINES
Xin all capitals, followed by a colon and space, then the number of fields
Xin each entry.
XThe second line is blank.
XThen, each record appears, separated by a blank line.
X(Note that the file
X.I ends
Xwith a blank line, too.)
X.PP
XFinally, if the template file (shown first) is called
X.IR temp ,
Xand the list file (shown second) is called
X.IR list ,
Xthen typing:
X.RS
Xmmerge temp list | lpr
X.RE
Xwould send the following to the lineprinter:
X.RS
X.nf
X.I "(new page)"
XRichard Salz
XCambridge, Massachusetts
XApril 1, 1986
X
XDear Richard Salz:
X
XYou may already be a winner!  That's right, Rich, you could be
Xthe first in the town of Cambridge, Massachusetts, to win $1000.
X	.
X.sp -.5
X	.
X.sp -.5
X	.
X	Sincerely,
X
X	Ed McMahon
X.I "(new page)"
XJohnny Carson
XBurbank, California
XApril 1, 1986
X
XDear Johnny Carson:
X
XYou may already be a winner!  That's right, Joohhny!, you could be
Xthe first in the town of Burbank, California, to win $1000.
X	.
X.sp -.5
X	.
X.sp -.5
X	.
X	Sincerely,
X
X	Ed McMahon
X.fi
X.RE
END_OF_mmerge.1
if test 2456 -ne `wc -c <mmerge.1`; then
    echo shar: \"mmerge.1\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: Extracting \"mmerge.l\" \(2511 characters\)
if test -f mmerge.l ; then 
  echo shar: Will not over-write existing file \"mmerge.l\"
else
sed "s/^X//" >mmerge.l <<'END_OF_mmerge.l'
X
X%{
X/*
X**  MMERGE
X**  A quick and dirty "mail merge" program.
X**
X**  Sloppy programming:  we let exit() close all open files for us.
X*/
X
X#include <stdio.h>
X
X#ifndef	lint
Xstatic char	 RCS[] = "$Header: mmerge.c,v 1.1 87/04/02 10:35:34 rs REL $";
X#endif	/* lint */
X
X#define LINESIZE	99
X#define MARK		'$'
X#define NIL		(char *)NULL
X
Xstatic int	  Lines;
Xstatic char	  LINES[] = "LINES: ";
Xstatic char	**Text;
X
Xextern int	 errno;
Xextern int	 sys_nerr;
Xextern char	*sys_errlist[];
Xextern char	*calloc();
Xextern char	*index();
X
X
Xstatic void
XYelp(a, b)
X    char	*a;
X    char	*b;
X{
X    int		 e;
X
X    e = errno;
X    fprintf(stderr, "mmerge:  ");
X    fprintf(stderr, a, b);
X    fprintf(stderr, " (errno = %d, %s).\n",
X	    e, e < 0 || e > sys_nerr ? "oops" : sys_errlist[errno]);
X    exit(1);
X}
X
X
Xmain(ac, av)
X    int			  ac;
X    char		 *av[];
X{
X    register FILE	 *Input;
X    register char	 *p;
X    register int	  i;
X    char		  buff[BUFSIZ];
X
X    /* Check usage. */
X    if (ac != 3)
X	Yelp("Bad usage", NIL);
X
X    /* Open files. */
X    if ((yyin = strcmp(av[1], "-") ? fopen(av[1], "r") : stdin) == NULL)
X	Yelp("Can't open %s as template file", av[1]);
X    if ((Input = strcmp(av[2], "-") ? fopen(av[2], "r") : stdin) == NULL)
X	Yelp("Can't open %s as input file", av[2]);
X    if (yyin == stdin && Input == stdin)
X	Yelp("Both files can't be from standard input", NIL);
X
X    /* First line of Input should be "LINES: #"; second should be blank. */
X    if (fgets(buff, sizeof buff, Input) == NULL
X     || strncmp(buff, LINES, sizeof LINES - 1) != 0
X     || (Lines = atoi(buff + sizeof LINES - 1)) == 0
X     || fgets(buff, sizeof buff, Input) == NULL
X     || buff[0] != '\n')
X	Yelp("Input file has bad format!", NIL);
X
X    /* Get space for each field. */
X    if ((Text = (char **)calloc((unsigned int)Lines+1, sizeof(char *))) == NULL)
X	Yelp("calloc failure #1", NIL);
X    for (i = Lines + 1; --i >= 0; )
X	if ((Text[i] = (char *)calloc(LINESIZE, 1)) == NULL)
X	    Yelp("calloc failure #2", NIL);
X
X    while (!feof(Input)) {
X	/* Read a field array from the list file. */
X	for (i = 0; i <= Lines; i++) {
X	    if (fgets(Text[i], LINESIZE, Input) == NULL) {
X		if (i)
X		    Yelp("Premature end of input?", NIL);
X		exit(0);
X	    }
X	    if (p = index(Text[i], '\n'))
X		*p = '\0';
X	}
X
X	/* Read the template file, doing fill-ins as directed. */
X	(void)rewind(yyin);
X	yylex();
X    }
X
X    exit(0);
X}
X%}
X
X%%
X
X\$\$[0-9]+\$	{
X			int	 i;
X
X			i = atoi(&yytext[2]) - 1;
X			printf("%s", i >= 0 && i < Lines ? Text[i] : yytext);
X		}
X.		ECHO;
X
X%%
END_OF_mmerge.l
if test 2511 -ne `wc -c <mmerge.l`; then
    echo shar: \"mmerge.l\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: Extracting \"Makefile\" \(366 characters\)
if test -f Makefile ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X##
X##  MMERGE MAKEFILE
X##
X
X##  Edit appropriately.
XCFLAGS	= -O
XDESTPROG= /usr/local/bin/mmerge
XDESTMAN	= /usr/man/man1/mmerge.1
X
Xall:	mmerge lint
X
Xinstall:	all
X	cp mmerge $(DESTPROG)
X	cp mmerge.1 $(DESTMAN)
X
Xmmerge:		mmerge.c
X	$(CC) $(CFLAGS) -o mmerge mmerge.c -ll
X
Xlint:		mmerge
X	lint -hba mmerge.c >lint
X
Xclean:
X	rm -f mmerge a.out foo core tags lint mmerge.[oc]
END_OF_Makefile
if test 366 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: Extracting \"Test.list\" \(97 characters\)
if test -f Test.list ; then 
  echo shar: Will not over-write existing file \"Test.list\"
else
sed "s/^X//" >Test.list <<'END_OF_Test.list'
XLINES: 4
X
XRichard Salz
XRich
XCambridge
XMassachusetts
X
XJohnny Carson
XJoohhhny!
XBurbank
XCalifornia
X
END_OF_Test.list
if test 97 -ne `wc -c <Test.list`; then
    echo shar: \"Test.list\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: Extracting \"Test.template\" \(193 characters\)
if test -f Test.template ; then 
  echo shar: Will not over-write existing file \"Test.template\"
else
sed "s/^X//" >Test.template <<'END_OF_Test.template'
X
X$$1$
X$$3$, $$4$
XApril 1, 1986
X
XDear $$1$:
X
XYou may already be a winner!  That's right, $$2$, you could be
Xthe first in the town of $$3$, $$4$, to win $1000.
X	.
X	.
X	.
X	Sincerely,
X
X	Ed McMahon
END_OF_Test.template
if test 193 -ne `wc -c <Test.template`; then
    echo shar: \"Test.template\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
-- 
--
Rich $alz					"Drug tests p**s me off"
Mirror Systems, Cambridge Massachusetts		rs@mirror.TMC.COM
{adelie, mit-eddie, ihnp4, harvard!wjh12, cca, cbosgd, seismo}!mirror!rs