diff options
| author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-04-14 02:12:01 +0200 |
|---|---|---|
| committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-10 18:50:04 +1000 |
| commit | fa37e9da4b02810eedbb0ac82e8cd0b99336aa82 (patch) | |
| tree | 47434020dbb9699ea6d51a708d376518dfba6abc | |
| parent | 909696f1394a67f38f678eb9cfb0d794173095fd (diff) | |
| download | busybox-w32-fa37e9da4b02810eedbb0ac82e8cd0b99336aa82.tar.gz busybox-w32-fa37e9da4b02810eedbb0ac82e8cd0b99336aa82.tar.bz2 busybox-w32-fa37e9da4b02810eedbb0ac82e8cd0b99336aa82.zip | |
win32: Support certain ANSI sequences on cmd.exe
Support sequences are mostly color ones.
This was extracted from commit
e56b799d6ad8afba4168fffa7218d44c041a72d2
in Git repository. Changes from original version:
> diff --git a/home/pclouds/w/git/compat/winansi.c b/tmp/winansi2.c
> index 44dc293..e2e7010 100644
> --- a/home/pclouds/w/git/compat/winansi.c
> +++ b/tmp/winansi2.c
> @@ -2,8 +2,9 @@
> * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
> */
>
> +#include "libbb.h"
> #include <windows.h>
> -#include "../git-compat-util.h"
> +#undef PACKED
>
> /*
> Functions to be wrapped:
| -rw-r--r-- | include/mingw.h | 11 | ||||
| -rw-r--r-- | win32/Kbuild | 1 | ||||
| -rw-r--r-- | win32/winansi.c | 358 |
3 files changed, 370 insertions, 0 deletions
diff --git a/include/mingw.h b/include/mingw.h index d5a675507..fd9630a58 100644 --- a/include/mingw.h +++ b/include/mingw.h | |||
| @@ -122,6 +122,17 @@ NOIMPL(sigfillset,int *mask UNUSED_PARAM); | |||
| 122 | int fdprintf(int fd, const char *format, ...); | 122 | int fdprintf(int fd, const char *format, ...); |
| 123 | 123 | ||
| 124 | /* | 124 | /* |
| 125 | * ANSI emulation wrappers | ||
| 126 | */ | ||
| 127 | |||
| 128 | int winansi_fputs(const char *str, FILE *stream); | ||
| 129 | int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2))); | ||
| 130 | int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3))); | ||
| 131 | #define fputs winansi_fputs | ||
| 132 | #define printf(...) winansi_printf(__VA_ARGS__) | ||
| 133 | #define fprintf(...) winansi_fprintf(__VA_ARGS__) | ||
| 134 | |||
| 135 | /* | ||
| 125 | * stdlib.h | 136 | * stdlib.h |
| 126 | */ | 137 | */ |
| 127 | #define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */ | 138 | #define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */ |
diff --git a/win32/Kbuild b/win32/Kbuild index 68a58ca77..dc3b24376 100644 --- a/win32/Kbuild +++ b/win32/Kbuild | |||
| @@ -9,3 +9,4 @@ lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o | |||
| 9 | lib-$(CONFIG_PLATFORM_MINGW32) += mingw.o | 9 | lib-$(CONFIG_PLATFORM_MINGW32) += mingw.o |
| 10 | lib-$(CONFIG_PLATFORM_MINGW32) += process.o | 10 | lib-$(CONFIG_PLATFORM_MINGW32) += process.o |
| 11 | lib-$(CONFIG_PLATFORM_MINGW32) += regex.o | 11 | lib-$(CONFIG_PLATFORM_MINGW32) += regex.o |
| 12 | lib-$(CONFIG_PLATFORM_MINGW32) += winansi.o | ||
diff --git a/win32/winansi.c b/win32/winansi.c new file mode 100644 index 000000000..e2e7010fb --- /dev/null +++ b/win32/winansi.c | |||
| @@ -0,0 +1,358 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2008 Peter Harris <git@peter.is-a-geek.org> | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include "libbb.h" | ||
| 6 | #include <windows.h> | ||
| 7 | #undef PACKED | ||
| 8 | |||
| 9 | /* | ||
| 10 | Functions to be wrapped: | ||
| 11 | */ | ||
| 12 | #undef printf | ||
| 13 | #undef fprintf | ||
| 14 | #undef fputs | ||
| 15 | /* TODO: write */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | ANSI codes used by git: m, K | ||
| 19 | |||
| 20 | This file is git-specific. Therefore, this file does not attempt | ||
| 21 | to implement any codes that are not used by git. | ||
| 22 | */ | ||
| 23 | |||
| 24 | static HANDLE console; | ||
| 25 | static WORD plain_attr; | ||
| 26 | static WORD attr; | ||
| 27 | static int negative; | ||
| 28 | |||
| 29 | static void init(void) | ||
| 30 | { | ||
| 31 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
| 32 | |||
| 33 | static int initialized = 0; | ||
| 34 | if (initialized) | ||
| 35 | return; | ||
| 36 | |||
| 37 | console = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 38 | if (console == INVALID_HANDLE_VALUE) | ||
| 39 | console = NULL; | ||
| 40 | |||
| 41 | if (!console) | ||
| 42 | return; | ||
| 43 | |||
| 44 | GetConsoleScreenBufferInfo(console, &sbi); | ||
| 45 | attr = plain_attr = sbi.wAttributes; | ||
| 46 | negative = 0; | ||
| 47 | |||
| 48 | initialized = 1; | ||
| 49 | } | ||
| 50 | |||
| 51 | |||
| 52 | #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) | ||
| 53 | #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) | ||
| 54 | |||
| 55 | static void set_console_attr(void) | ||
| 56 | { | ||
| 57 | WORD attributes = attr; | ||
| 58 | if (negative) { | ||
| 59 | attributes &= ~FOREGROUND_ALL; | ||
| 60 | attributes &= ~BACKGROUND_ALL; | ||
| 61 | |||
| 62 | /* This could probably use a bitmask | ||
| 63 | instead of a series of ifs */ | ||
| 64 | if (attr & FOREGROUND_RED) | ||
| 65 | attributes |= BACKGROUND_RED; | ||
| 66 | if (attr & FOREGROUND_GREEN) | ||
| 67 | attributes |= BACKGROUND_GREEN; | ||
| 68 | if (attr & FOREGROUND_BLUE) | ||
| 69 | attributes |= BACKGROUND_BLUE; | ||
| 70 | |||
| 71 | if (attr & BACKGROUND_RED) | ||
| 72 | attributes |= FOREGROUND_RED; | ||
| 73 | if (attr & BACKGROUND_GREEN) | ||
| 74 | attributes |= FOREGROUND_GREEN; | ||
| 75 | if (attr & BACKGROUND_BLUE) | ||
| 76 | attributes |= FOREGROUND_BLUE; | ||
| 77 | } | ||
| 78 | SetConsoleTextAttribute(console, attributes); | ||
| 79 | } | ||
| 80 | |||
| 81 | static void erase_in_line(void) | ||
| 82 | { | ||
| 83 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
| 84 | |||
| 85 | if (!console) | ||
| 86 | return; | ||
| 87 | |||
| 88 | GetConsoleScreenBufferInfo(console, &sbi); | ||
| 89 | FillConsoleOutputCharacterA(console, ' ', | ||
| 90 | sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, | ||
| 91 | NULL); | ||
| 92 | } | ||
| 93 | |||
| 94 | |||
| 95 | static const char *set_attr(const char *str) | ||
| 96 | { | ||
| 97 | const char *func; | ||
| 98 | size_t len = strspn(str, "0123456789;"); | ||
| 99 | func = str + len; | ||
| 100 | |||
| 101 | switch (*func) { | ||
| 102 | case 'm': | ||
| 103 | do { | ||
| 104 | long val = strtol(str, (char **)&str, 10); | ||
| 105 | switch (val) { | ||
| 106 | case 0: /* reset */ | ||
| 107 | attr = plain_attr; | ||
| 108 | negative = 0; | ||
| 109 | break; | ||
| 110 | case 1: /* bold */ | ||
| 111 | attr |= FOREGROUND_INTENSITY; | ||
| 112 | break; | ||
| 113 | case 2: /* faint */ | ||
| 114 | case 22: /* normal */ | ||
| 115 | attr &= ~FOREGROUND_INTENSITY; | ||
| 116 | break; | ||
| 117 | case 3: /* italic */ | ||
| 118 | /* Unsupported */ | ||
| 119 | break; | ||
| 120 | case 4: /* underline */ | ||
| 121 | case 21: /* double underline */ | ||
| 122 | /* Wikipedia says this flag does nothing */ | ||
| 123 | /* Furthermore, mingw doesn't define this flag | ||
| 124 | attr |= COMMON_LVB_UNDERSCORE; */ | ||
| 125 | break; | ||
| 126 | case 24: /* no underline */ | ||
| 127 | /* attr &= ~COMMON_LVB_UNDERSCORE; */ | ||
| 128 | break; | ||
| 129 | case 5: /* slow blink */ | ||
| 130 | case 6: /* fast blink */ | ||
| 131 | /* We don't have blink, but we do have | ||
| 132 | background intensity */ | ||
| 133 | attr |= BACKGROUND_INTENSITY; | ||
| 134 | break; | ||
| 135 | case 25: /* no blink */ | ||
| 136 | attr &= ~BACKGROUND_INTENSITY; | ||
| 137 | break; | ||
| 138 | case 7: /* negative */ | ||
| 139 | negative = 1; | ||
| 140 | break; | ||
| 141 | case 27: /* positive */ | ||
| 142 | negative = 0; | ||
| 143 | break; | ||
| 144 | case 8: /* conceal */ | ||
| 145 | case 28: /* reveal */ | ||
| 146 | /* Unsupported */ | ||
| 147 | break; | ||
| 148 | case 30: /* Black */ | ||
| 149 | attr &= ~FOREGROUND_ALL; | ||
| 150 | break; | ||
| 151 | case 31: /* Red */ | ||
| 152 | attr &= ~FOREGROUND_ALL; | ||
| 153 | attr |= FOREGROUND_RED; | ||
| 154 | break; | ||
| 155 | case 32: /* Green */ | ||
| 156 | attr &= ~FOREGROUND_ALL; | ||
| 157 | attr |= FOREGROUND_GREEN; | ||
| 158 | break; | ||
| 159 | case 33: /* Yellow */ | ||
| 160 | attr &= ~FOREGROUND_ALL; | ||
| 161 | attr |= FOREGROUND_RED | FOREGROUND_GREEN; | ||
| 162 | break; | ||
| 163 | case 34: /* Blue */ | ||
| 164 | attr &= ~FOREGROUND_ALL; | ||
| 165 | attr |= FOREGROUND_BLUE; | ||
| 166 | break; | ||
| 167 | case 35: /* Magenta */ | ||
| 168 | attr &= ~FOREGROUND_ALL; | ||
| 169 | attr |= FOREGROUND_RED | FOREGROUND_BLUE; | ||
| 170 | break; | ||
| 171 | case 36: /* Cyan */ | ||
| 172 | attr &= ~FOREGROUND_ALL; | ||
| 173 | attr |= FOREGROUND_GREEN | FOREGROUND_BLUE; | ||
| 174 | break; | ||
| 175 | case 37: /* White */ | ||
| 176 | attr |= FOREGROUND_RED | | ||
| 177 | FOREGROUND_GREEN | | ||
| 178 | FOREGROUND_BLUE; | ||
| 179 | break; | ||
| 180 | case 38: /* Unknown */ | ||
| 181 | break; | ||
| 182 | case 39: /* reset */ | ||
| 183 | attr &= ~FOREGROUND_ALL; | ||
| 184 | attr |= (plain_attr & FOREGROUND_ALL); | ||
| 185 | break; | ||
| 186 | case 40: /* Black */ | ||
| 187 | attr &= ~BACKGROUND_ALL; | ||
| 188 | break; | ||
| 189 | case 41: /* Red */ | ||
| 190 | attr &= ~BACKGROUND_ALL; | ||
| 191 | attr |= BACKGROUND_RED; | ||
| 192 | break; | ||
| 193 | case 42: /* Green */ | ||
| 194 | attr &= ~BACKGROUND_ALL; | ||
| 195 | attr |= BACKGROUND_GREEN; | ||
| 196 | break; | ||
| 197 | case 43: /* Yellow */ | ||
| 198 | attr &= ~BACKGROUND_ALL; | ||
| 199 | attr |= BACKGROUND_RED | BACKGROUND_GREEN; | ||
| 200 | break; | ||
| 201 | case 44: /* Blue */ | ||
| 202 | attr &= ~BACKGROUND_ALL; | ||
| 203 | attr |= BACKGROUND_BLUE; | ||
| 204 | break; | ||
| 205 | case 45: /* Magenta */ | ||
| 206 | attr &= ~BACKGROUND_ALL; | ||
| 207 | attr |= BACKGROUND_RED | BACKGROUND_BLUE; | ||
| 208 | break; | ||
| 209 | case 46: /* Cyan */ | ||
| 210 | attr &= ~BACKGROUND_ALL; | ||
| 211 | attr |= BACKGROUND_GREEN | BACKGROUND_BLUE; | ||
| 212 | break; | ||
| 213 | case 47: /* White */ | ||
| 214 | attr |= BACKGROUND_RED | | ||
| 215 | BACKGROUND_GREEN | | ||
| 216 | BACKGROUND_BLUE; | ||
| 217 | break; | ||
| 218 | case 48: /* Unknown */ | ||
| 219 | break; | ||
| 220 | case 49: /* reset */ | ||
| 221 | attr &= ~BACKGROUND_ALL; | ||
| 222 | attr |= (plain_attr & BACKGROUND_ALL); | ||
| 223 | break; | ||
| 224 | default: | ||
| 225 | /* Unsupported code */ | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | str++; | ||
| 229 | } while (*(str-1) == ';'); | ||
| 230 | |||
| 231 | set_console_attr(); | ||
| 232 | break; | ||
| 233 | case 'K': | ||
| 234 | erase_in_line(); | ||
| 235 | break; | ||
| 236 | default: | ||
| 237 | /* Unsupported code */ | ||
| 238 | break; | ||
| 239 | } | ||
| 240 | |||
| 241 | return func + 1; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int ansi_emulate(const char *str, FILE *stream) | ||
| 245 | { | ||
| 246 | int rv = 0; | ||
| 247 | const char *pos = str; | ||
| 248 | |||
| 249 | while (*pos) { | ||
| 250 | pos = strstr(str, "\033["); | ||
| 251 | if (pos) { | ||
| 252 | size_t len = pos - str; | ||
| 253 | |||
| 254 | if (len) { | ||
| 255 | size_t out_len = fwrite(str, 1, len, stream); | ||
| 256 | rv += out_len; | ||
| 257 | if (out_len < len) | ||
| 258 | return rv; | ||
| 259 | } | ||
| 260 | |||
| 261 | str = pos + 2; | ||
| 262 | rv += 2; | ||
| 263 | |||
| 264 | fflush(stream); | ||
| 265 | |||
| 266 | pos = set_attr(str); | ||
| 267 | rv += pos - str; | ||
| 268 | str = pos; | ||
| 269 | } else { | ||
| 270 | rv += strlen(str); | ||
| 271 | fputs(str, stream); | ||
| 272 | return rv; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | return rv; | ||
| 276 | } | ||
| 277 | |||
| 278 | int winansi_fputs(const char *str, FILE *stream) | ||
| 279 | { | ||
| 280 | int rv; | ||
| 281 | |||
| 282 | if (!isatty(fileno(stream))) | ||
| 283 | return fputs(str, stream); | ||
| 284 | |||
| 285 | init(); | ||
| 286 | |||
| 287 | if (!console) | ||
| 288 | return fputs(str, stream); | ||
| 289 | |||
| 290 | rv = ansi_emulate(str, stream); | ||
| 291 | |||
| 292 | if (rv >= 0) | ||
| 293 | return 0; | ||
| 294 | else | ||
| 295 | return EOF; | ||
| 296 | } | ||
| 297 | |||
| 298 | static int winansi_vfprintf(FILE *stream, const char *format, va_list list) | ||
| 299 | { | ||
| 300 | int len, rv; | ||
| 301 | char small_buf[256]; | ||
| 302 | char *buf = small_buf; | ||
| 303 | va_list cp; | ||
| 304 | |||
| 305 | if (!isatty(fileno(stream))) | ||
| 306 | goto abort; | ||
| 307 | |||
| 308 | init(); | ||
| 309 | |||
| 310 | if (!console) | ||
| 311 | goto abort; | ||
| 312 | |||
| 313 | va_copy(cp, list); | ||
| 314 | len = vsnprintf(small_buf, sizeof(small_buf), format, cp); | ||
| 315 | va_end(cp); | ||
| 316 | |||
| 317 | if (len > sizeof(small_buf) - 1) { | ||
| 318 | buf = malloc(len + 1); | ||
| 319 | if (!buf) | ||
| 320 | goto abort; | ||
| 321 | |||
| 322 | len = vsnprintf(buf, len + 1, format, list); | ||
| 323 | } | ||
| 324 | |||
| 325 | rv = ansi_emulate(buf, stream); | ||
| 326 | |||
| 327 | if (buf != small_buf) | ||
| 328 | free(buf); | ||
| 329 | return rv; | ||
| 330 | |||
| 331 | abort: | ||
| 332 | rv = vfprintf(stream, format, list); | ||
| 333 | return rv; | ||
| 334 | } | ||
| 335 | |||
| 336 | int winansi_fprintf(FILE *stream, const char *format, ...) | ||
| 337 | { | ||
| 338 | va_list list; | ||
| 339 | int rv; | ||
| 340 | |||
| 341 | va_start(list, format); | ||
| 342 | rv = winansi_vfprintf(stream, format, list); | ||
| 343 | va_end(list); | ||
| 344 | |||
| 345 | return rv; | ||
| 346 | } | ||
| 347 | |||
| 348 | int winansi_printf(const char *format, ...) | ||
| 349 | { | ||
| 350 | va_list list; | ||
| 351 | int rv; | ||
| 352 | |||
| 353 | va_start(list, format); | ||
| 354 | rv = winansi_vfprintf(stdout, format, list); | ||
| 355 | va_end(list); | ||
| 356 | |||
| 357 | return rv; | ||
| 358 | } | ||
