fio.c revision 82793
18876Srgrimes/*
24Srgrimes * Copyright (c) 1980, 1993
34Srgrimes *	The Regents of the University of California.  All rights reserved.
44Srgrimes *
58876Srgrimes * Redistribution and use in source and binary forms, with or without
64Srgrimes * modification, are permitted provided that the following conditions
74Srgrimes * are met:
84Srgrimes * 1. Redistributions of source code must retain the above copyright
94Srgrimes *    notice, this list of conditions and the following disclaimer.
104Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
118876Srgrimes *    notice, this list of conditions and the following disclaimer in the
128876Srgrimes *    documentation and/or other materials provided with the distribution.
134Srgrimes * 3. All advertising materials mentioning features or use of this software
144Srgrimes *    must display the following acknowledgement:
158876Srgrimes *	This product includes software developed by the University of
164Srgrimes *	California, Berkeley and its contributors.
178876Srgrimes * 4. Neither the name of the University nor the names of its contributors
184Srgrimes *    may be used to endorse or promote products derived from this software
194Srgrimes *    without specific prior written permission.
204Srgrimes *
214Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
228876Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
234Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
244Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25118Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
264Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
274Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28115683Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29115683Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30115683Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
314Srgrimes * SUCH DAMAGE.
324Srgrimes */
334Srgrimes
342056Swollman#ifndef lint
3524494Sbde#if 0
362056Swollmanstatic char sccsid[] = "@(#)fio.c	8.1 (Berkeley) 6/6/93";
374Srgrimes#endif
384Srgrimesstatic const char rcsid[] =
394Srgrimes  "$FreeBSD: head/usr.bin/mail/fio.c 82793 2001-09-02 14:40:51Z ache $";
404Srgrimes#endif /* not lint */
414Srgrimes
424Srgrimes#include "rcv.h"
434Srgrimes#include <sys/file.h>
444Srgrimes#include <sys/wait.h>
454Srgrimes
464Srgrimes#include <unistd.h>
474Srgrimes#include <paths.h>
484Srgrimes#include <errno.h>
494Srgrimes#include "extern.h"
504Srgrimes
514Srgrimes/*
524Srgrimes * Mail -- a mail program
534Srgrimes *
544Srgrimes * File I/O.
554Srgrimes */
564Srgrimes
574Srgrimesextern int wait_status;
584Srgrimes
594Srgrimes/*
604Srgrimes * Set up the input pointers while copying the mail file into /tmp.
614Srgrimes */
624Srgrimesvoid
634Srgrimessetptr(ibuf)
644Srgrimes	FILE *ibuf;
654Srgrimes{
664Srgrimes	int c, count;
674Srgrimes	char *cp, *cp2;
684Srgrimes	struct message this;
694Srgrimes	FILE *mestmp;
704Srgrimes	off_t offset;
714Srgrimes	int maybe, inhead;
724Srgrimes	char linebuf[LINESIZE], pathbuf[PATHSIZE];
734Srgrimes
744Srgrimes	/* Get temporary file. */
754Srgrimes	(void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir);
764Srgrimes	if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL)
774Srgrimes		err(1, "can't open %s", pathbuf);
784Srgrimes	(void)rm(pathbuf);
794Srgrimes
804Srgrimes	msgCount = 0;
814Srgrimes	maybe = 1;
824Srgrimes	inhead = 0;
834Srgrimes	offset = 0;
844Srgrimes	this.m_flag = MUSED|MNEW;
854Srgrimes	this.m_size = 0;
864Srgrimes	this.m_lines = 0;
874Srgrimes	this.m_block = 0;
884Srgrimes	this.m_offset = 0;
8921277Sbde	for (;;) {
9021277Sbde		if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) {
9121277Sbde			if (append(&this, mestmp))
924Srgrimes				errx(1, "temporary file");
9311940Sbde			makemessage(mestmp);
9414887Swollman			return;
954Srgrimes		}
964Srgrimes		count = strlen(linebuf);
974Srgrimes		/*
9817109Sbde		 * Transforms lines ending in <CR><LF> to just <LF>.
994Srgrimes		 * This allows mail to be able to read Eudora mailboxes.
1004Srgrimes		 */
1014Srgrimes		if (count >= 2 && linebuf[count - 1] == '\n' &&
1024Srgrimes		    linebuf[count - 2] == '\r') {
1034Srgrimes			count--;
1044Srgrimes			linebuf[count - 1] = '\n';
10511940Sbde		}
10614887Swollman
1074Srgrimes		(void)fwrite(linebuf, sizeof(*linebuf), count, otf);
1084Srgrimes		if (ferror(otf))
10917109Sbde			errx(1, "/tmp");
1104Srgrimes		if (count)
1114Srgrimes			linebuf[count - 1] = '\0';
1124Srgrimes		if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
11314887Swollman			msgCount++;
1144Srgrimes			if (append(&this, mestmp))
1154Srgrimes				errx(1, "temporary file");
1164Srgrimes			this.m_flag = MUSED|MNEW;
1174Srgrimes			this.m_size = 0;
1184Srgrimes			this.m_lines = 0;
1194Srgrimes			this.m_block = blockof(offset);
1204Srgrimes			this.m_offset = boffsetof(offset);
1214Srgrimes			inhead = 1;
1224Srgrimes		} else if (linebuf[0] == 0) {
1234Srgrimes			inhead = 0;
12414887Swollman		} else if (inhead) {
1254Srgrimes			for (cp = linebuf, cp2 = "status";; cp++) {
1264Srgrimes				if ((c = *cp2++) == '\0') {
1274Srgrimes					while (isspace(*cp++))
1284Srgrimes						;
1294Srgrimes					if (cp[-1] != ':')
1304Srgrimes						break;
1314Srgrimes					while ((c = *cp++) != '\0')
1324Srgrimes						if (c == 'R')
1334Srgrimes							this.m_flag |= MREAD;
1344Srgrimes						else if (c == 'O')
13514887Swollman							this.m_flag &= ~MNEW;
1364Srgrimes					inhead = 0;
1374Srgrimes					break;
1384Srgrimes				}
1394Srgrimes				if (*cp != c && *cp != toupper(c))
1404Srgrimes					break;
1414Srgrimes			}
1424Srgrimes		}
1434Srgrimes		offset += count;
1444Srgrimes		this.m_size += count;
1454Srgrimes		this.m_lines++;
14621277Sbde		maybe = linebuf[0] == 0;
14721277Sbde	}
14821277Sbde}
14921277Sbde
15021277Sbde/*
15121277Sbde * Drop the passed line onto the passed output buffer.
15221277Sbde * If a write error occurs, return -1, else the count of
15321277Sbde * characters written, including the newline.
15421277Sbde */
15521277Sbdeint
15621277Sbdeputline(obuf, linebuf)
15714887Swollman	FILE *obuf;
15817109Sbde	char *linebuf;
15917109Sbde{
1604Srgrimes	int c;
1614Srgrimes
1624Srgrimes	c = strlen(linebuf);
1634Srgrimes	(void)fwrite(linebuf, sizeof(*linebuf), c, obuf);
1644Srgrimes	fprintf(obuf, "\n");
1654Srgrimes	if (ferror(obuf))
1664Srgrimes		return (-1);
1674Srgrimes	return (c + 1);
1684Srgrimes}
1694Srgrimes
1704Srgrimes/*
1714Srgrimes * Read up a line from the specified input into the line
1724Srgrimes * buffer.  Return the number of characters read.  Do not
1734Srgrimes * include the newline (or carriage return) at the end.
1744Srgrimes */
1754Srgrimesint
1764Srgrimesreadline(ibuf, linebuf, linesize)
17717109Sbde	FILE *ibuf;
17821277Sbde	char *linebuf;
17921277Sbde	int linesize;
18021277Sbde{
18121277Sbde	int n;
18221277Sbde
1834Srgrimes	clearerr(ibuf);
18421277Sbde	if (fgets(linebuf, linesize, ibuf) == NULL)
1854Srgrimes		return (-1);
1864Srgrimes	n = strlen(linebuf);
1874Srgrimes	if (n > 0 && linebuf[n - 1] == '\n')
1884Srgrimes		linebuf[--n] = '\0';
1894Srgrimes	if (n > 0 && linebuf[n - 1] == '\r')
1904Srgrimes		linebuf[--n] = '\0';
1914Srgrimes	return (n);
1924Srgrimes}
1934Srgrimes
1944Srgrimes/*
1954Srgrimes * Return a file buffer all ready to read up the
1964Srgrimes * passed message pointer.
19714887Swollman */
19814887SwollmanFILE *
19914887Swollmansetinput(mp)
20014887Swollman	struct message *mp;
20114887Swollman{
20214887Swollman
20314887Swollman	(void)fflush(otf);
20414887Swollman	if (fseeko(itf,
20514887Swollman		   positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0)
20614887Swollman		err(1, "fseek");
20714887Swollman	return (itf);
20814887Swollman}
20914887Swollman
21014887Swollman/*
21114887Swollman * Take the data out of the passed ghost file and toss it into
21214887Swollman * a dynamically allocated message structure.
21314887Swollman */
21414887Swollmanvoid
21514887Swollmanmakemessage(f)
21614887Swollman	FILE *f;
21717109Sbde{
2184Srgrimes	int size = (msgCount + 1) * sizeof(struct message);
2194Srgrimes
2204Srgrimes	if (message != 0)
2214Srgrimes		(void)free(message);
2224Srgrimes	if ((message = malloc((unsigned)size)) == NULL)
2234Srgrimes		err(1, "Out of memory");
2244Srgrimes	dot = message;
2254Srgrimes	size -= sizeof(struct message);
2264Srgrimes	(void)fflush(f);
2274Srgrimes	(void)lseek(fileno(f), (off_t)sizeof(*message), 0);
2284Srgrimes	if (read(fileno(f), (char *)message, size) != size)
2294Srgrimes		errx(1, "Message temporary file corrupted");
2304Srgrimes	message[msgCount].m_size = 0;
2314Srgrimes	message[msgCount].m_lines = 0;
2324Srgrimes	(void)Fclose(f);
2334Srgrimes}
2344Srgrimes
2354Srgrimes/*
2364Srgrimes * Append the passed message descriptor onto the temp file.
23717109Sbde * If the write fails, return 1, else 0
2384Srgrimes */
2394Srgrimesint
2404Srgrimesappend(mp, f)
2414Srgrimes	struct message *mp;
2424Srgrimes	FILE *f;
2434Srgrimes{
2444Srgrimes	return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1);
2454Srgrimes}
2464Srgrimes
2474Srgrimes/*
2484Srgrimes * Delete a file, but only if the file is a plain file.
2494Srgrimes */
2504Srgrimesint
2514Srgrimesrm(name)
2524Srgrimes	char *name;
2534Srgrimes{
2544Srgrimes	struct stat sb;
2554Srgrimes
2564Srgrimes	if (stat(name, &sb) < 0)
25717109Sbde		return (-1);
2584Srgrimes	if (!S_ISREG(sb.st_mode)) {
2594Srgrimes		errno = EISDIR;
26021277Sbde		return (-1);
26121277Sbde	}
26217109Sbde	return (unlink(name));
26317109Sbde}
2644Srgrimes
2654Srgrimesstatic int sigdepth;		/* depth of holdsigs() */
2664Srgrimesstatic int omask;
2674Srgrimes/*
2684Srgrimes * Hold signals SIGHUP, SIGINT, and SIGQUIT.
26921277Sbde */
27021277Sbdevoid
27117109Sbdeholdsigs()
27217109Sbde{
2734Srgrimes
2744Srgrimes	if (sigdepth++ == 0)
2754Srgrimes		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
2764Srgrimes}
27717109Sbde
27821277Sbde/*
27921277Sbde * Release signals SIGHUP, SIGINT, and SIGQUIT.
2804Srgrimes */
28121277Sbdevoid
2824Srgrimesrelsesigs()
2834Srgrimes{
28421277Sbde
28521277Sbde	if (--sigdepth == 0)
2864Srgrimes		(void)sigsetmask(omask);
2874Srgrimes}
2884Srgrimes
28917109Sbde/*
2904Srgrimes * Determine the size of the file possessed by
2914Srgrimes * the passed buffer.
2924Srgrimes */
29321277Sbdeoff_t
29421277Sbdefsize(iob)
2954Srgrimes	FILE *iob;
2964Srgrimes{
29717109Sbde	struct stat sbuf;
2984Srgrimes
2994Srgrimes	if (fstat(fileno(iob), &sbuf) < 0)
3004Srgrimes		return (0);
3014Srgrimes	return (sbuf.st_size);
3024Srgrimes}
3034Srgrimes
3044Srgrimes/*
30521277Sbde * Evaluate the string given as a new mailbox name.
30621277Sbde * Supported meta characters:
30721277Sbde *	%	for my system mail box
30821277Sbde *	%user	for user's system mail box
30921277Sbde *	#	for previous file
31021277Sbde *	&	invoker's mbox file
31121277Sbde *	+file	file in folder directory
31221277Sbde *	any shell meta character
31321277Sbde * Return the file name as a dynamic string.
3144Srgrimes */
3154Srgrimeschar *
31614887Swollmanexpand(name)
3174Srgrimes	char *name;
3184Srgrimes{
3194Srgrimes	char xname[PATHSIZE];
32014887Swollman	char cmdbuf[PATHSIZE];		/* also used for file names */
3214Srgrimes	int pid, l;
3224Srgrimes	char *cp, *sh;
3234Srgrimes	int pivec[2];
3244Srgrimes	struct stat sbuf;
3254Srgrimes
3264Srgrimes	/*
3274Srgrimes	 * The order of evaluation is "%" and "#" expand into constants.
3284Srgrimes	 * "&" can expand into "+".  "+" can expand into shell meta characters.
3294Srgrimes	 * Shell meta characters expand into constants.
3304Srgrimes	 * This way, we make no recursive expansion.
33121277Sbde	 */
3324Srgrimes	switch (*name) {
3334Srgrimes	case '%':
3344Srgrimes		findmail(name[1] ? name + 1 : myname, xname, sizeof(xname));
33514887Swollman		return (savestr(xname));
3364Srgrimes	case '#':
3374Srgrimes		if (name[1] != 0)
33814887Swollman			break;
3394Srgrimes		if (prevfile[0] == 0) {
3404Srgrimes			printf("No previous file\n");
34117109Sbde			return (NULL);
3424Srgrimes		}
3434Srgrimes		return (savestr(prevfile));
34417109Sbde	case '&':
3454Srgrimes		if (name[1] == 0 && (name = value("MBOX")) == NULL)
3464Srgrimes			name = "~/mbox";
3474Srgrimes		/* fall through */
34814887Swollman	}
3494Srgrimes	if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) {
3504Srgrimes		(void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1);
3514Srgrimes		name = savestr(xname);
35221277Sbde	}
3534Srgrimes	/* catch the most common shell meta character */
3544Srgrimes	if (name[0] == '~' && homedir != NULL &&
3554Srgrimes	    (name[1] == '/' || name[1] == '\0')) {
35617109Sbde		(void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1);
35721277Sbde		name = savestr(xname);
3584Srgrimes	}
3594Srgrimes	if (!strpbrk(name, "~{[*?$`'\"\\"))
36014887Swollman		return (name);
3614Srgrimes	if (pipe(pivec) < 0) {
3624Srgrimes		warn("pipe");
3634Srgrimes		return (name);
36417109Sbde	}
3654Srgrimes	(void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
3664Srgrimes	if ((sh = value("SHELL")) == NULL)
3674Srgrimes		sh = _PATH_CSHELL;
36814887Swollman	pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL);
3694Srgrimes	if (pid < 0) {
3704Srgrimes		(void)close(pivec[0]);
3714Srgrimes		(void)close(pivec[1]);
3724Srgrimes		return (NULL);
3734Srgrimes	}
3744Srgrimes	(void)close(pivec[1]);
3754Srgrimes	l = read(pivec[0], xname, BUFSIZ);
3764Srgrimes	(void)close(pivec[0]);
3774Srgrimes	if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) &&
3784Srgrimes	    WTERMSIG(wait_status) != SIGPIPE) {
37914887Swollman		fprintf(stderr, "\"%s\": Expansion failed.\n", name);
3804Srgrimes		return (NULL);
3814Srgrimes	}
38217109Sbde	if (l < 0) {
38321277Sbde		warn("read");
38417109Sbde		return (NULL);
38517109Sbde	}
38617109Sbde	if (l == 0) {
38717109Sbde		fprintf(stderr, "\"%s\": No match.\n", name);
3884Srgrimes		return (NULL);
3894Srgrimes	}
39014887Swollman	if (l == BUFSIZ) {
39121277Sbde		fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
39221277Sbde		return (NULL);
39321277Sbde	}
39421277Sbde	xname[l] = '\0';
39521277Sbde	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
39621277Sbde		;
39721277Sbde	cp[1] = '\0';
39821277Sbde	if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
3994Srgrimes		fprintf(stderr, "\"%s\": Ambiguous.\n", name);
4004Srgrimes		return (NULL);
40114887Swollman	}
40221277Sbde	return (savestr(xname));
4034Srgrimes}
40421277Sbde
40521277Sbde/*
40617109Sbde * Determine the current folder directory name.
4074Srgrimes */
4084Srgrimesint
4094Srgrimesgetfold(name, namelen)
4104Srgrimes	char *name;
4114Srgrimes	int namelen;
41214887Swollman{
4134Srgrimes	char *folder;
4144Srgrimes	int copylen;
41521277Sbde
41621277Sbde	if ((folder = value("folder")) == NULL)
4174Srgrimes		return (-1);
4184Srgrimes	if (*folder == '/')
4194Srgrimes		copylen = strlcpy(name, folder, namelen);
4204Srgrimes	else
4214Srgrimes		copylen = snprintf(name, namelen, "%s/%s",
4224Srgrimes		    homedir ? homedir : ".", folder);
42314887Swollman	return (copylen < 0 || copylen >= namelen ? (-1) : (0));
4244Srgrimes}
4254Srgrimes
4264Srgrimes/*
4274Srgrimes * Return the name of the dead.letter file.
4284Srgrimes */
4294Srgrimeschar *
4304Srgrimesgetdeadletter()
4314Srgrimes{
4324Srgrimes	char *cp;
4334Srgrimes
43414887Swollman	if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL)
43521277Sbde		cp = expand("~/dead.letter");
43621277Sbde	else if (*cp != '/') {
43721277Sbde		char buf[PATHSIZE];
43821277Sbde
43921277Sbde		(void)snprintf(buf, sizeof(buf), "~/%s", cp);
44021277Sbde		cp = expand(buf);
44121277Sbde	}
44221277Sbde	return (cp);
4434Srgrimes}
4444Srgrimes