str.c (5726B)
1 /* Copyright (C) 2013-2023, 2025 Vincent Forest (vaplv@free.fr) 2 * 3 * The RSys library is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published 5 * by the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * The RSys library is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* vsnprintf support */ 17 #include "str.h" 18 19 #include <string.h> 20 21 /******************************************************************************* 22 * helper function 23 ******************************************************************************/ 24 static res_T 25 ensure_allocated(struct str* str, const size_t len, const char keep_old) 26 { 27 char* buf = NULL; 28 const size_t alloc_granularity = 32; 29 size_t mod = 0; 30 size_t new_len = 0; 31 size_t new_size = 0; 32 ASSERT( str ); 33 34 if(len * sizeof(char) <= str->allocated) 35 return RES_OK; 36 37 mod = len % alloc_granularity; 38 new_len = !mod ? len : len - mod + alloc_granularity; 39 new_size = new_len * sizeof(char); 40 buf = MEM_ALLOC(str->allocator, new_size); 41 if(!buf) 42 return RES_MEM_ERR; 43 44 if(keep_old) { 45 strncpy( buf, str->cstr, new_len - 1); 46 buf[new_len - 1] = '\0'; 47 } 48 49 str->allocated = new_len * sizeof(char); 50 if(str->cstr != str->buffer) 51 MEM_RM(str->allocator, str->cstr); 52 53 str->cstr = buf; 54 return RES_OK; 55 } 56 57 /******************************************************************************* 58 * Exported functions 59 ******************************************************************************/ 60 res_T 61 str_set(struct str* str, const char* cstr) 62 { 63 size_t cstr_len = 0; 64 res_T res = 0; 65 ASSERT(str && cstr); 66 67 cstr_len = strlen(cstr); 68 res = ensure_allocated(str, cstr_len + 1, 0); 69 if(res != RES_OK) return res; 70 strncpy(str->cstr, cstr, cstr_len + 1); 71 return RES_OK; 72 } 73 74 res_T 75 str_insert(struct str* str, const size_t i, const char* cstr) 76 { 77 size_t len = 0; 78 size_t cstr_len = 0; 79 ASSERT(str); 80 81 len = str_len(str); 82 83 if(i > len) 84 return RES_BAD_ARG; 85 86 cstr_len = strlen(cstr); 87 ASSERT(!MEM_AREA_OVERLAP 88 (str->cstr, str->allocated, cstr, (cstr_len + 1) * sizeof(char))); 89 90 if(i == len) { 91 return str_append(str, cstr); 92 } else { 93 const res_T res = ensure_allocated(str, cstr_len + len + 1, 1); 94 if(res != RES_OK) 95 return res; 96 memmove 97 (str->cstr + i + cstr_len, 98 str->cstr + i, 99 (len - i) * sizeof(char)); 100 memcpy(str->cstr + i, cstr, cstr_len * sizeof(char)); 101 str->cstr[len + cstr_len] = '\0'; 102 } 103 return RES_OK; 104 } 105 106 res_T 107 str_insert_char(struct str* str, const size_t i, const char ch) 108 { 109 size_t len = 0; 110 ASSERT(str); 111 112 len = str_len(str); 113 if(i > len) 114 return RES_BAD_ARG; 115 116 if(i == len) { 117 return str_append_char(str, ch); 118 } else if(ch == '\0') { 119 str->cstr[i] = ch; 120 } else { 121 const res_T res = ensure_allocated(str, len + 2, 1); 122 if(res != RES_OK) 123 return res; 124 memmove 125 (str->cstr + i + 1, 126 str->cstr + i, 127 (len - i) * sizeof(char)); 128 str->cstr[i] = ch; 129 str->cstr[len+1] = '\0'; 130 } 131 return RES_OK; 132 } 133 134 res_T 135 str_append(struct str* str, const char* cstr) 136 { 137 size_t len = 0; 138 size_t cstr_len = 0; 139 res_T res = RES_OK; 140 ASSERT(str && cstr); 141 142 cstr_len = strlen(cstr); 143 ASSERT(!MEM_AREA_OVERLAP 144 (str->cstr, str->allocated, cstr, (cstr_len + 1) * sizeof(char))); 145 146 len = str_len(str); 147 res = ensure_allocated(str, cstr_len + len + 1, 1); 148 if(res != RES_OK) 149 return res; 150 151 memcpy(str->cstr + len, cstr, cstr_len * sizeof(char)); 152 str->cstr[len + cstr_len] = '\0'; 153 return RES_OK; 154 } 155 156 res_T 157 str_append_char(struct str* str, const char ch) 158 { 159 size_t len = 0; 160 res_T res = RES_OK; 161 ASSERT(str); 162 163 if(ch == '\0') 164 return RES_OK; 165 166 len = str_len(str); 167 res = ensure_allocated(str, len + 2, 1); 168 if(res != RES_OK) return res; 169 170 str->cstr[len+0] = ch; 171 str->cstr[len+1] = '\0'; 172 return RES_OK; 173 } 174 175 res_T 176 str_reserve(struct str* str, const size_t capacity) 177 { 178 return ensure_allocated(str, capacity / sizeof(char), 1); 179 } 180 181 182 res_T 183 str_printf(struct str* str, const char* fmt, ...) 184 { 185 va_list ap; 186 res_T res = RES_OK; 187 ASSERT(str && fmt); 188 va_start(ap, fmt); 189 res = str_vprintf(str, fmt, ap); 190 va_end(ap); 191 return res; 192 } 193 194 res_T 195 str_append_printf(struct str* str, const char* fmt, ...) 196 { 197 va_list ap; 198 res_T res = RES_OK; 199 ASSERT(str && fmt); 200 va_start(ap, fmt); 201 res = str_append_vprintf(str, fmt, ap); 202 va_end(ap); 203 return res; 204 } 205 206 res_T 207 str_vprintf(struct str* str, const char* fmt, va_list vargs_list) 208 { 209 ASSERT(str && fmt); 210 str_clear(str); 211 return str_append_vprintf(str, fmt, vargs_list); 212 } 213 214 res_T 215 str_append_vprintf(struct str* str, const char* fmt, va_list vargs_list) 216 { 217 va_list ap; 218 size_t flen; /* Length of the formatted message */ 219 size_t slen; /* Length of the string */ 220 res_T res = RES_OK; 221 ASSERT(str && fmt); 222 223 slen = str_len(str); 224 225 VA_COPY(ap, vargs_list); 226 flen = (size_t)vsnprintf(str->cstr + slen, str->allocated - slen, fmt, ap); 227 va_end(ap); 228 229 if(slen + flen >= str->allocated) { 230 res = ensure_allocated(str, slen + flen + 1/* Null char */, 1); 231 if(res != RES_OK) goto error; 232 233 VA_COPY(ap, vargs_list); 234 flen = (size_t)vsnprintf(str->cstr + slen, str->allocated - slen, fmt, ap); 235 va_end(ap); 236 CHK(slen + flen < str->allocated); 237 } 238 239 exit: 240 return res; 241 error: 242 goto exit; 243 }