1
2
3
4
5
6
7
8
9 package jline.internal;
10
11 import java.io.ByteArrayOutputStream;
12 import java.io.Closeable;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.text.MessageFormat;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
19
20 import static jline.internal.Preconditions.checkNotNull;
21
22
23
24
25
26
27
28
29
30
31 public final class TerminalLineSettings
32 {
33 public static final String JLINE_STTY = "jline.stty";
34
35 public static final String DEFAULT_STTY = "stty";
36
37 public static final String JLINE_SH = "jline.sh";
38
39 public static final String DEFAULT_SH = "sh";
40
41 private String sttyCommand;
42
43 private String shCommand;
44
45 private String config;
46 private String initialConfig;
47
48 private long configLastFetched;
49
50 public TerminalLineSettings() throws IOException, InterruptedException {
51 sttyCommand = Configuration.getString(JLINE_STTY, DEFAULT_STTY);
52 shCommand = Configuration.getString(JLINE_SH, DEFAULT_SH);
53 initialConfig = get("-g").trim();
54 config = get("-a");
55 configLastFetched = System.currentTimeMillis();
56
57 Log.debug("Config: ", config);
58
59
60 if (config.length() == 0) {
61 throw new IOException(MessageFormat.format("Unrecognized stty code: {0}", config));
62 }
63 }
64
65 public String getConfig() {
66 return config;
67 }
68
69 public void restore() throws IOException, InterruptedException {
70 set(initialConfig);
71 }
72
73 public String get(final String args) throws IOException, InterruptedException {
74 return stty(args);
75 }
76
77 public void set(final String args) throws IOException, InterruptedException {
78 stty(args);
79 }
80
81
82
83
84
85
86
87
88
89 public int getProperty(String name) {
90 checkNotNull(name);
91 long currentTime = System.currentTimeMillis();
92 try {
93
94 if (config == null || currentTime - configLastFetched > 1000) {
95 config = get("-a");
96 }
97 } catch (Exception e) {
98 if (e instanceof InterruptedException) {
99 Thread.currentThread().interrupt();
100 }
101 Log.debug("Failed to query stty ", name, "\n", e);
102 if (config == null) {
103 return -1;
104 }
105 }
106
107
108 if (currentTime - configLastFetched > 1000) {
109 configLastFetched = currentTime;
110 }
111
112 return this.getProperty(name, config);
113 }
114
115
116
117
118
119
120
121
122
123
124 protected static int getProperty(String name, String stty) {
125
126 Pattern pattern = Pattern.compile(name + "\\s+=\\s+(.*?)[;\\n\\r]");
127 Matcher matcher = pattern.matcher(stty);
128 if (!matcher.find()) {
129
130 pattern = Pattern.compile(name + "\\s+([^;]*)[;\\n\\r]");
131 matcher = pattern.matcher(stty);
132 if (!matcher.find()) {
133
134 pattern = Pattern.compile("(\\S*)\\s+" + name);
135 matcher = pattern.matcher(stty);
136 if (!matcher.find()) {
137 return -1;
138 }
139 }
140 }
141 return parseControlChar(matcher.group(1));
142 }
143
144 private static int parseControlChar(String str) {
145
146 if ("<undef>".equals(str)) {
147 return -1;
148 }
149
150 if (str.charAt(0) == '0') {
151 return Integer.parseInt(str, 8);
152 }
153
154 if (str.charAt(0) >= '1' && str.charAt(0) <= '9') {
155 return Integer.parseInt(str, 10);
156 }
157
158 if (str.charAt(0) == '^') {
159 if (str.charAt(1) == '?') {
160 return 127;
161 } else {
162 return str.charAt(1) - 64;
163 }
164 } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') {
165 if (str.charAt(2) == '^') {
166 if (str.charAt(3) == '?') {
167 return 127 + 128;
168 } else {
169 return str.charAt(3) - 64 + 128;
170 }
171 } else {
172 return str.charAt(2) + 128;
173 }
174 } else {
175 return str.charAt(0);
176 }
177 }
178
179 private String stty(final String args) throws IOException, InterruptedException {
180 checkNotNull(args);
181 return exec(String.format("%s %s < /dev/tty", sttyCommand, args));
182 }
183
184 private String exec(final String cmd) throws IOException, InterruptedException {
185 checkNotNull(cmd);
186 return exec(shCommand, "-c", cmd);
187 }
188
189 private String exec(final String... cmd) throws IOException, InterruptedException {
190 checkNotNull(cmd);
191
192 ByteArrayOutputStream bout = new ByteArrayOutputStream();
193
194 Log.trace("Running: ", cmd);
195
196 Process p = Runtime.getRuntime().exec(cmd);
197
198 InputStream in = null;
199 InputStream err = null;
200 OutputStream out = null;
201 try {
202 int c;
203 in = p.getInputStream();
204 while ((c = in.read()) != -1) {
205 bout.write(c);
206 }
207 err = p.getErrorStream();
208 while ((c = err.read()) != -1) {
209 bout.write(c);
210 }
211 out = p.getOutputStream();
212 p.waitFor();
213 }
214 finally {
215 close(in, out, err);
216 }
217
218 String result = bout.toString();
219
220 Log.trace("Result: ", result);
221
222 return result;
223 }
224
225 private static void close(final Closeable... closeables) {
226 for (Closeable c : closeables) {
227 try {
228 c.close();
229 }
230 catch (Exception e) {
231
232 }
233 }
234 }
235 }
236