1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 /**********************************************************************
15 Allocated/allocatable strings
16 original author: David Pfitzner <dwp@mso.anu.edu.au>
17
18 A common technique is to have some memory dynamically allocated
19 (using malloc etc), to avoid compiled-in limits, but only allocate
20 enough space as initially needed, and then realloc later if/when
21 require more space. Typically, the realloc is made a bit more than
22 immediately necessary, to avoid frequent reallocs if the object
23 grows incrementally. Also, don't usually realloc at all if the
24 object shrinks. This is straightforward, but just requires a bit
25 of book-keeping to keep track of how much has been allocated etc.
26 This module provides some tools to make this a bit easier.
27
28 This is deliberately simple and light-weight. The user is allowed
29 full access to the struct elements rather than use accessor
30 functions etc.
31
32 Note one potential hazard: when the size is increased (astr_reserve()),
33 realloc (really fc_realloc) is used, which retains any data which
34 was there previously, _but_: any external pointers into the allocated
35 memory may then become wild. So you cannot safely use such external
36 pointers into the astring data, except strictly between times when
37 the astring size may be changed.
38
39 There are two ways of getting the resulting string as a char *:
40
41 - astr_str() returns a const char *. This should not be modified
42 or freed by the caller; the storage remains owned by the
43 struct astring, which should be freed with astr_free().
44
45 - astr_to_str() returns a char * and destroys the struct astring.
46 Responsibility for freeing the storage becomes the caller's.
47
48 One pattern for using astr_str() is to replace static buffers in
49 functions that return a pointer to static storage. Where previously
50 you would have had e.g. "static struct buf[128]" with an arbitrary
51 size limit, you can have "static struct astring buf", and re-use the
52 same astring on subsequent calls; the caller should behave the
53 same (only reading the string and not freeing it).
54
55 ***********************************************************************/
56
57 #ifdef HAVE_CONFIG_H
58 #include <fc_config.h>
59 #endif
60
61 #include "fc_prehdrs.h"
62
63 #include <stdarg.h>
64 #include <stdlib.h>
65 #include <string.h>
66
67 /* utility */
68 #include "fcintl.h"
69 #include "log.h" /* fc_assert */
70 #include "mem.h"
71 #include "support.h" /* fc_vsnprintf, fc_strlcat */
72
73 #include "astring.h"
74
75 #define str _private_str_
76 #define n _private_n_
77 #define n_alloc _private_n_alloc_
78
79 static const struct astring zero_astr = ASTRING_INIT;
80 static char *astr_buffer = NULL;
81 static size_t astr_buffer_alloc = 0;
82
83 static inline char *astr_buffer_get(size_t *alloc);
84 static inline char *astr_buffer_grow(size_t *alloc);
85 static void astr_buffer_free(void);
86
87
88 /****************************************************************************
89 Returns the astring buffer. Create it if necessary.
90 ****************************************************************************/
astr_buffer_get(size_t * alloc)91 static inline char *astr_buffer_get(size_t *alloc)
92 {
93 if (!astr_buffer) {
94 astr_buffer_alloc = 65536;
95 astr_buffer = fc_malloc(astr_buffer_alloc);
96 atexit(astr_buffer_free);
97 }
98
99 *alloc = astr_buffer_alloc;
100 return astr_buffer;
101 }
102
103 /****************************************************************************
104 Grow the astring buffer.
105 ****************************************************************************/
astr_buffer_grow(size_t * alloc)106 static inline char *astr_buffer_grow(size_t *alloc)
107 {
108 astr_buffer_alloc *= 2;
109 astr_buffer = fc_realloc(astr_buffer, astr_buffer_alloc);
110
111 *alloc = astr_buffer_alloc;
112 return astr_buffer;
113 }
114
115 /************************************************************************//**
116 Free the astring buffer.
117 ****************************************************************************/
astr_buffer_free(void)118 static void astr_buffer_free(void)
119 {
120 free(astr_buffer);
121 }
122
123 /****************************************************************************
124 Initialize the struct.
125 ****************************************************************************/
astr_init(struct astring * astr)126 void astr_init(struct astring *astr)
127 {
128 *astr = zero_astr;
129 }
130
131 /****************************************************************************
132 Free the memory associated with astr, and return astr to same
133 state as after astr_init.
134 ****************************************************************************/
astr_free(struct astring * astr)135 void astr_free(struct astring *astr)
136 {
137 if (astr->n_alloc > 0) {
138 fc_assert_ret(NULL != astr->str);
139 free(astr->str);
140 }
141 *astr = zero_astr;
142 }
143
144 /****************************************************************************
145 Return the raw string to the caller, and return astr to same state as
146 after astr_init().
147 Freeing the string's storage becomes the caller's responsibility.
148 ****************************************************************************/
astr_to_str(struct astring * astr)149 char *astr_to_str(struct astring *astr)
150 {
151 char *str = astr->str;
152 *astr = zero_astr;
153 return str;
154 }
155
156 /****************************************************************************
157 Check that astr has enough size to hold n, and realloc to a bigger
158 size if necessary. Here n must be big enough to include the trailing
159 ascii-null if required. The requested n is stored in astr->n.
160 The actual amount allocated may be larger than n, and is stored
161 in astr->n_alloc.
162 ****************************************************************************/
astr_reserve(struct astring * astr,size_t n)163 void astr_reserve(struct astring *astr, size_t n)
164 {
165 unsigned int n1;
166 bool was_null = (astr->n == 0);
167
168 fc_assert_ret(NULL != astr);
169
170 astr->n = n;
171 if (n <= astr->n_alloc) {
172 return;
173 }
174
175 /* Allocated more if this is only a small increase on before: */
176 n1 = (3 * (astr->n_alloc + 10)) / 2;
177 astr->n_alloc = (n > n1) ? n : n1;
178 astr->str = (char *) fc_realloc(astr->str, astr->n_alloc);
179 if (was_null) {
180 astr_clear(astr);
181 }
182 }
183
184 /****************************************************************************
185 Sets the content to the empty string.
186 ****************************************************************************/
astr_clear(struct astring * astr)187 void astr_clear(struct astring *astr)
188 {
189 if (astr->n == 0) {
190 /* astr_reserve is really astr_size, so we don't want to reduce the
191 * size. */
192 astr_reserve(astr, 1);
193 }
194 astr->str[0] = '\0';
195 }
196
197 /************************************************************************//**
198 Helper: add the text to the specified place in the string.
199 ****************************************************************************/
astr_vadd_at(struct astring * astr,size_t at,const char * format,va_list ap)200 static inline void astr_vadd_at(struct astring *astr, size_t at,
201 const char *format, va_list ap)
202 {
203 char *buffer;
204 size_t buffer_size;
205 size_t new_len;
206
207 buffer = astr_buffer_get(&buffer_size);
208 for (;;) {
209 new_len = fc_vsnprintf(buffer, buffer_size, format, ap);
210 if (new_len < buffer_size && (size_t) -1 != new_len) {
211 break;
212 }
213 buffer = astr_buffer_grow(&buffer_size);
214 }
215
216 new_len += at + 1;
217
218 astr_reserve(astr, new_len);
219 fc_strlcpy(astr->str + at, buffer, astr->n_alloc - at);
220 }
221
222 /****************************************************************************
223 Set the text to the string.
224 ****************************************************************************/
astr_set(struct astring * astr,const char * format,...)225 void astr_set(struct astring *astr, const char *format, ...)
226 {
227 va_list args;
228
229 va_start(args, format);
230 astr_vadd_at(astr, 0, format, args);
231 va_end(args);
232 }
233
234 /************************************************************************//**
235 Add the text to the string (varargs version).
236 ****************************************************************************/
astr_vadd(struct astring * astr,const char * format,va_list ap)237 void astr_vadd(struct astring *astr, const char *format, va_list ap)
238 {
239 astr_vadd_at(astr, astr_len(astr), format, ap);
240 }
241
242 /****************************************************************************
243 Add the text to the string.
244 ****************************************************************************/
astr_add(struct astring * astr,const char * format,...)245 void astr_add(struct astring *astr, const char *format, ...)
246 {
247 va_list args;
248
249 va_start(args, format);
250 astr_vadd_at(astr, astr_len(astr), format, args);
251 va_end(args);
252 }
253
254 /****************************************************************************
255 Add the text to the string in a new line.
256 ****************************************************************************/
astr_add_line(struct astring * astr,const char * format,...)257 void astr_add_line(struct astring *astr, const char *format, ...)
258 {
259 size_t len = astr_len(astr);
260 va_list args;
261
262 va_start(args, format);
263 if (0 < len) {
264 astr_vadd_at(astr, len + 1, format, args);
265 astr->str[len] = '\n';
266 } else {
267 astr_vadd_at(astr, len, format, args);
268 }
269 va_end(args);
270 }
271
272 /****************************************************************************
273 Replace the spaces by line breaks when the line lenght is over the desired
274 one.
275 ****************************************************************************/
astr_break_lines(struct astring * astr,size_t desired_len)276 void astr_break_lines(struct astring *astr, size_t desired_len)
277 {
278 fc_break_lines(astr->str, desired_len);
279 }
280
281 /****************************************************************************
282 Build a localized string with the given items. Items will be
283 "or"-separated.
284
285 See also astr_build_and_list(), strvec_to_or_list().
286 ****************************************************************************/
astr_build_or_list(struct astring * astr,const char * const * items,size_t number)287 const char *astr_build_or_list(struct astring *astr,
288 const char *const *items, size_t number)
289 {
290 fc_assert_ret_val(NULL != astr, NULL);
291 fc_assert_ret_val(0 < number, NULL);
292 fc_assert_ret_val(NULL != items, NULL);
293
294 if (1 == number) {
295 /* TRANS: "or"-separated string list with one single item. */
296 astr_set(astr, Q_("?or-list-single:%s"), *items);
297 } else if (2 == number) {
298 /* TRANS: "or"-separated string list with 2 items. */
299 astr_set(astr, Q_("?or-list:%s or %s"), items[0], items[1]);
300 } else {
301 /* Estimate the space we need. */
302 astr_reserve(astr, number * 64);
303 /* TRANS: start of an "or"-separated string list with more than two
304 * items. */
305 astr_set(astr, Q_("?or-list:%s"), *items++);
306 while (1 < --number) {
307 /* TRANS: next elements of an "or"-separated string list with more
308 * than two items. */
309 astr_add(astr, Q_("?or-list:, %s"), *items++);
310 }
311 /* TRANS: end of an "or"-separated string list with more than two
312 * items. */
313 astr_add(astr, Q_("?or-list:, or %s"), *items);
314 }
315
316 return astr->str;
317 }
318
319 /****************************************************************************
320 Build a localized string with the given items. Items will be
321 "and"-separated.
322
323 See also astr_build_or_list(), strvec_to_and_list().
324 ****************************************************************************/
astr_build_and_list(struct astring * astr,const char * const * items,size_t number)325 const char *astr_build_and_list(struct astring *astr,
326 const char *const *items, size_t number)
327 {
328 fc_assert_ret_val(NULL != astr, NULL);
329 fc_assert_ret_val(0 < number, NULL);
330 fc_assert_ret_val(NULL != items, NULL);
331
332 if (1 == number) {
333 /* TRANS: "and"-separated string list with one single item. */
334 astr_set(astr, Q_("?and-list-single:%s"), *items);
335 } else if (2 == number) {
336 /* TRANS: "and"-separated string list with 2 items. */
337 astr_set(astr, Q_("?and-list:%s and %s"), items[0], items[1]);
338 } else {
339 /* Estimate the space we need. */
340 astr_reserve(astr, number * 64);
341 /* TRANS: start of an "and"-separated string list with more than two
342 * items. */
343 astr_set(astr, Q_("?and-list:%s"), *items++);
344 while (1 < --number) {
345 /* TRANS: next elements of an "and"-separated string list with more
346 * than two items. */
347 astr_add(astr, Q_("?and-list:, %s"), *items++);
348 }
349 /* TRANS: end of an "and"-separated string list with more than two
350 * items. */
351 astr_add(astr, Q_("?and-list:, and %s"), *items);
352 }
353
354 return astr->str;
355 }
356
357 /****************************************************************************
358 Copy one astring in another.
359 ****************************************************************************/
astr_copy(struct astring * dest,const struct astring * src)360 void astr_copy(struct astring *dest, const struct astring *src)
361 {
362 if (astr_empty(src)) {
363 astr_clear(dest);
364 } else {
365 astr_set(dest, "%s", src->str);
366 }
367 }
368