diff --git a/lib/hcl-utl.h b/lib/hcl-utl.h index 11349d1..78cf1c3 100644 --- a/lib/hcl-utl.h +++ b/lib/hcl-utl.h @@ -429,8 +429,16 @@ HCL_EXPORT hcl_oow_t hcl_count_bcstr ( # define hcl_count_oocstr(str) hcl_count_bcstr(str) #endif +#define HCL_BYTE_TO_BCSTR_RADIXMASK (0xFF) +#define HCL_BYTE_TO_BCSTR_LOWERCASE (1 << 8) - +hcl_oow_t hcl_byte_to_bcstr ( + hcl_uint8_t byte, + hcl_bch_t* buf, + hcl_oow_t size, + int flagged_radix, + hcl_bch_t fill +); HCL_EXPORT int hcl_conv_bcs_to_ucs_with_cmgr ( diff --git a/lib/logfmtv.h b/lib/logfmtv.h index a34486f..f33f586 100644 --- a/lib/logfmtv.h +++ b/lib/logfmtv.h @@ -88,6 +88,16 @@ } \ } while (0) +#define PUT_BYTE_IN_HEX(byte) do { \ + hcl_bch_t __xbuf[3]; \ + hcl_byte_to_bcstr (byte, __xbuf, HCL_COUNTOF(__xbuf), (16 | (ch == 'w'? HCL_BYTE_TO_BCSTR_LOWERCASE: 0)), '0'); \ + PUT_OOCH(__xbuf[0], 1); \ + PUT_OOCH(__xbuf[1], 1); \ +} while (0) + +/* TODO: redefine this */ +#define BYTE_PRINTABLE(x) ((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || (x == ' ')) + static int logfmtv (hcl_t* hcl, const fmtchar_t* fmt, hcl_fmtout_t* data, va_list ap, hcl_outbfmt_t outbfmt) { const fmtchar_t* percent; @@ -604,6 +614,173 @@ static int logfmtv (hcl_t* hcl, const fmtchar_t* fmt, hcl_fmtout_t* data, va_lis break; } + case 'k': + case 'K': + { + /* byte or multibyte character string in escape sequence */ + + const hcl_uint8_t* bsp; + hcl_oow_t k_hex_width; + + /* zeropad must not take effect for 'k' and 'K' + * + * 'h' & 'l' is not used to differentiate qse_mchar_t and qse_wchar_t + * because 'k' means qse_byte_t. + * 'l', results in uppercase hexadecimal letters. + * 'h' drops the leading \x in the output + * -------------------------------------------------------- + * hk -> \x + non-printable in lowercase hex + * k -> all in lowercase hex + * lk -> \x + all in lowercase hex + * -------------------------------------------------------- + * hK -> \x + non-printable in uppercase hex + * K -> all in uppercase hex + * lK -> \x + all in uppercase hex + * -------------------------------------------------------- + * with 'k' or 'K', i don't substitute "(null)" for the NULL pointer + */ + if (flagc & FLAGC_ZEROPAD) padc = ' '; + + bsp = va_arg(ap, hcl_uint8_t*); + k_hex_width = (lm_flag & (LF_H | LF_L))? 4: 2; + + if (lm_flag& LF_H) + { + if (flagc & FLAGC_DOT) + { + /* if precision is specifed, it doesn't stop at the value of zero unlike 's' or 'S' */ + for (n = 0; n < precision; n++) width -= BYTE_PRINTABLE(bsp[n])? 1: k_hex_width; + } + else + { + for (n = 0; bsp[n]; n++) width -= BYTE_PRINTABLE(bsp[n])? 1: k_hex_width; + } + } + else + { + if (flagc & FLAGC_DOT) + { + /* if precision is specifed, it doesn't stop at the value of zero unlike 's' or 'S' */ + for (n = 0; n < precision; n++) /* nothing */; + } + else + { + for (n = 0; bsp[n]; n++) /* nothing */; + } + width -= (n * k_hex_width); + } + + if (!(flagc & FLAGC_LEFTADJ) && width > 0) PUT_OOCH (padc, width); + + while (n--) + { + if ((lm_flag & LF_H) && BYTE_PRINTABLE(*bsp)) + { + PUT_OOCH(*bsp, 1); + } + else + { + hcl_bch_t xbuf[3]; + hcl_byte_to_bcstr (*bsp, xbuf, HCL_COUNTOF(xbuf), (16 | (ch == 'k'? HCL_BYTE_TO_BCSTR_LOWERCASE: 0)), '0'); + if (lm_flag & (LF_H | LF_L)) + { + PUT_OOCH('\\', 1); + PUT_OOCH('x', 1); + } + PUT_OOCH(xbuf[0], 1); + PUT_OOCH(xbuf[1], 1); + } + bsp++; + } + + if ((flagc & FLAGC_LEFTADJ) && width > 0) PUT_OOCH (padc, width); + break; + } + + case 'w': + case 'W': + { + /* unicode string in unicode escape sequence. + * + * hw -> \uXXXX, \UXXXXXXXX, printable-byte(only in ascii range) + * w -> \uXXXX, \UXXXXXXXX + * lw -> all in \UXXXXXXXX + */ + const hcl_uch_t* usp; + hcl_oow_t uwid; + + if (flagc & FLAGC_ZEROPAD) padc = ' '; + usp = va_arg(ap, hcl_uch_t*); + + if (flagc & FLAGC_DOT) + { + /* if precision is specifed, it doesn't stop at the value of zero unlike 's' or 'S' */ + for (n = 0; n < precision; n++) + { + if ((lm_flag & LF_H) && BYTE_PRINTABLE(usp[n])) uwid = 1; + else if (!(lm_flag & LF_L) && usp[n] <= 0xFFFF) uwid = 6; + else uwid = 10; + width -= uwid; + } + } + else + { + for (n = 0; usp[n]; n++) + { + if ((lm_flag & LF_H) && BYTE_PRINTABLE(usp[n])) uwid = 1; + else if (!(lm_flag & LF_L) && usp[n] <= 0xFFFF) uwid = 6; + else uwid = 10; + width -= uwid; + } + } + + if (!(flagc & FLAGC_LEFTADJ) && width > 0) PUT_OOCH (padc, width); + + while (n--) + { + if ((lm_flag & LF_H) && BYTE_PRINTABLE(*usp)) + { + PUT_OOCH(*usp, 1); + } + else if (!(lm_flag & LF_L) && *usp <= 0xFFFF) + { + hcl_uint16_t u16 = *usp; + hcl_uint8_t* bsp = (hcl_uint8_t*)&u16; + PUT_OOCH('\\', 1); + PUT_OOCH('u', 1); + #if defined(HCL_ENDIAN_BIG) + PUT_BYTE_IN_HEX(bsp[0]); + PUT_BYTE_IN_HEX(bsp[1]); + #else + PUT_BYTE_IN_HEX(bsp[1]); + PUT_BYTE_IN_HEX(bsp[0]); + #endif + } + else + { + hcl_uint32_t u32 = *usp; + hcl_uint8_t* bsp = (hcl_uint8_t*)&u32; + PUT_OOCH('\\', 1); + PUT_OOCH('U', 1); + #if defined(HCL_ENDIAN_BIG) + PUT_BYTE_IN_HEX(bsp[0]); + PUT_BYTE_IN_HEX(bsp[1]); + PUT_BYTE_IN_HEX(bsp[2]); + PUT_BYTE_IN_HEX(bsp[3]); + #else + PUT_BYTE_IN_HEX(bsp[3]); + PUT_BYTE_IN_HEX(bsp[2]); + PUT_BYTE_IN_HEX(bsp[1]); + PUT_BYTE_IN_HEX(bsp[0]); + #endif + } + usp++; + } + + if ((flagc & FLAGC_LEFTADJ) && width > 0) PUT_OOCH (padc, width); + break; + } + case 'O': /* object - ignore precision, width, adjustment */ if (hcl_outfmtobj(hcl, (data->mask & ~HCL_LOG_PREFER_JSON), va_arg(ap, hcl_oop_t), outbfmt) <= -1) goto oops; break; diff --git a/lib/utl.c b/lib/utl.c index de0c747..7f83ef7 100644 --- a/lib/utl.c +++ b/lib/utl.c @@ -359,6 +359,42 @@ hcl_bch_t* hcl_find_bchar_in_bcstr (const hcl_bch_t* ptr, hcl_bch_t c) /* ----------------------------------------------------------------------- */ +hcl_oow_t hcl_byte_to_bcstr (hcl_uint8_t byte, hcl_bch_t* buf, hcl_oow_t size, int flagged_radix, hcl_bch_t fill) +{ + hcl_bch_t tmp[(HCL_SIZEOF(hcl_uint8_t) * 8)]; + hcl_bch_t* p = tmp, * bp = buf, * be = buf + size - 1; + int radix; + hcl_bch_t radix_char; + + radix = (flagged_radix & HCL_BYTE_TO_BCSTR_RADIXMASK); + radix_char = (flagged_radix & HCL_BYTE_TO_BCSTR_LOWERCASE)? 'a': 'A'; + if (radix < 2 || radix > 36 || size <= 0) return 0; + + do + { + hcl_uint8_t digit = byte % radix; + if (digit < 10) *p++ = digit + '0'; + else *p++ = digit + radix_char - 10; + byte /= radix; + } + while (byte > 0); + + if (fill != '\0') + { + while (size - 1 > p - tmp) + { + *bp++ = fill; + size--; + } + } + + while (p > tmp && bp < be) *bp++ = *--p; + *bp = '\0'; + return bp - buf; +} + +/* ----------------------------------------------------------------------- */ + HCL_INLINE int hcl_conv_bchars_to_uchars_with_cmgr ( const hcl_bch_t* bcs, hcl_oow_t* bcslen, hcl_uch_t* ucs, hcl_oow_t* ucslen, hcl_cmgr_t* cmgr, int all)