diff --git a/libc/Makefile b/libc/Makefile
index 9e7c5b4e..6d638172 100644
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -56,7 +56,6 @@ stdio/flbf.o \
stdio/flockfile.o \
stdio/flushlbf.o \
stdio/fnewfile.o \
-stdio/format.o \
stdio/fpending.o \
stdio/fpurge.o \
stdio/fputc.o \
@@ -91,6 +90,7 @@ stdio/sscanf.o \
stdio/ungetc.o \
stdio/vdprintf.o \
stdio/vfscanf.o \
+stdio/vprintf_callback.o \
stdio/vsnprintf.o \
stdio/vsprintf.o \
stdio/vsscanf.o \
diff --git a/libc/stdio/format.cpp b/libc/stdio/format.cpp
deleted file mode 100644
index 5b04ce28..00000000
--- a/libc/stdio/format.cpp
+++ /dev/null
@@ -1,527 +0,0 @@
-/*******************************************************************************
-
- Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
-
- This file is part of the Sortix C Library.
-
- The Sortix C Library is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or (at your
- option) any later version.
-
- The Sortix C Library is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with the Sortix C Library. If not, see .
-
- stdio/format.cpp
- Provides printf formatting functions that uses callbacks.
-
-*******************************************************************************/
-
-// Number of bugs seemingly unrelated bugs that have been traced to here:
-// Countless + 2
-
-#include
-#include
-#include
-#include
-
-namespace String {
-
-static int ConvertInt32(int32_t num, char* dest, char possign)
-{
- int result = 0;
- int32_t copy = num;
-
- if ( num < 0 )
- {
- *dest++ = '-';
- result++;
-
- int offset = 0;
- while ( copy < -9 ) { copy /= 10; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' - num % 10; num /= 10; offset--;
- }
- }
- else
- {
- if ( possign )
- *dest++ = possign,
- result++;
-
- int offset = 0;
- while ( copy > 9 ) { copy /= 10; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' + num % 10; num /= 10; offset--;
- }
- }
-
- return result + 1;
-}
-
-static int ConvertInt64(int64_t num, char* dest, char possign)
-{
- int result = 0;
- int64_t copy = num;
-
- if ( num < 0 )
- {
- *dest++ = '-';
- result++;
-
- int offset = 0;
- while ( copy < -9 ) { copy /= 10; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' - num % 10; num /= 10; offset--;
- }
- }
- else
- {
- if ( possign )
- *dest++ = possign,
- result++;
-
- int offset = 0;
- while ( copy > 9 ) { copy /= 10; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' + num % 10; num /= 10; offset--;
- }
- }
-
- return result + 1;
-}
-
-static int ConvertUInt32(uint32_t num, char* dest)
-{
- int result = 0;
- uint32_t copy = num;
- int offset = 0;
- while ( copy > 9 ) { copy /= 10; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' + num % 10; num /= 10; offset--;
- }
-
- return result + 1;
-}
-
-static int ConvertUInt64(uint64_t num, char* dest)
-{
- int result = 0;
- uint64_t copy = num;
- int offset = 0;
- while ( copy > 9 ) { copy /= 10; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' + num % 10; num /= 10; offset--;
- }
-
- return result + 1;
-}
-
-static int ConvertUInt32Octal(uint32_t num, char* dest)
-{
- int result = 0;
- uint32_t copy = num;
- int offset = 0;
- while ( copy > 7 ) { copy /= 8; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' + num % 8; num /= 8; offset--;
- }
-
- return result + 1;
-}
-
-static int ConvertUInt64Octal(uint64_t num, char* dest)
-{
- int result = 0;
- uint64_t copy = num;
- int offset = 0;
- while ( copy > 7 ) { copy /= 8; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = '0' + num % 8; num /= 8; offset--;
- }
-
- return result + 1;
-}
-
-static int ConvertUInt32Hex(uint32_t num, char* dest)
-{
- char chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'A', 'B', 'C', 'D', 'E', 'F' };
- int result = 0;
- uint32_t copy = num;
- int offset = 0;
- while ( copy > 15 ) { copy /= 16; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = chars[num % 16]; num /= 16; offset--;
- }
-
- return result + 1;
-}
-
-static int ConvertUInt64Hex(uint64_t num, char* dest)
-{
- char chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'A', 'B', 'C', 'D', 'E', 'F' };
- int result = 0;
- uint64_t copy = num;
- int offset = 0;
- while ( copy > 15 ) { copy /= 16; offset++; }
- result += offset;
- while ( offset >= 0 )
- {
- dest[offset] = chars[num % 16]; num /= 16; offset--;
- }
-
- return result + 1;
-}
-
-} // namespace String
-
-#define READY_SIZE 128
-
-#define READY_FLUSH() \
- ready[readylen] = '\0'; \
- if ( 0 < readylen && callback && callback(user, ready, readylen) != readylen ) { return SIZE_MAX; } \
- written += readylen; readylen = 0;
-
-#define REPEAT_BLANKS(num) \
- for ( unsigned int i = 0; i < (num); i++ ) \
- { \
- if ( readylen == READY_SIZE ) { READY_FLUSH(); } \
- ready[readylen++ + i - i] = blank_char; \
- }
-
-extern "C" size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
- void* user,
- const char* restrict format,
- va_list parameters)
-{
- size_t written = 0;
- size_t readylen = 0;
- char ready[READY_SIZE + 1];
-
- while ( *format != '\0' )
- {
- char c = *format++;
-
- if ( c != '%' )
- {
- print_c:
- if ( READY_SIZE <= readylen ) { READY_FLUSH(); }
- ready[readylen++] = c;
- continue;
- }
-
- if ( *format == '%' )
- {
- c = *format++;
- goto print_c;
- }
-
- const char* format_begun_at = format;
- if ( false )
- {
- unsupported_conversion:
- format = format_begun_at;
- c = '%';
- goto print_c;
- }
-
- const unsigned UNSIGNED = 0;
- const unsigned INTEGER = (1<<0);
- const unsigned BIT64 = (1<<1);
- const unsigned HEX = (1<<2);
- const unsigned OCTAL = (1<<3);
- const unsigned STRING = 16;
- const unsigned CHARACTER = 17;
- #if defined(__x86_64__)
- const unsigned WORDWIDTH = BIT64;
- #else
- const unsigned WORDWIDTH = 0;
- #endif
-
- // TODO: Support signed datatypes!
-
- unsigned type = 0;
-
- bool prepend_chars = false;
- bool append_chars = false;
- bool space_pad = false;
- bool zero_pad = false;
- bool alternate = false;
- bool possign_space = false;
- bool possign_plus = false;
- char blank_char = ' ';
- unsigned int field_width = 0;
-
- bool scanning = true;
- while ( scanning )
- {
- switch ( char c = *(format++) )
- {
- case ' ':
- possign_space = true;
- break;
- case '+':
- possign_plus = true;
- break;
- case '#':
- alternate = true;
- break;
- case '-':
- if ( prepend_chars || append_chars )
- goto unsupported_conversion;
- append_chars = space_pad = true;
- prepend_chars = zero_pad = false;
- blank_char = ' ';
- break;
- case '*':
- if ( !(prepend_chars || append_chars) )
- prepend_chars = true;
- field_width = (unsigned int) va_arg(parameters, int);
- break;
- case '0':
- if ( !(prepend_chars || append_chars) || (!field_width && space_pad) )
- {
- prepend_chars = zero_pad = true;
- append_chars = space_pad = false;
- blank_char = '0';
- break;
- }
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if ( !(prepend_chars || append_chars) )
- prepend_chars = true;
- field_width = field_width * 10 + c - '0';
- break;
- case 'p':
- type = WORDWIDTH | HEX;
- scanning = false;
- break;
- case 't':
- type |= INTEGER;
- case 'z':
- case 'l':
- if ( type & WORDWIDTH ) { type |= BIT64; }
- type |= WORDWIDTH;
- break;
- case 'j':
- type |= BIT64;
- break;
- case 'u':
- type |= UNSIGNED;
- scanning = false;
- break;
- case 'd':
- case 'i':
- type |= INTEGER;
- scanning = false;
- break;
- case 'o':
- type |= OCTAL;
- scanning = false;
- break;
- case 'x':
- case 'X':
- type |= HEX;
- scanning = false;
- break;
- case 's':
- type = STRING;
- scanning = false;
- break;
- case 'c':
- type = CHARACTER;
- scanning = false;
- break;
- default:
- goto unsupported_conversion;
- }
- }
-
- char possign = '\0';
- if ( possign_plus )
- possign = '+';
- else if ( possign_space )
- possign = ' ';
-
- switch ( type )
- {
- case INTEGER:
- {
- if ( READY_SIZE - readylen < 10 ) { READY_FLUSH(); }
- int32_t num = va_arg(parameters, int32_t);
- size_t chars = String::ConvertInt32(num, ready + readylen, possign);
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 10 ) { READY_FLUSH(); }
- String::ConvertInt32(num, ready + readylen, possign);
- readylen += chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case UNSIGNED:
- {
- if ( READY_SIZE - readylen < 10 ) { READY_FLUSH(); }
- uint32_t num = va_arg(parameters, uint32_t);
- size_t chars = String::ConvertUInt32(num, ready + readylen);
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 10 ) { READY_FLUSH(); }
- String::ConvertUInt32(num, ready + readylen);
- readylen += chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case INTEGER | BIT64:
- {
- if ( READY_SIZE - readylen < 20 ) { READY_FLUSH(); }
- int64_t num = va_arg(parameters, int64_t);
- size_t chars = String::ConvertInt64(num, ready + readylen, possign);
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 20 ) { READY_FLUSH(); }
- String::ConvertInt64(num, ready + readylen, possign);
- readylen += chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case UNSIGNED | BIT64:
- {
- if ( READY_SIZE - readylen < 20 ) { READY_FLUSH(); }
- uint64_t num = va_arg(parameters, uint64_t);
- size_t chars = String::ConvertUInt64(num, ready + readylen);
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 20 ) { READY_FLUSH(); }
- String::ConvertUInt64(num, ready + readylen);
- readylen += chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case INTEGER | HEX:
- case UNSIGNED | HEX:
- {
- if ( READY_SIZE - readylen < 8 ) { READY_FLUSH(); }
- uint32_t num = va_arg(parameters, uint32_t);
- size_t chars = String::ConvertUInt32Hex(num, ready + readylen);
- size_t real_chars = chars;
- if ( num && alternate ) chars += 2;
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 8 + 2 ) { READY_FLUSH(); }
- if ( alternate && num )
- ready[readylen++] = '0',
- ready[readylen++] = 'x';
- String::ConvertUInt32Hex(num, ready + readylen);
- readylen += real_chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case INTEGER | BIT64 | HEX:
- case UNSIGNED | BIT64 | HEX:
- {
- if ( READY_SIZE - readylen < 16 ) { READY_FLUSH(); }
- uint64_t num = va_arg(parameters, uint64_t);
- size_t chars = String::ConvertUInt64Hex(num, ready + readylen);
- size_t real_chars = chars;
- if ( num && alternate ) chars += 2;
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 16 + 2 ) { READY_FLUSH(); }
- if ( alternate && num )
- ready[readylen++] = '0',
- ready[readylen++] = 'x';
- String::ConvertUInt64Hex(num, ready + readylen);
- readylen += real_chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case INTEGER | OCTAL:
- case UNSIGNED | OCTAL:
- {
- if ( READY_SIZE - readylen < 20 ) { READY_FLUSH(); }
- uint32_t num = va_arg(parameters, uint32_t);
- size_t chars = String::ConvertUInt32Octal(num, ready + readylen);
- size_t real_chars = chars;
- if ( num && alternate ) chars += 1;
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 20 + 1 ) { READY_FLUSH(); }
- if ( alternate && num )
- ready[readylen++] = '0';
- String::ConvertUInt32Octal(num, ready + readylen);
- readylen += real_chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case INTEGER | BIT64 | OCTAL:
- case UNSIGNED | BIT64 | OCTAL:
- {
- if ( READY_SIZE - readylen < 40 ) { READY_FLUSH(); }
- uint64_t num = va_arg(parameters, uint64_t);
- size_t chars = String::ConvertUInt64Octal(num, ready + readylen);
- size_t real_chars = chars;
- if ( num && alternate ) chars += 1;
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE - readylen < 40 + 1 ) { READY_FLUSH(); }
- if ( alternate && num )
- ready[readylen++] = '0';
- String::ConvertUInt64Octal(num, ready + readylen);
- readylen += real_chars;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- case STRING:
- {
- const char* str = va_arg(parameters, const char*);
- size_t len = strlen(str);
- size_t chars = len;
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- READY_FLUSH();
- if ( callback && callback(user, str, len) != len ) { return SIZE_MAX; }
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- written += len;
- break;
- }
- case CHARACTER:
- {
- int c = va_arg(parameters, int);
- size_t len = 1;
- size_t chars = len;
- if ( prepend_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- if ( READY_SIZE <= readylen ) { READY_FLUSH(); }
- ready[readylen++] = c;
- if ( append_chars && chars < field_width ) { REPEAT_BLANKS(field_width - chars); }
- break;
- }
- }
- }
-
- READY_FLUSH();
-
- return written;
-}
diff --git a/libc/stdio/vprintf_callback.cpp b/libc/stdio/vprintf_callback.cpp
new file mode 100644
index 00000000..0e5c12c6
--- /dev/null
+++ b/libc/stdio/vprintf_callback.cpp
@@ -0,0 +1,408 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
+
+ This file is part of the Sortix C Library.
+
+ The Sortix C Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ The Sortix C Library is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with the Sortix C Library. If not, see .
+
+ stdio/vprintf_callback.cpp
+ Provides printf formatting functions that uses callbacks.
+
+*******************************************************************************/
+
+// Number of bugs seemingly unrelated bugs that have been traced to here:
+// Countless + 2
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static size_t convert_integer(char* destination, uintmax_t value,
+ uintmax_t base, const char* digits)
+{
+ size_t result = 1;
+ uintmax_t copy = value;
+ while ( base <= copy )
+ copy /= base, result++;
+ for ( size_t i = result; i != 0; i-- )
+ destination[i-1] = digits[value % base],
+ value /= base;
+ return result;
+}
+
+static size_t noop_callback(void*, const char*, size_t amount)
+{
+ return amount;
+}
+
+extern "C"
+size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
+ void* user,
+ const char* restrict format,
+ va_list parameters)
+{
+ if ( !callback )
+ callback = noop_callback;
+
+ size_t written = 0;
+ bool rejected_bad_specifier = false;
+
+ while ( *format != '\0' )
+ {
+ if ( *format != '%' )
+ {
+ print_c:
+ size_t amount = 1;
+ while ( format[amount] && format[amount] != '%' )
+ amount++;
+ if ( callback(user, format, amount) != amount )
+ return SIZE_MAX;
+ format += amount;
+ continue;
+ }
+
+ const char* format_begun_at = format;
+
+ if ( *(++format) == '%' )
+ goto print_c;
+
+ if ( rejected_bad_specifier )
+ {
+ incomprehensible_conversion:
+ rejected_bad_specifier = true;
+ unsupported_conversion:
+ format = format_begun_at;
+ goto print_c;
+ }
+
+ bool alternate = false;
+ bool zero_pad = false;
+ bool field_width_is_negative = false;
+ bool prepend_blank_if_positive = false;
+ bool prepend_plus_if_positive = false;
+ bool group_thousands = false;
+ bool alternate_output_digits = false;
+
+ (void) group_thousands;
+ (void) alternate_output_digits;
+
+ do switch ( *format++ )
+ {
+ case '#': alternate = true; continue;
+ case '0': zero_pad = true; continue;
+ case '-': field_width_is_negative = true; continue;
+ case ' ': prepend_blank_if_positive = true; continue;
+ case '+': prepend_plus_if_positive = true; continue;
+ case '\'': group_thousands = true; continue;
+ case 'I': alternate_output_digits = true; continue;
+ default: format--; break;
+ } while ( false );
+
+ int field_width = 0;
+ if ( *format == '*' && (format++, true) )
+ field_width = va_arg(parameters, int);
+ else while ( '0' <= *format && *format <= '9' )
+ field_width = 10 * field_width + *format++ - '0';
+ if ( field_width_is_negative )
+ field_width = -field_width;
+ size_t abs_field_width = (size_t) abs(field_width);
+
+ size_t precision = SIZE_MAX;
+ if ( *format == '.' && (format++, true) )
+ {
+ precision = 0;
+ if ( *format == '*' && (format++, true) )
+ {
+ int int_precision = va_arg(parameters, int);
+ precision = 0 <= int_precision ? (size_t) int_precision : 0;
+ }
+ else
+ {
+ if ( *format == '-' && (format++, true) )
+ while ( '0' <= *format && *format <= '9' )
+ format++;
+ else
+ while ( '0' <= *format && *format <= '9' )
+ precision = 10 * precision + *format++ - '0';
+ }
+ }
+
+ enum length
+ {
+ LENGTH_SHORT_SHORT,
+ LENGTH_SHORT,
+ LENGTH_DEFAULT,
+ LENGTH_LONG,
+ LENGTH_LONG_LONG,
+ LENGTH_LONG_DOUBLE,
+ LENGTH_INTMAX_T,
+ LENGTH_SIZE_T,
+ LENGTH_PTRDIFF_T,
+ };
+
+ struct length_modifer
+ {
+ const char* name;
+ enum length length;
+ };
+
+ struct length_modifer length_modifiers[] =
+ {
+ { "hh", LENGTH_SHORT_SHORT },
+ { "h", LENGTH_SHORT },
+ { "", LENGTH_DEFAULT },
+ { "l", LENGTH_LONG },
+ { "ll", LENGTH_LONG_LONG },
+ { "L", LENGTH_LONG_DOUBLE },
+ { "j", LENGTH_INTMAX_T },
+ { "z", LENGTH_SIZE_T },
+ { "t", LENGTH_PTRDIFF_T },
+ };
+
+ enum length length = LENGTH_DEFAULT;
+ size_t length_length = 0;
+ for ( size_t i = 0;
+ i < sizeof(length_modifiers) / sizeof(length_modifiers[0]);
+ i++ )
+ {
+ size_t name_length = strlen(length_modifiers[i].name);
+ if ( name_length < length_length )
+ continue;
+ if ( strncmp(format, length_modifiers[i].name, name_length) != 0 )
+ continue;
+ length = length_modifiers[i].length;
+ length_length = name_length;
+ }
+
+ format += length_length;
+
+ if ( *format == 'd' || *format == 'i' || *format == 'o' ||
+ *format == 'u' || *format == 'x' || *format == 'X' ||
+ *format == 'p' )
+ {
+ char conversion = *format++;
+
+ bool negative_value = false;
+ uintmax_t value;
+ if ( conversion == 'p' )
+ {
+ value = (uintmax_t) va_arg(parameters, void*);
+ conversion = 'x';
+ alternate = !alternate;
+ }
+ else if ( conversion == 'i' || conversion == 'd' )
+ {
+ intmax_t signed_value;
+ if ( length == LENGTH_SHORT_SHORT )
+ signed_value = va_arg(parameters, int);
+ else if ( length == LENGTH_SHORT )
+ signed_value = va_arg(parameters, int);
+ else if ( length == LENGTH_DEFAULT )
+ signed_value = va_arg(parameters, int);
+ else if ( length == LENGTH_LONG )
+ signed_value = va_arg(parameters, long);
+ else if ( length == LENGTH_LONG_LONG )
+ signed_value = va_arg(parameters, long long);
+ else if ( length == LENGTH_INTMAX_T )
+ signed_value = va_arg(parameters, intmax_t);
+ else if ( length == LENGTH_SIZE_T )
+ signed_value = va_arg(parameters, ssize_t);
+ else if ( length == LENGTH_PTRDIFF_T )
+ signed_value = va_arg(parameters, ptrdiff_t);
+ else
+ goto incomprehensible_conversion;
+ value = (negative_value = signed_value < 0) ?
+ - (uintmax_t) signed_value : signed_value;
+ }
+ else
+ {
+ if ( length == LENGTH_SHORT_SHORT )
+ value = va_arg(parameters, unsigned int);
+ else if ( length == LENGTH_SHORT )
+ value = va_arg(parameters, unsigned int);
+ else if ( length == LENGTH_DEFAULT )
+ value = va_arg(parameters, unsigned int);
+ else if ( length == LENGTH_LONG )
+ value = va_arg(parameters, unsigned long);
+ else if ( length == LENGTH_LONG_LONG )
+ value = va_arg(parameters, unsigned long long);
+ else if ( length == LENGTH_INTMAX_T )
+ value = va_arg(parameters, uintmax_t);
+ else if ( length == LENGTH_SIZE_T )
+ value = va_arg(parameters, size_t);
+ else if ( length == LENGTH_PTRDIFF_T )
+ value = (uintmax_t) va_arg(parameters, ptrdiff_t);
+ else
+ goto incomprehensible_conversion;
+ }
+
+ const char* digits = conversion == 'X' ? "0123456789ABCDEF" :
+ "0123456789abcdef";
+ uintmax_t base = (conversion == 'x' || conversion == 'X') ? 16 :
+ conversion == 'o' ? 8 : 10;
+ char prefix[3];
+ size_t prefix_length = 0;
+ if ( negative_value )
+ prefix[prefix_length++] = '-';
+ else if ( prepend_plus_if_positive )
+ prefix[prefix_length++] = '+';
+ else if ( prepend_blank_if_positive )
+ prefix[prefix_length++] = ' ';
+ if ( alternate && (conversion == 'x' || conversion == 'X') )
+ prefix[prefix_length++] = '0',
+ prefix[prefix_length++] = conversion;
+ if ( alternate && conversion == 'o' && value != 0 )
+ prefix[prefix_length++] = '0';
+
+ char output[sizeof(uintmax_t) * 3];
+ size_t output_length = convert_integer(output, value, base, digits);
+ size_t output_length_with_precision =
+ precision != SIZE_MAX && output_length < precision ?
+ precision :
+ output_length;
+
+ size_t normal_length = prefix_length + output_length;
+ size_t length_with_precision = prefix_length + output_length_with_precision;
+
+ bool use_precision = precision != SIZE_MAX;
+ bool use_zero_pad = zero_pad && 0 <= field_width && !use_precision;
+ bool use_left_pad = !use_zero_pad && 0 <= field_width;
+ bool use_right_pad = !use_zero_pad && field_width < 0;
+
+ char c;
+ if ( use_left_pad )
+ for ( size_t i = length_with_precision; i < abs_field_width; i++ )
+ if ( callback(user, &(c = ' '), 1) != 1 )
+ return SIZE_MAX;
+ else
+ written++;
+ if ( callback(user, prefix, prefix_length) != prefix_length )
+ return SIZE_MAX;
+ written += prefix_length;
+ if ( use_zero_pad )
+ for ( size_t i = normal_length; i < abs_field_width; i++ )
+ if ( callback(user, &(c = '0'), 1) != 1 )
+ return SIZE_MAX;
+ else
+ written++;
+ if ( use_precision )
+ for ( size_t i = normal_length; i < precision; i++ )
+ if ( callback(user, &(c = '0'), 1) != 1 )
+ return SIZE_MAX;
+ else
+ written++;
+ if ( callback(user, output, output_length) != output_length )
+ return SIZE_MAX;
+ written += output_length;
+ if ( use_right_pad )
+ for ( size_t i = length_with_precision; i < abs_field_width; i++ )
+ if ( callback(user, &(c = ' '), 1) != 1 )
+ return SIZE_MAX;
+ else
+ written++;
+ }
+ else if ( *format == 'e' || *format == 'E' ||
+ *format == 'f' || *format == 'F' ||
+ *format == 'g' || *format == 'G' ||
+ *format == 'a' || *format == 'A' )
+ {
+ char conversion = *format++;
+
+ long double value;
+ if ( length == LENGTH_DEFAULT )
+ value = va_arg(parameters, double);
+ else if ( length == LENGTH_LONG_DOUBLE )
+ value = va_arg(parameters, long double);
+ else
+ goto incomprehensible_conversion;
+
+ // TODO: Implement floating-point printing.
+ (void) conversion;
+ (void) value;
+
+ goto unsupported_conversion;
+ }
+ else if ( *format == 'c' && (format++, true) )
+ {
+ char c;
+ if ( length == LENGTH_DEFAULT )
+ c = (char) va_arg(parameters, int);
+ else if ( length == LENGTH_LONG )
+ {
+ // TODO: Implement wide character printing.
+ (void) va_arg(parameters, wint_t);
+ goto unsupported_conversion;
+ }
+ else
+ goto incomprehensible_conversion;
+
+ if ( callback(user, &c, 1) != 1 )
+ return SIZE_MAX;
+ written++;
+ }
+ else if ( *format == 's' && (format++, true) )
+ {
+ const char* string;
+ if ( length == LENGTH_DEFAULT )
+ string = va_arg(parameters, const char*);
+ else if ( length == LENGTH_LONG )
+ {
+ // TODO: Implement wide character string printing.
+ (void) va_arg(parameters, const wchar_t*);
+ goto unsupported_conversion;
+ }
+ else
+ goto incomprehensible_conversion;
+
+ size_t string_length = 0;
+ for ( size_t i = 0; i < precision && string[i]; i++ )
+ string_length++;
+
+ if ( callback(user, string, string_length) != string_length )
+ return SIZE_MAX;
+ written += string_length;
+
+ }
+ else if ( *format == 'n' && (format++, true) )
+ {
+ if ( length == LENGTH_SHORT_SHORT )
+ *va_arg(parameters, signed char*) = (signed char) written;
+ else if ( length == LENGTH_SHORT )
+ *va_arg(parameters, short*) = (short) written;
+ else if ( length == LENGTH_DEFAULT )
+ *va_arg(parameters, int*) = (int) written;
+ else if ( length == LENGTH_LONG )
+ *va_arg(parameters, long*) = (long) written;
+ else if ( length == LENGTH_LONG_LONG )
+ *va_arg(parameters, long long*) = (long long) written;
+ else if ( length == LENGTH_INTMAX_T )
+ *va_arg(parameters, uintmax_t*) = (uintmax_t) written;
+ else if ( length == LENGTH_SIZE_T )
+ *va_arg(parameters, size_t*) = (size_t) written;
+ else if ( length == LENGTH_PTRDIFF_T )
+ *va_arg(parameters, ptrdiff_t*) = (ptrdiff_t) written;
+ else
+ goto incomprehensible_conversion;
+ }
+ else
+ goto incomprehensible_conversion;
+ }
+
+ return written;
+}