aboutsummaryrefslogtreecommitdiffstats
path: root/erts/lib_src/common/erl_printf_format.c
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/lib_src/common/erl_printf_format.c
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/lib_src/common/erl_printf_format.c')
-rw-r--r--erts/lib_src/common/erl_printf_format.c940
1 files changed, 940 insertions, 0 deletions
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
new file mode 100644
index 0000000000..bd3d38e649
--- /dev/null
+++ b/erts/lib_src/common/erl_printf_format.c
@@ -0,0 +1,940 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * fmt:
+ * '%' <flag>* [ <width> [.<precision>]][<length>]<conversion>
+ *
+ * flag: # | O | - | <sp> | + | ' | I
+ * width: [0-9]+ | '*'
+ * precision: [0-9]+ | '*'
+ * length: hh | h | l | ll | L | j | t | b<sz>
+ * conversion: d,i | o,u,x,X | e,E | f,F | g,G | a,A | c | s | T |
+ * p | n | %
+ * sz: 8 | 16 | 32 | 64 | p
+ */
+
+/* Without this, variable argument lists break on VxWorks */
+#ifdef VXWORKS
+#include <vxWorks.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __WIN32__
+#undef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "erl_errno.h"
+#include <limits.h>
+#include "erl_printf.h"
+#include "erl_printf_format.h"
+
+#ifdef DEBUG
+#include <assert.h>
+#define ASSERT(X) assert(X)
+#else
+#define ASSERT(X)
+#endif
+
+#ifdef __WIN32__
+#define long_long LONGLONG
+#define signed_long_long LONGLONG
+#define unsigned_long_long ULONGLONG
+#undef SIZEOF_LONG_LONG
+#define SIZEOF_LONG_LONG 8
+#else
+#if SIZEOF_LONG_LONG
+#define long_long long long
+#define signed_long_long signed long long
+#define unsigned_long_long unsigned long long
+#endif
+#endif
+
+#if defined(__GNUC__)
+# undef inline
+# define inline __inline__
+#elif defined(__WIN32__)
+# undef inline
+# define inline __forceinline
+#else
+# ifndef inline
+# define inline
+# endif
+#endif
+
+#define FMTC_d 0x0000
+#define FMTC_i 0x0001
+#define FMTC_o 0x0002
+#define FMTC_u 0x0003
+#define FMTC_x 0x0004
+#define FMTC_X 0x0005
+#define FMTC_e 0x0006
+#define FMTC_E 0x0007
+#define FMTC_f 0x0008
+#define FMTC_T 0x0009
+#define FMTC_g 0x000a
+#define FMTC_G 0x000b
+#define FMTC_c 0x000c
+#define FMTC_s 0x000d
+#define FMTC_p 0x000e
+#define FMTC_n 0x000f
+#define FMTC_MASK 0x000f
+
+#define FMTL_no 0x0000
+#define FMTL_hh 0x0010
+#define FMTL_h 0x0020
+#define FMTL_l 0x0030
+#define FMTL_ll 0x0040
+#define FMTL_L 0x0050
+#define FMTL_j 0x0060
+#define FMTL_t 0x0070
+#define FMTL_MASK 0x00f0
+
+#define FMTF_alt 0x0100 /* # alterlate form ie 0x */
+#define FMTF_pad 0x0200 /* 0 zero pad */
+#define FMTF_adj 0x0400 /* left adjust */
+#define FMTF_blk 0x0800 /* add blank */
+#define FMTF_sgn 0x1000 /* add sign */
+#define FMTF_cnv 0x2000 /* decimal conversion */
+#define FMTF_cnV 0x4000 /* alternate decimal conversion */
+#define FMTF_MASK 0x7f00
+
+
+static char zeros[] = "00000000000000000000000000000000";
+static char blanks[] = " ";
+static char hex[] = "0123456789abcdef";
+static char heX[] = "0123456789ABCDEF";
+
+#define FMT(fn,arg,buf,len,count) do { \
+ int res__ = (fn)((arg),(buf),(len)); \
+ if (res__ < 0) \
+ return res__; \
+ (count) += (len); \
+ } while(0)
+
+#define FILL(fn,arg,cs,len, count) do { \
+ int __i = (len); \
+ while(__i >= sizeof(cs)-1) { \
+ FMT((fn),(arg),(cs),sizeof(cs)-1,(count)); \
+ __i -= sizeof(cs)-1; \
+ } \
+ if (__i) FMT((fn),(arg),(cs),__i,(count)); \
+ } while(0)
+
+#define BLANKS(fn,arg,n,count) FILL((fn),(arg),blanks,(n),count)
+#define ZEROS(fn,arg,n,count) FILL((fn),(arg),zeros,(n),count)
+
+#define SIGN(X) ((X) > 0 ? 1 : ((X) < 0 ? -1 : 0))
+#define USIGN(X) ((X) == 0 ? 0 : 1)
+
+int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long) = NULL;
+
+static int
+noop_fn(void *vfp, char* buf, size_t len)
+{
+ return 0;
+}
+
+static int fmt_fld(fmtfn_t fn,void* arg,
+ char* wbuf, int w, int sign,
+ int width,int precision,int fmt,int* count)
+{
+ char prefix[8];
+ char* pp = prefix;
+ int pw = 0;
+ int len;
+
+ /* format the prefix */
+ if ((sign || (fmt & (FMTF_sgn|FMTF_blk))) &&
+ (((fmt & FMTC_MASK) == FMTC_d) || ((fmt & FMTC_MASK) == FMTC_i))) {
+ if (sign < 0)
+ *pp++ = '-';
+ else if ((fmt & FMTF_sgn))
+ *pp++ = '+';
+ else if (fmt & FMTF_blk)
+ *pp++ = ' ';
+ }
+
+ if ((fmt & FMTF_alt)) {
+ switch((fmt & FMTC_MASK)) {
+ case FMTC_X: *pp++ = '0'; *pp++ = 'X'; break;
+ case FMTC_x: *pp++ = '0'; *pp++ = 'x'; break;
+ case FMTC_o: *pp++ = '0'; if (precision>1) precision--; break;
+ }
+ }
+
+ pw = pp-prefix;
+ len = ((w < precision) ? precision : w) + pw;
+
+ if (fmt & FMTF_adj) { /* left adjust */
+ if (pw)
+ FMT(fn,arg,prefix,pw,*count);
+ if (w < precision)
+ ZEROS(fn,arg,precision-w,*count);
+ FMT(fn,arg, wbuf, w, *count);
+ if (len < width)
+ BLANKS(fn,arg,width-len,*count);
+ }
+ else if ((fmt & FMTF_pad) && (precision<0)) { /* pad zeros */
+ if (pw)
+ FMT(fn,arg, prefix, pw, *count);
+ if (w < precision)
+ ZEROS(fn, arg, precision-w, *count);
+ if (len < width)
+ ZEROS(fn,arg,width-len,*count);
+ FMT(fn,arg,wbuf,w,*count);
+ }
+ else {
+ if (len < width)
+ BLANKS(fn,arg,width-len,*count);
+ if (pw)
+ FMT(fn,arg,prefix,pw,*count);
+ if (w < precision)
+ ZEROS(fn,arg,precision-w,*count);
+ FMT(fn,arg,wbuf,w,*count);
+ }
+ return 0;
+}
+
+static int fmt_long(fmtfn_t fn,void* arg,int sign,unsigned long uval,
+ int width,int precision,int fmt,int* count)
+{
+ char buf[32];
+ int base = 10;
+ int w = 0;
+ char* dc = hex;
+ char* p = buf+sizeof(buf);
+
+ switch(fmt & FMTC_MASK) {
+ case FMTC_d:
+ case FMTC_i:
+ case FMTC_u:
+ break;
+ case FMTC_o:
+ base = 8;
+ break;
+ case FMTC_X:
+ dc = heX;
+ case FMTC_x:
+ base = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* format the unsigned value */
+ if (!sign && precision) {
+ *--p = '0';
+ w++;
+ }
+ else {
+ while(uval) {
+ *--p = dc[(uval % base)];
+ uval /= base;
+ w++;
+ }
+ }
+ return fmt_fld(fn, arg, p, w, sign, width, precision, fmt, count);
+}
+
+#if SIZEOF_LONG_LONG
+
+static inline int
+do_div(unsigned_long_long *n, unsigned_long_long base)
+{
+ unsigned_long_long q = *n/base;
+ int mod = (int) (*n - q*base);
+ *n = q;
+ return mod;
+}
+
+static int fmt_long_long(fmtfn_t fn,void* arg,int sign,
+ unsigned_long_long uval,
+ int width,int precision,int fmt,int* count)
+{
+ char buf[32];
+ int base = 10;
+ int w = 0;
+ char* dc = hex;
+ char* p = buf+sizeof(buf);
+
+ switch(fmt & FMTC_MASK) {
+ case FMTC_d:
+ case FMTC_i:
+ case FMTC_u:
+ break;
+ case FMTC_o:
+ base = 8;
+ break;
+ case FMTC_X:
+ dc = heX;
+ case FMTC_x:
+ base = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* format the unsigned value */
+ if (!sign && precision) {
+ *--p = '0';
+ w++;
+ }
+ else {
+ while(uval) {
+ int m = do_div(&uval,base);
+ *--p = dc[m];
+ w++;
+ }
+ }
+ return fmt_fld(fn, arg, p, w, sign, width, precision, fmt, count);
+}
+
+#endif /* #if SIZEOF_LONG_LONG */
+
+static int fmt_double(fmtfn_t fn,void*arg,double val,
+ int width, int precision, int fmt,int* count)
+{
+ int res;
+ int fi = 0;
+ char format_str[7];
+ char sbuf[32];
+ char *bufp;
+ double dexp;
+ int exp;
+ size_t max_size = 1;
+ size_t size;
+ int new_fmt = fmt;
+ int fpe_was_unmasked;
+
+ fpe_was_unmasked = erts_printf_block_fpe ? (*erts_printf_block_fpe)() : 0;
+
+ if (val < 0.0)
+ dexp = log10(-val);
+ else if (val == 0.0)
+ dexp = 0.0;
+ else
+ dexp = log10(val);
+ exp = (int) dexp;
+
+ new_fmt &= ~FMTF_sgn;
+ new_fmt &= ~FMTF_blk;
+
+ format_str[fi++] = '%';
+ if (fmt & FMTF_alt)
+ format_str[fi++] = '#';
+ if (fmt & FMTF_sgn)
+ format_str[fi++] = '+';
+ else if (fmt & FMTF_blk)
+ format_str[fi++] = ' ';
+ format_str[fi++] = '0';
+ format_str[fi++] = '.';
+ format_str[fi++] = '*';
+
+ switch(fmt & FMTC_MASK) {
+ case FMTC_G:
+ format_str[fi] = 'E';
+ goto gG_common;
+ case FMTC_g:
+ format_str[fi] = 'e';
+ gG_common:
+ if (dexp < -4.0 || exp >= precision) {
+ fi++;
+ precision--;
+ if (precision < 1)
+ precision = 1;
+ goto eE_common;
+ }
+ /* fall through ... */
+ case FMTC_f:
+ format_str[fi++] = 'f';
+ max_size += exp > 0 ? exp : 1;
+ max_size++;
+ if (precision)
+ max_size += precision;
+ else if (fmt && FMTF_alt)
+ max_size++;
+ break;
+ case FMTC_E:
+ format_str[fi++] = 'E';
+ goto eE_common;
+ case FMTC_e:
+ format_str[fi++] = 'e';
+ eE_common: {
+ int aexp;
+
+ max_size += 4;
+ if (precision)
+ max_size += precision;
+ else if (fmt && FMTF_alt)
+ max_size++;
+ aexp = exp >= 0 ? exp : -exp;
+ if (aexp < 100)
+ max_size += 2;
+ else {
+ while (aexp) {
+ max_size++;
+ aexp /= 10;
+ }
+ }
+ break;
+ }
+ default:
+ res = -EINVAL;
+ goto out;
+ }
+
+ format_str[fi++] = '\0';
+ ASSERT(fi <= sizeof(format_str));
+
+ max_size++; /* '\0' */
+
+ if (max_size < sizeof(sbuf))
+ bufp = sbuf;
+ else {
+ bufp = (char *) malloc(sizeof(char)*max_size);
+ if (!bufp) {
+ res = -ENOMEM;
+ goto out;
+ }
+ }
+
+ size = sprintf(bufp, format_str, precision, val);
+ if (size < 0) {
+ if (errno > 0)
+ res = -errno;
+ else
+ res = -EIO;
+ goto out;
+ }
+
+ ASSERT(max_size >= size);
+
+ res = fmt_fld(fn, arg, bufp, size, 0, width, 0, new_fmt, count);
+
+ if (bufp != sbuf)
+ free((void *) bufp);
+
+ out:
+ if (erts_printf_unblock_fpe)
+ (*erts_printf_unblock_fpe)(fpe_was_unmasked);
+ return res;
+}
+
+int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
+{
+ char* ptr0 = fmt;
+ char* ptr = ptr0;
+ int count = 0;
+ int n;
+ int res = 0;
+
+ while(*ptr) {
+ unsigned long ul_val;
+ int fmt = 0;
+ int width = -1;
+ int precision = -1;
+
+ if (res < 0)
+ return res;
+
+ if (*ptr == '%') {
+ if ((n=ptr-ptr0))
+ FMT(fn,arg,ptr0,n,count);
+ ptr++;
+
+ do_flag:
+ switch(*ptr) {
+ case '#': fmt |= FMTF_alt; ptr++; goto do_flag;
+ case '0': fmt |= FMTF_pad; ptr++; goto do_flag;
+ case '-': fmt |= FMTF_adj; ptr++; goto do_flag;
+ case ' ': fmt |= FMTF_blk; ptr++; goto do_flag;
+ case '+': fmt |= FMTF_sgn; ptr++; goto do_flag;
+ case '\'': fmt |= FMTF_cnv; ptr++; goto do_flag;
+ case 'I': fmt |= FMTF_cnV; ptr++; goto do_flag;
+ }
+
+ /* width */
+ if (*ptr == '*') {
+ width = va_arg(ap, int);
+ ptr++;
+ }
+ else if (isdigit((int) *ptr)) {
+ width = *ptr++ - '0';
+ while(isdigit((int) *ptr))
+ width = 10*width + (*ptr++ - '0');
+ }
+
+ /* precision */
+ if (*ptr == '.') {
+ ptr++;
+ if (*ptr == '*') {
+ precision = va_arg(ap, int);
+ ptr++;
+ }
+ else if (isdigit((int) *ptr)) {
+ precision = *ptr++ - '0';
+ while(isdigit((int) *ptr))
+ precision = 10*precision + (*ptr++ - '0');
+ }
+ }
+
+ /* length modifier */
+ switch(*ptr) {
+ case 'b': {
+ ptr++;
+ if (*ptr == 'p') {
+ ptr++;
+#if SIZEOF_INT == SIZEOF_VOID_P
+#elif SIZEOF_LONG == SIZEOF_VOID_P
+ fmt |= FMTL_l;
+#elif SIZEOF_LONG_LONG == SIZEOF_VOID_P
+ fmt |= FMTL_ll;
+#else
+#error No integer datatype with the same size as 'void *' found
+#endif
+ }
+ else {
+ int bits = 0;
+ while(isdigit((int) *ptr))
+ bits = 10*bits + (*ptr++ - '0');
+ switch (bits) {
+ case 64:
+#if SIZEOF_INT == 8
+#elif SIZEOF_LONG == 8
+ fmt |= FMTL_l;
+#elif SIZEOF_LONG_LONG == 8
+ fmt |= FMTL_ll;
+#else
+#error No 64-bit integer datatype found
+#endif
+ break;
+ case 32:
+#if SIZEOF_INT == 4
+#elif SIZEOF_SHORT == 4
+ fmt |= FMTL_h;
+#elif SIZEOF_LONG == 4
+ fmt |= FMTL_l;
+#elif SIZEOF_LONG_LONG == 4
+ fmt |= FMTL_ll;
+#else
+#error No 32-bit integer datatype found
+#endif
+ break;
+ case 16:
+#if SIZEOF_INT == 2
+#elif SIZEOF_SHORT == 2
+ fmt |= FMTL_h;
+#elif SIZEOF_LONG == 2
+ fmt |= FMTL_l;
+#else
+#error No 16-bit integer datatype found
+#endif
+ case 8:
+#if SIZEOF_CHAR == 1
+ fmt |= FMTL_hh;
+#else
+#error Unexpected size of char
+#endif
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ break;
+ }
+ case 'h':
+ ptr++;
+ if (*ptr == 'h') {
+ ptr++;
+ fmt |= FMTL_hh;
+ }
+ else
+ fmt |= FMTL_h;
+ break;
+ case 'l':
+ ptr++;
+ if (*ptr == 'l') {
+ ptr++;
+#if SIZEOF_LONG_LONG
+ fmt |= FMTL_ll;
+#else
+ fmt |= FMTL_l;
+#endif
+ }
+ else
+ fmt |= FMTL_l;
+ break;
+ case 'L': ptr++; fmt |= FMTL_L; break;
+ case 'j': ptr++; fmt |= FMTL_j; break;
+ case 't': ptr++; fmt |= FMTL_t; break;
+ }
+
+ /* specifier */
+ switch(*ptr) {
+ case 'd': ptr++; fmt |= FMTC_d; break;
+ case 'i': ptr++; fmt |= FMTC_i; break;
+ case 'o': ptr++; fmt |= FMTC_o; break;
+ case 'u': ptr++; fmt |= FMTC_u; break;
+ case 'x': ptr++; fmt |= FMTC_x; break;
+ case 'X': ptr++; fmt |= FMTC_X; break;
+ case 'e': ptr++; fmt |= FMTC_e; break;
+ case 'E': ptr++; fmt |= FMTC_E; break;
+ case 'f': ptr++; fmt |= FMTC_f; break;
+ case 'g': ptr++; fmt |= FMTC_g; break;
+ case 'G': ptr++; fmt |= FMTC_G; break;
+ case 'c': ptr++; fmt |= FMTC_c; break;
+ case 's': ptr++; fmt |= FMTC_s; break;
+ case 'p': ptr++; fmt |= FMTC_p; break;
+ case 'n': ptr++; fmt |= FMTC_n; break;
+ case 'T': ptr++; fmt |= FMTC_T; break;
+ case '%':
+ FMT(fn,arg,ptr,1,count);
+ ptr++;
+ ptr0 = ptr;
+ continue;
+ default:
+ /* ignore */
+ ptr0 = ptr;
+ continue;
+ }
+
+ switch(fmt & FMTC_MASK) {
+ case FMTC_d:
+ case FMTC_i:
+ switch(fmt & FMTL_MASK) {
+ case FMTL_hh: {
+ signed char tval = (signed char) va_arg(ap,int);
+ ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
+ res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+ case FMTL_h: {
+ signed short tval = (signed short) va_arg(ap,int);
+ ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
+ res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+ case FMTL_l: {
+ signed long tval = (signed long) va_arg(ap,long);
+ ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
+ res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+#if SIZEOF_LONG_LONG
+ case FMTL_ll: {
+ unsigned_long_long ull_val;
+ signed_long_long tval;
+ tval = (signed_long_long) va_arg(ap,long_long);
+ ull_val = (unsigned_long_long) (tval < 0 ? (-tval) : tval);
+ res = fmt_long_long(fn,arg,SIGN(tval),ull_val,
+ width,precision,fmt,&count);
+ break;
+ }
+#endif
+ default: {
+ signed int tval = (signed int) va_arg(ap,int);
+ ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
+ res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+ }
+ break;
+ case FMTC_o:
+ case FMTC_u:
+ case FMTC_x:
+ case FMTC_X:
+ switch(fmt & FMTL_MASK) {
+ case FMTL_hh: {
+ unsigned char tval = (unsigned char) va_arg(ap,int);
+ ul_val = (unsigned long) tval;
+ res = fmt_long(fn,arg,USIGN(tval),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+ case FMTL_h: {
+ unsigned short tval = (unsigned short) va_arg(ap,int);
+ ul_val = (unsigned long) tval;
+ res = fmt_long(fn,arg,USIGN(tval),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+ case FMTL_l: {
+ ul_val = (unsigned long) va_arg(ap,long);
+ res = fmt_long(fn,arg,USIGN(ul_val),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+#if SIZEOF_LONG_LONG
+ case FMTL_ll: {
+ unsigned_long_long ull_val;
+ ull_val = (signed_long_long) va_arg(ap,long_long);
+ res = fmt_long_long(fn,arg,USIGN(ull_val),ull_val,
+ width,precision,fmt,&count);
+ break;
+ }
+#endif
+ default: {
+ unsigned int tval = (unsigned int) va_arg(ap,int);
+ ul_val = (unsigned long) tval;
+ res = fmt_long(fn,arg,USIGN(tval),ul_val,
+ width,precision,fmt,&count);
+ break;
+ }
+ }
+ break;
+ case FMTC_e:
+ case FMTC_E:
+ case FMTC_f:
+ case FMTC_g:
+ case FMTC_G:
+ if (precision < 0)
+ precision = 6;
+ switch(fmt & FMTL_MASK) {
+ case FMTL_L:
+ return -EINVAL;
+ break;
+ default:
+ res = fmt_double(fn,arg,va_arg(ap,double),
+ width,precision,fmt,&count);
+ break;
+ }
+ break;
+
+ case FMTC_c: {
+ /* fixme: add wide char support l-modifier */
+ char c = va_arg(ap,int);
+ int len = 1;
+ if (precision == 0)
+ len = 0;
+ if (width > 0 && !(fmt & FMTF_adj)) {
+ if (width > len)
+ BLANKS(fn, arg, width - len, count);
+ }
+ if (len)
+ FMT(fn,arg,&c,len,count);
+ if (width > len && fmt & FMTF_adj)
+ BLANKS(fn, arg, width - len, count);
+ break;
+ }
+
+ case FMTC_s: {
+ char* str = va_arg(ap,char*);
+ int len = strlen(str);
+ if (precision >= 0 && precision < len)
+ len = precision;
+ if (width > 0 && !(fmt & FMTF_adj)) {
+ if (width > len)
+ BLANKS(fn, arg, width - len, count);
+ }
+ if (len)
+ FMT(fn,arg,str,len,count);
+ if (width > len && fmt & FMTF_adj)
+ BLANKS(fn, arg, width - len, count);
+ break;
+ }
+
+ case FMTC_p: {
+ void* addr = va_arg(ap, void*);
+
+ res = fmt_long(fn,
+ arg,
+ USIGN((unsigned long) addr),
+ (unsigned long) addr,
+ width < 0 ? ((int) 2*sizeof(void *)) : width,
+ (precision < 0
+ ? ((int) 2*sizeof(void *))
+ : precision),
+ FMTC_x|FMTF_pad|FMTF_alt,
+ &count);
+ break;
+ }
+
+ case FMTC_n:
+ switch(fmt & FMTL_MASK) {
+ case FMTL_hh: *va_arg(ap,char*) = count; break;
+ case FMTL_h: *va_arg(ap,short*) = count; break;
+ case FMTL_l: *va_arg(ap,long*) = count; break;
+#if SIZEOF_LONG_LONG
+ case FMTL_ll: *va_arg(ap,long_long*) = count; break;
+#endif
+ default: *va_arg(ap,int*) = count; break;
+ }
+ break;
+ case FMTC_T: {
+ long prec;
+ unsigned long eterm;
+ if (!erts_printf_eterm_func)
+ return -EINVAL;
+ if (precision < 0)
+ prec = 100000;
+ else if (precision == INT_MAX)
+ prec = LONG_MAX;
+ else
+ prec = (long) precision;
+ eterm = va_arg(ap, unsigned long);
+ if (width > 0 && !(fmt & FMTF_adj)) {
+ res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec);
+ if (res < 0)
+ return res;
+ if (width > res)
+ BLANKS(fn, arg, width - res, count);
+ }
+ res = (*erts_printf_eterm_func)(fn, arg, eterm, prec);
+ if (res < 0)
+ return res;
+ count += res;
+ if (width > res && fmt & FMTF_adj)
+ BLANKS(fn, arg, width - res, count);
+ break;
+ }
+ default:
+ if ((n=ptr-ptr0))
+ FMT(fn,arg,ptr0,n,count);
+ }
+ ptr0 = ptr;
+ }
+ else
+ ptr++;
+ }
+
+ if ((n=ptr-ptr0))
+ FMT(fn,arg,ptr0,n,count);
+ return count;
+}
+
+
+int
+erts_printf_char(fmtfn_t fn, void *arg, char c)
+{
+ return (*fn)(arg, &c, 1);
+}
+
+int
+erts_printf_string(fmtfn_t fn, void *arg, char *str)
+{
+ size_t sz = strlen(str);
+ return (*fn)(arg, str, sz);
+}
+
+int
+erts_printf_buf(fmtfn_t fn, void *arg, char *buf, size_t sz)
+{
+ return (*fn)(arg, buf, sz);
+}
+
+int
+erts_printf_pointer(fmtfn_t fn, void *arg, void *ptr)
+{
+ int count = 0;
+ int res = fmt_long(fn, arg, USIGN((unsigned long) ptr),
+ (unsigned long) ptr, 2*sizeof(void *),
+ 2*sizeof(void *), FMTC_x|FMTF_pad|FMTF_alt, &count);
+ if (res < 0)
+ return res;
+ return count;
+}
+
+int
+erts_printf_ulong(fmtfn_t fn, void *arg, char conv, int pad, int width,
+ unsigned long val)
+{
+ int count = 0;
+ int res;
+ int fmt = 0;
+ int prec = -1;
+ switch (conv) {
+ case 'o': fmt |= FMTC_o; break;
+ case 'u': fmt |= FMTC_u; break;
+ case 'x': fmt |= FMTC_x; break;
+ case 'X': fmt |= FMTC_X; break;
+ case 'p': fmt |= FMTC_p; break;
+ default:
+ return -EINVAL;
+ }
+ if (pad)
+ prec = width;
+ res = fmt_long(fn, arg, USIGN(val), val, width, prec, fmt, &count);
+ if (res < 0)
+ return res;
+ return count;
+}
+
+extern int
+erts_printf_slong(fmtfn_t fn, void *arg, char conv, int pad, int width,
+ signed long val)
+{
+ int count = 0;
+ int res;
+ int fmt = 0;
+ int prec = -1;
+ unsigned long ul_val;
+ switch (conv) {
+ case 'd': fmt |= FMTC_d; break;
+ case 'i': fmt |= FMTC_i; break;
+ case 'o': fmt |= FMTC_o; break;
+ case 'x': fmt |= FMTC_x; break;
+ case 'X': fmt |= FMTC_X; break;
+ default:
+ return -EINVAL;
+ }
+ if (pad)
+ prec = width;
+ ul_val = (unsigned long) (val < 0 ? -val : val);
+ res = fmt_long(fn, arg, SIGN(val), ul_val, width, prec, fmt, &count);
+ if (res < 0)
+ return res;
+ return count;
+}
+
+int
+erts_printf_double(fmtfn_t fn, void *arg, char conv, int precision, int width,
+ double val)
+{
+ int count = 0;
+ int res;
+ int fmt = 0;
+ switch (conv) {
+ case 'e': fmt |= FMTC_e; break;
+ case 'E': fmt |= FMTC_E; break;
+ case 'f': fmt |= FMTC_f; break;
+ case 'g': fmt |= FMTC_g; break;
+ case 'G': fmt |= FMTC_G; break;
+ default:
+ return -EINVAL;
+ }
+ res = fmt_double(fn, arg, val, width, precision, fmt, &count);
+ if (res < 0)
+ return res;
+ return count;
+}