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