int erts_printf_format()

in erts/lib_src/common/erl_printf_format.c [457:850]


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) {
	ErlPfUWord 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 if (*ptr == 'e') {
		    ptr++;
#if SIZEOF_INT == ERTS_SIZEOF_ETERM
#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM
		    fmt |= FMTL_l;
#elif SIZEOF_LONG_LONG == ERTS_SIZEOF_ETERM
		    fmt |= FMTL_ll;
#else
#error No integer datatype with the same size as Eterm 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_d; 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:
		switch(fmt & FMTL_MASK) {
		case FMTL_hh: {
		    signed char tval = (signed char) va_arg(ap,int);
		    ul_val = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
		    res = fmt_uword(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 = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
		    res = fmt_uword(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 = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
		    res = fmt_uword(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 = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
		    res = fmt_uword(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 = (ErlPfUWord) tval;
		    res = fmt_uword(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 = (ErlPfUWord) tval;
		    res = fmt_uword(fn,arg,USIGN(tval),ul_val,
				   width,precision,fmt,&count);
		    break;
		}
		case FMTL_l: {
		    ul_val = (ErlPfUWord) va_arg(ap,long);
		    res = fmt_uword(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 = (ErlPfUWord) tval;
		    res = fmt_uword(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 = (precision >= 0) ? my_strnlen(str,precision) : strlen(str);
		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_uword(fn,
			       arg,
			       USIGN((ErlPfUWord) addr),
			       (ErlPfUWord) 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: {    /* Eterm */
		long prec;
		ErlPfEterm 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, ErlPfEterm);
		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;
}