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