1 /* 2 * Copyright (c) 1994 Eric P. Allman 3 * Copyright (c) 1994 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 # include "sendmail.h" 10 # include <string.h> 11 12 #ifndef lint 13 static char sccsid[] = "@(#)mime.c 8.19 (Berkeley) 04/24/95"; 14 #endif /* not lint */ 15 16 /* 17 ** MIME support. 18 ** 19 ** I am indebted to John Beck of Hewlett-Packard, who contributed 20 ** his code to me for inclusion. As it turns out, I did not use 21 ** his code since he used a "minimum change" approach that used 22 ** several temp files, and I wanted a "minimum impact" approach 23 ** that would avoid copying. However, looking over his code 24 ** helped me cement my understanding of the problem. 25 ** 26 ** I also looked at, but did not directly use, Nathaniel 27 ** Borenstein's "code.c" module. Again, it functioned as 28 ** a file-to-file translator, which did not fit within my 29 ** design bounds, but it was a useful base for understanding 30 ** the problem. 31 */ 32 33 34 /* character set for hex and base64 encoding */ 35 char Base16Code[] = "0123456789ABCDEF"; 36 char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 37 38 /* types of MIME boundaries */ 39 #define MBT_SYNTAX 0 /* syntax error */ 40 #define MBT_NOTSEP 1 /* not a boundary */ 41 #define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */ 42 #define MBT_FINAL 3 /* final boundary (trailing -- included) */ 43 44 static char *MimeBoundaryNames[] = 45 { 46 "SYNTAX", "NOTSEP", "INTERMED", "FINAL" 47 }; 48 /* 49 ** MIME8TO7 -- output 8 bit body in 7 bit format 50 ** 51 ** The header has already been output -- this has to do the 52 ** 8 to 7 bit conversion. It would be easy if we didn't have 53 ** to deal with nested formats (multipart/xxx and message/rfc822). 54 ** 55 ** We won't be called if we don't have to do a conversion, and 56 ** appropriate MIME-Version: and Content-Type: fields have been 57 ** output. Any Content-Transfer-Encoding: field has not been 58 ** output, and we can add it here. 59 ** 60 ** Parameters: 61 ** mci -- mailer connection information. 62 ** header -- the header for this body part. 63 ** e -- envelope. 64 ** boundaries -- the currently pending message boundaries. 65 ** NULL if we are processing the outer portion. 66 ** flags -- to tweak processing. 67 ** 68 ** Returns: 69 ** An indicator of what terminated the message part: 70 ** MBT_FINAL -- the final boundary 71 ** MBT_INTERMED -- an intermediate boundary 72 ** MBT_NOTSEP -- an end of file 73 */ 74 75 struct args 76 { 77 char *field; /* name of field */ 78 char *value; /* value of that field */ 79 }; 80 81 int 82 mime8to7(mci, header, e, boundaries, flags) 83 register MCI *mci; 84 HDR *header; 85 register ENVELOPE *e; 86 char **boundaries; 87 int flags; 88 { 89 register char *p; 90 int linelen; 91 int bt; 92 off_t offset; 93 size_t sectionsize, sectionhighbits; 94 int i; 95 char *type; 96 char *subtype; 97 char *cte; 98 char **pvp; 99 int argc = 0; 100 char *bp; 101 struct args argv[MAXMIMEARGS]; 102 char bbuf[128]; 103 char buf[MAXLINE]; 104 char pvpbuf[MAXLINE]; 105 extern char MimeTokenTab[256]; 106 107 if (tTd(43, 1)) 108 { 109 printf("mime8to7: flags = %x, boundaries =", flags); 110 if (boundaries[0] == NULL) 111 printf(" <none>"); 112 else 113 { 114 for (i = 0; boundaries[i] != NULL; i++) 115 printf(" %s", boundaries[i]); 116 } 117 printf("\n"); 118 } 119 p = hvalue("Content-Transfer-Encoding", header); 120 if (p == NULL || 121 (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, 122 MimeTokenTab)) == NULL || 123 pvp[0] == NULL) 124 { 125 cte = NULL; 126 } 127 else 128 { 129 cataddr(pvp, NULL, buf, sizeof buf, '\0'); 130 cte = newstr(buf); 131 } 132 133 type = subtype = NULL; 134 p = hvalue("Content-Type", header); 135 if (p == NULL) 136 { 137 if (bitset(M87F_DIGEST, flags)) 138 p = "message/rfc822"; 139 else 140 p = "text/plain"; 141 } 142 if (p != NULL && 143 (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, 144 MimeTokenTab)) != NULL && 145 pvp[0] != NULL) 146 { 147 if (tTd(43, 40)) 148 { 149 for (i = 0; pvp[i] != NULL; i++) 150 printf("pvp[%d] = \"%s\"\n", i, pvp[i]); 151 } 152 type = *pvp++; 153 if (*pvp != NULL && strcmp(*pvp, "/") == 0 && 154 *++pvp != NULL) 155 { 156 subtype = *pvp++; 157 } 158 159 /* break out parameters */ 160 while (*pvp != NULL && argc < MAXMIMEARGS) 161 { 162 /* skip to semicolon separator */ 163 while (*pvp != NULL && strcmp(*pvp, ";") != 0) 164 pvp++; 165 if (*pvp++ == NULL || *pvp == NULL) 166 break; 167 168 /* extract field name */ 169 argv[argc].field = *pvp++; 170 171 /* see if there is a value */ 172 if (*pvp != NULL && strcmp(*pvp, "=") == 0 && 173 (*++pvp == NULL || strcmp(*pvp, ";") != 0)) 174 { 175 argv[argc].value = *pvp; 176 argc++; 177 } 178 } 179 } 180 181 /* check for disaster cases */ 182 if (type == NULL) 183 type = "-none-"; 184 if (subtype == NULL) 185 subtype = "-none-"; 186 187 /* don't propogate some flags more than one level into the message */ 188 flags &= ~M87F_DIGEST; 189 190 /* 191 ** Check for cases that can not be encoded. 192 ** 193 ** For example, you can't encode certain kinds of types 194 ** or already-encoded messages. If we find this case, 195 ** just copy it through. 196 */ 197 198 sprintf(buf, "%s/%s", type, subtype); 199 if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) 200 flags |= M87F_NO8BIT; 201 202 /* 203 ** Multipart requires special processing. 204 ** 205 ** Do a recursive descent into the message. 206 */ 207 208 if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags)) 209 { 210 int blen; 211 212 if (strcasecmp(subtype, "digest") == 0) 213 flags |= M87F_DIGEST; 214 215 for (i = 0; i < argc; i++) 216 { 217 if (strcasecmp(argv[i].field, "boundary") == 0) 218 break; 219 } 220 if (i >= argc) 221 { 222 syserr("mime8to7: Content-Type: %s missing boundary", p); 223 p = "---"; 224 } 225 else 226 { 227 p = argv[i].value; 228 stripquotes(p); 229 } 230 blen = strlen(p); 231 if (blen > sizeof bbuf - 1) 232 { 233 syserr("mime8to7: multipart boundary \"%s\" too long", 234 p); 235 blen = sizeof bbuf - 1; 236 } 237 strncpy(bbuf, p, blen); 238 bbuf[blen] = '\0'; 239 if (tTd(43, 1)) 240 printf("mime8to7: multipart boundary \"%s\"\n", bbuf); 241 for (i = 0; i < MAXMIMENESTING; i++) 242 if (boundaries[i] == NULL) 243 break; 244 if (i >= MAXMIMENESTING) 245 syserr("mime8to7: multipart nesting boundary too deep"); 246 else 247 { 248 boundaries[i] = bbuf; 249 boundaries[i + 1] = NULL; 250 } 251 252 /* skip the early "comment" prologue */ 253 putline("", mci); 254 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 255 { 256 bt = mimeboundary(buf, boundaries); 257 if (bt != MBT_NOTSEP) 258 break; 259 putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 260 if (tTd(43, 99)) 261 printf(" ...%s", buf); 262 } 263 if (feof(e->e_dfp)) 264 bt = MBT_FINAL; 265 while (bt != MBT_FINAL) 266 { 267 auto HDR *hdr = NULL; 268 269 sprintf(buf, "--%s", bbuf); 270 putline(buf, mci); 271 if (tTd(43, 35)) 272 printf(" ...%s\n", buf); 273 collect(e->e_dfp, FALSE, FALSE, &hdr, e); 274 if (tTd(43, 101)) 275 putline("+++after collect", mci); 276 putheader(mci, hdr, e); 277 if (tTd(43, 101)) 278 putline("+++after putheader", mci); 279 bt = mime8to7(mci, hdr, e, boundaries, flags); 280 } 281 sprintf(buf, "--%s--", bbuf); 282 putline(buf, mci); 283 if (tTd(43, 35)) 284 printf(" ...%s\n", buf); 285 boundaries[i] = NULL; 286 287 /* skip the late "comment" epilogue */ 288 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 289 { 290 bt = mimeboundary(buf, boundaries); 291 if (bt != MBT_NOTSEP) 292 break; 293 putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 294 if (tTd(43, 99)) 295 printf(" ...%s", buf); 296 } 297 if (feof(e->e_dfp)) 298 bt = MBT_FINAL; 299 if (tTd(43, 3)) 300 printf("\t\t\tmime8to7=>%s (multipart)\n", 301 MimeBoundaryNames[bt]); 302 return bt; 303 } 304 305 /* 306 ** Message/* types -- recurse exactly once. 307 */ 308 309 if (strcasecmp(type, "message") == 0) 310 { 311 if (strcasecmp(subtype, "rfc822") != 0) 312 { 313 flags |= M87F_NO8BIT; 314 } 315 else 316 { 317 register char *q; 318 auto HDR *hdr = NULL; 319 320 putline("", mci); 321 322 collect(e->e_dfp, FALSE, FALSE, &hdr, e); 323 if (tTd(43, 101)) 324 putline("+++after collect", mci); 325 putheader(mci, hdr, e); 326 if (tTd(43, 101)) 327 putline("+++after putheader", mci); 328 bt = mime8to7(mci, hdr, e, boundaries, flags); 329 return bt; 330 } 331 } 332 333 /* 334 ** Non-compound body type 335 ** 336 ** Compute the ratio of seven to eight bit characters; 337 ** use that as a heuristic to decide how to do the 338 ** encoding. 339 */ 340 341 sectionsize = sectionhighbits = 0; 342 if (!bitset(M87F_NO8BIT, flags)) 343 { 344 /* remember where we were */ 345 offset = ftell(e->e_dfp); 346 if (offset == -1) 347 syserr("mime8to7: cannot ftell on df%s", e->e_id); 348 349 /* do a scan of this body type to count character types */ 350 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 351 { 352 if (mimeboundary(buf, boundaries) != MBT_NOTSEP) 353 break; 354 for (p = buf; *p != '\0'; p++) 355 { 356 /* count bytes with the high bit set */ 357 sectionsize++; 358 if (bitset(0200, *p)) 359 sectionhighbits++; 360 } 361 362 /* 363 ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, 364 ** assume base64. This heuristic avoids double-reading 365 ** large graphics or video files. 366 */ 367 368 if (sectionsize >= 4096 && 369 sectionhighbits > sectionsize / 4) 370 break; 371 } 372 373 /* return to the original offset for processing */ 374 /* XXX use relative seeks to handle >31 bit file sizes? */ 375 if (fseek(e->e_dfp, offset, SEEK_SET) < 0) 376 syserr("mime8to7: cannot fseek on df%s", e->e_id); 377 else 378 clearerr(e->e_dfp); 379 } 380 381 /* 382 ** Heuristically determine encoding method. 383 ** If more than 1/8 of the total characters have the 384 ** eighth bit set, use base64; else use quoted-printable. 385 ** However, only encode binary encoded data as base64, 386 ** since otherwise the NL=>CRLF mapping will be a problem. 387 */ 388 389 if (tTd(43, 8)) 390 { 391 printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n", 392 sectionhighbits, sectionsize, 393 cte == NULL ? "[none]" : cte); 394 } 395 if (cte != NULL && strcasecmp(cte, "binary") == 0) 396 sectionsize = sectionhighbits; 397 linelen = 0; 398 bp = buf; 399 if (sectionhighbits == 0) 400 { 401 /* no encoding necessary */ 402 if (cte != NULL) 403 { 404 sprintf(buf, "Content-Transfer-Encoding: %s", cte); 405 putline(buf, mci); 406 if (tTd(43, 36)) 407 printf(" ...%s\n", buf); 408 } 409 putline("", mci); 410 mci->mci_flags &= ~MCIF_INHEADER; 411 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 412 { 413 bt = mimeboundary(buf, boundaries); 414 if (bt != MBT_NOTSEP) 415 break; 416 putline(buf, mci); 417 } 418 if (feof(e->e_dfp)) 419 bt = MBT_FINAL; 420 } 421 else if (sectionsize / 8 < sectionhighbits) 422 { 423 /* use base64 encoding */ 424 int c1, c2; 425 426 putline("Content-Transfer-Encoding: base64", mci); 427 if (tTd(43, 36)) 428 printf(" ...Content-Transfer-Encoding: base64\n"); 429 putline("", mci); 430 mci->mci_flags &= ~MCIF_INHEADER; 431 while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) 432 { 433 if (linelen > 71) 434 { 435 *bp = '\0'; 436 putline(buf, mci); 437 linelen = 0; 438 bp = buf; 439 } 440 linelen += 4; 441 *bp++ = Base64Code[(c1 >> 2)]; 442 c1 = (c1 & 0x03) << 4; 443 c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 444 if (c2 == EOF) 445 { 446 *bp++ = Base64Code[c1]; 447 *bp++ = '='; 448 *bp++ = '='; 449 break; 450 } 451 c1 |= (c2 >> 4) & 0x0f; 452 *bp++ = Base64Code[c1]; 453 c1 = (c2 & 0x0f) << 2; 454 c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 455 if (c2 == EOF) 456 { 457 *bp++ = Base64Code[c1]; 458 *bp++ = '='; 459 break; 460 } 461 c1 |= (c2 >> 6) & 0x03; 462 *bp++ = Base64Code[c1]; 463 *bp++ = Base64Code[c2 & 0x3f]; 464 } 465 } 466 else 467 { 468 /* use quoted-printable encoding */ 469 int c1, c2; 470 int fromstate; 471 BITMAP badchars; 472 473 /* set up map of characters that must be mapped */ 474 clrbitmap(badchars); 475 for (c1 = 0x00; c1 < 0x20; c1++) 476 setbitn(c1, badchars); 477 clrbitn('\t', badchars); 478 for (c1 = 0x7f; c1 < 0x100; c1++) 479 setbitn(c1, badchars); 480 setbitn('=', badchars); 481 if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) 482 for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) 483 setbitn(*p, badchars); 484 485 putline("Content-Transfer-Encoding: quoted-printable", mci); 486 if (tTd(43, 36)) 487 printf(" ...Content-Transfer-Encoding: quoted-printable\n"); 488 putline("", mci); 489 mci->mci_flags &= ~MCIF_INHEADER; 490 fromstate = 0; 491 c2 = '\n'; 492 while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) 493 { 494 if (c1 == '\n') 495 { 496 if (c2 == ' ' || c2 == '\t') 497 { 498 *bp++ = '='; 499 *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 500 *bp++ = Base16Code[c2 & 0x0f]; 501 *bp = '\0'; 502 putline(buf, mci); 503 bp = buf; 504 } 505 *bp = '\0'; 506 putline(buf, mci); 507 linelen = fromstate = 0; 508 bp = buf; 509 c2 = c1; 510 continue; 511 } 512 if (c2 == ' ' && linelen == 4 && fromstate == 4 && 513 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 514 { 515 *bp++ = '='; 516 *bp++ = '2'; 517 *bp++ = '0'; 518 linelen += 3; 519 } 520 else if (c2 == ' ' || c2 == '\t') 521 { 522 *bp++ = c2; 523 linelen++; 524 } 525 if (linelen > 72) 526 { 527 *bp++ = '='; 528 *bp = '\0'; 529 putline(buf, mci); 530 linelen = fromstate = 0; 531 bp = buf; 532 c2 = '\n'; 533 } 534 if (bitnset(c1 & 0xff, badchars)) 535 { 536 *bp++ = '='; 537 *bp++ = Base16Code[(c1 >> 4) & 0x0f]; 538 *bp++ = Base16Code[c1 & 0x0f]; 539 linelen += 3; 540 } 541 else if (c1 != ' ' && c1 != '\t') 542 { 543 if (linelen < 4 && c1 == "From"[linelen]) 544 fromstate++; 545 *bp++ = c1; 546 linelen++; 547 } 548 c2 = c1; 549 } 550 551 /* output any saved character */ 552 if (c2 == ' ' || c2 == '\t') 553 { 554 *bp++ = '='; 555 *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 556 *bp++ = Base16Code[c2 & 0x0f]; 557 linelen += 3; 558 } 559 } 560 if (linelen > 0) 561 { 562 *bp = '\0'; 563 putline(buf, mci); 564 } 565 if (tTd(43, 3)) 566 printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); 567 return bt; 568 } 569 /* 570 ** MIME_GETCHAR -- get a character for MIME processing 571 ** 572 ** Treats boundaries as EOF. 573 ** 574 ** Parameters: 575 ** fp -- the input file. 576 ** boundaries -- the current MIME boundaries. 577 ** btp -- if the return value is EOF, *btp is set to 578 ** the type of the boundary. 579 ** 580 ** Returns: 581 ** The next character in the input stream. 582 */ 583 584 int 585 mime_getchar(fp, boundaries, btp) 586 register FILE *fp; 587 char **boundaries; 588 int *btp; 589 { 590 int c; 591 static u_char *bp = NULL; 592 static int buflen = 0; 593 static bool atbol = TRUE; /* at beginning of line */ 594 static int bt = MBT_SYNTAX; /* boundary type of next EOF */ 595 static u_char buf[128]; /* need not be a full line */ 596 597 if (buflen > 0) 598 { 599 buflen--; 600 return *bp++; 601 } 602 bp = buf; 603 buflen = 0; 604 c = getc(fp); 605 if (c == '\n') 606 { 607 /* might be part of a MIME boundary */ 608 *bp++ = c; 609 atbol = TRUE; 610 c = getc(fp); 611 if (c == '\n') 612 { 613 ungetc(c, fp); 614 return c; 615 } 616 } 617 if (c != EOF) 618 *bp++ = c; 619 else 620 bt = MBT_FINAL; 621 if (atbol && c == '-') 622 { 623 /* check for a message boundary */ 624 c = getc(fp); 625 if (c != '-') 626 { 627 if (c != EOF) 628 *bp++ = c; 629 else 630 bt = MBT_FINAL; 631 buflen = bp - buf - 1; 632 bp = buf; 633 return *bp++; 634 } 635 636 /* got "--", now check for rest of separator */ 637 *bp++ = '-'; 638 while (bp < &buf[sizeof buf - 2] && 639 (c = getc(fp)) != EOF && c != '\n') 640 { 641 *bp++ = c; 642 } 643 *bp = '\0'; 644 bt = mimeboundary(&buf[1], boundaries); 645 switch (bt) 646 { 647 case MBT_FINAL: 648 case MBT_INTERMED: 649 /* we have a message boundary */ 650 buflen = 0; 651 *btp = bt; 652 return EOF; 653 } 654 655 atbol = c == '\n'; 656 if (c != EOF) 657 *bp++ = c; 658 } 659 660 buflen = bp - buf - 1; 661 if (buflen < 0) 662 { 663 *btp = bt; 664 return EOF; 665 } 666 bp = buf; 667 return *bp++; 668 } 669 /* 670 ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF 671 ** 672 ** Parameters: 673 ** fp -- the input file. 674 ** boundaries -- the current MIME boundaries. 675 ** btp -- if the return value is EOF, *btp is set to 676 ** the type of the boundary. 677 ** 678 ** Returns: 679 ** The next character in the input stream. 680 */ 681 682 int 683 mime_getchar_crlf(fp, boundaries, btp) 684 register FILE *fp; 685 char **boundaries; 686 int *btp; 687 { 688 static bool sendlf = FALSE; 689 int c; 690 691 if (sendlf) 692 { 693 sendlf = FALSE; 694 return '\n'; 695 } 696 c = mime_getchar(fp, boundaries, btp); 697 if (c == '\n') 698 { 699 sendlf = TRUE; 700 return '\r'; 701 } 702 return c; 703 } 704 /* 705 ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type 706 ** 707 ** Parameters: 708 ** line -- the input line. 709 ** boundaries -- the set of currently pending boundaries. 710 ** 711 ** Returns: 712 ** MBT_NOTSEP -- if this is not a separator line 713 ** MBT_INTERMED -- if this is an intermediate separator 714 ** MBT_FINAL -- if this is a final boundary 715 ** MBT_SYNTAX -- if this is a boundary for the wrong 716 ** enclosure -- i.e., a syntax error. 717 */ 718 719 int 720 mimeboundary(line, boundaries) 721 register char *line; 722 char **boundaries; 723 { 724 int type; 725 int i; 726 int savec; 727 728 if (line[0] != '-' || line[1] != '-' || boundaries == NULL) 729 return MBT_NOTSEP; 730 i = strlen(line); 731 if (line[i - 1] == '\n') 732 i--; 733 if (tTd(43, 5)) 734 printf("mimeboundary: line=\"%.*s\"... ", i, line); 735 while (line[i - 1] == ' ' || line[i - 1] == '\t') 736 i--; 737 if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) 738 { 739 type = MBT_FINAL; 740 i -= 2; 741 } 742 else 743 type = MBT_INTERMED; 744 745 savec = line[i]; 746 line[i] = '\0'; 747 /* XXX should check for improper nesting here */ 748 if (isboundary(&line[2], boundaries) < 0) 749 type = MBT_NOTSEP; 750 line[i] = savec; 751 if (tTd(43, 5)) 752 printf("%s\n", MimeBoundaryNames[type]); 753 return type; 754 } 755 /* 756 ** DEFCHARSET -- return default character set for message 757 ** 758 ** The first choice for character set is for the mailer 759 ** corresponding to the envelope sender. If neither that 760 ** nor the global configuration file has a default character 761 ** set defined, return "unknown-8bit" as recommended by 762 ** RFC 1428 section 3. 763 ** 764 ** Parameters: 765 ** e -- the envelope for this message. 766 ** 767 ** Returns: 768 ** The default character set for that mailer. 769 */ 770 771 char * 772 defcharset(e) 773 register ENVELOPE *e; 774 { 775 if (e != NULL && e->e_from.q_mailer != NULL && 776 e->e_from.q_mailer->m_defcharset != NULL) 777 return e->e_from.q_mailer->m_defcharset; 778 if (DefaultCharSet != NULL) 779 return DefaultCharSet; 780 return "unknown-8bit"; 781 } 782 /* 783 ** ISBOUNDARY -- is a given string a currently valid boundary? 784 ** 785 ** Parameters: 786 ** line -- the current input line. 787 ** boundaries -- the list of valid boundaries. 788 ** 789 ** Returns: 790 ** The index number in boundaries if the line is found. 791 ** -1 -- otherwise. 792 ** 793 */ 794 795 int 796 isboundary(line, boundaries) 797 char *line; 798 char **boundaries; 799 { 800 register int i; 801 802 for (i = 0; boundaries[i] != NULL; i++) 803 { 804 if (strcmp(line, boundaries[i]) == 0) 805 return i; 806 } 807 return -1; 808 } 809