diff options
| author | Ron Yorston <rmy@pobox.com> | 2021-02-03 14:14:37 +0000 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2021-02-03 14:14:37 +0000 |
| commit | b032d8024e45eb84d21b8798cca8655b6366f01e (patch) | |
| tree | 36d4fcdd309af9eb5536fd8c41f929fc40d47380 | |
| parent | b108c7bb2181186a5d95d10a6f187c28b4c753f4 (diff) | |
| download | busybox-w32-b032d8024e45eb84d21b8798cca8655b6366f01e.tar.gz busybox-w32-b032d8024e45eb84d21b8798cca8655b6366f01e.tar.bz2 busybox-w32-b032d8024e45eb84d21b8798cca8655b6366f01e.zip | |
printf: better support for escape sequences
Upstream printf outputs one character at a time which doesn't
play well with emulation of ANSI escape sequences. Previous
workarounds for this only applied in limited circumstances.
Try a different approach: replace putchar() and printf() calls
in the printf applet with custom routines which store the output
in memory. It's only really output at the end of the program or
when a newline is detected and a non-trivial amount has been
collected.
| -rw-r--r-- | coreutils/printf.c | 108 |
1 files changed, 60 insertions, 48 deletions
diff --git a/coreutils/printf.c b/coreutils/printf.c index 01f730073..7d36fdd61 100644 --- a/coreutils/printf.c +++ b/coreutils/printf.c | |||
| @@ -152,22 +152,70 @@ static double my_xstrtod(const char *arg) | |||
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | #if ENABLE_PLATFORM_MINGW32 | 154 | #if ENABLE_PLATFORM_MINGW32 |
| 155 | static size_t fwrite_stdout(const char *s, const char *t) | 155 | static int buflen = 0; |
| 156 | static int bufmax = 0; | ||
| 157 | static char *buffer = NULL; | ||
| 158 | |||
| 159 | static void my_flush(void) | ||
| 160 | { | ||
| 161 | if (buffer) | ||
| 162 | full_write(STDOUT_FILENO, buffer, buflen); | ||
| 163 | free(buffer); | ||
| 164 | buffer = NULL; | ||
| 165 | buflen = bufmax = 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int my_putchar(int ch) | ||
| 156 | { | 169 | { |
| 157 | return fwrite(s, t - s, 1, stdout); | 170 | if (buflen + 1 >= bufmax) { |
| 171 | bufmax += 256; | ||
| 172 | buffer = xrealloc(buffer, bufmax); | ||
| 173 | } | ||
| 174 | buffer[buflen++] = ch; | ||
| 175 | if (buflen > 40 && ch == '\n') | ||
| 176 | my_flush(); | ||
| 177 | return ch; | ||
| 158 | } | 178 | } |
| 179 | |||
| 180 | static int my_printf(const char *format, ...) | ||
| 181 | { | ||
| 182 | va_list list; | ||
| 183 | char *str; | ||
| 184 | int len; | ||
| 185 | |||
| 186 | va_start(list, format); | ||
| 187 | len = vasprintf(&str, format, list); | ||
| 188 | va_end(list); | ||
| 189 | |||
| 190 | if (len < 0) | ||
| 191 | bb_die_memory_exhausted(); | ||
| 192 | |||
| 193 | if (buflen + len >= bufmax) { | ||
| 194 | bufmax += 256 + len; | ||
| 195 | buffer = xrealloc(buffer, bufmax); | ||
| 196 | } | ||
| 197 | memcpy(buffer + buflen, str, len); | ||
| 198 | buflen += len; | ||
| 199 | free(str); | ||
| 200 | |||
| 201 | if (buflen > 40 && buffer[buflen-1] == '\n') | ||
| 202 | my_flush(); | ||
| 203 | |||
| 204 | return len; | ||
| 205 | } | ||
| 206 | |||
| 207 | #undef bb_putchar | ||
| 208 | #undef putchar | ||
| 209 | #undef printf | ||
| 210 | #define bb_putchar(c) my_putchar(c) | ||
| 211 | #define putchar(c) my_putchar(c) | ||
| 212 | #define printf(...) my_printf(__VA_ARGS__) | ||
| 159 | #endif | 213 | #endif |
| 160 | 214 | ||
| 161 | /* Handles %b; return 1 if output is to be short-circuited by \c */ | 215 | /* Handles %b; return 1 if output is to be short-circuited by \c */ |
| 162 | static int print_esc_string(const char *str) | 216 | static int print_esc_string(const char *str) |
| 163 | { | 217 | { |
| 164 | char c; | 218 | char c; |
| 165 | #if ENABLE_PLATFORM_MINGW32 | ||
| 166 | char *s, *t; | ||
| 167 | int ret = 0; | ||
| 168 | |||
| 169 | s = t = xstrdup(str); | ||
| 170 | #endif | ||
| 171 | while ((c = *str) != '\0') { | 219 | while ((c = *str) != '\0') { |
| 172 | str++; | 220 | str++; |
| 173 | if (c == '\\') { | 221 | if (c == '\\') { |
| @@ -179,12 +227,7 @@ static int print_esc_string(const char *str) | |||
| 179 | } | 227 | } |
| 180 | } | 228 | } |
| 181 | else if (*str == 'c') { | 229 | else if (*str == 'c') { |
| 182 | #if ENABLE_PLATFORM_MINGW32 | ||
| 183 | ret = 1; | ||
| 184 | goto finish; | ||
| 185 | #else | ||
| 186 | return 1; | 230 | return 1; |
| 187 | #endif | ||
| 188 | } | 231 | } |
| 189 | { | 232 | { |
| 190 | /* optimization: don't force arg to be on-stack, | 233 | /* optimization: don't force arg to be on-stack, |
| @@ -194,20 +237,10 @@ static int print_esc_string(const char *str) | |||
| 194 | str = z; | 237 | str = z; |
| 195 | } | 238 | } |
| 196 | } | 239 | } |
| 197 | #if ENABLE_PLATFORM_MINGW32 | ||
| 198 | *t++ = c; | ||
| 199 | #else | ||
| 200 | putchar(c); | 240 | putchar(c); |
| 201 | #endif | ||
| 202 | } | 241 | } |
| 203 | #if ENABLE_PLATFORM_MINGW32 | 242 | |
| 204 | finish: | ||
| 205 | fwrite_stdout(s, t); | ||
| 206 | free(s); | ||
| 207 | return ret; | ||
| 208 | #else | ||
| 209 | return 0; | 243 | return 0; |
| 210 | #endif | ||
| 211 | } | 244 | } |
| 212 | 245 | ||
| 213 | static void print_direc(char *format, unsigned fmt_length, | 246 | static void print_direc(char *format, unsigned fmt_length, |
| @@ -321,18 +354,10 @@ static char **print_formatted(char *f, char **argv, int *conv_err) | |||
| 321 | int field_width; /* Arg to first '*' */ | 354 | int field_width; /* Arg to first '*' */ |
| 322 | int precision; /* Arg to second '*' */ | 355 | int precision; /* Arg to second '*' */ |
| 323 | char **saved_argv = argv; | 356 | char **saved_argv = argv; |
| 324 | #if ENABLE_PLATFORM_MINGW32 | ||
| 325 | char *s, *t; | ||
| 326 | s = t = auto_string(xstrdup(f)); | ||
| 327 | #endif | ||
| 328 | 357 | ||
| 329 | for (; *f; ++f) { | 358 | for (; *f; ++f) { |
| 330 | switch (*f) { | 359 | switch (*f) { |
| 331 | case '%': | 360 | case '%': |
| 332 | #if ENABLE_PLATFORM_MINGW32 | ||
| 333 | fwrite_stdout(s, t); | ||
| 334 | t = s; | ||
| 335 | #endif | ||
| 336 | direc_start = f++; | 361 | direc_start = f++; |
| 337 | direc_length = 1; | 362 | direc_length = 1; |
| 338 | field_width = precision = 0; | 363 | field_width = precision = 0; |
| @@ -421,18 +446,6 @@ static char **print_formatted(char *f, char **argv, int *conv_err) | |||
| 421 | free(p); | 446 | free(p); |
| 422 | } | 447 | } |
| 423 | break; | 448 | break; |
| 424 | #if ENABLE_PLATFORM_MINGW32 | ||
| 425 | case '\\': | ||
| 426 | if (*++f == 'c') { | ||
| 427 | fwrite_stdout(s, t); | ||
| 428 | return saved_argv; /* causes main() to exit */ | ||
| 429 | } | ||
| 430 | *t++ = bb_process_escape_sequence((const char **)&f); | ||
| 431 | f--; | ||
| 432 | break; | ||
| 433 | default: | ||
| 434 | *t++ = *f; | ||
| 435 | #else | ||
| 436 | case '\\': | 449 | case '\\': |
| 437 | if (*++f == 'c') { | 450 | if (*++f == 'c') { |
| 438 | return saved_argv; /* causes main() to exit */ | 451 | return saved_argv; /* causes main() to exit */ |
| @@ -442,12 +455,8 @@ static char **print_formatted(char *f, char **argv, int *conv_err) | |||
| 442 | break; | 455 | break; |
| 443 | default: | 456 | default: |
| 444 | putchar(*f); | 457 | putchar(*f); |
| 445 | #endif | ||
| 446 | } | 458 | } |
| 447 | } | 459 | } |
| 448 | #if ENABLE_PLATFORM_MINGW32 | ||
| 449 | fwrite_stdout(s, t); | ||
| 450 | #endif | ||
| 451 | 460 | ||
| 452 | return argv; | 461 | return argv; |
| 453 | } | 462 | } |
| @@ -495,6 +504,9 @@ int printf_main(int argc UNUSED_PARAM, char **argv) | |||
| 495 | do { | 504 | do { |
| 496 | argv = argv2; | 505 | argv = argv2; |
| 497 | argv2 = print_formatted(format, argv, &conv_err); | 506 | argv2 = print_formatted(format, argv, &conv_err); |
| 507 | #if ENABLE_PLATFORM_MINGW32 | ||
| 508 | my_flush(); | ||
| 509 | #endif | ||
| 498 | } while (argv2 > argv && *argv2); | 510 | } while (argv2 > argv && *argv2); |
| 499 | 511 | ||
| 500 | /* coreutils compat (bash doesn't do this): | 512 | /* coreutils compat (bash doesn't do this): |
