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)
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 ***********************************************************************/
14 /**********************************************************************
15 Allocated/allocatable strings
16 original author: David Pfitzner <dwp@mso.anu.edu.au>
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.
28 This is deliberately simple and light-weight. The user is allowed
29 full access to the struct elements rather than use accessor
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.
39 There are two ways of getting the resulting string as a char *:
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().
45 - astr_to_str() returns a char * and destroys the struct astring.
46 Responsibility for freeing the storage becomes the caller's.
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).
55 ***********************************************************************/
58 #include <fc_config.h>
61 #include "fc_prehdrs.h"
69 #include "log.h" /* fc_assert */
71 #include "support.h" /* fc_vsnprintf, fc_strlcat */
75 #define str _private_str_
77 #define n_alloc _private_n_alloc_
79 static const struct astring zero_astr
= ASTRING_INIT
;
80 static char *astr_buffer
= NULL
;
81 static size_t astr_buffer_alloc
= 0;
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);
88 /****************************************************************************
89 Returns the astring buffer. Create it if necessary.
90 ****************************************************************************/
91 static inline char *astr_buffer_get(size_t *alloc
)
94 astr_buffer_alloc
= 65536;
95 astr_buffer
= fc_malloc(astr_buffer_alloc
);
96 atexit(astr_buffer_free
);
99 *alloc
= astr_buffer_alloc
;
103 /****************************************************************************
104 Grow the astring buffer.
105 ****************************************************************************/
106 static inline char *astr_buffer_grow(size_t *alloc
)
108 astr_buffer_alloc
*= 2;
109 astr_buffer
= fc_realloc(astr_buffer
, astr_buffer_alloc
);
111 *alloc
= astr_buffer_alloc
;
115 /****************************************************************************
116 Grow the astring buffer.
117 ****************************************************************************/
118 static void astr_buffer_free(void)
124 /****************************************************************************
125 Initialize the struct.
126 ****************************************************************************/
127 void astr_init(struct astring
*astr
)
132 /****************************************************************************
133 Free the memory associated with astr, and return astr to same
134 state as after astr_init.
135 ****************************************************************************/
136 void astr_free(struct astring
*astr
)
138 if (astr
->n_alloc
> 0) {
139 fc_assert_ret(NULL
!= astr
->str
);
145 /****************************************************************************
146 Return the raw string to the caller, and return astr to same state as
148 Freeing the string's storage becomes the caller's responsibility.
149 ****************************************************************************/
150 char *astr_to_str(struct astring
*astr
)
152 char *str
= astr
->str
;
157 /****************************************************************************
158 Check that astr has enough size to hold n, and realloc to a bigger
159 size if necessary. Here n must be big enough to include the trailing
160 ascii-null if required. The requested n is stored in astr->n.
161 The actual amount allocated may be larger than n, and is stored
163 ****************************************************************************/
164 void astr_reserve(struct astring
*astr
, size_t n
)
167 bool was_null
= (astr
->n
== 0);
169 fc_assert_ret(NULL
!= astr
);
172 if (n
<= astr
->n_alloc
) {
176 /* Allocated more if this is only a small increase on before: */
177 n1
= (3 * (astr
->n_alloc
+ 10)) / 2;
178 astr
->n_alloc
= (n
> n1
) ? n
: n1
;
179 astr
->str
= (char *) fc_realloc(astr
->str
, astr
->n_alloc
);
185 /****************************************************************************
186 Sets the content to the empty string.
187 ****************************************************************************/
188 void astr_clear(struct astring
*astr
)
191 /* astr_reserve is really astr_size, so we don't want to reduce the
193 astr_reserve(astr
, 1);
198 /****************************************************************************
199 Add the text to the string.
200 ****************************************************************************/
201 static void astr_vadd(struct astring
*astr
, size_t at
,
202 const char *format
, va_list ap
)
208 buffer
= astr_buffer_get(&buffer_size
);
210 new_len
= fc_vsnprintf(buffer
, buffer_size
, format
, ap
);
211 if (new_len
< buffer_size
&& (size_t) -1 != new_len
) {
214 buffer
= astr_buffer_grow(&buffer_size
);
219 astr_reserve(astr
, new_len
);
220 fc_strlcpy(astr
->str
+ at
, buffer
, astr
->n_alloc
- at
);
223 /****************************************************************************
224 Set the text to the string.
225 ****************************************************************************/
226 void astr_set(struct astring
*astr
, const char *format
, ...)
230 va_start(args
, format
);
231 astr_vadd(astr
, 0, format
, args
);
236 /****************************************************************************
237 Add the text to the string.
238 ****************************************************************************/
239 void astr_add(struct astring
*astr
, const char *format
, ...)
243 va_start(args
, format
);
244 astr_vadd(astr
, astr_len(astr
), format
, args
);
248 /****************************************************************************
249 Add the text to the string in a new line.
250 ****************************************************************************/
251 void astr_add_line(struct astring
*astr
, const char *format
, ...)
253 size_t len
= astr_len(astr
);
256 va_start(args
, format
);
258 astr_vadd(astr
, len
+ 1, format
, args
);
259 astr
->str
[len
] = '\n';
261 astr_vadd(astr
, len
, format
, args
);
266 /****************************************************************************
267 Replace the spaces by line breaks when the line lenght is over the desired
269 ****************************************************************************/
270 void astr_break_lines(struct astring
*astr
, size_t desired_len
)
272 fc_break_lines(astr
->str
, desired_len
);
275 /****************************************************************************
276 Build a localized string with the given items. Items will be
279 See also astr_build_and_list(), strvec_to_or_list().
280 ****************************************************************************/
281 const char *astr_build_or_list(struct astring
*astr
,
282 const char *const *items
, size_t number
)
284 fc_assert_ret_val(NULL
!= astr
, NULL
);
285 fc_assert_ret_val(0 < number
, NULL
);
286 fc_assert_ret_val(NULL
!= items
, NULL
);
289 /* TRANS: "or"-separated string list with one single item. */
290 astr_set(astr
, Q_("?or-list-single:%s"), *items
);
291 } else if (2 == number
) {
292 /* TRANS: "or"-separated string list with 2 items. */
293 astr_set(astr
, Q_("?or-list:%s or %s"), items
[0], items
[1]);
295 /* Estimate the space we need. */
296 astr_reserve(astr
, number
* 64);
297 /* TRANS: start of an "or"-separated string list with more than two
299 astr_set(astr
, Q_("?or-list:%s"), *items
++);
300 while (1 < --number
) {
301 /* TRANS: next elements of an "or"-separated string list with more
303 astr_add(astr
, Q_("?or-list:, %s"), *items
++);
305 /* TRANS: end of an "or"-separated string list with more than two
307 astr_add(astr
, Q_("?or-list:, or %s"), *items
);
313 /****************************************************************************
314 Build a localized string with the given items. Items will be
317 See also astr_build_or_list(), strvec_to_and_list().
318 ****************************************************************************/
319 const char *astr_build_and_list(struct astring
*astr
,
320 const char *const *items
, size_t number
)
322 fc_assert_ret_val(NULL
!= astr
, NULL
);
323 fc_assert_ret_val(0 < number
, NULL
);
324 fc_assert_ret_val(NULL
!= items
, NULL
);
327 /* TRANS: "and"-separated string list with one single item. */
328 astr_set(astr
, Q_("?and-list-single:%s"), *items
);
329 } else if (2 == number
) {
330 /* TRANS: "and"-separated string list with 2 items. */
331 astr_set(astr
, Q_("?and-list:%s and %s"), items
[0], items
[1]);
333 /* Estimate the space we need. */
334 astr_reserve(astr
, number
* 64);
335 /* TRANS: start of an "and"-separated string list with more than two
337 astr_set(astr
, Q_("?and-list:%s"), *items
++);
338 while (1 < --number
) {
339 /* TRANS: next elements of an "and"-separated string list with more
341 astr_add(astr
, Q_("?and-list:, %s"), *items
++);
343 /* TRANS: end of an "and"-separated string list with more than two
345 astr_add(astr
, Q_("?and-list:, and %s"), *items
);
351 /****************************************************************************
352 Copy one astring in another.
353 ****************************************************************************/
354 void astr_copy(struct astring
*dest
, const struct astring
*src
)
356 if (astr_empty(src
)) {
359 astr_set(dest
, "%s", src
->str
);