1 /*******************************************************************************
2 
3     Copyright:
4         Copyright (c) 2004 Kris Bell.
5         Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6         All rights reserved.
7 
8     License:
9         Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10         See LICENSE_TANGO.txt for details.
11 
12     Version:
13         Feb 2005: Initial release
14         Nov 2005: Heavily revised for unicode
15         Dec 2006: Outback release
16 
17     Authors: Kris
18 
19 *******************************************************************************/
20 
21 module ocean.io.Console;
22 
23 import ocean.meta.types.Qualifiers;
24 
25 import ocean.sys.Common;
26 
27 import ocean.io.device.Device,
28        ocean.io.stream.Buffered;
29 
30 import core.sys.posix.unistd: isatty;
31 
32 
33 /*******************************************************************************
34 
35   Low-level console IO support.
36 
37   Note that for a while this was templated for each of char, wchar,
38   and dchar. It became clear after some usage that the console is
39   more useful if it sticks to UTF8 only. See Console.Conduit below
40   for details.
41 
42   Redirecting the standard IO handles (via a shell) operates as one
43   would expect, though the redirected content should likely restrict
44   itself to UTF8.
45 
46 *******************************************************************************/
47 
48 struct Console
49 {
50     enum istring Eol = "\n";
51 
52     /**********************************************************************
53 
54       Model console input as a buffer. Note that we read UTF8
55       only.
56 
57      **********************************************************************/
58 
59     class Input
60     {
61         private Bin     buffer;
62         private bool    redirect;
63 
64         public alias    copyln get;
65 
66         /**************************************************************
67 
68           Attach console input to the provided device.
69 
70          **************************************************************/
71 
72         private this (Conduit conduit, bool redirected)
73         {
74             redirect = redirected;
75             buffer = new Bin (conduit);
76         }
77 
78         /**************************************************************
79 
80           Return the next line available from the console,
81           or null when there is nothing available. The value
82           returned is a duplicate of the buffer content (it
83           has .dup applied).
84 
85           Each line ending is removed unless parameter raw is
86           set to true.
87 
88          **************************************************************/
89 
90         final mstring copyln (bool raw = false)
91         {
92             cstring line;
93 
94             return readln (line, raw) ? line.dup : null;
95         }
96 
97         /**************************************************************
98 
99           Retreive a line of text from the console and map
100           it to the given argument. The input is sliced,
101           not copied, so use .dup appropriately. Each line
102           ending is removed unless parameter raw is set to
103           true.
104 
105           Returns false when there is no more input.
106 
107          **************************************************************/
108 
109         final bool readln (out cstring content, bool raw=false)
110         {
111             size_t line (const(void)[] input)
112             {
113                 auto text = cast(cstring) input;
114                 foreach (i, c; text)
115                     if (c is '\n')
116                     {
117                         auto j = i;
118                         if (raw)
119                             ++j;
120                         else
121                             if (j && (text[j-1] is '\r'))
122                                 --j;
123                         content = text [0 .. j];
124                         return i+1;
125                     }
126                 return IConduit.Eof;
127             }
128 
129             // get next line, return true
130             if (buffer.next (&line))
131                 return true;
132 
133             // assign trailing content and return false
134             content = cast(cstring) buffer.slice (buffer.readable);
135             return false;
136         }
137 
138         /**************************************************************
139 
140           Return the associated stream.
141 
142          **************************************************************/
143 
144         final InputStream stream ()
145         {
146             return buffer;
147         }
148 
149         /**************************************************************
150 
151           Is this device redirected?
152 
153           Returns:
154           True if redirected, false otherwise.
155 
156           Remarks:
157           Reflects the console redirection status from when
158           this module was instantiated.
159 
160          **************************************************************/
161 
162         final bool redirected ()
163         {
164             return redirect;
165         }
166 
167         /**************************************************************
168 
169           Set redirection state to the provided boolean.
170 
171           Remarks:
172             Configure the console redirection status, where
173             a redirected console is more efficient (dictates
174             whether newline() performs automatic flushing or
175             not.)
176 
177          **************************************************************/
178 
179         final Input redirected (bool yes)
180         {
181             redirect = yes;
182             return this;
183         }
184 
185         /**************************************************************
186 
187           Returns the configured source
188 
189           Remarks:
190             Provides access to the underlying mechanism for
191             console input. Use this to retain prior state
192             when temporarily switching inputs.
193 
194          **************************************************************/
195 
196         final InputStream input ()
197         {
198             return buffer.input;
199         }
200 
201         /**************************************************************
202 
203           Divert input to an alternate source.
204 
205          **************************************************************/
206 
207         final Input input (InputStream source)
208         {
209             buffer.input = source;
210             return this;
211         }
212     }
213 
214 
215     /**********************************************************************
216 
217       Console output accepts UTF8 only.
218 
219      **********************************************************************/
220 
221     class Output
222     {
223         private Bout    buffer;
224         private bool    redirect;
225 
226         public  alias   append opCall;
227         public  alias   flush  opCall;
228 
229         /**************************************************************
230 
231           Attach console output to the provided device.
232 
233          **************************************************************/
234 
235         private this (Conduit conduit, bool redirected)
236         {
237             redirect = redirected;
238             buffer = new Bout (conduit);
239         }
240 
241         /**************************************************************
242 
243           Append to the console. We accept UTF8 only, so
244           all other encodings should be handled via some
245           higher level API.
246 
247          **************************************************************/
248 
249         final Output append (cstring x)
250         {
251             buffer.append (x.ptr, x.length);
252             return this;
253         }
254 
255         /**************************************************************
256 
257           Append content.
258 
259           Params:
260             other = An object with a useful toString() method.
261 
262           Returns:
263             Returns a chaining reference if all content was
264             written. Throws an IOException indicating Eof or
265             Eob if not.
266 
267           Remarks:
268             Append the result of other.toString() to the console.
269 
270          **************************************************************/
271 
272         final Output append (Object other)
273         {
274             return append (other.toString);
275         }
276 
277         /**************************************************************
278 
279           Append a newline and flush the console buffer. If
280           the output is redirected, flushing does not occur
281           automatically.
282 
283           Returns:
284             Returns a chaining reference if content was written.
285             Throws an IOException indicating Eof or Eob if not.
286 
287           Remarks:
288             Emit a newline into the buffer, and autoflush the
289             current buffer content for an interactive console.
290             Redirected consoles do not flush automatically on
291             a newline.
292 
293          **************************************************************/
294 
295         final Output newline ()
296         {
297             buffer.append (Eol);
298             if (redirect is false)
299                 buffer.flush;
300 
301             return this;
302         }
303 
304         /**************************************************************
305 
306           Explicitly flush console output.
307 
308           Returns:
309             Returns a chaining reference if content was written.
310             Throws an IOException indicating Eof or Eob if not.
311 
312           Remarks:
313             Flushes the console buffer to attached conduit.
314 
315          **************************************************************/
316 
317         final Output flush ()
318         {
319             buffer.flush;
320             return this;
321         }
322 
323         /**************************************************************
324 
325           Return the associated stream.
326 
327          **************************************************************/
328 
329         final OutputStream stream ()
330         {
331             return buffer;
332         }
333 
334         /**************************************************************
335 
336           Is this device redirected?
337 
338           Returns:
339             True if redirected, false otherwise.
340 
341           Remarks:
342             Reflects the console redirection status.
343 
344          **************************************************************/
345 
346         final bool redirected ()
347         {
348             return redirect;
349         }
350 
351         /**************************************************************
352 
353           Set redirection state to the provided boolean.
354 
355           Remarks:
356             Configure the console redirection status, where
357             a redirected console is more efficient (dictates
358             whether newline() performs automatic flushing or
359             not.)
360 
361          **************************************************************/
362 
363         final Output redirected (bool yes)
364         {
365             redirect = yes;
366             return this;
367         }
368 
369         /**************************************************************
370 
371           Returns the configured output sink.
372 
373           Remarks:
374             Provides access to the underlying mechanism for
375             console output. Use this to retain prior state
376             when temporarily switching outputs.
377 
378          **************************************************************/
379 
380         final OutputStream output ()
381         {
382             return buffer.output;
383         }
384 
385         /**************************************************************
386 
387           Divert output to an alternate sink.
388 
389          **************************************************************/
390 
391         final Output output (OutputStream sink)
392         {
393             buffer.output = sink;
394             return this;
395         }
396     }
397 
398 
399     /***********************************************************************
400 
401       Conduit for specifically handling the console devices. It used to have
402       special implementation for Win32 but it was removed as unmaintained
403       during D2 transition.
404 
405      ***********************************************************************/
406 
407     class Conduit : Device
408     {
409         private bool redirected = false;
410 
411         /***********************************************************************
412 
413           Return the name of this conduit.
414 
415          ***********************************************************************/
416 
417         override istring toString()
418         {
419             return "<console>";
420         }
421 
422         /*******************************************************
423 
424           Associate this device with a given handle.
425 
426           This is strictly for adapting existing
427           devices such as Stdout and friends.
428 
429          *******************************************************/
430 
431         private this (int handle)
432         {
433             this.handle = cast(Handle) handle;
434             redirected = (isatty(handle) is 0);
435         }
436     }
437 }
438 
439 
440 /******************************************************************************
441 
442   Globals representing Console IO.
443 
444  ******************************************************************************/
445 
446 public __gshared Console.Input Cin;  /// The standard input stream.
447 public __gshared Console.Output Cout; /// The standard output stream.
448 public __gshared Console.Output Cerr; /// The standard error stream.
449 
450 
451 /******************************************************************************
452 
453   Instantiate Console access.
454 
455  ******************************************************************************/
456 
457 shared static this ()
458 {
459     auto conduit = new Console.Conduit (0);
460     Cin  = new Console.Input (conduit, conduit.redirected);
461 
462     conduit = new Console.Conduit (1);
463     Cout = new Console.Output (conduit, conduit.redirected);
464 
465     conduit = new Console.Conduit (2);
466     Cerr = new Console.Output (conduit, conduit.redirected);
467 }
468 
469 /******************************************************************************
470 
471   Flush outputs before we exit.
472 
473   (Good idea from Frits Van Bommel.)
474 
475  ******************************************************************************/
476 
477 static ~this()
478 {
479     Cout.flush;
480     Cerr.flush;
481 }
482 
483 
484 /******************************************************************************
485 
486  ******************************************************************************/
487 
488 debug (Console)
489 {
490     void main()
491     {
492         Cout ("hello world").newline;
493     }
494 }