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