23#define READ_BUF_SIZE 4096
35static stream_state_t s_stdout_state;
36static stream_state_t s_stderr_state;
51static int utf8_complete_length(
const char *buf,
int len)
57 if (len == 0)
return 0;
61 while (i >= 0 && ((
unsigned char)buf[i] & 0xC0) == 0x80) {
66 lead = (
unsigned char)buf[i];
67 if ((lead & 0x80) == 0x00) seq_len = 1;
68 else if ((lead & 0xE0) == 0xC0) seq_len = 2;
69 else if ((lead & 0xF0) == 0xE0) seq_len = 3;
70 else if ((lead & 0xF8) == 0xF0) seq_len = 4;
74 return (len - i >= seq_len) ? len : i;
85static DWORD WINAPI reader_thread_proc(LPVOID param)
87 stream_state_t *s = (stream_state_t *)param;
88 char buf[READ_BUF_SIZE];
89 wchar_t wbuf[READ_BUF_SIZE];
92 BOOL is_console = (GetFileType(s->orig_handle) == FILE_TYPE_CHAR)
93 && GetConsoleMode(s->orig_handle, &mode);
97 BOOL ok = ReadFile(s->pipe_read,
99 (DWORD)(READ_BUF_SIZE - pending_len),
101 if (!ok || nread == 0)
break;
104 int total = pending_len + (int)nread;
108 int complete = utf8_complete_length(buf, total);
109 int new_pending = total - complete;
112 int wlen = MultiByteToWideChar(CP_UTF8, 0,
114 wbuf, READ_BUF_SIZE);
117 if (!WriteConsoleW(s->orig_handle, wbuf, (DWORD)wlen, &nw, NULL)) {
119 WriteFile(s->orig_handle, buf, (DWORD)complete, &nw, NULL);
125 if (new_pending > 0) {
126 memmove(buf, buf + complete, (
size_t)new_pending);
128 pending_len = new_pending;
132 WriteFile(s->orig_handle, buf, (DWORD)total, &nw, NULL);
154static int init_stream(stream_state_t *s, DWORD std_handle_id, FILE *crt_stream)
160 SECURITY_ATTRIBUTES sa;
163 orig = GetStdHandle(std_handle_id);
164 if (orig == NULL || orig == INVALID_HANDLE_VALUE)
return -1;
167 proc = GetCurrentProcess();
168 if (!DuplicateHandle(proc, orig, proc, &s->orig_handle,
169 0, FALSE, DUPLICATE_SAME_ACCESS))
return -1;
172 sa.nLength =
sizeof(sa);
173 sa.lpSecurityDescriptor = NULL;
174 sa.bInheritHandle = TRUE;
175 if (!CreatePipe(&pipe_read, &pipe_write, &sa, 0)) {
176 CloseHandle(s->orig_handle);
180 SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0);
181 s->pipe_read = pipe_read;
184 s->orig_crt_fd = _dup(_fileno(crt_stream));
185 if (s->orig_crt_fd == -1) {
186 CloseHandle(s->orig_handle);
187 CloseHandle(pipe_read);
188 CloseHandle(pipe_write);
193 new_fd = _open_osfhandle((intptr_t)pipe_write, _O_WRONLY | _O_BINARY);
195 _close(s->orig_crt_fd);
196 CloseHandle(s->orig_handle);
197 CloseHandle(pipe_read);
198 CloseHandle(pipe_write);
202 if (_dup2(new_fd, _fileno(crt_stream)) != 0) {
204 _close(s->orig_crt_fd);
205 CloseHandle(s->orig_handle);
206 CloseHandle(pipe_read);
210 setvbuf(crt_stream, NULL, _IONBF, 0);
213 s->thread = CreateThread(NULL, 0, reader_thread_proc, s, 0, NULL);
214 if (s->thread == NULL) {
216 _dup2(s->orig_crt_fd, _fileno(crt_stream));
217 _close(s->orig_crt_fd);
218 CloseHandle(s->orig_handle);
219 CloseHandle(pipe_read);
223 InterlockedExchange(&s->active, 1);
237static void dispose_stream(stream_state_t *s, FILE *crt_stream)
240 if (!InterlockedCompareExchange(&s->active, 0, 1))
return;
246 _dup2(s->orig_crt_fd, _fileno(crt_stream));
247 _close(s->orig_crt_fd);
250 WaitForSingleObject(s->thread, 5000);
252 CloseHandle(s->pipe_read);
253 CloseHandle(s->orig_handle);
254 CloseHandle(s->thread);
258 s->orig_handle = NULL;
267 if (s_stdout_state.active)
return;
269 if (init_stream(&s_stdout_state, STD_OUTPUT_HANDLE, stdout) != 0)
271 fprintf(stderr,
"console_init: stdout の初期化に失敗しました。\n");
275 if (init_stream(&s_stderr_state, STD_ERROR_HANDLE, stderr) != 0) {
277 dispose_stream(&s_stdout_state, stdout);
278 fprintf(stderr,
"console_init: stderr の初期化に失敗しました。\n");
286 dispose_stream(&s_stderr_state, stderr);
287 dispose_stream(&s_stdout_state, stdout);
void CONSOLE_UTIL_API console_dispose(void)
コンソールヘルパーを終了し、リソースを解放する。
void CONSOLE_UTIL_API console_init(void)
コンソールヘルパーを初期化する。
#define CONSOLE_UTIL_API
呼び出し規約マクロ。