1 # 2 3 #include "rcv.h" 4 #include <sys/stat.h> 5 #include <sgtty.h> 6 #include <ctype.h> 7 8 /* 9 * Mail -- a mail program 10 * 11 * Auxiliary functions. 12 */ 13 14 static char *SccsId = "@(#)aux.c 1.1 10/08/80"; 15 16 /* 17 * Return a pointer to a dynamic copy of the argument. 18 */ 19 20 char * 21 savestr(str) 22 char *str; 23 { 24 register char *cp, *cp2, *top; 25 26 for (cp = str; *cp; cp++) 27 ; 28 top = salloc(cp-str + 1); 29 if (top == NOSTR) 30 return(NOSTR); 31 for (cp = str, cp2 = top; *cp; cp++) 32 *cp2++ = *cp; 33 *cp2 = 0; 34 return(top); 35 } 36 37 /* 38 * Copy the name from the passed header line into the passed 39 * name buffer. Null pad the name buffer. 40 */ 41 42 copyname(linebuf, nbuf) 43 char *linebuf, *nbuf; 44 { 45 register char *cp, *cp2; 46 47 for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++) 48 *cp2++ = *cp; 49 while (cp2-nbuf < 8) 50 *cp2++ = 0; 51 } 52 53 /* 54 * Announce a fatal error and die. 55 */ 56 57 panic(str) 58 char *str; 59 { 60 prs("panic: "); 61 prs(str); 62 prs("\n"); 63 exit(1); 64 } 65 66 /* 67 * Catch stdio errors and report them more nicely. 68 */ 69 70 _error(str) 71 char *str; 72 { 73 prs("Stdio Error: "); 74 prs(str); 75 prs("\n"); 76 abort(); 77 } 78 79 /* 80 * Print a string on diagnostic output. 81 */ 82 83 prs(str) 84 char *str; 85 { 86 register char *s; 87 88 for (s = str; *s; s++) 89 ; 90 write(2, str, s-str); 91 } 92 93 /* 94 * Touch the named message by setting its MTOUCH flag. 95 * Touched messages have the effect of not being sent 96 * back to the system mailbox on exit. 97 */ 98 99 touch(mesg) 100 { 101 if (mesg >= 1 && mesg <= msgCount) 102 message[mesg-1].m_flag |= MTOUCH; 103 } 104 105 /* 106 * Test to see if the passed file name is a directory. 107 * Return true if it is. 108 */ 109 110 isdir(name) 111 char name[]; 112 { 113 struct stat sbuf; 114 115 if (stat(name, &sbuf) < 0) 116 return(0); 117 return((sbuf.st_mode & S_IFMT) == S_IFDIR); 118 } 119 120 /* 121 * Compute the size in characters of the passed message 122 */ 123 124 unsigned int 125 msize(messp) 126 struct message *messp; 127 { 128 register struct message *mp; 129 130 mp = messp; 131 return(mp->m_size); 132 } 133 134 /* 135 * Count the number of arguments in the given string raw list. 136 */ 137 138 argcount(argv) 139 char **argv; 140 { 141 register char **ap; 142 143 for (ap = argv; *ap != NOSTR; ap++) 144 ; 145 return(ap-argv); 146 } 147 148 /* 149 * Given a file address, determine the 150 * block number it represents. 151 */ 152 153 blockof(off) 154 off_t off; 155 { 156 off_t a; 157 158 a = off >> 9; 159 a &= 077777; 160 return((int) a); 161 } 162 163 /* 164 * Take a file address, and determine 165 * its offset in the current block. 166 */ 167 168 offsetof(off) 169 off_t off; 170 { 171 off_t a; 172 173 a = off & 0777; 174 return((int) a); 175 } 176 177 /* 178 * Determine if the passed file is actually a tty, via a call to 179 * gtty. This is not totally reliable, but . . . 180 */ 181 182 isatty(f) 183 { 184 struct sgttyb buf; 185 186 if (gtty(f, &buf) < 0) 187 return(0); 188 return(1); 189 } 190 191 /* 192 * Return the desired header line from the passed message 193 * pointer (or NOSTR if the desired header field is not available). 194 */ 195 196 char * 197 hfield(field, mp) 198 char field[]; 199 struct message *mp; 200 { 201 register FILE *ibuf; 202 char linebuf[LINESIZE]; 203 register int lc; 204 205 ibuf = setinput(mp); 206 if ((lc = mp->m_lines) <= 0) 207 return(NOSTR); 208 if (readline(ibuf, linebuf) < 0) 209 return(NOSTR); 210 lc--; 211 do { 212 lc = gethfield(ibuf, linebuf, lc); 213 if (lc == -1) 214 return(NOSTR); 215 if (ishfield(linebuf, field)) 216 return(savestr(hcontents(linebuf))); 217 } while (lc > 0); 218 return(NOSTR); 219 } 220 221 /* 222 * Return the next header field found in the given message. 223 * Return > 0 if something found, <= 0 elsewise. 224 * Must deal with \ continuations & other such fraud. 225 */ 226 227 gethfield(f, linebuf, rem) 228 register FILE *f; 229 char linebuf[]; 230 register int rem; 231 { 232 char line2[LINESIZE]; 233 long loc; 234 register char *cp, *cp2; 235 register int c; 236 237 238 for (;;) { 239 if (rem <= 0) 240 return(-1); 241 if (readline(f, linebuf) < 0) 242 return(-1); 243 rem--; 244 if (strlen(linebuf) == 0) 245 return(-1); 246 if (isspace(linebuf[0])) 247 continue; 248 if (linebuf[0] == '>') 249 continue; 250 cp = index(linebuf, ':'); 251 if (cp == NOSTR) 252 continue; 253 for (cp2 = linebuf; cp2 < cp; cp2++) 254 if (isdigit(*cp2)) 255 continue; 256 257 /* 258 * I guess we got a headline. 259 * Handle wraparounding 260 */ 261 262 for (;;) { 263 if (rem <= 0) 264 break; 265 #ifdef CANTELL 266 loc = ftell(f); 267 if (readline(f, line2) < 0) 268 break; 269 rem--; 270 if (!isspace(line2[0])) { 271 fseek(f, loc, 0); 272 rem++; 273 break; 274 } 275 #else 276 c = getc(f); 277 ungetc(c, f); 278 if (!isspace(c) || c == '\n') 279 break; 280 if (readline(f, line2) < 0) 281 break; 282 rem--; 283 #endif 284 cp2 = line2; 285 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 286 ; 287 if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2) 288 break; 289 cp = &linebuf[strlen(linebuf)]; 290 while (cp > linebuf && 291 (isspace(cp[-1]) || cp[-1] == '\\')) 292 cp--; 293 *cp++ = ' '; 294 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 295 ; 296 strcpy(cp, cp2); 297 } 298 if ((c = strlen(linebuf)) > 0) { 299 cp = &linebuf[c-1]; 300 while (cp > linebuf && isspace(*cp)) 301 cp--; 302 *++cp = 0; 303 } 304 return(rem); 305 } 306 /* NOTREACHED */ 307 } 308 309 /* 310 * Check whether the passed line is a header line of 311 * the desired breed. 312 */ 313 314 ishfield(linebuf, field) 315 char linebuf[], field[]; 316 { 317 register char *cp; 318 register int c; 319 320 if ((cp = index(linebuf, ':')) == NOSTR) 321 return(0); 322 if (cp == linebuf) 323 return(0); 324 cp--; 325 while (cp > linebuf && isspace(*cp)) 326 cp--; 327 c = *++cp; 328 *cp = 0; 329 if (icequal(linebuf ,field)) { 330 *cp = c; 331 return(1); 332 } 333 *cp = c; 334 return(0); 335 } 336 337 /* 338 * Extract the non label information from the given header field 339 * and return it. 340 */ 341 342 char * 343 hcontents(hfield) 344 char hfield[]; 345 { 346 register char *cp; 347 348 if ((cp = index(hfield, ':')) == NOSTR) 349 return(NOSTR); 350 cp++; 351 while (*cp && isspace(*cp)) 352 cp++; 353 return(cp); 354 } 355 356 /* 357 * Compare two strings, ignoring case. 358 */ 359 360 icequal(s1, s2) 361 register char *s1, *s2; 362 { 363 364 while (raise(*s1++) == raise(*s2)) 365 if (*s2++ == 0) 366 return(1); 367 return(0); 368 } 369 370 /* 371 * The following code deals with input stacking to do source 372 * commands. All but the current file pointer are saved on 373 * the stack. 374 */ 375 376 static int ssp = -1; /* Top of file stack */ 377 static FILE *sstack[_NFILE]; /* Saved input files */ 378 379 /* 380 * Pushdown current input file and switch to a new one. 381 * Set the global flag "sourcing" so that others will realize 382 * that they are no longer reading from a tty (in all probability). 383 */ 384 385 source(name) 386 char name[]; 387 { 388 register FILE *fi; 389 390 if ((fi = fopen(name, "r")) == NULL) { 391 perror(name); 392 return(1); 393 } 394 if (ssp >= _NFILE-2) { 395 printf("Too much \"sourcing\" going on.\n"); 396 fclose(fi); 397 return(1); 398 } 399 sstack[++ssp] = input; 400 input = fi; 401 sourcing++; 402 return(0); 403 } 404 405 /* 406 * Source a file, but do nothing if the file cannot be opened. 407 */ 408 409 source1(name) 410 char name[]; 411 { 412 register int f; 413 414 if ((f = open(name, 0)) < 0) 415 return(0); 416 close(f); 417 source(name); 418 } 419 420 /* 421 * Pop the current input back to the previous level. 422 * Update the "sourcing" flag as appropriate. 423 */ 424 425 unstack() 426 { 427 if (ssp < 0) { 428 printf("\"Source\" stack over-pop.\n"); 429 sourcing = 0; 430 return(1); 431 } 432 fclose(input); 433 input = sstack[ssp--]; 434 if (ssp < 0) 435 sourcing = 0; 436 return(0); 437 } 438 439 /* 440 * Touch the indicated file. 441 * This is nifty for the shell. 442 * If we have the utime() system call, this is better served 443 * by using that, since it will work for empty files. 444 * On non-utime systems, we must sleep a second, then read. 445 */ 446 447 alter(name) 448 char name[]; 449 { 450 #ifdef UTIME 451 struct stat statb; 452 long time(); 453 time_t time_p[2]; 454 #else 455 register int pid, f; 456 char w; 457 #endif UTIME 458 459 #ifdef UTIME 460 if (stat(name, &statb) < 0) 461 return; 462 time_p[0] = time((long *) 0) + 1; 463 time_p[1] = statb.st_mtime; 464 utime(name, time_p); 465 #else 466 if ((pid = fork()) != 0) 467 return; 468 clrbuf(stdout); 469 clrbuf(stderr); 470 clrbuf(stdin); 471 sleep(1); 472 if ((f = open(name, 0)) < 0) 473 exit(1); 474 read(f, &w, 1); 475 exit(0); 476 #endif 477 } 478 479 /* 480 * Examine the passed line buffer and 481 * return true if it is all blanks and tabs. 482 */ 483 484 blankline(linebuf) 485 char linebuf[]; 486 { 487 register char *cp; 488 489 for (cp = linebuf; *cp; cp++) 490 if (!any(*cp, " \t")) 491 return(0); 492 return(1); 493 } 494 495 /* 496 * Fetch the sender's name from the passed message. 497 */ 498 499 char * 500 nameof(mp) 501 register struct message *mp; 502 { 503 char namebuf[LINESIZE]; 504 char linebuf[LINESIZE]; 505 register char *cp, *cp2; 506 register FILE *ibuf; 507 int first = 1; 508 509 if ((cp = hfield("reply-to", mp)) != NOSTR) { 510 strcpy(namebuf, cp); 511 return(namebuf); 512 } 513 ibuf = setinput(mp); 514 copy("", namebuf); 515 if (readline(ibuf, linebuf) <= 0) 516 return(savestr(namebuf)); 517 newname: 518 for (cp = linebuf; *cp != ' '; cp++) 519 ; 520 while (any(*cp, " \t")) 521 cp++; 522 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 523 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 524 ; 525 *cp2 = '\0'; 526 if (readline(ibuf, linebuf) <= 0) 527 return(savestr(namebuf)); 528 if ((cp = index(linebuf, 'F')) == NULL) 529 return(savestr(namebuf)); 530 if (strncmp(cp, "From", 4) != 0) 531 return(savestr(namebuf)); 532 while ((cp = index(cp, 'r')) != NULL) { 533 if (strncmp(cp, "remote", 6) == 0) { 534 if ((cp = index(cp, 'f')) == NULL) 535 break; 536 if (strncmp(cp, "from", 4) != 0) 537 break; 538 if ((cp = index(cp, ' ')) == NULL) 539 break; 540 cp++; 541 if (first) { 542 copy(cp, namebuf); 543 first = 0; 544 } else 545 strcpy(rindex(namebuf, '!')+1, cp); 546 strcat(namebuf, "!"); 547 goto newname; 548 } 549 cp++; 550 } 551 return(savestr(namebuf)); 552 } 553 554 /* 555 * Find the rightmost pointer to an instance of the 556 * character in the string and return it. 557 */ 558 559 char * 560 rindex(str, c) 561 char str[]; 562 register int c; 563 { 564 register char *cp, *cp2; 565 566 for (cp = str, cp2 = NOSTR; *cp; cp++) 567 if (c == *cp) 568 cp2 = cp; 569 return(cp2); 570 } 571 572 /* 573 * See if the string is a number. 574 */ 575 576 numeric(str) 577 char str[]; 578 { 579 register char *cp = str; 580 581 while (*cp) 582 if (!isdigit(*cp++)) 583 return(0); 584 return(1); 585 } 586 587 /* 588 * Are any of the characters in the two strings the same? 589 */ 590 591 anyof(s1, s2) 592 register char *s1, *s2; 593 { 594 register int c; 595 596 while (c = *s1++) 597 if (any(c, s2)) 598 return(1); 599 return(0); 600 } 601 602 /* 603 * Determine the leftmost index of the character 604 * in the string. 605 */ 606 607 char * 608 index(str, ch) 609 char *str; 610 { 611 register char *cp; 612 register int c; 613 614 for (c = ch, cp = str; *cp; cp++) 615 if (*cp == c) 616 return(cp); 617 return(NOSTR); 618 } 619 620 /* 621 * String compare two strings of bounded length. 622 */ 623 624 strncmp(as1, as2, an) 625 char *as1, *as2; 626 { 627 register char *s1, *s2; 628 register int n; 629 630 s1 = as1; 631 s2 = as2; 632 n = an; 633 while (--n >= 0 && *s1 == *s2++) 634 if (*s1++ == '\0') 635 return(0); 636 return(n<0 ? 0 : *s1 - *--s2); 637 } 638 639