botched copyright
[unix-history] / usr / src / usr.bin / mail / fio.c
CommitLineData
9552e6b8
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
2ae9f53f 7#ifndef lint
2fd8b883 8static char *sccsid = "@(#)fio.c 5.2 (Berkeley) %G%";
9552e6b8 9#endif not lint
77a2ce8a
KS
10
11#include "rcv.h"
12#include <sys/stat.h>
13#include <errno.h>
14
15/*
16 * Mail -- a mail program
17 *
18 * File I/O.
19 */
20
77a2ce8a
KS
21/*
22 * Set up the input pointers while copying the mail file into
23 * /tmp.
24 */
25
26setptr(ibuf)
27 FILE *ibuf;
28{
9008b544 29 register int c;
9b888765 30 register char *cp, *cp2;
0154300b
CS
31 register int count, l;
32 long s;
77a2ce8a
KS
33 off_t offset;
34 char linebuf[LINESIZE];
9b888765
KS
35 char wbuf[LINESIZE];
36 int maybe, mestmp, flag, inhead;
77a2ce8a
KS
37 struct message this;
38 extern char tempSet[];
39
40 if ((mestmp = opentemp(tempSet)) < 0)
41 exit(1);
42 msgCount = 0;
43 offset = 0;
0154300b 44 s = 0L;
77a2ce8a
KS
45 l = 0;
46 maybe = 1;
9b888765 47 flag = MUSED|MNEW;
77a2ce8a 48 for (;;) {
9008b544
KS
49 cp = linebuf;
50 c = getc(ibuf);
51 while (c != EOF && c != '\n') {
53b2e4eb 52 if (cp - linebuf >= LINESIZE - 1) {
9008b544
KS
53 ungetc(c, ibuf);
54 *cp = 0;
55 break;
56 }
57 *cp++ = c;
58 c = getc(ibuf);
59 }
60 *cp = 0;
61 if (cp == linebuf && c == EOF) {
77a2ce8a 62 this.m_flag = flag;
9b888765 63 flag = MUSED|MNEW;
77a2ce8a
KS
64 this.m_offset = offsetof(offset);
65 this.m_block = blockof(offset);
66 this.m_size = s;
67 this.m_lines = l;
68 if (append(&this, mestmp)) {
69 perror(tempSet);
70 exit(1);
71 }
72 fclose(ibuf);
73 makemessage(mestmp);
74 close(mestmp);
75 return;
76 }
9008b544
KS
77 count = cp - linebuf + 1;
78 for (cp = linebuf; *cp;)
79 putc(*cp++, otf);
80 putc('\n', otf);
81 if (ferror(otf)) {
77a2ce8a
KS
82 perror("/tmp");
83 exit(1);
84 }
9b888765 85 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
77a2ce8a
KS
86 msgCount++;
87 this.m_flag = flag;
9b888765 88 flag = MUSED|MNEW;
29c01f3c 89 inhead = 1;
77a2ce8a
KS
90 this.m_block = blockof(offset);
91 this.m_offset = offsetof(offset);
92 this.m_size = s;
93 this.m_lines = l;
0154300b 94 s = 0L;
77a2ce8a
KS
95 l = 0;
96 if (append(&this, mestmp)) {
97 perror(tempSet);
98 exit(1);
99 }
100 }
9b888765
KS
101 if (linebuf[0] == 0)
102 inhead = 0;
103 if (inhead && index(linebuf, ':')) {
104 cp = linebuf;
105 cp2 = wbuf;
106 while (isalpha(*cp))
107 *cp2++ = *cp++;
108 *cp2 = 0;
109 if (icequal(wbuf, "status")) {
110 cp = index(linebuf, ':');
111 if (index(cp, 'R'))
112 flag |= MREAD;
113 if (index(cp, 'O'))
114 flag &= ~MNEW;
115 inhead = 0;
116 }
117 }
77a2ce8a 118 offset += count;
0154300b 119 s += (long) count;
77a2ce8a
KS
120 l++;
121 maybe = 0;
122 if (linebuf[0] == 0)
123 maybe = 1;
124 }
125}
126
127/*
128 * Drop the passed line onto the passed output buffer.
129 * If a write error occurs, return -1, else the count of
130 * characters written, including the newline.
131 */
132
133putline(obuf, linebuf)
134 FILE *obuf;
135 char *linebuf;
136{
137 register int c;
138
139 c = strlen(linebuf);
140 fputs(linebuf, obuf);
141 putc('\n', obuf);
142 if (ferror(obuf))
143 return(-1);
144 return(c+1);
145}
146
de9a3738
KS
147/*
148 * Quickly read a line from the specified input into the line
149 * buffer; return characters read.
150 */
151
152freadline(ibuf, linebuf)
153 register FILE *ibuf;
154 register char *linebuf;
155{
156 register int c;
157 register char *cp;
158
159 c = getc(ibuf);
160 cp = linebuf;
161 while (c != '\n' && c != EOF) {
162 if (c == 0) {
163 c = getc(ibuf);
164 continue;
165 }
166 if (cp - linebuf >= BUFSIZ-1) {
167 *cp = 0;
168 return(cp - linebuf + 1);
169 }
170 *cp++ = c;
171 c = getc(ibuf);
172 }
173 if (c == EOF && cp == linebuf)
174 return(0);
175 *cp = 0;
176 return(cp - linebuf + 1);
177}
178
77a2ce8a
KS
179/*
180 * Read up a line from the specified input into the line
181 * buffer. Return the number of characters read. Do not
182 * include the newline at the end.
183 */
184
185readline(ibuf, linebuf)
186 FILE *ibuf;
187 char *linebuf;
188{
189 register char *cp;
190 register int c;
191
192 do {
193 clearerr(ibuf);
194 c = getc(ibuf);
195 for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) {
196 if (c == 0)
197 continue;
198 if (cp - linebuf < LINESIZE-2)
199 *cp++ = c;
200 }
201 } while (ferror(ibuf) && ibuf == stdin);
202 *cp = 0;
203 if (c == EOF && cp == linebuf)
204 return(0);
205 return(cp - linebuf + 1);
206}
207
208/*
209 * Return a file buffer all ready to read up the
210 * passed message pointer.
211 */
212
213FILE *
214setinput(mp)
215 register struct message *mp;
216{
217 off_t off;
218
219 fflush(otf);
220 off = mp->m_block;
221 off <<= 9;
222 off += mp->m_offset;
223 if (fseek(itf, off, 0) < 0) {
224 perror("fseek");
225 panic("temporary file seek");
226 }
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
235makemessage(f)
236{
237 register struct message *m;
238 register char *mp;
239 register count;
240
241 mp = calloc((unsigned) (msgCount + 1), sizeof *m);
242 if (mp == NOSTR) {
243 printf("Insufficient memory for %d messages\n", msgCount);
244 exit(1);
245 }
838681f8
KS
246 if (message != (struct message *) 0)
247 cfree((char *) message);
77a2ce8a
KS
248 message = (struct message *) mp;
249 dot = message;
250 lseek(f, 0L, 0);
251 while (count = read(f, mp, BUFSIZ))
252 mp += count;
253 for (m = &message[0]; m < &message[msgCount]; m++) {
254 m->m_size = (m+1)->m_size;
255 m->m_lines = (m+1)->m_lines;
9b888765 256 m->m_flag = (m+1)->m_flag;
77a2ce8a 257 }
0154300b 258 message[msgCount].m_size = 0L;
77a2ce8a
KS
259 message[msgCount].m_lines = 0;
260}
261
262/*
263 * Append the passed message descriptor onto the temp file.
264 * If the write fails, return 1, else 0
265 */
266
267append(mp, f)
268 struct message *mp;
269{
270 if (write(f, (char *) mp, sizeof *mp) != sizeof *mp)
271 return(1);
272 return(0);
273}
274
275/*
276 * Delete a file, but only if the file is a plain file.
277 */
278
279remove(name)
280 char name[];
281{
282 struct stat statb;
283 extern int errno;
284
285 if (stat(name, &statb) < 0)
286 return(-1);
287 if ((statb.st_mode & S_IFMT) != S_IFREG) {
288 errno = EISDIR;
289 return(-1);
290 }
291 return(unlink(name));
292}
293
294/*
295 * Terminate an editing session by attempting to write out the user's
5ab0568d 296 * file from the temporary. Save any new stuff appended to the file.
77a2ce8a 297 */
77a2ce8a
KS
298edstop()
299{
300 register int gotcha, c;
301 register struct message *mp;
8b5a06af 302 FILE *obuf, *ibuf, *readstat;
5ab0568d 303 struct stat statb;
8b5a06af 304 char tempname[30], *id;
b3d49ad3 305 int (*sigs[3])();
77a2ce8a 306
838681f8
KS
307 if (readonly)
308 return;
985c722d 309 holdsigs();
8b5a06af
KS
310 if (Tflag != NOSTR) {
311 if ((readstat = fopen(Tflag, "w")) == NULL)
312 Tflag = NOSTR;
313 }
953c713a
KS
314 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
315 if (mp->m_flag & MNEW) {
316 mp->m_flag &= ~MNEW;
317 mp->m_flag |= MSTATUS;
318 }
8b5a06af 319 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
77a2ce8a 320 gotcha++;
8b5a06af
KS
321 if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
322 if ((id = hfield("article-id", mp)) != NOSTR)
323 fprintf(readstat, "%s\n", id);
77a2ce8a 324 }
953c713a 325 }
8b5a06af
KS
326 if (Tflag != NOSTR)
327 fclose(readstat);
d85bd93d 328 if (!gotcha || Tflag != NOSTR)
b3d49ad3 329 goto done;
5ab0568d
KS
330 ibuf = NULL;
331 if (stat(editfile, &statb) >= 0 && statb.st_size > mailsize) {
332 strcpy(tempname, "/tmp/mboxXXXXXX");
333 mktemp(tempname);
334 if ((obuf = fopen(tempname, "w")) == NULL) {
335 perror(tempname);
985c722d 336 relsesigs();
5ab0568d
KS
337 reset(0);
338 }
339 if ((ibuf = fopen(editfile, "r")) == NULL) {
340 perror(editfile);
341 fclose(obuf);
342 remove(tempname);
985c722d 343 relsesigs();
5ab0568d
KS
344 reset(0);
345 }
0c3a2f40 346 fseek(ibuf, mailsize, 0);
5ab0568d
KS
347 while ((c = getc(ibuf)) != EOF)
348 putc(c, obuf);
349 fclose(ibuf);
350 fclose(obuf);
351 if ((ibuf = fopen(tempname, "r")) == NULL) {
352 perror(tempname);
353 remove(tempname);
985c722d 354 relsesigs();
5ab0568d
KS
355 reset(0);
356 }
357 remove(tempname);
358 }
77a2ce8a 359 printf("\"%s\" ", editfile);
80187484 360 fflush(stdout);
b17445e6 361 if ((obuf = fopen(editfile, "r+")) == NULL) {
77a2ce8a 362 perror(editfile);
985c722d 363 relsesigs();
77a2ce8a
KS
364 reset(0);
365 }
b17445e6 366 trunc(obuf);
77a2ce8a
KS
367 c = 0;
368 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
369 if ((mp->m_flag & MDELETED) != 0)
370 continue;
371 c++;
c718ab56 372 if (send(mp, obuf, 0) < 0) {
77a2ce8a 373 perror(editfile);
985c722d 374 relsesigs();
77a2ce8a
KS
375 reset(0);
376 }
377 }
5ab0568d
KS
378 gotcha = (c == 0 && ibuf == NULL);
379 if (ibuf != NULL) {
380 while ((c = getc(ibuf)) != EOF)
381 putc(c, obuf);
382 fclose(ibuf);
383 }
77a2ce8a
KS
384 fflush(obuf);
385 if (ferror(obuf)) {
386 perror(editfile);
985c722d 387 relsesigs();
77a2ce8a
KS
388 reset(0);
389 }
5ab0568d
KS
390 fclose(obuf);
391 if (gotcha) {
77a2ce8a
KS
392 remove(editfile);
393 printf("removed\n");
394 }
395 else
396 printf("complete\n");
80187484 397 fflush(stdout);
b3d49ad3
KS
398
399done:
985c722d 400 relsesigs();
b3d49ad3
KS
401}
402
f5df67b9 403static int sigdepth = 0; /* depth of holdsigs() */
56d6aa4a 404static int omask = 0;
b3d49ad3 405/*
985c722d 406 * Hold signals SIGHUP - SIGQUIT.
b3d49ad3 407 */
985c722d 408holdsigs()
b3d49ad3
KS
409{
410 register int i;
411
f5df67b9 412 if (sigdepth++ == 0)
56d6aa4a 413 omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
b3d49ad3
KS
414}
415
416/*
985c722d 417 * Release signals SIGHUP - SIGQUIT
b3d49ad3 418 */
985c722d 419relsesigs()
b3d49ad3
KS
420{
421 register int i;
985c722d 422
f5df67b9 423 if (--sigdepth == 0)
56d6aa4a 424 sigsetmask(omask);
77a2ce8a
KS
425}
426
77a2ce8a
KS
427/*
428 * Open a temp file by creating, closing, unlinking, and
429 * reopening. Return the open file descriptor.
430 */
431
432opentemp(file)
433 char file[];
434{
435 register int f;
436
437 if ((f = creat(file, 0600)) < 0) {
438 perror(file);
439 return(-1);
440 }
441 close(f);
442 if ((f = open(file, 2)) < 0) {
443 perror(file);
444 remove(file);
445 return(-1);
446 }
447 remove(file);
448 return(f);
449}
450
77a2ce8a
KS
451/*
452 * Determine the size of the file possessed by
453 * the passed buffer.
454 */
455
456off_t
457fsize(iob)
458 FILE *iob;
459{
460 register int f;
461 struct stat sbuf;
462
463 f = fileno(iob);
464 if (fstat(f, &sbuf) < 0)
465 return(0);
466 return(sbuf.st_size);
467}
468
469/*
470 * Take a file name, possibly with shell meta characters
471 * in it and expand it by using "sh -c echo filename"
472 * Return the file name as a dynamic string.
473 */
474
475char *
476expand(name)
477 char name[];
478{
479 char xname[BUFSIZ];
480 char cmdbuf[BUFSIZ];
481 register int pid, l, rc;
482 register char *cp, *Shell;
483 int s, pivec[2], (*sigint)();
484 struct stat sbuf;
485
df3c4fbd
KS
486 if (name[0] == '+' && getfold(cmdbuf) >= 0) {
487 sprintf(xname, "%s/%s", cmdbuf, name + 1);
38a42545
KS
488 return(expand(savestr(xname)));
489 }
77a2ce8a
KS
490 if (!anyof(name, "~{[*?$`'\"\\"))
491 return(name);
77a2ce8a
KS
492 if (pipe(pivec) < 0) {
493 perror("pipe");
77a2ce8a
KS
494 return(name);
495 }
496 sprintf(cmdbuf, "echo %s", name);
497 if ((pid = vfork()) == 0) {
9ff57b47 498 sigchild();
77a2ce8a
KS
499 Shell = value("SHELL");
500 if (Shell == NOSTR)
501 Shell = SHELL;
502 close(pivec[0]);
503 close(1);
504 dup(pivec[1]);
505 close(pivec[1]);
506 close(2);
507 execl(Shell, Shell, "-c", cmdbuf, 0);
508 _exit(1);
509 }
510 if (pid == -1) {
511 perror("fork");
512 close(pivec[0]);
513 close(pivec[1]);
514 return(NOSTR);
515 }
516 close(pivec[1]);
517 l = read(pivec[0], xname, BUFSIZ);
518 close(pivec[0]);
519 while (wait(&s) != pid);
520 ;
521 s &= 0377;
522 if (s != 0 && s != SIGPIPE) {
523 fprintf(stderr, "\"Echo\" failed\n");
524 goto err;
525 }
526 if (l < 0) {
527 perror("read");
528 goto err;
529 }
530 if (l == 0) {
531 fprintf(stderr, "\"%s\": No match\n", name);
532 goto err;
533 }
534 if (l == BUFSIZ) {
535 fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
536 goto err;
537 }
538 xname[l] = 0;
539 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
540 ;
541 *++cp = '\0';
542 if (any(' ', xname) && stat(xname, &sbuf) < 0) {
543 fprintf(stderr, "\"%s\": Ambiguous\n", name);
544 goto err;
545 }
77a2ce8a
KS
546 return(savestr(xname));
547
548err:
77a2ce8a
KS
549 return(NOSTR);
550}
551
df3c4fbd
KS
552/*
553 * Determine the current folder directory name.
554 */
555getfold(name)
556 char *name;
557{
558 char *folder;
559
560 if ((folder = value("folder")) == NOSTR)
561 return(-1);
562 if (*folder == '/')
563 strcpy(name, folder);
564 else
565 sprintf(name, "%s/%s", homedir, folder);
566 return(0);
567}
568
77a2ce8a
KS
569/*
570 * A nicer version of Fdopen, which allows us to fclose
571 * without losing the open file.
572 */
573
574FILE *
575Fdopen(fildes, mode)
576 char *mode;
577{
578 register int f;
579 FILE *fdopen();
580
581 f = dup(fildes);
582 if (f < 0) {
583 perror("dup");
584 return(NULL);
585 }
586 return(fdopen(f, mode));
587}