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