Skip to content

Commit 8da980b

Browse files
committedOct 24, 2023
Support automatic parsing of an inputrc file in jline reader (#821)
1 parent 421bf59 commit 8da980b

File tree

4 files changed

+416
-360
lines changed

4 files changed

+416
-360
lines changed
 

‎builtins/src/main/java/org/jline/builtins/InputRC.java

+6-359
Original file line numberDiff line numberDiff line change
@@ -8,377 +8,24 @@
88
*/
99
package org.jline.builtins;
1010

11-
import java.io.*;
11+
import java.io.IOException;
12+
import java.io.InputStream;
13+
import java.io.Reader;
1214
import java.net.URL;
13-
import java.util.ArrayList;
14-
import java.util.List;
15-
import java.util.Locale;
1615

1716
import org.jline.reader.LineReader;
18-
import org.jline.reader.Macro;
19-
import org.jline.reader.Reference;
20-
import org.jline.utils.Log;
2117

2218
public final class InputRC {
2319

2420
public static void configure(LineReader reader, URL url) throws IOException {
25-
try (InputStream is = url.openStream()) {
26-
configure(reader, is);
27-
}
21+
org.jline.reader.impl.InputRC.configure(reader, url);
2822
}
2923

3024
public static void configure(LineReader reader, InputStream is) throws IOException {
31-
try (InputStreamReader r = new InputStreamReader(is)) {
32-
configure(reader, r);
33-
}
25+
org.jline.reader.impl.InputRC.configure(reader, is);
3426
}
3527

3628
public static void configure(LineReader reader, Reader r) throws IOException {
37-
BufferedReader br;
38-
if (r instanceof BufferedReader) {
39-
br = (BufferedReader) r;
40-
} else {
41-
br = new BufferedReader(r);
42-
}
43-
reader.getVariables().putIfAbsent(LineReader.EDITING_MODE, "emacs");
44-
reader.setKeyMap(LineReader.MAIN);
45-
if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) {
46-
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS));
47-
} else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) {
48-
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS));
49-
}
50-
new InputRC(reader).parse(br);
51-
if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) {
52-
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS));
53-
} else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) {
54-
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS));
55-
}
56-
}
57-
58-
private final LineReader reader;
59-
60-
private InputRC(LineReader reader) {
61-
this.reader = reader;
62-
}
63-
64-
private void parse(BufferedReader br) throws IOException, IllegalArgumentException {
65-
String line;
66-
boolean parsing = true;
67-
List<Boolean> ifsStack = new ArrayList<>();
68-
while ((line = br.readLine()) != null) {
69-
try {
70-
line = line.trim();
71-
if (line.length() == 0) {
72-
continue;
73-
}
74-
if (line.charAt(0) == '#') {
75-
continue;
76-
}
77-
int i = 0;
78-
if (line.charAt(i) == '$') {
79-
String cmd;
80-
String args;
81-
++i;
82-
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
83-
i++;
84-
}
85-
int s = i;
86-
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
87-
i++;
88-
}
89-
cmd = line.substring(s, i);
90-
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
91-
i++;
92-
}
93-
s = i;
94-
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
95-
i++;
96-
}
97-
args = line.substring(s, i);
98-
if ("if".equalsIgnoreCase(cmd)) {
99-
ifsStack.add(parsing);
100-
if (!parsing) {
101-
continue;
102-
}
103-
if (args.startsWith("term=")) {
104-
// TODO
105-
} else if (args.startsWith("mode=")) {
106-
String mode = (String) reader.getVariable(LineReader.EDITING_MODE);
107-
parsing = args.substring("mode=".length()).equalsIgnoreCase(mode);
108-
} else {
109-
parsing = args.equalsIgnoreCase(reader.getAppName());
110-
}
111-
} else if ("else".equalsIgnoreCase(cmd)) {
112-
if (ifsStack.isEmpty()) {
113-
throw new IllegalArgumentException("$else found without matching $if");
114-
}
115-
boolean invert = true;
116-
for (boolean b : ifsStack) {
117-
if (!b) {
118-
invert = false;
119-
break;
120-
}
121-
}
122-
if (invert) {
123-
parsing = !parsing;
124-
}
125-
} else if ("endif".equalsIgnoreCase(cmd)) {
126-
if (ifsStack.isEmpty()) {
127-
throw new IllegalArgumentException("endif found without matching $if");
128-
}
129-
parsing = ifsStack.remove(ifsStack.size() - 1);
130-
} else if ("include".equalsIgnoreCase(cmd)) {
131-
// TODO
132-
}
133-
continue;
134-
}
135-
if (!parsing) {
136-
continue;
137-
}
138-
if (line.charAt(i++) == '"') {
139-
boolean esc = false;
140-
for (; ; i++) {
141-
if (i >= line.length()) {
142-
throw new IllegalArgumentException("Missing closing quote on line '" + line + "'");
143-
}
144-
if (esc) {
145-
esc = false;
146-
} else if (line.charAt(i) == '\\') {
147-
esc = true;
148-
} else if (line.charAt(i) == '"') {
149-
break;
150-
}
151-
}
152-
}
153-
while (i < line.length() && line.charAt(i) != ':' && line.charAt(i) != ' ' && line.charAt(i) != '\t') {
154-
i++;
155-
}
156-
String keySeq = line.substring(0, i);
157-
boolean equivalency = i + 1 < line.length() && line.charAt(i) == ':' && line.charAt(i + 1) == '=';
158-
i++;
159-
if (equivalency) {
160-
i++;
161-
}
162-
if (keySeq.equalsIgnoreCase("set")) {
163-
String key;
164-
String val;
165-
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
166-
i++;
167-
}
168-
int s = i;
169-
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
170-
i++;
171-
}
172-
key = line.substring(s, i);
173-
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
174-
i++;
175-
}
176-
s = i;
177-
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
178-
i++;
179-
}
180-
val = line.substring(s, i);
181-
setVar(reader, key, val);
182-
} else {
183-
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
184-
i++;
185-
}
186-
int start = i;
187-
if (i < line.length() && (line.charAt(i) == '\'' || line.charAt(i) == '\"')) {
188-
char delim = line.charAt(i++);
189-
boolean esc = false;
190-
for (; ; i++) {
191-
if (i >= line.length()) {
192-
break;
193-
}
194-
if (esc) {
195-
esc = false;
196-
} else if (line.charAt(i) == '\\') {
197-
esc = true;
198-
} else if (line.charAt(i) == delim) {
199-
break;
200-
}
201-
}
202-
}
203-
for (; i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\t'; i++)
204-
;
205-
String val = line.substring(Math.min(start, line.length()), Math.min(i, line.length()));
206-
if (keySeq.charAt(0) == '"') {
207-
keySeq = translateQuoted(keySeq);
208-
} else {
209-
// Bind key name
210-
String keyName =
211-
keySeq.lastIndexOf('-') > 0 ? keySeq.substring(keySeq.lastIndexOf('-') + 1) : keySeq;
212-
char key = getKeyFromName(keyName);
213-
keyName = keySeq.toLowerCase();
214-
keySeq = "";
215-
if (keyName.contains("meta-") || keyName.contains("m-")) {
216-
keySeq += "\u001b";
217-
}
218-
if (keyName.contains("control-") || keyName.contains("c-") || keyName.contains("ctrl-")) {
219-
key = (char) (Character.toUpperCase(key) & 0x1f);
220-
}
221-
keySeq += key;
222-
}
223-
if (val.length() > 0 && (val.charAt(0) == '\'' || val.charAt(0) == '\"')) {
224-
reader.getKeys().bind(new Macro(translateQuoted(val)), keySeq);
225-
} else {
226-
reader.getKeys().bind(new Reference(val), keySeq);
227-
}
228-
}
229-
} catch (IllegalArgumentException e) {
230-
Log.warn("Unable to parse user configuration: ", e);
231-
}
232-
}
233-
}
234-
235-
private static String translateQuoted(String keySeq) {
236-
int i;
237-
String str = keySeq.substring(1, keySeq.length() - 1);
238-
StringBuilder sb = new StringBuilder();
239-
for (i = 0; i < str.length(); i++) {
240-
char c = str.charAt(i);
241-
if (c == '\\') {
242-
boolean ctrl = str.regionMatches(i, "\\C-", 0, 3) || str.regionMatches(i, "\\M-\\C-", 0, 6);
243-
boolean meta = str.regionMatches(i, "\\M-", 0, 3) || str.regionMatches(i, "\\C-\\M-", 0, 6);
244-
i += (meta ? 3 : 0) + (ctrl ? 3 : 0) + (!meta && !ctrl ? 1 : 0);
245-
if (i >= str.length()) {
246-
break;
247-
}
248-
c = str.charAt(i);
249-
if (meta) {
250-
sb.append("\u001b");
251-
}
252-
if (ctrl) {
253-
c = c == '?' ? 0x7f : (char) (Character.toUpperCase(c) & 0x1f);
254-
}
255-
if (!meta && !ctrl) {
256-
switch (c) {
257-
case 'a':
258-
c = 0x07;
259-
break;
260-
case 'b':
261-
c = '\b';
262-
break;
263-
case 'd':
264-
c = 0x7f;
265-
break;
266-
case 'e':
267-
c = 0x1b;
268-
break;
269-
case 'f':
270-
c = '\f';
271-
break;
272-
case 'n':
273-
c = '\n';
274-
break;
275-
case 'r':
276-
c = '\r';
277-
break;
278-
case 't':
279-
c = '\t';
280-
break;
281-
case 'v':
282-
c = 0x0b;
283-
break;
284-
case '\\':
285-
c = '\\';
286-
break;
287-
case '0':
288-
case '1':
289-
case '2':
290-
case '3':
291-
case '4':
292-
case '5':
293-
case '6':
294-
case '7':
295-
c = 0;
296-
for (int j = 0; j < 3; j++, i++) {
297-
if (i >= str.length()) {
298-
break;
299-
}
300-
int k = Character.digit(str.charAt(i), 8);
301-
if (k < 0) {
302-
break;
303-
}
304-
c = (char) (c * 8 + k);
305-
}
306-
c &= 0xFF;
307-
break;
308-
case 'x':
309-
i++;
310-
c = 0;
311-
for (int j = 0; j < 2; j++, i++) {
312-
if (i >= str.length()) {
313-
break;
314-
}
315-
int k = Character.digit(str.charAt(i), 16);
316-
if (k < 0) {
317-
break;
318-
}
319-
c = (char) (c * 16 + k);
320-
}
321-
c &= 0xFF;
322-
break;
323-
case 'u':
324-
i++;
325-
c = 0;
326-
for (int j = 0; j < 4; j++, i++) {
327-
if (i >= str.length()) {
328-
break;
329-
}
330-
int k = Character.digit(str.charAt(i), 16);
331-
if (k < 0) {
332-
break;
333-
}
334-
c = (char) (c * 16 + k);
335-
}
336-
break;
337-
}
338-
}
339-
sb.append(c);
340-
} else {
341-
sb.append(c);
342-
}
343-
}
344-
return sb.toString();
345-
}
346-
347-
private static char getKeyFromName(String name) {
348-
if ("DEL".equalsIgnoreCase(name) || "Rubout".equalsIgnoreCase(name)) {
349-
return 0x7f;
350-
} else if ("ESC".equalsIgnoreCase(name) || "Escape".equalsIgnoreCase(name)) {
351-
return '\033';
352-
} else if ("LFD".equalsIgnoreCase(name) || "NewLine".equalsIgnoreCase(name)) {
353-
return '\n';
354-
} else if ("RET".equalsIgnoreCase(name) || "Return".equalsIgnoreCase(name)) {
355-
return '\r';
356-
} else if ("SPC".equalsIgnoreCase(name) || "Space".equalsIgnoreCase(name)) {
357-
return ' ';
358-
} else if ("Tab".equalsIgnoreCase(name)) {
359-
return '\t';
360-
} else {
361-
return name.charAt(0);
362-
}
363-
}
364-
365-
private static void setVar(LineReader reader, String key, String val) {
366-
if (LineReader.KEYMAP.equalsIgnoreCase(key)) {
367-
reader.setKeyMap(val);
368-
return;
369-
}
370-
371-
for (LineReader.Option option : LineReader.Option.values()) {
372-
if (option.name().toLowerCase(Locale.ENGLISH).replace('_', '-').equals(val)) {
373-
if ("on".equalsIgnoreCase(val)) {
374-
reader.setOpt(option);
375-
} else if ("off".equalsIgnoreCase(val)) {
376-
reader.unsetOpt(option);
377-
}
378-
return;
379-
}
380-
}
381-
382-
reader.setVariable(key, val);
29+
org.jline.reader.impl.InputRC.configure(reader, r);
38330
}
38431
}

‎reader/src/main/java/org/jline/reader/LineReader.java

+5
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,11 @@ public interface LineReader {
406406
*/
407407
String TAB_WIDTH = "tab-width";
408408

409+
/**
410+
* Name of inputrc to read at line reader creation time.
411+
*/
412+
String INPUT_RC_FILE_NAME = "input-rc-file-name";
413+
409414
Map<String, KeyMap<Binding>> defaultKeyMaps();
410415

411416
enum Option {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
/*
2+
* Copyright (c) 2002-2023, the original author(s).
3+
*
4+
* This software is distributable under the BSD license. See the terms of the
5+
* BSD license in the documentation provided with this software.
6+
*
7+
* https://opensource.org/licenses/BSD-3-Clause
8+
*/
9+
package org.jline.reader.impl;
10+
11+
import java.io.BufferedReader;
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.io.InputStreamReader;
15+
import java.io.Reader;
16+
import java.net.URL;
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import java.util.Locale;
20+
21+
import org.jline.reader.LineReader;
22+
import org.jline.reader.Macro;
23+
import org.jline.reader.Reference;
24+
import org.jline.utils.Log;
25+
26+
public final class InputRC {
27+
28+
public static void configure(LineReader reader, URL url) throws IOException {
29+
try (InputStream is = url.openStream()) {
30+
configure(reader, is);
31+
}
32+
}
33+
34+
public static void configure(LineReader reader, InputStream is) throws IOException {
35+
try (InputStreamReader r = new InputStreamReader(is)) {
36+
configure(reader, r);
37+
}
38+
}
39+
40+
public static void configure(LineReader reader, Reader r) throws IOException {
41+
BufferedReader br;
42+
if (r instanceof BufferedReader) {
43+
br = (BufferedReader) r;
44+
} else {
45+
br = new BufferedReader(r);
46+
}
47+
reader.getVariables().putIfAbsent(LineReader.EDITING_MODE, "emacs");
48+
reader.setKeyMap(LineReader.MAIN);
49+
if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) {
50+
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS));
51+
} else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) {
52+
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS));
53+
}
54+
new InputRC(reader).parse(br);
55+
if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) {
56+
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS));
57+
} else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) {
58+
reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS));
59+
}
60+
}
61+
62+
private final LineReader reader;
63+
64+
private InputRC(LineReader reader) {
65+
this.reader = reader;
66+
}
67+
68+
private void parse(BufferedReader br) throws IOException, IllegalArgumentException {
69+
String line;
70+
boolean parsing = true;
71+
List<Boolean> ifsStack = new ArrayList<>();
72+
while ((line = br.readLine()) != null) {
73+
try {
74+
line = line.trim();
75+
if (line.length() == 0) {
76+
continue;
77+
}
78+
if (line.charAt(0) == '#') {
79+
continue;
80+
}
81+
int i = 0;
82+
if (line.charAt(i) == '$') {
83+
String cmd;
84+
String args;
85+
++i;
86+
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
87+
i++;
88+
}
89+
int s = i;
90+
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
91+
i++;
92+
}
93+
cmd = line.substring(s, i);
94+
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
95+
i++;
96+
}
97+
s = i;
98+
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
99+
i++;
100+
}
101+
args = line.substring(s, i);
102+
if ("if".equalsIgnoreCase(cmd)) {
103+
ifsStack.add(parsing);
104+
if (!parsing) {
105+
continue;
106+
}
107+
if (args.startsWith("term=")) {
108+
// TODO
109+
} else if (args.startsWith("mode=")) {
110+
String mode = (String) reader.getVariable(LineReader.EDITING_MODE);
111+
parsing = args.substring("mode=".length()).equalsIgnoreCase(mode);
112+
} else {
113+
parsing = args.equalsIgnoreCase(reader.getAppName());
114+
}
115+
} else if ("else".equalsIgnoreCase(cmd)) {
116+
if (ifsStack.isEmpty()) {
117+
throw new IllegalArgumentException("$else found without matching $if");
118+
}
119+
boolean invert = true;
120+
for (boolean b : ifsStack) {
121+
if (!b) {
122+
invert = false;
123+
break;
124+
}
125+
}
126+
if (invert) {
127+
parsing = !parsing;
128+
}
129+
} else if ("endif".equalsIgnoreCase(cmd)) {
130+
if (ifsStack.isEmpty()) {
131+
throw new IllegalArgumentException("endif found without matching $if");
132+
}
133+
parsing = ifsStack.remove(ifsStack.size() - 1);
134+
} else if ("include".equalsIgnoreCase(cmd)) {
135+
// TODO
136+
}
137+
continue;
138+
}
139+
if (!parsing) {
140+
continue;
141+
}
142+
if (line.charAt(i++) == '"') {
143+
boolean esc = false;
144+
for (; ; i++) {
145+
if (i >= line.length()) {
146+
throw new IllegalArgumentException("Missing closing quote on line '" + line + "'");
147+
}
148+
if (esc) {
149+
esc = false;
150+
} else if (line.charAt(i) == '\\') {
151+
esc = true;
152+
} else if (line.charAt(i) == '"') {
153+
break;
154+
}
155+
}
156+
}
157+
while (i < line.length() && line.charAt(i) != ':' && line.charAt(i) != ' ' && line.charAt(i) != '\t') {
158+
i++;
159+
}
160+
String keySeq = line.substring(0, i);
161+
boolean equivalency = i + 1 < line.length() && line.charAt(i) == ':' && line.charAt(i + 1) == '=';
162+
i++;
163+
if (equivalency) {
164+
i++;
165+
}
166+
if (keySeq.equalsIgnoreCase("set")) {
167+
String key;
168+
String val;
169+
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
170+
i++;
171+
}
172+
int s = i;
173+
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
174+
i++;
175+
}
176+
key = line.substring(s, i);
177+
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
178+
i++;
179+
}
180+
s = i;
181+
while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) {
182+
i++;
183+
}
184+
val = line.substring(s, i);
185+
setVar(reader, key, val);
186+
} else {
187+
while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) {
188+
i++;
189+
}
190+
int start = i;
191+
if (i < line.length() && (line.charAt(i) == '\'' || line.charAt(i) == '\"')) {
192+
char delim = line.charAt(i++);
193+
boolean esc = false;
194+
for (; ; i++) {
195+
if (i >= line.length()) {
196+
break;
197+
}
198+
if (esc) {
199+
esc = false;
200+
} else if (line.charAt(i) == '\\') {
201+
esc = true;
202+
} else if (line.charAt(i) == delim) {
203+
break;
204+
}
205+
}
206+
}
207+
for (; i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\t'; i++)
208+
;
209+
String val = line.substring(Math.min(start, line.length()), Math.min(i, line.length()));
210+
if (keySeq.charAt(0) == '"') {
211+
keySeq = translateQuoted(keySeq);
212+
} else {
213+
// Bind key name
214+
String keyName =
215+
keySeq.lastIndexOf('-') > 0 ? keySeq.substring(keySeq.lastIndexOf('-') + 1) : keySeq;
216+
char key = getKeyFromName(keyName);
217+
keyName = keySeq.toLowerCase();
218+
keySeq = "";
219+
if (keyName.contains("meta-") || keyName.contains("m-")) {
220+
keySeq += "\u001b";
221+
}
222+
if (keyName.contains("control-") || keyName.contains("c-") || keyName.contains("ctrl-")) {
223+
key = (char) (Character.toUpperCase(key) & 0x1f);
224+
}
225+
keySeq += key;
226+
}
227+
if (val.length() > 0 && (val.charAt(0) == '\'' || val.charAt(0) == '\"')) {
228+
reader.getKeys().bind(new Macro(translateQuoted(val)), keySeq);
229+
} else {
230+
reader.getKeys().bind(new Reference(val), keySeq);
231+
}
232+
}
233+
} catch (IllegalArgumentException e) {
234+
Log.warn("Unable to parse user configuration: ", e);
235+
}
236+
}
237+
}
238+
239+
private static String translateQuoted(String keySeq) {
240+
int i;
241+
String str = keySeq.substring(1, keySeq.length() - 1);
242+
StringBuilder sb = new StringBuilder();
243+
for (i = 0; i < str.length(); i++) {
244+
char c = str.charAt(i);
245+
if (c == '\\') {
246+
boolean ctrl = str.regionMatches(i, "\\C-", 0, 3) || str.regionMatches(i, "\\M-\\C-", 0, 6);
247+
boolean meta = str.regionMatches(i, "\\M-", 0, 3) || str.regionMatches(i, "\\C-\\M-", 0, 6);
248+
i += (meta ? 3 : 0) + (ctrl ? 3 : 0) + (!meta && !ctrl ? 1 : 0);
249+
if (i >= str.length()) {
250+
break;
251+
}
252+
c = str.charAt(i);
253+
if (meta) {
254+
sb.append("\u001b");
255+
}
256+
if (ctrl) {
257+
c = c == '?' ? 0x7f : (char) (Character.toUpperCase(c) & 0x1f);
258+
}
259+
if (!meta && !ctrl) {
260+
switch (c) {
261+
case 'a':
262+
c = 0x07;
263+
break;
264+
case 'b':
265+
c = '\b';
266+
break;
267+
case 'd':
268+
c = 0x7f;
269+
break;
270+
case 'e':
271+
c = 0x1b;
272+
break;
273+
case 'f':
274+
c = '\f';
275+
break;
276+
case 'n':
277+
c = '\n';
278+
break;
279+
case 'r':
280+
c = '\r';
281+
break;
282+
case 't':
283+
c = '\t';
284+
break;
285+
case 'v':
286+
c = 0x0b;
287+
break;
288+
case '\\':
289+
c = '\\';
290+
break;
291+
case '0':
292+
case '1':
293+
case '2':
294+
case '3':
295+
case '4':
296+
case '5':
297+
case '6':
298+
case '7':
299+
c = 0;
300+
for (int j = 0; j < 3; j++, i++) {
301+
if (i >= str.length()) {
302+
break;
303+
}
304+
int k = Character.digit(str.charAt(i), 8);
305+
if (k < 0) {
306+
break;
307+
}
308+
c = (char) (c * 8 + k);
309+
}
310+
c &= 0xFF;
311+
break;
312+
case 'x':
313+
i++;
314+
c = 0;
315+
for (int j = 0; j < 2; j++, i++) {
316+
if (i >= str.length()) {
317+
break;
318+
}
319+
int k = Character.digit(str.charAt(i), 16);
320+
if (k < 0) {
321+
break;
322+
}
323+
c = (char) (c * 16 + k);
324+
}
325+
c &= 0xFF;
326+
break;
327+
case 'u':
328+
i++;
329+
c = 0;
330+
for (int j = 0; j < 4; j++, i++) {
331+
if (i >= str.length()) {
332+
break;
333+
}
334+
int k = Character.digit(str.charAt(i), 16);
335+
if (k < 0) {
336+
break;
337+
}
338+
c = (char) (c * 16 + k);
339+
}
340+
break;
341+
}
342+
}
343+
sb.append(c);
344+
} else {
345+
sb.append(c);
346+
}
347+
}
348+
return sb.toString();
349+
}
350+
351+
private static char getKeyFromName(String name) {
352+
if ("DEL".equalsIgnoreCase(name) || "Rubout".equalsIgnoreCase(name)) {
353+
return 0x7f;
354+
} else if ("ESC".equalsIgnoreCase(name) || "Escape".equalsIgnoreCase(name)) {
355+
return '\033';
356+
} else if ("LFD".equalsIgnoreCase(name) || "NewLine".equalsIgnoreCase(name)) {
357+
return '\n';
358+
} else if ("RET".equalsIgnoreCase(name) || "Return".equalsIgnoreCase(name)) {
359+
return '\r';
360+
} else if ("SPC".equalsIgnoreCase(name) || "Space".equalsIgnoreCase(name)) {
361+
return ' ';
362+
} else if ("Tab".equalsIgnoreCase(name)) {
363+
return '\t';
364+
} else {
365+
return name.charAt(0);
366+
}
367+
}
368+
369+
static void setVar(LineReader reader, String key, String val) {
370+
if (LineReader.KEYMAP.equalsIgnoreCase(key)) {
371+
reader.setKeyMap(val);
372+
return;
373+
}
374+
375+
for (LineReader.Option option : LineReader.Option.values()) {
376+
if (option.name().toLowerCase(Locale.ENGLISH).replace('_', '-').equals(val)) {
377+
if ("on".equalsIgnoreCase(val)) {
378+
reader.setOpt(option);
379+
} else if ("off".equalsIgnoreCase(val)) {
380+
reader.unsetOpt(option);
381+
}
382+
return;
383+
}
384+
}
385+
386+
reader.setVariable(key, val);
387+
}
388+
}

‎reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2002-2022, the original author(s).
2+
* Copyright (c) 2002-2023, the original author(s).
33
*
44
* This software is distributable under the BSD license. See the terms of the
55
* BSD license in the documentation provided with this software.
@@ -18,6 +18,9 @@
1818
import java.io.InputStream;
1919
import java.io.InterruptedIOException;
2020
import java.lang.reflect.Constructor;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.nio.file.Paths;
2124
import java.time.Instant;
2225
import java.util.*;
2326
import java.util.concurrent.atomic.AtomicBoolean;
@@ -321,6 +324,19 @@ public LineReaderImpl(Terminal terminal, String appName, Map<String, Object> var
321324
builtinWidgets = builtinWidgets();
322325
widgets = new HashMap<>(builtinWidgets);
323326
bindingReader = new BindingReader(terminal.reader());
327+
328+
String inputRc = getString(INPUT_RC_FILE_NAME, null);
329+
if (inputRc != null) {
330+
Path inputRcPath = Paths.get(inputRc);
331+
if (Files.exists(inputRcPath)) {
332+
try (InputStream is = Files.newInputStream(inputRcPath)) {
333+
InputRC.configure(this, is);
334+
} catch (Exception e) {
335+
Log.debug("Error reading inputRc config file: ", inputRc, e);
336+
}
337+
}
338+
}
339+
324340
doDisplay();
325341
}
326342

0 commit comments

Comments
 (0)
Please sign in to comment.