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