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