xref: /original-bsd/usr.bin/mail/fio.c (revision cd18b70b)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char *sccsid = "@(#)fio.c	5.5 (Berkeley) 05/18/87";
9 #endif not lint
10 
11 #include "rcv.h"
12 #include <sys/stat.h>
13 #include <sys/file.h>
14 #include <sys/wait.h>
15 #include <errno.h>
16 
17 /*
18  * Mail -- a mail program
19  *
20  * File I/O.
21  */
22 
23 /*
24  * Set up the input pointers while copying the mail file into
25  * /tmp.
26  */
27 setptr(ibuf)
28 	register FILE *ibuf;
29 {
30 	register c;
31 	register char *cp, *cp2;
32 	register count;
33 	char linebuf[LINESIZE];
34 	int maybe, inhead;
35 	FILE *mestmp;
36 	off_t offset;
37 	struct message this;
38 	extern char tempSet[];
39 
40 	if ((c = opentemp(tempSet)) < 0)
41 		exit(1);
42 	if ((mestmp = fdopen(c, "r+")) == NULL)
43 		panic("Can't open temporary");
44 	msgCount = 0;
45 	maybe = 1;
46 	inhead = 0;
47 	offset = 0;
48 	this.m_flag = MUSED|MNEW;
49 	this.m_size = 0;
50 	this.m_lines = 0;
51 	this.m_block = 0;
52 	this.m_offset = 0;
53 	for (;;) {
54 		if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
55 			if (append(&this, mestmp)) {
56 				perror(tempSet);
57 				exit(1);
58 			}
59 			fclose(ibuf);
60 			makemessage(mestmp);
61 			return;
62 		}
63 		count = strlen(linebuf);
64 		fwrite(linebuf, sizeof *linebuf, count, otf);
65 		if (ferror(otf)) {
66 			perror("/tmp");
67 			exit(1);
68 		}
69 		linebuf[count - 1] = 0;
70 		if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
71 			msgCount++;
72 			if (append(&this, mestmp)) {
73 				perror(tempSet);
74 				exit(1);
75 			}
76 			this.m_flag = MUSED|MNEW;
77 			this.m_size = 0;
78 			this.m_lines = 0;
79 			this.m_block = blockof(offset);
80 			this.m_offset = offsetof(offset);
81 			inhead = 1;
82 		} else if (linebuf[0] == 0) {
83 			inhead = 0;
84 		} else if (inhead) {
85 			for (cp = linebuf, cp2 = "status";; cp++) {
86 				if ((c = *cp2++) == 0) {
87 					while (isspace(*cp++))
88 						;
89 					if (cp[-1] != ':')
90 						break;
91 					while (c = *cp++)
92 						if (c == 'R')
93 							this.m_flag |= MREAD;
94 						else if (c == 'O')
95 							this.m_flag &= ~MNEW;
96 					inhead = 0;
97 					break;
98 				}
99 				if (*cp != c && *cp != toupper(c))
100 					break;
101 			}
102 		}
103 		offset += count;
104 		this.m_size += count;
105 		this.m_lines++;
106 		maybe = linebuf[0] == 0;
107 	}
108 }
109 
110 /*
111  * Drop the passed line onto the passed output buffer.
112  * If a write error occurs, return -1, else the count of
113  * characters written, including the newline.
114  */
115 putline(obuf, linebuf)
116 	FILE *obuf;
117 	char *linebuf;
118 {
119 	register int c;
120 
121 	c = strlen(linebuf);
122 	fwrite(linebuf, sizeof *linebuf, c, obuf);
123 	putc('\n', obuf);
124 	if (ferror(obuf))
125 		return (-1);
126 	return (c + 1);
127 }
128 
129 /*
130  * Read up a line from the specified input into the line
131  * buffer.  Return the number of characters read.  Do not
132  * include the newline at the end.
133  */
134 readline(ibuf, linebuf)
135 	FILE *ibuf;
136 	char *linebuf;
137 {
138 	register int n;
139 
140 	clearerr(ibuf);
141 	if (fgets(linebuf, LINESIZE, ibuf) == NULL)
142 		return -1;
143 	n = strlen(linebuf);
144 	if (n > 0 && linebuf[n - 1] == '\n')
145 		linebuf[--n] = '\0';
146 	return n;
147 }
148 
149 /*
150  * Return a file buffer all ready to read up the
151  * passed message pointer.
152  */
153 FILE *
154 setinput(mp)
155 	register struct message *mp;
156 {
157 
158 	fflush(otf);
159 	if (fseek(itf, positionof(mp->m_block, mp->m_offset), 0) < 0) {
160 		perror("fseek");
161 		panic("temporary file seek");
162 	}
163 	return (itf);
164 }
165 
166 /*
167  * Take the data out of the passed ghost file and toss it into
168  * a dynamically allocated message structure.
169  */
170 makemessage(f)
171 	FILE *f;
172 {
173 	register size = (msgCount + 1) * sizeof (struct message);
174 	off_t lseek();
175 
176 	if (message != 0)
177 		free((char *) message);
178 	if ((message = (struct message *) malloc((unsigned) size)) == 0)
179 		panic("Insufficient memory for %d messages", msgCount);
180 	dot = message;
181 	size -= sizeof (struct message);
182 	fflush(f);
183 	lseek(fileno(f), (long) sizeof *message, 0);
184 	if (read(fileno(f), (char *) message, size) != size)
185 		panic("Message temporary file corrupted");
186 	message[msgCount].m_size = 0;
187 	message[msgCount].m_lines = 0;
188 	fclose(f);
189 }
190 
191 /*
192  * Append the passed message descriptor onto the temp file.
193  * If the write fails, return 1, else 0
194  */
195 append(mp, f)
196 	struct message *mp;
197 	FILE *f;
198 {
199 	return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
200 }
201 
202 /*
203  * Delete a file, but only if the file is a plain file.
204  */
205 remove(name)
206 	char name[];
207 {
208 	struct stat statb;
209 	extern int errno;
210 
211 	if (stat(name, &statb) < 0)
212 		return(-1);
213 	if ((statb.st_mode & S_IFMT) != S_IFREG) {
214 		errno = EISDIR;
215 		return(-1);
216 	}
217 	return unlink(name);
218 }
219 
220 /*
221  * Terminate an editing session by attempting to write out the user's
222  * file from the temporary.  Save any new stuff appended to the file.
223  */
224 edstop()
225 {
226 	register int gotcha, c;
227 	register struct message *mp;
228 	FILE *obuf, *ibuf, *readstat;
229 	struct stat statb;
230 	char tempname[30], *id;
231 	char *mktemp();
232 
233 	if (readonly)
234 		return;
235 	holdsigs();
236 	if (Tflag != NOSTR) {
237 		if ((readstat = fopen(Tflag, "w")) == NULL)
238 			Tflag = NOSTR;
239 	}
240 	for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
241 		if (mp->m_flag & MNEW) {
242 			mp->m_flag &= ~MNEW;
243 			mp->m_flag |= MSTATUS;
244 		}
245 		if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
246 			gotcha++;
247 		if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
248 			if ((id = hfield("article-id", mp)) != NOSTR)
249 				fprintf(readstat, "%s\n", id);
250 		}
251 	}
252 	if (Tflag != NOSTR)
253 		fclose(readstat);
254 	if (!gotcha || Tflag != NOSTR)
255 		goto done;
256 	ibuf = NULL;
257 	if (stat(editfile, &statb) >= 0 && statb.st_size > mailsize) {
258 		strcpy(tempname, "/tmp/mboxXXXXXX");
259 		mktemp(tempname);
260 		if ((obuf = fopen(tempname, "w")) == NULL) {
261 			perror(tempname);
262 			relsesigs();
263 			reset(0);
264 		}
265 		if ((ibuf = fopen(editfile, "r")) == NULL) {
266 			perror(editfile);
267 			fclose(obuf);
268 			remove(tempname);
269 			relsesigs();
270 			reset(0);
271 		}
272 		fseek(ibuf, mailsize, 0);
273 		while ((c = getc(ibuf)) != EOF)
274 			putc(c, obuf);
275 		fclose(ibuf);
276 		fclose(obuf);
277 		if ((ibuf = fopen(tempname, "r")) == NULL) {
278 			perror(tempname);
279 			remove(tempname);
280 			relsesigs();
281 			reset(0);
282 		}
283 		remove(tempname);
284 	}
285 	printf("\"%s\" ", editfile);
286 	fflush(stdout);
287 	if ((obuf = fopen(editfile, "r+")) == NULL) {
288 		perror(editfile);
289 		relsesigs();
290 		reset(0);
291 	}
292 	trunc(obuf);
293 	c = 0;
294 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
295 		if ((mp->m_flag & MDELETED) != 0)
296 			continue;
297 		c++;
298 		if (send(mp, obuf, 0) < 0) {
299 			perror(editfile);
300 			relsesigs();
301 			reset(0);
302 		}
303 	}
304 	gotcha = (c == 0 && ibuf == NULL);
305 	if (ibuf != NULL) {
306 		while ((c = getc(ibuf)) != EOF)
307 			putc(c, obuf);
308 		fclose(ibuf);
309 	}
310 	fflush(obuf);
311 	if (ferror(obuf)) {
312 		perror(editfile);
313 		relsesigs();
314 		reset(0);
315 	}
316 	fclose(obuf);
317 	if (gotcha) {
318 		remove(editfile);
319 		printf("removed\n");
320 	}
321 	else
322 		printf("complete\n");
323 	fflush(stdout);
324 
325 done:
326 	relsesigs();
327 }
328 
329 static int sigdepth = 0;		/* depth of holdsigs() */
330 static int omask = 0;
331 /*
332  * Hold signals SIGHUP, SIGINT, and SIGQUIT.
333  */
334 holdsigs()
335 {
336 
337 	if (sigdepth++ == 0)
338 		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
339 }
340 
341 /*
342  * Release signals SIGHUP, SIGINT, and SIGQUIT.
343  */
344 relsesigs()
345 {
346 
347 	if (--sigdepth == 0)
348 		sigsetmask(omask);
349 }
350 
351 /*
352  * Open a temp file by creating and unlinking.
353  * Return the open file descriptor.
354  */
355 opentemp(file)
356 	char file[];
357 {
358 	int f;
359 
360 	if ((f = open(file, O_CREAT|O_EXCL|O_RDWR, 0600)) < 0)
361 		perror(file);
362 	remove(file);
363 	return (f);
364 }
365 
366 /*
367  * Determine the size of the file possessed by
368  * the passed buffer.
369  */
370 off_t
371 fsize(iob)
372 	FILE *iob;
373 {
374 	struct stat sbuf;
375 
376 	if (fstat(fileno(iob), &sbuf) < 0)
377 		return 0;
378 	return sbuf.st_size;
379 }
380 
381 /*
382  * Take a file name, possibly with shell meta characters
383  * in it and expand it by using "sh -c echo filename"
384  * Return the file name as a dynamic string.
385  */
386 char *
387 expand(name)
388 	char name[];
389 {
390 	char xname[BUFSIZ];
391 	char cmdbuf[BUFSIZ];
392 	register int pid, l;
393 	register char *cp, *Shell;
394 	int pivec[2];
395 	struct stat sbuf;
396 	union wait s;
397 
398 	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
399 		sprintf(xname, "%s/%s", cmdbuf, name + 1);
400 		return(expand(savestr(xname)));
401 	}
402 	if (!anyof(name, "~{[*?$`'\"\\"))
403 		return(name);
404 	if (pipe(pivec) < 0) {
405 		perror("pipe");
406 		return(name);
407 	}
408 	sprintf(cmdbuf, "echo %s", name);
409 	if ((pid = vfork()) == 0) {
410 		Shell = value("SHELL");
411 		if (Shell == NOSTR)
412 			Shell = SHELL;
413 		close(pivec[0]);
414 		close(1);
415 		dup(pivec[1]);
416 		close(pivec[1]);
417 		close(2);
418 		execl(Shell, Shell, "-c", cmdbuf, 0);
419 		_exit(1);
420 	}
421 	if (pid == -1) {
422 		perror("fork");
423 		close(pivec[0]);
424 		close(pivec[1]);
425 		return(NOSTR);
426 	}
427 	close(pivec[1]);
428 	l = read(pivec[0], xname, BUFSIZ);
429 	close(pivec[0]);
430 	while (wait(&s) != pid);
431 		;
432 	if (s.w_status != 0 && s.w_termsig != SIGPIPE) {
433 		fprintf(stderr, "\"Echo\" failed\n");
434 		goto err;
435 	}
436 	if (l < 0) {
437 		perror("read");
438 		goto err;
439 	}
440 	if (l == 0) {
441 		fprintf(stderr, "\"%s\": No match\n", name);
442 		goto err;
443 	}
444 	if (l == BUFSIZ) {
445 		fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
446 		goto err;
447 	}
448 	xname[l] = 0;
449 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
450 		;
451 	*++cp = '\0';
452 	if (any(' ', xname) && stat(xname, &sbuf) < 0) {
453 		fprintf(stderr, "\"%s\": Ambiguous\n", name);
454 		goto err;
455 	}
456 	return(savestr(xname));
457 
458 err:
459 	return(NOSTR);
460 }
461 
462 /*
463  * Determine the current folder directory name.
464  */
465 getfold(name)
466 	char *name;
467 {
468 	char *folder;
469 
470 	if ((folder = value("folder")) == NOSTR)
471 		return (-1);
472 	if (*folder == '/')
473 		strcpy(name, folder);
474 	else
475 		sprintf(name, "%s/%s", homedir, folder);
476 	return (0);
477 }
478 
479 /*
480  * A nicer version of Fdopen, which allows us to fclose
481  * without losing the open file.
482  */
483 FILE *
484 Fdopen(fildes, mode)
485 	char *mode;
486 {
487 	int f;
488 
489 	if ((f = dup(fildes)) < 0) {
490 		perror("dup");
491 		return (NULL);
492 	}
493 	return fdopen(f, mode);
494 }
495