1 /*
2 * Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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>
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. 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 pw.println();
324 }
325 }
326 return passwd;
327 }
328
329 /**
330 * Reads a password or passphrase from the console with echoing disabled
331 *
332 * @throws IOError
333 * If an I/O error occurs.
334 *
335 * @return A character array containing the password or passphrase read
336 * from the console, not including any line-termination characters,
337 * or <tt>null</tt> if an end of stream has been reached.
338 */
339 public char[] readPassword() {
340 return readPassword("");
341 }
342
343 /**
344 * Flushes the console and forces any buffered output to be written
345 * immediately .
346 */
347 public void flush() {
348 pw.flush();
349 }
350
351 private Object readLock;
352 private Object writeLock;
353 private Reader reader;
354 private Writer out;
355 private PrintWriter pw;
356 private Formatter formatter;
357 private Charset cs;
358 private char[] rcb;
359 private static native String encoding();
360 private static native boolean echo(boolean on) throws IOException;
361 private static boolean echoOff;
362
363 private char[] readline(boolean zeroOut) throws IOException {
364 int len = reader.read(rcb, 0, rcb.length);
365 if (len < 0)
366 return null; //EOL
367 if (rcb[len-1] == '\r')
368 len--; //remove CR at end;
369 else if (rcb[len-1] == '\n') {
370 len--; //remove LF at end;
371 if (len > 0 && rcb[len-1] == '\r')
372 len--; //remove the CR, if there is one
373 }
374 char[] b = new char[len];
375 if (len > 0) {
376 System.arraycopy(rcb, 0, b, 0, len);
377 if (zeroOut) {
378 Arrays.fill(rcb, 0, len, ' ');
379 }
380 }
381 return b;
382 }
383
384 private char[] grow() {
385 assert Thread.holdsLock(readLock);
386 char[] t = new char[rcb.length * 2];
387 System.arraycopy(rcb, 0, t, 0, rcb.length);
388 rcb = t;
389 return rcb;
390 }
391
392 class LineReader extends Reader {
393 private Reader in;
394 private char[] cb;
395 private int nChars, nextChar;
396 boolean leftoverLF;
397 LineReader(Reader in) {
398 this.in = in;
399 cb = new char[1024];
400 nextChar = nChars = 0;
401 leftoverLF = false;
402 }
403 public void close () {}
404 public boolean ready() throws IOException {
405 //in.ready synchronizes on readLock already
406 return in.ready();
407 }
408
409 public int read(char cbuf[], int offset, int length)
410 throws IOException
411 {
412 int off = offset;
413 int end = offset + length;
414 if (offset < 0 || offset > cbuf.length || length < 0 ||
415 end < 0 || end > cbuf.length) {
416 throw new IndexOutOfBoundsException();
417 }
418 synchronized(readLock) {
419 boolean eof = false;
420 char c = 0;
421 for (;;) {
422 if (nextChar >= nChars) { //fill
423 int n = 0;
424 do {
425 n = in.read(cb, 0, cb.length);
426 } while (n == 0);
427 if (n > 0) {
428 nChars = n;
429 nextChar = 0;
430 if (n < cb.length &&
431 cb[n-1] != '\n' && cb[n-1] != '\r') {
432 /*
433 * we're in canonical mode so each "fill" should
434 * come back with an eol. if there no lf or nl at
435 * the end of returned bytes we reached an eof.
436 */
437 eof = true;
438 }
439 } else { /*EOF*/
440 if (off - offset == 0)
441 return -1;
442 return off - offset;
443 }
444 }
445 if (leftoverLF && cbuf == rcb && cb[nextChar] == '\n') {
446 /*
447 * if invoked by our readline, skip the leftover, otherwise
448 * return the LF.
449 */
450 nextChar++;
451 }
452 leftoverLF = false;
453 while (nextChar < nChars) {
454 c = cbuf[off++] = cb[nextChar];
455 cb[nextChar++] = 0;
456 if (c == '\n') {
457 return off - offset;
458 } else if (c == '\r') {
459 if (off == end) {
460 /* no space left even the next is LF, so return
461 * whatever we have if the invoker is not our
462 * readLine()
463 */
464 if (cbuf == rcb) {
465 cbuf = grow();
466 end = cbuf.length;
467 } else {
468 leftoverLF = true;
469 return off - offset;
470 }
471 }
472 if (nextChar == nChars && in.ready()) {
473 /*
474 * we have a CR and we reached the end of
475 * the read in buffer, fill to make sure we
476 * don't miss a LF, if there is one, it's possible
477 * that it got cut off during last round reading
478 * simply because the read in buffer was full.
479 */
480 nChars = in.read(cb, 0, cb.length);
481 nextChar = 0;
482 }
483 if (nextChar < nChars && cb[nextChar] == '\n') {
484 cbuf[off++] = '\n';
485 nextChar++;
486 }
487 return off - offset;
488 } else if (off == end) {
489 if (cbuf == rcb) {
490 cbuf = grow();
491 end = cbuf.length;
492 } else {
493 return off - offset;
494 }
495 }
496 }
497 if (eof)
498 return off - offset;
499 }
500 }
501 }
502 }
503
504 // Set up JavaIOAccess in SharedSecrets
505 static {
506 sun.misc.SharedSecrets.setJavaIOAccess(new sun.misc.JavaIOAccess() {
507 public Console console() {
508 if (istty()) {
509 if (cons == null)
510 cons = new Console();
511 return cons;
512 }
513 return null;
514 }
515
516 // Add a shutdown hook to restore console's echo state should
517 // it be necessary.
518 public Runnable consoleRestoreHook() {
519 return new Runnable() {
520 public void run() {
521 try {
522 if (echoOff) {
523 echo(true);
524 }
525 } catch (IOException x) {}
526 }
527 };
528 }
529
530 public Charset charset() {
531 // This method is called in sun.security.util.Password,
532 // cons already exists when this method is called
533 return cons.cs;
534 }
535 });
536 }
537 private static Console cons;
538 private native static boolean istty();
539 private Console() {
540 readLock = new Object();
541 writeLock = new Object();
542 String csname = encoding();
543 if (csname != null) {
544 try {
545 cs = Charset.forName(csname);
546 } catch (Exception x) {}
547 }
548 if (cs == null)
549 cs = Charset.defaultCharset();
550 out = StreamEncoder.forOutputStreamWriter(
551 new FileOutputStream(FileDescriptor.out),
552 writeLock,
553 cs);
554 pw = new PrintWriter(out, true) { public void close() {} };
555 formatter = new Formatter(out);
556 reader = new LineReader(StreamDecoder.forInputStreamReader(
557 new FileInputStream(FileDescriptor.in),
558 readLock,
559 cs));
560 rcb = new char[1024];
561 }
562 }