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