xref: /original-bsd/usr.bin/mail/fio.c (revision ba72ef4c)
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.2 10/09/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 	if (message != (struct message *) 0)
171 		cfree((char *) message);
172 	message = (struct message *) mp;
173 	dot = message;
174 	lseek(f, 0L, 0);
175 	while (count = read(f, mp, BUFSIZ))
176 		mp += count;
177 	for (m = &message[0]; m < &message[msgCount]; m++) {
178 		m->m_size = (m+1)->m_size;
179 		m->m_lines = (m+1)->m_lines;
180 	}
181 	message[msgCount].m_size = 0;
182 	message[msgCount].m_lines = 0;
183 }
184 
185 /*
186  * Append the passed message descriptor onto the temp file.
187  * If the write fails, return 1, else 0
188  */
189 
190 append(mp, f)
191 	struct message *mp;
192 {
193 	if (write(f, (char *) mp, sizeof *mp) != sizeof *mp)
194 		return(1);
195 	return(0);
196 }
197 
198 /*
199  * Delete a file, but only if the file is a plain file.
200  */
201 
202 remove(name)
203 	char name[];
204 {
205 	struct stat statb;
206 	extern int errno;
207 
208 	if (stat(name, &statb) < 0)
209 		return(-1);
210 	if ((statb.st_mode & S_IFMT) != S_IFREG) {
211 		errno = EISDIR;
212 		return(-1);
213 	}
214 	return(unlink(name));
215 }
216 
217 /*
218  * Terminate an editing session by attempting to write out the user's
219  * file from the temporary.
220  */
221 
222 edstop()
223 {
224 	register int gotcha, c;
225 	register struct message *mp;
226 	FILE *obuf;
227 
228 	if (readonly)
229 		return;
230 	for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++)
231 		if (mp->m_flag & (MODIFY|MDELETED)) {
232 			gotcha++;
233 			break;
234 		}
235 	if (!gotcha)
236 		return;
237 	printf("\"%s\" ", editfile);
238 	flush();
239 	if ((obuf = fopen(editfile, "w")) == NULL) {
240 		perror(editfile);
241 		reset(0);
242 	}
243 	c = 0;
244 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
245 		if ((mp->m_flag & MDELETED) != 0)
246 			continue;
247 		c++;
248 		if (send(mp, obuf) < 0) {
249 			perror(editfile);
250 			reset(0);
251 		}
252 	}
253 	fflush(obuf);
254 	if (ferror(obuf)) {
255 		perror(editfile);
256 		reset(0);
257 	}
258 	if (c == 0) {
259 		remove(editfile);
260 		printf("removed\n");
261 	}
262 	else
263 		printf("complete\n");
264 	flush();
265 }
266 
267 /*
268  * Empty the output buffer.
269  */
270 
271 clrbuf(buf)
272 	register FILE *buf;
273 {
274 
275 	buf = stdout;
276 	buf->_ptr = buf->_base;
277 	buf->_cnt = BUFSIZ;
278 }
279 
280 /*
281  * Open a temp file by creating, closing, unlinking, and
282  * reopening.  Return the open file descriptor.
283  */
284 
285 opentemp(file)
286 	char file[];
287 {
288 	register int f;
289 
290 	if ((f = creat(file, 0600)) < 0) {
291 		perror(file);
292 		return(-1);
293 	}
294 	close(f);
295 	if ((f = open(file, 2)) < 0) {
296 		perror(file);
297 		remove(file);
298 		return(-1);
299 	}
300 	remove(file);
301 	return(f);
302 }
303 
304 /*
305  * Flush the standard output.
306  */
307 
308 flush()
309 {
310 	fflush(stdout);
311 	fflush(stderr);
312 }
313 
314 /*
315  * Determine the size of the file possessed by
316  * the passed buffer.
317  */
318 
319 off_t
320 fsize(iob)
321 	FILE *iob;
322 {
323 	register int f;
324 	struct stat sbuf;
325 
326 	f = fileno(iob);
327 	if (fstat(f, &sbuf) < 0)
328 		return(0);
329 	return(sbuf.st_size);
330 }
331 
332 /*
333  * Take a file name, possibly with shell meta characters
334  * in it and expand it by using "sh -c echo filename"
335  * Return the file name as a dynamic string.
336  */
337 
338 char *
339 expand(name)
340 	char name[];
341 {
342 	char xname[BUFSIZ];
343 	char cmdbuf[BUFSIZ];
344 	register int pid, l, rc;
345 	register char *cp, *Shell;
346 	int s, pivec[2], (*sigint)();
347 	struct stat sbuf;
348 
349 	if (!anyof(name, "~{[*?$`'\"\\"))
350 		return(name);
351 	/* sigint = signal(SIGINT, SIG_IGN); */
352 	if (pipe(pivec) < 0) {
353 		perror("pipe");
354 		/* signal(SIGINT, sigint) */
355 		return(name);
356 	}
357 	sprintf(cmdbuf, "echo %s", name);
358 	if ((pid = vfork()) == 0) {
359 		Shell = value("SHELL");
360 		if (Shell == NOSTR)
361 			Shell = SHELL;
362 		close(pivec[0]);
363 		close(1);
364 		dup(pivec[1]);
365 		close(pivec[1]);
366 		close(2);
367 		execl(Shell, Shell, "-c", cmdbuf, 0);
368 		_exit(1);
369 	}
370 	if (pid == -1) {
371 		perror("fork");
372 		close(pivec[0]);
373 		close(pivec[1]);
374 		return(NOSTR);
375 	}
376 	close(pivec[1]);
377 	l = read(pivec[0], xname, BUFSIZ);
378 	close(pivec[0]);
379 	while (wait(&s) != pid);
380 		;
381 	s &= 0377;
382 	if (s != 0 && s != SIGPIPE) {
383 		fprintf(stderr, "\"Echo\" failed\n");
384 		goto err;
385 	}
386 	if (l < 0) {
387 		perror("read");
388 		goto err;
389 	}
390 	if (l == 0) {
391 		fprintf(stderr, "\"%s\": No match\n", name);
392 		goto err;
393 	}
394 	if (l == BUFSIZ) {
395 		fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
396 		goto err;
397 	}
398 	xname[l] = 0;
399 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
400 		;
401 	*++cp = '\0';
402 	if (any(' ', xname) && stat(xname, &sbuf) < 0) {
403 		fprintf(stderr, "\"%s\": Ambiguous\n", name);
404 		goto err;
405 	}
406 	/* signal(SIGINT, sigint) */
407 	return(savestr(xname));
408 
409 err:
410 	/* signal(SIGINT, sigint); */
411 	return(NOSTR);
412 }
413 
414 /*
415  * A nicer version of Fdopen, which allows us to fclose
416  * without losing the open file.
417  */
418 
419 FILE *
420 Fdopen(fildes, mode)
421 	char *mode;
422 {
423 	register int f;
424 	FILE *fdopen();
425 
426 	f = dup(fildes);
427 	if (f < 0) {
428 		perror("dup");
429 		return(NULL);
430 	}
431 	return(fdopen(f, mode));
432 }
433