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