1 /* Dynamically allocated strings
2
3 Copyright (c) 2001-2014 Free Software Foundation, Inc.
4
5 This file is part of GNU Zile.
6
7 GNU Zile is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 GNU Zile is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Zile; see the file COPYING. If not, write to the
19 Free Software Foundation, Fifth Floor, 51 Franklin Street, Boston,
20 MA 02111-1301, USA. */
21
22 #include <config.h>
23
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include "xalloc.h"
33 #include "xvasprintf.h"
34 #include "minmax.h"
35
36 #include "astr.h"
37
38 #define ALLOCATION_CHUNK_SIZE 16
39
40 /*
41 * The implementation of astr.
42 */
43 struct astr
44 {
45 char *text; /* The string buffer. */
46 size_t len; /* The length of the string. */
47 size_t maxlen; /* The buffer size. */
48 };
49
50 astr
astr_new(void)51 astr_new (void)
52 {
53 astr as;
54 as = (astr) XZALLOC (struct astr);
55 as->maxlen = ALLOCATION_CHUNK_SIZE;
56 as->len = 0;
57 as->text = (char *) xzalloc (as->maxlen + 1);
58 return as;
59 }
60
61 const_astr
const_astr_new_nstr(const char * s,size_t n)62 const_astr_new_nstr (const char *s, size_t n)
63 {
64 astr as;
65 as = (astr) XZALLOC (struct astr);
66 as->len = n;
67 as->text = (char *) s;
68 return as;
69 }
70
71 const char *
astr_cstr(const_astr as)72 astr_cstr (const_astr as)
73 {
74 return (const char *) (as->text);
75 }
76
77 size_t
astr_len(const_astr as)78 astr_len (const_astr as)
79 {
80 return as->len;
81 }
82
83 static
astr_set_len(astr as,size_t len)84 void astr_set_len (astr as, size_t len)
85 {
86 if (len > as->maxlen || len < as->maxlen / 2)
87 {
88 as->maxlen = len + ALLOCATION_CHUNK_SIZE;
89 as->text = xrealloc (as->text, as->maxlen + 1);
90 }
91 as->len = len;
92 as->text[as->len] = '\0';
93 }
94
95 astr
astr_cat_nstr(astr as,const char * s,size_t csize)96 astr_cat_nstr (astr as, const char *s, size_t csize)
97 {
98 assert (as != NULL);
99 size_t oldlen = as->len;
100 astr_set_len (as, as->len + csize);
101 memmove (as->text + oldlen, s, csize);
102 return as;
103 }
104
105 astr
astr_replace_nstr(astr as,size_t pos,const char * s,size_t size)106 astr_replace_nstr (astr as, size_t pos, const char *s, size_t size)
107 {
108 assert (as != NULL);
109 assert (pos <= as->len);
110 assert (size <= as->len - pos);
111 memmove (as->text + pos, s, size);
112 return as;
113 }
114
115 astr
astr_remove(astr as,size_t pos,size_t size)116 astr_remove (astr as, size_t pos, size_t size)
117 {
118 assert (as != NULL);
119 assert (pos <= as->len);
120 assert (size <= as->len - pos);
121 memmove (as->text + pos, as->text + pos + size, as->len - (pos + size));
122 astr_set_len (as, as->len - size);
123 return as;
124 }
125
126 astr
astr_insert(astr as,size_t pos,size_t size)127 astr_insert (astr as, size_t pos, size_t size)
128 {
129 assert (as != NULL);
130 assert (pos <= as->len);
131 assert (pos + size >= MAX (pos, size)); /* Check for overflow. */
132 astr_set_len (as, as->len + size);
133 memmove (as->text + pos + size, as->text + pos, as->len - (pos + size));
134 memset (as->text + pos, '\0', size);
135 return as;
136 }
137
138 astr
astr_move(astr as,size_t to,size_t from,size_t n)139 astr_move (astr as, size_t to, size_t from, size_t n)
140 {
141 assert (as != NULL);
142 assert (to <= as->len);
143 assert (from <= as->len);
144 assert (n <= as->len - MAX (from, to));
145 memmove (as->text + to, as->text + from, n);
146 return as;
147 }
148
149 astr
astr_set(astr as,size_t pos,int c,size_t n)150 astr_set (astr as, size_t pos, int c, size_t n)
151 {
152 assert (as != NULL);
153 assert (pos <= as->len);
154 assert (n <= as->len - pos);
155 memset (as->text + pos, c, n);
156 return as;
157 }
158
159
160 /*
161 * Derived functions.
162 */
163
164 char
astr_get(const_astr as,size_t pos)165 astr_get (const_astr as, size_t pos)
166 {
167 assert (pos <= astr_len (as));
168 return (astr_cstr (as))[pos];
169 }
170
171 static astr
astr_ncpy_cstr(astr as,const char * s,size_t len)172 astr_ncpy_cstr (astr as, const char *s, size_t len)
173 {
174 astr_truncate (as, 0);
175 return astr_cat_nstr (as, s, len);
176 }
177
178 astr
astr_new_cstr(const char * s)179 astr_new_cstr (const char *s)
180 {
181 return astr_cpy_cstr (astr_new (), s);
182 }
183
184 astr
astr_cpy(astr as,const_astr src)185 astr_cpy (astr as, const_astr src)
186 {
187 return astr_ncpy_cstr (as, astr_cstr (src), astr_len (src));
188 }
189
190 astr
astr_cpy_cstr(astr as,const char * s)191 astr_cpy_cstr (astr as, const char *s)
192 {
193 return astr_ncpy_cstr (as, s, strlen (s));
194 }
195
196 astr
astr_cat(astr as,const_astr src)197 astr_cat (astr as, const_astr src)
198 {
199 return astr_cat_nstr (as, astr_cstr (src), astr_len (src));
200 }
201
202 astr
astr_cat_cstr(astr as,const char * s)203 astr_cat_cstr (astr as, const char *s)
204 {
205 return astr_cat_nstr (as, s, strlen (s));
206 }
207
208 astr
astr_cat_char(astr as,int c)209 astr_cat_char (astr as, int c)
210 {
211 char c2 = c;
212 return astr_cat_nstr (as, &c2, 1);
213 }
214
215 astr
astr_substr(const_astr as,size_t pos,size_t size)216 astr_substr (const_astr as, size_t pos, size_t size)
217 {
218 assert (pos + size <= astr_len (as));
219 return astr_cat_nstr (astr_new (), astr_cstr (as) + pos, size);
220 }
221
222 astr
astr_truncate(astr as,size_t pos)223 astr_truncate (astr as, size_t pos)
224 {
225 return astr_remove (as, pos, astr_len (as) - pos);
226 }
227
228 astr
astr_readf(const char * filename)229 astr_readf (const char *filename)
230 {
231 astr as = NULL;
232 struct stat st;
233 if (stat (filename, &st) == 0)
234 {
235 size_t size = st.st_size;
236 int fd = open (filename, O_RDONLY);
237 if (fd >= 0)
238 {
239 char buf[BUFSIZ];
240 as = astr_new ();
241 while ((size = read (fd, buf, BUFSIZ)) > 0)
242 astr_cat_nstr (as, buf, size);
243 close (fd);
244 }
245 }
246 return as;
247 }
248
249 astr
astr_vfmt(const char * fmt,va_list ap)250 astr_vfmt (const char *fmt, va_list ap)
251 {
252 return astr_new_cstr (xvasprintf (fmt, ap));
253 }
254
255 astr
astr_fmt(const char * fmt,...)256 astr_fmt (const char *fmt, ...)
257 {
258 va_list ap;
259 va_start (ap, fmt);
260 astr as = astr_vfmt (fmt, ap);
261 va_end (ap);
262 return as;
263 }
264
265 astr
astr_recase(astr as,casing newcase)266 astr_recase (astr as, casing newcase)
267 {
268 astr bs = astr_new ();
269
270 if (newcase == case_capitalized || newcase == case_upper)
271 astr_cat_char (bs, toupper (astr_get (as, 0)));
272 else
273 astr_cat_char (bs, tolower (astr_get (as, 0)));
274
275 for (size_t i = 1, len = astr_len (as); i < len; i++)
276 astr_cat_char (bs, ((newcase == case_upper) ? toupper : tolower) (astr_get (as, i)));
277
278 return astr_cpy (as, bs);
279 }
280
281
282 #ifdef TEST
283
284 #include <stdio.h>
285 #include "progname.h"
286
287 #include "main.h"
288
289 static void
assert_eq(astr as,const char * s)290 assert_eq (astr as, const char *s)
291 {
292 if (!STREQ (astr_cstr (as), s))
293 {
294 printf ("test failed: \"%s\" != \"%s\"\n", astr_cstr (as), s);
295 exit (EXIT_FAILURE);
296 }
297 }
298
299 int
main(int argc _GL_UNUSED_PARAMETER,char ** argv)300 main (int argc _GL_UNUSED_PARAMETER, char **argv)
301 {
302 astr as1, as2, as3;
303
304 GC_init ();
305
306 set_program_name (argv[0]);
307
308 as1 = astr_new ();
309 astr_cpy_cstr (as1, "hello world");
310 astr_cat_char (as1, '!');
311 assert_eq (as1, "hello world!");
312
313 as3 = astr_substr (as1, 6, 5);
314 assert_eq (as3, "world");
315
316 as2 = astr_new ();
317 astr_cpy_cstr (as2, "The ");
318 astr_cat (as2, as3);
319 astr_cat_char (as2, '.');
320 assert_eq (as2, "The world.");
321
322 as3 = astr_substr (as1, astr_len (as1) - 6, 5);
323 assert_eq (as3, "world");
324
325 astr_cpy_cstr (as1, "1234567");
326 astr_replace_nstr (as1, 1, "foo", 3);
327 assert_eq (as1, "1foo567");
328
329 astr_cpy_cstr (as1, "12345");
330 as2 = astr_substr (as1, astr_len (as1) - 2, 2);
331 assert_eq (as2, "45");
332
333 astr_cpy_cstr (as1, "12345");
334 as2 = astr_substr (as1, astr_len (as1) - 5, 5);
335 assert_eq (as2, "12345");
336
337 as1 = astr_fmt ("%s * %d = ", "5", 3);
338 astr_cat (as1, astr_fmt ("%d", 15));
339 assert_eq (as1, "5 * 3 = 15");
340
341 astr_cpy_cstr (as1, "some text");
342 astr_recase (as1, case_capitalized);
343 assert_eq (as1, "Some text");
344 astr_recase (as1, case_upper);
345 assert_eq (as1, "SOME TEXT");
346 astr_recase (as1, case_lower);
347 assert_eq (as1, "some text");
348
349 printf ("astr test successful.\n");
350
351 return EXIT_SUCCESS;
352 }
353
354 #endif /* TEST */
355