tc.str.c revision 167465
1167465Smp/* $Header: /p/tcsh/cvsroot/tcsh/tc.str.c,v 3.26 2006/03/02 18:46:45 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
36145479Smp#include <limits.h>
3759243Sobrien
38167465SmpRCSID("$tcsh: tc.str.c,v 3.26 2006/03/02 18:46:45 christos Exp $")
39145479Smp
4059243Sobrien#define MALLOC_INCR	128
41145479Smp#ifdef WIDE_STRINGS
42145479Smp#define MALLOC_SURPLUS	MB_LEN_MAX /* Space for one multibyte character */
43145479Smp#else
44145479Smp#define MALLOC_SURPLUS	0
45145479Smp#endif
4659243Sobrien
47145479Smp#ifdef WIDE_STRINGS
48145479Smpsize_t
49145479Smpone_mbtowc(wchar_t *pwc, const char *s, size_t n)
50145479Smp{
51145479Smp    int len;
52145479Smp
53145479Smp    len = rt_mbtowc(pwc, s, n);
54145479Smp    if (len == -1) {
55145479Smp        mbtowc(NULL, NULL, 0);
56145479Smp	*pwc = (unsigned char)*s | INVALID_BYTE;
57145479Smp    }
58145479Smp    if (len <= 0)
59145479Smp	len = 1;
60145479Smp    return len;
61145479Smp}
62145479Smp
63145479Smpsize_t
64145479Smpone_wctomb(char *s, wchar_t wchar)
65145479Smp{
66145479Smp    int len;
67145479Smp
68145479Smp    if (wchar & INVALID_BYTE) {
69145479Smp	s[0] = wchar & 0xFF;
70145479Smp	len = 1;
71145479Smp    } else {
72145479Smp	len = wctomb(s, wchar);
73145479Smp	if (len == -1)
74145479Smp	    s[0] = wchar;
75145479Smp	if (len <= 0)
76145479Smp	    len = 1;
77145479Smp    }
78145479Smp    return len;
79145479Smp}
80167465Smp
81145479Smpint
82145479Smprt_mbtowc(wchar_t *pwc, const char *s, size_t n)
83145479Smp{
84145479Smp    int ret;
85145479Smp    char back[MB_LEN_MAX];
86145479Smp
87145479Smp    ret = mbtowc(pwc, s, n);
88145479Smp    if (ret > 0 && (wctomb(back, *pwc) != ret || memcmp(s, back, ret) != 0))
89145479Smp	ret = -1;
90145479Smp    return ret;
91145479Smp}
92167465Smp#endif
93145479Smp
94167465Smp#ifdef SHORT_STRINGS
9559243SobrienChar  **
96167465Smpblk2short(char **src)
9759243Sobrien{
9859243Sobrien    size_t     n;
99145479Smp    Char **sdst, **dst;
10059243Sobrien
10159243Sobrien    /*
10259243Sobrien     * Count
10359243Sobrien     */
10459243Sobrien    for (n = 0; src[n] != NULL; n++)
10559243Sobrien	continue;
106167465Smp    sdst = dst = xmalloc((n + 1) * sizeof(Char *));
10759243Sobrien
10859243Sobrien    for (; *src != NULL; src++)
10959243Sobrien	*dst++ = SAVE(*src);
11059243Sobrien    *dst = NULL;
11159243Sobrien    return (sdst);
11259243Sobrien}
11359243Sobrien
11459243Sobrienchar  **
115167465Smpshort2blk(Char **src)
11659243Sobrien{
11759243Sobrien    size_t     n;
118145479Smp    char **sdst, **dst;
11959243Sobrien
12059243Sobrien    /*
12159243Sobrien     * Count
12259243Sobrien     */
12359243Sobrien    for (n = 0; src[n] != NULL; n++)
12459243Sobrien	continue;
125167465Smp    sdst = dst = xmalloc((n + 1) * sizeof(char *));
12659243Sobrien
12759243Sobrien    for (; *src != NULL; src++)
12859243Sobrien	*dst++ = strsave(short2str(*src));
12959243Sobrien    *dst = NULL;
13059243Sobrien    return (sdst);
13159243Sobrien}
13259243Sobrien
13359243SobrienChar   *
134167465Smpstr2short(const char *src)
13559243Sobrien{
136167465Smp    static struct Strbuf buf; /* = Strbuf_INIT; */
13759243Sobrien
13859243Sobrien    if (src == NULL)
13959243Sobrien	return (NULL);
14059243Sobrien
141167465Smp    buf.len = 0;
142167465Smp    while (*src) {
143167465Smp	Char wc;
14459243Sobrien
145167465Smp	src += one_mbtowc(&wc, src, MB_LEN_MAX);
146167465Smp	Strbuf_append1(&buf, wc);
14759243Sobrien    }
148167465Smp    Strbuf_terminate(&buf);
149167465Smp    return buf.s;
15059243Sobrien}
15159243Sobrien
15259243Sobrienchar   *
153167465Smpshort2str(const Char *src)
15459243Sobrien{
15559243Sobrien    static char *sdst = NULL;
15659243Sobrien    static size_t dstsize = 0;
157145479Smp    char *dst, *edst;
15859243Sobrien
15959243Sobrien    if (src == NULL)
16059243Sobrien	return (NULL);
16159243Sobrien
16259243Sobrien    if (sdst == NULL) {
16359243Sobrien	dstsize = MALLOC_INCR;
164167465Smp	sdst = xmalloc((dstsize + MALLOC_SURPLUS) * sizeof(char));
16559243Sobrien    }
16659243Sobrien    dst = sdst;
16759243Sobrien    edst = &dst[dstsize];
16859243Sobrien    while (*src) {
169145479Smp	dst += one_wctomb(dst, *src & CHAR);
170145479Smp	src++;
171145479Smp	if (dst >= edst) {
17259243Sobrien	    dstsize += MALLOC_INCR;
173167465Smp	    sdst = xrealloc(sdst, (dstsize + MALLOC_SURPLUS) * sizeof(char));
17459243Sobrien	    edst = &sdst[dstsize];
17559243Sobrien	    dst = &edst[-MALLOC_INCR];
17659243Sobrien	}
17759243Sobrien    }
17859243Sobrien    *dst = 0;
17959243Sobrien    return (sdst);
18059243Sobrien}
18159243Sobrien
182145479Smp#ifndef WIDE_STRINGS
18359243SobrienChar   *
184167465Smps_strcpy(Char *dst, const Char *src)
18559243Sobrien{
186145479Smp    Char *sdst;
18759243Sobrien
18859243Sobrien    sdst = dst;
18959243Sobrien    while ((*dst++ = *src++) != '\0')
19059243Sobrien	continue;
19159243Sobrien    return (sdst);
19259243Sobrien}
19359243Sobrien
19459243SobrienChar   *
195167465Smps_strncpy(Char *dst, const Char *src, size_t n)
19659243Sobrien{
197145479Smp    Char *sdst;
19859243Sobrien
19959243Sobrien    if (n == 0)
20059243Sobrien	return(dst);
20159243Sobrien
20259243Sobrien    sdst = dst;
20359243Sobrien    do
20459243Sobrien	if ((*dst++ = *src++) == '\0') {
20559243Sobrien	    while (--n != 0)
20659243Sobrien		*dst++ = '\0';
20759243Sobrien	    return(sdst);
20859243Sobrien	}
20959243Sobrien    while (--n != 0);
21059243Sobrien    return (sdst);
21159243Sobrien}
21259243Sobrien
21359243SobrienChar   *
214167465Smps_strcat(Char *dst, const Char *src)
21559243Sobrien{
216167465Smp    Strcpy(Strend(dst), src);
217167465Smp    return dst;
21859243Sobrien}
21959243Sobrien
22059243Sobrien#ifdef NOTUSED
22159243SobrienChar   *
222167465Smps_strncat(Char *dst, const Char *src, size_t n)
22359243Sobrien{
224145479Smp    Char *sdst;
22559243Sobrien
22659243Sobrien    if (n == 0)
22759243Sobrien	return (dst);
22859243Sobrien
22959243Sobrien    sdst = dst;
23059243Sobrien
231167465Smp    while (*dst)
232167465Smp	dst++;
23359243Sobrien
23459243Sobrien    do
23559243Sobrien	if ((*dst++ = *src++) == '\0')
23659243Sobrien	    return(sdst);
23759243Sobrien    while (--n != 0)
23859243Sobrien	continue;
23959243Sobrien
24059243Sobrien    *dst = '\0';
24159243Sobrien    return (sdst);
24259243Sobrien}
24359243Sobrien
24459243Sobrien#endif
24559243Sobrien
24659243SobrienChar   *
247167465Smps_strchr(const Char *str, int ch)
24859243Sobrien{
24959243Sobrien    do
25059243Sobrien	if (*str == ch)
251145479Smp	    return ((Char *)(intptr_t)str);
25259243Sobrien    while (*str++);
25359243Sobrien    return (NULL);
25459243Sobrien}
25559243Sobrien
25659243SobrienChar   *
257167465Smps_strrchr(const Char *str, int ch)
25859243Sobrien{
259145479Smp    const Char *rstr;
26059243Sobrien
26159243Sobrien    rstr = NULL;
26259243Sobrien    do
26359243Sobrien	if (*str == ch)
26459243Sobrien	    rstr = str;
26559243Sobrien    while (*str++);
266145479Smp    return ((Char *)(intptr_t)rstr);
26759243Sobrien}
26859243Sobrien
26959243Sobriensize_t
270167465Smps_strlen(const Char *str)
27159243Sobrien{
272145479Smp    size_t n;
27359243Sobrien
27459243Sobrien    for (n = 0; *str++; n++)
27559243Sobrien	continue;
27659243Sobrien    return (n);
27759243Sobrien}
27859243Sobrien
27959243Sobrienint
280167465Smps_strcmp(const Char *str1, const Char *str2)
28159243Sobrien{
28259243Sobrien    for (; *str1 && *str1 == *str2; str1++, str2++)
28359243Sobrien	continue;
28459243Sobrien    /*
28559243Sobrien     * The following case analysis is necessary so that characters which look
28659243Sobrien     * negative collate low against normal characters but high against the
28759243Sobrien     * end-of-string NUL.
28859243Sobrien     */
28959243Sobrien    if (*str1 == '\0' && *str2 == '\0')
29059243Sobrien	return (0);
29159243Sobrien    else if (*str1 == '\0')
29259243Sobrien	return (-1);
29359243Sobrien    else if (*str2 == '\0')
29459243Sobrien	return (1);
29559243Sobrien    else
29659243Sobrien	return (*str1 - *str2);
29759243Sobrien}
29859243Sobrien
29959243Sobrienint
300167465Smps_strncmp(const Char *str1, const Char *str2, size_t n)
30159243Sobrien{
30259243Sobrien    if (n == 0)
30359243Sobrien	return (0);
30459243Sobrien    do {
30559243Sobrien	if (*str1 != *str2) {
30659243Sobrien	    /*
30759243Sobrien	     * The following case analysis is necessary so that characters
30859243Sobrien	     * which look negative collate low against normal characters
30959243Sobrien	     * but high against the end-of-string NUL.
31059243Sobrien	     */
31159243Sobrien	    if (*str1 == '\0')
31259243Sobrien		return (-1);
31359243Sobrien	    else if (*str2 == '\0')
31459243Sobrien		return (1);
31559243Sobrien	    else
31659243Sobrien		return (*str1 - *str2);
31759243Sobrien	}
31859243Sobrien        if (*str1 == '\0')
31959243Sobrien	    return(0);
32059243Sobrien	str1++, str2++;
32159243Sobrien    } while (--n != 0);
32259243Sobrien    return(0);
32359243Sobrien}
324145479Smp#endif /* not WIDE_STRINGS */
32559243Sobrien
326131962Smpint
327167465Smps_strcasecmp(const Char *str1, const Char *str2)
328131962Smp{
329145479Smp#ifdef WIDE_STRINGS
330145479Smp    wchar_t l1 = 0, l2 = 0;
331145479Smp    for (; *str1 && ((*str1 == *str2 && (l1 = l2 = 0) == 0) ||
332145479Smp	(l1 = towlower(*str1)) == (l2 = towlower(*str2))); str1++, str2++)
333145479Smp	continue;
334145479Smp
335145479Smp#else
336131962Smp    unsigned char c1, c2, l1 = 0, l2 = 0;
337131962Smp    for (; *str1 && ((*str1 == *str2 && (l1 = l2 = 0) == 0) ||
338131962Smp	((c1 = (unsigned char)*str1) == *str1 &&
339131962Smp	 (c2 = (unsigned char)*str2) == *str2 &&
340131962Smp	(l1 = tolower(c1)) == (l2 = tolower(c2)))); str1++, str2++)
341131962Smp	continue;
342145479Smp#endif
343131962Smp    /*
344131962Smp     * The following case analysis is necessary so that characters which look
345131962Smp     * negative collate low against normal characters but high against the
346131962Smp     * end-of-string NUL.
347131962Smp     */
348131962Smp    if (*str1 == '\0' && *str2 == '\0')
349131962Smp	return (0);
350131962Smp    else if (*str1 == '\0')
351131962Smp	return (-1);
352131962Smp    else if (*str2 == '\0')
353131962Smp	return (1);
354131962Smp    else if (l1 == l2)	/* They are zero when they are equal */
355131962Smp	return (*str1 - *str2);
356131962Smp    else
357131962Smp	return (l1 - l2);
358131962Smp}
359131962Smp
36059243SobrienChar   *
361167465Smps_strnsave(const Char *s, size_t len)
36259243Sobrien{
363167465Smp    Char *n;
364167465Smp
365167465Smp    n = xmalloc((len + 1) * sizeof (*n));
366167465Smp    memcpy(n, s, len * sizeof (*n));
367167465Smp    n[len] = '\0';
368167465Smp    return n;
369167465Smp}
370167465Smp
371167465SmpChar   *
372167465Smps_strsave(const Char *s)
373167465Smp{
37459243Sobrien    Char   *n;
375167465Smp    size_t size;
37659243Sobrien
377167465Smp    if (s == NULL)
37859243Sobrien	s = STRNULL;
379167465Smp    size = (Strlen(s) + 1) * sizeof(*n);
380167465Smp    n = xmalloc(size);
381167465Smp    memcpy(n, s, size);
38259243Sobrien    return (n);
38359243Sobrien}
38459243Sobrien
38559243SobrienChar   *
386167465Smps_strspl(const Char *cp, const Char *dp)
38759243Sobrien{
388167465Smp    Char *res, *ep;
389167465Smp    const Char *p, *q;
39059243Sobrien
39159243Sobrien    if (!cp)
39259243Sobrien	cp = STRNULL;
39359243Sobrien    if (!dp)
39459243Sobrien	dp = STRNULL;
395167465Smp    for (p = cp; *p++;)
39659243Sobrien	continue;
397167465Smp    for (q = dp; *q++;)
39859243Sobrien	continue;
399167465Smp    res = xmalloc(((p - cp) + (q - dp) - 1) * sizeof(Char));
400167465Smp    for (ep = res, q = cp; (*ep++ = *q++) != '\0';)
40159243Sobrien	continue;
402167465Smp    for (ep--, q = dp; (*ep++ = *q++) != '\0';)
40359243Sobrien	continue;
404167465Smp    return (res);
40559243Sobrien}
40659243Sobrien
40759243SobrienChar   *
408167465Smps_strend(const Char *cp)
40959243Sobrien{
41059243Sobrien    if (!cp)
411145479Smp	return ((Char *)(intptr_t) cp);
41259243Sobrien    while (*cp)
41359243Sobrien	cp++;
414145479Smp    return ((Char *)(intptr_t) cp);
41559243Sobrien}
41659243Sobrien
41759243SobrienChar   *
418167465Smps_strstr(const Char *s, const Char *t)
41959243Sobrien{
42059243Sobrien    do {
421145479Smp	const Char *ss = s;
422145479Smp	const Char *tt = t;
42359243Sobrien
42459243Sobrien	do
42559243Sobrien	    if (*tt == '\0')
426145479Smp		return ((Char *)(intptr_t) s);
42759243Sobrien	while (*ss++ == *tt++);
42859243Sobrien    } while (*s++ != '\0');
42959243Sobrien    return (NULL);
43059243Sobrien}
43159243Sobrien
432167465Smp#else /* !SHORT_STRINGS */
433167465Smpchar *
434167465Smpcaching_strip(const char *s)
435167465Smp{
436167465Smp    static char *buf = NULL;
437167465Smp    static size_t buf_size = 0;
438167465Smp    size_t size;
43959243Sobrien
440167465Smp    if (s == NULL)
441167465Smp      return NULL;
442167465Smp    size = strlen(s) + 1;
443167465Smp    if (buf_size < size) {
444167465Smp	buf = xrealloc(buf, size);
445167465Smp	buf_size = size;
446167465Smp    }
447167465Smp    memcpy(buf, s, size);
448167465Smp    strip(buf);
449167465Smp    return buf;
450167465Smp}
451167465Smp#endif
452167465Smp
45359243Sobrienchar   *
454167465Smpshort2qstr(const Char *src)
45559243Sobrien{
45659243Sobrien    static char *sdst = NULL;
45759243Sobrien    static size_t dstsize = 0;
458145479Smp    char *dst, *edst;
45959243Sobrien
46059243Sobrien    if (src == NULL)
46159243Sobrien	return (NULL);
46259243Sobrien
46359243Sobrien    if (sdst == NULL) {
46459243Sobrien	dstsize = MALLOC_INCR;
465167465Smp	sdst = xmalloc((dstsize + MALLOC_SURPLUS) * sizeof(char));
46659243Sobrien    }
46759243Sobrien    dst = sdst;
46859243Sobrien    edst = &dst[dstsize];
46959243Sobrien    while (*src) {
47059243Sobrien	if (*src & QUOTE) {
47159243Sobrien	    *dst++ = '\\';
47259243Sobrien	    if (dst == edst) {
47359243Sobrien		dstsize += MALLOC_INCR;
474167465Smp		sdst = xrealloc(sdst,
475167465Smp				(dstsize + MALLOC_SURPLUS) * sizeof(char));
47659243Sobrien		edst = &sdst[dstsize];
47759243Sobrien		dst = &edst[-MALLOC_INCR];
47859243Sobrien	    }
47959243Sobrien	}
480145479Smp	dst += one_wctomb(dst, *src & CHAR);
481145479Smp	src++;
482145479Smp	if (dst >= edst) {
48359243Sobrien	    dstsize += MALLOC_INCR;
484167465Smp	    sdst = xrealloc(sdst, (dstsize + MALLOC_SURPLUS) * sizeof(char));
48559243Sobrien	    edst = &sdst[dstsize];
48659243Sobrien	    dst = &edst[-MALLOC_INCR];
48759243Sobrien	}
48859243Sobrien    }
48959243Sobrien    *dst = 0;
49059243Sobrien    return (sdst);
49159243Sobrien}
492167465Smp
493167465Smpstatic void
494167465Smpbb_store(struct blk_buf *bb, Char *str)
495167465Smp{
496167465Smp    if (bb->len == bb->size) { /* Keep space for terminating NULL */
497167465Smp	if (bb->size == 0)
498167465Smp	    bb->size = 16; /* Arbitrary */
499167465Smp	else
500167465Smp	    bb->size *= 2;
501167465Smp	bb->vec = xrealloc(bb->vec, bb->size * sizeof (*bb->vec));
502167465Smp    }
503167465Smp    bb->vec[bb->len] = str;
504167465Smp}
505167465Smp
506167465Smpvoid
507167465Smpbb_append(struct blk_buf *bb, Char *str)
508167465Smp{
509167465Smp    bb_store(bb, str);
510167465Smp    bb->len++;
511167465Smp}
512167465Smp
513167465Smpvoid
514167465Smpbb_cleanup(void *xbb)
515167465Smp{
516167465Smp    struct blk_buf *bb;
517167465Smp    size_t i;
518167465Smp
519167465Smp    bb = xbb;
520167465Smp    for (i = 0; i < bb->len; i++)
521167465Smp	xfree(bb->vec[i]);
522167465Smp    xfree(bb->vec);
523167465Smp}
524167465Smp
525167465SmpChar **
526167465Smpbb_finish(struct blk_buf *bb)
527167465Smp{
528167465Smp    bb_store(bb, NULL);
529167465Smp    return xrealloc(bb->vec, (bb->len + 1) * sizeof (*bb->vec));
530167465Smp}
531167465Smp
532167465Smp#define DO_STRBUF(STRBUF, CHAR, STRLEN)				\
533167465Smpstatic void							\
534167465SmpSTRBUF##_store1(struct STRBUF *buf, CHAR c)			\
535167465Smp{								\
536167465Smp    if (buf->size == buf->len) {				\
537167465Smp	if (buf->size == 0)					\
538167465Smp	    buf->size = 64; /* Arbitrary */			\
539167465Smp	else							\
540167465Smp	    buf->size *= 2;					\
541167465Smp	buf->s = xrealloc(buf->s, buf->size * sizeof(*buf->s));	\
542167465Smp    }								\
543167465Smp    buf->s[buf->len] = c;					\
544167465Smp}								\
545167465Smp								\
546167465Smp/* Like strbuf_append1(buf, '\0'), but don't advance len */	\
547167465Smpvoid								\
548167465SmpSTRBUF##_terminate(struct STRBUF *buf)				\
549167465Smp{								\
550167465Smp    STRBUF##_store1(buf, '\0');					\
551167465Smp}								\
552167465Smp								\
553167465Smpvoid								\
554167465SmpSTRBUF##_append1(struct STRBUF *buf, CHAR c)			\
555167465Smp{								\
556167465Smp    STRBUF##_store1(buf, c);					\
557167465Smp    buf->len++;							\
558167465Smp}								\
559167465Smp								\
560167465Smpvoid								\
561167465SmpSTRBUF##_appendn(struct STRBUF *buf, const CHAR *s, size_t len)	\
562167465Smp{								\
563167465Smp    if (buf->size < buf->len + len) {				\
564167465Smp	if (buf->size == 0)					\
565167465Smp	    buf->size = 64; /* Arbitrary */			\
566167465Smp	while (buf->size < buf->len + len)			\
567167465Smp	    buf->size *= 2;					\
568167465Smp	buf->s = xrealloc(buf->s, buf->size * sizeof(*buf->s));	\
569167465Smp    }								\
570167465Smp    memcpy(buf->s + buf->len, s, len * sizeof(*buf->s));	\
571167465Smp    buf->len += len;						\
572167465Smp}								\
573167465Smp								\
574167465Smpvoid								\
575167465SmpSTRBUF##_append(struct STRBUF *buf, const CHAR *s)		\
576167465Smp{								\
577167465Smp    STRBUF##_appendn(buf, s, STRLEN(s));			\
578167465Smp}								\
579167465Smp								\
580167465SmpCHAR *								\
581167465SmpSTRBUF##_finish(struct STRBUF *buf)				\
582167465Smp{								\
583167465Smp    STRBUF##_append1(buf, 0);					\
584167465Smp    return xrealloc(buf->s, buf->len * sizeof(*buf->s));	\
585167465Smp}								\
586167465Smp								\
587167465Smpvoid								\
588167465SmpSTRBUF##_cleanup(void *xbuf)					\
589167465Smp{								\
590167465Smp    struct STRBUF *buf;						\
591167465Smp								\
592167465Smp    buf = xbuf;							\
593167465Smp    xfree(buf->s);						\
594167465Smp}								\
595167465Smp								\
596167465Smpconst struct STRBUF STRBUF##_init /* = STRBUF##_INIT; */
597167465Smp
598167465SmpDO_STRBUF(strbuf, char, strlen);
599167465SmpDO_STRBUF(Strbuf, Char, Strlen);
600