xref: /original-bsd/usr.bin/mail/fio.c (revision 552e81d8)
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.1 10/08/80";
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 count, s, l;
24 	off_t offset;
25 	char linebuf[LINESIZE];
26 	int maybe, mestmp, flag;
27 	struct message this;
28 	extern char tempSet[];
29 
30 	if ((mestmp = opentemp(tempSet)) < 0)
31 		exit(1);
32 	msgCount = 0;
33 	offset = 0;
34 	s = 0;
35 	l = 0;
36 	maybe = 1;
37 	flag = MUSED;
38 	if (value("hold") != NOSTR)
39 		flag = MPRESERVE|MUSED;
40 	for (;;) {
41 		if ((count = readline(ibuf, linebuf)) == 0) {
42 			this.m_flag = flag;
43 			this.m_offset = offsetof(offset);
44 			this.m_block = blockof(offset);
45 			this.m_size = s;
46 			this.m_lines = l;
47 			if (append(&this, mestmp)) {
48 				perror(tempSet);
49 				exit(1);
50 			}
51 			fclose(ibuf);
52 			makemessage(mestmp);
53 			close(mestmp);
54 			return;
55 		}
56 		if (putline(otf, linebuf) < 0) {
57 			perror("/tmp");
58 			exit(1);
59 		}
60 		if (maybe && ishead(linebuf)) {
61 			msgCount++;
62 			this.m_flag = flag;
63 			this.m_block = blockof(offset);
64 			this.m_offset = offsetof(offset);
65 			this.m_size = s;
66 			this.m_lines = l;
67 			s = 0;
68 			l = 0;
69 			if (append(&this, mestmp)) {
70 				perror(tempSet);
71 				exit(1);
72 			}
73 		}
74 		offset += count;
75 		s += count;
76 		l++;
77 		maybe = 0;
78 		if (linebuf[0] == 0)
79 			maybe = 1;
80 	}
81 }
82 
83 /*
84  * Drop the passed line onto the passed output buffer.
85  * If a write error occurs, return -1, else the count of
86  * characters written, including the newline.
87  */
88 
89 putline(obuf, linebuf)
90 	FILE *obuf;
91 	char *linebuf;
92 {
93 	register int c;
94 
95 	c = strlen(linebuf);
96 	fputs(linebuf, obuf);
97 	putc('\n', obuf);
98 	if (ferror(obuf))
99 		return(-1);
100 	return(c+1);
101 }
102 
103 /*
104  * Read up a line from the specified input into the line
105  * buffer.  Return the number of characters read.  Do not
106  * include the newline at the end.
107  */
108 
109 readline(ibuf, linebuf)
110 	FILE *ibuf;
111 	char *linebuf;
112 {
113 	register char *cp;
114 	register int c;
115 
116 	do {
117 		clearerr(ibuf);
118 		c = getc(ibuf);
119 		for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) {
120 			if (c == 0)
121 				continue;
122 			if (cp - linebuf < LINESIZE-2)
123 				*cp++ = c;
124 		}
125 	} while (ferror(ibuf) && ibuf == stdin);
126 	*cp = 0;
127 	if (c == EOF && cp == linebuf)
128 		return(0);
129 	return(cp - linebuf + 1);
130 }
131 
132 /*
133  * Return a file buffer all ready to read up the
134  * passed message pointer.
135  */
136 
137 FILE *
138 setinput(mp)
139 	register struct message *mp;
140 {
141 	off_t off;
142 
143 	fflush(otf);
144 	off = mp->m_block;
145 	off <<= 9;
146 	off += mp->m_offset;
147 	if (fseek(itf, off, 0) < 0) {
148 		perror("fseek");
149 		panic("temporary file seek");
150 	}
151 	return(itf);
152 }
153 
154 /*
155  * Take the data out of the passed ghost file and toss it into
156  * a dynamically allocated message structure.
157  */
158 
159 makemessage(f)
160 {
161 	register struct message *m;
162 	register char *mp;
163 	register count;
164 
165 	mp = calloc((unsigned) (msgCount + 1), sizeof *m);
166 	if (mp == NOSTR) {
167 		printf("Insufficient memory for %d messages\n", msgCount);
168 		exit(1);
169 	}
170 	message = (struct message *) mp;
171 	dot = message;
172 	lseek(f, 0L, 0);
173 	while (count = read(f, mp, BUFSIZ))
174 		mp += count;
175 	for (m = &message[0]; m < &message[msgCount]; m++) {
176 		m->m_size = (m+1)->m_size;
177 		m->m_lines = (m+1)->m_lines;
178 	}
179 	message[msgCount].m_size = 0;
180 	message[msgCount].m_lines = 0;
181 }
182 
183 /*
184  * Append the passed message descriptor onto the temp file.
185  * If the write fails, return 1, else 0
186  */
187 
188 append(mp, f)
189 	struct message *mp;
190 {
191 	if (write(f, (char *) mp, sizeof *mp) != sizeof *mp)
192 		return(1);
193 	return(0);
194 }
195 
196 /*
197  * Delete a file, but only if the file is a plain file.
198  */
199 
200 remove(name)
201 	char name[];
202 {
203 	struct stat statb;
204 	extern int errno;
205 
206 	if (stat(name, &statb) < 0)
207 		return(-1);
208 	if ((statb.st_mode & S_IFMT) != S_IFREG) {
209 		errno = EISDIR;
210 		return(-1);
211 	}
212 	return(unlink(name));
213 }
214 
215 /*
216  * Terminate an editing session by attempting to write out the user's
217  * file from the temporary.
218  */
219 
220 edstop()
221 {
222 	register int gotcha, c;
223 	register struct message *mp;
224 	FILE *obuf;
225 
226 	for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++)
227 		if (mp->m_flag & (MODIFY|MDELETED)) {
228 			gotcha++;
229 			break;
230 		}
231 	if (!gotcha)
232 		return;
233 	printf("\"%s\" ", editfile);
234 	flush();
235 	if ((obuf = fopen(editfile, "w")) == NULL) {
236 		perror(editfile);
237 		reset(0);
238 	}
239 	c = 0;
240 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
241 		if ((mp->m_flag & MDELETED) != 0)
242 			continue;
243 		c++;
244 		if (send(mp, obuf) < 0) {
245 			perror(editfile);
246 			reset(0);
247 		}
248 	}
249 	fflush(obuf);
250 	if (ferror(obuf)) {
251 		perror(editfile);
252 		reset(0);
253 	}
254 	if (c == 0) {
255 		remove(editfile);
256 		printf("removed\n");
257 	}
258 	else
259 		printf("complete\n");
260 	flush();
261 }
262 
263 /*
264  * Empty the output buffer.
265  */
266 
267 clrbuf(buf)
268 	register FILE *buf;
269 {
270 
271 	buf = stdout;
272 	buf->_ptr = buf->_base;
273 	buf->_cnt = BUFSIZ;
274 }
275 
276 /*
277  * Open a temp file by creating, closing, unlinking, and
278  * reopening.  Return the open file descriptor.
279  */
280 
281 opentemp(file)
282 	char file[];
283 {
284 	register int f;
285 
286 	if ((f = creat(file, 0600)) < 0) {
287 		perror(file);
288 		return(-1);
289 	}
290 	close(f);
291 	if ((f = open(file, 2)) < 0) {
292 		perror(file);
293 		remove(file);
294 		return(-1);
295 	}
296 	remove(file);
297 	return(f);
298 }
299 
300 /*
301  * Flush the standard output.
302  */
303 
304 flush()
305 {
306 	fflush(stdout);
307 	fflush(stderr);
308 }
309 
310 /*
311  * Determine the size of the file possessed by
312  * the passed buffer.
313  */
314 
315 off_t
316 fsize(iob)
317 	FILE *iob;
318 {
319 	register int f;
320 	struct stat sbuf;
321 
322 	f = fileno(iob);
323 	if (fstat(f, &sbuf) < 0)
324 		return(0);
325 	return(sbuf.st_size);
326 }
327 
328 /*
329  * Take a file name, possibly with shell meta characters
330  * in it and expand it by using "sh -c echo filename"
331  * Return the file name as a dynamic string.
332  */
333 
334 char *
335 expand(name)
336 	char name[];
337 {
338 	char xname[BUFSIZ];
339 	char cmdbuf[BUFSIZ];
340 	register int pid, l, rc;
341 	register char *cp, *Shell;
342 	int s, pivec[2], (*sigint)();
343 	struct stat sbuf;
344 
345 	if (!anyof(name, "~{[*?$`'\"\\"))
346 		return(name);
347 	/* sigint = signal(SIGINT, SIG_IGN); */
348 	if (pipe(pivec) < 0) {
349 		perror("pipe");
350 		/* signal(SIGINT, sigint) */
351 		return(name);
352 	}
353 	sprintf(cmdbuf, "echo %s", name);
354 	if ((pid = vfork()) == 0) {
355 		Shell = value("SHELL");
356 		if (Shell == NOSTR)
357 			Shell = SHELL;
358 		close(pivec[0]);
359 		close(1);
360 		dup(pivec[1]);
361 		close(pivec[1]);
362 		close(2);
363 		execl(Shell, Shell, "-c", cmdbuf, 0);
364 		_exit(1);
365 	}
366 	if (pid == -1) {
367 		perror("fork");
368 		close(pivec[0]);
369 		close(pivec[1]);
370 		return(NOSTR);
371 	}
372 	close(pivec[1]);
373 	l = read(pivec[0], xname, BUFSIZ);
374 	close(pivec[0]);
375 	while (wait(&s) != pid);
376 		;
377 	s &= 0377;
378 	if (s != 0 && s != SIGPIPE) {
379 		fprintf(stderr, "\"Echo\" failed\n");
380 		goto err;
381 	}
382 	if (l < 0) {
383 		perror("read");
384 		goto err;
385 	}
386 	if (l == 0) {
387 		fprintf(stderr, "\"%s\": No match\n", name);
388 		goto err;
389 	}
390 	if (l == BUFSIZ) {
391 		fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
392 		goto err;
393 	}
394 	xname[l] = 0;
395 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
396 		;
397 	*++cp = '\0';
398 	if (any(' ', xname) && stat(xname, &sbuf) < 0) {
399 		fprintf(stderr, "\"%s\": Ambiguous\n", name);
400 		goto err;
401 	}
402 	/* signal(SIGINT, sigint) */
403 	return(savestr(xname));
404 
405 err:
406 	/* signal(SIGINT, sigint); */
407 	return(NOSTR);
408 }
409 
410 /*
411  * A nicer version of Fdopen, which allows us to fclose
412  * without losing the open file.
413  */
414 
415 FILE *
416 Fdopen(fildes, mode)
417 	char *mode;
418 {
419 	register int f;
420 	FILE *fdopen();
421 
422 	f = dup(fildes);
423 	if (f < 0) {
424 		perror("dup");
425 		return(NULL);
426 	}
427 	return(fdopen(f, mode));
428 }
429