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