compat / winansi.con commit Merge branch 'cc/bisect' (702beb3)
   1/*
   2 * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
   3 */
   4
   5#include <windows.h>
   6#include "../git-compat-util.h"
   7
   8/*
   9 Functions to be wrapped:
  10*/
  11#undef printf
  12#undef fprintf
  13#undef fputs
  14/* TODO: write */
  15
  16/*
  17 ANSI codes used by git: m, K
  18
  19 This file is git-specific. Therefore, this file does not attempt
  20 to implement any codes that are not used by git.
  21*/
  22
  23static HANDLE console;
  24static WORD plain_attr;
  25static WORD attr;
  26static int negative;
  27
  28static void init(void)
  29{
  30        CONSOLE_SCREEN_BUFFER_INFO sbi;
  31
  32        static int initialized = 0;
  33        if (initialized)
  34                return;
  35
  36        console = GetStdHandle(STD_OUTPUT_HANDLE);
  37        if (console == INVALID_HANDLE_VALUE)
  38                console = NULL;
  39
  40        if (!console)
  41                return;
  42
  43        GetConsoleScreenBufferInfo(console, &sbi);
  44        attr = plain_attr = sbi.wAttributes;
  45        negative = 0;
  46
  47        initialized = 1;
  48}
  49
  50
  51#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
  52#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
  53
  54static void set_console_attr(void)
  55{
  56        WORD attributes = attr;
  57        if (negative) {
  58                attributes &= ~FOREGROUND_ALL;
  59                attributes &= ~BACKGROUND_ALL;
  60
  61                /* This could probably use a bitmask
  62                   instead of a series of ifs */
  63                if (attr & FOREGROUND_RED)
  64                        attributes |= BACKGROUND_RED;
  65                if (attr & FOREGROUND_GREEN)
  66                        attributes |= BACKGROUND_GREEN;
  67                if (attr & FOREGROUND_BLUE)
  68                        attributes |= BACKGROUND_BLUE;
  69
  70                if (attr & BACKGROUND_RED)
  71                        attributes |= FOREGROUND_RED;
  72                if (attr & BACKGROUND_GREEN)
  73                        attributes |= FOREGROUND_GREEN;
  74                if (attr & BACKGROUND_BLUE)
  75                        attributes |= FOREGROUND_BLUE;
  76        }
  77        SetConsoleTextAttribute(console, attributes);
  78}
  79
  80static void erase_in_line(void)
  81{
  82        CONSOLE_SCREEN_BUFFER_INFO sbi;
  83        DWORD dummy; /* Needed for Windows 7 (or Vista) regression */
  84
  85        if (!console)
  86                return;
  87
  88        GetConsoleScreenBufferInfo(console, &sbi);
  89        FillConsoleOutputCharacterA(console, ' ',
  90                sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
  91                &dummy);
  92}
  93
  94
  95static 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
 244static 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
 278int 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
 298static 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
 331abort:
 332        rv = vfprintf(stream, format, list);
 333        return rv;
 334}
 335
 336int 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
 348int 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}