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