#ifndef _BSD_SOURCE #define _BSD_SOURCE #endif #include #include #include #include #include #include #include #include #ifdef READLINE_HACK #include #endif #include static int terminal_fd = -1; static FILE *terminal = NULL; static struct termios terminal_settings, default_settings; #ifdef READLINE_HACK static void set_files0(void) { //(note that python will replace these with stdin and stdout, respectively, every iteration, so they have to be reset rl_instream = terminal; rl_outstream = terminal; //(python will configure the stdin terminal, not the one passed to it) tcsetattr(STDIN_FILENO, TCSAFLUSH, &default_settings); tcsetattr(terminal_fd, TCSAFLUSH, &terminal_settings); } //called by 'readline' when preparing to read a line of input static void set_files1(int flag) { set_files0(); rl_prep_terminal(flag); } #endif int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "%s [terminal command...]\n", argv[0]); return 1; } int master = -1, slave = -1, outcome = 0; openpty(&master, &slave, NULL, NULL, NULL); pid_t process = fork(); if (process < 0) { fprintf(stderr, "error allocating ptty: %s\n", strerror(errno)); return 1; } else if (process != 0) { close(master); terminal = fdopen((terminal_fd = slave), "a+"); //prepare the settings for the "real" terminal tcgetattr(slave, &terminal_settings); terminal_settings.c_lflag &= ~ISIG; terminal_settings.c_lflag &= ~ICANON; terminal_settings.c_lflag &= ~ECHO; #ifdef READLINE_HACK //save the stdin terminal settings to restore after python messes them up tcgetattr(STDIN_FILENO, &default_settings); #else tcsetattr(terminal_fd, TCSAFLUSH, &terminal_settings); #endif //set up the interpreter and I/O redirection Py_InitializeEx(0); PyRun_SimpleString("import sys, os"); char redirect[128]; snprintf(redirect, sizeof redirect, "sys.stdin = os.fdopen(%i, \"r\")", (int) slave); PyRun_SimpleString(redirect); snprintf(redirect, sizeof redirect, "sys.stdout = os.fdopen(%i, \"w\")", (int) slave); PyRun_SimpleString(redirect); snprintf(redirect, sizeof redirect, "sys.stderr = os.fdopen(%i, \"w\")", (int) slave); PyRun_SimpleString(redirect); #ifdef READLINE_HACK rl_prep_term_function = &set_files1; PyRun_SimpleString("import readline"); int dump = 1; //read and discard the first line, which is the X window ID that xterm prints while (dump > 0) { if (!readline(NULL)) break; --dump; } #endif fprintf(terminal, "***** Interactive Python Terminal (%i on %s) *****\n", getpid(), ttyname(slave)); //THE LINE BELOW IS THE MOST IMPORTANT!!! outcome = PyRun_InteractiveLoop(terminal /*<-- this is the pty!*/, ttyname(slave)); fclose(terminal); PyRun_SimpleString("sys.stdin = sys.__stdin__"); PyRun_SimpleString("sys.stdout = sys.__stdout__"); PyRun_SimpleString("sys.stderr = sys.__stderr__"); Py_Finalize(); } else { //run xterm close(slave); login_tty(master); execvp(argv[1], argv + 1); fprintf(stderr, "execvp error: %s\n", strerror(errno)); _exit(0); } return 0; }