1 /* $NetBSD: str.h,v 1.8 2021/04/14 17:39:11 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 const char *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
FStr_Init(const char * str,void * freeIt)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
FStr_InitOwn(char * str)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
FStr_InitRefer(const char * str)99 FStr_InitRefer(const char *str)
100 {
101 return FStr_Init(str, NULL);
102 }
103
104 MAKE_INLINE void
FStr_Done(FStr * fstr)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
MFStr_Init(char * str,void * freeIt)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
MFStr_InitOwn(char * str)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
MFStr_InitRefer(char * str)133 MFStr_InitRefer(char *str)
134 {
135 return MFStr_Init(str, NULL);
136 }
137
138 MAKE_INLINE void
MFStr_Done(MFStr * mfstr)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_STATIC Substring
Substring_Init(const char * start,const char * end)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
Substring_InitStr(const char * str)160 Substring_InitStr(const char *str)
161 {
162 return Substring_Init(str, str + strlen(str));
163 }
164
165 MAKE_STATIC size_t
Substring_Length(Substring sub)166 Substring_Length(Substring sub)
167 {
168 return (size_t)(sub.end - sub.start);
169 }
170
171 MAKE_STATIC bool
Substring_IsEmpty(Substring sub)172 Substring_IsEmpty(Substring sub)
173 {
174 return sub.start == sub.end;
175 }
176
177 MAKE_INLINE bool
Substring_Equals(Substring sub,const char * str)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_STATIC Substring
Substring_Sub(Substring sub,size_t start,size_t end)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_STATIC bool
Substring_HasPrefix(Substring sub,Substring prefix)194 Substring_HasPrefix(Substring sub, Substring prefix)
195 {
196 return Substring_Length(sub) >= Substring_Length(prefix) &&
197 memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0;
198 }
199
200 MAKE_STATIC bool
Substring_HasSuffix(Substring sub,Substring suffix)201 Substring_HasSuffix(Substring sub, Substring suffix)
202 {
203 size_t suffixLen = Substring_Length(suffix);
204 return Substring_Length(sub) >= suffixLen &&
205 memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0;
206 }
207
208 /* Returns an independent, null-terminated copy of the substring. */
209 MAKE_STATIC FStr
Substring_Str(Substring sub)210 Substring_Str(Substring sub)
211 {
212 if (Substring_IsEmpty(sub))
213 return FStr_InitRefer("");
214 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
215 }
216
217 MAKE_STATIC const char *
Substring_SkipFirst(Substring sub,char ch)218 Substring_SkipFirst(Substring sub, char ch)
219 {
220 const char *p;
221
222 for (p = sub.start; p != sub.end; p++)
223 if (*p == ch)
224 return p + 1;
225 return sub.start;
226 }
227
228 MAKE_STATIC const char *
Substring_LastIndex(Substring sub,char ch)229 Substring_LastIndex(Substring sub, char ch)
230 {
231 const char *p;
232
233 for (p = sub.end; p != sub.start; p--)
234 if (p[-1] == ch)
235 return p - 1;
236 return NULL;
237 }
238
239 MAKE_STATIC Substring
Substring_Dirname(Substring pathname)240 Substring_Dirname(Substring pathname)
241 {
242 const char *p;
243
244 for (p = pathname.end; p != pathname.start; p--)
245 if (p[-1] == '/')
246 return Substring_Init(pathname.start, p - 1);
247 return Substring_InitStr(".");
248 }
249
250 MAKE_STATIC Substring
Substring_Basename(Substring pathname)251 Substring_Basename(Substring pathname)
252 {
253 const char *p;
254
255 for (p = pathname.end; p != pathname.start; p--)
256 if (p[-1] == '/')
257 return Substring_Init(p, pathname.end);
258 return pathname;
259 }
260
261
262 MAKE_STATIC void
LazyBuf_Init(LazyBuf * buf,const char * expected)263 LazyBuf_Init(LazyBuf *buf, const char *expected)
264 {
265 buf->data = NULL;
266 buf->len = 0;
267 buf->cap = 0;
268 buf->expected = expected;
269 buf->freeIt = NULL;
270 }
271
272 MAKE_INLINE void
LazyBuf_Done(LazyBuf * buf)273 LazyBuf_Done(LazyBuf *buf)
274 {
275 free(buf->freeIt);
276 }
277
278 MAKE_STATIC void
LazyBuf_Add(LazyBuf * buf,char ch)279 LazyBuf_Add(LazyBuf *buf, char ch)
280 {
281
282 if (buf->data != NULL) {
283 if (buf->len == buf->cap) {
284 buf->cap *= 2;
285 buf->data = bmake_realloc(buf->data, buf->cap);
286 }
287 buf->data[buf->len++] = ch;
288
289 } else if (ch == buf->expected[buf->len]) {
290 buf->len++;
291 return;
292
293 } else {
294 buf->cap = buf->len + 16;
295 buf->data = bmake_malloc(buf->cap);
296 memcpy(buf->data, buf->expected, buf->len);
297 buf->data[buf->len++] = ch;
298 }
299 }
300
301 MAKE_STATIC void
LazyBuf_AddStr(LazyBuf * buf,const char * str)302 LazyBuf_AddStr(LazyBuf *buf, const char *str)
303 {
304 const char *p;
305
306 for (p = str; *p != '\0'; p++)
307 LazyBuf_Add(buf, *p);
308 }
309
310 MAKE_STATIC void
LazyBuf_AddBytesBetween(LazyBuf * buf,const char * start,const char * end)311 LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end)
312 {
313 const char *p;
314
315 for (p = start; p != end; p++)
316 LazyBuf_Add(buf, *p);
317 }
318
319 MAKE_INLINE void
LazyBuf_AddSubstring(LazyBuf * buf,Substring sub)320 LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
321 {
322 LazyBuf_AddBytesBetween(buf, sub.start, sub.end);
323 }
324
325 MAKE_STATIC Substring
LazyBuf_Get(const LazyBuf * buf)326 LazyBuf_Get(const LazyBuf *buf)
327 {
328 const char *start = buf->data != NULL ? buf->data : buf->expected;
329 return Substring_Init(start, start + buf->len);
330 }
331
332 MAKE_STATIC FStr
LazyBuf_DoneGet(LazyBuf * buf)333 LazyBuf_DoneGet(LazyBuf *buf)
334 {
335 if (buf->data != NULL) {
336 LazyBuf_Add(buf, '\0');
337 return FStr_InitOwn(buf->data);
338 }
339 return Substring_Str(LazyBuf_Get(buf));
340 }
341
342
343 Words Str_Words(const char *, bool);
344
345 MAKE_INLINE void
Words_Free(Words w)346 Words_Free(Words w)
347 {
348 free(w.words);
349 free(w.freeIt);
350 }
351
352
353 SubstringWords Substring_Words(const char *, bool);
354
355 MAKE_INLINE void
SubstringWords_Free(SubstringWords w)356 SubstringWords_Free(SubstringWords w)
357 {
358 free(w.words);
359 free(w.freeIt);
360 }
361
362
363 char *str_concat2(const char *, const char *);
364 char *str_concat3(const char *, const char *, const char *);
365 char *str_concat4(const char *, const char *, const char *, const char *);
366
367 bool Str_Match(const char *, const char *);
368