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