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