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