send.c revision 1590
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
351590Srgrimesstatic char sccsid[] = "@(#)send.c	8.1 (Berkeley) 6/6/93";
361590Srgrimes#endif /* not lint */
371590Srgrimes
381590Srgrimes#include "rcv.h"
391590Srgrimes#include "extern.h"
401590Srgrimes
411590Srgrimes/*
421590Srgrimes * Mail -- a mail program
431590Srgrimes *
441590Srgrimes * Mail to others.
451590Srgrimes */
461590Srgrimes
471590Srgrimes/*
481590Srgrimes * Send message described by the passed pointer to the
491590Srgrimes * passed output buffer.  Return -1 on error.
501590Srgrimes * Adjust the status: field if need be.
511590Srgrimes * If doign is given, suppress ignored header fields.
521590Srgrimes * prefix is a string to prepend to each output line.
531590Srgrimes */
541590Srgrimesint
551590Srgrimessend(mp, obuf, doign, prefix)
561590Srgrimes	register struct message *mp;
571590Srgrimes	FILE *obuf;
581590Srgrimes	struct ignoretab *doign;
591590Srgrimes	char *prefix;
601590Srgrimes{
611590Srgrimes	long count;
621590Srgrimes	register FILE *ibuf;
631590Srgrimes	char line[LINESIZE];
641590Srgrimes	int ishead, infld, ignoring, dostat, firstline;
651590Srgrimes	register char *cp, *cp2;
661590Srgrimes	register int c;
671590Srgrimes	int length;
681590Srgrimes	int prefixlen;
691590Srgrimes
701590Srgrimes	/*
711590Srgrimes	 * Compute the prefix string, without trailing whitespace
721590Srgrimes	 */
731590Srgrimes	if (prefix != NOSTR) {
741590Srgrimes		cp2 = 0;
751590Srgrimes		for (cp = prefix; *cp; cp++)
761590Srgrimes			if (*cp != ' ' && *cp != '\t')
771590Srgrimes				cp2 = cp;
781590Srgrimes		prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
791590Srgrimes	}
801590Srgrimes	ibuf = setinput(mp);
811590Srgrimes	count = mp->m_size;
821590Srgrimes	ishead = 1;
831590Srgrimes	dostat = doign == 0 || !isign("status", doign);
841590Srgrimes	infld = 0;
851590Srgrimes	firstline = 1;
861590Srgrimes	/*
871590Srgrimes	 * Process headers first
881590Srgrimes	 */
891590Srgrimes	while (count > 0 && ishead) {
901590Srgrimes		if (fgets(line, LINESIZE, ibuf) == NULL)
911590Srgrimes			break;
921590Srgrimes		count -= length = strlen(line);
931590Srgrimes		if (firstline) {
941590Srgrimes			/*
951590Srgrimes			 * First line is the From line, so no headers
961590Srgrimes			 * there to worry about
971590Srgrimes			 */
981590Srgrimes			firstline = 0;
991590Srgrimes			ignoring = doign == ignoreall;
1001590Srgrimes		} else if (line[0] == '\n') {
1011590Srgrimes			/*
1021590Srgrimes			 * If line is blank, we've reached end of
1031590Srgrimes			 * headers, so force out status: field
1041590Srgrimes			 * and note that we are no longer in header
1051590Srgrimes			 * fields
1061590Srgrimes			 */
1071590Srgrimes			if (dostat) {
1081590Srgrimes				statusput(mp, obuf, prefix);
1091590Srgrimes				dostat = 0;
1101590Srgrimes			}
1111590Srgrimes			ishead = 0;
1121590Srgrimes			ignoring = doign == ignoreall;
1131590Srgrimes		} else if (infld && (line[0] == ' ' || line[0] == '\t')) {
1141590Srgrimes			/*
1151590Srgrimes			 * If this line is a continuation (via space or tab)
1161590Srgrimes			 * of a previous header field, just echo it
1171590Srgrimes			 * (unless the field should be ignored).
1181590Srgrimes			 * In other words, nothing to do.
1191590Srgrimes			 */
1201590Srgrimes		} else {
1211590Srgrimes			/*
1221590Srgrimes			 * Pick up the header field if we have one.
1231590Srgrimes			 */
1241590Srgrimes			for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
1251590Srgrimes				;
1261590Srgrimes			cp2 = --cp;
1271590Srgrimes			while (isspace(*cp++))
1281590Srgrimes				;
1291590Srgrimes			if (cp[-1] != ':') {
1301590Srgrimes				/*
1311590Srgrimes				 * Not a header line, force out status:
1321590Srgrimes				 * This happens in uucp style mail where
1331590Srgrimes				 * there are no headers at all.
1341590Srgrimes				 */
1351590Srgrimes				if (dostat) {
1361590Srgrimes					statusput(mp, obuf, prefix);
1371590Srgrimes					dostat = 0;
1381590Srgrimes				}
1391590Srgrimes				if (doign != ignoreall)
1401590Srgrimes					/* add blank line */
1411590Srgrimes					(void) putc('\n', obuf);
1421590Srgrimes				ishead = 0;
1431590Srgrimes				ignoring = 0;
1441590Srgrimes			} else {
1451590Srgrimes				/*
1461590Srgrimes				 * If it is an ignored field and
1471590Srgrimes				 * we care about such things, skip it.
1481590Srgrimes				 */
1491590Srgrimes				*cp2 = 0;	/* temporarily null terminate */
1501590Srgrimes				if (doign && isign(line, doign))
1511590Srgrimes					ignoring = 1;
1521590Srgrimes				else if ((line[0] == 's' || line[0] == 'S') &&
1531590Srgrimes					 strcasecmp(line, "status") == 0) {
1541590Srgrimes					/*
1551590Srgrimes					 * If the field is "status," go compute
1561590Srgrimes					 * and print the real Status: field
1571590Srgrimes					 */
1581590Srgrimes					if (dostat) {
1591590Srgrimes						statusput(mp, obuf, prefix);
1601590Srgrimes						dostat = 0;
1611590Srgrimes					}
1621590Srgrimes					ignoring = 1;
1631590Srgrimes				} else {
1641590Srgrimes					ignoring = 0;
1651590Srgrimes					*cp2 = c;	/* restore */
1661590Srgrimes				}
1671590Srgrimes				infld = 1;
1681590Srgrimes			}
1691590Srgrimes		}
1701590Srgrimes		if (!ignoring) {
1711590Srgrimes			/*
1721590Srgrimes			 * Strip trailing whitespace from prefix
1731590Srgrimes			 * if line is blank.
1741590Srgrimes			 */
1751590Srgrimes			if (prefix != NOSTR)
1761590Srgrimes				if (length > 1)
1771590Srgrimes					fputs(prefix, obuf);
1781590Srgrimes				else
1791590Srgrimes					(void) fwrite(prefix, sizeof *prefix,
1801590Srgrimes							prefixlen, obuf);
1811590Srgrimes			(void) fwrite(line, sizeof *line, length, obuf);
1821590Srgrimes			if (ferror(obuf))
1831590Srgrimes				return -1;
1841590Srgrimes		}
1851590Srgrimes	}
1861590Srgrimes	/*
1871590Srgrimes	 * Copy out message body
1881590Srgrimes	 */
1891590Srgrimes	if (doign == ignoreall)
1901590Srgrimes		count--;		/* skip final blank line */
1911590Srgrimes	if (prefix != NOSTR)
1921590Srgrimes		while (count > 0) {
1931590Srgrimes			if (fgets(line, LINESIZE, ibuf) == NULL) {
1941590Srgrimes				c = 0;
1951590Srgrimes				break;
1961590Srgrimes			}
1971590Srgrimes			count -= c = strlen(line);
1981590Srgrimes			/*
1991590Srgrimes			 * Strip trailing whitespace from prefix
2001590Srgrimes			 * if line is blank.
2011590Srgrimes			 */
2021590Srgrimes			if (c > 1)
2031590Srgrimes				fputs(prefix, obuf);
2041590Srgrimes			else
2051590Srgrimes				(void) fwrite(prefix, sizeof *prefix,
2061590Srgrimes						prefixlen, obuf);
2071590Srgrimes			(void) fwrite(line, sizeof *line, c, obuf);
2081590Srgrimes			if (ferror(obuf))
2091590Srgrimes				return -1;
2101590Srgrimes		}
2111590Srgrimes	else
2121590Srgrimes		while (count > 0) {
2131590Srgrimes			c = count < LINESIZE ? count : LINESIZE;
2141590Srgrimes			if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
2151590Srgrimes				break;
2161590Srgrimes			count -= c;
2171590Srgrimes			if (fwrite(line, sizeof *line, c, obuf) != c)
2181590Srgrimes				return -1;
2191590Srgrimes		}
2201590Srgrimes	if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
2211590Srgrimes		/* no final blank line */
2221590Srgrimes		if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
2231590Srgrimes			return -1;
2241590Srgrimes	return 0;
2251590Srgrimes}
2261590Srgrimes
2271590Srgrimes/*
2281590Srgrimes * Output a reasonable looking status field.
2291590Srgrimes */
2301590Srgrimesvoid
2311590Srgrimesstatusput(mp, obuf, prefix)
2321590Srgrimes	register struct message *mp;
2331590Srgrimes	FILE *obuf;
2341590Srgrimes	char *prefix;
2351590Srgrimes{
2361590Srgrimes	char statout[3];
2371590Srgrimes	register char *cp = statout;
2381590Srgrimes
2391590Srgrimes	if (mp->m_flag & MREAD)
2401590Srgrimes		*cp++ = 'R';
2411590Srgrimes	if ((mp->m_flag & MNEW) == 0)
2421590Srgrimes		*cp++ = 'O';
2431590Srgrimes	*cp = 0;
2441590Srgrimes	if (statout[0])
2451590Srgrimes		fprintf(obuf, "%sStatus: %s\n",
2461590Srgrimes			prefix == NOSTR ? "" : prefix, statout);
2471590Srgrimes}
2481590Srgrimes
2491590Srgrimes/*
2501590Srgrimes * Interface between the argument list and the mail1 routine
2511590Srgrimes * which does all the dirty work.
2521590Srgrimes */
2531590Srgrimesint
2541590Srgrimesmail(to, cc, bcc, smopts, subject)
2551590Srgrimes	struct name *to, *cc, *bcc, *smopts;
2561590Srgrimes	char *subject;
2571590Srgrimes{
2581590Srgrimes	struct header head;
2591590Srgrimes
2601590Srgrimes	head.h_to = to;
2611590Srgrimes	head.h_subject = subject;
2621590Srgrimes	head.h_cc = cc;
2631590Srgrimes	head.h_bcc = bcc;
2641590Srgrimes	head.h_smopts = smopts;
2651590Srgrimes	mail1(&head, 0);
2661590Srgrimes	return(0);
2671590Srgrimes}
2681590Srgrimes
2691590Srgrimes
2701590Srgrimes/*
2711590Srgrimes * Send mail to a bunch of user names.  The interface is through
2721590Srgrimes * the mail routine below.
2731590Srgrimes */
2741590Srgrimesint
2751590Srgrimessendmail(str)
2761590Srgrimes	char *str;
2771590Srgrimes{
2781590Srgrimes	struct header head;
2791590Srgrimes
2801590Srgrimes	head.h_to = extract(str, GTO);
2811590Srgrimes	head.h_subject = NOSTR;
2821590Srgrimes	head.h_cc = NIL;
2831590Srgrimes	head.h_bcc = NIL;
2841590Srgrimes	head.h_smopts = NIL;
2851590Srgrimes	mail1(&head, 0);
2861590Srgrimes	return(0);
2871590Srgrimes}
2881590Srgrimes
2891590Srgrimes/*
2901590Srgrimes * Mail a message on standard input to the people indicated
2911590Srgrimes * in the passed header.  (Internal interface).
2921590Srgrimes */
2931590Srgrimesvoid
2941590Srgrimesmail1(hp, printheaders)
2951590Srgrimes	struct header *hp;
2961590Srgrimes	int printheaders;
2971590Srgrimes{
2981590Srgrimes	char *cp;
2991590Srgrimes	int pid;
3001590Srgrimes	char **namelist;
3011590Srgrimes	struct name *to;
3021590Srgrimes	FILE *mtf;
3031590Srgrimes
3041590Srgrimes	/*
3051590Srgrimes	 * Collect user's mail from standard input.
3061590Srgrimes	 * Get the result as mtf.
3071590Srgrimes	 */
3081590Srgrimes	if ((mtf = collect(hp, printheaders)) == NULL)
3091590Srgrimes		return;
3101590Srgrimes	if (value("interactive") != NOSTR)
3111590Srgrimes		if (value("askcc") != NOSTR)
3121590Srgrimes			grabh(hp, GCC);
3131590Srgrimes		else {
3141590Srgrimes			printf("EOT\n");
3151590Srgrimes			(void) fflush(stdout);
3161590Srgrimes		}
3171590Srgrimes	if (fsize(mtf) == 0)
3181590Srgrimes		if (hp->h_subject == NOSTR)
3191590Srgrimes			printf("No message, no subject; hope that's ok\n");
3201590Srgrimes		else
3211590Srgrimes			printf("Null message body; hope that's ok\n");
3221590Srgrimes	/*
3231590Srgrimes	 * Now, take the user names from the combined
3241590Srgrimes	 * to and cc lists and do all the alias
3251590Srgrimes	 * processing.
3261590Srgrimes	 */
3271590Srgrimes	senderr = 0;
3281590Srgrimes	to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
3291590Srgrimes	if (to == NIL) {
3301590Srgrimes		printf("No recipients specified\n");
3311590Srgrimes		senderr++;
3321590Srgrimes	}
3331590Srgrimes	/*
3341590Srgrimes	 * Look through the recipient list for names with /'s
3351590Srgrimes	 * in them which we write to as files directly.
3361590Srgrimes	 */
3371590Srgrimes	to = outof(to, mtf, hp);
3381590Srgrimes	if (senderr)
3391590Srgrimes		savedeadletter(mtf);
3401590Srgrimes	to = elide(to);
3411590Srgrimes	if (count(to) == 0)
3421590Srgrimes		goto out;
3431590Srgrimes	fixhead(hp, to);
3441590Srgrimes	if ((mtf = infix(hp, mtf)) == NULL) {
3451590Srgrimes		fprintf(stderr, ". . . message lost, sorry.\n");
3461590Srgrimes		return;
3471590Srgrimes	}
3481590Srgrimes	namelist = unpack(cat(hp->h_smopts, to));
3491590Srgrimes	if (debug) {
3501590Srgrimes		char **t;
3511590Srgrimes
3521590Srgrimes		printf("Sendmail arguments:");
3531590Srgrimes		for (t = namelist; *t != NOSTR; t++)
3541590Srgrimes			printf(" \"%s\"", *t);
3551590Srgrimes		printf("\n");
3561590Srgrimes		goto out;
3571590Srgrimes	}
3581590Srgrimes	if ((cp = value("record")) != NOSTR)
3591590Srgrimes		(void) savemail(expand(cp), mtf);
3601590Srgrimes	/*
3611590Srgrimes	 * Fork, set up the temporary mail file as standard
3621590Srgrimes	 * input for "mail", and exec with the user list we generated
3631590Srgrimes	 * far above.
3641590Srgrimes	 */
3651590Srgrimes	pid = fork();
3661590Srgrimes	if (pid == -1) {
3671590Srgrimes		perror("fork");
3681590Srgrimes		savedeadletter(mtf);
3691590Srgrimes		goto out;
3701590Srgrimes	}
3711590Srgrimes	if (pid == 0) {
3721590Srgrimes		prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
3731590Srgrimes			sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
3741590Srgrimes			fileno(mtf), -1);
3751590Srgrimes		if ((cp = value("sendmail")) != NOSTR)
3761590Srgrimes			cp = expand(cp);
3771590Srgrimes		else
3781590Srgrimes			cp = _PATH_SENDMAIL;
3791590Srgrimes		execv(cp, namelist);
3801590Srgrimes		perror(cp);
3811590Srgrimes		_exit(1);
3821590Srgrimes	}
3831590Srgrimes	if (value("verbose") != NOSTR)
3841590Srgrimes		(void) wait_child(pid);
3851590Srgrimes	else
3861590Srgrimes		free_child(pid);
3871590Srgrimesout:
3881590Srgrimes	(void) Fclose(mtf);
3891590Srgrimes}
3901590Srgrimes
3911590Srgrimes/*
3921590Srgrimes * Fix the header by glopping all of the expanded names from
3931590Srgrimes * the distribution list into the appropriate fields.
3941590Srgrimes */
3951590Srgrimesvoid
3961590Srgrimesfixhead(hp, tolist)
3971590Srgrimes	struct header *hp;
3981590Srgrimes	struct name *tolist;
3991590Srgrimes{
4001590Srgrimes	register struct name *np;
4011590Srgrimes
4021590Srgrimes	hp->h_to = NIL;
4031590Srgrimes	hp->h_cc = NIL;
4041590Srgrimes	hp->h_bcc = NIL;
4051590Srgrimes	for (np = tolist; np != NIL; np = np->n_flink)
4061590Srgrimes		if ((np->n_type & GMASK) == GTO)
4071590Srgrimes			hp->h_to =
4081590Srgrimes				cat(hp->h_to, nalloc(np->n_name, np->n_type));
4091590Srgrimes		else if ((np->n_type & GMASK) == GCC)
4101590Srgrimes			hp->h_cc =
4111590Srgrimes				cat(hp->h_cc, nalloc(np->n_name, np->n_type));
4121590Srgrimes		else if ((np->n_type & GMASK) == GBCC)
4131590Srgrimes			hp->h_bcc =
4141590Srgrimes				cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
4151590Srgrimes}
4161590Srgrimes
4171590Srgrimes/*
4181590Srgrimes * Prepend a header in front of the collected stuff
4191590Srgrimes * and return the new file.
4201590Srgrimes */
4211590SrgrimesFILE *
4221590Srgrimesinfix(hp, fi)
4231590Srgrimes	struct header *hp;
4241590Srgrimes	FILE *fi;
4251590Srgrimes{
4261590Srgrimes	extern char tempMail[];
4271590Srgrimes	register FILE *nfo, *nfi;
4281590Srgrimes	register int c;
4291590Srgrimes
4301590Srgrimes	if ((nfo = Fopen(tempMail, "w")) == NULL) {
4311590Srgrimes		perror(tempMail);
4321590Srgrimes		return(fi);
4331590Srgrimes	}
4341590Srgrimes	if ((nfi = Fopen(tempMail, "r")) == NULL) {
4351590Srgrimes		perror(tempMail);
4361590Srgrimes		(void) Fclose(nfo);
4371590Srgrimes		return(fi);
4381590Srgrimes	}
4391590Srgrimes	(void) rm(tempMail);
4401590Srgrimes	(void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
4411590Srgrimes	c = getc(fi);
4421590Srgrimes	while (c != EOF) {
4431590Srgrimes		(void) putc(c, nfo);
4441590Srgrimes		c = getc(fi);
4451590Srgrimes	}
4461590Srgrimes	if (ferror(fi)) {
4471590Srgrimes		perror("read");
4481590Srgrimes		rewind(fi);
4491590Srgrimes		return(fi);
4501590Srgrimes	}
4511590Srgrimes	(void) fflush(nfo);
4521590Srgrimes	if (ferror(nfo)) {
4531590Srgrimes		perror(tempMail);
4541590Srgrimes		(void) Fclose(nfo);
4551590Srgrimes		(void) Fclose(nfi);
4561590Srgrimes		rewind(fi);
4571590Srgrimes		return(fi);
4581590Srgrimes	}
4591590Srgrimes	(void) Fclose(nfo);
4601590Srgrimes	(void) Fclose(fi);
4611590Srgrimes	rewind(nfi);
4621590Srgrimes	return(nfi);
4631590Srgrimes}
4641590Srgrimes
4651590Srgrimes/*
4661590Srgrimes * Dump the to, subject, cc header on the
4671590Srgrimes * passed file buffer.
4681590Srgrimes */
4691590Srgrimesint
4701590Srgrimesputhead(hp, fo, w)
4711590Srgrimes	struct header *hp;
4721590Srgrimes	FILE *fo;
4731590Srgrimes	int w;
4741590Srgrimes{
4751590Srgrimes	register int gotcha;
4761590Srgrimes
4771590Srgrimes	gotcha = 0;
4781590Srgrimes	if (hp->h_to != NIL && w & GTO)
4791590Srgrimes		fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
4801590Srgrimes	if (hp->h_subject != NOSTR && w & GSUBJECT)
4811590Srgrimes		fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
4821590Srgrimes	if (hp->h_cc != NIL && w & GCC)
4831590Srgrimes		fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
4841590Srgrimes	if (hp->h_bcc != NIL && w & GBCC)
4851590Srgrimes		fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
4861590Srgrimes	if (gotcha && w & GNL)
4871590Srgrimes		(void) putc('\n', fo);
4881590Srgrimes	return(0);
4891590Srgrimes}
4901590Srgrimes
4911590Srgrimes/*
4921590Srgrimes * Format the given header line to not exceed 72 characters.
4931590Srgrimes */
4941590Srgrimesvoid
4951590Srgrimesfmt(str, np, fo, comma)
4961590Srgrimes	char *str;
4971590Srgrimes	register struct name *np;
4981590Srgrimes	FILE *fo;
4991590Srgrimes	int comma;
5001590Srgrimes{
5011590Srgrimes	register col, len;
5021590Srgrimes
5031590Srgrimes	comma = comma ? 1 : 0;
5041590Srgrimes	col = strlen(str);
5051590Srgrimes	if (col)
5061590Srgrimes		fputs(str, fo);
5071590Srgrimes	for (; np != NIL; np = np->n_flink) {
5081590Srgrimes		if (np->n_flink == NIL)
5091590Srgrimes			comma = 0;
5101590Srgrimes		len = strlen(np->n_name);
5111590Srgrimes		col++;		/* for the space */
5121590Srgrimes		if (col + len + comma > 72 && col > 4) {
5131590Srgrimes			fputs("\n    ", fo);
5141590Srgrimes			col = 4;
5151590Srgrimes		} else
5161590Srgrimes			putc(' ', fo);
5171590Srgrimes		fputs(np->n_name, fo);
5181590Srgrimes		if (comma)
5191590Srgrimes			putc(',', fo);
5201590Srgrimes		col += len + comma;
5211590Srgrimes	}
5221590Srgrimes	putc('\n', fo);
5231590Srgrimes}
5241590Srgrimes
5251590Srgrimes/*
5261590Srgrimes * Save the outgoing mail on the passed file.
5271590Srgrimes */
5281590Srgrimes
5291590Srgrimes/*ARGSUSED*/
5301590Srgrimesint
5311590Srgrimessavemail(name, fi)
5321590Srgrimes	char name[];
5331590Srgrimes	register FILE *fi;
5341590Srgrimes{
5351590Srgrimes	register FILE *fo;
5361590Srgrimes	char buf[BUFSIZ];
5371590Srgrimes	register i;
5381590Srgrimes	time_t now, time();
5391590Srgrimes	char *ctime();
5401590Srgrimes
5411590Srgrimes	if ((fo = Fopen(name, "a")) == NULL) {
5421590Srgrimes		perror(name);
5431590Srgrimes		return (-1);
5441590Srgrimes	}
5451590Srgrimes	(void) time(&now);
5461590Srgrimes	fprintf(fo, "From %s %s", myname, ctime(&now));
5471590Srgrimes	while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
5481590Srgrimes		(void) fwrite(buf, 1, i, fo);
5491590Srgrimes	(void) putc('\n', fo);
5501590Srgrimes	(void) fflush(fo);
5511590Srgrimes	if (ferror(fo))
5521590Srgrimes		perror(name);
5531590Srgrimes	(void) Fclose(fo);
5541590Srgrimes	rewind(fi);
5551590Srgrimes	return (0);
5561590Srgrimes}
557