xref: /original-bsd/usr.bin/mail/fio.c (revision e0c0d005)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)fio.c	5.24 (Berkeley) 02/03/91";
10 #endif /* not lint */
11 
12 #include "rcv.h"
13 #include <sys/stat.h>
14 #include <sys/file.h>
15 #include <sys/wait.h>
16 #include <paths.h>
17 #include <errno.h>
18 
19 /*
20  * Mail -- a mail program
21  *
22  * File I/O.
23  */
24 
25 /*
26  * Set up the input pointers while copying the mail file into /tmp.
27  */
28 setptr(ibuf)
29 	register FILE *ibuf;
30 {
31 	register int c, count;
32 	register char *cp, *cp2;
33 	struct message this;
34 	FILE *mestmp;
35 	off_t offset;
36 	int maybe, inhead;
37 	char linebuf[LINESIZE];
38 
39 	/* Get temporary file. */
40 	(void)sprintf(linebuf, "%s/mail.XXXXXX", _PATH_TMP);
41 	if ((c = mkstemp(linebuf)) == -1 ||
42 	    (mestmp = Fdopen(c, "r+")) == NULL) {
43 		(void)fprintf(stderr, "mail: can't open %s\n", linebuf);
44 		exit(1);
45 	}
46 	(void)unlink(linebuf);
47 
48 	msgCount = 0;
49 	maybe = 1;
50 	inhead = 0;
51 	offset = 0;
52 	this.m_flag = MUSED|MNEW;
53 	this.m_size = 0;
54 	this.m_lines = 0;
55 	this.m_block = 0;
56 	this.m_offset = 0;
57 	for (;;) {
58 		if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
59 			if (append(&this, mestmp)) {
60 				perror("temporary file");
61 				exit(1);
62 			}
63 			makemessage(mestmp);
64 			return;
65 		}
66 		count = strlen(linebuf);
67 		(void) fwrite(linebuf, sizeof *linebuf, count, otf);
68 		if (ferror(otf)) {
69 			perror("/tmp");
70 			exit(1);
71 		}
72 		linebuf[count - 1] = 0;
73 		if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
74 			msgCount++;
75 			if (append(&this, mestmp)) {
76 				perror("temporary file");
77 				exit(1);
78 			}
79 			this.m_flag = MUSED|MNEW;
80 			this.m_size = 0;
81 			this.m_lines = 0;
82 			this.m_block = blockof(offset);
83 			this.m_offset = offsetof(offset);
84 			inhead = 1;
85 		} else if (linebuf[0] == 0) {
86 			inhead = 0;
87 		} else if (inhead) {
88 			for (cp = linebuf, cp2 = "status";; cp++) {
89 				if ((c = *cp2++) == 0) {
90 					while (isspace(*cp++))
91 						;
92 					if (cp[-1] != ':')
93 						break;
94 					while (c = *cp++)
95 						if (c == 'R')
96 							this.m_flag |= MREAD;
97 						else if (c == 'O')
98 							this.m_flag &= ~MNEW;
99 					inhead = 0;
100 					break;
101 				}
102 				if (*cp != c && *cp != toupper(c))
103 					break;
104 			}
105 		}
106 		offset += count;
107 		this.m_size += count;
108 		this.m_lines++;
109 		maybe = linebuf[0] == 0;
110 	}
111 }
112 
113 /*
114  * Drop the passed line onto the passed output buffer.
115  * If a write error occurs, return -1, else the count of
116  * characters written, including the newline.
117  */
118 putline(obuf, linebuf)
119 	FILE *obuf;
120 	char *linebuf;
121 {
122 	register int c;
123 
124 	c = strlen(linebuf);
125 	(void) fwrite(linebuf, sizeof *linebuf, c, obuf);
126 	(void) putc('\n', obuf);
127 	if (ferror(obuf))
128 		return (-1);
129 	return (c + 1);
130 }
131 
132 /*
133  * Read up a line from the specified input into the line
134  * buffer.  Return the number of characters read.  Do not
135  * include the newline at the end.
136  */
137 readline(ibuf, linebuf, linesize)
138 	FILE *ibuf;
139 	char *linebuf;
140 {
141 	register int n;
142 
143 	clearerr(ibuf);
144 	if (fgets(linebuf, linesize, ibuf) == NULL)
145 		return -1;
146 	n = strlen(linebuf);
147 	if (n > 0 && linebuf[n - 1] == '\n')
148 		linebuf[--n] = '\0';
149 	return n;
150 }
151 
152 /*
153  * Return a file buffer all ready to read up the
154  * passed message pointer.
155  */
156 FILE *
157 setinput(mp)
158 	register struct message *mp;
159 {
160 
161 	fflush(otf);
162 	if (fseek(itf, positionof(mp->m_block, mp->m_offset), 0) < 0) {
163 		perror("fseek");
164 		panic("temporary file seek");
165 	}
166 	return (itf);
167 }
168 
169 /*
170  * Take the data out of the passed ghost file and toss it into
171  * a dynamically allocated message structure.
172  */
173 makemessage(f)
174 	FILE *f;
175 {
176 	register size = (msgCount + 1) * sizeof (struct message);
177 	off_t lseek();
178 
179 	if (message != 0)
180 		free((char *) message);
181 	if ((message = (struct message *) malloc((unsigned) size)) == 0)
182 		panic("Insufficient memory for %d messages", msgCount);
183 	dot = message;
184 	size -= sizeof (struct message);
185 	fflush(f);
186 	(void) lseek(fileno(f), (long) sizeof *message, 0);
187 	if (read(fileno(f), (char *) message, size) != size)
188 		panic("Message temporary file corrupted");
189 	message[msgCount].m_size = 0;
190 	message[msgCount].m_lines = 0;
191 	Fclose(f);
192 }
193 
194 /*
195  * Append the passed message descriptor onto the temp file.
196  * If the write fails, return 1, else 0
197  */
198 append(mp, f)
199 	struct message *mp;
200 	FILE *f;
201 {
202 	return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
203 }
204 
205 /*
206  * Delete a file, but only if the file is a plain file.
207  */
208 rm(name)
209 	char *name;
210 {
211 	struct stat sb;
212 
213 	if (stat(name, &sb) < 0)
214 		return(-1);
215 	if (!S_ISREG(sb.st_mode)) {
216 		errno = EISDIR;
217 		return(-1);
218 	}
219 	return(unlink(name));
220 }
221 
222 static int sigdepth;		/* depth of holdsigs() */
223 static int omask;
224 /*
225  * Hold signals SIGHUP, SIGINT, and SIGQUIT.
226  */
227 holdsigs()
228 {
229 
230 	if (sigdepth++ == 0)
231 		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
232 }
233 
234 /*
235  * Release signals SIGHUP, SIGINT, and SIGQUIT.
236  */
237 relsesigs()
238 {
239 
240 	if (--sigdepth == 0)
241 		sigsetmask(omask);
242 }
243 
244 /*
245  * Determine the size of the file possessed by
246  * the passed buffer.
247  */
248 off_t
249 fsize(iob)
250 	FILE *iob;
251 {
252 	struct stat sbuf;
253 
254 	if (fstat(fileno(iob), &sbuf) < 0)
255 		return 0;
256 	return sbuf.st_size;
257 }
258 
259 /*
260  * Evaluate the string given as a new mailbox name.
261  * Supported meta characters:
262  *	%	for my system mail box
263  *	%user	for user's system mail box
264  *	#	for previous file
265  *	&	invoker's mbox file
266  *	+file	file in folder directory
267  *	any shell meta character
268  * Return the file name as a dynamic string.
269  */
270 char *
271 expand(name)
272 	register char *name;
273 {
274 	char xname[PATHSIZE];
275 	char cmdbuf[PATHSIZE];		/* also used for file names */
276 	register int pid, l;
277 	register char *cp, *shell;
278 	int pivec[2];
279 	struct stat sbuf;
280 	extern union wait wait_status;
281 
282 	/*
283 	 * The order of evaluation is "%" and "#" expand into constants.
284 	 * "&" can expand into "+".  "+" can expand into shell meta characters.
285 	 * Shell meta characters expand into constants.
286 	 * This way, we make no recursive expansion.
287 	 */
288 	switch (*name) {
289 	case '%':
290 		findmail(name[1] ? name + 1 : myname, xname);
291 		return savestr(xname);
292 	case '#':
293 		if (name[1] != 0)
294 			break;
295 		if (prevfile[0] == 0) {
296 			printf("No previous file\n");
297 			return NOSTR;
298 		}
299 		return savestr(prevfile);
300 	case '&':
301 		if (name[1] == 0 && (name = value("MBOX")) == NOSTR)
302 			name = "~/mbox";
303 		/* fall through */
304 	}
305 	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
306 		sprintf(xname, "%s/%s", cmdbuf, name + 1);
307 		name = savestr(xname);
308 	}
309 	/* catch the most common shell meta character */
310 	if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
311 		sprintf(xname, "%s%s", homedir, name + 1);
312 		name = savestr(xname);
313 	}
314 	if (!anyof(name, "~{[*?$`'\"\\"))
315 		return name;
316 	if (pipe(pivec) < 0) {
317 		perror("pipe");
318 		return name;
319 	}
320 	sprintf(cmdbuf, "echo %s", name);
321 	if ((shell = value("SHELL")) == NOSTR)
322 		shell = _PATH_CSHELL;
323 	pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR);
324 	if (pid < 0) {
325 		close(pivec[0]);
326 		close(pivec[1]);
327 		return NOSTR;
328 	}
329 	close(pivec[1]);
330 	l = read(pivec[0], xname, BUFSIZ);
331 	close(pivec[0]);
332 	if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) {
333 		fprintf(stderr, "\"%s\": Expansion failed.\n", name);
334 		return NOSTR;
335 	}
336 	if (l < 0) {
337 		perror("read");
338 		return NOSTR;
339 	}
340 	if (l == 0) {
341 		fprintf(stderr, "\"%s\": No match.\n", name);
342 		return NOSTR;
343 	}
344 	if (l == BUFSIZ) {
345 		fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
346 		return NOSTR;
347 	}
348 	xname[l] = 0;
349 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
350 		;
351 	cp[1] = '\0';
352 	if (index(xname, ' ') && stat(xname, &sbuf) < 0) {
353 		fprintf(stderr, "\"%s\": Ambiguous.\n", name);
354 		return NOSTR;
355 	}
356 	return savestr(xname);
357 }
358 
359 /*
360  * Determine the current folder directory name.
361  */
362 getfold(name)
363 	char *name;
364 {
365 	char *folder;
366 
367 	if ((folder = value("folder")) == NOSTR)
368 		return (-1);
369 	if (*folder == '/')
370 		strcpy(name, folder);
371 	else
372 		sprintf(name, "%s/%s", homedir, folder);
373 	return (0);
374 }
375 
376 /*
377  * Return the name of the dead.letter file.
378  */
379 char *
380 getdeadletter()
381 {
382 	register char *cp;
383 
384 	if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR)
385 		cp = expand("~/dead.letter");
386 	else if (*cp != '/') {
387 		char buf[PATHSIZE];
388 
389 		(void) sprintf(buf, "~/%s", cp);
390 		cp = expand(buf);
391 	}
392 	return cp;
393 }
394