1 /*************************************************************************
2 * TinyFugue - programmable mud client
3 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
4 *
5 * TinyFugue (aka "tf") is protected under the terms of the GNU
6 * General Public License. See the file "COPYING" for details.
7 ************************************************************************/
8
9 /*********************************************************************
10 * Fugue dynamically allocated string handling *
11 * *
12 * dSinit() must be used to initialize a dynamically allocated *
13 * string, and dSfree() to free up the contents. To minimize *
14 * resize()s, initialize the size to be a little more than the *
15 * median expected size. *
16 *********************************************************************/
17
18 #include "tfconfig.h"
19 #include "port.h"
20 #include "malloc.h"
21 #include "tf.h"
22 #include "signals.h" /* core() */
23
24 static String *Stringpool = NULL; /* freelist */
25 conString blankline[1] = { STRING_LITERAL("") };
26
27 #if USE_MMALLOC
28 # define MD(str) (str->md)
29 #else
30 # define MD(str) (NULL)
31 #endif
32
33 #define lcheck(str, file, line) \
34 do { if ((str)->len >= (str)->size) resize(str, file, line); } while(0)
35
36 #if USE_DMALLOC
37 # define Smalloc(str, size) \
38 (str->static_struct ? \
39 mmalloc(MD(str), size) : xmalloc(MD(str), size, file, line))
40 # define Srealloc(str, ptr, size) \
41 (str->static_struct ? \
42 mrealloc(MD(str), ptr, size) : xrealloc(MD(str), ptr, size, file, line))
43 # define Sfree(str, ptr) \
44 (str->static_struct ? mfree(MD(str), ptr) : xfree(MD(str), ptr, file, line))
45 #else
46 # define Smalloc(str, size) xmalloc(MD(str), size, file, line)
47 # define Srealloc(str, ptr, size) xrealloc(MD(str), ptr, size, file, line)
48 # define Sfree(str, ptr) xfree(MD(str), ptr, file, line)
49 #endif
50
51 static void resize(String *str, const char *file, int line);
52
53
54 /* create charattrs and initialize first n elements */
check_charattrs(String * str,int n,cattr_t cattrs,const char * file,int line)55 void check_charattrs(String *str, int n, cattr_t cattrs,
56 const char *file, int line)
57 {
58 if (!str->charattrs) {
59 cattrs &= F_HWRITE;
60 str->charattrs = Smalloc(str, sizeof(cattr_t) * str->size);
61 while (--n >= 0)
62 str->charattrs[n] = cattrs;
63 }
64 }
65
66 /* copy old trailing charattr to new tail */
extend_charattrs(String * str,int oldlen,cattr_t cattrs)67 void extend_charattrs(String *str, int oldlen, cattr_t cattrs)
68 {
69 int i;
70
71 for (i = oldlen+1; i < str->len; i++)
72 str->charattrs[i] = str->charattrs[oldlen] | cattrs;
73 str->charattrs[str->len] = str->charattrs[oldlen];
74 str->charattrs[oldlen] = cattrs;
75 }
76
resize(String * str,const char * file,int line)77 static void resize(String *str, const char *file, int line)
78 {
79 if (!str->resizable) {
80 internal_error2(file, line, str->file, str->line, "");
81 core("resize: data not resizable", file, line, 0);
82 }
83 if (str->size < 0) {
84 internal_error2(file, line, str->file, str->line, "");
85 core("resize freed string", file, line, 0);
86 }
87 str->size = (str->len / ALLOCSIZE + 1) * ALLOCSIZE;
88
89 str->data = Srealloc(str, str->data, str->size);
90
91 if (str->charattrs) {
92 str->charattrs =
93 Srealloc(str, str->charattrs, sizeof(cattr_t) * str->size);
94 }
95 }
96
97 /* dSinit()
98 * data && len >= 0: allocate exactly len, copy data
99 * data && len < 0: allocate exactly strlen(data), copy data
100 * !data && len < 0: don't allocate
101 * !data && len >= 0: allocate rounded len (allocating for 0 sometimes
102 * wastes an allocation, but not doing it can cause
103 * problems in code that expects (str->data != NULL))
104 * md is used only if (!str && data).
105 */
dSinit(String * str,const char * data,int len,attr_t attrs,void * md,const char * file,int line)106 String *dSinit(
107 String *str, /* if NULL, a String will be allocated */
108 const char *data, /* optional initializer data */
109 int len, /* optional length of data */
110 attr_t attrs, /* line display attributes */
111 void *md, /* mmalloc descriptor */
112 const char *file,
113 int line)
114 {
115 if (data && len < 0)
116 len = strlen(data);
117 if (!str) {
118 if (data) {
119 /* allocate String and data in one chunk for better locality */
120 #if USE_MMALLOC
121 if (md) str = dmalloc(md, sizeof(*str) + len + 1, file, line);
122 if (!md || !str)
123 #endif
124 {
125 md = NULL;
126 str = xmalloc(NULL, sizeof(*str) + len + 1, file, line);
127 }
128 str->data = (char*)str + sizeof(*str);
129 str->dynamic_data = 0;
130 } else {
131 palloc(str, String, Stringpool, data, file, line);
132 str->dynamic_data = 1;
133 }
134 str->dynamic_struct = 1;
135 str->static_struct = 0;
136 } else {
137 str->dynamic_struct = 0;
138 str->static_struct = 0;
139 if (data) {
140 str->data = Smalloc(str, len + 1);
141 str->dynamic_data = 1;
142 }
143 }
144 if (data) {
145 #if USE_MMALLOC
146 str->md = md;
147 #endif
148 str->resizable = 0;
149 str->len = len;
150 str->size = len + 1;
151 memcpy(str->data, data, str->len);
152 str->data[str->len] = '\0';
153 } else if (len >= 0) {
154 #if USE_MMALLOC
155 str->md = NULL;
156 #endif
157 str->resizable = 1;
158 str->dynamic_data = 1;
159 str->size = ((len + ALLOCSIZE) / ALLOCSIZE) * ALLOCSIZE;
160 str->data = Smalloc(str, str->size);
161 str->len = 0;
162 str->data[str->len] = '\0';
163 } else {
164 #if USE_MMALLOC
165 str->md = NULL;
166 #endif
167 str->resizable = 1;
168 str->dynamic_data = 1;
169 str->data = NULL;
170 str->size = str->len = 0;
171 }
172 str->attrs = attrs;
173 str->charattrs = NULL;
174 str->links = 0;
175 str->time.tv_sec = str->time.tv_usec = -1; /* caller will set if needed */
176 str->file = file;
177 str->line = line;
178 return str;
179 }
180
181 /* dSfree() assumes links has been decremented and tested by Stringfree() */
dSfree(String * str,const char * file,int line)182 void dSfree(String *str, const char *file, int line)
183 {
184 if (str->links < 0) {
185 /* While it would be useful to print str->file, it may have been
186 * clobbered the first time str was freed, so is unsafe. */
187 internal_error(file, line,
188 "dSfree: links==%d, data=\"%.32b\"", str->links, '\"', str->data);
189 core("dSfree: links==%d", file, line, str->links);
190 }
191
192 if (str->charattrs) Sfree(str, str->charattrs);
193 if (str->dynamic_data && str->data)
194 Sfree(str, str->data);
195
196 str->size = -42; /* break lcheck if str is reused without dSinit */
197 str->len = 0;
198 if (str->dynamic_struct) {
199 if (!str->dynamic_data) /* str and data were alloced together */
200 Sfree(str, str);
201 #if USE_MMALLOC
202 else if (str->md)
203 Sfree(str, str);
204 #endif
205 else
206 pfree_fl(str, Stringpool, data, file, line);
207 }
208 }
209
dSadd(String * str,int c,const char * file,int line)210 String *dSadd(String *str, int c, const char *file, int line)
211 {
212 str->len++;
213 lcheck(str, file, line);
214 str->data[str->len - 1] = c;
215 str->data[str->len] = '\0';
216 if (str->charattrs) {
217 str->charattrs[str->len] = str->charattrs[str->len-1];
218 }
219 return str;
220 }
221
dSnadd(String * str,int c,int n,const char * file,int line)222 String *dSnadd(String *str, int c, int n, const char *file, int line)
223 {
224 int oldlen = str->len;
225 if (n < 0) core("dSnadd: n==%ld", file, line, (long)n);
226 str->len += n;
227 lcheck(str, file, line);
228 for (n = oldlen; n < str->len; n++)
229 str->data[n] = c;
230 str->data[str->len] = '\0';
231 if (str->charattrs) extend_charattrs(str, oldlen, 0);
232 return str;
233 }
234
dStrunc(String * str,int len,const char * file,int line)235 String *dStrunc(String *str, int len, const char *file, int line)
236 {
237 /* if (str->size && str->len < len) return str; */
238 unsigned int oldlen = str->len;
239 str->len = len;
240 lcheck(str, file, line);
241 if (len <= oldlen) {
242 str->data[len] = '\0';
243 } else {
244 str->len = oldlen;
245 }
246 return str;
247 }
248
dSshift(String * str,int start,const char * file,int line)249 String *dSshift(String *str, int start, const char *file, int line)
250 {
251 if (start < 0)
252 core("dSshift: start==%ld (<0)", file, line, (long)start);
253 if (start > str->len)
254 core("dSshift: start==%ld (>str->len)", file, line, (long)start);
255 /* if (start == str->len)
256 return dStrunc(str, 0, file, line); */
257 if (start == 0)
258 return str;
259 str->len = str->len - start;
260 lcheck(str, file, line); /* Don't really need this... */
261 memmove(str->data, str->data + start, str->len + 1);
262 return str;
263 }
264
dScpy(String * dest,const char * src,const char * file,int line)265 String *dScpy(String *dest, const char *src, const char *file, int line)
266 {
267 dest->len = strlen(src);
268 if (dest->charattrs) {
269 Sfree(dest, dest->charattrs);
270 dest->charattrs = NULL;
271 }
272 lcheck(dest, file, line);
273 memcpy(dest->data, src, dest->len + 1);
274 return dest;
275 }
276
dSncpy(String * dest,const char * src,int n,const char * file,int line)277 String *dSncpy(String *dest, const char *src, int n, const char *file, int line)
278 {
279 int len = strlen(src);
280
281 if (n < 0) core("dSncpy: n==%ld", file, line, (long)n);
282 if (n > len) n = len;
283 dest->len = n;
284 if (dest->charattrs) {
285 Sfree(dest, dest->charattrs);
286 dest->charattrs = NULL;
287 }
288 lcheck(dest, file, line);
289 memcpy(dest->data, src, n);
290 dest->data[n] = '\0';
291 return dest;
292 }
293
dSScpy(String * dest,const conString * src,const char * file,int line)294 String *dSScpy(String *dest, const conString *src, const char *file, int line)
295 {
296 if (dest->charattrs && !src->charattrs) {
297 Sfree(dest, dest->charattrs);
298 dest->charattrs = NULL;
299 }
300 dest->len = src->len;
301 lcheck(dest, file, line);
302 memcpy(dest->data, src->data ? src->data : "", src->len+1);
303 if (src->charattrs) {
304 check_charattrs(dest, 0, 0, file, line);
305 memcpy(dest->charattrs, src->charattrs, sizeof(cattr_t) * (src->len+1));
306 }
307 dest->attrs = src->attrs;
308 return dest;
309 }
310
dScat(String * dest,const char * src,const char * file,int line)311 String *dScat(String *dest, const char *src, const char *file, int line)
312 {
313 int oldlen = dest->len;
314
315 dest->len += strlen(src);
316 lcheck(dest, file, line);
317 memcpy(dest->data + oldlen, src, dest->len - oldlen + 1);
318 if (dest->charattrs) extend_charattrs(dest, oldlen, 0);
319 return dest;
320 }
321
dSSoncat(String * dest,const conString * src,int start,int len,const char * file,int line)322 String *dSSoncat(String *dest, const conString *src, int start, int len,
323 const char *file, int line)
324 {
325 int oldlen = dest->len;
326 int i, j;
327 cattr_t cattrs;
328
329 if (len < 0)
330 len = src->len - start;
331 dest->len += len;
332 lcheck(dest, file, line);
333 memcpy(dest->data + oldlen, src->data ? src->data + start: "", len);
334 dest->data[dest->len] = '\0';
335
336 if (src->charattrs || dest->charattrs || src->attrs != dest->attrs) {
337 if (dest->charattrs && dest->attrs) {
338 cattrs = attr2cattr(dest->attrs);
339 for (i = 0; i < oldlen; i++)
340 dest->charattrs[i] = adj_attr(cattrs, dest->charattrs[i]);
341 } else {
342 check_charattrs(dest, oldlen, dest->attrs, file, line);
343 }
344 dest->attrs = 0;
345
346 if (src->charattrs && src->attrs) {
347 cattrs = attr2cattr(src->attrs);
348 for (i = oldlen, j = start; i < dest->len; i++, j++)
349 dest->charattrs[i] = adj_attr(cattrs, src->charattrs[j]);
350 } else if (src->charattrs) {
351 memcpy(dest->charattrs + oldlen, src->charattrs + start,
352 sizeof(cattr_t) * len);
353 } else {
354 for (i = oldlen; i < dest->len; i++)
355 dest->charattrs[i] = src->attrs;
356 }
357 dest->charattrs[dest->len] = 0;
358 }
359
360 return dest;
361 }
362
363 /* slow version of dSncat, verifies that length of input >= n */
dSncat(String * dest,const char * src,int n,const char * file,int line)364 String *dSncat(String *dest, const char *src, int n, const char *file, int line)
365 {
366 int oldlen = dest->len;
367 int len = strlen(src);
368
369 if (n < 0) core("dSncat: n==%ld", file, line, (long)n);
370 if (n > len) n = len;
371 dest->len += n;
372 lcheck(dest, file, line);
373 memcpy(dest->data + oldlen, src, n);
374 dest->data[dest->len] = '\0';
375 if (dest->charattrs) extend_charattrs(dest, oldlen, 0);
376 return dest;
377 }
378
379 /* fast version of dSncat, assumes length of input >= n */
dSfncat(String * dest,const char * src,int n,const char * file,int line)380 String *dSfncat(String *dest, const char *src, int n, const char *file, int line)
381 {
382 unsigned int oldlen = dest->len;
383
384 if ((int)n < 0) core("dSfncat: n==%ld", file, line, (long)n);
385 dest->len += n;
386 lcheck(dest, file, line);
387 memcpy(dest->data + oldlen, src, n);
388 dest->data[dest->len] = '\0';
389 if (dest->charattrs) extend_charattrs(dest, oldlen, 0);
390 return dest;
391 }
392
Stringstriptrail(String * str)393 String *Stringstriptrail(String *str)
394 {
395 while (is_space(str->data[str->len - 1]))
396 --str->len;
397 str->data[str->len] = '\0';
398 return str;
399 }
400
401 /* like strcmp for Strings, extended to cover their attrs too. */
Stringcmp(const conString * s,const conString * t)402 int Stringcmp(const conString *s, const conString *t)
403 {
404 int retval, i;
405 attr_t s_attrs, t_attrs;
406 if (!s && !t) return 0;
407 if (!s && t) return -257;
408 if (s && !t) return 257;
409 if ((retval = strcmp(s->data, t->data)) != 0) return retval;
410 if (!s->charattrs && !t->charattrs) return s->attrs - t->attrs;
411 for (i = 0; i < s->len; i++) {
412 s_attrs = s->charattrs ? adj_attr(s->attrs, s->charattrs[i]) : s->attrs;
413 t_attrs = t->charattrs ? adj_attr(t->attrs, t->charattrs[i]) : t->attrs;
414 if (s_attrs != t_attrs) return s_attrs - t_attrs;
415 }
416 return 0;
417 }
418
419 #if USE_DMALLOC
free_dstring(void)420 void free_dstring(void)
421 {
422 pfreepool(String, Stringpool, data);
423 }
424 #endif
425
426