Freeciv-3.2
Loading...
Searching...
No Matches
astring.c
Go to the documentation of this file.
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 reuse
52 the same astring on subsequent calls; the caller should behave
53 the 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 "fcthread.h"
70#include "log.h" /* fc_assert */
71#include "mem.h"
72#include "support.h" /* fc_vsnprintf, fc_strlcat */
73
74#include "astring.h"
75
76#define str _private_str_
77#define n _private_n_
78#define n_alloc _private_n_alloc_
79
80static const struct astring zero_astr = ASTRING_INIT;
81static char *astr_buffer = NULL;
82static size_t astr_buffer_alloc = 0;
83
85
86static inline char *astr_buffer_get(size_t *alloc);
87static inline char *astr_buffer_grow(size_t request, size_t *alloc);
88static void astr_buffer_free(void);
89
90/************************************************************************/
93static inline char *astr_buffer_get(size_t *alloc)
94{
95 if (!astr_buffer) {
96#ifndef HAVE_VA_COPY
97 /* This buffer will never be grown, so it should be big enough
98 * from the beginning. */
99 astr_buffer_alloc = 65536;
100#else
101 astr_buffer_alloc = 4096;
102#endif
105 }
106
108 return astr_buffer;
109}
110
111/************************************************************************/
114static inline char *astr_buffer_grow(size_t request, size_t *alloc)
115{
117 /* We simply set buffer size to the requested one here.
118 * Old implementation went on by doubling the buffer size,
119 * presumably to match what low level memory handling does
120 * anyway. But need to increase the buffer size is so rare
121 * that we can as well call this function again, even when
122 * the increase would fall within limits of what doubling
123 * would have given us. */
125 }
127
129
130 return astr_buffer;
131}
132
133/************************************************************************/
136static void astr_buffer_free(void)
137{
139}
140
141/************************************************************************/
144void astr_init(struct astring *astr)
145{
146 *astr = zero_astr;
147}
148
149/************************************************************************/
153void astr_free(struct astring *astr)
154{
155 if (astr->n_alloc > 0) {
156 fc_assert_ret(NULL != astr->str);
157 free(astr->str);
158 }
159 *astr = zero_astr;
160}
161
162/************************************************************************/
168{
169 char *str = astr->str;
170
171 *astr = zero_astr;
172
173 return str;
174}
175
176/************************************************************************/
183void astr_reserve(struct astring *astr, size_t n)
184{
185 unsigned int n1;
186 bool was_null = (astr->n == 0);
187
188 astr->n = n;
189 if (n <= astr->n_alloc) {
190 return;
191 }
192
193 /* Allocated more if this is only a small increase on before: */
194 n1 = (3 * (astr->n_alloc + 10)) / 2;
195 astr->n_alloc = (n > n1) ? n : n1;
196 astr->str = (char *) fc_realloc(astr->str, astr->n_alloc);
197 if (was_null) {
199 }
200}
201
202/************************************************************************/
206{
207 if (astr->n == 0) {
208 /* astr_reserve is really astr_size, so we don't want to reduce the
209 * size. */
210 astr_reserve(astr, 1);
211 }
212 astr->str[0] = '\0';
213}
214
215/************************************************************************/
218static inline void astr_vadd_at(struct astring *astr, size_t at,
219 const char *format, va_list ap)
220{
221 char *buffer;
222 size_t buffer_size;
223 size_t req_len;
224
226
227#ifdef HAVE_VA_COPY
228 va_list copy;
229
230 buffer = astr_buffer_get(&buffer_size);
231
232 va_copy(copy, ap);
233
234 req_len = fc_vsnprintf(buffer, buffer_size, format, ap);
235 if (req_len + 1 > buffer_size) {
236 buffer = astr_buffer_grow(req_len + 1, &buffer_size);
237 /* Even if buffer is *still* too small, we fill what we can */
238 req_len = fc_vsnprintf(buffer, buffer_size, format, copy);
239 if (req_len > buffer_size) {
240 /* What we actually got */
242 }
243 }
244 va_end(copy);
245#else /* HAVE_VA_COPY */
246 buffer = astr_buffer_get(&buffer_size);
247
248 req_len = fc_vsnprintf(buffer, buffer_size, format, ap);
249
250 if (req_len > buffer_size) {
251 /* What we actually got */
253 }
254#endif /* HAVE_VA_COPY */
255
256 req_len += at + 1;
257
259 fc_strlcpy(astr->str + at, buffer, astr->n_alloc - at);
260
262}
263
264/************************************************************************/
267void astr_set(struct astring *astr, const char *format, ...)
268{
269 va_list args;
270
271 va_start(args, format);
272 astr_vadd_at(astr, 0, format, args);
273 va_end(args);
274}
275
276/************************************************************************/
279void astr_vadd(struct astring *astr, const char *format, va_list ap)
280{
281 astr_vadd_at(astr, astr_len(astr), format, ap);
282}
283
284/************************************************************************/
287void astr_add(struct astring *astr, const char *format, ...)
288{
289 va_list args;
290
291 va_start(args, format);
292 astr_vadd_at(astr, astr_len(astr), format, args);
293 va_end(args);
294}
295
296/************************************************************************/
299void astr_add_line(struct astring *astr, const char *format, ...)
300{
301 size_t len = astr_len(astr);
302 va_list args;
303
304 va_start(args, format);
305 if (0 < len) {
306 astr_vadd_at(astr, len + 1, format, args);
307 astr->str[len] = '\n';
308 } else {
309 astr_vadd_at(astr, len, format, args);
310 }
311 va_end(args);
312}
313
314/************************************************************************/
319{
321}
322
323/************************************************************************/
329const char *astr_build_or_list(struct astring *astr,
330 const char *const *items, size_t number)
331{
333 fc_assert_ret_val(0 < number, NULL);
334 fc_assert_ret_val(NULL != items, NULL);
335
336 if (1 == number) {
337 /* TRANS: "or"-separated string list with one single item. */
338 astr_set(astr, Q_("?or-list-single:%s"), *items);
339 } else if (2 == number) {
340 /* TRANS: "or"-separated string list with 2 items. */
341 astr_set(astr, Q_("?or-list:%s or %s"), items[0], items[1]);
342 } else {
343 /* Estimate the space we need. */
344 astr_reserve(astr, number * 64);
345 /* TRANS: start of an "or"-separated string list with more than two
346 * items. */
347 astr_set(astr, Q_("?or-list:%s"), *items++);
348 while (1 < --number) {
349 /* TRANS: next elements of an "or"-separated string list with more
350 * than two items. */
351 astr_add(astr, Q_("?or-list:, %s"), *items++);
352 }
353 /* TRANS: end of an "or"-separated string list with more than two
354 * items. */
355 astr_add(astr, Q_("?or-list:, or %s"), *items);
356 }
357
358 return astr->str;
359}
360
361/************************************************************************/
367const char *astr_build_and_list(struct astring *astr,
368 const char *const *items, size_t number)
369{
371 fc_assert_ret_val(0 < number, NULL);
372 fc_assert_ret_val(NULL != items, NULL);
373
374 if (1 == number) {
375 /* TRANS: "and"-separated string list with one single item. */
376 astr_set(astr, Q_("?and-list-single:%s"), *items);
377 } else if (2 == number) {
378 /* TRANS: "and"-separated string list with 2 items. */
379 astr_set(astr, Q_("?and-list:%s and %s"), items[0], items[1]);
380 } else {
381 /* Estimate the space we need. */
382 astr_reserve(astr, number * 64);
383 /* TRANS: start of an "and"-separated string list with more than two
384 * items. */
385 astr_set(astr, Q_("?and-list:%s"), *items++);
386 while (1 < --number) {
387 /* TRANS: next elements of an "and"-separated string list with more
388 * than two items. */
389 astr_add(astr, Q_("?and-list:, %s"), *items++);
390 }
391 /* TRANS: end of an "and"-separated string list with more than two
392 * items. */
393 astr_add(astr, Q_("?and-list:, and %s"), *items);
394 }
395
396 return astr->str;
397}
398
399/************************************************************************/
402void astr_copy(struct astring *dest, const struct astring *src)
403{
404 if (astr_empty(src)) {
405 astr_clear(dest);
406 } else {
407 astr_set(dest, "%s", src->str);
408 }
409}
410
411/************************************************************************/
414void fc_astr_init(void)
415{
417}
418
419/************************************************************************/
422void fc_astr_free(void)
423{
425}
static char * astr_buffer_get(size_t *alloc)
Definition astring.c:93
#define n_alloc
Definition astring.c:78
void astr_free(struct astring *astr)
Definition astring.c:153
const char * astr_build_or_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:329
static void astr_buffer_free(void)
Definition astring.c:136
void astr_vadd(struct astring *astr, const char *format, va_list ap)
Definition astring.c:279
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:267
const char * astr_build_and_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:367
void astr_add_line(struct astring *astr, const char *format,...)
Definition astring.c:299
void astr_reserve(struct astring *astr, size_t n)
Definition astring.c:183
void astr_clear(struct astring *astr)
Definition astring.c:205
static void astr_vadd_at(struct astring *astr, size_t at, const char *format, va_list ap)
Definition astring.c:218
void astr_init(struct astring *astr)
Definition astring.c:144
void fc_astr_init(void)
Definition astring.c:414
char * astr_to_str(struct astring *astr)
Definition astring.c:167
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:287
#define str
Definition astring.c:76
static char * astr_buffer_grow(size_t request, size_t *alloc)
Definition astring.c:114
static const struct astring zero_astr
Definition astring.c:80
static char * astr_buffer
Definition astring.c:81
void fc_astr_free(void)
Definition astring.c:422
#define n
Definition astring.c:77
static fc_mutex astr_mutex
Definition astring.c:84
static size_t astr_buffer_alloc
Definition astring.c:82
void astr_break_lines(struct astring *astr, size_t desired_len)
Definition astring.c:318
void astr_copy(struct astring *dest, const struct astring *src)
Definition astring.c:402
static size_t astr_len(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:101
#define ASTRING_INIT
Definition astring.h:44
static bool astr_empty(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:125
char * incite_cost
Definition comments.c:75
#define Q_(String)
Definition fcintl.h:70
void fc_mutex_allocate(fc_mutex *mutex)
void fc_mutex_init(fc_mutex *mutex)
void fc_mutex_release(fc_mutex *mutex)
void fc_mutex_destroy(fc_mutex *mutex)
#define fc_assert_ret(condition)
Definition log.h:191
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define fc_realloc(ptr, sz)
Definition mem.h:36
#define fc_malloc(sz)
Definition mem.h:34
int len
Definition packhand.c:127
char * str
Definition astring.h:37
size_t n
Definition astring.h:38
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:791
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:900
int fc_break_lines(char *str, size_t desired_len)
Definition support.c:1149