summaryrefslogtreecommitdiff
path: root/sys/src/libstdio/vfprintf.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/libstdio/vfprintf.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libstdio/vfprintf.c')
-rwxr-xr-xsys/src/libstdio/vfprintf.c558
1 files changed, 558 insertions, 0 deletions
diff --git a/sys/src/libstdio/vfprintf.c b/sys/src/libstdio/vfprintf.c
new file mode 100755
index 000000000..4600caff6
--- /dev/null
+++ b/sys/src/libstdio/vfprintf.c
@@ -0,0 +1,558 @@
+/*
+ * pANS stdio -- vfprintf
+ */
+#include "iolib.h"
+/*
+ * Leading flags
+ */
+#define SPACE 1 /* ' ' prepend space if no sign printed */
+#define ALT 2 /* '#' use alternate conversion */
+#define SIGN 4 /* '+' prepend sign, even if positive */
+#define LEFT 8 /* '-' left-justify */
+#define ZPAD 16 /* '0' zero-pad */
+/*
+ * Trailing flags
+ */
+#define SHORT 32 /* 'h' convert a short integer */
+#define LONG 64 /* 'l' convert a long integer */
+#define LDBL 128 /* 'L' convert a long double */
+#define PTR 256 /* convert a void * (%p) */
+
+static int lflag[] = { /* leading flags */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+SPACE, 0, 0, ALT, 0, 0, 0, 0, /* sp ! " # $ % & ' */
+0, 0, 0, SIGN, 0, LEFT, 0, 0, /* ( ) * + , - . / */
+ZPAD, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
+0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */
+0, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */
+0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */
+0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
+0, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */
+0, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */
+0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */
+0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */
+0, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */
+
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static int tflag[] = { /* trailing flags */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */
+0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */
+0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
+0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */
+0, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */
+0, 0, 0, 0, LDBL, 0, 0, 0, /* H I J K L M N O */
+0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
+0, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */
+0, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */
+SHORT, 0, 0, 0, LONG, 0, 0, 0, /* h i j k l m n o */
+0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */
+0, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */
+
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static int ocvt_E(FILE *, va_list *, int, int, int);
+static int ocvt_G(FILE *, va_list *, int, int, int);
+static int ocvt_X(FILE *, va_list *, int, int, int);
+static int ocvt_c(FILE *, va_list *, int, int, int);
+static int ocvt_d(FILE *, va_list *, int, int, int);
+static int ocvt_e(FILE *, va_list *, int, int, int);
+static int ocvt_f(FILE *, va_list *, int, int, int);
+static int ocvt_g(FILE *, va_list *, int, int, int);
+static int ocvt_n(FILE *, va_list *, int, int, int);
+static int ocvt_o(FILE *, va_list *, int, int, int);
+static int ocvt_p(FILE *, va_list *, int, int, int);
+static int ocvt_s(FILE *, va_list *, int, int, int);
+static int ocvt_u(FILE *, va_list *, int, int, int);
+static int ocvt_x(FILE *, va_list *, int, int, int);
+
+static int(*ocvt[])(FILE *, va_list *, int, int, int) = {
+0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */
+0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */
+0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
+0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */
+0, 0, 0, 0, 0, ocvt_E, 0, ocvt_G, /* @ A B C D E F G */
+0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */
+0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
+ocvt_X, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */
+0, 0, 0, ocvt_c, ocvt_d, ocvt_e, ocvt_f, ocvt_g, /* ` a b c d e f g */
+0, ocvt_d, 0, 0, 0, 0, ocvt_n, ocvt_o, /* h i j k l m n o */
+ocvt_p, 0, 0, ocvt_s, 0, ocvt_u, 0, 0, /* p q r s t u v w */
+ocvt_x, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */
+
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static int nprint;
+
+QLock _stdiolk;
+
+int
+vfprintf(FILE *f, const char *s, va_list args)
+{
+ int flags, width, precision;
+
+ qlock(&_stdiolk);
+
+ nprint = 0;
+ while(*s){
+ if(*s != '%'){
+ putc(*s++, f);
+ nprint++;
+ continue;
+ }
+ s++;
+ flags = 0;
+ while(lflag[*s&_IO_CHMASK]) flags |= lflag[*s++&_IO_CHMASK];
+ if(*s == '*'){
+ width = va_arg(args, int);
+ s++;
+ if(width<0){
+ flags |= LEFT;
+ width = -width;
+ }
+ }
+ else{
+ width = 0;
+ while('0'<=*s && *s<='9') width = width*10 + *s++ - '0';
+ }
+ if(*s == '.'){
+ s++;
+ if(*s == '*'){
+ precision = va_arg(args, int);
+ s++;
+ }
+ else{
+ precision = 0;
+ while('0'<=*s && *s<='9') precision = precision*10 + *s++ - '0';
+ }
+ }
+ else
+ precision = -1;
+ while(tflag[*s&_IO_CHMASK]) flags |= tflag[*s++&_IO_CHMASK];
+ if(ocvt[*s]) nprint += (*ocvt[*s++])(f, &args, flags, width, precision);
+ else if(*s){
+ putc(*s++, f);
+ nprint++;
+ }
+ }
+
+ qunlock(&_stdiolk);
+
+ if(ferror(f)){
+ if((f->flags&STRING) && f->wp==f->rp && f->wp>f->buf){
+ *(f->wp-1) = '\0';
+ return nprint;
+ }
+ return -1;
+ }
+ return nprint;
+}
+
+static int
+ocvt_c(FILE *f, va_list *args, int flags, int width, int precision)
+{
+#pragma ref precision
+ int i;
+
+ if(!(flags&LEFT)) for(i=1; i<width; i++) putc(' ', f);
+ putc((unsigned char)va_arg(*args, int), f);
+ if(flags&LEFT) for(i=1; i<width; i++) putc(' ', f);
+ return width<1 ? 1 : width;
+}
+
+static int
+ocvt_s(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ int i, n = 0;
+ char *s;
+
+ s = va_arg(*args, char *);
+ if(!(flags&LEFT)){
+ if(precision >= 0)
+ for(i=0; i!=precision && s[i]; i++);
+ else
+ for(i=0; s[i]; i++);
+ for(; i<width; i++){
+ putc(' ', f);
+ n++;
+ }
+ }
+ if(precision >= 0){
+ for(i=0; i!=precision && *s; i++){
+ putc(*s++, f);
+ n++;
+ }
+ } else{
+ for(i=0;*s;i++){
+ putc(*s++, f);
+ n++;
+ }
+ }
+ if(flags&LEFT){
+ for(; i<width; i++){
+ putc(' ', f);
+ n++;
+ }
+ }
+ return n;
+}
+
+static int
+ocvt_n(FILE *f, va_list *args, int flags, int width, int precision)
+{
+#pragma ref f
+#pragma ref width
+#pragma ref precision
+ if(flags&SHORT)
+ *va_arg(*args, short *) = nprint;
+ else if(flags&LONG)
+ *va_arg(*args, long *) = nprint;
+ else
+ *va_arg(*args, int *) = nprint;
+ return 0;
+}
+
+/*
+ * Generic fixed-point conversion
+ * f is the output FILE *;
+ * args is the va_list * from which to get the number;
+ * flags, width and precision are the results of printf-cracking;
+ * radix is the number base to print in;
+ * alphabet is the set of digits to use;
+ * prefix is the prefix to print before non-zero numbers when
+ * using ``alternate form.''
+ */
+static int
+ocvt_fixed(FILE *f, va_list *args, int flags, int width, int precision,
+ int radix, int sgned, char alphabet[], char *prefix)
+{
+ char digits[128]; /* no reasonable machine will ever overflow this */
+ char *sign;
+ char *dp;
+ long snum;
+ unsigned long num;
+ int nout, npad, nlzero;
+
+ if(sgned){
+ if(flags&PTR) snum = (long)va_arg(*args, void *);
+ else if(flags&SHORT) snum = va_arg(*args, short);
+ else if(flags&LONG) snum = va_arg(*args, long);
+ else snum = va_arg(*args, int);
+ if(snum < 0){
+ sign = "-";
+ num = -snum;
+ } else{
+ if(flags&SIGN) sign = "+";
+ else if(flags&SPACE) sign = " ";
+ else sign = "";
+ num = snum;
+ }
+ } else {
+ sign = "";
+ if(flags&PTR) num = (long)va_arg(*args, void *);
+ else if(flags&SHORT) num = va_arg(*args, unsigned short);
+ else if(flags&LONG) num = va_arg(*args, unsigned long);
+ else num = va_arg(*args, unsigned int);
+ }
+ if(num == 0) prefix = "";
+ dp = digits;
+ do{
+ *dp++ = alphabet[num%radix];
+ num /= radix;
+ }while(num);
+ if(precision==0 && dp-digits==1 && dp[-1]=='0')
+ dp--;
+ nlzero = precision-(dp-digits);
+ if(nlzero < 0) nlzero = 0;
+ if(flags&ALT){
+ if(radix == 8) if(dp[-1]=='0' || nlzero) prefix = "";
+ }
+ else prefix = "";
+ nout = dp-digits+nlzero+strlen(prefix)+strlen(sign);
+ npad = width-nout;
+ if(npad < 0) npad = 0;
+ nout += npad;
+ if(!(flags&LEFT)){
+ if(flags&ZPAD && precision <= 0){
+ fputs(sign, f);
+ fputs(prefix, f);
+ while(npad){
+ putc('0', f);
+ --npad;
+ }
+ } else{
+ while(npad){
+ putc(' ', f);
+ --npad;
+ }
+ fputs(sign, f);
+ fputs(prefix, f);
+ }
+ while(nlzero){
+ putc('0', f);
+ --nlzero;
+ }
+ while(dp!=digits) putc(*--dp, f);
+ }
+ else{
+ fputs(sign, f);
+ fputs(prefix, f);
+ while(nlzero){
+ putc('0', f);
+ --nlzero;
+ }
+ while(dp != digits) putc(*--dp, f);
+ while(npad){
+ putc(' ', f);
+ --npad;
+ }
+ }
+ return nout;
+}
+
+static int
+ocvt_X(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789ABCDEF", "0X");
+}
+
+static int
+ocvt_d(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 10, 1, "0123456789", "");
+}
+
+static int
+ocvt_o(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 8, 0, "01234567", "0");
+}
+
+static int
+ocvt_p(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags|PTR|ALT, width, precision, 16, 0,
+ "0123456789ABCDEF", "0X");
+}
+
+static int
+ocvt_u(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 10, 0, "0123456789", "");
+}
+
+static int
+ocvt_x(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789abcdef", "0x");
+}
+
+static int ocvt_flt(FILE *, va_list *, int, int, int, char);
+
+static int
+ocvt_E(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'E');
+}
+
+static int
+ocvt_G(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'G');
+}
+
+static int
+ocvt_e(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'e');
+}
+
+static int
+ocvt_f(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'f');
+}
+
+static int
+ocvt_g(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'g');
+}
+
+static int
+ocvt_flt(FILE *f, va_list *args, int flags, int width, int precision, char afmt)
+{
+ int echr;
+ char *digits, *edigits;
+ int exponent;
+ char fmt;
+ int sign;
+ int ndig;
+ int nout, i;
+ char ebuf[20]; /* no sensible machine will overflow this */
+ char *eptr;
+ double d;
+
+ echr = 'e';
+ fmt = afmt;
+ d = va_arg(*args, double);
+ if(precision < 0) precision = 6;
+ switch(fmt){
+ case 'f':
+ digits = dtoa(d, 3, precision, &exponent, &sign, &edigits);
+ break;
+ case 'E':
+ echr = 'E';
+ fmt = 'e';
+ /* fall through */
+ case 'e':
+ digits = dtoa(d, 2, 1+precision, &exponent, &sign, &edigits);
+ break;
+ case 'G':
+ echr = 'E';
+ /* fall through */
+ case 'g':
+ if (precision > 0)
+ digits = dtoa(d, 2, precision, &exponent, &sign, &edigits);
+ else {
+ digits = dtoa(d, 0, precision, &exponent, &sign, &edigits);
+ precision = edigits - digits;
+ if (exponent > precision && exponent <= precision + 4)
+ precision = exponent;
+ }
+ if(exponent >= -3 && exponent <= precision){
+ fmt = 'f';
+ precision -= exponent;
+ }else{
+ fmt = 'e';
+ --precision;
+ }
+ break;
+ }
+ if (exponent == 9999) {
+ /* Infinity or Nan */
+ precision = 0;
+ exponent = edigits - digits;
+ fmt = 'f';
+ }
+ ndig = edigits-digits;
+ if((afmt=='g' || afmt=='G') && !(flags&ALT)){ /* knock off trailing zeros */
+ if(fmt == 'f'){
+ if(precision+exponent > ndig) {
+ precision = ndig - exponent;
+ if(precision < 0)
+ precision = 0;
+ }
+ }
+ else{
+ if(precision > ndig-1) precision = ndig-1;
+ }
+ }
+ nout = precision; /* digits after decimal point */
+ if(precision!=0 || flags&ALT) nout++; /* decimal point */
+ if(fmt=='f' && exponent>0) nout += exponent; /* digits before decimal point */
+ else nout++; /* there's always at least one */
+ if(sign || flags&(SPACE|SIGN)) nout++; /* sign */
+ if(fmt != 'f'){ /* exponent */
+ eptr = ebuf;
+ for(i=exponent<=0?1-exponent:exponent-1; i; i/=10)
+ *eptr++ = '0' + i%10;
+ while(eptr<ebuf+2) *eptr++ = '0';
+ nout += eptr-ebuf+2; /* e+99 */
+ }
+ if(!(flags&ZPAD) && !(flags&LEFT))
+ while(nout < width){
+ putc(' ', f);
+ nout++;
+ }
+ if(sign) putc('-', f);
+ else if(flags&SIGN) putc('+', f);
+ else if(flags&SPACE) putc(' ', f);
+ if(flags&ZPAD)
+ while(nout < width){
+ putc('0', f);
+ nout++;
+ }
+ if(fmt == 'f'){
+ for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f);
+ if(i == 0) putc('0', f);
+ if(precision>0 || flags&ALT) putc('.', f);
+ for(i=0; i!=precision; i++)
+ putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f);
+ }
+ else{
+ putc(digits[0], f);
+ if(precision>0 || flags&ALT) putc('.', f);
+ for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f);
+ }
+ if(fmt != 'f'){
+ putc(echr, f);
+ putc(exponent<=0?'-':'+', f);
+ while(eptr>ebuf) putc(*--eptr, f);
+ }
+ while(nout < width){
+ putc(' ', f);
+ nout++;
+ }
+ freedtoa(digits);
+ return nout;
+}