str.h revision 1.19 1 1.19 rillig /* $NetBSD: str.h,v 1.19 2024/01/05 21:56:55 rillig Exp $ */
2 1.1 rillig
3 1.1 rillig /*
4 1.1 rillig Copyright (c) 2021 Roland Illig <rillig (at) NetBSD.org>
5 1.1 rillig All rights reserved.
6 1.1 rillig
7 1.1 rillig Redistribution and use in source and binary forms, with or without
8 1.1 rillig modification, are permitted provided that the following conditions
9 1.1 rillig are met:
10 1.1 rillig
11 1.1 rillig 1. Redistributions of source code must retain the above copyright
12 1.1 rillig notice, this list of conditions and the following disclaimer.
13 1.1 rillig 2. Redistributions in binary form must reproduce the above copyright
14 1.1 rillig notice, this list of conditions and the following disclaimer in the
15 1.1 rillig documentation and/or other materials provided with the distribution.
16 1.1 rillig
17 1.1 rillig THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 1.1 rillig "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 1.1 rillig TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 1.1 rillig PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
21 1.1 rillig BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 1.1 rillig CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 1.1 rillig SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 1.1 rillig INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 1.1 rillig CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 1.1 rillig ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 1.1 rillig POSSIBILITY OF SUCH DAMAGE.
28 1.1 rillig */
29 1.1 rillig
30 1.1 rillig
31 1.1 rillig /*
32 1.1 rillig * Memory-efficient string handling.
33 1.1 rillig */
34 1.1 rillig
35 1.1 rillig
36 1.1 rillig /* A read-only string that may need to be freed after use. */
37 1.1 rillig typedef struct FStr {
38 1.1 rillig const char *str;
39 1.1 rillig void *freeIt;
40 1.1 rillig } FStr;
41 1.1 rillig
42 1.1 rillig /* A read-only range of a character array, NOT null-terminated. */
43 1.4 rillig typedef struct Substring {
44 1.1 rillig const char *start;
45 1.1 rillig const char *end;
46 1.1 rillig } Substring;
47 1.1 rillig
48 1.1 rillig /*
49 1.1 rillig * Builds a string, only allocating memory if the string is different from the
50 1.1 rillig * expected string.
51 1.1 rillig */
52 1.1 rillig typedef struct LazyBuf {
53 1.1 rillig char *data;
54 1.1 rillig size_t len;
55 1.1 rillig size_t cap;
56 1.5 rillig const char *expected;
57 1.1 rillig } LazyBuf;
58 1.1 rillig
59 1.1 rillig /* The result of splitting a string into words. */
60 1.1 rillig typedef struct Words {
61 1.1 rillig char **words;
62 1.1 rillig size_t len;
63 1.1 rillig void *freeIt;
64 1.1 rillig } Words;
65 1.1 rillig
66 1.3 rillig /* The result of splitting a string into words. */
67 1.3 rillig typedef struct SubstringWords {
68 1.3 rillig Substring *words;
69 1.3 rillig size_t len;
70 1.3 rillig void *freeIt;
71 1.3 rillig } SubstringWords;
72 1.3 rillig
73 1.17 rillig typedef struct StrMatchResult {
74 1.17 rillig const char *error;
75 1.17 rillig bool matched;
76 1.17 rillig } StrMatchResult;
77 1.17 rillig
78 1.1 rillig
79 1.19 rillig /* Return a string that is the sole owner of str. */
80 1.1 rillig MAKE_INLINE FStr
81 1.19 rillig FStr_InitOwn(char *str)
82 1.1 rillig {
83 1.1 rillig FStr fstr;
84 1.1 rillig fstr.str = str;
85 1.19 rillig fstr.freeIt = str;
86 1.1 rillig return fstr;
87 1.1 rillig }
88 1.1 rillig
89 1.1 rillig /* Return a string that refers to the shared str. */
90 1.1 rillig MAKE_INLINE FStr
91 1.1 rillig FStr_InitRefer(const char *str)
92 1.1 rillig {
93 1.19 rillig FStr fstr;
94 1.19 rillig fstr.str = str;
95 1.19 rillig fstr.freeIt = NULL;
96 1.19 rillig return fstr;
97 1.1 rillig }
98 1.1 rillig
99 1.1 rillig MAKE_INLINE void
100 1.1 rillig FStr_Done(FStr *fstr)
101 1.1 rillig {
102 1.1 rillig free(fstr->freeIt);
103 1.1 rillig #ifdef CLEANUP
104 1.1 rillig fstr->str = NULL;
105 1.1 rillig fstr->freeIt = NULL;
106 1.1 rillig #endif
107 1.1 rillig }
108 1.1 rillig
109 1.1 rillig
110 1.8 rillig MAKE_STATIC Substring
111 1.1 rillig Substring_Init(const char *start, const char *end)
112 1.1 rillig {
113 1.1 rillig Substring sub;
114 1.1 rillig
115 1.1 rillig sub.start = start;
116 1.1 rillig sub.end = end;
117 1.1 rillig return sub;
118 1.1 rillig }
119 1.1 rillig
120 1.1 rillig MAKE_INLINE Substring
121 1.1 rillig Substring_InitStr(const char *str)
122 1.1 rillig {
123 1.1 rillig return Substring_Init(str, str + strlen(str));
124 1.1 rillig }
125 1.1 rillig
126 1.8 rillig MAKE_STATIC size_t
127 1.1 rillig Substring_Length(Substring sub)
128 1.1 rillig {
129 1.1 rillig return (size_t)(sub.end - sub.start);
130 1.1 rillig }
131 1.1 rillig
132 1.8 rillig MAKE_STATIC bool
133 1.2 rillig Substring_IsEmpty(Substring sub)
134 1.2 rillig {
135 1.2 rillig return sub.start == sub.end;
136 1.2 rillig }
137 1.2 rillig
138 1.2 rillig MAKE_INLINE bool
139 1.1 rillig Substring_Equals(Substring sub, const char *str)
140 1.1 rillig {
141 1.1 rillig size_t len = strlen(str);
142 1.1 rillig return Substring_Length(sub) == len &&
143 1.1 rillig memcmp(sub.start, str, len) == 0;
144 1.1 rillig }
145 1.1 rillig
146 1.11 rillig MAKE_INLINE bool
147 1.11 rillig Substring_Eq(Substring sub, Substring str)
148 1.11 rillig {
149 1.11 rillig size_t len = Substring_Length(sub);
150 1.11 rillig return len == Substring_Length(str) &&
151 1.11 rillig memcmp(sub.start, str.start, len) == 0;
152 1.11 rillig }
153 1.11 rillig
154 1.8 rillig MAKE_STATIC bool
155 1.6 rillig Substring_HasPrefix(Substring sub, Substring prefix)
156 1.6 rillig {
157 1.6 rillig return Substring_Length(sub) >= Substring_Length(prefix) &&
158 1.6 rillig memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0;
159 1.6 rillig }
160 1.6 rillig
161 1.8 rillig MAKE_STATIC bool
162 1.6 rillig Substring_HasSuffix(Substring sub, Substring suffix)
163 1.6 rillig {
164 1.6 rillig size_t suffixLen = Substring_Length(suffix);
165 1.6 rillig return Substring_Length(sub) >= suffixLen &&
166 1.6 rillig memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0;
167 1.6 rillig }
168 1.6 rillig
169 1.7 rillig /* Returns an independent, null-terminated copy of the substring. */
170 1.8 rillig MAKE_STATIC FStr
171 1.1 rillig Substring_Str(Substring sub)
172 1.1 rillig {
173 1.7 rillig if (Substring_IsEmpty(sub))
174 1.7 rillig return FStr_InitRefer("");
175 1.1 rillig return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
176 1.1 rillig }
177 1.1 rillig
178 1.8 rillig MAKE_STATIC const char *
179 1.6 rillig Substring_SkipFirst(Substring sub, char ch)
180 1.6 rillig {
181 1.6 rillig const char *p;
182 1.6 rillig
183 1.6 rillig for (p = sub.start; p != sub.end; p++)
184 1.6 rillig if (*p == ch)
185 1.6 rillig return p + 1;
186 1.6 rillig return sub.start;
187 1.6 rillig }
188 1.6 rillig
189 1.8 rillig MAKE_STATIC const char *
190 1.19 rillig Substring_FindLast(Substring sub, char ch)
191 1.2 rillig {
192 1.2 rillig const char *p;
193 1.2 rillig
194 1.2 rillig for (p = sub.end; p != sub.start; p--)
195 1.2 rillig if (p[-1] == ch)
196 1.2 rillig return p - 1;
197 1.2 rillig return NULL;
198 1.2 rillig }
199 1.2 rillig
200 1.8 rillig MAKE_STATIC Substring
201 1.2 rillig Substring_Dirname(Substring pathname)
202 1.2 rillig {
203 1.2 rillig const char *p;
204 1.2 rillig
205 1.2 rillig for (p = pathname.end; p != pathname.start; p--)
206 1.2 rillig if (p[-1] == '/')
207 1.2 rillig return Substring_Init(pathname.start, p - 1);
208 1.2 rillig return Substring_InitStr(".");
209 1.2 rillig }
210 1.2 rillig
211 1.8 rillig MAKE_STATIC Substring
212 1.2 rillig Substring_Basename(Substring pathname)
213 1.2 rillig {
214 1.2 rillig const char *p;
215 1.2 rillig
216 1.2 rillig for (p = pathname.end; p != pathname.start; p--)
217 1.2 rillig if (p[-1] == '/')
218 1.2 rillig return Substring_Init(p, pathname.end);
219 1.2 rillig return pathname;
220 1.2 rillig }
221 1.2 rillig
222 1.1 rillig
223 1.8 rillig MAKE_STATIC void
224 1.5 rillig LazyBuf_Init(LazyBuf *buf, const char *expected)
225 1.1 rillig {
226 1.1 rillig buf->data = NULL;
227 1.1 rillig buf->len = 0;
228 1.1 rillig buf->cap = 0;
229 1.1 rillig buf->expected = expected;
230 1.1 rillig }
231 1.1 rillig
232 1.1 rillig MAKE_INLINE void
233 1.1 rillig LazyBuf_Done(LazyBuf *buf)
234 1.1 rillig {
235 1.12 rillig free(buf->data);
236 1.1 rillig }
237 1.1 rillig
238 1.8 rillig MAKE_STATIC void
239 1.1 rillig LazyBuf_Add(LazyBuf *buf, char ch)
240 1.1 rillig {
241 1.1 rillig
242 1.1 rillig if (buf->data != NULL) {
243 1.1 rillig if (buf->len == buf->cap) {
244 1.1 rillig buf->cap *= 2;
245 1.1 rillig buf->data = bmake_realloc(buf->data, buf->cap);
246 1.1 rillig }
247 1.1 rillig buf->data[buf->len++] = ch;
248 1.1 rillig
249 1.5 rillig } else if (ch == buf->expected[buf->len]) {
250 1.1 rillig buf->len++;
251 1.1 rillig return;
252 1.1 rillig
253 1.1 rillig } else {
254 1.1 rillig buf->cap = buf->len + 16;
255 1.1 rillig buf->data = bmake_malloc(buf->cap);
256 1.5 rillig memcpy(buf->data, buf->expected, buf->len);
257 1.1 rillig buf->data[buf->len++] = ch;
258 1.1 rillig }
259 1.1 rillig }
260 1.1 rillig
261 1.8 rillig MAKE_STATIC void
262 1.1 rillig LazyBuf_AddStr(LazyBuf *buf, const char *str)
263 1.1 rillig {
264 1.1 rillig const char *p;
265 1.1 rillig
266 1.1 rillig for (p = str; *p != '\0'; p++)
267 1.1 rillig LazyBuf_Add(buf, *p);
268 1.1 rillig }
269 1.1 rillig
270 1.16 rillig MAKE_INLINE void
271 1.16 rillig LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
272 1.4 rillig {
273 1.4 rillig const char *p;
274 1.4 rillig
275 1.16 rillig for (p = sub.start; p != sub.end; p++)
276 1.4 rillig LazyBuf_Add(buf, *p);
277 1.4 rillig }
278 1.4 rillig
279 1.8 rillig MAKE_STATIC Substring
280 1.1 rillig LazyBuf_Get(const LazyBuf *buf)
281 1.1 rillig {
282 1.5 rillig const char *start = buf->data != NULL ? buf->data : buf->expected;
283 1.1 rillig return Substring_Init(start, start + buf->len);
284 1.1 rillig }
285 1.1 rillig
286 1.12 rillig /*
287 1.12 rillig * Returns the content of the buffer as a newly allocated string.
288 1.12 rillig *
289 1.12 rillig * See LazyBuf_Get to avoid unnecessary memory allocations.
290 1.12 rillig */
291 1.8 rillig MAKE_STATIC FStr
292 1.1 rillig LazyBuf_DoneGet(LazyBuf *buf)
293 1.1 rillig {
294 1.1 rillig if (buf->data != NULL) {
295 1.1 rillig LazyBuf_Add(buf, '\0');
296 1.1 rillig return FStr_InitOwn(buf->data);
297 1.1 rillig }
298 1.1 rillig return Substring_Str(LazyBuf_Get(buf));
299 1.1 rillig }
300 1.1 rillig
301 1.1 rillig
302 1.1 rillig Words Str_Words(const char *, bool);
303 1.1 rillig
304 1.1 rillig MAKE_INLINE void
305 1.1 rillig Words_Free(Words w)
306 1.1 rillig {
307 1.1 rillig free(w.words);
308 1.1 rillig free(w.freeIt);
309 1.1 rillig }
310 1.1 rillig
311 1.1 rillig
312 1.3 rillig SubstringWords Substring_Words(const char *, bool);
313 1.3 rillig
314 1.3 rillig MAKE_INLINE void
315 1.10 rillig SubstringWords_Init(SubstringWords *w)
316 1.10 rillig {
317 1.10 rillig w->words = NULL;
318 1.10 rillig w->len = 0;
319 1.10 rillig w->freeIt = NULL;
320 1.10 rillig }
321 1.10 rillig
322 1.10 rillig MAKE_INLINE void
323 1.3 rillig SubstringWords_Free(SubstringWords w)
324 1.3 rillig {
325 1.3 rillig free(w.words);
326 1.3 rillig free(w.freeIt);
327 1.3 rillig }
328 1.3 rillig
329 1.3 rillig
330 1.1 rillig char *str_concat2(const char *, const char *);
331 1.1 rillig char *str_concat3(const char *, const char *, const char *);
332 1.1 rillig
333 1.17 rillig StrMatchResult Str_Match(const char *, const char *);
334 1.14 rillig
335 1.14 rillig void Str_Intern_Init(void);
336 1.15 rillig void Str_Intern_End(void);
337 1.14 rillig const char *Str_Intern(const char *);
338