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