compat / winansi.con commit Merge branch 'ph/checkout' (99ea66e)
   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 TODO: K
  23*/
  24
  25static HANDLE console;
  26static WORD plain_attr;
  27static WORD attr;
  28static int negative;
  29
  30static void init(void)
  31{
  32        CONSOLE_SCREEN_BUFFER_INFO sbi;
  33
  34        static int initialized = 0;
  35        if (initialized)
  36                return;
  37
  38        console = GetStdHandle(STD_OUTPUT_HANDLE);
  39        if (console == INVALID_HANDLE_VALUE)
  40                console = NULL;
  41
  42        if (!console)
  43                return;
  44
  45        GetConsoleScreenBufferInfo(console, &sbi);
  46        attr = plain_attr = sbi.wAttributes;
  47        negative = 0;
  48
  49        initialized = 1;
  50}
  51
  52
  53#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
  54#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
  55
  56static void set_console_attr(void)
  57{
  58        WORD attributes = attr;
  59        if (negative) {
  60                attributes &= ~FOREGROUND_ALL;
  61                attributes &= ~BACKGROUND_ALL;
  62
  63                /* This could probably use a bitmask
  64                   instead of a series of ifs */
  65                if (attr & FOREGROUND_RED)
  66                        attributes |= BACKGROUND_RED;
  67                if (attr & FOREGROUND_GREEN)
  68                        attributes |= BACKGROUND_GREEN;
  69                if (attr & FOREGROUND_BLUE)
  70                        attributes |= BACKGROUND_BLUE;
  71
  72                if (attr & BACKGROUND_RED)
  73                        attributes |= FOREGROUND_RED;
  74                if (attr & BACKGROUND_GREEN)
  75                        attributes |= FOREGROUND_GREEN;
  76                if (attr & BACKGROUND_BLUE)
  77                        attributes |= FOREGROUND_BLUE;
  78        }
  79        SetConsoleTextAttribute(console, attributes);
  80}
  81
  82static const char *set_attr(const char *str)
  83{
  84        const char *func;
  85        size_t len = strspn(str, "0123456789;");
  86        func = str + len;
  87
  88        switch (*func) {
  89        case 'm':
  90                do {
  91                        long val = strtol(str, (char **)&str, 10);
  92                        switch (val) {
  93                        case 0: /* reset */
  94                                attr = plain_attr;
  95                                negative = 0;
  96                                break;
  97                        case 1: /* bold */
  98                                attr |= FOREGROUND_INTENSITY;
  99                                break;
 100                        case 2:  /* faint */
 101                        case 22: /* normal */
 102                                attr &= ~FOREGROUND_INTENSITY;
 103                                break;
 104                        case 3:  /* italic */
 105                                /* Unsupported */
 106                                break;
 107                        case 4:  /* underline */
 108                        case 21: /* double underline */
 109                                /* Wikipedia says this flag does nothing */
 110                                /* Furthermore, mingw doesn't define this flag
 111                                attr |= COMMON_LVB_UNDERSCORE; */
 112                                break;
 113                        case 24: /* no underline */
 114                                /* attr &= ~COMMON_LVB_UNDERSCORE; */
 115                                break;
 116                        case 5:  /* slow blink */
 117                        case 6:  /* fast blink */
 118                                /* We don't have blink, but we do have
 119                                   background intensity */
 120                                attr |= BACKGROUND_INTENSITY;
 121                                break;
 122                        case 25: /* no blink */
 123                                attr &= ~BACKGROUND_INTENSITY;
 124                                break;
 125                        case 7:  /* negative */
 126                                negative = 1;
 127                                break;
 128                        case 27: /* positive */
 129                                negative = 0;
 130                                break;
 131                        case 8:  /* conceal */
 132                        case 28: /* reveal */
 133                                /* Unsupported */
 134                                break;
 135                        case 30: /* Black */
 136                                attr &= ~FOREGROUND_ALL;
 137                                break;
 138                        case 31: /* Red */
 139                                attr &= ~FOREGROUND_ALL;
 140                                attr |= FOREGROUND_RED;
 141                                break;
 142                        case 32: /* Green */
 143                                attr &= ~FOREGROUND_ALL;
 144                                attr |= FOREGROUND_GREEN;
 145                                break;
 146                        case 33: /* Yellow */
 147                                attr &= ~FOREGROUND_ALL;
 148                                attr |= FOREGROUND_RED | FOREGROUND_GREEN;
 149                                break;
 150                        case 34: /* Blue */
 151                                attr &= ~FOREGROUND_ALL;
 152                                attr |= FOREGROUND_BLUE;
 153                                break;
 154                        case 35: /* Magenta */
 155                                attr &= ~FOREGROUND_ALL;
 156                                attr |= FOREGROUND_RED | FOREGROUND_BLUE;
 157                                break;
 158                        case 36: /* Cyan */
 159                                attr &= ~FOREGROUND_ALL;
 160                                attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
 161                                break;
 162                        case 37: /* White */
 163                                attr |= FOREGROUND_RED |
 164                                        FOREGROUND_GREEN |
 165                                        FOREGROUND_BLUE;
 166                                break;
 167                        case 38: /* Unknown */
 168                                break;
 169                        case 39: /* reset */
 170                                attr &= ~FOREGROUND_ALL;
 171                                attr |= (plain_attr & FOREGROUND_ALL);
 172                                break;
 173                        case 40: /* Black */
 174                                attr &= ~BACKGROUND_ALL;
 175                                break;
 176                        case 41: /* Red */
 177                                attr &= ~BACKGROUND_ALL;
 178                                attr |= BACKGROUND_RED;
 179                                break;
 180                        case 42: /* Green */
 181                                attr &= ~BACKGROUND_ALL;
 182                                attr |= BACKGROUND_GREEN;
 183                                break;
 184                        case 43: /* Yellow */
 185                                attr &= ~BACKGROUND_ALL;
 186                                attr |= BACKGROUND_RED | BACKGROUND_GREEN;
 187                                break;
 188                        case 44: /* Blue */
 189                                attr &= ~BACKGROUND_ALL;
 190                                attr |= BACKGROUND_BLUE;
 191                                break;
 192                        case 45: /* Magenta */
 193                                attr &= ~BACKGROUND_ALL;
 194                                attr |= BACKGROUND_RED | BACKGROUND_BLUE;
 195                                break;
 196                        case 46: /* Cyan */
 197                                attr &= ~BACKGROUND_ALL;
 198                                attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
 199                                break;
 200                        case 47: /* White */
 201                                attr |= BACKGROUND_RED |
 202                                        BACKGROUND_GREEN |
 203                                        BACKGROUND_BLUE;
 204                                break;
 205                        case 48: /* Unknown */
 206                                break;
 207                        case 49: /* reset */
 208                                attr &= ~BACKGROUND_ALL;
 209                                attr |= (plain_attr & BACKGROUND_ALL);
 210                                break;
 211                        default:
 212                                /* Unsupported code */
 213                                break;
 214                        }
 215                        str++;
 216                } while (*(str-1) == ';');
 217
 218                set_console_attr();
 219                break;
 220        case 'K':
 221                /* TODO */
 222                break;
 223        default:
 224                /* Unsupported code */
 225                break;
 226        }
 227
 228        return func + 1;
 229}
 230
 231static int ansi_emulate(const char *str, FILE *stream)
 232{
 233        int rv = 0;
 234        const char *pos = str;
 235
 236        while (*pos) {
 237                pos = strstr(str, "\033[");
 238                if (pos) {
 239                        size_t len = pos - str;
 240
 241                        if (len) {
 242                                size_t out_len = fwrite(str, 1, len, stream);
 243                                rv += out_len;
 244                                if (out_len < len)
 245                                        return rv;
 246                        }
 247
 248                        str = pos + 2;
 249                        rv += 2;
 250
 251                        fflush(stream);
 252
 253                        pos = set_attr(str);
 254                        rv += pos - str;
 255                        str = pos;
 256                } else {
 257                        rv += strlen(str);
 258                        fputs(str, stream);
 259                        return rv;
 260                }
 261        }
 262        return rv;
 263}
 264
 265int winansi_fputs(const char *str, FILE *stream)
 266{
 267        int rv;
 268
 269        if (!isatty(fileno(stream)))
 270                return fputs(str, stream);
 271
 272        init();
 273
 274        if (!console)
 275                return fputs(str, stream);
 276
 277        rv = ansi_emulate(str, stream);
 278
 279        if (rv >= 0)
 280                return 0;
 281        else
 282                return EOF;
 283}
 284
 285static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
 286{
 287        int len, rv;
 288        char small_buf[256];
 289        char *buf = small_buf;
 290        va_list cp;
 291
 292        if (!isatty(fileno(stream)))
 293                goto abort;
 294
 295        init();
 296
 297        if (!console)
 298                goto abort;
 299
 300        va_copy(cp, list);
 301        len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
 302        va_end(cp);
 303
 304        if (len > sizeof(small_buf) - 1) {
 305                buf = malloc(len + 1);
 306                if (!buf)
 307                        goto abort;
 308
 309                len = vsnprintf(buf, len + 1, format, list);
 310        }
 311
 312        rv = ansi_emulate(buf, stream);
 313
 314        if (buf != small_buf)
 315                free(buf);
 316        return rv;
 317
 318abort:
 319        rv = vfprintf(stream, format, list);
 320        return rv;
 321}
 322
 323int winansi_fprintf(FILE *stream, const char *format, ...)
 324{
 325        va_list list;
 326        int rv;
 327
 328        va_start(list, format);
 329        rv = winansi_vfprintf(stream, format, list);
 330        va_end(list);
 331
 332        return rv;
 333}
 334
 335int winansi_printf(const char *format, ...)
 336{
 337        va_list list;
 338        int rv;
 339
 340        va_start(list, format);
 341        rv = winansi_vfprintf(stdout, format, list);
 342        va_end(list);
 343
 344        return rv;
 345}