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