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