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