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