]> git.cameronkatri.com Git - apple_cmds.git/blob - mail_cmds/mail/fio.c
gitignore: Add executables and compressed manpages
[apple_cmds.git] / mail_cmds / mail / fio.c
1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95";
37 #endif
38 static const char rcsid[] =
39 "$FreeBSD: src/usr.bin/mail/fio.c,v 1.12 2002/06/30 05:25:06 obrien Exp $";
40 #endif /* not lint */
41
42 #include <sys/cdefs.h>
43
44 #include "rcv.h"
45 #include <sys/file.h>
46 #include <sys/wait.h>
47
48 #include <unistd.h>
49 #include <paths.h>
50 #include <errno.h>
51 #include "extern.h"
52
53 /*
54 * Mail -- a mail program
55 *
56 * File I/O.
57 */
58
59 extern int wait_status;
60
61 /*
62 * Set up the input pointers while copying the mail file into /tmp.
63 */
64 void
65 setptr(ibuf, offset)
66 FILE *ibuf;
67 off_t offset;
68 {
69 int c, count;
70 char *cp, *cp2;
71 struct message this;
72 FILE *mestmp;
73 int maybe, inhead;
74 char linebuf[LINESIZE], pathbuf[PATHSIZE];
75 int omsgCount;
76
77 /* Get temporary file. */
78 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir);
79 if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL)
80 err(1, "can't open %s", pathbuf);
81 (void)rm(pathbuf);
82
83 if (offset == 0) {
84 msgCount = 0;
85 } else {
86 /* Seek into the file to get to the new messages */
87 (void)fseeko(ibuf, offset, SEEK_SET);
88 /*
89 * We need to make "offset" a pointer to the end of
90 * the temp file that has the copy of the mail file.
91 * If any messages have been edited, this will be
92 * different from the offset into the mail file.
93 */
94 (void)fseeko(otf, (off_t)0, SEEK_END);
95 offset = ftello(otf);
96 }
97 omsgCount = msgCount;
98 maybe = 1;
99 inhead = 0;
100 this.m_flag = MUSED|MNEW;
101 this.m_size = 0;
102 this.m_lines = 0;
103 this.m_block = 0;
104 this.m_offset = 0;
105 for (;;) {
106 if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) {
107 if (append(&this, mestmp))
108 errx(1, "temporary file");
109 makemessage(mestmp, omsgCount);
110 return;
111 }
112 count = strlen(linebuf);
113 /*
114 * Transforms lines ending in <CR><LF> to just <LF>.
115 * This allows mail to be able to read Eudora mailboxes.
116 */
117 if (count >= 2 && linebuf[count - 1] == '\n' &&
118 linebuf[count - 2] == '\r') {
119 count--;
120 linebuf[count - 1] = '\n';
121 }
122
123 (void)fwrite(linebuf, sizeof(*linebuf), count, otf);
124 if (ferror(otf))
125 errx(1, "/tmp");
126 if (count)
127 linebuf[count - 1] = '\0';
128 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
129 msgCount++;
130 if (append(&this, mestmp))
131 errx(1, "temporary file");
132 this.m_flag = MUSED|MNEW;
133 this.m_size = 0;
134 this.m_lines = 0;
135 this.m_block = blockof(offset);
136 this.m_offset = boffsetof(offset);
137 inhead = 1;
138 } else if (linebuf[0] == 0) {
139 inhead = 0;
140 } else if (inhead) {
141 for (cp = linebuf, cp2 = "status";; cp++) {
142 if ((c = *cp2++) == '\0') {
143 while (isspace((unsigned char)*cp++))
144 ;
145 if (cp[-1] != ':')
146 break;
147 while ((c = *cp++) != '\0')
148 if (c == 'R')
149 this.m_flag |= MREAD;
150 else if (c == 'O')
151 this.m_flag &= ~MNEW;
152 inhead = 0;
153 break;
154 }
155 if (*cp != c && *cp != toupper((unsigned char)c))
156 break;
157 }
158 }
159 offset += count;
160 this.m_size += count;
161 this.m_lines++;
162 maybe = linebuf[0] == 0;
163 }
164 }
165
166 /*
167 * Drop the passed line onto the passed output buffer.
168 * If a write error occurs, return -1, else the count of
169 * characters written, including the newline if requested.
170 */
171 int
172 putline(obuf, linebuf, outlf)
173 FILE *obuf;
174 char *linebuf;
175 int outlf;
176 {
177 int c;
178
179 c = strlen(linebuf);
180 (void)fwrite(linebuf, sizeof(*linebuf), c, obuf);
181 if (outlf) {
182 fprintf(obuf, "\n");
183 c++;
184 }
185 if (ferror(obuf))
186 return (-1);
187 return (c);
188 }
189
190 /*
191 * Read up a line from the specified input into the line
192 * buffer. Return the number of characters read. Do not
193 * include the newline (or carriage return) at the end.
194 */
195 int
196 readline(ibuf, linebuf, linesize)
197 FILE *ibuf;
198 char *linebuf;
199 int linesize;
200 {
201 int n;
202
203 clearerr(ibuf);
204 if (fgets(linebuf, linesize, ibuf) == NULL)
205 return (-1);
206 n = strlen(linebuf);
207 if (n > 0 && linebuf[n - 1] == '\n')
208 linebuf[--n] = '\0';
209 if (n > 0 && linebuf[n - 1] == '\r')
210 linebuf[--n] = '\0';
211 return (n);
212 }
213
214 /*
215 * Return a file buffer all ready to read up the
216 * passed message pointer.
217 */
218 FILE *
219 setinput(mp)
220 struct message *mp;
221 {
222
223 (void)fflush(otf);
224 if (fseeko(itf,
225 positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0)
226 err(1, "fseeko");
227 return (itf);
228 }
229
230 /*
231 * Take the data out of the passed ghost file and toss it into
232 * a dynamically allocated message structure.
233 */
234 void
235 makemessage(f, omsgCount)
236 FILE *f;
237 int omsgCount;
238 {
239 size_t size;
240 struct message *nmessage;
241
242 size = (msgCount + 1) * sizeof(struct message);
243 nmessage = (struct message *)realloc(message, size);
244 if (nmessage == NULL)
245 errx(1, "Insufficient memory for %d messages\n",
246 msgCount);
247 if (omsgCount == 0 || message == NULL)
248 dot = nmessage;
249 else
250 dot = nmessage + (dot - message);
251 message = nmessage;
252 size -= (omsgCount + 1) * sizeof(struct message);
253 (void)fflush(f);
254 (void)lseek(fileno(f), (off_t)sizeof(*message), 0);
255 if (read(fileno(f), (char *)&message[omsgCount], size) != size)
256 errx(1, "Message temporary file corrupted");
257 message[msgCount].m_size = 0;
258 message[msgCount].m_lines = 0;
259 (void)Fclose(f);
260 }
261
262 /*
263 * Append the passed message descriptor onto the temp file.
264 * If the write fails, return 1, else 0
265 */
266 int
267 append(mp, f)
268 struct message *mp;
269 FILE *f;
270 {
271 return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1);
272 }
273
274 /*
275 * Delete a file, but only if the file is a plain file.
276 */
277 int
278 rm(name)
279 char *name;
280 {
281 struct stat sb;
282
283 if (stat(name, &sb) < 0)
284 return (-1);
285 if (!S_ISREG(sb.st_mode)) {
286 errno = EISDIR;
287 return (-1);
288 }
289 return (unlink(name));
290 }
291
292 static int sigdepth; /* depth of holdsigs() */
293 static sigset_t nset, oset;
294 /*
295 * Hold signals SIGHUP, SIGINT, and SIGQUIT.
296 */
297 void
298 holdsigs()
299 {
300
301 if (sigdepth++ == 0) {
302 (void)sigemptyset(&nset);
303 (void)sigaddset(&nset, SIGHUP);
304 (void)sigaddset(&nset, SIGINT);
305 (void)sigaddset(&nset, SIGQUIT);
306 (void)sigprocmask(SIG_BLOCK, &nset, &oset);
307 }
308 }
309
310 /*
311 * Release signals SIGHUP, SIGINT, and SIGQUIT.
312 */
313 void
314 relsesigs()
315 {
316
317 if (--sigdepth == 0)
318 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
319 }
320
321 /*
322 * Determine the size of the file possessed by
323 * the passed buffer.
324 */
325 off_t
326 fsize(iob)
327 FILE *iob;
328 {
329 struct stat sbuf;
330
331 if (fstat(fileno(iob), &sbuf) < 0)
332 return (0);
333 return (sbuf.st_size);
334 }
335
336 /*
337 * Evaluate the string given as a new mailbox name.
338 * Supported meta characters:
339 * % for my system mail box
340 * %user for user's system mail box
341 * # for previous file
342 * & invoker's mbox file
343 * +file file in folder directory
344 * any shell meta character
345 * Return the file name as a dynamic string.
346 */
347 char *
348 expand(name)
349 char *name;
350 {
351 char xname[PATHSIZE];
352 char cmdbuf[PATHSIZE]; /* also used for file names */
353 int pid, l;
354 char *cp, *sh;
355 int pivec[2];
356 struct stat sbuf;
357
358 /*
359 * The order of evaluation is "%" and "#" expand into constants.
360 * "&" can expand into "+". "+" can expand into shell meta characters.
361 * Shell meta characters expand into constants.
362 * This way, we make no recursive expansion.
363 */
364 switch (*name) {
365 case '%':
366 findmail(name[1] ? name + 1 : myname, xname, sizeof(xname));
367 return (savestr(xname));
368 case '#':
369 if (name[1] != 0)
370 break;
371 if (prevfile[0] == 0) {
372 printf("No previous file\n");
373 return (NULL);
374 }
375 return (savestr(prevfile));
376 case '&':
377 if (name[1] == 0 && (name = value("MBOX")) == NULL)
378 name = "~/mbox";
379 /* fall through */
380 }
381 if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) {
382 (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1);
383 name = savestr(xname);
384 }
385 /* catch the most common shell meta character */
386 if (name[0] == '~' && homedir != NULL &&
387 (name[1] == '/' || name[1] == '\0')) {
388 (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1);
389 name = savestr(xname);
390 }
391 if (!strpbrk(name, "~{[*?$`'\"\\"))
392 return (name);
393 if (pipe(pivec) < 0) {
394 warn("pipe");
395 return (name);
396 }
397 (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
398 if ((sh = value("SHELL")) == NULL)
399 sh = _PATH_BSHELL;
400 pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL);
401 if (pid < 0) {
402 (void)close(pivec[0]);
403 (void)close(pivec[1]);
404 return (NULL);
405 }
406 (void)close(pivec[1]);
407 l = read(pivec[0], xname, BUFSIZ);
408 (void)close(pivec[0]);
409 if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) &&
410 WTERMSIG(wait_status) != SIGPIPE) {
411 fprintf(stderr, "\"%s\": Expansion failed.\n", name);
412 return (NULL);
413 }
414 if (l < 0) {
415 warn("read");
416 return (NULL);
417 }
418 if (l == 0) {
419 fprintf(stderr, "\"%s\": No match.\n", name);
420 return (NULL);
421 }
422 if (l == BUFSIZ) {
423 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
424 return (NULL);
425 }
426 xname[l] = '\0';
427 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
428 ;
429 cp[1] = '\0';
430 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
431 fprintf(stderr, "\"%s\": Ambiguous.\n", name);
432 return (NULL);
433 }
434 return (savestr(xname));
435 }
436
437 /*
438 * Determine the current folder directory name.
439 */
440 int
441 getfold(name, namelen)
442 char *name;
443 int namelen;
444 {
445 char *folder;
446 int copylen;
447
448 if ((folder = value("folder")) == NULL)
449 return (-1);
450 if (*folder == '/')
451 copylen = strlcpy(name, folder, namelen);
452 else
453 copylen = snprintf(name, namelen, "%s/%s",
454 homedir ? homedir : ".", folder);
455 return (copylen < 0 || copylen >= namelen ? (-1) : (0));
456 }
457
458 /*
459 * Return the name of the dead.letter file.
460 */
461 char *
462 getdeadletter()
463 {
464 char *cp;
465
466 if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL)
467 cp = expand("~/dead.letter");
468 else if (*cp != '/') {
469 char buf[PATHSIZE];
470
471 (void)snprintf(buf, sizeof(buf), "~/%s", cp);
472 cp = expand(buf);
473 }
474 return (cp);
475 }