0

I know that similar questions have been asked before, but not exactly what I'm asking. To begin with, let me explain my purpose. I'm trying to write a kind of "remote shell" that will take in characters from the console (System.in) on character at a time and then send those to a remote session on another machine, write them to that machine and gather any characters it might output to return to my shell to display back to the user.

So, the issue is that System.in, no matter what I do, doesn't really support a "raw" mode where any type of reader is able to read just one character at a time UNTIL a terminator character is entered, typically new line.

Things I have tried, Using Scanner, using a buffered reader, creating a FileDescriptor.in and creating a fileInputStream from that, using a FileChannel and reading into a ByteBuffer that is one character long, etc. In all cases, it seems, System.in only makes characters available to the java application after a terminator character has been entered by the user. I'm convinced there is not a "java" way to do this, so the question is, does anyone have some native code, wrapped in a java library to do this? Its hard to find such a thing just searching GitHub.

BTW, for the remote console, I'm using the pty4J package. I've seen sample projects that connect to that code using other langauages, for example javaScript running in a browser to create a web based shell. Other languages all you to do a simple "get_char" on standard in.

Some examples of the code I've tried:

            Scanner scanner = new Scanner(System.in);
            FileDescriptor fd = FileDescriptor.in;
            FileInputStream fis = new FileInputStream(fd);
            FileChannel fc = fis.getChannel();
            while(process.isAlive()) {
                System.out.println(scanner.next());
//                ByteBuffer bb = ByteBuffer.allocate(1);
//                int c = fc.read(bb);
//                int c = fis.read();
//                System.err.println("Read " + c);
//                if (c == 1) {
//                    os.write(bb.get());
//                }
            }

You can see that I've tried various methods to read the input: scanner.next(), fc.read(byteBuffer), fileInputStream.read(), etc. All attempts "wait" till a terminator character is entered.

Additionally, I have tried using the "useDelimiter" and "next(pattern)" methods on the scanner too. That's still not working.

Any pointer or help is much appreciated.

Lord Moon
  • 23
  • 5
  • *In all cases, it seems, System.in only makes characters available to the java application after a terminator character has been entered by the user.* You're right. 'Raw' access to a system terminal is not possible in pure Java. Your best route for your use case is probably to make a Java GUI and do it with key listeners/actions – g00se Oct 16 '22 at 09:53
  • https://stackoverflow.com/questions/1066318/how-to-read-a-single-char-from-the-console-in-java-as-the-user-types-it – Cheng Thao Oct 16 '22 at 15:27
  • Great pointer there. The technique a little further down that post has what I needed. A simple (Linux/OS/x/Windows) JNA adapter to call the native functions. Don't like the highest recommendation of switching to "raw" mode -- which is highly platform dependent, but the little library works great. "I have written a Java class RawConsoleInput that uses JNA to call operating system functions of Windows and Unix/Linux." – Lord Moon Nov 14 '22 at 17:14

1 Answers1

0

Below is an example of reading one character at a time until end of stream is reached. On linux, you type control-d to signal the end input. I think on Windows, you type control-c to end of input.

import java.io.*;
class Test {
  public static void main(String[] args) throws IOException {
    int c = 0;
    while( (c=System.in.read()) != -1){
      System.out.println((char) c);
    }
  }
}
Cheng Thao
  • 1,467
  • 1
  • 3
  • 9
  • 1
    In windows it's ctrl-Z then enter. – tgdavies Oct 16 '22 at 07:37
  • I have tried that exact thing. It doesn't meet requirement in that you have to enter a terminator character before characters are available to read. That is the specific problem that I'm trying to solve: to be able to read a character from system.in without waiting for a terminator character. Note that this is possible in AWT/SWING/FX --> you just add a keyboard input listener. However, I have found no way to do this natively at the command line level. – Lord Moon Oct 16 '22 at 13:59