1/* $Id: mstring.c,v 1.9 2019/11/19 23:54:53 tom Exp $ */
2
3#include <stdlib.h>
4#include <stdio.h>
5#include <stdarg.h>
6#include <ctype.h>
7#include <string.h>
8#include "defs.h"
9
10/* parameters about string length.  HEAD is the starting size and
11** HEAD+TAIL should be a power of two */
12#define HEAD	24
13#define TAIL	8
14
15static char *buf_ptr;
16static size_t buf_len;
17
18void
19msprintf(struct mstring *s, const char *fmt, ...)
20{
21    va_list args;
22    size_t len;
23#ifdef HAVE_VSNPRINTF
24    int changed;
25#endif
26
27    if (!s || !s->base)
28	return;
29
30    if (buf_len == 0)
31    {
32	buf_ptr = malloc(buf_len = 4096);
33    }
34    if (buf_ptr == 0)
35    {
36	return;
37    }
38
39#ifdef HAVE_VSNPRINTF
40    do
41    {
42	va_start(args, fmt);
43	len = (size_t) vsnprintf(buf_ptr, buf_len, fmt, args);
44	va_end(args);
45	if ((changed = (len > buf_len)) != 0)
46	{
47	    char *new_ptr = realloc(buf_ptr, (buf_len * 3) / 2);
48	    if (new_ptr == 0)
49	    {
50		free(buf_ptr);
51		buf_ptr = 0;
52		return;
53	    }
54	    buf_ptr = new_ptr;
55	}
56    }
57    while (changed);
58#else
59    va_start(args, fmt);
60    len = (size_t) vsprintf(buf_ptr, fmt, args);
61    va_end(args);
62    if (len >= buf_len)
63	return;
64#endif
65
66    if (len > (size_t) (s->end - s->ptr))
67    {
68	char *new_base;
69	size_t cp = (size_t) (s->ptr - s->base);
70	size_t cl = (size_t) (s->end - s->base);
71	size_t nl = cl;
72	while (len > (nl - cp))
73	    nl = nl + nl + TAIL;
74	if ((new_base = realloc(s->base, nl)))
75	{
76	    s->base = new_base;
77	    s->ptr = s->base + cp;
78	    s->end = s->base + nl;
79	}
80	else
81	{
82	    free(s->base);
83	    s->base = 0;
84	    s->ptr = 0;
85	    s->end = 0;
86	    return;
87	}
88    }
89    memcpy(s->ptr, buf_ptr, len);
90    s->ptr += len;
91}
92
93int
94mputchar(struct mstring *s, int ch)
95{
96    if (!s || !s->base)
97	return ch;
98    if (s->ptr == s->end)
99    {
100	size_t len = (size_t) (s->end - s->base);
101	if ((s->base = realloc(s->base, len + len + TAIL)))
102	{
103	    s->ptr = s->base + len;
104	    s->end = s->base + len + len + TAIL;
105	}
106	else
107	{
108	    s->ptr = s->end = 0;
109	    return ch;
110	}
111    }
112    *s->ptr++ = (char)ch;
113    return ch;
114}
115
116struct mstring *
117msnew(void)
118{
119    struct mstring *n = TMALLOC(struct mstring, 1);
120
121    if (n)
122    {
123	if ((n->base = n->ptr = MALLOC(HEAD)) != 0)
124	{
125	    n->end = n->base + HEAD;
126	}
127	else
128	{
129	    free(n);
130	    n = 0;
131	}
132    }
133    return n;
134}
135
136struct mstring *
137msrenew(char *value)
138{
139    struct mstring *r = 0;
140    if (value != 0)
141    {
142	r = msnew();
143	r->base = value;
144	r->end = value + strlen(value);
145	r->ptr = r->end;
146    }
147    return r;
148}
149
150char *
151msdone(struct mstring *s)
152{
153    char *r = 0;
154    if (s)
155    {
156	mputc(s, 0);
157	r = s->base;
158	free(s);
159    }
160    return r;
161}
162
163#if defined(YYBTYACC)
164/* compare two strings, ignoring whitespace, except between two letters or
165** digits (and treat all of these as equal) */
166int
167strnscmp(const char *a, const char *b)
168{
169    while (1)
170    {
171	while (isspace(UCH(*a)))
172	    a++;
173	while (isspace(UCH(*b)))
174	    b++;
175	while (*a && *a == *b)
176	    a++, b++;
177	if (isspace(UCH(*a)))
178	{
179	    if (isalnum(UCH(a[-1])) && isalnum(UCH(*b)))
180		break;
181	}
182	else if (isspace(UCH(*b)))
183	{
184	    if (isalnum(UCH(b[-1])) && isalnum(UCH(*a)))
185		break;
186	}
187	else
188	    break;
189    }
190    return *a - *b;
191}
192
193unsigned int
194strnshash(const char *s)
195{
196    unsigned int h = 0;
197
198    while (*s)
199    {
200	if (!isspace(UCH(*s)))
201	    h = (h << 5) - h + (unsigned char)*s;
202	s++;
203    }
204    return h;
205}
206#endif
207
208#ifdef NO_LEAKS
209void
210mstring_leaks(void)
211{
212    free(buf_ptr);
213    buf_ptr = 0;
214    buf_len = 0;
215}
216#endif
217