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