aboutsummaryrefslogtreecommitdiff
path: root/03-refactor/src/printf.c
diff options
context:
space:
mode:
Diffstat (limited to '03-refactor/src/printf.c')
-rw-r--r--03-refactor/src/printf.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/03-refactor/src/printf.c b/03-refactor/src/printf.c
new file mode 100644
index 0000000..fa7f519
--- /dev/null
+++ b/03-refactor/src/printf.c
@@ -0,0 +1,152 @@
+#include "printf.h"
+
+enum PRINTF_TYPE {
+ PRINTF_TYPE_STRING = 0,
+ PRINTF_TYPE_INT = 1,
+ PRINTF_TYPE_PERCENT = 2,
+ PRINTF_TYPE_UNKNOWN = 999
+};
+
+static enum PRINTF_TYPE get_printf_type(const char* cur)
+{
+ while (*cur >= 0x30 && *cur < 0x3a) ++ cur;
+
+ if (*cur == 's') {
+ return PRINTF_TYPE_STRING;
+ } else if (*cur == 'd' || *cur == 'x' || *cur == 'X' || *cur == 'u') {
+ return PRINTF_TYPE_INT;
+ }
+
+ return PRINTF_TYPE_UNKNOWN;
+}
+
+static const char* printf_handle_string(
+ const char* fmt,
+ const char* next,
+ printf_callback_t callback,
+ volatile void* closure)
+{
+ const char* cur = next;
+ for (; *cur != 0; ++ cur) {
+ callback(closure, *cur);
+ }
+
+ return fmt;
+}
+
+static char printf_toupper(char ch)
+{
+ if (ch <= 0x7a && ch > 0x60) {
+ return ch - 0x20;
+ }
+ return ch;
+}
+
+static const char* parse_fmt(
+ const char* fmt,
+ int* out_padding,
+ char* out_pad_char)
+{
+ if (*fmt == '0') {
+ *out_pad_char = '0';
+ ++ fmt;
+ } else {
+ *out_pad_char = ' ';
+ }
+ int padding = 0;
+ while (*fmt < 0x3a && *fmt >= 0x30) {
+ padding *= 10;
+ padding += (*fmt ++) - 0x30;
+ }
+
+ *out_padding = padding;
+ return fmt;
+}
+
+static const char* mapping = "0123456789abcdef";
+static const char* printf_handle_int(
+ const char* fmt, unsigned int next, printf_callback_t callback, volatile void* closure)
+{
+ int base = 10;
+ char chars[32];
+ int pos = 31;
+ int upper = 0;
+ char pad_char;
+ int padding;
+ int is_unsigned = 0;
+
+ fmt = parse_fmt(fmt, &padding, &pad_char);
+
+ if (*fmt == 'x' || *fmt == 'X') {
+ base = 16;
+ is_unsigned = 1;
+ } else if (*fmt == 'u') {
+ is_unsigned = 1;
+ }
+
+ upper = *fmt == 'X';
+
+ int next_i = (int) next;
+ if (next_i < 0 && !is_unsigned) {
+ callback(closure, '-');
+ next_i *= -1;
+ next = (unsigned) next_i;
+ }
+
+ if (next == 0) {
+ callback(closure, '0');
+ } else {
+ while (next > 0) {
+ char next_ch = mapping[next % base];
+ if (upper) next_ch = printf_toupper(next_ch);
+ chars[pos --] = next_ch;
+ padding --;
+ next /= base;
+ }
+
+ while ((padding--) > 0) {
+ chars[pos --] = pad_char;
+ }
+
+ for (++ pos; pos < 32; ++ pos) {
+ callback(closure, chars[pos]);
+ }
+ }
+
+ return fmt;
+}
+
+void printf_format(
+ const char* fmt,
+ printf_callback_t callback,
+ volatile void* closure,
+ va_list ap)
+{
+ const char* cur = fmt;
+
+ const char* next_string;
+ int next_int;
+
+ for (; *cur != 0; ++ cur) {
+ if (*cur == '%') {
+ // Handle the formatting here.
+ ++ cur;
+ enum PRINTF_TYPE printf_type = get_printf_type(cur);
+
+ switch (printf_type) {
+ case PRINTF_TYPE_PERCENT:
+ callback(closure, '%');
+ break;
+ case PRINTF_TYPE_STRING:
+ next_string = va_arg(ap, const char*);
+ cur = printf_handle_string(cur, next_string, callback, closure);
+ break;
+ case PRINTF_TYPE_INT:
+ next_int = va_arg(ap, int);
+ cur = printf_handle_int(cur, next_int, callback, closure);
+ }
+ } else {
+ callback(closure, *cur);
+ }
+ }
+}