aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c')
-rwxr-xr-xvendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c b/vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c
new file mode 100755
index 000000000..e4b6e43ab
--- /dev/null
+++ b/vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c
@@ -0,0 +1,312 @@
+/*
+ * Minimal vsnprintf(), snprintf(), sprintf(), and sscanf() for Duktape.
+ * The supported conversion formats narrowly match what Duktape needs.
+ */
+
+#include <stdarg.h> /* va_list etc */
+#include <stddef.h> /* size_t */
+#include <stdint.h> /* SIZE_MAX */
+
+/* Write character with bound checking. Offset 'off' is updated regardless
+ * of whether an actual write is made. This is necessary to satisfy snprintf()
+ * return value semantics.
+ */
+#define DUK__WRITE_CHAR(c) do { \
+ if (off < size) { \
+ str[off] = (char) c; \
+ } \
+ off++; \
+ } while (0)
+
+/* Digits up to radix 16. */
+static const char duk__format_digits[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+/* Format an unsigned long with various options. An unsigned long is large
+ * enough for formatting all supported types.
+ */
+static size_t duk__format_long(char *str,
+ size_t size,
+ size_t off,
+ int fixed_length,
+ char pad,
+ int radix,
+ int neg_sign,
+ unsigned long v) {
+ char buf[24]; /* 2^64 = 18446744073709552000, length 20 */
+ char *required;
+ char *p;
+ int i;
+
+ /* Format in reverse order first. Ensure at least one digit is output
+ * to handle '0' correctly. Note that space padding and zero padding
+ * handle negative sign differently:
+ *
+ * %9d and -321 => ' -321'
+ * %09d and -321 => '-00000321'
+ */
+
+ for (i = 0; i < (int) sizeof(buf); i++) {
+ buf[i] = pad; /* compiles into memset() equivalent, avoid memset() dependency */
+ }
+
+ p = buf;
+ do {
+ *p++ = duk__format_digits[v % radix];
+ v /= radix;
+ } while (v != 0);
+
+ required = buf + fixed_length;
+ if (p < required && pad == (char) '0') {
+ /* Zero padding and we didn't reach maximum length: place
+ * negative sign at the last position. We can't get here
+ * with fixed_length == 0 so that required[-1] is safe.
+ *
+ * Technically we should only do this for 'neg_sign == 1',
+ * but it's OK to advance the pointer even when that's not
+ * the case.
+ */
+ p = required - 1;
+ }
+ if (neg_sign) {
+ *p++ = (char) '-';
+ }
+ if (p < required) {
+ p = required;
+ }
+
+ /* Now [buf,p[ contains the result in reverse; copy into place. */
+
+ while (p > buf) {
+ p--;
+ DUK__WRITE_CHAR(*p);
+ }
+
+ return off;
+}
+
+/* Parse a pointer. Must parse whatever is produced by '%p' in sprintf(). */
+static int duk__parse_pointer(const char *str, void **out) {
+ const unsigned char *p;
+ unsigned char ch;
+ int count;
+ int limit;
+ long val; /* assume void * fits into long */
+
+ /* We only need to parse what our minimal printf() produces, so that
+ * we can check for a '0x' prefix, and assume all hex digits are
+ * lowercase.
+ */
+
+ p = (const unsigned char *) str;
+ if (p[0] != (unsigned char) '0' || p[1] != (unsigned char) 'x') {
+ return 0;
+ }
+ p += 2;
+
+ for (val = 0, count = 0, limit = sizeof(void *) * 2; count < limit; count++) {
+ ch = *p++;
+
+ val <<= 4;
+ if (ch >= (unsigned char) '0' && ch <= (unsigned char) '9') {
+ val += ch - (unsigned char) '0';
+ } else if (ch >= (unsigned char) 'a' && ch <= (unsigned char) 'f') {
+ val += ch - (unsigned char) 'a' + 0x0a;
+ } else {
+ return 0;
+ }
+ }
+
+ /* The input may end at a NUL or garbage may follow. As long as we
+ * parse the '%p' correctly, garbage is allowed to follow, and the
+ * JX pointer parsing also relies on that.
+ */
+
+ *out = (void *) val;
+ return 1;
+}
+
+/* Minimal vsnprintf() entry point. */
+int duk_minimal_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ size_t off = 0;
+ const char *p;
+#if 0
+ const char *p_tmp;
+ const char *p_fmt_start;
+#endif
+ char c;
+ char pad;
+ int fixed_length;
+ int is_long;
+
+ /* Assume str != NULL unless size == 0.
+ * Assume format != NULL.
+ */
+
+ p = format;
+ for (;;) {
+ c = *p++;
+ if (c == (char) 0) {
+ break;
+ }
+ if (c != (char) '%') {
+ DUK__WRITE_CHAR(c);
+ continue;
+ }
+
+ /* Start format sequence. Scan flags and format specifier. */
+
+#if 0
+ p_fmt_start = p - 1;
+#endif
+ is_long = 0;
+ pad = ' ';
+ fixed_length = 0;
+ for (;;) {
+ c = *p++;
+ if (c == (char) 'l') {
+ is_long = 1;
+ } else if (c == (char) '0') {
+ /* Only support pad character '0'. */
+ pad = '0';
+ } else if (c >= (char) '1' && c <= (char) '9') {
+ /* Only support fixed lengths 1-9. */
+ fixed_length = (int) (c - (char) '0');
+ } else if (c == (char) 'd') {
+ long v;
+ int neg_sign = 0;
+ if (is_long) {
+ v = va_arg(ap, long);
+ } else {
+ v = (long) va_arg(ap, int);
+ }
+ if (v < 0) {
+ neg_sign = 1;
+ v = -v;
+ }
+ off = duk__format_long(str, size, off, fixed_length, pad, 10, neg_sign, (unsigned long) v);
+ break;
+ } else if (c == (char) 'u') {
+ unsigned long v;
+ if (is_long) {
+ v = va_arg(ap, unsigned long);
+ } else {
+ v = (unsigned long) va_arg(ap, unsigned int);
+ }
+ off = duk__format_long(str, size, off, fixed_length, pad, 10, 0, v);
+ break;
+ } else if (c == (char) 'x') {
+ unsigned long v;
+ if (is_long) {
+ v = va_arg(ap, unsigned long);
+ } else {
+ v = (unsigned long) va_arg(ap, unsigned int);
+ }
+ off = duk__format_long(str, size, off, fixed_length, pad, 16, 0, v);
+ break;
+ } else if (c == (char) 'c') {
+ char v;
+ v = (char) va_arg(ap, int); /* intentionally not 'char' */
+ DUK__WRITE_CHAR(v);
+ break;
+ } else if (c == (char) 's') {
+ const char *v;
+ char c_tmp;
+ v = va_arg(ap, const char *);
+ if (v) {
+ for (;;) {
+ c_tmp = *v++;
+ if (c_tmp) {
+ DUK__WRITE_CHAR(c_tmp);
+ } else {
+ break;
+ }
+ }
+ }
+ break;
+ } else if (c == (char) 'p') {
+ /* Assume a void * can be represented by 'long'. This is not
+ * always the case. NULL pointer is printed out as 0x0000...
+ */
+ void *v;
+ v = va_arg(ap, void *);
+ DUK__WRITE_CHAR('0');
+ DUK__WRITE_CHAR('x');
+ off = duk__format_long(str, size, off, sizeof(void *) * 2, '0', 16, 0, (unsigned long) v);
+ break;
+ } else {
+ /* Unrecognized, bail out early. We could also emit the format
+ * specifier verbatim, but it'd be a waste of footprint because
+ * this case should never happen in practice.
+ */
+#if 0
+ DUK__WRITE_CHAR('!');
+#endif
+#if 0
+ for (p_tmp = p_fmt_start; p_tmp != p; p_tmp++) {
+ DUK__WRITE_CHAR(*p_tmp);
+ }
+ break;
+#endif
+ goto finish;
+ }
+ }
+ }
+
+ finish:
+ if (off < size) {
+ str[off] = (char) 0; /* No increment for 'off', not counted in return value. */
+ } else if (size > 0) {
+ /* Forced termination. */
+ str[size - 1] = 0;
+ }
+
+ return (int) off;
+}
+
+/* Minimal snprintf() entry point. */
+int duk_minimal_snprintf(char *str, size_t size, const char *format, ...) {
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = duk_minimal_vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/* Minimal sprintf() entry point. */
+int duk_minimal_sprintf(char *str, const char *format, ...) {
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = duk_minimal_vsnprintf(str, SIZE_MAX, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/* Minimal sscanf() entry point. */
+int duk_minimal_sscanf(const char *str, const char *format, ...) {
+ va_list ap;
+ int ret;
+ void **out;
+
+ /* Only the exact "%p" format is supported. */
+ if (format[0] != (char) '%' ||
+ format[1] != (char) 'p' ||
+ format[2] != (char) 0) {
+ }
+
+ va_start(ap, format);
+ out = va_arg(ap, void **);
+ ret = duk__parse_pointer(str, out);
+ va_end(ap);
+
+ return ret;
+}
+
+#undef DUK__WRITE_CHAR