rev 51515 : 8209937: Enhance java.io.Console - provide methods to query console width and height Contributed-by: christoph.langer@sap.com, matthias.baesken@sap.com
1 /* 2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.io; 27 28 import java.util.*; 29 import java.nio.charset.Charset; 30 import jdk.internal.misc.JavaIOAccess; 31 import jdk.internal.misc.SharedSecrets; 32 import sun.nio.cs.StreamDecoder; 33 import sun.nio.cs.StreamEncoder; 34 35 /** 36 * Methods to access the character-based console device, if any, associated 37 * with the current Java virtual machine. 38 * 39 * <p> Whether a virtual machine has a console is dependent upon the 40 * underlying platform and also upon the manner in which the virtual 41 * machine is invoked. If the virtual machine is started from an 42 * interactive command line without redirecting the standard input and 43 * output streams then its console will exist and will typically be 44 * connected to the keyboard and display from which the virtual machine 45 * was launched. If the virtual machine is started automatically, for 46 * example by a background job scheduler, then it will typically not 47 * have a console. 48 * <p> 49 * If this virtual machine has a console then it is represented by a 50 * unique instance of this class which can be obtained by invoking the 51 * {@link java.lang.System#console()} method. If no console device is 52 * available then an invocation of that method will return {@code null}. 53 * <p> 54 * Read and write operations are synchronized to guarantee the atomic 55 * completion of critical operations; therefore invoking methods 56 * {@link #readLine()}, {@link #readPassword()}, {@link #format format()}, 57 * {@link #printf printf()} as well as the read, format and write operations 58 * on the objects returned by {@link #reader()} and {@link #writer()} may 59 * block in multithreaded scenarios. 60 * <p> 61 * Invoking {@code close()} on the objects returned by the {@link #reader()} 62 * and the {@link #writer()} will not close the underlying stream of those 63 * objects. 64 * <p> 65 * The console-read methods return {@code null} when the end of the 66 * console input stream is reached, for example by typing control-D on 67 * Unix or control-Z on Windows. Subsequent read operations will succeed 68 * if additional characters are later entered on the console's input 69 * device. 70 * <p> 71 * Unless otherwise specified, passing a {@code null} argument to any method 72 * in this class will cause a {@link NullPointerException} to be thrown. 73 * <p> 74 * <b>Security note:</b> 75 * If an application needs to read a password or other secure data, it should 76 * use {@link #readPassword()} or {@link #readPassword(String, Object...)} and 77 * manually zero the returned character array after processing to minimize the 78 * lifetime of sensitive data in memory. 79 * 80 * <blockquote><pre>{@code 81 * Console cons; 82 * char[] passwd; 83 * if ((cons = System.console()) != null && 84 * (passwd = cons.readPassword("[%s]", "Password:")) != null) { 85 * ... 86 * java.util.Arrays.fill(passwd, ' '); 87 * } 88 * }</pre></blockquote> 89 * 90 * @author Xueming Shen 91 * @since 1.6 92 */ 93 94 public final class Console implements Flushable 95 { 96 /** 97 * Retrieves the unique {@link java.io.PrintWriter PrintWriter} object 98 * associated with this console. 99 * 100 * @return The printwriter associated with this console 101 */ 102 public PrintWriter writer() { 103 return pw; 104 } 105 106 /** 107 * Retrieves the unique {@link java.io.Reader Reader} object associated 108 * with this console. 109 * <p> 110 * This method is intended to be used by sophisticated applications, for 111 * example, a {@link java.util.Scanner} object which utilizes the rich 112 * parsing/scanning functionality provided by the {@code Scanner}: 113 * <blockquote><pre> 114 * Console con = System.console(); 115 * if (con != null) { 116 * Scanner sc = new Scanner(con.reader()); 117 * ... 118 * } 119 * </pre></blockquote> 120 * <p> 121 * For simple applications requiring only line-oriented reading, use 122 * {@link #readLine}. 123 * <p> 124 * The bulk read operations {@link java.io.Reader#read(char[]) read(char[]) }, 125 * {@link java.io.Reader#read(char[], int, int) read(char[], int, int) } and 126 * {@link java.io.Reader#read(java.nio.CharBuffer) read(java.nio.CharBuffer)} 127 * on the returned object will not read in characters beyond the line 128 * bound for each invocation, even if the destination buffer has space for 129 * more characters. The {@code Reader}'s {@code read} methods may block if a 130 * line bound has not been entered or reached on the console's input device. 131 * A line bound is considered to be any one of a line feed ({@code '\n'}), 132 * a carriage return ({@code '\r'}), a carriage return followed immediately 133 * by a linefeed, or an end of stream. 134 * 135 * @return The reader associated with this console 136 */ 137 public Reader reader() { 138 return reader; 139 } 140 141 /** 142 * Writes a formatted string to this console's output stream using 143 * the specified format string and arguments. 144 * 145 * @param fmt 146 * A format string as described in <a 147 * href="../util/Formatter.html#syntax">Format string syntax</a> 148 * 149 * @param args 150 * Arguments referenced by the format specifiers in the format 151 * string. If there are more arguments than format specifiers, the 152 * extra arguments are ignored. The number of arguments is 153 * variable and may be zero. The maximum number of arguments is 154 * limited by the maximum dimension of a Java array as defined by 155 * <cite>The Java™ Virtual Machine Specification</cite>. 156 * The behaviour on a 157 * {@code null} argument depends on the <a 158 * href="../util/Formatter.html#syntax">conversion</a>. 159 * 160 * @throws IllegalFormatException 161 * If a format string contains an illegal syntax, a format 162 * specifier that is incompatible with the given arguments, 163 * insufficient arguments given the format string, or other 164 * illegal conditions. For specification of all possible 165 * formatting errors, see the <a 166 * href="../util/Formatter.html#detail">Details</a> section 167 * of the formatter class specification. 168 * 169 * @return This console 170 */ 171 public Console format(String fmt, Object ...args) { 172 formatter.format(fmt, args).flush(); 173 return this; 174 } 175 176 /** 177 * A convenience method to write a formatted string to this console's 178 * output stream using the specified format string and arguments. 179 * 180 * <p> An invocation of this method of the form 181 * {@code con.printf(format, args)} behaves in exactly the same way 182 * as the invocation of 183 * <pre>con.format(format, args)</pre>. 184 * 185 * @param format 186 * A format string as described in <a 187 * href="../util/Formatter.html#syntax">Format string syntax</a>. 188 * 189 * @param args 190 * Arguments referenced by the format specifiers in the format 191 * string. If there are more arguments than format specifiers, the 192 * extra arguments are ignored. The number of arguments is 193 * variable and may be zero. The maximum number of arguments is 194 * limited by the maximum dimension of a Java array as defined by 195 * <cite>The Java™ Virtual Machine Specification</cite>. 196 * The behaviour on a 197 * {@code null} argument depends on the <a 198 * href="../util/Formatter.html#syntax">conversion</a>. 199 * 200 * @throws IllegalFormatException 201 * If a format string contains an illegal syntax, a format 202 * specifier that is incompatible with the given arguments, 203 * insufficient arguments given the format string, or other 204 * illegal conditions. For specification of all possible 205 * formatting errors, see the <a 206 * href="../util/Formatter.html#detail">Details</a> section of the 207 * formatter class specification. 208 * 209 * @return This console 210 */ 211 public Console printf(String format, Object ... args) { 212 return format(format, args); 213 } 214 215 /** 216 * Provides a formatted prompt, then reads a single line of text from the 217 * console. 218 * 219 * @param fmt 220 * A format string as described in <a 221 * href="../util/Formatter.html#syntax">Format string syntax</a>. 222 * 223 * @param args 224 * Arguments referenced by the format specifiers in the format 225 * string. If there are more arguments than format specifiers, the 226 * extra arguments are ignored. The maximum number of arguments is 227 * limited by the maximum dimension of a Java array as defined by 228 * <cite>The Java™ Virtual Machine Specification</cite>. 229 * 230 * @throws IllegalFormatException 231 * If a format string contains an illegal syntax, a format 232 * specifier that is incompatible with the given arguments, 233 * insufficient arguments given the format string, or other 234 * illegal conditions. For specification of all possible 235 * formatting errors, see the <a 236 * href="../util/Formatter.html#detail">Details</a> section 237 * of the formatter class specification. 238 * 239 * @throws IOError 240 * If an I/O error occurs. 241 * 242 * @return A string containing the line read from the console, not 243 * including any line-termination characters, or {@code null} 244 * if an end of stream has been reached. 245 */ 246 public String readLine(String fmt, Object ... args) { 247 String line = null; 248 synchronized (writeLock) { 249 synchronized(readLock) { 250 if (fmt.length() != 0) 251 pw.format(fmt, args); 252 try { 253 char[] ca = readline(false); 254 if (ca != null) 255 line = new String(ca); 256 } catch (IOException x) { 257 throw new IOError(x); 258 } 259 } 260 } 261 return line; 262 } 263 264 /** 265 * Reads a single line of text from the console. 266 * 267 * @throws IOError 268 * If an I/O error occurs. 269 * 270 * @return A string containing the line read from the console, not 271 * including any line-termination characters, or {@code null} 272 * if an end of stream has been reached. 273 */ 274 public String readLine() { 275 return readLine(""); 276 } 277 278 /** 279 * Provides a formatted prompt, then reads a password or passphrase from 280 * the console with echoing disabled. 281 * 282 * @param fmt 283 * A format string as described in <a 284 * href="../util/Formatter.html#syntax">Format string syntax</a> 285 * for the prompt text. 286 * 287 * @param args 288 * Arguments referenced by the format specifiers in the format 289 * string. If there are more arguments than format specifiers, the 290 * extra arguments are ignored. The maximum number of arguments is 291 * limited by the maximum dimension of a Java array as defined by 292 * <cite>The Java™ Virtual Machine Specification</cite>. 293 * 294 * @throws IllegalFormatException 295 * If a format string contains an illegal syntax, a format 296 * specifier that is incompatible with the given arguments, 297 * insufficient arguments given the format string, or other 298 * illegal conditions. For specification of all possible 299 * formatting errors, see the <a 300 * href="../util/Formatter.html#detail">Details</a> 301 * section of the formatter class specification. 302 * 303 * @throws IOError 304 * If an I/O error occurs. 305 * 306 * @return A character array containing the password or passphrase read 307 * from the console, not including any line-termination characters, 308 * or {@code null} if an end of stream has been reached. 309 */ 310 public char[] readPassword(String fmt, Object ... args) { 311 char[] passwd = null; 312 synchronized (writeLock) { 313 synchronized(readLock) { 314 installShutdownHook(); 315 try { 316 restoreEcho = echo(false); 317 } catch (IOException x) { 318 throw new IOError(x); 319 } 320 IOError ioe = null; 321 try { 322 if (fmt.length() != 0) 323 pw.format(fmt, args); 324 passwd = readline(true); 325 } catch (IOException x) { 326 ioe = new IOError(x); 327 } finally { 328 try { 329 if (restoreEcho) 330 restoreEcho = echo(true); 331 } catch (IOException x) { 332 if (ioe == null) 333 ioe = new IOError(x); 334 else 335 ioe.addSuppressed(x); 336 } 337 if (ioe != null) 338 throw ioe; 339 } 340 pw.println(); 341 } 342 } 343 return passwd; 344 } 345 346 private void installShutdownHook() { 347 if (shutdownHookInstalled) 348 return; 349 try { 350 // Add a shutdown hook to restore console's echo state should 351 // it be necessary. 352 SharedSecrets.getJavaLangAccess() 353 .registerShutdownHook(0 /* shutdown hook invocation order */, 354 false /* only register if shutdown is not in progress */, 355 new Runnable() { 356 public void run() { 357 try { 358 if (restoreEcho) { 359 echo(true); 360 } 361 } catch (IOException x) { } 362 } 363 }); 364 } catch (IllegalStateException e) { 365 // shutdown is already in progress and readPassword is first used 366 // by a shutdown hook 367 } 368 shutdownHookInstalled = true; 369 } 370 371 /** 372 * Reads a password or passphrase from the console with echoing disabled 373 * 374 * @throws IOError 375 * If an I/O error occurs. 376 * 377 * @return A character array containing the password or passphrase read 378 * from the console, not including any line-termination characters, 379 * or {@code null} if an end of stream has been reached. 380 */ 381 public char[] readPassword() { 382 return readPassword(""); 383 } 384 385 /** 386 * Flushes the console and forces any buffered output to be written 387 * immediately . 388 */ 389 public void flush() { 390 pw.flush(); 391 } 392 393 private Object readLock; 394 private Object writeLock; 395 private Reader reader; 396 private Writer out; 397 private PrintWriter pw; 398 private Formatter formatter; 399 private Charset cs; 400 private char[] rcb; 401 private boolean restoreEcho; 402 private boolean shutdownHookInstalled; 403 private static native String encoding(); 404 /* 405 * Sets the console echo status to {@code on} and returns the previous 406 * console on/off status. 407 * @param on the echo status to set to. {@code true} for echo on and 408 * {@code false} for echo off 409 * @return true if the previous console echo status is on 410 */ 411 private static native boolean echo(boolean on) throws IOException; 412 413 private char[] readline(boolean zeroOut) throws IOException { 414 int len = reader.read(rcb, 0, rcb.length); 415 if (len < 0) 416 return null; //EOL 417 if (rcb[len-1] == '\r') 418 len--; //remove CR at end; 419 else if (rcb[len-1] == '\n') { 420 len--; //remove LF at end; 421 if (len > 0 && rcb[len-1] == '\r') 422 len--; //remove the CR, if there is one 423 } 424 char[] b = new char[len]; 425 if (len > 0) { 426 System.arraycopy(rcb, 0, b, 0, len); 427 if (zeroOut) { 428 Arrays.fill(rcb, 0, len, ' '); 429 } 430 } 431 return b; 432 } 433 434 private char[] grow() { 435 assert Thread.holdsLock(readLock); 436 char[] t = new char[rcb.length * 2]; 437 System.arraycopy(rcb, 0, t, 0, rcb.length); 438 rcb = t; 439 return rcb; 440 } 441 442 class LineReader extends Reader { 443 private Reader in; 444 private char[] cb; 445 private int nChars, nextChar; 446 boolean leftoverLF; 447 LineReader(Reader in) { 448 this.in = in; 449 cb = new char[1024]; 450 nextChar = nChars = 0; 451 leftoverLF = false; 452 } 453 public void close () {} 454 public boolean ready() throws IOException { 455 //in.ready synchronizes on readLock already 456 return in.ready(); 457 } 458 459 public int read(char cbuf[], int offset, int length) 460 throws IOException 461 { 462 int off = offset; 463 int end = offset + length; 464 if (offset < 0 || offset > cbuf.length || length < 0 || 465 end < 0 || end > cbuf.length) { 466 throw new IndexOutOfBoundsException(); 467 } 468 synchronized(readLock) { 469 boolean eof = false; 470 char c = 0; 471 for (;;) { 472 if (nextChar >= nChars) { //fill 473 int n = 0; 474 do { 475 n = in.read(cb, 0, cb.length); 476 } while (n == 0); 477 if (n > 0) { 478 nChars = n; 479 nextChar = 0; 480 if (n < cb.length && 481 cb[n-1] != '\n' && cb[n-1] != '\r') { 482 /* 483 * we're in canonical mode so each "fill" should 484 * come back with an eol. if there no lf or nl at 485 * the end of returned bytes we reached an eof. 486 */ 487 eof = true; 488 } 489 } else { /*EOF*/ 490 if (off - offset == 0) 491 return -1; 492 return off - offset; 493 } 494 } 495 if (leftoverLF && cbuf == rcb && cb[nextChar] == '\n') { 496 /* 497 * if invoked by our readline, skip the leftover, otherwise 498 * return the LF. 499 */ 500 nextChar++; 501 } 502 leftoverLF = false; 503 while (nextChar < nChars) { 504 c = cbuf[off++] = cb[nextChar]; 505 cb[nextChar++] = 0; 506 if (c == '\n') { 507 return off - offset; 508 } else if (c == '\r') { 509 if (off == end) { 510 /* no space left even the next is LF, so return 511 * whatever we have if the invoker is not our 512 * readLine() 513 */ 514 if (cbuf == rcb) { 515 cbuf = grow(); 516 end = cbuf.length; 517 } else { 518 leftoverLF = true; 519 return off - offset; 520 } 521 } 522 if (nextChar == nChars && in.ready()) { 523 /* 524 * we have a CR and we reached the end of 525 * the read in buffer, fill to make sure we 526 * don't miss a LF, if there is one, it's possible 527 * that it got cut off during last round reading 528 * simply because the read in buffer was full. 529 */ 530 nChars = in.read(cb, 0, cb.length); 531 nextChar = 0; 532 } 533 if (nextChar < nChars && cb[nextChar] == '\n') { 534 cbuf[off++] = '\n'; 535 nextChar++; 536 } 537 return off - offset; 538 } else if (off == end) { 539 if (cbuf == rcb) { 540 cbuf = grow(); 541 end = cbuf.length; 542 } else { 543 return off - offset; 544 } 545 } 546 } 547 if (eof) 548 return off - offset; 549 } 550 } 551 } 552 } 553 554 // Set up JavaIOAccess in SharedSecrets 555 static { 556 SharedSecrets.setJavaIOAccess(new JavaIOAccess() { 557 public Console console() { 558 if (istty()) { 559 if (cons == null) 560 cons = new Console(); 561 return cons; 562 } 563 return null; 564 } 565 566 public Charset charset() { 567 // This method is called in sun.security.util.Password, 568 // cons already exists when this method is called 569 return cons.cs; 570 } 571 }); 572 } 573 private static Console cons; 574 private static native boolean istty(); 575 private Console() { 576 readLock = new Object(); 577 writeLock = new Object(); 578 String csname = encoding(); 579 if (csname != null) { 580 try { 581 cs = Charset.forName(csname); 582 } catch (Exception x) {} 583 } 584 if (cs == null) 585 cs = Charset.defaultCharset(); 586 out = StreamEncoder.forOutputStreamWriter( 587 new FileOutputStream(FileDescriptor.out), 588 writeLock, 589 cs); 590 pw = new PrintWriter(out, true) { public void close() {} }; 591 formatter = new Formatter(out); 592 reader = new LineReader(StreamDecoder.forInputStreamReader( 593 new FileInputStream(FileDescriptor.in), 594 readLock, 595 cs)); 596 rcb = new char[1024]; 597 } 598 } --- EOF ---