printf.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <AK/PrintfImplementation.h>
  27. #include <AK/String.h>
  28. #include <AK/StringBuilder.h>
  29. #include <AK/Types.h>
  30. #include <stdio.h>
  31. #include <unistd.h>
  32. [[gnu::noreturn]] static void fail(const char* message)
  33. {
  34. fputs("\e[31m", stderr);
  35. fputs(message, stderr);
  36. fputs("\e[0m\n", stderr);
  37. exit(1);
  38. }
  39. template<typename PutChFunc, typename ArgumentListRefT, template<typename T, typename U = ArgumentListRefT> typename NextArgument>
  40. struct PrintfImpl : public PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument> {
  41. ALWAYS_INLINE PrintfImpl(PutChFunc& putch, char*& bufptr, const int& nwritten)
  42. : PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument>(putch, bufptr, nwritten)
  43. {
  44. }
  45. ALWAYS_INLINE int format_b(const PrintfImplementation::ModifierState&, ArgumentListRefT&) const
  46. {
  47. fail("format specifier 'b' is not supported");
  48. }
  49. ALWAYS_INLINE int format_q(const PrintfImplementation::ModifierState& state, ArgumentListRefT& ap) const
  50. {
  51. auto state_copy = state;
  52. auto str = NextArgument<const char*>()(ap);
  53. if (!str)
  54. str = "(null)";
  55. constexpr auto make_len_or_escape = [](auto str, bool mk_len, size_t field_width, auto putc) {
  56. unsigned len = 2;
  57. if (!mk_len)
  58. putc('"');
  59. for (size_t i = 0; str[i] && (mk_len ? true : (field_width >= len)); ++i) {
  60. auto ch = str[i];
  61. switch (ch) {
  62. case '"':
  63. case '$':
  64. case '\\':
  65. ++len;
  66. if (!mk_len)
  67. putc('\\');
  68. }
  69. ++len;
  70. if (!mk_len)
  71. putc(ch);
  72. }
  73. if (!mk_len)
  74. putc('"');
  75. return len;
  76. };
  77. auto len = make_len_or_escape(str, true, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
  78. if (!state_copy.dot && (!state_copy.field_width || state_copy.field_width < len))
  79. state_copy.field_width = len;
  80. size_t pad_amount = state_copy.field_width > len ? state_copy.field_width - len : 0;
  81. if (!state_copy.left_pad) {
  82. for (size_t i = 0; i < pad_amount; ++i)
  83. this->m_putch(this->m_bufptr, ' ');
  84. }
  85. make_len_or_escape(str, false, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
  86. if (state_copy.left_pad) {
  87. for (size_t i = 0; i < pad_amount; ++i)
  88. this->m_putch(this->m_bufptr, ' ');
  89. }
  90. return state_copy.field_width;
  91. }
  92. };
  93. template<typename T, typename V>
  94. struct ArgvNextArgument {
  95. ALWAYS_INLINE T operator()(V) const
  96. {
  97. static_assert(sizeof(V) != sizeof(V), "Base instantiated");
  98. return declval<T>();
  99. }
  100. };
  101. template<typename V>
  102. struct ArgvNextArgument<char*, V> {
  103. ALWAYS_INLINE char* operator()(V arg) const
  104. {
  105. if (arg.argc == 0)
  106. fail("Not enough arguments");
  107. auto result = *arg.argv++;
  108. --arg.argc;
  109. return result;
  110. }
  111. };
  112. template<typename V>
  113. struct ArgvNextArgument<const char*, V> {
  114. ALWAYS_INLINE const char* operator()(V arg) const
  115. {
  116. if (arg.argc == 0)
  117. return "";
  118. auto result = *arg.argv++;
  119. --arg.argc;
  120. return result;
  121. }
  122. };
  123. template<typename V>
  124. struct ArgvNextArgument<int, V> {
  125. ALWAYS_INLINE int operator()(V arg) const
  126. {
  127. if (arg.argc == 0)
  128. return 0;
  129. auto result = *arg.argv++;
  130. --arg.argc;
  131. return atoi(result);
  132. }
  133. };
  134. template<typename V>
  135. struct ArgvNextArgument<unsigned, V> {
  136. ALWAYS_INLINE unsigned operator()(V arg) const
  137. {
  138. if (arg.argc == 0)
  139. return 0;
  140. auto result = *arg.argv++;
  141. --arg.argc;
  142. return strtoul(result, nullptr, 10);
  143. }
  144. };
  145. template<typename V>
  146. struct ArgvNextArgument<i64, V> {
  147. ALWAYS_INLINE i64 operator()(V arg) const
  148. {
  149. if (arg.argc == 0)
  150. return 0;
  151. auto result = *arg.argv++;
  152. --arg.argc;
  153. return strtoll(result, nullptr, 10);
  154. }
  155. };
  156. template<typename V>
  157. struct ArgvNextArgument<u64, V> {
  158. ALWAYS_INLINE u64 operator()(V arg) const
  159. {
  160. if (arg.argc == 0)
  161. return 0;
  162. auto result = *arg.argv++;
  163. --arg.argc;
  164. return strtoull(result, nullptr, 10);
  165. }
  166. };
  167. template<typename V>
  168. struct ArgvNextArgument<double, V> {
  169. ALWAYS_INLINE double operator()(V arg) const
  170. {
  171. if (arg.argc == 0)
  172. return 0;
  173. auto result = *arg.argv++;
  174. --arg.argc;
  175. return strtod(result, nullptr);
  176. }
  177. };
  178. template<typename V>
  179. struct ArgvNextArgument<int*, V> {
  180. ALWAYS_INLINE int* operator()(V) const
  181. {
  182. ASSERT_NOT_REACHED();
  183. return nullptr;
  184. }
  185. };
  186. struct ArgvWithCount {
  187. char**& argv;
  188. int& argc;
  189. };
  190. static String handle_escapes(const char* string)
  191. {
  192. StringBuilder builder;
  193. for (auto c = *string; c; c = *++string) {
  194. if (c == '\\') {
  195. if (string[1]) {
  196. switch (c = *++string) {
  197. case '\\':
  198. case '"':
  199. builder.append(c);
  200. break;
  201. case 'a':
  202. builder.append('\a');
  203. break;
  204. case 'b':
  205. builder.append('\b');
  206. break;
  207. case 'c':
  208. return builder.build();
  209. case 'e':
  210. builder.append('\e');
  211. break;
  212. case 'f':
  213. builder.append('\f');
  214. break;
  215. case 'n':
  216. builder.append('\n');
  217. break;
  218. case 'r':
  219. builder.append('\r');
  220. break;
  221. case 't':
  222. builder.append('\t');
  223. break;
  224. case 'v':
  225. builder.append('\v');
  226. break;
  227. case 'x':
  228. fail("Unsupported escape '\\x'");
  229. case 'u':
  230. fail("Unsupported escape '\\u'");
  231. case 'U':
  232. fail("Unsupported escape '\\U'");
  233. default:
  234. builder.append(c);
  235. }
  236. } else {
  237. builder.append(c);
  238. }
  239. } else {
  240. builder.append(c);
  241. }
  242. }
  243. return builder.build();
  244. }
  245. int main(int argc, char** argv)
  246. {
  247. if (argc < 2)
  248. return 1;
  249. ++argv;
  250. String format = handle_escapes(*(argv++));
  251. auto format_string = format.characters();
  252. argc -= 2;
  253. ArgvWithCount arg { argv, argc };
  254. auto putch = [](auto*, auto ch) {
  255. putchar(ch);
  256. };
  257. auto previous_argc = 0;
  258. do {
  259. previous_argc = argc;
  260. PrintfImplementation::printf_internal<decltype(putch), PrintfImpl, ArgvWithCount, ArgvNextArgument>(putch, nullptr, format_string, arg);
  261. } while (argc && previous_argc != argc);
  262. return 0;
  263. }