Annotation of embedaddon/mpd/src/contrib/libpdel/http/http_head.c, revision 1.1.1.2
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:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>