1 /* $NetBSD: str.h,v 1.4 2021/04/11 20:38:43 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 Substring 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_INLINE 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_INLINE size_t 166 Substring_Length(Substring sub) 167 { 168 return (size_t)(sub.end - sub.start); 169 } 170 171 MAKE_INLINE 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_INLINE 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_INLINE FStr 194 Substring_Str(Substring sub) 195 { 196 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end)); 197 } 198 199 MAKE_INLINE const char * 200 Substring_LastIndex(Substring sub, char ch) 201 { 202 const char *p; 203 204 for (p = sub.end; p != sub.start; p--) 205 if (p[-1] == ch) 206 return p - 1; 207 return NULL; 208 } 209 210 MAKE_INLINE Substring 211 Substring_Dirname(Substring pathname) 212 { 213 const char *p; 214 215 for (p = pathname.end; p != pathname.start; p--) 216 if (p[-1] == '/') 217 return Substring_Init(pathname.start, p - 1); 218 return Substring_InitStr("."); 219 } 220 221 MAKE_INLINE Substring 222 Substring_Basename(Substring pathname) 223 { 224 const char *p; 225 226 for (p = pathname.end; p != pathname.start; p--) 227 if (p[-1] == '/') 228 return Substring_Init(p, pathname.end); 229 return pathname; 230 } 231 232 233 MAKE_INLINE void 234 LazyBuf_Init(LazyBuf *buf, Substring expected) 235 { 236 buf->data = NULL; 237 buf->len = 0; 238 buf->cap = 0; 239 buf->expected = expected; 240 buf->freeIt = NULL; 241 } 242 243 MAKE_INLINE void 244 LazyBuf_Done(LazyBuf *buf) 245 { 246 free(buf->freeIt); 247 } 248 249 MAKE_INLINE 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 (buf->len < Substring_Length(buf->expected) && 261 ch == buf->expected.start[buf->len]) { 262 buf->len++; 263 return; 264 265 } else { 266 buf->cap = buf->len + 16; 267 buf->data = bmake_malloc(buf->cap); 268 memcpy(buf->data, buf->expected.start, buf->len); 269 buf->data[buf->len++] = ch; 270 } 271 } 272 273 MAKE_INLINE void 274 LazyBuf_AddStr(LazyBuf *buf, const char *str) 275 { 276 const char *p; 277 278 for (p = str; *p != '\0'; p++) 279 LazyBuf_Add(buf, *p); 280 } 281 282 MAKE_INLINE void 283 LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end) 284 { 285 const char *p; 286 287 for (p = start; p != end; p++) 288 LazyBuf_Add(buf, *p); 289 } 290 291 MAKE_INLINE void 292 LazyBuf_AddSubstring(LazyBuf *buf, Substring sub) 293 { 294 LazyBuf_AddBytesBetween(buf, sub.start, sub.end); 295 } 296 297 MAKE_INLINE Substring 298 LazyBuf_Get(const LazyBuf *buf) 299 { 300 const char *start = buf->data != NULL 301 ? buf->data : buf->expected.start; 302 return Substring_Init(start, start + buf->len); 303 } 304 305 MAKE_INLINE FStr 306 LazyBuf_DoneGet(LazyBuf *buf) 307 { 308 if (buf->data != NULL) { 309 LazyBuf_Add(buf, '\0'); 310 return FStr_InitOwn(buf->data); 311 } 312 return Substring_Str(LazyBuf_Get(buf)); 313 } 314 315 316 Words Str_Words(const char *, bool); 317 318 MAKE_INLINE void 319 Words_Free(Words w) 320 { 321 free(w.words); 322 free(w.freeIt); 323 } 324 325 326 SubstringWords Substring_Words(const char *, bool); 327 328 MAKE_INLINE void 329 SubstringWords_Free(SubstringWords w) 330 { 331 free(w.words); 332 free(w.freeIt); 333 } 334 335 336 char *str_concat2(const char *, const char *); 337 char *str_concat3(const char *, const char *, const char *); 338 char *str_concat4(const char *, const char *, const char *, const char *); 339 340 bool Str_Match(const char *, const char *); 341