xref: /freebsd/contrib/bmake/str.h (revision 34a3834eadd03bec7703b8fbf9123f27b1114986)
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