summaryrefslogtreecommitdiff
path: root/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc.c')
-rw-r--r--misc.c5421
1 files changed, 5421 insertions, 0 deletions
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..822b590
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,5421 @@
+/* $XTermId: misc.c,v 1.591 2012/06/06 08:32:19 tom Exp $ */
+
+/*
+ * Copyright 1999-2011,2012 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 <main.h>
+#include <xterm.h>
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/wait.h>
+
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <X11/Xlocale.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 <xtermcap.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(xw)) || \
+ (tekWidget && \
+ event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
+#else
+#define OUR_EVENT(event,Type) \
+ (event.type == Type && \
+ (event.xcrossing.window == XtWindow(XtParent(xw))))
+#endif
+
+static Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
+static Cursor make_hidden_cursor(XtermWidget);
+
+#if OPT_EXEC_XTERM
+/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
+ error; adapted from libc docs */
+static char *
+Readlink(const char *filename)
+{
+ char *buf = NULL;
+ size_t size = 100;
+ int n;
+
+ for (;;) {
+ buf = TypeRealloc(char, size, buf);
+ memset(buf, 0, size);
+
+ n = (int) readlink(filename, buf, size);
+ if (n < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ if ((unsigned) n < size) {
+ return buf;
+ }
+
+ size *= 2;
+ }
+}
+#endif /* OPT_EXEC_XTERM */
+
+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);
+}
+
+static void
+selectwindow(XtermWidget xw, int flag)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
+
+#if OPT_TEK4014
+ if (TEK4014_ACTIVE(xw)) {
+ if (!Ttoggled)
+ TCursorToggle(tekWidget, TOGGLE);
+ screen->select |= flag;
+ if (!Ttoggled)
+ TCursorToggle(tekWidget, TOGGLE);
+ } else
+#endif
+ {
+#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
+ TInput *input = lookupTInput(xw, (Widget) xw);
+ if (input && input->xic)
+ XSetICFocus(input->xic);
+#endif
+
+ if (screen->cursor_state && CursorMoved(screen))
+ HideCursor();
+ screen->select |= flag;
+ if (screen->cursor_state)
+ ShowCursor();
+ }
+ GetScrollLock(screen);
+}
+
+static void
+unselectwindow(XtermWidget xw, int flag)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
+
+ if (screen->hide_pointer) {
+ screen->hide_pointer = False;
+ xtermDisplayCursor(xw);
+ }
+
+ if (!screen->always_highlight) {
+#if OPT_TEK4014
+ if (TEK4014_ACTIVE(xw)) {
+ if (!Ttoggled)
+ TCursorToggle(tekWidget, TOGGLE);
+ screen->select &= ~flag;
+ if (!Ttoggled)
+ TCursorToggle(tekWidget, TOGGLE);
+ } else
+#endif
+ {
+#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
+ TInput *input = lookupTInput(xw, (Widget) xw);
+ if (input && input->xic)
+ XUnsetICFocus(input->xic);
+#endif
+
+ screen->select &= ~flag;
+ if (screen->cursor_state && CursorMoved(screen))
+ HideCursor();
+ if (screen->cursor_state)
+ ShowCursor();
+ }
+ }
+}
+
+static void
+DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent * ev)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
+ TRACE_FOCUS(xw, ev);
+ if (((ev->detail) != NotifyInferior) &&
+ ev->focus &&
+ !(screen->select & FOCUS))
+ selectwindow(xw, INWINDOW);
+}
+
+static void
+DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent * ev)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
+ TRACE_FOCUS(xw, ev);
+ if (((ev->detail) != NotifyInferior) &&
+ ev->focus &&
+ !(screen->select & FOCUS))
+ unselectwindow(xw, INWINDOW);
+}
+
+#ifndef XUrgencyHint
+#define XUrgencyHint (1L << 8) /* X11R5 does not define */
+#endif
+
+static void
+setXUrgency(XtermWidget xw, Bool enable)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (screen->bellIsUrgent) {
+ XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
+ if (h != 0) {
+ if (enable && !(screen->select & FOCUS)) {
+ h->flags |= XUrgencyHint;
+ } else {
+ h->flags &= ~XUrgencyHint;
+ }
+ XSetWMHints(screen->display, VShellWindow(xw), h);
+ }
+ }
+}
+
+void
+do_xevents(void)
+{
+ TScreen *screen = TScreenOf(term);
+
+ if (xtermAppPending()
+ ||
+#if defined(VMS) || defined(__VMS)
+ screen->display->qlen > 0
+#else
+ GetBytesAvailable(ConnectionNumber(screen->display)) > 0
+#endif
+ )
+ xevents();
+}
+
+void
+xtermDisplayCursor(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (screen->Vshow) {
+ if (screen->hide_pointer) {
+ TRACE(("Display hidden_cursor\n"));
+ XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
+ } else {
+ TRACE(("Display pointer_cursor\n"));
+ recolor_cursor(screen,
+ screen->pointer_cursor,
+ T_COLOR(screen, MOUSE_FG),
+ T_COLOR(screen, MOUSE_BG));
+ XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
+ }
+ }
+}
+
+void
+xtermShowPointer(XtermWidget xw, Bool enable)
+{
+ static int tried = -1;
+ TScreen *screen = TScreenOf(xw);
+
+#if OPT_TEK4014
+ if (TEK4014_SHOWN(xw))
+ enable = True;
+#endif
+
+ /*
+ * Whether we actually hide the pointer depends on the pointer-mode and
+ * the mouse-mode:
+ */
+ if (!enable) {
+ switch (screen->pointer_mode) {
+ case pNever:
+ enable = True;
+ break;
+ case pNoMouse:
+ if (screen->send_mouse_pos != MOUSE_OFF)
+ enable = True;
+ break;
+ case pAlways:
+ break;
+ }
+ }
+
+ if (enable) {
+ if (screen->hide_pointer) {
+ screen->hide_pointer = False;
+ xtermDisplayCursor(xw);
+ switch (screen->send_mouse_pos) {
+ case ANY_EVENT_MOUSE:
+ break;
+ default:
+ MotionOff(screen, xw);
+ break;
+ }
+ }
+ } else if (!(screen->hide_pointer) && (tried <= 0)) {
+ if (screen->hidden_cursor == 0) {
+ screen->hidden_cursor = make_hidden_cursor(xw);
+ }
+ if (screen->hidden_cursor == 0) {
+ tried = 1;
+ } else {
+ tried = 0;
+ screen->hide_pointer = True;
+ xtermDisplayCursor(xw);
+ MotionOn(screen, xw);
+ }
+ }
+}
+
+#if OPT_TRACE
+static void
+TraceExposeEvent(XEvent * arg)
+{
+ XExposeEvent *event = (XExposeEvent *) arg;
+
+ TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n",
+ event->serial,
+ event->count,
+ event->y,
+ event->x,
+ event->height,
+ event->width,
+ event->window));
+}
+
+#else
+#define TraceExposeEvent(event) /* nothing */
+#endif
+
+/* true if p contains q */
+#define ExposeContains(p,q) \
+ ((p)->y <= (q)->y \
+ && (p)->x <= (q)->x \
+ && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
+ && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
+
+static XtInputMask
+mergeExposeEvents(XEvent * target)
+{
+ XEvent next_event;
+ XExposeEvent *p, *q;
+
+ TRACE(("pending Expose...?\n"));
+ TraceExposeEvent(target);
+ XtAppNextEvent(app_con, target);
+ p = (XExposeEvent *) target;
+
+ while (XtAppPending(app_con)
+ && XtAppPeekEvent(app_con, &next_event)
+ && next_event.type == Expose) {
+ Boolean merge_this = False;
+
+ TraceExposeEvent(&next_event);
+ q = (XExposeEvent *) (&next_event);
+ XtAppNextEvent(app_con, &next_event);
+
+ /*
+ * If either window is contained within the other, merge the events.
+ * The traces show that there are also cases where a full repaint of
+ * a window is broken into 3 or more rectangles, which do not arrive
+ * in the same instant. We could merge those if xterm were modified
+ * to skim several events ahead.
+ */
+ if (p->window == q->window) {
+ if (ExposeContains(p, q)) {
+ TRACE(("pending Expose...merged forward\n"));
+ merge_this = True;
+ next_event = *target;
+ } else if (ExposeContains(q, p)) {
+ TRACE(("pending Expose...merged backward\n"));
+ merge_this = True;
+ }
+ }
+ if (!merge_this) {
+ XtDispatchEvent(target);
+ }
+ *target = next_event;
+ }
+ XtDispatchEvent(target);
+ return XtAppPending(app_con);
+}
+
+#if OPT_TRACE
+static void
+TraceConfigureEvent(XEvent * arg)
+{
+ XConfigureEvent *event = (XConfigureEvent *) arg;
+
+ TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n",
+ event->serial,
+ event->y,
+ event->x,
+ event->height,
+ event->width,
+ event->window));
+}
+
+#else
+#define TraceConfigureEvent(event) /* nothing */
+#endif
+
+/*
+ * On entry, we have peeked at the event queue and see a configure-notify
+ * event. Remove that from the queue so we can look further.
+ *
+ * Then, as long as there is a configure-notify event in the queue, remove
+ * that. If the adjacent events are for different windows, process the older
+ * event and update the event used for comparing windows. If they are for the
+ * same window, only the newer event is of interest.
+ *
+ * Finally, process the (remaining) configure-notify event.
+ */
+static XtInputMask
+mergeConfigureEvents(XEvent * target)
+{
+ XEvent next_event;
+ XConfigureEvent *p, *q;
+
+ XtAppNextEvent(app_con, target);
+ p = (XConfigureEvent *) target;
+
+ TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no"));
+ TraceConfigureEvent(target);
+
+ if (XtAppPending(app_con)
+ && XtAppPeekEvent(app_con, &next_event)
+ && next_event.type == ConfigureNotify) {
+ Boolean merge_this = False;
+
+ TraceConfigureEvent(&next_event);
+ XtAppNextEvent(app_con, &next_event);
+ q = (XConfigureEvent *) (&next_event);
+
+ if (p->window == q->window) {
+ TRACE(("pending Configure...merged\n"));
+ merge_this = True;
+ }
+ if (!merge_this) {
+ TRACE(("pending Configure...skipped\n"));
+ XtDispatchEvent(target);
+ }
+ *target = next_event;
+ }
+ XtDispatchEvent(target);
+ return XtAppPending(app_con);
+}
+
+/*
+ * Filter redundant Expose- and ConfigureNotify-events. This is limited to
+ * adjacent events because there could be other event-loop processing. Absent
+ * that limitation, it might be possible to scan ahead to find when the screen
+ * would be completely updated, skipping unnecessary re-repainting before that
+ * point.
+ *
+ * Note: all cases should allow doing XtAppNextEvent if result is true.
+ */
+XtInputMask
+xtermAppPending(void)
+{
+ XtInputMask result = XtAppPending(app_con);
+ XEvent this_event;
+
+ while (result && XtAppPeekEvent(app_con, &this_event)) {
+ if (this_event.type == Expose) {
+ result = mergeExposeEvents(&this_event);
+ } else if (this_event.type == ConfigureNotify) {
+ result = mergeConfigureEvents(&this_event);
+ } else {
+ TRACE(("pending %s\n", visibleEventType(this_event.type)));
+ break;
+ }
+ }
+ return result;
+}
+
+void
+xevents(void)
+{
+ XtermWidget xw = term;
+ TScreen *screen = TScreenOf(xw);
+ XEvent event;
+ XtInputMask input_mask;
+
+ if (need_cleanup)
+ Cleanup(0);
+
+ if (screen->scroll_amt)
+ FlushScroll(xw);
+ /*
+ * 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 = xtermAppPending()) != 0) {
+ if (input_mask & XtIMTimer)
+ XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
+#if OPT_SESSION_MGT
+ /*
+ * Session management events are alternative input events. Deal with
+ * them in the same way.
+ */
+ else if (input_mask & XtIMAlternateInput)
+ XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
+#endif
+ else
+ break;
+ }
+
+ /*
+ * 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 (screen->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(xw, &event.xcrossing);
+ } else if (OUR_EVENT(event, LeaveNotify)) {
+ DoSpecialLeaveNotify(xw, &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(xw)) {
+ SendMousePosition(xw, &event);
+ xtermShowPointer(xw, True);
+ continue;
+ }
+
+ /*
+ * If the event is interesting (and not a keyboard event), turn the
+ * mouse pointer back on.
+ */
+ if (screen->hide_pointer) {
+ switch (event.xany.type) {
+ case KeyPress:
+ case KeyRelease:
+ case ButtonPress:
+ case ButtonRelease:
+ /* also these... */
+ case Expose:
+ case NoExpose:
+ case PropertyNotify:
+ case ClientMessage:
+ break;
+ default:
+ xtermShowPointer(xw, True);
+ break;
+ }
+ }
+
+ 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 (xtermAppPending() & XtIMXEvent);
+}
+
+static Cursor
+make_hidden_cursor(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+ Cursor c;
+ Display *dpy = screen->display;
+ XFontStruct *fn;
+
+ static XColor dummy;
+
+ /*
+ * Prefer nil2 (which is normally available) to "fixed" (which is supposed
+ * to be "always" available), since it's a smaller glyph in case the
+ * server insists on drawing _something_.
+ */
+ TRACE(("Ask for nil2 font\n"));
+ if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
+ TRACE(("...Ask for fixed font\n"));
+ fn = XLoadQueryFont(dpy, DEFFONT);
+ }
+
+ if (fn != 0) {
+ /* a space character seems to work as a cursor (dots are not needed) */
+ c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
+ XFreeFont(dpy, fn);
+ } else {
+ c = 0;
+ }
+ TRACE(("XCreateGlyphCursor ->%#lx\n", c));
+ return (c);
+}
+
+Cursor
+make_colored_cursor(unsigned cursorindex, /* index into font */
+ unsigned long fg, /* pixel value */
+ unsigned long bg) /* pixel value */
+{
+ TScreen *screen = TScreenOf(term);
+ Cursor c;
+ Display *dpy = screen->display;
+
+ c = XCreateFontCursor(dpy, cursorindex);
+ if (c != None) {
+ 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 insert-seven-bit for %p\n", (void *) w));
+ Input(term, &event->xkey, False);
+}
+
+/* ARGSUSED */
+void
+HandleEightBitKeyPressed(Widget w GCC_UNUSED,
+ XEvent * event,
+ String * params GCC_UNUSED,
+ Cardinal *nparams GCC_UNUSED)
+{
+ TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
+ Input(term, &event->xkey, True);
+}
+
+/* ARGSUSED */
+void
+HandleStringEvent(Widget w GCC_UNUSED,
+ XEvent * event GCC_UNUSED,
+ String * params,
+ Cardinal *nparams)
+{
+
+ if (*nparams != 1)
+ return;
+
+ if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
+ const char *abcdef = "ABCDEF";
+ const char *xxxxxx;
+ Char c;
+ UString p;
+ unsigned value = 0;
+
+ for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
+ '\0'; p++) {
+ value *= 16;
+ if (c >= '0' && c <= '9')
+ value += (unsigned) (c - '0');
+ else if ((xxxxxx = strchr(abcdef, c)) != 0)
+ value += (unsigned) (xxxxxx - abcdef) + 10;
+ else
+ break;
+ }
+ if (c == '\0') {
+ Char hexval[2];
+ hexval[0] = (Char) value;
+ hexval[1] = 0;
+ StringInput(term, hexval, (size_t) 1);
+ }
+ } else {
+ StringInput(term, (const Char *) *params, strlen(*params));
+ }
+}
+
+#if OPT_EXEC_XTERM
+
+#ifndef PROCFS_ROOT
+#define PROCFS_ROOT "/proc"
+#endif
+
+/* ARGSUSED */
+void
+HandleSpawnTerminal(Widget w GCC_UNUSED,
+ XEvent * event GCC_UNUSED,
+ String * params,
+ Cardinal *nparams)
+{
+ TScreen *screen = TScreenOf(term);
+ char *child_cwd = NULL;
+ char *child_exe;
+ pid_t pid;
+
+ /*
+ * Try to find the actual program which is running in the child process.
+ * This works for Linux. If we cannot find the program, fall back to the
+ * xterm program (which is usually adequate). Give up if we are given only
+ * a relative path to xterm, since that would not always match $PATH.
+ */
+ child_exe = Readlink(PROCFS_ROOT "/self/exe");
+ if (!child_exe) {
+ if (strncmp(ProgramName, "./", (size_t) 2)
+ && strncmp(ProgramName, "../", (size_t) 3)) {
+ child_exe = xtermFindShell(ProgramName, True);
+ } else {
+ xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
+ }
+ if (child_exe == 0)
+ return;
+ }
+
+ /*
+ * Determine the current working directory of the child so that we can
+ * spawn a new terminal in the same directory.
+ *
+ * If we cannot get the CWD of the child, just use our own.
+ */
+ if (screen->pid) {
+ char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
+ sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid);
+ child_cwd = Readlink(child_cwd_link);
+ }
+
+ /* The reaper will take care of cleaning up the child */
+ pid = fork();
+ if (pid == -1) {
+ xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
+ } else if (!pid) {
+ /* We are the child */
+ if (child_cwd) {
+ IGNORE_RC(chdir(child_cwd)); /* We don't care if this fails */
+ }
+
+ if (setuid(screen->uid) == -1
+ || setgid(screen->gid) == -1) {
+ xtermWarning("Cannot reset uid/gid\n");
+ } else {
+ unsigned myargc = *nparams + 1;
+ char **myargv = TypeMallocN(char *, myargc + 1);
+ unsigned n = 0;
+
+ myargv[n++] = child_exe;
+
+ while (n < myargc) {
+ myargv[n++] = *params++;
+ }
+
+ myargv[n] = 0;
+ execv(child_exe, myargv);
+
+ /* If we get here, we've failed */
+ xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
+ }
+ _exit(0);
+ } else {
+ /* We are the parent; clean up */
+ if (child_cwd)
+ free(child_cwd);
+ if (child_exe)
+ free(child_exe);
+ }
+}
+#endif /* OPT_EXEC_XTERM */
+
+/*
+ * 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) {
+ const char *value = params[0];
+ int need = (int) strlen(value);
+ int used = (int) (VTbuffer->next - VTbuffer->buffer);
+ int have = (int) (VTbuffer->last - VTbuffer->buffer);
+
+ if (have - used + need < BUF_SIZE) {
+
+ fillPtyData(term, VTbuffer, value, (int) strlen(value));
+
+ TRACE(("Interpret %s\n", value));
+ VTbuffer->update++;
+ }
+ }
+}
+
+/*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"));
+ TRACE_FOCUS(w, event);
+}
+
+/*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"));
+ TRACE_FOCUS(w, event);
+}
+
+/*ARGSUSED*/
+void
+HandleFocusChange(Widget w GCC_UNUSED,
+ XtPointer eventdata GCC_UNUSED,
+ XEvent * ev,
+ Boolean * cont GCC_UNUSED)
+{
+ XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
+ XtermWidget xw = term;
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("HandleFocusChange type=%s, mode=%d, detail=%d\n",
+ visibleEventType(event->type),
+ event->mode,
+ event->detail));
+ TRACE_FOCUS(xw, event);
+
+ if (screen->quiet_grab
+ && (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
+ /* EMPTY */ ;
+ } else if (event->type == FocusIn) {
+ setXUrgency(xw, False);
+
+ /*
+ * 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(xw, INWINDOW);
+ }
+ selectwindow(xw,
+ ((event->detail == NotifyPointer)
+ ? INWINDOW
+ : FOCUS));
+ SendFocusButton(xw, event);
+ } else {
+#if OPT_FOCUS_EVENT
+ if (event->type == FocusOut) {
+ SendFocusButton(xw, event);
+ }
+#endif
+ /*
+ * XGrabKeyboard() will generate NotifyGrab event that we want to
+ * ignore.
+ */
+ if (event->mode != NotifyGrab) {
+ unselectwindow(xw,
+ ((event->detail == NotifyPointer)
+ ? INWINDOW
+ : FOCUS));
+ }
+ if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
+ Bell(xw, XkbBI_Info, 100);
+ ReverseVideo(xw);
+ screen->grabbedKbd = False;
+ update_securekbd();
+ }
+ }
+}
+
+static long lastBellTime; /* in milliseconds */
+
+#if defined(HAVE_XKB_BELL_EXT)
+static Atom
+AtomBell(XtermWidget xw, int which)
+{
+#define DATA(name) { XkbBI_##name, XkbBN_##name }
+ static struct {
+ int value;
+ const char *name;
+ } table[] = {
+ DATA(Info),
+ DATA(MarginBell),
+ DATA(MinorError),
+ DATA(TerminalBell)
+ };
+ Cardinal n;
+ Atom result = None;
+
+ for (n = 0; n < XtNumber(table); ++n) {
+ if (table[n].value == which) {
+ result = XInternAtom(XtDisplay(xw), table[n].name, False);
+ break;
+ }
+ }
+ return result;
+}
+#endif
+
+void
+xtermBell(XtermWidget xw, int which, int percent)
+{
+ TScreen *screen = TScreenOf(xw);
+#if defined(HAVE_XKB_BELL_EXT)
+ Atom tony = AtomBell(xw, which);
+#endif
+
+ switch (which) {
+ case XkbBI_Info:
+ case XkbBI_MinorError:
+ case XkbBI_MajorError:
+ case XkbBI_TerminalBell:
+ switch (screen->warningVolume) {
+ case bvOff:
+ percent = -100;
+ break;
+ case bvLow:
+ break;
+ case bvHigh:
+ percent = 100;
+ break;
+ }
+ break;
+ case XkbBI_MarginBell:
+ switch (screen->marginVolume) {
+ case bvOff:
+ percent = -100;
+ break;
+ case bvLow:
+ break;
+ case bvHigh:
+ percent = 100;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+#if defined(HAVE_XKB_BELL_EXT)
+ if (tony != None) {
+ XkbBell(screen->display, VShellWindow(xw), percent, tony);
+ } else
+#endif
+ XBell(screen->display, percent);
+}
+
+void
+Bell(XtermWidget xw, int which, int percent)
+{
+ TScreen *screen = TScreenOf(xw);
+ struct timeval curtime;
+ long now_msecs;
+
+ TRACE(("BELL %d %d%%\n", which, percent));
+ if (!XtIsRealized((Widget) xw)) {
+ return;
+ }
+
+ setXUrgency(xw, True);
+
+ /* 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 {
+ xtermBell(xw, which, percent);
+ }
+
+ if (screen->poponbell)
+ XRaiseWindow(screen->display, VShellWindow(xw));
+
+ 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();
+ 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)
+{
+ int y = 0;
+ int x = 0;
+
+ if (screen->flash_line) {
+ y = CursorY(screen, screen->cur_row);
+ height = (unsigned) FontHeight(screen);
+ }
+ XFillRectangle(screen->display, window, visualGC, x, y, width, height);
+ XFlush(screen->display);
+ Sleep(VB_DELAY);
+ XFillRectangle(screen->display, window, visualGC, x, y, width, height);
+}
+
+void
+VisualBell(void)
+{
+ TScreen *screen = TScreenOf(term);
+
+ 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 (TEK4014_ACTIVE(term)) {
+ TekScreen *tekscr = TekScreenOf(tekWidget);
+ flashWindow(screen, TWindow(tekscr), visualGC,
+ TFullWidth(tekscr),
+ TFullHeight(tekscr));
+ } 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 = TScreenOf(term);
+
+ if (ev->xproperty.atom == XA_NOTICE) {
+ screen->bellInProgress = False;
+ }
+}
+
+void
+xtermWarning(const char *fmt,...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s: ", ProgramName);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ (void) fflush(stderr);
+
+ va_end(ap);
+}
+
+void
+xtermPerror(const char *fmt,...)
+{
+ char *msg = strerror(errno);
+ va_list ap;
+
+ fprintf(stderr, "%s: ", ProgramName);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", msg);
+ (void) fflush(stderr);
+
+ va_end(ap);
+}
+
+Window
+WMFrameWindow(XtermWidget xw)
+{
+ Window win_root, win_current, *children;
+ Window win_parent = 0;
+ unsigned int nchildren;
+
+ win_current = XtWindow(xw);
+
+ /* find the parent which is child of root */
+ do {
+ if (win_parent)
+ win_current = win_parent;
+ XQueryTree(TScreenOf(xw)->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(TScreen * screen, CELL * cell, LineData ** ld)
+{
+ int result = -1;
+ int firstLine = -(screen->savedlines);
+
+ *ld = getLineData(screen, cell->row);
+ while (cell->row >= firstLine) {
+ if (--(cell->col) >= 0) {
+ result = (int) (*ld)->charData[cell->col];
+ break;
+ }
+ if (--(cell->row) < firstLine)
+ break; /* ...there is no previous line */
+ *ld = getLineData(screen, cell->row);
+ cell->col = MaxCols(screen);
+ if (!LineTstWrapped(*ld)) {
+ result = ' '; /* treat lines as separate */
+ break;
+ }
+ }
+ return result;
+}
+
+static char *
+dabbrev_prev_word(TScreen * screen, CELL * cell, LineData ** ld)
+{
+ static char ab[MAXWLEN];
+
+ char *abword;
+ int c;
+ char *ab_end = (ab + MAXWLEN - 1);
+ char *result = 0;
+
+ abword = ab_end;
+ *abword = '\0'; /* end of string marker */
+
+ while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
+ IS_WORD_CONSTITUENT(c)) {
+ if (abword > ab) /* store only |MAXWLEN| last chars */
+ *(--abword) = (char) c;
+ }
+
+ if (c >= 0) {
+ result = abword;
+ } else if (abword != ab_end) {
+ result = abword;
+ }
+
+ if (result != 0) {
+ while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
+ !IS_WORD_CONSTITUENT(c)) {
+ ; /* skip preceding spaces */
+ }
+ (cell->col)++; /* can be | > screen->max_col| */
+ }
+ return result;
+}
+
+static int
+dabbrev_expand(TScreen * screen)
+{
+ int pty = screen->respond; /* file descriptor of pty */
+
+ static CELL cell;
+ static char *dabbrev_hint = 0, *lastexpansion = 0;
+ static unsigned int expansions;
+
+ char *expansion;
+ Char *copybuffer;
+ size_t hint_len;
+ size_t del_cnt;
+ size_t buf_cnt;
+ int result = 0;
+ LineData *ld;
+
+ if (!screen->dabbrev_working) { /* initialize */
+ expansions = 0;
+ cell.col = screen->cur_col;
+ cell.row = screen->cur_row;
+
+ if (dabbrev_hint != 0)
+ free(dabbrev_hint);
+
+ if ((dabbrev_hint = dabbrev_prev_word(screen, &cell, &ld)) != 0) {
+
+ if (lastexpansion != 0)
+ free(lastexpansion);
+
+ if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
+
+ /* make own copy */
+ if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
+ screen->dabbrev_working = True;
+ /* we are in the middle of dabbrev process */
+ }
+ } else {
+ return result;
+ }
+ } else {
+ return result;
+ }
+ if (!screen->dabbrev_working) {
+ if (lastexpansion != 0) {
+ free(lastexpansion);
+ lastexpansion = 0;
+ }
+ return result;
+ }
+ }
+
+ if (dabbrev_hint == 0)
+ return result;
+
+ hint_len = strlen(dabbrev_hint);
+ for (;;) {
+ if ((expansion = dabbrev_prev_word(screen, &cell, &ld)) == 0) {
+ if (expansions >= 2) {
+ expansions = 0;
+ cell.col = screen->cur_col;
+ cell.row = screen->cur_row;
+ continue;
+ }
+ break;
+ }
+ 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 != 0) {
+ del_cnt = strlen(lastexpansion) - hint_len;
+ buf_cnt = del_cnt + strlen(expansion) - hint_len;
+
+ if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
+ /* delete previous expansion */
+ memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
+ memmove(copybuffer + del_cnt,
+ expansion + hint_len,
+ strlen(expansion) - hint_len);
+ v_write(pty, copybuffer, (unsigned) buf_cnt);
+ /* v_write() just reset our flag */
+ screen->dabbrev_working = True;
+ free(copybuffer);
+
+ free(lastexpansion);
+
+ if ((lastexpansion = strdup(expansion)) != 0) {
+ result = 1;
+ expansions++;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*ARGSUSED*/
+void
+HandleDabbrevExpand(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *nparams GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ if (!dabbrev_expand(screen))
+ Bell(xw, XkbBI_TerminalBell, 0);
+ }
+}
+#endif /* OPT_DABBREV */
+
+#if OPT_MAXIMIZE
+/*ARGSUSED*/
+void
+HandleDeIconify(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *nparams GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ XMapWindow(screen->display, VShellWindow(xw));
+ }
+}
+
+/*ARGSUSED*/
+void
+HandleIconify(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *nparams GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ XIconifyWindow(screen->display,
+ VShellWindow(xw),
+ DefaultScreen(screen->display));
+ }
+}
+
+int
+QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
+{
+ TScreen *screen = TScreenOf(xw);
+ 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(xw)),
+ &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(xw),
+ &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 = (unsigned) hints.max_width;
+ if ((unsigned) hints.max_height < *height)
+ *height = (unsigned) hints.max_height;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+void
+RequestMaximize(XtermWidget xw, int maximize)
+{
+ TScreen *screen = TScreenOf(xw);
+ XWindowAttributes wm_attrs, vshell_attrs;
+ unsigned root_width, root_height;
+
+ TRACE(("RequestMaximize %s\n", maximize ? "maximize" : "restore"));
+
+ if (maximize) {
+
+ if (QueryMaximize(xw, &root_width, &root_height)) {
+
+ if (XGetWindowAttributes(screen->display,
+ WMFrameWindow(xw),
+ &wm_attrs)) {
+
+ if (XGetWindowAttributes(screen->display,
+ VShellWindow(xw),
+ &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 = (unsigned) vshell_attrs.width;
+ screen->restore_height = (unsigned) 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 -=
+ (unsigned) ((wm_attrs.width - vshell_attrs.width)
+ + (wm_attrs.border_width * 2));
+ root_height -=
+ (unsigned) ((wm_attrs.height - vshell_attrs.height)
+ + (wm_attrs.border_width * 2));
+
+ XMoveResizeWindow(screen->display, VShellWindow(xw),
+ 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(xw),
+ screen->restore_x,
+ screen->restore_y,
+ screen->restore_width,
+ screen->restore_height);
+ }
+ }
+}
+
+/*ARGSUSED*/
+void
+HandleMaximize(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *nparams GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ RequestMaximize(xw, 1);
+ }
+}
+
+/*ARGSUSED*/
+void
+HandleRestoreSize(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *nparams GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ RequestMaximize(xw, 0);
+ }
+}
+#endif /* OPT_MAXIMIZE */
+
+void
+Redraw(void)
+{
+ TScreen *screen = TScreenOf(term);
+ XExposeEvent event;
+
+ TRACE(("Redraw\n"));
+
+ 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 (TEK4014_SHOWN(term)) {
+ TekScreen *tekscr = TekScreenOf(tekWidget);
+ event.window = TWindow(tekscr);
+ 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,
+ (int) 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;
+ xtermWarning("cannot open %s: %d:%s\n",
+ 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;
+ xtermWarning("cannot open %s: %d:%s\n",
+ 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) {
+ xtermWarning("you do not own %s\n", 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, unsigned 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",
+ (int) uid, (int) geteuid(),
+ (int) gid, (int) 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 == TScreenOf(term)->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) {
+ xtermWarning("unable to reset group-id\n");
+ result = -1;
+ }
+ if (setuid(screen->uid) == -1) {
+ xtermWarning("unable to reset user-id\n");
+ 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)
+{
+ XtermWidget xw = term;
+ TScreen *screen = TScreenOf(xw);
+
+#ifdef SYSV
+ (void) signal(SIGPIPE, SIG_IGN);
+#endif /* SYSV */
+ if (screen->logging)
+ CloseLog(xw);
+}
+#endif /* ALLOWLOGFILEEXEC */
+
+void
+StartLog(XtermWidget xw)
+{
+ static char *log_default;
+ TScreen *screen = TScreenOf(xw);
+
+ 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, (int) 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 ((shell = x_getenv("SHELL")) == NULL) {
+
+ if (x_getpwuid(screen->uid, &pw)) {
+ x_getlogin(screen->uid, &pw);
+ if (*(pw.pw_shell)) {
+ shell = pw.pw_shell;
+ }
+ }
+ }
+
+ if (shell == 0) {
+ static char dummy[] = "/bin/sh";
+ shell = dummy;
+ }
+
+ 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);
+
+ xtermWarning("Can't exec `%s'\n", &screen->logfile[1]);
+ exit(ERROR_LOGEXEC);
+ }
+ close(p[0]);
+ screen->logfd = p[1];
+ signal(SIGPIPE, logpipe);
+#else
+ Bell(xw, XkbBI_Info, 0);
+ Bell(xw, 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(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (!screen->logging || (screen->inhibit & I_LOG))
+ return;
+ FlushLog(xw);
+ close(screen->logfd);
+ screen->logging = False;
+ update_logging();
+}
+
+void
+FlushLog(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ 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 = (int) (cp - screen->logstart)) > 0) {
+ IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
+ }
+ screen->logstart = VTbuffer->next;
+ }
+}
+
+#endif /* ALLOWLOGGING */
+
+/***====================================================================***/
+
+#if OPT_ISO_COLORS
+static void
+ReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
+{
+ if (AllowColorOps(xw, ecGetAnsiColor)) {
+ XColor color;
+ Colormap cmap = xw->core.colormap;
+ char buffer[80];
+
+ TRACE(("ReportAnsiColorRequest %d\n", colornum));
+ color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
+ XQueryColor(TScreenOf(xw)->display, cmap, &color);
+ sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
+ colornum,
+ color.red,
+ color.green,
+ color.blue);
+ unparseputc1(xw, ANSI_OSC);
+ unparseputs(xw, buffer);
+ unparseputc1(xw, final);
+ unparse_end(xw);
+ }
+}
+
+static void
+getColormapInfo(Display * display, unsigned *typep, unsigned *sizep)
+{
+ int numFound;
+ XVisualInfo myTemplate, *visInfoPtr;
+
+ myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(display,
+ XDefaultScreen(display)));
+ visInfoPtr = XGetVisualInfo(display, (long) VisualIDMask,
+ &myTemplate, &numFound);
+ *typep = (numFound >= 1) ? (unsigned) visInfoPtr->class : 0;
+ *sizep = (numFound >= 1) ? (unsigned) visInfoPtr->colormap_size : 0;
+
+ XFree((char *) visInfoPtr);
+
+ TRACE(("getColormapInfo type %d (%s), size %d\n",
+ *typep, ((*typep & 1) ? "dynamic" : "static"), *sizep));
+}
+
+#define MAX_COLORTABLE 4096
+
+/*
+ * Make only one call to XQueryColors(), since it can be slow.
+ */
+static Boolean
+loadColorTable(XtermWidget xw, unsigned length)
+{
+ Colormap cmap = xw->core.colormap;
+ TScreen *screen = TScreenOf(xw);
+ unsigned i;
+ Boolean result = False;
+
+ if (screen->cmap_data == 0
+ && length != 0
+ && length < MAX_COLORTABLE) {
+ screen->cmap_data = TypeMallocN(XColor, (size_t) length);
+ if (screen->cmap_data != 0) {
+ screen->cmap_size = length;
+
+ for (i = 0; i < screen->cmap_size; i++) {
+ screen->cmap_data[i].pixel = (unsigned long) i;
+ }
+ result = (Boolean) (XQueryColors(screen->display,
+ cmap,
+ screen->cmap_data,
+ (int) screen->cmap_size) != 0);
+ }
+ }
+ return result;
+}
+
+/*
+ * 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.
+ * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
+ *
+ * These provide some introduction:
+ * http://en.wikipedia.org/wiki/YIQ
+ * for an introduction to YIQ weights.
+ * http://en.wikipedia.org/wiki/Luminance_(video)
+ * for a discussion of luma.
+ * http://en.wikipedia.org/wiki/YUV
+ *
+ * Return False if not able to find or allocate a color.
+ */
+static Boolean
+allocateClosestRGB(XtermWidget xw, Colormap cmap, XColor * def)
+{
+ TScreen *screen = TScreenOf(xw);
+ Boolean result = False;
+ char *tried;
+ double diff, thisRGB, bestRGB;
+ unsigned attempts;
+ unsigned bestInx;
+ unsigned cmap_type;
+ unsigned cmap_size;
+ unsigned i;
+
+ getColormapInfo(screen->display, &cmap_type, &cmap_size);
+
+ if ((cmap_type & 1) != 0) {
+
+ if (loadColorTable(xw, cmap_size)) {
+
+ tried = TypeCallocN(char, (size_t) cmap_size);
+ if (tried != 0) {
+
+ /*
+ * Try (possibly each entry in the color map) to find the best
+ * approximation to the requested color.
+ */
+ for (attempts = 0; attempts < cmap_size; attempts++) {
+ Boolean first = True;
+
+ bestRGB = 0.0;
+ bestInx = 0;
+ for (i = 0; i < cmap_size; i++) {
+ if (!tried[bestInx]) {
+ /*
+ * Look for the best match based on luminance.
+ * Measure this by the least-squares difference of
+ * the weighted R/G/B components from the color map
+ * versus the requested color. Use the Y (luma)
+ * component of the YIQ color space model for
+ * weights that correspond to the luminance.
+ */
+#define AddColorWeight(weight, color) \
+ diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
+ thisRGB = diff * diff
+
+ AddColorWeight(0.30, red);
+ AddColorWeight(0.61, green);
+ AddColorWeight(0.11, blue);
+
+ if (first || (thisRGB < bestRGB)) {
+ first = False;
+ bestInx = i;
+ bestRGB = thisRGB;
+ }
+ }
+ }
+ if (XAllocColor(screen->display, cmap,
+ &screen->cmap_data[bestInx]) != 0) {
+ *def = screen->cmap_data[bestInx];
+ TRACE(("...closest %x/%x/%x\n", def->red,
+ def->green, def->blue));
+ result = True;
+ break;
+ }
+ /*
+ * It failed - either the color map entry was readonly, or
+ * another client has allocated the entry. Mark the entry
+ * so we will ignore it
+ */
+ tried[bestInx] = True;
+ }
+ free(tried);
+ }
+ }
+ }
+ return result;
+}
+
+#ifndef ULONG_MAX
+#define ULONG_MAX (unsigned long)(~(0L))
+#endif
+
+#define CheckColor(result, value) \
+ result = 0; \
+ if (value.red) \
+ result |= 1; \
+ if (value.green) \
+ result |= 2; \
+ if (value.blue) \
+ result |= 4
+
+#define SelectColor(state, value, result) \
+ switch (state) { \
+ default: \
+ case 1: \
+ result = value.red; \
+ break; \
+ case 2: \
+ result = value.green; \
+ break; \
+ case 4: \
+ result = value.blue; \
+ break; \
+ }
+
+/*
+ * Check if the color map consists of values in exactly one of the red, green
+ * or blue columns. If it is not, we do not know how to use it for the exact
+ * match.
+ */
+static int
+simpleColors(XColor * colortable, unsigned length)
+{
+ unsigned n;
+ int state = -1;
+ int check;
+
+ for (n = 0; n < length; ++n) {
+ if (state == -1) {
+ CheckColor(state, colortable[n]);
+ if (state == 0)
+ state = -1;
+ }
+ if (state > 0) {
+ CheckColor(check, colortable[n]);
+ if (check > 0 && check != state) {
+ state = 0;
+ break;
+ }
+ }
+ }
+ switch (state) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ return state;
+}
+
+static unsigned
+searchColors(XColor * colortable, unsigned length, unsigned color, int state)
+{
+ unsigned result = 0;
+ unsigned n;
+ unsigned long best = ULONG_MAX;
+ unsigned long diff;
+ unsigned value;
+
+ for (n = 0; n < length; ++n) {
+ SelectColor(state, colortable[n], value);
+ diff = (color - value);
+ diff *= diff;
+ if (diff < best) {
+#if 0
+ TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
+ n, color,
+ colortable[n].red,
+ colortable[n].green,
+ colortable[n].blue,
+ diff));
+#endif
+ result = n;
+ best = diff;
+ }
+ }
+ SelectColor(state, colortable[result], value);
+ return value;
+}
+
+/*
+ * This is a workaround for a longstanding defect in the X libraries.
+ *
+ * According to
+ * http://www.unix.com/man-page/all/3x/XAllocColoA/
+ *
+ * XAllocColor() acts differently on static and dynamic visuals. On Pseu-
+ * doColor, DirectColor, and GrayScale visuals, XAllocColor() fails if
+ * there are no unallocated colorcells and no allocated read-only cell
+ * exactly matches the requested RGB values. On StaticColor, TrueColor,
+ * and StaticGray visuals, XAllocColor() returns the closest RGB values
+ * available in the colormap. The colorcell_in_out structure returns the
+ * actual RGB values allocated.
+ *
+ * That is, XAllocColor() should suffice unless the color map is full. In that
+ * case, allocateClosesRGB() is useful for the dynamic display classes such as
+ * PseudoColor. It is not useful for TrueColor, since XQueryColors() does not
+ * return regular RGB triples (unless a different scheme was used for
+ * specifying the pixel values); only the blue value is filled in. However, it
+ * is filled in with the colors that the server supports.
+ *
+ * Also (the reason for this function), XAllocColor() does not really work as
+ * described. For some TrueColor configurations it merely returns a close
+ * approximation, but not the closest.
+ */
+static Boolean
+allocateExactRGB(XtermWidget xw, Colormap cmap, XColor * def)
+{
+ XColor save = *def;
+ TScreen *screen = TScreenOf(xw);
+ Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
+
+ /*
+ * If this is a statically allocated display, e.g., TrueColor, see if we
+ * can improve on the result by using the color values actually supported
+ * by the server.
+ */
+ if (result) {
+ unsigned cmap_type;
+ unsigned cmap_size;
+ int state;
+
+ getColormapInfo(screen->display, &cmap_type, &cmap_size);
+
+ if ((cmap_type & 1) == 0) {
+ XColor temp = *def;
+
+ if (loadColorTable(xw, cmap_size)
+ && (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
+#define SearchColors(which) temp.which = (unsigned short) searchColors(screen->cmap_data, cmap_size, save.which, state)
+ SearchColors(red);
+ SearchColors(green);
+ SearchColors(blue);
+ if (XAllocColor(screen->display, cmap, &temp) != 0) {
+#if OPT_TRACE
+ if (temp.red != save.red
+ || temp.green != save.green
+ || temp.blue != save.blue) {
+ TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
+ save.red, save.green, save.blue,
+ temp.red, temp.green, temp.blue));
+ } else {
+ TRACE(("...no improvement for %x/%x/%x\n",
+ save.red, save.green, save.blue));
+ }
+#endif
+ *def = temp;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Allocate a color for the "ANSI" colors. That actually includes colors up
+ * to 256.
+ *
+ * Returns
+ * -1 on error
+ * 0 on no change
+ * 1 if a new color was allocated.
+ */
+static int
+AllocateAnsiColor(XtermWidget xw,
+ ColorRes * res,
+ const char *spec)
+{
+ int result;
+ XColor def;
+
+ if (xtermAllocColor(xw, &def, spec)) {
+ if (
+#if OPT_COLOR_RES
+ res->mode == True &&
+#endif
+ EQL_COLOR_RES(res, def.pixel)) {
+ result = 0;
+ } else {
+ result = 1;
+ SET_COLOR_RES(res, def.pixel);
+ TRACE(("AllocateAnsiColor[%d] %s (pixel 0x%06lx)\n",
+ (int) (res - TScreenOf(xw)->Acolors), spec, def.pixel));
+#if OPT_COLOR_RES
+ if (!res->mode)
+ result = 0;
+ res->mode = True;
+#endif
+ }
+ } else {
+ TRACE(("AllocateAnsiColor %s (failed)\n", spec));
+ result = -1;
+ }
+ return (result);
+}
+
+#if OPT_COLOR_RES
+Pixel
+xtermGetColorRes(XtermWidget xw, ColorRes * res)
+{
+ Pixel result = 0;
+
+ if (res->mode) {
+ result = res->value;
+ } else {
+ TRACE(("xtermGetColorRes for Acolors[%d]\n",
+ (int) (res - TScreenOf(xw)->Acolors)));
+
+ if (res >= TScreenOf(xw)->Acolors) {
+ assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
+
+ if (AllocateAnsiColor(xw, res, res->resource) < 0) {
+ res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
+ res->mode = -True;
+ xtermWarning("Cannot allocate color \"%s\"\n",
+ NonNull(res->resource));
+ }
+ result = res->value;
+ } else {
+ result = 0;
+ }
+ }
+ return result;
+}
+#endif
+
+static int
+ChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
+{
+ int code;
+
+ if (color < 0 || color >= MAXCOLORS) {
+ code = -1;
+ } else {
+ ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
+
+ TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
+ code = AllocateAnsiColor(xw, res, name);
+ }
+ return code;
+}
+
+/*
+ * Set or query entries in the Acolors[] array by parsing pairs of color/name
+ * values from the given buffer.
+ *
+ * The color can be any legal index into Acolors[], which consists of the
+ * 16/88/256 "ANSI" colors, followed by special color values for the various
+ * colorXX resources. The indices for the special color values are not
+ * simple to work with, so an alternative is to use the calls which pass in
+ * 'first' set to the beginning of those indices.
+ *
+ * If the name is "?", report to the host the current value for the color.
+ */
+static Bool
+ChangeAnsiColorRequest(XtermWidget xw,
+ char *buf,
+ int first,
+ int final)
+{
+ char *name;
+ int color;
+ int repaint = False;
+ int code;
+ int last = (MAXCOLORS - first);
+
+ 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 >= last)
+ break; /* quit on any error */
+ buf = strchr(name, ';');
+ if (buf) {
+ *buf = '\0';
+ buf++;
+ }
+ if (!strcmp(name, "?")) {
+ ReportAnsiColorRequest(xw, color + first, final);
+ } else {
+ code = ChangeOneAnsiColor(xw, color + first, name);
+ if (code < 0) {
+ /* stop on any error */
+ break;
+ } else if (code > 0) {
+ repaint = True;
+ }
+ /* FIXME: free old color somehow? We aren't for the other color
+ * change style (dynamic colors).
+ */
+ }
+ }
+
+ return (repaint);
+}
+
+static Bool
+ResetOneAnsiColor(XtermWidget xw, int color, int start)
+{
+ Bool repaint = False;
+ int last = MAXCOLORS - start;
+
+ if (color >= 0 && color < last) {
+ ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
+
+ if (res->mode) {
+ /* a color has been allocated for this slot - test further... */
+ if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
+ repaint = True;
+ }
+ }
+ }
+ return repaint;
+}
+
+int
+ResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
+{
+ int repaint = 0;
+ int color;
+
+ TRACE(("ResetAnsiColorRequest(%s)\n", buf));
+ if (*buf != '\0') {
+ /* reset specific colors */
+ while (!IsEmpty(buf)) {
+ char *next;
+
+ color = (int) strtol(buf, &next, 10);
+ if ((next == buf) || (color < 0))
+ break; /* no number at all */
+ if (next != 0) {
+ if (strchr(";", *next) == 0)
+ break; /* unexpected delimiter */
+ ++next;
+ }
+
+ if (ResetOneAnsiColor(xw, color, start)) {
+ ++repaint;
+ }
+ buf = next;
+ }
+ } else {
+ TRACE(("...resetting all %d colors\n", MAXCOLORS));
+ for (color = 0; color < MAXCOLORS; ++color) {
+ if (ResetOneAnsiColor(xw, color, start)) {
+ ++repaint;
+ }
+ }
+ }
+ TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
+ return repaint;
+}
+#else
+#define allocateClosestRGB(xw, cmap, def) 0
+#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
+#endif /* OPT_ISO_COLORS */
+
+static Boolean
+xtermAllocColor(XtermWidget xw, XColor * def, const char *spec)
+{
+ Boolean result = False;
+ TScreen *screen = TScreenOf(xw);
+ Colormap cmap = xw->core.colormap;
+
+ if (XParseColor(screen->display, cmap, spec, def)
+ && (allocateExactRGB(xw, cmap, def)
+ || allocateClosestRGB(xw, cmap, def))) {
+ TRACE(("xtermAllocColor -> %x/%x/%x\n",
+ def->red, def->green, def->blue));
+ result = True;
+ }
+ return result;
+}
+
+#if OPT_PASTE64
+static void
+ManipulateSelectionData(XtermWidget xw, TScreen * screen, char *buf, int final)
+{
+#define PDATA(a,b) { a, #b }
+ static struct {
+ char given;
+ String 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),
+ };
+
+ const char *base = buf;
+ char *used = x_strdup(base);
+ Cardinal j, n = 0;
+ String *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, "?")) {
+ if (AllowWindowOps(xw, ewGetSelection)) {
+ TRACE(("Getting selection\n"));
+ unparseputc1(xw, ANSI_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, (Time) 0, select_args, n, NULL);
+ }
+ } else {
+ if (AllowWindowOps(xw, ewSetSelection)) {
+ TRACE(("Setting selection with %s\n", buf));
+ ClearSelectionBuffer(screen);
+ while (*buf != '\0')
+ AppendToSelectionBuffer(screen, CharOf(*buf++));
+ CompleteSelection(xw, select_args, n);
+ }
+ }
+ }
+}
+#endif /* OPT_PASTE64 */
+
+/***====================================================================***/
+
+#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
+
+static Bool
+xtermIsPrintable(XtermWidget xw, Char ** bufp, Char * last)
+{
+ TScreen *screen = TScreenOf(xw);
+ Bool result = False;
+ Char *cp = *bufp;
+ Char *next = cp;
+
+ (void) screen;
+ (void) last;
+
+#if OPT_WIDE_CHARS
+ if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
+ PtyData data;
+
+ if (decodeUtf8(fakePtyData(&data, cp, last))) {
+ if (data.utf_data != UCS_REPL
+ && (data.utf_data >= 128 ||
+ ansi_table[data.utf_data] == CASE_PRINT)) {
+ next += (data.utf_size - 1);
+ result = True;
+ } else {
+ result = False;
+ }
+ } else {
+ result = False;
+ }
+ } else
+#endif
+#if OPT_C1_PRINT
+ if (screen->c1_printable
+ && (*cp >= 128 && *cp < 160)) {
+ result = True;
+ } else
+#endif
+ if (ansi_table[*cp] == CASE_PRINT) {
+ result = True;
+ }
+ *bufp = next;
+ return result;
+}
+
+/***====================================================================***/
+
+/*
+ * Enum corresponding to the actual OSC codes rather than the internal
+ * array indices. Compare with TermColors.
+ */
+typedef enum {
+ OSC_TEXT_FG = 10
+ ,OSC_TEXT_BG
+ ,OSC_TEXT_CURSOR
+ ,OSC_MOUSE_FG
+ ,OSC_MOUSE_BG
+#if OPT_TEK4014
+ ,OSC_TEK_FG = 15
+ ,OSC_TEK_BG
+#endif
+#if OPT_HIGHLIGHT_COLOR
+ ,OSC_HIGHLIGHT_BG = 17
+#endif
+#if OPT_TEK4014
+ ,OSC_TEK_CURSOR = 18
+#endif
+#if OPT_HIGHLIGHT_COLOR
+ ,OSC_HIGHLIGHT_FG = 19
+#endif
+ ,OSC_NCOLORS
+} OscTextColors;
+
+/*
+ * Map codes to OSC controls that can reset colors.
+ */
+#define OSC_RESET 100
+#define OSC_Reset(code) (code) + OSC_RESET
+
+static ScrnColors *pOldColors = NULL;
+
+static Bool
+GetOldColors(XtermWidget xw)
+{
+ int i;
+ if (pOldColors == NULL) {
+ pOldColors = TypeXtMalloc(ScrnColors);
+ if (pOldColors == NULL) {
+ xtermWarning("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
+#if OPT_HIGHLIGHT_COLOR
+ case HIGHLIGHT_FG:
+ n = HIGHLIGHT_BG;
+ break;
+ case HIGHLIGHT_BG:
+ n = HIGHLIGHT_FG;
+ break;
+#endif
+ default:
+ break;
+ }
+ return n;
+}
+
+static void
+ReportColorRequest(XtermWidget xw, int ndx, int final)
+{
+ if (AllowColorOps(xw, ecGetColor)) {
+ 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(TScreenOf(xw)->display, cmap, &color);
+ sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
+ color.red,
+ color.green,
+ color.blue);
+ TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
+ ndx, pOldColors->colors[ndx], buffer));
+ unparseputc1(xw, ANSI_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);
+}
+
+/*
+ * OSC codes are constant, but the indices for the color arrays depend on how
+ * xterm is compiled.
+ */
+static int
+OscToColorIndex(OscTextColors mode)
+{
+ int result = 0;
+
+#define CASE(name) case OSC_##name: result = name; break
+ switch (mode) {
+ CASE(TEXT_FG);
+ CASE(TEXT_BG);
+ CASE(TEXT_CURSOR);
+ CASE(MOUSE_FG);
+ CASE(MOUSE_BG);
+#if OPT_TEK4014
+ CASE(TEK_FG);
+ CASE(TEK_BG);
+#endif
+#if OPT_HIGHLIGHT_COLOR
+ CASE(HIGHLIGHT_BG);
+ CASE(HIGHLIGHT_FG);
+#endif
+#if OPT_TEK4014
+ CASE(TEK_CURSOR);
+#endif
+ case OSC_NCOLORS:
+ break;
+ }
+ return result;
+}
+
+static Bool
+ChangeColorsRequest(XtermWidget xw,
+ int start,
+ char *names,
+ int final)
+{
+ Bool result = False;
+ char *thisName;
+ ScrnColors newColors;
+ int i, ndx;
+
+ TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
+
+ if (GetOldColors(xw)) {
+ newColors.which = 0;
+ for (i = 0; i < NCOLORS; i++) {
+ newColors.names[i] = NULL;
+ }
+ for (i = start; i < OSC_NCOLORS; i++) {
+ ndx = OscToColorIndex((OscTextColors) i);
+ if (xw->misc.re_verse)
+ ndx = oppositeColor(ndx);
+
+ if (IsEmpty(names)) {
+ newColors.names[ndx] = NULL;
+ } else {
+ if (names[0] == ';')
+ thisName = NULL;
+ else
+ thisName = names;
+ names = strchr(names, ';');
+ if (names != NULL) {
+ *names++ = '\0';
+ }
+ 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, False);
+ }
+ }
+ }
+
+ if (newColors.which != 0) {
+ ChangeColors(xw, &newColors);
+ UpdateOldColors(xw, &newColors);
+ }
+ result = True;
+ }
+ return result;
+}
+
+static Bool
+ResetColorsRequest(XtermWidget xw,
+ int code)
+{
+ Bool result = False;
+ const char *thisName;
+ ScrnColors newColors;
+ int ndx;
+
+ TRACE(("ResetColorsRequest code=%d\n", code));
+
+#if OPT_COLOR_RES
+ if (GetOldColors(xw)) {
+ ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
+ if (xw->misc.re_verse)
+ ndx = oppositeColor(ndx);
+
+ thisName = xw->screen.Tcolors[ndx].resource;
+
+ newColors.which = 0;
+ newColors.names[ndx] = NULL;
+
+ if (thisName != 0
+ && pOldColors->names[ndx] != 0
+ && strcmp(thisName, pOldColors->names[ndx])) {
+ AllocateTermColor(xw, &newColors, ndx, thisName, False);
+
+ if (newColors.which != 0) {
+ ChangeColors(xw, &newColors);
+ UpdateOldColors(xw, &newColors);
+ }
+ }
+ result = True;
+ }
+#endif
+ return result;
+}
+
+#if OPT_SHIFT_FONTS
+/*
+ * Initially, 'source' points to '#' or '?'.
+ *
+ * Look for an optional sign and optional number. If those are found, lookup
+ * the corresponding menu font entry.
+ */
+static int
+ParseShiftedFont(XtermWidget xw, String source, String * target)
+{
+ TScreen *screen = TScreenOf(xw);
+ int num = screen->menu_font_number;
+ int rel = 0;
+
+ if (*++source == '+') {
+ rel = 1;
+ source++;
+ } else if (*source == '-') {
+ rel = -1;
+ source++;
+ }
+
+ if (isdigit(CharOf(*source))) {
+ int val = atoi(source);
+ if (rel > 0)
+ rel = val;
+ else if (rel < 0)
+ rel = -val;
+ else
+ num = val;
+ }
+
+ if (rel != 0) {
+ num = lookupRelativeFontSize(xw,
+ screen->menu_font_number, rel);
+
+ }
+ TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
+ *target = source;
+ return num;
+}
+
+static void
+QueryFontRequest(XtermWidget xw, String buf, int final)
+{
+ if (AllowFontOps(xw, efGetFont)) {
+ TScreen *screen = TScreenOf(xw);
+ Bool success = True;
+ int num;
+ String base = buf + 1;
+ const char *name = 0;
+ char temp[10];
+
+ num = ParseShiftedFont(xw, buf, &buf);
+ if (num < 0
+ || num > fontMenu_lastBuiltin) {
+ Bell(xw, XkbBI_MinorError, 0);
+ success = False;
+ } else {
+#if OPT_RENDERFONT
+ if (UsingRenderFont(xw)) {
+ name = getFaceName(xw, False);
+ } else
+#endif
+ if ((name = screen->MenuFontName(num)) == 0) {
+ success = False;
+ }
+ }
+
+ unparseputc1(xw, ANSI_OSC);
+ unparseputs(xw, "50");
+
+ if (success) {
+ unparseputc(xw, ';');
+ if (buf >= base) {
+ /* identify the font-entry, unless it is the current one */
+ if (*buf != '\0') {
+ unparseputc(xw, '#');
+ sprintf(temp, "%d", num);
+ unparseputs(xw, temp);
+ if (*name != '\0')
+ unparseputc(xw, ' ');
+ }
+ }
+ unparseputs(xw, name);
+ }
+
+ unparseputc1(xw, final);
+ unparse_end(xw);
+ }
+}
+
+static void
+ChangeFontRequest(XtermWidget xw, String buf)
+{
+ if (AllowFontOps(xw, efSetFont)) {
+ TScreen *screen = TScreenOf(xw);
+ Bool success = True;
+ int num;
+ VTFontNames fonts;
+ char *name;
+
+ /*
+ * If the font specification is a "#", followed by an optional sign and
+ * optional number, lookup the corresponding menu font entry.
+ *
+ * Further, if the "#", etc., is followed by a font name, use that
+ * to load the font entry.
+ */
+ if (*buf == '#') {
+ num = ParseShiftedFont(xw, buf, &buf);
+
+ if (num < 0
+ || num > fontMenu_lastBuiltin) {
+ Bell(xw, XkbBI_MinorError, 0);
+ success = False;
+ } else {
+ /*
+ * Skip past the optional number, and any whitespace to look
+ * for a font specification within the control.
+ */
+ while (isdigit(CharOf(*buf))) {
+ ++buf;
+ }
+ while (isspace(CharOf(*buf))) {
+ ++buf;
+ }
+#if OPT_RENDERFONT
+ if (UsingRenderFont(xw)) {
+ /* EMPTY */
+ /* there is only one font entry to load */
+ ;
+ } else
+#endif
+ {
+ /*
+ * Normally there is no font specified in the control.
+ * But if there is, simply overwrite the font entry.
+ */
+ if (*buf == '\0') {
+ if ((buf = screen->MenuFontName(num)) == 0) {
+ success = False;
+ }
+ }
+ }
+ }
+ } else {
+ num = screen->menu_font_number;
+ }
+ name = x_strtrim(buf);
+ if (success && !IsEmpty(name)) {
+#if OPT_RENDERFONT
+ if (UsingRenderFont(xw)) {
+ setFaceName(xw, name);
+ xtermUpdateFontInfo(xw, True);
+ } else
+#endif
+ {
+ memset(&fonts, 0, sizeof(fonts));
+ fonts.f_n = name;
+ SetVTFont(xw, num, True, &fonts);
+ }
+ } else {
+ Bell(xw, XkbBI_MinorError, 0);
+ }
+ free(name);
+ }
+}
+#endif /* OPT_SHIFT_FONTS */
+
+/***====================================================================***/
+
+void
+do_osc(XtermWidget xw, Char * oscbuf, size_t len, int final)
+{
+ TScreen *screen = TScreenOf(xw);
+ int mode;
+ Char *cp;
+ int state = 0;
+ char *buf = 0;
+ char temp[2];
+#if OPT_ISO_COLORS
+ int ansi_colors = 0;
+#endif
+ Bool need_data = True;
+
+ TRACE(("do_osc %s\n", oscbuf));
+
+ (void) screen;
+
+ /*
+ * 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",
+ (int) (cp - oscbuf)));
+ return;
+ }
+ state = 2;
+ break;
+ case 2:
+ buf = (char *) cp;
+ state = 3;
+ /* FALLTHRU */
+ default:
+ if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
+ switch (mode) {
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ TRACE(("do_osc found nonprinting char %02X offset %d\n",
+ CharOf(*cp),
+ (int) (cp - oscbuf)));
+ return;
+ }
+ }
+ }
+ }
+
+ /*
+ * Most OSC controls other than resets require data. Handle the others as
+ * a special case.
+ */
+ switch (mode) {
+#if OPT_ISO_COLORS
+ case OSC_Reset(4):
+ case OSC_Reset(5):
+ case OSC_Reset(OSC_TEXT_FG):
+ case OSC_Reset(OSC_TEXT_BG):
+ case OSC_Reset(OSC_TEXT_CURSOR):
+ case OSC_Reset(OSC_MOUSE_FG):
+ case OSC_Reset(OSC_MOUSE_BG):
+#if OPT_HIGHLIGHT_COLOR
+ case OSC_Reset(OSC_HIGHLIGHT_BG):
+ case OSC_Reset(OSC_HIGHLIGHT_FG):
+#endif
+#if OPT_TEK4014
+ case OSC_Reset(OSC_TEK_FG):
+ case OSC_Reset(OSC_TEK_BG):
+ case OSC_Reset(OSC_TEK_CURSOR):
+#endif
+ need_data = False;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /*
+ * Check if we have data when we want, and not when we do not want it.
+ * Either way, that is a malformed control sequence, and will be ignored.
+ */
+ if (IsEmpty(buf)) {
+ if (need_data) {
+ TRACE(("do_osc found no data\n"));
+ return;
+ }
+ temp[0] = '\0';
+ buf = temp;
+ } else if (!need_data) {
+ TRACE(("do_osc found found unwanted data\n"));
+ return;
+ }
+
+ switch (mode) {
+ case 0: /* new icon name and title */
+ ChangeIconName(xw, buf);
+ ChangeTitle(xw, buf);
+ break;
+
+ case 1: /* new icon name only */
+ ChangeIconName(xw, buf);
+ break;
+
+ case 2: /* new title only */
+ ChangeTitle(xw, buf);
+ break;
+
+ case 3: /* change X property */
+ if (AllowWindowOps(xw, ewSetXprop))
+ ChangeXprop(buf);
+ break;
+#if OPT_ISO_COLORS
+ case 5:
+ ansi_colors = NUM_ANSI_COLORS;
+ /* FALLTHRU */
+ case 4:
+ if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
+ xtermRepaint(xw);
+ break;
+ case OSC_Reset(5):
+ ansi_colors = NUM_ANSI_COLORS;
+ /* FALLTHRU */
+ case OSC_Reset(4):
+ if (ResetAnsiColorRequest(xw, buf, ansi_colors))
+ xtermRepaint(xw);
+ break;
+#endif
+ case OSC_TEXT_FG:
+ case OSC_TEXT_BG:
+ case OSC_TEXT_CURSOR:
+ case OSC_MOUSE_FG:
+ case OSC_MOUSE_BG:
+#if OPT_HIGHLIGHT_COLOR
+ case OSC_HIGHLIGHT_BG:
+ case OSC_HIGHLIGHT_FG:
+#endif
+#if OPT_TEK4014
+ case OSC_TEK_FG:
+ case OSC_TEK_BG:
+ case OSC_TEK_CURSOR:
+#endif
+ if (xw->misc.dynamicColors) {
+ ChangeColorsRequest(xw, mode, buf, final);
+ }
+ break;
+ case OSC_Reset(OSC_TEXT_FG):
+ case OSC_Reset(OSC_TEXT_BG):
+ case OSC_Reset(OSC_TEXT_CURSOR):
+ case OSC_Reset(OSC_MOUSE_FG):
+ case OSC_Reset(OSC_MOUSE_BG):
+#if OPT_HIGHLIGHT_COLOR
+ case OSC_Reset(OSC_HIGHLIGHT_BG):
+ case OSC_Reset(OSC_HIGHLIGHT_FG):
+#endif
+#if OPT_TEK4014
+ case OSC_Reset(OSC_TEK_FG):
+ case OSC_Reset(OSC_TEK_BG):
+ case OSC_Reset(OSC_TEK_CURSOR):
+#endif
+ if (xw->misc.dynamicColors) {
+ ResetColorsRequest(xw, mode);
+ }
+ 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 (strcmp(buf, "?")
+ && (cp = CastMallocN(char, strlen(buf)) != NULL)) {
+ strcpy(cp, buf);
+ if (screen->logfile)
+ free(screen->logfile);
+ screen->logfile = cp;
+ break;
+ }
+#endif
+ Bell(xw, XkbBI_Info, 0);
+ Bell(xw, XkbBI_Info, 0);
+ break;
+#endif /* ALLOWLOGGING */
+
+ case 50:
+#if OPT_SHIFT_FONTS
+ if (*buf == '?') {
+ QueryFontRequest(xw, buf, final);
+ } else if (xw->misc.shift_fonts) {
+ ChangeFontRequest(xw, buf);
+ }
+#endif /* OPT_SHIFT_FONTS */
+ break;
+ case 51:
+ /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
+ break;
+
+#if OPT_PASTE64
+ case 52:
+ 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.
+ */
+ default:
+ TRACE(("do_osc - unrecognized code\n"));
+ break;
+ }
+ 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(const char **cp)
+{
+ int result = -1;
+ int c;
+
+ for (;;) {
+ if ((c = **cp) != '\0')
+ *cp = *cp + 1;
+ if (c == ';' || c == '\0')
+ break;
+ if ((result = x_hex2int(c)) >= 0)
+ break;
+ }
+
+ return result;
+}
+
+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(const char *cp)
+{
+ while (*cp) {
+ const 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) + (unsigned) (*cp++ - '0');
+ if (*cp == '/') {
+ cp++;
+ while ((hi = udk_value(&cp)) >= 0
+ && (lo = udk_value(&cp)) >= 0) {
+ str[len++] = (char) ((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, const 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 >= ANSI_SPA && ch <= 0x2f) {
+ if (len < 2)
+ DscsName[len++] = (char) ch;
+ } else if (ch >= 0x30 && ch <= 0x7e) {
+ DscsName[len++] = (char) 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 = CharOf(ch - 0x3f);
+ for (n = 0; n < 6; ++n) {
+ bits[row + n][col] = CharOf((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, const char **string)
+{
+ const char *cp = *string;
+ ParmType nparam = 0;
+
+ memset(params, 0, sizeof(*params));
+ while (*cp != '\0') {
+ Char ch = CharOf(*cp++);
+
+ if (isdigit(ch)) {
+ if (nparam < NPARAM) {
+ params->a_param[nparam] =
+ (ParmType) ((params->a_param[nparam] * 10)
+ + (ch - '0'));
+ }
+ } else if (ch == ';') {
+ if (++nparam < NPARAM)
+ params->a_nparam = nparam;
+ } else if (ch < 32) {
+ /* EMPTY */ ;
+ } 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 = TScreenOf(xw);
+ char reply[BUFSIZ];
+ const char *cp = (const char *) dcsbuf;
+ Bool okay;
+ ANSI params;
+
+ TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) 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 */
+ if (screen->vtXX_level < 2) {
+ /* actually none of DECRQSS is valid for vt100's */
+ break;
+ }
+ 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, "s")) { /* DECSLRM */
+ if (screen->vtXX_level >= 4) { /* VT420 */
+ sprintf(reply, "%d;%ds",
+ screen->lft_marg + 1,
+ screen->rgt_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_256_COLORS || OPT_88_COLORS
+ if_OPT_ISO_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);
+ }
+ });
+#elif OPT_ISO_COLORS
+ if_OPT_ISO_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);
+ });
+#endif
+ strcat(reply, "m");
+ } else if (!strcmp(cp, " q")) { /* DECSCUSR */
+ int code = 0;
+ if (screen->cursor_underline != 0)
+ code |= 2;
+#if OPT_BLINK_CURS
+ if (screen->cursor_blink_esc == 0)
+ code |= 1;
+#endif
+ sprintf(reply, "%d%s", code + 1, cp);
+ } else
+ okay = False;
+
+ if (okay) {
+ unparseputc1(xw, ANSI_DCS);
+ unparseputc(xw, okay ? '1' : '0');
+ unparseputc(xw, '$');
+ unparseputc(xw, 'r');
+ cp = reply;
+ unparseputs(xw, cp);
+ unparseputc1(xw, ANSI_ST);
+ } else {
+ unparseputc(xw, ANSI_CAN);
+ }
+ } else {
+ unparseputc(xw, ANSI_CAN);
+ }
+ break;
+#if OPT_TCAP_QUERY
+ case '+':
+ cp++;
+ switch (*cp) {
+ case 'p':
+ if (AllowTcapOps(xw, etSetTcap)) {
+ set_termcap(xw, cp + 1);
+ }
+ break;
+ case 'q':
+ if (AllowTcapOps(xw, etGetTcap)) {
+ Bool fkey;
+ unsigned state;
+ int code;
+ const char *tmp;
+ const char *parsed = ++cp;
+
+ code = xtermcapKeycode(xw, &parsed, &state, &fkey);
+
+ unparseputc1(xw, ANSI_DCS);
+
+ unparseputc(xw, code >= 0 ? '1' : '0');
+
+ unparseputc(xw, '+');
+ unparseputc(xw, 'r');
+
+ while (*cp != 0 && (code >= -1)) {
+ 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;
+#if OPT_ISO_COLORS
+ /* XK_COLORS is a fake code for the "Co" entry (maximum
+ * number of colors) */
+ if (code == XK_COLORS) {
+ unparseputn(xw, NUM_ANSI_COLORS);
+ } else
+#endif
+ if (code == XK_TCAPNAME) {
+ unparseputs(xw, resource.term_name);
+ } 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, ANSI_ST);
+ }
+ break;
+ }
+ break;
+#endif
+ default:
+ if (screen->vtXX_level >= 2) { /* VT220 */
+ parse_ansi_params(&params, &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(&params, cp);
+ break;
+ }
+ }
+ break;
+ }
+ unparse_end(xw);
+}
+
+#if OPT_DEC_RECTOPS
+enum {
+ mdUnknown = 0,
+ mdMaybeSet = 1,
+ mdMaybeReset = 2,
+ mdAlwaysSet = 3,
+ mdAlwaysReset = 4
+};
+
+#define MdBool(bool) ((bool) ? mdMaybeSet : mdMaybeReset)
+#define MdFlag(mode,flag) MdBool((mode) & (flag))
+
+/*
+ * Reply is the same format as the query, with pair of mode/value:
+ * 0 - not recognized
+ * 1 - set
+ * 2 - reset
+ * 3 - permanently set
+ * 4 - permanently reset
+ * Only one mode can be reported at a time.
+ */
+void
+do_rpm(XtermWidget xw, int nparams, int *params)
+{
+ ANSI reply;
+ int result = 0;
+ int count = 0;
+
+ TRACE(("do_rpm %d:%d\n", nparams, params[0]));
+ memset(&reply, 0, sizeof(reply));
+ if (nparams >= 1) {
+ switch (params[0]) {
+ case 1: /* GATM */
+ result = mdAlwaysReset;
+ break;
+ case 2:
+ result = MdFlag(xw->keyboard.flags, MODE_KAM);
+ break;
+ case 3: /* CRM */
+ result = mdMaybeReset;
+ break;
+ case 4:
+ result = MdFlag(xw->flags, INSERT);
+ break;
+ case 5: /* SRTM */
+ case 7: /* VEM */
+ case 10: /* HEM */
+ case 11: /* PUM */
+ result = mdAlwaysReset;
+ break;
+ case 12:
+ result = MdFlag(xw->keyboard.flags, MODE_SRM);
+ break;
+ case 13: /* FEAM */
+ case 14: /* FETM */
+ case 15: /* MATM */
+ case 16: /* TTM */
+ case 17: /* SATM */
+ case 18: /* TSM */
+ case 19: /* EBM */
+ result = mdAlwaysReset;
+ break;
+ case 20:
+ result = MdFlag(xw->flags, LINEFEED);
+ break;
+ }
+ reply.a_param[count++] = (ParmType) params[0];
+ reply.a_param[count++] = (ParmType) result;
+ }
+ reply.a_type = ANSI_CSI;
+ reply.a_nparam = (ParmType) count;
+ reply.a_inters = '$';
+ reply.a_final = 'y';
+ unparseseq(xw, &reply);
+}
+
+void
+do_decrpm(XtermWidget xw, int nparams, int *params)
+{
+ ANSI reply;
+ int result = 0;
+ int count = 0;
+
+ TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
+ memset(&reply, 0, sizeof(reply));
+ if (nparams >= 1) {
+ TScreen *screen = TScreenOf(xw);
+
+ switch (params[0]) {
+ case 1: /* DECCKM */
+ result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
+ break;
+ case 2: /* DECANM - ANSI/VT52 mode */
+#if OPT_VT52_MODE
+ result = MdBool(screen->vtXX_level >= 1);
+#else
+ result = mdMaybeSet;
+#endif
+ break;
+ case 3: /* DECCOLM */
+ result = MdFlag(xw->flags, IN132COLUMNS);
+ break;
+ case 4: /* DECSCLM (slow scroll) */
+ result = MdFlag(xw->flags, SMOOTHSCROLL);
+ break;
+ case 5: /* DECSCNM */
+ result = MdFlag(xw->flags, REVERSE_VIDEO);
+ break;
+ case 6: /* DECOM */
+ result = MdFlag(xw->flags, ORIGIN);
+ break;
+ case 7: /* DECAWM */
+ result = MdFlag(xw->flags, WRAPAROUND);
+ break;
+ case 8: /* DECARM */
+ result = mdAlwaysReset;
+ break;
+ case SET_X10_MOUSE: /* X10 mouse */
+ result = MdBool(screen->send_mouse_pos == X10_MOUSE);
+ break;
+#if OPT_TOOLBAR
+ case 10: /* rxvt */
+ result = MdBool(resource.toolBar);
+ break;
+#endif
+#if OPT_BLINK_CURS
+ case 12: /* att610: Start/stop blinking cursor */
+ result = MdBool(screen->cursor_blink_res);
+ break;
+#endif
+ case 18: /* DECPFF: print form feed */
+ result = MdBool(PrinterOf(screen).printer_formfeed);
+ break;
+ case 19: /* DECPEX: print extent */
+ result = MdBool(PrinterOf(screen).printer_extent);
+ break;
+ case 25: /* DECTCEM: Show/hide cursor (VT200) */
+ result = MdBool(screen->cursor_set);
+ break;
+ case 30: /* rxvt */
+ result = MdBool(screen->fullVwin.sb_info.width != OFF);
+ break;
+#if OPT_SHIFT_FONTS
+ case 35: /* rxvt */
+ result = MdBool(xw->misc.shift_fonts);
+ break;
+#endif
+#if OPT_TEK4014
+ case 38: /* DECTEK */
+ result = MdBool(TEK4014_ACTIVE(xw));
+ break;
+#endif
+ case 40: /* 132 column mode */
+ result = MdBool(screen->c132);
+ break;
+ case 41: /* curses hack */
+ result = MdBool(screen->curses);
+ break;
+ case 42: /* DECNRCM national charset (VT220) */
+ result = MdFlag(xw->flags, NATIONAL);
+ break;
+ case 44: /* margin bell */
+ result = MdBool(screen->marginbell);
+ break;
+ case 45: /* reverse wraparound */
+ result = MdFlag(xw->flags, REVERSEWRAP);
+ break;
+#ifdef ALLOWLOGGING
+ case 46: /* logging */
+#ifdef ALLOWLOGFILEONOFF
+ result = MdBool(screen->logging);
+#endif /* ALLOWLOGFILEONOFF */
+ break;
+#endif
+ case 1049: /* alternate buffer & cursor */
+ /* FALLTHRU */
+ case 1047:
+ /* FALLTHRU */
+ case 47: /* alternate buffer */
+ result = MdBool(screen->whichBuf);
+ break;
+ case 66: /* DECNKM */
+ result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
+ break;
+ case 67: /* DECBKM */
+ result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
+ break;
+ case 69: /* DECLRMM */
+ result = MdFlag(xw->flags, LEFT_RIGHT);
+ break;
+ case 95: /* DECNCSM */
+ result = MdFlag(xw->flags, NOCLEAR_COLM);
+ break;
+ case SET_VT200_MOUSE: /* xterm bogus sequence */
+ result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
+ break;
+ case SET_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */
+ result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
+ break;
+ case SET_BTN_EVENT_MOUSE:
+ result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
+ break;
+ case SET_ANY_EVENT_MOUSE:
+ result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
+ break;
+#if OPT_FOCUS_EVENT
+ case SET_FOCUS_EVENT_MOUSE:
+ result = MdBool(screen->send_focus_pos);
+ break;
+#endif
+ case SET_EXT_MODE_MOUSE:
+ /* FALLTHRU */
+ case SET_SGR_EXT_MODE_MOUSE:
+ /* FALLTHRU */
+ case SET_URXVT_EXT_MODE_MOUSE:
+ result = MdBool(screen->extend_coords == params[0]);
+ break;
+ case 1010: /* rxvt */
+ result = MdBool(screen->scrollttyoutput);
+ break;
+ case 1011: /* rxvt */
+ result = MdBool(screen->scrollkey);
+ break;
+ case 1034:
+ result = MdBool(screen->eight_bit_meta);
+ break;
+#if OPT_NUM_LOCK
+ case 1035:
+ result = MdBool(xw->misc.real_NumLock);
+ break;
+ case 1036:
+ result = MdBool(screen->meta_sends_esc);
+ break;
+#endif
+ case 1037:
+ result = MdBool(screen->delete_is_del);
+ break;
+#if OPT_NUM_LOCK
+ case 1039:
+ result = MdBool(screen->alt_sends_esc);
+ break;
+#endif
+ case 1040:
+ result = MdBool(screen->keepSelection);
+ break;
+ case 1041:
+ result = MdBool(screen->selectToClipboard);
+ break;
+ case 1042:
+ result = MdBool(screen->bellIsUrgent);
+ break;
+ case 1043:
+ result = MdBool(screen->poponbell);
+ break;
+ case 1048:
+ result = MdBool(screen->sc[screen->whichBuf].saved);
+ break;
+#if OPT_TCAP_FKEYS
+ case 1050:
+ result = MdBool(xw->keyboard.type == keyboardIsTermcap);
+ break;
+#endif
+#if OPT_SUN_FUNC_KEYS
+ case 1051:
+ result = MdBool(xw->keyboard.type == keyboardIsSun);
+ break;
+#endif
+#if OPT_HP_FUNC_KEYS
+ case 1052:
+ result = MdBool(xw->keyboard.type == keyboardIsHP);
+ break;
+#endif
+#if OPT_SCO_FUNC_KEYS
+ case 1053:
+ result = MdBool(xw->keyboard.type == keyboardIsSCO);
+ break;
+#endif
+ case 1060:
+ result = MdBool(xw->keyboard.type == keyboardIsLegacy);
+ break;
+#if OPT_SUNPC_KBD
+ case 1061:
+ result = MdBool(xw->keyboard.type == keyboardIsVT220);
+ break;
+#endif
+#if OPT_READLINE
+ case SET_BUTTON1_MOVE_POINT:
+ result = MdBool(screen->click1_moves);
+ break;
+ case SET_BUTTON2_MOVE_POINT:
+ result = MdBool(screen->paste_moves);
+ break;
+ case SET_DBUTTON3_DELETE:
+ result = MdBool(screen->dclick3_deletes);
+ break;
+ case SET_PASTE_IN_BRACKET:
+ result = MdBool(screen->paste_brackets);
+ break;
+ case SET_PASTE_QUOTE:
+ result = MdBool(screen->paste_quotes);
+ break;
+ case SET_PASTE_LITERAL_NL:
+ result = MdBool(screen->paste_literal_nl);
+ break;
+#endif /* OPT_READLINE */
+ }
+ reply.a_param[count++] = (ParmType) params[0];
+ reply.a_param[count++] = (ParmType) result;
+ }
+ reply.a_type = ANSI_CSI;
+ reply.a_pintro = '?';
+ reply.a_nparam = (ParmType) count;
+ reply.a_inters = '$';
+ reply.a_final = 'y';
+ unparseseq(xw, &reply);
+}
+#endif /* OPT_DEC_RECTOPS */
+
+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;
+}
+
+void
+ChangeGroup(XtermWidget xw, const char *attribute, char *value)
+{
+#if OPT_WIDE_CHARS
+ static Char *converted; /* NO_LEAKS */
+#endif
+ static char empty[1];
+
+ Arg args[1];
+ Boolean changed = True;
+ Widget w = CURRENT_EMU();
+ Widget top = SHELL_OF(w);
+
+ char *my_attr;
+ char *name;
+ size_t limit;
+ Char *c1;
+ Char *cp;
+
+ if (!AllowTitleOps(xw))
+ return;
+
+ if (value == 0)
+ value = empty;
+ if (IsTitleMode(xw, tmSetBase16)) {
+ const char *temp;
+ char *test;
+
+ value = x_decode_hex(value, &temp);
+ if (*temp != '\0')
+ return;
+ for (test = value; *test != '\0'; ++test) {
+ if (CharOf(*test) < 32) {
+ *test = '\0';
+ break;
+ }
+ }
+ }
+
+ c1 = (Char *) value;
+ name = value;
+ limit = strlen(name);
+ my_attr = x_strdup(attribute);
+
+ TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
+
+ /*
+ * Ignore titles that are too long to be plausible requests.
+ */
+ if (limit > 0 && limit < 1024) {
+
+ /*
+ * After all decoding, overwrite nonprintable characters with '?'.
+ */
+ for (cp = c1; *cp != 0; ++cp) {
+ Char *c2 = cp;
+ if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
+ memset(c2, '?', (size_t) (cp + 1 - c2));
+ }
+ }
+
+#if OPT_WIDE_CHARS
+ /*
+ * If we're running in UTF-8 mode, and have not been told that the
+ * title string is in UTF-8, 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() && !IsSetUtf8Title(xw)) {
+ int n;
+
+ for (n = 0; name[n] != '\0'; ++n) {
+ if (CharOf(name[n]) > 127) {
+ if (converted != 0)
+ free(converted);
+ if ((converted = TypeMallocN(Char, 1 + (6 * 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 (resource.sameName) {
+ char *buf = 0;
+ XtSetArg(args[0], my_attr, &buf);
+ XtGetValues(top, args, 1);
+ TRACE(("...comparing{%s}\n", buf));
+ if (buf != 0 && strcmp(name, buf) == 0)
+ changed = False;
+ }
+#endif /* OPT_SAME_NAME */
+
+ if (changed) {
+ TRACE(("...updating %s\n", my_attr));
+ TRACE(("...value is %s\n", name));
+ XtSetArg(args[0], my_attr, name);
+ XtSetValues(top, args, 1);
+
+#if OPT_WIDE_CHARS
+ if (xtermEnvUTF8()) {
+ Display *dpy = XtDisplay(xw);
+ Atom my_atom;
+
+ const char *propname = (!strcmp(my_attr, XtNtitle)
+ ? "_NET_WM_NAME"
+ : "_NET_WM_ICON_NAME");
+ if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
+ if (IsSetUtf8Title(xw)) {
+ TRACE(("...updating %s\n", propname));
+ TRACE(("...value is %s\n", value));
+ XChangeProperty(dpy, VShellWindow(xw), my_atom,
+ XA_UTF8_STRING(dpy), 8,
+ PropModeReplace,
+ (Char *) value,
+ (int) strlen(value));
+ } else {
+ TRACE(("...deleting %s\n", propname));
+ XDeleteProperty(dpy, VShellWindow(xw), my_atom);
+ }
+ }
+ }
+#endif
+ }
+
+ free(my_attr);
+
+ if (IsTitleMode(xw, tmSetBase16))
+ free(value);
+
+ }
+ return;
+}
+
+void
+ChangeIconName(XtermWidget xw, char *name)
+{
+ if (name == 0) {
+ static char dummy[] = "";
+ name = dummy;
+ }
+ if (!showZIconBeep(xw, name))
+ ChangeGroup(xw, XtNiconName, name);
+}
+
+void
+ChangeTitle(XtermWidget xw, char *name)
+{
+ ChangeGroup(xw, XtNtitle, name);
+}
+
+#define Strlen(s) strlen((const 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);
+ }
+}
+
+/***====================================================================***/
+
+/*
+ * 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,
+ Bool always)
+{
+ Bool result = False;
+
+ if (always || AllowColorOps(xw, ecSetColor)) {
+ XColor def;
+ char *newName;
+
+ result = True;
+ if (!x_strcasecmp(name, XtDefaultForeground)) {
+ def.pixel = xw->old_foreground;
+ } else if (!x_strcasecmp(name, XtDefaultBackground)) {
+ def.pixel = xw->old_background;
+ } else if (!xtermAllocColor(xw, &def, name)) {
+ result = False;
+ }
+
+ if (result
+ && (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 0x%06lx)\n",
+ ndx, newName, def.pixel));
+ } else {
+ TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
+ result = False;
+ }
+ }
+ return result;
+}
+/***====================================================================***/
+
+/* ARGSUSED */
+void
+Panic(const char *s GCC_UNUSED, int a GCC_UNUSED)
+{
+#ifdef DEBUG
+ if (debug) {
+ xtermWarning(s, a);
+ }
+#endif /* DEBUG */
+}
+
+const char *
+SysErrorMsg(int code)
+{
+ static char unknown[] = "unknown error";
+ char *s = strerror(code);
+ return s ? s : unknown;
+}
+
+const char *
+SysReasonMsg(int code)
+{
+ /* *INDENT-OFF* */
+ static const struct {
+ int code;
+ const char *name;
+ } table[] = {
+ { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" },
+ { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" },
+ { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", },
+ { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", },
+ { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", },
+ { ERROR_PTSNAME, "spawn: ptsname() failed", },
+ { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", },
+ { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" },
+ { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" },
+ { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
+ { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
+ { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" },
+ { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" },
+ { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" },
+ { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" },
+ { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" },
+ { ERROR_INIGROUPS, "spawn: initgroups() failed" },
+ { ERROR_FORK, "spawn: fork() failed" },
+ { ERROR_EXEC, "spawn: exec() failed" },
+ { ERROR_PTYS, "get_pty: not enough ptys" },
+ { ERROR_PTY_EXEC, "waiting for initial map" },
+ { ERROR_SETUID, "spawn: setuid() failed" },
+ { ERROR_INIT, "spawn: can't initialize window" },
+ { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" },
+ { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" },
+ { ERROR_LUMALLOC, "luit: command-line malloc failed" },
+ { ERROR_SELECT, "in_put: select() failed" },
+ { ERROR_VINIT, "VTInit: can't initialize window" },
+ { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" },
+ { ERROR_TSELECT, "Tinput: select() failed" },
+ { ERROR_TINIT, "TekInit: can't initialize window" },
+ { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" },
+ { ERROR_LOGEXEC, "StartLog: exec() failed" },
+ { ERROR_XERROR, "xerror: XError event" },
+ { ERROR_XIOERROR, "xioerror: X I/O error" },
+ { ERROR_SCALLOC, "Alloc: calloc() failed on base" },
+ { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" },
+ { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" },
+ { ERROR_MMALLOC, "my_memmove: malloc/realloc failed" },
+ };
+ /* *INDENT-ON* */
+
+ Cardinal n;
+ const char *result = "?";
+
+ for (n = 0; n < XtNumber(table); ++n) {
+ if (code == table[n].code) {
+ result = table[n].name;
+ break;
+ }
+ }
+ return result;
+}
+
+void
+SysError(int code)
+{
+ int oerrno = errno;
+
+ fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
+ fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
+ fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
+
+ Cleanup(code);
+}
+
+/*
+ * cleanup by sending SIGHUP to client processes
+ */
+void
+Cleanup(int code)
+{
+ static Bool cleaning;
+ TScreen *screen = TScreenOf(term);
+
+ /*
+ * 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,
+ (void *) 0);
+ }
+#endif
+ }
+
+ if (screen->pid > 1) {
+ (void) kill_process_group(screen->pid, SIGHUP);
+ }
+ Exit(code);
+}
+
+#ifndef VMS
+#ifndef PATH_MAX
+#define PATH_MAX 512 /* ... is not defined consistently in Xos.h */
+#endif
+char *
+xtermFindShell(char *leaf, Bool warning)
+{
+ char *s;
+ char *d;
+ char *tmp;
+ char *result = leaf;
+
+ TRACE(("xtermFindShell(%s)\n", leaf));
+
+ if (!strncmp("./", result, (size_t) 2)
+ || !strncmp("../", result, (size_t) 3)) {
+ size_t need = PATH_MAX;
+ size_t used = strlen(result) + 2;
+ char *buffer = malloc(used + need);
+ if (buffer != 0) {
+ if (getcwd(buffer, need) != 0) {
+ sprintf(buffer + strlen(buffer), "/%s", result);
+ result = buffer;
+ } else {
+ free(buffer);
+ }
+ }
+ } else if (*result != '\0' && strchr("+/-", *result) == 0) {
+ /* find it in $PATH */
+ if ((s = x_getenv("PATH")) != 0) {
+ if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 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)
+ xtermWarning("No absolute path found for shell: %s\n", result);
+ result = 0;
+ }
+ return result;
+}
+#endif /* VMS */
+
+#define ENV_HUNK(n) (unsigned) ((((n) + 1) | 31) + 1)
+
+/*
+ * If we do not have unsetenv(), make consistent updates for environ[].
+ * This could happen on some older machines due to the uneven standardization
+ * process for the two functions.
+ *
+ * That is, putenv() makes a copy of environ, and some implementations do not
+ * update the environ pointer, so the fallback when unsetenv() is missing would
+ * not work as intended. Likewise, the reverse could be true, i.e., unsetenv
+ * could copy environ.
+ */
+#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
+#undef HAVE_PUTENV
+#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
+#undef HAVE_UNSETENV
+#endif
+
+/*
+ * copy the environment before Setenv'ing.
+ */
+void
+xtermCopyEnv(char **oldenv)
+{
+#ifdef HAVE_PUTENV
+ (void) oldenv;
+#else
+ unsigned size;
+ char **newenv;
+
+ for (size = 0; oldenv[size] != NULL; size++) {
+ ;
+ }
+
+ newenv = TypeCallocN(char *, ENV_HUNK(size));
+ memmove(newenv, oldenv, size * sizeof(char *));
+ environ = newenv;
+#endif
+}
+
+#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
+static int
+findEnv(const char *var, int *lengthp)
+{
+ char *test;
+ int envindex = 0;
+ size_t len = strlen(var);
+ int found = -1;
+
+ TRACE(("findEnv(%s=..)\n", var));
+
+ while ((test = environ[envindex]) != NULL) {
+ if (strncmp(test, var, len) == 0 && test[len] == '=') {
+ found = envindex;
+ break;
+ }
+ envindex++;
+ }
+ *lengthp = envindex;
+ return found;
+}
+#endif
+
+/*
+ * 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(const char *var, const char *value)
+{
+ if (value != 0) {
+#ifdef HAVE_PUTENV
+ char *both = malloc(2 + strlen(var) + strlen(value));
+ TRACE(("xtermSetenv(%s=%s)\n", var, value));
+ if (both) {
+ sprintf(both, "%s=%s", var, value);
+ putenv(both);
+ }
+#else
+ size_t len = strlen(var);
+ int envindex;
+ int found = findEnv(var, &envindex);
+
+ TRACE(("xtermSetenv(%s=%s)\n", var, value));
+
+ if (found < 0) {
+ unsigned need = ENV_HUNK(envindex + 1);
+ unsigned have = ENV_HUNK(envindex);
+
+ if (need > have) {
+ char **newenv;
+ newenv = TypeMallocN(char *, need);
+ if (newenv == 0) {
+ xtermWarning("Cannot increase environment\n");
+ return;
+ }
+ memmove(newenv, environ, have * sizeof(*newenv));
+ free(environ);
+ environ = newenv;
+ }
+
+ found = envindex;
+ environ[found + 1] = NULL;
+ environ = environ;
+ }
+
+ environ[found] = CastMallocN(char, 1 + len + strlen(value));
+ if (environ[found] == 0) {
+ xtermWarning("Cannot allocate environment %s\n", var);
+ return;
+ }
+ sprintf(environ[found], "%s=%s", var, value);
+#endif
+ }
+}
+
+void
+xtermUnsetenv(const char *var)
+{
+ TRACE(("xtermUnsetenv(%s)\n", var));
+#ifdef HAVE_UNSETENV
+ unsetenv(var);
+#else
+ {
+ int ignore;
+ int item = findEnv(var, &ignore);
+ if (item >= 0) {
+ while ((environ[item] = environ[item + 1]) != 0) {
+ ++item;
+ }
+ }
+ }
+#endif
+}
+
+/*ARGSUSED*/
+int
+xerror(Display * d, XErrorEvent * ev)
+{
+ xtermWarning("warning, error event received:\n");
+ (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
+ Exit(ERROR_XERROR);
+ return 0; /* appease the compiler */
+}
+
+void
+ice_error(IceConn iceConn)
+{
+ (void) iceConn;
+
+ xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
+ (long) getpid(), errno);
+
+ Exit(ERROR_ICEERROR);
+}
+
+/*ARGSUSED*/
+int
+xioerror(Display * dpy)
+{
+ int the_error = errno;
+
+ xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
+ the_error, SysErrorMsg(the_error),
+ DisplayString(dpy));
+
+ Exit(ERROR_XIOERROR);
+ return 0; /* appease the compiler */
+}
+
+void
+xt_error(String message)
+{
+ xtermWarning("Xt error: %s\n", message);
+
+ /*
+ * Check for the obvious - Xt does a poor job of reporting this.
+ */
+ if (x_getenv("DISPLAY") == 0) {
+ xtermWarning("DISPLAY is not set\n");
+ }
+ 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)
+{
+ XtermWidget xw = term;
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("set_vt_visibility(%d)\n", on));
+ if (on) {
+ if (!screen->Vshow && xw) {
+ VTInit(xw);
+ XtMapWidget(XtParent(xw));
+#if OPT_TOOLBAR
+ /* we need both of these during initialization */
+ XtMapWidget(SHELL_OF(xw));
+ ShowToolbar(resource.toolBar);
+#endif
+ screen->Vshow = True;
+ }
+ }
+#if OPT_TEK4014
+ else {
+ if (screen->Vshow && xw) {
+ withdraw_window(XtDisplay(xw),
+ VShellWindow(xw),
+ XScreenNumberOfScreen(XtScreen(xw)));
+ 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)
+{
+ TRACE(("set_tek_visibility(%d)\n", on));
+
+ if (on) {
+ if (!TEK4014_SHOWN(term)) {
+ if (tekWidget == 0) {
+ TekInit(); /* will exit on failure */
+ }
+ if (tekWidget != 0) {
+ 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);
+ TEK4014_SHOWN(term) = True;
+ }
+ }
+ } else {
+ if (TEK4014_SHOWN(term) && tekWidget) {
+ withdraw_window(XtDisplay(tekWidget),
+ TShellWindow,
+ XScreenNumberOfScreen(XtScreen(tekWidget)));
+ TEK4014_SHOWN(term) = False;
+ }
+ }
+ set_tekhide_sensitivity();
+ set_vthide_sensitivity();
+ update_vtshow();
+ update_tekshow();
+ update_vttekmode();
+ return;
+}
+
+void
+end_tek_mode(void)
+{
+ XtermWidget xw = term;
+
+ if (TEK4014_ACTIVE(xw)) {
+ FlushLog(xw);
+ longjmp(Tekend, 1);
+ }
+ return;
+}
+
+void
+end_vt_mode(void)
+{
+ XtermWidget xw = term;
+
+ if (!TEK4014_ACTIVE(xw)) {
+ FlushLog(xw);
+ TEK4014_ACTIVE(xw) = True;
+ longjmp(VTend, 1);
+ }
+ return;
+}
+
+void
+switch_modes(Bool tovt) /* if true, then become vt mode */
+{
+ if (tovt) {
+ if (tekRefreshList)
+ TekRefresh(tekWidget);
+ end_tek_mode(); /* WARNING: this does a longjmp... */
+ } else {
+ end_vt_mode(); /* WARNING: this does a longjmp... */
+ }
+}
+
+void
+hide_vt_window(void)
+{
+ set_vt_visibility(False);
+ if (!TEK4014_ACTIVE(term))
+ switch_modes(False); /* switch to tek mode */
+}
+
+void
+hide_tek_window(void)
+{
+ set_tek_visibility(False);
+ tekRefreshList = (TekLink *) 0;
+ if (TEK4014_ACTIVE(term))
+ 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) {
+ if (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);
+ if (res_array != 0) {
+ for (j = 0; j < res_count; j++)
+ res_array[j] = descs[j];
+ qsort(res_array, (size_t) 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;
+ return 0;
+ } else if (options == 0 || descs == 0) {
+ return 0;
+ }
+#endif
+
+ if (opt_array == 0) {
+ size_t opt_count, j;
+#if OPT_TRACE
+ Cardinal k;
+ XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
+ int code;
+ const 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, "-/+", (size_t) 3)) {
+ char temp[80];
+ const char *name = opt_array[j].opt + 3;
+ for (k = 0; k < numDescs; ++k) {
+ const 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;
+ }
+ strcpy(temp, opt_array[j].desc);
+ if (x_strindex(temp, "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 ", (size_t) 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 character-type locale that xterm was started in.
+ */
+String
+xtermEnvLocale(void)
+{
+ static String result;
+
+ if (result == 0) {
+ if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
+ result = x_strdup("C");
+ } else {
+ result = x_strdup(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 (!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 vendor_version[] = __vendorversion__;
+ static char *result;
+
+ if (result == 0) {
+ char *vendor = vendor_version;
+ 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 ", (size_t) 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;
+}
+
+/*
+ * Check if the current widget, or any parent, is the VT100 "xterm" widget.
+ */
+XtermWidget
+getXtermWidget(Widget w)
+{
+ XtermWidget xw;
+
+ if (w == 0) {
+ xw = (XtermWidget) CURRENT_EMU();
+ if (!IsXtermWidget(xw)) {
+ xw = 0;
+ }
+ } else if (IsXtermWidget(w)) {
+ xw = (XtermWidget) w;
+ } else {
+ xw = getXtermWidget(XtParent(w));
+ }
+ TRACE2(("getXtermWidget %p -> %p\n", w, xw));
+ return xw;
+}
+
+#if OPT_SESSION_MGT
+static void
+die_callback(Widget w GCC_UNUSED,
+ XtPointer client_data GCC_UNUSED,
+ XtPointer call_data GCC_UNUSED)
+{
+ Cleanup(0);
+}
+
+static void
+save_callback(Widget w GCC_UNUSED,
+ XtPointer client_data GCC_UNUSED,
+ XtPointer call_data)
+{
+ XtCheckpointToken token = (XtCheckpointToken) call_data;
+ /* we have nothing to save */
+ token->save_success = True;
+}
+
+static void
+icewatch(IceConn iceConn,
+ IcePointer clientData GCC_UNUSED,
+ Bool opening,
+ IcePointer * watchData GCC_UNUSED)
+{
+ if (opening) {
+ ice_fd = IceConnectionNumber(iceConn);
+ TRACE(("got IceConnectionNumber %d\n", ice_fd));
+ } else {
+ ice_fd = -1;
+ TRACE(("reset IceConnectionNumber\n"));
+ }
+}
+
+void
+xtermOpenSession(void)
+{
+ if (resource.sessionMgt) {
+ TRACE(("Enabling session-management callbacks\n"));
+ XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
+ XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
+ }
+}
+
+void
+xtermCloseSession(void)
+{
+ IceRemoveConnectionWatch(icewatch, NULL);
+}
+#endif /* OPT_SESSION_MGT */
+
+Widget
+xtermOpenApplication(XtAppContext * app_context_return,
+ String my_class,
+ XrmOptionDescRec * options,
+ Cardinal num_options,
+ int *argc_in_out,
+ String * argv_in_out,
+ String * fallback_resources,
+ WidgetClass widget_class,
+ ArgList args,
+ Cardinal num_args)
+{
+ Widget result;
+
+ XtSetErrorHandler(xt_error);
+#if OPT_SESSION_MGT
+ result = XtOpenApplication(app_context_return,
+ my_class,
+ options,
+ num_options,
+ argc_in_out,
+ argv_in_out,
+ fallback_resources,
+ widget_class,
+ args,
+ num_args);
+ IceAddConnectionWatch(icewatch, NULL);
+#else
+ result = XtAppInitialize(app_context_return,
+ my_class,
+ options,
+ num_options,
+ argc_in_out,
+ argv_in_out,
+ fallback_resources,
+ NULL, 0);
+#endif /* OPT_SESSION_MGT */
+ XtSetErrorHandler((XtErrorHandler) 0);
+
+ return result;
+}
+
+static int x11_errors;
+
+static int
+catch_x11_error(Display * display, XErrorEvent * error_event)
+{
+ (void) display;
+ (void) error_event;
+ ++x11_errors;
+ return 0;
+}
+
+static Boolean
+validWindow(Display * dpy, Window win, XWindowAttributes * attrs)
+{
+ Boolean result = False;
+ Status code;
+
+ if (win != None) {
+ XErrorHandler save = XSetErrorHandler(catch_x11_error);
+ x11_errors = 0;
+ code = XGetWindowAttributes(dpy, win, attrs);
+ XSetErrorHandler(save);
+ result = (Boolean) ((code != 0) && !x11_errors);
+ if (result) {
+ TRACE_WIN_ATTRS(attrs);
+ } else {
+ xtermWarning("invalid window-id %ld\n", (long) win);
+ }
+ }
+ return result;
+}
+
+void
+xtermEmbedWindow(Window winToEmbedInto)
+{
+ Display *dpy = XtDisplay(toplevel);
+ XWindowAttributes attrs;
+
+ TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
+ if (validWindow(dpy, winToEmbedInto, &attrs)) {
+ XtermWidget xw = term;
+ TScreen *screen = TScreenOf(xw);
+
+ XtRealizeWidget(toplevel);
+
+ TRACE(("...reparenting toplevel %#lx into %#lx\n",
+ XtWindow(toplevel),
+ winToEmbedInto));
+ XReparentWindow(dpy,
+ XtWindow(toplevel),
+ winToEmbedInto, 0, 0);
+
+ screen->embed_high = (Dimension) attrs.height;
+ screen->embed_wide = (Dimension) attrs.width;
+ }
+}