159243Sobrien/*
259243Sobrien * tc.str.c: Short string package
359243Sobrien * 	     This has been a lesson of how to write buggy code!
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35231990Smp#include <assert.h>
36145479Smp#include <limits.h>
3759243Sobrien
3859243Sobrien#define MALLOC_INCR	128
39145479Smp#ifdef WIDE_STRINGS
40145479Smp#define MALLOC_SURPLUS	MB_LEN_MAX /* Space for one multibyte character */
41145479Smp#else
42145479Smp#define MALLOC_SURPLUS	0
43145479Smp#endif
4459243Sobrien
45145479Smp#ifdef WIDE_STRINGS
46145479Smpsize_t
47231990Smpone_mbtowc(Char *pwc, const char *s, size_t n)
48145479Smp{
49145479Smp    int len;
50145479Smp
51145479Smp    len = rt_mbtowc(pwc, s, n);
52145479Smp    if (len == -1) {
53195609Smp        reset_mbtowc();
54145479Smp	*pwc = (unsigned char)*s | INVALID_BYTE;
55145479Smp    }
56145479Smp    if (len <= 0)
57145479Smp	len = 1;
58145479Smp    return len;
59145479Smp}
60145479Smp
61145479Smpsize_t
62231990Smpone_wctomb(char *s, Char wchar)
63145479Smp{
64145479Smp    int len;
65145479Smp
66316957Sdchagin#if INVALID_BYTE != 0
67316957Sdchagin    if ((wchar & INVALID_BYTE) == INVALID_BYTE) {    /* wchar >= INVALID_BYTE */
68316957Sdchagin	/* invalid char
69316957Sdchagin	 * exmaple)
70316957Sdchagin	 * if wchar = f0000090(=90|INVALID_BYTE), then *s = ffffff90 */
71316957Sdchagin	*s = (char)wchar;
72145479Smp	len = 1;
73316957Sdchagin#else
74316957Sdchagin    if (wchar & (CHAR & INVALID_BYTE)) {
75316957Sdchagin	s[0] = wchar & (CHAR & 0xFF);
76316957Sdchagin	len = 1;
77316957Sdchagin#endif
78145479Smp    } else {
79316957Sdchagin#if INVALID_BYTE != 0
80316957Sdchagin	wchar &= MAX_UTF32;
81316957Sdchagin#else
82316957Sdchagin	wchar &= CHAR;
83316957Sdchagin#endif
84231990Smp#ifdef UTF16_STRINGS
85231990Smp	if (wchar >= 0x10000) {
86231990Smp	    /* UTF-16 systems can't handle these values directly in calls to
87231990Smp	       wctomb.  Convert value to UTF-16 surrogate and call wcstombs to
88231990Smp	       convert the "string" to the correct multibyte representation,
89231990Smp	       if any. */
90231990Smp	    wchar_t ws[3];
91231990Smp	    wchar -= 0x10000;
92231990Smp	    ws[0] = 0xd800 | (wchar >> 10);
93231990Smp	    ws[1] = 0xdc00 | (wchar & 0x3ff);
94231990Smp	    ws[2] = 0;
95231990Smp	    /* The return value of wcstombs excludes the trailing 0, so len is
96231990Smp	       the correct number of multibytes for the Unicode char. */
97231990Smp	    len = wcstombs (s, ws, MB_CUR_MAX + 1);
98231990Smp	} else
99231990Smp#endif
100231990Smp	len = wctomb(s, (wchar_t) wchar);
101145479Smp	if (len == -1)
102145479Smp	    s[0] = wchar;
103145479Smp	if (len <= 0)
104145479Smp	    len = 1;
105145479Smp    }
106145479Smp    return len;
107145479Smp}
108167465Smp
109145479Smpint
110231990Smprt_mbtowc(Char *pwc, const char *s, size_t n)
111145479Smp{
112145479Smp    int ret;
113145479Smp    char back[MB_LEN_MAX];
114231990Smp    wchar_t tmp;
115231990Smp#if defined(UTF16_STRINGS) && defined(HAVE_MBRTOWC)
116231990Smp# if defined(AUTOSET_KANJI)
117231990Smp    static mbstate_t mb_zero, mb;
118231990Smp    /*
119231990Smp     * Workaround the Shift-JIS endcoding that translates unshifted 7 bit ASCII!
120231990Smp     */
121231990Smp    if (!adrof(STRnokanji) && n && pwc && s && (*s == '\\' || *s == '~') &&
122231990Smp	!memcmp(&mb, &mb_zero, sizeof(mb)))
123231990Smp    {
124231990Smp	*pwc = *s;
125231990Smp	return 1;
126231990Smp    }
127231990Smp# else
128231990Smp    mbstate_t mb;
129231990Smp# endif
130145479Smp
131231990Smp    memset (&mb, 0, sizeof mb);
132231990Smp    ret = mbrtowc(&tmp, s, n, &mb);
133231990Smp#else
134231990Smp    ret = mbtowc(&tmp, s, n);
135231990Smp#endif
136231990Smp    if (ret > 0) {
137231990Smp	*pwc = tmp;
138231990Smp#if defined(UTF16_STRINGS) && defined(HAVE_MBRTOWC)
139231990Smp	if (tmp >= 0xd800 && tmp <= 0xdbff) {
140231990Smp	    /* UTF-16 surrogate pair.  Fetch second half and compute
141231990Smp	       UTF-32 value.  Dispense with the inverse test in this case. */
142231990Smp	    size_t n2 = mbrtowc(&tmp, s + ret, n - ret, &mb);
143231990Smp	    if (n2 == 0 || n2 == (size_t)-1 || n2 == (size_t)-2)
144231990Smp		ret = -1;
145231990Smp	    else {
146231990Smp		*pwc = (((*pwc & 0x3ff) << 10) | (tmp & 0x3ff)) + 0x10000;
147231990Smp		ret += n2;
148231990Smp	    }
149231990Smp	} else
150231990Smp#endif
151231990Smp      	if (wctomb(back, *pwc) != ret || memcmp(s, back, ret) != 0)
152231990Smp	    ret = -1;
153231990Smp
154231990Smp    } else if (ret == -2)
155145479Smp	ret = -1;
156231990Smp    else if (ret == 0)
157231990Smp	*pwc = '\0';
158231990Smp
159145479Smp    return ret;
160145479Smp}
161167465Smp#endif
162145479Smp
163167465Smp#ifdef SHORT_STRINGS
16459243SobrienChar  **
165167465Smpblk2short(char **src)
16659243Sobrien{
16759243Sobrien    size_t     n;
168145479Smp    Char **sdst, **dst;
16959243Sobrien
17059243Sobrien    /*
17159243Sobrien     * Count
17259243Sobrien     */
17359243Sobrien    for (n = 0; src[n] != NULL; n++)
17459243Sobrien	continue;
175167465Smp    sdst = dst = xmalloc((n + 1) * sizeof(Char *));
17659243Sobrien
17759243Sobrien    for (; *src != NULL; src++)
17859243Sobrien	*dst++ = SAVE(*src);
17959243Sobrien    *dst = NULL;
18059243Sobrien    return (sdst);
18159243Sobrien}
18259243Sobrien
18359243Sobrienchar  **
184167465Smpshort2blk(Char **src)
18559243Sobrien{
18659243Sobrien    size_t     n;
187145479Smp    char **sdst, **dst;
18859243Sobrien
18959243Sobrien    /*
19059243Sobrien     * Count
19159243Sobrien     */
19259243Sobrien    for (n = 0; src[n] != NULL; n++)
19359243Sobrien	continue;
194167465Smp    sdst = dst = xmalloc((n + 1) * sizeof(char *));
19559243Sobrien
19659243Sobrien    for (; *src != NULL; src++)
19759243Sobrien	*dst++ = strsave(short2str(*src));
19859243Sobrien    *dst = NULL;
19959243Sobrien    return (sdst);
20059243Sobrien}
20159243Sobrien
20259243SobrienChar   *
203167465Smpstr2short(const char *src)
20459243Sobrien{
205167465Smp    static struct Strbuf buf; /* = Strbuf_INIT; */
20659243Sobrien
20759243Sobrien    if (src == NULL)
20859243Sobrien	return (NULL);
20959243Sobrien
210167465Smp    buf.len = 0;
211167465Smp    while (*src) {
212167465Smp	Char wc;
21359243Sobrien
214167465Smp	src += one_mbtowc(&wc, src, MB_LEN_MAX);
215167465Smp	Strbuf_append1(&buf, wc);
21659243Sobrien    }
217167465Smp    Strbuf_terminate(&buf);
218167465Smp    return buf.s;
21959243Sobrien}
22059243Sobrien
22159243Sobrienchar   *
222167465Smpshort2str(const Char *src)
22359243Sobrien{
22459243Sobrien    static char *sdst = NULL;
22559243Sobrien    static size_t dstsize = 0;
226145479Smp    char *dst, *edst;
22759243Sobrien
22859243Sobrien    if (src == NULL)
22959243Sobrien	return (NULL);
23059243Sobrien
23159243Sobrien    if (sdst == NULL) {
23259243Sobrien	dstsize = MALLOC_INCR;
233167465Smp	sdst = xmalloc((dstsize + MALLOC_SURPLUS) * sizeof(char));
23459243Sobrien    }
23559243Sobrien    dst = sdst;
23659243Sobrien    edst = &dst[dstsize];
23759243Sobrien    while (*src) {
238316957Sdchagin	dst += one_wctomb(dst, *src);
239145479Smp	src++;
240145479Smp	if (dst >= edst) {
241195609Smp	    char *wdst = dst;
242195609Smp	    char *wedst = edst;
243195609Smp
24459243Sobrien	    dstsize += MALLOC_INCR;
245167465Smp	    sdst = xrealloc(sdst, (dstsize + MALLOC_SURPLUS) * sizeof(char));
24659243Sobrien	    edst = &sdst[dstsize];
24759243Sobrien	    dst = &edst[-MALLOC_INCR];
248195609Smp	    while (wdst > wedst) {
249195609Smp		dst++;
250195609Smp		wdst--;
251195609Smp	    }
25259243Sobrien	}
25359243Sobrien    }
25459243Sobrien    *dst = 0;
25559243Sobrien    return (sdst);
25659243Sobrien}
25759243Sobrien
258231990Smp#if !defined (WIDE_STRINGS) || defined (UTF16_STRINGS)
25959243SobrienChar   *
260167465Smps_strcpy(Char *dst, const Char *src)
26159243Sobrien{
262145479Smp    Char *sdst;
26359243Sobrien
26459243Sobrien    sdst = dst;
26559243Sobrien    while ((*dst++ = *src++) != '\0')
26659243Sobrien	continue;
26759243Sobrien    return (sdst);
26859243Sobrien}
26959243Sobrien
27059243SobrienChar   *
271167465Smps_strncpy(Char *dst, const Char *src, size_t n)
27259243Sobrien{
273145479Smp    Char *sdst;
27459243Sobrien
27559243Sobrien    if (n == 0)
27659243Sobrien	return(dst);
27759243Sobrien
27859243Sobrien    sdst = dst;
27959243Sobrien    do
28059243Sobrien	if ((*dst++ = *src++) == '\0') {
28159243Sobrien	    while (--n != 0)
28259243Sobrien		*dst++ = '\0';
28359243Sobrien	    return(sdst);
28459243Sobrien	}
28559243Sobrien    while (--n != 0);
28659243Sobrien    return (sdst);
28759243Sobrien}
28859243Sobrien
28959243SobrienChar   *
290167465Smps_strcat(Char *dst, const Char *src)
29159243Sobrien{
292167465Smp    Strcpy(Strend(dst), src);
293167465Smp    return dst;
29459243Sobrien}
29559243Sobrien
29659243Sobrien#ifdef NOTUSED
29759243SobrienChar   *
298167465Smps_strncat(Char *dst, const Char *src, size_t n)
29959243Sobrien{
300145479Smp    Char *sdst;
30159243Sobrien
30259243Sobrien    if (n == 0)
30359243Sobrien	return (dst);
30459243Sobrien
30559243Sobrien    sdst = dst;
30659243Sobrien
307167465Smp    while (*dst)
308167465Smp	dst++;
30959243Sobrien
31059243Sobrien    do
31159243Sobrien	if ((*dst++ = *src++) == '\0')
31259243Sobrien	    return(sdst);
31359243Sobrien    while (--n != 0)
31459243Sobrien	continue;
31559243Sobrien
31659243Sobrien    *dst = '\0';
31759243Sobrien    return (sdst);
31859243Sobrien}
31959243Sobrien
32059243Sobrien#endif
32159243Sobrien
32259243SobrienChar   *
323167465Smps_strchr(const Char *str, int ch)
32459243Sobrien{
32559243Sobrien    do
32659243Sobrien	if (*str == ch)
327145479Smp	    return ((Char *)(intptr_t)str);
32859243Sobrien    while (*str++);
32959243Sobrien    return (NULL);
33059243Sobrien}
33159243Sobrien
33259243SobrienChar   *
333167465Smps_strrchr(const Char *str, int ch)
33459243Sobrien{
335145479Smp    const Char *rstr;
33659243Sobrien
33759243Sobrien    rstr = NULL;
33859243Sobrien    do
33959243Sobrien	if (*str == ch)
34059243Sobrien	    rstr = str;
34159243Sobrien    while (*str++);
342145479Smp    return ((Char *)(intptr_t)rstr);
34359243Sobrien}
34459243Sobrien
34559243Sobriensize_t
346167465Smps_strlen(const Char *str)
34759243Sobrien{
348145479Smp    size_t n;
34959243Sobrien
35059243Sobrien    for (n = 0; *str++; n++)
35159243Sobrien	continue;
35259243Sobrien    return (n);
35359243Sobrien}
35459243Sobrien
35559243Sobrienint
356167465Smps_strcmp(const Char *str1, const Char *str2)
35759243Sobrien{
35859243Sobrien    for (; *str1 && *str1 == *str2; str1++, str2++)
35959243Sobrien	continue;
36059243Sobrien    /*
36159243Sobrien     * The following case analysis is necessary so that characters which look
36259243Sobrien     * negative collate low against normal characters but high against the
36359243Sobrien     * end-of-string NUL.
36459243Sobrien     */
36559243Sobrien    if (*str1 == '\0' && *str2 == '\0')
36659243Sobrien	return (0);
36759243Sobrien    else if (*str1 == '\0')
36859243Sobrien	return (-1);
36959243Sobrien    else if (*str2 == '\0')
37059243Sobrien	return (1);
37159243Sobrien    else
37259243Sobrien	return (*str1 - *str2);
37359243Sobrien}
37459243Sobrien
37559243Sobrienint
376167465Smps_strncmp(const Char *str1, const Char *str2, size_t n)
37759243Sobrien{
37859243Sobrien    if (n == 0)
37959243Sobrien	return (0);
38059243Sobrien    do {
38159243Sobrien	if (*str1 != *str2) {
38259243Sobrien	    /*
38359243Sobrien	     * The following case analysis is necessary so that characters
38459243Sobrien	     * which look negative collate low against normal characters
38559243Sobrien	     * but high against the end-of-string NUL.
38659243Sobrien	     */
38759243Sobrien	    if (*str1 == '\0')
38859243Sobrien		return (-1);
38959243Sobrien	    else if (*str2 == '\0')
39059243Sobrien		return (1);
39159243Sobrien	    else
39259243Sobrien		return (*str1 - *str2);
39359243Sobrien	}
39459243Sobrien        if (*str1 == '\0')
39559243Sobrien	    return(0);
39659243Sobrien	str1++, str2++;
39759243Sobrien    } while (--n != 0);
39859243Sobrien    return(0);
39959243Sobrien}
400145479Smp#endif /* not WIDE_STRINGS */
40159243Sobrien
402131962Smpint
403167465Smps_strcasecmp(const Char *str1, const Char *str2)
404131962Smp{
405145479Smp#ifdef WIDE_STRINGS
406231990Smp    wint_t l1 = 0, l2 = 0;
407231990Smp    for (; *str1; str1++, str2++)
408231990Smp	if (*str1 == *str2)
409231990Smp	    l1 = l2 = 0;
410231990Smp	else if ((l1 = towlower(*str1)) != (l2 = towlower(*str2)))
411231990Smp	    break;
412145479Smp#else
413231990Smp    unsigned char l1 = 0, l2 = 0;
414231990Smp    for (; *str1; str1++, str2++)
415231990Smp	if (*str1 == *str2)
416231990Smp		l1 = l2 = 0;
417231990Smp	else if ((l1 = tolower((unsigned char)*str1)) !=
418231990Smp	    (l2 = tolower((unsigned char)*str2)))
419231990Smp	    break;
420145479Smp#endif
421131962Smp    /*
422131962Smp     * The following case analysis is necessary so that characters which look
423131962Smp     * negative collate low against normal characters but high against the
424131962Smp     * end-of-string NUL.
425131962Smp     */
426131962Smp    if (*str1 == '\0' && *str2 == '\0')
427131962Smp	return (0);
428131962Smp    else if (*str1 == '\0')
429131962Smp	return (-1);
430131962Smp    else if (*str2 == '\0')
431131962Smp	return (1);
432131962Smp    else if (l1 == l2)	/* They are zero when they are equal */
433131962Smp	return (*str1 - *str2);
434131962Smp    else
435131962Smp	return (l1 - l2);
436131962Smp}
437131962Smp
43859243SobrienChar   *
439167465Smps_strnsave(const Char *s, size_t len)
44059243Sobrien{
441167465Smp    Char *n;
442167465Smp
443167465Smp    n = xmalloc((len + 1) * sizeof (*n));
444167465Smp    memcpy(n, s, len * sizeof (*n));
445167465Smp    n[len] = '\0';
446167465Smp    return n;
447167465Smp}
448167465Smp
449167465SmpChar   *
450167465Smps_strsave(const Char *s)
451167465Smp{
45259243Sobrien    Char   *n;
453167465Smp    size_t size;
45459243Sobrien
455167465Smp    if (s == NULL)
45659243Sobrien	s = STRNULL;
457167465Smp    size = (Strlen(s) + 1) * sizeof(*n);
458167465Smp    n = xmalloc(size);
459167465Smp    memcpy(n, s, size);
46059243Sobrien    return (n);
46159243Sobrien}
46259243Sobrien
46359243SobrienChar   *
464167465Smps_strspl(const Char *cp, const Char *dp)
46559243Sobrien{
466167465Smp    Char *res, *ep;
467167465Smp    const Char *p, *q;
46859243Sobrien
46959243Sobrien    if (!cp)
47059243Sobrien	cp = STRNULL;
47159243Sobrien    if (!dp)
47259243Sobrien	dp = STRNULL;
473167465Smp    for (p = cp; *p++;)
47459243Sobrien	continue;
475167465Smp    for (q = dp; *q++;)
47659243Sobrien	continue;
477167465Smp    res = xmalloc(((p - cp) + (q - dp) - 1) * sizeof(Char));
478167465Smp    for (ep = res, q = cp; (*ep++ = *q++) != '\0';)
47959243Sobrien	continue;
480167465Smp    for (ep--, q = dp; (*ep++ = *q++) != '\0';)
48159243Sobrien	continue;
482167465Smp    return (res);
48359243Sobrien}
48459243Sobrien
48559243SobrienChar   *
486167465Smps_strend(const Char *cp)
48759243Sobrien{
48859243Sobrien    if (!cp)
489145479Smp	return ((Char *)(intptr_t) cp);
49059243Sobrien    while (*cp)
49159243Sobrien	cp++;
492145479Smp    return ((Char *)(intptr_t) cp);
49359243Sobrien}
49459243Sobrien
49559243SobrienChar   *
496167465Smps_strstr(const Char *s, const Char *t)
49759243Sobrien{
49859243Sobrien    do {
499145479Smp	const Char *ss = s;
500145479Smp	const Char *tt = t;
50159243Sobrien
50259243Sobrien	do
50359243Sobrien	    if (*tt == '\0')
504145479Smp		return ((Char *)(intptr_t) s);
50559243Sobrien	while (*ss++ == *tt++);
50659243Sobrien    } while (*s++ != '\0');
50759243Sobrien    return (NULL);
50859243Sobrien}
50959243Sobrien
510167465Smp#else /* !SHORT_STRINGS */
511167465Smpchar *
512167465Smpcaching_strip(const char *s)
513167465Smp{
514167465Smp    static char *buf = NULL;
515167465Smp    static size_t buf_size = 0;
516167465Smp    size_t size;
51759243Sobrien
518167465Smp    if (s == NULL)
519167465Smp      return NULL;
520167465Smp    size = strlen(s) + 1;
521167465Smp    if (buf_size < size) {
522167465Smp	buf = xrealloc(buf, size);
523167465Smp	buf_size = size;
524167465Smp    }
525167465Smp    memcpy(buf, s, size);
526167465Smp    strip(buf);
527167465Smp    return buf;
528167465Smp}
529167465Smp#endif
530167465Smp
53159243Sobrienchar   *
532167465Smpshort2qstr(const Char *src)
53359243Sobrien{
53459243Sobrien    static char *sdst = NULL;
53559243Sobrien    static size_t dstsize = 0;
536145479Smp    char *dst, *edst;
53759243Sobrien
53859243Sobrien    if (src == NULL)
53959243Sobrien	return (NULL);
54059243Sobrien
54159243Sobrien    if (sdst == NULL) {
54259243Sobrien	dstsize = MALLOC_INCR;
543167465Smp	sdst = xmalloc((dstsize + MALLOC_SURPLUS) * sizeof(char));
54459243Sobrien    }
54559243Sobrien    dst = sdst;
54659243Sobrien    edst = &dst[dstsize];
54759243Sobrien    while (*src) {
54859243Sobrien	if (*src & QUOTE) {
54959243Sobrien	    *dst++ = '\\';
55059243Sobrien	    if (dst == edst) {
55159243Sobrien		dstsize += MALLOC_INCR;
552167465Smp		sdst = xrealloc(sdst,
553167465Smp				(dstsize + MALLOC_SURPLUS) * sizeof(char));
55459243Sobrien		edst = &sdst[dstsize];
55559243Sobrien		dst = &edst[-MALLOC_INCR];
55659243Sobrien	    }
55759243Sobrien	}
558316957Sdchagin	dst += one_wctomb(dst, *src);
559145479Smp	src++;
560145479Smp	if (dst >= edst) {
561195609Smp	    ptrdiff_t i = dst - edst;
56259243Sobrien	    dstsize += MALLOC_INCR;
563167465Smp	    sdst = xrealloc(sdst, (dstsize + MALLOC_SURPLUS) * sizeof(char));
56459243Sobrien	    edst = &sdst[dstsize];
565195609Smp	    dst = &edst[-MALLOC_INCR + i];
56659243Sobrien	}
56759243Sobrien    }
56859243Sobrien    *dst = 0;
56959243Sobrien    return (sdst);
57059243Sobrien}
571167465Smp
572195609Smpstruct blk_buf *
573316957Sdchaginbb_alloc(void)
574195609Smp{
575195609Smp    return xcalloc(1, sizeof(struct blk_buf));
576195609Smp}
577195609Smp
578167465Smpstatic void
579167465Smpbb_store(struct blk_buf *bb, Char *str)
580167465Smp{
581167465Smp    if (bb->len == bb->size) { /* Keep space for terminating NULL */
582167465Smp	if (bb->size == 0)
583167465Smp	    bb->size = 16; /* Arbitrary */
584167465Smp	else
585167465Smp	    bb->size *= 2;
586167465Smp	bb->vec = xrealloc(bb->vec, bb->size * sizeof (*bb->vec));
587167465Smp    }
588167465Smp    bb->vec[bb->len] = str;
589167465Smp}
590167465Smp
591167465Smpvoid
592167465Smpbb_append(struct blk_buf *bb, Char *str)
593167465Smp{
594167465Smp    bb_store(bb, str);
595167465Smp    bb->len++;
596167465Smp}
597167465Smp
598167465Smpvoid
599167465Smpbb_cleanup(void *xbb)
600167465Smp{
601167465Smp    struct blk_buf *bb;
602167465Smp    size_t i;
603167465Smp
604316957Sdchagin    bb = (struct blk_buf *)xbb;
605316957Sdchagin    if (bb->vec) {
606316957Sdchagin	for (i = 0; i < bb->len; i++)
607316957Sdchagin	    xfree(bb->vec[i]);
608316957Sdchagin	xfree(bb->vec);
609316957Sdchagin    }
610316957Sdchagin    bb->vec = NULL;
611316957Sdchagin    bb->len = 0;
612167465Smp}
613167465Smp
614195609Smpvoid
615195609Smpbb_free(void *bb)
616195609Smp{
617195609Smp    bb_cleanup(bb);
618195609Smp    xfree(bb);
619195609Smp}
620195609Smp
621167465SmpChar **
622167465Smpbb_finish(struct blk_buf *bb)
623167465Smp{
624167465Smp    bb_store(bb, NULL);
625167465Smp    return xrealloc(bb->vec, (bb->len + 1) * sizeof (*bb->vec));
626167465Smp}
627167465Smp
628167465Smp#define DO_STRBUF(STRBUF, CHAR, STRLEN)				\
629195609Smp								\
630195609Smpstruct STRBUF *							\
631195609SmpSTRBUF##_alloc(void)						\
632195609Smp{								\
633195609Smp    return xcalloc(1, sizeof(struct STRBUF));			\
634195609Smp}								\
635195609Smp								\
636167465Smpstatic void							\
637167465SmpSTRBUF##_store1(struct STRBUF *buf, CHAR c)			\
638167465Smp{								\
639167465Smp    if (buf->size == buf->len) {				\
640167465Smp	if (buf->size == 0)					\
641167465Smp	    buf->size = 64; /* Arbitrary */			\
642167465Smp	else							\
643167465Smp	    buf->size *= 2;					\
644167465Smp	buf->s = xrealloc(buf->s, buf->size * sizeof(*buf->s));	\
645167465Smp    }								\
646231990Smp    assert(buf->s);						\
647167465Smp    buf->s[buf->len] = c;					\
648167465Smp}								\
649167465Smp								\
650167465Smp/* Like strbuf_append1(buf, '\0'), but don't advance len */	\
651167465Smpvoid								\
652167465SmpSTRBUF##_terminate(struct STRBUF *buf)				\
653167465Smp{								\
654167465Smp    STRBUF##_store1(buf, '\0');					\
655167465Smp}								\
656167465Smp								\
657167465Smpvoid								\
658167465SmpSTRBUF##_append1(struct STRBUF *buf, CHAR c)			\
659167465Smp{								\
660167465Smp    STRBUF##_store1(buf, c);					\
661167465Smp    buf->len++;							\
662167465Smp}								\
663167465Smp								\
664167465Smpvoid								\
665167465SmpSTRBUF##_appendn(struct STRBUF *buf, const CHAR *s, size_t len)	\
666167465Smp{								\
667167465Smp    if (buf->size < buf->len + len) {				\
668167465Smp	if (buf->size == 0)					\
669167465Smp	    buf->size = 64; /* Arbitrary */			\
670167465Smp	while (buf->size < buf->len + len)			\
671167465Smp	    buf->size *= 2;					\
672167465Smp	buf->s = xrealloc(buf->s, buf->size * sizeof(*buf->s));	\
673167465Smp    }								\
674167465Smp    memcpy(buf->s + buf->len, s, len * sizeof(*buf->s));	\
675167465Smp    buf->len += len;						\
676167465Smp}								\
677167465Smp								\
678167465Smpvoid								\
679167465SmpSTRBUF##_append(struct STRBUF *buf, const CHAR *s)		\
680167465Smp{								\
681167465Smp    STRBUF##_appendn(buf, s, STRLEN(s));			\
682167465Smp}								\
683167465Smp								\
684167465SmpCHAR *								\
685167465SmpSTRBUF##_finish(struct STRBUF *buf)				\
686167465Smp{								\
687167465Smp    STRBUF##_append1(buf, 0);					\
688167465Smp    return xrealloc(buf->s, buf->len * sizeof(*buf->s));	\
689167465Smp}								\
690167465Smp								\
691167465Smpvoid								\
692167465SmpSTRBUF##_cleanup(void *xbuf)					\
693167465Smp{								\
694167465Smp    struct STRBUF *buf;						\
695167465Smp								\
696167465Smp    buf = xbuf;							\
697167465Smp    xfree(buf->s);						\
698167465Smp}								\
699167465Smp								\
700195609Smpvoid								\
701195609SmpSTRBUF##_free(void *xbuf)					\
702195609Smp{								\
703195609Smp    STRBUF##_cleanup(xbuf);					\
704195609Smp    xfree(xbuf);						\
705195609Smp}								\
706195609Smp								\
707167465Smpconst struct STRBUF STRBUF##_init /* = STRBUF##_INIT; */
708167465Smp
709167465SmpDO_STRBUF(strbuf, char, strlen);
710167465SmpDO_STRBUF(Strbuf, Char, Strlen);
711