1 /* $NetBSD: str.h,v 1.9 2021/05/30 21:16:54 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 void *freeIt; 64 } LazyBuf; 65 66 /* The result of splitting a string into words. */ 67 typedef struct Words { 68 char **words; 69 size_t len; 70 void *freeIt; 71 } Words; 72 73 /* The result of splitting a string into words. */ 74 typedef struct SubstringWords { 75 Substring *words; 76 size_t len; 77 void *freeIt; 78 } SubstringWords; 79 80 81 MAKE_INLINE FStr 82 FStr_Init(const char *str, void *freeIt) 83 { 84 FStr fstr; 85 fstr.str = str; 86 fstr.freeIt = freeIt; 87 return fstr; 88 } 89 90 /* Return a string that is the sole owner of str. */ 91 MAKE_INLINE FStr 92 FStr_InitOwn(char *str) 93 { 94 return FStr_Init(str, str); 95 } 96 97 /* Return a string that refers to the shared str. */ 98 MAKE_INLINE FStr 99 FStr_InitRefer(const char *str) 100 { 101 return FStr_Init(str, NULL); 102 } 103 104 MAKE_INLINE void 105 FStr_Done(FStr *fstr) 106 { 107 free(fstr->freeIt); 108 #ifdef CLEANUP 109 fstr->str = NULL; 110 fstr->freeIt = NULL; 111 #endif 112 } 113 114 115 MAKE_INLINE MFStr 116 MFStr_Init(char *str, void *freeIt) 117 { 118 MFStr mfstr; 119 mfstr.str = str; 120 mfstr.freeIt = freeIt; 121 return mfstr; 122 } 123 124 /* Return a string that is the sole owner of str. */ 125 MAKE_INLINE MFStr 126 MFStr_InitOwn(char *str) 127 { 128 return MFStr_Init(str, str); 129 } 130 131 /* Return a string that refers to the shared str. */ 132 MAKE_INLINE MFStr 133 MFStr_InitRefer(char *str) 134 { 135 return MFStr_Init(str, NULL); 136 } 137 138 MAKE_INLINE void 139 MFStr_Done(MFStr *mfstr) 140 { 141 free(mfstr->freeIt); 142 #ifdef CLEANUP 143 mfstr->str = NULL; 144 mfstr->freeIt = NULL; 145 #endif 146 } 147 148 149 MAKE_STATIC Substring 150 Substring_Init(const char *start, const char *end) 151 { 152 Substring sub; 153 154 sub.start = start; 155 sub.end = end; 156 return sub; 157 } 158 159 MAKE_INLINE Substring 160 Substring_InitStr(const char *str) 161 { 162 return Substring_Init(str, str + strlen(str)); 163 } 164 165 MAKE_STATIC size_t 166 Substring_Length(Substring sub) 167 { 168 return (size_t)(sub.end - sub.start); 169 } 170 171 MAKE_STATIC bool 172 Substring_IsEmpty(Substring sub) 173 { 174 return sub.start == sub.end; 175 } 176 177 MAKE_INLINE bool 178 Substring_Equals(Substring sub, const char *str) 179 { 180 size_t len = strlen(str); 181 return Substring_Length(sub) == len && 182 memcmp(sub.start, str, len) == 0; 183 } 184 185 MAKE_STATIC Substring 186 Substring_Sub(Substring sub, size_t start, size_t end) 187 { 188 assert(start <= Substring_Length(sub)); 189 assert(end <= Substring_Length(sub)); 190 return Substring_Init(sub.start + start, sub.start + end); 191 } 192 193 MAKE_STATIC bool 194 Substring_HasPrefix(Substring sub, Substring prefix) 195 { 196 return Substring_Length(sub) >= Substring_Length(prefix) && 197 memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0; 198 } 199 200 MAKE_STATIC bool 201 Substring_HasSuffix(Substring sub, Substring suffix) 202 { 203 size_t suffixLen = Substring_Length(suffix); 204 return Substring_Length(sub) >= suffixLen && 205 memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0; 206 } 207 208 /* Returns an independent, null-terminated copy of the substring. */ 209 MAKE_STATIC FStr 210 Substring_Str(Substring sub) 211 { 212 if (Substring_IsEmpty(sub)) 213 return FStr_InitRefer(""); 214 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end)); 215 } 216 217 MAKE_STATIC const char * 218 Substring_SkipFirst(Substring sub, char ch) 219 { 220 const char *p; 221 222 for (p = sub.start; p != sub.end; p++) 223 if (*p == ch) 224 return p + 1; 225 return sub.start; 226 } 227 228 MAKE_STATIC const char * 229 Substring_LastIndex(Substring sub, char ch) 230 { 231 const char *p; 232 233 for (p = sub.end; p != sub.start; p--) 234 if (p[-1] == ch) 235 return p - 1; 236 return NULL; 237 } 238 239 MAKE_STATIC Substring 240 Substring_Dirname(Substring pathname) 241 { 242 const char *p; 243 244 for (p = pathname.end; p != pathname.start; p--) 245 if (p[-1] == '/') 246 return Substring_Init(pathname.start, p - 1); 247 return Substring_InitStr("."); 248 } 249 250 MAKE_STATIC Substring 251 Substring_Basename(Substring pathname) 252 { 253 const char *p; 254 255 for (p = pathname.end; p != pathname.start; p--) 256 if (p[-1] == '/') 257 return Substring_Init(p, pathname.end); 258 return pathname; 259 } 260 261 262 MAKE_STATIC void 263 LazyBuf_Init(LazyBuf *buf, const char *expected) 264 { 265 buf->data = NULL; 266 buf->len = 0; 267 buf->cap = 0; 268 buf->expected = expected; 269 buf->freeIt = NULL; 270 } 271 272 MAKE_INLINE void 273 LazyBuf_Done(LazyBuf *buf) 274 { 275 free(buf->freeIt); 276 } 277 278 MAKE_STATIC void 279 LazyBuf_Add(LazyBuf *buf, char ch) 280 { 281 282 if (buf->data != NULL) { 283 if (buf->len == buf->cap) { 284 buf->cap *= 2; 285 buf->data = bmake_realloc(buf->data, buf->cap); 286 } 287 buf->data[buf->len++] = ch; 288 289 } else if (ch == buf->expected[buf->len]) { 290 buf->len++; 291 return; 292 293 } else { 294 buf->cap = buf->len + 16; 295 buf->data = bmake_malloc(buf->cap); 296 memcpy(buf->data, buf->expected, buf->len); 297 buf->data[buf->len++] = ch; 298 } 299 } 300 301 MAKE_STATIC void 302 LazyBuf_AddStr(LazyBuf *buf, const char *str) 303 { 304 const char *p; 305 306 for (p = str; *p != '\0'; p++) 307 LazyBuf_Add(buf, *p); 308 } 309 310 MAKE_STATIC void 311 LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end) 312 { 313 const char *p; 314 315 for (p = start; p != end; p++) 316 LazyBuf_Add(buf, *p); 317 } 318 319 MAKE_INLINE void 320 LazyBuf_AddSubstring(LazyBuf *buf, Substring sub) 321 { 322 LazyBuf_AddBytesBetween(buf, sub.start, sub.end); 323 } 324 325 MAKE_STATIC Substring 326 LazyBuf_Get(const LazyBuf *buf) 327 { 328 const char *start = buf->data != NULL ? buf->data : buf->expected; 329 return Substring_Init(start, start + buf->len); 330 } 331 332 MAKE_STATIC FStr 333 LazyBuf_DoneGet(LazyBuf *buf) 334 { 335 if (buf->data != NULL) { 336 LazyBuf_Add(buf, '\0'); 337 return FStr_InitOwn(buf->data); 338 } 339 return Substring_Str(LazyBuf_Get(buf)); 340 } 341 342 343 Words Str_Words(const char *, bool); 344 345 MAKE_INLINE void 346 Words_Free(Words w) 347 { 348 free(w.words); 349 free(w.freeIt); 350 } 351 352 353 SubstringWords Substring_Words(const char *, bool); 354 355 MAKE_INLINE void 356 SubstringWords_Free(SubstringWords w) 357 { 358 free(w.words); 359 free(w.freeIt); 360 } 361 362 363 char *str_concat2(const char *, const char *); 364 char *str_concat3(const char *, const char *, const char *); 365 366 bool Str_Match(const char *, const char *); 367