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 <code>stty</code> command against the
029     *  <code>/dev/tty</code> file to disable character echoing and enable
030     *  character input. All known unix systems (including
031     *  Linux and Macintosh OS X) support the <code>stty</code>), 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                                            }
084                                    }
085                            });
086                    }
087                    catch (AbstractMethodError ame)
088                    {
089                            // JDK 1.3+ only method. Bummer.
090                    }
091            }
092    
093    
094            public boolean isSupported ()
095            {
096                    return true;
097            }
098    
099    
100            public boolean getEcho ()
101            {
102                    return false;
103            }
104    
105    
106            /** 
107             *      Returns the value of "stty size" width param.
108             *
109             *      <strong>Note</strong>: this method caches the value from the
110             *      first time it is called in order to increase speed, which means
111             *      that changing to size of the terminal will not be reflected
112             *      in the console.
113             */
114            public int getTerminalWidth ()
115            {
116                    if (width != -1)
117                            return width;
118    
119                    int val = 80;
120                    try
121                    {
122                            String size = stty ("size");
123                            if (size.length () != 0 && size.indexOf (" ") != -1)
124                            {
125                                    val = Integer.parseInt (
126                                            size.substring (size.indexOf (" ") + 1));
127                            }
128                    }
129                    catch (Exception e)
130                    {
131                    }
132    
133                    return width = val;
134            }
135    
136    
137            /** 
138             *      Returns the value of "stty size" height param.
139             *
140             *      <strong>Note</strong>: this method caches the value from the
141             *      first time it is called in order to increase speed, which means
142             *      that changing to size of the terminal will not be reflected
143             *      in the console.
144             */
145            public int getTerminalHeight ()
146            {
147                    if (height != -1)
148                            return height;
149    
150                    int val = 24;
151    
152                    try
153                    {
154                            String size = stty ("size");
155                            if (size.length () != 0 && size.indexOf (" ") != -1)
156                            {
157                                    val = Integer.parseInt (
158                                            size.substring (0, size.indexOf (" ")));
159                            }
160                    }
161                    catch (Exception e)
162                    {
163                    }
164    
165                    return height = val;
166            }
167    
168    
169            /** 
170             *  Execute the stty command with the specified arguments
171             *  against the current active terminal.
172             */
173            private static String stty (String args)
174                    throws IOException, InterruptedException
175            {
176                    return exec ("stty " + args + " < /dev/tty").trim ();
177            }
178    
179    
180            /** 
181             *  Execute the specified command and return the output
182             *  (both stdout and stderr).
183             */
184            private static String exec (String cmd)
185                    throws IOException, InterruptedException
186            {
187                    return exec (new String [] { "sh", "-c", cmd });
188            }
189    
190    
191            /** 
192             *  Execute the specified command and return the output
193             *  (both stdout and stderr).
194             */
195            private static String exec (String [] cmd)
196                    throws IOException, InterruptedException
197            {
198                    ByteArrayOutputStream bout = new ByteArrayOutputStream ();
199    
200                    Process p = Runtime.getRuntime ().exec (cmd);
201                    int c;
202                    InputStream in;
203                            
204                    in = p.getInputStream ();
205                    while ((c = in.read ()) != -1)
206                            bout.write (c);
207    
208                    in = p.getErrorStream ();
209                    while ((c = in.read ()) != -1)
210                            bout.write (c);
211    
212                    p.waitFor ();
213    
214                    String result = new String (bout.toByteArray ());
215                    return result;
216            }
217    }
218