1 /* $NetBSD: str.h,v 1.2 2021/04/11 18:44:57 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 { 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 74 MAKE_INLINE FStr 75 FStr_Init(const char *str, void *freeIt) 76 { 77 FStr fstr; 78 fstr.str = str; 79 fstr.freeIt = freeIt; 80 return fstr; 81 } 82 83 /* Return a string that is the sole owner of str. */ 84 MAKE_INLINE FStr 85 FStr_InitOwn(char *str) 86 { 87 return FStr_Init(str, str); 88 } 89 90 /* Return a string that refers to the shared str. */ 91 MAKE_INLINE FStr 92 FStr_InitRefer(const char *str) 93 { 94 return FStr_Init(str, NULL); 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_INLINE MFStr 109 MFStr_Init(char *str, void *freeIt) 110 { 111 MFStr mfstr; 112 mfstr.str = str; 113 mfstr.freeIt = freeIt; 114 return mfstr; 115 } 116 117 /* Return a string that is the sole owner of str. */ 118 MAKE_INLINE MFStr 119 MFStr_InitOwn(char *str) 120 { 121 return MFStr_Init(str, str); 122 } 123 124 /* Return a string that refers to the shared str. */ 125 MAKE_INLINE MFStr 126 MFStr_InitRefer(char *str) 127 { 128 return MFStr_Init(str, NULL); 129 } 130 131 MAKE_INLINE void 132 MFStr_Done(MFStr *mfstr) 133 { 134 free(mfstr->freeIt); 135 #ifdef CLEANUP 136 mfstr->str = NULL; 137 mfstr->freeIt = NULL; 138 #endif 139 } 140 141 142 MAKE_INLINE Substring 143 Substring_Init(const char *start, const char *end) 144 { 145 Substring sub; 146 147 sub.start = start; 148 sub.end = end; 149 return sub; 150 } 151 152 MAKE_INLINE Substring 153 Substring_InitStr(const char *str) 154 { 155 return Substring_Init(str, str + strlen(str)); 156 } 157 158 MAKE_INLINE size_t 159 Substring_Length(Substring sub) 160 { 161 return (size_t)(sub.end - sub.start); 162 } 163 164 MAKE_INLINE bool 165 Substring_IsEmpty(Substring sub) 166 { 167 return sub.start == sub.end; 168 } 169 170 MAKE_INLINE bool 171 Substring_Equals(Substring sub, const char *str) 172 { 173 size_t len = strlen(str); 174 return Substring_Length(sub) == len && 175 memcmp(sub.start, str, len) == 0; 176 } 177 178 MAKE_INLINE Substring 179 Substring_Sub(Substring sub, size_t start, size_t end) 180 { 181 assert(start <= Substring_Length(sub)); 182 assert(end <= Substring_Length(sub)); 183 return Substring_Init(sub.start + start, sub.start + end); 184 } 185 186 MAKE_INLINE FStr 187 Substring_Str(Substring sub) 188 { 189 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end)); 190 } 191 192 MAKE_INLINE const char * 193 Substring_LastIndex(Substring sub, char ch) 194 { 195 const char *p; 196 197 for (p = sub.end; p != sub.start; p--) 198 if (p[-1] == ch) 199 return p - 1; 200 return NULL; 201 } 202 203 MAKE_INLINE Substring 204 Substring_Dirname(Substring pathname) 205 { 206 const char *p; 207 208 for (p = pathname.end; p != pathname.start; p--) 209 if (p[-1] == '/') 210 return Substring_Init(pathname.start, p - 1); 211 return Substring_InitStr("."); 212 } 213 214 MAKE_INLINE Substring 215 Substring_Basename(Substring pathname) 216 { 217 const char *p; 218 219 for (p = pathname.end; p != pathname.start; p--) 220 if (p[-1] == '/') 221 return Substring_Init(p, pathname.end); 222 return pathname; 223 } 224 225 226 MAKE_INLINE void 227 LazyBuf_Init(LazyBuf *buf, Substring expected) 228 { 229 buf->data = NULL; 230 buf->len = 0; 231 buf->cap = 0; 232 buf->expected = expected; 233 buf->freeIt = NULL; 234 } 235 236 MAKE_INLINE void 237 LazyBuf_Done(LazyBuf *buf) 238 { 239 free(buf->freeIt); 240 } 241 242 MAKE_INLINE void 243 LazyBuf_Add(LazyBuf *buf, char ch) 244 { 245 246 if (buf->data != NULL) { 247 if (buf->len == buf->cap) { 248 buf->cap *= 2; 249 buf->data = bmake_realloc(buf->data, buf->cap); 250 } 251 buf->data[buf->len++] = ch; 252 253 } else if (buf->len < Substring_Length(buf->expected) && 254 ch == buf->expected.start[buf->len]) { 255 buf->len++; 256 return; 257 258 } else { 259 buf->cap = buf->len + 16; 260 buf->data = bmake_malloc(buf->cap); 261 memcpy(buf->data, buf->expected.start, buf->len); 262 buf->data[buf->len++] = ch; 263 } 264 } 265 266 MAKE_INLINE void 267 LazyBuf_AddStr(LazyBuf *buf, const char *str) 268 { 269 const char *p; 270 271 for (p = str; *p != '\0'; p++) 272 LazyBuf_Add(buf, *p); 273 } 274 275 MAKE_INLINE Substring 276 LazyBuf_Get(const LazyBuf *buf) 277 { 278 const char *start = buf->data != NULL 279 ? buf->data : buf->expected.start; 280 return Substring_Init(start, start + buf->len); 281 } 282 283 MAKE_INLINE FStr 284 LazyBuf_DoneGet(LazyBuf *buf) 285 { 286 if (buf->data != NULL) { 287 LazyBuf_Add(buf, '\0'); 288 return FStr_InitOwn(buf->data); 289 } 290 return Substring_Str(LazyBuf_Get(buf)); 291 } 292 293 294 Words Str_Words(const char *, bool); 295 296 MAKE_INLINE void 297 Words_Free(Words w) 298 { 299 free(w.words); 300 free(w.freeIt); 301 } 302 303 304 char *str_concat2(const char *, const char *); 305 char *str_concat3(const char *, const char *, const char *); 306 char *str_concat4(const char *, const char *, const char *, const char *); 307 308 bool Str_Match(const char *, const char *); 309