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