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 }