summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/common/fdprintf.c56
-rw-r--r--firmware/common/format.c267
-rw-r--r--firmware/common/vuprintf.c974
-rw-r--r--firmware/include/vuprintf.h (renamed from firmware/include/format.h)34
-rw-r--r--firmware/libc/sprintf.c110
-rw-r--r--firmware/logf.c12
-rw-r--r--firmware/target/arm/s3c2440/uart-s3c2440.c2
8 files changed, 1125 insertions, 333 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 9337c14234..1310fa0434 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -204,7 +204,7 @@ common/fileobj_mgr.c
common/dircache.c
#endif /* HAVE_DIRCACHE */
common/pathfuncs.c
-common/format.c
+common/fdprintf.c
common/linked_list.c
common/strcasecmp.c
common/strcasestr.c
@@ -214,6 +214,7 @@ common/strlcpy.c
common/structec.c
common/timefuncs.c
common/unicode.c
+common/vuprintf.c
/* Display */
scroll_engine.c
diff --git a/firmware/common/fdprintf.c b/firmware/common/fdprintf.c
new file mode 100644
index 0000000000..21d0a72e58
--- /dev/null
+++ b/firmware/common/fdprintf.c
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 by Felix Arends
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <limits.h>
+#include "file.h"
+#include "vuprintf.h"
+
+struct for_fprintf {
+ int fd; /* where to store it */
+ int rem; /* amount remaining */
+};
+
+static int fprfunc(void *pr, int letter)
+{
+ struct for_fprintf *fpr = (struct for_fprintf *)pr;
+
+ /* TODO: add a small buffer to reduce write() calls */
+ if (write(fpr->fd, &(char){ letter }, 1) > 0) {
+ return --fpr->rem;
+ }
+
+ return -1;
+}
+
+int fdprintf(int fd, const char *fmt, ...)
+{
+ int bytes;
+ struct for_fprintf fpr;
+ va_list ap;
+
+ fpr.fd = fd;
+ fpr.rem = INT_MAX;
+
+ va_start(ap, fmt);
+ bytes = vuprintf(fprfunc, &fpr, fmt, ap);
+ va_end(ap);
+
+ return bytes;
+}
diff --git a/firmware/common/format.c b/firmware/common/format.c
deleted file mode 100644
index 60c50ccd89..0000000000
--- a/firmware/common/format.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Gary Czvitkovicz
- *
- * 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 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-
-#include <stdarg.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <string.h>
-#include "file.h"
-#include "format.h"
-
-static const char hexdigit[] = "0123456789ABCDEF";
-
-void format(
- /* call 'push()' for each output letter */
- int (*push)(void *userp, unsigned char data),
- void *userp,
- const char *fmt,
- va_list ap)
-{
- char *str;
- char tmpbuf[12], pad;
- int ch, width, val, sign, precision;
- long lval, lsign;
- unsigned int uval;
- unsigned long ulval;
- size_t uszval;
- ssize_t szval, szsign;
- bool ok = true;
-
- tmpbuf[sizeof tmpbuf - 1] = '\0';
-
- while ((ch = *fmt++) != '\0' && ok)
- {
- if (ch == '%')
- {
- ch = *fmt++;
- pad = ' ';
- if (ch == '0')
- pad = '0';
-
- width = 0;
- while (ch >= '0' && ch <= '9')
- {
- width = 10*width + ch - '0';
- ch = *fmt++;
- }
-
- precision = 0;
- if(ch == '.')
- {
- ch = *fmt++;
- while (ch >= '0' && ch <= '9')
- {
- precision = 10*precision + ch - '0';
- ch = *fmt++;
- }
- } else {
- precision = INT_MAX;
- }
-
- str = tmpbuf + sizeof tmpbuf - 1;
- switch (ch)
- {
- case 'c':
- *--str = va_arg (ap, int);
- break;
-
- case 's':
- str = va_arg (ap, char*);
- break;
-
- case 'd':
- val = sign = va_arg (ap, int);
- if (val < 0)
- val = -val;
- do
- {
- *--str = (val % 10) + '0';
- val /= 10;
- }
- while (val > 0);
- if (sign < 0)
- *--str = '-';
- break;
-
- case 'u':
- uval = va_arg(ap, unsigned int);
- do
- {
- *--str = (uval % 10) + '0';
- uval /= 10;
- }
- while (uval > 0);
- break;
- case 'p':
- case 'P':
- /* for pointers prepend 0x and act like 'X' */
- push(userp, '0');
- push(userp, 'x');
- /* fall through */
- case 'x':
- case 'X':
- pad='0';
- uval = va_arg (ap, int);
- do
- {
- *--str = hexdigit[uval & 0xf];
- uval >>= 4;
- }
- while (uval);
- break;
-
- case 'l':
- ch = *fmt++;
- switch(ch) {
- case 'x':
- case 'X':
- pad='0';
- ulval = va_arg (ap, long);
- do
- {
- *--str = hexdigit[ulval & 0xf];
- ulval >>= 4;
- }
- while (ulval);
- break;
- case 'd':
- lval = lsign = va_arg (ap, long);
- if (lval < 0)
- lval = -lval;
- do
- {
- *--str = (lval % 10) + '0';
- lval /= 10;
- }
- while (lval > 0);
- if (lsign < 0)
- *--str = '-';
- break;
-
- case 'u':
- ulval = va_arg(ap, unsigned long);
- do
- {
- *--str = (ulval % 10) + '0';
- ulval /= 10;
- }
- while (ulval > 0);
- break;
-
- default:
- *--str = 'l';
- *--str = ch;
- }
-
- break;
-
- case 'z':
- ch = *fmt++;
- switch(ch) {
- case 'd':
- szval = szsign = va_arg (ap, ssize_t);
- if (szval < 0)
- szval = -szval;
- do
- {
- *--str = (szval % 10) + '0';
- szval /= 10;
- }
- while (szval > 0);
- if (szsign < 0)
- *--str = '-';
- break;
-
- case 'u':
- uszval = va_arg(ap, size_t);
- do
- {
- *--str = (uszval % 10) + '0';
- uszval /= 10;
- }
- while (uszval > 0);
- break;
-
- default:
- *--str = 'z';
- *--str = ch;
- }
-
- break;
-
- default:
- *--str = ch;
- break;
- }
-
- if (width > 0)
- {
- width -= strlen (str);
- while (width-- > 0 && ok)
- ok=push(userp, pad);
- }
- while (*str != '\0' && ok && precision--)
- ok=push(userp, *str++);
- }
- else
- ok=push(userp, ch);
- }
-}
-
-struct for_fprintf {
- int fd; /* where to store it */
- int bytes; /* amount stored */
-};
-
-static int fprfunc(void *pr, unsigned char letter)
-{
- struct for_fprintf *fpr = (struct for_fprintf *)pr;
- int rc = write(fpr->fd, &letter, 1);
-
- if(rc > 0) {
- fpr->bytes++; /* count them */
- return true; /* we are ok */
- }
-
- return false; /* failure */
-}
-
-
-int fdprintf(int fd, const char *fmt, ...)
-{
- va_list ap;
- struct for_fprintf fpr;
-
- fpr.fd=fd;
- fpr.bytes=0;
-
- va_start(ap, fmt);
- format(fprfunc, &fpr, fmt, ap);
- va_end(ap);
-
- return fpr.bytes; /* return 0 on error */
-}
-
-void vuprintf(int (*push)(void *userp, unsigned char data), void *userp, const char *fmt, va_list ap)
-{
- format(push, userp, fmt, ap);
-}
diff --git a/firmware/common/vuprintf.c b/firmware/common/vuprintf.c
new file mode 100644
index 0000000000..c32b690cf8
--- /dev/null
+++ b/firmware/common/vuprintf.c
@@ -0,0 +1,974 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 by Gary Czvitkovicz
+ * Copyright (C) 2017 by Michael A. Sevakis
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <sys/types.h>
+#include <limits.h>
+#include <string.h>
+#include "system.h"
+#include "vuprintf.h"
+
+#if 0
+/* turn everything on */
+#define FMT_LENMOD (0xffffffff)
+#define FMT_RADIX (0xffffffff)
+#endif
+
+/* these are the defaults if no other preference is given */
+#ifndef FMT_LENMOD
+#define FMT_LENMOD (FMT_LENMOD_l | \
+ FMT_LENMOD_z)
+#endif /* FMT_LENMOD */
+
+#ifndef FMT_RADIX
+#define FMT_RADIX (FMT_RADIX_c | \
+ FMT_RADIX_d | \
+ FMT_RADIX_p | \
+ FMT_RADIX_s | \
+ FMT_RADIX_u | \
+ FMT_RADIX_x)
+#endif /* FMT_RADIX */
+
+/** Length modifier and radix flags **/
+
+/* compulsory length modifiers: NONE
+ * however a compatible 'l' or 'll' must be defined if another requires it */
+#define FMT_LENMOD_h 0x001 /* signed/unsigned short (%h<radix>) */
+#define FMT_LENMOD_hh 0x002 /* signed/unsigned char (%hh<radix>) */
+#define FMT_LENMOD_j 0x004 /* intmax_t/uintmax_t (%j<radix>) */
+#define FMT_LENMOD_l 0x008 /* signed/unsigned long (%l<radix>) */
+#define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */
+#define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */
+#define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */
+#define FMT_LENMOD_L 0x080 /* long double (instead of double) */
+
+/* compulsory radixes: c, d, i, u, s */
+#define FMT_RADIX_c 0x001 /* single character (%c) */
+#define FMT_RADIX_d 0x002 /* signed integer type, decimal (%d %i) */
+#define FMT_RADIX_n 0x004 /* bytes output so far (%n) */
+#define FMT_RADIX_o 0x008 /* unsigned integer type, octal (%o) */
+#define FMT_RADIX_p 0x010 /* pointer (%p %P) */
+#define FMT_RADIX_s 0x020 /* string (%s) */
+#define FMT_RADIX_u 0x040 /* unsigned integer type, decimal (%u) */
+#define FMT_RADIX_x 0x080 /* unsigned integer type, hex (%x %X) */
+#define FMT_RADIX_a 0x100 /* hex floating point "[-]0xh.hhhhp±d" */
+#define FMT_RADIX_e 0x200 /* floating point with exponent "[-]d.ddde±dd" */
+#define FMT_RADIX_f 0x400 /* floating point "[-]ddd.ddd" */
+#define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending
+ upon value and precision */
+
+/* avoid defining redundant functions if two or more types can use the same
+ * something not getting a macro means it gets assigned its own value and
+ * formatter */
+
+/* l */
+#if LONG_MIN == INT_MIN && LONG_MAX == INT_MAX
+#define val_ld val_d
+#define format_ld format_d
+#define branch_fmt_ld branch_fmt_d
+#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
+#define val_ld
+#endif /* LONG_ */
+
+#if ULONG_MAX == UINT_MAX
+#define val_lu val_u
+#define format_lu format_u
+#define branch_fmt_lu branch_fmt_u
+#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
+#define val_lu
+#endif /* ULONG_ */
+
+/* ll */
+#if LLONG_MIN == INT_MIN && LLONG_MAX == INT_MAX
+#define val_lld val_d
+#define format_lld format_d
+#define branch_fmt_lld branch_fmt_d
+#elif LLONG_MIN == LONG_MIN && LLONG_MAX == LONG_MAX
+#define val_lld val_ld
+#define format_lld format_ld
+#define branch_fmt_lld branch_fmt_ld
+#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
+#define val_lld
+#endif /* LLONG_ */
+
+#if ULLONG_MAX == UINT_MAX
+#define val_llu val_u
+#define format_llu format_u
+#define branch_fmt_llu branch_fmt_u
+#elif ULLONG_MAX == ULONG_MAX
+#define val_llu val_lu
+#define format_llu format_lu
+#define branch_fmt_llu branch_fmt_lu
+#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
+#define val_llu
+#endif /* ULLONG_ */
+
+/* char/short parameter type promotions */
+#define SCHAR_INT_ARG int
+#define UCHAR_INT_ARG int
+#define SSHRT_INT_ARG int
+#if USHRT_MAX == UINT_MAX
+#define USHRT_INT_ARG unsigned int
+#else
+#define USHRT_INT_ARG int
+#endif
+
+/* some macros to have conditional work inside macros */
+#if (FMT_LENMOD & FMT_LENMOD_l)
+#define IF_FMT_LENMOD_l(...) __VA_ARGS__
+#else
+#define IF_FMT_LENMOD_l(...)
+#endif
+
+#if (FMT_LENMOD & FMT_LENMOD_ll)
+#define IF_FMT_LENMOD_ll(...) __VA_ARGS__
+#else
+#define IF_FMT_LENMOD_ll(...)
+#endif
+
+#if (FMT_RADIX & FMT_RADIX_o)
+#define IF_FMT_RADIX_o(...) __VA_ARGS__
+#else
+#define IF_FMT_RADIX_o(...)
+#endif
+
+#if (FMT_RADIX & FMT_RADIX_x)
+#define IF_FMT_RADIX_x(...) __VA_ARGS__
+#else
+#define IF_FMT_RADIX_x(...)
+#endif
+
+/* synthesize multicharacter constant */
+#define LENMOD2(cv, ch) \
+ (((cv) << CHAR_BIT) | (ch))
+
+#define LENMOD_NONE 0
+
+#if (FMT_LENMOD & FMT_LENMOD_h)
+#define LENMOD_h 'h'
+#endif
+#if (FMT_LENMOD & FMT_LENMOD_hh)
+#define LENMOD_hh LENMOD2('h', 'h') /* 'hh' */
+#endif
+#if (FMT_LENMOD & FMT_LENMOD_j)
+#define LENMOD_j 'j'
+#endif
+#if (FMT_LENMOD & FMT_LENMOD_l)
+#define LENMOD_l 'l'
+#endif
+#if (FMT_LENMOD & FMT_LENMOD_ll)
+#undef FMT_MAX_L
+#define LENMOD_ll LENMOD2('l', 'l') /* 'll' */
+#endif
+#if (FMT_LENMOD & FMT_LENMOD_t)
+#define LENMOD_t 't'
+#endif
+#if (FMT_LENMOD & FMT_LENMOD_z)
+#define LENMOD_z 'z'
+#endif
+
+/* select type-compatible length modifier
+ * (a bit hacky; it should be range-based) */
+#define LENMOD_INTCOMPAT_SEL(type, signd) \
+ ({ int __lenmod; \
+ size_t __size = sizeof (type); \
+ if (__size == ((signd) ? sizeof (int) : \
+ sizeof (unsigned int))) { \
+ __lenmod = LENMOD_NONE; \
+ } \
+ else if (__size == ((signd) ? sizeof (long) : \
+ sizeof (unsigned long))) { \
+ IF_FMT_LENMOD_l(__lenmod = LENMOD_l;) \
+ } \
+ else if (__size == ((signd) ? sizeof (long long) : \
+ sizeof (unsigned long long))) { \
+ IF_FMT_LENMOD_ll(__lenmod = LENMOD_ll;) \
+ } \
+ __lenmod; })
+
+/* call formatting function for the compatible integer type */
+#define LENMOD_INTCOMPAT_CALL(inteqv, val, fmt_buf, x, signd) \
+ ({ const char *__buf; \
+ switch (inteqv) { \
+ case LENMOD_NONE: \
+ __buf = (signd) ? \
+ format_d((val), (fmt_buf), (x)) : \
+ format_u((val), (fmt_buf), (x)); \
+ break; \
+ IF_FMT_LENMOD_l( \
+ case LENMOD_l: \
+ __buf = (signd) ? \
+ format_ld((val), (fmt_buf), (x)) : \
+ format_lu((val), (fmt_buf), (x)); \
+ break; \
+ ) \
+ IF_FMT_LENMOD_ll( \
+ case LENMOD_ll: \
+ __buf = (signd) ? \
+ format_lld((val), (fmt_buf), (x)) : \
+ format_llu((val), (fmt_buf), (x)); \
+ break; \
+ ) \
+ } \
+ __buf; \
+ })
+
+/* execute formatting branch for the compatible integer type */
+#define LENMOD_INTCOMPAT_BRANCH(inteqv, val, signd) \
+ ({ switch (inteqv) { \
+ case LENMOD_NONE: \
+ if (signd) { \
+ val_d = (val); \
+ goto branch_fmt_d; \
+ } \
+ else { \
+ val_u = (val); \
+ goto branch_fmt_u; \
+ } \
+ IF_FMT_LENMOD_l( \
+ case LENMOD_l: \
+ if (signd) { \
+ val_ld = (val); \
+ goto branch_fmt_ld; \
+ } \
+ else { \
+ val_lu = (val); \
+ goto branch_fmt_lu; \
+ } \
+ ) \
+ IF_FMT_LENMOD_ll( \
+ case LENMOD_ll: \
+ if (signd) { \
+ val_lld = (val); \
+ goto branch_fmt_lld; \
+ } \
+ else { \
+ val_llu = (val); \
+ goto branch_fmt_llu; \
+ } \
+ ) \
+ } \
+ })
+
+#define CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, type) \
+ do { \
+ if (val) { \
+ unsigned type v; \
+ \
+ if (val < 0) { \
+ v = (typeof (v))-(val + 1) + 1; \
+ signchar = '-'; \
+ } \
+ else { \
+ v = val; \
+ } \
+ \
+ do { \
+ *--p = (v % 10) + '0'; \
+ v /= 10; \
+ } while (v); \
+ } \
+ \
+ if (signchar) { \
+ p[-1] = signchar; \
+ fmt_buf->length = 1; \
+ break; \
+ } \
+ \
+ fmt_buf->length = 0; \
+ } while (0)
+
+#define CONVERT_RADIX_8(val, fmt_buf, p) \
+ do { \
+ if (val) { \
+ typeof (val) v = val; \
+ \
+ do { \
+ *--p = (v % 010) + '0'; \
+ v /= 010; \
+ } while (v); \
+ } \
+ \
+ if (fmt_buf->length) { \
+ *--p = '0'; \
+ fmt_buf->length = 0; \
+ } \
+ } while (0)
+
+#define CONVERT_RADIX_10(val, fmt_buf, p) \
+ do { \
+ if (val) { \
+ typeof (val) v = val; \
+ \
+ do { \
+ *--p = (v % 10) + '0'; \
+ v /= 10; \
+ } while (v); \
+ } \
+ \
+ fmt_buf->length = 0; \
+ } while (0)
+
+#define CONVERT_RADIX_16(val, fmt_buf, p, x) \
+ do { \
+ if (val) { \
+ const int h = x - 'X' - 0xA \
+ + 'A' - '0'; \
+ typeof (val) v = val; \
+ \
+ do { \
+ unsigned int d = v % 0x10; \
+ if (d >= 0xA) { \
+ d += h; \
+ } \
+ *--p = d + '0'; \
+ v /= 0x10; \
+ } while (v); \
+ \
+ if (fmt_buf->length) { \
+ p[-1] = x; \
+ p[-2] = '0'; \
+ fmt_buf->length = 2; \
+ break; \
+ } \
+ } \
+ \
+ fmt_buf->length = 0; \
+ } while (0)
+
+#define CONVERT_RADIX_NOSIGN(val, fmt_buf, p, x) \
+ switch (x) \
+ { \
+ IF_FMT_RADIX_o( case 'o': \
+ CONVERT_RADIX_8(val, fmt_buf, p); \
+ break; ) \
+ case 'u': \
+ CONVERT_RADIX_10(val, fmt_buf, p); \
+ break; \
+ IF_FMT_RADIX_x( default: \
+ CONVERT_RADIX_16(val, fmt_buf, p, x); \
+ break; ) \
+ }
+
+struct fmt_buf {
+ const char *fmt_start; /* second character of formatter after '%' */
+ size_t length; /* length of formatted text (non-numeric)
+ or prefix (numeric) */
+ char buf[24]; /* work buffer */
+ char bufend[1]; /* buffer end marker and guard '0' */
+};
+
+/* %d %i */
+static inline const char * format_d(int val,
+ struct fmt_buf *fmt_buf,
+ int signchar)
+{
+ char *p = fmt_buf->bufend;
+ CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, int);
+ return p;
+}
+
+/* %o %u %x %X */
+static inline const char * format_u(unsigned int val,
+ struct fmt_buf *fmt_buf,
+ int radixchar)
+{
+ char *p = fmt_buf->bufend;
+ CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
+ return p;
+}
+
+#if (FMT_LENMOD & FMT_LENMOD_l)
+#ifndef format_ld
+/* %ld %li */
+static inline const char * format_ld(long val,
+ struct fmt_buf *fmt_buf,
+ int signchar)
+{
+ char *p = fmt_buf->bufend;
+ CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long);
+ return p;
+}
+#endif /* format_ld */
+
+#ifndef format_lu
+/* %lo %lu %lx %lX */
+static inline const char * format_lu(unsigned long val,
+ struct fmt_buf *fmt_buf,
+ int radixchar)
+{
+ char *p = fmt_buf->bufend;
+ CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
+ return p;
+}
+#endif /* format_lu */
+#endif /* FMT_LENMOD_l */
+
+#if (FMT_LENMOD & FMT_LENMOD_ll)
+#ifndef format_lld
+/* %lld %lli */
+static inline const char * format_lld(long long val,
+ struct fmt_buf *fmt_buf,
+ int signchar)
+{
+ char *p = fmt_buf->bufend;
+ CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long long);
+ return p;
+}
+#endif /* format_lld */
+
+#ifndef format_llu
+/* %llo %llu %llx %llX */
+static inline const char * format_llu(unsigned long long val,
+ struct fmt_buf *fmt_buf,
+ int radixchar)
+{
+ char *p = fmt_buf->bufend;
+ CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
+ return p;
+}
+#endif /* format_llu */
+#endif /* FMT_LENMOD_ll */
+
+/* %c */
+static inline const char * format_c(int c,
+ struct fmt_buf *fmt_buf,
+ int lenmod)
+{
+ if (lenmod != LENMOD_NONE) {
+ return NULL; /* wchar_t support for now */
+ }
+
+ char *p = fmt_buf->bufend;
+ fmt_buf->length = 1;
+ *--p = (unsigned char)c;
+ return p;
+}
+
+/* %s */
+static inline const char * format_s(const void *str,
+ struct fmt_buf *fmt_buf,
+ int precision,
+ int lenmod)
+{
+ if (lenmod != LENMOD_NONE) {
+ return NULL; /* wchar_t support for now */
+ }
+
+ /* string length may be specified by precision instead of \0-
+ terminated; however, don't go past a \0 if one is there */
+ const char *s = str;
+ size_t len = precision >= 0 ? precision : -1;
+
+ const char *nil = memchr(s, '\0', len);
+ if (nil) {
+ len = nil - s;
+ }
+
+ fmt_buf->length = len;
+ return s;
+}
+
+#if (FMT_RADIX & FMT_RADIX_n)
+/* %n */
+static inline bool format_n(void *np,
+ int count,
+ int lenmod)
+{
+ if (lenmod != LENMOD_NONE) {
+ return false; /* int only for now */
+ }
+
+ *(int *)np = count;
+ return true;
+}
+#endif /* FMT_RADIX_n */
+
+#if (FMT_RADIX & FMT_RADIX_p)
+/* %p %P */
+static inline const char * format_p(const void *p,
+ struct fmt_buf *fmt_buf,
+ int radixchar,
+ bool *numericp)
+{
+ if (p) {
+ /* format as %#x or %#X */
+ *numericp = true;
+ radixchar -= 'P' - 'X';
+ fmt_buf->length = 2;
+ return LENMOD_INTCOMPAT_CALL(LENMOD_INTCOMPAT_SEL(uintptr_t, false),
+ (uintptr_t)p, fmt_buf, radixchar, false);
+ }
+ else {
+ /* format as %s */
+ fmt_buf->length = 5;
+ return "(nil)";
+ }
+}
+#endif /* FMT_RADIX_p */
+
+/* parse fixed width or precision field */
+static const char * parse_number_spec(const char *fmt,
+ int ch,
+ int *out)
+{
+ int i = ch - '0';
+
+ while (1) {
+ ch = *fmt - '0';
+
+ if (ch < 0 || ch > 9 || i > INT_MAX / 10 ||
+ (i == INT_MAX / 10 && ch > INT_MAX % 10)) {
+ break;
+ }
+
+ i = i * 10 + ch;
+ fmt++;
+ }
+
+ *out = i;
+ return fmt;
+}
+
+int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
+ void *userp,
+ const char *fmt,
+ va_list ap)
+{
+ #define PUSHCHAR(ch) \
+ do { \
+ int __ch = (ch); \
+ int __rc = push(userp, __ch); \
+ count += __rc >= 0; \
+ if (__rc <= 0) { \
+ goto done; \
+ } \
+ } while (0)
+
+ int count = 0;
+ int ch;
+
+ /* macrofied identifiers share a variable with another */
+ unsigned int val_d;
+ unsigned int val_u;
+ #ifndef val_ld
+ unsigned long val_ld;
+ #endif
+ #ifndef val_lu
+ unsigned long val_lu;
+ #endif
+ #ifndef val_lld
+ unsigned long long val_lld;
+ #endif
+ #ifndef val_llu
+ unsigned long long val_llu;
+ #endif
+
+ struct fmt_buf fmt_buf;
+ fmt_buf.bufend[0] = '0';
+
+ while (1) {
+ while (1) {
+ if ((ch = *fmt++) == '\0') {
+ goto done;
+ }
+
+ if (ch == '%' && (ch = *fmt++) != '%') {
+ break;
+ }
+
+ PUSHCHAR(ch);
+ }
+
+ /* set to defaults */
+ fmt_buf.fmt_start = fmt;
+
+ int signchar = 0;
+ unsigned int width = 0;
+ int lenmod = LENMOD_NONE;
+ size_t length = 0;
+ size_t pfxlen = 0;
+ bool numeric = false;
+ int alignchar = '0' + 1;
+ int precision = -1;
+ const char *buf = NULL;
+
+ /*** flags ***/
+ while (1) {
+ switch (ch)
+ {
+ case ' ': /* <space> before non-negative value (signed conversion) */
+ case '+': /* '+' before non-negative value (signed conversion) */
+ /* '+' overrides ' ' */
+ if (ch > signchar) {
+ signchar = ch;
+ }
+ break;
+ case '-': /* left-justify in field */
+ case '0': /* zero-pad to fill field */
+ /* '-' overrides '0' */
+ if (ch < alignchar) {
+ alignchar = ch;
+ }
+ break;
+ case '#': /* number prefix (nonzero %o:'0' %x/%X:'0x') */
+ /* indicate; formatter updates with actual length */
+ pfxlen = 1;
+ break;
+ #if 0
+ case '\'': /* digit grouping (non-monetary) */
+ break;
+ #endif
+ default:
+ goto flags_done;
+ }
+
+ ch = *fmt++;
+ }
+ flags_done:
+
+ /*** width ***/
+ if (ch == '*') {
+ /* variable width */
+ int w = va_arg(ap, int);
+ if (w < 0) {
+ /* negative width is width with implied '-' */
+ width = (unsigned int)-(w + 1) + 1;
+ alignchar = '-';
+ }
+ else {
+ width = w;
+ }
+
+ ch = *fmt++;
+ }
+ else if (ch >= '1' && ch <= '9') {
+ /* fixed width */
+ fmt = parse_number_spec(fmt, ch, &width);
+ ch = *fmt++;
+ }
+
+ /*** precision ***/
+ if (ch == '.') {
+ ch = *fmt++;
+
+ if (ch == '*') {
+ /* variable precision; negative precision is ignored */
+ precision = va_arg (ap, int);
+ ch = *fmt++;
+ }
+ else if (ch >= '0' && ch <= '9') {
+ /* fixed precision */
+ fmt = parse_number_spec(fmt, ch, &precision);
+ ch = *fmt++;
+ }
+ }
+
+ /*** length modifier ***/
+ #if FMT_LENMOD
+ switch (ch)
+ {
+ #if (FMT_LENMOD & (FMT_LENMOD_h | FMT_LENMOD_hh))
+ case 'h':
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_j)
+ case 'j':
+ #endif
+ #if (FMT_LENMOD & (FMT_LENMOD_l | FMT_LENMOD_ll))
+ case 'l':
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_t)
+ case 't':
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_z)
+ case 'z':
+ #endif
+ lenmod = ch;
+ ch = *fmt++;
+ #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll))
+ /* doesn't matter if jj, tt or zz happen; they will be rejected
+ by the radix handler */
+ if (ch == lenmod) {
+ lenmod = LENMOD2(lenmod, ch);
+ ch = *fmt++;
+ }
+ #endif
+ }
+ #endif /* FMT_LENMOD */
+
+ /*** radix ***/
+ switch (ch)
+ {
+ /** non-numeric **/
+ case 'c':
+ buf = format_c(va_arg(ap, int), &fmt_buf, lenmod);
+ break;
+ #if (FMT_RADIX & FMT_RADIX_n)
+ case 'n':
+ if (format_n(va_arg(ap, void *), count, lenmod)) {
+ continue; /* no output */
+ }
+ break;
+ #endif
+ case 's':
+ buf = format_s(va_arg(ap, const void *), &fmt_buf,
+ precision, lenmod);
+ break;
+
+ /** non-integer **/
+ #if (FMT_RADIX & FMT_RADIX_p)
+ case 'p':
+ case 'P':
+ buf = format_p(va_arg(ap, void *), &fmt_buf, ch,
+ &numeric);
+ break;
+ #endif
+
+ /** signed integer **/
+ case 'd':
+ case 'i':
+ fmt_buf.length = pfxlen;
+
+ switch (lenmod)
+ {
+ case LENMOD_NONE:
+ val_d = va_arg(ap, signed int);
+ goto branch_fmt_d;
+ #if (FMT_LENMOD & FMT_LENMOD_h)
+ case LENMOD_h:
+ val_d = (signed short)va_arg(ap, SSHRT_INT_ARG);
+ goto branch_fmt_d;
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_hh)
+ case LENMOD_hh:
+ val_d = (signed char)va_arg(ap, SCHAR_INT_ARG);
+ goto branch_fmt_d;
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_j)
+ case LENMOD_j:
+ LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(intmax_t, true),
+ va_arg(ap, intmax_t), true);
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_l)
+ case LENMOD_l:
+ val_ld = va_arg(ap, signed long);
+ goto branch_fmt_ld;
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_ll)
+ case LENMOD_ll:
+ val_lld = va_arg(ap, signed long long);
+ goto branch_fmt_lld;
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_t)
+ case LENMOD_t:
+ LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ptrdiff_t, true),
+ va_arg(ap, ptrdiff_t), true);
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_z)
+ case LENMOD_z:
+ LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ssize_t, true),
+ va_arg(ap, ssize_t), true);
+ #endif
+ }
+
+ /* macrofied labels share a formatter with another */
+ if (0) {
+ branch_fmt_d:
+ buf = format_d(val_d, &fmt_buf, signchar);
+ } else if (0) {
+ #ifndef val_ld
+ branch_fmt_ld:
+ buf = format_ld(val_ld, &fmt_buf, signchar);
+ #endif
+ } else if (0) {
+ #ifndef val_lld
+ branch_fmt_lld:
+ buf = format_lld(val_lld, &fmt_buf, signchar);
+ #endif
+ }
+
+ numeric = true;
+ break;
+
+ /** unsigned integer **/
+ #if (FMT_RADIX & FMT_RADIX_o)
+ case 'o':
+ #endif
+ case 'u':
+ #if (FMT_RADIX & FMT_RADIX_x)
+ case 'x':
+ case 'X':
+ #endif
+ fmt_buf.length = pfxlen;
+
+ switch (lenmod)
+ {
+ case LENMOD_NONE:
+ val_u = va_arg(ap, unsigned int);
+ goto branch_fmt_u;
+ #if (FMT_LENMOD & FMT_LENMOD_h)
+ case LENMOD_h:
+ val_u = (unsigned short)va_arg(ap, USHRT_INT_ARG);
+ goto branch_fmt_u;
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_hh)
+ case LENMOD_hh:
+ val_u = (unsigned char)va_arg(ap, UCHAR_INT_ARG);
+ goto branch_fmt_u;
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_j)
+ case LENMOD_j:
+ LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(uintmax_t, false),
+ va_arg(ap, uintmax_t), false);
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_l)
+ case LENMOD_l:
+ val_lu = va_arg(ap, unsigned long);
+ goto branch_fmt_lu;
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_ll)
+ case LENMOD_ll:
+ val_llu = va_arg(ap, unsigned long long);
+ goto branch_fmt_llu;
+ #endif
+ #if (FMT_LENMOD & (FMT_LENMOD_t | FMT_LENMOD_z))
+ /* format "uptrdiff_t" as size_t (unless it becomes standard) */
+ #if (FMT_LENMOD & FMT_LENMOD_t)
+ case LENMOD_t:
+ #endif
+ #if (FMT_LENMOD & FMT_LENMOD_z)
+ case LENMOD_z:
+ #endif
+ LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(size_t, false),
+ va_arg(ap, size_t), false);
+ #endif
+ }
+
+ /* macrofied labels share a formatter with another */
+ if (0) {
+ branch_fmt_u:
+ buf = format_u(val_u, &fmt_buf, ch);
+ } else if (0) {
+ #ifndef val_lu
+ branch_fmt_lu:
+ buf = format_lu(val_lu, &fmt_buf, ch);
+ #endif
+ } else if (0) {
+ #ifndef val_llu
+ branch_fmt_llu:
+ buf = format_llu(val_llu, &fmt_buf, ch);
+ #endif
+ }
+
+ numeric = true;
+ break;
+ }
+
+ if (buf) {
+ /** padding **/
+ if (numeric) {
+ /* numeric formats into fmt_buf.buf */
+ pfxlen = fmt_buf.length;
+ length = fmt_buf.bufend - buf;
+
+ size_t size = pfxlen + length;
+
+ if (precision >= 0) {
+ /* explicit precision */
+ precision -= (int)length;
+
+ if (precision > 0) {
+ size += precision;
+ }
+
+ width -= MIN(width, size);
+ }
+ else {
+ /* default precision */
+ if (!length) {
+ length = 1;
+ size++;
+ }
+
+ width -= MIN(width, size);
+
+ if (alignchar == '0') {
+ /* width zero-fill */
+ precision = width;
+ width = 0;
+ }
+ }
+ }
+ else {
+ /* non-numeric: supress prefix and precision; keep length and
+ width */
+ pfxlen = 0;
+ precision = 0;
+ length = fmt_buf.length;
+ width -= MIN(width, length);
+ }
+ }
+ else {
+ /* format not accepted; print it literally */
+ buf = fmt_buf.fmt_start - 2;
+ length = fmt - buf;
+ width = 0;
+ pfxlen = 0;
+ precision = 0;
+ }
+
+ /** push all the stuff **/
+
+ if (alignchar != '-') {
+ /* left padding */
+ while (width > 0) {
+ PUSHCHAR(' ');
+ width--;
+ }
+ }
+
+ /* prefix */
+ while (pfxlen > 0) {
+ PUSHCHAR(buf[-pfxlen]);
+ pfxlen--;
+ }
+
+ /* 0-padding */
+ while (precision > 0) {
+ PUSHCHAR('0');
+ precision--;
+ }
+
+ /* field */
+ while (length > 0) {
+ PUSHCHAR(*buf++);
+ length--;
+ }
+
+ /* right padding */
+ while (width > 0) {
+ PUSHCHAR(' ');
+ width--;
+ }
+ }
+
+done:
+ return count;
+}
diff --git a/firmware/include/format.h b/firmware/include/vuprintf.h
index 30a072aca8..3876482fb2 100644
--- a/firmware/include/format.h
+++ b/firmware/include/vuprintf.h
@@ -19,19 +19,27 @@
*
****************************************************************************/
-#ifndef __FORMAT_H__
-#define __FORMAT_H__
+#ifndef __VUPRINTF_H__
+#define __VUPRINTF_H__
-void format(
- /* call 'push()' for each output letter */
- int (*push)(void *userp, unsigned char data),
- void *userp,
- const char *fmt,
- va_list ap);
+#include <stdarg.h>
-/* callback function is called for every output character (byte) with userp and
- * should return 0 when ch is a char other than '\0' that should stop printing */
-void vuprintf(int (*push)(void *userp, unsigned char data),
- void *userp, const char *fmt, va_list ap);
+/* callback function is called for every output character (byte) in the
+ * output with userp
+ *
+ * it must return > 0 to continue (increments counter)
+ * it may return 0 to stop (increments counter)
+ * it may return < 0 to stop (does not increment counter)
+ * a zero in the format string stops (does not increment counter)
+ *
+ * caller is reponsible for stopping formatting in order to keep the return
+ * value from overflowing (assuming they have a reason to care)
+ */
+typedef int (* vuprintf_push_cb)(void *userp, int c);
+
+/*
+ * returns the number of times push() was called and returned >= 0
+ */
+int vuprintf(vuprintf_push_cb push, void *userp, const char *fmt, va_list ap);
-#endif /* __FORMAT_H__ */
+#endif /* __VUPRINTF_H__ */
diff --git a/firmware/libc/sprintf.c b/firmware/libc/sprintf.c
index 18e2ce6fd2..a56f454c34 100644
--- a/firmware/libc/sprintf.c
+++ b/firmware/libc/sprintf.c
@@ -18,74 +18,94 @@
* KIND, either express or implied.
*
****************************************************************************/
-
-/*
- * Minimal printf and snprintf formatting functions
- *
- * These support %c %s %d and %x
- * Field width and zero-padding flag only
- */
-
#include <stdio.h>
-#include <stdarg.h>
-#include <stdbool.h>
#include <limits.h>
-#include "format.h"
+#include <errno.h>
+#include "vuprintf.h"
/* ALSA library requires a more advanced snprintf, so let's not
override it in simulator for Linux. Note that Cygwin requires
our snprintf or it produces garbled output after a while. */
struct for_snprintf {
- unsigned char *ptr; /* where to store it */
- size_t bytes; /* amount already stored */
- size_t max; /* max amount to store */
+ char *ptr; /* where to store it */
+ int rem; /* unwritten buffer remaining */
};
-static int sprfunc(void *ptr, unsigned char letter)
+static int sprfunc(void *ptr, int letter)
{
struct for_snprintf *pr = (struct for_snprintf *)ptr;
- if(pr->bytes < pr->max) {
- *pr->ptr = letter;
- pr->ptr++;
- pr->bytes++;
- return true;
+
+ if (pr->rem > 0) {
+ if (--pr->rem == 0) {
+ return 0;
+ }
+
+ *pr->ptr++ = letter;
+ return 1;
}
- return false; /* filled buffer */
-}
+ else {
+ if (pr->rem == -INT_MAX) {
+ pr->rem = 1;
+ return -1;
+ }
+ --pr->rem;
+ return 1;
+ }
+}
-int snprintf(char *buf, size_t size, const char *fmt, ...)
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
{
- va_list ap;
- struct for_snprintf pr;
+ if (size <= INT_MAX) {
+ int bytes;
+ struct for_snprintf pr;
+
+ pr.ptr = buf;
+ pr.rem = size;
- pr.ptr = (unsigned char *)buf;
- pr.bytes = 0;
- pr.max = size;
+ bytes = vuprintf(sprfunc, &pr, fmt, ap);
- va_start(ap, fmt);
- format(sprfunc, &pr, fmt, ap);
- va_end(ap);
+ if (size) {
+ *pr.ptr = '\0';
+ }
+ else if (pr.rem > 0) {
+ goto overflow;
+ }
- /* make sure it ends with a trailing zero */
- pr.ptr[(pr.bytes < pr.max) ? 0 : -1] = '\0';
-
- return pr.bytes;
+ return bytes;
+ }
+
+overflow:
+ errno = EOVERFLOW;
+ return -1;
}
-int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
+int snprintf(char *buf, size_t size, const char *fmt, ...)
{
- struct for_snprintf pr;
+ if (size <= INT_MAX) {
+ int bytes;
+ struct for_snprintf pr;
+ va_list ap;
+
+ pr.ptr = buf;
+ pr.rem = size;
- pr.ptr = (unsigned char *)buf;
- pr.bytes = 0;
- pr.max = size;
+ va_start(ap, fmt);
+ bytes = vuprintf(sprfunc, &pr, fmt, ap);
+ va_end(ap);
- format(sprfunc, &pr, fmt, ap);
+ if (size) {
+ *pr.ptr = '\0';
+ }
+ else if (pr.rem > 0) {
+ goto overflow;
+ }
+
+ return bytes;
+ }
- /* make sure it ends with a trailing zero */
- pr.ptr[(pr.bytes < pr.max) ? 0 : -1] = '\0';
-
- return pr.bytes;
+overflow:
+ errno = EOVERFLOW;
+ return -1;
}
diff --git a/firmware/logf.c b/firmware/logf.c
index bdc5ad9cc0..3f9bd36112 100644
--- a/firmware/logf.c
+++ b/firmware/logf.c
@@ -38,7 +38,7 @@
#endif
#include "logf.h"
#include "serial.h"
-#include "format.h"
+#include "vuprintf.h"
#ifdef HAVE_USBSTACK
#include "usb_core.h"
@@ -193,7 +193,7 @@ static void check_logfindex(void)
}
}
-static int logf_push(void *userp, unsigned char c)
+static int logf_push(void *userp, int c)
{
(void)userp;
@@ -210,7 +210,7 @@ static int logf_push(void *userp, unsigned char c)
}
#endif
- return true;
+ return 1;
}
void _logf(const char *fmt, ...)
@@ -310,7 +310,7 @@ void logf_panic_dump(int *y)
#endif
#ifdef ROCKBOX_HAS_LOGDISKF
-static int logdiskf_push(void *userp, unsigned char c)
+static int logdiskf_push(void *userp, int c)
{
(void)userp;
@@ -319,11 +319,11 @@ static int logdiskf_push(void *userp, unsigned char c)
{
strcpy(&logdiskfbuffer[logdiskfindex-8], "LOGFULL");
logdiskfindex=MAX_LOGDISKF_SIZE;
- return false;
+ return 0;
}
logdiskfbuffer[logdiskfindex++] = c;
- return true;
+ return 1;
}
static void flush_buffer(void);
diff --git a/firmware/target/arm/s3c2440/uart-s3c2440.c b/firmware/target/arm/s3c2440/uart-s3c2440.c
index e9c9345103..47f11a92b6 100644
--- a/firmware/target/arm/s3c2440/uart-s3c2440.c
+++ b/firmware/target/arm/s3c2440/uart-s3c2440.c
@@ -60,7 +60,7 @@ void tx_writec(unsigned char c)
}
-static int uart_push(void *user_data, unsigned char ch)
+static int uart_push(void *user_data, int ch)
{
(void)user_data;