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