xref: /original-bsd/usr.bin/mail/fio.c (revision e5dafb11)
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	2.8 03/15/82";
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 >= LINESIZE - 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, *readstat;
296 	struct stat statb;
297 	char tempname[30], *id;
298 	int (*sigs[3])();
299 
300 	if (readonly)
301 		return;
302 	holdsigs();
303 	if (Tflag != NOSTR) {
304 		if ((readstat = fopen(Tflag, "w")) == NULL)
305 			Tflag = NOSTR;
306 	}
307 	for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
308 		if (mp->m_flag & MNEW) {
309 			mp->m_flag &= ~MNEW;
310 			mp->m_flag |= MSTATUS;
311 		}
312 		if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
313 			gotcha++;
314 		if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
315 			if ((id = hfield("article-id", mp)) != NOSTR)
316 				fprintf(readstat, "%s\n", id);
317 		}
318 	}
319 	if (Tflag != NOSTR)
320 		fclose(readstat);
321 	if (!gotcha || Tflag != NOSTR)
322 		goto done;
323 	ibuf = NULL;
324 	if (stat(editfile, &statb) >= 0 && statb.st_size > mailsize) {
325 		strcpy(tempname, "/tmp/mboxXXXXXX");
326 		mktemp(tempname);
327 		if ((obuf = fopen(tempname, "w")) == NULL) {
328 			perror(tempname);
329 			relsesigs();
330 			reset(0);
331 		}
332 		if ((ibuf = fopen(editfile, "r")) == NULL) {
333 			perror(editfile);
334 			fclose(obuf);
335 			remove(tempname);
336 			relsesigs();
337 			reset(0);
338 		}
339 		fseek(ibuf, mailsize, 0);
340 		while ((c = getc(ibuf)) != EOF)
341 			putc(c, obuf);
342 		fclose(ibuf);
343 		fclose(obuf);
344 		if ((ibuf = fopen(tempname, "r")) == NULL) {
345 			perror(tempname);
346 			remove(tempname);
347 			relsesigs();
348 			reset(0);
349 		}
350 		remove(tempname);
351 	}
352 	printf("\"%s\" ", editfile);
353 	flush();
354 	if ((obuf = fopen(editfile, "w")) == NULL) {
355 		perror(editfile);
356 		relsesigs();
357 		reset(0);
358 	}
359 	c = 0;
360 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
361 		if ((mp->m_flag & MDELETED) != 0)
362 			continue;
363 		c++;
364 		if (send(mp, obuf) < 0) {
365 			perror(editfile);
366 			relsesigs();
367 			reset(0);
368 		}
369 	}
370 	gotcha = (c == 0 && ibuf == NULL);
371 	if (ibuf != NULL) {
372 		while ((c = getc(ibuf)) != EOF)
373 			putc(c, obuf);
374 		fclose(ibuf);
375 	}
376 	fflush(obuf);
377 	if (ferror(obuf)) {
378 		perror(editfile);
379 		relsesigs();
380 		reset(0);
381 	}
382 	fclose(obuf);
383 	if (gotcha) {
384 		remove(editfile);
385 		printf("removed\n");
386 	}
387 	else
388 		printf("complete\n");
389 	flush();
390 
391 done:
392 	relsesigs();
393 }
394 
395 # ifndef VMUNIX
396 static int	SaveSigs[32];
397 # endif VMUNIX
398 
399 /*
400  * Hold signals SIGHUP - SIGQUIT.
401  */
402 holdsigs()
403 {
404 	register int i;
405 
406 	for (i = SIGHUP; i <= SIGQUIT; i++)
407 # ifdef VMUNIX
408 		sighold(i);
409 # else
410 		SaveSigs[i] = signal(i, SIG_IGN);
411 # endif
412 }
413 
414 /*
415  * Release signals SIGHUP - SIGQUIT
416  */
417 relsesigs()
418 {
419 	register int i;
420 
421 	for (i = SIGHUP; i <= SIGQUIT; i++)
422 # ifdef VMUNIX
423 		sigrelse(i);
424 # else
425 		signal(i, SaveSigs[i]);
426 # endif
427 }
428 
429 /*
430  * Empty the output buffer.
431  */
432 
433 clrbuf(buf)
434 	register FILE *buf;
435 {
436 
437 	buf = stdout;
438 	buf->_ptr = buf->_base;
439 	buf->_cnt = BUFSIZ;
440 }
441 
442 /*
443  * Open a temp file by creating, closing, unlinking, and
444  * reopening.  Return the open file descriptor.
445  */
446 
447 opentemp(file)
448 	char file[];
449 {
450 	register int f;
451 
452 	if ((f = creat(file, 0600)) < 0) {
453 		perror(file);
454 		return(-1);
455 	}
456 	close(f);
457 	if ((f = open(file, 2)) < 0) {
458 		perror(file);
459 		remove(file);
460 		return(-1);
461 	}
462 	remove(file);
463 	return(f);
464 }
465 
466 /*
467  * Flush the standard output.
468  */
469 
470 flush()
471 {
472 	fflush(stdout);
473 	fflush(stderr);
474 }
475 
476 /*
477  * Determine the size of the file possessed by
478  * the passed buffer.
479  */
480 
481 off_t
482 fsize(iob)
483 	FILE *iob;
484 {
485 	register int f;
486 	struct stat sbuf;
487 
488 	f = fileno(iob);
489 	if (fstat(f, &sbuf) < 0)
490 		return(0);
491 	return(sbuf.st_size);
492 }
493 
494 /*
495  * Take a file name, possibly with shell meta characters
496  * in it and expand it by using "sh -c echo filename"
497  * Return the file name as a dynamic string.
498  */
499 
500 char *
501 expand(name)
502 	char name[];
503 {
504 	char xname[BUFSIZ];
505 	char cmdbuf[BUFSIZ];
506 	register int pid, l, rc;
507 	register char *cp, *Shell;
508 	int s, pivec[2], (*sigint)();
509 	struct stat sbuf;
510 
511 	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
512 		sprintf(xname, "%s/%s", cmdbuf, name + 1);
513 		return(expand(savestr(xname)));
514 	}
515 	if (!anyof(name, "~{[*?$`'\"\\"))
516 		return(name);
517 	if (pipe(pivec) < 0) {
518 		perror("pipe");
519 		return(name);
520 	}
521 	sprintf(cmdbuf, "echo %s", name);
522 	if ((pid = vfork()) == 0) {
523 		Shell = value("SHELL");
524 		if (Shell == NOSTR)
525 			Shell = SHELL;
526 		close(pivec[0]);
527 		close(1);
528 		dup(pivec[1]);
529 		close(pivec[1]);
530 		close(2);
531 		execl(Shell, Shell, "-c", cmdbuf, 0);
532 		_exit(1);
533 	}
534 	if (pid == -1) {
535 		perror("fork");
536 		close(pivec[0]);
537 		close(pivec[1]);
538 		return(NOSTR);
539 	}
540 	close(pivec[1]);
541 	l = read(pivec[0], xname, BUFSIZ);
542 	close(pivec[0]);
543 	while (wait(&s) != pid);
544 		;
545 	s &= 0377;
546 	if (s != 0 && s != SIGPIPE) {
547 		fprintf(stderr, "\"Echo\" failed\n");
548 		goto err;
549 	}
550 	if (l < 0) {
551 		perror("read");
552 		goto err;
553 	}
554 	if (l == 0) {
555 		fprintf(stderr, "\"%s\": No match\n", name);
556 		goto err;
557 	}
558 	if (l == BUFSIZ) {
559 		fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
560 		goto err;
561 	}
562 	xname[l] = 0;
563 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
564 		;
565 	*++cp = '\0';
566 	if (any(' ', xname) && stat(xname, &sbuf) < 0) {
567 		fprintf(stderr, "\"%s\": Ambiguous\n", name);
568 		goto err;
569 	}
570 	return(savestr(xname));
571 
572 err:
573 	return(NOSTR);
574 }
575 
576 /*
577  * Determine the current folder directory name.
578  */
579 getfold(name)
580 	char *name;
581 {
582 	char *folder;
583 
584 	if ((folder = value("folder")) == NOSTR)
585 		return(-1);
586 	if (*folder == '/')
587 		strcpy(name, folder);
588 	else
589 		sprintf(name, "%s/%s", homedir, folder);
590 	return(0);
591 }
592 
593 /*
594  * A nicer version of Fdopen, which allows us to fclose
595  * without losing the open file.
596  */
597 
598 FILE *
599 Fdopen(fildes, mode)
600 	char *mode;
601 {
602 	register int f;
603 	FILE *fdopen();
604 
605 	f = dup(fildes);
606 	if (f < 0) {
607 		perror("dup");
608 		return(NULL);
609 	}
610 	return(fdopen(f, mode));
611 }
612