Mercurial > projects > ldc
annotate tango/tango/io/Console.d @ 133:44a95ac7368a trunk
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
In particular, assertions has been fixed to include file/line info, and much more!
author | lindquist |
---|---|
date | Mon, 14 Jan 2008 05:11:54 +0100 |
parents | 1700239cab2e |
children | 0e28624814e8 |
rev | line source |
---|---|
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 | |
133
44a95ac7368a
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
lindquist
parents:
132
diff
changeset
|
624 extern(C) int printf(char*, ...); |
44a95ac7368a
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
lindquist
parents:
132
diff
changeset
|
625 |
132 | 626 static this () |
627 { | |
133
44a95ac7368a
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
lindquist
parents:
132
diff
changeset
|
628 printf("STATIC INIT FOR CONSOLE\n"); |
44a95ac7368a
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
lindquist
parents:
132
diff
changeset
|
629 printf("Cin\n"); |
132 | 630 auto conduit = new Console.Conduit (0); |
133
44a95ac7368a
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
lindquist
parents:
132
diff
changeset
|
631 assert(conduit); |
132 | 632 Cin = new Console.Input (conduit, conduit.redirected); |
633 | |
133
44a95ac7368a
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
lindquist
parents:
132
diff
changeset
|
634 printf("Cout\n"); |
132 | 635 conduit = new Console.Conduit (1); |
636 Cout = new Console.Output (conduit, conduit.redirected); | |
637 | |
133
44a95ac7368a
[svn r137] Many fixes towards tango.io.Console working, but not quite there yet...
lindquist
parents:
132
diff
changeset
|
638 printf("Cerr\n"); |
132 | 639 conduit = new Console.Conduit (2); |
640 Cerr = new Console.Output (conduit, conduit.redirected); | |
641 } | |
642 | |
643 | |
644 /****************************************************************************** | |
645 | |
646 Flush outputs before we exit | |
647 | |
648 (good idea from Frits Van Bommel) | |
649 | |
650 ******************************************************************************/ | |
651 | |
652 static ~this() | |
653 { | |
654 synchronized (Cout) | |
655 Cout.flush; | |
656 | |
657 synchronized (Cerr) | |
658 Cerr.flush; | |
659 } |