/*--------------------------------*-C-*---------------------------------* * File: command.c */ /* notes: */ /*----------------------------------------------------------------------* * Copyright 1992 John Bovey, University of Kent at Canterbury. * * You can do what you like with this source code as long as * you don't try to make money out of it and you include an * unaltered copy of this message (including the copyright). * * This module has been very heavily modified by R. Nation * * No additional restrictions are applied * * Additional modification by Garrett D'Amore to * allow vt100 printing. No additional restrictions are applied. * * Integrated modifications by Steven Hirsch to * properly support X11 mouse report mode and support for DEC * "private mode" save/restore functions. * * Integrated key-related changes by Jakub Jelinek * to handle Shift+function keys properly. * Should be used with enclosed termcap / terminfo database. * * Extensive modifications by mj olesen * No additional restrictions. * * Further modification and cleanups for Solaris 2.x and Linux 1.2.x * by Raul Garcia Garcia . No additional restrictions. * * As usual, the author accepts no responsibility for anything, nor does * he guarantee anything whatsoever. *----------------------------------------------------------------------*/ static const char cvs_ident[] = "$Id$"; /* includes: */ #include "config.h" #include "feature.h" /* System Headers */ #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_SYS_IOCTL_H # include #endif #include #include /* X11 Headers */ #include #include #include #include #include #include #include #include #ifdef USE_GETGRNAME # include #endif #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #if defined (__svr4__) # include /* for struct rlimit */ # include /* for I_PUSH */ # ifdef HAVE_SYS_STRTIO_H # include # endif # ifdef HAVE_BSDTTY_H # include # endif # define _NEW_TTY_CTRL /* to get proper defines in */ #endif #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_TERMIOS_H # include #else # include #endif #if defined(__sun) && defined(__SVR4) # include #endif #include #include #if defined(linux) # include /* For N_TTY_BUF_SIZE. */ #endif #if defined(linux) # include /* For strsep(). -vendu */ #endif /* Eterm-specific Headers */ #include "command.h" #include "main.h" #include "../libmej/debug.h" #include "debug.h" #include "../libmej/mem.h" #include "../libmej/strings.h" #include "events.h" #include "font.h" #include "graphics.h" #include "grkelot.h" #include "options.h" #include "pixmap.h" #ifdef PROFILE # include "profile.h" #endif #include "screen.h" #include "scrollbar.h" #include "string.h" #include "term.h" #ifdef USE_POSIX_THREADS # include "threads.h" #endif #ifdef UTMP_SUPPORT # include "eterm_utmp.h" #endif #include "windows.h" /* local variables */ int my_ruid, my_euid, my_rgid, my_egid; char initial_dir[PATH_MAX + 1]; static char *ptydev = NULL, *ttydev = NULL; /* pty/tty name */ int cmd_fd = -1; /* file descriptor connected to the command */ pid_t cmd_pid = -1; /* process id if child */ int Xfd = -1; /* file descriptor of X server connection */ unsigned int num_fds = 0; /* number of file descriptors being used */ struct stat ttyfd_stat; /* original status of the tty we will use */ int refresh_count = 0, refresh_limit = 1, refresh_type = FAST_REFRESH; Atom wmDeleteWindow; unsigned char cmdbuf_base[CMD_BUF_SIZE], *cmdbuf_ptr, *cmdbuf_endp; /* Addresses pasting large amounts of data * code pinched from xterm */ static char *v_buffer; /* pointer to physical buffer */ static char *v_bufstr = NULL; /* beginning of area to write */ static char *v_bufptr; /* end of area to write */ static char *v_bufend; /* end of physical buffer */ /* OffiX Dnd (drag 'n' drop) support */ #ifdef OFFIX_DND static Atom DndProtocol, DndSelection; #endif /* OFFIX_DND */ #ifdef USE_XIM XIC Input_Context = NULL; /* input context */ #endif /* Substitutes for missing system functions */ #if !defined(_POSIX_VERSION) && defined(__svr4__) int getdtablesize(void) { struct rlimit rlim; getrlimit(RLIMIT_NOFILE, &rlim); return rlim.rlim_cur; } # endif /* Take care of suid/sgid super-user (root) privileges */ void privileges(int mode) { #ifdef __CYGWIN32__ return; #endif switch (mode) { case IGNORE: /* Revoke suid/sgid privs and return to normal uid/gid -- mej */ D_UTMP(("[%ld]: Before privileges(REVERT): [ %ld, %ld ] [ %ld, %ld ]\n", getpid(), getuid(), getgid(), geteuid(), getegid())); #ifdef HAVE_SETRESGID setresgid(my_rgid, my_rgid, my_egid); #elif defined(HAVE_SAVED_UIDS) setregid(my_rgid, my_rgid); #else setregid(my_egid, -1); setregid(-1, my_rgid); #endif #ifdef HAVE_SETRESUID setresuid(my_ruid, my_ruid, my_euid); #elif defined(HAVE_SAVED_UIDS) setreuid(my_ruid, my_ruid); #else setreuid(my_euid, -1); setreuid(-1, my_ruid); #endif D_UTMP(("[%ld]: After privileges(REVERT): [ %ld, %ld ] [ %ld, %ld ]\n", getpid(), getuid(), getgid(), geteuid(), getegid())); break; case SAVE: break; case RESTORE: D_UTMP(("[%ld]: Before privileges(INVOKE): [ %ld, %ld ] [ %ld, %ld ]\n", getpid(), getuid(), getgid(), geteuid(), getegid())); #ifdef HAVE_SETRESUID setresuid(my_ruid, my_euid, my_euid); #elif defined(HAVE_SAVED_UIDS) setreuid(my_ruid, my_euid); #else setreuid(-1, my_euid); setreuid(my_ruid, -1); #endif #ifdef HAVE_SETRESGID setresgid(my_rgid, my_egid, my_egid); #elif defined(HAVE_SAVED_UIDS) setregid(my_rgid, my_egid); #else setregid(-1, my_egid); setregid(my_rgid, -1); #endif D_UTMP(("[%ld]: After privileges(INVOKE): [ %ld, %ld ] [ %ld, %ld ]\n", getpid(), getuid(), getgid(), geteuid(), getegid())); break; } } char * sig_to_str(int sig) { /* NOTE: This can't be done with a switch because of possible conflicting * conflicting signal types. -vendu */ #ifdef SIGHUP if (sig == SIGHUP) { return ("SIGHUP"); } #endif #ifdef SIGINT if (sig == SIGINT) { return ("SIGINT"); } #endif #ifdef SIGQUIT if (sig == SIGQUIT) { return ("SIGQUIT"); } #endif #ifdef SIGILL if (sig == SIGILL) { return ("SIGILL"); } #endif #ifdef SIGTRAP if (sig == SIGTRAP) { return ("SIGTRAP"); } #endif #ifdef SIGABRT if (sig == SIGABRT) { return ("SIGABRT"); } #endif #ifdef SIGIOT if (sig == SIGIOT) { return ("SIGIOT"); } #endif #ifdef SIGEMT if (sig == SIGEMT) { return ("SIGEMT"); } #endif #ifdef SIGFPE if (sig == SIGFPE) { return ("SIGFPE"); } #endif #ifdef SIGKILL if (sig == SIGKILL) { return ("SIGKILL"); } #endif #ifdef SIGBUS if (sig == SIGBUS) { return ("SIGBUS"); } #endif #ifdef SIGSEGV if (sig == SIGSEGV) { return ("SIGSEGV"); } #endif #ifdef SIGSYS if (sig == SIGSYS) { return ("SIGSYS"); } #endif #ifdef SIGPIPE if (sig == SIGPIPE) { return ("SIGPIPE"); } #endif #ifdef SIGALRM if (sig == SIGALRM) { return ("SIGALRM"); } #endif #ifdef SIGTERM if (sig == SIGTERM) { return ("SIGTERM"); } #endif #ifdef SIGUSR1 if (sig == SIGUSR1) { return ("SIGUSR1"); } #endif #ifdef SIGUSR2 if (sig == SIGUSR2) { return ("SIGUSR2"); } #endif #ifdef SIGCHLD if (sig == SIGCHLD) { return ("SIGCHLD"); } #endif #ifdef SIGCLD if (sig == SIGCLD) { return ("SIGCLD"); } #endif #ifdef SIGPWR if (sig == SIGPWR) { return ("SIGPWR"); } #endif #ifdef SIGVTALRM if (sig == SIGVTALRM) { return ("SIGVTALRM"); } #endif #ifdef SIGPROF if (sig == SIGPROF) { return ("SIGPROF"); } #endif #ifdef SIGIO if (sig == SIGIO) { return ("SIGIO"); } #endif #ifdef SIGPOLL if (sig == SIGPOLL) { return ("SIGPOLL"); } #endif #ifdef SIGWINCH if (sig == SIGWINCH) { return ("SIGWINCH"); } #endif #ifdef SIGWINDOW if (sig == SIGWINDOW) { return ("SIGWINDOW"); } #endif #ifdef SIGSTOP if (sig == SIGSTOP) { return ("SIGSTOP"); } #endif #ifdef SIGTSTP if (sig == SIGTSTP) { return ("SIGTSTP"); } #endif #ifdef SIGCONT if (sig == SIGCONT) { return ("SIGCONT"); } #endif #ifdef SIGTTIN if (sig == SIGTTIN) { return ("SIGTTIN"); } #endif #ifdef SIGTTOU if (sig == SIGTTOU) { return ("SIGTTOU"); } #endif #ifdef SIGURG if (sig == SIGURG) { return ("SIGURG"); } #endif #ifdef SIGLOST if (sig == SIGLOST) { return ("SIGLOST"); } #endif #ifdef SIGRESERVE if (sig == SIGRESERVE) { return ("SIGRESERVE"); } #endif #ifdef SIGDIL if (sig == SIGDIL) { return ("SIGDIL"); } #endif #ifdef SIGXCPU if (sig == SIGXCPU) { return ("SIGXCPU"); } #endif #ifdef SIGXFSZ if (sig == SIGXFSZ) { return ("SIGXFSZ"); } #endif return ("Unknown signal"); } const char * event_type_to_name(int type) { if (type == KeyPress) { return "KeyPress"; } if (type == KeyRelease) { return "KeyRelease"; } if (type == ButtonPress) { return "ButtonPress"; } if (type == ButtonRelease) { return "ButtonRelease"; } if (type == MotionNotify) { return "MotionNotify"; } if (type == EnterNotify) { return "EnterNotify"; } if (type == LeaveNotify) { return "LeaveNotify"; } if (type == FocusIn) { return "FocusIn"; } if (type == FocusOut) { return "FocusOut"; } if (type == KeymapNotify) { return "KeymapNotify"; } if (type == Expose) { return "Expose"; } if (type == GraphicsExpose) { return "GraphicsExpose"; } if (type == NoExpose) { return "NoExpose"; } if (type == VisibilityNotify) { return "VisibilityNotify"; } if (type == CreateNotify) { return "CreateNotify"; } if (type == DestroyNotify) { return "DestroyNotify"; } if (type == UnmapNotify) { return "UnmapNotify"; } if (type == MapNotify) { return "MapNotify"; } if (type == MapRequest) { return "MapRequest"; } if (type == ReparentNotify) { return "ReparentNotify"; } if (type == ConfigureNotify) { return "ConfigureNotify"; } if (type == ConfigureRequest) { return "ConfigureRequest"; } if (type == GravityNotify) { return "GravityNotify"; } if (type == ResizeRequest) { return "ResizeRequest"; } if (type == CirculateNotify) { return "CirculateNotify"; } if (type == CirculateRequest) { return "CirculateRequest"; } if (type == PropertyNotify) { return "PropertyNotify"; } if (type == SelectionClear) { return "SelectionClear"; } if (type == SelectionRequest) { return "SelectionRequest"; } if (type == SelectionNotify) { return "SelectionNotify"; } if (type == ColormapNotify) { return "ColormapNotify"; } if (type == ClientMessage) { return "ClientMessage"; } if (type == MappingNotify) { return "MappingNotify"; } return "Bad Event!"; } const char * request_code_to_name(int code) { if (code == X_CreateWindow) { return "XCreateWindow"; } if (code == X_ChangeWindowAttributes) { return "XChangeWindowAttributes"; } if (code == X_GetWindowAttributes) { return "XGetWindowAttributes"; } if (code == X_DestroyWindow) { return "XDestroyWindow"; } if (code == X_DestroySubwindows) { return "XDestroySubwindows"; } if (code == X_ChangeSaveSet) { return "XChangeSaveSet"; } if (code == X_ReparentWindow) { return "XReparentWindow"; } if (code == X_MapWindow) { return "XMapWindow"; } if (code == X_MapSubwindows) { return "XMapSubwindows"; } if (code == X_UnmapWindow) { return "XUnmapWindow"; } if (code == X_UnmapSubwindows) { return "XUnmapSubwindows"; } if (code == X_ConfigureWindow) { return "XConfigureWindow"; } if (code == X_CirculateWindow) { return "XCirculateWindow"; } if (code == X_GetGeometry) { return "XGetGeometry"; } if (code == X_QueryTree) { return "XQueryTree"; } if (code == X_InternAtom) { return "XInternAtom"; } if (code == X_GetAtomName) { return "XGetAtomName"; } if (code == X_ChangeProperty) { return "XChangeProperty"; } if (code == X_DeleteProperty) { return "XDeleteProperty"; } if (code == X_GetProperty) { return "XGetProperty"; } if (code == X_ListProperties) { return "XListProperties"; } if (code == X_SetSelectionOwner) { return "XSetSelectionOwner"; } if (code == X_GetSelectionOwner) { return "XGetSelectionOwner"; } if (code == X_ConvertSelection) { return "XConvertSelection"; } if (code == X_SendEvent) { return "XSendEvent"; } if (code == X_GrabPointer) { return "XGrabPointer"; } if (code == X_UngrabPointer) { return "XUngrabPointer"; } if (code == X_GrabButton) { return "XGrabButton"; } if (code == X_UngrabButton) { return "XUngrabButton"; } if (code == X_ChangeActivePointerGrab) { return "XChangeActivePointerGrab"; } if (code == X_GrabKeyboard) { return "XGrabKeyboard"; } if (code == X_UngrabKeyboard) { return "XUngrabKeyboard"; } if (code == X_GrabKey) { return "XGrabKey"; } if (code == X_UngrabKey) { return "XUngrabKey"; } if (code == X_AllowEvents) { return "XAllowEvents"; } if (code == X_GrabServer) { return "XGrabServer"; } if (code == X_UngrabServer) { return "XUngrabServer"; } if (code == X_QueryPointer) { return "XQueryPointer"; } if (code == X_GetMotionEvents) { return "XGetMotionEvents"; } if (code == X_TranslateCoords) { return "XTranslateCoords"; } if (code == X_WarpPointer) { return "XWarpPointer"; } if (code == X_SetInputFocus) { return "XSetInputFocus"; } if (code == X_GetInputFocus) { return "XGetInputFocus"; } if (code == X_QueryKeymap) { return "XQueryKeymap"; } if (code == X_OpenFont) { return "XOpenFont"; } if (code == X_CloseFont) { return "XCloseFont"; } if (code == X_QueryFont) { return "XQueryFont"; } if (code == X_QueryTextExtents) { return "XQueryTextExtents"; } if (code == X_ListFonts) { return "XListFonts"; } if (code == X_ListFontsWithInfo) { return "XListFontsWithInfo"; } if (code == X_SetFontPath) { return "XSetFontPath"; } if (code == X_GetFontPath) { return "XGetFontPath"; } if (code == X_CreatePixmap) { return "XCreatePixmap"; } if (code == X_FreePixmap) { return "XFreePixmap"; } if (code == X_CreateGC) { return "XCreateGC"; } if (code == X_ChangeGC) { return "XChangeGC"; } if (code == X_CopyGC) { return "XCopyGC"; } if (code == X_SetDashes) { return "XSetDashes"; } if (code == X_SetClipRectangles) { return "XSetClipRectangles"; } if (code == X_FreeGC) { return "XFreeGC"; } if (code == X_ClearArea) { return "XClearArea"; } if (code == X_CopyArea) { return "XCopyArea"; } if (code == X_CopyPlane) { return "XCopyPlane"; } if (code == X_PolyPoint) { return "XPolyPoint"; } if (code == X_PolyLine) { return "XPolyLine"; } if (code == X_PolySegment) { return "XPolySegment"; } if (code == X_PolyRectangle) { return "XPolyRectangle"; } if (code == X_PolyArc) { return "XPolyArc"; } if (code == X_FillPoly) { return "XFillPoly"; } if (code == X_PolyFillRectangle) { return "XPolyFillRectangle"; } if (code == X_PolyFillArc) { return "XPolyFillArc"; } if (code == X_PutImage) { return "XPutImage"; } if (code == X_GetImage) { return "XGetImage"; } if (code == X_PolyText8) { return "XPolyText8"; } if (code == X_PolyText16) { return "XPolyText16"; } if (code == X_ImageText8) { return "XImageText8"; } if (code == X_ImageText16) { return "XImageText16"; } if (code == X_CreateColormap) { return "XCreateColormap"; } if (code == X_FreeColormap) { return "XFreeColormap"; } if (code == X_CopyColormapAndFree) { return "XCopyColormapAndFree"; } if (code == X_InstallColormap) { return "XInstallColormap"; } if (code == X_UninstallColormap) { return "XUninstallColormap"; } if (code == X_ListInstalledColormaps) { return "XListInstalledColormaps"; } if (code == X_AllocColor) { return "XAllocColor"; } if (code == X_AllocNamedColor) { return "XAllocNamedColor"; } if (code == X_AllocColorCells) { return "XAllocColorCells"; } if (code == X_AllocColorPlanes) { return "XAllocColorPlanes"; } if (code == X_FreeColors) { return "XFreeColors"; } if (code == X_StoreColors) { return "XStoreColors"; } if (code == X_StoreNamedColor) { return "XStoreNamedColor"; } if (code == X_QueryColors) { return "XQueryColors"; } if (code == X_LookupColor) { return "XLookupColor"; } if (code == X_CreateCursor) { return "XCreateCursor"; } if (code == X_CreateGlyphCursor) { return "XCreateGlyphCursor"; } if (code == X_FreeCursor) { return "XFreeCursor"; } if (code == X_RecolorCursor) { return "XRecolorCursor"; } if (code == X_QueryBestSize) { return "XQueryBestSize"; } if (code == X_QueryExtension) { return "XQueryExtension"; } if (code == X_ListExtensions) { return "XListExtensions"; } if (code == X_ChangeKeyboardMapping) { return "XChangeKeyboardMapping"; } if (code == X_GetKeyboardMapping) { return "XGetKeyboardMapping"; } if (code == X_ChangeKeyboardControl) { return "XChangeKeyboardControl"; } if (code == X_GetKeyboardControl) { return "XGetKeyboardControl"; } if (code == X_Bell) { return "XBell"; } if (code == X_ChangePointerControl) { return "XChangePointerControl"; } if (code == X_GetPointerControl) { return "XGetPointerControl"; } if (code == X_SetScreenSaver) { return "XSetScreenSaver"; } if (code == X_GetScreenSaver) { return "XGetScreenSaver"; } if (code == X_ChangeHosts) { return "XChangeHosts"; } if (code == X_ListHosts) { return "XListHosts"; } if (code == X_SetAccessControl) { return "XSetAccessControl"; } if (code == X_SetCloseDownMode) { return "XSetCloseDownMode"; } if (code == X_KillClient) { return "XKillClient"; } if (code == X_RotateProperties) { return "XRotateProperties"; } if (code == X_ForceScreenSaver) { return "XForceScreenSaver"; } if (code == X_SetPointerMapping) { return "XSetPointerMapping"; } if (code == X_GetPointerMapping) { return "XGetPointerMapping"; } if (code == X_SetModifierMapping) { return "XSetModifierMapping"; } if (code == X_GetModifierMapping) { return "XGetModifierMapping"; } if (code == X_NoOperation) { return "XNoOperation"; } return "Unknown"; } static void hard_exit(void) { #ifdef HAVE__EXIT _exit(-1); #elif defined(SIGKILL) kill(cmd_pid, SIGKILL); raise(SIGKILL); #else kill(cmd_pid, 9); raise(9); #endif } /* Try to get a stack trace when we croak */ void dump_stack_trace(void) { char cmd[256]; #ifdef NO_STACK_TRACE return; #endif print_error("Attempting to dump a stack trace....\n"); signal(SIGTSTP, exit); /* Don't block on tty output, just die */ #ifdef HAVE_U_STACK_TRACE U_STACK_TRACE(); return; #elif defined(GDB) snprintf(cmd, sizeof(cmd), "/bin/echo backtrace | " GDB " " APL_NAME " %d", getpid()); #elif defined(PSTACK) snprintf(cmd, sizeof(cmd), PSTACK " %d", getpid()); #elif defined(DBX) # ifdef _AIX snprintf(cmd, sizeof(cmd), "/bin/echo 'where\ndetach' | " DBX " -a %d", getpid()); # elif defined(__sgi) snprintf(cmd, sizeof(cmd), "/bin/echo 'where\ndetach' | " DBX " -p %d", getpid()); # else snprintf(cmd, sizeof(cmd), "/bin/echo 'where\ndetach' | " DBX " %s %d", orig_argv0, getpid()); # endif #else print_error("Your system does not support any of the methods Eterm uses. Exiting.\n"); return; #endif signal(SIGALRM, (sighandler_t) hard_exit); alarm(10); system(cmd); } /* signal handling, exit handler */ /* * Catch a SIGCHLD signal and exit if the direct child has died */ RETSIGTYPE Child_signal(int sig) { int pid, save_errno = errno; D_CMD(("Received signal %s (%d)\n", sig_to_str(sig), sig)); do { errno = 0; } while ((-1 == (pid = waitpid(-1, NULL, WNOHANG))) && (errno == EINTR)); D_CMD(("pid == %d, cmd_pid == %d\n", pid, cmd_pid)); /* If the child that exited is the command we spawned, or if the child exited before fork() returned in the parent, it must be our immediate child that exited. We exit gracefully. */ if (pid == cmd_pid || cmd_pid == -1) { if (Options & Opt_pause) { const char *message = "\r\nPress any key to exit " APL_NAME "...."; scr_refresh(DEFAULT_REFRESH); scr_add_lines(message, 1, strlen(message)); scr_refresh(DEFAULT_REFRESH); keypress_exit = 1; return; } exit(EXIT_SUCCESS); } errno = save_errno; D_CMD(("Child_signal: installing signal handler\n")); signal(SIGCHLD, Child_signal); return ((RETSIGTYPE) 0); } /* Handles signals usually sent by a user, like HUP, TERM, INT. */ RETSIGTYPE Exit_signal(int sig) { print_error("Received terminal signal %s (%d)", sig_to_str(sig), sig); signal(sig, SIG_DFL); #ifdef UTMP_SUPPORT privileges(INVOKE); cleanutent(); privileges(REVERT); #endif D_CMD(("Exit_signal(): exit(%s)\n", sig_to_str(sig))); exit(sig); } /* Handles abnormal termination signals -- mej */ static RETSIGTYPE SegvHandler(int sig) { print_error("Received terminal signal %s (%d)", sig_to_str(sig), sig); signal(sig, SIG_DFL); /* Let the OS handle recursive seg faults */ /* Lock down security so we don't write any core files as root. */ privileges(REVERT); umask(077); /* Make an attempt to dump a stack trace */ dump_stack_trace(); /* Exit */ exit(sig); } /* * Exit gracefully, clearing the utmp entry and restoring tty attributes * TODO: Also free up X resources, etc., if possible */ void clean_exit(void) { scr_release(); privileges(INVOKE); #ifndef __CYGWIN32__ if (ttydev) { D_CMD(("Restoring \"%s\" to mode %03o, uid %d, gid %d\n", ttydev, ttyfd_stat.st_mode, ttyfd_stat.st_uid, ttyfd_stat.st_gid)); if (chmod(ttydev, ttyfd_stat.st_mode) != 0) { D_UTMP(("chmod(\"%s\", %03o) failed: %s\n", ttydev, ttyfd_stat.st_mode, strerror(errno))); } if (chown(ttydev, ttyfd_stat.st_uid, ttyfd_stat.st_gid) != 0) { D_UTMP(("chown(\"%s\", %d, %d) failed: %s\n", ttydev, ttyfd_stat.st_uid, ttyfd_stat.st_gid, strerror(errno))); } } #endif /* __CYGWIN32__ */ #ifdef UTMP_SUPPORT cleanutent(); #endif privileges(REVERT); #ifdef USE_POSIX_THREADS /* Get rid of threads if there are any running. Doesn't work yet. */ # if 0 D_THREADS(("pthread_kill_other_threads_np();\n")); pthread_kill_other_threads_np(); D_THREADS(("pthread_exit();\n")); # endif #endif } /* Acquire a pseudo-teletype from the system. */ /* * On failure, returns -1. * On success, returns the file descriptor. * * If successful, ttydev and ptydev point to the names of the * master and slave parts */ #ifdef __sgi inline int sgi_get_pty(void) { int fd = -1; ptydev = ttydev = _getpty(&fd, O_RDWR | O_NDELAY, 0620, 0); return (ptydev == NULL ? -1 : fd); } #endif #ifdef _AIX inline int aix_get_pty(void) { int fd = -1; if ((fd = open("/dev/ptc", O_RDWR)) < 0) return (-1); else ptydev = ttydev = ttyname(fd); return (fd); } #endif #ifdef ALL_NUMERIC_PTYS inline int sco_get_pty(void) { static char pty_name[] = "/dev/ptyp??\0\0\0"; static char tty_name[] = "/dev/ttyp??\0\0\0"; int idx; int fd = -1; ptydev = pty_name; ttydev = tty_name; for (idx = 0; idx < 256; idx++) { sprintf(ptydev, "%s%d", "/dev/ptyp", idx); sprintf(ttydev, "%s%d", "/dev/ttyp", idx); if (access(ttydev, F_OK) < 0) { idx = 256; break; } if ((fd = open(ptydev, O_RDWR)) >= 0) { if (access(ttydev, R_OK | W_OK) == 0) return (fd); close(fd); } } return (-1); } #endif #if defined (__svr4__) || defined(__CYGWIN32__) || ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)) inline int svr_get_pty(void) { int fd = -1; /* open the STREAMS, clone device /dev/ptmx (master pty) */ if ((fd = open("/dev/ptmx", O_RDWR)) < 0) { return (-1); } else { if (grantpt(fd) != 0) { print_error("grantpt(%d) failed: %s\n", fd, strerror(errno)); return (-1); } else if (unlockpt(fd) != 0) { print_error("unlockpt(%d) failed: %s\n", fd, strerror(errno)); return (-1); } else { ptydev = ttydev = ptsname(fd); if (ttydev == NULL) { print_error("ptsname(%d) failed: %s\n", fd, strerror(errno)); return (-1); } } } return (fd); } #endif #define PTYCHAR1 "pqrstuvwxyz" #define PTYCHAR2 "0123456789abcdefghijklmnopqrstuvwxyz" inline int gen_get_pty(void) { static char pty_name[] = "/dev/pty??"; static char tty_name[] = "/dev/tty??"; int len = sizeof(tty_name); char *c1, *c2; int fd = -1; ptydev = pty_name; ttydev = tty_name; for (c1 = PTYCHAR1; *c1; c1++) { ptydev[len - 3] = ttydev[len - 3] = *c1; for (c2 = PTYCHAR2; *c2; c2++) { ptydev[len - 2] = ttydev[len - 2] = *c2; if ((fd = open(ptydev, O_RDWR)) >= 0) { if (access(ttydev, R_OK | W_OK) == 0) return (fd); close(fd); } } } return (-1); } int get_pty(void) { int fd = -1; #if defined(__sgi) fd = sgi_get_pty(); #elif defined(_AIX) fd = aix_get_pty(); #elif defined(__svr4__) || defined(__CYGWIN32__) fd = svr_get_pty(); #elif ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)) fd = svr_get_pty(); #elif defined(ALL_NUMERIC_PTYS) /* SCO OSr5 */ fd = sco_get_pty(); #endif /* Fall back on this method */ if (fd == -1) { fd = gen_get_pty(); } if (fd != -1) { fcntl(fd, F_SETFL, O_NDELAY); return (fd); } else { print_error("Can't open pseudo-tty -- %s", strerror(errno)); return (-1); } } /* establish a controlling teletype for new session */ /* * On some systems this can be done with ioctl() but on others we * need to re-open the slave tty. */ int get_tty(void) { int fd; pid_t pid; /* * setsid() [or setpgrp] must be before open of the terminal, * otherwise there is no controlling terminal (Solaris 2.4, HP-UX 9) */ #ifndef ultrix # ifdef NO_SETSID pid = setpgrp(0, 0); # else pid = setsid(); # endif if (pid < 0) { D_TTYMODE(("%s: setsid() failed: %s, PID == %d\n", rs_name, strerror(errno), pid)); } #endif /* ultrix */ privileges(INVOKE); if (ttydev == NULL) { print_error("Slave tty device name is NULL. Failed to open slave pty.\n"); exit(EXIT_FAILURE); } else if ((fd = open(ttydev, O_RDWR)) < 0) { print_error("Can't open slave tty %s -- %s", ttydev, strerror(errno)); exit(EXIT_FAILURE); } else { D_TTY(("Opened slave tty %s\n", ttydev)); privileges(REVERT); } #if defined (__svr4__) /* * Push STREAMS modules: * ptem: pseudo-terminal hardware emulation module. * ldterm: standard terminal line discipline. * ttcompat: V7, 4BSD and XENIX STREAMS compatibility module. */ ioctl(fd, I_PUSH, "ptem"); ioctl(fd, I_PUSH, "ldterm"); ioctl(fd, I_PUSH, "ttcompat"); #else /* __svr4__ */ { /* change ownership of tty to real uid and real group */ unsigned int mode = 0620; gid_t gid = my_rgid; # ifdef USE_GETGRNAME { struct group *gr = getgrnam(TTY_GRP_NAME); if (gr) { /* change ownership of tty to real uid, "tty" gid */ gid = gr->gr_gid; mode = 0620; } } # endif /* USE_GETGRNAME */ privileges(INVOKE); # ifndef __CYGWIN32__ fchown(fd, my_ruid, gid); /* fail silently */ fchmod(fd, mode); # endif privileges(REVERT); } #endif /* __svr4__ */ /* * Close all file descriptors. If only stdin/out/err are closed, * child processes remain alive upon deletion of the window. */ { unsigned short i; for (i = 0; i < num_fds; i++) { if (i != fd) close(i); } } /* Reopen stdin, stdout and stderr over the tty file descriptor */ dup(fd); /* 0: stdin */ dup(fd); /* 1: stdout */ dup(fd); /* 2: stderr */ if (fd > 2) close(fd); privileges(INVOKE); #ifdef ultrix if ((fd = open("/dev/tty", O_RDONLY)) >= 0) { ioctl(fd, TIOCNOTTY, 0); close(fd); } else { # ifdef NO_SETSID pid = setpgrp(0, 0); # else pid = setsid(); # endif if (pid < 0) { D_TTYMODE(("%s: setsid() failed: %s, PID == %d\n", rs_name, strerror(errno), pid)); } } /* no error, we could run with no tty to begin with */ #else /* ultrix */ # ifdef TIOCSCTTY ioctl(0, TIOCSCTTY, 0); # endif /* set process group */ # if defined (_POSIX_VERSION) || defined (__svr4__) tcsetpgrp(0, pid); # elif defined (TIOCSPGRP) ioctl(0, TIOCSPGRP, &pid); # endif /* svr4 problems: reports no tty, no job control */ /* # if !defined (__svr4__) && defined (TIOCSPGRP) */ close(open(ttydev, O_RDWR, 0)); /* # endif */ #endif /* ultrix */ privileges(REVERT); return (fd); } /* debug_ttymode() */ #if DEBUG >= DEBUG_TTYMODE && defined(HAVE_TERMIOS_H) /* cpp token stringize doesn't work on all machines */ # define SHOW_TTY_FLAG(flag,name) do { \ if ((ttymode->c_iflag) & (flag)) { \ fprintf(stderr, "+%s ", name); \ } else { \ fprintf(stderr, "-%s ", name); \ } \ } while (0) # define SHOW_CONT_CHAR(entry, name) fprintf(stderr, "%s=%#3o ", name, ttymode->c_cc[entry]) static void debug_ttymode(ttymode_t * ttymode) { /* c_iflag bits */ fprintf(stderr, "Input flags: "); SHOW_TTY_FLAG(IGNBRK, "IGNBRK"); SHOW_TTY_FLAG(BRKINT, "BRKINT"); SHOW_TTY_FLAG(IGNPAR, "IGNPAR"); SHOW_TTY_FLAG(PARMRK, "PARMRK"); SHOW_TTY_FLAG(INPCK, "INPCK"); SHOW_TTY_FLAG(ISTRIP, "ISTRIP"); SHOW_TTY_FLAG(INLCR, "INLCR"); SHOW_TTY_FLAG(IGNCR, "IGNCR"); SHOW_TTY_FLAG(ICRNL, "ICRNL"); SHOW_TTY_FLAG(IXON, "IXON"); SHOW_TTY_FLAG(IXOFF, "IXOFF"); # ifdef IUCLC SHOW_TTY_FLAG(IUCLC, "IUCLC"); # endif # ifdef IXANY SHOW_TTY_FLAG(IXANY, "IXANY"); # endif # ifdef IMAXBEL SHOW_TTY_FLAG(IMAXBEL, "IMAXBEL"); # endif fprintf(stderr, "\n"); fprintf(stderr, "Control character mappings: "); SHOW_CONT_CHAR(VINTR, "VINTR"); SHOW_CONT_CHAR(VQUIT, "VQUIT"); SHOW_CONT_CHAR(VERASE, "VERASE"); SHOW_CONT_CHAR(VKILL, "VKILL"); SHOW_CONT_CHAR(VEOF, "VEOF"); SHOW_CONT_CHAR(VEOL, "VEOL"); # ifdef VEOL2 SHOW_CONT_CHAR(VEOL2, "VEOL2"); # endif # ifdef VSWTC SHOW_CONT_CHAR(VSWTC, "VSWTC"); # endif # ifdef VSWTCH SHOW_CONT_CHAR(VSWTCH, "VSWTCH"); # endif SHOW_CONT_CHAR(VSTART, "VSTART"); SHOW_CONT_CHAR(VSTOP, "VSTOP"); SHOW_CONT_CHAR(VSUSP, "VSUSP"); # ifdef VDSUSP SHOW_CONT_CHAR(VDSUSP, "VDSUSP"); # endif # ifdef VREPRINT SHOW_CONT_CHAR(VREPRINT, "VREPRINT"); # endif # ifdef VDISCRD SHOW_CONT_CHAR(VDISCRD, "VDISCRD"); # endif # ifdef VWERSE SHOW_CONT_CHAR(VWERSE, "VWERSE"); # endif # ifdef VLNEXT SHOW_CONT_CHAR(VLNEXT, "VLNEXT"); # endif fprintf(stderr, "\n\n"); } # undef SHOW_TTY_FLAG # undef SHOW_CONT_CHAR #endif /* DEBUG_TTYMODE */ /* get_ttymode() */ static void get_ttymode(ttymode_t * tio) { #ifdef HAVE_TERMIOS_H /* * standard System V termios interface */ if (GET_TERMIOS(0, tio) < 0) { /* return error - use system defaults */ tio->c_cc[VINTR] = CINTR; tio->c_cc[VQUIT] = CQUIT; tio->c_cc[VERASE] = CERASE; tio->c_cc[VKILL] = CKILL; tio->c_cc[VSTART] = CSTART; tio->c_cc[VSTOP] = CSTOP; tio->c_cc[VSUSP] = CSUSP; # ifdef VDSUSP tio->c_cc[VDSUSP] = CDSUSP; # endif # ifdef VREPRINT tio->c_cc[VREPRINT] = CRPRNT; # endif # ifdef VDISCRD tio->c_cc[VDISCRD] = CFLUSH; # endif # ifdef VWERSE tio->c_cc[VWERSE] = CWERASE; # endif # ifdef VLNEXT tio->c_cc[VLNEXT] = CLNEXT; # endif # ifdef VSTATUS tio->c_cc[VSTATUS] = CSTATUS; # endif } tio->c_cc[VEOF] = CEOF; tio->c_cc[VEOL] = VDISABLE; # ifdef VEOL2 tio->c_cc[VEOL2] = VDISABLE; # endif # ifdef VSWTC tio->c_cc[VSWTC] = VDISABLE; # endif # ifdef VSWTCH tio->c_cc[VSWTCH] = VDISABLE; # endif # if VMIN != VEOF tio->c_cc[VMIN] = 1; # endif # if VTIME != VEOL tio->c_cc[VTIME] = 0; # endif /* input modes */ tio->c_iflag = (BRKINT | IGNPAR | ICRNL | IXON # ifdef IMAXBEL | IMAXBEL # endif ); /* output modes */ tio->c_oflag = (OPOST | ONLCR); /* control modes */ tio->c_cflag = (CS8 | CREAD); /* line discipline modes */ tio->c_lflag = (ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK # if defined (ECHOCTL) && defined (ECHOKE) | ECHOCTL | ECHOKE # endif ); #if defined(FORCE_BACKSPACE) && 0 PrivMode(1, PrivMode_BackSpace); tio->c_cc[VERASE] = '\b'; /* force ^H for stty setting... */ SET_TERMIOS(0, tio); /* ...and make it stick -- casey */ #elif defined(FORCE_DELETE) && 0 PrivMode(0, PrivMode_BackSpace); tio->c_cc[VERASE] = 0x7f; /* force ^? for stty setting... */ SET_TERMIOS(0, tio); /* ...and make it stick -- casey */ #else PrivMode((tio->c_cc[VERASE] == '\b'), PrivMode_BackSpace); #endif #else /* HAVE_TERMIOS_H */ /* * sgtty interface */ /* get parameters -- gtty */ if (ioctl(0, TIOCGETP, &(tio->sg)) < 0) { tio->sg.sg_erase = CERASE; /* ^H */ tio->sg.sg_kill = CKILL; /* ^U */ } tio->sg.sg_flags = (CRMOD | ECHO | EVENP | ODDP); /* get special characters */ if (ioctl(0, TIOCGETC, &(tio->tc)) < 0) { tio->tc.t_intrc = CINTR; /* ^C */ tio->tc.t_quitc = CQUIT; /* ^\ */ tio->tc.t_startc = CSTART; /* ^Q */ tio->tc.t_stopc = CSTOP; /* ^S */ tio->tc.t_eofc = CEOF; /* ^D */ tio->tc.t_brkc = -1; } /* get local special chars */ if (ioctl(0, TIOCGLTC, &(tio->lc)) < 0) { tio->lc.t_suspc = CSUSP; /* ^Z */ tio->lc.t_dsuspc = CDSUSP; /* ^Y */ tio->lc.t_rprntc = CRPRNT; /* ^R */ tio->lc.t_flushc = CFLUSH; /* ^O */ tio->lc.t_werasc = CWERASE; /* ^W */ tio->lc.t_lnextc = CLNEXT; /* ^V */ } /* get line discipline */ ioctl(0, TIOCGETD, &(tio->line)); # ifdef NTTYDISC tio->line = NTTYDISC; # endif /* NTTYDISC */ tio->local = (LCRTBS | LCRTERA | LCTLECH | LPASS8 | LCRTKIL); # ifdef FORCE_BACKSPACE PrivMode(1, PrivMode_BackSpace); tio->sg.sg_erase = '\b'; SET_TTYMODE(0, tio); # elif defined (FORCE_DELETE) PrivMode(0, PrivMode_BackSpace); tio->sg.sg_erase = 0x7f; SET_TTYMODE(0, tio); # else PrivMode((tio->sg.sg_erase == '\b'), PrivMode_BackSpace); # endif #endif /* HAVE_TERMIOS_H */ } /* Xlocale */ XFontSet create_fontset(const char *font1, const char *font2) { XFontSet fontset = 0; char *fontname, **ml, *ds; int mc; const char fs_base[] = ",-misc-fixed-*-r-*-*-*-120-*-*-*-*-*-*"; ASSERT(font1 != NULL); #ifdef MULTI_CHARSET if (!font2) { font2 = "*"; } fontname = MALLOC(strlen(font1) + strlen(font2) + sizeof(fs_base) + 2); if (fontname) { strcpy(fontname, font1); strcat(fontname, fs_base); strcat(fontname, ","); strcat(fontname, font2); } #else fontname = MALLOC(strlen(font1) + sizeof(fs_base) + 1); if (fontname) { strcpy(fontname, font1); strcat(fontname, fs_base); } #endif if (fontname) { setlocale(LC_ALL, ""); fontset = XCreateFontSet(Xdisplay, fontname, &ml, &mc, &ds); FREE(fontname); if (mc) { XFreeStringList(ml); fontset = 0; } } return fontset; } #if defined(USE_XIM) || defined(MULTI_CHARSET) #ifdef USE_XIM static int xim_real_init(void); # ifdef USE_X11R6_XIM static void xim_destroy_cb(XIM xim, XPointer client_data, XPointer call_data); static void xim_instantiate_cb(Display *display, XPointer client_data, XPointer call_data); # endif #endif void init_locale(void) { char *locale = NULL; locale = setlocale(LC_CTYPE, ""); TermWin.fontset = (XFontSet) -1; if (locale == NULL) { print_error("Setting locale failed."); } else { #ifdef MULTI_CHARSET TermWin.fontset = create_fontset(rs_font[0], rs_mfont[0]); #else TermWin.fontset = create_fontset(rs_font[0], (const char *) NULL); #endif #ifdef USE_XIM # ifdef MULTI_CHARSET if (strcmp(locale, "C")) # endif { if (xim_real_init() != -1) { return; } # ifdef USE_X11R6_XIM XRegisterIMInstantiateCallback(Xdisplay, NULL, NULL, NULL, xim_instantiate_cb, NULL); # endif } #endif } } #endif /* USE_XIM || MULTI_CHARSET */ #ifdef USE_XIM static void xim_set_size(XRectangle * size) { size->x = TermWin.internalBorder; size->y = TermWin.internalBorder; size->width = Width2Pixel(TermWin.ncol); size->height = Height2Pixel(TermWin.nrow); } static void xim_set_color(unsigned long *fg, unsigned long *bg) { *fg = PixColors[fgColor]; *bg = PixColors[bgColor]; } static void xim_send_spot(void) { XPoint spot; XVaNestedList preedit_attr; XIMStyle input_style; if (Input_Context == NULL) { return; } else { XGetICValues(Input_Context, XNInputStyle, &input_style, NULL); if (!(input_style & XIMPreeditPosition)) { return; } } xim_get_position(&spot); preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); XSetICValues(Input_Context, XNPreeditAttributes, preedit_attr, NULL); XFree(preedit_attr); } static void xim_get_area(XRectangle *preedit_rect, XRectangle *status_rect, XRectangle *needed_rect) { preedit_rect->x = needed_rect->width + (scrollbar_visible() && !(Options & Opt_scrollBar_right) ? (scrollbar_trough_width()) : 0); preedit_rect->y = Height2Pixel(TermWin.nrow - 1); preedit_rect->width = Width2Pixel(TermWin.ncol + 1) - needed_rect->width + (!(Options & Opt_scrollBar_right) ? (scrollbar_trough_width()) : 0); preedit_rect->height = Height2Pixel(1); status_rect->x = (scrollbar_visible() && !(Options & Opt_scrollBar_right)) ? (scrollbar_trough_width()) : 0; status_rect->y = Height2Pixel(TermWin.nrow - 1); status_rect->width = needed_rect->width ? needed_rect->width : Width2Pixel(TermWin.ncol + 1); status_rect->height = Height2Pixel(1); } #ifdef USE_X11R6_XIM static void xim_destroy_cb(XIM xim, XPointer client_data, XPointer call_data) { Input_Context = NULL; XRegisterIMInstantiateCallback(Xdisplay, NULL, NULL, NULL, xim_instantiate_cb, NULL); } static void xim_instantiate_cb(Display *display, XPointer client_data, XPointer call_data) { xim_real_init(); if (Input_Context) { XUnregisterIMInstantiateCallback(Xdisplay, NULL, NULL, NULL, xim_instantiate_cb, NULL); } } #endif static int xim_real_init(void) { char *p, *s, buf[64], tmp[1024]; char *end, *next_s; XIM xim = NULL; XIMStyle input_style = 0; XIMStyles *xim_styles = NULL; int found; XPoint spot; XRectangle rect, status_rect, needed_rect; unsigned long fg, bg; XVaNestedList preedit_attr = NULL; XVaNestedList status_attr = NULL; REQUIRE(Input_Context == NULL); if (rs_inputMethod && *rs_inputMethod) { strncpy(tmp, rs_inputMethod, sizeof(tmp) - 1); for (s = tmp; *s; s = next_s + 1) { for (; *s && isspace(*s); s++); if (!*s) { break; } for (end = s; (*end && (*end != ',')); end++); for (next_s = end--; ((end >= s) && isspace(*end)); end--); *(end + 1) = '\0'; if (*s) { snprintf(buf, sizeof(buf), "@im=%s", s); if (((p = XSetLocaleModifiers(buf)) != NULL) && (*p) && ((xim = XOpenIM(Xdisplay, NULL, NULL, NULL)) != NULL)) { break; } } if (!*next_s) { break; } } } /* try with XMODIFIERS env. var. */ if (xim == NULL && (p = XSetLocaleModifiers("")) != NULL && *p) { xim = XOpenIM(Xdisplay, NULL, NULL, NULL); } #ifndef USE_X11R6_XIM /* try with no modifiers base */ if (xim == NULL && (p = XSetLocaleModifiers("@im=none")) != NULL && *p) { xim = XOpenIM(Xdisplay, NULL, NULL, NULL); } #endif if (xim == NULL) { xim = XOpenIM(Xdisplay, NULL, NULL, NULL); } if (xim == NULL) { return -1; } #ifdef USE_X11R6_XIM { XIMCallback destroy_cb; destroy_cb.callback = xim_destroy_cb; destroy_cb.client_data = NULL; if (XSetIMValues(xim, XNDestroyCallback, &destroy_cb, NULL)) { print_error("Could not set destroy callback to IM"); } } #endif if ((XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL)) || (!xim_styles)) { print_error("input method doesn't support any style"); XCloseIM(xim); return -1; } strncpy(tmp, (rs_preeditType ? rs_preeditType : "OverTheSpot,OffTheSpot,Root"), sizeof(tmp) - 1); for (found = 0, s = tmp; *s && !found; s = next_s + 1) { unsigned short i; for (; *s && isspace(*s); s++); if (!*s) { break; } for (end = s; (*end && (*end != ',')); end++); for (next_s = end--; ((end >= s) && isspace(*end)); end--); *(end + 1) = '\0'; if (!strcmp(s, "OverTheSpot")) { input_style = (XIMPreeditPosition | XIMStatusNothing); } else if (!strcmp(s, "OffTheSpot")) { input_style = (XIMPreeditArea | XIMStatusArea); } else if (!strcmp(s, "Root")) { input_style = (XIMPreeditNothing | XIMStatusNothing); } for (i = 0; i < xim_styles->count_styles; i++) { if (input_style == xim_styles->supported_styles[i]) { found = 1; break; } } } XFree(xim_styles); if (found == 0) { print_error("input method doesn't support my preedit type"); XCloseIM(xim); return -1; } if ((input_style != (XIMPreeditNothing | XIMStatusNothing)) && (input_style != (XIMPreeditArea | XIMStatusArea)) && (input_style != (XIMPreeditPosition | XIMStatusNothing))) { print_error("This program does not support the preedit type"); XCloseIM(xim); return -1; } if (input_style & XIMPreeditPosition) { xim_set_size(&rect); xim_get_position(&spot); xim_set_color(&fg, &bg); preedit_attr = XVaCreateNestedList(0, XNArea, &rect, XNSpotLocation, &spot, XNForeground, fg, XNBackground, bg, XNFontSet, TermWin.fontset, NULL); } else if (input_style & XIMPreeditArea) { xim_set_color(&fg, &bg); /* The necessary width of preedit area is unknown until create input context. */ needed_rect.width = 0; xim_get_area(&rect, &status_rect, &needed_rect); preedit_attr = XVaCreateNestedList(0, XNArea, &rect, XNForeground, fg, XNBackground, bg, XNFontSet, TermWin.fontset, NULL); status_attr = XVaCreateNestedList(0, XNArea, &status_rect, XNForeground, fg, XNBackground, bg, XNFontSet, TermWin.fontset, NULL); } Input_Context = XCreateIC(xim, XNInputStyle, input_style, XNClientWindow, TermWin.parent, XNFocusWindow, TermWin.parent, preedit_attr ? XNPreeditAttributes : NULL, preedit_attr, status_attr ? XNStatusAttributes : NULL, status_attr, NULL); XFree(preedit_attr); XFree(status_attr); if (Input_Context == NULL) { print_error("Failed to create input context"); XCloseIM(xim); return -1; } if (input_style & XIMPreeditArea) xim_set_status_position(); return 0; } void xim_set_status_position(void) { XIMStyle input_style; XRectangle preedit_rect, status_rect, *needed_rect; XVaNestedList preedit_attr, status_attr; REQUIRE(Input_Context != NULL); XGetICValues(Input_Context, XNInputStyle, &input_style, NULL); if (input_style & XIMPreeditArea) { /* Getting the necessary width of preedit area */ status_attr = XVaCreateNestedList(0, XNAreaNeeded, &needed_rect, NULL); XGetICValues(Input_Context, XNStatusAttributes, status_attr, NULL); XFree(status_attr); xim_get_area(&preedit_rect, &status_rect, needed_rect); preedit_attr = XVaCreateNestedList(0, XNArea, &preedit_rect, NULL); status_attr = XVaCreateNestedList(0, XNArea, &status_rect, NULL); XSetICValues(Input_Context, XNPreeditAttributes, preedit_attr, XNStatusAttributes, status_attr, NULL); XFree(preedit_attr); XFree(status_attr); } } void xim_set_fontset(void) { XIMStyle input_style; XVaNestedList preedit_attr; XVaNestedList status_attr; REQUIRE(Input_Context != NULL); XGetICValues(Input_Context, XNInputStyle, &input_style, NULL); if (input_style & (XIMPreeditArea | XIMPreeditPosition)) { preedit_attr = XVaCreateNestedList(0, XNFontSet, TermWin.fontset, NULL); status_attr = XVaCreateNestedList(0, XNFontSet, TermWin.fontset, NULL); XSetICValues(Input_Context, XNPreeditAttributes, preedit_attr, XNStatusAttributes, status_attr, NULL); XFree(preedit_attr); XFree(status_attr); } } #endif /* USE_XIM */ /* run_command() */ /* * Run the command in a subprocess and return a file descriptor for the * master end of the pseudo-teletype pair with the command talking to * the slave. */ int run_command(char *argv[]) { ttymode_t tio; int ptyfd; /* Save and then give up any super-user privileges */ privileges(IGNORE); ptyfd = get_pty(); if (ptyfd < 0) return (-1); /* store original tty status for restoration clean_exit() -- rgg 04/12/95 */ lstat(ttydev, &ttyfd_stat); D_CMD(("Original settings of %s are mode %o, uid %d, gid %d\n", ttydev, ttyfd_stat.st_mode, ttyfd_stat.st_uid, ttyfd_stat.st_gid)); /* install exit handler for cleanup */ #ifdef HAVE_ATEXIT atexit(clean_exit); #else # if defined (__sun__) on_exit(clean_exit, NULL); /* non-ANSI exit handler */ # else print_error("no atexit(), UTMP entries can't be cleaned"); # endif #endif /* * get tty settings before fork() * and make a reasonable guess at the value for BackSpace */ get_ttymode(&tio); /* add Backspace value */ SavedModes |= (PrivateModes & PrivMode_BackSpace); /* add value for scrollBar */ if (scrollbar_visible()) { PrivateModes |= PrivMode_scrollBar; SavedModes |= PrivMode_scrollBar; } #if DEBUG >= DEBUG_TTYMODE && defined(HAVE_TERMIOS_H) if (debug_level >= DEBUG_TTYMODE) { debug_ttymode(&tio); } #endif /* spin off the command interpreter */ signal(SIGHUP, Exit_signal); #ifndef __svr4__ signal(SIGINT, Exit_signal); #endif signal(SIGQUIT, SegvHandler); signal(SIGTERM, Exit_signal); signal(SIGCHLD, Child_signal); signal(SIGSEGV, SegvHandler); signal(SIGBUS, SegvHandler); signal(SIGABRT, SegvHandler); signal(SIGFPE, SegvHandler); signal(SIGILL, SegvHandler); signal(SIGSYS, SegvHandler); /* need to trap SIGURG for SVR4 (Unixware) rlogin */ /* signal (SIGURG, SIG_DFL); */ D_CMD(("run_command(): forking\n")); cmd_pid = fork(); D_CMD(("After fork(), cmd_pid == %d\n", cmd_pid)); if (cmd_pid < 0) { print_error("fork(): %s", strerror(errno)); return (-1); } if (cmd_pid == 0) { /* child */ /* signal (SIGHUP, Exit_signal); */ /* signal (SIGINT, Exit_signal); */ #ifdef HAVE_UNSETENV /* avoid passing old settings and confusing term size */ unsetenv("LINES"); unsetenv("COLUMNS"); /* avoid passing termcap since terminfo should be okay */ unsetenv("TERMCAP"); #endif /* HAVE_UNSETENV */ /* establish a controlling teletype for the new session */ get_tty(); /* initialize terminal attributes */ SET_TTYMODE(0, &tio); /* become virtual console, fail silently */ if (Options & Opt_console) { int fd = 1; privileges(INVOKE); #ifdef SRIOCSREDIR fd = open(CONSOLE, O_WRONLY); if (fd < 0 || ioctl(fd, SRIOCSREDIR, 0) < 0) { if (fd >= 0) close(fd); } #elif defined(TIOCCONS) ioctl(0, TIOCCONS, &fd); #endif /* SRIOCSREDIR */ privileges(REVERT); } tt_winsize(0); /* set window size */ /* Permanently revoke all privileges for the child process. Root shells for everyone are tres uncool.... ;^) -- mej */ #ifdef _HPUX_SOURCE setresuid(my_ruid, my_ruid, my_euid); setresgid(my_rgid, my_rgid, my_egid); #else /* No special treatment is needed for systems with saved uids/gids, because the exec*() calls reset the saved uid/gid to the effective uid/gid -- mej */ # ifndef __CYGWIN32__ setregid(my_rgid, my_rgid); setreuid(my_ruid, my_ruid); # endif /* __CYGWIN32__ */ #endif /* _HPUX_SOURCE */ D_UTMP(("Child process reset\n")); my_euid = my_ruid; my_egid = my_rgid; /* reset signals and spin off the command interpreter */ signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGBUS, SIG_DFL); signal(SIGABRT, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGILL, SIG_DFL); signal(SIGSYS, SIG_DFL); signal(SIGALRM, SIG_DFL); /* * mimick login's behavior by disabling the job control signals * a shell that wants them can turn them back on */ #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); #endif /* SIGTSTP */ /* command interpreter path */ D_CMD(("[%d] About to spawn shell\n", getpid())); if (chdir(initial_dir)) { print_warning("Unable to chdir to \"%s\" -- %s\n", initial_dir, strerror(errno)); } if (argv != NULL) { #if DEBUG >= DEBUG_CMD if (debug_level >= DEBUG_CMD) { int i; for (i = 0; argv[i]; i++) { DPRINTF1(("argv[%d] = \"%s\"\n", i, argv[i])); } } #endif execvp(argv[0], argv); print_error("execvp() failed, cannot execute \"%s\": %s", argv[0], strerror(errno)); } else { const char *argv0, *shell; if ((shell = getenv("SHELL")) == NULL || *shell == '\0') shell = "/bin/sh"; argv0 = my_basename(shell); if (Options & Opt_loginShell) { char *p = MALLOC((strlen(argv0) + 2) * sizeof(char)); p[0] = '-'; strcpy(&p[1], argv0); argv0 = p; } execlp(shell, argv0, NULL); print_error("execlp() failed, cannot execute \"%s\": %s", shell, strerror(errno)); } sleep(3); /* Sleep to make sure fork() returns in the parent, and so user can read error message */ exit(EXIT_FAILURE); } #ifdef UTMP_SUPPORT privileges(RESTORE); if (Options & Opt_utmpLogging) makeutent(ttydev, display_name); /* stamp /etc/utmp */ privileges(IGNORE); #endif #if 0 D_THREADS(("run_command(): pthread_join(resize_sub_thr)\n")); pthread_join(resize_sub_thr, NULL); #endif D_CMD(("run_command() returning\n")); return (ptyfd); } /* init_command() */ void init_command(char *argv[]) { /* Initialize the command connection. This should be called after the X server connection is established. */ /* Enable delete window protocol */ wmDeleteWindow = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", False); XSetWMProtocols(Xdisplay, TermWin.parent, &wmDeleteWindow, 1); #ifdef OFFIX_DND /* Enable OffiX Dnd (drag 'n' drop) protocol */ DndProtocol = XInternAtom(Xdisplay, "DndProtocol", False); DndSelection = XInternAtom(Xdisplay, "DndSelection", False); #endif /* OFFIX_DND */ init_locale(); /* get number of available file descriptors */ #ifdef _POSIX_VERSION num_fds = sysconf(_SC_OPEN_MAX); #else num_fds = getdtablesize(); #endif #ifdef META8_OPTION meta_char = (Options & Opt_meta8 ? 0x80 : 033); #endif #ifdef GREEK_SUPPORT greek_init(); #endif Xfd = XConnectionNumber(Xdisplay); D_CMD(("Xfd = %d\n", Xfd)); cmdbuf_ptr = cmdbuf_endp = cmdbuf_base; if ((cmd_fd = run_command(argv)) < 0) { print_error("aborting"); exit(EXIT_FAILURE); } } /* window resizing */ /* * Tell the teletype handler what size the window is. * Called after a window size change. */ void tt_winsize(int fd) { struct winsize ws; if (fd < 0) return; ws.ws_col = (unsigned short) TermWin.ncol; ws.ws_row = (unsigned short) TermWin.nrow; #ifndef __CYGWIN32__ ws.ws_xpixel = ws.ws_ypixel = 0; #endif ioctl(fd, TIOCSWINSZ, &ws); } void tt_resize(void) { tt_winsize(cmd_fd); } /* attempt to `write' COUNT to the input buffer */ unsigned int cmd_write(const unsigned char *str, unsigned int count) { int n; n = (count - (cmdbuf_ptr - cmdbuf_base)); /* need to insert more chars that space available in the front */ if (n > 0) { /* try and get more space from the end */ unsigned char *src, *dst; dst = (cmdbuf_base + sizeof(cmdbuf_base) - 1); /* max pointer */ if ((cmdbuf_ptr + n) > dst) n = (dst - cmdbuf_ptr); /* max # chars to insert */ if ((cmdbuf_endp + n) > dst) cmdbuf_endp = (dst - n); /* truncate end if needed */ /* equiv: memmove ((cmdbuf_ptr+n), cmdbuf_ptr, n); */ src = cmdbuf_endp; dst = src + n; /* FIXME: anything special to avoid possible pointer wrap? */ while (src >= cmdbuf_ptr) *dst-- = *src--; /* done */ cmdbuf_ptr += n; cmdbuf_endp += n; } while (count-- && cmdbuf_ptr > cmdbuf_base) { /* sneak one in */ cmdbuf_ptr--; *cmdbuf_ptr = str[count]; } return (0); } #ifdef BACKGROUND_CYCLING_SUPPORT RETSIGTYPE check_pixmap_change(int sig) { static time_t last_update = 0; time_t now; static unsigned long image_idx = 0; void (*old_handler) (int); static unsigned char in_cpc = 0; if (in_cpc) CPC_RETURN(0); in_cpc = 1; D_PIXMAP(("check_pixmap_change(%d): rs_anim_delay == %lu seconds, last_update == %lu\n", sig, rs_anim_delay, last_update)); if (!rs_anim_delay) CPC_RETURN(0); if (last_update == 0) { last_update = time(NULL); old_handler = signal(SIGALRM, check_pixmap_change); alarm(rs_anim_delay); in_cpc = 0; CPC_RETURN(0); } now = time(NULL); D_PIXMAP(("now %lu >= %lu (last_update %lu + rs_anim_delay %lu) ?\n", now, last_update + rs_anim_delay, last_update, rs_anim_delay)); if (now >= last_update + rs_anim_delay || 1) { D_PIXMAP(("Time to update pixmap. now == %lu\n", now)); Imlib_destroy_image(imlib_id, images[image_bg].current->iml->im); images[image_bg].current->iml->im = NULL; xterm_seq(XTerm_Pixmap, rs_anim_pixmaps[image_idx++]); last_update = now; old_handler = signal(SIGALRM, check_pixmap_change); alarm(rs_anim_delay); if (rs_anim_pixmaps[image_idx] == NULL) { image_idx = 0; } } in_cpc = 0; if (old_handler) { CPC_RETURN((*old_handler) (sig)); } else { CPC_RETURN(sig); } } #endif /* BACKGROUND_CYCLING_SUPPORT */ /* cmd_getc() - Return next input character */ /* * Return the next input character after first passing any keyboard input * to the command. */ unsigned char cmd_getc(void) { #define TIMEOUT_USEC 2500 static short refreshed = 0; fd_set readfds; int retval; struct timeval value, *delay; /* If there has been a lot of new lines, then update the screen * What the heck I'll cheat and only refresh less than every page-full. * the number of pages between refreshes is refresh_limit, which * is incremented here because we must be doing flat-out scrolling. * * refreshing should be correct for small scrolls, because of the * time-out */ if (refresh_count >= (refresh_limit * (TermWin.nrow - 1))) { if (refresh_limit < REFRESH_PERIOD) refresh_limit++; refresh_count = 0; refreshed = 1; D_CMD(("cmd_getc(): scr_refresh() #1\n")); #ifdef PROFILE P_CALL(scr_refresh(refresh_type), "cmd_getc()->scr_refresh()"); #else scr_refresh(refresh_type); #endif } /* characters already read in */ if (CHARS_READ()) { RETURN_CHAR(); } for (;;) { v_doPending(); while (XPending(Xdisplay)) { /* process pending X events */ XEvent ev; refreshed = 0; XNextEvent(Xdisplay, &ev); #ifdef USE_XIM if (!XFilterEvent(&ev, ev.xkey.window)) { event_dispatch(&ev); } #else event_dispatch(&ev); #endif /* in case button actions pushed chars to cmdbuf */ if (CHARS_READ()) { RETURN_CHAR(); } } #ifdef SCROLLBAR_BUTTON_CONTINUAL_SCROLLING if (scrollbar_isUp()) { if (!scroll_arrow_delay-- && scr_page(UP, 1)) { scroll_arrow_delay = SCROLLBAR_CONTINUOUS_DELAY; refreshed = 0; } } else if (scrollbar_isDn()) { if (!scroll_arrow_delay-- && scr_page(DN, 1)) { scroll_arrow_delay = SCROLLBAR_CONTINUOUS_DELAY; refreshed = 0; } } #endif /* SCROLLBAR_BUTTON_CONTINUAL_SCROLLING */ /* Nothing to do! */ FD_ZERO(&readfds); FD_SET(cmd_fd, &readfds); FD_SET(Xfd, &readfds); value.tv_usec = TIMEOUT_USEC; value.tv_sec = 0; if (refreshed #ifdef SCROLLBAR_BUTTON_CONTINUAL_SCROLLING && !(scrollbar_isUpDn()) #endif ) { delay = NULL; } else { delay = &value; } retval = select(num_fds, &readfds, NULL, NULL, delay); /* See if we can read from the application */ if (FD_ISSET(cmd_fd, &readfds)) { /* unsigned int count = BUFSIZ; */ register unsigned int count = CMD_BUF_SIZE; cmdbuf_ptr = cmdbuf_endp = cmdbuf_base; /* while (count > sizeof(cmdbuf_base) / 2) */ while (count) { /* int n = read(cmd_fd, cmdbuf_endp, count); */ register int n = read(cmd_fd, cmdbuf_endp, count); if (n <= 0) break; cmdbuf_endp += n; count -= n; } /* some characters read in */ if (CHARS_BUFFERED()) { RETURN_CHAR(); } } /* select statement timed out - better update the screen */ if (retval == 0) { refresh_count = 0; refresh_limit = 1; if (!refreshed) { refreshed = 1; D_CMD(("cmd_getc(): scr_refresh() #2\n")); scr_refresh(refresh_type); if (scrollbar_visible()) scrollbar_show(1); #ifdef USE_XIM xim_send_spot(); #endif } } } D_CMD(("cmd_getc() returning\n")); return (0); } /* tt_write(), tt_printf() - output to command */ /* * Send count characters directly to the command */ void tt_write(const unsigned char *buf, unsigned int count) { v_writeBig(cmd_fd, (char *) buf, count); #if 0 /* Fixes the bug that hung Eterm when pasting a lot of stuff */ while (count > 0) { int n = write(cmd_fd, buf, count); if (n > 0) { count -= n; buf += n; } } #endif } /* * Send printf() formatted output to the command. * Only use for small ammounts of data. */ void tt_printf(const unsigned char *fmt,...) { static unsigned char buf[256]; va_list arg_ptr; va_start(arg_ptr, fmt); vsprintf(buf, fmt, arg_ptr); va_end(arg_ptr); tt_write(buf, strlen(buf)); } #ifndef USE_POSIX_THREADS /* Read and process output from the application */ void main_loop(void) { /* int ch; */ register int ch; D_CMD(("[%d] main_loop() called\n", getpid())); #ifdef BACKGROUND_CYCLING_SUPPORT if (rs_anim_delay) { check_pixmap_change(0); } #endif do { while ((ch = cmd_getc()) == 0); /* wait for something */ if (ch >= ' ' || ch == '\t' || ch == '\n' || ch == '\r') { /* Read a text string from the input buffer */ int nlines = 0; /* unsigned char * str; */ register unsigned char *str; /* * point to the start of the string, * decrement first since already did get_com_char () */ str = --cmdbuf_ptr; while (cmdbuf_ptr < cmdbuf_endp) { ch = *cmdbuf_ptr++; if (ch >= ' ' || ch == '\t' || ch == '\r') { /* nothing */ } else if (ch == '\n') { nlines++; if (++refresh_count >= (refresh_limit * (TermWin.nrow - 1))) break; } else { /* unprintable */ cmdbuf_ptr--; break; } } D_SCREEN(("Adding lines, str == 0x%08x, cmdbuf_ptr == 0x%08x, cmdbuf_endp == 0x%08x\n", str, cmdbuf_ptr, cmdbuf_endp)); D_SCREEN(("Command buffer base == 0x%08x, length %lu, end at 0x%08x\n", cmdbuf_base, CMD_BUF_SIZE, cmdbuf_base + CMD_BUF_SIZE - 1)); scr_add_lines(str, nlines, (cmdbuf_ptr - str)); } else { switch (ch) { # ifdef NO_VT100_ANS case 005: break; # else case 005: tt_printf(VT100_ANS); break; /* terminal Status */ # endif case 007: scr_bell(); break; /* bell */ case '\b': scr_backspace(); break; /* backspace */ case 013: case 014: scr_index(UP); break; /* vertical tab, form feed */ case 016: scr_charset_choose(1); break; /* shift out - acs */ case 017: scr_charset_choose(0); break; /* shift in - acs */ case 033: process_escape_seq(); break; } } } while (ch != EOF); } #endif /* output a burst of any pending data from a paste... */ int v_doPending(void) { if (v_bufstr >= v_bufptr) return (0); v_writeBig(cmd_fd, NULL, 0); return (1); } /* Write data to the pty as typed by the user, pasted with the mouse, * or generated by us in response to a query ESC sequence. * Code stolen from xterm */ void v_writeBig(int f, char *d, int len) { int written; int c = len; if (v_bufstr == NULL && len > 0) { v_buffer = malloc(len); v_bufstr = v_buffer; v_bufptr = v_buffer; v_bufend = v_buffer + len; } /* * Append to the block we already have. * Always doing this simplifies the code, and * isn't too bad, either. If this is a short * block, it isn't too expensive, and if this is * a long block, we won't be able to write it all * anyway. */ if (len > 0) { if (v_bufend < v_bufptr + len) { /* we've run out of room */ if (v_bufstr != v_buffer) { /* there is unused space, move everything down */ /* possibly overlapping bcopy here */ /* bcopy(v_bufstr, v_buffer, v_bufptr - v_bufstr); */ memcpy(v_buffer, v_bufstr, v_bufptr - v_bufstr); v_bufptr -= v_bufstr - v_buffer; v_bufstr = v_buffer; } if (v_bufend < v_bufptr + len) { /* still won't fit: get more space */ /* Don't use XtRealloc because an error is not fatal. */ int size = v_bufptr - v_buffer; /* save across realloc */ v_buffer = realloc(v_buffer, size + len); if (v_buffer) { v_bufstr = v_buffer; v_bufptr = v_buffer + size; v_bufend = v_bufptr + len; } else { /* no memory: ignore entire write request */ print_error("cannot allocate buffer space\n"); v_buffer = v_bufstr; /* restore clobbered pointer */ c = 0; } } } if (v_bufend >= v_bufptr + len) { /* new stuff will fit */ memcpy(v_bufptr, d, len); /* bcopy(d, v_bufptr, len); */ v_bufptr += len; } } /* * Write out as much of the buffer as we can. * Be careful not to overflow the pty's input silo. * We are conservative here and only write * a small amount at a time. * * If we can't push all the data into the pty yet, we expect write * to return a non-negative number less than the length requested * (if some data written) or -1 and set errno to EAGAIN, * EWOULDBLOCK, or EINTR (if no data written). * * (Not all systems do this, sigh, so the code is actually * a little more forgiving.) */ if (v_bufptr > v_bufstr) { written = write(f, v_bufstr, v_bufptr - v_bufstr <= MAX_PTY_WRITE ? v_bufptr - v_bufstr : MAX_PTY_WRITE); if (written < 0) { written = 0; } D_TTY(("v_writeBig(): Wrote %d characters\n", written)); v_bufstr += written; if (v_bufstr >= v_bufptr) /* we wrote it all */ v_bufstr = v_bufptr = v_buffer; } /* * If we have lots of unused memory allocated, return it */ if (v_bufend - v_bufptr > 1024) { /* arbitrary hysteresis */ /* save pointers across realloc */ int start = v_bufstr - v_buffer; int size = v_bufptr - v_buffer; int allocsize = size ? size : 1; v_buffer = realloc(v_buffer, allocsize); if (v_buffer) { v_bufstr = v_buffer + start; v_bufptr = v_buffer + size; v_bufend = v_buffer + allocsize; } else { /* should we print a warning if couldn't return memory? */ v_buffer = v_bufstr - start; /* restore clobbered pointer */ } } }