1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * lt-string.c
4 * Copyright (C) 2011-2012 Akira TAGOH
5 *
6 * Authors:
7 * Akira TAGOH <akira@tagoh.org>
8 *
9 * You may distribute under the terms of either the GNU
10 * Lesser General Public License or the Mozilla Public
11 * License, as specified in the README file.
12 */
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include "lt-mem.h"
20 #include "lt-messages.h"
21 #include "lt-utils.h"
22 #include "lt-string.h"
23
24 #define LT_STRING_SIZE 128
25
26 /**
27 * SECTION: lt-string
28 * @Short_Description: text buffers which grow automatically as text is added
29 * @Title: Strings
30 *
31 * A #lt_string_t is an object that handles the memory management of a C
32 * string.
33 */
34 struct _lt_string_t {
35 lt_mem_t parent;
36 char *string;
37 size_t len;
38 size_t allocated_len;
39 };
40
41 lt_bool_t _lt_string_expand(lt_string_t *string,
42 size_t size);
43
44 /*< private >*/
45 lt_bool_t
_lt_string_expand(lt_string_t * string,size_t size)46 _lt_string_expand(lt_string_t *string,
47 size_t size)
48 {
49 char *s;
50 size_t n = string->allocated_len;
51 lt_bool_t retval = TRUE;
52
53 n += LT_ALIGNED_TO_POINTER (size + LT_STRING_SIZE);
54 lt_mem_remove_ref(&string->parent, string->string);
55 s = realloc(string->string, n);
56 if (s) {
57 string->string = s;
58 string->allocated_len = n;
59 } else {
60 retval = FALSE;
61 }
62 lt_mem_add_ref(&string->parent, string->string, free);
63
64 return retval;
65 }
66
67 /*< protected >*/
68
69 /*< public >*/
70
71 /**
72 * lt_string_new:
73 * @string: an initial string to set
74 *
75 * Creates an instance of #lt_string_t with @string.
76 *
77 * Returns: a new instance of #lt_string_t.
78 */
79 lt_string_t *
lt_string_new(const char * string)80 lt_string_new(const char *string)
81 {
82 lt_string_t *retval = lt_mem_alloc_object(sizeof (lt_string_t));
83
84 if (retval) {
85 retval->len = string ? strlen(string) : 0;
86 retval->allocated_len = LT_ALIGNED_TO_POINTER (retval->len + LT_STRING_SIZE);
87 retval->string = malloc(retval->allocated_len);
88 if (!retval->string) {
89 lt_mem_unref(&retval->parent);
90 return NULL;
91 }
92 if (string)
93 strcpy(retval->string, string);
94 else
95 retval->string[retval->len] = 0;
96 lt_mem_add_ref(&retval->parent, retval->string, free);
97 }
98
99 return retval;
100 }
101
102 /**
103 * lt_string_ref:
104 * @string: a #lt_string_t
105 *
106 * Increases the reference count of @string.
107 *
108 * Returns: (transfer none): the same @string object.
109 */
110 lt_string_t *
lt_string_ref(lt_string_t * string)111 lt_string_ref(lt_string_t *string)
112 {
113 lt_return_val_if_fail (string != NULL, NULL);
114
115 return lt_mem_ref(&string->parent);
116 }
117
118 /**
119 * lt_string_unref:
120 * @string: a #lt_string_t
121 *
122 * Decreases the reference count of @string. when its reference count
123 * drops to 0, the object is finalized (i.e. its memory is freed).
124 */
125 void
lt_string_unref(lt_string_t * string)126 lt_string_unref(lt_string_t *string)
127 {
128 if (string)
129 lt_mem_unref(&string->parent);
130 }
131
132 /**
133 * lt_string_free:
134 * @string: a #lt_string_t
135 * @free_segment: if %TRUE, the actual character data is freed as well
136 *
137 * Frees the memory allocated for the #lt_string_t.
138 * If @free_segment is %TRUE it also frees the character data. If
139 * it's %FALSE, the caller gains ownership of the buffer and must
140 * free it after use with free().
141 *
142 * Returns: the character data of @string
143 * (i.e. %NULL if @free_segment is %TRUE)
144 */
145 char *
lt_string_free(lt_string_t * string,lt_bool_t free_segment)146 lt_string_free(lt_string_t *string,
147 lt_bool_t free_segment)
148 {
149 char *retval = NULL;
150
151 if (string) {
152 if (!free_segment) {
153 lt_mem_remove_ref(&string->parent, string->string);
154 retval = string->string;
155 }
156 lt_string_unref(string);
157 }
158
159 return retval;
160 }
161
162 /**
163 * lt_string_length:
164 * @string: a #lt_string_t
165 *
166 * Returns the number of characters in buffer for @string.
167 *
168 * Returns: the number of characters
169 */
170 size_t
lt_string_length(const lt_string_t * string)171 lt_string_length(const lt_string_t *string)
172 {
173 lt_return_val_if_fail (string != NULL, 0);
174
175 return string->len;
176 }
177
178 /**
179 * lt_string_value:
180 * @string: a #lt_string_t
181 *
182 * Returns the buffer in @string.
183 *
184 * Returns: a string which @string has.
185 */
186 const char *
lt_string_value(const lt_string_t * string)187 lt_string_value(const lt_string_t *string)
188 {
189 lt_return_val_if_fail (string != NULL, NULL);
190
191 return string->string;
192 }
193
194 /**
195 * lt_string_truncate:
196 * @string: a #lt_string_t
197 * @len: the number of characters to be truncated from the buffer.
198 *
199 * Truncates the characters in the buffer according to @len. if @len is
200 * a negative, how many characters is truncated will be calculated from
201 * current size. i.e. if the buffer contains "abc", and @len is -1,
202 * the buffer will be "ab" after this call.
203 *
204 * Returns: (transfer none): the same @string object.
205 */
206 lt_string_t *
lt_string_truncate(lt_string_t * string,ssize_t len)207 lt_string_truncate(lt_string_t *string,
208 ssize_t len)
209 {
210 lt_return_val_if_fail (string != NULL, NULL);
211
212 if (len < 0)
213 len = string->len + len;
214 len = LT_MAX (len, 0);
215 string->len = LT_MIN (len, string->len);
216 string->string[string->len] = 0;
217
218 return string;
219 }
220
221 /**
222 * lt_string_clear:
223 * @string: a #lt_string_t
224 *
225 * Clean up the buffer in @string.
226 */
227 void
lt_string_clear(lt_string_t * string)228 lt_string_clear(lt_string_t *string)
229 {
230 lt_string_truncate(string, 0);
231 }
232
233 /**
234 * lt_string_append_c:
235 * @string: a #lt_string_t
236 * @c: the byte to append onto the end of @string
237 *
238 * Adds a byte onto the end of a #lt_string_t, expanding
239 * it if necessary.
240 *
241 * Returns: (transfer none): the same @string object.
242 */
243 lt_string_t *
lt_string_append_c(lt_string_t * string,char c)244 lt_string_append_c(lt_string_t *string,
245 char c)
246 {
247 lt_return_val_if_fail (string != NULL, NULL);
248
249 if ((string->len + 2) >= string->allocated_len) {
250 if (!_lt_string_expand(string, 1))
251 return string;
252 }
253 string->string[string->len++] = c;
254 string->string[string->len] = 0;
255
256 return string;
257 }
258
259 /**
260 * lt_string_append:
261 * @string: a #lt_string_t
262 * @str: the string to append onto the end of @string
263 *
264 * Adds a string onto the end of a #lt_string_t, expanding
265 * it if necessary.
266 *
267 * Returns: (transfer none): the same @string object
268 */
269 lt_string_t *
lt_string_append(lt_string_t * string,const char * str)270 lt_string_append(lt_string_t *string,
271 const char *str)
272 {
273 size_t len;
274
275 lt_return_val_if_fail (string != NULL, NULL);
276 lt_return_val_if_fail (str != NULL, string);
277
278 len = strlen(str);
279 if ((string->len + len + 1) >= string->allocated_len) {
280 if (!_lt_string_expand(string, len))
281 return string;
282 }
283 strncpy(&string->string[string->len], str, len);
284 string->len += len;
285 string->string[string->len] = 0;
286
287 return string;
288 }
289
290 /**
291 * lt_string_append_filename:
292 * @string: a #lt_string_t
293 * @path: the string to append onto the end of @string as a file path
294 * @...: a %NULL-terminated list of strings to append onto the end of @string as a file path
295 *
296 * Adds a string onto the end of a #lt_string_t as a file path.
297 *
298 * Returns: (transfer none): the same @string object
299 */
300 lt_string_t *
lt_string_append_filename(lt_string_t * string,const char * path,...)301 lt_string_append_filename(lt_string_t *string,
302 const char *path,
303 ...)
304 {
305 va_list ap;
306 const char *p;
307
308 lt_return_val_if_fail (string != NULL, NULL);
309 lt_return_val_if_fail (path != NULL, string);
310
311 #ifdef _WIN32
312 /* This simply does not work on Windows with "D:\..." */
313 #else
314 if (lt_string_length(string) == 0 && path[0] != LT_DIR_SEPARATOR)
315 lt_string_append(string, LT_DIR_SEPARATOR_S);
316 #endif
317
318 va_start(ap, path);
319 p = path;
320 while (p) {
321 if (lt_string_length(string) > 0 && lt_string_at(string, -1) != LT_DIR_SEPARATOR)
322 lt_string_append(string, LT_DIR_SEPARATOR_S);
323 lt_string_append(string, p);
324 p = (const char *)va_arg(ap, const char *);
325 }
326 va_end(ap);
327
328 return string;
329 }
330
331 /**
332 * lt_string_append_printf:
333 * @string: a #lt_string_t
334 * @format: the string format. See the printf() documentation
335 * @...: the parameters to insert into the format string
336 *
337 * Appends a formatted string onto the end of a #lt_string_t.
338 * This is similar to the standard sprintf() function,
339 * except that the text is appended to the #lt_string_t.
340 */
341 void
lt_string_append_printf(lt_string_t * string,const char * format,...)342 lt_string_append_printf(lt_string_t *string,
343 const char *format,
344 ...)
345 {
346 va_list ap;
347 char *str;
348
349 lt_return_if_fail (string != NULL);
350 lt_return_if_fail (format != NULL);
351
352 va_start(ap, format);
353 str = lt_strdup_vprintf(format, ap);
354 lt_string_append(string, str);
355 free(str);
356
357 va_end(ap);
358 }
359
360 /**
361 * lt_string_replace_c:
362 * @string: a #lt_string_t
363 * @pos: position in @string where replacement should happen
364 * @c: the byte to replace
365 *
366 * Replaces a character in @string at @pos.
367 *
368 * Returns: (transfer none): the same @string object
369 */
370 lt_string_t *
lt_string_replace_c(lt_string_t * string,size_t pos,char c)371 lt_string_replace_c(lt_string_t *string,
372 size_t pos,
373 char c)
374 {
375 lt_return_val_if_fail (string != NULL, NULL);
376 lt_return_val_if_fail (pos < string->len, string);
377 lt_return_val_if_fail (pos > 0, string);
378
379 string->string[pos] = c;
380
381 return string;
382 }
383
384 /**
385 * lt_string_at:
386 * @string: a #lt_string_t
387 * @pos: position in @string where to obtain the byte
388 *
389 * Obtain a byte in a #lt_string_t at @pos. If @pos is a negative,
390 * the position is calculated from current size. i.e. if the buffer
391 * contains "abc", and @pos is -1, this will returns 'c' then.
392 *
393 * Returns: the byte in @string at @pos
394 */
395 char
lt_string_at(lt_string_t * string,ssize_t pos)396 lt_string_at(lt_string_t *string,
397 ssize_t pos)
398 {
399 lt_return_val_if_fail (string != NULL, 0);
400
401 if (pos < 0)
402 pos = string->len + pos;
403 pos = LT_MAX (pos, 0);
404 pos = LT_MIN (pos, string->len);
405
406 return string->string[pos];
407 }
408