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