1 /* $NetBSD: str.h,v 1.21 2026/01/03 19:57:38 rillig Exp $ */
2
3 /*
4 * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Roland Illig.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /* Memory-efficient string handling. */
33
34 /* A read-only string that may need to be freed after use. */
35 typedef struct FStr {
36 const char *str;
37 void *freeIt;
38 } FStr;
39
40 /* A read-only range of a character array, NOT null-terminated. */
41 typedef struct Substring {
42 const char *start;
43 const char *end;
44 } Substring;
45
46 /*
47 * Builds a string, only allocating memory if the string is different from the
48 * expected string.
49 */
50 typedef struct LazyBuf {
51 char *data;
52 size_t len;
53 size_t cap;
54 const char *expected;
55 } LazyBuf;
56
57 /* The result of splitting a string into words. */
58 typedef struct Words {
59 char **words;
60 size_t len;
61 void *freeIt;
62 } Words;
63
64 /* The result of splitting a string into words. */
65 typedef struct SubstringWords {
66 Substring *words;
67 size_t len;
68 void *freeIt;
69 } SubstringWords;
70
71 typedef struct StrMatchResult {
72 const char *error;
73 bool matched;
74 } StrMatchResult;
75
76
77 /* Return a string that is the sole owner of str. */
78 MAKE_INLINE FStr
FStr_InitOwn(char * str)79 FStr_InitOwn(char *str)
80 {
81 FStr fstr;
82 fstr.str = str;
83 fstr.freeIt = str;
84 return fstr;
85 }
86
87 /* Return a string that refers to the shared str. */
88 MAKE_INLINE FStr
FStr_InitRefer(const char * str)89 FStr_InitRefer(const char *str)
90 {
91 FStr fstr;
92 fstr.str = str;
93 fstr.freeIt = NULL;
94 return fstr;
95 }
96
97 MAKE_INLINE void
FStr_Done(FStr * fstr)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_STATIC Substring
Substring_Init(const char * start,const char * end)109 Substring_Init(const char *start, const char *end)
110 {
111 Substring sub;
112
113 sub.start = start;
114 sub.end = end;
115 return sub;
116 }
117
118 MAKE_INLINE Substring
Substring_InitStr(const char * str)119 Substring_InitStr(const char *str)
120 {
121 return Substring_Init(str, str + strlen(str));
122 }
123
124 MAKE_STATIC size_t
Substring_Length(Substring sub)125 Substring_Length(Substring sub)
126 {
127 return (size_t)(sub.end - sub.start);
128 }
129
130 MAKE_STATIC bool
Substring_IsEmpty(Substring sub)131 Substring_IsEmpty(Substring sub)
132 {
133 return sub.start == sub.end;
134 }
135
136 MAKE_INLINE bool
Substring_Equals(Substring sub,const char * str)137 Substring_Equals(Substring sub, const char *str)
138 {
139 size_t len = strlen(str);
140 return Substring_Length(sub) == len &&
141 memcmp(sub.start, str, len) == 0;
142 }
143
144 MAKE_INLINE bool
Substring_Eq(Substring sub,Substring str)145 Substring_Eq(Substring sub, Substring str)
146 {
147 size_t len = Substring_Length(sub);
148 return len == Substring_Length(str) &&
149 memcmp(sub.start, str.start, len) == 0;
150 }
151
152 MAKE_STATIC bool
Substring_HasPrefix(Substring sub,Substring prefix)153 Substring_HasPrefix(Substring sub, Substring prefix)
154 {
155 return Substring_Length(sub) >= Substring_Length(prefix) &&
156 memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0;
157 }
158
159 MAKE_STATIC bool
Substring_HasSuffix(Substring sub,Substring suffix)160 Substring_HasSuffix(Substring sub, Substring suffix)
161 {
162 size_t suffixLen = Substring_Length(suffix);
163 return Substring_Length(sub) >= suffixLen &&
164 memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0;
165 }
166
167 /* Returns an independent, null-terminated copy of the substring. */
168 MAKE_STATIC FStr
Substring_Str(Substring sub)169 Substring_Str(Substring sub)
170 {
171 if (Substring_IsEmpty(sub))
172 return FStr_InitRefer("");
173 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
174 }
175
176 MAKE_STATIC const char *
Substring_SkipFirst(Substring sub,char ch)177 Substring_SkipFirst(Substring sub, char ch)
178 {
179 const char *p;
180
181 for (p = sub.start; p != sub.end; p++)
182 if (*p == ch)
183 return p + 1;
184 return sub.start;
185 }
186
187 MAKE_STATIC const char *
Substring_FindLast(Substring sub,char ch)188 Substring_FindLast(Substring sub, char ch)
189 {
190 const char *p;
191
192 for (p = sub.end; p != sub.start; p--)
193 if (p[-1] == ch)
194 return p - 1;
195 return NULL;
196 }
197
198 MAKE_STATIC Substring
Substring_Dirname(Substring pathname)199 Substring_Dirname(Substring pathname)
200 {
201 const char *p;
202
203 for (p = pathname.end; p != pathname.start; p--)
204 if (p[-1] == '/')
205 return Substring_Init(pathname.start, p - 1);
206 return Substring_InitStr(".");
207 }
208
209 MAKE_STATIC Substring
Substring_Basename(Substring pathname)210 Substring_Basename(Substring pathname)
211 {
212 const char *p;
213
214 for (p = pathname.end; p != pathname.start; p--)
215 if (p[-1] == '/')
216 return Substring_Init(p, pathname.end);
217 return pathname;
218 }
219
220
221 MAKE_STATIC void
LazyBuf_Init(LazyBuf * buf,const char * expected)222 LazyBuf_Init(LazyBuf *buf, const char *expected)
223 {
224 buf->data = NULL;
225 buf->len = 0;
226 buf->cap = 0;
227 buf->expected = expected;
228 }
229
230 MAKE_INLINE void
LazyBuf_Done(LazyBuf * buf)231 LazyBuf_Done(LazyBuf *buf)
232 {
233 free(buf->data);
234 }
235
236 MAKE_STATIC void
LazyBuf_Add(LazyBuf * buf,char ch)237 LazyBuf_Add(LazyBuf *buf, char ch)
238 {
239
240 if (buf->data != NULL) {
241 if (buf->len == buf->cap) {
242 buf->cap *= 2;
243 buf->data = bmake_realloc(buf->data, buf->cap);
244 }
245 buf->data[buf->len++] = ch;
246
247 } else if (ch == buf->expected[buf->len]) {
248 buf->len++;
249 return;
250
251 } else {
252 buf->cap = buf->len + 16;
253 buf->data = bmake_malloc(buf->cap);
254 memcpy(buf->data, buf->expected, buf->len);
255 buf->data[buf->len++] = ch;
256 }
257 }
258
259 MAKE_STATIC void
LazyBuf_AddStr(LazyBuf * buf,const char * str)260 LazyBuf_AddStr(LazyBuf *buf, const char *str)
261 {
262 const char *p;
263
264 for (p = str; *p != '\0'; p++)
265 LazyBuf_Add(buf, *p);
266 }
267
268 MAKE_INLINE void
LazyBuf_AddSubstring(LazyBuf * buf,Substring sub)269 LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
270 {
271 const char *p;
272
273 for (p = sub.start; p != sub.end; p++)
274 LazyBuf_Add(buf, *p);
275 }
276
277 MAKE_STATIC Substring
LazyBuf_Get(const LazyBuf * buf)278 LazyBuf_Get(const LazyBuf *buf)
279 {
280 const char *start = buf->data != NULL ? buf->data : buf->expected;
281 return Substring_Init(start, start + buf->len);
282 }
283
284 /*
285 * Returns the content of the buffer as a newly allocated string.
286 *
287 * See LazyBuf_Get to avoid unnecessary memory allocations.
288 */
289 MAKE_STATIC FStr
LazyBuf_DoneGet(LazyBuf * buf)290 LazyBuf_DoneGet(LazyBuf *buf)
291 {
292 if (buf->data != NULL) {
293 LazyBuf_Add(buf, '\0');
294 return FStr_InitOwn(buf->data);
295 }
296 return Substring_Str(LazyBuf_Get(buf));
297 }
298
299
300 Words Str_Words(const char *, bool);
301
302 MAKE_INLINE void
Words_Free(Words w)303 Words_Free(Words w)
304 {
305 free(w.words);
306 free(w.freeIt);
307 }
308
309
310 SubstringWords Substring_Words(const char *, bool);
311
312 MAKE_INLINE void
SubstringWords_Init(SubstringWords * w)313 SubstringWords_Init(SubstringWords *w)
314 {
315 w->words = NULL;
316 w->len = 0;
317 w->freeIt = NULL;
318 }
319
320 MAKE_INLINE void
SubstringWords_Free(SubstringWords w)321 SubstringWords_Free(SubstringWords w)
322 {
323 free(w.words);
324 free(w.freeIt);
325 }
326
327
328 char *str_concat2(const char *, const char *);
329 char *str_concat3(const char *, const char *, const char *);
330
331 StrMatchResult Str_Match(const char *, const char *);
332
333 void Str_Intern_Init(void);
334 #ifdef CLEANUP
335 void Str_Intern_End(void);
336 #endif
337 const char *Str_Intern(const char *);
338