Serenity Operating System
1/*
2 * Copyright (c) 2020, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Assertions.h>
8#include <AK/DeprecatedString.h>
9#include <AK/PrintfImplementation.h>
10#include <AK/StringBuilder.h>
11#include <AK/Types.h>
12#include <LibMain/Main.h>
13#include <stdio.h>
14#include <unistd.h>
15
16[[gnu::noreturn]] static void fail(char const* message)
17{
18 fputs("\e[31m", stderr);
19 fputs(message, stderr);
20 fputs("\e[0m\n", stderr);
21 exit(1);
22}
23
24template<typename PutChFunc, typename ArgumentListRefT, template<typename T, typename U = ArgumentListRefT> typename NextArgument, typename CharType>
25requires(IsSame<CharType, char>) struct PrintfImpl : public PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument, CharType> {
26 ALWAYS_INLINE PrintfImpl(PutChFunc& putch, char*& bufptr, int const& nwritten)
27 : PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument>(putch, bufptr, nwritten)
28 {
29 }
30
31 ALWAYS_INLINE int format_q(PrintfImplementation::ModifierState const& state, ArgumentListRefT& ap) const
32 {
33 auto state_copy = state;
34 auto str = NextArgument<char const*>()(ap);
35 if (!str)
36 str = "(null)";
37
38 constexpr auto make_len_or_escape = [](auto str, bool mk_len, size_t field_width, auto putc) {
39 unsigned len = 2;
40 if (!mk_len)
41 putc('"');
42
43 for (size_t i = 0; str[i] && (mk_len ? true : (field_width >= len)); ++i) {
44 auto ch = str[i];
45 switch (ch) {
46 case '"':
47 case '$':
48 case '\\':
49 ++len;
50 if (!mk_len)
51 putc('\\');
52 }
53 ++len;
54 if (!mk_len)
55 putc(ch);
56 }
57
58 if (!mk_len)
59 putc('"');
60
61 return len;
62 };
63
64 auto len = make_len_or_escape(str, true, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
65
66 if (!state_copy.dot && (!state_copy.field_width || state_copy.field_width < len))
67 state_copy.field_width = len;
68 size_t pad_amount = state_copy.field_width > len ? state_copy.field_width - len : 0;
69
70 if (!state_copy.left_pad) {
71 for (size_t i = 0; i < pad_amount; ++i)
72 this->m_putch(this->m_bufptr, ' ');
73 }
74
75 make_len_or_escape(str, false, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
76
77 if (state_copy.left_pad) {
78 for (size_t i = 0; i < pad_amount; ++i)
79 this->m_putch(this->m_bufptr, ' ');
80 }
81
82 return state_copy.field_width;
83 }
84};
85
86template<typename T, typename V>
87struct ArgvNextArgument {
88 ALWAYS_INLINE T operator()(V) const
89 {
90 static_assert(sizeof(V) != sizeof(V), "Base instantiated");
91 return declval<T>();
92 }
93};
94
95template<typename V>
96struct ArgvNextArgument<char*, V> {
97 ALWAYS_INLINE char* operator()(V arg) const
98 {
99 if (arg.argc == 0)
100 fail("Not enough arguments");
101
102 auto result = *arg.argv++;
103 --arg.argc;
104 return result;
105 }
106};
107
108template<typename V>
109struct ArgvNextArgument<char const*, V> {
110 ALWAYS_INLINE char const* operator()(V arg) const
111 {
112 if (arg.argc == 0)
113 return "";
114
115 auto result = *arg.argv++;
116 --arg.argc;
117 return result;
118 }
119};
120
121template<typename V>
122struct ArgvNextArgument<wchar_t const*, V> {
123 ALWAYS_INLINE wchar_t const* operator()(V arg) const
124 {
125 if (arg.argc == 0)
126 return L"";
127
128 return L"";
129 }
130};
131
132template<typename V>
133struct ArgvNextArgument<int, V> {
134 ALWAYS_INLINE int operator()(V arg) const
135 {
136 if (arg.argc == 0)
137 return 0;
138
139 auto result = *arg.argv++;
140 --arg.argc;
141 return atoi(result);
142 }
143};
144
145template<typename V>
146struct ArgvNextArgument<unsigned, V> {
147 ALWAYS_INLINE unsigned operator()(V arg) const
148 {
149 if (arg.argc == 0)
150 return 0;
151
152 auto result = *arg.argv++;
153 --arg.argc;
154 return strtoul(result, nullptr, 10);
155 }
156};
157
158template<typename V>
159struct ArgvNextArgument<long int, V> {
160 ALWAYS_INLINE long int operator()(V arg) const
161 {
162 if (arg.argc == 0)
163 return 0;
164
165 auto result = *arg.argv++;
166 --arg.argc;
167 return strtol(result, nullptr, 10);
168 }
169};
170
171template<typename V>
172struct ArgvNextArgument<long long int, V> {
173 ALWAYS_INLINE long long int operator()(V arg) const
174 {
175 if (arg.argc == 0)
176 return 0;
177
178 auto result = *arg.argv++;
179 --arg.argc;
180 return strtoll(result, nullptr, 10);
181 }
182};
183
184template<typename V>
185struct ArgvNextArgument<unsigned long int, V> {
186 ALWAYS_INLINE unsigned long int operator()(V arg) const
187 {
188 if (arg.argc == 0)
189 return 0;
190
191 auto result = *arg.argv++;
192 --arg.argc;
193 return strtoul(result, nullptr, 10);
194 }
195};
196
197template<typename V>
198struct ArgvNextArgument<unsigned long long int, V> {
199 ALWAYS_INLINE unsigned long long int operator()(V arg) const
200 {
201 if (arg.argc == 0)
202 return 0;
203
204 auto result = *arg.argv++;
205 --arg.argc;
206 return strtoull(result, nullptr, 10);
207 }
208};
209
210template<typename V>
211struct ArgvNextArgument<double, V> {
212 ALWAYS_INLINE double operator()(V arg) const
213 {
214 if (arg.argc == 0)
215 return 0;
216
217 auto result = *arg.argv++;
218 --arg.argc;
219 return strtod(result, nullptr);
220 }
221};
222
223template<typename V>
224struct ArgvNextArgument<int*, V> {
225 ALWAYS_INLINE int* operator()(V) const
226 {
227 VERIFY_NOT_REACHED();
228 return nullptr;
229 }
230};
231
232struct ArgvWithCount {
233 char**& argv;
234 int& argc;
235};
236
237static DeprecatedString handle_escapes(char const* string)
238{
239 StringBuilder builder;
240 for (auto c = *string; c; c = *++string) {
241 if (c == '\\') {
242 if (string[1]) {
243 switch (c = *++string) {
244 case '\\':
245 case '"':
246 builder.append(c);
247 break;
248 case 'a':
249 builder.append('\a');
250 break;
251 case 'b':
252 builder.append('\b');
253 break;
254 case 'c':
255 return builder.to_deprecated_string();
256 case 'e':
257 builder.append('\e');
258 break;
259 case 'f':
260 builder.append('\f');
261 break;
262 case 'n':
263 builder.append('\n');
264 break;
265 case 'r':
266 builder.append('\r');
267 break;
268 case 't':
269 builder.append('\t');
270 break;
271 case 'v':
272 builder.append('\v');
273 break;
274 case 'x':
275 fail("Unsupported escape '\\x'");
276 case 'u':
277 fail("Unsupported escape '\\u'");
278 case 'U':
279 fail("Unsupported escape '\\U'");
280 default:
281 builder.append(c);
282 }
283 } else {
284 builder.append(c);
285 }
286 } else {
287 builder.append(c);
288 }
289 }
290
291 return builder.to_deprecated_string();
292}
293
294ErrorOr<int> serenity_main(Main::Arguments arguments)
295{
296 if (arguments.argc < 2)
297 return Error::from_errno(EINVAL);
298
299 auto argc = arguments.argc;
300 auto argv = arguments.argv;
301
302 ++argv;
303 DeprecatedString format = handle_escapes(*(argv++));
304 auto format_string = format.characters();
305
306 argc -= 2;
307
308 ArgvWithCount arg { argv, argc };
309
310 auto putch = [](auto*, auto ch) {
311 putchar(ch);
312 };
313
314 auto previous_argc = 0;
315 do {
316 previous_argc = argc;
317 PrintfImplementation::printf_internal<decltype(putch), PrintfImpl, ArgvWithCount, ArgvNextArgument, char>(putch, nullptr, format_string, arg);
318 } while (argc && previous_argc != argc);
319
320 return 0;
321}