13/10/2018

# PTerm: yet another Terminal Emulator for Pharo

I use Unix terminal a lot in work, when i work with Pharo and ROS (PhaROS), switching regularly between Pharo and native terminal application (for ROS command line) is kind of inconvenient. I've been thinking of using a terminal emulator application for Pharo. Googling around, i found out that there is no such thing that is ready for production work on modern Pharo, except a prototype work of Pavel Krivanek available at: https://github.com/pavel-krivanek/terminal. However, that code is messy, buggy, and not ready for production work . So i decided to take my time to work on it.

I grabbed the code from Pavel's repository, kept and improved only the UI and protocol parts (fix display/keyboard event bugs, clean up unused code, etc.), then decided to implement my own FFI calls to the underlying system terminal.

## In-image FFI calls to spawn a tty in a child process

Since libc's fork() does not work really well on Pharo, an in-image (no external C lib calls other than libc) solution to spawn a tty using classic fork() is not feasible in this case. An alternative way is to use posix_spawn(), this function allows to spawn a child process and execute an Unix command on that process (eg. /bin/bash). With posix_spawn and some setting functions (for IO redirection), i am able to spawn a Bash on a new child process and redirect its IO to Pharo using only in-image FFI calls to libC (no external C code needed). However, due to the limitation of posix_spawn we cannot control what happens inside the child process, including making the child process a new session leader (using setsid()). Consequently, the spawned tty is not fully interactive, the following warning will display:

The spawned shell works well for almost single process commands like ls, top, htop, etc, but does not support commands that require job control or command that creates new subprocess like ssh.

## Fully interactive shell on Pharo using custom C lib

Since i really need a fully interactive shell, the classic way is to use fork() in an custom C library for spawning a tty in a child process, then make FFI calls to that library in Pharo whenever we want the terminal access. My solution in this case is similar to what Pavel did. However, the C code is a little bit different. The down side is that this solution requires gcc to be installed on the system. This allows to compile the custom C code to shared library on the first run of the application. The compilation is automatic and off-scene without user notice though.

## Combine the two solutions into one application

The idea is that we can use the in-image solution as a fallback situation for the custom C library solution. On the first run, the application will try to compile the C code and make FFI calls to the compiled lib in order to spawn a fully interactive shell. For some reason, if the application fail to access or compile the C lib, a second attempt will be made by using the in-image FFI calls, if success, a shell without job control will be spawned.

## Install on Pharo 7

Metacello new
repository: 'github://lxsang/PTerm';
baseline:'PTerm';