Serenity Operating System
at master 321 lines 8.3 kB view raw
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}