summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-06-09 20:21:41 +0200
committerRichard Braun <rbraun@sceen.net>2017-06-09 20:21:41 +0200
commit88a92b367618409cf7c6cd50112e3b81f93d5272 (patch)
treec8f15c6597efaffe69741cb67cf410a1da212b63
parent1d657d440f090ca1e4514f5c88a50be21815eb41 (diff)
kern/fmt new module
This module replaces kern/sprintf, and also implements sscanf functions.
-rw-r--r--Makefrag.am4
-rw-r--r--include/stdio.h10
-rw-r--r--kern/fmt.c1430
-rw-r--r--kern/fmt.h73
-rw-r--r--kern/printf.c5
-rw-r--r--kern/sprintf.c577
-rw-r--r--kern/sprintf.h47
7 files changed, 1516 insertions, 630 deletions
diff --git a/Makefrag.am b/Makefrag.am
index a9f588e6..cab1e950 100644
--- a/Makefrag.am
+++ b/Makefrag.am
@@ -29,6 +29,8 @@ x15_SOURCES += \
kern/error.c \
kern/error.h \
kern/hash.h \
+ kern/fmt.c \
+ kern/fmt.h \
kern/init.h \
kern/intr.c \
kern/intr.h \
@@ -79,8 +81,6 @@ x15_SOURCES += \
kern/spinlock.h \
kern/spinlock_i.h \
kern/spinlock_types.h \
- kern/sprintf.c \
- kern/sprintf.h \
kern/sref.c \
kern/sref.h \
kern/sref_i.h \
diff --git a/include/stdio.h b/include/stdio.h
index e1aa1a33..96c03316 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -18,8 +18,8 @@
#ifndef _STDIO_H
#define _STDIO_H
+#include <kern/fmt.h>
#include <kern/printf.h>
-#include <kern/sprintf.h>
#ifndef EOF
#define EOF (-1)
@@ -31,4 +31,12 @@ char console_getchar(void);
#define getchar console_getchar
#define putchar console_putchar
+#define sprintf fmt_sprintf
+#define snprintf fmt_snprintf
+#define vsprintf fmt_vsprintf
+#define vsnprintf fmt_vsnprintf
+
+#define sscanf fmt_sscanf
+#define vsscanf fmt_vsscanf
+
#endif /* _STDIO_H */
diff --git a/kern/fmt.c b/kern/fmt.c
new file mode 100644
index 00000000..06284de6
--- /dev/null
+++ b/kern/fmt.c
@@ -0,0 +1,1430 @@
+/*
+ * Copyright (c) 2010-2017 Richard Braun.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * Upstream site with license notes :
+ * http://git.sceen.net/rbraun/librbraun.git/
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <kern/error.h>
+#include <kern/fmt.h>
+#include <kern/types.h>
+
+/*
+ * Size for the temporary number buffer. The minimum base is 8 so 3 bits
+ * are consumed per digit. Add one to round up. The conversion algorithm
+ * doesn't use the null byte.
+ */
+#define FMT_MAX_NUM_SIZE (((sizeof(unsigned long long) * CHAR_BIT) / 3) + 1)
+
+/*
+ * Special size for fmt_vsnprintf(), used when the buffer size is unknown.
+ */
+#define FMT_NOLIMIT ((size_t)-1)
+
+/*
+ * Formatting flags.
+ *
+ * FMT_FORMAT_LOWER must be 0x20 as it is OR'd with fmt_digits, eg.
+ * '0': 0x30 | 0x20 => 0x30 ('0')
+ * 'A': 0x41 | 0x20 => 0x61 ('a')
+ */
+#define FMT_FORMAT_ALT_FORM 0x0001 /* "Alternate form" */
+#define FMT_FORMAT_ZERO_PAD 0x0002 /* Zero padding on the left */
+#define FMT_FORMAT_LEFT_JUSTIFY 0x0004 /* Align text on the left */
+#define FMT_FORMAT_BLANK 0x0008 /* Blank space before positive number */
+#define FMT_FORMAT_SIGN 0x0010 /* Always place a sign (either + or -) */
+#define FMT_FORMAT_LOWER 0x0020 /* To lowercase (for %x) */
+#define FMT_FORMAT_CONV_SIGNED 0x0040 /* Format specifies signed conversion */
+#define FMT_FORMAT_DISCARD 0x0080 /* Discard output (scanf) */
+#define FMT_FORMAT_CHECK_WIDTH 0x0100 /* Check field width (scanf) */
+
+enum {
+ FMT_MODIFIER_NONE,
+ FMT_MODIFIER_CHAR,
+ FMT_MODIFIER_SHORT,
+ FMT_MODIFIER_LONG,
+ FMT_MODIFIER_LONGLONG,
+ FMT_MODIFIER_PTR, /* Used only for %p */
+ FMT_MODIFIER_SIZE,
+ FMT_MODIFIER_PTRDIFF,
+};
+
+enum {
+ FMT_SPECIFIER_INVALID,
+ FMT_SPECIFIER_INT,
+ FMT_SPECIFIER_CHAR,
+ FMT_SPECIFIER_STR,
+ FMT_SPECIFIER_NRCHARS,
+ FMT_SPECIFIER_PERCENT,
+};
+
+/*
+ * Note that copies of the original va_list object are made, because va_arg()
+ * may not reliably be used by different callee functions, and despite the
+ * standard explicitely allowing pointers to va_list objects, it's apparently
+ * very difficult for implementations to provide and is best avoided.
+ */
+
+struct fmt_sprintf_state {
+ const char *format;
+ va_list ap;
+ unsigned int flags;
+ int width;
+ int precision;
+ unsigned int modifier;
+ unsigned int specifier;
+ unsigned int base;
+ char *str;
+ char *start;
+ char *end;
+};
+
+struct fmt_sscanf_state {
+ const char *str;
+ const char *start;
+ const char *format;
+ va_list ap;
+ unsigned int flags;
+ int width;
+ int precision;
+ unsigned int modifier;
+ unsigned int specifier;
+ unsigned int base;
+ int nr_convs;
+};
+
+static const char fmt_digits[] = "0123456789ABCDEF";
+
+static char
+fmt_consume(const char **strp)
+{
+ char c;
+
+ c = **strp;
+ (*strp)++;
+ return c;
+}
+
+static void
+fmt_restore(const char **strp)
+{
+ (*strp)--;
+}
+
+static void
+fmt_vsnprintf_produce(char **strp, char *end, char c)
+{
+ if (*strp < end) {
+ **strp = c;
+ }
+
+ (*strp)++;
+}
+
+static bool
+fmt_isdigit(char c)
+{
+ return (c >= '0') && (c <= '9');
+}
+
+static bool
+fmt_isxdigit(char c)
+{
+ return fmt_isdigit(c)
+ || ((c >= 'a') && (c <= 'f'))
+ || ((c >= 'A') && (c <= 'F'));
+}
+
+static void
+fmt_sprintf_state_init(struct fmt_sprintf_state *state,
+ char *str, size_t size,
+ const char *format, va_list ap)
+{
+ state->format = format;
+ va_copy(state->ap, ap);
+ state->str = str;
+ state->start = str;
+
+ if (size == 0) {
+ state->end = NULL;
+ } else if (size == FMT_NOLIMIT) {
+ state->end = (char *)-1;
+ } else {
+ state->end = state->start + size - 1;
+ }
+}
+
+static int
+fmt_sprintf_state_finalize(struct fmt_sprintf_state *state)
+{
+ va_end(state->ap);
+
+ if (state->str < state->end) {
+ *state->str = '\0';
+ } else if (state->end != NULL) {
+ *state->end = '\0';
+ }
+
+ return state->str - state->start;
+}
+
+static char
+fmt_sprintf_state_consume_format(struct fmt_sprintf_state *state)
+{
+ return fmt_consume(&state->format);
+}
+
+static void
+fmt_sprintf_state_restore_format(struct fmt_sprintf_state *state)
+{
+ fmt_restore(&state->format);
+}
+
+static void
+fmt_sprintf_state_consume_flags(struct fmt_sprintf_state *state)
+{
+ bool found;
+ char c;
+
+ found = true;
+ state->flags = 0;
+
+ do {
+ c = fmt_sprintf_state_consume_format(state);
+
+ switch (c) {
+ case '#':
+ state->flags |= FMT_FORMAT_ALT_FORM;
+ break;
+ case '0':
+ state->flags |= FMT_FORMAT_ZERO_PAD;
+ break;
+ case '-':
+ state->flags |= FMT_FORMAT_LEFT_JUSTIFY;
+ break;
+ case ' ':
+ state->flags |= FMT_FORMAT_BLANK;
+ break;
+ case '+':
+ state->flags |= FMT_FORMAT_SIGN;
+ break;
+ default:
+ found = false;
+ break;
+ }
+ } while (found);
+
+ fmt_sprintf_state_restore_format(state);
+}
+
+static void
+fmt_sprintf_state_consume_width(struct fmt_sprintf_state *state)
+{
+ char c;
+
+ c = fmt_sprintf_state_consume_format(state);
+
+ if (fmt_isdigit(c)) {
+ state->width = 0;
+
+ do {
+ state->width = state->width * 10 + (c - '0');
+ c = fmt_sprintf_state_consume_format(state);
+ } while (fmt_isdigit(c));
+
+ fmt_sprintf_state_restore_format(state);
+ } else if (c == '*') {
+ state->width = va_arg(state->ap, int);
+
+ if (state->width < 0) {
+ state->flags |= FMT_FORMAT_LEFT_JUSTIFY;
+ state->width = -state->width;
+ }
+ } else {
+ state->width = 0;
+ fmt_sprintf_state_restore_format(state);
+ }
+}
+
+static void
+fmt_sprintf_state_consume_precision(struct fmt_sprintf_state *state)
+{
+ char c;
+
+ c = fmt_sprintf_state_consume_format(state);
+
+ if (c == '.') {
+ c = fmt_sprintf_state_consume_format(state);
+
+ if (fmt_isdigit(c)) {
+ state->precision = 0;
+
+ do {
+ state->precision = state->precision * 10 + (c - '0');
+ c = fmt_sprintf_state_consume_format(state);
+ } while (fmt_isdigit(c));
+
+ fmt_sprintf_state_restore_format(state);
+ } else if (c == '*') {
+ state->precision = va_arg(state->ap, int);
+
+ if (state->precision < 0) {
+ state->precision = 0;
+ }
+ } else {
+ state->precision = 0;
+ fmt_sprintf_state_restore_format(state);
+ }
+ } else {
+ /* precision is >= 0 only if explicit */
+ state->precision = -1;
+ fmt_sprintf_state_restore_format(state);
+ }
+}
+
+static void
+fmt_sprintf_state_consume_modifier(struct fmt_sprintf_state *state)
+{
+ char c, c2;
+
+ c = fmt_sprintf_state_consume_format(state);
+
+ switch (c) {
+ case 'h':
+ case 'l':
+ c2 = fmt_sprintf_state_consume_format(state);
+
+ if (c == c2) {
+ state->modifier = (c == 'h') ? FMT_MODIFIER_CHAR
+ : FMT_MODIFIER_LONGLONG;
+ } else {
+ state->modifier = (c == 'h') ? FMT_MODIFIER_SHORT
+ : FMT_MODIFIER_LONG;
+ fmt_sprintf_state_restore_format(state);
+ }
+
+ break;
+ case 'z':
+ state->modifier = FMT_MODIFIER_SIZE;
+ case 't':
+ state->modifier = FMT_MODIFIER_PTRDIFF;
+ break;
+ default:
+ state->modifier = FMT_MODIFIER_NONE;
+ fmt_sprintf_state_restore_format(state);
+ break;
+ }
+}
+
+static void
+fmt_sprintf_state_consume_specifier(struct fmt_sprintf_state *state)
+{
+ char c;
+
+ c = fmt_sprintf_state_consume_format(state);
+
+ switch (c) {
+ case 'd':
+ case 'i':
+ state->flags |= FMT_FORMAT_CONV_SIGNED;
+ case 'u':
+ state->base = 10;
+ state->specifier = FMT_SPECIFIER_INT;
+ break;
+ case 'o':
+ state->base = 8;
+ state->specifier = FMT_SPECIFIER_INT;
+ break;
+ case 'p':
+ state->flags |= FMT_FORMAT_ALT_FORM;
+ state->modifier = FMT_MODIFIER_PTR;
+ case 'x':
+ state->flags |= FMT_FORMAT_LOWER;
+ case 'X':
+ state->base = 16;
+ state->specifier = FMT_SPECIFIER_INT;
+ break;
+ case 'c':
+ state->specifier = FMT_SPECIFIER_CHAR;
+ break;
+ case 's':
+ state->specifier = FMT_SPECIFIER_STR;
+ break;
+ case 'n':
+ state->specifier = FMT_SPECIFIER_NRCHARS;
+ break;
+ case '%':
+ state->specifier = FMT_SPECIFIER_PERCENT;
+ break;
+ default:
+ state->specifier = FMT_SPECIFIER_INVALID;
+ fmt_sprintf_state_restore_format(state);
+ break;
+ }
+}
+
+static void
+fmt_sprintf_state_produce_raw_char(struct fmt_sprintf_state *state, char c)
+{
+ fmt_vsnprintf_produce(&state->str, state->end, c);
+}
+
+static int
+fmt_sprintf_state_consume(struct fmt_sprintf_state *state)
+{
+ char c;
+
+ c = fmt_consume(&state->format);
+
+ if (c == '\0') {
+ return ERROR_IO;
+ }
+
+ if (c != '%') {
+ fmt_sprintf_state_produce_raw_char(state, c);
+ return ERROR_AGAIN;
+ }
+
+ fmt_sprintf_state_consume_flags(state);
+ fmt_sprintf_state_consume_width(state);
+ fmt_sprintf_state_consume_precision(state);
+ fmt_sprintf_state_consume_modifier(state);
+ fmt_sprintf_state_consume_specifier(state);
+ return 0;
+}
+
+static void
+fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state)
+{
+ char c, sign, tmp[FMT_MAX_NUM_SIZE];
+ unsigned int r, mask, shift;
+ unsigned long long n;
+ int i;
+
+ switch (state->modifier) {
+ case FMT_MODIFIER_CHAR:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ n = (signed char)va_arg(state->ap, int);
+ } else {
+ n = (unsigned char)va_arg(state->ap, int);
+ }
+
+ break;
+ case FMT_MODIFIER_SHORT:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ n = (short)va_arg(state->ap, int);
+ } else {
+ n = (unsigned short)va_arg(state->ap, int);
+ }
+
+ break;
+ case FMT_MODIFIER_LONG:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ n = va_arg(state->ap, long);
+ } else {
+ n = va_arg(state->ap, unsigned long);
+ }
+
+ break;
+ case FMT_MODIFIER_LONGLONG:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ n = va_arg(state->ap, long long);
+ } else {
+ n = va_arg(state->ap, unsigned long long);
+ }
+
+ break;
+ case FMT_MODIFIER_PTR:
+ n = (uintptr_t)va_arg(state->ap, void *);
+ break;
+ case FMT_MODIFIER_SIZE:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ n = va_arg(state->ap, ssize_t);
+ } else {
+ n = va_arg(state->ap, size_t);
+ }
+
+ break;
+ case FMT_MODIFIER_PTRDIFF:
+ n = va_arg(state->ap, ptrdiff_t);
+ break;
+ default:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ n = va_arg(state->ap, int);
+ } else {
+ n = va_arg(state->ap, unsigned int);
+ }
+
+ break;
+ }
+
+ if ((state->flags & FMT_FORMAT_LEFT_JUSTIFY) || (state->precision >= 0)) {
+ state->flags &= ~FMT_FORMAT_ZERO_PAD;
+ }
+
+ sign = '\0';
+
+ if (state->flags & FMT_FORMAT_ALT_FORM) {
+ /* '0' for octal */
+ state->width--;
+
+ /* '0x' or '0X' for hexadecimal */
+ if (state->base == 16) {
+ state->width--;
+ }
+ } else if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ if ((long long)n < 0) {
+ sign = '-';
+ state->width--;
+ n = -(long long)n;
+ } else if (state->flags & FMT_FORMAT_SIGN) {
+ /* FMT_FORMAT_SIGN must precede FMT_FORMAT_BLANK. */
+ sign = '+';
+ state->width--;
+ } else if (state->flags & FMT_FORMAT_BLANK) {
+ sign = ' ';
+ state->width--;
+ }
+ }
+
+ /* Conversion, in reverse order */
+
+ i = 0;
+
+ if (n == 0) {
+ if (state->precision != 0) {
+ tmp[i++] = '0';
+ }
+ } else if (state->base == 10) {
+ /*
+ * Try to avoid 64 bits operations if the processor doesn't
+ * support them. Note that even when using modulus and
+ * division operators close to each other, the compiler may
+ * forge two functions calls to compute the quotient and the
+ * remainder, whereas processor instructions are generally
+ * correctly used once, giving both results at once, through
+ * plain or reciprocal division.
+ */
+#ifndef __LP64__
+ if (state->modifier == FMT_MODIFIER_LONGLONG) {
+#endif /* __LP64__ */
+ do {
+ r = n % 10;
+ n /= 10;
+ tmp[i++] = fmt_digits[r];
+ } while (n != 0);
+#ifndef __LP64__
+ } else {
+ unsigned long m;
+
+ m = (unsigned long)n;
+
+ do {
+ r = m % 10;
+ m /= 10;
+ tmp[i++] = fmt_digits[r];
+ } while (m != 0);
+ }
+#endif /* __LP64__ */
+ } else {
+ mask = state->base - 1;
+ shift = (state->base == 8) ? 3 : 4;
+
+ do {
+ r = n & mask;
+ n >>= shift;
+ tmp[i++] = fmt_digits[r] | (state->flags & FMT_FORMAT_LOWER);
+ } while (n != 0);
+ }
+
+ if (i > state->precision) {
+ state->precision = i;
+ }
+
+ state->width -= state->precision;
+
+ if (!(state->flags & (FMT_FORMAT_LEFT_JUSTIFY | FMT_FORMAT_ZERO_PAD))) {
+ while (state->width-- > 0) {
+ fmt_sprintf_state_produce_raw_char(state, ' ');
+ }
+ }
+
+ if (state->flags & FMT_FORMAT_ALT_FORM) {
+ fmt_sprintf_state_produce_raw_char(state, '0');
+
+ if (state->base == 16) {
+ c = 'X' | (state->flags & FMT_FORMAT_LOWER);
+ fmt_sprintf_state_produce_raw_char(state, c);
+ }
+ } else if (sign != '\0') {
+ fmt_sprintf_state_produce_raw_char(state, sign);
+ }
+
+ if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) {
+ c = (state->flags & FMT_FORMAT_ZERO_PAD) ? '0' : ' ';
+
+ while (state->width-- > 0) {
+ fmt_sprintf_state_produce_raw_char(state, c);
+ }
+ }
+
+ while (i < state->precision--) {
+ fmt_sprintf_state_produce_raw_char(state, '0');
+ }
+
+ while (i-- > 0) {
+ fmt_sprintf_state_produce_raw_char(state, tmp[i]);
+ }
+
+ while (state->width-- > 0) {
+ fmt_sprintf_state_produce_raw_char(state, ' ');
+ }
+}
+
+static void
+fmt_sprintf_state_produce_char(struct fmt_sprintf_state *state)
+{
+ char c;
+
+ c = va_arg(state->ap, int);
+
+ if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) {
+ while (--state->width > 0) {
+ fmt_sprintf_state_produce_raw_char(state, ' ');
+ }
+ }
+
+ fmt_sprintf_state_produce_raw_char(state, c);
+
+ while (--state->width > 0) {
+ fmt_sprintf_state_produce_raw_char(state, ' ');
+ }
+}
+
+static void
+fmt_sprintf_state_produce_str(struct fmt_sprintf_state *state)
+{
+ int i, len;
+ char *s;
+
+ s = va_arg(state->ap, char *);
+
+ if (s == NULL) {
+ s = "(null)";
+ }
+
+ len = 0;
+
+ for (len = 0; s[len] != '\0'; len++)
+ if (len == state->precision) {
+ break;
+ }
+
+ if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) {
+ while (len < state->width) {
+ state->width--;
+ fmt_sprintf_state_produce_raw_char(state, ' ');
+ }
+ }
+
+ for (i = 0; i < len; i++) {
+ fmt_sprintf_state_produce_raw_char(state, *s);
+ s++;
+ }
+
+ while (len < state->width) {
+ state->width--;
+ fmt_sprintf_state_produce_raw_char(state, ' ');
+ }
+}
+
+static void
+fmt_sprintf_state_produce_nrchars(struct fmt_sprintf_state *state)
+{
+ if (state->modifier == FMT_MODIFIER_CHAR) {
+ signed char *ptr = va_arg(state->ap, signed char *);
+ *ptr = state->str - state->start;
+ } else if (state->modifier == FMT_MODIFIER_SHORT) {
+ short *ptr = va_arg(state->ap, short *);
+ *ptr = state->str - state->start;
+ } else if (state->modifier == FMT_MODIFIER_LONG) {
+ long *ptr = va_arg(state->ap, long *);
+ *ptr = state->str - state->start;
+ } else if (state->modifier == FMT_MODIFIER_LONGLONG) {
+ long long *ptr = va_arg(state->ap, long long *);
+ *ptr = state->str - state->start;
+ } else if (state->modifier == FMT_MODIFIER_SIZE) {
+ ssize_t *ptr = va_arg(state->ap, ssize_t *);
+ *ptr = state->str - state->start;
+ } else if (state->modifier == FMT_MODIFIER_PTRDIFF) {
+ ptrdiff_t *ptr = va_arg(state->ap, ptrdiff_t *);
+ *ptr = state->str - state->start;
+ } else {
+ int *ptr = va_arg(state->ap, int *);
+ *ptr = state->str - state->start;
+ }
+}
+
+static void
+fmt_sprintf_state_produce(struct fmt_sprintf_state *state)
+{
+ switch (state->specifier) {
+ case FMT_SPECIFIER_INT:
+ fmt_sprintf_state_produce_int(state);
+ break;
+ case FMT_SPECIFIER_CHAR:
+ fmt_sprintf_state_produce_char(state);
+ break;
+ case FMT_SPECIFIER_STR:
+ fmt_sprintf_state_produce_str(state);
+ break;
+ case FMT_SPECIFIER_NRCHARS:
+ fmt_sprintf_state_produce_nrchars(state);
+ break;
+ case FMT_SPECIFIER_PERCENT:
+ case FMT_SPECIFIER_INVALID:
+ fmt_sprintf_state_produce_raw_char(state, '%');
+ break;
+ }
+}
+
+int
+fmt_sprintf(char *str, const char *format, ...)
+{
+ va_list ap;
+ int length;
+
+ va_start(ap, format);
+ length = fmt_vsprintf(str, format, ap);
+ va_end(ap);
+
+ return length;
+}
+
+int
+fmt_vsprintf(char *str, const char *format, va_list ap)
+{
+ return fmt_vsnprintf(str, FMT_NOLIMIT, format, ap);
+}
+
+int
+fmt_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int length;
+
+ va_start(ap, format);
+ length = fmt_vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ return length;
+}
+
+int
+fmt_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+{
+ struct fmt_sprintf_state state;
+ int error;
+
+ fmt_sprintf_state_init(&state, str, size, format, ap);
+
+ for (;;) {
+ error = fmt_sprintf_state_consume(&state);
+
+ if (error == ERROR_AGAIN) {
+ continue;
+ } else if (error) {
+ break;
+ }
+
+ fmt_sprintf_state_produce(&state);
+ }
+
+ return fmt_sprintf_state_finalize(&state);
+}
+
+static char
+fmt_atoi(char c)
+{
+ assert(fmt_isxdigit(c));
+
+ if (fmt_isdigit(c)) {
+ return c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ return 10 + (c - 'a');
+ } else {
+ return 10 + (c - 'A');
+ }
+}
+
+static bool
+fmt_isspace(char c)
+{
+ if (c == ' ') {
+ return true;
+ }
+
+ if ((c >= '\t') && (c <= '\f')) {
+ return true;
+ }
+
+ return false;
+}
+
+static void
+fmt_skip(const char **strp)
+{
+ while (fmt_isspace(**strp)) {
+ (*strp)++;
+ }
+}
+
+static void
+fmt_sscanf_state_init(struct fmt_sscanf_state *state, const char *str,
+ const char *format, va_list ap)
+{
+ state->str = str;
+ state->start = str;
+ state->format = format;
+ state->flags = 0;
+ state->width = 0;
+ va_copy(state->ap, ap);
+ state->nr_convs = 0;
+}
+
+static int
+fmt_sscanf_state_finalize(struct fmt_sscanf_state *state)
+{
+ va_end(state->ap);
+ return state->nr_convs;
+}
+
+static void
+fmt_sscanf_state_report_conv(struct fmt_sscanf_state *state)
+{
+ if (state->nr_convs == EOF) {
+ state->nr_convs = 1;
+ return;
+ }
+
+ state->nr_convs++;
+}
+
+static void
+fmt_sscanf_state_report_error(struct fmt_sscanf_state *state)
+{
+ if (state->nr_convs != 0) {
+ return;
+ }
+
+ state->nr_convs = EOF;
+}
+
+static void
+fmt_sscanf_state_skip_space(struct fmt_sscanf_state *state)
+{
+ fmt_skip(&state->str);
+}
+
+static char
+fmt_sscanf_state_consume_string(struct fmt_sscanf_state *state)
+{
+ char c;
+
+ c = fmt_consume(&state->str);
+
+ if (state->flags & FMT_FORMAT_CHECK_WIDTH) {
+ if (state->width == 0) {
+ c = EOF;
+ } else {
+ state->width--;
+ }
+ }
+
+ return c;
+}
+
+static void
+fmt_sscanf_state_restore_string(struct fmt_sscanf_state *state)
+{
+ fmt_restore(&state->str);
+}
+
+static char
+fmt_sscanf_state_consume_format(struct fmt_sscanf_state *state)
+{
+ return fmt_consume(&state->format);
+}
+
+static void
+fmt_sscanf_state_restore_format(struct fmt_sscanf_state *state)
+{
+ fmt_restore(&state->format);
+}
+
+static void
+fmt_sscanf_state_consume_flags(struct fmt_sscanf_state *state)
+{
+ bool found;
+ char c;
+
+ found = true;
+ state->flags = 0;
+
+ do {
+ c = fmt_sscanf_state_consume_format(state);
+
+ switch (c) {
+ case '*':
+ state->flags |= FMT_FORMAT_DISCARD;
+ break;
+ default:
+ found = false;
+ break;
+ }
+ } while (found);
+
+ fmt_sscanf_state_restore_format(state);
+}
+
+static void
+fmt_sscanf_state_consume_width(struct fmt_sscanf_state *state)
+{
+ char c;
+
+ state->width = 0;
+
+ for (;;) {
+ c = fmt_sscanf_state_consume_format(state);
+
+ if (!fmt_isdigit(c)) {
+ break;
+ }
+
+ state->width = state->width * 10 + (c - '0');
+ }
+
+ if (state->width != 0) {
+ state->flags |= FMT_FORMAT_CHECK_WIDTH;
+ }
+
+ fmt_sscanf_state_restore_format(state);
+}
+
+static void
+fmt_sscanf_state_consume_modifier(struct fmt_sscanf_state *state)
+{
+ char c, c2;
+
+ c = fmt_sscanf_state_consume_format(state);
+
+ switch (c) {
+ case 'h':
+ case 'l':
+ c2 = fmt_sscanf_state_consume_format(state);
+
+ if (c == c2) {
+ state->modifier = (c == 'h') ? FMT_MODIFIER_CHAR
+ : FMT_MODIFIER_LONGLONG;
+ } else {
+ state->modifier = (c == 'h') ? FMT_MODIFIER_SHORT
+ : FMT_MODIFIER_LONG;
+ fmt_sscanf_state_restore_format(state);
+ }
+
+ break;
+ case 'z':
+ state->modifier = FMT_MODIFIER_SIZE;
+ break;
+ case 't':
+ state->modifier = FMT_MODIFIER_PTRDIFF;
+ break;
+ default:
+ state->modifier = FMT_MODIFIER_NONE;
+ fmt_sscanf_state_restore_format(state);
+ break;
+ }
+}
+
+static void
+fmt_sscanf_state_consume_specifier(struct fmt_sscanf_state *state)
+{
+ char c;
+
+ c = fmt_sscanf_state_consume_format(state);
+
+ switch (c) {
+ case 'i':
+ state->base = 0;
+ state->flags |= FMT_FORMAT_CONV_SIGNED;
+ state->specifier = FMT_SPECIFIER_INT;
+ break;
+ case 'd':
+ state->flags |= FMT_FORMAT_CONV_SIGNED;
+ case 'u':
+ state->base = 10;
+ state->specifier = FMT_SPECIFIER_INT;
+ break;
+ case 'o':
+ state->base = 8;
+ state->specifier = FMT_SPECIFIER_INT;
+ break;
+ case 'p':
+ state->modifier = FMT_MODIFIER_PTR;
+ case 'x':
+ case 'X':
+ state->base = 16;
+ state->specifier = FMT_SPECIFIER_INT;
+ break;
+ case 'c':
+ state->specifier = FMT_SPECIFIER_CHAR;
+ break;
+ case 's':
+ state->specifier = FMT_SPECIFIER_STR;
+ break;
+ case 'n':
+ state->specifier = FMT_SPECIFIER_NRCHARS;
+ break;
+ case '%':
+ state->specifier = FMT_SPECIFIER_PERCENT;
+ break;
+ default:
+ state->specifier = FMT_SPECIFIER_INVALID;
+ fmt_sscanf_state_restore_format(state);
+ break;
+ }
+}
+
+static int
+fmt_sscanf_state_discard_char(struct fmt_sscanf_state *state, char c)
+{
+ char c2;
+
+ if (fmt_isspace(c)) {
+ fmt_sscanf_state_skip_space(state);
+ return 0;
+ }
+
+ c2 = fmt_sscanf_state_consume_string(state);
+
+ if (c != c2) {
+ if ((c2 == '\0') && (state->nr_convs == 0)) {
+ state->nr_convs = EOF;
+ }
+
+ return ERROR_INVAL;
+ }
+
+ return 0;
+}
+
+static int
+fmt_sscanf_state_consume(struct fmt_sscanf_state *state)
+{
+ int error;
+ char c;
+
+ state->flags = 0;
+
+ c = fmt_consume(&state->format);
+
+ if (c == '\0') {
+ return ERROR_IO;
+ }
+
+ if (c != '%') {
+ error = fmt_sscanf_state_discard_char(state, c);
+
+ if (error) {
+ return error;
+ }
+
+ return ERROR_AGAIN;
+ }
+
+ fmt_sscanf_state_consume_flags(state);
+ fmt_sscanf_state_consume_width(state);
+ fmt_sscanf_state_consume_modifier(state);
+ fmt_sscanf_state_consume_specifier(state);
+ return 0;
+}
+
+static int
+fmt_sscanf_state_produce_int(struct fmt_sscanf_state *state)
+{
+ unsigned long long n, m, tmp;
+ char c, buf[FMT_MAX_NUM_SIZE];
+ bool negative;
+ size_t i;
+
+ negative = 0;
+
+ fmt_sscanf_state_skip_space(state);
+ c = fmt_sscanf_state_consume_string(state);
+
+ if (c == '-') {
+ negative = true;
+ c = fmt_sscanf_state_consume_string(state);
+ }
+
+ if (c == '0') {
+ c = fmt_sscanf_state_consume_string(state);
+
+ if ((c == 'x') || (c == 'X')) {
+ if (state->base == 0) {
+ state->base = 16;
+ }
+
+ if (state->base == 16) {
+ c = fmt_sscanf_state_consume_string(state);
+ } else {
+ fmt_sscanf_state_restore_string(state);
+ }
+ } else {
+ if (state->base == 0) {
+ state->base = 8;
+ }
+ }
+ }
+
+ i = 0;
+
+ while (c != '\0') {
+ if (state->base == 8) {
+ if (!((c >= '0') && (c <= '7'))) {
+ break;
+ }
+ } else if (state->base == 16) {
+ if (!fmt_isxdigit(c)) {
+ break;
+ }
+ } else {
+ if (!fmt_isdigit(c)) {
+ break;
+ }
+ }
+
+ /* XXX Standard sscanf provides no way to cleanly handle overflows */
+ if (i < (ARRAY_SIZE(buf) - 1)) {
+ buf[i] = c;
+ } else if (i == (ARRAY_SIZE(buf) - 1)) {
+ strcpy(buf, "1");
+ negative = true;
+ }
+
+ i++;
+ c = fmt_sscanf_state_consume_string(state);
+ }
+
+ fmt_sscanf_state_restore_string(state);
+
+ if (state->flags & FMT_FORMAT_DISCARD) {
+ return 0;
+ }
+
+ if (i == 0) {
+ if (c == '\0') {
+ fmt_sscanf_state_report_error(state);
+ return ERROR_INVAL;
+ }
+
+ buf[0] = '0';
+ i = 1;
+ }
+
+ if (i < ARRAY_SIZE(buf)) {
+ buf[i] = '\0';
+ i--;
+ } else {
+ i = strlen(buf) - 1;
+ }
+
+ n = 0;
+
+#ifndef __LP64__
+ if (state->modifier == FMT_MODIFIER_LONGLONG) {
+#endif /* __LP64__ */
+ m = 1;
+ tmp = 0;
+
+ while (&buf[i] >= buf) {
+ tmp += fmt_atoi(buf[i]) * m;
+
+ if (tmp < n) {
+ n = 1;
+ negative = true;
+ break;
+ }
+
+ n = tmp;
+ m *= state->base;
+ i--;
+ }
+#ifndef __LP64__
+ } else {
+ unsigned long _n, _m, _tmp;
+
+ _n = 0;
+ _m = 1;
+ _tmp = 0;
+
+ while (&buf[i] >= buf) {
+ _tmp += fmt_atoi(buf[i]) * _m;
+
+ if (_tmp < _n) {
+ _n = 1;
+ negative = true;
+ break;
+ }
+
+ _n = _tmp;
+ _m *= state->base;
+ i--;
+ }
+
+ n = _n;
+ }
+#endif /* __LP64__ */
+
+ if (negative) {
+ n = -n;
+ }
+
+ switch (state->modifier) {
+ case FMT_MODIFIER_CHAR:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ *va_arg(state->ap, char *) = n;
+ } else {
+ *va_arg(state->ap, unsigned char *) = n;
+ }
+
+ break;
+ case FMT_MODIFIER_SHORT:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ *va_arg(state->ap, short *) = n;
+ } else {
+ *va_arg(state->ap, unsigned short *) = n;
+ }
+
+ break;
+ case FMT_MODIFIER_LONG:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ *va_arg(state->ap, long *) = n;
+ } else {
+ *va_arg(state->ap, unsigned long *) = n;
+ }
+
+ break;
+ case FMT_MODIFIER_LONGLONG:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ *va_arg(state->ap, long long *) = n;
+ } else {
+ *va_arg(state->ap, unsigned long long *) = n;
+ }
+
+ break;
+ case FMT_MODIFIER_PTR:
+ *va_arg(state->ap, uintptr_t *) = n;
+ break;
+ case FMT_MODIFIER_SIZE:
+ *va_arg(state->ap, size_t *) = n;
+ break;
+ case FMT_MODIFIER_PTRDIFF:
+ *va_arg(state->ap, ptrdiff_t *) = n;
+ break;
+ default:
+ if (state->flags & FMT_FORMAT_CONV_SIGNED) {
+ *va_arg(state->ap, int *) = n;
+ } else {
+ *va_arg(state->ap, unsigned int *) = n;
+ }
+ }
+
+ fmt_sscanf_state_report_conv(state);
+ return 0;
+}
+
+static int
+fmt_sscanf_state_produce_char(struct fmt_sscanf_state *state)
+{
+ char c, *dest;
+ int i, width;
+
+ if (state->flags & FMT_FORMAT_DISCARD) {
+ dest = NULL;
+ } else {
+ dest = va_arg(state->ap, char *);
+ }
+
+ if (state->flags & FMT_FORMAT_CHECK_WIDTH) {
+ width = state->width;
+ } else {
+ width = 1;
+ }
+
+ for (i = 0; i < width; i++) {
+ c = fmt_sscanf_state_consume_string(state);
+
+ if ((c == '\0') || (c == EOF)) {
+ break;
+ }
+
+ if (dest != NULL) {
+ *dest = c;
+ dest++;
+ }
+ }
+
+ if (i < width) {
+ fmt_sscanf_state_restore_string(state);
+ }
+
+ if (dest != NULL) {
+ fmt_sscanf_state_report_conv(state);
+ }
+
+ return 0;
+}
+
+static int
+fmt_sscanf_state_produce_str(struct fmt_sscanf_state *state)
+{
+ const char *orig;
+ char c, *dest;
+
+ orig = state->str;
+
+ fmt_sscanf_state_skip_space(state);
+
+ if (state->flags & FMT_FORMAT_DISCARD) {
+ dest = NULL;
+ } else {
+ dest = va_arg(state->ap, char *);
+ }
+
+ for (;;) {
+ c = fmt_sscanf_state_consume_string(state);
+
+ if ((c == '\0') || (c == ' ') || (c == EOF)) {
+ break;
+ }
+
+ if (dest != NULL) {
+ *dest = c;
+ dest++;
+ }
+ }
+
+ fmt_sscanf_state_restore_string(state);
+
+ if (state->str == orig) {
+ fmt_sscanf_state_report_error(state);
+ return ERROR_INVAL;
+ }
+
+ if (dest != NULL) {
+ *dest = '\0';
+ fmt_sscanf_state_report_conv(state);
+ }
+
+ return 0;
+}
+
+static int
+fmt_sscanf_state_produce_nrchars(struct fmt_sscanf_state *state)
+{
+ *va_arg(state->ap, int *) = state->str - state->start;
+ return 0;
+}
+
+static int
+fmt_sscanf_state_produce(struct fmt_sscanf_state *state)
+{
+ switch (state->specifier) {
+ case FMT_SPECIFIER_INT:
+ return fmt_sscanf_state_produce_int(state);
+ case FMT_SPECIFIER_CHAR:
+ return fmt_sscanf_state_produce_char(state);
+ case FMT_SPECIFIER_STR:
+ return fmt_sscanf_state_produce_str(state);
+ case FMT_SPECIFIER_NRCHARS:
+ return fmt_sscanf_state_produce_nrchars(state);
+ case FMT_SPECIFIER_PERCENT:
+ fmt_sscanf_state_skip_space(state);
+ return fmt_sscanf_state_discard_char(state, '%');
+ default:
+ fmt_sscanf_state_report_error(state);
+ return ERROR_INVAL;
+ }
+}
+
+int
+fmt_sscanf(const char *str, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = fmt_vsscanf(str, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+int
+fmt_vsscanf(const char *str, const char *format, va_list ap)
+{
+ struct fmt_sscanf_state state;
+ int error;
+
+ fmt_sscanf_state_init(&state, str, format, ap);
+
+ for (;;) {
+ error = fmt_sscanf_state_consume(&state);
+
+ if (error == ERROR_AGAIN) {
+ continue;
+ } else if (error) {
+ break;
+ }
+
+ error = fmt_sscanf_state_produce(&state);
+
+ if (error) {
+ break;
+ }
+ }
+
+ return fmt_sscanf_state_finalize(&state);
+}
diff --git a/kern/fmt.h b/kern/fmt.h
new file mode 100644
index 00000000..a339ed46
--- /dev/null
+++ b/kern/fmt.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010-2017 Richard Braun.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * Formatted string functions.
+ *
+ * The functions provided by this module implement a subset of the standard
+ * sprintf- and sscanf-like functions.
+ *
+ * sprintf:
+ * - flags: # 0 - ' ' (space) +
+ * - field width is supported
+ * - precision is supported
+ *
+ * sscanf:
+ * - flags: *
+ * - field width is supported
+ *
+ * common:
+ * - modifiers: hh h l ll z t
+ * - specifiers: d i o u x X c s p n %
+ *
+ *
+ * Upstream site with license notes :
+ * http://git.sceen.net/rbraun/librbraun.git/
+ */
+
+#ifndef _FMT_H
+#define _FMT_H
+
+#include <stdarg.h>
+#include <stddef.h>
+
+int fmt_sprintf(char *str, const char *format, ...)
+ __attribute__((format(printf, 2, 3)));
+
+int fmt_vsprintf(char *str, const char *format, va_list ap)
+ __attribute__((format(printf, 2, 0)));
+
+int fmt_snprintf(char *str, size_t size, const char *format, ...)
+ __attribute__((format(printf, 3, 4)));
+
+int fmt_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+ __attribute__((format(printf, 3, 0)));
+
+int fmt_sscanf(const char *str, const char *format, ...)
+ __attribute__((format(scanf, 2, 3)));
+
+int fmt_vsscanf(const char *str, const char *format, va_list ap)
+ __attribute__((format(scanf, 2, 0)));
+
+#endif /* _FMT_H */
diff --git a/kern/printf.c b/kern/printf.c
index f657fdfc..64324a04 100644
--- a/kern/printf.c
+++ b/kern/printf.c
@@ -15,9 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-
#include <kern/console.h>
+#include <kern/fmt.h>
#include <kern/spinlock.h>
#include <machine/cpu.h>
@@ -51,7 +50,7 @@ vprintf(const char *format, va_list ap)
spinlock_lock_intr_save(&printf_lock, &flags);
- length = vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap);
+ length = fmt_vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap);
for (ptr = printf_buffer; *ptr != '\0'; ptr++) {
console_putchar(*ptr);
diff --git a/kern/sprintf.c b/kern/sprintf.c
deleted file mode 100644
index f6346e99..00000000
--- a/kern/sprintf.c
+++ /dev/null
@@ -1,577 +0,0 @@
-/*
- * Copyright (c) 2010 Richard Braun.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <limits.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include <kern/types.h>
-
-/*
- * Formatting flags.
- *
- * FORMAT_LOWER must be 0x20 as it is OR'd with digits, eg.
- * '0': 0x30 | 0x20 => 0x30 ('0')
- * 'A': 0x41 | 0x20 => 0x61 ('a')
- */
-#define SPRINTF_FORMAT_ALT_FORM 0x01
-#define SPRINTF_FORMAT_ZERO_PAD 0x02
-#define SPRINTF_FORMAT_LEFT_JUSTIFY 0x04
-#define SPRINTF_FORMAT_BLANK 0x08
-#define SPRINTF_FORMAT_SIGN 0x10
-#define SPRINTF_FORMAT_LOWER 0x20
-#define SPRINTF_FORMAT_CONV_SIGNED 0x40
-
-enum {
- SPRINTF_MODIFIER_NONE,
- SPRINTF_MODIFIER_CHAR,
- SPRINTF_MODIFIER_SHORT,
- SPRINTF_MODIFIER_LONG,
- SPRINTF_MODIFIER_LONGLONG,
- SPRINTF_MODIFIER_PTR, /* Used only for %p */
- SPRINTF_MODIFIER_SIZE,
- SPRINTF_MODIFIER_PTRDIFF
-};
-
-enum {
- SPRINTF_SPECIFIER_INVALID,
- SPRINTF_SPECIFIER_INT,
- SPRINTF_SPECIFIER_CHAR,
- SPRINTF_SPECIFIER_STR,
- SPRINTF_SPECIFIER_NRCHARS,
- SPRINTF_SPECIFIER_PERCENT
-};
-
-/*
- * Size for the temporary number buffer. The minimum base is 8 so 3 bits
- * are consumed per digit. Add one to round up. The conversion algorithm
- * doesn't use the null byte.
- */
-#define SPRINTF_MAX_NUM_SIZE (((sizeof(uint64_t) * CHAR_BIT) / 3) + 1)
-
-/*
- * Special size for vsnprintf(), used by sprintf()/vsprintf() when the
- * buffer size is unknown.
- */
-#define SPRINTF_NOLIMIT ((size_t)-1)
-
-static const char sprintf_digits[] = "0123456789ABCDEF";
-
-static inline char *
-sprintf_putchar(char *str, char *end, char c)
-{
- if (str < end) {
- *str = c;
- }
-
- str++;
-
- return str;
-}
-
-static inline int
-sprintf_isdigit(char c)
-{
- return (c >= '0') && (c <= '9');
-}
-
-int
-sprintf(char *str, const char *format, ...)
-{
- va_list ap;
- int length;
-
- va_start(ap, format);
- length = vsprintf(str, format, ap);
- va_end(ap);
-
- return length;
-}
-
-int
-vsprintf(char *str, const char *format, va_list ap)
-{
- return vsnprintf(str, SPRINTF_NOLIMIT, format, ap);
-}
-
-int
-snprintf(char *str, size_t size, const char *format, ...)
-{
- va_list ap;
- int length;
-
- va_start(ap, format);
- length = vsnprintf(str, size, format, ap);
- va_end(ap);
-
- return length;
-}
-
-int
-vsnprintf(char *str, size_t size, const char *format, va_list ap)
-{
- unsigned long long n;
- int i, len, found, flags, width, precision, modifier, specifier, shift;
- unsigned char r, base, mask;
- char c, *s, *start, *end, sign, tmp[SPRINTF_MAX_NUM_SIZE];
-
- start = str;
-
- if (size == 0) {
- end = NULL;
- } else if (size == SPRINTF_NOLIMIT) {
- end = (char *)-1;
- } else {
- end = start + size - 1;
- }
-
- while ((c = *format) != '\0') {
- if (c != '%') {
- str = sprintf_putchar(str, end, c);
- format++;
- continue;
- }
-
- /* Flags */
-
- found = 1;
- flags = 0;
-
- do {
- format++;
- c = *format;
-
- switch (c) {
- case '#':
- flags |= SPRINTF_FORMAT_ALT_FORM;
- break;
- case '0':
- flags |= SPRINTF_FORMAT_ZERO_PAD;
- break;
- case '-':
- flags |= SPRINTF_FORMAT_LEFT_JUSTIFY;
- break;
- case ' ':
- flags |= SPRINTF_FORMAT_BLANK;
- break;
- case '+':
- flags |= SPRINTF_FORMAT_SIGN;
- break;
- default:
- found = 0;
- break;
- }
- } while (found);
-
- /* Width */
-
- if (sprintf_isdigit(c)) {
- width = 0;
-
- while (sprintf_isdigit(c)) {
- width = width * 10 + (c - '0');
- format++;
- c = *format;
- }
- } else if (c == '*') {
- width = va_arg(ap, int);
-
- if (width < 0) {
- flags |= SPRINTF_FORMAT_LEFT_JUSTIFY;
- width = -width;
- }
-
- format++;
- c = *format;
- } else {
- width = 0;
- }
-
- /* Precision */
-
- if (c == '.') {
- format++;
- c = *format;
-
- if (sprintf_isdigit(c)) {
- precision = 0;
-
- while (sprintf_isdigit(c)) {
- precision = precision * 10 + (c - '0');
- format++;
- c = *format;
- }
- } else if (c == '*') {
- precision = va_arg(ap, int);
-
- if (precision < 0) {
- precision = 0;
- }
-
- format++;
- c = *format;
- } else {
- precision = 0;
- }
- } else {
- /* precision is >= 0 only if explicit */
- precision = -1;
- }
-
- /* Length modifier */
-
- switch (c) {
- case 'h':
- case 'l':
- format++;
-
- if (c == *format) {
- modifier = (c == 'h')
- ? SPRINTF_MODIFIER_CHAR
- : SPRINTF_MODIFIER_LONGLONG;
- goto skip_modifier;
- } else {
- modifier = (c == 'h')
- ? SPRINTF_MODIFIER_SHORT
- : SPRINTF_MODIFIER_LONG;
- c = *format;
- }
-
- break;
- case 'z':
- modifier = SPRINTF_MODIFIER_SIZE;
- goto skip_modifier;
- case 't':
- modifier = SPRINTF_MODIFIER_PTRDIFF;
-skip_modifier:
- format++;
- c = *format;
- break;
- default:
- modifier = SPRINTF_MODIFIER_NONE;
- break;
- }
-
- /* Specifier */
-
- switch (c) {
- case 'd':
- case 'i':
- flags |= SPRINTF_FORMAT_CONV_SIGNED;
- case 'u':
- base = 10;
- goto integer;
- case 'o':
- base = 8;
- goto integer;
- case 'p':
- flags |= SPRINTF_FORMAT_ALT_FORM;
- modifier = SPRINTF_MODIFIER_PTR;
- case 'x':
- flags |= SPRINTF_FORMAT_LOWER;
- case 'X':
- base = 16;
-integer:
- specifier = SPRINTF_SPECIFIER_INT;
- break;
- case 'c':
- specifier = SPRINTF_SPECIFIER_CHAR;
- break;
- case 's':
- specifier = SPRINTF_SPECIFIER_STR;
- break;
- case 'n':
- specifier = SPRINTF_SPECIFIER_NRCHARS;
- break;
- case '%':
- specifier = SPRINTF_SPECIFIER_PERCENT;
- break;
- default:
- specifier = SPRINTF_SPECIFIER_INVALID;
- break;
- }
-
- /* Output */
-
- switch (specifier) {
- case SPRINTF_SPECIFIER_INT:
- switch (modifier) {
- case SPRINTF_MODIFIER_CHAR:
- if (flags & SPRINTF_FORMAT_CONV_SIGNED) {
- n = (signed char)va_arg(ap, int);
- } else {
- n = (unsigned char)va_arg(ap, int);
- }
- break;
- case SPRINTF_MODIFIER_SHORT:
- if (flags & SPRINTF_FORMAT_CONV_SIGNED) {
- n = (short)va_arg(ap, int);
- } else {
- n = (unsigned short)va_arg(ap, int);
- }
- break;
- case SPRINTF_MODIFIER_LONG:
- if (flags & SPRINTF_FORMAT_CONV_SIGNED) {
- n = va_arg(ap, long);
- } else {
- n = va_arg(ap, unsigned long);
- }
- break;
- case SPRINTF_MODIFIER_LONGLONG:
- if (flags & SPRINTF_FORMAT_CONV_SIGNED) {
- n = va_arg(ap, long long);
- } else {
- n = va_arg(ap, unsigned long long);
- }
- break;
- case SPRINTF_MODIFIER_PTR:
- n = (uintptr_t)va_arg(ap, void *);
- break;
- case SPRINTF_MODIFIER_SIZE:
- if (flags & SPRINTF_FORMAT_CONV_SIGNED) {
- n = va_arg(ap, ssize_t);
- } else {
- n = va_arg(ap, size_t);
- }
- break;
- case SPRINTF_MODIFIER_PTRDIFF:
- n = va_arg(ap, ptrdiff_t);
- break;
- default:
- if (flags & SPRINTF_FORMAT_CONV_SIGNED) {
- n = va_arg(ap, int);
- } else {
- n = va_arg(ap, unsigned int);
- }
- break;
- }
-
- if ((flags & SPRINTF_FORMAT_LEFT_JUSTIFY) || (precision >= 0)) {
- flags &= ~SPRINTF_FORMAT_ZERO_PAD;
- }
-
- sign = 0;
-
- if (flags & SPRINTF_FORMAT_ALT_FORM) {
- /* '0' for octal */
- width--;
-
- /* '0x' or '0X' for hexadecimal */
- if (base == 16) {
- width--;
- }
- } else if (flags & SPRINTF_FORMAT_CONV_SIGNED) {
- if ((long long)n < 0) {
- sign = '-';
- width--;
- n = -(long long)n;
- } else if (flags & SPRINTF_FORMAT_SIGN) {
- /* SPRINTF_FORMAT_SIGN must precede SPRINTF_FORMAT_BLANK. */
- sign = '+';
- width--;
- } else if (flags & SPRINTF_FORMAT_BLANK) {
- sign = ' ';
- width--;
- }
- }
-
- /* Conversion, in reverse order */
-
- i = 0;
-
- if (n == 0) {
- if (precision != 0) {
- tmp[i++] = '0';
- }
- } else if (base == 10) {
- /*
- * Try to avoid 64 bits operations if the processor doesn't
- * support them. Note that even when using modulus and
- * division operators close to each other, the compiler will
- * forge two calls to __udivdi3() and __umoddi3() instead of
- * one to __udivmoddi3(), whereas processor instructions are
- * generally correctly used once, giving both the remainder
- * and the quotient, through plain or reciprocal division.
- */
-#ifndef __LP64__
- if (modifier == SPRINTF_MODIFIER_LONGLONG) {
-#endif /* __LP64__ */
- do {
- r = n % 10;
- n /= 10;
- tmp[i++] = sprintf_digits[r];
- } while (n != 0);
-#ifndef __LP64__
- } else {
- unsigned long m;
-
- m = (unsigned long)n;
-
- do {
- r = m % 10;
- m /= 10;
- tmp[i++] = sprintf_digits[r];
- } while (m != 0);
- }
-#endif /* __LP64__ */
- } else {
- mask = base - 1;
- shift = (base == 8) ? 3 : 4;
-
- do {
- r = (unsigned char)n & mask;
- n >>= shift;
- tmp[i++] = sprintf_digits[r]
- | (flags & SPRINTF_FORMAT_LOWER);
- } while (n != 0);
- }
-
- if (i > precision) {
- precision = i;
- }
-
- width -= precision;
-
- if (!(flags & (SPRINTF_FORMAT_LEFT_JUSTIFY
- | SPRINTF_FORMAT_ZERO_PAD)))
- while (width-- > 0) {
- str = sprintf_putchar(str, end, ' ');
- }
-
- if (flags & SPRINTF_FORMAT_ALT_FORM) {
- str = sprintf_putchar(str, end, '0');
-
- if (base == 16)
- str = sprintf_putchar(str, end,
- 'X' | (flags & SPRINTF_FORMAT_LOWER));
- } else if (sign) {
- str = sprintf_putchar(str, end, sign);
- }
-
- if (!(flags & SPRINTF_FORMAT_LEFT_JUSTIFY)) {
- c = (flags & SPRINTF_FORMAT_ZERO_PAD) ? '0' : ' ';
-
- while (width-- > 0) {
- str = sprintf_putchar(str, end, c);
- }
- }
-
- while (i < precision--) {
- str = sprintf_putchar(str, end, '0');
- }
-
- while (i-- > 0) {
- str = sprintf_putchar(str, end, tmp[i]);
- }
-
- while (width-- > 0) {
- str = sprintf_putchar(str, end, ' ');
- }
-
- break;
- case SPRINTF_SPECIFIER_CHAR:
- c = (unsigned char)va_arg(ap, int);
-
- if (!(flags & SPRINTF_FORMAT_LEFT_JUSTIFY))
- while (--width > 0) {
- str = sprintf_putchar(str, end, ' ');
- }
-
- str = sprintf_putchar(str, end, c);
-
- while (--width > 0) {
- str = sprintf_putchar(str, end, ' ');
- }
-
- break;
- case SPRINTF_SPECIFIER_STR:
- s = va_arg(ap, char *);
-
- if (s == NULL) {
- s = "(null)";
- }
-
- len = 0;
-
- for (len = 0; s[len] != '\0'; len++)
- if (len == precision) {
- break;
- }
-
- if (!(flags & SPRINTF_FORMAT_LEFT_JUSTIFY))
- while (len < width--) {
- str = sprintf_putchar(str, end, ' ');
- }
-
- for (i = 0; i < len; i++) {
- str = sprintf_putchar(str, end, *s);
- s++;
- }
-
- while (len < width--) {
- str = sprintf_putchar(str, end, ' ');
- }
-
- break;
- case SPRINTF_SPECIFIER_NRCHARS:
- if (modifier == SPRINTF_MODIFIER_CHAR) {
- signed char *ptr = va_arg(ap, signed char *);
- *ptr = str - start;
- } else if (modifier == SPRINTF_MODIFIER_SHORT) {
- short *ptr = va_arg(ap, short *);
- *ptr = str - start;
- } else if (modifier == SPRINTF_MODIFIER_LONG) {
- long *ptr = va_arg(ap, long *);
- *ptr = str - start;
- } else if (modifier == SPRINTF_MODIFIER_LONGLONG) {
- long long *ptr = va_arg(ap, long long *);
- *ptr = str - start;
- } else if (modifier == SPRINTF_MODIFIER_SIZE) {
- ssize_t *ptr = va_arg(ap, ssize_t *);
- *ptr = str - start;
- } else if (modifier == SPRINTF_MODIFIER_PTRDIFF) {
- ptrdiff_t *ptr = va_arg(ap, ptrdiff_t *);
- *ptr = str - start;
- } else {
- int *ptr = va_arg(ap, int *);
- *ptr = str - start;
- }
-
- break;
- case SPRINTF_SPECIFIER_PERCENT:
- case SPRINTF_SPECIFIER_INVALID:
- str = sprintf_putchar(str, end, '%');
- break;
- default:
- break;
- }
-
- if (specifier != SPRINTF_SPECIFIER_INVALID) {
- format++;
- }
- }
-
- if (str < end) {
- *str = '\0';
- } else if (end != NULL) {
- *end = '\0';
- }
-
- return str - start;
-}
diff --git a/kern/sprintf.h b/kern/sprintf.h
deleted file mode 100644
index 1cb51e7d..00000000
--- a/kern/sprintf.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2010 Richard Braun.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Formatted string functions.
- *
- * The functions provided by this module implement a subset of the C99
- * sprintf() like functions, mostly centered around character, string, and
- * integer conversions.
- *
- * The supported specifiers are: d i o u x X c s p n %
- * The supported length modifiers are: hh h l ll z t
- */
-
-#ifndef _KERN_SPRINTF_H
-#define _KERN_SPRINTF_H
-
-#ifndef _STDIO_H
-#error "do not use <kern/sprintf.h> directly; include <stdio.h> instead"
-#endif /* _STDIO_H */
-
-#include <stdarg.h>
-
-#include <kern/macros.h>
-
-int sprintf(char *str, const char *format, ...) __format_printf(2, 3);
-int vsprintf(char *str, const char *format, va_list ap) __format_printf(2, 0);
-
-int snprintf(char *str, size_t size, const char *format, ...)
- __format_printf(3, 4);
-int vsnprintf(char *str, size_t size, const char *format, va_list ap)
- __format_printf(3, 0);
-
-#endif /* _KERN_SPRINTF_H */