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
27package java.io;
28
29import java.util.*;
30import java.nio.charset.Charset;
31import sun.nio.cs.StreamDecoder;
32import 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
93public 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