123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- /*
- * Copyright (c) 2020, the SerenityOS developers.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/Assertions.h>
- #include <AK/PrintfImplementation.h>
- #include <AK/String.h>
- #include <AK/StringBuilder.h>
- #include <AK/Types.h>
- #include <LibMain/Main.h>
- #include <stdio.h>
- #include <unistd.h>
- [[gnu::noreturn]] static void fail(char const* message)
- {
- fputs("\e[31m", stderr);
- fputs(message, stderr);
- fputs("\e[0m\n", stderr);
- exit(1);
- }
- template<typename PutChFunc, typename ArgumentListRefT, template<typename T, typename U = ArgumentListRefT> typename NextArgument, typename CharType>
- requires(IsSame<CharType, char>) struct PrintfImpl : public PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument, CharType> {
- ALWAYS_INLINE PrintfImpl(PutChFunc& putch, char*& bufptr, int const& nwritten)
- : PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument>(putch, bufptr, nwritten)
- {
- }
- ALWAYS_INLINE int format_q(PrintfImplementation::ModifierState const& state, ArgumentListRefT& ap) const
- {
- auto state_copy = state;
- auto str = NextArgument<char const*>()(ap);
- if (!str)
- str = "(null)";
- constexpr auto make_len_or_escape = [](auto str, bool mk_len, size_t field_width, auto putc) {
- unsigned len = 2;
- if (!mk_len)
- putc('"');
- for (size_t i = 0; str[i] && (mk_len ? true : (field_width >= len)); ++i) {
- auto ch = str[i];
- switch (ch) {
- case '"':
- case '$':
- case '\\':
- ++len;
- if (!mk_len)
- putc('\\');
- }
- ++len;
- if (!mk_len)
- putc(ch);
- }
- if (!mk_len)
- putc('"');
- return len;
- };
- auto len = make_len_or_escape(str, true, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
- if (!state_copy.dot && (!state_copy.field_width || state_copy.field_width < len))
- state_copy.field_width = len;
- size_t pad_amount = state_copy.field_width > len ? state_copy.field_width - len : 0;
- if (!state_copy.left_pad) {
- for (size_t i = 0; i < pad_amount; ++i)
- this->m_putch(this->m_bufptr, ' ');
- }
- make_len_or_escape(str, false, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
- if (state_copy.left_pad) {
- for (size_t i = 0; i < pad_amount; ++i)
- this->m_putch(this->m_bufptr, ' ');
- }
- return state_copy.field_width;
- }
- };
- template<typename T, typename V>
- struct ArgvNextArgument {
- ALWAYS_INLINE T operator()(V) const
- {
- static_assert(sizeof(V) != sizeof(V), "Base instantiated");
- return declval<T>();
- }
- };
- template<typename V>
- struct ArgvNextArgument<char*, V> {
- ALWAYS_INLINE char* operator()(V arg) const
- {
- if (arg.argc == 0)
- fail("Not enough arguments");
- auto result = *arg.argv++;
- --arg.argc;
- return result;
- }
- };
- template<typename V>
- struct ArgvNextArgument<char const*, V> {
- ALWAYS_INLINE char const* operator()(V arg) const
- {
- if (arg.argc == 0)
- return "";
- auto result = *arg.argv++;
- --arg.argc;
- return result;
- }
- };
- template<typename V>
- struct ArgvNextArgument<wchar_t const*, V> {
- ALWAYS_INLINE wchar_t const* operator()(V arg) const
- {
- if (arg.argc == 0)
- return L"";
- return L"";
- }
- };
- template<typename V>
- struct ArgvNextArgument<int, V> {
- ALWAYS_INLINE int operator()(V arg) const
- {
- if (arg.argc == 0)
- return 0;
- auto result = *arg.argv++;
- --arg.argc;
- return atoi(result);
- }
- };
- template<typename V>
- struct ArgvNextArgument<unsigned, V> {
- ALWAYS_INLINE unsigned operator()(V arg) const
- {
- if (arg.argc == 0)
- return 0;
- auto result = *arg.argv++;
- --arg.argc;
- return strtoul(result, nullptr, 10);
- }
- };
- template<typename V>
- struct ArgvNextArgument<long int, V> {
- ALWAYS_INLINE long int operator()(V arg) const
- {
- if (arg.argc == 0)
- return 0;
- auto result = *arg.argv++;
- --arg.argc;
- return strtol(result, nullptr, 10);
- }
- };
- template<typename V>
- struct ArgvNextArgument<long long int, V> {
- ALWAYS_INLINE long long int operator()(V arg) const
- {
- if (arg.argc == 0)
- return 0;
- auto result = *arg.argv++;
- --arg.argc;
- return strtoll(result, nullptr, 10);
- }
- };
- template<typename V>
- struct ArgvNextArgument<unsigned long int, V> {
- ALWAYS_INLINE unsigned long int operator()(V arg) const
- {
- if (arg.argc == 0)
- return 0;
- auto result = *arg.argv++;
- --arg.argc;
- return strtoul(result, nullptr, 10);
- }
- };
- template<typename V>
- struct ArgvNextArgument<unsigned long long int, V> {
- ALWAYS_INLINE unsigned long long int operator()(V arg) const
- {
- if (arg.argc == 0)
- return 0;
- auto result = *arg.argv++;
- --arg.argc;
- return strtoull(result, nullptr, 10);
- }
- };
- template<typename V>
- struct ArgvNextArgument<double, V> {
- ALWAYS_INLINE double operator()(V arg) const
- {
- if (arg.argc == 0)
- return 0;
- auto result = *arg.argv++;
- --arg.argc;
- return strtod(result, nullptr);
- }
- };
- template<typename V>
- struct ArgvNextArgument<int*, V> {
- ALWAYS_INLINE int* operator()(V) const
- {
- VERIFY_NOT_REACHED();
- return nullptr;
- }
- };
- struct ArgvWithCount {
- char**& argv;
- int& argc;
- };
- static String handle_escapes(char const* string)
- {
- StringBuilder builder;
- for (auto c = *string; c; c = *++string) {
- if (c == '\\') {
- if (string[1]) {
- switch (c = *++string) {
- case '\\':
- case '"':
- builder.append(c);
- break;
- case 'a':
- builder.append('\a');
- break;
- case 'b':
- builder.append('\b');
- break;
- case 'c':
- return builder.build();
- case 'e':
- builder.append('\e');
- break;
- case 'f':
- builder.append('\f');
- break;
- case 'n':
- builder.append('\n');
- break;
- case 'r':
- builder.append('\r');
- break;
- case 't':
- builder.append('\t');
- break;
- case 'v':
- builder.append('\v');
- break;
- case 'x':
- fail("Unsupported escape '\\x'");
- case 'u':
- fail("Unsupported escape '\\u'");
- case 'U':
- fail("Unsupported escape '\\U'");
- default:
- builder.append(c);
- }
- } else {
- builder.append(c);
- }
- } else {
- builder.append(c);
- }
- }
- return builder.build();
- }
- ErrorOr<int> serenity_main(Main::Arguments arguments)
- {
- if (arguments.argc < 2)
- return Error::from_errno(EINVAL);
- auto argc = arguments.argc;
- auto argv = arguments.argv;
- ++argv;
- String format = handle_escapes(*(argv++));
- auto format_string = format.characters();
- argc -= 2;
- ArgvWithCount arg { argv, argc };
- auto putch = [](auto*, auto ch) {
- putchar(ch);
- };
- auto previous_argc = 0;
- do {
- previous_argc = argc;
- PrintfImplementation::printf_internal<decltype(putch), PrintfImpl, ArgvWithCount, ArgvNextArgument, char>(putch, nullptr, format_string, arg);
- } while (argc && previous_argc != argc);
- return 0;
- }
|