jline / jline3

JLine is a Java library for handling console input.
Other
1.49k stars 218 forks source link

Exception on embedded systems without infocmp and completer is working but not displaying the completion #87

Closed adibacco closed 7 years ago

adibacco commented 7 years ago

On my arm embedded system there is no infocmp, my TERM variable is set to vt102 and if I try to start the jline example I get an exception.

zynq> java -jar jlinetest.jar param
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8

Jan 01, 1970 12:04:55 AM org.jline.utils.Log logr
WARNING: Unable to retrieve infocmp for type vt102
java.io.IOException: Cannot run program "infocmp": error=2, No such file or directory
    at java.lang.ProcessBuilder.start(Unknown Source)
    at org.jline.utils.InfoCmp.getInfoCmp(InfoCmp.java:533)
    at org.jline.terminal.impl.AbstractTerminal.parseInfoCmp(AbstractTerminal.java:161)
    at org.jline.terminal.impl.LineDisciplineTerminal.<init>(LineDisciplineTerminal.java:101)
    at org.jline.terminal.impl.ExternalTerminal.<init>(ExternalTerminal.java:48)
    at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:261)
    at org.jline.terminal.TerminalBuilder.build(TerminalBuilder.java:149)
    at example.Example.main(Example.java:189)
Caused by: java.io.IOException: error=2, No such file or directory
    at java.lang.UNIXProcess.forkAndExec(Native Method)
    at java.lang.UNIXProcess.<init>(Unknown Source)
    at java.lang.ProcessImpl.start(Unknown Source)
    ... 8 more

prompt> 
======>""
prompt> 

======>""
prompt> 

======>""
prompt> C   
Command1 
======>"Command1"
prompt> 

I setup a terminal like this one:

            Terminal terminal = builder.system(false).streams(System.in, System.out).build();

            LineReader reader = LineReaderBuilder.builder()
                    .terminal(terminal)
                    .completer(completer)
                    .parser(parser)
                    .build();
gnodet commented 7 years ago

You can avoid calling the infocmp program by using the following:

    public static final String VT102_CAPS = 
            "#\tReconstructed via infocmp from file: /usr/share/terminfo/76/vt102\n" +
            "vt102|dec vt102,\n" +
            "\tam, mc5i, msgr, xenl, xon,\n" +
            "\tcols#80, it#8, lines#24, vt#3,\n" +
            "\tacsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,\n" +
            "\tbel=^G, blink=\\E[5m$<2>, bold=\\E[1m$<2>,\n" +
            "\tclear=\\E[H\\E[J$<50>, cr=^M, csr=\\E[%i%p1%d;%p2%dr,\n" +
            "\tcub=\\E[%p1%dD, cub1=^H, cud=\\E[%p1%dB, cud1=^J,\n" +
            "\tcuf=\\E[%p1%dC, cuf1=\\E[C$<2>,\n" +
            "\tcup=\\E[%i%p1%d;%p2%dH$<5>, cuu=\\E[%p1%dA,\n" +
            "\tcuu1=\\E[A$<2>, dch1=\\E[P, dl1=\\E[M, ed=\\E[J$<50>,\n" +
            "\tel=\\E[K$<3>, el1=\\E[1K$<3>, enacs=\\E(B\\E)0, home=\\E[H,\n" +
            "\tht=^I, hts=\\EH, il1=\\E[L, ind=^J, ka1=\\EOq, ka3=\\EOs, kb2=\\EOr,\n" +
            "\tkbs=^H, kc1=\\EOp, kc3=\\EOn, kcub1=\\EOD, kcud1=\\EOB,\n" +
            "\tkcuf1=\\EOC, kcuu1=\\EOA, kent=\\EOM, kf0=\\EOy, kf1=\\EOP,\n" +
            "\tkf10=\\EOx, kf2=\\EOQ, kf3=\\EOR, kf4=\\EOS, kf5=\\EOt, kf6=\\EOu,\n" +
            "\tkf7=\\EOv, kf8=\\EOl, kf9=\\EOw, lf1=pf1, lf2=pf2, lf3=pf3,\n" +
            "\tlf4=pf4, mc0=\\E[0i, mc4=\\E[4i, mc5=\\E[5i, rc=\\E8,\n" +
            "\trev=\\E[7m$<2>, ri=\\EM$<5>, rmacs=^O, rmam=\\E[?7l,\n" +
            "\trmir=\\E[4l, rmkx=\\E[?1l\\E>, rmso=\\E[m$<2>, rmul=\\E[m$<2>,\n" +
            "\trs2=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h, sc=\\E7,\n" +
            "\tsgr=\\E[0%?%p1%p6%|%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\\016%e\\017%;$<2>,\n" +
            "\tsgr0=\\E[m\\017$<2>, smacs=^N, smam=\\E[?7h, smir=\\E[4h,\n" +
            "\tsmkx=\\E[?1h\\E=, smso=\\E[7m$<2>, smul=\\E[4m$<2>,\n" +
            "\ttbc=\\E[3g,\n";

InfoCmp.setDefaultInfoCmp("vt102", VT102_CAPS);

Unless you don't have the stty program either, you should use system(true). If stty is not available, you could try adding jna to your classpath to use native calls instead.

adibacco commented 7 years ago

Your suggestion did the trick.

I changed to system(true) because I have stty now the situation is like this:

1) Connecting through TELNET everything is fine, also command completion. THX.

2) Connecting through SERIAL port I get this exception:

zynq> java -jar jlinetest.jar param
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
java.io.IOError: java.io.IOException: Unable to parse columns
    at org.jline.terminal.impl.AbstractPosixTerminal.getSize(AbstractPosixTerminal.java:60)
    at org.jline.reader.impl.LineReaderImpl.readLine(LineReaderImpl.java:477)
    at example.Example.main(Example.java:234)
Caused by: java.io.IOException: Unable to parse columns
    at org.jline.terminal.impl.ExecPty.doGetInt(ExecPty.java:261)
    at org.jline.terminal.impl.ExecPty.doGetSize(ExecPty.java:246)
    at org.jline.terminal.impl.ExecPty.getSize(ExecPty.java:153)
    at org.jline.terminal.impl.AbstractPosixTerminal.getSize(AbstractPosixTerminal.java:58)
    ... 2 more

To avoid this exception I need to use system(false) but then no completion is working.

gnodet commented 7 years ago

Could you give me the output of stty -a when you run it in your terminal ? It must use an unsupported (yet) syntax. This should be quite easy to fix. Using jna may be a workaround.

adibacco commented 7 years ago

jline3 is really wonderful anyway!

zynq> stty -a
speed 115200 baud;stty: standard input
 line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke
zynq> 
gnodet commented 7 years ago

Btw, what's your system ?

adibacco commented 7 years ago

It is a linux arm box (Linux (none) 4.4.0-xilinx-49048-g402062f #23 SMP PREEMPT Fri Dec 2 11:21:00 CET 2016 armv7l GNU/Linux) that uses BUSYBOX.

gnodet commented 7 years ago

Usually, stty -a reports the terminal size. In your case, it does not, that's why you end up with an exception. Looking the source code of BUSYBOX, it looks like the call to get_terminal_width_height does not succeed. IIUC, this exception happen when connecting through the serial port. You have a real terminal on the other side ? If so, it should be able to return the terminal size correctly from the stty -a call. As a workaround, I'd suggest you implement your own terminal overriding PosixSysTerminal and return new Size(0, 0) in getSize() instead of having this method thrown an exception.

The underlying issue could be investigated, but it's a bit out of my area...

gnodet commented 7 years ago

Again, maybe try with adding jna library to your classpath as a workaround.

gnodet commented 7 years ago

Let me know if you have more information, I'm willing to find a fix to make jline works in all environment.

adibacco commented 7 years ago

I modified the getSize in AbstractPosixTerminal returning new Size(80, 8) instead of throwing the exception and now it is working fine also from the serial. (returning Size(0,0) was causing an ArithmeticException) Regarding the client I'm using to connect to the serial port, its name is GTKTERM on Ubuntu. Thx again.

gnodet commented 7 years ago

See http://cafbit.com/entry/terminal_window_size_detection_over. Calling stty rows 24 columns 80 in your terminal before launching jline may help getting around the not correctly reported terminal size through the serial port.

adibacco commented 7 years ago

OK, the command you suggest does the trick. Now it is working perfectly also from serial. Thx.