Return to http_head.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / contrib / libpdel / http |
1.1 misho 1: 2: /* 3: * Copyright (c) 2001-2002 Packet Design, LLC. 4: * All rights reserved. 5: * 6: * Subject to the following obligations and disclaimer of warranty, 7: * use and redistribution of this software, in source or object code 8: * forms, with or without modifications are expressly permitted by 9: * Packet Design; provided, however, that: 10: * 11: * (i) Any and all reproductions of the source or object code 12: * must include the copyright notice above and the following 13: * disclaimer of warranties; and 14: * (ii) No rights are granted, in any manner or form, to use 15: * Packet Design trademarks, including the mark "PACKET DESIGN" 16: * on advertising, endorsements, or otherwise except as such 17: * appears in the above copyright notice or in the software. 18: * 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF 36: * THE POSSIBILITY OF SUCH DAMAGE. 37: * 38: * Author: Archie Cobbs <archie@freebsd.org> 39: */ 40: 41: #include <sys/types.h> 42: #include <sys/queue.h> 43: 44: #include <netinet/in.h> 45: 46: #include <stdio.h> 47: #include <stdlib.h> 48: #include <stdarg.h> 49: #include <string.h> 50: #include <limits.h> 51: #include <pthread.h> 52: #include <errno.h> 53: #include <ctype.h> 54: 55: #include <openssl/ssl.h> 56: 57: #include "structs/structs.h" 58: #include "structs/type/array.h" 59: 60: #include "util/typed_mem.h" 61: #include "http/http_defs.h" 62: #include "http/http_server.h" 63: #include "http/http_internal.h" 64: 65: #define CR '\r' 66: #define LF '\n' 67: 68: #define MAX_STRING (64 * 1024) 69: 70: #define MEM_TYPE_HEAD "http_head" 71: #define MEM_TYPE_HDRS "http_head.hdrs" 72: #define MEM_TYPE_NAME "http_head.name" 73: #define MEM_TYPE_VALUE "http_head.value" 74: 75: #define issep(ch) (strchr("()<>@,;:\\\"/[]?={} \t", ch) != NULL) 76: 77: struct http_header { 78: char *name; 79: char *value; 80: }; 81: 1.1.1.2 ! misho 82: struct const_http_header { ! 83: const char *name; ! 84: char *value; ! 85: }; ! 86: 1.1 misho 87: struct http_head { 88: char *words[3]; /* first line stuff */ 1.1.1.2 ! misho 89: unsigned num_hdrs; /* number of headers */ 1.1 misho 90: struct http_header *hdrs; /* headers, sorted */ 91: }; 92: 93: /* 94: * Internal variables 95: */ 96: static const char *header_sort[] = { 97: HTTP_HEADER_DATE, 98: HTTP_HEADER_SERVER, 99: HTTP_HEADER_CONNECTION, 100: HTTP_HEADER_PROXY_CONNECTION, 101: HTTP_HEADER_CACHE_CONTROL, 102: HTTP_HEADER_PRAGMA, 103: HTTP_HEADER_LAST_MODIFIED, 104: HTTP_HEADER_WWW_AUTHENTICATE, 105: HTTP_HEADER_CONTENT_TYPE, 106: HTTP_HEADER_CONTENT_LENGTH, 107: HTTP_HEADER_CONTENT_ENCODING, 108: }; 109: #define NUM_HEADER_SORT (sizeof(header_sort) / sizeof(*header_sort)) 110: 111: /* 112: * Internal functions 113: */ 114: static int http_head_special(const char *name); 115: static int http_header_cmp(const void *v1, const void *v2); 116: static char *read_hval(FILE *fp); 117: static char *read_line(FILE *fp, const char *mtype); 118: static char *read_token(FILE *fp, int liberal, const char *mtype); 119: static void read_whitespace(FILE *fp); 120: static int addch(char **sp, int *slen, int ch, const char *mtype); 121: 122: /* 123: * Create new header object. 124: */ 125: struct http_head * 126: _http_head_new(void) 127: { 128: struct http_head *head; 129: 130: /* Create structure */ 131: if ((head = MALLOC(MEM_TYPE_HEAD, sizeof(*head))) == NULL) 132: return (NULL); 133: memset(head, 0, sizeof(*head)); 134: return (head); 135: } 136: 137: /* 138: * Free a header object. 139: */ 140: void 141: _http_head_free(struct http_head **headp) 142: { 143: struct http_head *const head = *headp; 1.1.1.2 ! misho 144: unsigned i; 1.1 misho 145: 146: if (head == NULL) 147: return; 148: for (i = 0; i < 3; i++) 149: FREE(MEM_TYPE_VALUE, head->words[i]); 150: for (i = 0; i < head->num_hdrs; i++) { 151: struct http_header *const hdr = &head->hdrs[i]; 152: 153: FREE(MEM_TYPE_NAME, hdr->name); 154: FREE(MEM_TYPE_VALUE, hdr->value); 155: } 156: FREE(MEM_TYPE_HDRS, head->hdrs); 157: FREE(MEM_TYPE_HEAD, head); 158: *headp = NULL; 159: } 160: 161: /* 162: * Copy headers 163: */ 164: struct http_head * 165: _http_head_copy(struct http_head *head0) 166: { 167: struct http_head *head; 1.1.1.2 ! misho 168: unsigned i; 1.1 misho 169: 170: /* Get new header struct */ 171: if ((head = _http_head_new()) == NULL) 172: goto fail; 173: 174: /* Copy first line words */ 175: for (i = 0; i < 3; i++) { 176: if (head0->words[i] == NULL) 177: continue; 178: if ((head->words[i] = STRDUP(MEM_TYPE_VALUE, 179: head0->words[i])) == NULL) 180: goto fail; 181: } 182: 183: /* Copy other headers */ 184: for (i = 0; i < head0->num_hdrs; i++) { 185: const char *name; 186: const char *value; 187: 188: if (_http_head_get_by_index(head0, i, &name, &value) == -1) 189: goto fail; 190: if (_http_head_set(head, 0, name, "%s", value) == -1) 191: goto fail; 192: } 193: 194: /* Done */ 195: return (head); 196: 197: fail: 198: _http_head_free(&head); 199: return (NULL); 200: } 201: 202: /* 203: * Get a header field value. 204: * 205: * For headers listed multiple times, this only gets the first instance. 206: */ 207: const char * 208: _http_head_get(struct http_head *head, const char *name) 209: { 1.1.1.2 ! misho 210: struct const_http_header key; 1.1 misho 211: struct http_header *hdr; 212: int i; 213: 214: /* First line stuff */ 215: if ((i = http_head_special(name)) != -1) 216: return (head->words[i]); 217: 218: /* Normal headers */ 1.1.1.2 ! misho 219: key.name = name; 1.1 misho 220: if ((hdr = bsearch(&key, head->hdrs, head->num_hdrs, 221: sizeof(*head->hdrs), http_header_cmp)) == NULL) { 222: errno = ENOENT; 223: return (NULL); 224: } 225: return (hdr->value); 226: } 227: 228: /* 229: * Get the number of headers 230: */ 231: int 232: _http_head_num_headers(struct http_head *head) 233: { 234: return (head->num_hdrs); 235: } 236: 237: /* 238: * Get a header by index. 239: */ 240: int 241: _http_head_get_by_index(struct http_head *head, u_int index, 242: const char **namep, const char **valuep) 243: { 244: if (index >= head->num_hdrs) { 245: errno = EINVAL; 246: return (-1); 247: } 248: if (namep != NULL) 249: *namep = head->hdrs[index].name; 250: if (valuep != NULL) 251: *valuep = head->hdrs[index].value; 252: return (0); 253: } 254: 255: /* 256: * Get the names of all headers. 257: */ 258: int 259: _http_head_get_headers(struct http_head *head, 260: const char **names, size_t max_names) 261: { 1.1.1.2 ! misho 262: unsigned i; 1.1 misho 263: 264: if (max_names > head->num_hdrs) 265: max_names = head->num_hdrs; 266: for (i = 0; i < max_names; i++) 267: names[i] = head->hdrs[i].name; 268: return (i); 269: } 270: 271: /* 272: * Set header value, in either append or replace mode. 273: */ 274: int 275: _http_head_set(struct http_head *head, int append, 276: const char *name, const char *valfmt, ...) 277: { 278: va_list args; 279: int ret; 280: 281: va_start(args, valfmt); 282: ret = _http_head_vset(head, append, name, valfmt, args); 283: va_end(args); 284: return (ret); 285: } 286: 287: /* 288: * Set header value, in either append or replace mode. 289: */ 290: int 291: _http_head_vset(struct http_head *head, int append, 292: const char *name, const char *valfmt, va_list args) 293: { 1.1.1.2 ! misho 294: struct const_http_header key; 1.1 misho 295: struct http_header *hdr; 296: char *value; 297: void *mem; 298: int i; 299: 300: /* Generate value */ 301: VASPRINTF(MEM_TYPE_VALUE, &value, valfmt, args); 302: if (value == NULL) 303: return (-1); 304: 305: /* First line stuff */ 306: if ((i = http_head_special(name)) != -1) { 307: FREE(MEM_TYPE_VALUE, head->words[i]); 308: head->words[i] = value; 309: return (0); 310: } 311: 312: /* If header doesn't already exist, add it (unless special) */ 1.1.1.2 ! misho 313: key.name = name; 1.1 misho 314: if (strcasecmp(name, HTTP_HEADER_SET_COOKIE) == 0 /* XXX blech */ 315: || (hdr = bsearch(&key, head->hdrs, head->num_hdrs, 316: sizeof(*head->hdrs), http_header_cmp)) == NULL) { 317: 318: /* Extend headers array */ 319: if ((mem = REALLOC(MEM_TYPE_HDRS, head->hdrs, 320: (head->num_hdrs + 1) * sizeof(*head->hdrs))) == NULL) { 321: FREE(MEM_TYPE_VALUE, value); 322: return (-1); 323: } 324: head->hdrs = mem; 325: hdr = &head->hdrs[head->num_hdrs]; 326: 327: /* Copy name */ 328: if ((hdr->name = STRDUP(MEM_TYPE_NAME, name)) == NULL) { 329: FREE(MEM_TYPE_VALUE, value); 330: return (-1); 331: } 332: 333: /* Add new header */ 334: hdr->value = value; 335: head->num_hdrs++; 336: 337: /* Keep array sorted */ 338: (void)mergesort(head->hdrs, head->num_hdrs, 339: sizeof(*head->hdrs), http_header_cmp); 340: return (0); 341: } 342: 343: /* Append or replace */ 344: if (append) { 345: const int plen = strlen(hdr->value); 346: 347: if ((mem = REALLOC(MEM_TYPE_VALUE, 348: hdr->value, plen + strlen(value) + 3)) == NULL) { 349: FREE(MEM_TYPE_VALUE, value); 350: return (-1); 351: } 352: hdr->value = mem; 353: sprintf(hdr->value + plen, ", %s", value); 354: FREE(MEM_TYPE_VALUE, value); 355: } else { 356: FREE(MEM_TYPE_VALUE, hdr->value); 357: hdr->value = value; 358: } 359: return (0); 360: } 361: 362: /* 363: * Remove a header. 364: */ 365: int 366: _http_head_remove(struct http_head *head, const char *name) 367: { 1.1.1.2 ! misho 368: struct const_http_header key; 1.1 misho 369: struct http_header *hdr; 370: int i; 371: 372: /* First line stuff */ 373: if ((i = http_head_special(name)) != -1) { 374: if (head->words[i] != NULL) { 375: FREE(MEM_TYPE_VALUE, head->words[i]); 376: head->words[i] = NULL; 377: return (1); 378: } 379: return (0); 380: } 381: 382: /* Does header exist? */ 1.1.1.2 ! misho 383: key.name = name; 1.1 misho 384: if ((hdr = bsearch(&key, head->hdrs, head->num_hdrs, 385: sizeof(*head->hdrs), http_header_cmp)) == NULL) 386: return (0); 387: 388: /* Remove header */ 389: i = hdr - head->hdrs; 390: FREE(MEM_TYPE_NAME, hdr->name); 391: FREE(MEM_TYPE_VALUE, hdr->value); 392: memmove(head->hdrs + i, head->hdrs + i + 1, 393: (--head->num_hdrs - i) * sizeof(*head->hdrs)); 394: return (1); 395: } 396: 397: /* 398: * Return index for one of the 'special' headers representing 399: * one of the three parts of the first line of the HTTP request 400: * or response. 401: */ 402: static int 403: http_head_special(const char *name) 404: { 405: if (name == HDR_REQUEST_METHOD) /* also HDR_REPLY_VERSION */ 406: return (0); 407: if (name == HDR_REQUEST_URI) /* also HDR_REPLY_STATUS */ 408: return (1); 409: if (name == HDR_REQUEST_VERSION) /* also HDR_REPLY_REASON */ 410: return (2); 411: return (-1); 412: } 413: 414: /* 415: * Compare two headers by name. 416: * 417: * XXX this could be faster 418: */ 419: static int 420: http_header_cmp(const void *v1, const void *v2) 421: { 422: const struct http_header *const hdrs[] = { v1, v2 }; 423: int sortval[2]; 1.1.1.2 ! misho 424: unsigned i, j; 1.1 misho 425: 426: /* Check assigned ordering list */ 427: for (i = 0; i < 2; i++) { 428: const char *hdr = hdrs[i]->name; 429: 430: sortval[i] = INT_MAX; 431: for (j = 0; j < NUM_HEADER_SORT; j++) { 432: if (strcasecmp(hdr, header_sort[j]) == 0) { 433: sortval[i] = j; 434: break; 435: } 436: } 437: } 438: if (sortval[0] == INT_MAX && sortval[1] == INT_MAX) 439: return (strcasecmp(hdrs[0]->name, hdrs[1]->name)); 440: return (sortval[0] - sortval[1]); 441: } 442: 443: /* 444: * Read an entire HTTP request or response header. 445: */ 446: int 447: _http_head_read(struct http_head *head, FILE *fp, int req) 448: { 449: int ch; 450: 451: /* Read any initial whitespace including CR, LF */ 452: while ((ch = getc(fp)) != EOF) { 453: if (!isspace(ch)) { 454: ungetc(ch, fp); 455: break; 456: } 457: } 458: 459: /* Read first word */ 460: FREE(MEM_TYPE_VALUE, head->words[0]); 461: if ((head->words[0] = read_token(fp, 1, MEM_TYPE_VALUE)) == NULL) 462: return (-1); 463: 464: /* Get whitespace */ 465: read_whitespace(fp); 466: 467: /* Read second word */ 468: FREE(MEM_TYPE_VALUE, head->words[1]); 469: if ((head->words[1] = read_token(fp, 1, MEM_TYPE_VALUE)) == NULL) 470: return (-1); 471: 472: /* Get whitespace */ 473: read_whitespace(fp); 474: 475: /* Read third and remaining words on the line including CR-LF */ 476: FREE(MEM_TYPE_VALUE, head->words[2]); 477: if ((head->words[2] = read_line(fp, MEM_TYPE_VALUE)) == NULL) 478: return (-1); 479: 480: /* Special format for HTTP 0.9 request (no protocol or headers) */ 481: if (req && *head->words[2] == '\0') { 482: FREE(MEM_TYPE_VALUE, head->words[2]); 483: if ((head->words[2] = STRDUP(MEM_TYPE_VALUE, 484: HTTP_PROTO_0_9)) == NULL) 485: return (-1); 486: return (0); 487: } 488: 489: /* Read headers */ 490: if (_http_head_read_headers(head, fp) == -1) 491: return (-1); 492: 493: /* Done */ 494: return (0); 495: } 496: 497: /* 498: * Read HTTP headers 499: */ 500: int 501: _http_head_read_headers(struct http_head *head, FILE *fp) 502: { 503: char *name; 504: char *value; 505: int ret; 506: int ch; 507: 508: while (1) { 509: 510: /* Check for CR-LF */ 511: if ((ch = getc(fp)) == EOF) { 512: if (ferror(fp)) 513: goto fail; 514: goto fail_invalid; 515: } 516: if (ch == CR) { 517: if ((ch = getc(fp)) != LF) { 518: if (ch == EOF && ferror(fp)) 519: goto fail; 520: goto fail_invalid; 521: } 522: return (0); 523: } 524: ungetc(ch, fp); 525: 526: /* Get header name */ 527: if ((name = read_token(fp, 0, MEM_TYPE_NAME)) == NULL) 528: goto fail; 529: 530: /* Get separator */ 531: if ((ch = getc(fp)) != ':') { 532: FREE(MEM_TYPE_NAME, name); 533: goto fail_invalid; 534: } 535: 536: /* Get whitespace */ 537: read_whitespace(fp); 538: 539: /* Get header value including final CR-LF */ 540: if ((value = read_hval(fp)) == NULL) { 541: FREE(MEM_TYPE_NAME, name); 542: goto fail; 543: } 544: 545: /* Append to header value */ 546: ret = _http_head_set(head, 1, name, "%s", value); 547: FREE(MEM_TYPE_NAME, name); 548: FREE(MEM_TYPE_VALUE, value); 549: if (ret == -1) 550: goto fail; 551: } 552: 553: fail_invalid: 554: errno = EINVAL; 555: fail: 556: return (-1); 557: } 558: 559: /* 560: * Write out an HTTP header. 561: */ 562: int 563: _http_head_write(struct http_head *head, FILE *fp) 564: { 1.1.1.2 ! misho 565: unsigned i; 1.1 misho 566: 567: for (i = 0; i < 3; i++) { 568: if (head->words[i] == NULL) { 569: errno = EINVAL; 570: return (-1); 571: } 572: } 573: fprintf(fp, "%s %s %s\r\n", 574: head->words[0], head->words[1], head->words[2]); 575: for (i = 0; i < head->num_hdrs; i++) { 576: struct http_header *const hdr = &head->hdrs[i]; 577: 578: fprintf(fp, "%s: %s\r\n", hdr->name, hdr->value); 579: } 580: fprintf(fp, "\r\n"); 581: #if 0 582: fflush(fp); 583: #endif 584: return (0); 585: } 586: 587: /* 588: * Figure out whether there is anything in the head or not. 589: */ 590: int 591: _http_head_has_anything(struct http_head *head) 592: { 593: return (head->words[0] != NULL); 594: } 595: 596: /* 597: * Determine if keep alive is requested. 598: */ 599: int 600: _http_head_want_keepalive(struct http_head *head) 601: { 602: const char *hval; 603: 604: return (((hval = _http_head_get(head, HTTP_HEADER_CONNECTION)) != NULL 605: || (hval = _http_head_get(head, 606: HTTP_HEADER_PROXY_CONNECTION)) != NULL) 607: && strcasecmp(hval, "Keep-Alive") == 0); 608: } 609: 610: /* 611: * Read an HTTP header value. 612: */ 613: static char * 614: read_hval(FILE *fp) 615: { 616: char *s = NULL; 617: int inquote = 0; 618: int bslash = 0; 619: int slen = 0; 620: int ch; 621: 622: /* Read characters */ 623: while (1) { 624: if ((ch = getc(fp)) == EOF) { 625: if (ferror(fp)) 626: goto fail; 627: goto fail_invalid; 628: } 629: if (bslash) { /* implies inquote */ 630: if (!addch(&s, &slen, ch, MEM_TYPE_VALUE)) 631: goto fail; 632: bslash = 0; 633: continue; 634: } 635: switch (ch) { 636: case '"': 637: inquote = !inquote; 638: break; 639: case '\\': 640: if (inquote) { 641: bslash = 1; 642: continue; 643: } 644: case CR: 645: if ((ch = getc(fp)) != LF) { /* read linefeed */ 646: if (ch == EOF && ferror(fp)) 647: goto fail; 648: goto fail_invalid; 649: } 650: if ((ch = getc(fp)) == EOF) { /* get next char */ 651: if (ferror(fp)) 652: goto fail; 653: goto fail_invalid; 654: } 655: if (ch == ' ' || ch == '\t') { /* line continuation */ 656: read_whitespace(fp); 657: ch = ' '; 658: break; 659: } 660: ungetc(ch, fp); 661: goto done; 662: default: 663: if (iscntrl(ch) && ch != ' ' && ch != '\t') 664: goto fail_invalid; 665: break; 666: } 667: if (!addch(&s, &slen, ch, MEM_TYPE_VALUE)) 668: goto fail; 669: } 670: 671: done: 672: /* Terminate and return string */ 673: if (!addch(&s, &slen, '\0', MEM_TYPE_VALUE)) 674: goto fail; 675: return (s); 676: 677: fail_invalid: 678: errno = EINVAL; 679: fail: 680: FREE(MEM_TYPE_VALUE, s); 681: return (NULL); 682: } 683: 684: /* 685: * Read up through end of line and return value, not including CR-LF. 686: */ 687: static char * 688: read_line(FILE *fp, const char *mtype) 689: { 690: char *s = NULL; 691: int slen = 0; 692: int ch; 693: 694: /* Read characters */ 695: while (1) { 696: if ((ch = getc(fp)) == EOF) { 697: if (ferror(fp)) 698: goto fail; 699: goto fail_invalid; 700: } 701: if (ch == CR) { 702: if ((ch = getc(fp)) != LF) { 703: if (ch == EOF && ferror(fp)) 704: goto fail; 705: goto fail_invalid; 706: } 707: goto done; 708: } 709: if (ch == LF) /* handle broken clients */ 710: break; 711: if (!addch(&s, &slen, ch, mtype)) 712: goto fail; 713: } 714: 715: done: 716: /* Terminate and return string */ 717: if (!addch(&s, &slen, '\0', mtype)) 718: goto fail; 719: return (s); 720: 721: fail_invalid: 722: errno = EINVAL; 723: fail: 724: FREE(mtype, s); 725: return (NULL); 726: } 727: 728: /* 729: * Read an HTTP header token. 730: */ 731: static char * 732: read_token(FILE *fp, int liberal, const char *mtype) 733: { 734: char *s = NULL; 735: int slen = 0; 736: int ch; 737: 738: /* Read characters */ 739: while (1) { 740: if ((ch = getc(fp)) == EOF) { 741: if (ferror(fp)) 742: goto fail; 743: goto fail_invalid; 744: } 745: if (!isprint(ch) 746: || (ch == ' ' || ch == '\t') 747: || (!liberal && issep(ch))) { 748: ungetc(ch, fp); 749: break; 750: } 751: if (!addch(&s, &slen, ch, mtype)) 752: goto fail; 753: } 754: 755: /* Terminate and return string */ 756: if (!addch(&s, &slen, '\0', mtype)) 757: goto fail; 758: return (s); 759: 760: fail_invalid: 761: errno = EINVAL; 762: fail: 763: FREE(mtype, s); 764: return (NULL); 765: } 766: 767: /* 768: * Read any amount of whitespace. 769: */ 770: static void 771: read_whitespace(FILE *fp) 772: { 773: int ch; 774: 775: while (1) { 776: if ((ch = getc(fp)) == EOF) 777: return; 778: if (ch != ' ' && ch != '\t') { 779: ungetc(ch, fp); 780: break; 781: } 782: } 783: } 784: 785: /* 786: * Add a character to a malloc'd string. 787: */ 788: static int 789: addch(char **sp, int *slen, int ch, const char *mtype) 790: { 791: void *mem; 792: 793: if (*slen >= MAX_STRING) { 794: errno = E2BIG; 795: return (0); 796: } 797: if (*slen % 128 == 0) { 798: if ((mem = REALLOC(mtype, *sp, *slen + 128)) == NULL) 799: return (0); 800: *sp = mem; 801: } 802: (*sp)[(*slen)++] = ch; 803: return (1); 804: } 805: