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