1//========================================================================
2//
3// Win32Console.cc
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright (C) 2017 Adrian Johnson <ajohnson@redneon.com>
8//
9// To see a description of the changes please see the Changelog file that
10// came with your tarball or type make ChangeLog if you are building from git
11//
12//========================================================================
13
14#ifdef _WIN32
15
16# include "goo/gmem.h"
17# include "UTF.h"
18
19# define WIN32_CONSOLE_IMPL
20# include "Win32Console.h"
21
22# include <windows.h>
23# include <shellapi.h>
24
25static const int BUF_SIZE = 4096;
26static int bufLen = 0;
27static char buf[BUF_SIZE];
28static wchar_t wbuf[BUF_SIZE];
29static bool stdoutIsConsole = true;
30static bool stderrIsConsole = true;
31static HANDLE consoleHandle = nullptr;
32
33// If all = true, flush all characters to console.
34// If all = false, flush up to and including last newline.
35// Also flush all if buffer > half full to ensure space for future
36// writes.
37static void flush(bool all = false)
38{
39 int nchars = 0;
40
41 if (all || bufLen > BUF_SIZE / 2) {
42 nchars = bufLen;
43 } else if (bufLen > 0) {
44 // find num chars up to and including last '\n'
45 for (nchars = bufLen; nchars > 0; --nchars) {
46 if (buf[nchars - 1] == '\n')
47 break;
48 }
49 }
50
51 if (nchars > 0) {
52 DWORD wlen = utf8ToUtf16(buf, (uint16_t *)wbuf, BUF_SIZE, nchars);
53 WriteConsoleW(consoleHandle, wbuf, wlen, &wlen, nullptr);
54 if (nchars < bufLen) {
55 memmove(buf, buf + nchars, bufLen - nchars);
56 bufLen -= nchars;
57 } else {
58 bufLen = 0;
59 }
60 }
61}
62
63static inline bool streamIsConsole(FILE *stream)
64{
65 return ((stream == stdout && stdoutIsConsole) || (stream == stderr && stderrIsConsole));
66}
67
68int win32_fprintf(FILE *stream, ...)
69{
70 va_list args;
71 int ret = 0;
72
73 va_start(args, stream);
74 const char *format = va_arg(args, const char *);
75 if (streamIsConsole(stream)) {
76 ret = vsnprintf(buf + bufLen, BUF_SIZE - bufLen, format, args);
77 bufLen += ret;
78 if (ret >= BUF_SIZE - bufLen) {
79 // output was truncated
80 buf[BUF_SIZE - 1] = 0;
81 bufLen = BUF_SIZE - 1;
82 }
83 flush();
84 } else {
85 vfprintf(stream, format, args);
86 }
87 va_end(args);
88
89 return ret;
90}
91
92size_t win32_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
93{
94 size_t ret = 0;
95
96 if (streamIsConsole(stream)) {
97 int n = size * nmemb;
98 if (n > BUF_SIZE - bufLen - 1)
99 n = BUF_SIZE - bufLen - 1;
100 memcpy(buf + bufLen, ptr, n);
101 bufLen += n;
102 buf[bufLen] = 0;
103 flush();
104 } else {
105 ret = fwrite(ptr, size, nmemb, stream);
106 }
107
108 return ret;
109}
110
111Win32Console::Win32Console(int *argc, char **argv[])
112{
113 LPWSTR *wargv;
114 fpos_t pos;
115
116 argList = nullptr;
117 privateArgList = nullptr;
118 wargv = CommandLineToArgvW(GetCommandLineW(), &numArgs);
119 if (wargv) {
120 argList = new char *[numArgs];
121 privateArgList = new char *[numArgs];
122 for (int i = 0; i < numArgs; i++) {
123 argList[i] = utf16ToUtf8((uint16_t *)(wargv[i]));
124 // parseArgs will rearrange the argv list so we keep our own copy
125 // to use for freeing all the strings
126 privateArgList[i] = argList[i];
127 }
128 LocalFree(wargv);
129 *argc = numArgs;
130 *argv = argList;
131 }
132
133 bufLen = 0;
134 buf[0] = 0;
135 wbuf[0] = 0;
136
137 // check if stdout or stderr redirected
138 // GetFileType() returns CHAR for console and special devices COMx, PRN, CON, NUL etc
139 // fgetpos() succeeds on all CHAR devices except console and CON.
140
141 stdoutIsConsole = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == FILE_TYPE_CHAR) && (fgetpos(stdout, &pos) != 0);
142
143 stderrIsConsole = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR) && (fgetpos(stderr, &pos) != 0);
144
145 // Need a handle to the console. Doesn't matter if we use stdout or stderr as
146 // long as the handle output is to the console.
147 if (stdoutIsConsole)
148 consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
149 else if (stderrIsConsole)
150 consoleHandle = GetStdHandle(STD_ERROR_HANDLE);
151}
152
153Win32Console::~Win32Console()
154{
155 flush(true);
156 if (argList) {
157 for (int i = 0; i < numArgs; i++)
158 gfree(privateArgList[i]);
159 delete[] argList;
160 delete[] privateArgList;
161 }
162}
163
164#endif // _WIN32
165

source code of poppler/utils/Win32Console.cc