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