📄 psnprintf.c
字号:
/* This is part of psnprintf-0.4, see the file "license-psnprintf-0.4". Originally by Alex Holkner. Changed 16/10/04 for cos by Paul Barker: Stripped out all floating point stuff. Changed headers to fit with cos. 06/11/04: Added initialisation for several vars to get rid of warnings.*/#include <cosbase.h>#include <cos/mem.h> /* for memset */#include <cos/string.h> /* for strlen */#include <psnprintf.h>int psnprintf(char *str, size_t n, const char *format, ...){ va_list args; int ret; va_start(args, format); ret = pvsnprintf(str, n, format, args); va_end(args); return ret;}#define STATE_NONE 0#define STATE_OPERATOR 1 /* Just received % */#define STATE_FLAG 2 /* Just received a flag or prefix or width */#define STATE_WIDTH 3#define STATE_BEFORE_PRECISION 4 /* just got dot */#define STATE_PRECISION 5 /* got at least one number after dot */#define STATE_PREFIX 6 /* just received prefix (h, l or L) */#define UNKNOWN_WIDTH 0#define VARIABLE_WIDTH -2#define UNKNOWN_PRECISION -1#define VARIABLE_PRECISION -2/* Following macros give reusable switch cases, used in combination * depending on current state. Sucks to do these as macros, but should * give the compiler lots of freedom to optimize. */#define CHECK_FLAG \ case '-': \ flags |= FLAG_LEFT_ALIGN; \ state = STATE_FLAG; \ break; \ case '+': \ flags |= FLAG_SIGNED; \ state = STATE_FLAG; \ break; \ case '0': \ flags |= FLAG_ZERO_PAD; \ state = STATE_FLAG; \ break; \ case ' ': \ flags |= FLAG_SIGN_PAD; \ state = STATE_FLAG; \ break; \ case '#': \ flags |= FLAG_HASH; \ state = STATE_FLAG; \ break; #define CHECK_WIDTH \ case '1': \ case '2': \ case '3': \ case '4': \ case '5': \ case '6': \ case '7': \ case '8': \ case '9': \ width = *pfmt - '0'; /* convert to integer */ \ state = STATE_WIDTH; \ break; \ case '*': \ width = VARIABLE_WIDTH; \ state = STATE_WIDTH; \ break;#define CHECK_PRECISION \ case '.': \ precision = 0; \ state = STATE_BEFORE_PRECISION; \ break; #define CHECK_PREFIX \ case 'h': \ case 'l': \ case 'L': \ prefix = *pfmt; \ state = STATE_PREFIX; \ break;#define GET_VARS \ if (width == VARIABLE_WIDTH) \ width = va_arg(ap, int); \ if (precision == VARIABLE_PRECISION) \ precision = va_arg(ap, int);#define CHECK_TYPE \ case 'd': \ case 'i': \ case 'u': \ case 'o': \ case 'x': \ case 'X': \ case 'p': \ GET_VARS \ ncount += pvsnfmt_int(&pinsertion, &nmax, *pfmt, flags, width, precision, prefix, &ap); \ state = STATE_NONE; \ break; \ case 'c': \ GET_VARS \ ncount += pvsnfmt_char(&pinsertion, &nmax, *pfmt, flags, width, precision, prefix, &ap); \ state = STATE_NONE; \ break; \ case 's': \ GET_VARS \ ncount += pvsnfmt_str(&pinsertion, &nmax, *pfmt, flags, width, precision, prefix, &ap); \ state = STATE_NONE; \ break; \ case 'n': \ *(va_arg(ap, int *)) = ncount; \ state = STATE_NONE; \ break;#define PUTCHAR(ch) \ if (nmax > 1) \ { \ *pinsertion++ = ch; \ nmax--; \ } \ ncount++; int pvsnprintf(char *str, size_t nmax, const char *format, va_list ap){ /* nmax gives total size of buffer including null * null is ALWAYS added, even if buffer too small for format * (contrary to C99) */ char *pinsertion = str; const char *pfmt = format; int ncount = 0; /* number of characters printed so far */ int state = STATE_NONE; char flags = 0; int width = 0; int precision = 0; char prefix = 0; while (*pfmt) { switch (state) { case STATE_NONE: switch (*pfmt) { case '%': state = STATE_OPERATOR; flags = FLAG_DEFAULT; width = UNKNOWN_WIDTH; precision = UNKNOWN_PRECISION; prefix = '\0'; break; default: PUTCHAR(*pfmt) } break; case STATE_OPERATOR: switch (*pfmt) { CHECK_FLAG CHECK_WIDTH CHECK_PRECISION CHECK_PREFIX CHECK_TYPE default: PUTCHAR(*pfmt) /* Unknown format, just print it (e.g. "%%") */ state = STATE_NONE; } break; case STATE_FLAG: switch (*pfmt) { CHECK_FLAG CHECK_WIDTH CHECK_PRECISION CHECK_PREFIX CHECK_TYPE } break; case STATE_WIDTH: if (*pfmt >= '0' && *pfmt <= '9' && width != -1) { width = width * 10 + (*pfmt - '0'); break; } switch (*pfmt) { CHECK_PRECISION CHECK_PREFIX CHECK_TYPE } break; case STATE_BEFORE_PRECISION: if (*pfmt >= '0' && *pfmt <= '9') { precision = *pfmt - '0'; state = STATE_PRECISION; } else if (*pfmt == '*') { precision = VARIABLE_PRECISION; state = STATE_PRECISION; } switch (*pfmt) { CHECK_PREFIX CHECK_TYPE } break; case STATE_PRECISION: if (*pfmt >= '0' && *pfmt <= '9' && precision != -1) { precision = precision * 10 + (*pfmt - '0'); break; } switch (*pfmt) { CHECK_PREFIX CHECK_TYPE } break; case STATE_PREFIX: switch (*pfmt) { CHECK_TYPE } } /* switch state */ pfmt++; } /* while *pfmt */ /* Add null if there is room * NOTE there is always room even if str doesn't fit unless * nmax initially passed in as 0. fmt functions take care to * always leave at least one free byte at end. */ if (nmax > 0) *pinsertion = '\0'; return ncount;}int pvsnfmt_char(char **pinsertion, size_t *nmax, const char fmt, int flags, int width, int precision, char prefix, va_list *ap){ if (*nmax > 1) { *(*pinsertion)++ = (char) va_arg(*ap, int); (*nmax)--; } return 1;}/* strnlen not available on all platforms.. maybe autoconf it? */size_t pstrnlen(const char *s, size_t count){ const char *p = s; while (*p && count-- > 0) p++; return p - s;} /* Format a string into the buffer. Parameters: * **pinsertion Pointer to pointer to buffer (can be reference to NULL) * *nmax Pointer to size of buffer. This is may be modified * fmt Format character ('s') * flags 0 or combination of flags (see .h file for #defines) * width Width of string, as defined in printf * precision Precision of string, as defined in printf * ap Argument list */ int pvsnfmt_str(char **pinsertion, size_t *nmax, const char fmt, int flags, int width, int precision, char prefix, va_list *ap){ const char *str = va_arg(*ap, const char *); int nprinted; int len; int pad = 0; /* Get width magnitude, set aligment flag */ if (width < 0) { width = -width; flags |= FLAG_LEFT_ALIGN; } /* Truncate due to precision */ if (precision < 0) len = strlen(str); else len = pstrnlen(str, precision); /* Determine padding length */ if (width > len) pad = width - len; /* Exit if just counting (not printing) */ if (*nmax <= 1) return len + pad; /* If right-aligned, print pad */ if ( !(flags & FLAG_LEFT_ALIGN) ) { char padchar; if (flags & FLAG_ZERO_PAD) padchar = '0'; else padchar = ' '; if (*nmax - 1 < pad) nprinted = *nmax - 1; else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -