xref: /netbsd/usr.bin/make/str.h (revision 213d0a50)
1 /*	$NetBSD: str.h,v 1.2 2021/04/11 18:44:57 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 {
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 	Substring 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 
74 MAKE_INLINE FStr
75 FStr_Init(const char *str, void *freeIt)
76 {
77 	FStr fstr;
78 	fstr.str = str;
79 	fstr.freeIt = freeIt;
80 	return fstr;
81 }
82 
83 /* Return a string that is the sole owner of str. */
84 MAKE_INLINE FStr
85 FStr_InitOwn(char *str)
86 {
87 	return FStr_Init(str, str);
88 }
89 
90 /* Return a string that refers to the shared str. */
91 MAKE_INLINE FStr
92 FStr_InitRefer(const char *str)
93 {
94 	return FStr_Init(str, NULL);
95 }
96 
97 MAKE_INLINE void
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_INLINE MFStr
109 MFStr_Init(char *str, void *freeIt)
110 {
111 	MFStr mfstr;
112 	mfstr.str = str;
113 	mfstr.freeIt = freeIt;
114 	return mfstr;
115 }
116 
117 /* Return a string that is the sole owner of str. */
118 MAKE_INLINE MFStr
119 MFStr_InitOwn(char *str)
120 {
121 	return MFStr_Init(str, str);
122 }
123 
124 /* Return a string that refers to the shared str. */
125 MAKE_INLINE MFStr
126 MFStr_InitRefer(char *str)
127 {
128 	return MFStr_Init(str, NULL);
129 }
130 
131 MAKE_INLINE void
132 MFStr_Done(MFStr *mfstr)
133 {
134 	free(mfstr->freeIt);
135 #ifdef CLEANUP
136 	mfstr->str = NULL;
137 	mfstr->freeIt = NULL;
138 #endif
139 }
140 
141 
142 MAKE_INLINE Substring
143 Substring_Init(const char *start, const char *end)
144 {
145 	Substring sub;
146 
147 	sub.start = start;
148 	sub.end = end;
149 	return sub;
150 }
151 
152 MAKE_INLINE Substring
153 Substring_InitStr(const char *str)
154 {
155 	return Substring_Init(str, str + strlen(str));
156 }
157 
158 MAKE_INLINE size_t
159 Substring_Length(Substring sub)
160 {
161 	return (size_t)(sub.end - sub.start);
162 }
163 
164 MAKE_INLINE bool
165 Substring_IsEmpty(Substring sub)
166 {
167 	return sub.start == sub.end;
168 }
169 
170 MAKE_INLINE bool
171 Substring_Equals(Substring sub, const char *str)
172 {
173 	size_t len = strlen(str);
174 	return Substring_Length(sub) == len &&
175 	       memcmp(sub.start, str, len) == 0;
176 }
177 
178 MAKE_INLINE Substring
179 Substring_Sub(Substring sub, size_t start, size_t end)
180 {
181 	assert(start <= Substring_Length(sub));
182 	assert(end <= Substring_Length(sub));
183 	return Substring_Init(sub.start + start, sub.start + end);
184 }
185 
186 MAKE_INLINE FStr
187 Substring_Str(Substring sub)
188 {
189 	return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
190 }
191 
192 MAKE_INLINE const char *
193 Substring_LastIndex(Substring sub, char ch)
194 {
195 	const char *p;
196 
197 	for (p = sub.end; p != sub.start; p--)
198 		if (p[-1] == ch)
199 			return p - 1;
200 	return NULL;
201 }
202 
203 MAKE_INLINE Substring
204 Substring_Dirname(Substring pathname)
205 {
206 	const char *p;
207 
208 	for (p = pathname.end; p != pathname.start; p--)
209 		if (p[-1] == '/')
210 			return Substring_Init(pathname.start, p - 1);
211 	return Substring_InitStr(".");
212 }
213 
214 MAKE_INLINE Substring
215 Substring_Basename(Substring pathname)
216 {
217 	const char *p;
218 
219 	for (p = pathname.end; p != pathname.start; p--)
220 		if (p[-1] == '/')
221 			return Substring_Init(p, pathname.end);
222 	return pathname;
223 }
224 
225 
226 MAKE_INLINE void
227 LazyBuf_Init(LazyBuf *buf, Substring expected)
228 {
229 	buf->data = NULL;
230 	buf->len = 0;
231 	buf->cap = 0;
232 	buf->expected = expected;
233 	buf->freeIt = NULL;
234 }
235 
236 MAKE_INLINE void
237 LazyBuf_Done(LazyBuf *buf)
238 {
239 	free(buf->freeIt);
240 }
241 
242 MAKE_INLINE void
243 LazyBuf_Add(LazyBuf *buf, char ch)
244 {
245 
246 	if (buf->data != NULL) {
247 		if (buf->len == buf->cap) {
248 			buf->cap *= 2;
249 			buf->data = bmake_realloc(buf->data, buf->cap);
250 		}
251 		buf->data[buf->len++] = ch;
252 
253 	} else if (buf->len < Substring_Length(buf->expected) &&
254 	    ch == buf->expected.start[buf->len]) {
255 		buf->len++;
256 		return;
257 
258 	} else {
259 		buf->cap = buf->len + 16;
260 		buf->data = bmake_malloc(buf->cap);
261 		memcpy(buf->data, buf->expected.start, buf->len);
262 		buf->data[buf->len++] = ch;
263 	}
264 }
265 
266 MAKE_INLINE void
267 LazyBuf_AddStr(LazyBuf *buf, const char *str)
268 {
269 	const char *p;
270 
271 	for (p = str; *p != '\0'; p++)
272 		LazyBuf_Add(buf, *p);
273 }
274 
275 MAKE_INLINE Substring
276 LazyBuf_Get(const LazyBuf *buf)
277 {
278 	const char *start = buf->data != NULL
279 	    ? buf->data : buf->expected.start;
280 	return Substring_Init(start, start + buf->len);
281 }
282 
283 MAKE_INLINE FStr
284 LazyBuf_DoneGet(LazyBuf *buf)
285 {
286 	if (buf->data != NULL) {
287 		LazyBuf_Add(buf, '\0');
288 		return FStr_InitOwn(buf->data);
289 	}
290 	return Substring_Str(LazyBuf_Get(buf));
291 }
292 
293 
294 Words Str_Words(const char *, bool);
295 
296 MAKE_INLINE void
297 Words_Free(Words w)
298 {
299 	free(w.words);
300 	free(w.freeIt);
301 }
302 
303 
304 char *str_concat2(const char *, const char *);
305 char *str_concat3(const char *, const char *, const char *);
306 char *str_concat4(const char *, const char *, const char *, const char *);
307 
308 bool Str_Match(const char *, const char *);
309