xref: /csrg-svn/usr.bin/mail/fio.c (revision 68818)
122452Sdist /*
262083Sbostic  * Copyright (c) 1980, 1993
362083Sbostic  *	The Regents of the University of California.  All rights reserved.
433499Sbostic  *
542741Sbostic  * %sccs.include.redist.c%
622452Sdist  */
722452Sdist 
834905Sbostic #ifndef lint
9*68818Sdab static char sccsid[] = "@(#)fio.c	8.2 (Berkeley) 04/20/95";
1034905Sbostic #endif /* not lint */
111230Skas 
121230Skas #include "rcv.h"
1331142Sedward #include <sys/file.h>
1431142Sedward #include <sys/wait.h>
1554505Sbostic 
1654199Sbostic #include <unistd.h>
1746242Sbostic #include <paths.h>
181230Skas #include <errno.h>
1954505Sbostic #include "extern.h"
201230Skas 
211230Skas /*
221230Skas  * Mail -- a mail program
231230Skas  *
241230Skas  * File I/O.
251230Skas  */
261230Skas 
271230Skas /*
2846242Sbostic  * Set up the input pointers while copying the mail file into /tmp.
291230Skas  */
3054505Sbostic void
setptr(ibuf,offset)31*68818Sdab setptr(ibuf, offset)
3231142Sedward 	register FILE *ibuf;
33*68818Sdab 	off_t offset;
341230Skas {
3552909Sbostic 	extern char *tmpdir;
3646242Sbostic 	register int c, count;
372059Skas 	register char *cp, *cp2;
3846242Sbostic 	struct message this;
3931142Sedward 	FILE *mestmp;
4046242Sbostic 	int maybe, inhead;
4146242Sbostic 	char linebuf[LINESIZE];
42*68818Sdab 	int omsgCount;
431230Skas 
4446242Sbostic 	/* Get temporary file. */
4552909Sbostic 	(void)sprintf(linebuf, "%s/mail.XXXXXX", tmpdir);
4646242Sbostic 	if ((c = mkstemp(linebuf)) == -1 ||
4746242Sbostic 	    (mestmp = Fdopen(c, "r+")) == NULL) {
4846242Sbostic 		(void)fprintf(stderr, "mail: can't open %s\n", linebuf);
491230Skas 		exit(1);
5046242Sbostic 	}
5146242Sbostic 	(void)unlink(linebuf);
5246242Sbostic 
53*68818Sdab 	if (offset == 0) {
54*68818Sdab 		 msgCount = 0;
55*68818Sdab 	} else {
56*68818Sdab 		/* Seek into the file to get to the new messages */
57*68818Sdab 		(void) fseek(ibuf, offset, 0);
58*68818Sdab 		/*
59*68818Sdab 		 * We need to make "offset" a pointer to the end of
60*68818Sdab 		 * the temp file that has the copy of the mail file.
61*68818Sdab 		 * If any messages have been edited, this will be
62*68818Sdab 		 * different from the offset into the mail file.
63*68818Sdab 		 */
64*68818Sdab 		(void) fseek(otf, 0L, 2);
65*68818Sdab 		offset = ftell(otf);
66*68818Sdab 	}
67*68818Sdab 	omsgCount = msgCount;
6831142Sedward 	maybe = 1;
6931142Sedward 	inhead = 0;
7031142Sedward 	this.m_flag = MUSED|MNEW;
7131142Sedward 	this.m_size = 0;
7231142Sedward 	this.m_lines = 0;
7331142Sedward 	this.m_block = 0;
7431142Sedward 	this.m_offset = 0;
751230Skas 	for (;;) {
7624536Sdonn 		if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
771230Skas 			if (append(&this, mestmp)) {
7846242Sbostic 				perror("temporary file");
791230Skas 				exit(1);
801230Skas 			}
81*68818Sdab 			makemessage(mestmp, omsgCount);
821230Skas 			return;
831230Skas 		}
8424536Sdonn 		count = strlen(linebuf);
8536554Sedward 		(void) fwrite(linebuf, sizeof *linebuf, count, otf);
862059Skas 		if (ferror(otf)) {
871230Skas 			perror("/tmp");
881230Skas 			exit(1);
891230Skas 		}
9031142Sedward 		linebuf[count - 1] = 0;
911474Skas 		if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
921230Skas 			msgCount++;
931230Skas 			if (append(&this, mestmp)) {
9446242Sbostic 				perror("temporary file");
951230Skas 				exit(1);
961230Skas 			}
9731142Sedward 			this.m_flag = MUSED|MNEW;
9831142Sedward 			this.m_size = 0;
9931142Sedward 			this.m_lines = 0;
10031142Sedward 			this.m_block = blockof(offset);
10131142Sedward 			this.m_offset = offsetof(offset);
10231142Sedward 			inhead = 1;
10331142Sedward 		} else if (linebuf[0] == 0) {
1041474Skas 			inhead = 0;
10531142Sedward 		} else if (inhead) {
10631142Sedward 			for (cp = linebuf, cp2 = "status";; cp++) {
10731142Sedward 				if ((c = *cp2++) == 0) {
10831142Sedward 					while (isspace(*cp++))
10931142Sedward 						;
11031142Sedward 					if (cp[-1] != ':')
11131142Sedward 						break;
11231142Sedward 					while (c = *cp++)
11331142Sedward 						if (c == 'R')
11431142Sedward 							this.m_flag |= MREAD;
11531142Sedward 						else if (c == 'O')
11631142Sedward 							this.m_flag &= ~MNEW;
11731142Sedward 					inhead = 0;
11831142Sedward 					break;
11931142Sedward 				}
12031142Sedward 				if (*cp != c && *cp != toupper(c))
12131142Sedward 					break;
1221474Skas 			}
1231474Skas 		}
1241230Skas 		offset += count;
12531142Sedward 		this.m_size += count;
12631142Sedward 		this.m_lines++;
12731142Sedward 		maybe = linebuf[0] == 0;
1281230Skas 	}
1291230Skas }
1301230Skas 
1311230Skas /*
1321230Skas  * Drop the passed line onto the passed output buffer.
1331230Skas  * If a write error occurs, return -1, else the count of
1341230Skas  * characters written, including the newline.
1351230Skas  */
13654505Sbostic int
putline(obuf,linebuf)1371230Skas putline(obuf, linebuf)
1381230Skas 	FILE *obuf;
1391230Skas 	char *linebuf;
1401230Skas {
1411230Skas 	register int c;
1421230Skas 
1431230Skas 	c = strlen(linebuf);
14436554Sedward 	(void) fwrite(linebuf, sizeof *linebuf, c, obuf);
14536554Sedward 	(void) putc('\n', obuf);
1461230Skas 	if (ferror(obuf))
14731142Sedward 		return (-1);
14831142Sedward 	return (c + 1);
1491230Skas }
1501230Skas 
1511230Skas /*
1521230Skas  * Read up a line from the specified input into the line
1531230Skas  * buffer.  Return the number of characters read.  Do not
1541230Skas  * include the newline at the end.
1551230Skas  */
15654505Sbostic int
readline(ibuf,linebuf,linesize)15736478Sedward readline(ibuf, linebuf, linesize)
1581230Skas 	FILE *ibuf;
1591230Skas 	char *linebuf;
16054505Sbostic 	int linesize;
1611230Skas {
16224536Sdonn 	register int n;
1631230Skas 
16424536Sdonn 	clearerr(ibuf);
16536478Sedward 	if (fgets(linebuf, linesize, ibuf) == NULL)
16631142Sedward 		return -1;
16724536Sdonn 	n = strlen(linebuf);
16831142Sedward 	if (n > 0 && linebuf[n - 1] == '\n')
16931142Sedward 		linebuf[--n] = '\0';
17031142Sedward 	return n;
1711230Skas }
1721230Skas 
1731230Skas /*
1741230Skas  * Return a file buffer all ready to read up the
1751230Skas  * passed message pointer.
1761230Skas  */
1771230Skas FILE *
setinput(mp)1781230Skas setinput(mp)
1791230Skas 	register struct message *mp;
1801230Skas {
1811230Skas 
1821230Skas 	fflush(otf);
18354505Sbostic 	if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) {
1841230Skas 		perror("fseek");
1851230Skas 		panic("temporary file seek");
1861230Skas 	}
18731142Sedward 	return (itf);
1881230Skas }
1891230Skas 
1901230Skas /*
1911230Skas  * Take the data out of the passed ghost file and toss it into
1921230Skas  * a dynamically allocated message structure.
1931230Skas  */
19454505Sbostic void
makemessage(f,omsgCount)195*68818Sdab makemessage(f, omsgCount)
19631142Sedward 	FILE *f;
197*68818Sdab 	int omsgCount;
1981230Skas {
19931142Sedward 	register size = (msgCount + 1) * sizeof (struct message);
2001230Skas 
201*68818Sdab 	if (omsgCount) {
202*68818Sdab 		message = (struct message *)realloc(message, (unsigned) size);
203*68818Sdab 		if (message == 0)
204*68818Sdab 			panic("Insufficient memory for %d messages\n", msgCount);
205*68818Sdab 	} else {
206*68818Sdab 		if (message != 0)
207*68818Sdab 			free((char *) message);
208*68818Sdab 		if ((message = (struct message *) malloc((unsigned) size)) == 0)
209*68818Sdab 			panic("Insufficient memory for %d messages", msgCount);
210*68818Sdab 		dot = message;
211*68818Sdab 	}
212*68818Sdab 	size -= (omsgCount + 1) * sizeof (struct message);
21331142Sedward 	fflush(f);
21454505Sbostic 	(void) lseek(fileno(f), (off_t)sizeof *message, 0);
215*68818Sdab 	if (read(fileno(f), (char *) &message[omsgCount], size) != size)
21631142Sedward 		panic("Message temporary file corrupted");
21731142Sedward 	message[msgCount].m_size = 0;
2181230Skas 	message[msgCount].m_lines = 0;
21943865Sedward 	Fclose(f);
2201230Skas }
2211230Skas 
2221230Skas /*
2231230Skas  * Append the passed message descriptor onto the temp file.
2241230Skas  * If the write fails, return 1, else 0
2251230Skas  */
22654505Sbostic int
append(mp,f)2271230Skas append(mp, f)
2281230Skas 	struct message *mp;
22931142Sedward 	FILE *f;
2301230Skas {
23131142Sedward 	return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
2321230Skas }
2331230Skas 
2341230Skas /*
2351230Skas  * Delete a file, but only if the file is a plain file.
2361230Skas  */
23754505Sbostic int
rm(name)23846242Sbostic rm(name)
23946242Sbostic 	char *name;
2401230Skas {
24146242Sbostic 	struct stat sb;
2421230Skas 
24346242Sbostic 	if (stat(name, &sb) < 0)
2441230Skas 		return(-1);
24546242Sbostic 	if (!S_ISREG(sb.st_mode)) {
2461230Skas 		errno = EISDIR;
2471230Skas 		return(-1);
2481230Skas 	}
24946242Sbostic 	return(unlink(name));
2501230Skas }
2511230Skas 
25234965Sedward static int sigdepth;		/* depth of holdsigs() */
25334965Sedward static int omask;
2541230Skas /*
25531142Sedward  * Hold signals SIGHUP, SIGINT, and SIGQUIT.
2563411Skurt  */
25754505Sbostic void
holdsigs()2583565Skurt holdsigs()
2593411Skurt {
2603411Skurt 
26111285Sleres 	if (sigdepth++ == 0)
26217668Sralph 		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
2633411Skurt }
2643411Skurt 
2653411Skurt /*
26631142Sedward  * Release signals SIGHUP, SIGINT, and SIGQUIT.
2673411Skurt  */
26854505Sbostic void
relsesigs()2693565Skurt relsesigs()
2703411Skurt {
2713565Skurt 
27211285Sleres 	if (--sigdepth == 0)
27317668Sralph 		sigsetmask(omask);
2743411Skurt }
2753411Skurt 
2763411Skurt /*
2771230Skas  * Determine the size of the file possessed by
2781230Skas  * the passed buffer.
2791230Skas  */
2801230Skas off_t
fsize(iob)2811230Skas fsize(iob)
2821230Skas 	FILE *iob;
2831230Skas {
2841230Skas 	struct stat sbuf;
2851230Skas 
28631142Sedward 	if (fstat(fileno(iob), &sbuf) < 0)
28731142Sedward 		return 0;
28831142Sedward 	return sbuf.st_size;
2891230Skas }
2901230Skas 
2911230Skas /*
29234753Sedward  * Evaluate the string given as a new mailbox name.
29334753Sedward  * Supported meta characters:
29434753Sedward  *	%	for my system mail box
29534753Sedward  *	%user	for user's system mail box
29634753Sedward  *	#	for previous file
29734753Sedward  *	&	invoker's mbox file
29834753Sedward  *	+file	file in folder directory
29934753Sedward  *	any shell meta character
3001230Skas  * Return the file name as a dynamic string.
3011230Skas  */
3021230Skas char *
expand(name)3031230Skas expand(name)
30434754Sedward 	register char *name;
3051230Skas {
30634968Sedward 	char xname[PATHSIZE];
30734968Sedward 	char cmdbuf[PATHSIZE];		/* also used for file names */
30831142Sedward 	register int pid, l;
30934963Sedward 	register char *cp, *shell;
31031142Sedward 	int pivec[2];
3111230Skas 	struct stat sbuf;
31234976Sedward 	extern union wait wait_status;
3131230Skas 
31435067Sedward 	/*
31535067Sedward 	 * The order of evaluation is "%" and "#" expand into constants.
31635067Sedward 	 * "&" can expand into "+".  "+" can expand into shell meta characters.
31735067Sedward 	 * Shell meta characters expand into constants.
31835067Sedward 	 * This way, we make no recursive expansion.
31935067Sedward 	 */
32034753Sedward 	switch (*name) {
32134753Sedward 	case '%':
32234968Sedward 		findmail(name[1] ? name + 1 : myname, xname);
32334968Sedward 		return savestr(xname);
32434753Sedward 	case '#':
32534753Sedward 		if (name[1] != 0)
32634753Sedward 			break;
32734753Sedward 		if (prevfile[0] == 0) {
32834753Sedward 			printf("No previous file\n");
32934753Sedward 			return NOSTR;
33034753Sedward 		}
33134754Sedward 		return savestr(prevfile);
33234753Sedward 	case '&':
33335351Sedward 		if (name[1] == 0 && (name = value("MBOX")) == NOSTR)
33435067Sedward 			name = "~/mbox";
33534753Sedward 		/* fall through */
33634753Sedward 	}
3376225Skurt 	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
3386225Skurt 		sprintf(xname, "%s/%s", cmdbuf, name + 1);
33934753Sedward 		name = savestr(xname);
3406225Skurt 	}
34135067Sedward 	/* catch the most common shell meta character */
34235067Sedward 	if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
34335351Sedward 		sprintf(xname, "%s%s", homedir, name + 1);
34435067Sedward 		name = savestr(xname);
34535067Sedward 	}
3461230Skas 	if (!anyof(name, "~{[*?$`'\"\\"))
34734754Sedward 		return name;
3481230Skas 	if (pipe(pivec) < 0) {
3491230Skas 		perror("pipe");
35034754Sedward 		return name;
3511230Skas 	}
3521230Skas 	sprintf(cmdbuf, "echo %s", name);
35334963Sedward 	if ((shell = value("SHELL")) == NOSTR)
35437870Sbostic 		shell = _PATH_CSHELL;
35534963Sedward 	pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR);
35634963Sedward 	if (pid < 0) {
3571230Skas 		close(pivec[0]);
3581230Skas 		close(pivec[1]);
35934754Sedward 		return NOSTR;
3601230Skas 	}
3611230Skas 	close(pivec[1]);
3621230Skas 	l = read(pivec[0], xname, BUFSIZ);
3631230Skas 	close(pivec[0]);
36434976Sedward 	if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) {
36535331Sbostic 		fprintf(stderr, "\"%s\": Expansion failed.\n", name);
36634754Sedward 		return NOSTR;
3671230Skas 	}
3681230Skas 	if (l < 0) {
3691230Skas 		perror("read");
37034754Sedward 		return NOSTR;
3711230Skas 	}
3721230Skas 	if (l == 0) {
37335067Sedward 		fprintf(stderr, "\"%s\": No match.\n", name);
37434754Sedward 		return NOSTR;
3751230Skas 	}
3761230Skas 	if (l == BUFSIZ) {
37735067Sedward 		fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
37834754Sedward 		return NOSTR;
3791230Skas 	}
3801230Skas 	xname[l] = 0;
3811230Skas 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
3821230Skas 		;
38334754Sedward 	cp[1] = '\0';
38434987Sedward 	if (index(xname, ' ') && stat(xname, &sbuf) < 0) {
38535067Sedward 		fprintf(stderr, "\"%s\": Ambiguous.\n", name);
38634754Sedward 		return NOSTR;
3871230Skas 	}
38834754Sedward 	return savestr(xname);
3891230Skas }
3901230Skas 
3911230Skas /*
3926225Skurt  * Determine the current folder directory name.
3936225Skurt  */
39454505Sbostic int
getfold(name)3956225Skurt getfold(name)
3966225Skurt 	char *name;
3976225Skurt {
3986225Skurt 	char *folder;
3996225Skurt 
4006225Skurt 	if ((folder = value("folder")) == NOSTR)
40131142Sedward 		return (-1);
4026225Skurt 	if (*folder == '/')
4036225Skurt 		strcpy(name, folder);
4046225Skurt 	else
4056225Skurt 		sprintf(name, "%s/%s", homedir, folder);
40631142Sedward 	return (0);
4076225Skurt }
4086225Skurt 
4096225Skurt /*
41035351Sedward  * Return the name of the dead.letter file.
41135351Sedward  */
41235351Sedward char *
getdeadletter()41335351Sedward getdeadletter()
41435351Sedward {
41535351Sedward 	register char *cp;
41635351Sedward 
41735351Sedward 	if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR)
41835351Sedward 		cp = expand("~/dead.letter");
41935351Sedward 	else if (*cp != '/') {
42035351Sedward 		char buf[PATHSIZE];
42135351Sedward 
42235351Sedward 		(void) sprintf(buf, "~/%s", cp);
42335351Sedward 		cp = expand(buf);
42435351Sedward 	}
42535351Sedward 	return cp;
42635351Sedward }
427