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