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 
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>
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     */
writer()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     */
reader()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     */
format(String fmt, Object ...args)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     */
printf(String format, Object ... args)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     */
readLine(String fmt, Object ... args)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     */
readLine()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     */
readPassword(String fmt, Object ... args)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     */
readPassword()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      */
flush()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;
encoding()370     private static native String encoding();
echo(boolean on)371     private static native boolean echo(boolean on) throws IOException;
372     private static boolean echoOff;
373 
readline(boolean zeroOut)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 
grow()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;
LineReader(Reader in)408         LineReader(Reader in) {
409             this.in = in;
410             cb = new char[1024];
411             nextChar = nChars = 0;
412             leftoverLF = false;
413         }
close()414         public void close () {}
ready()415         public boolean ready() throws IOException {
416             //in.ready synchronizes on readLock already
417             return in.ready();
418         }
419 
read(char cbuf[], int offset, int length)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 */
console()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 
istty()530     private native static boolean istty();
531 
Console()532     private Console() {
533       this(new FileInputStream(FileDescriptor.in), new FileOutputStream(FileDescriptor.out));
534     }
535 
536     // Constructor for tests
Console(InputStream inStream, OutputStream outStream)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      */
getConsole()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