| | 2366 | |
|---|
| | 2367 | __EOF__ |
|---|
| | 2368 | |
|---|
| | 2369 | import std.variant : Algebraic; |
|---|
| | 2370 | |
|---|
| | 2371 | version (Windows) private |
|---|
| | 2372 | { |
|---|
| | 2373 | import core.sys.windows.windows; |
|---|
| | 2374 | import std.windows.syserror; |
|---|
| | 2375 | |
|---|
| | 2376 | version (DigitalMars) |
|---|
| | 2377 | { |
|---|
| | 2378 | extern(C) extern __gshared HANDLE[_NFILE] _osfhnd; |
|---|
| | 2379 | @safe HANDLE peekHANDLE(FILE* f) { return _osfhnd[f._file]; } |
|---|
| | 2380 | version = WITH_WINDOWS_CONSOLE; |
|---|
| | 2381 | } |
|---|
| | 2382 | |
|---|
| | 2383 | /* |
|---|
| | 2384 | * Returns $(D true) if $(D handle) is a valid console handle that |
|---|
| | 2385 | * characters can be written to. |
|---|
| | 2386 | */ |
|---|
| | 2387 | @trusted bool isConsole(HANDLE handle) |
|---|
| | 2388 | { |
|---|
| | 2389 | DWORD dummy; |
|---|
| | 2390 | |
|---|
| | 2391 | return GetConsoleMode(handle, &dummy) && |
|---|
| | 2392 | WriteConsoleA(handle, "\0".ptr, 0, null, null); |
|---|
| | 2393 | } |
|---|
| | 2394 | } |
|---|
| | 2395 | |
|---|
| | 2396 | |
|---|
| | 2397 | /** |
|---|
| | 2398 | $(D output range) that locks the file and provides writing to the |
|---|
| | 2399 | file in the native codeset. |
|---|
| | 2400 | */ |
|---|
| | 2401 | struct LockingNativeTextWriter |
|---|
| | 2402 | { |
|---|
| | 2403 | private |
|---|
| | 2404 | { |
|---|
| | 2405 | alias NativeTextEncoder!(UnsharedNarrowWriter) NativeWriter; |
|---|
| | 2406 | alias WideTextEncoder!(UnsharedWideWriter) WideWriter; |
|---|
| | 2407 | |
|---|
| | 2408 | version (Windows) |
|---|
| | 2409 | { |
|---|
| | 2410 | alias WideTextEncoder!(WindowsWideConsoleWriter) ConsoleWideWriter; |
|---|
| | 2411 | alias Algebraic!(NativeWriter, WideWriter, ConsoleWideWriter) |
|---|
| | 2412 | Writer; |
|---|
| | 2413 | } |
|---|
| | 2414 | else |
|---|
| | 2415 | { |
|---|
| | 2416 | alias Algebraic!(NativeWriter, WideWriter) Writer; |
|---|
| | 2417 | } |
|---|
| | 2418 | |
|---|
| | 2419 | File _file; |
|---|
| | 2420 | Writer _writer; |
|---|
| | 2421 | } |
|---|
| | 2422 | |
|---|
| | 2423 | /** |
|---|
| | 2424 | * Constructs a $(D LockingNativeTextWriter) object. |
|---|
| | 2425 | * |
|---|
| | 2426 | * Params: |
|---|
| | 2427 | * file = |
|---|
| | 2428 | * An open $(D File) to write in. |
|---|
| | 2429 | * |
|---|
| | 2430 | * Throws: |
|---|
| | 2431 | * $(UL |
|---|
| | 2432 | * $(LI $(D enforcement) fails if $(D file) is not open.) |
|---|
| | 2433 | * $(LI $(D enforcement) fails if there is no safe mean to |
|---|
| | 2434 | * convert UTF to the native codeset.) |
|---|
| | 2435 | * ) |
|---|
| | 2436 | */ |
|---|
| | 2437 | @trusted this(File file) |
|---|
| | 2438 | { |
|---|
| | 2439 | enforce(file.isOpen); |
|---|
| | 2440 | swap(_file, file); |
|---|
| | 2441 | FLOCK(_file.p.handle); |
|---|
| | 2442 | auto handle = cast(_iobuf*) _file.p.handle; |
|---|
| | 2443 | |
|---|
| | 2444 | // prefer ConsoleWideWriter |
|---|
| | 2445 | version (Windows) static if (is(typeof(peekHANDLE) == function)) |
|---|
| | 2446 | { |
|---|
| | 2447 | HANDLE console = peekHANDLE(_file.p.handle); |
|---|
| | 2448 | |
|---|
| | 2449 | if (isConsole(console) && WindowsWideConsoleWriter.supported) |
|---|
| | 2450 | { |
|---|
| | 2451 | _file.flush(); // need to sync |
|---|
| | 2452 | _writer = ConsoleWideWriter(WindowsWideConsoleWriter(console)); |
|---|
| | 2453 | return; |
|---|
| | 2454 | } |
|---|
| | 2455 | } |
|---|
| | 2456 | |
|---|
| | 2457 | if (fwide(handle, 0) > 0) |
|---|
| | 2458 | _writer = WideWriter(UnsharedWideWriter(handle)); |
|---|
| | 2459 | else |
|---|
| | 2460 | _writer = NativeWriter(UnsharedNarrowWriter(handle)); |
|---|
| | 2461 | } |
|---|
| | 2462 | |
|---|
| | 2463 | @trusted this(this) |
|---|
| | 2464 | { |
|---|
| | 2465 | FLOCK(_file.p.handle); |
|---|
| | 2466 | } |
|---|
| | 2467 | |
|---|
| | 2468 | @trusted ~this() |
|---|
| | 2469 | { |
|---|
| | 2470 | if (_file.isOpen) |
|---|
| | 2471 | FUNLOCK(_file.p.handle); |
|---|
| | 2472 | } |
|---|
| | 2473 | |
|---|
| | 2474 | @trusted void opAssign(LockingNativeTextWriter rhs) |
|---|
| | 2475 | { |
|---|
| | 2476 | swap(this, rhs); |
|---|
| | 2477 | } |
|---|
| | 2478 | |
|---|
| | 2479 | /// Range primitive implementation. |
|---|
| | 2480 | @safe void put(T)(T e) |
|---|
| | 2481 | { |
|---|
| | 2482 | _writer.put(e); |
|---|
| | 2483 | } |
|---|
| | 2484 | } |
|---|
| | 2485 | |
|---|
| | 2486 | |
|---|
| | 2487 | /* |
|---|
| | 2488 | Range that writes multibyte string to a locked, byte-oriented FILE* stream. |
|---|
| | 2489 | */ |
|---|
| | 2490 | private struct UnsharedNarrowWriter |
|---|
| | 2491 | { |
|---|
| | 2492 | private _iobuf* _handle; |
|---|
| | 2493 | |
|---|
| | 2494 | @trusted void put(in ubyte[] str) |
|---|
| | 2495 | { |
|---|
| | 2496 | for (const(ubyte)[] inbuf = str; inbuf.length > 0; ) |
|---|
| | 2497 | { |
|---|
| | 2498 | immutable nwritten = fwrite( |
|---|
| | 2499 | inbuf.ptr, 1, inbuf.length, cast(shared) _handle); |
|---|
| | 2500 | if (nwritten == inbuf.length) |
|---|
| | 2501 | break; |
|---|
| | 2502 | switch (errno) |
|---|
| | 2503 | { |
|---|
| | 2504 | case EINTR: |
|---|
| | 2505 | str = str[nwritten .. $]; |
|---|
| | 2506 | continue; |
|---|
| | 2507 | |
|---|
| | 2508 | default: |
|---|
| | 2509 | throw new StdioException(null); |
|---|
| | 2510 | } |
|---|
| | 2511 | } |
|---|
| | 2512 | } |
|---|
| | 2513 | } |
|---|
| | 2514 | |
|---|
| | 2515 | |
|---|
| | 2516 | /* |
|---|
| | 2517 | Range that writes wide string to a locked, wide-oriented FILE* stream. |
|---|
| | 2518 | */ |
|---|
| | 2519 | private struct UnsharedWideWriter |
|---|
| | 2520 | { |
|---|
| | 2521 | private _iobuf* _handle; |
|---|
| | 2522 | |
|---|
| | 2523 | @trusted void put(wchar_t ch) |
|---|
| | 2524 | { |
|---|
| | 2525 | while (FPUTWC(ch, _handle) == WEOF) |
|---|
| | 2526 | { |
|---|
| | 2527 | switch (errno) |
|---|
| | 2528 | { |
|---|
| | 2529 | case EINTR: |
|---|
| | 2530 | continue; |
|---|
| | 2531 | default: |
|---|
| | 2532 | throw new StdioException(null); |
|---|
| | 2533 | } |
|---|
| | 2534 | } |
|---|
| | 2535 | } |
|---|
| | 2536 | |
|---|
| | 2537 | @trusted void put(in wchar_t[] str) |
|---|
| | 2538 | { |
|---|
| | 2539 | foreach (ch; str) |
|---|
| | 2540 | { |
|---|
| | 2541 | while (FPUTWC(ch, _handle) == WEOF) |
|---|
| | 2542 | { |
|---|
| | 2543 | switch (errno) |
|---|
| | 2544 | { |
|---|
| | 2545 | case EINTR: |
|---|
| | 2546 | continue; |
|---|
| | 2547 | default: |
|---|
| | 2548 | throw new StdioException(null); |
|---|
| | 2549 | } |
|---|
| | 2550 | } |
|---|
| | 2551 | } |
|---|
| | 2552 | } |
|---|
| | 2553 | } |
|---|
| | 2554 | |
|---|
| | 2555 | |
|---|
| | 2556 | /* |
|---|
| | 2557 | Range that writes wide string to a Windows console. |
|---|
| | 2558 | */ |
|---|
| | 2559 | version (Windows) |
|---|
| | 2560 | private struct WindowsWideConsoleWriter |
|---|
| | 2561 | { |
|---|
| | 2562 | private HANDLE _console; |
|---|
| | 2563 | |
|---|
| | 2564 | @trusted void put(WCHAR wc) |
|---|
| | 2565 | { |
|---|
| | 2566 | if (!indirectWriteConsoleW(_console, &wc, 1, null, null)) |
|---|
| | 2567 | throw new /+StdioException+/Exception( |
|---|
| | 2568 | sysErrorString(GetLastError())); |
|---|
| | 2569 | } |
|---|
| | 2570 | |
|---|
| | 2571 | @trusted void put(in WCHAR[] str) |
|---|
| | 2572 | { |
|---|
| | 2573 | for (const(WCHAR)[] outbuf = str; outbuf.length > 0; ) |
|---|
| | 2574 | { |
|---|
| | 2575 | DWORD nwritten; |
|---|
| | 2576 | |
|---|
| | 2577 | if (!indirectWriteConsoleW(_console, |
|---|
| | 2578 | outbuf.ptr, outbuf.length, &nwritten, null)) |
|---|
| | 2579 | throw new /+StdioException+/Exception( |
|---|
| | 2580 | sysErrorString(GetLastError())); |
|---|
| | 2581 | outbuf = outbuf[nwritten .. $]; |
|---|
| | 2582 | } |
|---|
| | 2583 | } |
|---|
| | 2584 | |
|---|
| | 2585 | |
|---|
| | 2586 | /* |
|---|
| | 2587 | * Returns $(D true) if $(D WriteConsoleW) is supported under the |
|---|
| | 2588 | * running environment. |
|---|
| | 2589 | */ |
|---|
| | 2590 | static @safe @property bool supported() |
|---|
| | 2591 | { |
|---|
| | 2592 | return indirectWriteConsoleW !is null; |
|---|
| | 2593 | } |
|---|
| | 2594 | |
|---|
| | 2595 | private static __gshared typeof(&WriteConsoleW) indirectWriteConsoleW; |
|---|
| | 2596 | |
|---|
| | 2597 | shared static this() |
|---|
| | 2598 | { |
|---|
| | 2599 | indirectWriteConsoleW = cast(typeof(&WriteConsoleW)) |
|---|
| | 2600 | GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteConsoleW"); |
|---|
| | 2601 | } |
|---|
| | 2602 | } |
|---|
| | 2603 | |
|---|