Freeciv-3.3
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 = nullptr;
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 == nullptr) {
96 astr_buffer_alloc = 4096;
99 }
100
102
103 return astr_buffer;
104}
105
106/************************************************************************/
109static inline char *astr_buffer_grow(size_t request, size_t *alloc)
110{
112 /* We simply set buffer size to the requested one here.
113 * Old implementation went on by doubling the buffer size,
114 * presumably to match what low level memory handling does
115 * anyway. But need to increase the buffer size is so rare
116 * that we can as well call this function again, even when
117 * the increase would fall within limits of what doubling
118 * would have given us. */
120 }
122
124
125 return astr_buffer;
126}
127
128/************************************************************************/
131static void astr_buffer_free(void)
132{
134}
135
136/************************************************************************/
139void astr_init(struct astring *astr)
140{
141 *astr = zero_astr;
142}
143
144/************************************************************************/
148void astr_free(struct astring *astr)
149{
150 if (astr->n_alloc > 0) {
151 fc_assert_ret(astr->str != nullptr);
152 free(astr->str);
153 }
154
155 *astr = zero_astr;
156}
157
158/************************************************************************/
164{
165 char *str = astr->str;
166
167 *astr = zero_astr;
168
169 return str;
170}
171
172/************************************************************************/
179void astr_reserve(struct astring *astr, size_t n)
180{
181 unsigned int n1;
182 bool was_null = (astr->n == 0);
183
184 astr->n = n;
185 if (n <= astr->n_alloc) {
186 return;
187 }
188
189 /* Allocated more if this is only a small increase on before: */
190 n1 = (3 * (astr->n_alloc + 10)) / 2;
191 astr->n_alloc = (n > n1) ? n : n1;
192 astr->str = (char *) fc_realloc(astr->str, astr->n_alloc);
193 if (was_null) {
195 }
196}
197
198/************************************************************************/
202{
203 if (astr->n == 0) {
204 /* astr_reserve() is really astr_size(), so we don't want to reduce
205 * the size. */
206 astr_reserve(astr, 1);
207 }
208 astr->str[0] = '\0';
209}
210
211/************************************************************************/
214static inline void astr_vadd_at(struct astring *astr, size_t at,
215 const char *format, va_list ap)
216{
217 char *buffer;
218 size_t buffer_size;
219 size_t req_len;
220 va_list copy;
221
223
224 buffer = astr_buffer_get(&buffer_size);
225
226 va_copy(copy, ap);
227
228 req_len = fc_vsnprintf(buffer, buffer_size, format, ap);
229 if (req_len + 1 > buffer_size) {
230 buffer = astr_buffer_grow(req_len + 1, &buffer_size);
231 /* Even if buffer is *still* too small, we fill what we can */
232 req_len = fc_vsnprintf(buffer, buffer_size, format, copy);
233 if (req_len > buffer_size) {
234 /* What we actually got */
236 }
237 }
238 va_end(copy);
239
240 req_len += at + 1;
241
243 fc_strlcpy(astr->str + at, buffer, astr->n_alloc - at);
244
246}
247
248/************************************************************************/
251void astr_set(struct astring *astr, const char *format, ...)
252{
253 va_list args;
254
255 va_start(args, format);
256 astr_vadd_at(astr, 0, format, args);
257 va_end(args);
258}
259
260/************************************************************************/
263void astr_vadd(struct astring *astr, const char *format, va_list ap)
264{
265 astr_vadd_at(astr, astr_len(astr), format, ap);
266}
267
268/************************************************************************/
271void astr_add(struct astring *astr, const char *format, ...)
272{
273 va_list args;
274
275 va_start(args, format);
276 astr_vadd_at(astr, astr_len(astr), format, args);
277 va_end(args);
278}
279
280/************************************************************************/
283void astr_add_line(struct astring *astr, const char *format, ...)
284{
285 size_t len = astr_len(astr);
286 va_list args;
287
288 va_start(args, format);
289 if (0 < len) {
290 astr_vadd_at(astr, len + 1, format, args);
291 astr->str[len] = '\n';
292 } else {
293 astr_vadd_at(astr, len, format, args);
294 }
295 va_end(args);
296}
297
298/************************************************************************/
303{
305}
306
307/************************************************************************/
313const char *astr_build_or_list(struct astring *astr,
314 const char *const *items, size_t number)
315{
316 fc_assert_ret_val(astr != nullptr, nullptr);
317 fc_assert_ret_val(0 < number, nullptr);
318 fc_assert_ret_val(items != nullptr, nullptr);
319
320 if (1 == number) {
321 /* TRANS: "or"-separated string list with one single item. */
322 astr_set(astr, Q_("?or-list-single:%s"), *items);
323 } else if (2 == number) {
324 /* TRANS: "or"-separated string list with 2 items. */
325 astr_set(astr, Q_("?or-list:%s or %s"), items[0], items[1]);
326 } else {
327 /* Estimate the space we need. */
328 astr_reserve(astr, number * 64);
329 /* TRANS: start of an "or"-separated string list with more than
330 * two items. */
331 astr_set(astr, Q_("?or-list:%s"), *items++);
332 while (1 < --number) {
333 /* TRANS: next elements of an "or"-separated string list with more
334 * than two items. */
335 astr_add(astr, Q_("?or-list:, %s"), *items++);
336 }
337 /* TRANS: end of an "or"-separated string list with more than
338 * two items. */
339 astr_add(astr, Q_("?or-list:, or %s"), *items);
340 }
341
342 return astr->str;
343}
344
345/************************************************************************/
351const char *astr_build_and_list(struct astring *astr,
352 const char *const *items, size_t number)
353{
354 fc_assert_ret_val(astr != nullptr, nullptr);
355 fc_assert_ret_val(0 < number, nullptr);
356 fc_assert_ret_val(items != nullptr, nullptr);
357
358 if (1 == number) {
359 /* TRANS: "and"-separated string list with one single item. */
360 astr_set(astr, Q_("?and-list-single:%s"), *items);
361 } else if (2 == number) {
362 /* TRANS: "and"-separated string list with 2 items. */
363 astr_set(astr, Q_("?and-list:%s and %s"), items[0], items[1]);
364 } else {
365 /* Estimate the space we need. */
366 astr_reserve(astr, number * 64);
367 /* TRANS: start of an "and"-separated string list with more than
368 * two items. */
369 astr_set(astr, Q_("?and-list:%s"), *items++);
370 while (1 < --number) {
371 /* TRANS: next elements of an "and"-separated string list with more
372 * than two items. */
373 astr_add(astr, Q_("?and-list:, %s"), *items++);
374 }
375 /* TRANS: end of an "and"-separated string list with more than
376 * two items. */
377 astr_add(astr, Q_("?and-list:, and %s"), *items);
378 }
379
380 return astr->str;
381}
382
383/************************************************************************/
386void astr_copy(struct astring *dest, const struct astring *src)
387{
388 if (astr_empty(src)) {
389 astr_clear(dest);
390 } else {
391 astr_set(dest, "%s", src->str);
392 }
393}
394
395/************************************************************************/
398void fc_astr_init(void)
399{
401}
402
403/************************************************************************/
406void fc_astr_free(void)
407{
409}
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:148
const char * astr_build_or_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:313
static void astr_buffer_free(void)
Definition astring.c:131
void astr_vadd(struct astring *astr, const char *format, va_list ap)
Definition astring.c:263
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:251
const char * astr_build_and_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:351
void astr_add_line(struct astring *astr, const char *format,...)
Definition astring.c:283
void astr_reserve(struct astring *astr, size_t n)
Definition astring.c:179
void astr_clear(struct astring *astr)
Definition astring.c:201
static void astr_vadd_at(struct astring *astr, size_t at, const char *format, va_list ap)
Definition astring.c:214
void astr_init(struct astring *astr)
Definition astring.c:139
void fc_astr_init(void)
Definition astring.c:398
char * astr_to_str(struct astring *astr)
Definition astring.c:163
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:271
#define str
Definition astring.c:76
static char * astr_buffer_grow(size_t request, size_t *alloc)
Definition astring.c:109
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:406
#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:302
void astr_copy(struct astring *dest, const struct astring *src)
Definition astring.c:386
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:76
#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:192
#define fc_assert_ret_val(condition, val)
Definition log.h:195
#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:777
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:886
int fc_break_lines(char *str, size_t desired_len)
Definition support.c:1135