diff options
author | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2006-11-26 11:11:55 +0000 |
---|---|---|
committer | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2006-11-26 11:11:55 +0000 |
commit | 145a665014b2aa230b81b582689c8ec17ef9968e (patch) | |
tree | 5ea1019527fe16cc21b9702371c72cc7f8c8090c /app/xterm/misc.c | |
parent | 95c2d1cbda23a41cdf6e63520c7f0b825e63dd5b (diff) |
Importing xterm 216
Diffstat (limited to 'app/xterm/misc.c')
-rw-r--r-- | app/xterm/misc.c | 3351 |
1 files changed, 3351 insertions, 0 deletions
diff --git a/app/xterm/misc.c b/app/xterm/misc.c new file mode 100644 index 000000000..8589fadfa --- /dev/null +++ b/app/xterm/misc.c @@ -0,0 +1,3351 @@ +/* $XTermId: misc.c,v 1.314 2006/08/03 23:54:32 tom Exp $ */ + +/* $XFree86: xc/programs/xterm/misc.c,v 3.107 2006/06/19 00:36:51 dickey Exp $ */ + +/* + * + * Copyright 1999-2005,2006 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + * + * + * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Digital Equipment + * Corporation not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * + * + * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include <version.h> +#include <xterm.h> + +#include <sys/stat.h> +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <pwd.h> +#include <sys/wait.h> + +#include <X11/Xatom.h> +#include <X11/cursorfont.h> + +#include <X11/Xmu/Error.h> +#include <X11/Xmu/SysUtil.h> +#include <X11/Xmu/WinUtil.h> +#include <X11/Xmu/Xmu.h> +#if HAVE_X11_SUNKEYSYM_H +#include <X11/Sunkeysym.h> +#endif + +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif + +#include <xutf8.h> + +#include <data.h> +#include <error.h> +#include <menu.h> +#include <fontutils.h> +#include <xcharmouse.h> +#include <xstrings.h> +#include <VTparse.h> + +#include <assert.h> + +#if (XtSpecificationRelease < 6) +#ifndef X_GETTIMEOFDAY +#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0) +#endif +#endif + +#ifdef VMS +#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT" +#ifdef ALLOWLOGFILEEXEC +#undef ALLOWLOGFILEEXEC +#endif +#endif /* VMS */ + +#if OPT_TEK4014 +#define OUR_EVENT(event,Type) \ + (event.type == Type && \ + (event.xcrossing.window == XtWindow(XtParent(term)) || \ + (tekWidget && \ + event.xcrossing.window == XtWindow(XtParent(tekWidget))))) +#else +#define OUR_EVENT(event,Type) \ + (event.type == Type && \ + (event.xcrossing.window == XtWindow(XtParent(term)))) +#endif + +static Bool ChangeColorsRequest(XtermWidget xw, int start, char + *names, int final); +static void DoSpecialEnterNotify(XEnterWindowEvent * ev); +static void DoSpecialLeaveNotify(XEnterWindowEvent * ev); +static void selectwindow(TScreen * screen, int flag); +static void unselectwindow(TScreen * screen, int flag); +static void Sleep(int msec); + +void +do_xevents(void) +{ + TScreen *screen = &term->screen; + + if (XtAppPending(app_con) + || +#if defined(VMS) || defined(__VMS) + screen->display->qlen > 0 +#else + GetBytesAvailable(ConnectionNumber(screen->display)) > 0 +#endif + ) + xevents(); +} + +void +xevents(void) +{ + XEvent event; + XtInputMask input_mask; + TScreen *screen = &term->screen; + + if (need_cleanup) + Cleanup(0); + + if (screen->scroll_amt) + FlushScroll(term); + /* + * process timeouts, relying on the fact that XtAppProcessEvent + * will process the timeout and return without blockng on the + * XEvent queue. Other sources i.e. the pty are handled elsewhere + * with select(). + */ + while ((input_mask = XtAppPending(app_con)) & XtIMTimer) + XtAppProcessEvent(app_con, XtIMTimer); +#if OPT_SESSION_MGT + /* + * Session management events are alternative input events. Deal with + * them in the same way. + */ + while ((input_mask = XtAppPending(app_con)) & XtIMAlternateInput) + XtAppProcessEvent(app_con, XtIMAlternateInput); +#endif + + /* + * If there's no XEvents, don't wait around... + */ + if ((input_mask & XtIMXEvent) != XtIMXEvent) + return; + do { + /* + * This check makes xterm hang when in mouse hilite tracking mode. + * We simply ignore all events except for those not passed down to + * this function, e.g., those handled in in_put(). + */ + if (waitingForTrackInfo) { + Sleep(10); + return; + } + XtAppNextEvent(app_con, &event); + /* + * Hack to get around problems with the toolkit throwing away + * eventing during the exclusive grab of the menu popup. By + * looking at the event ourselves we make sure that we can + * do the right thing. + */ + if (OUR_EVENT(event, EnterNotify)) + DoSpecialEnterNotify(&event.xcrossing); + else if (OUR_EVENT(event, LeaveNotify)) + DoSpecialLeaveNotify(&event.xcrossing); + else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE +#if OPT_DEC_LOCATOR + || screen->send_mouse_pos == DEC_LOCATOR +#endif /* OPT_DEC_LOCATOR */ + ) + && event.xany.type == MotionNotify + && event.xcrossing.window == XtWindow(term)) { + SendMousePosition(term, &event); + continue; + } + + if (!event.xany.send_event || + screen->allowSendEvents || + ((event.xany.type != KeyPress) && + (event.xany.type != KeyRelease) && + (event.xany.type != ButtonPress) && + (event.xany.type != ButtonRelease))) + XtDispatchEvent(&event); + } while ((input_mask = XtAppPending(app_con)) & XtIMXEvent); +} + +Cursor +make_colored_cursor(unsigned cursorindex, /* index into font */ + unsigned long fg, /* pixel value */ + unsigned long bg) /* pixel value */ +{ + TScreen *screen = &term->screen; + Cursor c; + Display *dpy = screen->display; + + c = XCreateFontCursor(dpy, cursorindex); + if (c == (Cursor) 0) + return (c); + + recolor_cursor(screen, c, fg, bg); + return (c); +} + +/* ARGSUSED */ +void +HandleKeyPressed(Widget w GCC_UNUSED, + XEvent * event, + String * params GCC_UNUSED, + Cardinal *nparams GCC_UNUSED) +{ + TRACE(("Handle 7bit-key\n")); +#ifdef ACTIVEWINDOWINPUTONLY + if (w == CURRENT_EMU(&(term->screen))) +#endif + Input(term, &event->xkey, False); +} + +/* ARGSUSED */ +void +HandleEightBitKeyPressed(Widget w GCC_UNUSED, + XEvent * event, + String * params GCC_UNUSED, + Cardinal *nparams GCC_UNUSED) +{ + TRACE(("Handle 8bit-key\n")); +#ifdef ACTIVEWINDOWINPUTONLY + if (w == CURRENT_EMU(&(term->screen))) +#endif + Input(term, &event->xkey, True); +} + +/* ARGSUSED */ +void +HandleStringEvent(Widget w GCC_UNUSED, + XEvent * event GCC_UNUSED, + String * params, + Cardinal *nparams) +{ +#ifdef ACTIVEWINDOWINPUTONLY + if (w != CURRENT_EMU(&(term->screen))) + return; +#endif + + if (*nparams != 1) + return; + + if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') { + Char c, *p; + Char hexval[2]; + hexval[0] = hexval[1] = 0; + for (p = (Char *) (*params + 2); (c = *p); p++) { + hexval[0] *= 16; + if (isupper(c)) + c = tolower(c); + if (c >= '0' && c <= '9') + hexval[0] += c - '0'; + else if (c >= 'a' && c <= 'f') + hexval[0] += c - 'a' + 10; + else + break; + } + if (c == '\0') + StringInput(term, hexval, 1); + } else { + StringInput(term, (Char *) * params, strlen(*params)); + } +} + +/* + * Rather than sending characters to the host, put them directly into our + * input queue. That lets a user have access to any of the control sequences + * for a key binding. This is the equivalent of local function key support. + * + * NOTE: This code does not support the hexadecimal kludge used in + * HandleStringEvent because it prevents us from sending an arbitrary string + * (but it appears in a lot of examples - so we are stuck with it). The + * standard string converter does recognize "\" for newline ("\n") and for + * octal constants (e.g., "\007" for BEL). So we assume the user can make do + * without a specialized converter. (Don't try to use \000, though). + */ +/* ARGSUSED */ +void +HandleInterpret(Widget w GCC_UNUSED, + XEvent * event GCC_UNUSED, + String * params, + Cardinal *param_count) +{ + if (*param_count == 1) { + char *value = params[0]; + int need = strlen(value); + int used = VTbuffer->next - VTbuffer->buffer; + int have = VTbuffer->last - VTbuffer->buffer; + + if (have - used + need < BUF_SIZE) { + + fillPtyData(&term->screen, VTbuffer, value, (int) strlen(value)); + + TRACE(("Interpret %s\n", value)); + VTbuffer->update++; + } + } +} + +static void +DoSpecialEnterNotify(XEnterWindowEvent * ev) +{ + TScreen *screen = &term->screen; + + TRACE(("DoSpecialEnterNotify(%d)\n", screen->select)); +#ifdef ACTIVEWINDOWINPUTONLY + if (ev->window == XtWindow(XtParent(CURRENT_EMU(screen)))) +#endif + if (((ev->detail) != NotifyInferior) && + ev->focus && + !(screen->select & FOCUS)) + selectwindow(screen, INWINDOW); +} + +/*ARGSUSED*/ +void +HandleEnterWindow(Widget w GCC_UNUSED, + XtPointer eventdata GCC_UNUSED, + XEvent * event GCC_UNUSED, + Boolean * cont GCC_UNUSED) +{ + /* NOP since we handled it above */ + TRACE(("HandleEnterWindow ignored\n")); +} + +static void +DoSpecialLeaveNotify(XEnterWindowEvent * ev) +{ + TScreen *screen = &term->screen; + + TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select)); +#ifdef ACTIVEWINDOWINPUTONLY + if (ev->window == XtWindow(XtParent(CURRENT_EMU(screen)))) +#endif + if (((ev->detail) != NotifyInferior) && + ev->focus && + !(screen->select & FOCUS)) + unselectwindow(screen, INWINDOW); +} + +/*ARGSUSED*/ +void +HandleLeaveWindow(Widget w GCC_UNUSED, + XtPointer eventdata GCC_UNUSED, + XEvent * event GCC_UNUSED, + Boolean * cont GCC_UNUSED) +{ + /* NOP since we handled it above */ + TRACE(("HandleLeaveWindow ignored\n")); +} + +/*ARGSUSED*/ +void +HandleFocusChange(Widget w GCC_UNUSED, + XtPointer eventdata GCC_UNUSED, + XEvent * ev, + Boolean * cont GCC_UNUSED) +{ + XFocusChangeEvent *event = (XFocusChangeEvent *) ev; + TScreen *screen = &term->screen; + + TRACE(("HandleFocusChange type=%d, mode=%d, detail=%d\n", + event->type, + event->mode, + event->detail)); + + if (event->type == FocusIn) { + /* + * NotifyNonlinear only happens (on FocusIn) if the pointer was not in + * one of our windows. Use this to reset a case where one xterm is + * partly obscuring another, and X gets (us) confused about whether the + * pointer was in the window. In particular, this can happen if the + * user is resizing the obscuring window, causing some events to not be + * delivered to the obscured window. + */ + if (event->detail == NotifyNonlinear + && (screen->select & INWINDOW) != 0) { + unselectwindow(screen, INWINDOW); + } + selectwindow(screen, + ((event->detail == NotifyPointer) + ? INWINDOW + : FOCUS)); + } else { + /* + * XGrabKeyboard() will generate FocusOut/NotifyGrab event that we want + * to ignore. + */ + if (event->mode != NotifyGrab) { + unselectwindow(screen, + ((event->detail == NotifyPointer) + ? INWINDOW + : FOCUS)); + } + if (screen->grabbedKbd && (event->mode == NotifyUngrab)) { + Bell(XkbBI_Info, 100); + ReverseVideo(term); + screen->grabbedKbd = False; + update_securekbd(); + } + } +} + +static void +selectwindow(TScreen * screen, int flag) +{ + TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag)); + +#if OPT_TEK4014 + if (screen->TekEmu) { + if (!Ttoggled) + TCursorToggle(TOGGLE); + screen->select |= flag; + if (!Ttoggled) + TCursorToggle(TOGGLE); + return; + } else +#endif + { + if (screen->xic) + XSetICFocus(screen->xic); + + if (screen->cursor_state && CursorMoved(screen)) + HideCursor(); + screen->select |= flag; + if (screen->cursor_state) + ShowCursor(); + return; + } +} + +static void +unselectwindow(TScreen * screen, int flag) +{ + TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag)); + + if (screen->always_highlight) + return; + +#if OPT_TEK4014 + if (screen->TekEmu) { + if (!Ttoggled) + TCursorToggle(TOGGLE); + screen->select &= ~flag; + if (!Ttoggled) + TCursorToggle(TOGGLE); + } else +#endif + { + if (screen->xic) + XUnsetICFocus(screen->xic); + screen->select &= ~flag; + if (screen->cursor_state && CursorMoved(screen)) + HideCursor(); + if (screen->cursor_state) + ShowCursor(); + } +} + +static long lastBellTime; /* in milliseconds */ + +void +Bell(Atom which GCC_UNUSED, int percent) +{ + TScreen *screen = &term->screen; + struct timeval curtime; + long now_msecs; + + TRACE(("BELL %ld %d%%\n", (long) which, percent)); + if (!XtIsRealized((Widget) term)) { + return; + } + + /* has enough time gone by that we are allowed to ring + the bell again? */ + if (screen->bellSuppressTime) { + if (screen->bellInProgress) { + do_xevents(); + if (screen->bellInProgress) { /* even after new events? */ + return; + } + } + X_GETTIMEOFDAY(&curtime); + now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000; + if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 && + now_msecs - lastBellTime < screen->bellSuppressTime) { + return; + } + lastBellTime = now_msecs; + } + + if (screen->visualbell) { + VisualBell(); + } else { +#if defined(HAVE_XKB_BELL_EXT) + XkbBell(screen->display, VShellWindow, percent, which); +#else + XBell(screen->display, percent); +#endif + } + + if (screen->poponbell) + XRaiseWindow(screen->display, VShellWindow); + + if (screen->bellSuppressTime) { + /* now we change a property and wait for the notify event to come + back. If the server is suspending operations while the bell + is being emitted (problematic for audio bell), this lets us + know when the previous bell has finished */ + Widget w = CURRENT_EMU(screen); + XChangeProperty(XtDisplay(w), XtWindow(w), + XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0); + screen->bellInProgress = True; + } +} + +#define VB_DELAY screen->visualBellDelay + +static void +flashWindow(TScreen * screen, Window window, GC visualGC, unsigned width, unsigned height) +{ + XFillRectangle(screen->display, window, visualGC, 0, 0, width, height); + XFlush(screen->display); + Sleep(VB_DELAY); + XFillRectangle(screen->display, window, visualGC, 0, 0, width, height); +} + +void +VisualBell(void) +{ + TScreen *screen = &term->screen; + + if (VB_DELAY > 0) { + Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^ + T_COLOR(screen, TEXT_BG)); + XGCValues gcval; + GC visualGC; + + gcval.function = GXxor; + gcval.foreground = xorPixel; + visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval); +#if OPT_TEK4014 + if (screen->TekEmu) { + flashWindow(screen, TWindow(screen), visualGC, + TFullWidth(screen), + TFullHeight(screen)); + } else +#endif + { + flashWindow(screen, VWindow(screen), visualGC, + FullWidth(screen), + FullHeight(screen)); + } + XtReleaseGC((Widget) term, visualGC); + } +} + +/* ARGSUSED */ +void +HandleBellPropertyChange(Widget w GCC_UNUSED, + XtPointer data GCC_UNUSED, + XEvent * ev, + Boolean * more GCC_UNUSED) +{ + TScreen *screen = &term->screen; + + if (ev->xproperty.atom == XA_NOTICE) { + screen->bellInProgress = False; + } +} + +Window +WMFrameWindow(XtermWidget termw) +{ + Window win_root, win_current, *children; + Window win_parent = 0; + unsigned int nchildren; + + win_current = XtWindow(termw); + + /* find the parent which is child of root */ + do { + if (win_parent) + win_current = win_parent; + XQueryTree((&termw->screen)->display, + win_current, + &win_root, + &win_parent, + &children, + &nchildren); + XFree(children); + } while (win_root != win_parent); + + return win_current; +} + +#if OPT_DABBREV +/* + * The following code implements `dynamic abbreviation' expansion a la + * Emacs. It looks in the preceding visible screen and its scrollback + * to find expansions of a typed word. It compares consecutive + * expansions and ignores one of them if they are identical. + * (Tomasz J. Cholewo, t.cholewo@ieee.org) + */ + +#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0') +#define MAXWLEN 1024 /* maximum word length as in tcsh */ + +static int +dabbrev_prev_char(int *xp, int *yp, TScreen * screen) +{ + Char *linep; + + while (*yp >= 0) { + linep = BUF_CHARS(screen->allbuf, *yp); + if (--*xp >= 0) + return linep[*xp]; + if (--*yp < 0) /* go to previous line */ + break; + *xp = MaxCols(screen); + if (!((long) BUF_FLAGS(screen->allbuf, *yp) & LINEWRAPPED)) + return ' '; /* treat lines as separate */ + } + return -1; +} + +static char * +dabbrev_prev_word(int *xp, int *yp, TScreen * screen) +{ + static char ab[MAXWLEN]; + char *abword; + int c; + + abword = ab + MAXWLEN - 1; + *abword = '\0'; /* end of string marker */ + + while ((c = dabbrev_prev_char(xp, yp, screen)) >= 0 && + IS_WORD_CONSTITUENT(c)) + if (abword > ab) /* store only |MAXWLEN| last chars */ + *(--abword) = c; + if (c < 0) { + if (abword < ab + MAXWLEN - 1) + return abword; + else + return 0; + } + + while ((c = dabbrev_prev_char(xp, yp, screen)) >= 0 && + !IS_WORD_CONSTITUENT(c)) ; /* skip preceding spaces */ + (*xp)++; /* can be | > screen->max_col| */ + return abword; +} + +static int +dabbrev_expand(TScreen * screen) +{ + int pty = screen->respond; /* file descriptor of pty */ + + static int x, y; + static char *dabbrev_hint = 0, *lastexpansion = 0; + + char *expansion; + Char *copybuffer; + size_t hint_len; + unsigned i; + unsigned del_cnt; + unsigned buf_cnt; + + if (!screen->dabbrev_working) { /* initialize */ + x = screen->cur_col; + y = screen->cur_row + screen->savelines; + + free(dabbrev_hint); /* free(NULL) is OK */ + dabbrev_hint = dabbrev_prev_word(&x, &y, screen); + if (!dabbrev_hint) + return 0; /* no preceding word? */ + free(lastexpansion); + if (!(lastexpansion = strdup(dabbrev_hint))) /* make own copy */ + return 0; + if (!(dabbrev_hint = strdup(dabbrev_hint))) { + free(lastexpansion); + return 0; + } + screen->dabbrev_working = 1; /* we are in the middle of dabbrev process */ + } + + hint_len = strlen(dabbrev_hint); + while ((expansion = dabbrev_prev_word(&x, &y, screen))) { + if (!strncmp(dabbrev_hint, expansion, hint_len) && /* empty hint matches everything */ + strlen(expansion) > hint_len && /* trivial expansion disallowed */ + strcmp(expansion, lastexpansion)) /* different from previous */ + break; + } + if (!expansion) /* no expansion found */ + return 0; + + del_cnt = strlen(lastexpansion) - hint_len; + buf_cnt = del_cnt + strlen(expansion) - hint_len; + if (!(copybuffer = TypeMallocN(Char, buf_cnt))) + return 0; + for (i = 0; i < del_cnt; i++) { /* delete previous expansion */ + copybuffer[i] = screen->dabbrev_erase_char; + } + memmove(copybuffer + del_cnt, + expansion + hint_len, + strlen(expansion) - hint_len); + v_write(pty, copybuffer, buf_cnt); + screen->dabbrev_working = 1; /* v_write() just set it to 1 */ + free(copybuffer); + + free(lastexpansion); + lastexpansion = strdup(expansion); + if (!lastexpansion) + return 0; + return 1; +} + +/*ARGSUSED*/ +void +HandleDabbrevExpand(Widget gw, + XEvent * event GCC_UNUSED, + String * params GCC_UNUSED, + Cardinal *nparams GCC_UNUSED) +{ + XtermWidget w = (XtermWidget) gw; + TScreen *screen = &w->screen; + if (!dabbrev_expand(screen)) + Bell(XkbBI_TerminalBell, 0); +} +#endif /* OPT_DABBREV */ + +#if OPT_MAXIMIZE +/*ARGSUSED*/ +void +HandleDeIconify(Widget gw, + XEvent * event GCC_UNUSED, + String * params GCC_UNUSED, + Cardinal *nparams GCC_UNUSED) +{ + if (IsXtermWidget(gw)) { + TScreen *screen = &((XtermWidget) gw)->screen; + XMapWindow(screen->display, VShellWindow); + } +} + +/*ARGSUSED*/ +void +HandleIconify(Widget gw, + XEvent * event GCC_UNUSED, + String * params GCC_UNUSED, + Cardinal *nparams GCC_UNUSED) +{ + if (IsXtermWidget(gw)) { + TScreen *screen = &((XtermWidget) gw)->screen; + XIconifyWindow(screen->display, + VShellWindow, + DefaultScreen(screen->display)); + } +} + +int +QueryMaximize(XtermWidget termw, unsigned *width, unsigned *height) +{ + TScreen *screen = &termw->screen; + XSizeHints hints; + long supp = 0; + Window root_win; + int root_x = -1; /* saved co-ordinates */ + int root_y = -1; + unsigned root_border; + unsigned root_depth; + + if (XGetGeometry(screen->display, + RootWindowOfScreen(XtScreen(termw)), + &root_win, + &root_x, + &root_y, + width, + height, + &root_border, + &root_depth)) { + TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n", + root_x, + root_y, + *width, + *height, + root_border)); + + *width -= (root_border * 2); + *height -= (root_border * 2); + + hints.flags = PMaxSize; + if (XGetWMNormalHints(screen->display, + VShellWindow, + &hints, + &supp) + && (hints.flags & PMaxSize) != 0) { + + TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n", + hints.max_width, + hints.max_height)); + + if ((unsigned) hints.max_width < *width) + *width = hints.max_width; + if ((unsigned) hints.max_height < *height) + *height = hints.max_height; + } + return 1; + } + return 0; +} + +void +RequestMaximize(XtermWidget termw, int maximize) +{ + TScreen *screen = &termw->screen; + XWindowAttributes wm_attrs, vshell_attrs; + unsigned root_width, root_height; + + if (maximize) { + + if (QueryMaximize(termw, &root_width, &root_height)) { + + if (XGetWindowAttributes(screen->display, + WMFrameWindow(termw), + &wm_attrs)) { + + if (XGetWindowAttributes(screen->display, + VShellWindow, + &vshell_attrs)) { + + if (screen->restore_data != True + || screen->restore_width != root_width + || screen->restore_height != root_height) { + screen->restore_data = True; + screen->restore_x = wm_attrs.x + wm_attrs.border_width; + screen->restore_y = wm_attrs.y + wm_attrs.border_width; + screen->restore_width = vshell_attrs.width; + screen->restore_height = vshell_attrs.height; + TRACE(("HandleMaximize: save window position %d,%d size %d,%d\n", + screen->restore_x, + screen->restore_y, + screen->restore_width, + screen->restore_height)); + } + + /* subtract wm decoration dimensions */ + root_width -= ((wm_attrs.width - vshell_attrs.width) + + (wm_attrs.border_width * 2)); + root_height -= ((wm_attrs.height - vshell_attrs.height) + + (wm_attrs.border_width * 2)); + + XMoveResizeWindow(screen->display, VShellWindow, + 0 + wm_attrs.border_width, /* x */ + 0 + wm_attrs.border_width, /* y */ + root_width, + root_height); + } + } + } + } else { + if (screen->restore_data) { + TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n", + screen->restore_x, + screen->restore_y, + screen->restore_width, + screen->restore_height)); + screen->restore_data = False; + + XMoveResizeWindow(screen->display, + VShellWindow, + screen->restore_x, + screen->restore_y, + screen->restore_width, + screen->restore_height); + } + } +} + +/*ARGSUSED*/ +void +HandleMaximize(Widget gw, + XEvent * event GCC_UNUSED, + String * params GCC_UNUSED, + Cardinal *nparams GCC_UNUSED) +{ + if (IsXtermWidget(gw)) { + RequestMaximize((XtermWidget) gw, 1); + } +} + +/*ARGSUSED*/ +void +HandleRestoreSize(Widget gw, + XEvent * event GCC_UNUSED, + String * params GCC_UNUSED, + Cardinal *nparams GCC_UNUSED) +{ + if (IsXtermWidget(gw)) { + RequestMaximize((XtermWidget) gw, 0); + } +} +#endif /* OPT_MAXIMIZE */ + +void +Redraw(void) +{ + TScreen *screen = &term->screen; + XExposeEvent event; + + event.type = Expose; + event.display = screen->display; + event.x = 0; + event.y = 0; + event.count = 0; + + if (VWindow(screen)) { + event.window = VWindow(screen); + event.width = term->core.width; + event.height = term->core.height; + (*term->core.widget_class->core_class.expose) ((Widget) term, + (XEvent *) & event, + NULL); + if (ScrollbarWidth(screen)) { + (screen->scrollWidget->core.widget_class->core_class.expose) + (screen->scrollWidget, (XEvent *) & event, NULL); + } + } +#if OPT_TEK4014 + if (TWindow(screen) && screen->Tshow) { + event.window = TWindow(screen); + event.width = tekWidget->core.width; + event.height = tekWidget->core.height; + TekExpose((Widget) tekWidget, (XEvent *) & event, NULL); + } +#endif +} + +#ifdef VMS +#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d" +#else +#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d" +#endif + +void +timestamp_filename(char *dst, const char *src) +{ + time_t tstamp; + struct tm *tstruct; + + tstamp = time((time_t *) 0); + tstruct = localtime(&tstamp); + sprintf(dst, TIMESTAMP_FMT, + src, + tstruct->tm_year + 1900, + tstruct->tm_mon + 1, + tstruct->tm_mday, + tstruct->tm_hour, + tstruct->tm_min, + tstruct->tm_sec); +} + +int +open_userfile(uid_t uid, gid_t gid, char *path, Bool append) +{ + int fd; + struct stat sb; + +#ifdef VMS + if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { + int the_error = errno; + fprintf(stderr, "%s: cannot open %s: %d:%s\n", + xterm_name, + path, + the_error, + SysErrorMsg(the_error)); + return -1; + } + chown(path, uid, gid); +#else + if ((access(path, F_OK) != 0 && (errno != ENOENT)) + || (creat_as(uid, gid, append, path, 0644) <= 0) + || ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) { + int the_error = errno; + fprintf(stderr, "%s: cannot open %s: %d:%s\n", + xterm_name, + path, + the_error, + SysErrorMsg(the_error)); + return -1; + } +#endif + + /* + * Doublecheck that the user really owns the file that we've opened before + * we do any damage, and that it is not world-writable. + */ + if (fstat(fd, &sb) < 0 + || sb.st_uid != uid + || (sb.st_mode & 022) != 0) { + fprintf(stderr, "%s: you do not own %s\n", xterm_name, path); + close(fd); + return -1; + } + return fd; +} + +#ifndef VMS +/* + * Create a file only if we could with the permissions of the real user id. + * We could emulate this with careful use of access() and following + * symbolic links, but that is messy and has race conditions. + * Forking is messy, too, but we can't count on setreuid() or saved set-uids + * being available. + * + * Note: When called for user logging, we have ensured that the real and + * effective user ids are the same, so this remains as a convenience function + * for the debug logs. + * + * Returns + * 1 if we can proceed to open the file in relative safety, + * -1 on error, e.g., cannot fork + * 0 otherwise. + */ +int +creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, int mode) +{ + int fd; + pid_t pid; + int retval = 0; + int childstat = 0; +#ifndef HAVE_WAITPID + int waited; + SIGNAL_T(*chldfunc) (int); + + chldfunc = signal(SIGCHLD, SIG_DFL); +#endif /* HAVE_WAITPID */ + + TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n", + uid, geteuid(), + gid, getegid(), + append, + pathname, + mode)); + + if (uid == geteuid() && gid == getegid()) { + fd = open(pathname, + O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), + mode); + if (fd >= 0) + close(fd); + return (fd >= 0); + } + + pid = fork(); + switch (pid) { + case 0: /* child */ + if (setgid(gid) == -1 + || setuid(uid) == -1) { + /* we cannot report an error here via stderr, just quit */ + retval = 1; + } else { + fd = open(pathname, + O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), + mode); + if (fd >= 0) { + close(fd); + retval = 0; + } else { + retval = 1; + } + } + _exit(retval); + /* NOTREACHED */ + case -1: /* error */ + return retval; + default: /* parent */ +#ifdef HAVE_WAITPID + while (waitpid(pid, &childstat, 0) < 0) { +#ifdef EINTR + if (errno == EINTR) + continue; +#endif /* EINTR */ +#ifdef ERESTARTSYS + if (errno == ERESTARTSYS) + continue; +#endif /* ERESTARTSYS */ + break; + } +#else /* HAVE_WAITPID */ + waited = wait(&childstat); + signal(SIGCHLD, chldfunc); + /* + Since we had the signal handler uninstalled for a while, + we might have missed the termination of our screen child. + If we can check for this possibility without hanging, do so. + */ + do + if (waited == term->screen.pid) + Cleanup(0); + while ((waited = nonblocking_wait()) > 0) ; +#endif /* HAVE_WAITPID */ +#ifndef WIFEXITED +#define WIFEXITED(status) ((status & 0xff) != 0) +#endif + if (WIFEXITED(childstat)) + retval = 1; + return retval; + } +} +#endif /* !VMS */ + +int +xtermResetIds(TScreen * screen) +{ + int result = 0; + if (setgid(screen->gid) == -1) { + fprintf(stderr, "%s: unable to reset group-id\n", ProgramName); + result = -1; + } + if (setuid(screen->uid) == -1) { + fprintf(stderr, "%s: unable to reset user-id\n", ProgramName); + result = -1; + } + return result; +} + +#ifdef ALLOWLOGGING + +/* + * Logging is a security hole, since it allows a setuid program to write + * arbitrary data to an arbitrary file. So it is disabled by default. + */ + +#ifdef ALLOWLOGFILEEXEC +static SIGNAL_T +logpipe(int sig GCC_UNUSED) +{ + TScreen *screen = &term->screen; + +#ifdef SYSV + (void) signal(SIGPIPE, SIG_IGN); +#endif /* SYSV */ + if (screen->logging) + CloseLog(screen); +} +#endif /* ALLOWLOGFILEEXEC */ + +void +StartLog(TScreen * screen) +{ + static char *log_default; +#ifdef ALLOWLOGFILEEXEC + char *cp; +#endif /* ALLOWLOGFILEEXEC */ + + if (screen->logging || (screen->inhibit & I_LOG)) + return; +#ifdef VMS /* file name is fixed in VMS variant */ + screen->logfd = open(XTERM_VMS_LOGFILE, + O_CREAT | O_TRUNC | O_APPEND | O_RDWR, + 0640); + if (screen->logfd < 0) + return; /* open failed */ +#else /*VMS */ + if (screen->logfile == NULL || *screen->logfile == 0) { + if (screen->logfile) + free(screen->logfile); + if (log_default == NULL) { +#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME) + char log_def_name[512]; /* see sprintf below */ + char hostname[255 + 1]; /* Internet standard limit (RFC 1035): + ``To simplify implementations, the + total length of a domain name (i.e., + label octets and label length + octets) is restricted to 255 octets + or less.'' */ + char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1]; + time_t now; + struct tm *ltm; + + now = time((time_t *) 0); + ltm = (struct tm *) localtime(&now); + if ((gethostname(hostname, sizeof(hostname)) == 0) && + (strftime(yyyy_mm_dd_hh_mm_ss, + sizeof(yyyy_mm_dd_hh_mm_ss), + "%Y.%m.%d.%H.%M.%S", ltm) > 0)) { + (void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d", + hostname, yyyy_mm_dd_hh_mm_ss, getpid()); + } + if ((log_default = x_strdup(log_def_name)) == NULL) + return; +#else + const char *log_def_name = "XtermLog.XXXXXX"; + if ((log_default = x_strdup(log_def_name)) == NULL) + return; + + mktemp(log_default); +#endif + } + if ((screen->logfile = x_strdup(log_default)) == 0) + return; + } + if (*screen->logfile == '|') { /* exec command */ +#ifdef ALLOWLOGFILEEXEC + /* + * Warning, enabling this "feature" allows arbitrary programs + * to be run. If ALLOWLOGFILECHANGES is enabled, this can be + * done through escape sequences.... You have been warned. + */ + int pid; + int p[2]; + static char *shell; + struct passwd *pw; + + if (pipe(p) < 0 || (pid = fork()) < 0) + return; + if (pid == 0) { /* child */ + /* + * Close our output (we won't be talking back to the + * parent), and redirect our child's output to the + * original stderr. + */ + close(p[1]); + dup2(p[0], 0); + close(p[0]); + dup2(fileno(stderr), 1); + dup2(fileno(stderr), 2); + + close(fileno(stderr)); + close(ConnectionNumber(screen->display)); + close(screen->respond); + + if ((((cp = getenv("SHELL")) == NULL || *cp == 0) + && ((pw = getpwuid(screen->uid)) == NULL + || *(cp = pw->pw_shell) == 0)) + || (shell = CastMallocN(char, strlen(cp))) == 0) { + shell = "/bin/sh"; + } else { + strcpy(shell, cp); + } + + signal(SIGHUP, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + /* (this is redundant) */ + if (xtermResetIds(screen) < 0) + exit(ERROR_SETUID); + + execl(shell, shell, "-c", &screen->logfile[1], (void *) 0); + + fprintf(stderr, "%s: Can't exec `%s'\n", xterm_name, + &screen->logfile[1]); + exit(ERROR_LOGEXEC); + } + close(p[0]); + screen->logfd = p[1]; + signal(SIGPIPE, logpipe); +#else + Bell(XkbBI_Info, 0); + Bell(XkbBI_Info, 0); + return; +#endif + } else { + if ((screen->logfd = open_userfile(screen->uid, + screen->gid, + screen->logfile, + (log_default != 0))) < 0) + return; + } +#endif /*VMS */ + screen->logstart = VTbuffer->next; + screen->logging = True; + update_logging(); +} + +void +CloseLog(TScreen * screen) +{ + if (!screen->logging || (screen->inhibit & I_LOG)) + return; + FlushLog(screen); + close(screen->logfd); + screen->logging = False; + update_logging(); +} + +void +FlushLog(TScreen * screen) +{ + if (screen->logging && !(screen->inhibit & I_LOG)) { + Char *cp; + int i; + +#ifdef VMS /* avoid logging output loops which otherwise occur sometimes + when there is no output and cp/screen->logstart are 1 apart */ + if (!tt_new_output) + return; + tt_new_output = False; +#endif /* VMS */ + cp = VTbuffer->next; + if (screen->logstart != 0 + && (i = cp - screen->logstart) > 0) { + write(screen->logfd, (char *) screen->logstart, (unsigned) i); + } + screen->logstart = VTbuffer->next; + } +} + +#endif /* ALLOWLOGGING */ + +/***====================================================================***/ + +#if OPT_ISO_COLORS +static void +ReportAnsiColorRequest(XtermWidget xw, int colornum, int final) +{ + XColor color; + Colormap cmap = xw->core.colormap; + char buffer[80]; + + TRACE(("ReportAnsiColorRequest %d\n", colornum)); + color.pixel = GET_COLOR_RES(xw->screen.Acolors[colornum]); + XQueryColor(xw->screen.display, cmap, &color); + sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x", + colornum, + color.red, + color.green, + color.blue); + unparseputc1(xw, OSC); + unparseputs(xw, buffer); + unparseputc1(xw, final); + unparse_end(xw); +} + +/* +* Find closest color for "def" in "cmap". +* Set "def" to the resulting color. +* Based on Monish Shah's "find_closest_color()" for Vim 6.0, +* modified with ideas from David Tong's "noflash" library. +* Return False if not able to find or allocate a color. +*/ +static int +find_closest_color(Display * display, Colormap cmap, XColor * def) +{ + double tmp, distance, closestDistance; + int closest, numFound; + XColor *colortable; + XVisualInfo myTemplate, *visInfoPtr; + char *found; + unsigned i; + unsigned cmap_size; + unsigned attempts; + + myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(display, + XDefaultScreen(display))); + visInfoPtr = XGetVisualInfo(display, (long) VisualIDMask, + &myTemplate, &numFound); + if (numFound < 1) { + /* FindClosestColor couldn't lookup visual */ + return False; + } + + cmap_size = visInfoPtr->colormap_size; + XFree((char *) visInfoPtr); + colortable = TypeMallocN(XColor, cmap_size); + if (!colortable) { + return False; /* out of memory */ + } + found = TypeCallocN(char, cmap_size); + if (!found) { + free(colortable); + return False; /* out of memory */ + } + + for (i = 0; i < cmap_size; i++) { + colortable[i].pixel = (unsigned long) i; + } + XQueryColors(display, cmap, colortable, (int) cmap_size); + + /* + * Find the color that best approximates the desired one, then + * try to allocate that color. If that fails, it must mean that + * the color was read-write (so we can't use it, since its owner + * might change it) or else it was already freed. Try again, + * over and over again, until something succeeds. + */ + for (attempts = 0; attempts < cmap_size; attempts++) { + closestDistance = 1e30; + closest = 0; + for (i = 0; i < cmap_size; i++) { + if (!found[closest]) { + /* + * Use Euclidean distance in RGB space, weighted by Y (of YIQ) + * as the objective function; this accounts for differences + * in the color sensitivity of the eye. + */ + tmp = .30 * (((int) def->red) - (int) colortable[i].red); + distance = tmp * tmp; + tmp = .61 * (((int) def->green) - (int) colortable[i].green); + distance += tmp * tmp; + tmp = .11 * (((int) def->blue) - (int) colortable[i].blue); + distance += tmp * tmp; + if (distance < closestDistance) { + closest = i; + closestDistance = distance; + } + } + } + if (XAllocColor(display, cmap, &colortable[closest]) != 0) { + *def = colortable[closest]; + break; + } + found[closest] = True; /* Don't look at this entry again */ + } + + free(colortable); + free(found); + if (attempts < cmap_size) { + return True; /* Got a closest matching color */ + } else { + return False; /* Couldn't allocate a near match */ + } +} + +static Bool +AllocateAnsiColor(XtermWidget xw, + ColorRes * res, + char *spec) +{ + XColor def; + TScreen *screen = &xw->screen; + Colormap cmap = xw->core.colormap; + + if (XParseColor(screen->display, cmap, spec, &def) + && (XAllocColor(screen->display, cmap, &def) + || find_closest_color(screen->display, cmap, &def))) { + SET_COLOR_RES(res, def.pixel); + TRACE(("AllocateAnsiColor[%d] %s (pixel %#lx)\n", + (res - screen->Acolors), spec, def.pixel)); +#if OPT_COLOR_RES + res->mode = True; +#endif + return (True); + } + TRACE(("AllocateAnsiColor %s (failed)\n", spec)); + return (False); +} + +#if OPT_COLOR_RES +Pixel +xtermGetColorRes(ColorRes * res) +{ + Pixel result = 0; + + if (res->mode) { + result = res->value; + } else { + TRACE(("xtermGetColorRes for Acolors[%d]\n", + res - term->screen.Acolors)); + + if (res >= term->screen.Acolors) { + assert(res - term->screen.Acolors < MAXCOLORS); + + if (!AllocateAnsiColor(term, res, res->resource)) { + res->value = term->screen.Tcolors[TEXT_FG].value; + res->mode = -True; + fprintf(stderr, + "%s: Cannot allocate color %s\n", + xterm_name, + NonNull(res->resource)); + } + result = res->value; + } else { + result = 0; + } + } + return result; +} +#endif + +static Bool +ChangeAnsiColorRequest(XtermWidget xw, + char *buf, + int final) +{ + char *name; + int color; + int r = False; + + TRACE(("ChangeAnsiColorRequest string='%s'\n", buf)); + + while (buf && *buf) { + name = strchr(buf, ';'); + if (name == NULL) + break; + *name = '\0'; + name++; + color = atoi(buf); + if (color < 0 || color >= NUM_ANSI_COLORS) + break; + buf = strchr(name, ';'); + if (buf) { + *buf = '\0'; + buf++; + } + if (!strcmp(name, "?")) + ReportAnsiColorRequest(xw, color, final); + else { + TRACE(("ChangeAnsiColor for Acolors[%d]\n", color)); + if (!AllocateAnsiColor(xw, &(xw->screen.Acolors[color]), name)) + break; + /* FIXME: free old color somehow? We aren't for the other color + * change style (dynamic colors). + */ + r = True; + } + } + if (r) + ChangeAnsiColors(xw); + return (r); +} +#else +#define find_closest_color(display, cmap, def) 0 +#endif /* OPT_ISO_COLORS */ + +#if OPT_PASTE64 +static void +ManipulateSelectionData(XtermWidget xw, TScreen * screen, char *buf, int final) +{ +#define PDATA(a,b) { a, #b } + static struct { + char given; + char *result; + } table[] = { + PDATA('s', SELECT), + PDATA('p', PRIMARY), + PDATA('c', CLIPBOARD), + PDATA('0', CUT_BUFFER0), + PDATA('1', CUT_BUFFER1), + PDATA('2', CUT_BUFFER2), + PDATA('3', CUT_BUFFER3), + PDATA('4', CUT_BUFFER4), + PDATA('5', CUT_BUFFER5), + PDATA('6', CUT_BUFFER6), + PDATA('7', CUT_BUFFER7), + }; + + char *base = buf; + char *used = x_strdup(base); + Cardinal j, n = 0; + char **select_args = 0; + + TRACE(("Manipulate selection data\n")); + + while (*buf != ';' && *buf != '\0') { + ++buf; + } + + if (*buf == ';') { + *buf++ = '\0'; + + if (*base == '\0') + base = "s0"; + if ((select_args = TypeCallocN(String, 1 + strlen(base))) == 0) + return; + while (*base != '\0') { + for (j = 0; j < XtNumber(table); ++j) { + if (*base == table[j].given) { + used[n] = *base; + select_args[n++] = table[j].result; + TRACE(("atom[%d] %s\n", n, table[j].result)); + break; + } + } + ++base; + } + used[n] = 0; + + if (!strcmp(buf, "?")) { + TRACE(("Getting selection\n")); + unparseputc1(xw, OSC); + unparseputs(xw, "52"); + unparseputc(xw, ';'); + + unparseputs(xw, used); + unparseputc(xw, ';'); + + /* Tell xtermGetSelection data is base64 encoded */ + screen->base64_paste = n; + screen->base64_final = final; + + /* terminator will be written in this call */ + xtermGetSelection((Widget) xw, 0, select_args, n, NULL); + } else { + TRACE(("Setting selection with %s\n", buf)); + ClearSelectionBuffer(screen); + while (*buf != '\0') + AppendToSelectionBuffer(screen, CharOf(*buf++)); + CompleteSelection(xw, select_args, n); + } + } +} +#endif /* OPT_PASTE64 */ + +/***====================================================================***/ + +void +do_osc(XtermWidget xw, Char * oscbuf, unsigned len GCC_UNUSED, int final) +{ + TScreen *screen = &(xw->screen); + int mode; + Char *cp; + int state = 0; + char *buf = 0; + + TRACE(("do_osc %s\n", oscbuf)); + + /* + * Lines should be of the form <OSC> number ; string <ST>, however + * older xterms can accept <BEL> as a final character. We will respond + * with the same final character as the application sends to make this + * work better with shell scripts, which may have trouble reading an + * <ESC><backslash>, which is the 7-bit equivalent to <ST>. + */ + mode = 0; + for (cp = oscbuf; *cp != '\0'; cp++) { + switch (state) { + case 0: + if (isdigit(*cp)) { + mode = 10 * mode + (*cp - '0'); + if (mode > 65535) { + TRACE(("do_osc found unknown mode %d\n", mode)); + return; + } + break; + } + /* FALLTHRU */ + case 1: + if (*cp != ';') { + TRACE(("do_osc did not find semicolon offset %d\n", cp - oscbuf)); + return; + } + state = 2; + break; + case 2: + buf = (char *) cp; + state = 3; + /* FALLTHRU */ + default: + if (ansi_table[CharOf(*cp)] != CASE_PRINT) { + switch (mode) { + case 0: + case 1: + case 2: +#if OPT_WIDE_CHARS + /* + * If we're running with UTF-8, it is possible for title + * strings to contain "nonprinting" text. + */ + if (xtermEnvUTF8()) +#endif + break; + default: + TRACE(("do_osc found nonprinting char %02X offset %d\n", + CharOf(*cp), + cp - oscbuf)); + return; + } + } + } + } + if (buf == 0) + return; + + switch (mode) { + case 0: /* new icon name and title */ + Changename(buf); + Changetitle(buf); + break; + + case 1: /* new icon name only */ + Changename(buf); + break; + + case 2: /* new title only */ + Changetitle(buf); + break; + + case 3: /* change X property */ + ChangeXprop(buf); + break; +#if OPT_ISO_COLORS + case 4: + ChangeAnsiColorRequest(xw, buf, final); + break; +#endif + case 10 + TEXT_FG: + case 10 + TEXT_BG: + case 10 + TEXT_CURSOR: + case 10 + MOUSE_FG: + case 10 + MOUSE_BG: +#if OPT_HIGHLIGHT_COLOR + case 10 + HIGHLIGHT_BG: +#endif +#if OPT_TEK4014 + case 10 + TEK_FG: + case 10 + TEK_BG: + case 10 + TEK_CURSOR: +#endif + if (xw->misc.dynamicColors) + ChangeColorsRequest(xw, mode - 10, buf, final); + break; + + case 30: + case 31: + /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ + break; + +#ifdef ALLOWLOGGING + case 46: /* new log file */ +#ifdef ALLOWLOGFILECHANGES + /* + * Warning, enabling this feature allows people to overwrite + * arbitrary files accessible to the person running xterm. + */ + if (buf != 0 + && strcmp(buf, "?") + && (cp = CastMallocN(char, strlen(buf)) != NULL)) { + strcpy(cp, buf); + if (screen->logfile) + free(screen->logfile); + screen->logfile = cp; + break; + } +#endif + Bell(XkbBI_Info, 0); + Bell(XkbBI_Info, 0); + break; +#endif /* ALLOWLOGGING */ + + case 50: + if (buf != 0 && !strcmp(buf, "?")) { + int num = screen->menu_font_number; + + unparseputc1(xw, OSC); + unparseputs(xw, "50"); + + if ((buf = screen->MenuFontName(num)) != 0) { + unparseputc(xw, ';'); + unparseputs(xw, buf); + } + unparseputc1(xw, final); + unparse_end(xw); + } else if (buf != 0) { + VTFontNames fonts; + + memset(&fonts, 0, sizeof(fonts)); + + /* + * If the font specification is a "#", followed by an + * optional sign and optional number, lookup the + * corresponding menu font entry. + */ + if (*buf == '#') { + int num = screen->menu_font_number; + int rel = 0; + + if (*++buf == '+') { + rel = 1; + buf++; + } else if (*buf == '-') { + rel = -1; + buf++; + } + + if (isdigit(CharOf(*buf))) { + int val = atoi(buf); + if (rel > 0) + rel = val; + else if (rel < 0) + rel = -val; + else + num = val; + } else if (rel == 0) { + num = 0; + } + + if (rel != 0) + num = lookupRelativeFontSize(screen, + screen->menu_font_number, rel); + + if (num < 0 + || num > fontMenu_lastBuiltin + || (buf = screen->MenuFontName(num)) == 0) { + Bell(XkbBI_MinorError, 0); + break; + } + } + fonts.f_n = buf; + SetVTFont(xw, fontMenu_fontescape, True, &fonts); + } + break; + case 51: + /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ + break; + +#if OPT_PASTE64 + case 52: + if (screen->allowWindowOps && (buf != 0)) + ManipulateSelectionData(xw, screen, buf, final); + break; +#endif + /* + * One could write code to send back the display and host names, + * but that could potentially open a fairly nasty security hole. + */ + } + unparse_end(xw); +} + +#ifdef SunXK_F36 +#define MAX_UDK 37 +#else +#define MAX_UDK 35 +#endif +static struct { + char *str; + int len; +} user_keys[MAX_UDK]; + +/* + * Parse one nibble of a hex byte from the OSC string. We have removed the + * string-terminator (replacing it with a null), so the only other delimiter + * that is expected is semicolon. Ignore other characters (Ray Neuman says + * "real" terminals accept commas in the string definitions). + */ +static int +udk_value(char **cp) +{ + int c; + + for (;;) { + if ((c = **cp) != '\0') + *cp = *cp + 1; + if (c == ';' || c == '\0') + return -1; + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + } +} + +void +reset_decudk(void) +{ + int n; + for (n = 0; n < MAX_UDK; n++) { + if (user_keys[n].str != 0) { + free(user_keys[n].str); + user_keys[n].str = 0; + user_keys[n].len = 0; + } + } +} + +/* + * Parse the data for DECUDK (user-defined keys). + */ +static void +parse_decudk(char *cp) +{ + while (*cp) { + char *base = cp; + char *str = CastMallocN(char, strlen(cp) + 1); + unsigned key = 0; + int lo, hi; + int len = 0; + + while (isdigit(CharOf(*cp))) + key = (key * 10) + (*cp++ - '0'); + if (*cp == '/') { + cp++; + while ((hi = udk_value(&cp)) >= 0 + && (lo = udk_value(&cp)) >= 0) { + str[len++] = (hi << 4) | lo; + } + } + if (len > 0 && key < MAX_UDK) { + if (user_keys[key].str != 0) + free(user_keys[key].str); + user_keys[key].str = str; + user_keys[key].len = len; + } else { + free(str); + } + if (*cp == ';') + cp++; + if (cp == base) /* badly-formed sequence - bail out */ + break; + } +} + +#if OPT_TRACE +#define SOFT_WIDE 10 +#define SOFT_HIGH 20 + +static void +parse_decdld(ANSI * params, char *string) +{ + char DscsName[8]; + int len; + int Pfn = params->a_param[0]; + int Pcn = params->a_param[1]; + int Pe = params->a_param[2]; + int Pcmw = params->a_param[3]; + int Pw = params->a_param[4]; + int Pt = params->a_param[5]; + int Pcmh = params->a_param[6]; + int Pcss = params->a_param[7]; + + int start_char = Pcn + 0x20; + int char_wide = ((Pcmw == 0) + ? (Pcss ? 6 : 10) + : (Pcmw > 4 + ? Pcmw + : (Pcmw + 3))); + int char_high = ((Pcmh == 0) + ? ((Pcmw >= 2 || Pcmw <= 4) + ? 10 + : 20) + : Pcmh); + Char ch; + Char bits[SOFT_HIGH][SOFT_WIDE]; + Bool first = True; + Bool prior = False; + int row = 0, col = 0; + + TRACE(("Parsing DECDLD\n")); + TRACE((" font number %d\n", Pfn)); + TRACE((" starting char %d\n", Pcn)); + TRACE((" erase control %d\n", Pe)); + TRACE((" char-width %d\n", Pcmw)); + TRACE((" font-width %d\n", Pw)); + TRACE((" text/full %d\n", Pt)); + TRACE((" char-height %d\n", Pcmh)); + TRACE((" charset-size %d\n", Pcss)); + + if (Pfn > 1 + || Pcn > 95 + || Pe > 2 + || Pcmw > 10 + || Pcmw == 1 + || Pt > 2 + || Pcmh > 20 + || Pcss > 1 + || char_wide > SOFT_WIDE + || char_high > SOFT_HIGH) { + TRACE(("DECDLD illegal parameter\n")); + return; + } + + len = 0; + while (*string != '\0') { + ch = CharOf(*string++); + if (ch >= 0x20 && ch <= 0x2f) { + if (len < 2) + DscsName[len++] = ch; + } else if (ch >= 0x30 && ch <= 0x7e) { + DscsName[len++] = ch; + break; + } + } + DscsName[len] = 0; + TRACE((" Dscs name '%s'\n", DscsName)); + + TRACE((" character matrix %dx%d\n", char_high, char_wide)); + while (*string != '\0') { + if (first) { + TRACE(("Char %d:\n", start_char)); + if (prior) { + for (row = 0; row < char_high; ++row) { + TRACE(("%.*s\n", char_wide, bits[row])); + } + } + prior = False; + first = False; + for (row = 0; row < char_high; ++row) { + for (col = 0; col < char_wide; ++col) { + bits[row][col] = '.'; + } + } + row = col = 0; + } + ch = CharOf(*string++); + if (ch >= 0x3f && ch <= 0x7e) { + int n; + + ch -= 0x3f; + for (n = 0; n < 6; ++n) { + bits[row + n][col] = (ch & (1 << n)) ? '*' : '.'; + } + col += 1; + prior = True; + } else if (ch == '/') { + row += 6; + col = 0; + } else if (ch == ';') { + first = True; + ++start_char; + } + } +} +#else +#define parse_decdld(p,q) /* nothing */ +#endif + +/* + * Parse numeric parameters. Normally we use a state machine to simplify + * interspersing with control characters, but have the string already. + */ +static void +parse_ansi_params(ANSI * params, char **string) +{ + char *cp = *string; + short nparam = 0; + + memset(params, 0, sizeof(*params)); + while (*cp != '\0') { + Char ch = CharOf(*cp++); + + if (isdigit(ch)) { + if (nparam < NPARAM) { + params->a_param[nparam] *= 10; + params->a_param[nparam] += (ch - '0'); + } + } else if (ch == ';') { + if (++nparam < NPARAM) + params->a_nparam = nparam; + } else if (ch < 32) { + ; + } else { + /* should be 0x30 to 0x7e */ + params->a_final = ch; + break; + } + } + *string = cp; +} + +void +do_dcs(XtermWidget xw, Char * dcsbuf, size_t dcslen) +{ + TScreen *screen = &xw->screen; + char reply[BUFSIZ]; + char *cp = (char *) dcsbuf; + Bool okay; + ANSI params; + + TRACE(("do_dcs(%s:%d)\n", (char *) dcsbuf, dcslen)); + + if (dcslen != strlen(cp)) + /* shouldn't have nulls in the string */ + return; + + switch (*cp) { /* intermediate character, or parameter */ + case '$': /* DECRQSS */ + okay = True; + + cp++; + if (*cp++ == 'q') { + if (!strcmp(cp, "\"q")) { /* DECSCA */ + sprintf(reply, "%d%s", + (screen->protected_mode == DEC_PROTECT) + && (xw->flags & PROTECTED) ? 1 : 0, + cp); + } else if (!strcmp(cp, "\"p")) { /* DECSCL */ + sprintf(reply, "%d%s%s", + (screen->vtXX_level ? + screen->vtXX_level : 1) + 60, + (screen->vtXX_level >= 2) + ? (screen->control_eight_bits + ? ";0" : ";1") + : "", + cp); + } else if (!strcmp(cp, "r")) { /* DECSTBM */ + sprintf(reply, "%d;%dr", + screen->top_marg + 1, + screen->bot_marg + 1); + } else if (!strcmp(cp, "m")) { /* SGR */ + strcpy(reply, "0"); + if (xw->flags & BOLD) + strcat(reply, ";1"); + if (xw->flags & UNDERLINE) + strcat(reply, ";4"); + if (xw->flags & BLINK) + strcat(reply, ";5"); + if (xw->flags & INVERSE) + strcat(reply, ";7"); + if (xw->flags & INVISIBLE) + strcat(reply, ";8"); + if_OPT_EXT_COLORS(screen, { + if (xw->flags & FG_COLOR) { + if (xw->cur_foreground >= 16) + sprintf(reply + strlen(reply), + ";38;5;%d", xw->cur_foreground); + else + sprintf(reply + strlen(reply), + ";%d%d", + xw->cur_foreground >= 8 ? 9 : 3, + xw->cur_foreground >= 8 ? + xw->cur_foreground - 8 : + xw->cur_foreground); + } + if (xw->flags & BG_COLOR) { + if (xw->cur_background >= 16) + sprintf(reply + strlen(reply), + ";48;5;%d", xw->cur_foreground); + else + sprintf(reply + strlen(reply), + ";%d%d", + xw->cur_background >= 8 ? 10 : 4, + xw->cur_background >= 8 ? + xw->cur_background - 8 : + xw->cur_background); + } + }); + if_OPT_ISO_TRADITIONAL_COLORS(screen, { + if (xw->flags & FG_COLOR) + sprintf(reply + strlen(reply), + ";%d%d", + xw->cur_foreground >= 8 ? 9 : 3, + xw->cur_foreground >= 8 ? + xw->cur_foreground - 8 : + xw->cur_foreground); + if (xw->flags & BG_COLOR) + sprintf(reply + strlen(reply), + ";%d%d", + xw->cur_background >= 8 ? 10 : 4, + xw->cur_background >= 8 ? + xw->cur_background - 8 : + xw->cur_background); + }); + strcat(reply, "m"); + } else + okay = False; + + unparseputc1(xw, DCS); + unparseputc(xw, okay ? '1' : '0'); + unparseputc(xw, '$'); + unparseputc(xw, 'r'); + if (okay) + cp = reply; + unparseputs(xw, cp); + unparseputc1(xw, ST); + } else { + unparseputc(xw, CAN); + } + break; +#if OPT_TCAP_QUERY + case '+': + cp++; + if (*cp == 'q') { + Bool fkey; + unsigned state; + int code; + char *tmp; + char *parsed = ++cp; + + unparseputc1(xw, DCS); + + code = xtermcapKeycode(xw, &parsed, &state, &fkey); + + unparseputc(xw, code >= 0 ? '1' : '0'); + + unparseputc(xw, '+'); + unparseputc(xw, 'r'); + + while (*cp != 0) { + if (cp == parsed) + break; /* no data found, error */ + + for (tmp = cp; tmp != parsed; ++tmp) + unparseputc(xw, *tmp); + + if (code >= 0) { + unparseputc(xw, '='); + screen->tc_query_code = code; + screen->tc_query_fkey = fkey; + /* XK_COLORS is a fake code for the "Co" entry (maximum + * number of colors) */ + if (code == XK_COLORS) { + unparseputn(xw, NUM_ANSI_COLORS); + } else { + XKeyEvent event; + event.state = state; + Input(xw, &event, False); + } + screen->tc_query_code = -1; + } else { + break; /* no match found, error */ + } + + cp = parsed; + if (*parsed == ';') { + unparseputc(xw, *parsed++); + cp = parsed; + code = xtermcapKeycode(xw, &parsed, &state, &fkey); + } + } + unparseputc1(xw, ST); + } + break; +#endif + default: + parse_ansi_params(¶ms, &cp); + switch (params.a_final) { + case '|': /* DECUDK */ + if (params.a_param[0] == 0) + reset_decudk(); + parse_decudk(cp); + break; + case '{': /* DECDLD (no '}' case though) */ + parse_decdld(¶ms, cp); + break; + } + break; + } + unparse_end(xw); +} + +char * +udk_lookup(int keycode, int *len) +{ + if (keycode >= 0 && keycode < MAX_UDK) { + *len = user_keys[keycode].len; + return user_keys[keycode].str; + } + return 0; +} + +static void +ChangeGroup(String attribute, char *value) +{ +#if OPT_WIDE_CHARS + static Char *converted; /* NO_LEAKS */ +#endif + Arg args[1]; + char *original = (value != 0) ? value : ""; + char *name = original; + TScreen *screen = &term->screen; + Widget w = CURRENT_EMU(screen); + Widget top = SHELL_OF(w); + unsigned limit = strlen(name); + + TRACE(("ChangeGroup(attribute=%s, value=%s)\n", attribute, name)); + + (void) screen; + + /* + * Ignore titles that are too long to be plausible requests. + */ + if (limit >= 1024) + return; + +#if OPT_WIDE_CHARS + /* + * Title strings are limited to ISO-8859-1, which is consistent with the + * printable data in sos_table. However, if we're running in UTF-8 mode, + * it is likely that non-ASCII text in the string will be rejected because + * it is not printable in the current locale. So we convert it to UTF-8, + * allowing the X library to convert it back. + */ + if (xtermEnvUTF8() && !screen->utf8_title) { + int n; + + for (n = 0; name[n] != '\0'; ++n) { + if (CharOf(name[n]) > 127) { + if (converted != 0) + free(converted); + if ((converted = TypeMallocN(Char, 1 + (5 * limit))) != 0) { + Char *temp = converted; + while (*name != 0) { + temp = convertToUTF8(temp, CharOf(*name)); + ++name; + } + *temp = 0; + name = (char *) converted; + TRACE(("...converted{%s}\n", name)); + } + break; + } + } + } +#endif + +#if OPT_SAME_NAME + /* If the attribute isn't going to change, then don't bother... */ + + if (sameName) { + char *buf; + XtSetArg(args[0], attribute, &buf); + XtGetValues(top, args, 1); + TRACE(("...comparing{%s}\n", buf)); + if (strcmp(name, buf) == 0) + return; + } +#endif /* OPT_SAME_NAME */ + + TRACE(("...updating %s\n", attribute)); + XtSetArg(args[0], attribute, name); + XtSetValues(top, args, 1); + +#if OPT_WIDE_CHARS + if (xtermEnvUTF8()) { + Display *dpy = XtDisplay(term); + Atom my_atom; + char *propname = (!strcmp(attribute, XtNtitle) + ? "_NET_WM_NAME" + : "_NET_WM_ICON_NAME"); + if ((my_atom = XInternAtom(dpy, propname, False)) != None) { + if (screen->utf8_title) { + TRACE(("...updating %s\n", propname)); + XChangeProperty(dpy, VShellWindow, + my_atom, XA_UTF8_STRING(dpy), 8, + PropModeReplace, + (Char *) original, (int) strlen(original)); + } else { + TRACE(("...deleting %s\n", propname)); + XDeleteProperty(dpy, VShellWindow, my_atom); + } + } + } +#endif +} + +void +Changename(char *name) +{ + if (name == 0) + name = ""; +#if OPT_ZICONBEEP /* If warning should be given then give it */ + if (zIconBeep && zIconBeep_flagged) { + char *newname = CastMallocN(char, strlen(name) + 4); + if (!newname) { + fprintf(stderr, "malloc failed in Changename\n"); + return; + } + strcpy(newname, "*** "); + strcat(newname, name); + ChangeGroup(XtNiconName, newname); + free(newname); + } else +#endif /* OPT_ZICONBEEP */ + ChangeGroup(XtNiconName, name); +} + +void +Changetitle(char *name) +{ + ChangeGroup(XtNtitle, name); +} + +#define Strlen(s) strlen((char *)(s)) + +void +ChangeXprop(char *buf) +{ + Display *dpy = XtDisplay(toplevel); + Window w = XtWindow(toplevel); + XTextProperty text_prop; + Atom aprop; + Char *pchEndPropName = (Char *) strchr(buf, '='); + + if (pchEndPropName) + *pchEndPropName = '\0'; + aprop = XInternAtom(dpy, buf, False); + if (pchEndPropName == NULL) { + /* no "=value" given, so delete the property */ + XDeleteProperty(dpy, w, aprop); + } else { + text_prop.value = pchEndPropName + 1; + text_prop.encoding = XA_STRING; + text_prop.format = 8; + text_prop.nitems = Strlen(text_prop.value); + XSetTextProperty(dpy, w, &text_prop, aprop); + } +} + +/***====================================================================***/ + +static ScrnColors *pOldColors = NULL; + +static Bool +GetOldColors(XtermWidget xw) +{ + int i; + if (pOldColors == NULL) { + pOldColors = (ScrnColors *) XtMalloc(sizeof(ScrnColors)); + if (pOldColors == NULL) { + fprintf(stderr, "allocation failure in GetOldColors\n"); + return (False); + } + pOldColors->which = 0; + for (i = 0; i < NCOLORS; i++) { + pOldColors->colors[i] = 0; + pOldColors->names[i] = NULL; + } + GetColors(xw, pOldColors); + } + return (True); +} + +static int +oppositeColor(int n) +{ + switch (n) { + case TEXT_FG: + n = TEXT_BG; + break; + case TEXT_BG: + n = TEXT_FG; + break; + case MOUSE_FG: + n = MOUSE_BG; + break; + case MOUSE_BG: + n = MOUSE_FG; + break; +#if OPT_TEK4014 + case TEK_FG: + n = TEK_BG; + break; + case TEK_BG: + n = TEK_FG; + break; +#endif + default: + break; + } + return n; +} + +static void +ReportColorRequest(XtermWidget xw, int ndx, int final) +{ + XColor color; + Colormap cmap = xw->core.colormap; + char buffer[80]; + + /* + * ChangeColorsRequest() has "always" chosen the opposite color when + * reverse-video is set. Report this as the original color index, but + * reporting the opposite color which would be used. + */ + int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx; + + GetOldColors(xw); + color.pixel = pOldColors->colors[ndx]; + XQueryColor(xw->screen.display, cmap, &color); + sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10, + color.red, + color.green, + color.blue); + TRACE(("ReportColors %d: %#lx as %s\n", ndx, pOldColors->colors[ndx], buffer)); + unparseputc1(xw, OSC); + unparseputs(xw, buffer); + unparseputc1(xw, final); + unparse_end(xw); +} + +static Bool +UpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew) +{ + int i; + + /* if we were going to free old colors, this would be the place to + * do it. I've decided not to (for now), because it seems likely + * that we'd have a small set of colors we use over and over, and that + * we could save some overhead this way. The only case in which this + * (clearly) fails is if someone is trying a boatload of colors, in + * which case they can restart xterm + */ + for (i = 0; i < NCOLORS; i++) { + if (COLOR_DEFINED(pNew, i)) { + if (pOldColors->names[i] != NULL) { + XtFree(pOldColors->names[i]); + pOldColors->names[i] = NULL; + } + if (pNew->names[i]) { + pOldColors->names[i] = pNew->names[i]; + } + pOldColors->colors[i] = pNew->colors[i]; + } + } + return (True); +} + +/* + * This is part of ReverseVideo(). It reverses the data stored for the old + * "dynamic" colors that might have been retrieved using OSC 10-18. + */ +void +ReverseOldColors(void) +{ + ScrnColors *pOld = pOldColors; + Pixel tmpPix; + char *tmpName; + + if (pOld) { + /* change text cursor, if necesary */ + if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { + pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; + if (pOld->names[TEXT_CURSOR]) { + XtFree(pOldColors->names[TEXT_CURSOR]); + pOld->names[TEXT_CURSOR] = NULL; + } + if (pOld->names[TEXT_BG]) { + if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { + pOld->names[TEXT_CURSOR] = tmpName; + } + } + } + + EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); + EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); + + EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); + EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); + +#if OPT_TEK4014 + EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); + EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); +#endif + } + return; +} + +Bool +AllocateTermColor(XtermWidget xw, + ScrnColors * pNew, + int ndx, + const char *name) +{ + XColor def; + TScreen *screen = &xw->screen; + Colormap cmap = xw->core.colormap; + char *newName; + + if (XParseColor(screen->display, cmap, name, &def) + && (XAllocColor(screen->display, cmap, &def) + || find_closest_color(screen->display, cmap, &def)) + && (newName = x_strdup(name)) != 0) { + if (COLOR_DEFINED(pNew, ndx)) + free(pNew->names[ndx]); + SET_COLOR_VALUE(pNew, ndx, def.pixel); + SET_COLOR_NAME(pNew, ndx, newName); + TRACE(("AllocateTermColor #%d: %s (pixel %#lx)\n", ndx, newName, def.pixel)); + return (True); + } + TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); + return (False); +} + +static Bool +ChangeColorsRequest(XtermWidget xw, + int start, + char *names, + int final) +{ + char *thisName; + ScrnColors newColors; + int i, ndx; + + TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names)); + + if ((pOldColors == NULL) + && (!GetOldColors(xw))) { + return (False); + } + newColors.which = 0; + for (i = 0; i < NCOLORS; i++) { + newColors.names[i] = NULL; + } + for (i = start; i < NCOLORS; i++) { + if (xw->misc.re_verse) + ndx = oppositeColor(i); + else + ndx = i; + if ((names == NULL) || (names[0] == '\0')) { + newColors.names[ndx] = NULL; + } else { + if (names[0] == ';') + thisName = NULL; + else + thisName = names; + names = strchr(names, ';'); + if (names != NULL) { + *names = '\0'; + names++; + } + if (thisName != 0 && !strcmp(thisName, "?")) + ReportColorRequest(xw, ndx, final); + else if (!pOldColors->names[ndx] + || (thisName + && strcmp(thisName, pOldColors->names[ndx]))) { + AllocateTermColor(xw, &newColors, ndx, thisName); + } + } + } + + if (newColors.which == 0) + return (True); + + ChangeColors(xw, &newColors); + UpdateOldColors(xw, &newColors); + return (True); +} + +/***====================================================================***/ + +#ifndef DEBUG +/* ARGSUSED */ +#endif +void +Panic(char *s GCC_UNUSED, int a GCC_UNUSED) +{ +#ifdef DEBUG + if (debug) { + fprintf(stderr, "%s: PANIC!\t", xterm_name); + fprintf(stderr, s, a); + fputs("\r\n", stderr); + fflush(stderr); + } +#endif /* DEBUG */ +} + +char * +SysErrorMsg(int n) +{ + static char unknown[] = "unknown error"; + char *s = strerror(n); + return s ? s : unknown; +} + +void +SysError(int i) +{ + static const char *table[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ,"main: ioctl() failed on FIONBIO" /* 11 */ + ,"main: ioctl() failed on F_GETFL" /* 12 */ + ,"main: ioctl() failed on F_SETFL" /* 13 */ + ,"spawn: open() failed on /dev/tty" /* 14 */ + ,"spawn: ioctl() failed on TIOCGETP" /* 15 */ + ,0 + ,"spawn: ptsname() failed" /* 17 */ + ,"spawn: open() failed on ptsname" /* 18 */ + ,"spawn: ioctl() failed on I_PUSH/\"ptem\"" /* 19 */ + ,"spawn: ioctl() failed on I_PUSH/\"consem\"" /* 20 */ + ,"spawn: ioctl() failed on I_PUSH/\"ldterm\"" /* 21 */ + ,"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" /* 22 */ + ,"spawn: ioctl() failed on TIOCSETP" /* 23 */ + ,"spawn: ioctl() failed on TIOCSETC" /* 24 */ + ,"spawn: ioctl() failed on TIOCSETD" /* 25 */ + ,"spawn: ioctl() failed on TIOCSLTC" /* 26 */ + ,"spawn: ioctl() failed on TIOCLSET" /* 27 */ + ,"spawn: initgroups() failed" /* 28 */ + ,"spawn: fork() failed" /* 29 */ + ,"spawn: exec() failed" /* 30 */ + ,0 + ,"get_pty: not enough ptys" /* 32 */ + ,0 + ,"waiting for initial map" /* 34 */ + ,"spawn: setuid() failed" /* 35 */ + ,"spawn: can't initialize window" /* 36 */ + ,0, 0, 0, 0, 0, 0, 0, 0, 0 + ,"spawn: ioctl() failed on TIOCKSET" /* 46 */ + ,"spawn: ioctl() failed on TIOCKSETC" /* 47 */ + ,"spawn: realloc of ttydev failed" /* 48 */ + ,"luit: command-line malloc failed" /* 49 */ + ,"in_put: select() failed" /* 50 */ + ,0, 0, 0 + ,"VTInit: can't initialize window" /* 54 */ + ,0, 0 + ,"HandleKeymapChange: malloc failed" /* 57 */ + ,0, 0 + ,"Tinput: select() failed" /* 60 */ + ,0, 0, 0 + ,"TekInit: can't initialize window" /* 64 */ + ,0, 0, 0, 0, 0, 0 + ,"SaltTextAway: malloc() failed" /* 71 */ + ,0, 0, 0, 0, 0, 0, 0, 0 + ,"StartLog: exec() failed" /* 80 */ + ,0, 0 + ,"xerror: XError event" /* 83 */ + ,"xioerror: X I/O error" /* 84 */ + ,0, 0, 0, 0, 0 + ,"Alloc: calloc() failed on base" /* 90 */ + ,"Alloc: calloc() failed on rows" /* 91 */ + ,"ScreenResize: realloc() failed on alt base" /* 92 */ + ,0, 0, 0 + ,"ScreenResize: malloc() or realloc() failed" /* 96 */ + ,0, 0, 0, 0, 0 + ,"ScrnPointers: malloc/realloc() failed" /* 102 */ + ,0, 0, 0, 0, 0, 0, 0 + ,"ScrollBarOn: realloc() failed on base" /* 110 */ + ,"ScrollBarOn: realloc() failed on rows" /* 111 */ + ,0, 0, 0, 0, 0, 0, 0, 0, 0 + ,"my_memmove: malloc/realloc failed" /* 121 */ + }; + int oerrno; + + oerrno = errno; + fprintf(stderr, "%s: Error %d, errno %d: ", xterm_name, i, oerrno); + fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); + if ((Cardinal) i < XtNumber(table) && table[i] != 0) { + fprintf(stderr, "Reason: %s\n", table[i]); + } + Cleanup(i); +} + +static void +Sleep(int msec) +{ + static struct timeval select_timeout; + + select_timeout.tv_sec = 0; + select_timeout.tv_usec = msec * 1000; + select(0, 0, 0, 0, &select_timeout); +} + +/* + * cleanup by sending SIGHUP to client processes + */ +void +Cleanup(int code) +{ + static Bool cleaning; + TScreen *screen = &term->screen; + + /* + * Process "-hold" and session cleanup only for a normal exit. + */ + if (code == 0) { + if (cleaning) { + hold_screen = 0; + return; + } + + cleaning = True; + need_cleanup = FALSE; + + TRACE(("Cleanup %d\n", code)); + + if (hold_screen) { + hold_screen = 2; + while (hold_screen) { + xevents(); + Sleep(10); + } + } +#if OPT_SESSION_MGT + if (resource.sessionMgt) { + XtVaSetValues(toplevel, + XtNjoinSession, False, + (XtPointer *) 0); + } +#endif + } + + if (screen->pid > 1) { + (void) kill_process_group(screen->pid, SIGHUP); + } + Exit(code); +} + +#ifndef VMS +char * +xtermFindShell(char *leaf, Bool warning) +{ + char *s; + char *d; + char *tmp; + char *result = leaf; + + TRACE(("xtermFindShell(%s)\n", leaf)); + if (*result != '\0' && strchr("+/-", *result) == 0) { + /* find it in $PATH */ + if ((s = getenv("PATH")) != 0) { + if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 1)) != 0) { + Bool found = False; + while (*s != '\0') { + strcpy(tmp, s); + for (d = tmp;; ++d) { + if (*d == ':' || *d == '\0') { + int skip = (*d != '\0'); + *d = '/'; + strcpy(d + 1, leaf); + if (skip) + ++d; + s += (d - tmp); + if (*tmp == '/' + && strstr(tmp, "..") == 0 + && access(tmp, X_OK) == 0) { + result = x_strdup(tmp); + found = True; + } + break; + } + if (found) + break; + } + if (found) + break; + } + free(tmp); + } + } + } + TRACE(("...xtermFindShell(%s)\n", result)); + if (*result != '/' + || strstr(result, "..") != 0 + || access(result, X_OK) != 0) { + if (warning) + fprintf(stderr, "No absolute path found for shell: %s\n", result); + result = 0; + } + return result; +} +#endif /* VMS */ + +/* + * sets the value of var to be arg in the Unix 4.2 BSD environment env. + * Var should end with '=' (bindings are of the form "var=value"). + * This procedure assumes the memory for the first level of environ + * was allocated using calloc, with enough extra room at the end so not + * to have to do a realloc(). + */ +void +xtermSetenv(char *var, char *value) +{ + if (value != 0) { + int envindex = 0; + size_t len = strlen(var); + + TRACE(("xtermSetenv(var=%s, value=%s)\n", var, value)); + + while (environ[envindex] != NULL) { + if (strncmp(environ[envindex], var, len) == 0) { + /* found it */ + environ[envindex] = CastMallocN(char, len + strlen(value)); + strcpy(environ[envindex], var); + strcat(environ[envindex], value); + return; + } + envindex++; + } + + TRACE(("...expanding env to %d\n", envindex + 1)); + + environ[envindex] = CastMallocN(char, len + strlen(value)); + (void) strcpy(environ[envindex], var); + strcat(environ[envindex], value); + environ[++envindex] = NULL; + } +} + +/*ARGSUSED*/ +int +xerror(Display * d, XErrorEvent * ev) +{ + fprintf(stderr, "%s: warning, error event received:\n", xterm_name); + (void) XmuPrintDefaultErrorMessage(d, ev, stderr); + Exit(ERROR_XERROR); + return 0; /* appease the compiler */ +} + +/*ARGSUSED*/ +int +xioerror(Display * dpy) +{ + int the_error = errno; + + (void) fprintf(stderr, + "%s: fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", + xterm_name, the_error, SysErrorMsg(the_error), + DisplayString(dpy)); + + Exit(ERROR_XIOERROR); + return 0; /* appease the compiler */ +} + +void +xt_error(String message) +{ + char *ptr; + + (void) fprintf(stderr, "%s Xt error: %s\n", ProgramName, message); + + /* + * Check for the obvious - Xt does a poor job of reporting this. + */ + if ((ptr = getenv("DISPLAY")) == 0 || *x_strtrim(ptr) == '\0') { + fprintf(stderr, "%s: DISPLAY is not set\n", ProgramName); + } + exit(1); +} + +int +XStrCmp(char *s1, char *s2) +{ + if (s1 && s2) + return (strcmp(s1, s2)); + if (s1 && *s1) + return (1); + if (s2 && *s2) + return (-1); + return (0); +} + +#if OPT_TEK4014 +static void +withdraw_window(Display * dpy, Window w, int scr) +{ + TRACE(("withdraw_window %#lx\n", (long) w)); + (void) XmuUpdateMapHints(dpy, w, NULL); + XWithdrawWindow(dpy, w, scr); + return; +} +#endif + +void +set_vt_visibility(Bool on) +{ + TScreen *screen = &term->screen; + + TRACE(("set_vt_visibility(%d)\n", on)); + if (on) { + if (!screen->Vshow && term) { + VTInit(); + XtMapWidget(XtParent(term)); +#if OPT_TOOLBAR + /* we need both of these during initialization */ + XtMapWidget(SHELL_OF(term)); + ShowToolbar(resource.toolBar); +#endif + screen->Vshow = True; + } + } +#if OPT_TEK4014 + else { + if (screen->Vshow && term) { + withdraw_window(XtDisplay(term), + VShellWindow, + XScreenNumberOfScreen(XtScreen(term))); + screen->Vshow = False; + } + } + set_vthide_sensitivity(); + set_tekhide_sensitivity(); + update_vttekmode(); + update_tekshow(); + update_vtshow(); +#endif + return; +} + +#if OPT_TEK4014 +void +set_tek_visibility(Bool on) +{ + TScreen *screen = &term->screen; + + TRACE(("set_tek_visibility(%d)\n", on)); + if (on) { + if (!screen->Tshow && (tekWidget || TekInit())) { + Widget tekParent = SHELL_OF(tekWidget); + XtRealizeWidget(tekParent); + XtMapWidget(XtParent(tekWidget)); +#if OPT_TOOLBAR + /* we need both of these during initialization */ + XtMapWidget(tekParent); + XtMapWidget(tekWidget); +#endif + XtOverrideTranslations(tekParent, + XtParseTranslationTable + ("<Message>WM_PROTOCOLS: DeleteWindow()")); + (void) XSetWMProtocols(XtDisplay(tekParent), + XtWindow(tekParent), + &wm_delete_window, 1); + screen->Tshow = True; + } + } else { + if (screen->Tshow && tekWidget) { + withdraw_window(XtDisplay(tekWidget), + TShellWindow, + XScreenNumberOfScreen(XtScreen(tekWidget))); + screen->Tshow = False; + } + } + set_tekhide_sensitivity(); + set_vthide_sensitivity(); + update_vtshow(); + update_tekshow(); + update_vttekmode(); + return; +} + +void +end_tek_mode(void) +{ + TScreen *screen = &term->screen; + + if (screen->TekEmu) { + FlushLog(screen); + longjmp(Tekend, 1); + } + return; +} + +void +end_vt_mode(void) +{ + TScreen *screen = &term->screen; + + if (!screen->TekEmu) { + FlushLog(screen); + screen->TekEmu = True; + longjmp(VTend, 1); + } + return; +} + +void +switch_modes(Bool tovt) /* if true, then become vt mode */ +{ + if (tovt) { + if (TekRefresh) + dorefresh(); + end_tek_mode(); /* WARNING: this does a longjmp... */ + } else { + end_vt_mode(); /* WARNING: this does a longjmp... */ + } +} + +void +hide_vt_window(void) +{ + TScreen *screen = &term->screen; + + set_vt_visibility(False); + if (!screen->TekEmu) + switch_modes(False); /* switch to tek mode */ +} + +void +hide_tek_window(void) +{ + TScreen *screen = &term->screen; + + set_tek_visibility(False); + TekRefresh = (TekLink *) 0; + if (screen->TekEmu) + switch_modes(True); /* does longjmp to vt mode */ +} +#endif /* OPT_TEK4014 */ + +static const char * +skip_punct(const char *s) +{ + while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { + ++s; + } + return s; +} + +static int +cmp_options(const void *a, const void *b) +{ + const char *s1 = skip_punct(((const OptionHelp *) a)->opt); + const char *s2 = skip_punct(((const OptionHelp *) b)->opt); + return strcmp(s1, s2); +} + +static int +cmp_resources(const void *a, const void *b) +{ + return strcmp(((const XrmOptionDescRec *) a)->option, + ((const XrmOptionDescRec *) b)->option); +} + +XrmOptionDescRec * +sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) +{ + static XrmOptionDescRec *res_array = 0; + +#ifdef NO_LEAKS + if (descs == 0 && res_array != 0) { + free(res_array); + res_array = 0; + } else +#endif + if (res_array == 0) { + Cardinal j; + + /* make a sorted index to 'resources' */ + res_array = TypeCallocN(XrmOptionDescRec, res_count); + for (j = 0; j < res_count; j++) + res_array[j] = descs[j]; + qsort(res_array, res_count, sizeof(*res_array), cmp_resources); + } + return res_array; +} + +/* + * The first time this is called, construct sorted index to the main program's + * list of options, taking into account the on/off options which will be + * compressed into one token. It's a lot simpler to do it this way than + * maintain the list in sorted form with lots of ifdef's. + */ +OptionHelp * +sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) +{ + static OptionHelp *opt_array = 0; + +#ifdef NO_LEAKS + if (descs == 0 && opt_array != 0) { + sortedOptDescs(descs, numDescs); + free(opt_array); + opt_array = 0; + } else if (options != 0 && descs != 0) +#endif + if (opt_array == 0) { + Cardinal opt_count, j; +#if OPT_TRACE + Cardinal k; + XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); + int code; + char *mesg; +#else + (void) descs; + (void) numDescs; +#endif + + /* count 'options' and make a sorted index to it */ + for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { + ; + } + opt_array = TypeCallocN(OptionHelp, opt_count + 1); + for (j = 0; j < opt_count; j++) + opt_array[j] = options[j]; + qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); + + /* supply the "turn on/off" strings if needed */ +#if OPT_TRACE + for (j = 0; j < opt_count; j++) { + if (!strncmp(opt_array[j].opt, "-/+", 3)) { + char *name = opt_array[j].opt + 3; + for (k = 0; k < numDescs; ++k) { + char *value = res_array[k].value; + if (res_array[k].option[0] == '-') { + code = -1; + } else if (res_array[k].option[0] == '+') { + code = 1; + } else { + code = 0; + } + if (x_strindex(opt_array[j].desc, "inhibit") != 0) + code = -code; + if (code != 0 + && res_array[k].value != 0 + && !strcmp(name, res_array[k].option + 1)) { + if (((code < 0) && !strcmp(value, "on")) + || ((code > 0) && !strcmp(value, "off")) + || ((code > 0) && !strcmp(value, "0"))) { + mesg = "turn on/off"; + } else { + mesg = "turn off/on"; + } + if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) { + if (strncmp(opt_array[j].desc, "turn ", 5)) { + char *s = CastMallocN(char, + strlen(mesg) + + 1 + + strlen(opt_array[j].desc)); + if (s != 0) { + sprintf(s, "%s %s", mesg, opt_array[j].desc); + opt_array[j].desc = s; + } + } else { + TRACE(("OOPS ")); + } + } + TRACE(("%s: %s %s: %s (%s)\n", + mesg, + res_array[k].option, + res_array[k].value, + opt_array[j].opt, + opt_array[j].desc)); + break; + } + } + } + } +#endif + } + return opt_array; +} + +/* + * Report the locale that xterm was started in. + */ +char * +xtermEnvLocale(void) +{ + static char *result; + + if (result == 0) { + if ((result = getenv("LC_ALL")) == 0 || *result == '\0') + if ((result = getenv("LC_CTYPE")) == 0 || *result == '\0') + if ((result = getenv("LANG")) == 0 || *result == '\0') + result = ""; + TRACE(("xtermEnvLocale ->%s\n", result)); + } + return result; +} + +char * +xtermEnvEncoding(void) +{ + static char *result; + + if (result == 0) { +#ifdef HAVE_LANGINFO_CODESET + result = nl_langinfo(CODESET); +#else + char *locale = xtermEnvLocale(); + if (*locale == 0 || !strcmp(locale, "C") || !strcmp(locale, "POSIX")) { + result = "ASCII"; + } else { + result = "ISO-8859-1"; + } +#endif + TRACE(("xtermEnvEncoding ->%s\n", result)); + } + return result; +} + +#if OPT_WIDE_CHARS +/* + * Tell whether xterm was started in a locale that uses UTF-8 encoding for + * characters. That environment is inherited by subprocesses and used in + * various library calls. + */ +Bool +xtermEnvUTF8(void) +{ + static Bool init = False; + static Bool result = False; + + if (!init) { + init = True; +#ifdef HAVE_LANGINFO_CODESET + result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); +#else + result = (strstr(xtermEnvLocale(), "UTF-8") != NULL); +#endif + TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); + } + return result; +} +#endif /* OPT_WIDE_CHARS */ + +/* + * Returns the version-string used in the "-v' message as well as a few other + * places. It is derived (when possible) from the __vendorversion__ symbol + * that some newer imake configurations define. + */ +char * +xtermVersion(void) +{ + static char *result; + if (result == 0) { + char *vendor = __vendorversion__; + char first[BUFSIZ]; + char second[BUFSIZ]; + + result = CastMallocN(char, strlen(vendor) + 9); + if (result == 0) + result = vendor; + else { + /* some vendors leave trash in this string */ + for (;;) { + if (!strncmp(vendor, "Version ", 8)) + vendor += 8; + else if (isspace(CharOf(*vendor))) + ++vendor; + else + break; + } + if (strlen(vendor) < BUFSIZ && + sscanf(vendor, "%[0-9.] %[A-Za-z_0-9.]", first, second) == 2) + sprintf(result, "%s %s(%d)", second, first, XTERM_PATCH); + else + sprintf(result, "%s(%d)", vendor, XTERM_PATCH); + } + } + return result; +} |