send.c revision 77274
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 3574769Smikeh#if 0 361590Srgrimesstatic char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93"; 3774769Smikeh#endif 3874769Smikehstatic const char rcsid[] = 3974769Smikeh "$FreeBSD: head/usr.bin/mail/send.c 77274 2001-05-27 20:26:22Z mikeh $"; 401590Srgrimes#endif /* not lint */ 411590Srgrimes 421590Srgrimes#include "rcv.h" 431590Srgrimes#include "extern.h" 441590Srgrimes 451590Srgrimes/* 461590Srgrimes * Mail -- a mail program 471590Srgrimes * 481590Srgrimes * Mail to others. 491590Srgrimes */ 501590Srgrimes 511590Srgrimes/* 521590Srgrimes * Send message described by the passed pointer to the 531590Srgrimes * passed output buffer. Return -1 on error. 541590Srgrimes * Adjust the status: field if need be. 551590Srgrimes * If doign is given, suppress ignored header fields. 561590Srgrimes * prefix is a string to prepend to each output line. 571590Srgrimes */ 581590Srgrimesint 5974769Smikehsendmessage(mp, obuf, doign, prefix) 6077274Smikeh struct message *mp; 611590Srgrimes FILE *obuf; 621590Srgrimes struct ignoretab *doign; 631590Srgrimes char *prefix; 641590Srgrimes{ 651590Srgrimes long count; 6677274Smikeh FILE *ibuf; 6777274Smikeh char *cp, *cp2, line[LINESIZE]; 681590Srgrimes int ishead, infld, ignoring, dostat, firstline; 6977274Smikeh int c, length, prefixlen; 701590Srgrimes 711590Srgrimes /* 721590Srgrimes * Compute the prefix string, without trailing whitespace 731590Srgrimes */ 7477274Smikeh if (prefix != NULL) { 751590Srgrimes cp2 = 0; 7677274Smikeh for (cp = prefix; *cp != '\0'; cp++) 771590Srgrimes if (*cp != ' ' && *cp != '\t') 781590Srgrimes cp2 = cp; 7977274Smikeh prefixlen = cp2 == NULL ? 0 : cp2 - prefix + 1; 801590Srgrimes } 811590Srgrimes ibuf = setinput(mp); 821590Srgrimes count = mp->m_size; 831590Srgrimes ishead = 1; 841590Srgrimes dostat = doign == 0 || !isign("status", doign); 851590Srgrimes infld = 0; 861590Srgrimes firstline = 1; 871590Srgrimes /* 881590Srgrimes * Process headers first 891590Srgrimes */ 901590Srgrimes while (count > 0 && ishead) { 9174769Smikeh if (fgets(line, sizeof(line), ibuf) == NULL) 921590Srgrimes break; 931590Srgrimes count -= length = strlen(line); 941590Srgrimes if (firstline) { 958874Srgrimes /* 961590Srgrimes * First line is the From line, so no headers 971590Srgrimes * there to worry about 981590Srgrimes */ 991590Srgrimes firstline = 0; 1001590Srgrimes ignoring = doign == ignoreall; 1011590Srgrimes } else if (line[0] == '\n') { 1021590Srgrimes /* 1031590Srgrimes * If line is blank, we've reached end of 1041590Srgrimes * headers, so force out status: field 1051590Srgrimes * and note that we are no longer in header 1061590Srgrimes * fields 1071590Srgrimes */ 1081590Srgrimes if (dostat) { 1091590Srgrimes statusput(mp, obuf, prefix); 1101590Srgrimes dostat = 0; 1111590Srgrimes } 1121590Srgrimes ishead = 0; 1131590Srgrimes ignoring = doign == ignoreall; 1141590Srgrimes } else if (infld && (line[0] == ' ' || line[0] == '\t')) { 1151590Srgrimes /* 1161590Srgrimes * If this line is a continuation (via space or tab) 1171590Srgrimes * of a previous header field, just echo it 1181590Srgrimes * (unless the field should be ignored). 1191590Srgrimes * In other words, nothing to do. 1201590Srgrimes */ 1211590Srgrimes } else { 1221590Srgrimes /* 1231590Srgrimes * Pick up the header field if we have one. 1241590Srgrimes */ 12577274Smikeh for (cp = line; (c = *cp++) != '\0' && c != ':' && 12677274Smikeh !isspace(c);) 1271590Srgrimes ; 1281590Srgrimes cp2 = --cp; 1291590Srgrimes while (isspace(*cp++)) 1301590Srgrimes ; 1311590Srgrimes if (cp[-1] != ':') { 1321590Srgrimes /* 1331590Srgrimes * Not a header line, force out status: 1341590Srgrimes * This happens in uucp style mail where 1351590Srgrimes * there are no headers at all. 1361590Srgrimes */ 1371590Srgrimes if (dostat) { 1381590Srgrimes statusput(mp, obuf, prefix); 1391590Srgrimes dostat = 0; 1401590Srgrimes } 1411590Srgrimes if (doign != ignoreall) 1421590Srgrimes /* add blank line */ 14377274Smikeh (void)putc('\n', obuf); 1441590Srgrimes ishead = 0; 1451590Srgrimes ignoring = 0; 1461590Srgrimes } else { 1471590Srgrimes /* 1481590Srgrimes * If it is an ignored field and 1491590Srgrimes * we care about such things, skip it. 1501590Srgrimes */ 15177274Smikeh *cp2 = '\0'; /* temporarily null terminate */ 1521590Srgrimes if (doign && isign(line, doign)) 1531590Srgrimes ignoring = 1; 1541590Srgrimes else if ((line[0] == 's' || line[0] == 'S') && 1551590Srgrimes strcasecmp(line, "status") == 0) { 1561590Srgrimes /* 1571590Srgrimes * If the field is "status," go compute 1581590Srgrimes * and print the real Status: field 1591590Srgrimes */ 1601590Srgrimes if (dostat) { 1611590Srgrimes statusput(mp, obuf, prefix); 1621590Srgrimes dostat = 0; 1631590Srgrimes } 1641590Srgrimes ignoring = 1; 1651590Srgrimes } else { 1661590Srgrimes ignoring = 0; 1671590Srgrimes *cp2 = c; /* restore */ 1681590Srgrimes } 1691590Srgrimes infld = 1; 1701590Srgrimes } 1711590Srgrimes } 1721590Srgrimes if (!ignoring) { 1731590Srgrimes /* 1741590Srgrimes * Strip trailing whitespace from prefix 1751590Srgrimes * if line is blank. 1761590Srgrimes */ 17777274Smikeh if (prefix != NULL) { 1781590Srgrimes if (length > 1) 1791590Srgrimes fputs(prefix, obuf); 1801590Srgrimes else 18177274Smikeh (void)fwrite(prefix, sizeof(*prefix), 18277274Smikeh prefixlen, obuf); 18374769Smikeh } 18477274Smikeh (void)fwrite(line, sizeof(*line), length, obuf); 1851590Srgrimes if (ferror(obuf)) 18677274Smikeh return (-1); 1871590Srgrimes } 1881590Srgrimes } 1891590Srgrimes /* 1901590Srgrimes * Copy out message body 1911590Srgrimes */ 1921590Srgrimes if (doign == ignoreall) 1931590Srgrimes count--; /* skip final blank line */ 19477274Smikeh if (prefix != NULL) 1951590Srgrimes while (count > 0) { 19674769Smikeh if (fgets(line, sizeof(line), ibuf) == NULL) { 1971590Srgrimes c = 0; 1981590Srgrimes break; 1991590Srgrimes } 2001590Srgrimes count -= c = strlen(line); 2011590Srgrimes /* 2021590Srgrimes * Strip trailing whitespace from prefix 2031590Srgrimes * if line is blank. 2041590Srgrimes */ 2051590Srgrimes if (c > 1) 2061590Srgrimes fputs(prefix, obuf); 2071590Srgrimes else 20877274Smikeh (void)fwrite(prefix, sizeof(*prefix), 20977274Smikeh prefixlen, obuf); 21077274Smikeh (void)fwrite(line, sizeof(*line), c, obuf); 2111590Srgrimes if (ferror(obuf)) 21277274Smikeh return (-1); 2131590Srgrimes } 2141590Srgrimes else 2151590Srgrimes while (count > 0) { 2161590Srgrimes c = count < LINESIZE ? count : LINESIZE; 21777274Smikeh if ((c = fread(line, sizeof(*line), c, ibuf)) <= 0) 2181590Srgrimes break; 2191590Srgrimes count -= c; 22077274Smikeh if (fwrite(line, sizeof(*line), c, obuf) != c) 22177274Smikeh return (-1); 2221590Srgrimes } 2231590Srgrimes if (doign == ignoreall && c > 0 && line[c - 1] != '\n') 2241590Srgrimes /* no final blank line */ 2251590Srgrimes if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 22677274Smikeh return (-1); 22777274Smikeh return (0); 2281590Srgrimes} 2291590Srgrimes 2301590Srgrimes/* 2311590Srgrimes * Output a reasonable looking status field. 2321590Srgrimes */ 2331590Srgrimesvoid 2341590Srgrimesstatusput(mp, obuf, prefix) 23577274Smikeh struct message *mp; 2361590Srgrimes FILE *obuf; 2371590Srgrimes char *prefix; 2381590Srgrimes{ 2391590Srgrimes char statout[3]; 24077274Smikeh char *cp = statout; 2411590Srgrimes 2421590Srgrimes if (mp->m_flag & MREAD) 2431590Srgrimes *cp++ = 'R'; 2441590Srgrimes if ((mp->m_flag & MNEW) == 0) 2451590Srgrimes *cp++ = 'O'; 24677274Smikeh *cp = '\0'; 24777274Smikeh if (statout[0] != '\0') 2481590Srgrimes fprintf(obuf, "%sStatus: %s\n", 24977274Smikeh prefix == NULL ? "" : prefix, statout); 2501590Srgrimes} 2511590Srgrimes 2521590Srgrimes/* 2531590Srgrimes * Interface between the argument list and the mail1 routine 2541590Srgrimes * which does all the dirty work. 2551590Srgrimes */ 2561590Srgrimesint 25732189Sjoergmail(to, cc, bcc, smopts, subject, replyto) 2581590Srgrimes struct name *to, *cc, *bcc, *smopts; 25932189Sjoerg char *subject, *replyto; 2601590Srgrimes{ 2611590Srgrimes struct header head; 2621590Srgrimes 2631590Srgrimes head.h_to = to; 2641590Srgrimes head.h_subject = subject; 2651590Srgrimes head.h_cc = cc; 2661590Srgrimes head.h_bcc = bcc; 2671590Srgrimes head.h_smopts = smopts; 26832189Sjoerg head.h_replyto = replyto; 26977274Smikeh head.h_inreplyto = NULL; 2701590Srgrimes mail1(&head, 0); 27177274Smikeh return (0); 2721590Srgrimes} 2731590Srgrimes 2741590Srgrimes 2751590Srgrimes/* 2761590Srgrimes * Send mail to a bunch of user names. The interface is through 2771590Srgrimes * the mail routine below. 2781590Srgrimes */ 2791590Srgrimesint 2801590Srgrimessendmail(str) 2811590Srgrimes char *str; 2821590Srgrimes{ 2831590Srgrimes struct header head; 2841590Srgrimes 2851590Srgrimes head.h_to = extract(str, GTO); 28677274Smikeh head.h_subject = NULL; 28777274Smikeh head.h_cc = NULL; 28877274Smikeh head.h_bcc = NULL; 28977274Smikeh head.h_smopts = NULL; 29032189Sjoerg if ((head.h_replyto = getenv("REPLYTO")) == NULL) 29177274Smikeh head.h_replyto = NULL; 29277274Smikeh head.h_inreplyto = NULL; 2931590Srgrimes mail1(&head, 0); 29477274Smikeh return (0); 2951590Srgrimes} 2961590Srgrimes 2971590Srgrimes/* 2981590Srgrimes * Mail a message on standard input to the people indicated 2991590Srgrimes * in the passed header. (Internal interface). 3001590Srgrimes */ 3011590Srgrimesvoid 3021590Srgrimesmail1(hp, printheaders) 3031590Srgrimes struct header *hp; 3041590Srgrimes int printheaders; 3051590Srgrimes{ 3061590Srgrimes char *cp; 3071590Srgrimes int pid; 3081590Srgrimes char **namelist; 3091590Srgrimes struct name *to; 3101590Srgrimes FILE *mtf; 3111590Srgrimes 3121590Srgrimes /* 3131590Srgrimes * Collect user's mail from standard input. 3141590Srgrimes * Get the result as mtf. 3151590Srgrimes */ 3161590Srgrimes if ((mtf = collect(hp, printheaders)) == NULL) 3171590Srgrimes return; 31877274Smikeh if (value("interactive") != NULL) { 31977274Smikeh if (value("askcc") != NULL) 3201590Srgrimes grabh(hp, GCC); 3211590Srgrimes else { 3221590Srgrimes printf("EOT\n"); 32377274Smikeh (void)fflush(stdout); 3241590Srgrimes } 32574769Smikeh } 32674769Smikeh if (fsize(mtf) == 0) { 32777274Smikeh if (hp->h_subject == NULL) 3281590Srgrimes printf("No message, no subject; hope that's ok\n"); 3291590Srgrimes else 3301590Srgrimes printf("Null message body; hope that's ok\n"); 33174769Smikeh } 3321590Srgrimes /* 3331590Srgrimes * Now, take the user names from the combined 3341590Srgrimes * to and cc lists and do all the alias 3351590Srgrimes * processing. 3361590Srgrimes */ 3371590Srgrimes senderr = 0; 3381590Srgrimes to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 33977274Smikeh if (to == NULL) { 3401590Srgrimes printf("No recipients specified\n"); 3411590Srgrimes senderr++; 3421590Srgrimes } 3431590Srgrimes /* 3441590Srgrimes * Look through the recipient list for names with /'s 3451590Srgrimes * in them which we write to as files directly. 3461590Srgrimes */ 3471590Srgrimes to = outof(to, mtf, hp); 3481590Srgrimes if (senderr) 3491590Srgrimes savedeadletter(mtf); 3501590Srgrimes to = elide(to); 3511590Srgrimes if (count(to) == 0) 3521590Srgrimes goto out; 3531590Srgrimes fixhead(hp, to); 3541590Srgrimes if ((mtf = infix(hp, mtf)) == NULL) { 3551590Srgrimes fprintf(stderr, ". . . message lost, sorry.\n"); 3561590Srgrimes return; 3571590Srgrimes } 3581590Srgrimes namelist = unpack(cat(hp->h_smopts, to)); 3591590Srgrimes if (debug) { 3601590Srgrimes char **t; 3611590Srgrimes 3621590Srgrimes printf("Sendmail arguments:"); 36377274Smikeh for (t = namelist; *t != NULL; t++) 3641590Srgrimes printf(" \"%s\"", *t); 3651590Srgrimes printf("\n"); 3661590Srgrimes goto out; 3671590Srgrimes } 36877274Smikeh if ((cp = value("record")) != NULL) 36977274Smikeh (void)savemail(expand(cp), mtf); 3701590Srgrimes /* 3711590Srgrimes * Fork, set up the temporary mail file as standard 3721590Srgrimes * input for "mail", and exec with the user list we generated 3731590Srgrimes * far above. 3741590Srgrimes */ 3751590Srgrimes pid = fork(); 3761590Srgrimes if (pid == -1) { 37774769Smikeh warn("fork"); 3781590Srgrimes savedeadletter(mtf); 3791590Srgrimes goto out; 3801590Srgrimes } 3811590Srgrimes if (pid == 0) { 3821590Srgrimes prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)| 3831590Srgrimes sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU), 3841590Srgrimes fileno(mtf), -1); 38577274Smikeh if ((cp = value("sendmail")) != NULL) 3861590Srgrimes cp = expand(cp); 3871590Srgrimes else 3881590Srgrimes cp = _PATH_SENDMAIL; 3891590Srgrimes execv(cp, namelist); 39074769Smikeh warn("%s", cp); 3911590Srgrimes _exit(1); 3921590Srgrimes } 39377274Smikeh if (value("verbose") != NULL) 39477274Smikeh (void)wait_child(pid); 3951590Srgrimes else 3961590Srgrimes free_child(pid); 3971590Srgrimesout: 39877274Smikeh (void)Fclose(mtf); 3991590Srgrimes} 4001590Srgrimes 4011590Srgrimes/* 4021590Srgrimes * Fix the header by glopping all of the expanded names from 4031590Srgrimes * the distribution list into the appropriate fields. 4041590Srgrimes */ 4051590Srgrimesvoid 4061590Srgrimesfixhead(hp, tolist) 4071590Srgrimes struct header *hp; 4081590Srgrimes struct name *tolist; 4091590Srgrimes{ 41077274Smikeh struct name *np; 4111590Srgrimes 41277274Smikeh hp->h_to = NULL; 41377274Smikeh hp->h_cc = NULL; 41477274Smikeh hp->h_bcc = NULL; 41577274Smikeh for (np = tolist; np != NULL; np = np->n_flink) 4161590Srgrimes if ((np->n_type & GMASK) == GTO) 4171590Srgrimes hp->h_to = 41877274Smikeh cat(hp->h_to, nalloc(np->n_name, np->n_type)); 4191590Srgrimes else if ((np->n_type & GMASK) == GCC) 4201590Srgrimes hp->h_cc = 42177274Smikeh cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 4221590Srgrimes else if ((np->n_type & GMASK) == GBCC) 4231590Srgrimes hp->h_bcc = 42477274Smikeh cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 4251590Srgrimes} 4261590Srgrimes 4271590Srgrimes/* 4281590Srgrimes * Prepend a header in front of the collected stuff 4291590Srgrimes * and return the new file. 4301590Srgrimes */ 4311590SrgrimesFILE * 4321590Srgrimesinfix(hp, fi) 4331590Srgrimes struct header *hp; 4341590Srgrimes FILE *fi; 4351590Srgrimes{ 43677274Smikeh FILE *nfo, *nfi; 43777274Smikeh int c, fd; 43874769Smikeh char tempname[PATHSIZE]; 4391590Srgrimes 44077274Smikeh (void)snprintf(tempname, sizeof(tempname), 44177274Smikeh "%s/mail.RsXXXXXXXXXX", tmpdir); 44274769Smikeh if ((fd = mkstemp(tempname)) == -1 || 44374769Smikeh (nfo = Fdopen(fd, "w")) == NULL) { 44474769Smikeh warn("%s", tempname); 44577274Smikeh return (fi); 4461590Srgrimes } 44774769Smikeh if ((nfi = Fopen(tempname, "r")) == NULL) { 44874769Smikeh warn("%s", tempname); 44977274Smikeh (void)Fclose(nfo); 45077274Smikeh (void)rm(tempname); 45177274Smikeh return (fi); 4521590Srgrimes } 45377274Smikeh (void)rm(tempname); 45477274Smikeh (void)puthead(hp, nfo, 45577274Smikeh GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA); 4561590Srgrimes c = getc(fi); 4571590Srgrimes while (c != EOF) { 45877274Smikeh (void)putc(c, nfo); 4591590Srgrimes c = getc(fi); 4601590Srgrimes } 4611590Srgrimes if (ferror(fi)) { 46274769Smikeh warnx("read"); 4631590Srgrimes rewind(fi); 46477274Smikeh return (fi); 4651590Srgrimes } 46677274Smikeh (void)fflush(nfo); 4671590Srgrimes if (ferror(nfo)) { 46874769Smikeh warn("%s", tempname); 46977274Smikeh (void)Fclose(nfo); 47077274Smikeh (void)Fclose(nfi); 4711590Srgrimes rewind(fi); 47277274Smikeh return (fi); 4731590Srgrimes } 47477274Smikeh (void)Fclose(nfo); 47577274Smikeh (void)Fclose(fi); 4761590Srgrimes rewind(nfi); 47777274Smikeh return (nfi); 4781590Srgrimes} 4791590Srgrimes 4801590Srgrimes/* 4811590Srgrimes * Dump the to, subject, cc header on the 4821590Srgrimes * passed file buffer. 4831590Srgrimes */ 4841590Srgrimesint 4851590Srgrimesputhead(hp, fo, w) 4861590Srgrimes struct header *hp; 4871590Srgrimes FILE *fo; 4881590Srgrimes int w; 4891590Srgrimes{ 49077274Smikeh int gotcha; 4911590Srgrimes 4921590Srgrimes gotcha = 0; 49377274Smikeh if (hp->h_to != NULL && w & GTO) 4941590Srgrimes fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 49577274Smikeh if (hp->h_subject != NULL && w & GSUBJECT) 4961590Srgrimes fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 49777274Smikeh if (hp->h_cc != NULL && w & GCC) 4981590Srgrimes fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 49977274Smikeh if (hp->h_bcc != NULL && w & GBCC) 5001590Srgrimes fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 50177274Smikeh if (hp->h_replyto != NULL && w & GREPLYTO) 50232189Sjoerg fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++; 50377274Smikeh if (hp->h_inreplyto != NULL && w & GINREPLYTO) 50432189Sjoerg fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++; 5051590Srgrimes if (gotcha && w & GNL) 50677274Smikeh (void)putc('\n', fo); 50777274Smikeh return (0); 5081590Srgrimes} 5091590Srgrimes 5101590Srgrimes/* 5111590Srgrimes * Format the given header line to not exceed 72 characters. 5121590Srgrimes */ 5131590Srgrimesvoid 5141590Srgrimesfmt(str, np, fo, comma) 51577274Smikeh const char *str; 51677274Smikeh struct name *np; 5171590Srgrimes FILE *fo; 5181590Srgrimes int comma; 5191590Srgrimes{ 52077274Smikeh int col, len; 5211590Srgrimes 5221590Srgrimes comma = comma ? 1 : 0; 5231590Srgrimes col = strlen(str); 5241590Srgrimes if (col) 5251590Srgrimes fputs(str, fo); 52677274Smikeh for (; np != NULL; np = np->n_flink) { 52777274Smikeh if (np->n_flink == NULL) 5281590Srgrimes comma = 0; 5291590Srgrimes len = strlen(np->n_name); 5301590Srgrimes col++; /* for the space */ 5311590Srgrimes if (col + len + comma > 72 && col > 4) { 53277274Smikeh fprintf(fo, "\n "); 5331590Srgrimes col = 4; 5341590Srgrimes } else 53577274Smikeh fprintf(fo, " "); 5361590Srgrimes fputs(np->n_name, fo); 5371590Srgrimes if (comma) 53877274Smikeh fprintf(fo, ","); 5391590Srgrimes col += len + comma; 5401590Srgrimes } 54177274Smikeh fprintf(fo, "\n"); 5421590Srgrimes} 5431590Srgrimes 5441590Srgrimes/* 5451590Srgrimes * Save the outgoing mail on the passed file. 5461590Srgrimes */ 5471590Srgrimes 5481590Srgrimes/*ARGSUSED*/ 5491590Srgrimesint 5501590Srgrimessavemail(name, fi) 5511590Srgrimes char name[]; 55277274Smikeh FILE *fi; 5531590Srgrimes{ 55477274Smikeh FILE *fo; 5551590Srgrimes char buf[BUFSIZ]; 55677274Smikeh int i; 55777274Smikeh time_t now; 5581590Srgrimes 5591590Srgrimes if ((fo = Fopen(name, "a")) == NULL) { 56074769Smikeh warn("%s", name); 5611590Srgrimes return (-1); 5621590Srgrimes } 56377274Smikeh (void)time(&now); 5641590Srgrimes fprintf(fo, "From %s %s", myname, ctime(&now)); 56577274Smikeh while ((i = fread(buf, 1, sizeof(buf), fi)) > 0) 56677274Smikeh (void)fwrite(buf, 1, i, fo); 56777274Smikeh fprintf(fo, "\n"); 56877274Smikeh (void)fflush(fo); 5691590Srgrimes if (ferror(fo)) 57074769Smikeh warn("%s", name); 57177274Smikeh (void)Fclose(fo); 5721590Srgrimes rewind(fi); 5731590Srgrimes return (0); 5741590Srgrimes} 575