1 #include "SGPStrings.h"
2 #include "Debug.h"
3 
4 #include <string_theory/format>
5 #include <string_theory/string>
6 
7 #if defined(__linux__) || defined(_WIN32)
8 
9 #include <sys/types.h>
10 
11 
12 /*	$OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $	*/
13 
14 /*
15  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
16  *
17  * Permission to use, copy, modify, and distribute this software for any
18  * purpose with or without fee is hereby granted, provided that the above
19  * copyright notice and this permission notice appear in all copies.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
22  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
24  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
26  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
27  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28  */
29 
30 
31 /*
32  * Copy src to string dst of size siz.  At most siz-1 characters
33  * will be copied.  Always NUL terminates (unless siz == 0).
34  * Returns strlen(src); if retval >= siz, truncation occurred.
35  */
36 size_t
strlcpy(char * dst,const char * src,size_t siz)37 strlcpy(char *dst, const char *src, size_t siz)
38 {
39 	char *d = dst;
40 	const char *s = src;
41 	size_t n = siz;
42 
43 	/* Copy as many bytes as will fit */
44 	if (n != 0) {
45 		while (--n != 0) {
46 			if ((*d++ = *s++) == '\0')
47 				break;
48 		}
49 	}
50 
51 	/* Not enough room in dst, add NUL and traverse rest of src */
52 	if (n == 0) {
53 		if (siz != 0)
54 			*d = '\0';		/* NUL-terminate dst */
55 		while (*s++)
56 			;
57 	}
58 
59 	return(s - src - 1);	/* count does not include NUL */
60 }
61 
62 #endif
63 
64 
65 #ifdef _WIN32
66 #ifndef __MINGW32__
67 
WINsnprintf(char * const s,size_t const n,const char * const fmt,...)68 int WINsnprintf(char* const s, size_t const n, const char* const fmt, ...)
69 {
70 	va_list arg;
71 	va_start(arg, fmt);
72 	int const ret = _vsnprintf(s, n, fmt, arg);
73 	va_end(arg);
74 	if (n != 0) s[n - 1] = '\0'; // _vsnprintf() does not guarantee NUL termination
75 	return ret;
76 }
77 
78 
79 #endif
80 #endif
81 
82 
st_fmt_printf_to_format(const ST::string & fmt_printf)83 ST::string st_fmt_printf_to_format(const ST::string& fmt_printf)
84 {
85 	ST::utf32_buffer codepoints = fmt_printf.to_utf32();
86 	ST::string fmt;
87 	ST::string err;
88 	ST::string param;
89 	enum class Parse {
90 		Literal,
91 		ParamFlags,
92 		ParamWidth,
93 		ParamPrecision,
94 		ParamPrecisionValue,
95 		ParamLengthModifier,
96 		ParamSpecifier,
97 	} state = Parse::Literal;
98 	for (char32_t c : codepoints)
99 	{
100 		if (state == Parse::Literal)
101 		{
102 			if (c == U'%')
103 			{
104 				param = "{"; // param start
105 				state = Parse::ParamFlags;
106 				continue;
107 			}
108 			if (c == U'{')
109 			{
110 				fmt += "{{"; // literal '{'
111 				continue;
112 			}
113 			fmt += c; // literal
114 			continue;
115 		}
116 		if (state == Parse::ParamFlags)
117 		{
118 			if (c == U'-')
119 			{
120 				param += '<';
121 				continue;
122 			}
123 			if (c == U'+' || c == U'0' || c == U'#')
124 			{
125 				param += c;
126 				continue;
127 			}
128 			if (c == U' ')
129 			{
130 				err = ST::format("param flag U+{04X} '{c}' is not supported", c, c);
131 				break;
132 			}
133 			state = Parse::ParamWidth; // with the same codepoint
134 		}
135 		if (state == Parse::ParamWidth)
136 		{
137 			if (c >= U'0' && c <= U'9')
138 			{
139 				param += c;
140 				continue;
141 			}
142 			if (c == U'*')
143 			{
144 				err = ST::format("param width U+{04X} '{c}' is not supported", c, c);
145 				break;
146 			}
147 			state = Parse::ParamPrecision; // with the same codepoint
148 		}
149 		if (state == Parse::ParamPrecision)
150 		{
151 			if (c == U'.')
152 			{
153 				param += c;
154 				state = Parse::ParamPrecisionValue;
155 				continue;
156 			}
157 			state = Parse::ParamLengthModifier; // with the same codepoint
158 		}
159 		if (state == Parse::ParamPrecisionValue)
160 		{
161 			if (c >= U'0' && c <= U'9')
162 			{
163 				param += c;
164 				continue;
165 			}
166 			state = Parse::ParamLengthModifier; // with the same codepoint
167 		}
168 		if (state == Parse::ParamLengthModifier)
169 		{
170 			if (c == U'h' || c == U'l' || c == U'j' || c == U'z' || c == U't' || c == U'L')
171 			{
172 				// ignore
173 				continue;
174 			}
175 			state = Parse::ParamSpecifier; // with the same codepoint
176 		}
177 		if (state == Parse::ParamSpecifier)
178 		{
179 			if (c == U'%' && param == "{")
180 			{
181 				fmt += c; // literal '%'
182 				state = Parse::Literal;
183 				continue;
184 			}
185 			if (c == U'c' || c == U'o' || c == U'x' || c == U'X' || c == U'e' || c == U'E')
186 			{
187 				param += c;
188 				param += '}';
189 				fmt += param;
190 				state = Parse::Literal;
191 				continue;
192 			}
193 			if (c == U'f' || c == U'F')
194 			{
195 				param += "f}";
196 				fmt += param;
197 				state = Parse::Literal;
198 				continue;
199 			}
200 			if (c == U'd' || c == U'i' || c == U'u')
201 			{
202 				param += "d}";
203 				fmt += param;
204 				state = Parse::Literal;
205 				continue;
206 			}
207 			if (c == U's')
208 			{
209 				param += "}";
210 				fmt += param;
211 				state = Parse::Literal;
212 				continue;
213 			}
214 			if (c == U'a' || c == U'A' || c == U'g' || c == U'G' || c == U'n' || c == U'p')
215 			{
216 				err = ST::format("param specifier U+{04X} '{c}' is not supported", c, c);
217 				break;
218 			}
219 		}
220 		err = ST::format("unexpected codepoint U+{04X} '{c}'", c, c);
221 		break;
222 	}
223 	if (err.empty() && state != Parse::Literal)
224 	{
225 		err = ST::format("format param is incomplete '{}'", param);
226 	}
227 	if (!err.empty())
228 	{
229 		ST::string what = ST::format("{}: '{}' -> '{}'", err, fmt_printf, fmt);
230 		throw ST::bad_format(what.c_str());
231 	}
232 	return fmt;
233 }
234 
235 
st_buffer_escape(const ST::char_buffer & buf)236 ST::string st_buffer_escape(const ST::char_buffer& buf)
237 {
238 	ST::string escaped;
239 	for (char c : buf)
240 	{
241 		escaped += ST::format("\\x{02X}", c);
242 	}
243 	return escaped;
244 }
245 
246 
st_buffer_escape(const ST::utf16_buffer & buf)247 ST::string st_buffer_escape(const ST::utf16_buffer& buf)
248 {
249 	ST::string escaped;
250 	for (char16_t c : buf)
251 	{
252 		escaped += ST::format("\\u{04X}", c);
253 	}
254 	return escaped;
255 }
256 
257 
st_buffer_escape(const ST::utf32_buffer & buf)258 ST::string st_buffer_escape(const ST::utf32_buffer& buf)
259 {
260 	ST::string escaped;
261 	for (char32_t c : buf)
262 	{
263 		escaped += ST::format("\\U{08X}", c);
264 	}
265 	return escaped;
266 }
267