001 /*
002 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.io;
027
028 import java.util.*;
029 import java.nio.charset.Charset;
030 import sun.nio.cs.StreamDecoder;
031 import sun.nio.cs.StreamEncoder;
032
033 /**
034 * Methods to access the character-based console device, if any, associated
035 * with the current Java virtual machine.
036 *
037 * <p> Whether a virtual machine has a console is dependent upon the
038 * underlying platform and also upon the manner in which the virtual
039 * machine is invoked. If the virtual machine is started from an
040 * interactive command line without redirecting the standard input and
041 * output streams then its console will exist and will typically be
042 * connected to the keyboard and display from which the virtual machine
043 * was launched. If the virtual machine is started automatically, for
044 * example by a background job scheduler, then it will typically not
045 * have a console.
046 * <p>
047 * If this virtual machine has a console then it is represented by a
048 * unique instance of this class which can be obtained by invoking the
049 * {@link java.lang.System#console()} method. If no console device is
050 * available then an invocation of that method will return <tt>null</tt>.
051 * <p>
052 * Read and write operations are synchronized to guarantee the atomic
053 * completion of critical operations; therefore invoking methods
054 * {@link #readLine()}, {@link #readPassword()}, {@link #format format()},
055 * {@link #printf printf()} as well as the read, format and write operations
056 * on the objects returned by {@link #reader()} and {@link #writer()} may
057 * block in multithreaded scenarios.
058 * <p>
059 * Invoking <tt>close()</tt> on the objects returned by the {@link #reader()}
060 * and the {@link #writer()} will not close the underlying stream of those
061 * objects.
062 * <p>
063 * The console-read methods return <tt>null</tt> when the end of the
064 * console input stream is reached, for example by typing control-D on
065 * Unix or control-Z on Windows. Subsequent read operations will succeed
066 * if additional characters are later entered on the console's input
067 * device.
068 * <p>
069 * Unless otherwise specified, passing a <tt>null</tt> argument to any method
070 * in this class will cause a {@link NullPointerException} to be thrown.
071 * <p>
072 * <b>Security note:</b>
073 * If an application needs to read a password or other secure data, it should
074 * use {@link #readPassword()} or {@link #readPassword(String, Object...)} and
075 * manually zero the returned character array after processing to minimize the
076 * lifetime of sensitive data in memory.
077 *
078 * <blockquote><pre>
079 * Console cons;
080 * char[] passwd;
081 * if ((cons = System.console()) != null &&
082 * (passwd = cons.readPassword("[%s]", "Password:")) != null) {
083 * ...
084 * java.util.Arrays.fill(passwd, ' ');
085 * }
086 * </pre></blockquote>
087 *
088 * @author Xueming Shen
089 * @version 1.17, 05/05/07
090 * @since 1.6
091 */
092
093 public final class Console implements Flushable {
094 /**
095 * Retrieves the unique {@link java.io.PrintWriter PrintWriter} object
096 * associated with this console.
097 *
098 * @return The printwriter associated with this console
099 */
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. A line bound is considered to be any one of a line feed
128 * (<tt>'\n'</tt>), a carriage return (<tt>'\r'</tt>), a carriage return
129 * followed immediately by a linefeed, or an end of stream.
130 *
131 * @return The reader associated with this console
132 */
133 public Reader reader() {
134 return reader;
135 }
136
137 /**
138 * Writes a formatted string to this console's output stream using
139 * the specified format string and arguments.
140 *
141 * @param fmt
142 * A format string as described in <a
143 * href="../util/Formatter.html#syntax">Format string syntax</a>
144 *
145 * @param args
146 * Arguments referenced by the format specifiers in the format
147 * string. If there are more arguments than format specifiers, the
148 * extra arguments are ignored. The number of arguments is
149 * variable and may be zero. The maximum number of arguments is
150 * limited by the maximum dimension of a Java array as defined by
151 * the <a href="http://java.sun.com/docs/books/vmspec/">Java
152 * Virtual Machine Specification</a>. The behaviour on a
153 * <tt>null</tt> argument depends on the <a
154 * href="../util/Formatter.html#syntax">conversion</a>.
155 *
156 * @throws IllegalFormatException
157 * If a format string contains an illegal syntax, a format
158 * specifier that is incompatible with the given arguments,
159 * insufficient arguments given the format string, or other
160 * illegal conditions. For specification of all possible
161 * formatting errors, see the <a
162 * href="../util/Formatter.html#detail">Details</a> section
163 * of the formatter class specification.
164 *
165 * @return This console
166 */
167 public Console format(String fmt, Object... args) {
168 formatter.format(fmt, args).flush();
169 return this ;
170 }
171
172 /**
173 * A convenience method to write a formatted string to this console's
174 * output stream using the specified format string and arguments.
175 *
176 * <p> An invocation of this method of the form <tt>con.printf(format,
177 * args)</tt> behaves in exactly the same way as the invocation of
178 * <pre>con.format(format, args)</pre>.
179 *
180 * @param format
181 * A format string as described in <a
182 * href="../util/Formatter.html#syntax">Format string syntax</a>.
183 *
184 * @param args
185 * Arguments referenced by the format specifiers in the format
186 * string. If there are more arguments than format specifiers, the
187 * extra arguments are ignored. The number of arguments is
188 * variable and may be zero. The maximum number of arguments is
189 * limited by the maximum dimension of a Java array as defined by
190 * the <a href="http://java.sun.com/docs/books/vmspec/">Java
191 * Virtual Machine Specification</a>. The behaviour on a
192 * <tt>null</tt> argument depends on the <a
193 * href="../util/Formatter.html#syntax">conversion</a>.
194 *
195 * @throws IllegalFormatException
196 * If a format string contains an illegal syntax, a format
197 * specifier that is incompatible with the given arguments,
198 * insufficient arguments given the format string, or other
199 * illegal conditions. For specification of all possible
200 * formatting errors, see the <a
201 * href="../util/Formatter.html#detail">Details</a> section of the
202 * formatter class specification.
203 *
204 * @return This console
205 */
206 public Console printf(String format, Object... args) {
207 return format(format, args);
208 }
209
210 /**
211 * Provides a formatted prompt, then reads a single line of text from the
212 * console.
213 *
214 * @param fmt
215 * A format string as described in <a
216 * href="../util/Formatter.html#syntax">Format string syntax</a>.
217 *
218 * @param args
219 * Arguments referenced by the format specifiers in the format
220 * string. If there are more arguments than format specifiers, the
221 * extra arguments are ignored. The maximum number of arguments is
222 * limited by the maximum dimension of a Java array as defined by
223 * the <a href="http://java.sun.com/docs/books/vmspec/">Java
224 * Virtual Machine Specification</a>.
225 *
226 * @throws IllegalFormatException
227 * If a format string contains an illegal syntax, a format
228 * specifier that is incompatible with the given arguments,
229 * insufficient arguments given the format string, or other
230 * illegal conditions. For specification of all possible
231 * formatting errors, see the <a
232 * href="../util/Formatter.html#detail">Details</a> section
233 * of the formatter class specification.
234 *
235 * @throws IOError
236 * If an I/O error occurs.
237 *
238 * @return A string containing the line read from the console, not
239 * including any line-termination characters, or <tt>null</tt>
240 * if an end of stream has been reached.
241 */
242 public String readLine(String fmt, Object... args) {
243 String line = null;
244 synchronized (writeLock) {
245 synchronized (readLock) {
246 if (fmt.length() != 0)
247 pw.format(fmt, args);
248 try {
249 char[] ca = readline(false);
250 if (ca != null)
251 line = new String(ca);
252 } catch (IOException x) {
253 throw new IOError(x);
254 }
255 }
256 }
257 return line;
258 }
259
260 /**
261 * Reads a single line of text from the console.
262 *
263 * @throws IOError
264 * If an I/O error occurs.
265 *
266 * @return A string containing the line read from the console, not
267 * including any line-termination characters, or <tt>null</tt>
268 * if an end of stream has been reached.
269 */
270 public String readLine() {
271 return readLine("");
272 }
273
274 /**
275 * Provides a formatted prompt, then reads a password or passphrase from
276 * the console with echoing disabled.
277 *
278 * @param fmt
279 * A format string as described in <a
280 * href="../util/Formatter.html#syntax">Format string syntax</a>
281 * for the prompt text.
282 *
283 * @param args
284 * Arguments referenced by the format specifiers in the format
285 * string. If there are more arguments than format specifiers, the
286 * extra arguments are ignored. The maximum number of arguments is
287 * limited by the maximum dimension of a Java array as defined by
288 * the <a href="http://java.sun.com/docs/books/vmspec/">Java
289 * Virtual Machine Specification</a>.
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 if (fmt.length() != 0)
312 pw.format(fmt, args);
313 try {
314 echoOff = echo(false);
315 passwd = readline(true);
316 } catch (IOException x) {
317 throw new IOError(x);
318 } finally {
319 try {
320 echoOff = echo(true);
321 } catch (IOException xx) {
322 }
323 }
324 pw.println();
325 }
326 }
327 return passwd;
328 }
329
330 /**
331 * Reads a password or passphrase from the console with echoing disabled
332 *
333 * @throws IOError
334 * If an I/O error occurs.
335 *
336 * @return A character array containing the password or passphrase read
337 * from the console, not including any line-termination characters,
338 * or <tt>null</tt> if an end of stream has been reached.
339 */
340 public char[] readPassword() {
341 return readPassword("");
342 }
343
344 /**
345 * Flushes the console and forces any buffered output to be written
346 * immediately .
347 */
348 public void flush() {
349 pw.flush();
350 }
351
352 private Object readLock;
353 private Object writeLock;
354 private Reader reader;
355 private Writer out;
356 private PrintWriter pw;
357 private Formatter formatter;
358 private Charset cs;
359 private char[] rcb;
360
361 private static native String encoding();
362
363 private static native boolean echo(boolean on) throws IOException;
364
365 private static boolean echoOff;
366
367 private char[] readline(boolean zeroOut) throws IOException {
368 int len = reader.read(rcb, 0, rcb.length);
369 if (len < 0)
370 return null; //EOL
371 if (rcb[len - 1] == '\r')
372 len--; //remove CR at end;
373 else if (rcb[len - 1] == '\n') {
374 len--; //remove LF at end;
375 if (len > 0 && rcb[len - 1] == '\r')
376 len--; //remove the CR, if there is one
377 }
378 char[] b = new char[len];
379 if (len > 0) {
380 System.arraycopy(rcb, 0, b, 0, len);
381 if (zeroOut) {
382 Arrays.fill(rcb, 0, len, ' ');
383 }
384 }
385 return b;
386 }
387
388 private char[] grow() {
389 assert Thread.holdsLock(readLock);
390 char[] t = new char[rcb.length * 2];
391 System.arraycopy(rcb, 0, t, 0, rcb.length);
392 rcb = t;
393 return rcb;
394 }
395
396 class LineReader extends Reader {
397 private Reader in;
398 private char[] cb;
399 private int nChars, nextChar;
400 boolean leftoverLF;
401
402 LineReader(Reader in) {
403 this .in = in;
404 cb = new char[1024];
405 nextChar = nChars = 0;
406 leftoverLF = false;
407 }
408
409 public void close() {
410 }
411
412 public boolean ready() throws IOException {
413 //in.ready synchronizes on readLock already
414 return in.ready();
415 }
416
417 public int read(char cbuf[], int offset, int length)
418 throws IOException {
419 int off = offset;
420 int end = offset + length;
421 if (offset < 0 || offset > cbuf.length || length < 0
422 || end < 0 || end > cbuf.length) {
423 throw new IndexOutOfBoundsException();
424 }
425 synchronized (readLock) {
426 boolean eof = false;
427 char c = 0;
428 for (;;) {
429 if (nextChar >= nChars) { //fill
430 int n = 0;
431 do {
432 n = in.read(cb, 0, cb.length);
433 } while (n == 0);
434 if (n > 0) {
435 nChars = n;
436 nextChar = 0;
437 if (n < cb.length && cb[n - 1] != '\n'
438 && cb[n - 1] != '\r') {
439 /*
440 * we're in canonical mode so each "fill" should
441 * come back with an eol. if there no lf or nl at
442 * the end of returned bytes we reached an eof.
443 */
444 eof = true;
445 }
446 } else { /*EOF*/
447 if (off - offset == 0)
448 return -1;
449 return off - offset;
450 }
451 }
452 if (leftoverLF && cbuf == rcb
453 && cb[nextChar] == '\n') {
454 /*
455 * if invoked by our readline, skip the leftover, otherwise
456 * return the LF.
457 */
458 nextChar++;
459 }
460 leftoverLF = false;
461 while (nextChar < nChars) {
462 c = cbuf[off++] = cb[nextChar];
463 cb[nextChar++] = 0;
464 if (c == '\n') {
465 return off - offset;
466 } else if (c == '\r') {
467 if (off == end) {
468 /* no space left even the next is LF, so return
469 * whatever we have if the invoker is not our
470 * readLine()
471 */
472 if (cbuf == rcb) {
473 cbuf = grow();
474 end = cbuf.length;
475 } else {
476 leftoverLF = true;
477 return off - offset;
478 }
479 }
480 if (nextChar == nChars && in.ready()) {
481 /*
482 * we have a CR and we reached the end of
483 * the read in buffer, fill to make sure we
484 * don't miss a LF, if there is one, it's possible
485 * that it got cut off during last round reading
486 * simply because the read in buffer was full.
487 */
488 nChars = in.read(cb, 0, cb.length);
489 nextChar = 0;
490 }
491 if (nextChar < nChars
492 && cb[nextChar] == '\n') {
493 cbuf[off++] = '\n';
494 nextChar++;
495 }
496 return off - offset;
497 } else if (off == end) {
498 if (cbuf == rcb) {
499 cbuf = grow();
500 end = cbuf.length;
501 } else {
502 return off - offset;
503 }
504 }
505 }
506 if (eof)
507 return off - offset;
508 }
509 }
510 }
511 }
512
513 // Set up JavaIOAccess in SharedSecrets
514 static {
515 sun.misc.SharedSecrets
516 .setJavaIOAccess(new sun.misc.JavaIOAccess() {
517 public Console console() {
518 if (istty()) {
519 if (cons == null)
520 cons = new Console();
521 return cons;
522 }
523 return null;
524 }
525
526 // Add a shutdown hook to restore console's echo state should
527 // it be necessary.
528 public Runnable consoleRestoreHook() {
529 return new Runnable() {
530 public void run() {
531 try {
532 if (echoOff) {
533 echo(true);
534 }
535 } catch (IOException x) {
536 }
537 }
538 };
539 }
540
541 public Charset charset() {
542 // This method is called in sun.security.util.Password,
543 // cons already exists when this method is called
544 return cons.cs;
545 }
546 });
547 }
548 private static Console cons;
549
550 private native static boolean istty();
551
552 private Console() {
553 readLock = new Object();
554 writeLock = new Object();
555 String csname = encoding();
556 if (csname != null) {
557 try {
558 cs = Charset.forName(csname);
559 } catch (Exception x) {
560 }
561 }
562 if (cs == null)
563 cs = Charset.defaultCharset();
564 out = StreamEncoder.forOutputStreamWriter(new FileOutputStream(
565 FileDescriptor.out), writeLock, cs);
566 pw = new PrintWriter(out, true) {
567 public void close() {
568 }
569 };
570 formatter = new Formatter(out);
571 reader = new LineReader(StreamDecoder.forInputStreamReader(
572 new FileInputStream(FileDescriptor.in), readLock, cs));
573 rcb = new char[1024];
574 }
575 }
|