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
setptr(ibuf,offset)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
putline(obuf,linebuf)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
readline(ibuf,linebuf,linesize)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 *
setinput(mp)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
makemessage(f,omsgCount)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
append(mp,f)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
rm(name)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
holdsigs()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
relsesigs()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
fsize(iob)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 *
expand(name)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
getfold(name)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 *
getdeadletter()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