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