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