1 # 2 3 #include "rcv.h" 4 #include <sys/stat.h> 5 #include <errno.h> 6 7 /* 8 * Mail -- a mail program 9 * 10 * File I/O. 11 */ 12 13 static char *SccsId = "@(#)fio.c 2.8 03/15/82"; 14 15 /* 16 * Set up the input pointers while copying the mail file into 17 * /tmp. 18 */ 19 20 setptr(ibuf) 21 FILE *ibuf; 22 { 23 register int c; 24 register char *cp, *cp2; 25 register int count, s, l; 26 off_t offset; 27 char linebuf[LINESIZE]; 28 char wbuf[LINESIZE]; 29 int maybe, mestmp, flag, inhead; 30 struct message this; 31 extern char tempSet[]; 32 33 if ((mestmp = opentemp(tempSet)) < 0) 34 exit(1); 35 msgCount = 0; 36 offset = 0; 37 s = 0; 38 l = 0; 39 maybe = 1; 40 flag = MUSED|MNEW; 41 for (;;) { 42 cp = linebuf; 43 c = getc(ibuf); 44 while (c != EOF && c != '\n') { 45 if (cp - linebuf >= LINESIZE - 1) { 46 ungetc(c, ibuf); 47 *cp = 0; 48 break; 49 } 50 *cp++ = c; 51 c = getc(ibuf); 52 } 53 *cp = 0; 54 if (cp == linebuf && c == EOF) { 55 this.m_flag = flag; 56 flag = MUSED|MNEW; 57 this.m_offset = offsetof(offset); 58 this.m_block = blockof(offset); 59 this.m_size = s; 60 this.m_lines = l; 61 if (append(&this, mestmp)) { 62 perror(tempSet); 63 exit(1); 64 } 65 fclose(ibuf); 66 makemessage(mestmp); 67 close(mestmp); 68 return; 69 } 70 count = cp - linebuf + 1; 71 for (cp = linebuf; *cp;) 72 putc(*cp++, otf); 73 putc('\n', otf); 74 if (ferror(otf)) { 75 perror("/tmp"); 76 exit(1); 77 } 78 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 79 msgCount++; 80 this.m_flag = flag; 81 flag = MUSED|MNEW; 82 inhead = 1; 83 this.m_block = blockof(offset); 84 this.m_offset = offsetof(offset); 85 this.m_size = s; 86 this.m_lines = l; 87 s = 0; 88 l = 0; 89 if (append(&this, mestmp)) { 90 perror(tempSet); 91 exit(1); 92 } 93 } 94 if (linebuf[0] == 0) 95 inhead = 0; 96 if (inhead && index(linebuf, ':')) { 97 cp = linebuf; 98 cp2 = wbuf; 99 while (isalpha(*cp)) 100 *cp2++ = *cp++; 101 *cp2 = 0; 102 if (icequal(wbuf, "status")) { 103 cp = index(linebuf, ':'); 104 if (index(cp, 'R')) 105 flag |= MREAD; 106 if (index(cp, 'O')) 107 flag &= ~MNEW; 108 inhead = 0; 109 } 110 } 111 offset += count; 112 s += count; 113 l++; 114 maybe = 0; 115 if (linebuf[0] == 0) 116 maybe = 1; 117 } 118 } 119 120 /* 121 * Drop the passed line onto the passed output buffer. 122 * If a write error occurs, return -1, else the count of 123 * characters written, including the newline. 124 */ 125 126 putline(obuf, linebuf) 127 FILE *obuf; 128 char *linebuf; 129 { 130 register int c; 131 132 c = strlen(linebuf); 133 fputs(linebuf, obuf); 134 putc('\n', obuf); 135 if (ferror(obuf)) 136 return(-1); 137 return(c+1); 138 } 139 140 /* 141 * Quickly read a line from the specified input into the line 142 * buffer; return characters read. 143 */ 144 145 freadline(ibuf, linebuf) 146 register FILE *ibuf; 147 register char *linebuf; 148 { 149 register int c; 150 register char *cp; 151 152 c = getc(ibuf); 153 cp = linebuf; 154 while (c != '\n' && c != EOF) { 155 if (c == 0) { 156 c = getc(ibuf); 157 continue; 158 } 159 if (cp - linebuf >= BUFSIZ-1) { 160 *cp = 0; 161 return(cp - linebuf + 1); 162 } 163 *cp++ = c; 164 c = getc(ibuf); 165 } 166 if (c == EOF && cp == linebuf) 167 return(0); 168 *cp = 0; 169 return(cp - linebuf + 1); 170 } 171 172 /* 173 * Read up a line from the specified input into the line 174 * buffer. Return the number of characters read. Do not 175 * include the newline at the end. 176 */ 177 178 readline(ibuf, linebuf) 179 FILE *ibuf; 180 char *linebuf; 181 { 182 register char *cp; 183 register int c; 184 185 do { 186 clearerr(ibuf); 187 c = getc(ibuf); 188 for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) { 189 if (c == 0) 190 continue; 191 if (cp - linebuf < LINESIZE-2) 192 *cp++ = c; 193 } 194 } while (ferror(ibuf) && ibuf == stdin); 195 *cp = 0; 196 if (c == EOF && cp == linebuf) 197 return(0); 198 return(cp - linebuf + 1); 199 } 200 201 /* 202 * Return a file buffer all ready to read up the 203 * passed message pointer. 204 */ 205 206 FILE * 207 setinput(mp) 208 register struct message *mp; 209 { 210 off_t off; 211 212 fflush(otf); 213 off = mp->m_block; 214 off <<= 9; 215 off += mp->m_offset; 216 if (fseek(itf, off, 0) < 0) { 217 perror("fseek"); 218 panic("temporary file seek"); 219 } 220 return(itf); 221 } 222 223 /* 224 * Take the data out of the passed ghost file and toss it into 225 * a dynamically allocated message structure. 226 */ 227 228 makemessage(f) 229 { 230 register struct message *m; 231 register char *mp; 232 register count; 233 234 mp = calloc((unsigned) (msgCount + 1), sizeof *m); 235 if (mp == NOSTR) { 236 printf("Insufficient memory for %d messages\n", msgCount); 237 exit(1); 238 } 239 if (message != (struct message *) 0) 240 cfree((char *) message); 241 message = (struct message *) mp; 242 dot = message; 243 lseek(f, 0L, 0); 244 while (count = read(f, mp, BUFSIZ)) 245 mp += count; 246 for (m = &message[0]; m < &message[msgCount]; m++) { 247 m->m_size = (m+1)->m_size; 248 m->m_lines = (m+1)->m_lines; 249 m->m_flag = (m+1)->m_flag; 250 } 251 message[msgCount].m_size = 0; 252 message[msgCount].m_lines = 0; 253 } 254 255 /* 256 * Append the passed message descriptor onto the temp file. 257 * If the write fails, return 1, else 0 258 */ 259 260 append(mp, f) 261 struct message *mp; 262 { 263 if (write(f, (char *) mp, sizeof *mp) != sizeof *mp) 264 return(1); 265 return(0); 266 } 267 268 /* 269 * Delete a file, but only if the file is a plain file. 270 */ 271 272 remove(name) 273 char name[]; 274 { 275 struct stat statb; 276 extern int errno; 277 278 if (stat(name, &statb) < 0) 279 return(-1); 280 if ((statb.st_mode & S_IFMT) != S_IFREG) { 281 errno = EISDIR; 282 return(-1); 283 } 284 return(unlink(name)); 285 } 286 287 /* 288 * Terminate an editing session by attempting to write out the user's 289 * file from the temporary. Save any new stuff appended to the file. 290 */ 291 edstop() 292 { 293 register int gotcha, c; 294 register struct message *mp; 295 FILE *obuf, *ibuf, *readstat; 296 struct stat statb; 297 char tempname[30], *id; 298 int (*sigs[3])(); 299 300 if (readonly) 301 return; 302 holdsigs(); 303 if (Tflag != NOSTR) { 304 if ((readstat = fopen(Tflag, "w")) == NULL) 305 Tflag = NOSTR; 306 } 307 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { 308 if (mp->m_flag & MNEW) { 309 mp->m_flag &= ~MNEW; 310 mp->m_flag |= MSTATUS; 311 } 312 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) 313 gotcha++; 314 if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) { 315 if ((id = hfield("article-id", mp)) != NOSTR) 316 fprintf(readstat, "%s\n", id); 317 } 318 } 319 if (Tflag != NOSTR) 320 fclose(readstat); 321 if (!gotcha || Tflag != NOSTR) 322 goto done; 323 ibuf = NULL; 324 if (stat(editfile, &statb) >= 0 && statb.st_size > mailsize) { 325 strcpy(tempname, "/tmp/mboxXXXXXX"); 326 mktemp(tempname); 327 if ((obuf = fopen(tempname, "w")) == NULL) { 328 perror(tempname); 329 relsesigs(); 330 reset(0); 331 } 332 if ((ibuf = fopen(editfile, "r")) == NULL) { 333 perror(editfile); 334 fclose(obuf); 335 remove(tempname); 336 relsesigs(); 337 reset(0); 338 } 339 fseek(ibuf, mailsize, 0); 340 while ((c = getc(ibuf)) != EOF) 341 putc(c, obuf); 342 fclose(ibuf); 343 fclose(obuf); 344 if ((ibuf = fopen(tempname, "r")) == NULL) { 345 perror(tempname); 346 remove(tempname); 347 relsesigs(); 348 reset(0); 349 } 350 remove(tempname); 351 } 352 printf("\"%s\" ", editfile); 353 flush(); 354 if ((obuf = fopen(editfile, "w")) == NULL) { 355 perror(editfile); 356 relsesigs(); 357 reset(0); 358 } 359 c = 0; 360 for (mp = &message[0]; mp < &message[msgCount]; mp++) { 361 if ((mp->m_flag & MDELETED) != 0) 362 continue; 363 c++; 364 if (send(mp, obuf) < 0) { 365 perror(editfile); 366 relsesigs(); 367 reset(0); 368 } 369 } 370 gotcha = (c == 0 && ibuf == NULL); 371 if (ibuf != NULL) { 372 while ((c = getc(ibuf)) != EOF) 373 putc(c, obuf); 374 fclose(ibuf); 375 } 376 fflush(obuf); 377 if (ferror(obuf)) { 378 perror(editfile); 379 relsesigs(); 380 reset(0); 381 } 382 fclose(obuf); 383 if (gotcha) { 384 remove(editfile); 385 printf("removed\n"); 386 } 387 else 388 printf("complete\n"); 389 flush(); 390 391 done: 392 relsesigs(); 393 } 394 395 # ifndef VMUNIX 396 static int SaveSigs[32]; 397 # endif VMUNIX 398 399 /* 400 * Hold signals SIGHUP - SIGQUIT. 401 */ 402 holdsigs() 403 { 404 register int i; 405 406 for (i = SIGHUP; i <= SIGQUIT; i++) 407 # ifdef VMUNIX 408 sighold(i); 409 # else 410 SaveSigs[i] = signal(i, SIG_IGN); 411 # endif 412 } 413 414 /* 415 * Release signals SIGHUP - SIGQUIT 416 */ 417 relsesigs() 418 { 419 register int i; 420 421 for (i = SIGHUP; i <= SIGQUIT; i++) 422 # ifdef VMUNIX 423 sigrelse(i); 424 # else 425 signal(i, SaveSigs[i]); 426 # endif 427 } 428 429 /* 430 * Empty the output buffer. 431 */ 432 433 clrbuf(buf) 434 register FILE *buf; 435 { 436 437 buf = stdout; 438 buf->_ptr = buf->_base; 439 buf->_cnt = BUFSIZ; 440 } 441 442 /* 443 * Open a temp file by creating, closing, unlinking, and 444 * reopening. Return the open file descriptor. 445 */ 446 447 opentemp(file) 448 char file[]; 449 { 450 register int f; 451 452 if ((f = creat(file, 0600)) < 0) { 453 perror(file); 454 return(-1); 455 } 456 close(f); 457 if ((f = open(file, 2)) < 0) { 458 perror(file); 459 remove(file); 460 return(-1); 461 } 462 remove(file); 463 return(f); 464 } 465 466 /* 467 * Flush the standard output. 468 */ 469 470 flush() 471 { 472 fflush(stdout); 473 fflush(stderr); 474 } 475 476 /* 477 * Determine the size of the file possessed by 478 * the passed buffer. 479 */ 480 481 off_t 482 fsize(iob) 483 FILE *iob; 484 { 485 register int f; 486 struct stat sbuf; 487 488 f = fileno(iob); 489 if (fstat(f, &sbuf) < 0) 490 return(0); 491 return(sbuf.st_size); 492 } 493 494 /* 495 * Take a file name, possibly with shell meta characters 496 * in it and expand it by using "sh -c echo filename" 497 * Return the file name as a dynamic string. 498 */ 499 500 char * 501 expand(name) 502 char name[]; 503 { 504 char xname[BUFSIZ]; 505 char cmdbuf[BUFSIZ]; 506 register int pid, l, rc; 507 register char *cp, *Shell; 508 int s, pivec[2], (*sigint)(); 509 struct stat sbuf; 510 511 if (name[0] == '+' && getfold(cmdbuf) >= 0) { 512 sprintf(xname, "%s/%s", cmdbuf, name + 1); 513 return(expand(savestr(xname))); 514 } 515 if (!anyof(name, "~{[*?$`'\"\\")) 516 return(name); 517 if (pipe(pivec) < 0) { 518 perror("pipe"); 519 return(name); 520 } 521 sprintf(cmdbuf, "echo %s", name); 522 if ((pid = vfork()) == 0) { 523 Shell = value("SHELL"); 524 if (Shell == NOSTR) 525 Shell = SHELL; 526 close(pivec[0]); 527 close(1); 528 dup(pivec[1]); 529 close(pivec[1]); 530 close(2); 531 execl(Shell, Shell, "-c", cmdbuf, 0); 532 _exit(1); 533 } 534 if (pid == -1) { 535 perror("fork"); 536 close(pivec[0]); 537 close(pivec[1]); 538 return(NOSTR); 539 } 540 close(pivec[1]); 541 l = read(pivec[0], xname, BUFSIZ); 542 close(pivec[0]); 543 while (wait(&s) != pid); 544 ; 545 s &= 0377; 546 if (s != 0 && s != SIGPIPE) { 547 fprintf(stderr, "\"Echo\" failed\n"); 548 goto err; 549 } 550 if (l < 0) { 551 perror("read"); 552 goto err; 553 } 554 if (l == 0) { 555 fprintf(stderr, "\"%s\": No match\n", name); 556 goto err; 557 } 558 if (l == BUFSIZ) { 559 fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name); 560 goto err; 561 } 562 xname[l] = 0; 563 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 564 ; 565 *++cp = '\0'; 566 if (any(' ', xname) && stat(xname, &sbuf) < 0) { 567 fprintf(stderr, "\"%s\": Ambiguous\n", name); 568 goto err; 569 } 570 return(savestr(xname)); 571 572 err: 573 return(NOSTR); 574 } 575 576 /* 577 * Determine the current folder directory name. 578 */ 579 getfold(name) 580 char *name; 581 { 582 char *folder; 583 584 if ((folder = value("folder")) == NOSTR) 585 return(-1); 586 if (*folder == '/') 587 strcpy(name, folder); 588 else 589 sprintf(name, "%s/%s", homedir, folder); 590 return(0); 591 } 592 593 /* 594 * A nicer version of Fdopen, which allows us to fclose 595 * without losing the open file. 596 */ 597 598 FILE * 599 Fdopen(fildes, mode) 600 char *mode; 601 { 602 register int f; 603 FILE *fdopen(); 604 605 f = dup(fildes); 606 if (f < 0) { 607 perror("dup"); 608 return(NULL); 609 } 610 return(fdopen(f, mode)); 611 } 612