/* $NetBSD: str.h,v 1.21 2026/01/03 19:57:38 rillig Exp $ */ /* * Copyright (c) 2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Roland Illig. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* Memory-efficient string handling. */ /* A read-only string that may need to be freed after use. */ typedef struct FStr { const char *str; void *freeIt; } FStr; /* A read-only range of a character array, NOT null-terminated. */ typedef struct Substring { const char *start; const char *end; } Substring; /* * Builds a string, only allocating memory if the string is different from the * expected string. */ typedef struct LazyBuf { char *data; size_t len; size_t cap; const char *expected; } LazyBuf; /* The result of splitting a string into words. */ typedef struct Words { char **words; size_t len; void *freeIt; } Words; /* The result of splitting a string into words. */ typedef struct SubstringWords { Substring *words; size_t len; void *freeIt; } SubstringWords; typedef struct StrMatchResult { const char *error; bool matched; } StrMatchResult; /* Return a string that is the sole owner of str. */ MAKE_INLINE FStr FStr_InitOwn(char *str) { FStr fstr; fstr.str = str; fstr.freeIt = str; return fstr; } /* Return a string that refers to the shared str. */ MAKE_INLINE FStr FStr_InitRefer(const char *str) { FStr fstr; fstr.str = str; fstr.freeIt = NULL; return fstr; } MAKE_INLINE void FStr_Done(FStr *fstr) { free(fstr->freeIt); #ifdef CLEANUP fstr->str = NULL; fstr->freeIt = NULL; #endif } MAKE_STATIC Substring Substring_Init(const char *start, const char *end) { Substring sub; sub.start = start; sub.end = end; return sub; } MAKE_INLINE Substring Substring_InitStr(const char *str) { return Substring_Init(str, str + strlen(str)); } MAKE_STATIC size_t Substring_Length(Substring sub) { return (size_t)(sub.end - sub.start); } MAKE_STATIC bool Substring_IsEmpty(Substring sub) { return sub.start == sub.end; } MAKE_INLINE bool Substring_Equals(Substring sub, const char *str) { size_t len = strlen(str); return Substring_Length(sub) == len && memcmp(sub.start, str, len) == 0; } MAKE_INLINE bool Substring_Eq(Substring sub, Substring str) { size_t len = Substring_Length(sub); return len == Substring_Length(str) && memcmp(sub.start, str.start, len) == 0; } MAKE_STATIC bool Substring_HasPrefix(Substring sub, Substring prefix) { return Substring_Length(sub) >= Substring_Length(prefix) && memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0; } MAKE_STATIC bool Substring_HasSuffix(Substring sub, Substring suffix) { size_t suffixLen = Substring_Length(suffix); return Substring_Length(sub) >= suffixLen && memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0; } /* Returns an independent, null-terminated copy of the substring. */ MAKE_STATIC FStr Substring_Str(Substring sub) { if (Substring_IsEmpty(sub)) return FStr_InitRefer(""); return FStr_InitOwn(bmake_strsedup(sub.start, sub.end)); } MAKE_STATIC const char * Substring_SkipFirst(Substring sub, char ch) { const char *p; for (p = sub.start; p != sub.end; p++) if (*p == ch) return p + 1; return sub.start; } MAKE_STATIC const char * Substring_FindLast(Substring sub, char ch) { const char *p; for (p = sub.end; p != sub.start; p--) if (p[-1] == ch) return p - 1; return NULL; } MAKE_STATIC Substring Substring_Dirname(Substring pathname) { const char *p; for (p = pathname.end; p != pathname.start; p--) if (p[-1] == '/') return Substring_Init(pathname.start, p - 1); return Substring_InitStr("."); } MAKE_STATIC Substring Substring_Basename(Substring pathname) { const char *p; for (p = pathname.end; p != pathname.start; p--) if (p[-1] == '/') return Substring_Init(p, pathname.end); return pathname; } MAKE_STATIC void LazyBuf_Init(LazyBuf *buf, const char *expected) { buf->data = NULL; buf->len = 0; buf->cap = 0; buf->expected = expected; } MAKE_INLINE void LazyBuf_Done(LazyBuf *buf) { free(buf->data); } MAKE_STATIC void LazyBuf_Add(LazyBuf *buf, char ch) { if (buf->data != NULL) { if (buf->len == buf->cap) { buf->cap *= 2; buf->data = bmake_realloc(buf->data, buf->cap); } buf->data[buf->len++] = ch; } else if (ch == buf->expected[buf->len]) { buf->len++; return; } else { buf->cap = buf->len + 16; buf->data = bmake_malloc(buf->cap); memcpy(buf->data, buf->expected, buf->len); buf->data[buf->len++] = ch; } } MAKE_STATIC void LazyBuf_AddStr(LazyBuf *buf, const char *str) { const char *p; for (p = str; *p != '\0'; p++) LazyBuf_Add(buf, *p); } MAKE_INLINE void LazyBuf_AddSubstring(LazyBuf *buf, Substring sub) { const char *p; for (p = sub.start; p != sub.end; p++) LazyBuf_Add(buf, *p); } MAKE_STATIC Substring LazyBuf_Get(const LazyBuf *buf) { const char *start = buf->data != NULL ? buf->data : buf->expected; return Substring_Init(start, start + buf->len); } /* * Returns the content of the buffer as a newly allocated string. * * See LazyBuf_Get to avoid unnecessary memory allocations. */ MAKE_STATIC FStr LazyBuf_DoneGet(LazyBuf *buf) { if (buf->data != NULL) { LazyBuf_Add(buf, '\0'); return FStr_InitOwn(buf->data); } return Substring_Str(LazyBuf_Get(buf)); } Words Str_Words(const char *, bool); MAKE_INLINE void Words_Free(Words w) { free(w.words); free(w.freeIt); } SubstringWords Substring_Words(const char *, bool); MAKE_INLINE void SubstringWords_Init(SubstringWords *w) { w->words = NULL; w->len = 0; w->freeIt = NULL; } MAKE_INLINE void SubstringWords_Free(SubstringWords w) { free(w.words); free(w.freeIt); } char *str_concat2(const char *, const char *); char *str_concat3(const char *, const char *, const char *); StrMatchResult Str_Match(const char *, const char *); void Str_Intern_Init(void); #ifdef CLEANUP void Str_Intern_End(void); #endif const char *Str_Intern(const char *); .