comparison tango/tango/io/Console.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children 44a95ac7368a
comparison
equal deleted inserted replaced
131:5825d48b27d1 132:1700239cab2e
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 }