1 /******************************************************************************* 2 3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: Feb 2005: Initial release 8 Nov 2005: Heavily revised for unicode 9 Dec 2006: Outback release 10 11 author: Kris 12 13 *******************************************************************************/ 14 15 module tango.io.Console; 16 17 private import tango.sys.Common; 18 19 private import tango.io.device.Device, 20 tango.io.stream.Buffered; 21 22 version (Posix) 23 private import tango.stdc.posix.unistd; // needed for isatty() 24 25 /******************************************************************************* 26 27 Low-level console IO support. 28 29 Note that for a while this was templated for each of char, wchar, 30 and dchar. It became clear after some usage that the console is 31 more useful if it sticks to UTF8 only. See Console.Conduit below 32 for details. 33 34 Redirecting the standard IO handles (via a shell) operates as one 35 would expect, though the redirected content should likely restrict 36 itself to UTF8. 37 38 *******************************************************************************/ 39 40 struct Console 41 { 42 version (Win32) 43 __gshared immutable immutable(char)[] Eol = "\r\n"; 44 else 45 __gshared immutable immutable(char)[] Eol = "\n"; 46 47 48 /********************************************************************** 49 50 Model console input as a buffer. Note that we read UTF8 51 only. 52 53 **********************************************************************/ 54 55 class Input 56 { 57 private Bin buffer; 58 private bool redirect; 59 60 public alias copyln get; 61 62 /************************************************************** 63 64 Attach console input to the provided device. 65 66 **************************************************************/ 67 68 private this (Conduit conduit, bool redirected) 69 { 70 redirect = redirected; 71 buffer = new Bin (conduit); 72 } 73 74 /************************************************************** 75 76 Return the next line available from the console, 77 or null when there is nothing available. The value 78 returned is a duplicate of the buffer content (it 79 has .dup applied). 80 81 Each line ending is removed unless parameter raw is 82 set to true. 83 84 **************************************************************/ 85 86 final char[] copyln (bool raw = false) 87 { 88 char[] line; 89 90 return readln (line, raw) ? line.dup : null; 91 } 92 93 /************************************************************** 94 95 Retreive a line of text from the console and map 96 it to the given argument. The input is sliced, 97 not copied, so use .dup appropriately. Each line 98 ending is removed unless parameter raw is set to 99 true. 100 101 Returns false when there is no more input. 102 103 **************************************************************/ 104 105 final bool readln (ref char[] content, bool raw=false) 106 { 107 size_t line (const(void)[] input) 108 { 109 auto text = cast(const(char[])) input; 110 foreach (i, c; text) 111 if (c is '\n') 112 { 113 auto j = i; 114 if (raw) 115 ++j; 116 else 117 if (j && (text[j-1] is '\r')) 118 --j; 119 content = cast(char[])text [0 .. j]; 120 return i+1; 121 } 122 return IConduit.Eof; 123 } 124 125 // get next line, return true 126 if (buffer.next (&line)) 127 return true; 128 129 // assign trailing content and return false 130 content = cast(char[]) buffer.slice (buffer.readable()); 131 return false; 132 } 133 134 /************************************************************** 135 136 Return the associated stream. 137 138 **************************************************************/ 139 140 @property final InputStream stream () 141 { 142 return buffer; 143 } 144 145 /************************************************************** 146 147 Is this device redirected? 148 149 Returns: 150 True if redirected, false otherwise. 151 152 Remarks: 153 Reflects the console redirection status from when 154 this module was instantiated. 155 156 **************************************************************/ 157 158 @property final bool redirected () 159 { 160 return redirect; 161 } 162 163 /************************************************************** 164 165 Set redirection state to the provided boolean. 166 167 Remarks: 168 Configure the console redirection status, where 169 a redirected console is more efficient (dictates 170 whether newline() performs automatic flushing or 171 not.) 172 173 **************************************************************/ 174 175 @property final Input redirected (bool yes) 176 { 177 redirect = yes; 178 return this; 179 } 180 181 /************************************************************** 182 183 Returns the configured source 184 185 Remarks: 186 Provides access to the underlying mechanism for 187 console input. Use this to retain prior state 188 when temporarily switching inputs. 189 190 **************************************************************/ 191 192 @property final InputStream input () 193 { 194 return buffer.input; 195 } 196 197 /************************************************************** 198 199 Divert input to an alternate source. 200 201 **************************************************************/ 202 203 @property final Input input (InputStream source) 204 { 205 buffer.input = source; 206 return this; 207 } 208 } 209 210 211 /********************************************************************** 212 213 Console output accepts UTF8 only. 214 215 **********************************************************************/ 216 217 class Output 218 { 219 private Bout buffer; 220 private bool redirect; 221 222 public alias append opCall; 223 public alias flush opCall; 224 225 /************************************************************** 226 227 Attach console output to the provided device. 228 229 **************************************************************/ 230 231 private this (Conduit conduit, bool redirected) 232 { 233 redirect = redirected; 234 buffer = new Bout (conduit); 235 } 236 237 /************************************************************** 238 239 Append to the console. We accept UTF8 only, so 240 all other encodings should be handled via some 241 higher level API. 242 243 **************************************************************/ 244 245 final Output append (const(char[]) x) 246 { 247 buffer.append (x.ptr, x.length); 248 return this; 249 } 250 251 /************************************************************** 252 253 Append content. 254 255 Params: 256 other = An object with a useful toString() method. 257 258 Returns: 259 Returns a chaining reference if all content was 260 written. Throws an IOException indicating Eof or 261 Eob if not. 262 263 Remarks: 264 Append the result of other.toString() to the console. 265 266 **************************************************************/ 267 268 final Output append (Object other) 269 { 270 return append (other.toString()); 271 } 272 273 /************************************************************** 274 275 Append a newline and flush the console buffer. If 276 the output is redirected, flushing does not occur 277 automatically. 278 279 Returns: 280 Returns a chaining reference if content was written. 281 Throws an IOException indicating Eof or Eob if not. 282 283 Remarks: 284 Emit a newline into the buffer, and autoflush the 285 current buffer content for an interactive console. 286 Redirected consoles do not flush automatically on 287 a newline. 288 289 **************************************************************/ 290 291 @property final Output newline () 292 { 293 buffer.append (Eol); 294 if (redirect is false) 295 buffer.flush(); 296 297 return this; 298 } 299 300 /************************************************************** 301 302 Explicitly flush console output. 303 304 Returns: 305 Returns a chaining reference if content was written. 306 Throws an IOException indicating Eof or Eob if not. 307 308 Remarks: 309 Flushes the console buffer to attached conduit. 310 311 **************************************************************/ 312 313 final Output flush () 314 { 315 buffer.flush(); 316 return this; 317 } 318 319 /************************************************************** 320 321 Return the associated stream. 322 323 **************************************************************/ 324 325 @property final OutputStream stream () 326 { 327 return buffer; 328 } 329 330 /************************************************************** 331 332 Is this device redirected? 333 334 Returns: 335 True if redirected, false otherwise. 336 337 Remarks: 338 Reflects the console redirection status. 339 340 **************************************************************/ 341 342 @property final bool redirected () 343 { 344 return redirect; 345 } 346 347 /************************************************************** 348 349 Set redirection state to the provided boolean. 350 351 Remarks: 352 Configure the console redirection status, where 353 a redirected console is more efficient (dictates 354 whether newline() performs automatic flushing or 355 not.) 356 357 **************************************************************/ 358 359 @property final Output redirected (bool yes) 360 { 361 redirect = yes; 362 return this; 363 } 364 365 /************************************************************** 366 367 Returns the configured output sink. 368 369 Remarks: 370 Provides access to the underlying mechanism for 371 console output. Use this to retain prior state 372 when temporarily switching outputs. 373 374 **************************************************************/ 375 376 @property final OutputStream output () 377 { 378 return buffer.output; 379 } 380 381 /************************************************************** 382 383 Divert output to an alternate sink. 384 385 **************************************************************/ 386 387 @property final Output output (OutputStream sink) 388 { 389 buffer.output = sink; 390 return this; 391 } 392 } 393 394 395 /*********************************************************************** 396 397 Conduit for specifically handling the console devices. This 398 takes care of certain implementation details on the Win32 399 platform. 400 401 Note that the console is fixed at UTF8 for both linux and 402 Win32. The latter is actually UTF16 native, but it's just 403 too much hassle for a developer to handle the distinction 404 when it really should be a no-brainer. In particular, the 405 Win32 console functions don't work with redirection. This 406 causes additional difficulties that can be ameliorated by 407 asserting console I/O is always UTF8, in all modes. 408 409 ***********************************************************************/ 410 411 class Conduit : Device 412 { 413 private bool redirected = false; 414 415 /*********************************************************************** 416 417 Return the name of this conduit. 418 419 ***********************************************************************/ 420 421 override immutable(char)[] toString() 422 { 423 return "<console>"; 424 } 425 426 /*************************************************************** 427 428 Windows-specific code. 429 430 ***************************************************************/ 431 432 version (Win32) 433 { 434 private wchar[] input; 435 private wchar[] output; 436 437 /******************************************************* 438 439 Associate this device with a given handle. 440 441 This is strictly for adapting existing 442 devices such as Stdout and friends. 443 444 *******************************************************/ 445 446 this (size_t handle) 447 { 448 input = new wchar [1024 * 1]; 449 output = new wchar [1024 * 1]; 450 reopen (handle); 451 } 452 453 /******************************************************* 454 455 Gain access to the standard IO handles. 456 457 *******************************************************/ 458 459 private void reopen (size_t handle_) 460 { 461 __gshared immutable DWORD[] id = [ 462 cast(DWORD) -10, 463 cast(DWORD) -11, 464 cast(DWORD) -12 465 ]; 466 __gshared immutable char[][] f = [ 467 "CONIN$\0", 468 "CONOUT$\0", 469 "CONOUT$\0" 470 ]; 471 472 assert (handle_ < 3); 473 io.handle = GetStdHandle (id[handle_]); 474 if (io.handle is null || io.handle is INVALID_HANDLE_VALUE) 475 io.handle = CreateFileA ( cast(PCHAR) f[handle_].ptr, 476 GENERIC_READ | GENERIC_WRITE, 477 FILE_SHARE_READ | FILE_SHARE_WRITE, 478 null, OPEN_EXISTING, 0, cast(HANDLE) 0); 479 480 // allow invalid handles to remain, since it 481 // may be patched later in some special cases 482 if (io.handle != INVALID_HANDLE_VALUE) 483 { 484 DWORD mode; 485 // are we redirecting? Note that we cannot 486 // use the 'appending' mode triggered via 487 // setting overlapped.Offset to -1, so we 488 // just track the byte-count instead 489 if (! GetConsoleMode (io.handle, &mode)) 490 redirected = io.track = true; 491 } 492 } 493 494 /******************************************************* 495 496 Write a chunk of bytes to the console from 497 the provided array. 498 499 *******************************************************/ 500 501 version (Win32SansUnicode) 502 {} 503 else 504 { 505 override size_t write (const(void)[] src) 506 { 507 if (redirected) 508 return super.write (src); 509 else 510 { 511 DWORD i = src.length; 512 513 // protect conversion from empty strings 514 if (i is 0) 515 return 0; 516 517 // expand buffer appropriately 518 if (output.length < i) 519 output.length = i; 520 521 // convert into output buffer 522 i = MultiByteToWideChar (CP_UTF8, 0, cast(PCHAR) src.ptr, i, 523 output.ptr, output.length); 524 525 // flush produced output 526 for (wchar* p=output.ptr, end=output.ptr+i; p < end; p+=i) 527 { 528 const int MAX = 16 * 1024; 529 530 // avoid console limitation of 64KB 531 DWORD len = end - p; 532 if (len > MAX) 533 { 534 len = MAX; 535 // check for trailing surrogate ... 536 if ((p[len-1] & 0xfc00) is 0xdc00) 537 --len; 538 } 539 if (! WriteConsoleW (io.handle, p, len, &i, null)) 540 error(); 541 } 542 return src.length; 543 } 544 } 545 } 546 547 /******************************************************* 548 549 Read a chunk of bytes from the console into 550 the provided array. 551 552 *******************************************************/ 553 554 version (Win32SansUnicode) 555 {} 556 else 557 { 558 protected override size_t read (void[] dst) 559 { 560 if (redirected) 561 return super.read (dst); 562 else 563 { 564 DWORD i = dst.length / 4; 565 566 assert (i); 567 568 if (i > input.length) 569 i = input.length; 570 571 // read a chunk of wchars from the console 572 if (! ReadConsoleW (io.handle, input.ptr, i, &i, null)) 573 error(); 574 575 // no input ~ go home 576 if (i is 0) 577 return Eof; 578 579 // translate to utf8, directly into dst 580 i = WideCharToMultiByte (CP_UTF8, 0, input.ptr, i, 581 cast(PCHAR) dst.ptr, dst.length, null, null); 582 if (i is 0) 583 error (); 584 585 return i; 586 } 587 } 588 } 589 590 } 591 else 592 { 593 /******************************************************* 594 595 Associate this device with a given handle. 596 597 This is strictly for adapting existing 598 devices such as Stdout and friends. 599 600 *******************************************************/ 601 602 private this (size_t handle) 603 { 604 this.handle = cast(Handle) handle; 605 redirected = (isatty(cast(int)handle) is 0); 606 } 607 } 608 } 609 } 610 611 612 /****************************************************************************** 613 614 Globals representing Console IO. 615 616 ******************************************************************************/ 617 618 static __gshared Console.Input Cin; /// The standard input stream. 619 static __gshared Console.Output Cout; /// The standard output stream. 620 static __gshared Console.Output Cerr; /// The standard error stream. 621 622 623 /****************************************************************************** 624 625 Instantiate Console access. 626 627 ******************************************************************************/ 628 629 shared static this () 630 { 631 auto conduit = new Console.Conduit (0); 632 Cin = new Console.Input (conduit, conduit.redirected); 633 634 conduit = new Console.Conduit (1); 635 Cout = new Console.Output (conduit, conduit.redirected); 636 637 conduit = new Console.Conduit (2); 638 Cerr = new Console.Output (conduit, conduit.redirected); 639 } 640 641 /****************************************************************************** 642 643 Flush outputs before we exit. 644 645 (Good idea from Frits Van Bommel.) 646 647 ******************************************************************************/ 648 649 static ~this() 650 { 651 synchronized (Cout.stream) 652 Cout.flush(); 653 654 synchronized (Cerr.stream) 655 Cerr.flush(); 656 } 657 658 659 /****************************************************************************** 660 661 ******************************************************************************/ 662 663 debug (Console) 664 { 665 void main() 666 { 667 Cout ("hello world").newline; 668 } 669 }