1 /* $NetBSD: fio.c,v 1.8 1997/07/07 22:57:55 phil 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.2 (Berkeley) 4/20/95"; 39 #else 40 static char rcsid[] = "$NetBSD: fio.c,v 1.8 1997/07/07 22:57:55 phil 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, offset) 64 register FILE *ibuf; 65 off_t offset; 66 { 67 extern char *tmpdir; 68 register int c, count; 69 register char *cp, *cp2; 70 struct message this; 71 FILE *mestmp; 72 int maybe, inhead; 73 char linebuf[LINESIZE]; 74 int omsgCount; 75 76 /* Get temporary file. */ 77 (void)snprintf(linebuf, LINESIZE, "%s/mail.XXXXXX", tmpdir); 78 if ((c = mkstemp(linebuf)) == -1 || 79 (mestmp = Fdopen(c, "r+")) == NULL) { 80 (void)fprintf(stderr, "mail: can't open %s\n", linebuf); 81 exit(1); 82 } 83 (void)unlink(linebuf); 84 85 if (offset == 0) { 86 msgCount = 0; 87 } else { 88 /* Seek into the file to get to the new messages */ 89 (void) fseek(ibuf, offset, 0); 90 /* 91 * We need to make "offset" a pointer to the end of 92 * the temp file that has the copy of the mail file. 93 * If any messages have been edited, this will be 94 * different from the offset into the mail file. 95 */ 96 (void) fseek(otf, 0L, 2); 97 offset = ftell(otf); 98 } 99 omsgCount = msgCount; 100 maybe = 1; 101 inhead = 0; 102 this.m_flag = MUSED|MNEW; 103 this.m_size = 0; 104 this.m_lines = 0; 105 this.m_block = 0; 106 this.m_offset = 0; 107 for (;;) { 108 if (fgets(linebuf, LINESIZE, ibuf) == NULL) { 109 if (append(&this, mestmp)) { 110 perror("temporary file"); 111 exit(1); 112 } 113 makemessage(mestmp, omsgCount); 114 return; 115 } 116 count = strlen(linebuf); 117 (void) fwrite(linebuf, sizeof *linebuf, count, otf); 118 if (ferror(otf)) { 119 perror("/tmp"); 120 exit(1); 121 } 122 linebuf[count - 1] = 0; 123 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 124 msgCount++; 125 if (append(&this, mestmp)) { 126 perror("temporary file"); 127 exit(1); 128 } 129 this.m_flag = MUSED|MNEW; 130 this.m_size = 0; 131 this.m_lines = 0; 132 this.m_block = blockof(offset); 133 this.m_offset = offsetof(offset); 134 inhead = 1; 135 } else if (linebuf[0] == 0) { 136 inhead = 0; 137 } else if (inhead) { 138 for (cp = linebuf, cp2 = "status";; cp++) { 139 if ((c = *cp2++) == 0) { 140 while (isspace(*cp++)) 141 ; 142 if (cp[-1] != ':') 143 break; 144 while ((c = *cp++) != '\0') 145 if (c == 'R') 146 this.m_flag |= MREAD; 147 else if (c == 'O') 148 this.m_flag &= ~MNEW; 149 inhead = 0; 150 break; 151 } 152 if (*cp != c && *cp != toupper(c)) 153 break; 154 } 155 } 156 offset += count; 157 this.m_size += count; 158 this.m_lines++; 159 maybe = linebuf[0] == 0; 160 } 161 } 162 163 /* 164 * Drop the passed line onto the passed output buffer. 165 * If a write error occurs, return -1, else the count of 166 * characters written, including the newline if requested. 167 */ 168 int 169 putline(obuf, linebuf, outlf) 170 FILE *obuf; 171 char *linebuf; 172 int outlf; 173 { 174 register int c; 175 176 c = strlen(linebuf); 177 (void) fwrite(linebuf, sizeof *linebuf, c, obuf); 178 if (outlf) { 179 (void) putc('\n', obuf); 180 c++; 181 } 182 if (ferror(obuf)) 183 return (-1); 184 return (c); 185 } 186 187 /* 188 * Read up a line from the specified input into the line 189 * buffer. Return the number of characters read. Do not 190 * include the newline at the end. 191 */ 192 int 193 readline(ibuf, linebuf, linesize) 194 FILE *ibuf; 195 char *linebuf; 196 int linesize; 197 { 198 register int n; 199 200 clearerr(ibuf); 201 if (fgets(linebuf, linesize, ibuf) == NULL) 202 return -1; 203 n = strlen(linebuf); 204 if (n > 0 && linebuf[n - 1] == '\n') 205 linebuf[--n] = '\0'; 206 return n; 207 } 208 209 /* 210 * Return a file buffer all ready to read up the 211 * passed message pointer. 212 */ 213 FILE * 214 setinput(mp) 215 register struct message *mp; 216 { 217 218 fflush(otf); 219 if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) { 220 perror("fseek"); 221 panic("temporary file seek"); 222 } 223 return (itf); 224 } 225 226 /* 227 * Take the data out of the passed ghost file and toss it into 228 * a dynamically allocated message structure. 229 */ 230 void 231 makemessage(f, omsgCount) 232 FILE *f; 233 int omsgCount; 234 { 235 register size = (msgCount + 1) * sizeof (struct message); 236 237 if (omsgCount) { 238 message = (struct message *)realloc(message, (unsigned) size); 239 if (message == 0) 240 panic("Insufficient memory for %d messages\n", 241 msgCount); 242 } else { 243 if (message != 0) 244 free((char *) message); 245 if ((message = (struct message *) malloc((unsigned) size)) == 0) 246 panic("Insufficient memory for %d messages", msgCount); 247 dot = message; 248 } 249 size -= (omsgCount + 1) * sizeof (struct message); 250 fflush(f); 251 (void) lseek(fileno(f), (off_t)sizeof *message, 0); 252 if (read(fileno(f), (char *) &message[omsgCount], size) != size) 253 panic("Message temporary file corrupted"); 254 message[msgCount].m_size = 0; 255 message[msgCount].m_lines = 0; 256 Fclose(f); 257 } 258 259 /* 260 * Append the passed message descriptor onto the temp file. 261 * If the write fails, return 1, else 0 262 */ 263 int 264 append(mp, f) 265 struct message *mp; 266 FILE *f; 267 { 268 return fwrite((char *) mp, sizeof *mp, 1, f) != 1; 269 } 270 271 /* 272 * Delete a file, but only if the file is a plain file. 273 */ 274 int 275 rm(name) 276 char *name; 277 { 278 struct stat sb; 279 280 if (stat(name, &sb) < 0) 281 return(-1); 282 if (!S_ISREG(sb.st_mode)) { 283 errno = EISDIR; 284 return(-1); 285 } 286 return(unlink(name)); 287 } 288 289 static int sigdepth; /* depth of holdsigs() */ 290 static sigset_t nset, oset; 291 /* 292 * Hold signals SIGHUP, SIGINT, and SIGQUIT. 293 */ 294 void 295 holdsigs() 296 { 297 298 if (sigdepth++ == 0) { 299 sigemptyset(&nset); 300 sigaddset(&nset, SIGHUP); 301 sigaddset(&nset, SIGINT); 302 sigaddset(&nset, SIGQUIT); 303 sigprocmask(SIG_BLOCK, &nset, &oset); 304 } 305 } 306 307 /* 308 * Release signals SIGHUP, SIGINT, and SIGQUIT. 309 */ 310 void 311 relsesigs() 312 { 313 314 if (--sigdepth == 0) 315 sigprocmask(SIG_SETMASK, &oset, NULL); 316 } 317 318 /* 319 * Determine the size of the file possessed by 320 * the passed buffer. 321 */ 322 off_t 323 fsize(iob) 324 FILE *iob; 325 { 326 struct stat sbuf; 327 328 if (fstat(fileno(iob), &sbuf) < 0) 329 return 0; 330 return sbuf.st_size; 331 } 332 333 /* 334 * Evaluate the string given as a new mailbox name. 335 * Supported meta characters: 336 * % for my system mail box 337 * %user for user's system mail box 338 * # for previous file 339 * & invoker's mbox file 340 * +file file in folder directory 341 * any shell meta character 342 * Return the file name as a dynamic string. 343 */ 344 char * 345 expand(name) 346 register char *name; 347 { 348 char xname[PATHSIZE]; 349 char cmdbuf[PATHSIZE]; /* also used for file names */ 350 register int pid, l; 351 register char *cp, *shell; 352 int pivec[2]; 353 struct stat sbuf; 354 extern union wait wait_status; 355 356 /* 357 * The order of evaluation is "%" and "#" expand into constants. 358 * "&" can expand into "+". "+" can expand into shell meta characters. 359 * Shell meta characters expand into constants. 360 * This way, we make no recursive expansion. 361 */ 362 switch (*name) { 363 case '%': 364 findmail(name[1] ? name + 1 : myname, xname); 365 return savestr(xname); 366 case '#': 367 if (name[1] != 0) 368 break; 369 if (prevfile[0] == 0) { 370 printf("No previous file\n"); 371 return NOSTR; 372 } 373 return savestr(prevfile); 374 case '&': 375 if (name[1] == 0 && (name = value("MBOX")) == NOSTR) 376 name = "~/mbox"; 377 /* fall through */ 378 } 379 if (name[0] == '+' && getfold(cmdbuf) >= 0) { 380 snprintf(xname, PATHSIZE, "%s/%s", cmdbuf, name + 1); 381 name = savestr(xname); 382 } 383 /* catch the most common shell meta character */ 384 if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { 385 snprintf(xname, PATHSIZE, "%s%s", homedir, name + 1); 386 name = savestr(xname); 387 } 388 if (!anyof(name, "~{[*?$`'\"\\")) 389 return name; 390 if (pipe(pivec) < 0) { 391 perror("pipe"); 392 return name; 393 } 394 snprintf(cmdbuf, PATHSIZE, "echo %s", name); 395 if ((shell = value("SHELL")) == NOSTR) 396 shell = _PATH_CSHELL; 397 pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR); 398 if (pid < 0) { 399 close(pivec[0]); 400 close(pivec[1]); 401 return NOSTR; 402 } 403 close(pivec[1]); 404 l = read(pivec[0], xname, PATHSIZE); 405 close(pivec[0]); 406 if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) { 407 fprintf(stderr, "\"%s\": Expansion failed.\n", name); 408 return NOSTR; 409 } 410 if (l < 0) { 411 perror("read"); 412 return NOSTR; 413 } 414 if (l == 0) { 415 fprintf(stderr, "\"%s\": No match.\n", name); 416 return NOSTR; 417 } 418 if (l == PATHSIZE) { 419 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 420 return NOSTR; 421 } 422 xname[l] = '\0'; 423 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 424 ; 425 cp[1] = '\0'; 426 if (index(xname, ' ') && stat(xname, &sbuf) < 0) { 427 fprintf(stderr, "\"%s\": Ambiguous.\n", name); 428 return NOSTR; 429 } 430 return savestr(xname); 431 } 432 433 /* 434 * Determine the current folder directory name. 435 */ 436 int 437 getfold(name) 438 char *name; 439 { 440 char *folder; 441 442 if ((folder = value("folder")) == NOSTR) 443 return (-1); 444 if (*folder == '/') { 445 strncpy(name, folder, PATHSIZE - 1); 446 name[PATHSIZE - 1] = '\0' ; 447 } 448 else 449 snprintf(name, PATHSIZE, "%s/%s", homedir, folder); 450 return (0); 451 } 452 453 /* 454 * Return the name of the dead.letter file. 455 */ 456 char * 457 getdeadletter() 458 { 459 register char *cp; 460 461 if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR) 462 cp = expand("~/dead.letter"); 463 else if (*cp != '/') { 464 char buf[PATHSIZE]; 465 466 (void) snprintf(buf, PATHSIZE, "~/%s", cp); 467 cp = expand(buf); 468 } 469 return cp; 470 } 471