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