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