📄 echo.cc
字号:
#include <ctype.h>#include <stdio.h>#include <string.h>#include "inttypes.hh"#include "sulima.hh"// echo() is a simplified printf-like function that writes up to (max - 1)// characters to the (buf) array, followed by a NUL.//// The following formatting fields are recognised and have the usual meaning:// %%, %c, %d, %i, %o, %s, %u, %x, and %X. Further, the "l" option may be// inserted before the format characters "d", "i", "o", "u", "x" and "X" to// indicate that the corresponding argument is an Int64/UInt64. If "l" is// ommited, the argument is interpreted as an Int32/UInt32. Also, the "#" flag// may be inserted after "%" is the %c, %s, %o, %x and %X fields. In the %o,// %x and %X field, it has the usual effect of printing the number as a C// constant. In a %c field, it indicates that non-printable characters should// be converted to the ^X (if possible) or \xxx escape sequence (this includes// \t and \v.) In a %s field, it escapes all non-printable characters except// for spaces which are compressed to a single space. The '0', '+', '-' and '// ' flags and the field width specifier may also preceed the format// character. Note that the %p format specifier, the precision field, the '*'// syntax and the XPG4 '`' flag and '$' syntax are not supported.//// All that said, echo() wouldn't be worth the bother if the code had to be// written from scratch ;)struct State { char *p, *end;};// Write a single character.inline voidechoc(State &state, char c){ if (state.p < state.end) { *(state.p)++ = c; }}// Echo an octal, decimal or hexadecimal number.// (hex_case) is either 'a' or 'A'.// (nice) is true if the '#' flag is specified.// (padding) is the left-padding character.// (sign) is set to the prevailing '+' or ' ' flag, or is 0.// (signed) is true for signed conversions// (width) is the field width, negated for the '-' flag.static voidecho_oct(State &state, UInt64 x, char padding, int width, bool nice){ // Don't print the initial '0' in front of a 0. if (!x) nice = false; // Align left if width is negative. bool flush_left; if (width >= 0) flush_left = false; else { flush_left = true; width = -width; } // Generate the digits (least-signficant one first.) char buffer[(sizeof(x) * 8 + 2) / 3 + 2], *p = buffer; do { *p++ = (x & 7) + '0'; } while (x >>= 3); // Print the 'nice' zero. if (nice) *p++ = '0'; // Adjust the width by the number of already-generated characters. width -= (p - buffer); // Print the left padding. if (!flush_left) for (; width > 0; --width) echoc(state, padding); // Print the generated digits in a reversed order. do { echoc(state, *--p); } while (p > buffer); // Print the right padding. for (; width > 0; --width) echoc(state, ' ');}static voidecho_dec(State &state, UInt64 x, char padding, int width, bool is_signed, char sign){ // For signed conversions, grab the absolute value of the number. if (!is_signed) sign = 0; else { // Process the sign and distard it from x. if ((Int64)x < 0) { sign = '-'; x = -x; } } // Align left if width is negative. bool flush_left; if (width >= 0) flush_left = false; else { flush_left = true; width = -width; } // Generate the digits (least-signficant one first.) // The buffer size uses log10(2) == 0.30103. char buffer[sizeof(x) * 8 * 30103L / 100000L + 2], *p = buffer; do { *p++ = (x % 10) + '0'; } while (x /= 10); // Print the sign character. if (sign) *p++ = sign; // Adjust the width by the number of already-generated characters. width -= (p - buffer); // Print the left padding. if (!flush_left) for (; width > 0; --width) echoc(state, padding); // Print the generated digits in a reversed order. do { echoc(state, *--p); } while (p > buffer); // Print the right padding. for (; width > 0; --width) echoc(state, ' ');}static voidecho_hex(State &state, UInt64 x, char padding, int width, bool nice, char hex_case){ // Align left if width is negative. bool flush_left; if (width >= 0) flush_left = false; else { flush_left = true; width = -width; } // Generate the digits (least-signficant one first.) char buffer[sizeof(x) * 8 / 4 + 2], *p = buffer; do { int digit = x & 15; *p++ = digit + (digit < 10 ? '0' : hex_case - 10); } while (x >>= 4); // Print the 'nice' 0x. if (nice) { *p++ = 'x' - 'a' + hex_case; *p++ = '0'; } // Adjust the width by the number of already-generated characters. width -= (p - buffer); // Print the left padding. if (!flush_left) { for (; width > 0; --width) echoc(state, padding); } // Print the generated digits in a reversed order. do { echoc(state, *--p); } while (p > buffer); // Print the right padding. for (; width > 0; --width) echoc(state, ' ');}// Echo a character or a string.static voidecho_chr(State &state, char c, int width, bool nice){ unsigned char x = c; if (!nice || (x >= ' ' && x < '\177')) { while (width > 1) { // Pad on the left. echoc(state, ' '); --width; } echoc(state, x); while (width < -1) { // Pad on the right. echoc(state, ' '); ++width; } } else if (x > '\177') { while (width > 4) { // Pad on the left. echoc(state, ' '); --width; } echoc(state, '\\'); echoc(state, (x >> 6) & 7); echoc(state, (x >> 3) & 7); echoc(state, (x >> 0) & 7); while (width < -4) { // Pad on the right. echoc(state, ' '); ++width; } } else { while (width > 2) { // Pad on the left. echoc(state, ' '); --width; } echoc(state, '^'); echoc(state, (x + '@') & 127); width -= 2; while (width < -2) { // Pad on the right. echoc(state, ' '); ++width; } }}static voidecho_str(State &state, const char *str, int width, bool nice){ int length = 0; if (width) { if (!nice) length = strlen(str); else { for (const char *p = str; *p; ++p) { unsigned char x = *p; if ((x >= ' ' && x < '\177') || isspace(x)) ++length; else if (x > '\177') length += 4; else length += 2; } } } while (width > length) { // Pad on the left. echoc(state, ' '); --width; } if (!nice) { while (*str) echoc(state, *str++); } else { while (*str) { unsigned char x = *str++; if ((x >= ' ' && x < '\177') || isspace(x)) echoc(state, x); else if (x > '\177') { echoc(state, '\\'); echoc(state, (x >> 6) & 7); echoc(state, (x >> 3) & 7); echoc(state, (x >> 0) & 7); } else { echoc(state, '^'); echoc(state, (x + '@') & 127); } } } while (width < -length) { // Pad on the right. echoc(state, ' '); ++width; }}// The actual global interfaces.size_techo(char *buffer, size_t size, const char *templ, ...){ va_list ap; va_start(ap, templ); size = vecho(buffer, size, templ, ap); va_end(ap); return size;}size_tvecho(char *buffer, size_t size, const char *templ, va_list ap){ State state; const char *p; UInt64 x; /* Initialize the state. */ state.p = buffer; state.end = buffer + size - 1; /* Process the template. */ for (p = templ; *p; ++p) { if (*p != '%' || *++p == '%') echoc(state, *p); else { bool left = false; // - flag bool nice = false; // # flag char padding = ' '; // 0 flag char sign = 0; // + and ' ' flags int width = 0; // - flag and the field width bool lflag; // l modifier // Process a format specifier flags. for (;;) { char flag = *p++; if (flag == '-') left = true; else if (flag == '+') sign = '+'; else if (flag == ' ') sign = (sign ? sign : ' '); else if (flag == '#') nice = true; else if (flag == '0') padding = '0'; else { --p; break; } } // Process the field width. char digit = *p++; while (digit >= '0' && digit <= '9') { width = width * 10 + digit - '0'; digit = *p++; } --p; if (left) width = -width; // Process the "l" format modifier. if (*p != 'l') lflag = false; else { lflag = true; ++p; } // Process the format specifier. switch (*p) { case 'c': echo_chr(state, va_arg(ap, int), width, nice); break; case 'd': case 'i': x = (lflag ? va_arg(ap, Int64) : (Int64)va_arg(ap, Int32)); echo_dec(state, x, padding, width, true, sign); break; case 'o': x = (lflag ? va_arg(ap, UInt64) : (Int64)va_arg(ap, UInt32)); echo_oct(state, x, padding, width, nice); break; case 's': echo_str(state, va_arg(ap, const char *), width, nice); break; case 'u': x = (lflag ? va_arg(ap, UInt64) : (Int64)va_arg(ap, UInt32)); echo_dec(state, x, padding, width, false, sign); break; case 'x': x = (lflag ? va_arg(ap, UInt64) : (Int64)va_arg(ap, UInt32)); echo_hex(state, x, padding, width, nice, 'a'); break; case 'X': x = (lflag ? va_arg(ap, UInt64) : (Int64)va_arg(ap, UInt32)); echo_hex(state, x, padding, width, nice, 'A'); break; } } } // NUL-terminate the string and return the number of characters generated. *(state.p) = '\0'; return (state.p - buffer);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -