aboutsummaryrefslogtreecommitdiff
path: root/winconsole.c
blob: 4eded1318895c56e309169cfad0ae7a2e7f49258 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#define WINCONSOLE_NO_DEF
#include "winconsole.h"

#include <windows.h>
#include <io.h>

/*

    To correctly make console output on Windows, we need to check if the
    output stream we are writing to is a console. If it is, we use
    WriteConsoleW() to directly write to the console. Otherwise we use the
    fprintf() function. This dance makes us correctly output whatever codepage
    the console is in (we assume that our internal strings and dictionary are
    UTF-8).

*/

static int is_console(HANDLE h)
{
    DWORD mode;

    if (h == INVALID_HANDLE_VALUE)
        return 0;

    return !!(GetConsoleMode(h, &mode));
}

int winconsole_vfprintf(FILE *fp, const char *fmt, va_list ap)
{
    wchar_t *wbuf;
    va_list aq;
    DWORD nwritten;
    char *buf;
    int bufsz, wbufsz;
    HANDLE h = (HANDLE) _get_osfhandle(_fileno(fp));

    /* WriteConsoleW works only with the console */
    if (!is_console(h))
        return vfprintf(fp, fmt, ap);

    /* get the length of the buffer */
    va_copy(aq, ap);
    bufsz = vsnprintf(NULL, 0, fmt, aq);
    if (bufsz < 0)
        return bufsz;
    va_end(aq);

    if (bufsz == 0)
        return 0;

    buf = malloc(bufsz+1);
    if (!buf)
        return -1;

    /* now really print to the buffer */
    bufsz = vsnprintf(buf, bufsz+1, fmt, ap);
    if (bufsz < 0)
        return bufsz;

    /* get the length of the wide buf */
    wbufsz = MultiByteToWideChar(CP_UTF8, 0, buf, bufsz+1, NULL, 0);
    if (wbufsz == 0xFFFD || wbufsz == 0)
    {
        free(buf);
        return -1;
    }

    wbuf = malloc(sizeof(wchar_t) * wbufsz);
    if (!wbuf)
    {
        free(buf);
        return -1;
    }

    MultiByteToWideChar(CP_UTF8, 0, buf, bufsz+1, wbuf, wbufsz);

    if (!WriteConsoleW(h, wbuf, wbufsz-1, &nwritten, NULL))
        return -1;

    return nwritten;
}

int winconsole_fprintf(FILE *fp, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    int ret = winconsole_vfprintf(fp, fmt, ap);
    va_end(ap);
    return ret;
}

int winconsole_vprintf(const char *fmt, va_list ap)
{
    return winconsole_vfprintf(stdout, fmt, ap);
}

int winconsole_printf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    int ret = winconsole_vprintf(fmt, ap);
    va_end(ap);
    return ret;
}