xref: /original-bsd/usr.bin/mail/fio.c (revision f71cd02e)
1 #ifndef lint
2 static char sccsid[] = "@(#)fio.c	2.18 (Berkeley) 01/09/85";
3 #endif
4 
5 #include "rcv.h"
6 #include <sys/stat.h>
7 #include <errno.h>
8 
9 /*
10  * Mail -- a mail program
11  *
12  * File I/O.
13  */
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 	fflush(stdout);
355 	if ((obuf = fopen(editfile, "r+")) == NULL) {
356 		perror(editfile);
357 		relsesigs();
358 		reset(0);
359 	}
360 	trunc(obuf);
361 	c = 0;
362 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
363 		if ((mp->m_flag & MDELETED) != 0)
364 			continue;
365 		c++;
366 		if (send(mp, obuf, 0) < 0) {
367 			perror(editfile);
368 			relsesigs();
369 			reset(0);
370 		}
371 	}
372 	gotcha = (c == 0 && ibuf == NULL);
373 	if (ibuf != NULL) {
374 		while ((c = getc(ibuf)) != EOF)
375 			putc(c, obuf);
376 		fclose(ibuf);
377 	}
378 	fflush(obuf);
379 	if (ferror(obuf)) {
380 		perror(editfile);
381 		relsesigs();
382 		reset(0);
383 	}
384 	fclose(obuf);
385 	if (gotcha) {
386 		remove(editfile);
387 		printf("removed\n");
388 	}
389 	else
390 		printf("complete\n");
391 	fflush(stdout);
392 
393 done:
394 	relsesigs();
395 }
396 
397 static int sigdepth = 0;		/* depth of holdsigs() */
398 static int omask = 0;
399 /*
400  * Hold signals SIGHUP - SIGQUIT.
401  */
402 holdsigs()
403 {
404 	register int i;
405 
406 	if (sigdepth++ == 0)
407 		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
408 }
409 
410 /*
411  * Release signals SIGHUP - SIGQUIT
412  */
413 relsesigs()
414 {
415 	register int i;
416 
417 	if (--sigdepth == 0)
418 		sigsetmask(omask);
419 }
420 
421 /*
422  * Open a temp file by creating, closing, unlinking, and
423  * reopening.  Return the open file descriptor.
424  */
425 
426 opentemp(file)
427 	char file[];
428 {
429 	register int f;
430 
431 	if ((f = creat(file, 0600)) < 0) {
432 		perror(file);
433 		return(-1);
434 	}
435 	close(f);
436 	if ((f = open(file, 2)) < 0) {
437 		perror(file);
438 		remove(file);
439 		return(-1);
440 	}
441 	remove(file);
442 	return(f);
443 }
444 
445 /*
446  * Determine the size of the file possessed by
447  * the passed buffer.
448  */
449 
450 off_t
451 fsize(iob)
452 	FILE *iob;
453 {
454 	register int f;
455 	struct stat sbuf;
456 
457 	f = fileno(iob);
458 	if (fstat(f, &sbuf) < 0)
459 		return(0);
460 	return(sbuf.st_size);
461 }
462 
463 /*
464  * Take a file name, possibly with shell meta characters
465  * in it and expand it by using "sh -c echo filename"
466  * Return the file name as a dynamic string.
467  */
468 
469 char *
470 expand(name)
471 	char name[];
472 {
473 	char xname[BUFSIZ];
474 	char cmdbuf[BUFSIZ];
475 	register int pid, l, rc;
476 	register char *cp, *Shell;
477 	int s, pivec[2], (*sigint)();
478 	struct stat sbuf;
479 
480 	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
481 		sprintf(xname, "%s/%s", cmdbuf, name + 1);
482 		return(expand(savestr(xname)));
483 	}
484 	if (!anyof(name, "~{[*?$`'\"\\"))
485 		return(name);
486 	if (pipe(pivec) < 0) {
487 		perror("pipe");
488 		return(name);
489 	}
490 	sprintf(cmdbuf, "echo %s", name);
491 	if ((pid = vfork()) == 0) {
492 		sigchild();
493 		Shell = value("SHELL");
494 		if (Shell == NOSTR)
495 			Shell = SHELL;
496 		close(pivec[0]);
497 		close(1);
498 		dup(pivec[1]);
499 		close(pivec[1]);
500 		close(2);
501 		execl(Shell, Shell, "-c", cmdbuf, 0);
502 		_exit(1);
503 	}
504 	if (pid == -1) {
505 		perror("fork");
506 		close(pivec[0]);
507 		close(pivec[1]);
508 		return(NOSTR);
509 	}
510 	close(pivec[1]);
511 	l = read(pivec[0], xname, BUFSIZ);
512 	close(pivec[0]);
513 	while (wait(&s) != pid);
514 		;
515 	s &= 0377;
516 	if (s != 0 && s != SIGPIPE) {
517 		fprintf(stderr, "\"Echo\" failed\n");
518 		goto err;
519 	}
520 	if (l < 0) {
521 		perror("read");
522 		goto err;
523 	}
524 	if (l == 0) {
525 		fprintf(stderr, "\"%s\": No match\n", name);
526 		goto err;
527 	}
528 	if (l == BUFSIZ) {
529 		fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
530 		goto err;
531 	}
532 	xname[l] = 0;
533 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
534 		;
535 	*++cp = '\0';
536 	if (any(' ', xname) && stat(xname, &sbuf) < 0) {
537 		fprintf(stderr, "\"%s\": Ambiguous\n", name);
538 		goto err;
539 	}
540 	return(savestr(xname));
541 
542 err:
543 	return(NOSTR);
544 }
545 
546 /*
547  * Determine the current folder directory name.
548  */
549 getfold(name)
550 	char *name;
551 {
552 	char *folder;
553 
554 	if ((folder = value("folder")) == NOSTR)
555 		return(-1);
556 	if (*folder == '/')
557 		strcpy(name, folder);
558 	else
559 		sprintf(name, "%s/%s", homedir, folder);
560 	return(0);
561 }
562 
563 /*
564  * A nicer version of Fdopen, which allows us to fclose
565  * without losing the open file.
566  */
567 
568 FILE *
569 Fdopen(fildes, mode)
570 	char *mode;
571 {
572 	register int f;
573 	FILE *fdopen();
574 
575 	f = dup(fildes);
576 	if (f < 0) {
577 		perror("dup");
578 		return(NULL);
579 	}
580 	return(fdopen(f, mode));
581 }
582