Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/trunk/tango/io/Console.d

Revision 3856, 24.8 kB (checked in by kris, 4 months ago)

moved Conduit and friends into tango.io.device in an effort to bring further clarity into tango.io -- this requires adjusting imports such that, for example, tango.io.FileConduit? becomes tango.io.device.FileConduit?

  • Property svn:mime-type set to text/x-dsrc
  • Property svn:eol-style set to native
Line 
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.device.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 || handle is INVALID_HANDLE_VALUE)
473                                     handle = CreateFileA ( cast(PCHAR) f[handle_].ptr,
474                                              GENERIC_READ | GENERIC_WRITE, 
475                                              FILE_SHARE_READ | FILE_SHARE_WRITE,
476                                              null, OPEN_EXISTING, 0, cast(HANDLE) 0);
477
478                                 // allow invalid handles to remain, since it
479                                 // may be patched later in some special cases
480                                 if (handle != INVALID_HANDLE_VALUE)
481                                    {
482                                    // are we redirecting?
483                                    DWORD mode;
484                                    if (! GetConsoleMode (handle, &mode))
485                                          redirected = true;
486                                    }
487                         }
488
489                         /*******************************************************
490
491                                 Write a chunk of bytes to the console from the
492                                 provided array (typically that belonging to
493                                 an IBuffer)
494
495                         *******************************************************/
496
497                         version (Win32SansUnicode)
498                                 {}
499                              else
500                                 {
501                                 override uint write (void[] src)
502                                 {
503                                 if (redirected)
504                                     return super.write (src);
505                                 else
506                                    {
507                                    DWORD i = src.length;
508
509                                    // protect conversion from empty strings
510                                    if (i is 0)
511                                        return 0;
512
513                                    // expand buffer appropriately
514                                    if (output.length < i)
515                                        output.length = i;
516
517                                    // convert into output buffer
518                                    i = MultiByteToWideChar (CP_UTF8, 0, cast(PCHAR) src.ptr, i,
519                                                             output.ptr, output.length);
520                                            
521                                    // flush produced output
522                                    for (wchar* p=output.ptr, end=output.ptr+i; p < end; p+=i)
523                                        {
524                                        const int MAX = 16 * 1024;
525
526                                        // avoid console limitation of 64KB
527                                        DWORD len = end - p;
528                                        if (len > MAX)
529                                           {
530                                           len = MAX;
531                                           // check for trailing surrogate ...
532                                           if ((p[len-1] & 0xfc00) is 0xdc00)
533                                                --len;
534                                           }
535                                        if (! WriteConsoleW (handle, p, len, &i, null))
536                                              error();
537                                        }
538                                    return src.length;
539                                    }
540                                 }
541                                 }
542                        
543                         /*******************************************************
544
545                                 Read a chunk of bytes from the console into the
546                                 provided array (typically that belonging to
547                                 an IBuffer)
548
549                         *******************************************************/
550
551                         version (Win32SansUnicode)
552                                 {}
553                              else
554                                 {
555                                 protected override uint read (void[] dst)
556                                 {
557                                 if (redirected)
558                                     return super.read (dst);
559                                 else
560                                    {
561                                    DWORD i = dst.length / 4;
562
563                                    assert (i);
564
565                                    if (i > input.length)
566                                        i = input.length;
567                                        
568                                    // read a chunk of wchars from the console
569                                    if (! ReadConsoleW (handle, input.ptr, i, &i, null))
570                                          error();
571
572                                    // no input ~ go home
573                                    if (i is 0)
574                                        return Eof;
575
576                                    // translate to utf8, directly into dst
577                                    i = WideCharToMultiByte (CP_UTF8, 0, input.ptr, i,
578                                                             cast(PCHAR) dst.ptr, dst.length, null, null);
579                                    if (i is 0)
580                                        error ();
581
582                                    return i;
583                                    }
584                                 }
585                                 }
586
587                         }
588                      else
589                         {
590                         /*******************************************************
591
592                                 Create a FileConduit on the provided
593                                 FileDevice.
594
595                                 This is strictly for adapting existing
596                                 devices such as Stdout and friends
597
598                         *******************************************************/
599
600                         private this (Handle handle)
601                         {
602                                 reopen (handle);
603                                 redirected = (isatty(handle) is 0);
604                         }
605                         }
606         }
607 }
608
609
610 /******************************************************************************
611
612         Globals representing Console IO
613
614 ******************************************************************************/
615
616 static Console.Input    Cin;                    /// the standard input stream
617 static Console.Output   Cout,                   /// the standard output stream
618                         Cerr;                   /// the standard error stream
619
620
621 /******************************************************************************
622
623         Instantiate Console access
624
625 ******************************************************************************/
626
627 static this ()
628 {
629         auto conduit = new Console.Conduit (0);
630         Cin  = new Console.Input (conduit, conduit.redirected);
631
632         conduit = new Console.Conduit (1);
633         Cout = new Console.Output (conduit, conduit.redirected);
634
635         conduit = new Console.Conduit (2);
636         Cerr = new Console.