xref: /original-bsd/usr.bin/mail/fio.c (revision 0842ddeb)
1 /*
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)fio.c	8.2 (Berkeley) 04/20/95";
10 #endif /* not lint */
11 
12 #include "rcv.h"
13 #include <sys/file.h>
14 #include <sys/wait.h>
15 
16 #include <unistd.h>
17 #include <paths.h>
18 #include <errno.h>
19 #include "extern.h"
20 
21 /*
22  * Mail -- a mail program
23  *
24  * File I/O.
25  */
26 
27 /*
28  * Set up the input pointers while copying the mail file into /tmp.
29  */
30 void
31 setptr(ibuf, offset)
32 	register FILE *ibuf;
33 	off_t offset;
34 {
35 	extern char *tmpdir;
36 	register int c, count;
37 	register char *cp, *cp2;
38 	struct message this;
39 	FILE *mestmp;
40 	int maybe, inhead;
41 	char linebuf[LINESIZE];
42 	int omsgCount;
43 
44 	/* Get temporary file. */
45 	(void)sprintf(linebuf, "%s/mail.XXXXXX", tmpdir);
46 	if ((c = mkstemp(linebuf)) == -1 ||
47 	    (mestmp = Fdopen(c, "r+")) == NULL) {
48 		(void)fprintf(stderr, "mail: can't open %s\n", linebuf);
49 		exit(1);
50 	}
51 	(void)unlink(linebuf);
52 
53 	if (offset == 0) {
54 		 msgCount = 0;
55 	} else {
56 		/* Seek into the file to get to the new messages */
57 		(void) fseek(ibuf, offset, 0);
58 		/*
59 		 * We need to make "offset" a pointer to the end of
60 		 * the temp file that has the copy of the mail file.
61 		 * If any messages have been edited, this will be
62 		 * different from the offset into the mail file.
63 		 */
64 		(void) fseek(otf, 0L, 2);
65 		offset = ftell(otf);
66 	}
67 	omsgCount = msgCount;
68 	maybe = 1;
69 	inhead = 0;
70 	this.m_flag = MUSED|MNEW;
71 	this.m_size = 0;
72 	this.m_lines = 0;
73 	this.m_block = 0;
74 	this.m_offset = 0;
75 	for (;;) {
76 		if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
77 			if (append(&this, mestmp)) {
78 				perror("temporary file");
79 				exit(1);
80 			}
81 			makemessage(mestmp, omsgCount);
82 			return;
83 		}
84 		count = strlen(linebuf);
85 		(void) fwrite(linebuf, sizeof *linebuf, count, otf);
86 		if (ferror(otf)) {
87 			perror("/tmp");
88 			exit(1);
89 		}
90 		linebuf[count - 1] = 0;
91 		if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
92 			msgCount++;
93 			if (append(&this, mestmp)) {
94 				perror("temporary file");
95 				exit(1);
96 			}
97 			this.m_flag = MUSED|MNEW;
98 			this.m_size = 0;
99 			this.m_lines = 0;
100 			this.m_block = blockof(offset);
101 			this.m_offset = offsetof(offset);
102 			inhead = 1;
103 		} else if (linebuf[0] == 0) {
104 			inhead = 0;
105 		} else if (inhead) {
106 			for (cp = linebuf, cp2 = "status";; cp++) {
107 				if ((c = *cp2++) == 0) {
108 					while (isspace(*cp++))
109 						;
110 					if (cp[-1] != ':')
111 						break;
112 					while (c = *cp++)
113 						if (c == 'R')
114 							this.m_flag |= MREAD;
115 						else if (c == 'O')
116 							this.m_flag &= ~MNEW;
117 					inhead = 0;
118 					break;
119 				}
120 				if (*cp != c && *cp != toupper(c))
121 					break;
122 			}
123 		}
124 		offset += count;
125 		this.m_size += count;
126 		this.m_lines++;
127 		maybe = linebuf[0] == 0;
128 	}
129 }
130 
131 /*
132  * Drop the passed line onto the passed output buffer.
133  * If a write error occurs, return -1, else the count of
134  * characters written, including the newline.
135  */
136 int
137 putline(obuf, linebuf)
138 	FILE *obuf;
139 	char *linebuf;
140 {
141 	register int c;
142 
143 	c = strlen(linebuf);
144 	(void) fwrite(linebuf, sizeof *linebuf, c, obuf);
145 	(void) putc('\n', obuf);
146 	if (ferror(obuf))
147 		return (-1);
148 	return (c + 1);
149 }
150 
151 /*
152  * Read up a line from the specified input into the line
153  * buffer.  Return the number of characters read.  Do not
154  * include the newline at the end.
155  */
156 int
157 readline(ibuf, linebuf, linesize)
158 	FILE *ibuf;
159 	char *linebuf;
160 	int linesize;
161 {
162 	register int n;
163 
164 	clearerr(ibuf);
165 	if (fgets(linebuf, linesize, ibuf) == NULL)
166 		return -1;
167 	n = strlen(linebuf);
168 	if (n > 0 && linebuf[n - 1] == '\n')
169 		linebuf[--n] = '\0';
170 	return n;
171 }
172 
173 /*
174  * Return a file buffer all ready to read up the
175  * passed message pointer.
176  */
177 FILE *
178 setinput(mp)
179 	register struct message *mp;
180 {
181 
182 	fflush(otf);
183 	if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) {
184 		perror("fseek");
185 		panic("temporary file seek");
186 	}
187 	return (itf);
188 }
189 
190 /*
191  * Take the data out of the passed ghost file and toss it into
192  * a dynamically allocated message structure.
193  */
194 void
195 makemessage(f, omsgCount)
196 	FILE *f;
197 	int omsgCount;
198 {
199 	register size = (msgCount + 1) * sizeof (struct message);
200 
201 	if (omsgCount) {
202 		message = (struct message *)realloc(message, (unsigned) size);
203 		if (message == 0)
204 			panic("Insufficient memory for %d messages\n", msgCount);
205 	} else {
206 		if (message != 0)
207 			free((char *) message);
208 		if ((message = (struct message *) malloc((unsigned) size)) == 0)
209 			panic("Insufficient memory for %d messages", msgCount);
210 		dot = message;
211 	}
212 	size -= (omsgCount + 1) * sizeof (struct message);
213 	fflush(f);
214 	(void) lseek(fileno(f), (off_t)sizeof *message, 0);
215 	if (read(fileno(f), (char *) &message[omsgCount], size) != size)
216 		panic("Message temporary file corrupted");
217 	message[msgCount].m_size = 0;
218 	message[msgCount].m_lines = 0;
219 	Fclose(f);
220 }
221 
222 /*
223  * Append the passed message descriptor onto the temp file.
224  * If the write fails, return 1, else 0
225  */
226 int
227 append(mp, f)
228 	struct message *mp;
229 	FILE *f;
230 {
231 	return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
232 }
233 
234 /*
235  * Delete a file, but only if the file is a plain file.
236  */
237 int
238 rm(name)
239 	char *name;
240 {
241 	struct stat sb;
242 
243 	if (stat(name, &sb) < 0)
244 		return(-1);
245 	if (!S_ISREG(sb.st_mode)) {
246 		errno = EISDIR;
247 		return(-1);
248 	}
249 	return(unlink(name));
250 }
251 
252 static int sigdepth;		/* depth of holdsigs() */
253 static int omask;
254 /*
255  * Hold signals SIGHUP, SIGINT, and SIGQUIT.
256  */
257 void
258 holdsigs()
259 {
260 
261 	if (sigdepth++ == 0)
262 		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
263 }
264 
265 /*
266  * Release signals SIGHUP, SIGINT, and SIGQUIT.
267  */
268 void
269 relsesigs()
270 {
271 
272 	if (--sigdepth == 0)
273 		sigsetmask(omask);
274 }
275 
276 /*
277  * Determine the size of the file possessed by
278  * the passed buffer.
279  */
280 off_t
281 fsize(iob)
282 	FILE *iob;
283 {
284 	struct stat sbuf;
285 
286 	if (fstat(fileno(iob), &sbuf) < 0)
287 		return 0;
288 	return sbuf.st_size;
289 }
290 
291 /*
292  * Evaluate the string given as a new mailbox name.
293  * Supported meta characters:
294  *	%	for my system mail box
295  *	%user	for user's system mail box
296  *	#	for previous file
297  *	&	invoker's mbox file
298  *	+file	file in folder directory
299  *	any shell meta character
300  * Return the file name as a dynamic string.
301  */
302 char *
303 expand(name)
304 	register char *name;
305 {
306 	char xname[PATHSIZE];
307 	char cmdbuf[PATHSIZE];		/* also used for file names */
308 	register int pid, l;
309 	register char *cp, *shell;
310 	int pivec[2];
311 	struct stat sbuf;
312 	extern union wait wait_status;
313 
314 	/*
315 	 * The order of evaluation is "%" and "#" expand into constants.
316 	 * "&" can expand into "+".  "+" can expand into shell meta characters.
317 	 * Shell meta characters expand into constants.
318 	 * This way, we make no recursive expansion.
319 	 */
320 	switch (*name) {
321 	case '%':
322 		findmail(name[1] ? name + 1 : myname, xname);
323 		return savestr(xname);
324 	case '#':
325 		if (name[1] != 0)
326 			break;
327 		if (prevfile[0] == 0) {
328 			printf("No previous file\n");
329 			return NOSTR;
330 		}
331 		return savestr(prevfile);
332 	case '&':
333 		if (name[1] == 0 && (name = value("MBOX")) == NOSTR)
334 			name = "~/mbox";
335 		/* fall through */
336 	}
337 	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
338 		sprintf(xname, "%s/%s", cmdbuf, name + 1);
339 		name = savestr(xname);
340 	}
341 	/* catch the most common shell meta character */
342 	if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
343 		sprintf(xname, "%s%s", homedir, name + 1);
344 		name = savestr(xname);
345 	}
346 	if (!anyof(name, "~{[*?$`'\"\\"))
347 		return name;
348 	if (pipe(pivec) < 0) {
349 		perror("pipe");
350 		return name;
351 	}
352 	sprintf(cmdbuf, "echo %s", name);
353 	if ((shell = value("SHELL")) == NOSTR)
354 		shell = _PATH_CSHELL;
355 	pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR);
356 	if (pid < 0) {
357 		close(pivec[0]);
358 		close(pivec[1]);
359 		return NOSTR;
360 	}
361 	close(pivec[1]);
362 	l = read(pivec[0], xname, BUFSIZ);
363 	close(pivec[0]);
364 	if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) {
365 		fprintf(stderr, "\"%s\": Expansion failed.\n", name);
366 		return NOSTR;
367 	}
368 	if (l < 0) {
369 		perror("read");
370 		return NOSTR;
371 	}
372 	if (l == 0) {
373 		fprintf(stderr, "\"%s\": No match.\n", name);
374 		return NOSTR;
375 	}
376 	if (l == BUFSIZ) {
377 		fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
378 		return NOSTR;
379 	}
380 	xname[l] = 0;
381 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
382 		;
383 	cp[1] = '\0';
384 	if (index(xname, ' ') && stat(xname, &sbuf) < 0) {
385 		fprintf(stderr, "\"%s\": Ambiguous.\n", name);
386 		return NOSTR;
387 	}
388 	return savestr(xname);
389 }
390 
391 /*
392  * Determine the current folder directory name.
393  */
394 int
395 getfold(name)
396 	char *name;
397 {
398 	char *folder;
399 
400 	if ((folder = value("folder")) == NOSTR)
401 		return (-1);
402 	if (*folder == '/')
403 		strcpy(name, folder);
404 	else
405 		sprintf(name, "%s/%s", homedir, folder);
406 	return (0);
407 }
408 
409 /*
410  * Return the name of the dead.letter file.
411  */
412 char *
413 getdeadletter()
414 {
415 	register char *cp;
416 
417 	if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR)
418 		cp = expand("~/dead.letter");
419 	else if (*cp != '/') {
420 		char buf[PATHSIZE];
421 
422 		(void) sprintf(buf, "~/%s", cp);
423 		cp = expand(buf);
424 	}
425 	return cp;
426 }
427