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