1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/tc.str.c,v 3.42 2012/01/10 21:34:31 christos Exp $ */
259243Sobrien/*
359243Sobrien * tc.str.c: Short string package
459243Sobrien * 	     This has been a lesson of how to write buggy code!
559243Sobrien */
659243Sobrien/*-
759243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
859243Sobrien * All rights reserved.
959243Sobrien *
1059243Sobrien * Redistribution and use in source and binary forms, with or without
1159243Sobrien * modification, are permitted provided that the following conditions
1259243Sobrien * are met:
1359243Sobrien * 1. Redistributions of source code must retain the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer.
1559243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1659243Sobrien *    notice, this list of conditions and the following disclaimer in the
1759243Sobrien *    documentation and/or other materials provided with the distribution.
18100616Smp * 3. Neither the name of the University nor the names of its contributors
1959243Sobrien *    may be used to endorse or promote products derived from this software
2059243Sobrien *    without specific prior written permission.
2159243Sobrien *
2259243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2359243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2459243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2559243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2659243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2759243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2859243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2959243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3059243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3159243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3259243Sobrien * SUCH DAMAGE.
3359243Sobrien */
3459243Sobrien#include "sh.h"
3559243Sobrien
36232633Smp#include <assert.h>
37145479Smp#include <limits.h>
3859243Sobrien
39232633SmpRCSID("$tcsh: tc.str.c,v 3.42 2012/01/10 21:34:31 christos Exp $")
40145479Smp
4159243Sobrien#define MALLOC_INCR	128
42145479Smp#ifdef WIDE_STRINGS
43145479Smp#define MALLOC_SURPLUS	MB_LEN_MAX /* Space for one multibyte character */
44145479Smp#else
45145479Smp#define MALLOC_SURPLUS	0
46145479Smp#endif
4759243Sobrien
48145479Smp#ifdef WIDE_STRINGS
49145479Smpsize_t
50232633Smpone_mbtowc(Char *pwc, const char *s, size_t n)
51145479Smp{
52145479Smp    int len;
53145479Smp
54145479Smp    len = rt_mbtowc(pwc, s, n);
55145479Smp    if (len == -1) {
56195609Smp        reset_mbtowc();
57145479Smp	*pwc = (unsigned char)*s | INVALID_BYTE;
58145479Smp    }
59145479Smp    if (len <= 0)
60145479Smp	len = 1;
61145479Smp    return len;
62145479Smp}
63145479Smp
64145479Smpsize_t
65232633Smpone_wctomb(char *s, Char wchar)
66145479Smp{
67145479Smp    int len;
68145479Smp
69145479Smp    if (wchar & INVALID_BYTE) {
70145479Smp	s[0] = wchar & 0xFF;
71145479Smp	len = 1;
72145479Smp    } else {
73232633Smp#ifdef UTF16_STRINGS
74232633Smp	if (wchar >= 0x10000) {
75232633Smp	    /* UTF-16 systems can't handle these values directly in calls to
76232633Smp	       wctomb.  Convert value to UTF-16 surrogate and call wcstombs to
77232633Smp	       convert the "string" to the correct multibyte representation,
78232633Smp	       if any. */
79232633Smp	    wchar_t ws[3];
80232633Smp	    wchar -= 0x10000;
81232633Smp	    ws[0] = 0xd800 | (wchar >> 10);
82232633Smp	    ws[1] = 0xdc00 | (wchar & 0x3ff);
83232633Smp	    ws[2] = 0;
84232633Smp	    /* The return value of wcstombs excludes the trailing 0, so len is
85232633Smp	       the correct number of multibytes for the Unicode char. */
86232633Smp	    len = wcstombs (s, ws, MB_CUR_MAX + 1);
87232633Smp	} else
88232633Smp#endif
89232633Smp	len = wctomb(s, (wchar_t) wchar);
90145479Smp	if (len == -1)
91145479Smp	    s[0] = wchar;
92145479Smp	if (len <= 0)
93145479Smp	    len = 1;
94145479Smp    }
95145479Smp    return len;
96145479Smp}
97167465Smp
98145479Smpint
99232633Smprt_mbtowc(Char *pwc, const char *s, size_t n)
100145479Smp{
101145479Smp    int ret;
102145479Smp    char back[MB_LEN_MAX];
103232633Smp    wchar_t tmp;
104232633Smp#if defined(UTF16_STRINGS) && defined(HAVE_MBRTOWC)
105232633Smp# if defined(AUTOSET_KANJI)
106232633Smp    static mbstate_t mb_zero, mb;
107232633Smp    /*
108232633Smp     * Workaround the Shift-JIS endcoding that translates unshifted 7 bit ASCII!
109232633Smp     */
110232633Smp    if (!adrof(STRnokanji) && n && pwc && s && (*s == '\\' || *s == '~') &&
111232633Smp	!memcmp(&mb, &mb_zero, sizeof(mb)))
112232633Smp    {
113232633Smp	*pwc = *s;
114232633Smp	return 1;
115232633Smp    }
116232633Smp# else
117232633Smp    mbstate_t mb;
118232633Smp# endif
119145479Smp
120232633Smp    memset (&mb, 0, sizeof mb);
121232633Smp    ret = mbrtowc(&tmp, s, n, &mb);
122232633Smp#else
123232633Smp    ret = mbtowc(&tmp, s, n);
124232633Smp#endif
125232633Smp    if (ret > 0) {
126232633Smp	*pwc = tmp;
127232633Smp#if defined(UTF16_STRINGS) && defined(HAVE_MBRTOWC)
128232633Smp	if (tmp >= 0xd800 && tmp <= 0xdbff) {
129232633Smp	    /* UTF-16 surrogate pair.  Fetch second half and compute
130232633Smp	       UTF-32 value.  Dispense with the inverse test in this case. */
131232633Smp	    size_t n2 = mbrtowc(&tmp, s + ret, n - ret, &mb);
132232633Smp	    if (n2 == 0 || n2 == (size_t)-1 || n2 == (size_t)-2)
133232633Smp		ret = -1;
134232633Smp	    else {
135232633Smp		*pwc = (((*pwc & 0x3ff) << 10) | (tmp & 0x3ff)) + 0x10000;
136232633Smp		ret += n2;
137232633Smp	    }
138232633Smp	} else
139232633Smp#endif
140232633Smp      	if (wctomb(back, *pwc) != ret || memcmp(s, back, ret) != 0)
141232633Smp	    ret = -1;
142232633Smp
143232633Smp    } else if (ret == -2)
144145479Smp	ret = -1;
145232633Smp    else if (ret == 0)
146232633Smp	*pwc = '\0';
147232633Smp
148145479Smp    return ret;
149145479Smp}
150167465Smp#endif
151145479Smp
152167465Smp#ifdef SHORT_STRINGS
15359243SobrienChar  **
154167465Smpblk2short(char **src)
15559243Sobrien{
15659243Sobrien    size_t     n;
157145479Smp    Char **sdst, **dst;
15859243Sobrien
15959243Sobrien    /*
16059243Sobrien     * Count
16159243Sobrien     */
16259243Sobrien    for (n = 0; src[n] != NULL; n++)
16359243Sobrien	continue;
164167465Smp    sdst = dst = xmalloc((n + 1) * sizeof(Char *));
16559243Sobrien
16659243Sobrien    for (; *src != NULL; src++)
16759243Sobrien	*dst++ = SAVE(*src);
16859243Sobrien    *dst = NULL;
16959243Sobrien    return (sdst);
17059243Sobrien}
17159243Sobrien
17259243Sobrienchar  **
173167465Smpshort2blk(Char **src)
17459243Sobrien{
17559243Sobrien    size_t     n;
176145479Smp    char **sdst, **dst;
17759243Sobrien
17859243Sobrien    /*
17959243Sobrien     * Count
18059243Sobrien     */
18159243Sobrien    for (n = 0; src[n] != NULL; n++)
18259243Sobrien	continue;
183167465Smp    sdst = dst = xmalloc((n + 1) * sizeof(char *));
18459243Sobrien
18559243Sobrien    for (; *src != NULL; src++)
18659243Sobrien	*dst++ = strsave(short2str(*src));
18759243Sobrien    *dst = NULL;
18859243Sobrien    return (sdst);
18959243Sobrien}
19059243Sobrien
19159243SobrienChar   *
192167465Smpstr2short(const char *src)
19359243Sobrien{
194167465Smp    static struct Strbuf buf; /* = Strbuf_INIT; */
19559243Sobrien
19659243Sobrien    if (src == NULL)
19759243Sobrien	return (NULL);
19859243Sobrien
199167465Smp    buf.len = 0;
200167465Smp    while (*src) {
201167465Smp	Char wc;
20259243Sobrien
203167465Smp	src += one_mbtowc(&wc, src, MB_LEN_MAX);
204167465Smp	Strbuf_append1(&buf, wc);
20559243Sobrien    }
206167465Smp    Strbuf_terminate(&buf);
207167465Smp    return buf.s;
20859243Sobrien}
20959243Sobrien
21059243Sobrienchar   *
211167465Smpshort2str(const Char *src)
21259243Sobrien{
21359243Sobrien    static char *sdst = NULL;
21459243Sobrien    static size_t dstsize = 0;
215145479Smp    char *dst, *edst;
21659243Sobrien
21759243Sobrien    if (src == NULL)
21859243Sobrien	return (NULL);
21959243Sobrien
22059243Sobrien    if (sdst == NULL) {
22159243Sobrien	dstsize = MALLOC_INCR;
222167465Smp	sdst = xmalloc((dstsize + MALLOC_SURPLUS) * sizeof(char));
22359243Sobrien    }
22459243Sobrien    dst = sdst;
22559243Sobrien    edst = &dst[dstsize];
22659243Sobrien    while (*src) {
227145479Smp	dst += one_wctomb(dst, *src & CHAR);
228145479Smp	src++;
229145479Smp	if (dst >= edst) {
230195609Smp	    char *wdst = dst;
231195609Smp	    char *wedst = edst;
232195609Smp
23359243Sobrien	    dstsize += MALLOC_INCR;
234167465Smp	    sdst = xrealloc(sdst, (dstsize + MALLOC_SURPLUS) * sizeof(char));
23559243Sobrien	    edst = &sdst[dstsize];
23659243Sobrien	    dst = &edst[-MALLOC_INCR];
237195609Smp	    while (wdst > wedst) {
238195609Smp		dst++;
239195609Smp		wdst--;
240195609Smp	    }
24159243Sobrien	}
24259243Sobrien    }
24359243Sobrien    *dst = 0;
24459243Sobrien    return (sdst);
24559243Sobrien}
24659243Sobrien
247232633Smp#if !defined (WIDE_STRINGS) || defined (UTF16_STRINGS)
24859243SobrienChar   *
249167465Smps_strcpy(Char *dst, const Char *src)
25059243Sobrien{
251145479Smp    Char *sdst;
25259243Sobrien
25359243Sobrien    sdst = dst;
25459243Sobrien    while ((*dst++ = *src++) != '\0')
25559243Sobrien	continue;
25659243Sobrien    return (sdst);
25759243Sobrien}
25859243Sobrien
25959243SobrienChar   *
260167465Smps_strncpy(Char *dst, const Char *src, size_t n)
26159243Sobrien{
262145479Smp    Char *sdst;
26359243Sobrien
26459243Sobrien    if (n == 0)
26559243Sobrien	return(dst);
26659243Sobrien
26759243Sobrien    sdst = dst;
26859243Sobrien    do
26959243Sobrien	if ((*dst++ = *src++) == '\0') {
27059243Sobrien	    while (--n != 0)
27159243Sobrien		*dst++ = '\0';
27259243Sobrien	    return(sdst);
27359243Sobrien	}
27459243Sobrien    while (--n != 0);
27559243Sobrien    return (sdst);
27659243Sobrien}
27759243Sobrien
27859243SobrienChar   *
279167465Smps_strcat(Char *dst, const Char *src)
28059243Sobrien{
281167465Smp    Strcpy(Strend(dst), src);
282167465Smp    return dst;
28359243Sobrien}
28459243Sobrien
28559243Sobrien#ifdef NOTUSED
28659243SobrienChar   *
287167465Smps_strncat(Char *dst, const Char *src, size_t n)
28859243Sobrien{
289145479Smp    Char *sdst;
29059243Sobrien
29159243Sobrien    if (n == 0)
29259243Sobrien	return (dst);
29359243Sobrien
29459243Sobrien    sdst = dst;
29559243Sobrien
296167465Smp    while (*dst)
297167465Smp	dst++;
29859243Sobrien
29959243Sobrien    do
30059243Sobrien	if ((*dst++ = *src++) == '\0')
30159243Sobrien	    return(sdst);
30259243Sobrien    while (--n != 0)
30359243Sobrien	continue;
30459243Sobrien
30559243Sobrien    *dst = '\0';
30659243Sobrien    return (sdst);
30759243Sobrien}
30859243Sobrien
30959243Sobrien#endif
31059243Sobrien
31159243SobrienChar   *
312167465Smps_strchr(const Char *str, int ch)
31359243Sobrien{
31459243Sobrien    do
31559243Sobrien	if (*str == ch)
316145479Smp	    return ((Char *)(intptr_t)str);
31759243Sobrien    while (*str++);
31859243Sobrien    return (NULL);
31959243Sobrien}
32059243Sobrien
32159243SobrienChar   *
322167465Smps_strrchr(const Char *str, int ch)
32359243Sobrien{
324145479Smp    const Char *rstr;
32559243Sobrien
32659243Sobrien    rstr = NULL;
32759243Sobrien    do
32859243Sobrien	if (*str == ch)
32959243Sobrien	    rstr = str;
33059243Sobrien    while (*str++);
331145479Smp    return ((Char *)(intptr_t)rstr);
33259243Sobrien}
33359243Sobrien
33459243Sobriensize_t
335167465Smps_strlen(const Char *str)
33659243Sobrien{
337145479Smp    size_t n;
33859243Sobrien
33959243Sobrien    for (n = 0; *str++; n++)
34059243Sobrien	continue;
34159243Sobrien    return (n);
34259243Sobrien}
34359243Sobrien
34459243Sobrienint
345167465Smps_strcmp(const Char *str1, const Char *str2)
34659243Sobrien{
34759243Sobrien    for (; *str1 && *str1 == *str2; str1++, str2++)
34859243Sobrien	continue;
34959243Sobrien    /*
35059243Sobrien     * The following case analysis is necessary so that characters which look
35159243Sobrien     * negative collate low against normal characters but high against the
35259243Sobrien     * end-of-string NUL.
35359243Sobrien     */
35459243Sobrien    if (*str1 == '\0' && *str2 == '\0')
35559243Sobrien	return (0);
35659243Sobrien    else if (*str1 == '\0')
35759243Sobrien	return (-1);
35859243Sobrien    else if (*str2 == '\0')
35959243Sobrien	return (1);
36059243Sobrien    else
36159243Sobrien	return (*str1 - *str2);
36259243Sobrien}
36359243Sobrien
36459243Sobrienint
365167465Smps_strncmp(const Char *str1, const Char *str2, size_t n)
36659243Sobrien{
36759243Sobrien    if (n == 0)
36859243Sobrien	return (0);
36959243Sobrien    do {
37059243Sobrien	if (*str1 != *str2) {
37159243Sobrien	    /*
37259243Sobrien	     * The following case analysis is necessary so that characters
37359243Sobrien	     * which look negative collate low against normal characters
37459243Sobrien	     * but high against the end-of-string NUL.
37559243Sobrien	     */
37659243Sobrien	    if (*str1 == '\0')
37759243Sobrien		return (-1);
37859243Sobrien	    else if (*str2 == '\0')
37959243Sobrien		return (1);
38059243Sobrien	    else
38159243Sobrien		return (*str1 - *str2);
38259243Sobrien	}
38359243Sobrien        if (*str1 == '\0')
38459243Sobrien	    return(0);
38559243Sobrien	str1++, str2++;
38659243Sobrien    } while (--n != 0);
38759243Sobrien    return(0);
38859243Sobrien}
389145479Smp#endif /* not WIDE_STRINGS */
39059243Sobrien
391131962Smpint
392167465Smps_strcasecmp(const Char *str1, const Char *str2)
393131962Smp{
394145479Smp#ifdef WIDE_STRINGS
395232633Smp    wint_t l1 = 0, l2 = 0;
396232633Smp    for (; *str1; str1++, str2++)
397232633Smp	if (*str1 == *str2)
398232633Smp	    l1 = l2 = 0;
399232633Smp	else if ((l1 = towlower(*str1)) != (l2 = towlower(*str2)))
400232633Smp	    break;
401145479Smp#else
402232633Smp    unsigned char l1 = 0, l2 = 0;
403232633Smp    for (; *str1; str1++, str2++)
404232633Smp	if (*str1 == *str2)
405232633Smp		l1 = l2 = 0;
406232633Smp	else if ((l1 = tolower((unsigned char)*str1)) !=
407232633Smp	    (l2 = tolower((unsigned char)*str2)))
408232633Smp	    break;
409145479Smp#endif
410131962Smp    /*
411131962Smp     * The following case analysis is necessary so that characters which look
412131962Smp     * negative collate low against normal characters but high against the
413131962Smp     * end-of-string NUL.
414131962Smp     */
415131962Smp    if (*str1 == '\0' && *str2 == '\0')
416131962Smp	return (0);
417131962Smp    else if (*str1 == '\0')
418131962Smp	return (-1);
419131962Smp    else if (*str2 == '\0')
420131962Smp	return (1);
421131962Smp    else if (l1 == l2)	/* They are zero when they are equal */
422131962Smp	return (*str1 - *str2);
423131962Smp    else
424131962Smp	return (l1 - l2);
425131962Smp}
426131962Smp
42759243SobrienChar   *
428167465Smps_strnsave(const Char *s, size_t len)
42959243Sobrien{
430167465Smp    Char *n;
431167465Smp
432167465Smp    n = xmalloc((len + 1) * sizeof (*n));
433167465Smp    memcpy(n, s, len * sizeof (*n));
434167465Smp    n[len] = '\0';
435167465Smp    return n;
436167465Smp}
437167465Smp
438167465SmpChar   *
439167465Smps_strsave(const Char *s)
440167465Smp{
44159243Sobrien    Char   *n;
442167465Smp    size_t size;
44359243Sobrien
444167465Smp    if (s == NULL)
44559243Sobrien	s = STRNULL;
446167465Smp    size = (Strlen(s) + 1) * sizeof(*n);
447167465Smp    n = xmalloc(size);
448167465Smp    memcpy(n, s, size);
44959243Sobrien    return (n);
45059243Sobrien}
45159243Sobrien
45259243SobrienChar   *
453167465Smps_strspl(const Char *cp, const Char *dp)
45459243Sobrien{
455167465Smp    Char *res, *ep;
456167465Smp    const Char *p, *q;
45759243Sobrien
45859243Sobrien    if (!cp)
45959243Sobrien	cp = STRNULL;
46059243Sobrien    if (!dp)
46159243Sobrien	dp = STRNULL;
462167465Smp    for (p = cp; *p++;)
46359243Sobrien	continue;
464167465Smp    for (q = dp; *q++;)
46559243Sobrien	continue;
466167465Smp    res = xmalloc(((p - cp) + (q - dp) - 1) * sizeof(Char));
467167465Smp    for (ep = res, q = cp; (*ep++ = *q++) != '\0';)
46859243Sobrien	continue;
469167465Smp    for (ep--, q = dp; (*ep++ = *q++) != '\0';)
47059243Sobrien	continue;
471167465Smp    return (res);
47259243Sobrien}
47359243Sobrien
47459243SobrienChar   *
475167465Smps_strend(const Char *cp)
47659243Sobrien{
47759243Sobrien    if (!cp)
478145479Smp	return ((Char *)(intptr_t) cp);
47959243Sobrien    while (*cp)
48059243Sobrien	cp++;
481145479Smp    return ((Char *)(intptr_t) cp);
48259243Sobrien}
48359243Sobrien
48459243SobrienChar   *
485167465Smps_strstr(const Char *s, const Char *t)
48659243Sobrien{
48759243Sobrien    do {
488145479Smp	const Char *ss = s;
489145479Smp	const Char *tt = t;
49059243Sobrien
49159243Sobrien	do
49259243Sobrien	    if (*tt == '\0')
493145479Smp		return ((Char *)(intptr_t) s);
49459243Sobrien	while (*ss++ == *tt++);
49559243Sobrien    } while (*s++ != '\0');
49659243Sobrien    return (NULL);
49759243Sobrien}
49859243Sobrien
499167465Smp#else /* !SHORT_STRINGS */
500167465Smpchar *
501167465Smpcaching_strip(const char *s)
502167465Smp{
503167465Smp    static char *buf = NULL;
504167465Smp    static size_t buf_size = 0;
505167465Smp    size_t size;
50659243Sobrien
507167465Smp    if (s == NULL)
508167465Smp      return NULL;
509167465Smp    size = strlen(s) + 1;
510167465Smp    if (buf_size < size) {
511167465Smp	buf = xrealloc(buf, size);
512167465Smp	buf_size = size;
513167465Smp    }
514167465Smp    memcpy(buf, s, size);
515167465Smp    strip(buf);
516167465Smp    return buf;
517167465Smp}
518167465Smp#endif
519167465Smp
52059243Sobrienchar   *
521167465Smpshort2qstr(const Char *src)
52259243Sobrien{
52359243Sobrien    static char *sdst = NULL;
52459243Sobrien    static size_t dstsize = 0;
525145479Smp    char *dst, *edst;
52659243Sobrien
52759243Sobrien    if (src == NULL)
52859243Sobrien	return (NULL);
52959243Sobrien
53059243Sobrien    if (sdst == NULL) {
53159243Sobrien	dstsize = MALLOC_INCR;
532167465Smp	sdst = xmalloc((dstsize + MALLOC_SURPLUS) * sizeof(char));
53359243Sobrien    }
53459243Sobrien    dst = sdst;
53559243Sobrien    edst = &dst[dstsize];
53659243Sobrien    while (*src) {
53759243Sobrien	if (*src & QUOTE) {
53859243Sobrien	    *dst++ = '\\';
53959243Sobrien	    if (dst == edst) {
54059243Sobrien		dstsize += MALLOC_INCR;
541167465Smp		sdst = xrealloc(sdst,
542167465Smp				(dstsize + MALLOC_SURPLUS) * sizeof(char));
54359243Sobrien		edst = &sdst[dstsize];
54459243Sobrien		dst = &edst[-MALLOC_INCR];
54559243Sobrien	    }
54659243Sobrien	}
547145479Smp	dst += one_wctomb(dst, *src & CHAR);
548145479Smp	src++;
549145479Smp	if (dst >= edst) {
550195609Smp	    ptrdiff_t i = dst - edst;
55159243Sobrien	    dstsize += MALLOC_INCR;
552167465Smp	    sdst = xrealloc(sdst, (dstsize + MALLOC_SURPLUS) * sizeof(char));
55359243Sobrien	    edst = &sdst[dstsize];
554195609Smp	    dst = &edst[-MALLOC_INCR + i];
55559243Sobrien	}
55659243Sobrien    }
55759243Sobrien    *dst = 0;
55859243Sobrien    return (sdst);
55959243Sobrien}
560167465Smp
561195609Smpstruct blk_buf *
562195609Smpbb_alloc()
563195609Smp{
564195609Smp    return xcalloc(1, sizeof(struct blk_buf));
565195609Smp}
566195609Smp
567167465Smpstatic void
568167465Smpbb_store(struct blk_buf *bb, Char *str)
569167465Smp{
570167465Smp    if (bb->len == bb->size) { /* Keep space for terminating NULL */
571167465Smp	if (bb->size == 0)
572167465Smp	    bb->size = 16; /* Arbitrary */
573167465Smp	else
574167465Smp	    bb->size *= 2;
575167465Smp	bb->vec = xrealloc(bb->vec, bb->size * sizeof (*bb->vec));
576167465Smp    }
577167465Smp    bb->vec[bb->len] = str;
578167465Smp}
579167465Smp
580167465Smpvoid
581167465Smpbb_append(struct blk_buf *bb, Char *str)
582167465Smp{
583167465Smp    bb_store(bb, str);
584167465Smp    bb->len++;
585167465Smp}
586167465Smp
587167465Smpvoid
588167465Smpbb_cleanup(void *xbb)
589167465Smp{
590167465Smp    struct blk_buf *bb;
591167465Smp    size_t i;
592167465Smp
593167465Smp    bb = xbb;
594167465Smp    for (i = 0; i < bb->len; i++)
595167465Smp	xfree(bb->vec[i]);
596167465Smp    xfree(bb->vec);
597167465Smp}
598167465Smp
599195609Smpvoid
600195609Smpbb_free(void *bb)
601195609Smp{
602195609Smp    bb_cleanup(bb);
603195609Smp    xfree(bb);
604195609Smp}
605195609Smp
606167465SmpChar **
607167465Smpbb_finish(struct blk_buf *bb)
608167465Smp{
609167465Smp    bb_store(bb, NULL);
610167465Smp    return xrealloc(bb->vec, (bb->len + 1) * sizeof (*bb->vec));
611167465Smp}
612167465Smp
613167465Smp#define DO_STRBUF(STRBUF, CHAR, STRLEN)				\
614195609Smp								\
615195609Smpstruct STRBUF *							\
616195609SmpSTRBUF##_alloc(void)						\
617195609Smp{								\
618195609Smp    return xcalloc(1, sizeof(struct STRBUF));			\
619195609Smp}								\
620195609Smp								\
621167465Smpstatic void							\
622167465SmpSTRBUF##_store1(struct STRBUF *buf, CHAR c)			\
623167465Smp{								\
624167465Smp    if (buf->size == buf->len) {				\
625167465Smp	if (buf->size == 0)					\
626167465Smp	    buf->size = 64; /* Arbitrary */			\
627167465Smp	else							\
628167465Smp	    buf->size *= 2;					\
629167465Smp	buf->s = xrealloc(buf->s, buf->size * sizeof(*buf->s));	\
630167465Smp    }								\
631232633Smp    assert(buf->s);						\
632167465Smp    buf->s[buf->len] = c;					\
633167465Smp}								\
634167465Smp								\
635167465Smp/* Like strbuf_append1(buf, '\0'), but don't advance len */	\
636167465Smpvoid								\
637167465SmpSTRBUF##_terminate(struct STRBUF *buf)				\
638167465Smp{								\
639167465Smp    STRBUF##_store1(buf, '\0');					\
640167465Smp}								\
641167465Smp								\
642167465Smpvoid								\
643167465SmpSTRBUF##_append1(struct STRBUF *buf, CHAR c)			\
644167465Smp{								\
645167465Smp    STRBUF##_store1(buf, c);					\
646167465Smp    buf->len++;							\
647167465Smp}								\
648167465Smp								\
649167465Smpvoid								\
650167465SmpSTRBUF##_appendn(struct STRBUF *buf, const CHAR *s, size_t len)	\
651167465Smp{								\
652167465Smp    if (buf->size < buf->len + len) {				\
653167465Smp	if (buf->size == 0)					\
654167465Smp	    buf->size = 64; /* Arbitrary */			\
655167465Smp	while (buf->size < buf->len + len)			\
656167465Smp	    buf->size *= 2;					\
657167465Smp	buf->s = xrealloc(buf->s, buf->size * sizeof(*buf->s));	\
658167465Smp    }								\
659167465Smp    memcpy(buf->s + buf->len, s, len * sizeof(*buf->s));	\
660167465Smp    buf->len += len;						\
661167465Smp}								\
662167465Smp								\
663167465Smpvoid								\
664167465SmpSTRBUF##_append(struct STRBUF *buf, const CHAR *s)		\
665167465Smp{								\
666167465Smp    STRBUF##_appendn(buf, s, STRLEN(s));			\
667167465Smp}								\
668167465Smp								\
669167465SmpCHAR *								\
670167465SmpSTRBUF##_finish(struct STRBUF *buf)				\
671167465Smp{								\
672167465Smp    STRBUF##_append1(buf, 0);					\
673167465Smp    return xrealloc(buf->s, buf->len * sizeof(*buf->s));	\
674167465Smp}								\
675167465Smp								\
676167465Smpvoid								\
677167465SmpSTRBUF##_cleanup(void *xbuf)					\
678167465Smp{								\
679167465Smp    struct STRBUF *buf;						\
680167465Smp								\
681167465Smp    buf = xbuf;							\
682167465Smp    xfree(buf->s);						\
683167465Smp}								\
684167465Smp								\
685195609Smpvoid								\
686195609SmpSTRBUF##_free(void *xbuf)					\
687195609Smp{								\
688195609Smp    STRBUF##_cleanup(xbuf);					\
689195609Smp    xfree(xbuf);						\
690195609Smp}								\
691195609Smp								\
692167465Smpconst struct STRBUF STRBUF##_init /* = STRBUF##_INIT; */
693167465Smp
694167465SmpDO_STRBUF(strbuf, char, strlen);
695167465SmpDO_STRBUF(Strbuf, Char, Strlen);
696