📄 vsnprintf.c
字号:
/* * vsnprintf.c * * vsnprintf(), from which the rest of the printf() * family is built */#include <stdarg.h>#include <stddef.h>#include <inttypes.h>#include <string.h>#include <limits.h>#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 */};/* 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};#define MIN_RANK rank_char#define MAX_RANK rank_longlong#define INTMAX_RANK rank_longlong#define SIZE_T_RANK rank_long#define PTRDIFF_T_RANK rank_long#define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })static size_tformat_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--; } } /* 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--; } } 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 */ return o;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -