1c2c66affSColin Finck /*
2c2c66affSColin Finck stringbuf: mimicking a bit of C++ to more safely handle strings
3c2c66affSColin Finck
4*aa811c00SThomas Faber copyright 2006-20 by the mpg123 project
5c2c66affSColin Finck - free software under the terms of the LGPL 2.1
6c2c66affSColin Finck see COPYING and AUTHORS files in distribution or http://mpg123.org
7c2c66affSColin Finck initially written by Thomas Orgis
8c2c66affSColin Finck */
9c2c66affSColin Finck
10c2c66affSColin Finck #include "mpg123lib_intern.h"
11c2c66affSColin Finck #include "config.h"
12c2c66affSColin Finck #include "mpg123.h"
13c2c66affSColin Finck #include "compat.h"
14c2c66affSColin Finck #include <string.h>
15c2c66affSColin Finck #include "debug.h"
16c2c66affSColin Finck
mpg123_new_string(const char * val)17*aa811c00SThomas Faber mpg123_string* attribute_align_arg mpg123_new_string(const char *val)
18*aa811c00SThomas Faber {
19*aa811c00SThomas Faber mpg123_string *sb = malloc(sizeof(mpg123_string));
20*aa811c00SThomas Faber if(!sb)
21*aa811c00SThomas Faber return NULL;
22*aa811c00SThomas Faber mpg123_init_string(sb);
23*aa811c00SThomas Faber mpg123_set_string(sb, val);
24*aa811c00SThomas Faber return sb;
25*aa811c00SThomas Faber }
26*aa811c00SThomas Faber
mpg123_delete_string(mpg123_string * sb)27*aa811c00SThomas Faber void attribute_align_arg mpg123_delete_string(mpg123_string* sb)
28*aa811c00SThomas Faber {
29*aa811c00SThomas Faber if(!sb)
30*aa811c00SThomas Faber return;
31*aa811c00SThomas Faber mpg123_free_string(sb);
32*aa811c00SThomas Faber free(sb);
33*aa811c00SThomas Faber }
34*aa811c00SThomas Faber
mpg123_init_string(mpg123_string * sb)35c2c66affSColin Finck void attribute_align_arg mpg123_init_string(mpg123_string* sb)
36c2c66affSColin Finck {
37c2c66affSColin Finck /* Handing in NULL here is a fatal mistake and rightfully so. */
38c2c66affSColin Finck sb->p = NULL;
39c2c66affSColin Finck sb->size = 0;
40c2c66affSColin Finck sb->fill = 0;
41c2c66affSColin Finck }
42c2c66affSColin Finck
mpg123_free_string(mpg123_string * sb)43c2c66affSColin Finck void attribute_align_arg mpg123_free_string(mpg123_string* sb)
44c2c66affSColin Finck {
45c2c66affSColin Finck if(!sb)
46c2c66affSColin Finck return;
47c2c66affSColin Finck if(sb->p != NULL) free(sb->p);
48c2c66affSColin Finck mpg123_init_string(sb);
49c2c66affSColin Finck }
50c2c66affSColin Finck
mpg123_grow_string(mpg123_string * sb,size_t new)51c2c66affSColin Finck int attribute_align_arg mpg123_grow_string(mpg123_string* sb, size_t new)
52c2c66affSColin Finck {
53c2c66affSColin Finck if(!sb)
54c2c66affSColin Finck return 0;
55c2c66affSColin Finck if(sb->size < new) return mpg123_resize_string(sb, new);
56c2c66affSColin Finck else return 1;
57c2c66affSColin Finck }
58c2c66affSColin Finck
mpg123_resize_string(mpg123_string * sb,size_t new)59c2c66affSColin Finck int attribute_align_arg mpg123_resize_string(mpg123_string* sb, size_t new)
60c2c66affSColin Finck {
61c2c66affSColin Finck if(!sb)
62c2c66affSColin Finck return 0;
63c2c66affSColin Finck debug3("resizing string pointer %p from %lu to %lu", (void*) sb->p, (unsigned long)sb->size, (unsigned long)new);
64c2c66affSColin Finck if(new == 0)
65c2c66affSColin Finck {
66c2c66affSColin Finck if(sb->size && sb->p != NULL) free(sb->p);
67c2c66affSColin Finck mpg123_init_string(sb);
68c2c66affSColin Finck return 1;
69c2c66affSColin Finck }
70c2c66affSColin Finck if(sb->size != new)
71c2c66affSColin Finck {
72c2c66affSColin Finck char* t;
73c2c66affSColin Finck debug("really!");
74c2c66affSColin Finck t = (char*) safe_realloc(sb->p, new*sizeof(char));
75c2c66affSColin Finck debug1("safe_realloc returned %p", (void*) t);
76c2c66affSColin Finck if(t != NULL)
77c2c66affSColin Finck {
78c2c66affSColin Finck sb->p = t;
79c2c66affSColin Finck sb->size = new;
80*aa811c00SThomas Faber if(sb->size < sb->fill)
81*aa811c00SThomas Faber {
82*aa811c00SThomas Faber // Cut short the existing data, properly.
83*aa811c00SThomas Faber sb->fill = sb->size;
84*aa811c00SThomas Faber sb->p[sb->fill-1] = 0;
85*aa811c00SThomas Faber }
86c2c66affSColin Finck return 1;
87c2c66affSColin Finck }
88c2c66affSColin Finck else return 0;
89c2c66affSColin Finck }
90c2c66affSColin Finck else return 1; /* success */
91c2c66affSColin Finck }
92c2c66affSColin Finck
mpg123_copy_string(mpg123_string * from,mpg123_string * to)93c2c66affSColin Finck int attribute_align_arg mpg123_copy_string(mpg123_string* from, mpg123_string* to)
94c2c66affSColin Finck {
95c2c66affSColin Finck size_t fill;
96c2c66affSColin Finck char *text;
97c2c66affSColin Finck
98c2c66affSColin Finck debug2("called copy_string with %p -> %p", (void*)from, (void*)to);
99c2c66affSColin Finck if(to == NULL)
100c2c66affSColin Finck return 0;
101c2c66affSColin Finck if(from == NULL)
102c2c66affSColin Finck {
103c2c66affSColin Finck fill = 0;
104c2c66affSColin Finck text = NULL;
105c2c66affSColin Finck }
106c2c66affSColin Finck else
107c2c66affSColin Finck {
108c2c66affSColin Finck fill = from->fill;
109c2c66affSColin Finck text = from->p;
110c2c66affSColin Finck }
111c2c66affSColin Finck
112c2c66affSColin Finck if(mpg123_resize_string(to, fill))
113c2c66affSColin Finck {
114c2c66affSColin Finck if(fill) /* Avoid memcpy(NULL, NULL, 0) */
115c2c66affSColin Finck memcpy(to->p, text, fill);
116c2c66affSColin Finck to->fill = fill;
117c2c66affSColin Finck return 1;
118c2c66affSColin Finck }
119c2c66affSColin Finck else return 0;
120c2c66affSColin Finck }
121c2c66affSColin Finck
mpg123_move_string(mpg123_string * from,mpg123_string * to)122*aa811c00SThomas Faber int attribute_align_arg mpg123_move_string(mpg123_string *from, mpg123_string *to)
123*aa811c00SThomas Faber {
124*aa811c00SThomas Faber if(to)
125*aa811c00SThomas Faber mpg123_free_string(to);
126*aa811c00SThomas Faber else
127*aa811c00SThomas Faber mpg123_free_string(from);
128*aa811c00SThomas Faber if(from && to)
129*aa811c00SThomas Faber *to = *from;
130*aa811c00SThomas Faber if(from)
131*aa811c00SThomas Faber mpg123_init_string(from);
132*aa811c00SThomas Faber return (from && to) ? 1 : 0;
133*aa811c00SThomas Faber }
134*aa811c00SThomas Faber
mpg123_add_string(mpg123_string * sb,const char * stuff)135c2c66affSColin Finck int attribute_align_arg mpg123_add_string(mpg123_string* sb, const char* stuff)
136c2c66affSColin Finck {
137c2c66affSColin Finck debug1("adding %s", stuff);
138c2c66affSColin Finck return mpg123_add_substring(sb, stuff, 0, stuff ? strlen(stuff) : 0);
139c2c66affSColin Finck }
140c2c66affSColin Finck
mpg123_add_substring(mpg123_string * sb,const char * stuff,size_t from,size_t count)141c2c66affSColin Finck int attribute_align_arg mpg123_add_substring(mpg123_string *sb, const char *stuff, size_t from, size_t count)
142c2c66affSColin Finck {
143c2c66affSColin Finck debug("adding a substring");
144c2c66affSColin Finck if(!sb || !stuff)
145c2c66affSColin Finck return 0;
146c2c66affSColin Finck if(sb->fill) /* includes zero byte... */
147c2c66affSColin Finck {
148c2c66affSColin Finck if( (SIZE_MAX - sb->fill >= count) /* Avoid overflow. */
149c2c66affSColin Finck && (sb->size >= sb->fill+count || mpg123_grow_string(sb, sb->fill+count)) )
150c2c66affSColin Finck {
151c2c66affSColin Finck memcpy(sb->p+sb->fill-1, stuff+from, count);
152c2c66affSColin Finck sb->fill += count;
153c2c66affSColin Finck sb->p[sb->fill-1] = 0; /* Terminate! */
154c2c66affSColin Finck }
155c2c66affSColin Finck else return 0;
156c2c66affSColin Finck }
157c2c66affSColin Finck else
158c2c66affSColin Finck {
159c2c66affSColin Finck if( count < SIZE_MAX && mpg123_grow_string(sb, count+1) )
160c2c66affSColin Finck {
161c2c66affSColin Finck memcpy(sb->p, stuff+from, count);
162c2c66affSColin Finck sb->fill = count+1;
163c2c66affSColin Finck sb->p[sb->fill-1] = 0; /* Terminate! */
164c2c66affSColin Finck }
165c2c66affSColin Finck else return 0;
166c2c66affSColin Finck }
167c2c66affSColin Finck return 1;
168c2c66affSColin Finck }
169c2c66affSColin Finck
mpg123_set_substring(mpg123_string * sb,const char * stuff,size_t from,size_t count)170c2c66affSColin Finck int attribute_align_arg mpg123_set_substring(mpg123_string* sb, const char* stuff, size_t from, size_t count)
171c2c66affSColin Finck {
172c2c66affSColin Finck if(!sb)
173c2c66affSColin Finck return 0;
174c2c66affSColin Finck sb->fill = 0;
175c2c66affSColin Finck return mpg123_add_substring(sb, stuff, from, count);
176c2c66affSColin Finck }
177c2c66affSColin Finck
mpg123_set_string(mpg123_string * sb,const char * stuff)178c2c66affSColin Finck int attribute_align_arg mpg123_set_string(mpg123_string* sb, const char* stuff)
179c2c66affSColin Finck {
180c2c66affSColin Finck if(!sb)
181c2c66affSColin Finck return 0;
182c2c66affSColin Finck sb->fill = 0;
183c2c66affSColin Finck return mpg123_add_string(sb, stuff);
184c2c66affSColin Finck }
185c2c66affSColin Finck
mpg123_strlen(mpg123_string * sb,int utf8)186c2c66affSColin Finck size_t attribute_align_arg mpg123_strlen(mpg123_string *sb, int utf8)
187c2c66affSColin Finck {
188c2c66affSColin Finck size_t i;
189c2c66affSColin Finck size_t bytelen;
190c2c66affSColin Finck
191c2c66affSColin Finck /* Notions of empty string. If there's only a single character, it has to be the trailing zero, and if the first is the trailing zero anyway, we got empty. */
192c2c66affSColin Finck if(!sb || sb->fill < 2 || sb->p[0] == 0) return 0;
193c2c66affSColin Finck
194c2c66affSColin Finck /* Find the first non-null character from the back.
195c2c66affSColin Finck We already established that the first character is non-null
196c2c66affSColin Finck That at fill-2 has to be null, though. */
197c2c66affSColin Finck for(i=sb->fill-2; i>0; --i)
198c2c66affSColin Finck if(sb->p[i] != 0) break;
199c2c66affSColin Finck
200c2c66affSColin Finck /* For simple byte strings, we are done now. */
201c2c66affSColin Finck bytelen = i+1;
202c2c66affSColin Finck
203c2c66affSColin Finck if(!utf8) return bytelen;
204c2c66affSColin Finck else
205c2c66affSColin Finck {
206c2c66affSColin Finck /* Work out the actual count of UTF8 bytes.
207c2c66affSColin Finck This employs no particular encoding error checking. */
208c2c66affSColin Finck size_t len = 0;
209c2c66affSColin Finck for(i=0; i<bytelen; ++i)
210c2c66affSColin Finck {
211c2c66affSColin Finck /* Every byte that is not a continuation byte ( 0xc0 == 10xx xxxx ) stands for a character. */
212c2c66affSColin Finck if((sb->p[i] & 0xc0) != 0x80) len++;
213c2c66affSColin Finck }
214c2c66affSColin Finck return len;
215c2c66affSColin Finck }
216c2c66affSColin Finck }
217c2c66affSColin Finck
mpg123_chomp_string(mpg123_string * sb)218c2c66affSColin Finck int attribute_align_arg mpg123_chomp_string(mpg123_string *sb)
219c2c66affSColin Finck {
220c2c66affSColin Finck ssize_t i;
221c2c66affSColin Finck if(!sb || !sb->fill) return 0;
222c2c66affSColin Finck
223c2c66affSColin Finck /* Ensure that it is zero-terminated. */
224c2c66affSColin Finck sb->p[sb->fill-1] = 0;
225c2c66affSColin Finck for(i=sb->fill-2; i>=0; --i)
226c2c66affSColin Finck {
227c2c66affSColin Finck char *c = sb->p+i;
228c2c66affSColin Finck /* Stop at the first proper character. */
229c2c66affSColin Finck if(*c && *c != '\r' && *c != '\n') break;
230c2c66affSColin Finck else *c = 0;
231c2c66affSColin Finck }
232c2c66affSColin Finck /* initial fill at least 1, so i at least -1,
233c2c66affSColin Finck +2 means nothing happened for fill=1 .
234c2c66affSColin Finck With i=0, we got one non-null character, fill shall be 2
235c2c66affSColin Finck to accomodate the trailing zero. */
236c2c66affSColin Finck sb->fill = (size_t)i+2;
237c2c66affSColin Finck
238c2c66affSColin Finck return 1;
239c2c66affSColin Finck }
240*aa811c00SThomas Faber
mpg123_same_string(mpg123_string * a,mpg123_string * b)241*aa811c00SThomas Faber int attribute_align_arg mpg123_same_string(mpg123_string *a, mpg123_string *b)
242*aa811c00SThomas Faber {
243*aa811c00SThomas Faber if(!a || !b)
244*aa811c00SThomas Faber return 0;
245*aa811c00SThomas Faber if(a->fill != b->fill)
246*aa811c00SThomas Faber return 0;
247*aa811c00SThomas Faber if(memcmp(a->p, b->p, a->fill))
248*aa811c00SThomas Faber return 0;
249*aa811c00SThomas Faber return 1;
250*aa811c00SThomas Faber }
251