#include <stdio.h>
enum flags {
- FL_ZERO = 0x01, /* Zero modifier */
- FL_MINUS = 0x02, /* Minus modifier */
- FL_PLUS = 0x04, /* Plus modifier */
- FL_TICK = 0x08, /* ' modifier */
- FL_SPACE = 0x10, /* Space modifier */
- FL_HASH = 0x20, /* # modifier */
- FL_SIGNED = 0x40, /* Number is signed */
- FL_UPPER = 0x80 /* Upper case digits */
+ FL_ZERO = 0x01, /* Zero modifier */
+ FL_MINUS = 0x02, /* Minus modifier */
+ FL_PLUS = 0x04, /* Plus modifier */
+ FL_TICK = 0x08, /* ' modifier */
+ FL_SPACE = 0x10, /* Space modifier */
+ FL_HASH = 0x20, /* # modifier */
+ FL_SIGNED = 0x40, /* Number is signed */
+ FL_UPPER = 0x80 /* Upper case digits */
};
/* These may have to be adjusted on certain implementations */
enum ranks {
- rank_char = -2,
- rank_short = -1,
- rank_int = 0,
- rank_long = 1,
- rank_longlong = 2
+ rank_char = -2,
+ rank_short = -1,
+ rank_int = 0,
+ rank_long = 1,
+ rank_longlong = 2
};
#define MIN_RANK rank_char
format_int(char *q, size_t n, uintmax_t val, enum flags flags,
int base, int width, int prec)
{
- char *qq;
- size_t o = 0, oo;
- static const char lcdigits[] = "0123456789abcdef";
- static const char ucdigits[] = "0123456789ABCDEF";
- const char *digits;
- uintmax_t tmpval;
- int minus = 0;
- int ndigits = 0, nchars;
- int tickskip, b4tick;
-
- /* Select type of digits */
- digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
-
- /* If signed, separate out the minus */
- if ( flags & FL_SIGNED && (intmax_t)val < 0 ) {
- minus = 1;
- val = (uintmax_t)(-(intmax_t)val);
- }
-
- /* Count the number of digits needed. This returns zero for 0. */
- tmpval = val;
- while ( tmpval ) {
- tmpval /= base;
- ndigits++;
- }
-
- /* Adjust ndigits for size of output */
-
- if ( flags & FL_HASH && base == 8 ) {
- if ( prec < ndigits+1 )
- prec = ndigits+1;
- }
-
- if ( ndigits < prec ) {
- ndigits = prec; /* Mandatory number padding */
- } else if ( val == 0 ) {
- ndigits = 1; /* Zero still requires space */
- }
-
- /* For ', figure out what the skip should be */
- if ( flags & FL_TICK ) {
- tickskip = (base == 16) ? 4 : 3;
- } else {
- tickskip = ndigits; /* No tick marks */
- }
-
- /* Tick marks aren't digits, but generated by the number converter */
- ndigits += (ndigits-1)/tickskip;
-
- /* Now compute the number of nondigits */
- nchars = ndigits;
-
- if ( minus || (flags & (FL_PLUS|FL_SPACE)) )
- nchars++; /* Need space for sign */
- if ( (flags & FL_HASH) && base == 16 ) {
- nchars += 2; /* Add 0x for hex */
- }
-
- /* Emit early space padding */
- if ( !(flags & (FL_MINUS|FL_ZERO)) && width > nchars ) {
- while ( width > nchars ) {
- EMIT(' ');
- width--;
+ char *qq;
+ size_t o = 0, oo;
+ static const char lcdigits[] = "0123456789abcdef";
+ static const char ucdigits[] = "0123456789ABCDEF";
+ const char *digits;
+ uintmax_t tmpval;
+ int minus = 0;
+ int ndigits = 0, nchars;
+ int tickskip, b4tick;
+
+ /* Select type of digits */
+ digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
+
+ /* If signed, separate out the minus */
+ if (flags & FL_SIGNED && (intmax_t) val < 0) {
+ minus = 1;
+ val = (uintmax_t) (-(intmax_t) val);
}
- }
-
- /* Emit nondigits */
- if ( minus )
- EMIT('-');
- else if ( flags & FL_PLUS )
- EMIT('+');
- else if ( flags & FL_SPACE )
- EMIT(' ');
-
- if ( (flags & FL_HASH) && base == 16 ) {
- EMIT('0');
- EMIT((flags & FL_UPPER) ? 'X' : 'x');
- }
-
- /* Emit zero padding */
- if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) {
- while ( width > nchars ) {
- EMIT('0');
- width--;
+
+ /* Count the number of digits needed. This returns zero for 0. */
+ tmpval = val;
+ while (tmpval) {
+ tmpval /= base;
+ ndigits++;
+ }
+
+ /* Adjust ndigits for size of output */
+
+ if (flags & FL_HASH && base == 8) {
+ if (prec < ndigits + 1)
+ prec = ndigits + 1;
}
- }
-
- /* Generate the number. This is done from right to left. */
- q += ndigits; /* Advance the pointer to end of number */
- o += ndigits;
- qq = q; oo = o; /* Temporary values */
-
- b4tick = tickskip;
- while ( ndigits > 0 ) {
- if ( !b4tick-- ) {
- qq--; oo--; ndigits--;
- if ( oo < n ) *qq = '_';
- b4tick = tickskip-1;
+
+ if (ndigits < prec) {
+ ndigits = prec; /* Mandatory number padding */
+ } else if (val == 0) {
+ ndigits = 1; /* Zero still requires space */
}
- qq--; oo--; ndigits--;
- if ( oo < n ) *qq = digits[val%base];
- val /= base;
- }
-
- /* Emit late space padding */
- while ( (flags & FL_MINUS) && width > nchars ) {
- EMIT(' ');
- width--;
- }
-
- return o;
-}
+ /* For ', figure out what the skip should be */
+ if (flags & FL_TICK) {
+ tickskip = (base == 16) ? 4 : 3;
+ } else {
+ tickskip = ndigits; /* No tick marks */
+ }
+
+ /* Tick marks aren't digits, but generated by the number converter */
+ ndigits += (ndigits - 1) / tickskip;
+
+ /* Now compute the number of nondigits */
+ nchars = ndigits;
+
+ if (minus || (flags & (FL_PLUS | FL_SPACE)))
+ nchars++; /* Need space for sign */
+ if ((flags & FL_HASH) && base == 16) {
+ nchars += 2; /* Add 0x for hex */
+ }
+
+ /* Emit early space padding */
+ if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars) {
+ while (width > nchars) {
+ EMIT(' ');
+ width--;
+ }
+ }
+
+ /* Emit nondigits */
+ if (minus)
+ EMIT('-');
+ else if (flags & FL_PLUS)
+ EMIT('+');
+ else if (flags & FL_SPACE)
+ EMIT(' ');
+
+ if ((flags & FL_HASH) && base == 16) {
+ EMIT('0');
+ EMIT((flags & FL_UPPER) ? 'X' : 'x');
+ }
+
+ /* Emit zero padding */
+ if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits) {
+ while (width > nchars) {
+ EMIT('0');
+ width--;
+ }
+ }
+
+ /* Generate the number. This is done from right to left. */
+ q += ndigits; /* Advance the pointer to end of number */
+ o += ndigits;
+ qq = q;
+ oo = o; /* Temporary values */
+
+ b4tick = tickskip;
+ while (ndigits > 0) {
+ if (!b4tick--) {
+ qq--;
+ oo--;
+ ndigits--;
+ if (oo < n)
+ *qq = '_';
+ b4tick = tickskip - 1;
+ }
+ qq--;
+ oo--;
+ ndigits--;
+ if (oo < n)
+ *qq = digits[val % base];
+ val /= base;
+ }
+
+ /* Emit late space padding */
+ while ((flags & FL_MINUS) && width > nchars) {
+ EMIT(' ');
+ width--;
+ }
+
+ return o;
+}
int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
{
- const char *p = format;
- char ch;
- char *q = buffer;
- size_t o = 0; /* Number of characters output */
- uintmax_t val = 0;
- int rank = rank_int; /* Default rank */
- int width = 0;
- int prec = -1;
- int base;
- size_t sz;
- enum flags flags = 0;
- enum {
- st_normal, /* Ground state */
- st_flags, /* Special flags */
- st_width, /* Field width */
- st_prec, /* Field precision */
- st_modifiers /* Length or conversion modifiers */
- } state = st_normal;
- const char *sarg; /* %s string argument */
- char carg; /* %c char argument */
- int slen; /* String length */
-
- while ( (ch = *p++) ) {
- switch ( state ) {
- case st_normal:
- if ( ch == '%' ) {
- state = st_flags;
- flags = 0; rank = rank_int; width = 0; prec = -1;
- } else {
- EMIT(ch);
- }
- break;
-
- case st_flags:
- switch ( ch ) {
- case '-':
- flags |= FL_MINUS;
- break;
- case '+':
- flags |= FL_PLUS;
- break;
- case '\'':
- flags |= FL_TICK;
- break;
- case ' ':
- flags |= FL_SPACE;
- break;
- case '#':
- flags |= FL_HASH;
- break;
- case '0':
- flags |= FL_ZERO;
- break;
- default:
- state = st_width;
- p--; /* Process this character again */
- break;
- }
- break;
-
- case st_width:
- if ( ch >= '0' && ch <= '9' ) {
- width = width*10+(ch-'0');
- } else if ( ch == '*' ) {
- width = va_arg(ap, int);
- if ( width < 0 ) {
- width = -width;
- flags |= FL_MINUS;
- }
- } else if ( ch == '.' ) {
- prec = 0; /* Precision given */
- state = st_prec;
- } else {
- state = st_modifiers;
- p--; /* Process this character again */
- }
- break;
-
- case st_prec:
- if ( ch >= '0' && ch <= '9' ) {
- prec = prec*10+(ch-'0');
- } else if ( ch == '*' ) {
- prec = va_arg(ap, int);
- if ( prec < 0 )
- prec = -1;
- } else {
- state = st_modifiers;
- p--; /* Process this character again */
- }
- break;
-
- case st_modifiers:
- switch ( ch ) {
- /* Length modifiers - nonterminal sequences */
- case 'h':
- rank--; /* Shorter rank */
- break;
- case 'l':
- rank++; /* Longer rank */
- break;
- case 'j':
- rank = INTMAX_RANK;
- break;
- case 'z':
- rank = SIZE_T_RANK;
- break;
- case 't':
- rank = PTRDIFF_T_RANK;
- break;
- case 'L':
- case 'q':
- rank += 2;
- break;
- default:
- /* Output modifiers - terminal sequences */
- state = st_normal; /* Next state will be normal */
- if ( rank < MIN_RANK ) /* Canonicalize rank */
- rank = MIN_RANK;
- else if ( rank > MAX_RANK )
- rank = MAX_RANK;
-
- switch ( ch ) {
- case 'P': /* Upper case pointer */
- flags |= FL_UPPER;
- /* fall through */
- case 'p': /* Pointer */
- base = 16;
- prec = (CHAR_BIT*sizeof(void *)+3)/4;
- flags |= FL_HASH;
- val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
- goto is_integer;
-
- case 'd': /* Signed decimal output */
- case 'i':
- base = 10;
- flags |= FL_SIGNED;
- switch (rank) {
- case rank_char:
- /* Yes, all these casts are needed... */
- val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
- break;
- case rank_short:
- val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
- break;
- case rank_int:
- val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
- break;
- case rank_long:
- val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
- break;
- case rank_longlong:
- val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
- break;
- }
- goto is_integer;
- case 'o': /* Octal */
- base = 8;
- goto is_unsigned;
- case 'u': /* Unsigned decimal */
- base = 10;
- goto is_unsigned;
- case 'X': /* Upper case hexadecimal */
- flags |= FL_UPPER;
- /* fall through */
- case 'x': /* Hexadecimal */
- base = 16;
- goto is_unsigned;
-
- is_unsigned:
- switch (rank) {
- case rank_char:
- val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
- break;
- case rank_short:
- val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
- break;
- case rank_int:
- val = (uintmax_t)va_arg(ap, unsigned int);
- break;
- case rank_long:
- val = (uintmax_t)va_arg(ap, unsigned long);
- break;
- case rank_longlong:
- val = (uintmax_t)va_arg(ap, unsigned long long);
- break;
- }
- /* fall through */
-
- is_integer:
- sz = format_int(q, (o<n) ? n-o : 0, val, flags, base, width, prec);
- q += sz; o += sz;
- break;
-
- case 'c': /* Character */
- carg = (char)va_arg(ap, int);
- sarg = &carg;
- slen = 1;
- goto is_string;
- case 's': /* String */
- sarg = va_arg(ap, const char *);
- sarg = sarg ? sarg : "(null)";
- slen = strlen(sarg);
- goto is_string;
-
- is_string:
- {
- char sch;
- int i;
-
- if ( prec != -1 && slen > prec )
- slen = prec;
-
- if ( width > slen && !(flags & FL_MINUS) ) {
- char pad = (flags & FL_ZERO) ? '0' : ' ';
- while ( width > slen ) {
- EMIT(pad);
- width--;
- }
+ const char *p = format;
+ char ch;
+ char *q = buffer;
+ size_t o = 0; /* Number of characters output */
+ uintmax_t val = 0;
+ int rank = rank_int; /* Default rank */
+ int width = 0;
+ int prec = -1;
+ int base;
+ size_t sz;
+ enum flags flags = 0;
+ enum {
+ st_normal, /* Ground state */
+ st_flags, /* Special flags */
+ st_width, /* Field width */
+ st_prec, /* Field precision */
+ st_modifiers /* Length or conversion modifiers */
+ } state = st_normal;
+ const char *sarg; /* %s string argument */
+ char carg; /* %c char argument */
+ int slen; /* String length */
+
+ while ((ch = *p++)) {
+ switch (state) {
+ case st_normal:
+ if (ch == '%') {
+ state = st_flags;
+ flags = 0;
+ rank = rank_int;
+ width = 0;
+ prec = -1;
+ } else {
+ EMIT(ch);
}
- for ( i = slen ; i ; i-- ) {
- sch = *sarg++;
- EMIT(sch);
+ break;
+
+ case st_flags:
+ switch (ch) {
+ case '-':
+ flags |= FL_MINUS;
+ break;
+ case '+':
+ flags |= FL_PLUS;
+ break;
+ case '\'':
+ flags |= FL_TICK;
+ break;
+ case ' ':
+ flags |= FL_SPACE;
+ break;
+ case '#':
+ flags |= FL_HASH;
+ break;
+ case '0':
+ flags |= FL_ZERO;
+ break;
+ default:
+ state = st_width;
+ p--; /* Process this character again */
+ break;
}
- if ( width > slen && (flags & FL_MINUS) ) {
- while ( width > slen ) {
- EMIT(' ');
- width--;
- }
+ break;
+
+ case st_width:
+ if (ch >= '0' && ch <= '9') {
+ width = width * 10 + (ch - '0');
+ } else if (ch == '*') {
+ width = va_arg(ap, int);
+ if (width < 0) {
+ width = -width;
+ flags |= FL_MINUS;
+ }
+ } else if (ch == '.') {
+ prec = 0; /* Precision given */
+ state = st_prec;
+ } else {
+ state = st_modifiers;
+ p--; /* Process this character again */
}
- }
- break;
-
- case 'n': /* Output the number of characters written */
- {
- switch (rank) {
- case rank_char:
- *va_arg(ap, signed char *) = o;
- break;
- case rank_short:
- *va_arg(ap, signed short *) = o;
- break;
- case rank_int:
- *va_arg(ap, signed int *) = o;
- break;
- case rank_long:
- *va_arg(ap, signed long *) = o;
- break;
- case rank_longlong:
- *va_arg(ap, signed long long *) = o;
- break;
+ break;
+
+ case st_prec:
+ if (ch >= '0' && ch <= '9') {
+ prec = prec * 10 + (ch - '0');
+ } else if (ch == '*') {
+ prec = va_arg(ap, int);
+ if (prec < 0)
+ prec = -1;
+ } else {
+ state = st_modifiers;
+ p--; /* Process this character again */
}
- }
- break;
+ break;
- default: /* Anything else, including % */
- EMIT(ch);
- break;
+ case st_modifiers:
+ switch (ch) {
+ /* Length modifiers - nonterminal sequences */
+ case 'h':
+ rank--; /* Shorter rank */
+ break;
+ case 'l':
+ rank++; /* Longer rank */
+ break;
+ case 'j':
+ rank = INTMAX_RANK;
+ break;
+ case 'z':
+ rank = SIZE_T_RANK;
+ break;
+ case 't':
+ rank = PTRDIFF_T_RANK;
+ break;
+ case 'L':
+ case 'q':
+ rank += 2;
+ break;
+ default:
+ /* Output modifiers - terminal sequences */
+ state = st_normal; /* Next state will be normal */
+ if (rank < MIN_RANK) /* Canonicalize rank */
+ rank = MIN_RANK;
+ else if (rank > MAX_RANK)
+ rank = MAX_RANK;
+
+ switch (ch) {
+ case 'P': /* Upper case pointer */
+ flags |= FL_UPPER;
+ /* fall through */
+ case 'p': /* Pointer */
+ base = 16;
+ prec = (CHAR_BIT * sizeof(void *) + 3) / 4;
+ flags |= FL_HASH;
+ val = (uintmax_t) (uintptr_t) va_arg(ap, void *);
+ goto is_integer;
+
+ case 'd': /* Signed decimal output */
+ case 'i':
+ base = 10;
+ flags |= FL_SIGNED;
+ switch (rank) {
+ case rank_char:
+ /* Yes, all these casts are needed... */
+ val =
+ (uintmax_t) (intmax_t) (signed char)va_arg(ap,
+ signed
+ int);
+ break;
+ case rank_short:
+ val =
+ (uintmax_t) (intmax_t) (signed short)va_arg(ap,
+ signed
+ int);
+ break;
+ case rank_int:
+ val = (uintmax_t) (intmax_t) va_arg(ap, signed int);
+ break;
+ case rank_long:
+ val = (uintmax_t) (intmax_t) va_arg(ap, signed long);
+ break;
+ case rank_longlong:
+ val =
+ (uintmax_t) (intmax_t) va_arg(ap, signed long long);
+ break;
+ }
+ goto is_integer;
+ case 'o': /* Octal */
+ base = 8;
+ goto is_unsigned;
+ case 'u': /* Unsigned decimal */
+ base = 10;
+ goto is_unsigned;
+ case 'X': /* Upper case hexadecimal */
+ flags |= FL_UPPER;
+ /* fall through */
+ case 'x': /* Hexadecimal */
+ base = 16;
+ goto is_unsigned;
+
+is_unsigned:
+ switch (rank) {
+ case rank_char:
+ val =
+ (uintmax_t) (unsigned char)va_arg(ap, unsigned int);
+ break;
+ case rank_short:
+ val =
+ (uintmax_t) (unsigned short)va_arg(ap,
+ unsigned int);
+ break;
+ case rank_int:
+ val = (uintmax_t) va_arg(ap, unsigned int);
+ break;
+ case rank_long:
+ val = (uintmax_t) va_arg(ap, unsigned long);
+ break;
+ case rank_longlong:
+ val = (uintmax_t) va_arg(ap, unsigned long long);
+ break;
+ }
+ /* fall through */
+
+is_integer:
+ sz = format_int(q, (o < n) ? n - o : 0, val, flags, base,
+ width, prec);
+ q += sz;
+ o += sz;
+ break;
+
+ case 'c': /* Character */
+ carg = (char)va_arg(ap, int);
+ sarg = &carg;
+ slen = 1;
+ goto is_string;
+ case 's': /* String */
+ sarg = va_arg(ap, const char *);
+ sarg = sarg ? sarg : "(null)";
+ slen = strlen(sarg);
+ goto is_string;
+
+is_string:
+ {
+ char sch;
+ int i;
+
+ if (prec != -1 && slen > prec)
+ slen = prec;
+
+ if (width > slen && !(flags & FL_MINUS)) {
+ char pad = (flags & FL_ZERO) ? '0' : ' ';
+ while (width > slen) {
+ EMIT(pad);
+ width--;
+ }
+ }
+ for (i = slen; i; i--) {
+ sch = *sarg++;
+ EMIT(sch);
+ }
+ if (width > slen && (flags & FL_MINUS)) {
+ while (width > slen) {
+ EMIT(' ');
+ width--;
+ }
+ }
+ }
+ break;
+
+ case 'n': /* Output the number of characters written */
+ {
+ switch (rank) {
+ case rank_char:
+ *va_arg(ap, signed char *) = o;
+ break;
+ case rank_short:
+ *va_arg(ap, signed short *) = o;
+ break;
+ case rank_int:
+ *va_arg(ap, signed int *) = o;
+ break;
+ case rank_long:
+ *va_arg(ap, signed long *) = o;
+ break;
+ case rank_longlong:
+ *va_arg(ap, signed long long *) = o;
+ break;
+ }
+ }
+ break;
+
+ default: /* Anything else, including % */
+ EMIT(ch);
+ break;
+ }
+ }
}
- }
}
- }
- /* Null-terminate the string */
- if ( o<n )
- *q = '\0'; /* No overflow */
- else if ( n>0 )
- buffer[n-1] = '\0'; /* Overflow - terminate at end of buffer */
+ /* Null-terminate the string */
+ if (o < n)
+ *q = '\0'; /* No overflow */
+ else if (n > 0)
+ buffer[n - 1] = '\0'; /* Overflow - terminate at end of buffer */
- return o;
+ return o;
}