1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017-2021, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17#pragma once
18
19#include "C4Breakpoint.h"
20
21#include <concepts>
22#include <cstddef>
23#include <cstdio>
24#include <format>
25#include <limits>
26#include <span>
27#include <string>
28#include <string_view>
29#include <utility>
30
31#ifdef _WIN32
32#include "C4Windows.h"
33#else
34#include <strings.h>
35#endif
36
37#ifndef _WIN32
38inline int stricmp(const char *s1, const char *s2)
39{
40 return strcasecmp(s1: s1, s2: s2);
41}
42#endif
43
44constexpr auto SizeMax = std::numeric_limits<size_t>::max();
45
46char CharCapital(char cChar);
47bool IsIdentifier(char cChar);
48bool IsWhiteSpace(char cChar);
49
50size_t SLen(const char *sptr);
51
52bool SEqual(const char *szStr1, const char *szStr2);
53bool SEqual2(const char *szStr1, const char *szStr2);
54
55bool SEqualNoCase(const char *szStr1, const char *szStr2, size_t iLen = SizeMax);
56bool SEqual2NoCase(const char *szStr1, const char *szStr2, size_t iLen = SizeMax);
57
58void SCopy(const char *szSource, char *sTarget, size_t iMaxL = SizeMax);
59void SCopyUntil(const char *szSource, char *sTarget, char cUntil, size_t iMaxL = SizeMax, size_t iIndex = 0);
60void SCopyUntil(const char *szSource, char *sTarget, const char *sUntil, size_t iMaxL);
61void SCopyIdentifier(const char *szSource, char *sTarget, size_t iMaxL = SizeMax);
62bool SCopySegment(const char *fstr, size_t segn, char *tstr, char sepa = ';', size_t iMaxL = SizeMax, bool fSkipWhitespace = false);
63bool SCopySegmentEx(const char *fstr, size_t segn, char *tstr, char sepa1, char sepa2, size_t iMaxL = SizeMax, bool fSkipWhitespace = false);
64bool SCopyEnclosed(const char *szSource, char cOpen, char cClose, char *sTarget, size_t iSize);
65
66void SAppend(const char *szSource, char *szTarget, size_t iMaxL = SizeMax);
67void SAppendChar(char cChar, char *szStr);
68
69void SInsert(char *szString, const char *szInsert, size_t iPosition = 0, size_t iMaxL = SizeMax);
70void SDelete(char *szString, size_t iLen, size_t iPosition = 0);
71
72int SCharPos(char cTarget, const char *szInStr, size_t iIndex = 0);
73int SCharLastPos(char cTarget, const char *szInStr);
74int SCharCount(char cTarget, const char *szInStr, const char *cpUntil = nullptr);
75int SCharCountEx(const char *szString, const char *szCharList);
76
77void SReplaceChar(char *str, char fc, char tc);
78
79const char *SSearch(const char *szString, const char *szIndex);
80const char *SSearchNoCase(const char *szString, const char *szIndex);
81
82const char *SAdvanceSpace(const char *szSPos);
83const char *SAdvancePast(const char *szSPos, char cPast);
84
85bool SGetModule(const char *szList, size_t iIndex, char *sTarget, size_t iSize = SizeMax);
86bool SIsModule(const char *szList, const char *szString, size_t *ipIndex = nullptr, bool fCaseSensitive = false);
87bool SAddModule(char *szList, const char *szModule, bool fCaseSensitive = false);
88bool SAddModules(char *szList, const char *szModules, bool fCaseSensitive = false);
89bool SRemoveModule(char *szList, const char *szModule, bool fCaseSensitive = false);
90bool SRemoveModules(char *szList, const char *szModules, bool fCaseSensitive = false);
91int SModuleCount(const char *szList);
92
93const char *SGetParameter(const char *strCommandLine, size_t iParameter, char *strTarget = nullptr, size_t iSize = SizeMax, bool *pWasQuoted = nullptr);
94
95void SNewSegment(char *szStr, const char *szSepa = ";");
96void SCapitalize(char *szString);
97void SWordWrap(char *szText, char cSpace, char cSepa, size_t iMaxLine);
98int SClearFrontBack(char *szString, char cClear = ' ');
99
100int SGetLine(const char *szText, const char *cpPosition);
101int SLineGetCharacters(const char *szText, const char *cpPosition);
102
103// case sensitive wildcard match with some extra functionality
104// can match strings like "*Cl?nk*vour" to "Clonk Endeavour"
105bool SWildcardMatchEx(const char *szString, const char *szWildcard);
106
107template <typename T>
108concept StringViewable = requires (T t) { std::basic_string_view{t}; };
109
110template <StringViewable T>
111using StringViewOf = decltype(std::basic_string_view{std::declval<T>()});
112
113template <StringViewable T>
114using StringOf = std::basic_string<typename StringViewOf<T>::value_type, typename StringViewOf<T>::traits_type>;
115
116template<StringViewable Subject, typename String = StringOf<Subject>>
117String ReplaceInString(Subject&& subject, StringViewOf<Subject> needle, StringViewOf<Subject> value)
118{
119 String result;
120 std::size_t previousPos{0};
121 std::size_t pos{0};
122
123 for (;;)
124 {
125 previousPos = pos;
126 pos = subject.find(needle, pos);
127 if (pos == String::npos)
128 {
129 result.append(subject, previousPos);
130 return result;
131 }
132
133 result.append(subject, previousPos, pos - previousPos);
134 result.append(value);
135 pos += needle.size();
136 }
137
138 result.append(subject, previousPos, subject.size() - previousPos);
139 return result;
140}
141
142#define LineFeed "\x00D\x00A"
143#define EndOfFile "\x020"
144
145template<typename Char, typename Traits = std::char_traits<Char>>
146struct C4NullableBasicStringView
147{
148 std::basic_string_view<Char, Traits> View;
149
150 constexpr C4NullableBasicStringView(auto &&...args)
151 : View{std::forward<decltype(args)>(args)...} {}
152
153 constexpr C4NullableBasicStringView(const Char *const ptr)
154 {
155 if (ptr)
156 {
157 View = ptr;
158 }
159 else
160 {
161 View = {};
162 }
163 }
164
165 constexpr C4NullableBasicStringView(const C4NullableBasicStringView &other) noexcept
166 {
167 View = other.View;
168 }
169
170 constexpr C4NullableBasicStringView &operator=(const C4NullableBasicStringView &other) noexcept
171 {
172 View = other.View;
173 return *this;
174 }
175
176 constexpr operator std::basic_string_view<Char, Traits>() const noexcept
177 {
178 return View;
179 }
180};
181
182using C4NullableStringView = C4NullableBasicStringView<char>;
183
184template<std::size_t N, typename...Args> requires (!std::disjunction_v<std::is_array<std::remove_cvref_t<Args>>...>)
185void FormatWithNull(char(&buf)[N], const std::format_string<Args...> fmt, Args&&...args)
186{
187 *std::format_to_n(buf, N - 1, fmt, std::forward<Args>(args)...).out = '\0';
188}
189
190template<std::size_t N, typename...Args> requires (!std::disjunction_v<std::is_array<std::remove_cvref_t<Args>>...>)
191void FormatWithNull(std::array<char, N> &buf, const std::format_string<Args...> fmt, Args&&...args)
192{
193 *std::format_to_n(buf.begin(), N - 1, fmt, std::forward<Args>(args)...).out = '\0';
194}
195
196template<std::size_t N, typename... Args> requires (!std::disjunction_v<std::is_array<std::remove_cvref_t<Args>>...>)
197void FormatWithNull(const std::span<char, N> buf, const std::format_string<Args...> fmt, Args &&...args)
198{
199 *std::format_to_n(buf.begin(), buf.size() - 1, fmt, std::forward<Args>(args)...).out = '\0';
200}
201
202namespace C4Strings
203{
204
205template<std::integral T>
206inline constexpr std::size_t NumberOfCharactersForDigits{std::numeric_limits<T>::digits10 + 1};
207
208}
209