xref: /illumos-gate/usr/src/cmd/awk/lib.c (revision 8ef4c21a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <errno.h>
33 #include "awk.h"
34 #include "y.tab.h"
35 
36 uchar	*record;
37 size_t	record_size;
38 
39 int	donefld;	/* 1 = implies rec broken into fields */
40 int	donerec;	/* 1 = record is valid (no flds have changed) */
41 
42 static struct fldtab_chunk {
43 	struct fldtab_chunk	*next;
44 	Cell			fields[FLD_INCR];
45 } *fldtab_head, *fldtab_tail;
46 
47 static	size_t	fldtab_maxidx;
48 
49 static FILE	*infile	= NULL;
50 static uchar	*file	= (uchar*) "";
51 static uchar	*fields;
52 static size_t	fields_size = LINE_INCR;
53 
54 static int	maxfld	= 0;	/* last used field */
55 static int	argno	= 1;	/* current input argument number */
56 
57 static	uchar	*getargv(int);
58 static	void	cleanfld(int, int);
59 static	int	refldbld(uchar *, uchar *);
60 static	void	bcheck2(int, int, int);
61 static	void	eprint(void);
62 static	void	bclass(int);
63 
64 static void
65 initgetrec(void)
66 {
67 	int i;
68 	uchar *p;
69 
70 	for (i = 1; i < *ARGC; i++) {
71 		if (!isclvar(p = getargv(i)))	/* find 1st real filename */
72 			return;
73 		setclvar(p);	/* a commandline assignment before filename */
74 		argno++;
75 	}
76 	infile = stdin;		/* no filenames, so use stdin */
77 	/* *FILENAME = file = (uchar*) "-"; */
78 }
79 
80 int
81 getrec(uchar **bufp, size_t *bufsizep)
82 {
83 	int c;
84 	static int firsttime = 1;
85 	uchar_t	*buf, *nbuf;
86 	size_t	len;
87 
88 	if (firsttime) {
89 		firsttime = 0;
90 		initgetrec();
91 	}
92 	dprintf(("RS=<%s>, FS=<%s>, ARGC=%f, FILENAME=%s\n",
93 	    *RS, *FS, *ARGC, *FILENAME));
94 	donefld = 0;
95 	donerec = 1;
96 	while (argno < *ARGC || infile == stdin) {
97 		dprintf(("argno=%d, file=|%s|\n", argno, file));
98 		if (infile == NULL) {	/* have to open a new file */
99 			file = getargv(argno);
100 			if (*file == '\0') {	/* it's been zapped */
101 				argno++;
102 				continue;
103 			}
104 			if (isclvar(file)) {	/* a var=value arg */
105 				setclvar(file);
106 				argno++;
107 				continue;
108 			}
109 			*FILENAME = file;
110 			dprintf(("opening file %s\n", file));
111 			if (*file == '-' && *(file+1) == '\0')
112 				infile = stdin;
113 			else if ((infile = fopen((char *)file, "r")) == NULL)
114 				ERROR "can't open file %s", file FATAL;
115 			(void) setfval(fnrloc, 0.0);
116 		}
117 		c = readrec(&nbuf, &len, infile);
118 		expand_buf(bufp, bufsizep, len);
119 		buf = *bufp;
120 		(void) memcpy(buf, nbuf, len);
121 		buf[len] = '\0';
122 		free(nbuf);
123 
124 		if (c != 0 || buf[0] != '\0') {	/* normal record */
125 			if (bufp == &record) {
126 				if (!(recloc->tval & DONTFREE))
127 					xfree(recloc->sval);
128 				recloc->sval = record;
129 				recloc->tval = REC | STR | DONTFREE;
130 				if (is_number(recloc->sval)) {
131 					recloc->fval =
132 					    atof((const char *)recloc->sval);
133 					recloc->tval |= NUM;
134 				}
135 			}
136 			(void) setfval(nrloc, nrloc->fval+1);
137 			(void) setfval(fnrloc, fnrloc->fval+1);
138 			return (1);
139 		}
140 		/* EOF arrived on this file; set up next */
141 		if (infile != stdin)
142 			(void) fclose(infile);
143 		infile = NULL;
144 		argno++;
145 	}
146 	return (0);	/* true end of file */
147 }
148 
149 int
150 readrec(uchar **bufp, size_t *sizep, FILE *inf)	/* read one record into buf */
151 {
152 	int sep, c;
153 	uchar	*buf;
154 	int	count;
155 	size_t	bufsize;
156 
157 	init_buf(&buf, &bufsize, LINE_INCR);
158 	if ((sep = **RS) == 0) {
159 		sep = '\n';
160 		/* skip leading \n's */
161 		while ((c = getc(inf)) == '\n' && c != EOF)
162 			;
163 		if (c != EOF)
164 			(void) ungetc(c, inf);
165 	}
166 	count = 0;
167 	for (;;) {
168 		while ((c = getc(inf)) != sep && c != EOF) {
169 			expand_buf(&buf, &bufsize, count);
170 			buf[count++] = c;
171 		}
172 		if (**RS == sep || c == EOF)
173 			break;
174 		if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
175 			break;
176 		expand_buf(&buf, &bufsize, count + 1);
177 		buf[count++] = '\n';
178 		buf[count++] = c;
179 	}
180 	buf[count] = '\0';
181 	dprintf(("readrec saw <%s>, returns %d\n",
182 	    buf, c == EOF && count == 0 ? 0 : 1));
183 	*bufp = buf;
184 	*sizep = count;
185 	return (c == EOF && count == 0 ? 0 : 1);
186 }
187 
188 /* get ARGV[n] */
189 static uchar *
190 getargv(int n)
191 {
192 	Cell *x;
193 	uchar *s, temp[11];
194 	extern Array *ARGVtab;
195 
196 	(void) sprintf((char *)temp, "%d", n);
197 	x = setsymtab(temp, (uchar *)"", 0.0, STR, ARGVtab);
198 	s = getsval(x);
199 	dprintf(("getargv(%d) returns |%s|\n", n, s));
200 	return (s);
201 }
202 
203 void
204 setclvar(uchar *s)	/* set var=value from s */
205 {
206 	uchar *p;
207 	Cell *q;
208 
209 	for (p = s; *p != '='; p++)
210 		;
211 	*p++ = 0;
212 	p = qstring(p, '\0');
213 	q = setsymtab(s, p, 0.0, STR, symtab);
214 	(void) setsval(q, p);
215 	if (is_number(q->sval)) {
216 		q->fval = atof((const char *)q->sval);
217 		q->tval |= NUM;
218 	}
219 	dprintf(("command line set %s to |%s|\n", s, p));
220 	free(p);
221 }
222 
223 void
224 fldbld(void)
225 {
226 	uchar *r, *fr, sep;
227 	Cell *p;
228 	int i;
229 	size_t	len;
230 
231 	if (donefld)
232 		return;
233 	if (!(recloc->tval & STR))
234 		(void) getsval(recloc);
235 	r = recloc->sval;	/* was record! */
236 
237 	/* make sure fields is always allocated */
238 	adjust_buf(&fields, fields_size);
239 
240 	/*
241 	 * make sure fields has enough size. We don't expand the buffer
242 	 * in the middle of the loop, since p->sval has already pointed
243 	 * the address in the fields.
244 	 */
245 	len = strlen((char *)r) + 1;
246 	expand_buf(&fields, &fields_size, len);
247 	fr = fields;
248 
249 	i = 0;	/* number of fields accumulated here */
250 	if (strlen((char *)*FS) > 1) {	/* it's a regular expression */
251 		i = refldbld(r, *FS);
252 	} else if ((sep = **FS) == ' ') {
253 		for (i = 0; ; ) {
254 			while (*r == ' ' || *r == '\t' || *r == '\n')
255 				r++;
256 			if (*r == 0)
257 				break;
258 			i++;
259 			p = getfld(i);
260 			if (!(p->tval & DONTFREE))
261 				xfree(p->sval);
262 			p->sval = fr;
263 			p->tval = FLD | STR | DONTFREE;
264 			do
265 				*fr++ = *r++;
266 			while (*r != ' ' && *r != '\t' && *r != '\n' &&
267 			    *r != '\0')
268 				;
269 			*fr++ = 0;
270 		}
271 		*fr = 0;
272 	} else if (*r != 0) {	/* if 0, it's a null field */
273 		for (;;) {
274 			i++;
275 			p = getfld(i);
276 			if (!(p->tval & DONTFREE))
277 				xfree(p->sval);
278 			p->sval = fr;
279 			p->tval = FLD | STR | DONTFREE;
280 			/* \n always a separator */
281 			while (*r != sep && *r != '\n' && *r != '\0')
282 				*fr++ = *r++;
283 			*fr++ = 0;
284 			if (*r++ == 0)
285 				break;
286 		}
287 		*fr = 0;
288 	}
289 	/* clean out junk from previous record */
290 	cleanfld(i, maxfld);
291 	maxfld = i;
292 	donefld = 1;
293 	for (i = 1; i <= maxfld; i++) {
294 		p = getfld(i);
295 		if (is_number(p->sval)) {
296 			p->fval = atof((const char *)p->sval);
297 			p->tval |= NUM;
298 		}
299 	}
300 
301 	(void) setfval(nfloc, (Awkfloat) maxfld);
302 	if (dbg) {
303 		for (i = 0; i <= maxfld; i++) {
304 			p = getfld(i);
305 			(void) printf("field %d: |%s|\n", i, p->sval);
306 		}
307 	}
308 }
309 
310 static void
311 cleanfld(int n1, int n2)	/* clean out fields n1..n2 inclusive */
312 {
313 	static uchar *nullstat = (uchar *) "";
314 	Cell *p;
315 	int	i;
316 
317 	for (i = n2; i > n1; i--) {
318 		p = getfld(i);
319 		if (!(p->tval & DONTFREE))
320 			xfree(p->sval);
321 		p->tval = FLD | STR | DONTFREE;
322 		p->sval = nullstat;
323 	}
324 }
325 
326 void
327 newfld(int n)	/* add field n (after end) */
328 {
329 	if (n < 0)
330 		ERROR "accessing invalid field", record FATAL;
331 	(void) getfld(n);
332 	cleanfld(maxfld, n);
333 	maxfld = n;
334 	(void) setfval(nfloc, (Awkfloat) n);
335 }
336 
337 /*
338  * allocate field table. We don't reallocate the table since there
339  * might be somewhere recording the address of the table.
340  */
341 static void
342 morefld(void)
343 {
344 	int	i;
345 	struct fldtab_chunk *fldcp;
346 	Cell	*newfld;
347 
348 	if ((fldcp = calloc(sizeof (struct fldtab_chunk), 1)) == NULL)
349 		ERROR "out of space in morefld" FATAL;
350 
351 	newfld = &fldcp->fields[0];
352 	for (i = 0; i < FLD_INCR; i++) {
353 		newfld[i].ctype = OCELL;
354 		newfld[i].csub = CFLD;
355 		newfld[i].nval = NULL;
356 		newfld[i].sval = (uchar *)"";
357 		newfld[i].fval = 0.0;
358 		newfld[i].tval = FLD|STR|DONTFREE;
359 		newfld[i].cnext = NULL;
360 	}
361 	/*
362 	 * link this field chunk
363 	 */
364 	if (fldtab_head == NULL)
365 		fldtab_head = fldcp;
366 	else
367 		fldtab_tail->next = fldcp;
368 	fldtab_tail = fldcp;
369 	fldcp->next = NULL;
370 
371 	fldtab_maxidx += FLD_INCR;
372 }
373 
374 Cell *
375 getfld(int idx)
376 {
377 	struct fldtab_chunk *fldcp;
378 	int	cbase;
379 
380 	if (idx < 0)
381 		ERROR "trying to access field %d", idx FATAL;
382 	while (idx >= fldtab_maxidx)
383 		morefld();
384 	cbase = 0;
385 	for (fldcp = fldtab_head; fldcp != NULL; fldcp = fldcp->next) {
386 		if (idx < (cbase + FLD_INCR))
387 			return (&fldcp->fields[idx - cbase]);
388 		cbase += FLD_INCR;
389 	}
390 	/* should never happen */
391 	ERROR "trying to access invalid field %d", idx FATAL;
392 	return (NULL);
393 }
394 
395 int
396 fldidx(Cell *vp)
397 {
398 	struct fldtab_chunk *fldcp;
399 	Cell	*tbl;
400 	int	cbase;
401 
402 	cbase = 0;
403 	for (fldcp = fldtab_head; fldcp != NULL; fldcp = fldcp->next) {
404 		tbl = &fldcp->fields[0];
405 		if (vp >= tbl && vp < (tbl + FLD_INCR))
406 			return (cbase + (vp - tbl));
407 		cbase += FLD_INCR;
408 	}
409 	/* should never happen */
410 	ERROR "trying to access unknown field" FATAL;
411 	return (0);
412 }
413 
414 static int
415 refldbld(uchar *rec, uchar *fs)	/* build fields from reg expr in FS */
416 {
417 	uchar *fr;
418 	int i, tempstat;
419 	fa *pfa;
420 	Cell	*p;
421 	size_t	len;
422 
423 	/* make sure fields is allocated */
424 	adjust_buf(&fields, fields_size);
425 	fr = fields;
426 	*fr = '\0';
427 	if (*rec == '\0')
428 		return (0);
429 
430 	len = strlen((char *)rec) + 1;
431 	expand_buf(&fields, &fields_size, len);
432 	fr = fields;
433 
434 	pfa = makedfa(fs, 1);
435 	dprintf(("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs));
436 	tempstat = pfa->initstat;
437 	for (i = 1; ; i++) {
438 		p = getfld(i);
439 		if (!(p->tval & DONTFREE))
440 			xfree(p->sval);
441 		p->tval = FLD | STR | DONTFREE;
442 		p->sval = fr;
443 		dprintf(("refldbld: i=%d\n", i));
444 		if (nematch(pfa, rec)) {
445 			pfa->initstat = 2;
446 			dprintf(("match %s (%d chars)\n", patbeg, patlen));
447 			(void) strncpy((char *)fr, (char *)rec, patbeg-rec);
448 			fr += patbeg - rec + 1;
449 			*(fr-1) = '\0';
450 			rec = patbeg + patlen;
451 		} else {
452 			dprintf(("no match %s\n", rec));
453 			(void) strcpy((char *)fr, (char *)rec);
454 			pfa->initstat = tempstat;
455 			break;
456 		}
457 	}
458 	return (i);
459 }
460 
461 void
462 recbld(void)
463 {
464 	int i;
465 	uchar *p;
466 	size_t cnt, len, olen;
467 
468 	if (donerec == 1)
469 		return;
470 	cnt = 0;
471 	olen = strlen((char *)*OFS);
472 	for (i = 1; i <= *NF; i++) {
473 		p = getsval(getfld(i));
474 		len = strlen((char *)p);
475 		expand_buf(&record, &record_size, cnt + len + olen);
476 		(void) memcpy(&record[cnt], p, len);
477 		cnt += len;
478 		if (i < *NF) {
479 			(void) memcpy(&record[cnt], *OFS, olen);
480 			cnt += olen;
481 		}
482 	}
483 	record[cnt] = '\0';
484 	dprintf(("in recbld FS=%o, recloc=%p\n", **FS, (void *)recloc));
485 	if (!(recloc->tval & DONTFREE))
486 		xfree(recloc->sval);
487 	recloc->tval = REC | STR | DONTFREE;
488 	recloc->sval = record;
489 	dprintf(("in recbld FS=%o, recloc=%p\n", **FS, (void *)recloc));
490 	dprintf(("recbld = |%s|\n", record));
491 	donerec = 1;
492 }
493 
494 Cell *
495 fieldadr(int n)
496 {
497 	if (n < 0)
498 		ERROR "trying to access field %d", n FATAL;
499 	return (getfld(n));
500 }
501 
502 int	errorflag	= 0;
503 char	errbuf[200];
504 
505 void
506 yyerror(char *s)
507 {
508 	extern uchar *cmdname, *curfname;
509 	static int been_here = 0;
510 
511 	if (been_here++ > 2)
512 		return;
513 	(void) fprintf(stderr, "%s: %s", cmdname, s);
514 	(void) fprintf(stderr, gettext(" at source line %lld"), lineno);
515 	if (curfname != NULL)
516 		(void) fprintf(stderr, gettext(" in function %s"), curfname);
517 	(void) fprintf(stderr, "\n");
518 	errorflag = 2;
519 	eprint();
520 }
521 
522 /*ARGSUSED*/
523 void
524 fpecatch(int sig)
525 {
526 	ERROR "floating point exception" FATAL;
527 }
528 
529 extern int bracecnt, brackcnt, parencnt;
530 
531 void
532 bracecheck(void)
533 {
534 	int c;
535 	static int beenhere = 0;
536 
537 	if (beenhere++)
538 		return;
539 	while ((c = input()) != EOF && c != '\0')
540 		bclass(c);
541 	bcheck2(bracecnt, '{', '}');
542 	bcheck2(brackcnt, '[', ']');
543 	bcheck2(parencnt, '(', ')');
544 }
545 
546 /*ARGSUSED*/
547 static void
548 bcheck2(int n, int c1, int c2)
549 {
550 	if (n == 1)
551 		(void) fprintf(stderr, gettext("\tmissing %c\n"), c2);
552 	else if (n > 1)
553 		(void) fprintf(stderr, gettext("\t%d missing %c's\n"), n, c2);
554 	else if (n == -1)
555 		(void) fprintf(stderr, gettext("\textra %c\n"), c2);
556 	else if (n < -1)
557 		(void) fprintf(stderr, gettext("\t%d extra %c's\n"), -n, c2);
558 }
559 
560 void
561 error(int f, char *s)
562 {
563 	extern Node *curnode;
564 	extern uchar *cmdname;
565 
566 	(void) fflush(stdout);
567 	(void) fprintf(stderr, "%s: ", cmdname);
568 	(void) fprintf(stderr, "%s", s);
569 	(void) fprintf(stderr, "\n");
570 	if (compile_time != 2 && NR && *NR > 0) {
571 		(void) fprintf(stderr,
572 		    gettext(" input record number %g"), *FNR);
573 		if (strcmp((char *)*FILENAME, "-") != 0)
574 			(void) fprintf(stderr, gettext(", file %s"), *FILENAME);
575 		(void) fprintf(stderr, "\n");
576 	}
577 	if (compile_time != 2 && curnode)
578 		(void) fprintf(stderr, gettext(" source line number %lld\n"),
579 		    curnode->lineno);
580 	else if (compile_time != 2 && lineno) {
581 		(void) fprintf(stderr,
582 		    gettext(" source line number %lld\n"), lineno);
583 	}
584 	eprint();
585 	if (f) {
586 		if (dbg)
587 			abort();
588 		exit(2);
589 	}
590 }
591 
592 static void
593 eprint(void)	/* try to print context around error */
594 {
595 	uchar *p, *q;
596 	int c;
597 	static int been_here = 0;
598 	extern uchar ebuf[300], *ep;
599 
600 	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
601 		return;
602 	p = ep - 1;
603 	if (p > ebuf && *p == '\n')
604 		p--;
605 	for (; p > ebuf && *p != '\n' && *p != '\0'; p--)
606 		;
607 	while (*p == '\n')
608 		p++;
609 	(void) fprintf(stderr, gettext(" context is\n\t"));
610 	for (q = ep-1; q >= p && *q != ' ' && *q != '\t' && *q != '\n'; q--)
611 		;
612 	for (; p < q; p++)
613 		if (*p)
614 			(void) putc(*p, stderr);
615 	(void) fprintf(stderr, " >>> ");
616 	for (; p < ep; p++)
617 		if (*p)
618 			(void) putc(*p, stderr);
619 	(void) fprintf(stderr, " <<< ");
620 	if (*ep)
621 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
622 			(void) putc(c, stderr);
623 			bclass(c);
624 		}
625 	(void) putc('\n', stderr);
626 	ep = ebuf;
627 }
628 
629 static void
630 bclass(int c)
631 {
632 	switch (c) {
633 	case '{': bracecnt++; break;
634 	case '}': bracecnt--; break;
635 	case '[': brackcnt++; break;
636 	case ']': brackcnt--; break;
637 	case '(': parencnt++; break;
638 	case ')': parencnt--; break;
639 	}
640 }
641 
642 double
643 errcheck(double x, char *s)
644 {
645 	extern int errno;
646 
647 	if (errno == EDOM) {
648 		errno = 0;
649 		ERROR "%s argument out of domain", s WARNING;
650 		x = 1;
651 	} else if (errno == ERANGE) {
652 		errno = 0;
653 		ERROR "%s result out of range", s WARNING;
654 		x = 1;
655 	}
656 	return (x);
657 }
658 
659 void
660 PUTS(uchar *s)
661 {
662 	dprintf(("%s\n", s));
663 }
664 
665 int
666 isclvar(uchar *s)	/* is s of form var=something? */
667 {
668 	if (s != NULL) {
669 
670 		/* Must begin with an underscore or alphabetic character */
671 		if (isalpha(*s) || (*s == '_')) {
672 
673 			for (s++; *s; s++) {
674 				/*
675 				 * followed by a sequence of underscores,
676 				 * digits, and alphabetics
677 				 */
678 				if (!(isalnum(*s) || *s == '_')) {
679 					break;
680 				}
681 			}
682 			return (*s == '=' && *(s + 1) != '=');
683 		}
684 	}
685 
686 	return (0);
687 }
688 
689 #define	MAXEXPON	38	/* maximum exponent for fp number */
690 
691 int
692 is_number(uchar *s)
693 {
694 	int d1, d2;
695 	int point;
696 	uchar *es;
697 	extern char	radixpoint;
698 
699 	d1 = d2 = point = 0;
700 	while (*s == ' ' || *s == '\t' || *s == '\n')
701 		s++;
702 	if (*s == '\0')
703 		return (0);	/* empty stuff isn't number */
704 	if (*s == '+' || *s == '-')
705 		s++;
706 	if (!isdigit(*s) && *s != radixpoint)
707 		return (0);
708 	if (isdigit(*s)) {
709 		do {
710 			d1++;
711 			s++;
712 		} while (isdigit(*s));
713 	}
714 	if (d1 >= MAXEXPON)
715 		return (0);	/* too many digits to convert */
716 	if (*s == radixpoint) {
717 		point++;
718 		s++;
719 	}
720 	if (isdigit(*s)) {
721 		d2++;
722 		do {
723 			s++;
724 		} while (isdigit(*s));
725 	}
726 	if (!(d1 || point && d2))
727 		return (0);
728 	if (*s == 'e' || *s == 'E') {
729 		s++;
730 		if (*s == '+' || *s == '-')
731 			s++;
732 		if (!isdigit(*s))
733 			return (0);
734 		es = s;
735 		do {
736 			s++;
737 		} while (isdigit(*s));
738 		if (s - es > 2) {
739 			return (0);
740 		} else if (s - es == 2 &&
741 		    (int)(10 * (*es-'0') + *(es+1)-'0') >= MAXEXPON) {
742 			return (0);
743 		}
744 	}
745 	while (*s == ' ' || *s == '\t' || *s == '\n')
746 		s++;
747 	if (*s == '\0')
748 		return (1);
749 	else
750 		return (0);
751 }
752 
753 void
754 init_buf(uchar **optr, size_t *sizep, size_t amt)
755 {
756 	uchar	*nptr = NULL;
757 
758 	if ((nptr = malloc(amt)) == NULL)
759 		ERROR "out of space in init_buf" FATAL;
760 	/* initial buffer should have NULL terminated */
761 	*nptr = '\0';
762 	if (sizep != NULL)
763 		*sizep = amt;
764 	*optr = nptr;
765 }
766 
767 void
768 r_expand_buf(uchar **optr, size_t *sizep, size_t req)
769 {
770 	uchar	*nptr;
771 	size_t	amt, size = *sizep;
772 
773 	if (size != 0 && req < (size - 1))
774 		return;
775 	amt = req + 1 - size;
776 	amt = (amt / LINE_INCR + 1) * LINE_INCR;
777 
778 	if ((nptr = realloc(*optr, size + amt)) == NULL)
779 		ERROR "out of space in expand_buf" FATAL;
780 	/* initial buffer should have NULL terminated */
781 	if (size == 0)
782 		*nptr = '\0';
783 	*sizep += amt;
784 	*optr = nptr;
785 }
786 
787 void
788 adjust_buf(uchar **optr, size_t size)
789 {
790 	uchar	*nptr;
791 
792 	if ((nptr = realloc(*optr, size)) == NULL)
793 		ERROR "out of space in adjust_buf" FATAL;
794 	*optr = nptr;
795 }
796