• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2005, 2021, 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 jdk.internal.access.SharedSecrets;
32 import sun.nio.cs.StreamDecoder;
33 import sun.nio.cs.StreamEncoder;
34 import sun.security.action.GetPropertyAction;
35 
36 /**
37  * Methods to access the character-based console device, if any, associated
38  * with the current Java virtual machine.
39  *
40  * <p> Whether a virtual machine has a console is dependent upon the
41  * underlying platform and also upon the manner in which the virtual
42  * machine is invoked.  If the virtual machine is started from an
43  * interactive command line without redirecting the standard input and
44  * output streams then its console will exist and will typically be
45  * connected to the keyboard and display from which the virtual machine
46  * was launched.  If the virtual machine is started automatically, for
47  * example by a background job scheduler, then it will typically not
48  * have a console.
49  * <p>
50  * If this virtual machine has a console then it is represented by a
51  * unique instance of this class which can be obtained by invoking the
52  * {@link java.lang.System#console()} method.  If no console device is
53  * available then an invocation of that method will return {@code null}.
54  * <p>
55  * Read and write operations are synchronized to guarantee the atomic
56  * completion of critical operations; therefore invoking methods
57  * {@link #readLine()}, {@link #readPassword()}, {@link #format format()},
58  * {@link #printf printf()} as well as the read, format and write operations
59  * on the objects returned by {@link #reader()} and {@link #writer()} may
60  * block in multithreaded scenarios.
61  * <p>
62  * Invoking {@code close()} on the objects returned by the {@link #reader()}
63  * and the {@link #writer()} will not close the underlying stream of those
64  * objects.
65  * <p>
66  * The console-read methods return {@code null} when the end of the
67  * console input stream is reached, for example by typing control-D on
68  * Unix or control-Z on Windows.  Subsequent read operations will succeed
69  * if additional characters are later entered on the console's input
70  * device.
71  * <p>
72  * Unless otherwise specified, passing a {@code null} argument to any method
73  * in this class will cause a {@link NullPointerException} to be thrown.
74  * <p>
75  * <b>Security note:</b>
76  * If an application needs to read a password or other secure data, it should
77  * use {@link #readPassword()} or {@link #readPassword(String, Object...)} and
78  * manually zero the returned character array after processing to minimize the
79  * lifetime of sensitive data in memory.
80  *
81  * <blockquote><pre>{@code
82  * Console cons;
83  * char[] passwd;
84  * if ((cons = System.console()) != null &&
85  *     (passwd = cons.readPassword("[%s]", "Password:")) != null) {
86  *     ...
87  *     java.util.Arrays.fill(passwd, ' ');
88  * }
89  * }</pre></blockquote>
90  *
91  * @author  Xueming Shen
92  * @since   1.6
93  */
94 
95 public final class Console implements Flushable
96 {
97    /**
98     * Retrieves the unique {@link java.io.PrintWriter PrintWriter} object
99     * associated with this console.
100     *
101     * @return  The printwriter associated with this console
102     */
writer()103     public PrintWriter writer() {
104         return pw;
105     }
106 
107    /**
108     * Retrieves the unique {@link java.io.Reader Reader} object associated
109     * with this console.
110     * <p>
111     * This method is intended to be used by sophisticated applications, for
112     * example, a {@link java.util.Scanner} object which utilizes the rich
113     * parsing/scanning functionality provided by the {@code Scanner}:
114     * <blockquote><pre>
115     * Console con = System.console();
116     * if (con != null) {
117     *     Scanner sc = new Scanner(con.reader());
118     *     ...
119     * }
120     * </pre></blockquote>
121     * <p>
122     * For simple applications requiring only line-oriented reading, use
123     * {@link #readLine}.
124     * <p>
125     * The bulk read operations {@link java.io.Reader#read(char[]) read(char[]) },
126     * {@link java.io.Reader#read(char[], int, int) read(char[], int, int) } and
127     * {@link java.io.Reader#read(java.nio.CharBuffer) read(java.nio.CharBuffer)}
128     * on the returned object will not read in characters beyond the line
129     * bound for each invocation, even if the destination buffer has space for
130     * more characters. The {@code Reader}'s {@code read} methods may block if a
131     * line bound has not been entered or reached on the console's input device.
132     * A line bound is considered to be any one of a line feed ({@code '\n'}),
133     * a carriage return ({@code '\r'}), a carriage return followed immediately
134     * by a linefeed, or an end of stream.
135     *
136     * @return  The reader associated with this console
137     */
reader()138     public Reader reader() {
139         return reader;
140     }
141 
142    /**
143     * Writes a formatted string to this console's output stream using
144     * the specified format string and arguments.
145     *
146     * @param  fmt
147     *         A format string as described in <a
148     *         href="../util/Formatter.html#syntax">Format string syntax</a>
149     *
150     * @param  args
151     *         Arguments referenced by the format specifiers in the format
152     *         string.  If there are more arguments than format specifiers, the
153     *         extra arguments are ignored.  The number of arguments is
154     *         variable and may be zero.  The maximum number of arguments is
155     *         limited by the maximum dimension of a Java array as defined by
156     *         <cite>The Java Virtual Machine Specification</cite>.
157     *         The behaviour on a
158     *         {@code null} argument depends on the <a
159     *         href="../util/Formatter.html#syntax">conversion</a>.
160     *
161     * @throws  IllegalFormatException
162     *          If a format string contains an illegal syntax, a format
163     *          specifier that is incompatible with the given arguments,
164     *          insufficient arguments given the format string, or other
165     *          illegal conditions.  For specification of all possible
166     *          formatting errors, see the <a
167     *          href="../util/Formatter.html#detail">Details</a> section
168     *          of the formatter class specification.
169     *
170     * @return  This console
171     */
format(String fmt, Object ...args)172     public Console format(String fmt, Object ...args) {
173         formatter.format(fmt, args).flush();
174         return this;
175     }
176 
177    /**
178     * A convenience method to write a formatted string to this console's
179     * output stream using the specified format string and arguments.
180     *
181     * <p> An invocation of this method of the form
182     * {@code con.printf(format, args)} behaves in exactly the same way
183     * as the invocation of
184     * <pre>con.format(format, args)</pre>.
185     *
186     * @param  format
187     *         A format string as described in <a
188     *         href="../util/Formatter.html#syntax">Format string syntax</a>.
189     *
190     * @param  args
191     *         Arguments referenced by the format specifiers in the format
192     *         string.  If there are more arguments than format specifiers, the
193     *         extra arguments are ignored.  The number of arguments is
194     *         variable and may be zero.  The maximum number of arguments is
195     *         limited by the maximum dimension of a Java array as defined by
196     *         <cite>The Java Virtual Machine Specification</cite>.
197     *         The behaviour on a
198     *         {@code null} argument depends on the <a
199     *         href="../util/Formatter.html#syntax">conversion</a>.
200     *
201     * @throws  IllegalFormatException
202     *          If a format string contains an illegal syntax, a format
203     *          specifier that is incompatible with the given arguments,
204     *          insufficient arguments given the format string, or other
205     *          illegal conditions.  For specification of all possible
206     *          formatting errors, see the <a
207     *          href="../util/Formatter.html#detail">Details</a> section of the
208     *          formatter class specification.
209     *
210     * @return  This console
211     */
printf(String format, Object ... args)212     public Console printf(String format, Object ... args) {
213         return format(format, args);
214     }
215 
216    /**
217     * Provides a formatted prompt, then reads a single line of text from the
218     * console.
219     *
220     * @param  fmt
221     *         A format string as described in <a
222     *         href="../util/Formatter.html#syntax">Format string syntax</a>.
223     *
224     * @param  args
225     *         Arguments referenced by the format specifiers in the format
226     *         string.  If there are more arguments than format specifiers, the
227     *         extra arguments are ignored.  The maximum number of arguments is
228     *         limited by the maximum dimension of a Java array as defined by
229     *         <cite>The Java Virtual Machine Specification</cite>.
230     *
231     * @throws  IllegalFormatException
232     *          If a format string contains an illegal syntax, a format
233     *          specifier that is incompatible with the given arguments,
234     *          insufficient arguments given the format string, or other
235     *          illegal conditions.  For specification of all possible
236     *          formatting errors, see the <a
237     *          href="../util/Formatter.html#detail">Details</a> section
238     *          of the formatter class specification.
239     *
240     * @throws IOError
241     *         If an I/O error occurs.
242     *
243     * @return  A string containing the line read from the console, not
244     *          including any line-termination characters, or {@code null}
245     *          if an end of stream has been reached.
246     */
readLine(String fmt, Object ... args)247     public String readLine(String fmt, Object ... args) {
248         String line = null;
249         synchronized (writeLock) {
250             synchronized(readLock) {
251                 if (!fmt.isEmpty())
252                     pw.format(fmt, args);
253                 try {
254                     char[] ca = readline(false);
255                     if (ca != null)
256                         line = new String(ca);
257                 } catch (IOException x) {
258                     throw new IOError(x);
259                 }
260             }
261         }
262         return line;
263     }
264 
265    /**
266     * Reads a single line of text from the console.
267     *
268     * @throws IOError
269     *         If an I/O error occurs.
270     *
271     * @return  A string containing the line read from the console, not
272     *          including any line-termination characters, or {@code null}
273     *          if an end of stream has been reached.
274     */
readLine()275     public String readLine() {
276         return readLine("");
277     }
278 
279    /**
280     * Provides a formatted prompt, then reads a password or passphrase from
281     * the console with echoing disabled.
282     *
283     * @param  fmt
284     *         A format string as described in <a
285     *         href="../util/Formatter.html#syntax">Format string syntax</a>
286     *         for the prompt text.
287     *
288     * @param  args
289     *         Arguments referenced by the format specifiers in the format
290     *         string.  If there are more arguments than format specifiers, the
291     *         extra arguments are ignored.  The maximum number of arguments is
292     *         limited by the maximum dimension of a Java array as defined by
293     *         <cite>The Java Virtual Machine Specification</cite>.
294     *
295     * @throws  IllegalFormatException
296     *          If a format string contains an illegal syntax, a format
297     *          specifier that is incompatible with the given arguments,
298     *          insufficient arguments given the format string, or other
299     *          illegal conditions.  For specification of all possible
300     *          formatting errors, see the <a
301     *          href="../util/Formatter.html#detail">Details</a>
302     *          section of the formatter class specification.
303     *
304     * @throws IOError
305     *         If an I/O error occurs.
306     *
307     * @return  A character array containing the password or passphrase read
308     *          from the console, not including any line-termination characters,
309     *          or {@code null} if an end of stream has been reached.
310     */
readPassword(String fmt, Object ... args)311     public char[] readPassword(String fmt, Object ... args) {
312         char[] passwd = null;
313         synchronized (writeLock) {
314             synchronized(readLock) {
315                 // Android-removed: The hook is a no-op (but causes trouble when it's turned on).
316                 // installShutdownHook();
317                 try {
318                     restoreEcho = echo(false);
319                 } catch (IOException x) {
320                     throw new IOError(x);
321                 }
322                 IOError ioe = null;
323                 try {
324                     if (!fmt.isEmpty())
325                         pw.format(fmt, args);
326                     passwd = readline(true);
327                 } catch (IOException x) {
328                     ioe = new IOError(x);
329                 } finally {
330                     try {
331                         if (restoreEcho)
332                             restoreEcho = echo(true);
333                     } catch (IOException x) {
334                         if (ioe == null)
335                             ioe = new IOError(x);
336                         else
337                             ioe.addSuppressed(x);
338                     }
339                     if (ioe != null)
340                         throw ioe;
341                 }
342                 pw.println();
343             }
344         }
345         return passwd;
346     }
347 
348 
349     // Android-removed: The hook is a no-op (but causes trouble when it's turned on).
350     /*
351     private void installShutdownHook() {
352         if (shutdownHookInstalled)
353             return;
354         try {
355             // Add a shutdown hook to restore console's echo state should
356             // it be necessary.
357             SharedSecrets.getJavaLangAccess()
358                 .registerShutdownHook(0 /* shutdown hook invocation order *//*,
359                     false /* only register if shutdown is not in progress *//*,
360                     new Runnable() {
361                         public void run() {
362                             try {
363                                 if (restoreEcho) {
364                                     echo(true);
365                                 }
366                             } catch (IOException x) { }
367                         }
368                     });
369         } catch (IllegalStateException e) {
370             // shutdown is already in progress and readPassword is first used
371             // by a shutdown hook
372         }
373         shutdownHookInstalled = true;
374     }
375     */
376 
377    /**
378     * Reads a password or passphrase from the console with echoing disabled
379     *
380     * @throws IOError
381     *         If an I/O error occurs.
382     *
383     * @return  A character array containing the password or passphrase read
384     *          from the console, not including any line-termination characters,
385     *          or {@code null} if an end of stream has been reached.
386     */
readPassword()387     public char[] readPassword() {
388         return readPassword("");
389     }
390 
391     /**
392      * Flushes the console and forces any buffered output to be written
393      * immediately .
394      */
flush()395     public void flush() {
396         pw.flush();
397     }
398 
399 
400     /**
401      * Returns the {@link java.nio.charset.Charset Charset} object used for
402      * the {@code Console}.
403      * <p>
404      * The returned charset corresponds to the input and output source
405      * (e.g., keyboard and/or display) specified by the host environment or user.
406      * It may not necessarily be the same as the default charset returned from
407      * {@link java.nio.charset.Charset#defaultCharset() Charset.defaultCharset()}.
408      *
409      * @return a {@link java.nio.charset.Charset Charset} object used for the
410      *          {@code Console}
411      * @since 17
412      */
charset()413     public Charset charset() {
414         assert CHARSET != null : "charset() should not return null";
415         return CHARSET;
416     }
417 
418     private Object readLock;
419     private Object writeLock;
420     private Reader reader;
421     private Writer out;
422     private PrintWriter pw;
423     private Formatter formatter;
424     private char[] rcb;
425     private boolean restoreEcho;
426     private boolean shutdownHookInstalled;
encoding()427     private static native String encoding();
428     /*
429      * Sets the console echo status to {@code on} and returns the previous
430      * console on/off status.
431      * @param on    the echo status to set to. {@code true} for echo on and
432      *              {@code false} for echo off
433      * @return true if the previous console echo status is on
434      */
echo(boolean on)435     private static native boolean echo(boolean on) throws IOException;
436 
readline(boolean zeroOut)437     private char[] readline(boolean zeroOut) throws IOException {
438         int len = reader.read(rcb, 0, rcb.length);
439         if (len < 0)
440             return null;  //EOL
441         if (rcb[len-1] == '\r')
442             len--;        //remove CR at end;
443         else if (rcb[len-1] == '\n') {
444             len--;        //remove LF at end;
445             if (len > 0 && rcb[len-1] == '\r')
446                 len--;    //remove the CR, if there is one
447         }
448         char[] b = new char[len];
449         if (len > 0) {
450             System.arraycopy(rcb, 0, b, 0, len);
451             if (zeroOut) {
452                 Arrays.fill(rcb, 0, len, ' ');
453             }
454         }
455         return b;
456     }
457 
grow()458     private char[] grow() {
459         assert Thread.holdsLock(readLock);
460         char[] t = new char[rcb.length * 2];
461         System.arraycopy(rcb, 0, t, 0, rcb.length);
462         rcb = t;
463         return rcb;
464     }
465 
466     class LineReader extends Reader {
467         private Reader in;
468         private char[] cb;
469         private int nChars, nextChar;
470         boolean leftoverLF;
LineReader(Reader in)471         LineReader(Reader in) {
472             this.in = in;
473             cb = new char[1024];
474             nextChar = nChars = 0;
475             leftoverLF = false;
476         }
close()477         public void close () {}
ready()478         public boolean ready() throws IOException {
479             //in.ready synchronizes on readLock already
480             return in.ready();
481         }
482 
read(char cbuf[], int offset, int length)483         public int read(char cbuf[], int offset, int length)
484             throws IOException
485         {
486             int off = offset;
487             int end = offset + length;
488             if (offset < 0 || offset > cbuf.length || length < 0 ||
489                 end < 0 || end > cbuf.length) {
490                 throw new IndexOutOfBoundsException();
491             }
492             synchronized(readLock) {
493                 boolean eof = false;
494                 char c = 0;
495                 for (;;) {
496                     if (nextChar >= nChars) {   //fill
497                         int n = 0;
498                         do {
499                             n = in.read(cb, 0, cb.length);
500                         } while (n == 0);
501                         if (n > 0) {
502                             nChars = n;
503                             nextChar = 0;
504                             if (n < cb.length &&
505                                 cb[n-1] != '\n' && cb[n-1] != '\r') {
506                                 /*
507                                  * we're in canonical mode so each "fill" should
508                                  * come back with an eol. if there no lf or nl at
509                                  * the end of returned bytes we reached an eof.
510                                  */
511                                 eof = true;
512                             }
513                         } else { /*EOF*/
514                             if (off - offset == 0)
515                                 return -1;
516                             return off - offset;
517                         }
518                     }
519                     if (leftoverLF && cbuf == rcb && cb[nextChar] == '\n') {
520                         /*
521                          * if invoked by our readline, skip the leftover, otherwise
522                          * return the LF.
523                          */
524                         nextChar++;
525                     }
526                     leftoverLF = false;
527                     while (nextChar < nChars) {
528                         c = cbuf[off++] = cb[nextChar];
529                         cb[nextChar++] = 0;
530                         if (c == '\n') {
531                             return off - offset;
532                         } else if (c == '\r') {
533                             if (off == end) {
534                                 /* no space left even the next is LF, so return
535                                  * whatever we have if the invoker is not our
536                                  * readLine()
537                                  */
538                                 if (cbuf == rcb) {
539                                     cbuf = grow();
540                                     end = cbuf.length;
541                                 } else {
542                                     leftoverLF = true;
543                                     return off - offset;
544                                 }
545                             }
546                             if (nextChar == nChars && in.ready()) {
547                                 /*
548                                  * we have a CR and we reached the end of
549                                  * the read in buffer, fill to make sure we
550                                  * don't miss a LF, if there is one, it's possible
551                                  * that it got cut off during last round reading
552                                  * simply because the read in buffer was full.
553                                  */
554                                 nChars = in.read(cb, 0, cb.length);
555                                 nextChar = 0;
556                             }
557                             if (nextChar < nChars && cb[nextChar] == '\n') {
558                                 cbuf[off++] = '\n';
559                                 nextChar++;
560                             }
561                             return off - offset;
562                         } else if (off == end) {
563                            if (cbuf == rcb) {
564                                 cbuf = grow();
565                                 end = cbuf.length;
566                            } else {
567                                return off - offset;
568                            }
569                         }
570                     }
571                     if (eof)
572                         return off - offset;
573                 }
574             }
575         }
576     }
577 
578     private static final Charset CHARSET;
579     static {
580         String csname = encoding();
581         Charset cs = null;
582         // Android-changed: libcore doesn't traditionally support sun.stdout.encoding property.
583         /*
584         if (csname == null) {
585             csname = GetPropertyAction.privilegedGetProperty("sun.stdout.encoding");
586         }
587         */
588         if (csname != null) {
589             try {
590                 cs = Charset.forName(csname);
591             } catch (Exception ignored) {
592             }
593         }
594         CHARSET = cs == null ? Charset.defaultCharset() : cs;
595 
596         // Set up JavaIOAccess in SharedSecrets
597         // Android-removed: SharedSecrets setup and also the shutdown hook.
598         /*
599         SharedSecrets.setJavaIOAccess(new JavaIOAccess() {
600             public Console console() {
601                 if (istty()) {
602                     if (cons == null)
603                         cons = new Console();
604                     return cons;
605                 }
606                 return null;
607             }
608 
609             public Charset charset() {
610                 return CHARSET;
611             }
612         });
613         */
614     }
615 
616     // Android-changed: Use @hide rather than sun.misc.SharedSecrets to expose console().
617     /** @hide */
console()618     public static Console console() {
619         if (istty()) {
620             if (cons == null)
621                 cons = new Console();
622             return cons;
623         }
624         return null;
625     }
626     private static Console cons;
istty()627     private static native boolean istty();
Console()628     private Console() {
629     // BEGIN Android-changed: Support custom in/out streams for testing.
630       this(new FileInputStream(FileDescriptor.in), new FileOutputStream(FileDescriptor.out));
631     }
632 
633     // Constructor for tests
Console(InputStream inStream, OutputStream outStream)634     private Console(InputStream inStream, OutputStream outStream) {
635     // END Android-changed: Support custom in/out streams for testing.
636         readLock = new Object();
637         writeLock = new Object();
638         out = StreamEncoder.forOutputStreamWriter(
639                   outStream,
640                   writeLock,
641                   CHARSET);
642         pw = new PrintWriter(out, true) { public void close() {} };
643         formatter = new Formatter(out);
644         reader = new LineReader(StreamDecoder.forInputStreamReader(
645                      inStream,
646                      readLock,
647                      CHARSET));
648         rcb = new char[1024];
649     }
650 }
651