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