001 /**
002 * jline - Java console input library
003 * Copyright (c) 2002,2003 Marc Prud'hommeaux mwp1@cornell.edu
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018 */
019 package jline;
020
021 import java.io.*;
022 import java.util.*;
023
024
025 /**
026 * <p>
027 * Terminal that is used for unix platforms. Terminal initialization
028 * is handled by issuing the <em>stty</em> command against the
029 * <em>/dev/tty</em> file to disable character echoing and enable
030 * character input. All known unix systems (including
031 * Linux and Macintosh OS X) support the <em>stty</em>), so this
032 * implementation should work for an reasonable POSIX system.
033 * </p>
034 *
035 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
036 */
037 public class UnixTerminal
038 extends Terminal
039 {
040 private Map terminfo;
041 private int width = -1;
042 private int height = -1;
043
044
045 /**
046 * Remove line-buffered input by invoking "stty -icanon min 1"
047 * against the current terminal.
048 */
049 public void initializeTerminal ()
050 throws IOException, InterruptedException
051 {
052 // save the initial tty configuration
053 final String ttyConfig = stty ("-g");
054
055 // sanity check
056 if (ttyConfig.length () == 0
057 || (ttyConfig.indexOf ("=") == -1
058 && ttyConfig.indexOf (":") == -1))
059 {
060 throw new IOException ("Unrecognized stty code: " + ttyConfig);
061 }
062
063
064 // set the console to be character-buffered instead of line-buffered
065 stty ("-icanon min 1");
066
067 // disable character echoing
068 stty ("-echo");
069
070 // at exit, restore the original tty configuration (for JDK 1.3+)
071 try
072 {
073 Runtime.getRuntime ().addShutdownHook (new Thread ()
074 {
075 public void start ()
076 {
077 try
078 {
079 stty (ttyConfig);
080 }
081 catch (Exception e)
082 {
083 consumeException (e);
084 }
085 }
086 });
087 }
088 catch (AbstractMethodError ame)
089 {
090 // JDK 1.3+ only method. Bummer.
091 consumeException (ame);
092 }
093 }
094
095
096 /**
097 * No-op for exceptions we want to silently consume.
098 */
099 private void consumeException (Throwable e)
100 {
101 }
102
103
104 public boolean isSupported ()
105 {
106 return true;
107 }
108
109
110 public boolean getEcho ()
111 {
112 return false;
113 }
114
115
116 /**
117 * Returns the value of "stty size" width param.
118 *
119 * <strong>Note</strong>: this method caches the value from the
120 * first time it is called in order to increase speed, which means
121 * that changing to size of the terminal will not be reflected
122 * in the console.
123 */
124 public int getTerminalWidth ()
125 {
126 if (width != -1)
127 return width;
128
129 int val = 80;
130 try
131 {
132 String size = stty ("size");
133 if (size.length () != 0 && size.indexOf (" ") != -1)
134 {
135 val = Integer.parseInt (
136 size.substring (size.indexOf (" ") + 1));
137 }
138 }
139 catch (Exception e)
140 {
141 consumeException (e);
142 }
143
144 return width = val;
145 }
146
147
148 /**
149 * Returns the value of "stty size" height param.
150 *
151 * <strong>Note</strong>: this method caches the value from the
152 * first time it is called in order to increase speed, which means
153 * that changing to size of the terminal will not be reflected
154 * in the console.
155 */
156 public int getTerminalHeight ()
157 {
158 if (height != -1)
159 return height;
160
161 int val = 24;
162
163 try
164 {
165 String size = stty ("size");
166 if (size.length () != 0 && size.indexOf (" ") != -1)
167 {
168 val = Integer.parseInt (
169 size.substring (0, size.indexOf (" ")));
170 }
171 }
172 catch (Exception e)
173 {
174 }
175
176 return height = val;
177 }
178
179
180 /**
181 * Execute the stty command with the specified arguments
182 * against the current active terminal.
183 */
184 private static String stty (final String args)
185 throws IOException, InterruptedException
186 {
187 return exec ("stty " + args + " < /dev/tty").trim ();
188 }
189
190
191 /**
192 * Execute the specified command and return the output
193 * (both stdout and stderr).
194 */
195 private static String exec (final String cmd)
196 throws IOException, InterruptedException
197 {
198 return exec (new String [] { "sh", "-c", cmd });
199 }
200
201
202 /**
203 * Execute the specified command and return the output
204 * (both stdout and stderr).
205 */
206 private static String exec (final String [] cmd)
207 throws IOException, InterruptedException
208 {
209 ByteArrayOutputStream bout = new ByteArrayOutputStream ();
210
211 Process p = Runtime.getRuntime ().exec (cmd);
212 int c;
213 InputStream in;
214
215 in = p.getInputStream ();
216 while ((c = in.read ()) != -1)
217 bout.write (c);
218
219 in = p.getErrorStream ();
220 while ((c = in.read ()) != -1)
221 bout.write (c);
222
223 p.waitFor ();
224
225 String result = new String (bout.toByteArray ());
226 return result;
227 }
228 }
229