Path: blob/master/src/java.base/share/classes/java/io/Console.java
41152 views
/*1* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.io;2627import java.util.*;28import java.nio.charset.Charset;29import jdk.internal.access.JavaIOAccess;30import jdk.internal.access.SharedSecrets;31import sun.nio.cs.StreamDecoder;32import sun.nio.cs.StreamEncoder;33import sun.security.action.GetPropertyAction;3435/**36* Methods to access the character-based console device, if any, associated37* with the current Java virtual machine.38*39* <p> Whether a virtual machine has a console is dependent upon the40* underlying platform and also upon the manner in which the virtual41* machine is invoked. If the virtual machine is started from an42* interactive command line without redirecting the standard input and43* output streams then its console will exist and will typically be44* connected to the keyboard and display from which the virtual machine45* was launched. If the virtual machine is started automatically, for46* example by a background job scheduler, then it will typically not47* have a console.48* <p>49* If this virtual machine has a console then it is represented by a50* unique instance of this class which can be obtained by invoking the51* {@link java.lang.System#console()} method. If no console device is52* available then an invocation of that method will return {@code null}.53* <p>54* Read and write operations are synchronized to guarantee the atomic55* completion of critical operations; therefore invoking methods56* {@link #readLine()}, {@link #readPassword()}, {@link #format format()},57* {@link #printf printf()} as well as the read, format and write operations58* on the objects returned by {@link #reader()} and {@link #writer()} may59* 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 those63* objects.64* <p>65* The console-read methods return {@code null} when the end of the66* console input stream is reached, for example by typing control-D on67* Unix or control-Z on Windows. Subsequent read operations will succeed68* if additional characters are later entered on the console's input69* device.70* <p>71* Unless otherwise specified, passing a {@code null} argument to any method72* 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 should76* use {@link #readPassword()} or {@link #readPassword(String, Object...)} and77* manually zero the returned character array after processing to minimize the78* lifetime of sensitive data in memory.79*80* <blockquote><pre>{@code81* 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 Shen91* @since 1.692*/9394public final class Console implements Flushable95{96/**97* Retrieves the unique {@link java.io.PrintWriter PrintWriter} object98* associated with this console.99*100* @return The printwriter associated with this console101*/102public PrintWriter writer() {103return pw;104}105106/**107* Retrieves the unique {@link java.io.Reader Reader} object associated108* with this console.109* <p>110* This method is intended to be used by sophisticated applications, for111* example, a {@link java.util.Scanner} object which utilizes the rich112* 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, use122* {@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) } and126* {@link java.io.Reader#read(java.nio.CharBuffer) read(java.nio.CharBuffer)}127* on the returned object will not read in characters beyond the line128* bound for each invocation, even if the destination buffer has space for129* more characters. The {@code Reader}'s {@code read} methods may block if a130* 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 immediately133* by a linefeed, or an end of stream.134*135* @return The reader associated with this console136*/137public Reader reader() {138return reader;139}140141/**142* Writes a formatted string to this console's output stream using143* the specified format string and arguments.144*145* @param fmt146* A format string as described in <a147* href="../util/Formatter.html#syntax">Format string syntax</a>148*149* @param args150* Arguments referenced by the format specifiers in the format151* string. If there are more arguments than format specifiers, the152* extra arguments are ignored. The number of arguments is153* variable and may be zero. The maximum number of arguments is154* limited by the maximum dimension of a Java array as defined by155* <cite>The Java Virtual Machine Specification</cite>.156* The behaviour on a157* {@code null} argument depends on the <a158* href="../util/Formatter.html#syntax">conversion</a>.159*160* @throws IllegalFormatException161* If a format string contains an illegal syntax, a format162* specifier that is incompatible with the given arguments,163* insufficient arguments given the format string, or other164* illegal conditions. For specification of all possible165* formatting errors, see the <a166* href="../util/Formatter.html#detail">Details</a> section167* of the formatter class specification.168*169* @return This console170*/171public Console format(String fmt, Object ...args) {172formatter.format(fmt, args).flush();173return this;174}175176/**177* A convenience method to write a formatted string to this console's178* output stream using the specified format string and arguments.179*180* <p> An invocation of this method of the form181* {@code con.printf(format, args)} behaves in exactly the same way182* as the invocation of183* <pre>con.format(format, args)</pre>.184*185* @param format186* A format string as described in <a187* href="../util/Formatter.html#syntax">Format string syntax</a>.188*189* @param args190* Arguments referenced by the format specifiers in the format191* string. If there are more arguments than format specifiers, the192* extra arguments are ignored. The number of arguments is193* variable and may be zero. The maximum number of arguments is194* limited by the maximum dimension of a Java array as defined by195* <cite>The Java Virtual Machine Specification</cite>.196* The behaviour on a197* {@code null} argument depends on the <a198* href="../util/Formatter.html#syntax">conversion</a>.199*200* @throws IllegalFormatException201* If a format string contains an illegal syntax, a format202* specifier that is incompatible with the given arguments,203* insufficient arguments given the format string, or other204* illegal conditions. For specification of all possible205* formatting errors, see the <a206* href="../util/Formatter.html#detail">Details</a> section of the207* formatter class specification.208*209* @return This console210*/211public Console printf(String format, Object ... args) {212return format(format, args);213}214215/**216* Provides a formatted prompt, then reads a single line of text from the217* console.218*219* @param fmt220* A format string as described in <a221* href="../util/Formatter.html#syntax">Format string syntax</a>.222*223* @param args224* Arguments referenced by the format specifiers in the format225* string. If there are more arguments than format specifiers, the226* extra arguments are ignored. The maximum number of arguments is227* limited by the maximum dimension of a Java array as defined by228* <cite>The Java Virtual Machine Specification</cite>.229*230* @throws IllegalFormatException231* If a format string contains an illegal syntax, a format232* specifier that is incompatible with the given arguments,233* insufficient arguments given the format string, or other234* illegal conditions. For specification of all possible235* formatting errors, see the <a236* href="../util/Formatter.html#detail">Details</a> section237* of the formatter class specification.238*239* @throws IOError240* If an I/O error occurs.241*242* @return A string containing the line read from the console, not243* including any line-termination characters, or {@code null}244* if an end of stream has been reached.245*/246public String readLine(String fmt, Object ... args) {247String line = null;248synchronized (writeLock) {249synchronized(readLock) {250if (!fmt.isEmpty())251pw.format(fmt, args);252try {253char[] ca = readline(false);254if (ca != null)255line = new String(ca);256} catch (IOException x) {257throw new IOError(x);258}259}260}261return line;262}263264/**265* Reads a single line of text from the console.266*267* @throws IOError268* If an I/O error occurs.269*270* @return A string containing the line read from the console, not271* including any line-termination characters, or {@code null}272* if an end of stream has been reached.273*/274public String readLine() {275return readLine("");276}277278/**279* Provides a formatted prompt, then reads a password or passphrase from280* the console with echoing disabled.281*282* @param fmt283* A format string as described in <a284* href="../util/Formatter.html#syntax">Format string syntax</a>285* for the prompt text.286*287* @param args288* Arguments referenced by the format specifiers in the format289* string. If there are more arguments than format specifiers, the290* extra arguments are ignored. The maximum number of arguments is291* limited by the maximum dimension of a Java array as defined by292* <cite>The Java Virtual Machine Specification</cite>.293*294* @throws IllegalFormatException295* If a format string contains an illegal syntax, a format296* specifier that is incompatible with the given arguments,297* insufficient arguments given the format string, or other298* illegal conditions. For specification of all possible299* formatting errors, see the <a300* href="../util/Formatter.html#detail">Details</a>301* section of the formatter class specification.302*303* @throws IOError304* If an I/O error occurs.305*306* @return A character array containing the password or passphrase read307* from the console, not including any line-termination characters,308* or {@code null} if an end of stream has been reached.309*/310public char[] readPassword(String fmt, Object ... args) {311char[] passwd = null;312synchronized (writeLock) {313synchronized(readLock) {314installShutdownHook();315try {316restoreEcho = echo(false);317} catch (IOException x) {318throw new IOError(x);319}320IOError ioe = null;321try {322if (!fmt.isEmpty())323pw.format(fmt, args);324passwd = readline(true);325} catch (IOException x) {326ioe = new IOError(x);327} finally {328try {329if (restoreEcho)330restoreEcho = echo(true);331} catch (IOException x) {332if (ioe == null)333ioe = new IOError(x);334else335ioe.addSuppressed(x);336}337if (ioe != null)338throw ioe;339}340pw.println();341}342}343return passwd;344}345346private void installShutdownHook() {347if (shutdownHookInstalled)348return;349try {350// Add a shutdown hook to restore console's echo state should351// it be necessary.352SharedSecrets.getJavaLangAccess()353.registerShutdownHook(0 /* shutdown hook invocation order */,354false /* only register if shutdown is not in progress */,355new Runnable() {356public void run() {357try {358if (restoreEcho) {359echo(true);360}361} catch (IOException x) { }362}363});364} catch (IllegalStateException e) {365// shutdown is already in progress and readPassword is first used366// by a shutdown hook367}368shutdownHookInstalled = true;369}370371/**372* Reads a password or passphrase from the console with echoing disabled373*374* @throws IOError375* If an I/O error occurs.376*377* @return A character array containing the password or passphrase read378* from the console, not including any line-termination characters,379* or {@code null} if an end of stream has been reached.380*/381public char[] readPassword() {382return readPassword("");383}384385/**386* Flushes the console and forces any buffered output to be written387* immediately .388*/389public void flush() {390pw.flush();391}392393394/**395* Returns the {@link java.nio.charset.Charset Charset} object used for396* the {@code Console}.397* <p>398* The returned charset corresponds to the input and output source399* (e.g., keyboard and/or display) specified by the host environment or user.400* It may not necessarily be the same as the default charset returned from401* {@link java.nio.charset.Charset#defaultCharset() Charset.defaultCharset()}.402*403* @return a {@link java.nio.charset.Charset Charset} object used for the404* {@code Console}405* @since 17406*/407public Charset charset() {408assert CHARSET != null : "charset() should not return null";409return CHARSET;410}411412private Object readLock;413private Object writeLock;414private Reader reader;415private Writer out;416private PrintWriter pw;417private Formatter formatter;418private char[] rcb;419private boolean restoreEcho;420private boolean shutdownHookInstalled;421private static native String encoding();422/*423* Sets the console echo status to {@code on} and returns the previous424* console on/off status.425* @param on the echo status to set to. {@code true} for echo on and426* {@code false} for echo off427* @return true if the previous console echo status is on428*/429private static native boolean echo(boolean on) throws IOException;430431private char[] readline(boolean zeroOut) throws IOException {432int len = reader.read(rcb, 0, rcb.length);433if (len < 0)434return null; //EOL435if (rcb[len-1] == '\r')436len--; //remove CR at end;437else if (rcb[len-1] == '\n') {438len--; //remove LF at end;439if (len > 0 && rcb[len-1] == '\r')440len--; //remove the CR, if there is one441}442char[] b = new char[len];443if (len > 0) {444System.arraycopy(rcb, 0, b, 0, len);445if (zeroOut) {446Arrays.fill(rcb, 0, len, ' ');447}448}449return b;450}451452private char[] grow() {453assert Thread.holdsLock(readLock);454char[] t = new char[rcb.length * 2];455System.arraycopy(rcb, 0, t, 0, rcb.length);456rcb = t;457return rcb;458}459460class LineReader extends Reader {461private Reader in;462private char[] cb;463private int nChars, nextChar;464boolean leftoverLF;465LineReader(Reader in) {466this.in = in;467cb = new char[1024];468nextChar = nChars = 0;469leftoverLF = false;470}471public void close () {}472public boolean ready() throws IOException {473//in.ready synchronizes on readLock already474return in.ready();475}476477public int read(char cbuf[], int offset, int length)478throws IOException479{480int off = offset;481int end = offset + length;482if (offset < 0 || offset > cbuf.length || length < 0 ||483end < 0 || end > cbuf.length) {484throw new IndexOutOfBoundsException();485}486synchronized(readLock) {487boolean eof = false;488char c = 0;489for (;;) {490if (nextChar >= nChars) { //fill491int n = 0;492do {493n = in.read(cb, 0, cb.length);494} while (n == 0);495if (n > 0) {496nChars = n;497nextChar = 0;498if (n < cb.length &&499cb[n-1] != '\n' && cb[n-1] != '\r') {500/*501* we're in canonical mode so each "fill" should502* come back with an eol. if there no lf or nl at503* the end of returned bytes we reached an eof.504*/505eof = true;506}507} else { /*EOF*/508if (off - offset == 0)509return -1;510return off - offset;511}512}513if (leftoverLF && cbuf == rcb && cb[nextChar] == '\n') {514/*515* if invoked by our readline, skip the leftover, otherwise516* return the LF.517*/518nextChar++;519}520leftoverLF = false;521while (nextChar < nChars) {522c = cbuf[off++] = cb[nextChar];523cb[nextChar++] = 0;524if (c == '\n') {525return off - offset;526} else if (c == '\r') {527if (off == end) {528/* no space left even the next is LF, so return529* whatever we have if the invoker is not our530* readLine()531*/532if (cbuf == rcb) {533cbuf = grow();534end = cbuf.length;535} else {536leftoverLF = true;537return off - offset;538}539}540if (nextChar == nChars && in.ready()) {541/*542* we have a CR and we reached the end of543* the read in buffer, fill to make sure we544* don't miss a LF, if there is one, it's possible545* that it got cut off during last round reading546* simply because the read in buffer was full.547*/548nChars = in.read(cb, 0, cb.length);549nextChar = 0;550}551if (nextChar < nChars && cb[nextChar] == '\n') {552cbuf[off++] = '\n';553nextChar++;554}555return off - offset;556} else if (off == end) {557if (cbuf == rcb) {558cbuf = grow();559end = cbuf.length;560} else {561return off - offset;562}563}564}565if (eof)566return off - offset;567}568}569}570}571572private static final Charset CHARSET;573static {574String csname = encoding();575Charset cs = null;576if (csname == null) {577csname = GetPropertyAction.privilegedGetProperty("sun.stdout.encoding");578}579if (csname != null) {580try {581cs = Charset.forName(csname);582} catch (Exception ignored) { }583}584CHARSET = cs == null ? Charset.defaultCharset() : cs;585586// Set up JavaIOAccess in SharedSecrets587SharedSecrets.setJavaIOAccess(new JavaIOAccess() {588public Console console() {589if (istty()) {590if (cons == null)591cons = new Console();592return cons;593}594return null;595}596597public Charset charset() {598return CHARSET;599}600});601}602private static Console cons;603private static native boolean istty();604private Console() {605readLock = new Object();606writeLock = new Object();607out = StreamEncoder.forOutputStreamWriter(608new FileOutputStream(FileDescriptor.out),609writeLock,610CHARSET);611pw = new PrintWriter(out, true) { public void close() {} };612formatter = new Formatter(out);613reader = new LineReader(StreamDecoder.forInputStreamReader(614new FileInputStream(FileDescriptor.in),615readLock,616CHARSET));617rcb = new char[1024];618}619}620621622