1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2005, 2011, 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>
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. A line bound is considered to be any one of a line feed
129    * (<tt>'\n'</tt>), a carriage return (<tt>'\r'</tt>), a carriage return
130    * followed immediately by a linefeed, or an end of stream.
131    *
132    * @return  The reader associated with this console
133    */
134    public Reader reader() {
135        return reader;
136    }
137
138   /**
139    * Writes a formatted string to this console's output stream using
140    * the specified format string and arguments.
141    *
142    * @param  fmt
143    *         A format string as described in <a
144    *         href="../util/Formatter.html#syntax">Format string syntax</a>
145    *
146    * @param  args
147    *         Arguments referenced by the format specifiers in the format
148    *         string.  If there are more arguments than format specifiers, the
149    *         extra arguments are ignored.  The number of arguments is
150    *         variable and may be zero.  The maximum number of arguments is
151    *         limited by the maximum dimension of a Java array as defined by
152    *         <cite>The Java&trade; Virtual Machine Specification</cite>.
153    *         The behaviour on a
154    *         <tt>null</tt> argument depends on the <a
155    *         href="../util/Formatter.html#syntax">conversion</a>.
156    *
157    * @throws  IllegalFormatException
158    *          If a format string contains an illegal syntax, a format
159    *          specifier that is incompatible with the given arguments,
160    *          insufficient arguments given the format string, or other
161    *          illegal conditions.  For specification of all possible
162    *          formatting errors, see the <a
163    *          href="../util/Formatter.html#detail">Details</a> section
164    *          of the formatter class specification.
165    *
166    * @return  This console
167    */
168    public Console format(String fmt, Object ...args) {
169        formatter.format(fmt, args).flush();
170        return this;
171    }
172
173   /**
174    * A convenience method to write a formatted string to this console's
175    * output stream using the specified format string and arguments.
176    *
177    * <p> An invocation of this method of the form <tt>con.printf(format,
178    * args)</tt> behaves in exactly the same way as the invocation of
179    * <pre>con.format(format, args)</pre>.
180    *
181    * @param  format
182    *         A format string as described in <a
183    *         href="../util/Formatter.html#syntax">Format string syntax</a>.
184    *
185    * @param  args
186    *         Arguments referenced by the format specifiers in the format
187    *         string.  If there are more arguments than format specifiers, the
188    *         extra arguments are ignored.  The number of arguments is
189    *         variable and may be zero.  The maximum number of arguments is
190    *         limited by the maximum dimension of a Java array as defined by
191    *         <cite>The Java&trade; Virtual Machine Specification</cite>.
192    *         The behaviour on a
193    *         <tt>null</tt> argument depends on the <a
194    *         href="../util/Formatter.html#syntax">conversion</a>.
195    *
196    * @throws  IllegalFormatException
197    *          If a format string contains an illegal syntax, a format
198    *          specifier that is incompatible with the given arguments,
199    *          insufficient arguments given the format string, or other
200    *          illegal conditions.  For specification of all possible
201    *          formatting errors, see the <a
202    *          href="../util/Formatter.html#detail">Details</a> section of the
203    *          formatter class specification.
204    *
205    * @return  This console
206    */
207    public Console printf(String format, Object ... args) {
208        return format(format, args);
209    }
210
211   /**
212    * Provides a formatted prompt, then reads a single line of text from the
213    * console.
214    *
215    * @param  fmt
216    *         A format string as described in <a
217    *         href="../util/Formatter.html#syntax">Format string syntax</a>.
218    *
219    * @param  args
220    *         Arguments referenced by the format specifiers in the format
221    *         string.  If there are more arguments than format specifiers, the
222    *         extra arguments are ignored.  The maximum number of arguments is
223    *         limited by the maximum dimension of a Java array as defined by
224    *         <cite>The Java&trade; Virtual Machine Specification</cite>.
225    *
226    * @throws  IllegalFormatException
227    *          If a format string contains an illegal syntax, a format
228    *          specifier that is incompatible with the given arguments,
229    *          insufficient arguments given the format string, or other
230    *          illegal conditions.  For specification of all possible
231    *          formatting errors, see the <a
232    *          href="../util/Formatter.html#detail">Details</a> section
233    *          of the formatter class specification.
234    *
235    * @throws IOError
236    *         If an I/O error occurs.
237    *
238    * @return  A string containing the line read from the console, not
239    *          including any line-termination characters, or <tt>null</tt>
240    *          if an end of stream has been reached.
241    */
242    public String readLine(String fmt, Object ... args) {
243        String line = null;
244        synchronized (writeLock) {
245            synchronized(readLock) {
246                if (fmt.length() != 0)
247                    pw.format(fmt, args);
248                try {
249                    char[] ca = readline(false);
250                    if (ca != null)
251                        line = new String(ca);
252                } catch (IOException x) {
253                    throw new IOError(x);
254                }
255            }
256        }
257        return line;
258    }
259
260   /**
261    * Reads a single line of text from the console.
262    *
263    * @throws IOError
264    *         If an I/O error occurs.
265    *
266    * @return  A string containing the line read from the console, not
267    *          including any line-termination characters, or <tt>null</tt>
268    *          if an end of stream has been reached.
269    */
270    public String readLine() {
271        return readLine("");
272    }
273
274   /**
275    * Provides a formatted prompt, then reads a password or passphrase from
276    * the console with echoing disabled.
277    *
278    * @param  fmt
279    *         A format string as described in <a
280    *         href="../util/Formatter.html#syntax">Format string syntax</a>
281    *         for the prompt text.
282    *
283    * @param  args
284    *         Arguments referenced by the format specifiers in the format
285    *         string.  If there are more arguments than format specifiers, the
286    *         extra arguments are ignored.  The maximum number of arguments is
287    *         limited by the maximum dimension of a Java array as defined by
288    *         <cite>The Java&trade; Virtual Machine Specification</cite>.
289    *
290    * @throws  IllegalFormatException
291    *          If a format string contains an illegal syntax, a format
292    *          specifier that is incompatible with the given arguments,
293    *          insufficient arguments given the format string, or other
294    *          illegal conditions.  For specification of all possible
295    *          formatting errors, see the <a
296    *          href="../util/Formatter.html#detail">Details</a>
297    *          section of the formatter class specification.
298    *
299    * @throws IOError
300    *         If an I/O error occurs.
301    *
302    * @return  A character array containing the password or passphrase read
303    *          from the console, not including any line-termination characters,
304    *          or <tt>null</tt> if an end of stream has been reached.
305    */
306    public char[] readPassword(String fmt, Object ... args) {
307        char[] passwd = null;
308        synchronized (writeLock) {
309            synchronized(readLock) {
310                try {
311                    echoOff = echo(false);
312                } catch (IOException x) {
313                    throw new IOError(x);
314                }
315                IOError ioe = null;
316                try {
317                    if (fmt.length() != 0)
318                        pw.format(fmt, args);
319                    passwd = readline(true);
320                } catch (IOException x) {
321                    ioe = new IOError(x);
322                } finally {
323                    try {
324                        echoOff = echo(true);
325                    } catch (IOException x) {
326                        if (ioe == null)
327                            ioe = new IOError(x);
328                        else
329                            ioe.addSuppressed(x);
330                    }
331                    if (ioe != null)
332                        throw ioe;
333                }
334                pw.println();
335            }
336        }
337        return passwd;
338    }
339
340   /**
341    * Reads a password or passphrase from the console with echoing disabled
342    *
343    * @throws IOError
344    *         If an I/O error occurs.
345    *
346    * @return  A character array containing the password or passphrase read
347    *          from the console, not including any line-termination characters,
348    *          or <tt>null</tt> if an end of stream has been reached.
349    */
350    public char[] readPassword() {
351        return readPassword("");
352    }
353
354    /**
355     * Flushes the console and forces any buffered output to be written
356     * immediately .
357     */
358    public void flush() {
359        pw.flush();
360    }
361
362    private Object readLock;
363    private Object writeLock;
364    private Reader reader;
365    private Writer out;
366    private PrintWriter pw;
367    private Formatter formatter;
368    private Charset cs;
369    private char[] rcb;
370    private static native String encoding();
371    private static native boolean echo(boolean on) throws IOException;
372    private static boolean echoOff;
373
374    private char[] readline(boolean zeroOut) throws IOException {
375        int len = reader.read(rcb, 0, rcb.length);
376        if (len < 0)
377            return null;  //EOL
378        if (rcb[len-1] == '\r')
379            len--;        //remove CR at end;
380        else if (rcb[len-1] == '\n') {
381            len--;        //remove LF at end;
382            if (len > 0 && rcb[len-1] == '\r')
383                len--;    //remove the CR, if there is one
384        }
385        char[] b = new char[len];
386        if (len > 0) {
387            System.arraycopy(rcb, 0, b, 0, len);
388            if (zeroOut) {
389                Arrays.fill(rcb, 0, len, ' ');
390            }
391        }
392        return b;
393    }
394
395    private char[] grow() {
396        assert Thread.holdsLock(readLock);
397        char[] t = new char[rcb.length * 2];
398        System.arraycopy(rcb, 0, t, 0, rcb.length);
399        rcb = t;
400        return rcb;
401    }
402
403    class LineReader extends Reader {
404        private Reader in;
405        private char[] cb;
406        private int nChars, nextChar;
407        boolean leftoverLF;
408        LineReader(Reader in) {
409            this.in = in;
410            cb = new char[1024];
411            nextChar = nChars = 0;
412            leftoverLF = false;
413        }
414        public void close () {}
415        public boolean ready() throws IOException {
416            //in.ready synchronizes on readLock already
417            return in.ready();
418        }
419
420        public int read(char cbuf[], int offset, int length)
421            throws IOException
422        {
423            int off = offset;
424            int end = offset + length;
425            if (offset < 0 || offset > cbuf.length || length < 0 ||
426                end < 0 || end > cbuf.length) {
427                throw new IndexOutOfBoundsException();
428            }
429            synchronized(readLock) {
430                boolean eof = false;
431                char c = 0;
432                for (;;) {
433                    if (nextChar >= nChars) {   //fill
434                        int n = 0;
435                        do {
436                            n = in.read(cb, 0, cb.length);
437                        } while (n == 0);
438                        if (n > 0) {
439                            nChars = n;
440                            nextChar = 0;
441                            if (n < cb.length &&
442                                cb[n-1] != '\n' && cb[n-1] != '\r') {
443                                /*
444                                 * we're in canonical mode so each "fill" should
445                                 * come back with an eol. if there no lf or nl at
446                                 * the end of returned bytes we reached an eof.
447                                 */
448                                eof = true;
449                            }
450                        } else { /*EOF*/
451                            if (off - offset == 0)
452                                return -1;
453                            return off - offset;
454                        }
455                    }
456                    if (leftoverLF && cbuf == rcb && cb[nextChar] == '\n') {
457                        /*
458                         * if invoked by our readline, skip the leftover, otherwise
459                         * return the LF.
460                         */
461                        nextChar++;
462                    }
463                    leftoverLF = false;
464                    while (nextChar < nChars) {
465                        c = cbuf[off++] = cb[nextChar];
466                        cb[nextChar++] = 0;
467                        if (c == '\n') {
468                            return off - offset;
469                        } else if (c == '\r') {
470                            if (off == end) {
471                                /* no space left even the next is LF, so return
472                                 * whatever we have if the invoker is not our
473                                 * readLine()
474                                 */
475                                if (cbuf == rcb) {
476                                    cbuf = grow();
477                                    end = cbuf.length;
478                                } else {
479                                    leftoverLF = true;
480                                    return off - offset;
481                                }
482                            }
483                            if (nextChar == nChars && in.ready()) {
484                                /*
485                                 * we have a CR and we reached the end of
486                                 * the read in buffer, fill to make sure we
487                                 * don't miss a LF, if there is one, it's possible
488                                 * that it got cut off during last round reading
489                                 * simply because the read in buffer was full.
490                                 */
491                                nChars = in.read(cb, 0, cb.length);
492                                nextChar = 0;
493                            }
494                            if (nextChar < nChars && cb[nextChar] == '\n') {
495                                cbuf[off++] = '\n';
496                                nextChar++;
497                            }
498                            return off - offset;
499                        } else if (off == end) {
500                           if (cbuf == rcb) {
501                                cbuf = grow();
502                                end = cbuf.length;
503                           } else {
504                               return off - offset;
505                           }
506                        }
507                    }
508                    if (eof)
509                        return off - offset;
510                }
511            }
512        }
513    }
514
515    // Android-changed: Remove SharedSecrets setup and also the shutdown
516    // hook that's a no-op (but causes trouble when it's turned on).
517
518    private static Console cons;
519
520    /** @hide */
521    public static Console console() {
522        if (istty()) {
523            if (cons == null)
524                cons = new Console();
525            return cons;
526        }
527        return null;
528    }
529
530    private native static boolean istty();
531
532    private Console() {
533      this(new FileInputStream(FileDescriptor.in), new FileOutputStream(FileDescriptor.out));
534    }
535
536    // Constructor for tests
537    private Console(InputStream inStream, OutputStream outStream) {
538        readLock = new Object();
539        writeLock = new Object();
540        String csname = encoding();
541        if (csname != null) {
542            try {
543                cs = Charset.forName(csname);
544            } catch (Exception x) {}
545        }
546        if (cs == null)
547            cs = Charset.defaultCharset();
548        out = StreamEncoder.forOutputStreamWriter(
549                  outStream,
550                  writeLock,
551                  cs);
552        pw = new PrintWriter(out, true) { public void close() {} };
553        formatter = new Formatter(out);
554        reader = new LineReader(StreamDecoder.forInputStreamReader(
555                     inStream,
556                     readLock,
557                     cs));
558        rcb = new char[1024];
559    }
560
561    /**
562     * Android-changed: Added method for internal use only, and also in use
563     * by tests.
564     *
565     * @hide
566     */
567    public static synchronized Console getConsole() {
568        if (istty()) {
569            if (cons == null)
570                cons = new Console();
571            return cons;
572        }
573        return null;
574    }
575}
576