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