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