Mercurial > hg > th-libs
view th_printf.c @ 602:aec713767482
Cosmetic fix in help.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 16 Jan 2020 00:48:51 +0200 |
parents | 308531ac74e7 |
children | 7f1efa37288b |
line wrap: on
line source
/* * A printf() implementation * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2016-2020 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #ifdef TH_PRINTF_DEBUG BOOL th_printf_debug = FALSE; char *th_printf_debug_prefix = NULL; static void pflag(char *buf, const char *str, const int sep, const int flags, const int flg) { strcat(buf, (flags & flg) ? str : " "); if (sep) strcat(buf, "|"); } static const char *get_flags(const int flags) { static char buf[256]; buf[0] = 0; pflag(buf, "ALT", 1, flags, TH_PF_ALT); pflag(buf, "SGN", 1, flags, TH_PF_SIGN); pflag(buf, "SPC", 1, flags, TH_PF_SPACE); pflag(buf, "GRP", 1, flags, TH_PF_GROUP); pflag(buf, "ZER", 1, flags, TH_PF_ZERO); pflag(buf, "LFT", 0, flags, TH_PF_LEFT); return buf; } #define PP_LINE(...) \ do { \ if (th_printf_debug) { \ if (th_printf_debug_prefix != NULL) \ fputs(th_printf_debug_prefix, stdout); \ fprintf(stdout, __VA_ARGS__); \ } \ } while (0) #define PP_PRINTF(...) \ do { \ if (th_printf_debug) { \ fprintf(stdout, __VA_ARGS__); \ } \ } while (0) #else #define PP_LINE(...) /* stub */ #define PP_PRINTF(...) /* stub */ #endif static int th_vprintf_put_pstr(th_vprintf_ctx *ctx, th_vprintf_putch vputch, const char *str) { while (*str) { int ret; if ((ret = vputch(ctx, *str++)) == EOF) return ret; } return 0; } static int th_vprintf_put_repch(th_vprintf_ctx *ctx, th_vprintf_putch vputch, int count, const char ch) { while (count-- > 0) { int ret; if ((ret = vputch(ctx, ch)) == EOF) return ret; } return 0; } static int th_printf_pad_pre(th_vprintf_ctx *ctx, th_vprintf_putch vputch, int f_width, const int f_flags) { if (f_width > 0 && (f_flags & TH_PF_LEFT) == 0) return th_vprintf_put_repch(ctx, vputch, f_width, ' '); else return 0; } static int th_printf_pad_post(th_vprintf_ctx *ctx, th_vprintf_putch vputch, int f_width, const int f_flags) { if (f_width > 0 && (f_flags & TH_PF_LEFT)) return th_vprintf_put_repch(ctx, vputch, f_width, ' '); else return 0; } #define TH_PFUNC_NAME th_vprintf_buf_int #define TH_PFUNC_TYPE_S int #define TH_PFUNC_TYPE_U unsigned int #include "th_printf1.c" #define TH_PFUNC_NAME th_vprintf_buf_int64 #define TH_PFUNC_TYPE_S int64_t #define TH_PFUNC_TYPE_U uint64_t #include "th_printf1.c" TH_VPRINTF_ALTFMT_FUNC(th_vprintf_altfmt_oct) { (void) vret; (void) flags; (void) prec; // This is not very nice if (buf[len - 1] != '0') { if (*prec > 0) (*prec)--; *outlen = 1; return "0"; } else return NULL; } TH_VPRINTF_ALTFMT_FUNC(th_vprintf_altfmt_hex) { (void) buf; (void) len; (void) prec; if (vret != 0) { *outlen = 2; return (*flags & TH_PF_UPCASE) ? "0X" : "0x"; } else return NULL; } int th_vprintf_put_int_format(th_vprintf_ctx *ctx, th_vprintf_putch vputch, char *buf, int f_flags, int f_width, int f_prec, int f_len, int vret, BOOL f_neg, BOOL f_unsig, th_vprintf_altfmt_func f_alt) { int ret = 0, nspacepad, nzeropad, f_altstr_len = 0; char f_sign, *f_altstr; // Special case for value of 0 if (vret == 0) { // For NULL pointers, use "(nil)" if (f_flags & TH_PF_POINTER) { strcpy(buf, ")lin("); f_len = 5; } else if (f_prec != 0) { buf[f_len++] = '0'; buf[f_len] = 0; } } // Get alternative format string, if needed and available f_altstr = (f_flags & TH_PF_ALT) && f_alt != NULL ? f_alt(buf, f_len, vret, &f_prec, &f_flags, &f_altstr_len) : NULL; // Are we using a sign prefix? f_sign = f_unsig ? 0 : ((f_flags & TH_PF_SIGN) ? (f_neg ? '-' : '+') : (f_neg ? '-' : ((f_flags & TH_PF_SPACE) ? ' ' : 0))); // Check for left padding, negates zero prefix padding if (f_flags & TH_PF_LEFT) f_flags &= ~TH_PF_ZERO; // Calculate necessary padding, etc // // XXX TODO FIXME: The logic here is not very elegant // and would benefit from commenting on how it works. // Also same goes for the th_vprintf_altfmt_* logic. // int nlen = (f_sign ? 1 : 0) + f_altstr_len; int qlen = (f_prec > f_len ? f_prec : f_len) + nlen; if (f_flags & TH_PF_POINTER && vret == 0) { PP_LINE("^"); qlen = f_len + nlen; nspacepad = f_width > qlen ? f_width - qlen : 0; nzeropad = 0; } else if ((f_flags & TH_PF_ZERO) && f_prec < 0 && f_width > 0) { PP_LINE("#"); nzeropad = f_width - qlen; nspacepad = 0; } else { PP_LINE("$"); nzeropad = (f_prec >= 0) ? f_prec - f_len : 0; nspacepad = (f_width >= 0) ? f_width - qlen : 0; } PP_PRINTF(": vret=%3d, f_flags=[%s], f_unsig=%d, f_sign='%c', f_len=%3d, f_width=%3d, f_prec=%3d, nspacepad=%3d, nzeropad=%3d, qlen=%3d\n", vret, get_flags(f_flags), f_unsig, f_sign ? f_sign : '?', f_len, f_width, f_prec, nspacepad, nzeropad, qlen); // Do prefix padding, if any if ((ret = th_printf_pad_pre(ctx, vputch, nspacepad, f_flags)) == EOF) return ret; // Sign prefix if (f_sign && (ret = vputch(ctx, f_sign)) == EOF) return ret; // Alternative format string if (f_altstr && (ret = th_vprintf_put_pstr(ctx, vputch, f_altstr)) == EOF) return ret; // Zero padding if (nzeropad > 0 && (ret = th_vprintf_put_repch(ctx, vputch, nzeropad, '0')) == EOF) return ret; // Output the value while (f_len-- > 0) { if ((ret = vputch(ctx, buf[f_len])) == EOF) return ret; } // Do postfix padding, if any return th_printf_pad_post(ctx, vputch, nspacepad, f_flags); } int th_vprintf_put_int(th_vprintf_ctx *ctx, th_vprintf_putch vputch, va_list ap, const int f_radix, int f_flags, int f_width, int f_prec, const BOOL f_unsig, th_vprintf_altfmt_func f_alt) { char buf[32]; int f_len = 0, vret; BOOL f_neg = FALSE; if (f_flags & TH_PF_LONGLONG) { vret = th_vprintf_buf_int64(buf, sizeof(buf), &f_len, va_arg(ap, int64_t), f_radix, f_flags & TH_PF_UPCASE, f_unsig, &f_neg); } else { vret = th_vprintf_buf_int(buf, sizeof(buf), &f_len, va_arg(ap, unsigned int), f_radix, f_flags & TH_PF_UPCASE, f_unsig, &f_neg); } if (vret == EOF) return 0; return th_vprintf_put_int_format(ctx, vputch, buf, f_flags, f_width, f_prec, f_len, vret, f_neg, f_unsig, f_alt); } int th_vprintf_put_str(th_vprintf_ctx *ctx, th_vprintf_putch vputch, const char *str, int f_flags, const int f_width, const int f_prec) { int nspacepad, f_len, ret; // Check for null strings if (str == NULL) { // If the "(null)" string does not fit precision, return empty if (f_prec >= 0 && f_prec < 6) return 0; str = "(null)"; } // Compute padding etc f_len = strlen(str); if (f_prec >= 0 && f_len > f_prec) f_len = f_prec; nspacepad = f_width - f_len; // Do prefix padding, if any if ((ret = th_printf_pad_pre(ctx, vputch, nspacepad, f_flags)) == EOF) return ret; while (*str && f_len--) { if ((ret = vputch(ctx, *str++)) == EOF) return ret; } // Do postfix padding, if any return th_printf_pad_post(ctx, vputch, nspacepad, f_flags); } #ifdef TH_WIP_FLOAT_SUPPORT int th_vprintf_put_float(th_vprintf_ctx *ctx, th_vprintf_putch vputch, va_list ap, int f_flags, int f_width, int f_prec) { double pval = va_arg(ap, double); // This needs to be double for type promotion to occur int64_t val; // This is a hack, trying to avoid dereferencing a type-punned // pointer and all that stuff related to strict aliasing (although // double and int64_t should be same size and have same aliasing rules.) memcpy(&val, &pval, sizeof(int64_t)); // We have sign, exponent and mantissa BOOL f_sign = (val >> 63) & 0x01; int64_t d_exp = (val >> 52) & 0x7ff; uint64_t d_man = val & 0x0fffffffffffff; return 0; } #endif int th_vprintf_do_format( th_vprintf_ctx *ctx, th_vprintf_putch vputch, int f_width, int f_prec, int f_flags, const char fmt, va_list ap) { int ret; switch (fmt) { case 0: // Unexpected end of format string return -104; case 'c': if ((ret = th_printf_pad_pre(ctx, vputch, f_width - 1, f_flags)) == EOF) return ret; if ((ret = vputch(ctx, va_arg(ap, int))) == EOF) return ret; return th_printf_pad_post(ctx, vputch, f_width - 1, f_flags); case 'o': return th_vprintf_put_int(ctx, vputch, ap, 8, f_flags, f_width, f_prec, TRUE, th_vprintf_altfmt_oct); case 'u': case 'i': case 'd': return th_vprintf_put_int(ctx, vputch, ap, 10, f_flags, f_width, f_prec, fmt == 'u', NULL); case 'x': case 'X': if (fmt == 'X') f_flags |= TH_PF_UPCASE; return th_vprintf_put_int(ctx, vputch, ap, 16, f_flags, f_width, f_prec, TRUE, th_vprintf_altfmt_hex); case 'p': // Check for invalid flags if (f_flags & (TH_PF_LONG | TH_PF_LONGLONG)) return -120; #if (TH_PTRSIZE == 32) f_flags |= TH_PF_LONG; #elif (TH_PTRSIZE == 64) f_flags |= TH_PF_LONGLONG; #endif f_flags |= TH_PF_ALT | TH_PF_POINTER; return th_vprintf_put_int(ctx, vputch, ap, 16, f_flags, f_width, f_prec, TRUE, th_vprintf_altfmt_hex); #ifdef TH_WIP_FLOAT_SUPPORT case 'f': case 'F': return th_vprintf_put_float(ctx, vputch, ap, f_flags, f_width, f_prec); #endif case 's': return th_vprintf_put_str(ctx, vputch, va_arg(ap, char *), f_flags, f_width, f_prec); //case '%': default: return vputch(ctx, fmt); } } int th_vprintf_do(th_vprintf_ctx *ctx, th_vprintf_putch vputch, const char *fmt, va_list ap) { int ret = 0; while (*fmt) { if (*fmt != '%') { if ((ret = vputch(ctx, *fmt)) == EOF) goto out; } else { int f_width = -1, f_prec = -1, f_flags = 0; BOOL end = FALSE; fmt++; // Check for flags while (!end) { switch (*fmt) { case '#': f_flags |= TH_PF_ALT; break; case '+': f_flags |= TH_PF_SIGN; break; case '0': f_flags |= TH_PF_ZERO; break; case '-': f_flags |= TH_PF_LEFT; break; case ' ': f_flags |= TH_PF_SPACE; break; case '\'': f_flags |= TH_PF_GROUP; break; default: end = TRUE; break; } if (!end) fmt++; } // Get field width if (*fmt == '*') { fmt++; f_width = va_arg(ap, int); if (f_width < 0) { f_flags |= TH_PF_LEFT; f_width = -f_width; } } else { f_width = 0; while (th_isdigit(*fmt)) f_width = f_width * 10 + (*fmt++ - '0'); } // Check for field precision if (*fmt == '.') { fmt++; if (*fmt == '*') { fmt++; f_prec = va_arg(ap, int); } else { // If no digit after '.', precision is to be 0 f_prec = 0; while (th_isdigit(*fmt)) f_prec = f_prec * 10 + (*fmt++ - '0'); } } // Check for length modifiers (only some are supported currently) switch (*fmt) { case 'l': if (*++fmt == 'l') { f_flags |= TH_PF_LONGLONG; fmt++; } else f_flags |= TH_PF_LONG; break; case 'L': fmt++; f_flags |= TH_PF_LONGLONG; break; case 'h': case 'j': case 'z': case 't': // Unsupported for now return -202; } ret = th_vprintf_do_format(ctx, vputch, f_width, f_prec, f_flags, *fmt, ap); if (ret == EOF) goto out; if (ret < 0) return ret; } fmt++; } out: return ret == EOF ? ret : ctx->ipos; }