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