1 /*
2 Z88DK Z80 Macro Assembler
3 
4 Dynamic strings based on vector.c
5 
6 Copyright (C) Gunther Strube, InterLogic 1993-99
7 Copyright (C) Paulo Custodio, 2011-2020
8 License: The Artistic License 2.0, http://www.perlfoundation.org/artistic_license_2_0
9 Repository: https://github.com/z88dk/z88dk
10 */
11 
12 #include "str.h"
13 #include <ctype.h>
14 #include <string.h>
15 
16 #ifdef _MSC_VER
17 #define vsnprintf _vsnprintf
18 #endif
19 
20 /*-----------------------------------------------------------------------------
21 *   initialize and delete
22 *----------------------------------------------------------------------------*/
23 
str_remove_data(Str * str)24 static void str_remove_data(Str *str)
25 {
26 	if (str && str->flag.data_alloc)
27 		m_free(str->data);
28 	str->data = NULL;
29 	str->flag.data_alloc = false;
30 }
31 
Str_new_(int size)32 Str *Str_new_(int size)
33 {
34 	Str *str;
35 
36 	check(size > 1, "size=%d", size);
37 
38 	str = m_new(Str);
39 	m_set_destructor(str, (destructor_t) str_remove_data);
40 
41 	str->data = m_malloc(size);
42 	m_set_in_collection(str->data);
43 	str->size = size;
44 
45 	str->flag.data_alloc = true;
46 	str->flag.header_alloc = true;
47 
48 	Str_clear(str);
49 
50 	return str;
51 error:
52 	return NULL;
53 }
54 
Str_delete_(Str * str)55 void Str_delete_(Str *str)
56 {
57 	if (str) {
58 		str_remove_data(str);
59 		if (str->flag.header_alloc)
60 			m_free(str);
61 	}
62 }
63 
64 /* expand string buffer if needed */
Str_reserve(Str * str,int size)65 void Str_reserve(Str *str, int size)
66 {
67 	char *new_data;
68 	int   new_size;
69 
70 	check_node(str);
71 
72 	if (str->len + size + 1 > str->size) {
73 		new_size = (1 + ((str->len + size + 1) / STR_SIZE)) * STR_SIZE;
74 
75 		if (str->flag.data_alloc) {
76 			str->data = m_realloc(str->data, new_size);
77 		}
78 		else {								/* points to char[], need to copy to heap */
79 			new_data = m_malloc(new_size);
80 			m_destroy_atexit(new_data);		/* no need to free explicitly */
81 
82 			memcpy(new_data, str->data, str->size);
83 			str->data = new_data;
84 
85 			str->flag.data_alloc = true;
86 		}
87 		str->size = new_size;
88 	}
89 error:;
90 }
91 
92 /*-----------------------------------------------------------------------------
93 *   Set strings
94 *----------------------------------------------------------------------------*/
95 
96 /* clear the string, keep allocated space */
Str_clear(Str * str)97 void Str_clear(Str *str)
98 {
99 	str->len = 0;
100 	str->data[0] = '\0';
101 }
102 
103 /* sync length in case string was modified in place */
Str_sync_len(Str * str)104 void Str_sync_len(Str *str)
105 {
106 	str->len = strlen(str->data);
107 }
108 
109 /* set / append bytes */
Str_set_bytes(Str * str,const char * source,int size)110 void Str_set_bytes(Str *str, const char *source, int size)
111 {
112 	Str_clear(str);
113 	Str_append_bytes(str, source, size);
114 }
115 
Str_append_bytes(Str * str,const char * source,int size)116 void Str_append_bytes(Str *str, const char *source, int size)
117 {
118 	/* expand string if needed */
119 	Str_reserve(str, size);
120 
121 	/* copy buffer and add null terminator */
122 	memcpy(str->data + str->len, source, size);
123 	str->len += size;
124 	str->data[str->len] = '\0';					/* add zero terminator */
125 }
126 
127 /* set / append string */
Str_set(Str * str,const char * source)128 void Str_set(Str *str, const char *source)
129 {
130 	Str_clear(str);
131 	Str_append(str, source);
132 }
133 
Str_append(Str * str,const char * source)134 void Str_append(Str *str, const char *source)
135 {
136 	Str_append_bytes(str, source, strlen(source));
137 }
138 
139 /* set / append substring */
Str_set_n(Str * str,const char * source,int count)140 void Str_set_n(Str *str, const char *source, int count)
141 {
142 	Str_clear(str);
143 	Str_append_n(str, source, count);
144 }
145 
Str_append_n(Str * str,const char * source,int count)146 void Str_append_n(Str *str, const char *source, int count)
147 {
148 	int num_copy = strlen(source);
149 
150 	Str_append_bytes(str, source, MIN(count, num_copy));
151 }
152 
153 /* set / append char */
Str_set_char(Str * str,char ch)154 void Str_set_char(Str *str, char ch)
155 {
156 	Str_clear(str);
157 	Str_append_char(str, ch);
158 }
159 
Str_append_char(Str * str,char ch)160 void Str_append_char(Str *str, char ch)
161 {
162 	/* expand string if needed */
163 	Str_reserve(str, 1);
164 
165 	/* add bytes */
166 	str->data[str->len++] = ch;
167 	str->data[str->len] = '\0';
168 }
169 
170 /* set / append with va_list argument */
Str_vsprintf(Str * str,const char * format,va_list argptr)171 void Str_vsprintf(Str *str, const char *format, va_list argptr)
172 {
173 	Str_clear(str);
174 	Str_append_vsprintf(str, format, argptr);
175 }
176 
Str_append_vsprintf(Str * str,const char * format,va_list argptr)177 void Str_append_vsprintf(Str *str, const char *format, va_list argptr)
178 {
179 	int free_space;      /* may be negative */
180 	int need_space;
181 	va_list savearg;	/* save argptr before new invocations of vsnprintf */
182 	bool ok;
183 
184 	va_copy(savearg, argptr);
185 	do
186 	{
187 		/* NOTE: Linux vsnprintf always terminates string; Win32 only if there is enough space */
188 		free_space = str->size - str->len;
189 
190 		if (free_space > 0)
191 			need_space = vsnprintf(str->data + str->len, free_space, format, argptr);
192 		else
193 			need_space = -1;
194 
195 		/* printed OK? */
196 		ok = need_space >= 0 && need_space < free_space;
197 
198 		if (ok)
199 		{
200 			str->len += need_space;
201 			str->data[str->len] = '\0';	/* string may not be terminated */
202 		}
203 		else
204 		{
205 			/* increase the size by STR_SIZE and retry */
206 			Str_reserve(str, str->size - str->len);
207 			va_copy(argptr, savearg);
208 		}
209 	} while (!ok);
210 }
211 
212 /* set / append with printf-like parameters */
Str_sprintf(Str * str,const char * format,...)213 void Str_sprintf(Str *str, const char *format, ...)
214 {
215 	va_list argptr;
216 
217 	va_start(argptr, format);
218 	Str_vsprintf(str, format, argptr);
219 	va_end(argptr);
220 }
221 
Str_append_sprintf(Str * str,const char * format,...)222 void Str_append_sprintf(Str *str, const char *format, ...)
223 {
224 	va_list argptr;
225 
226 	va_start(argptr, format);
227 	Str_append_vsprintf(str, format, argptr);
228 	va_end(argptr);
229 }
230 
231 /*-----------------------------------------------------------------------------
232 *   Modify strings
233 *----------------------------------------------------------------------------*/
234 
235 
236 /* get one line from input, convert end-of-line sequences,
237 *  return string including one LF character
238 *  return false on end of input */
Str_getline(Str * str,FILE * fp)239 bool Str_getline(Str *str, FILE *fp)
240 {
241 	int c1, c2;
242 
243 	Str_clear(str);
244 
245 	while ((c1 = getc(fp)) != EOF && c1 != '\n' && c1 != '\r')
246 		Str_append_char(str, c1);
247 
248 	if (c1 == EOF)
249 	{
250 		if (str->len > 0)				/* read some chars */
251 			Str_append_char(str, '\n');		/* missing newline at end of line */
252 	}
253 	else
254 	{
255 		Str_append_char(str, '\n');			/* end of line */
256 
257 		if ((c2 = getc(fp)) != EOF &&
258 			!((c1 == '\n' && c2 == '\r') ||
259 			  (c1 == '\r' && c2 == '\n')))
260 		{
261 			ungetc(c2, fp);					/* push back to input */
262 		}
263 	}
264 
265 	return str->len > 0 ? true : false;
266 }
267