1 /* $NetBSD: str.h,v 1.12 2021/12/12 13:43:47 rillig Exp $ */ 2 3 /* 4 Copyright (c) 2021 Roland Illig <rillig@NetBSD.org> 5 All rights reserved. 6 7 Redistribution and use in source and binary forms, with or without 8 modification, are permitted provided that the following conditions 9 are met: 10 11 1. Redistributions of source code must retain the above copyright 12 notice, this list of conditions and the following disclaimer. 13 2. Redistributions in binary form must reproduce the above copyright 14 notice, this list of conditions and the following disclaimer in the 15 documentation and/or other materials provided with the distribution. 16 17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 21 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 31 /* 32 * Memory-efficient string handling. 33 */ 34 35 36 /* A read-only string that may need to be freed after use. */ 37 typedef struct FStr { 38 const char *str; 39 void *freeIt; 40 } FStr; 41 42 /* A modifiable string that may need to be freed after use. */ 43 typedef struct MFStr { 44 char *str; 45 void *freeIt; 46 } MFStr; 47 48 /* A read-only range of a character array, NOT null-terminated. */ 49 typedef struct Substring { 50 const char *start; 51 const char *end; 52 } Substring; 53 54 /* 55 * Builds a string, only allocating memory if the string is different from the 56 * expected string. 57 */ 58 typedef struct LazyBuf { 59 char *data; 60 size_t len; 61 size_t cap; 62 const char *expected; 63 } LazyBuf; 64 65 /* The result of splitting a string into words. */ 66 typedef struct Words { 67 char **words; 68 size_t len; 69 void *freeIt; 70 } Words; 71 72 /* The result of splitting a string into words. */ 73 typedef struct SubstringWords { 74 Substring *words; 75 size_t len; 76 void *freeIt; 77 } SubstringWords; 78 79 80 MAKE_INLINE FStr 81 FStr_Init(const char *str, void *freeIt) 82 { 83 FStr fstr; 84 fstr.str = str; 85 fstr.freeIt = freeIt; 86 return fstr; 87 } 88 89 /* Return a string that is the sole owner of str. */ 90 MAKE_INLINE FStr 91 FStr_InitOwn(char *str) 92 { 93 return FStr_Init(str, str); 94 } 95 96 /* Return a string that refers to the shared str. */ 97 MAKE_INLINE FStr 98 FStr_InitRefer(const char *str) 99 { 100 return FStr_Init(str, NULL); 101 } 102 103 MAKE_INLINE void 104 FStr_Done(FStr *fstr) 105 { 106 free(fstr->freeIt); 107 #ifdef CLEANUP 108 fstr->str = NULL; 109 fstr->freeIt = NULL; 110 #endif 111 } 112 113 114 MAKE_INLINE MFStr 115 MFStr_Init(char *str, void *freeIt) 116 { 117 MFStr mfstr; 118 mfstr.str = str; 119 mfstr.freeIt = freeIt; 120 return mfstr; 121 } 122 123 /* Return a string that is the sole owner of str. */ 124 MAKE_INLINE MFStr 125 MFStr_InitOwn(char *str) 126 { 127 return MFStr_Init(str, str); 128 } 129 130 /* Return a string that refers to the shared str. */ 131 MAKE_INLINE MFStr 132 MFStr_InitRefer(char *str) 133 { 134 return MFStr_Init(str, NULL); 135 } 136 137 MAKE_INLINE void 138 MFStr_Done(MFStr *mfstr) 139 { 140 free(mfstr->freeIt); 141 #ifdef CLEANUP 142 mfstr->str = NULL; 143 mfstr->freeIt = NULL; 144 #endif 145 } 146 147 148 MAKE_STATIC Substring 149 Substring_Init(const char *start, const char *end) 150 { 151 Substring sub; 152 153 sub.start = start; 154 sub.end = end; 155 return sub; 156 } 157 158 MAKE_INLINE Substring 159 Substring_InitStr(const char *str) 160 { 161 return Substring_Init(str, str + strlen(str)); 162 } 163 164 MAKE_STATIC size_t 165 Substring_Length(Substring sub) 166 { 167 return (size_t)(sub.end - sub.start); 168 } 169 170 MAKE_STATIC bool 171 Substring_IsEmpty(Substring sub) 172 { 173 return sub.start == sub.end; 174 } 175 176 MAKE_INLINE bool 177 Substring_Equals(Substring sub, const char *str) 178 { 179 size_t len = strlen(str); 180 return Substring_Length(sub) == len && 181 memcmp(sub.start, str, len) == 0; 182 } 183 184 MAKE_INLINE bool 185 Substring_Eq(Substring sub, Substring str) 186 { 187 size_t len = Substring_Length(sub); 188 return len == Substring_Length(str) && 189 memcmp(sub.start, str.start, len) == 0; 190 } 191 192 MAKE_STATIC Substring 193 Substring_Sub(Substring sub, size_t start, size_t end) 194 { 195 assert(start <= Substring_Length(sub)); 196 assert(end <= Substring_Length(sub)); 197 return Substring_Init(sub.start + start, sub.start + end); 198 } 199 200 MAKE_STATIC bool 201 Substring_HasPrefix(Substring sub, Substring prefix) 202 { 203 return Substring_Length(sub) >= Substring_Length(prefix) && 204 memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0; 205 } 206 207 MAKE_STATIC bool 208 Substring_HasSuffix(Substring sub, Substring suffix) 209 { 210 size_t suffixLen = Substring_Length(suffix); 211 return Substring_Length(sub) >= suffixLen && 212 memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0; 213 } 214 215 /* Returns an independent, null-terminated copy of the substring. */ 216 MAKE_STATIC FStr 217 Substring_Str(Substring sub) 218 { 219 if (Substring_IsEmpty(sub)) 220 return FStr_InitRefer(""); 221 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end)); 222 } 223 224 MAKE_STATIC const char * 225 Substring_SkipFirst(Substring sub, char ch) 226 { 227 const char *p; 228 229 for (p = sub.start; p != sub.end; p++) 230 if (*p == ch) 231 return p + 1; 232 return sub.start; 233 } 234 235 MAKE_STATIC const char * 236 Substring_LastIndex(Substring sub, char ch) 237 { 238 const char *p; 239 240 for (p = sub.end; p != sub.start; p--) 241 if (p[-1] == ch) 242 return p - 1; 243 return NULL; 244 } 245 246 MAKE_STATIC Substring 247 Substring_Dirname(Substring pathname) 248 { 249 const char *p; 250 251 for (p = pathname.end; p != pathname.start; p--) 252 if (p[-1] == '/') 253 return Substring_Init(pathname.start, p - 1); 254 return Substring_InitStr("."); 255 } 256 257 MAKE_STATIC Substring 258 Substring_Basename(Substring pathname) 259 { 260 const char *p; 261 262 for (p = pathname.end; p != pathname.start; p--) 263 if (p[-1] == '/') 264 return Substring_Init(p, pathname.end); 265 return pathname; 266 } 267 268 269 MAKE_STATIC void 270 LazyBuf_Init(LazyBuf *buf, const char *expected) 271 { 272 buf->data = NULL; 273 buf->len = 0; 274 buf->cap = 0; 275 buf->expected = expected; 276 } 277 278 MAKE_INLINE void 279 LazyBuf_Done(LazyBuf *buf) 280 { 281 free(buf->data); 282 } 283 284 MAKE_STATIC void 285 LazyBuf_Add(LazyBuf *buf, char ch) 286 { 287 288 if (buf->data != NULL) { 289 if (buf->len == buf->cap) { 290 buf->cap *= 2; 291 buf->data = bmake_realloc(buf->data, buf->cap); 292 } 293 buf->data[buf->len++] = ch; 294 295 } else if (ch == buf->expected[buf->len]) { 296 buf->len++; 297 return; 298 299 } else { 300 buf->cap = buf->len + 16; 301 buf->data = bmake_malloc(buf->cap); 302 memcpy(buf->data, buf->expected, buf->len); 303 buf->data[buf->len++] = ch; 304 } 305 } 306 307 MAKE_STATIC void 308 LazyBuf_AddStr(LazyBuf *buf, const char *str) 309 { 310 const char *p; 311 312 for (p = str; *p != '\0'; p++) 313 LazyBuf_Add(buf, *p); 314 } 315 316 MAKE_STATIC void 317 LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end) 318 { 319 const char *p; 320 321 for (p = start; p != end; p++) 322 LazyBuf_Add(buf, *p); 323 } 324 325 MAKE_INLINE void 326 LazyBuf_AddSubstring(LazyBuf *buf, Substring sub) 327 { 328 LazyBuf_AddBytesBetween(buf, sub.start, sub.end); 329 } 330 331 MAKE_STATIC Substring 332 LazyBuf_Get(const LazyBuf *buf) 333 { 334 const char *start = buf->data != NULL ? buf->data : buf->expected; 335 return Substring_Init(start, start + buf->len); 336 } 337 338 /* 339 * Returns the content of the buffer as a newly allocated string. 340 * 341 * See LazyBuf_Get to avoid unnecessary memory allocations. 342 */ 343 MAKE_STATIC FStr 344 LazyBuf_DoneGet(LazyBuf *buf) 345 { 346 if (buf->data != NULL) { 347 LazyBuf_Add(buf, '\0'); 348 return FStr_InitOwn(buf->data); 349 } 350 return Substring_Str(LazyBuf_Get(buf)); 351 } 352 353 354 Words Str_Words(const char *, bool); 355 356 MAKE_INLINE void 357 Words_Free(Words w) 358 { 359 free(w.words); 360 free(w.freeIt); 361 } 362 363 364 SubstringWords Substring_Words(const char *, bool); 365 366 MAKE_INLINE void 367 SubstringWords_Init(SubstringWords *w) 368 { 369 w->words = NULL; 370 w->len = 0; 371 w->freeIt = NULL; 372 } 373 374 MAKE_INLINE void 375 SubstringWords_Free(SubstringWords w) 376 { 377 free(w.words); 378 free(w.freeIt); 379 } 380 381 382 char *str_concat2(const char *, const char *); 383 char *str_concat3(const char *, const char *, const char *); 384 385 bool Str_Match(const char *, const char *); 386