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