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