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