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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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