A pseudo-terminal launcher for TurtleEdit
Programs running inside TurtleEdit may buffer their output differently from programs running in a terminal. This small launcher connects a program to a pseudo-terminal so it behaves as though it were running in a terminal.
Usage
Download run.c and compile it on macOS or Linux
using the following command. This launcher does not support Windows.
cc -o run run.c
This creates an executable named run. Copy or move it to the folder
where you keep your programs.
In TurtleEdit, choose Settings from the Run menu.
To run a compiled C program named a.out, set Command for execution to:
./run ./a.out
To run a Ruby program, use:
./run ruby %f
With the launcher in place, you can run interactive examples from textbooks without adding code to flush the output buffer.
Source code
/* a simple program launcher for TurtleEdit */
/* Yoshinori Hayakawa, Tohoku University */
/* 07-MAY-2014 */
/* most of this code is based on the exmaples in */
/* http://rachid.koucha.free.fr/tech_corner/pty_pdip.html */
/* */
#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFLEN 1024
char **cmdargv = NULL ;
char readbuf[BUFFLEN] ;
int terminating ;
void sigchld_handler(int);
int main(int argc, char *argv[]) {
int i,master,slave ;
pid_t pid ;
if (argc<=1) return 0 ;
else {
cmdargv = (char **) malloc(sizeof(char *)*argc) ;
for (i=1 ; i<argc ; i++) {
cmdargv[i-1] = argv[i] ;
}
cmdargv[argc-1]='\0' ;
}
master = open("/dev/ptmx", O_RDWR|O_NOCTTY);
grantpt(master);
unlockpt(master);
slave = open(ptsname(master), O_RDWR);
terminating = 0 ;
if (pid=fork()) { /* parent */
fd_set fd_input ;
int rc,k ;
signal(SIGCHLD, sigchld_handler);
close(slave) ;
while (1)
{
FD_ZERO(&fd_input);
FD_SET(0, &fd_input);
FD_SET(master, &fd_input);
rc = select(master + 1, &fd_input, NULL, NULL, NULL);
switch(rc)
{
case -1 :
exit(0);
default :
if (FD_ISSET(0, &fd_input)) { /* check stdin */
rc = read(0, readbuf, sizeof(readbuf));
if (rc > 0) {
write(master, readbuf, rc);
} else {
if (rc < 0) {
fprintf(stderr, "Error on read standard input\n");
exit(1);
}
}
}
if (FD_ISSET(master, &fd_input)) { /* check PTY */
rc = read(master, readbuf, sizeof(readbuf));
if (rc > 0) {
write(1, readbuf, rc);
}
if (terminating) exit(0) ;
}
}
}
}
else { /* child process */
struct termios term_setting ;
close(master) ;
dup2(slave,0) ;
dup2(slave,1) ;
dup2(slave,2) ;
/* disable echo */
tcgetattr (0, &term_setting) ;
term_setting.c_lflag &= ~ECHO;
term_setting.c_cc[VEOF] = 0x04 ; // CTRL-D
term_setting.c_cc[VERASE] = 0x08 ; // BACKSPACE
term_setting.c_lflag |= ICANON ;
tcsetattr (0, TCSAFLUSH, &term_setting) ;
setsid() ;
ioctl(0, TIOCSCTTY, 1);
if (execvp(argv[1],cmdargv)==-1) {
fprintf(stderr,"cannot execute %s\n",argv[1]) ;
}
_exit(0) ;
}
return 0 ;
}
void sigchld_handler(int x)
{
terminating = 1 ;
}