"Fossies" - the Fresh Open Source Software Archive

Member "xterm-368/misc.c" (7 Jun 2021, 190568 Bytes) of package /linux/misc/xterm-368.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "misc.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 367_vs_368.

    1 /* $XTermId: misc.c,v 1.988 2021/06/07 23:19:42 tom Exp $ */
    2 
    3 /*
    4  * Copyright 1999-2020,2021 by Thomas E. Dickey
    5  *
    6  *                         All Rights Reserved
    7  *
    8  * Permission is hereby granted, free of charge, to any person obtaining a
    9  * copy of this software and associated documentation files (the
   10  * "Software"), to deal in the Software without restriction, including
   11  * without limitation the rights to use, copy, modify, merge, publish,
   12  * distribute, sublicense, and/or sell copies of the Software, and to
   13  * permit persons to whom the Software is furnished to do so, subject to
   14  * the following conditions:
   15  *
   16  * The above copyright notice and this permission notice shall be included
   17  * in all copies or substantial portions of the Software.
   18  *
   19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
   23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   26  *
   27  * Except as contained in this notice, the name(s) of the above copyright
   28  * holders shall not be used in advertising or otherwise to promote the
   29  * sale, use or other dealings in this Software without prior written
   30  * authorization.
   31  *
   32  *
   33  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
   34  *
   35  *                         All Rights Reserved
   36  *
   37  * Permission to use, copy, modify, and distribute this software and its
   38  * documentation for any purpose and without fee is hereby granted,
   39  * provided that the above copyright notice appear in all copies and that
   40  * both that copyright notice and this permission notice appear in
   41  * supporting documentation, and that the name of Digital Equipment
   42  * Corporation not be used in advertising or publicity pertaining to
   43  * distribution of the software without specific, written prior permission.
   44  *
   45  *
   46  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
   47  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
   48  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
   49  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
   50  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
   51  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
   52  * SOFTWARE.
   53  */
   54 
   55 #include <version.h>
   56 #include <main.h>
   57 #include <xterm.h>
   58 #include <xterm_io.h>
   59 
   60 #include <sys/stat.h>
   61 #include <stdio.h>
   62 #include <stdarg.h>
   63 #include <signal.h>
   64 #include <ctype.h>
   65 #include <pwd.h>
   66 #include <sys/wait.h>
   67 
   68 #include <X11/keysym.h>
   69 #include <X11/Xatom.h>
   70 
   71 #include <X11/Xmu/Error.h>
   72 #include <X11/Xmu/SysUtil.h>
   73 #include <X11/Xmu/WinUtil.h>
   74 #include <X11/Xmu/Xmu.h>
   75 #if HAVE_X11_SUNKEYSYM_H
   76 #include <X11/Sunkeysym.h>
   77 #endif
   78 
   79 #ifdef HAVE_LIBXPM
   80 #include <X11/xpm.h>
   81 #endif
   82 
   83 #ifdef HAVE_LANGINFO_CODESET
   84 #include <langinfo.h>
   85 #endif
   86 
   87 #include <xutf8.h>
   88 
   89 #include <data.h>
   90 #include <error.h>
   91 #include <menu.h>
   92 #include <fontutils.h>
   93 #include <xstrings.h>
   94 #include <xtermcap.h>
   95 #include <VTparse.h>
   96 #include <graphics.h>
   97 #include <graphics_regis.h>
   98 #include <graphics_sixel.h>
   99 
  100 #include <assert.h>
  101 
  102 #ifdef VMS
  103 #define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
  104 #ifdef ALLOWLOGFILEEXEC
  105 #undef ALLOWLOGFILEEXEC
  106 #endif
  107 #endif /* VMS */
  108 
  109 #if USE_DOUBLE_BUFFER
  110 #include <X11/extensions/Xdbe.h>
  111 #endif
  112 
  113 #if OPT_WIDE_CHARS
  114 #include <wctype.h>
  115 #endif
  116 
  117 #if OPT_TEK4014
  118 #define OUR_EVENT(event,Type) \
  119         (event.type == Type && \
  120           (event.xcrossing.window == XtWindow(XtParent(xw)) || \
  121             (tekWidget && \
  122              event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
  123 #else
  124 #define OUR_EVENT(event,Type) \
  125         (event.type == Type && \
  126            (event.xcrossing.window == XtWindow(XtParent(xw))))
  127 #endif
  128 
  129 #define VB_DELAY    screen->visualBellDelay
  130 #define EVENT_DELAY TScreenOf(term)->nextEventDelay
  131 
  132 static Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
  133 static Cursor make_hidden_cursor(XtermWidget);
  134 
  135 static char emptyString[] = "";
  136 
  137 #if OPT_EXEC_XTERM
  138 /* Like readlink(2), but returns a malloc()ed buffer, or NULL on
  139    error; adapted from libc docs */
  140 static char *
  141 Readlink(const char *filename)
  142 {
  143     char *buf = NULL;
  144     size_t size = 100;
  145 
  146     for (;;) {
  147     int n;
  148     char *tmp = TypeRealloc(char, size, buf);
  149     if (tmp == NULL) {
  150         free(buf);
  151         return NULL;
  152     }
  153     buf = tmp;
  154     memset(buf, 0, size);
  155 
  156     n = (int) readlink(filename, buf, size);
  157     if (n < 0) {
  158         free(buf);
  159         return NULL;
  160     }
  161 
  162     if ((unsigned) n < size) {
  163         return buf;
  164     }
  165 
  166     size *= 2;
  167     }
  168 }
  169 #endif /* OPT_EXEC_XTERM */
  170 
  171 static void
  172 Sleep(int msec)
  173 {
  174     static struct timeval select_timeout;
  175 
  176     select_timeout.tv_sec = 0;
  177     select_timeout.tv_usec = msec * 1000;
  178     select(0, 0, 0, 0, &select_timeout);
  179 }
  180 
  181 static void
  182 selectwindow(XtermWidget xw, int flag)
  183 {
  184     TScreen *screen = TScreenOf(xw);
  185 
  186     TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
  187 
  188 #if OPT_TEK4014
  189     if (TEK4014_ACTIVE(xw)) {
  190     if (!Ttoggled)
  191         TCursorToggle(tekWidget, TOGGLE);
  192     screen->select |= flag;
  193     if (!Ttoggled)
  194         TCursorToggle(tekWidget, TOGGLE);
  195     } else
  196 #endif
  197     {
  198 #if OPT_INPUT_METHOD
  199     TInput *input = lookupTInput(xw, (Widget) xw);
  200     if (input && input->xic)
  201         XSetICFocus(input->xic);
  202 #endif
  203 
  204     if (screen->cursor_state && CursorMoved(screen))
  205         HideCursor(xw);
  206     screen->select |= flag;
  207     if (screen->cursor_state)
  208         ShowCursor(xw);
  209     }
  210     GetScrollLock(screen);
  211 }
  212 
  213 static void
  214 unselectwindow(XtermWidget xw, int flag)
  215 {
  216     TScreen *screen = TScreenOf(xw);
  217 
  218     TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
  219 
  220     if (screen->hide_pointer && screen->pointer_mode < pFocused) {
  221     screen->hide_pointer = False;
  222     xtermDisplayPointer(xw);
  223     }
  224 
  225     screen->select &= ~flag;
  226 
  227     if (!screen->always_highlight) {
  228 #if OPT_TEK4014
  229     if (TEK4014_ACTIVE(xw)) {
  230         if (!Ttoggled)
  231         TCursorToggle(tekWidget, TOGGLE);
  232         if (!Ttoggled)
  233         TCursorToggle(tekWidget, TOGGLE);
  234     } else
  235 #endif
  236     {
  237 #if OPT_INPUT_METHOD
  238         TInput *input = lookupTInput(xw, (Widget) xw);
  239         if (input && input->xic)
  240         XUnsetICFocus(input->xic);
  241 #endif
  242 
  243         if (screen->cursor_state && CursorMoved(screen))
  244         HideCursor(xw);
  245         if (screen->cursor_state)
  246         ShowCursor(xw);
  247     }
  248     }
  249 }
  250 
  251 static void
  252 DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
  253 {
  254     TScreen *screen = TScreenOf(xw);
  255 
  256     TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
  257     TRACE_FOCUS(xw, ev);
  258     if (((ev->detail) != NotifyInferior) &&
  259     ev->focus &&
  260     !(screen->select & FOCUS))
  261     selectwindow(xw, INWINDOW);
  262 }
  263 
  264 static void
  265 DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
  266 {
  267     TScreen *screen = TScreenOf(xw);
  268 
  269     TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
  270     TRACE_FOCUS(xw, ev);
  271     if (((ev->detail) != NotifyInferior) &&
  272     ev->focus &&
  273     !(screen->select & FOCUS))
  274     unselectwindow(xw, INWINDOW);
  275 }
  276 
  277 #ifndef XUrgencyHint
  278 #define XUrgencyHint (1L << 8)  /* X11R5 does not define */
  279 #endif
  280 
  281 static void
  282 setXUrgency(XtermWidget xw, Bool enable)
  283 {
  284     TScreen *screen = TScreenOf(xw);
  285 
  286     if (screen->bellIsUrgent) {
  287     XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
  288     if (h != 0) {
  289         if (enable && !(screen->select & FOCUS)) {
  290         h->flags |= XUrgencyHint;
  291         } else {
  292         h->flags &= ~XUrgencyHint;
  293         }
  294         XSetWMHints(screen->display, VShellWindow(xw), h);
  295     }
  296     }
  297 }
  298 
  299 void
  300 do_xevents(XtermWidget xw)
  301 {
  302     TScreen *screen = TScreenOf(xw);
  303 
  304     if (xtermAppPending()
  305     ||
  306 #if defined(VMS) || defined(__VMS)
  307     screen->display->qlen > 0
  308 #else
  309     GetBytesAvailable(ConnectionNumber(screen->display)) > 0
  310 #endif
  311     )
  312     xevents(xw);
  313 }
  314 
  315 void
  316 xtermDisplayPointer(XtermWidget xw)
  317 {
  318     TScreen *screen = TScreenOf(xw);
  319 
  320     if (screen->Vshow) {
  321     if (screen->hide_pointer) {
  322         TRACE(("Display text pointer (hidden)\n"));
  323         XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
  324     } else {
  325         TRACE(("Display text pointer (visible)\n"));
  326         recolor_cursor(screen,
  327                screen->pointer_cursor,
  328                T_COLOR(screen, MOUSE_FG),
  329                T_COLOR(screen, MOUSE_BG));
  330         XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
  331     }
  332     }
  333 }
  334 
  335 void
  336 xtermShowPointer(XtermWidget xw, Bool enable)
  337 {
  338     static int tried = -1;
  339     TScreen *screen = TScreenOf(xw);
  340 
  341 #if OPT_TEK4014
  342     if (TEK4014_SHOWN(xw))
  343     enable = True;
  344 #endif
  345 
  346     /*
  347      * Whether we actually hide the pointer depends on the pointer-mode and
  348      * the mouse-mode:
  349      */
  350     if (!enable) {
  351     switch (screen->pointer_mode) {
  352     case pNever:
  353         enable = True;
  354         break;
  355     case pNoMouse:
  356         if (screen->send_mouse_pos != MOUSE_OFF)
  357         enable = True;
  358         break;
  359     case pAlways:
  360     case pFocused:
  361         break;
  362     }
  363     }
  364 
  365     if (enable) {
  366     if (screen->hide_pointer) {
  367         screen->hide_pointer = False;
  368         xtermDisplayPointer(xw);
  369         switch (screen->send_mouse_pos) {
  370         case ANY_EVENT_MOUSE:
  371         break;
  372         default:
  373         MotionOff(screen, xw);
  374         break;
  375         }
  376     }
  377     } else if (!(screen->hide_pointer) && (tried <= 0)) {
  378     if (screen->hidden_cursor == 0) {
  379         screen->hidden_cursor = make_hidden_cursor(xw);
  380     }
  381     if (screen->hidden_cursor == 0) {
  382         tried = 1;
  383     } else {
  384         tried = 0;
  385         screen->hide_pointer = True;
  386         xtermDisplayPointer(xw);
  387         MotionOn(screen, xw);
  388     }
  389     }
  390 }
  391 
  392 /* true if p contains q */
  393 #define ExposeContains(p,q) \
  394         ((p)->y <= (q)->y \
  395       && (p)->x <= (q)->x \
  396       && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
  397       && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
  398 
  399 static XtInputMask
  400 mergeExposeEvents(XEvent *target)
  401 {
  402     XEvent next_event;
  403     XExposeEvent *p;
  404 
  405     XtAppNextEvent(app_con, target);
  406     p = (XExposeEvent *) target;
  407 
  408     while (XtAppPending(app_con)
  409        && XtAppPeekEvent(app_con, &next_event)
  410        && next_event.type == Expose) {
  411     Boolean merge_this = False;
  412     XExposeEvent *q = (XExposeEvent *) (&next_event);
  413 
  414     XtAppNextEvent(app_con, &next_event);
  415     TRACE_EVENT("pending", &next_event, (String *) 0, 0);
  416 
  417     /*
  418      * If either window is contained within the other, merge the events.
  419      * The traces show that there are also cases where a full repaint of
  420      * a window is broken into 3 or more rectangles, which do not arrive
  421      * in the same instant.  We could merge those if xterm were modified
  422      * to skim several events ahead.
  423      */
  424     if (p->window == q->window) {
  425         if (ExposeContains(p, q)) {
  426         TRACE(("pending Expose...merged forward\n"));
  427         merge_this = True;
  428         next_event = *target;
  429         } else if (ExposeContains(q, p)) {
  430         TRACE(("pending Expose...merged backward\n"));
  431         merge_this = True;
  432         }
  433     }
  434     if (!merge_this) {
  435         XtDispatchEvent(target);
  436     }
  437     *target = next_event;
  438     }
  439     XtDispatchEvent(target);
  440     return XtAppPending(app_con);
  441 }
  442 
  443 /*
  444  * On entry, we have peeked at the event queue and see a configure-notify
  445  * event.  Remove that from the queue so we can look further.
  446  *
  447  * Then, as long as there is a configure-notify event in the queue, remove
  448  * that.  If the adjacent events are for different windows, process the older
  449  * event and update the event used for comparing windows.  If they are for the
  450  * same window, only the newer event is of interest.
  451  *
  452  * Finally, process the (remaining) configure-notify event.
  453  */
  454 static XtInputMask
  455 mergeConfigureEvents(XEvent *target)
  456 {
  457     XEvent next_event;
  458     XConfigureEvent *p;
  459 
  460     XtAppNextEvent(app_con, target);
  461     p = (XConfigureEvent *) target;
  462 
  463     if (XtAppPending(app_con)
  464     && XtAppPeekEvent(app_con, &next_event)
  465     && next_event.type == ConfigureNotify) {
  466     Boolean merge_this = False;
  467     XConfigureEvent *q = (XConfigureEvent *) (&next_event);
  468 
  469     XtAppNextEvent(app_con, &next_event);
  470     TRACE_EVENT("pending", &next_event, (String *) 0, 0);
  471 
  472     if (p->window == q->window) {
  473         TRACE(("pending Configure...merged\n"));
  474         merge_this = True;
  475     }
  476     if (!merge_this) {
  477         TRACE(("pending Configure...skipped\n"));
  478         XtDispatchEvent(target);
  479     }
  480     *target = next_event;
  481     }
  482     XtDispatchEvent(target);
  483     return XtAppPending(app_con);
  484 }
  485 
  486 #define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
  487 #define SameButtonEvent(a,b) ( \
  488     SAME(a,b,type) && \
  489     SAME(a,b,serial) && \
  490     SAME(a,b,send_event) && \
  491     SAME(a,b,display) && \
  492     SAME(a,b,window) && \
  493     SAME(a,b,root) && \
  494     SAME(a,b,subwindow) && \
  495     SAME(a,b,time) && \
  496     SAME(a,b,x) && \
  497     SAME(a,b,y) && \
  498     SAME(a,b,x_root) && \
  499     SAME(a,b,y_root) && \
  500     SAME(a,b,state) && \
  501     SAME(a,b,button) && \
  502     SAME(a,b,same_screen))
  503 
  504 /*
  505  * Work around a bug in the X mouse code, which delivers duplicate events.
  506  */
  507 static XtInputMask
  508 mergeButtonEvents(XEvent *target)
  509 {
  510     XEvent next_event;
  511     XButtonEvent *p;
  512 
  513     XtAppNextEvent(app_con, target);
  514     p = (XButtonEvent *) target;
  515 
  516     if (XtAppPending(app_con)
  517     && XtAppPeekEvent(app_con, &next_event)
  518     && SameButtonEvent(target, &next_event)) {
  519     Boolean merge_this = False;
  520     XButtonEvent *q = (XButtonEvent *) (&next_event);
  521 
  522     XtAppNextEvent(app_con, &next_event);
  523     TRACE_EVENT("pending", &next_event, (String *) 0, 0);
  524 
  525     if (p->window == q->window) {
  526         TRACE(("pending ButtonEvent...merged\n"));
  527         merge_this = True;
  528     }
  529     if (!merge_this) {
  530         TRACE(("pending ButtonEvent...skipped\n"));
  531         XtDispatchEvent(target);
  532     }
  533     *target = next_event;
  534     }
  535     XtDispatchEvent(target);
  536     return XtAppPending(app_con);
  537 }
  538 
  539 /*
  540  * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
  541  * adjacent events because there could be other event-loop processing.  Absent
  542  * that limitation, it might be possible to scan ahead to find when the screen
  543  * would be completely updated, skipping unnecessary re-repainting before that
  544  * point.
  545  *
  546  * Note: all cases should allow doing XtAppNextEvent if result is true.
  547  */
  548 XtInputMask
  549 xtermAppPending(void)
  550 {
  551     XtInputMask result = XtAppPending(app_con);
  552     XEvent this_event;
  553     Boolean found = False;
  554 
  555     while (result && XtAppPeekEvent(app_con, &this_event)) {
  556     found = True;
  557     TRACE_EVENT("pending", &this_event, (String *) 0, 0);
  558     if (this_event.type == Expose) {
  559         result = mergeExposeEvents(&this_event);
  560     } else if (this_event.type == ConfigureNotify) {
  561         result = mergeConfigureEvents(&this_event);
  562     } else if (this_event.type == ButtonPress ||
  563            this_event.type == ButtonRelease) {
  564         result = mergeButtonEvents(&this_event);
  565     } else {
  566         break;
  567     }
  568     }
  569 
  570     /*
  571      * With NetBSD, closing a shell results in closing the X input event
  572      * stream, which interferes with the "-hold" option.  Wait a short time in
  573      * this case, to avoid max'ing the CPU.
  574      */
  575     if (hold_screen && caught_intr && !found) {
  576     Sleep(EVENT_DELAY);
  577     }
  578     return result;
  579 }
  580 
  581 void
  582 xevents(XtermWidget xw)
  583 {
  584     TScreen *screen = TScreenOf(xw);
  585     XEvent event;
  586     XtInputMask input_mask;
  587 
  588     if (need_cleanup)
  589     NormalExit();
  590 
  591     if (screen->scroll_amt)
  592     FlushScroll(xw);
  593     /*
  594      * process timeouts, relying on the fact that XtAppProcessEvent
  595      * will process the timeout and return without blockng on the
  596      * XEvent queue.  Other sources i.e., the pty are handled elsewhere
  597      * with select().
  598      */
  599     while ((input_mask = xtermAppPending()) != 0) {
  600     if (input_mask & XtIMTimer)
  601         XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
  602 #if OPT_SESSION_MGT
  603     /*
  604      * Session management events are alternative input events. Deal with
  605      * them in the same way.
  606      */
  607     else if (input_mask & XtIMAlternateInput)
  608         XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
  609 #endif
  610     else
  611         break;
  612     }
  613 
  614     /*
  615      * If there are no XEvents, don't wait around...
  616      */
  617     if ((input_mask & XtIMXEvent) != XtIMXEvent)
  618     return;
  619     do {
  620     /*
  621      * This check makes xterm hang when in mouse hilite tracking mode.
  622      * We simply ignore all events except for those not passed down to
  623      * this function, e.g., those handled in in_put().
  624      */
  625     if (screen->waitingForTrackInfo) {
  626         Sleep(EVENT_DELAY);
  627         return;
  628     }
  629     XtAppNextEvent(app_con, &event);
  630     /*
  631      * Hack to get around problems with the toolkit throwing away
  632      * eventing during the exclusive grab of the menu popup.  By
  633      * looking at the event ourselves we make sure that we can
  634      * do the right thing.
  635      */
  636     if (OUR_EVENT(event, EnterNotify)) {
  637         DoSpecialEnterNotify(xw, &event.xcrossing);
  638     } else if (OUR_EVENT(event, LeaveNotify)) {
  639         DoSpecialLeaveNotify(xw, &event.xcrossing);
  640     } else if (event.xany.type == MotionNotify
  641            && event.xcrossing.window == XtWindow(xw)) {
  642         switch (screen->send_mouse_pos) {
  643         case ANY_EVENT_MOUSE:
  644 #if OPT_DEC_LOCATOR
  645         case DEC_LOCATOR:
  646 #endif /* OPT_DEC_LOCATOR */
  647         SendMousePosition(xw, &event);
  648         xtermShowPointer(xw, True);
  649         continue;
  650         case BTN_EVENT_MOUSE:
  651         SendMousePosition(xw, &event);
  652         xtermShowPointer(xw, True);
  653         }
  654     }
  655 
  656     /*
  657      * If the event is interesting (and not a keyboard event), turn the
  658      * mouse pointer back on.
  659      */
  660     if (screen->hide_pointer) {
  661         if (screen->pointer_mode >= pFocused) {
  662         switch (event.xany.type) {
  663         case MotionNotify:
  664             xtermShowPointer(xw, True);
  665             break;
  666         }
  667         } else {
  668         switch (event.xany.type) {
  669         case KeyPress:
  670         case KeyRelease:
  671         case ButtonPress:
  672         case ButtonRelease:
  673             /* also these... */
  674         case Expose:
  675         case GraphicsExpose:
  676         case NoExpose:
  677         case PropertyNotify:
  678         case ClientMessage:
  679             break;
  680         default:
  681             xtermShowPointer(xw, True);
  682             break;
  683         }
  684         }
  685     }
  686 
  687     if (!event.xany.send_event ||
  688         screen->allowSendEvents ||
  689         ((event.xany.type != KeyPress) &&
  690          (event.xany.type != KeyRelease) &&
  691          (event.xany.type != ButtonPress) &&
  692          (event.xany.type != ButtonRelease))) {
  693 
  694         if (event.xany.type == MappingNotify) {
  695         XRefreshKeyboardMapping(&(event.xmapping));
  696         VTInitModifiers(xw);
  697         }
  698         XtDispatchEvent(&event);
  699     }
  700     } while (xtermAppPending() & XtIMXEvent);
  701 }
  702 
  703 static Cursor
  704 make_hidden_cursor(XtermWidget xw)
  705 {
  706     TScreen *screen = TScreenOf(xw);
  707     Cursor c;
  708     Display *dpy = screen->display;
  709     XFontStruct *fn;
  710 
  711     static XColor dummy;
  712 
  713     /*
  714      * Prefer nil2 (which is normally available) to "fixed" (which is supposed
  715      * to be "always" available), since it's a smaller glyph in case the
  716      * server insists on drawing _something_.
  717      */
  718     TRACE(("Ask for nil2 font\n"));
  719     if ((fn = xtermLoadQueryFont(xw, "nil2")) == 0) {
  720     TRACE(("...Ask for fixed font\n"));
  721     fn = xtermLoadQueryFont(xw, DEFFONT);
  722     }
  723 
  724     if (fn != None) {
  725     /* a space character seems to work as a cursor (dots are not needed) */
  726     c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
  727     XFreeFont(dpy, fn);
  728     } else {
  729     c = None;
  730     }
  731     TRACE(("XCreateGlyphCursor ->%#lx\n", c));
  732     return c;
  733 }
  734 
  735 /*
  736  * Xlib uses Xcursor to customize cursor coloring, which interferes with
  737  * xterm's pointerColor resource.  Work around this by providing our own
  738  * default theme.  Testing seems to show that we only have to provide this
  739  * until the window is initialized.
  740  */
  741 #ifdef HAVE_LIB_XCURSOR
  742 void
  743 init_colored_cursor(Display *dpy)
  744 {
  745     static const char theme[] = "index.theme";
  746     static const char pattern[] = "xtermXXXXXX";
  747     char *env = getenv("XCURSOR_THEME");
  748 
  749     xterm_cursor_theme = 0;
  750     /*
  751      * The environment variable overrides a (possible) resource Xcursor.theme
  752      */
  753     if (IsEmpty(env)) {
  754     env = XGetDefault(dpy, "Xcursor", "theme");
  755     TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
  756     } else {
  757     TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
  758     }
  759 
  760     /*
  761      * If neither found, provide our own default theme.
  762      */
  763     if (IsEmpty(env)) {
  764     const char *tmp_dir;
  765     char *filename;
  766     size_t needed;
  767 
  768     TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
  769 
  770     if ((tmp_dir = getenv("TMPDIR")) == 0) {
  771         tmp_dir = P_tmpdir;
  772     }
  773     needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
  774     if ((filename = malloc(needed)) != 0) {
  775         sprintf(filename, "%s/%s", tmp_dir, pattern);
  776 
  777 #ifdef HAVE_MKDTEMP
  778         xterm_cursor_theme = mkdtemp(filename);
  779 #else
  780         if (mktemp(filename) != 0
  781         && mkdir(filename, 0700) == 0) {
  782         xterm_cursor_theme = filename;
  783         }
  784 #endif
  785         if (xterm_cursor_theme != filename)
  786         free(filename);
  787         /*
  788          * Actually, Xcursor does what _we_ want just by steering its
  789          * search path away from home.  We are setting up the complete
  790          * theme just in case the library ever acquires a maintainer.
  791          */
  792         if (xterm_cursor_theme != 0) {
  793         char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
  794         FILE *fp;
  795 
  796         strcat(leaf, "/");
  797         strcat(leaf, theme);
  798 
  799         if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
  800             fprintf(fp, "[Icon Theme]\n");
  801             fclose(fp);
  802             *leaf = '\0';
  803             xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
  804             *leaf = '/';
  805 
  806             TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
  807                xterm_cursor_theme));
  808             atexit(cleanup_colored_cursor);
  809         } else {
  810             FreeAndNull(xterm_cursor_theme);
  811         }
  812         }
  813     }
  814     }
  815 }
  816 #endif /* HAVE_LIB_XCURSOR */
  817 
  818 /*
  819  * Once done, discard the file and directory holding it.
  820  */
  821 void
  822 cleanup_colored_cursor(void)
  823 {
  824 #ifdef HAVE_LIB_XCURSOR
  825     if (xterm_cursor_theme != 0) {
  826     char *my_path = getenv("XCURSOR_PATH");
  827     struct stat sb;
  828     if (!IsEmpty(my_path)
  829         && stat(my_path, &sb) == 0
  830         && (sb.st_mode & S_IFMT) == S_IFDIR) {
  831         unlink(xterm_cursor_theme);
  832         rmdir(my_path);
  833     }
  834     FreeAndNull(xterm_cursor_theme);
  835     }
  836 #endif /* HAVE_LIB_XCURSOR */
  837 }
  838 
  839 Cursor
  840 make_colored_cursor(unsigned c_index,       /* index into font */
  841             unsigned long fg,   /* pixel value */
  842             unsigned long bg)   /* pixel value */
  843 {
  844     TScreen *screen = TScreenOf(term);
  845     Cursor c = None;
  846     Display *dpy = screen->display;
  847 
  848     TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
  849     if (!IsEmpty(screen->cursor_font_name)) {
  850     static XTermFonts myFont;
  851 
  852     /* adapted from XCreateFontCursor(), which hardcodes the font name */
  853     TRACE(("loading cursor from alternate cursor font\n"));
  854     myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
  855     if (myFont.fs != NULL) {
  856         if (!xtermMissingChar(c_index, &myFont)
  857         && !xtermMissingChar(c_index + 1, &myFont)) {
  858 #define DATA(c) { 0UL, c, c, c, 0, 0 }
  859         static XColor foreground = DATA(0);
  860         static XColor background = DATA(65535);
  861 #undef DATA
  862 
  863         /*
  864          * Cursor fonts follow each shape glyph with a mask glyph; so
  865          * that character position 0 contains a shape, 1 the mask for
  866          * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
  867          * contains defined names for each shape.
  868          */
  869         c = XCreateGlyphCursor(dpy,
  870                        myFont.fs->fid,  /* source_font */
  871                        myFont.fs->fid,  /* mask_font */
  872                        c_index + 0, /* source_char */
  873                        c_index + 1, /* mask_char */
  874                        &foreground,
  875                        &background);
  876         }
  877         XFreeFont(dpy, myFont.fs);
  878     }
  879     if (c == None) {
  880         xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
  881              c_index, screen->cursor_font_name);
  882     }
  883     }
  884     if (c == None)
  885     c = XCreateFontCursor(dpy, c_index);
  886 
  887     if (c != None) {
  888     recolor_cursor(screen, c, fg, bg);
  889     }
  890     return c;
  891 }
  892 
  893 /* adapted from <X11/cursorfont.h> */
  894 static int
  895 LookupCursorShape(const char *name)
  896 {
  897 #define DATA(name) { XC_##name, #name }
  898     static struct {
  899     int code;
  900     const char name[25];
  901     } table[] = {
  902     DATA(X_cursor),
  903         DATA(arrow),
  904         DATA(based_arrow_down),
  905         DATA(based_arrow_up),
  906         DATA(boat),
  907         DATA(bogosity),
  908         DATA(bottom_left_corner),
  909         DATA(bottom_right_corner),
  910         DATA(bottom_side),
  911         DATA(bottom_tee),
  912         DATA(box_spiral),
  913         DATA(center_ptr),
  914         DATA(circle),
  915         DATA(clock),
  916         DATA(coffee_mug),
  917         DATA(cross),
  918         DATA(cross_reverse),
  919         DATA(crosshair),
  920         DATA(diamond_cross),
  921         DATA(dot),
  922         DATA(dotbox),
  923         DATA(double_arrow),
  924         DATA(draft_large),
  925         DATA(draft_small),
  926         DATA(draped_box),
  927         DATA(exchange),
  928         DATA(fleur),
  929         DATA(gobbler),
  930         DATA(gumby),
  931         DATA(hand1),
  932         DATA(hand2),
  933         DATA(heart),
  934         DATA(icon),
  935         DATA(iron_cross),
  936         DATA(left_ptr),
  937         DATA(left_side),
  938         DATA(left_tee),
  939         DATA(leftbutton),
  940         DATA(ll_angle),
  941         DATA(lr_angle),
  942         DATA(man),
  943         DATA(middlebutton),
  944         DATA(mouse),
  945         DATA(pencil),
  946         DATA(pirate),
  947         DATA(plus),
  948         DATA(question_arrow),
  949         DATA(right_ptr),
  950         DATA(right_side),
  951         DATA(right_tee),
  952         DATA(rightbutton),
  953         DATA(rtl_logo),
  954         DATA(sailboat),
  955         DATA(sb_down_arrow),
  956         DATA(sb_h_double_arrow),
  957         DATA(sb_left_arrow),
  958         DATA(sb_right_arrow),
  959         DATA(sb_up_arrow),
  960         DATA(sb_v_double_arrow),
  961         DATA(shuttle),
  962         DATA(sizing),
  963         DATA(spider),
  964         DATA(spraycan),
  965         DATA(star),
  966         DATA(target),
  967         DATA(tcross),
  968         DATA(top_left_arrow),
  969         DATA(top_left_corner),
  970         DATA(top_right_corner),
  971         DATA(top_side),
  972         DATA(top_tee),
  973         DATA(trek),
  974         DATA(ul_angle),
  975         DATA(umbrella),
  976         DATA(ur_angle),
  977         DATA(watch),
  978         DATA(xterm),
  979     };
  980 #undef DATA
  981     Cardinal j;
  982     int result = -1;
  983     if (!IsEmpty(name)) {
  984     for (j = 0; j < XtNumber(table); ++j) {
  985         if (!strcmp(name, table[j].name)) {
  986         result = table[j].code;
  987         break;
  988         }
  989     }
  990     }
  991     return result;
  992 }
  993 
  994 void
  995 xtermSetupPointer(XtermWidget xw, const char *theShape)
  996 {
  997     TScreen *screen = TScreenOf(xw);
  998     unsigned shape = XC_xterm;
  999     int other = LookupCursorShape(theShape);
 1000     unsigned which;
 1001 
 1002     if (other >= 0 && other < XC_num_glyphs)
 1003     shape = (unsigned) other;
 1004 
 1005     TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
 1006        NonNull(theShape)));
 1007 
 1008     which = (unsigned) (shape / 2);
 1009     if (xw->work.pointer_cursors[which] == None) {
 1010     TRACE(("creating text pointer cursor from shape %d\n", shape));
 1011     xw->work.pointer_cursors[which] =
 1012         make_colored_cursor(shape,
 1013                 T_COLOR(screen, MOUSE_FG),
 1014                 T_COLOR(screen, MOUSE_BG));
 1015     } else {
 1016     TRACE(("updating text pointer cursor for shape %d\n", shape));
 1017     recolor_cursor(screen,
 1018                screen->pointer_cursor,
 1019                T_COLOR(screen, MOUSE_FG),
 1020                T_COLOR(screen, MOUSE_BG));
 1021     }
 1022     if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
 1023     screen->pointer_cursor = xw->work.pointer_cursors[which];
 1024     TRACE(("defining text pointer cursor with shape %d\n", shape));
 1025     XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
 1026     if (XtIsRealized((Widget) xw)) {
 1027         /* briefly override pointerMode after changing the pointer */
 1028         if (screen->pointer_mode != pNever)
 1029         screen->hide_pointer = True;
 1030         xtermShowPointer(xw, True);
 1031     }
 1032     }
 1033 }
 1034 
 1035 /* ARGSUSED */
 1036 void
 1037 HandleKeyPressed(Widget w GCC_UNUSED,
 1038          XEvent *event,
 1039          String *params GCC_UNUSED,
 1040          Cardinal *nparams GCC_UNUSED)
 1041 {
 1042     TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
 1043     Input(term, &event->xkey, False);
 1044 }
 1045 
 1046 /* ARGSUSED */
 1047 void
 1048 HandleEightBitKeyPressed(Widget w GCC_UNUSED,
 1049              XEvent *event,
 1050              String *params GCC_UNUSED,
 1051              Cardinal *nparams GCC_UNUSED)
 1052 {
 1053     TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
 1054     Input(term, &event->xkey, True);
 1055 }
 1056 
 1057 /* ARGSUSED */
 1058 void
 1059 HandleStringEvent(Widget w GCC_UNUSED,
 1060           XEvent *event GCC_UNUSED,
 1061           String *params,
 1062           Cardinal *nparams)
 1063 {
 1064 
 1065     if (*nparams != 1)
 1066     return;
 1067 
 1068     if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
 1069     const char *abcdef = "ABCDEF";
 1070     const char *xxxxxx;
 1071     Char c;
 1072     UString p;
 1073     unsigned value = 0;
 1074 
 1075     for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
 1076          '\0'; p++) {
 1077         value *= 16;
 1078         if (c >= '0' && c <= '9')
 1079         value += (unsigned) (c - '0');
 1080         else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
 1081         value += (unsigned) (xxxxxx - abcdef) + 10;
 1082         else
 1083         break;
 1084     }
 1085     if (c == '\0') {
 1086         Char hexval[2];
 1087         hexval[0] = (Char) value;
 1088         hexval[1] = 0;
 1089         StringInput(term, hexval, (size_t) 1);
 1090     }
 1091     } else {
 1092     StringInput(term, (const Char *) *params, strlen(*params));
 1093     }
 1094 }
 1095 
 1096 #if OPT_EXEC_XTERM
 1097 
 1098 #ifndef PROCFS_ROOT
 1099 #define PROCFS_ROOT "/proc"
 1100 #endif
 1101 
 1102 /*
 1103  * Determine the current working directory of the child so that we can
 1104  * spawn a new terminal in the same directory.
 1105  *
 1106  * If we cannot get the CWD of the child, just use our own.
 1107  */
 1108 char *
 1109 ProcGetCWD(pid_t pid)
 1110 {
 1111     char *child_cwd = NULL;
 1112 
 1113     if (pid) {
 1114     char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
 1115     sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
 1116     child_cwd = Readlink(child_cwd_link);
 1117     }
 1118     return child_cwd;
 1119 }
 1120 
 1121 /* ARGSUSED */
 1122 void
 1123 HandleSpawnTerminal(Widget w GCC_UNUSED,
 1124             XEvent *event GCC_UNUSED,
 1125             String *params,
 1126             Cardinal *nparams)
 1127 {
 1128     TScreen *screen = TScreenOf(term);
 1129     char *child_cwd = NULL;
 1130     char *child_exe;
 1131     pid_t pid;
 1132 
 1133     /*
 1134      * Try to find the actual program which is running in the child process.
 1135      * This works for Linux.  If we cannot find the program, fall back to the
 1136      * xterm program (which is usually adequate).  Give up if we are given only
 1137      * a relative path to xterm, since that would not always match $PATH.
 1138      */
 1139     child_exe = Readlink(PROCFS_ROOT "/self/exe");
 1140     if (!child_exe) {
 1141     if (strncmp(ProgramName, "./", (size_t) 2)
 1142         && strncmp(ProgramName, "../", (size_t) 3)) {
 1143         child_exe = xtermFindShell(ProgramName, True);
 1144     } else {
 1145         xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
 1146     }
 1147     if (child_exe == 0)
 1148         return;
 1149     }
 1150 
 1151     child_cwd = ProcGetCWD(screen->pid);
 1152 
 1153     /* The reaper will take care of cleaning up the child */
 1154     pid = fork();
 1155     if (pid == -1) {
 1156     xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
 1157     } else if (!pid) {
 1158     /* We are the child */
 1159     if (child_cwd) {
 1160         IGNORE_RC(chdir(child_cwd));    /* We don't care if this fails */
 1161     }
 1162 
 1163     if (setuid(screen->uid) == -1
 1164         || setgid(screen->gid) == -1) {
 1165         xtermWarning("Cannot reset uid/gid\n");
 1166     } else {
 1167         unsigned myargc = *nparams + 1;
 1168         char **myargv = TypeMallocN(char *, myargc + 1);
 1169 
 1170         if (myargv != 0) {
 1171         unsigned n = 0;
 1172 
 1173         myargv[n++] = child_exe;
 1174 
 1175         while (n < myargc) {
 1176             myargv[n++] = (char *) *params++;
 1177         }
 1178 
 1179         myargv[n] = 0;
 1180         execv(child_exe, myargv);
 1181         }
 1182 
 1183         /* If we get here, we've failed */
 1184         xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
 1185     }
 1186     _exit(0);
 1187     }
 1188 
 1189     /* We are the parent; clean up */
 1190     free(child_cwd);
 1191     free(child_exe);
 1192 }
 1193 #endif /* OPT_EXEC_XTERM */
 1194 
 1195 /*
 1196  * Rather than sending characters to the host, put them directly into our
 1197  * input queue.  That lets a user have access to any of the control sequences
 1198  * for a key binding.  This is the equivalent of local function key support.
 1199  *
 1200  * NOTE:  This code does not support the hexadecimal kludge used in
 1201  * HandleStringEvent because it prevents us from sending an arbitrary string
 1202  * (but it appears in a lot of examples - so we are stuck with it).  The
 1203  * standard string converter does recognize "\" for newline ("\n") and for
 1204  * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
 1205  * without a specialized converter.  (Don't try to use \000, though).
 1206  */
 1207 /* ARGSUSED */
 1208 void
 1209 HandleInterpret(Widget w GCC_UNUSED,
 1210         XEvent *event GCC_UNUSED,
 1211         String *params,
 1212         Cardinal *param_count)
 1213 {
 1214     if (*param_count == 1) {
 1215     const char *value = params[0];
 1216     int need = (int) strlen(value);
 1217     int used = (int) (VTbuffer->next - VTbuffer->buffer);
 1218     int have = (int) (VTbuffer->last - VTbuffer->buffer);
 1219 
 1220     if (have - used + need < BUF_SIZE) {
 1221 
 1222         fillPtyData(term, VTbuffer, value, (int) strlen(value));
 1223 
 1224         TRACE(("Interpret %s\n", value));
 1225         VTbuffer->update++;
 1226     }
 1227     }
 1228 }
 1229 
 1230 /*ARGSUSED*/
 1231 void
 1232 HandleEnterWindow(Widget w GCC_UNUSED,
 1233           XtPointer eventdata GCC_UNUSED,
 1234           XEvent *event GCC_UNUSED,
 1235           Boolean *cont GCC_UNUSED)
 1236 {
 1237     /* NOP since we handled it above */
 1238     TRACE(("HandleEnterWindow ignored\n"));
 1239     TRACE_FOCUS(w, event);
 1240 }
 1241 
 1242 /*ARGSUSED*/
 1243 void
 1244 HandleLeaveWindow(Widget w GCC_UNUSED,
 1245           XtPointer eventdata GCC_UNUSED,
 1246           XEvent *event GCC_UNUSED,
 1247           Boolean *cont GCC_UNUSED)
 1248 {
 1249     /* NOP since we handled it above */
 1250     TRACE(("HandleLeaveWindow ignored\n"));
 1251     TRACE_FOCUS(w, event);
 1252 }
 1253 
 1254 /*ARGSUSED*/
 1255 void
 1256 HandleFocusChange(Widget w GCC_UNUSED,
 1257           XtPointer eventdata GCC_UNUSED,
 1258           XEvent *ev,
 1259           Boolean *cont GCC_UNUSED)
 1260 {
 1261     XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
 1262     XtermWidget xw = term;
 1263     TScreen *screen = TScreenOf(xw);
 1264 
 1265     TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
 1266        visibleEventType(event->type),
 1267        visibleNotifyMode(event->mode),
 1268        visibleNotifyDetail(event->detail)));
 1269     TRACE_FOCUS(xw, event);
 1270 
 1271     if (screen->quiet_grab
 1272     && (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
 1273     /* EMPTY */ ;
 1274     } else if (event->type == FocusIn) {
 1275     if (event->detail != NotifyPointer) {
 1276         setXUrgency(xw, False);
 1277     }
 1278 
 1279     /*
 1280      * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
 1281      * one of our windows.  Use this to reset a case where one xterm is
 1282      * partly obscuring another, and X gets (us) confused about whether the
 1283      * pointer was in the window.  In particular, this can happen if the
 1284      * user is resizing the obscuring window, causing some events to not be
 1285      * delivered to the obscured window.
 1286      */
 1287     if (event->detail == NotifyNonlinear
 1288         && (screen->select & INWINDOW) != 0) {
 1289         unselectwindow(xw, INWINDOW);
 1290     }
 1291     selectwindow(xw,
 1292              ((event->detail == NotifyPointer)
 1293               ? INWINDOW
 1294               : FOCUS));
 1295     SendFocusButton(xw, event);
 1296     } else {
 1297 #if OPT_FOCUS_EVENT
 1298     if (event->type == FocusOut) {
 1299         SendFocusButton(xw, event);
 1300     }
 1301 #endif
 1302     /*
 1303      * XGrabKeyboard() will generate NotifyGrab event that we want to
 1304      * ignore.
 1305      */
 1306     if (event->mode != NotifyGrab) {
 1307         unselectwindow(xw,
 1308                ((event->detail == NotifyPointer)
 1309                 ? INWINDOW
 1310                 : FOCUS));
 1311     }
 1312     if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
 1313         Bell(xw, XkbBI_Info, 100);
 1314         ReverseVideo(xw);
 1315         screen->grabbedKbd = False;
 1316         update_securekbd();
 1317     }
 1318     }
 1319 }
 1320 
 1321 static long lastBellTime;   /* in milliseconds */
 1322 
 1323 #if defined(HAVE_XKB_BELL_EXT)
 1324 static Atom
 1325 AtomBell(XtermWidget xw, int which)
 1326 {
 1327 #define DATA(name) { XkbBI_##name, XkbBN_##name }
 1328     static struct {
 1329     int value;
 1330     const char *name;
 1331     } table[] = {
 1332     DATA(Info),
 1333         DATA(MarginBell),
 1334         DATA(MinorError),
 1335         DATA(TerminalBell)
 1336     };
 1337 #undef DATA
 1338     Cardinal n;
 1339     Atom result = None;
 1340 
 1341     for (n = 0; n < XtNumber(table); ++n) {
 1342     if (table[n].value == which) {
 1343         result = XInternAtom(XtDisplay(xw), table[n].name, False);
 1344         break;
 1345     }
 1346     }
 1347     return result;
 1348 }
 1349 #endif
 1350 
 1351 void
 1352 xtermBell(XtermWidget xw, int which, int percent)
 1353 {
 1354     TScreen *screen = TScreenOf(xw);
 1355 #if defined(HAVE_XKB_BELL_EXT)
 1356     Atom tony = AtomBell(xw, which);
 1357 #endif
 1358 
 1359     switch (which) {
 1360     case XkbBI_Info:
 1361     case XkbBI_MinorError:
 1362     case XkbBI_MajorError:
 1363     case XkbBI_TerminalBell:
 1364     switch (screen->warningVolume) {
 1365     case bvOff:
 1366         percent = -100;
 1367         break;
 1368     case bvLow:
 1369         break;
 1370     case bvHigh:
 1371         percent = 100;
 1372         break;
 1373     }
 1374     break;
 1375     case XkbBI_MarginBell:
 1376     switch (screen->marginVolume) {
 1377     case bvOff:
 1378         percent = -100;
 1379         break;
 1380     case bvLow:
 1381         break;
 1382     case bvHigh:
 1383         percent = 100;
 1384         break;
 1385     }
 1386     break;
 1387     default:
 1388     break;
 1389     }
 1390 
 1391 #if defined(HAVE_XKB_BELL_EXT)
 1392     if (tony != None) {
 1393     XkbBell(screen->display, VShellWindow(xw), percent, tony);
 1394     } else
 1395 #endif
 1396     XBell(screen->display, percent);
 1397 }
 1398 
 1399 void
 1400 Bell(XtermWidget xw, int which, int percent)
 1401 {
 1402     TScreen *screen = TScreenOf(xw);
 1403     struct timeval curtime;
 1404 
 1405     TRACE(("BELL %d %d%%\n", which, percent));
 1406     if (!XtIsRealized((Widget) xw)) {
 1407     return;
 1408     }
 1409 
 1410     setXUrgency(xw, True);
 1411 
 1412     /* has enough time gone by that we are allowed to ring
 1413        the bell again? */
 1414     if (screen->bellSuppressTime) {
 1415     long now_msecs;
 1416 
 1417     if (screen->bellInProgress) {
 1418         do_xevents(xw);
 1419         if (screen->bellInProgress) {   /* even after new events? */
 1420         return;
 1421         }
 1422     }
 1423     X_GETTIMEOFDAY(&curtime);
 1424     now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
 1425     if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
 1426         now_msecs - lastBellTime < screen->bellSuppressTime) {
 1427         return;
 1428     }
 1429     lastBellTime = now_msecs;
 1430     }
 1431 
 1432     if (screen->visualbell) {
 1433     VisualBell();
 1434     } else {
 1435     xtermBell(xw, which, percent);
 1436     }
 1437 
 1438     if (screen->poponbell)
 1439     XRaiseWindow(screen->display, VShellWindow(xw));
 1440 
 1441     if (screen->bellSuppressTime) {
 1442     /* now we change a property and wait for the notify event to come
 1443        back.  If the server is suspending operations while the bell
 1444        is being emitted (problematic for audio bell), this lets us
 1445        know when the previous bell has finished */
 1446     Widget w = CURRENT_EMU();
 1447     XChangeProperty(XtDisplay(w), XtWindow(w),
 1448             XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
 1449     screen->bellInProgress = True;
 1450     }
 1451 }
 1452 
 1453 static void
 1454 flashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
 1455 {
 1456     int y = 0;
 1457     int x = 0;
 1458 
 1459     if (screen->flash_line) {
 1460     y = CursorY(screen, screen->cur_row);
 1461     height = (unsigned) FontHeight(screen);
 1462     }
 1463     XFillRectangle(screen->display, window, visualGC, x, y, width, height);
 1464     XFlush(screen->display);
 1465     Sleep(VB_DELAY);
 1466     XFillRectangle(screen->display, window, visualGC, x, y, width, height);
 1467 }
 1468 
 1469 void
 1470 VisualBell(void)
 1471 {
 1472     XtermWidget xw = term;
 1473     TScreen *screen = TScreenOf(xw);
 1474 
 1475     if (VB_DELAY > 0) {
 1476     Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
 1477               T_COLOR(screen, TEXT_BG));
 1478     XGCValues gcval;
 1479     GC visualGC;
 1480 
 1481     gcval.function = GXxor;
 1482     gcval.foreground = xorPixel;
 1483     visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
 1484 #if OPT_TEK4014
 1485     if (TEK4014_ACTIVE(xw)) {
 1486         TekScreen *tekscr = TekScreenOf(tekWidget);
 1487         flashWindow(screen, TWindow(tekscr), visualGC,
 1488             TFullWidth(tekscr),
 1489             TFullHeight(tekscr));
 1490     } else
 1491 #endif
 1492     {
 1493         flashWindow(screen, VWindow(screen), visualGC,
 1494             FullWidth(screen),
 1495             FullHeight(screen));
 1496     }
 1497     XtReleaseGC((Widget) xw, visualGC);
 1498     }
 1499 }
 1500 
 1501 /* ARGSUSED */
 1502 void
 1503 HandleBellPropertyChange(Widget w GCC_UNUSED,
 1504              XtPointer data GCC_UNUSED,
 1505              XEvent *ev,
 1506              Boolean *more GCC_UNUSED)
 1507 {
 1508     TScreen *screen = TScreenOf(term);
 1509 
 1510     if (ev->xproperty.atom == XA_NOTICE) {
 1511     screen->bellInProgress = False;
 1512     }
 1513 }
 1514 
 1515 void
 1516 xtermWarning(const char *fmt, ...)
 1517 {
 1518     int save_err = errno;
 1519     va_list ap;
 1520 
 1521     fflush(stdout);
 1522 
 1523 #if OPT_TRACE
 1524     va_start(ap, fmt);
 1525     Trace("xtermWarning: ");
 1526     TraceVA(fmt, ap);
 1527     va_end(ap);
 1528 #endif
 1529 
 1530     fprintf(stderr, "%s: ", ProgramName);
 1531     va_start(ap, fmt);
 1532     vfprintf(stderr, fmt, ap);
 1533     (void) fflush(stderr);
 1534 
 1535     va_end(ap);
 1536     errno = save_err;
 1537 }
 1538 
 1539 void
 1540 xtermPerror(const char *fmt, ...)
 1541 {
 1542     int save_err = errno;
 1543     const char *msg = strerror(errno);
 1544     va_list ap;
 1545 
 1546     fflush(stdout);
 1547 
 1548 #if OPT_TRACE
 1549     va_start(ap, fmt);
 1550     Trace("xtermPerror: ");
 1551     TraceVA(fmt, ap);
 1552     va_end(ap);
 1553 #endif
 1554 
 1555     fprintf(stderr, "%s: ", ProgramName);
 1556     va_start(ap, fmt);
 1557     vfprintf(stderr, fmt, ap);
 1558     fprintf(stderr, ": %s\n", msg);
 1559     (void) fflush(stderr);
 1560 
 1561     va_end(ap);
 1562     errno = save_err;
 1563 }
 1564 
 1565 Window
 1566 WMFrameWindow(XtermWidget xw)
 1567 {
 1568     Window win_root, win_current, *children;
 1569     Window win_parent = 0;
 1570     unsigned int nchildren;
 1571 
 1572     win_current = XtWindow(xw);
 1573 
 1574     /* find the parent which is child of root */
 1575     do {
 1576     if (win_parent)
 1577         win_current = win_parent;
 1578     XQueryTree(TScreenOf(xw)->display,
 1579            win_current,
 1580            &win_root,
 1581            &win_parent,
 1582            &children,
 1583            &nchildren);
 1584     XFree(children);
 1585     } while (win_root != win_parent);
 1586 
 1587     return win_current;
 1588 }
 1589 
 1590 #if OPT_DABBREV
 1591 /*
 1592  * The following code implements `dynamic abbreviation' expansion a la
 1593  * Emacs.  It looks in the preceding visible screen and its scrollback
 1594  * to find expansions of a typed word.  It compares consecutive
 1595  * expansions and ignores one of them if they are identical.
 1596  * (Tomasz J. Cholewo, t.cholewo@ieee.org)
 1597  */
 1598 
 1599 #define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
 1600 
 1601 static int
 1602 dabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
 1603 {
 1604     int result = -1;
 1605     int firstLine = -(screen->savedlines);
 1606 
 1607     *ld = getLineData(screen, cell->row);
 1608     while (cell->row >= firstLine) {
 1609     if (--(cell->col) >= 0) {
 1610         result = (int) (*ld)->charData[cell->col];
 1611         break;
 1612     }
 1613     if (--(cell->row) < firstLine)
 1614         break;      /* ...there is no previous line */
 1615     *ld = getLineData(screen, cell->row);
 1616     cell->col = MaxCols(screen);
 1617     if (!LineTstWrapped(*ld)) {
 1618         result = ' ';   /* treat lines as separate */
 1619         break;
 1620     }
 1621     }
 1622     return result;
 1623 }
 1624 
 1625 static char *
 1626 dabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
 1627 {
 1628     TScreen *screen = TScreenOf(xw);
 1629     char *abword;
 1630     int c;
 1631     char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
 1632     char *result = 0;
 1633 
 1634     abword = ab_end;
 1635     *abword = '\0';     /* end of string marker */
 1636 
 1637     while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
 1638        IS_WORD_CONSTITUENT(c)) {
 1639     if (abword > xw->work.dabbrev_data) /* store only the last chars */
 1640         *(--abword) = (char) c;
 1641     }
 1642 
 1643     if (c >= 0) {
 1644     result = abword;
 1645     } else if (abword != ab_end) {
 1646     result = abword;
 1647     }
 1648 
 1649     if (result != 0) {
 1650     while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
 1651            !IS_WORD_CONSTITUENT(c)) {
 1652         ;           /* skip preceding spaces */
 1653     }
 1654     (cell->col)++;      /* can be | > screen->max_col| */
 1655     }
 1656     return result;
 1657 }
 1658 
 1659 static int
 1660 dabbrev_expand(XtermWidget xw)
 1661 {
 1662     TScreen *screen = TScreenOf(xw);
 1663     int pty = screen->respond;  /* file descriptor of pty */
 1664 
 1665     static CELL cell;
 1666     static char *dabbrev_hint = 0, *lastexpansion = 0;
 1667     static unsigned int expansions;
 1668 
 1669     char *expansion;
 1670     size_t hint_len;
 1671     int result = 0;
 1672     LineData *ld;
 1673 
 1674     if (!screen->dabbrev_working) { /* initialize */
 1675     expansions = 0;
 1676     cell.col = screen->cur_col;
 1677     cell.row = screen->cur_row;
 1678 
 1679     free(dabbrev_hint);
 1680 
 1681     if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
 1682 
 1683         free(lastexpansion);
 1684 
 1685         if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
 1686 
 1687         /* make own copy */
 1688         if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
 1689             screen->dabbrev_working = True;
 1690             /* we are in the middle of dabbrev process */
 1691         }
 1692         } else {
 1693         return result;
 1694         }
 1695     } else {
 1696         return result;
 1697     }
 1698     if (!screen->dabbrev_working) {
 1699         free(lastexpansion);
 1700         lastexpansion = 0;
 1701         return result;
 1702     }
 1703     }
 1704 
 1705     if (dabbrev_hint == 0)
 1706     return result;
 1707 
 1708     hint_len = strlen(dabbrev_hint);
 1709     for (;;) {
 1710     if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
 1711         if (expansions >= 2) {
 1712         expansions = 0;
 1713         cell.col = screen->cur_col;
 1714         cell.row = screen->cur_row;
 1715         continue;
 1716         }
 1717         break;
 1718     }
 1719     if (!strncmp(dabbrev_hint, expansion, hint_len) &&  /* empty hint matches everything */
 1720         strlen(expansion) > hint_len && /* trivial expansion disallowed */
 1721         strcmp(expansion, lastexpansion))   /* different from previous */
 1722         break;
 1723     }
 1724 
 1725     if (expansion != 0) {
 1726     Char *copybuffer;
 1727     size_t del_cnt = strlen(lastexpansion) - hint_len;
 1728     size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
 1729 
 1730     if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
 1731         /* delete previous expansion */
 1732         memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
 1733         memmove(copybuffer + del_cnt,
 1734             expansion + hint_len,
 1735             strlen(expansion) - hint_len);
 1736         v_write(pty, copybuffer, (unsigned) buf_cnt);
 1737         /* v_write() just reset our flag */
 1738         screen->dabbrev_working = True;
 1739         free(copybuffer);
 1740 
 1741         free(lastexpansion);
 1742 
 1743         if ((lastexpansion = strdup(expansion)) != 0) {
 1744         result = 1;
 1745         expansions++;
 1746         }
 1747     }
 1748     }
 1749 
 1750     return result;
 1751 }
 1752 
 1753 /*ARGSUSED*/
 1754 void
 1755 HandleDabbrevExpand(Widget w,
 1756             XEvent *event GCC_UNUSED,
 1757             String *params GCC_UNUSED,
 1758             Cardinal *nparams GCC_UNUSED)
 1759 {
 1760     XtermWidget xw;
 1761 
 1762     TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
 1763     if ((xw = getXtermWidget(w)) != 0) {
 1764     if (!dabbrev_expand(xw))
 1765         Bell(xw, XkbBI_TerminalBell, 0);
 1766     }
 1767 }
 1768 #endif /* OPT_DABBREV */
 1769 
 1770 void
 1771 xtermDeiconify(XtermWidget xw)
 1772 {
 1773     TScreen *screen = TScreenOf(xw);
 1774     Display *dpy = screen->display;
 1775     Window target = VShellWindow(xw);
 1776     XEvent e;
 1777     Atom atom_state = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
 1778 
 1779     if (xtermIsIconified(xw)) {
 1780     TRACE(("...de-iconify window %#lx\n", target));
 1781     XMapWindow(dpy, target);
 1782 
 1783     memset(&e, 0, sizeof(e));
 1784     e.xclient.type = ClientMessage;
 1785     e.xclient.message_type = atom_state;
 1786     e.xclient.display = dpy;
 1787     e.xclient.window = target;
 1788     e.xclient.format = 32;
 1789     e.xclient.data.l[0] = 1;
 1790     e.xclient.data.l[1] = CurrentTime;
 1791 
 1792     XSendEvent(dpy, DefaultRootWindow(dpy), False,
 1793            SubstructureRedirectMask | SubstructureNotifyMask, &e);
 1794     xevents(xw);
 1795     }
 1796 }
 1797 
 1798 void
 1799 xtermIconify(XtermWidget xw)
 1800 {
 1801     TScreen *screen = TScreenOf(xw);
 1802     Window target = VShellWindow(xw);
 1803 
 1804     if (!xtermIsIconified(xw)) {
 1805     TRACE(("...iconify window %#lx\n", target));
 1806     XIconifyWindow(screen->display,
 1807                target,
 1808                DefaultScreen(screen->display));
 1809     xevents(xw);
 1810     }
 1811 }
 1812 
 1813 Boolean
 1814 xtermIsIconified(XtermWidget xw)
 1815 {
 1816     XWindowAttributes win_attrs;
 1817     TScreen *screen = TScreenOf(xw);
 1818     Window target = VShellWindow(xw);
 1819     Display *dpy = screen->display;
 1820     Boolean result = False;
 1821 
 1822     if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
 1823     Atom actual_return_type;
 1824     int actual_format_return = 0;
 1825     unsigned long nitems_return = 0;
 1826     unsigned long bytes_after_return = 0;
 1827     unsigned char *prop_return = 0;
 1828     long long_length = 1024;
 1829     Atom requested_type = XA_ATOM;
 1830     Atom is_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
 1831     Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
 1832 
 1833     /* this works with non-EWMH */
 1834     result = (win_attrs.map_state != IsViewable) ? True : False;
 1835 
 1836     /* this is a convention used by some EWMH applications */
 1837     if (xtermGetWinProp(dpy,
 1838                 target,
 1839                 wm_state,
 1840                 0L,
 1841                 long_length,
 1842                 requested_type,
 1843                 &actual_return_type,
 1844                 &actual_format_return,
 1845                 &nitems_return,
 1846                 &bytes_after_return,
 1847                 &prop_return)
 1848         && prop_return != 0
 1849         && actual_return_type == requested_type
 1850         && actual_format_return == 32) {
 1851         unsigned long n;
 1852         for (n = 0; n < nitems_return; ++n) {
 1853         unsigned long check = (((unsigned long *)
 1854                     (void *) prop_return)[n]);
 1855         if (check == is_hidden) {
 1856             result = True;
 1857             break;
 1858         }
 1859         }
 1860     }
 1861     }
 1862     TRACE(("...window %#lx is%s iconified\n",
 1863        target,
 1864        result ? "" : " not"));
 1865     return result;
 1866 }
 1867 
 1868 #if OPT_MAXIMIZE
 1869 /*ARGSUSED*/
 1870 void
 1871 HandleDeIconify(Widget w,
 1872         XEvent *event GCC_UNUSED,
 1873         String *params GCC_UNUSED,
 1874         Cardinal *nparams GCC_UNUSED)
 1875 {
 1876     XtermWidget xw;
 1877 
 1878     if ((xw = getXtermWidget(w)) != 0) {
 1879     xtermDeiconify(xw);
 1880     }
 1881 }
 1882 
 1883 /*ARGSUSED*/
 1884 void
 1885 HandleIconify(Widget w,
 1886           XEvent *event GCC_UNUSED,
 1887           String *params GCC_UNUSED,
 1888           Cardinal *nparams GCC_UNUSED)
 1889 {
 1890     XtermWidget xw;
 1891 
 1892     if ((xw = getXtermWidget(w)) != 0) {
 1893     xtermIconify(xw);
 1894     }
 1895 }
 1896 
 1897 int
 1898 QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
 1899 {
 1900     TScreen *screen = TScreenOf(xw);
 1901     XSizeHints hints;
 1902     long supp = 0;
 1903     Window root_win;
 1904     int root_x = -1;        /* saved co-ordinates */
 1905     int root_y = -1;
 1906     unsigned root_border;
 1907     unsigned root_depth;
 1908     int code;
 1909 
 1910     if (XGetGeometry(screen->display,
 1911              RootWindowOfScreen(XtScreen(xw)),
 1912              &root_win,
 1913              &root_x,
 1914              &root_y,
 1915              width,
 1916              height,
 1917              &root_border,
 1918              &root_depth)) {
 1919     TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
 1920            root_x,
 1921            root_y,
 1922            *width,
 1923            *height,
 1924            root_border));
 1925 
 1926     *width -= (root_border * 2);
 1927     *height -= (root_border * 2);
 1928 
 1929     hints.flags = PMaxSize;
 1930     if (XGetWMNormalHints(screen->display,
 1931                   VShellWindow(xw),
 1932                   &hints,
 1933                   &supp)
 1934         && (hints.flags & PMaxSize) != 0) {
 1935 
 1936         TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
 1937            hints.max_width,
 1938            hints.max_height));
 1939 
 1940         if ((unsigned) hints.max_width < *width)
 1941         *width = (unsigned) hints.max_width;
 1942         if ((unsigned) hints.max_height < *height)
 1943         *height = (unsigned) hints.max_height;
 1944     }
 1945     code = 1;
 1946     } else {
 1947     *width = 0;
 1948     *height = 0;
 1949     code = 0;
 1950     }
 1951     return code;
 1952 }
 1953 
 1954 void
 1955 RequestMaximize(XtermWidget xw, int maximize)
 1956 {
 1957     TScreen *screen = TScreenOf(xw);
 1958     XWindowAttributes wm_attrs, vshell_attrs;
 1959     unsigned root_width = 0, root_height = 0;
 1960     Boolean success = False;
 1961 
 1962     TRACE(("RequestMaximize %d:%s\n",
 1963        maximize,
 1964        (maximize
 1965         ? "maximize"
 1966         : "restore")));
 1967 
 1968     /*
 1969      * Before any maximize, ensure that we can capture the current screensize
 1970      * as well as the estimated root-window size.
 1971      */
 1972     if (maximize
 1973     && QueryMaximize(xw, &root_width, &root_height)
 1974     && xtermGetWinAttrs(screen->display,
 1975                 WMFrameWindow(xw),
 1976                 &wm_attrs)
 1977     && xtermGetWinAttrs(screen->display,
 1978                 VShellWindow(xw),
 1979                 &vshell_attrs)) {
 1980 
 1981     if (screen->restore_data != True
 1982         || screen->restore_width != root_width
 1983         || screen->restore_height != root_height) {
 1984         screen->restore_data = True;
 1985         screen->restore_x = wm_attrs.x;
 1986         screen->restore_y = wm_attrs.y;
 1987         screen->restore_width = (unsigned) vshell_attrs.width;
 1988         screen->restore_height = (unsigned) vshell_attrs.height;
 1989         TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
 1990            screen->restore_x,
 1991            screen->restore_y,
 1992            screen->restore_width,
 1993            screen->restore_height));
 1994     }
 1995 
 1996     /* subtract wm decoration dimensions */
 1997     root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
 1998     root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
 1999     success = True;
 2000     } else if (screen->restore_data) {
 2001     success = True;
 2002     maximize = 0;
 2003     }
 2004 
 2005     if (success) {
 2006     switch (maximize) {
 2007     case 3:
 2008         FullScreen(xw, 3);  /* depends on EWMH */
 2009         break;
 2010     case 2:
 2011         FullScreen(xw, 2);  /* depends on EWMH */
 2012         break;
 2013     case 1:
 2014         FullScreen(xw, 0);  /* overrides any EWMH hint */
 2015         TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
 2016            0,
 2017            0,
 2018            root_width,
 2019            root_height));
 2020         XMoveResizeWindow(screen->display, VShellWindow(xw),
 2021                   0,    /* x */
 2022                   0,    /* y */
 2023                   root_width,
 2024                   root_height);
 2025         break;
 2026 
 2027     default:
 2028         FullScreen(xw, 0);  /* reset any EWMH hint */
 2029         if (screen->restore_data) {
 2030         screen->restore_data = False;
 2031 
 2032         TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
 2033                screen->restore_x,
 2034                screen->restore_y,
 2035                screen->restore_width,
 2036                screen->restore_height));
 2037 
 2038         XMoveResizeWindow(screen->display,
 2039                   VShellWindow(xw),
 2040                   screen->restore_x,
 2041                   screen->restore_y,
 2042                   screen->restore_width,
 2043                   screen->restore_height);
 2044         }
 2045         break;
 2046     }
 2047     }
 2048 }
 2049 
 2050 /*ARGSUSED*/
 2051 void
 2052 HandleMaximize(Widget w,
 2053            XEvent *event GCC_UNUSED,
 2054            String *params GCC_UNUSED,
 2055            Cardinal *nparams GCC_UNUSED)
 2056 {
 2057     XtermWidget xw;
 2058 
 2059     if ((xw = getXtermWidget(w)) != 0) {
 2060     RequestMaximize(xw, 1);
 2061     }
 2062 }
 2063 
 2064 /*ARGSUSED*/
 2065 void
 2066 HandleRestoreSize(Widget w,
 2067           XEvent *event GCC_UNUSED,
 2068           String *params GCC_UNUSED,
 2069           Cardinal *nparams GCC_UNUSED)
 2070 {
 2071     XtermWidget xw;
 2072 
 2073     if ((xw = getXtermWidget(w)) != 0) {
 2074     RequestMaximize(xw, 0);
 2075     }
 2076 }
 2077 #endif /* OPT_MAXIMIZE */
 2078 
 2079 void
 2080 Redraw(void)
 2081 {
 2082     XtermWidget xw = term;
 2083     TScreen *screen = TScreenOf(xw);
 2084     XExposeEvent event;
 2085 
 2086     TRACE(("Redraw\n"));
 2087 
 2088     event.type = Expose;
 2089     event.display = screen->display;
 2090     event.x = 0;
 2091     event.y = 0;
 2092     event.count = 0;
 2093 
 2094     if (VWindow(screen)) {
 2095     event.window = VWindow(screen);
 2096     event.width = xw->core.width;
 2097     event.height = xw->core.height;
 2098     (*xw->core.widget_class->core_class.expose) ((Widget) xw,
 2099                              (XEvent *) &event,
 2100                              NULL);
 2101     if (ScrollbarWidth(screen)) {
 2102         (screen->scrollWidget->core.widget_class->core_class.expose)
 2103         (screen->scrollWidget, (XEvent *) &event, NULL);
 2104     }
 2105     }
 2106 #if OPT_TEK4014
 2107     if (TEK4014_SHOWN(xw)) {
 2108     TekScreen *tekscr = TekScreenOf(tekWidget);
 2109     event.window = TWindow(tekscr);
 2110     event.width = tekWidget->core.width;
 2111     event.height = tekWidget->core.height;
 2112     TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
 2113     }
 2114 #endif
 2115 }
 2116 
 2117 #ifdef VMS
 2118 #define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
 2119 #else
 2120 #define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
 2121 #endif
 2122 
 2123 void
 2124 timestamp_filename(char *dst, const char *src)
 2125 {
 2126     time_t tstamp;
 2127     struct tm *tstruct;
 2128 
 2129     tstamp = time((time_t *) 0);
 2130     tstruct = localtime(&tstamp);
 2131     sprintf(dst, TIMESTAMP_FMT,
 2132         src,
 2133         (int) tstruct->tm_year + 1900,
 2134         tstruct->tm_mon + 1,
 2135         tstruct->tm_mday,
 2136         tstruct->tm_hour,
 2137         tstruct->tm_min,
 2138         tstruct->tm_sec);
 2139 }
 2140 
 2141 FILE *
 2142 create_printfile(XtermWidget xw, const char *suffix)
 2143 {
 2144     TScreen *screen = TScreenOf(xw);
 2145     char fname[1024];
 2146     int fd;
 2147     FILE *fp;
 2148 
 2149 #ifdef VMS
 2150     sprintf(fname, "sys$scratch:xterm%s", suffix);
 2151 #elif defined(HAVE_STRFTIME)
 2152     {
 2153     char format[1024];
 2154     time_t now;
 2155     struct tm *ltm;
 2156 
 2157     now = time((time_t *) 0);
 2158     ltm = localtime(&now);
 2159 
 2160     sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
 2161     if (strftime(fname, sizeof fname, format, ltm) == 0) {
 2162         sprintf(fname, "xterm%s", suffix);
 2163     }
 2164     }
 2165 #else
 2166     sprintf(fname, "xterm%s", suffix);
 2167 #endif
 2168     fd = open_userfile(screen->uid, screen->gid, fname, False);
 2169     fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
 2170     return fp;
 2171 }
 2172 
 2173 int
 2174 open_userfile(uid_t uid, gid_t gid, char *path, Bool append)
 2175 {
 2176     int fd;
 2177     struct stat sb;
 2178 
 2179 #ifdef VMS
 2180     if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
 2181     int the_error = errno;
 2182     xtermWarning("cannot open %s: %d:%s\n",
 2183              path,
 2184              the_error,
 2185              SysErrorMsg(the_error));
 2186     return -1;
 2187     }
 2188     chown(path, uid, gid);
 2189 #else
 2190     if ((access(path, F_OK) != 0 && (errno != ENOENT))
 2191     || (creat_as(uid, gid, append, path, 0644) <= 0)
 2192     || ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
 2193     int the_error = errno;
 2194     xtermWarning("cannot open %s: %d:%s\n",
 2195              path,
 2196              the_error,
 2197              SysErrorMsg(the_error));
 2198     return -1;
 2199     }
 2200 #endif
 2201 
 2202     /*
 2203      * Doublecheck that the user really owns the file that we've opened before
 2204      * we do any damage, and that it is not world-writable.
 2205      */
 2206     if (fstat(fd, &sb) < 0
 2207     || sb.st_uid != uid
 2208     || (sb.st_mode & 022) != 0) {
 2209     xtermWarning("you do not own %s\n", path);
 2210     close(fd);
 2211     return -1;
 2212     }
 2213     return fd;
 2214 }
 2215 
 2216 #ifndef VMS
 2217 /*
 2218  * Create a file only if we could with the permissions of the real user id.
 2219  * We could emulate this with careful use of access() and following
 2220  * symbolic links, but that is messy and has race conditions.
 2221  * Forking is messy, too, but we can't count on setreuid() or saved set-uids
 2222  * being available.
 2223  *
 2224  * Note: When called for user logging, we have ensured that the real and
 2225  * effective user ids are the same, so this remains as a convenience function
 2226  * for the debug logs.
 2227  *
 2228  * Returns
 2229  *   1 if we can proceed to open the file in relative safety,
 2230  *  -1 on error, e.g., cannot fork
 2231  *   0 otherwise.
 2232  */
 2233 int
 2234 creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
 2235 {
 2236     int fd;
 2237     pid_t pid;
 2238     int retval = 0;
 2239     int childstat = 0;
 2240 #ifndef HAVE_WAITPID
 2241     int waited;
 2242     void (*chldfunc) (int);
 2243 
 2244     chldfunc = signal(SIGCHLD, SIG_DFL);
 2245 #endif /* HAVE_WAITPID */
 2246 
 2247     TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
 2248        (int) uid, (int) geteuid(),
 2249        (int) gid, (int) getegid(),
 2250        append,
 2251        pathname,
 2252        mode));
 2253 
 2254     if (uid == geteuid() && gid == getegid()) {
 2255     fd = open(pathname,
 2256           O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
 2257           mode);
 2258     if (fd >= 0)
 2259         close(fd);
 2260     return (fd >= 0);
 2261     }
 2262 
 2263     pid = fork();
 2264     switch (pid) {
 2265     case 0:         /* child */
 2266     if (setgid(gid) == -1
 2267         || setuid(uid) == -1) {
 2268         /* we cannot report an error here via stderr, just quit */
 2269         retval = 1;
 2270     } else {
 2271         fd = open(pathname,
 2272               O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
 2273               mode);
 2274         if (fd >= 0) {
 2275         close(fd);
 2276         retval = 0;
 2277         } else {
 2278         retval = 1;
 2279         }
 2280     }
 2281     _exit(retval);
 2282     /* NOTREACHED */
 2283     case -1:            /* error */
 2284     return retval;
 2285     default:            /* parent */
 2286 #ifdef HAVE_WAITPID
 2287     while (waitpid(pid, &childstat, 0) < 0) {
 2288 #ifdef EINTR
 2289         if (errno == EINTR)
 2290         continue;
 2291 #endif /* EINTR */
 2292 #ifdef ERESTARTSYS
 2293         if (errno == ERESTARTSYS)
 2294         continue;
 2295 #endif /* ERESTARTSYS */
 2296         break;
 2297     }
 2298 #else /* HAVE_WAITPID */
 2299     waited = wait(&childstat);
 2300     signal(SIGCHLD, chldfunc);
 2301     /*
 2302        Since we had the signal handler uninstalled for a while,
 2303        we might have missed the termination of our screen child.
 2304        If we can check for this possibility without hanging, do so.
 2305      */
 2306     do
 2307         if (waited == TScreenOf(term)->pid)
 2308         NormalExit();
 2309     while ((waited = nonblocking_wait()) > 0) ;
 2310 #endif /* HAVE_WAITPID */
 2311 #ifndef WIFEXITED
 2312 #define WIFEXITED(status) ((status & 0xff) != 0)
 2313 #endif
 2314     if (WIFEXITED(childstat))
 2315         retval = 1;
 2316     return retval;
 2317     }
 2318 }
 2319 #endif /* !VMS */
 2320 
 2321 int
 2322 xtermResetIds(TScreen *screen)
 2323 {
 2324     int result = 0;
 2325     if (setgid(screen->gid) == -1) {
 2326     xtermWarning("unable to reset group-id\n");
 2327     result = -1;
 2328     }
 2329     if (setuid(screen->uid) == -1) {
 2330     xtermWarning("unable to reset user-id\n");
 2331     result = -1;
 2332     }
 2333     return result;
 2334 }
 2335 
 2336 #ifdef ALLOWLOGGING
 2337 
 2338 /*
 2339  * Logging is a security hole, since it allows a setuid program to write
 2340  * arbitrary data to an arbitrary file.  So it is disabled by default.
 2341  */
 2342 
 2343 #ifdef ALLOWLOGFILEEXEC
 2344 static void
 2345 handle_SIGPIPE(int sig GCC_UNUSED)
 2346 {
 2347     XtermWidget xw = term;
 2348     TScreen *screen = TScreenOf(xw);
 2349 
 2350     DEBUG_MSG("handle:logpipe\n");
 2351 #ifdef SYSV
 2352     (void) signal(SIGPIPE, SIG_IGN);
 2353 #endif /* SYSV */
 2354     if (screen->logging)
 2355     CloseLog(xw);
 2356 }
 2357 
 2358 /*
 2359  * Open a command to pipe log data to it.
 2360  * Warning, enabling this "feature" allows arbitrary programs
 2361  * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
 2362  * done through escape sequences....  You have been warned.
 2363  */
 2364 static void
 2365 StartLogExec(TScreen *screen)
 2366 {
 2367     int pid;
 2368     int p[2];
 2369     static char *shell;
 2370     struct passwd pw;
 2371 
 2372     if ((shell = x_getenv("SHELL")) == NULL) {
 2373 
 2374     if (x_getpwuid(screen->uid, &pw)) {
 2375         char *name = x_getlogin(screen->uid, &pw);
 2376         if (*(pw.pw_shell)) {
 2377         shell = pw.pw_shell;
 2378         }
 2379         free(name);
 2380     }
 2381     }
 2382 
 2383     if (shell == 0) {
 2384     static char dummy[] = "/bin/sh";
 2385     shell = dummy;
 2386     }
 2387 
 2388     if (access(shell, X_OK) != 0) {
 2389     xtermPerror("Can't execute `%s'\n", shell);
 2390     return;
 2391     }
 2392 
 2393     if (pipe(p) < 0) {
 2394     xtermPerror("Can't make a pipe connection\n");
 2395     return;
 2396     } else if ((pid = fork()) < 0) {
 2397     xtermPerror("Can't fork...\n");
 2398     return;
 2399     }
 2400     if (pid == 0) {     /* child */
 2401     /*
 2402      * Close our output (we won't be talking back to the
 2403      * parent), and redirect our child's output to the
 2404      * original stderr.
 2405      */
 2406     close(p[1]);
 2407     dup2(p[0], 0);
 2408     close(p[0]);
 2409     dup2(fileno(stderr), 1);
 2410     dup2(fileno(stderr), 2);
 2411 
 2412     close(fileno(stderr));
 2413     close(ConnectionNumber(screen->display));
 2414     close(screen->respond);
 2415 
 2416     signal(SIGHUP, SIG_DFL);
 2417     signal(SIGCHLD, SIG_DFL);
 2418 
 2419     /* (this is redundant) */
 2420     if (xtermResetIds(screen) < 0)
 2421         exit(ERROR_SETUID);
 2422 
 2423     execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
 2424     xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
 2425     exit(ERROR_LOGEXEC);
 2426     }
 2427     close(p[0]);
 2428     screen->logfd = p[1];
 2429     signal(SIGPIPE, handle_SIGPIPE);
 2430 }
 2431 #endif /* ALLOWLOGFILEEXEC */
 2432 
 2433 /*
 2434  * Generate a path for a logfile if no default path is given.
 2435  */
 2436 static char *
 2437 GenerateLogPath(void)
 2438 {
 2439     static char *log_default = NULL;
 2440 
 2441     /* once opened we just reuse the same log name */
 2442     if (log_default)
 2443     return (log_default);
 2444 
 2445 #if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
 2446     {
 2447 #define LEN_HOSTNAME 255
 2448     /* Internet standard limit (RFC 1035):  ``To simplify implementations,
 2449      * the total length of a domain name (i.e., label octets and label
 2450      * length octets) is restricted to 255 octets or less.''
 2451      */
 2452 #define LEN_GETPID 9
 2453     /*
 2454      * This is arbitrary...
 2455      */
 2456     const char form[] = "Xterm.log.%s%s.%lu";
 2457     char where[LEN_HOSTNAME + 1];
 2458     char when[LEN_TIMESTAMP];
 2459     time_t now = time((time_t *) 0);
 2460     struct tm *ltm = (struct tm *) localtime(&now);
 2461 
 2462     if ((gethostname(where, sizeof(where)) == 0) &&
 2463         (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
 2464         ((log_default = (char *) malloc((sizeof(form)
 2465                          + strlen(where)
 2466                          + strlen(when)
 2467                          + LEN_GETPID))) != NULL)) {
 2468         (void) sprintf(log_default,
 2469                form,
 2470                where, when,
 2471                ((unsigned long) getpid()) % ((unsigned long) 1e10));
 2472     }
 2473     }
 2474 #else
 2475     static const char log_def_name[] = "XtermLog.XXXXXX";
 2476     if ((log_default = x_strdup(log_def_name)) != NULL) {
 2477     mktemp(log_default);
 2478     }
 2479 #endif
 2480 
 2481     return (log_default);
 2482 }
 2483 
 2484 void
 2485 StartLog(XtermWidget xw)
 2486 {
 2487     TScreen *screen = TScreenOf(xw);
 2488 
 2489     if (screen->logging || (screen->inhibit & I_LOG))
 2490     return;
 2491 #ifdef VMS          /* file name is fixed in VMS variant */
 2492     screen->logfd = open(XTERM_VMS_LOGFILE,
 2493              O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
 2494              0640);
 2495     if (screen->logfd < 0)
 2496     return;         /* open failed */
 2497 #else /*VMS */
 2498 
 2499     /* if we weren't supplied with a logfile path, generate one */
 2500     if (IsEmpty(screen->logfile))
 2501     screen->logfile = GenerateLogPath();
 2502 
 2503     /* give up if we were unable to allocate the filename */
 2504     if (!screen->logfile)
 2505     return;
 2506 
 2507     if (*screen->logfile == '|') {  /* exec command */
 2508 #ifdef ALLOWLOGFILEEXEC
 2509     StartLogExec(screen);
 2510 #else
 2511     Bell(xw, XkbBI_Info, 0);
 2512     Bell(xw, XkbBI_Info, 0);
 2513     return;
 2514 #endif
 2515     } else if (strcmp(screen->logfile, "-") == 0) {
 2516     screen->logfd = STDOUT_FILENO;
 2517     } else {
 2518     if ((screen->logfd = open_userfile(screen->uid,
 2519                        screen->gid,
 2520                        screen->logfile,
 2521                        True)) < 0)
 2522         return;
 2523     }
 2524 #endif /*VMS */
 2525     screen->logstart = VTbuffer->next;
 2526     screen->logging = True;
 2527     update_logging();
 2528 }
 2529 
 2530 void
 2531 CloseLog(XtermWidget xw)
 2532 {
 2533     TScreen *screen = TScreenOf(xw);
 2534 
 2535     if (!screen->logging || (screen->inhibit & I_LOG))
 2536     return;
 2537     FlushLog(xw);
 2538     close(screen->logfd);
 2539     screen->logging = False;
 2540     update_logging();
 2541 }
 2542 
 2543 void
 2544 FlushLog(XtermWidget xw)
 2545 {
 2546     TScreen *screen = TScreenOf(xw);
 2547 
 2548     if (screen->logging && !(screen->inhibit & I_LOG)) {
 2549     Char *cp;
 2550     int i;
 2551 
 2552 #ifdef VMS          /* avoid logging output loops which otherwise occur sometimes
 2553                    when there is no output and cp/screen->logstart are 1 apart */
 2554     if (!tt_new_output)
 2555         return;
 2556     tt_new_output = False;
 2557 #endif /* VMS */
 2558     cp = VTbuffer->next;
 2559     if (screen->logstart != 0
 2560         && (i = (int) (cp - screen->logstart)) > 0) {
 2561         IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
 2562     }
 2563     screen->logstart = VTbuffer->next;
 2564     }
 2565 }
 2566 
 2567 #endif /* ALLOWLOGGING */
 2568 
 2569 /***====================================================================***/
 2570 
 2571 static unsigned
 2572 maskToShift(unsigned long mask)
 2573 {
 2574     unsigned result = 0;
 2575     if (mask != 0) {
 2576     while ((mask & 1) == 0) {
 2577         mask >>= 1;
 2578         ++result;
 2579     }
 2580     }
 2581     return result;
 2582 }
 2583 
 2584 static unsigned
 2585 maskToWidth(unsigned long mask)
 2586 {
 2587     unsigned result = 0;
 2588     while (mask != 0) {
 2589     if ((mask & 1) != 0)
 2590         ++result;
 2591     mask >>= 1;
 2592     }
 2593     return result;
 2594 }
 2595 
 2596 int
 2597 getVisualInfo(XtermWidget xw)
 2598 {
 2599 #define MYFMT "getVisualInfo \
 2600 depth %d, \
 2601 type %d (%s), \
 2602 size %d \
 2603 rgb masks (%04lx/%04lx/%04lx)\n"
 2604 #define MYARG \
 2605        vi->depth,\
 2606        vi->class,\
 2607        ((vi->class & 1) ? "dynamic" : "static"),\
 2608        vi->colormap_size,\
 2609        vi->red_mask,\
 2610        vi->green_mask,\
 2611        vi->blue_mask
 2612 
 2613     TScreen *screen = TScreenOf(xw);
 2614     Display *dpy = screen->display;
 2615     XVisualInfo myTemplate;
 2616 
 2617     if (xw->visInfo == 0 && xw->numVisuals == 0) {
 2618     myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
 2619                                 XDefaultScreen(dpy)));
 2620     xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
 2621                      &myTemplate, &xw->numVisuals);
 2622 
 2623     if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
 2624         XVisualInfo *vi = xw->visInfo;
 2625         xw->rgb_widths[0] = maskToWidth(vi->red_mask);
 2626         xw->rgb_widths[1] = maskToWidth(vi->green_mask);
 2627         xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
 2628         xw->rgb_shifts[0] = maskToShift(vi->red_mask);
 2629         xw->rgb_shifts[1] = maskToShift(vi->green_mask);
 2630         xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
 2631 
 2632         xw->has_rgb = ((vi->red_mask != 0) &&
 2633                (vi->green_mask != 0) &&
 2634                (vi->blue_mask != 0) &&
 2635                ((vi->red_mask & vi->green_mask) == 0) &&
 2636                ((vi->green_mask & vi->blue_mask) == 0) &&
 2637                ((vi->blue_mask & vi->red_mask) == 0));
 2638 
 2639         if (resource.reportColors) {
 2640         printf(MYFMT, MYARG);
 2641         }
 2642         TRACE((MYFMT, MYARG));
 2643         TRACE(("...shifts %u/%u/%u\n",
 2644            xw->rgb_shifts[0],
 2645            xw->rgb_shifts[1],
 2646            xw->rgb_shifts[2]));
 2647     }
 2648     }
 2649     return (xw->visInfo != 0) && (xw->numVisuals > 0);
 2650 #undef MYFMT
 2651 #undef MYARG
 2652 }
 2653 
 2654 #if OPT_ISO_COLORS
 2655 static Bool
 2656 ReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
 2657 {
 2658     Bool result = False;
 2659 
 2660     if (AllowColorOps(xw, ecGetAnsiColor)) {
 2661     XColor color;
 2662     Colormap cmap = xw->core.colormap;
 2663     char buffer[80];
 2664 
 2665     TRACE(("ReportAnsiColorRequest %d\n", colornum));
 2666     color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
 2667     XQueryColor(TScreenOf(xw)->display, cmap, &color);
 2668     sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
 2669         opcode,
 2670         (opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
 2671         color.red,
 2672         color.green,
 2673         color.blue);
 2674     unparseputc1(xw, ANSI_OSC);
 2675     unparseputs(xw, buffer);
 2676     unparseputc1(xw, final);
 2677     result = True;
 2678     }
 2679     return result;
 2680 }
 2681 
 2682 static void
 2683 getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
 2684 {
 2685     if (getVisualInfo(xw)) {
 2686     *typep = (unsigned) xw->visInfo->class;
 2687     *sizep = (unsigned) xw->visInfo->colormap_size;
 2688     } else {
 2689     *typep = 0;
 2690     *sizep = 0;
 2691     }
 2692 }
 2693 
 2694 #define MAX_COLORTABLE 4096
 2695 
 2696 /*
 2697  * Make only one call to XQueryColors(), since it can be slow.
 2698  */
 2699 static Boolean
 2700 loadColorTable(XtermWidget xw, unsigned length)
 2701 {
 2702     Colormap cmap = xw->core.colormap;
 2703     TScreen *screen = TScreenOf(xw);
 2704     Boolean result = (screen->cmap_data != 0);
 2705 
 2706     if (!result
 2707     && length != 0
 2708     && length < MAX_COLORTABLE) {
 2709     screen->cmap_data = TypeMallocN(XColor, (size_t) length);
 2710 
 2711     if (screen->cmap_data != 0) {
 2712         unsigned i;
 2713         unsigned shift;
 2714 
 2715         if (getVisualInfo(xw))
 2716         shift = xw->rgb_shifts[2];
 2717         else
 2718         shift = 0;
 2719 
 2720         screen->cmap_size = length;
 2721 
 2722         for (i = 0; i < screen->cmap_size; i++) {
 2723         screen->cmap_data[i].pixel = (unsigned long) i << shift;
 2724         }
 2725         result = (Boolean) (XQueryColors(screen->display,
 2726                          cmap,
 2727                          screen->cmap_data,
 2728                          (int) screen->cmap_size) != 0);
 2729     }
 2730     }
 2731     return result;
 2732 }
 2733 
 2734 /*
 2735  * Find closest color for "def" in "cmap".
 2736  * Set "def" to the resulting color.
 2737  *
 2738  * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
 2739  * modified with ideas from David Tong's "noflash" library.
 2740  * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
 2741  *
 2742  * Return False if not able to find or allocate a color.
 2743  */
 2744 static Boolean
 2745 allocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def)
 2746 {
 2747     TScreen *screen = TScreenOf(xw);
 2748     Boolean result = False;
 2749     unsigned cmap_type;
 2750     unsigned cmap_size;
 2751 
 2752     getColormapInfo(xw, &cmap_type, &cmap_size);
 2753 
 2754     if ((cmap_type & 1) != 0) {
 2755 
 2756     if (loadColorTable(xw, cmap_size)) {
 2757         char *tried = TypeCallocN(char, (size_t) cmap_size);
 2758 
 2759         if (tried != 0) {
 2760         unsigned attempts;
 2761 
 2762         /*
 2763          * Try (possibly each entry in the color map) to find the best
 2764          * approximation to the requested color.
 2765          */
 2766         for (attempts = 0; attempts < cmap_size; attempts++) {
 2767             Boolean first = True;
 2768             double bestRGB = 0.0;
 2769             unsigned bestInx = 0;
 2770             unsigned i;
 2771 
 2772             for (i = 0; i < cmap_size; i++) {
 2773             if (!tried[bestInx]) {
 2774                 double diff, thisRGB = 0.0;
 2775 
 2776                 /*
 2777                  * Look for the best match based on luminance.
 2778                  * Measure this by the least-squares difference of
 2779                  * the weighted R/G/B components from the color map
 2780                  * versus the requested color.  Use the Y (luma)
 2781                  * component of the YIQ color space model for
 2782                  * weights that correspond to the luminance.
 2783                  */
 2784 #define AddColorWeight(weight, color) \
 2785                 diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
 2786                 thisRGB += diff * diff
 2787 
 2788                 AddColorWeight(0.30, red);
 2789                 AddColorWeight(0.61, green);
 2790                 AddColorWeight(0.11, blue);
 2791 
 2792                 if (first || (thisRGB < bestRGB)) {
 2793                 first = False;
 2794                 bestInx = i;
 2795                 bestRGB = thisRGB;
 2796                 }
 2797             }
 2798             }
 2799             if (XAllocColor(screen->display, cmap,
 2800                     &screen->cmap_data[bestInx]) != 0) {
 2801             *def = screen->cmap_data[bestInx];
 2802             TRACE(("...closest %x/%x/%x\n", def->red,
 2803                    def->green, def->blue));
 2804             result = True;
 2805             break;
 2806             }
 2807             /*
 2808              * It failed - either the color map entry was readonly, or
 2809              * another client has allocated the entry.  Mark the entry
 2810              * so we will ignore it
 2811              */
 2812             tried[bestInx] = True;
 2813         }
 2814         free(tried);
 2815         }
 2816     }
 2817     }
 2818     return result;
 2819 }
 2820 
 2821 #ifndef ULONG_MAX
 2822 #define ULONG_MAX (unsigned long)(~(0L))
 2823 #endif
 2824 
 2825 #define CheckColor(result, value) \
 2826         result = 0; \
 2827         if (value.red) \
 2828         result |= 1; \
 2829         if (value.green) \
 2830         result |= 2; \
 2831         if (value.blue) \
 2832         result |= 4
 2833 
 2834 #define SelectColor(state, value, result) \
 2835     switch (state) { \
 2836     default: \
 2837     case 1: \
 2838         result = value.red; \
 2839         break; \
 2840     case 2: \
 2841         result = value.green; \
 2842         break; \
 2843     case 4: \
 2844         result = value.blue; \
 2845         break; \
 2846     }
 2847 
 2848 /*
 2849  * Check if the color map consists of values in exactly one of the red, green
 2850  * or blue columns.  If it is not, we do not know how to use it for the exact
 2851  * match.
 2852  */
 2853 static int
 2854 simpleColors(XColor *colortable, unsigned length)
 2855 {
 2856     unsigned n;
 2857     int state = 0;
 2858     int check;
 2859 
 2860     for (n = 0; n < length; ++n) {
 2861     if (state > 0) {
 2862         CheckColor(check, colortable[n]);
 2863         if (check > 0 && check != state) {
 2864         state = 0;
 2865         break;
 2866         }
 2867     } else {
 2868         CheckColor(state, colortable[n]);
 2869     }
 2870     }
 2871     switch (state) {
 2872     case 1:
 2873     case 2:
 2874     case 4:
 2875     break;
 2876     default:
 2877     state = 0;
 2878     break;
 2879     }
 2880     return state;
 2881 }
 2882 
 2883 /*
 2884  * Shift the mask left or right to put its most significant bit at the 16-bit
 2885  * mark.
 2886  */
 2887 static unsigned
 2888 normalizeMask(unsigned mask)
 2889 {
 2890     while (mask < 0x8000) {
 2891     mask <<= 1;
 2892     }
 2893     while (mask >= 0x10000) {
 2894     mask >>= 1;
 2895     }
 2896     return mask;
 2897 }
 2898 
 2899 static unsigned
 2900 searchColors(XColor *colortable, unsigned mask, unsigned length, unsigned
 2901          color, int state)
 2902 {
 2903     unsigned result = 0;
 2904     unsigned n;
 2905     unsigned long best = ULONG_MAX;
 2906     unsigned value;
 2907 
 2908     mask = normalizeMask(mask);
 2909     for (n = 0; n < length; ++n) {
 2910     unsigned long diff;
 2911 
 2912     SelectColor(state, colortable[n], value);
 2913     diff = ((color & mask) - (value & mask));
 2914     diff *= diff;
 2915     if (diff < best) {
 2916 #if 0
 2917         TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
 2918            n, color,
 2919            colortable[n].red,
 2920            colortable[n].green,
 2921            colortable[n].blue,
 2922            diff));
 2923 #endif
 2924         result = n;
 2925         best = diff;
 2926     }
 2927     }
 2928     SelectColor(state, colortable[result], value);
 2929     return value;
 2930 }
 2931 
 2932 /*
 2933  * This is a workaround for a longstanding defect in the X libraries.
 2934  *
 2935  * According to
 2936  * http://www.unix.com/man-page/all/3x/XAllocColoA/
 2937  *
 2938  *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
 2939  *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
 2940  *     there  are  no  unallocated  colorcells and no allocated read-only cell
 2941  *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
 2942  *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
 2943  *     available in the colormap.  The colorcell_in_out structure returns  the
 2944  *     actual RGB values allocated.
 2945  *
 2946  * That is, XAllocColor() should suffice unless the color map is full.  In that
 2947  * case, allocateClosestRGB() is useful for the dynamic display classes such as
 2948  * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
 2949  * return regular RGB triples (unless a different scheme was used for
 2950  * specifying the pixel values); only the blue value is filled in.  However, it
 2951  * is filled in with the colors that the server supports.
 2952  *
 2953  * Also (the reason for this function), XAllocColor() does not really work as
 2954  * described.  For some TrueColor configurations it merely returns a close
 2955  * approximation, but not the closest.
 2956  */
 2957 static Boolean
 2958 allocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def)
 2959 {
 2960     XColor save = *def;
 2961     TScreen *screen = TScreenOf(xw);
 2962     Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
 2963 
 2964     /*
 2965      * If this is a statically allocated display with too many items to store
 2966      * in our array, i.e., TrueColor, see if we can improve on the result by
 2967      * using the color values actually supported by the server.
 2968      */
 2969     if (result) {
 2970     unsigned cmap_type;
 2971     unsigned cmap_size;
 2972 
 2973     getColormapInfo(xw, &cmap_type, &cmap_size);
 2974 
 2975     if (cmap_type == TrueColor) {
 2976         XColor temp = *def;
 2977         int state;
 2978 
 2979         if (loadColorTable(xw, cmap_size)
 2980         && (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
 2981 #define SearchColors(which) \
 2982     temp.which = (unsigned short) searchColors(screen->cmap_data, \
 2983                            (unsigned) xw->visInfo->which##_mask,\
 2984                            cmap_size, \
 2985                            save.which, \
 2986                            state)
 2987         SearchColors(red);
 2988         SearchColors(green);
 2989         SearchColors(blue);
 2990         if (XAllocColor(screen->display, cmap, &temp) != 0) {
 2991 #if OPT_TRACE
 2992             if (temp.red != save.red
 2993             || temp.green != save.green
 2994             || temp.blue != save.blue) {
 2995             TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
 2996                    save.red, save.green, save.blue,
 2997                    temp.red, temp.green, temp.blue));
 2998             } else {
 2999             TRACE(("...no improvement for %x/%x/%x\n",
 3000                    save.red, save.green, save.blue));
 3001             }
 3002 #endif
 3003             *def = temp;
 3004         }
 3005         }
 3006     }
 3007     }
 3008 
 3009     return result;
 3010 }
 3011 
 3012 /*
 3013  * Allocate a color for the "ANSI" colors.  That actually includes colors up
 3014  * to 256.
 3015  *
 3016  * Returns
 3017  *  -1 on error
 3018  *  0 on no change
 3019  *  1 if a new color was allocated.
 3020  */
 3021 static int
 3022 AllocateAnsiColor(XtermWidget xw,
 3023           ColorRes * res,
 3024           const char *spec)
 3025 {
 3026     int result;
 3027     XColor def;
 3028 
 3029     if (xtermAllocColor(xw, &def, spec)) {
 3030     if (
 3031 #if OPT_COLOR_RES
 3032            res->mode == True &&
 3033 #endif
 3034            EQL_COLOR_RES(res, def.pixel)) {
 3035         result = 0;
 3036     } else {
 3037         result = 1;
 3038         SET_COLOR_RES(res, def.pixel);
 3039         res->red = def.red;
 3040         res->green = def.green;
 3041         res->blue = def.blue;
 3042         TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
 3043            (int) (res - TScreenOf(xw)->Acolors), spec,
 3044            def.red,
 3045            def.green,
 3046            def.blue,
 3047            def.pixel));
 3048 #if OPT_COLOR_RES
 3049         if (!res->mode)
 3050         result = 0;
 3051         res->mode = True;
 3052 #endif
 3053     }
 3054     } else {
 3055     TRACE(("AllocateAnsiColor %s (failed)\n", spec));
 3056     result = -1;
 3057     }
 3058     return (result);
 3059 }
 3060 
 3061 #if OPT_COLOR_RES
 3062 Pixel
 3063 xtermGetColorRes(XtermWidget xw, ColorRes * res)
 3064 {
 3065     Pixel result = 0;
 3066 
 3067     if (res->mode) {
 3068     result = res->value;
 3069     } else {
 3070     TRACE(("xtermGetColorRes for Acolors[%d]\n",
 3071            (int) (res - TScreenOf(xw)->Acolors)));
 3072 
 3073     if (res >= TScreenOf(xw)->Acolors) {
 3074         assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
 3075 
 3076         if (AllocateAnsiColor(xw, res, res->resource) < 0) {
 3077         res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
 3078         res->mode = -True;
 3079         xtermWarning("Cannot allocate color \"%s\"\n",
 3080                  NonNull(res->resource));
 3081         }
 3082         result = res->value;
 3083     } else {
 3084         result = 0;
 3085     }
 3086     }
 3087     return result;
 3088 }
 3089 #endif
 3090 
 3091 static int
 3092 ChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
 3093 {
 3094     int code;
 3095 
 3096     if (color < 0 || color >= MAXCOLORS) {
 3097     code = -1;
 3098     } else {
 3099     ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
 3100 
 3101     TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
 3102     code = AllocateAnsiColor(xw, res, name);
 3103     }
 3104     return code;
 3105 }
 3106 
 3107 /*
 3108  * Set or query entries in the Acolors[] array by parsing pairs of color/name
 3109  * values from the given buffer.
 3110  *
 3111  * The color can be any legal index into Acolors[], which consists of the
 3112  * 16/88/256 "ANSI" colors, followed by special color values for the various
 3113  * colorXX resources.  The indices for the special color values are not
 3114  * simple to work with, so an alternative is to use the calls which pass in
 3115  * 'first' set to the beginning of those indices.
 3116  *
 3117  * If the name is "?", report to the host the current value for the color.
 3118  */
 3119 static Bool
 3120 ChangeAnsiColorRequest(XtermWidget xw,
 3121                int opcode,
 3122                char *buf,
 3123                int first,
 3124                int final)
 3125 {
 3126     int repaint = False;
 3127     int code;
 3128     int last = (MAXCOLORS - first);
 3129     int queried = 0;
 3130 
 3131     TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
 3132 
 3133     while (buf && *buf) {
 3134     int color;
 3135     char *name = strchr(buf, ';');
 3136 
 3137     if (name == NULL)
 3138         break;
 3139     *name = '\0';
 3140     name++;
 3141     color = atoi(buf);
 3142     if (color < 0 || color >= last)
 3143         break;      /* quit on any error */
 3144     buf = strchr(name, ';');
 3145     if (buf) {
 3146         *buf = '\0';
 3147         buf++;
 3148     }
 3149     if (!strcmp(name, "?")) {
 3150         if (ReportAnsiColorRequest(xw, opcode, color + first, final))
 3151         ++queried;
 3152     } else {
 3153         code = ChangeOneAnsiColor(xw, color + first, name);
 3154         if (code < 0) {
 3155         /* stop on any error */
 3156         break;
 3157         } else if (code > 0) {
 3158         repaint = True;
 3159         }
 3160         /* FIXME:  free old color somehow?  We aren't for the other color
 3161          * change style (dynamic colors).
 3162          */
 3163     }
 3164     }
 3165     if (queried)
 3166     unparse_end(xw);
 3167 
 3168     return (repaint);
 3169 }
 3170 
 3171 static Bool
 3172 ResetOneAnsiColor(XtermWidget xw, int color, int start)
 3173 {
 3174     Bool repaint = False;
 3175     int last = MAXCOLORS - start;
 3176 
 3177     if (color >= 0 && color < last) {
 3178     ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
 3179 
 3180     if (res->mode) {
 3181         /* a color has been allocated for this slot - test further... */
 3182         if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
 3183         repaint = True;
 3184         }
 3185     }
 3186     }
 3187     return repaint;
 3188 }
 3189 
 3190 int
 3191 ResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
 3192 {
 3193     int repaint = 0;
 3194     int color;
 3195 
 3196     TRACE(("ResetAnsiColorRequest(%s)\n", buf));
 3197     if (*buf != '\0') {
 3198     /* reset specific colors */
 3199     while (!IsEmpty(buf)) {
 3200         char *next;
 3201 
 3202         color = (int) (strtol) (buf, &next, 10);
 3203         if (!PartS2L(buf, next) || (color < 0))
 3204         break;      /* no number at all */
 3205         if (next != 0) {
 3206         if (strchr(";", *next) == 0)
 3207             break;  /* unexpected delimiter */
 3208         ++next;
 3209         }
 3210 
 3211         if (ResetOneAnsiColor(xw, color, start)) {
 3212         ++repaint;
 3213         }
 3214         buf = next;
 3215     }
 3216     } else {
 3217     TRACE(("...resetting all %d colors\n", MAXCOLORS));
 3218     for (color = 0; color < MAXCOLORS; ++color) {
 3219         if (ResetOneAnsiColor(xw, color, start)) {
 3220         ++repaint;
 3221         }
 3222     }
 3223     }
 3224     TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
 3225     return repaint;
 3226 }
 3227 #else
 3228 #define allocateClosestRGB(xw, cmap, def) 0
 3229 #define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
 3230 #endif /* OPT_ISO_COLORS */
 3231 
 3232 Boolean
 3233 allocateBestRGB(XtermWidget xw, XColor *def)
 3234 {
 3235     Colormap cmap = xw->core.colormap;
 3236 
 3237     return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
 3238 }
 3239 
 3240 static Boolean
 3241 xtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
 3242 {
 3243     Boolean result = False;
 3244     TScreen *screen = TScreenOf(xw);
 3245     Colormap cmap = xw->core.colormap;
 3246     size_t have = strlen(spec);
 3247 
 3248     if (have == 0 || have > MAX_U_STRING) {
 3249     if (resource.reportColors) {
 3250         printf("color  (ignored, length %lu)\n", have);
 3251     }
 3252     } else if (XParseColor(screen->display, cmap, spec, def)) {
 3253     XColor save_def = *def;
 3254     if (resource.reportColors) {
 3255         printf("color  %04x/%04x/%04x = \"%s\"\n",
 3256            def->red, def->green, def->blue,
 3257            spec);
 3258     }
 3259     if (allocateBestRGB(xw, def)) {
 3260         if (resource.reportColors) {
 3261         if (def->red != save_def.red ||
 3262             def->green != save_def.green ||
 3263             def->blue != save_def.blue) {
 3264             printf("color  %04x/%04x/%04x ~ \"%s\"\n",
 3265                def->red, def->green, def->blue,
 3266                spec);
 3267         }
 3268         }
 3269         TRACE(("xtermAllocColor -> %x/%x/%x\n",
 3270            def->red, def->green, def->blue));
 3271         result = True;
 3272     }
 3273     }
 3274     return result;
 3275 }
 3276 
 3277 /*
 3278  * This provides an approximation (the closest color from xterm's palette)
 3279  * rather than the "exact" color (whatever the display could provide, actually)
 3280  * because of the context in which it is used.
 3281  */
 3282 #define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
 3283 int
 3284 xtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
 3285 {
 3286     int result = -1;
 3287 #if OPT_COLOR_RES && OPT_ISO_COLORS
 3288     int n;
 3289     int best_index = -1;
 3290     unsigned long best_value = 0;
 3291     unsigned long this_value;
 3292     long diff_red, diff_green, diff_blue;
 3293 
 3294     TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
 3295 
 3296     for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
 3297     ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
 3298 
 3299     /* ensure that we have a value for each of the colors */
 3300     if (!res->mode) {
 3301         (void) AllocateAnsiColor(xw, res, res->resource);
 3302     }
 3303 
 3304     /* find the closest match */
 3305     if (res->mode == True) {
 3306         TRACE2(("...lookup %lx -> %x/%x/%x\n",
 3307             res->value, res->red, res->green, res->blue));
 3308         diff_red = ColorDiff(find_red, res->red);
 3309         diff_green = ColorDiff(find_green, res->green);
 3310         diff_blue = ColorDiff(find_blue, res->blue);
 3311         this_value = (unsigned long) ((diff_red * diff_red)
 3312                       + (diff_green * diff_green)
 3313                       + (diff_blue * diff_blue));
 3314         if (best_index < 0 || this_value < best_value) {
 3315         best_index = n;
 3316         best_value = this_value;
 3317         }
 3318     }
 3319     }
 3320     TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
 3321     result = best_index;
 3322 #else
 3323     (void) xw;
 3324     (void) find_red;
 3325     (void) find_green;
 3326     (void) find_blue;
 3327 #endif
 3328     return result;
 3329 }
 3330 
 3331 #if OPT_DIRECT_COLOR
 3332 int
 3333 getDirectColor(XtermWidget xw, int red, int green, int blue)
 3334 {
 3335 #define nRGB(name,shift) \
 3336     ((unsigned long)(name << xw->rgb_shifts[shift]) \
 3337                  & xw->visInfo->name ##_mask)
 3338     MyPixel result = (MyPixel) (nRGB(red, 0) | nRGB(green, 1) | nRGB(blue, 2));
 3339     return (int) result;
 3340 }
 3341 
 3342 static void
 3343 formatDirectColor(char *target, XtermWidget xw, unsigned value)
 3344 {
 3345 #define fRGB(name, shift) \
 3346     (value & xw->visInfo->name ## _mask) >> xw->rgb_shifts[shift]
 3347     sprintf(target, "%lu:%lu:%lu", fRGB(red, 0), fRGB(green, 1), fRGB(blue, 2));
 3348 }
 3349 #endif /* OPT_DIRECT_COLOR */
 3350 
 3351 #define fg2SGR(n) \
 3352         (n) >= 8 ? 9 : 3, \
 3353         (n) >= 8 ? (n) - 8 : (n)
 3354 #define bg2SGR(n) \
 3355         (n) >= 8 ? 10 : 4, \
 3356         (n) >= 8 ? (n) - 8 : (n)
 3357 
 3358 #define EndOf(s) (s) + strlen(s)
 3359 
 3360 char *
 3361 xtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
 3362 {
 3363     TScreen *screen = TScreenOf(xw);
 3364     char *msg = target;
 3365 
 3366     strcpy(target, "0");
 3367     if (attr & BOLD)
 3368     strcat(msg, ";1");
 3369     if (attr & UNDERLINE)
 3370     strcat(msg, ";4");
 3371     if (attr & BLINK)
 3372     strcat(msg, ";5");
 3373     if (attr & INVERSE)
 3374     strcat(msg, ";7");
 3375     if (attr & INVISIBLE)
 3376     strcat(msg, ";8");
 3377 #if OPT_WIDE_ATTRS
 3378     if (attr & ATR_FAINT)
 3379     strcat(msg, ";2");
 3380     if (attr & ATR_ITALIC)
 3381     strcat(msg, ";3");
 3382     if (attr & ATR_STRIKEOUT)
 3383     strcat(msg, ";9");
 3384     if (attr & ATR_DBL_UNDER)
 3385     strcat(msg, ";21");
 3386 #endif
 3387 #if OPT_256_COLORS || OPT_88_COLORS
 3388     if_OPT_ISO_COLORS(screen, {
 3389     if (attr & FG_COLOR) {
 3390         if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
 3391         strcat(msg, ";38:2::");
 3392         formatDirectColor(EndOf(msg), xw, (unsigned) fg);
 3393         }) if (fg >= 16) {
 3394         sprintf(EndOf(msg), ";38:5:%d", fg);
 3395         } else {
 3396         sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
 3397         }
 3398     }
 3399     if (attr & BG_COLOR) {
 3400         if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
 3401         strcat(msg, ";48:2::");
 3402         formatDirectColor(EndOf(msg), xw, (unsigned) bg);
 3403         }) if (bg >= 16) {
 3404         sprintf(EndOf(msg), ";48:5:%d", bg);
 3405         } else {
 3406         sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
 3407         }
 3408     }
 3409     });
 3410 #elif OPT_ISO_COLORS
 3411     if_OPT_ISO_COLORS(screen, {
 3412     if (attr & FG_COLOR) {
 3413         sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
 3414     }
 3415     if (attr & BG_COLOR) {
 3416         sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
 3417     }
 3418     });
 3419 #else
 3420     (void) screen;
 3421     (void) fg;
 3422     (void) bg;
 3423 #endif
 3424     return target;
 3425 }
 3426 
 3427 #if OPT_PASTE64
 3428 static void
 3429 ManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
 3430 {
 3431 #define PDATA(a,b) { a, #b }
 3432     static struct {
 3433     char given;
 3434     String result;
 3435     } table[] = {
 3436     PDATA('s', SELECT),
 3437         PDATA('p', PRIMARY),
 3438         PDATA('q', SECONDARY),
 3439         PDATA('c', CLIPBOARD),
 3440         PDATA('0', CUT_BUFFER0),
 3441         PDATA('1', CUT_BUFFER1),
 3442         PDATA('2', CUT_BUFFER2),
 3443         PDATA('3', CUT_BUFFER3),
 3444         PDATA('4', CUT_BUFFER4),
 3445         PDATA('5', CUT_BUFFER5),
 3446         PDATA('6', CUT_BUFFER6),
 3447         PDATA('7', CUT_BUFFER7),
 3448     };
 3449 
 3450     const char *base = buf;
 3451     Cardinal j, n = 0;
 3452 
 3453     TRACE(("Manipulate selection data\n"));
 3454 
 3455     while (*buf != ';' && *buf != '\0') {
 3456     ++buf;
 3457     }
 3458 
 3459     if (*buf == ';') {
 3460     char *used;
 3461 
 3462     *buf++ = '\0';
 3463 
 3464     if (*base == '\0')
 3465         base = "s0";
 3466 
 3467     if ((used = x_strdup(base)) != 0) {
 3468         String *select_args;
 3469 
 3470         if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
 3471         while (*base != '\0') {
 3472             for (j = 0; j < XtNumber(table); ++j) {
 3473             if (*base == table[j].given) {
 3474                 used[n] = *base;
 3475                 select_args[n++] = table[j].result;
 3476                 TRACE(("atom[%d] %s\n", n, table[j].result));
 3477                 break;
 3478             }
 3479             }
 3480             ++base;
 3481         }
 3482         used[n] = 0;
 3483 
 3484         if (!strcmp(buf, "?")) {
 3485             if (AllowWindowOps(xw, ewGetSelection)) {
 3486             TRACE(("Getting selection\n"));
 3487             unparseputc1(xw, ANSI_OSC);
 3488             unparseputs(xw, "52");
 3489             unparseputc(xw, ';');
 3490 
 3491             unparseputs(xw, used);
 3492             unparseputc(xw, ';');
 3493 
 3494             /* Tell xtermGetSelection data is base64 encoded */
 3495             screen->base64_paste = n;
 3496             screen->base64_final = final;
 3497 
 3498             screen->selection_time =
 3499                 XtLastTimestampProcessed(TScreenOf(xw)->display);
 3500 
 3501             /* terminator will be written in this call */
 3502             xtermGetSelection((Widget) xw,
 3503                       screen->selection_time,
 3504                       select_args, n,
 3505                       NULL);
 3506             /*
 3507              * select_args is used via SelectionReceived, cannot
 3508              * free it here.
 3509              */
 3510             } else {
 3511             free(select_args);
 3512             }
 3513         } else {
 3514             if (AllowWindowOps(xw, ewSetSelection)) {
 3515             char *old = buf;
 3516 
 3517             TRACE(("Setting selection(%s) with %s\n", used, buf));
 3518             screen->selection_time =
 3519                 XtLastTimestampProcessed(TScreenOf(xw)->display);
 3520 
 3521             for (j = 0; j < n; ++j) {
 3522                 buf = old;
 3523                 ClearSelectionBuffer(screen, select_args[j]);
 3524                 while (*buf != '\0') {
 3525                 AppendToSelectionBuffer(screen,
 3526                             CharOf(*buf++),
 3527                             select_args[j]);
 3528                 }
 3529             }
 3530             CompleteSelection(xw, select_args, n);
 3531             }
 3532             free(select_args);
 3533         }
 3534         }
 3535         free(used);
 3536     }
 3537     }
 3538 }
 3539 #endif /* OPT_PASTE64 */
 3540 
 3541 /***====================================================================***/
 3542 
 3543 #define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
 3544              || (xw->screen.utf8_title) \
 3545              || (xw->screen.c1_printable))
 3546 
 3547 static Bool
 3548 xtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
 3549 {
 3550     TScreen *screen = TScreenOf(xw);
 3551     Bool result = False;
 3552     Char *cp = *bufp;
 3553     Char *next = cp;
 3554 
 3555     (void) screen;
 3556     (void) last;
 3557 
 3558 #if OPT_WIDE_CHARS
 3559     if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
 3560     PtyData data;
 3561 
 3562     if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
 3563         if (data.utf_data != UCS_REPL
 3564         && (data.utf_data >= 128 ||
 3565             ansi_table[data.utf_data] == CASE_PRINT)) {
 3566         next += (data.utf_size - 1);
 3567         result = True;
 3568         } else {
 3569         result = False;
 3570         }
 3571     } else {
 3572         result = False;
 3573     }
 3574     } else
 3575 #endif
 3576 #if OPT_C1_PRINT
 3577     if (screen->c1_printable
 3578         && (*cp >= 128 && *cp < 160)) {
 3579     result = True;
 3580     } else
 3581 #endif
 3582     if (ansi_table[*cp] == CASE_PRINT) {
 3583     result = True;
 3584     }
 3585     *bufp = next;
 3586     return result;
 3587 }
 3588 
 3589 /***====================================================================***/
 3590 
 3591 /*
 3592  * Enum corresponding to the actual OSC codes rather than the internal
 3593  * array indices.  Compare with TermColors.
 3594  */
 3595 typedef enum {
 3596     OSC_TEXT_FG = 10
 3597     ,OSC_TEXT_BG
 3598     ,OSC_TEXT_CURSOR
 3599     ,OSC_MOUSE_FG
 3600     ,OSC_MOUSE_BG
 3601 #if OPT_TEK4014
 3602     ,OSC_TEK_FG = 15
 3603     ,OSC_TEK_BG
 3604 #endif
 3605 #if OPT_HIGHLIGHT_COLOR
 3606     ,OSC_HIGHLIGHT_BG = 17
 3607 #endif
 3608 #if OPT_TEK4014
 3609     ,OSC_TEK_CURSOR = 18
 3610 #endif
 3611 #if OPT_HIGHLIGHT_COLOR
 3612     ,OSC_HIGHLIGHT_FG = 19
 3613 #endif
 3614     ,OSC_NCOLORS
 3615 } OscTextColors;
 3616 
 3617 /*
 3618  * Map codes to OSC controls that can reset colors.
 3619  */
 3620 #define OSC_RESET 100
 3621 #define OSC_Reset(code) (code) + OSC_RESET
 3622 
 3623 static Bool
 3624 GetOldColors(XtermWidget xw)
 3625 {
 3626     if (xw->work.oldColors == NULL) {
 3627     int i;
 3628 
 3629     xw->work.oldColors = TypeXtMalloc(ScrnColors);
 3630     if (xw->work.oldColors == NULL) {
 3631         xtermWarning("allocation failure in GetOldColors\n");
 3632         return (False);
 3633     }
 3634     xw->work.oldColors->which = 0;
 3635     for (i = 0; i < NCOLORS; i++) {
 3636         xw->work.oldColors->colors[i] = 0;
 3637         xw->work.oldColors->names[i] = NULL;
 3638     }
 3639     GetColors(xw, xw->work.oldColors);
 3640     }
 3641     return (True);
 3642 }
 3643 
 3644 static int
 3645 oppositeColor(XtermWidget xw, int n)
 3646 {
 3647     Boolean reversed = (xw->misc.re_verse);
 3648 
 3649     switch (n) {
 3650     case TEXT_FG:
 3651     n = reversed ? TEXT_FG : TEXT_BG;
 3652     break;
 3653     case TEXT_BG:
 3654     n = reversed ? TEXT_BG : TEXT_FG;
 3655     break;
 3656     case MOUSE_FG:
 3657     n = MOUSE_BG;
 3658     break;
 3659     case MOUSE_BG:
 3660     n = MOUSE_FG;
 3661     break;
 3662 #if OPT_TEK4014
 3663     case TEK_FG:
 3664     n = reversed ? TEK_FG : TEK_BG;
 3665     break;
 3666     case TEK_BG:
 3667     n = reversed ? TEK_BG : TEK_FG;
 3668     break;
 3669 #endif
 3670 #if OPT_HIGHLIGHT_COLOR
 3671     case HIGHLIGHT_FG:
 3672     n = HIGHLIGHT_BG;
 3673     break;
 3674     case HIGHLIGHT_BG:
 3675     n = HIGHLIGHT_FG;
 3676     break;
 3677 #endif
 3678     default:
 3679     break;
 3680     }
 3681     return n;
 3682 }
 3683 
 3684 static Bool
 3685 ReportColorRequest(XtermWidget xw, int ndx, int final)
 3686 {
 3687     Bool result = False;
 3688 
 3689     if (AllowColorOps(xw, ecGetColor)) {
 3690     XColor color;
 3691     Colormap cmap = xw->core.colormap;
 3692     char buffer[80];
 3693 
 3694     /*
 3695      * ChangeColorsRequest() has "always" chosen the opposite color when
 3696      * reverse-video is set.  Report this as the original color index, but
 3697      * reporting the opposite color which would be used.
 3698      */
 3699     int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
 3700 
 3701     GetOldColors(xw);
 3702     color.pixel = xw->work.oldColors->colors[ndx];
 3703     XQueryColor(TScreenOf(xw)->display, cmap, &color);
 3704     sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
 3705         color.red,
 3706         color.green,
 3707         color.blue);
 3708     TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
 3709            ndx, xw->work.oldColors->colors[ndx], buffer));
 3710     unparseputc1(xw, ANSI_OSC);
 3711     unparseputs(xw, buffer);
 3712     unparseputc1(xw, final);
 3713     result = True;
 3714     }
 3715     return result;
 3716 }
 3717 
 3718 static Bool
 3719 UpdateOldColors(XtermWidget xw, ScrnColors * pNew)
 3720 {
 3721     int i;
 3722 
 3723     /* if we were going to free old colors, this would be the place to
 3724      * do it.   I've decided not to (for now), because it seems likely
 3725      * that we'd have a small set of colors we use over and over, and that
 3726      * we could save some overhead this way.   The only case in which this
 3727      * (clearly) fails is if someone is trying a boatload of colors, in
 3728      * which case they can restart xterm
 3729      */
 3730     for (i = 0; i < NCOLORS; i++) {
 3731     if (COLOR_DEFINED(pNew, i)) {
 3732         if (xw->work.oldColors->names[i] != NULL) {
 3733         XtFree(xw->work.oldColors->names[i]);
 3734         xw->work.oldColors->names[i] = NULL;
 3735         }
 3736         if (pNew->names[i]) {
 3737         xw->work.oldColors->names[i] = pNew->names[i];
 3738         }
 3739         xw->work.oldColors->colors[i] = pNew->colors[i];
 3740     }
 3741     }
 3742     return (True);
 3743 }
 3744 
 3745 /*
 3746  * OSC codes are constant, but the indices for the color arrays depend on how
 3747  * xterm is compiled.
 3748  */
 3749 static int
 3750 OscToColorIndex(OscTextColors mode)
 3751 {
 3752     int result = 0;
 3753 
 3754 #define CASE(name) case OSC_##name: result = name; break
 3755     switch (mode) {
 3756     CASE(TEXT_FG);
 3757     CASE(TEXT_BG);
 3758     CASE(TEXT_CURSOR);
 3759     CASE(MOUSE_FG);
 3760     CASE(MOUSE_BG);
 3761 #if OPT_TEK4014
 3762     CASE(TEK_FG);
 3763     CASE(TEK_BG);
 3764 #endif
 3765 #if OPT_HIGHLIGHT_COLOR
 3766     CASE(HIGHLIGHT_BG);
 3767     CASE(HIGHLIGHT_FG);
 3768 #endif
 3769 #if OPT_TEK4014
 3770     CASE(TEK_CURSOR);
 3771 #endif
 3772     case OSC_NCOLORS:
 3773     break;
 3774     }
 3775     return result;
 3776 }
 3777 
 3778 static Bool
 3779 ChangeColorsRequest(XtermWidget xw,
 3780             int start,
 3781             char *names,
 3782             int final)
 3783 {
 3784     Bool result = False;
 3785     ScrnColors newColors;
 3786 
 3787     TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
 3788 
 3789     if (GetOldColors(xw)) {
 3790     int i;
 3791     int queried = 0;
 3792 
 3793     newColors.which = 0;
 3794     for (i = 0; i < NCOLORS; i++) {
 3795         newColors.names[i] = NULL;
 3796     }
 3797     for (i = start; i < OSC_NCOLORS; i++) {
 3798         int ndx = OscToColorIndex((OscTextColors) i);
 3799         if (xw->misc.re_verse)
 3800         ndx = oppositeColor(xw, ndx);
 3801 
 3802         if (IsEmpty(names)) {
 3803         newColors.names[ndx] = NULL;
 3804         } else {
 3805         char *thisName = ((names[0] == ';') ? NULL : names);
 3806 
 3807         names = strchr(names, ';');
 3808         if (names != NULL) {
 3809             *names++ = '\0';
 3810         }
 3811         if (thisName != 0) {
 3812             if (!strcmp(thisName, "?")) {
 3813             if (ReportColorRequest(xw, ndx, final))
 3814                 ++queried;
 3815             } else if (!xw->work.oldColors->names[ndx]
 3816                    || strcmp(thisName, xw->work.oldColors->names[ndx])) {
 3817             AllocateTermColor(xw, &newColors, ndx, thisName, False);
 3818             }
 3819         }
 3820         }
 3821     }
 3822 
 3823     if (newColors.which != 0) {
 3824         ChangeColors(xw, &newColors);
 3825         UpdateOldColors(xw, &newColors);
 3826     } else if (queried) {
 3827         unparse_end(xw);
 3828     }
 3829     result = True;
 3830     }
 3831     return result;
 3832 }
 3833 
 3834 static Bool
 3835 ResetColorsRequest(XtermWidget xw,
 3836            int code)
 3837 {
 3838     Bool result = False;
 3839 
 3840     (void) xw;
 3841     (void) code;
 3842 
 3843     TRACE(("ResetColorsRequest code=%d\n", code));
 3844 
 3845 #if OPT_COLOR_RES
 3846     if (GetOldColors(xw)) {
 3847     ScrnColors newColors;
 3848     const char *thisName;
 3849     int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
 3850 
 3851     if (xw->misc.re_verse)
 3852         ndx = oppositeColor(xw, ndx);
 3853 
 3854     thisName = xw->screen.Tcolors[ndx].resource;
 3855 
 3856     newColors.which = 0;
 3857     newColors.names[ndx] = NULL;
 3858 
 3859     if (thisName != 0
 3860         && xw->work.oldColors->names[ndx] != 0
 3861         && strcmp(thisName, xw->work.oldColors->names[ndx])) {
 3862         AllocateTermColor(xw, &newColors, ndx, thisName, False);
 3863 
 3864         if (newColors.which != 0) {
 3865         ChangeColors(xw, &newColors);
 3866         UpdateOldColors(xw, &newColors);
 3867         }
 3868     }
 3869     result = True;
 3870     }
 3871 #endif
 3872     return result;
 3873 }
 3874 
 3875 #if OPT_SHIFT_FONTS
 3876 /*
 3877  * Initially, 'source' points to '#' or '?'.
 3878  *
 3879  * Look for an optional sign and optional number.  If those are found, lookup
 3880  * the corresponding menu font entry.
 3881  */
 3882 static int
 3883 ParseShiftedFont(XtermWidget xw, String source, String *target)
 3884 {
 3885     TScreen *screen = TScreenOf(xw);
 3886     int num = screen->menu_font_number;
 3887     int rel = 0;
 3888 
 3889     if (*++source == '+') {
 3890     rel = 1;
 3891     source++;
 3892     } else if (*source == '-') {
 3893     rel = -1;
 3894     source++;
 3895     }
 3896 
 3897     if (isdigit(CharOf(*source))) {
 3898     int val = atoi(source);
 3899     if (rel > 0)
 3900         rel = val;
 3901     else if (rel < 0)
 3902         rel = -val;
 3903     else
 3904         num = val;
 3905     }
 3906 
 3907     if (rel != 0) {
 3908     num = lookupRelativeFontSize(xw,
 3909                      screen->menu_font_number, rel);
 3910 
 3911     }
 3912     TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
 3913     *target = source;
 3914     return num;
 3915 }
 3916 
 3917 static void
 3918 QueryFontRequest(XtermWidget xw, String buf, int final)
 3919 {
 3920     if (AllowFontOps(xw, efGetFont)) {
 3921     TScreen *screen = TScreenOf(xw);
 3922     Bool success = True;
 3923     int num;
 3924     String base = buf + 1;
 3925     const char *name = 0;
 3926 
 3927     num = ParseShiftedFont(xw, buf, &buf);
 3928     if (num < 0
 3929         || num > fontMenu_lastBuiltin) {
 3930         Bell(xw, XkbBI_MinorError, 0);
 3931         success = False;
 3932     } else {
 3933 #if OPT_RENDERFONT
 3934         if (UsingRenderFont(xw)) {
 3935         name = getFaceName(xw, False);
 3936         } else
 3937 #endif
 3938         if ((name = screen->MenuFontName(num)) == 0) {
 3939         success = False;
 3940         }
 3941     }
 3942 
 3943     unparseputc1(xw, ANSI_OSC);
 3944     unparseputs(xw, "50");
 3945 
 3946     if (success) {
 3947         unparseputc(xw, ';');
 3948         if (buf >= base) {
 3949         /* identify the font-entry, unless it is the current one */
 3950         if (*buf != '\0') {
 3951             char temp[10];
 3952 
 3953             unparseputc(xw, '#');
 3954             sprintf(temp, "%d", num);
 3955             unparseputs(xw, temp);
 3956             if (*name != '\0')
 3957             unparseputc(xw, ' ');
 3958         }
 3959         }
 3960         unparseputs(xw, name);
 3961     }
 3962 
 3963     unparseputc1(xw, final);
 3964     unparse_end(xw);
 3965     }
 3966 }
 3967 
 3968 static void
 3969 ChangeFontRequest(XtermWidget xw, String buf)
 3970 {
 3971     if (AllowFontOps(xw, efSetFont)) {
 3972     TScreen *screen = TScreenOf(xw);
 3973     Bool success = True;
 3974     int num;
 3975     VTFontNames fonts;
 3976     char *name;
 3977 
 3978     /*
 3979      * If the font specification is a "#", followed by an optional sign and
 3980      * optional number, lookup the corresponding menu font entry.
 3981      *
 3982      * Further, if the "#", etc., is followed by a font name, use that
 3983      * to load the font entry.
 3984      */
 3985     if (*buf == '#') {
 3986         num = ParseShiftedFont(xw, buf, &buf);
 3987 
 3988         if (num < 0
 3989         || num > fontMenu_lastBuiltin) {
 3990         Bell(xw, XkbBI_MinorError, 0);
 3991         success = False;
 3992         } else {
 3993         /*
 3994          * Skip past the optional number, and any whitespace to look
 3995          * for a font specification within the control.
 3996          */
 3997         while (isdigit(CharOf(*buf))) {
 3998             ++buf;
 3999         }
 4000         while (isspace(CharOf(*buf))) {
 4001             ++buf;
 4002         }
 4003 #if OPT_RENDERFONT
 4004         if (UsingRenderFont(xw)) {
 4005             /* EMPTY */
 4006             /* there is only one font entry to load */
 4007             ;
 4008         } else
 4009 #endif
 4010         {
 4011             /*
 4012              * Normally there is no font specified in the control.
 4013              * But if there is, simply overwrite the font entry.
 4014              */
 4015             if (*buf == '\0') {
 4016             if ((buf = screen->MenuFontName(num)) == 0) {
 4017                 success = False;
 4018             }
 4019             }
 4020         }
 4021         }
 4022     } else {
 4023         num = screen->menu_font_number;
 4024     }
 4025     name = x_strtrim(buf);
 4026     if (screen->EscapeFontName()) {
 4027         FREE_STRING(screen->EscapeFontName());
 4028         screen->EscapeFontName() = 0;
 4029     }
 4030     if (success && !IsEmpty(name)) {
 4031 #if OPT_RENDERFONT
 4032         if (UsingRenderFont(xw)) {
 4033         setFaceName(xw, name);
 4034         xtermUpdateFontInfo(xw, True);
 4035         } else
 4036 #endif
 4037         {
 4038         memset(&fonts, 0, sizeof(fonts));
 4039         fonts.f_n = name;
 4040         SetVTFont(xw, num, True, &fonts);
 4041         if (num == screen->menu_font_number &&
 4042             num != fontMenu_fontescape) {
 4043             screen->EscapeFontName() = x_strdup(name);
 4044         }
 4045         }
 4046     } else {
 4047         Bell(xw, XkbBI_MinorError, 0);
 4048     }
 4049     update_font_escape();
 4050     free(name);
 4051     }
 4052 }
 4053 #endif /* OPT_SHIFT_FONTS */
 4054 
 4055 /***====================================================================***/
 4056 
 4057 void
 4058 do_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
 4059 {
 4060     TScreen *screen = TScreenOf(xw);
 4061     int mode;
 4062     Char *cp;
 4063     int state = 0;
 4064     char *buf = 0;
 4065     char temp[2];
 4066 #if OPT_ISO_COLORS
 4067     int ansi_colors = 0;
 4068 #endif
 4069     Bool need_data = True;
 4070     Bool optional_data = False;
 4071 
 4072     TRACE(("do_osc %s\n", oscbuf));
 4073 
 4074     (void) screen;
 4075 
 4076     /*
 4077      * Lines should be of the form <OSC> number ; string <ST>, however
 4078      * older xterms can accept <BEL> as a final character.  We will respond
 4079      * with the same final character as the application sends to make this
 4080      * work better with shell scripts, which may have trouble reading an
 4081      * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
 4082      */
 4083     mode = 0;
 4084     for (cp = oscbuf; *cp != '\0'; cp++) {
 4085     switch (state) {
 4086     case 0:
 4087         if (isdigit(*cp)) {
 4088         mode = 10 * mode + (*cp - '0');
 4089         if (mode > 65535) {
 4090             TRACE(("do_osc found unknown mode %d\n", mode));
 4091             return;
 4092         }
 4093         break;
 4094         } else {
 4095         switch (*cp) {
 4096         case 'I':
 4097             xtermLoadIcon(xw, (char *) ++cp);
 4098             return;
 4099         case 'l':
 4100             ChangeTitle(xw, (char *) ++cp);
 4101             return;
 4102         case 'L':
 4103             ChangeIconName(xw, (char *) ++cp);
 4104             return;
 4105         }
 4106         }
 4107         /* FALLTHRU */
 4108     case 1:
 4109         if (*cp != ';') {
 4110         TRACE(("do_osc did not find semicolon offset %d\n",
 4111                (int) (cp - oscbuf)));
 4112         return;
 4113         }
 4114         state = 2;
 4115         break;
 4116     case 2:
 4117         buf = (char *) cp;
 4118         state = 3;
 4119         /* FALLTHRU */
 4120     default:
 4121         if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
 4122         switch (mode) {
 4123         case 0:
 4124         case 1:
 4125         case 2:
 4126             break;
 4127         default:
 4128             TRACE(("do_osc found nonprinting char %02X offset %d\n",
 4129                CharOf(*cp),
 4130                (int) (cp - oscbuf)));
 4131             return;
 4132         }
 4133         }
 4134     }
 4135     }
 4136 
 4137     /*
 4138      * Check if the palette changed and there are no more immediate changes
 4139      * that could be deferred to the next repaint.
 4140      */
 4141     if (xw->work.palette_changed) {
 4142     switch (mode) {
 4143     case 03:        /* change X property */
 4144     case 30:        /* Konsole (unused) */
 4145     case 31:        /* Konsole (unused) */
 4146     case 50:        /* font operations */
 4147     case 51:        /* Emacs (unused) */
 4148 #if OPT_PASTE64
 4149     case 52:        /* selection data */
 4150 #endif
 4151         TRACE(("forced repaint after palette changed\n"));
 4152         xw->work.palette_changed = False;
 4153         xtermRepaint(xw);
 4154         break;
 4155     default:
 4156         xtermNeedSwap(xw, 1);
 4157         break;
 4158     }
 4159     }
 4160 
 4161     /*
 4162      * Most OSC controls other than resets require data.  Handle the others as
 4163      * a special case.
 4164      */
 4165     switch (mode) {
 4166     case 50:
 4167 #if OPT_ISO_COLORS
 4168     case OSC_Reset(4):
 4169     case OSC_Reset(5):
 4170     need_data = False;
 4171     optional_data = True;
 4172     break;
 4173     case OSC_Reset(OSC_TEXT_FG):
 4174     case OSC_Reset(OSC_TEXT_BG):
 4175     case OSC_Reset(OSC_TEXT_CURSOR):
 4176     case OSC_Reset(OSC_MOUSE_FG):
 4177     case OSC_Reset(OSC_MOUSE_BG):
 4178 #if OPT_HIGHLIGHT_COLOR
 4179     case OSC_Reset(OSC_HIGHLIGHT_BG):
 4180     case OSC_Reset(OSC_HIGHLIGHT_FG):
 4181 #endif
 4182 #if OPT_TEK4014
 4183     case OSC_Reset(OSC_TEK_FG):
 4184     case OSC_Reset(OSC_TEK_BG):
 4185     case OSC_Reset(OSC_TEK_CURSOR):
 4186 #endif
 4187     need_data = False;
 4188     break;
 4189 #endif
 4190     default:
 4191     break;
 4192     }
 4193 
 4194     /*
 4195      * Check if we have data when we want, and not when we do not want it.
 4196      * Either way, that is a malformed control sequence, and will be ignored.
 4197      */
 4198     if (IsEmpty(buf)) {
 4199     if (need_data) {
 4200         TRACE(("do_osc found no data\n"));
 4201         return;
 4202     }
 4203     temp[0] = '\0';
 4204     buf = temp;
 4205     } else if (!need_data && !optional_data) {
 4206     TRACE(("do_osc found unwanted data\n"));
 4207     return;
 4208     }
 4209 
 4210     switch (mode) {
 4211     case 0:         /* new icon name and title */
 4212     ChangeIconName(xw, buf);
 4213     ChangeTitle(xw, buf);
 4214     break;
 4215 
 4216     case 1:         /* new icon name only */
 4217     ChangeIconName(xw, buf);
 4218     break;
 4219 
 4220     case 2:         /* new title only */
 4221     ChangeTitle(xw, buf);
 4222     break;
 4223 
 4224     case 3:         /* change X property */
 4225     if (AllowWindowOps(xw, ewSetXprop))
 4226         ChangeXprop(buf);
 4227     break;
 4228 #if OPT_ISO_COLORS
 4229     case 5:
 4230     ansi_colors = NUM_ANSI_COLORS;
 4231     /* FALLTHRU */
 4232     case 4:
 4233     if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
 4234         xw->work.palette_changed = True;
 4235     break;
 4236     case 6:
 4237     /* FALLTHRU */
 4238     case OSC_Reset(6):
 4239     TRACE(("parse colorXXMode:%s\n", buf));
 4240     while (*buf != '\0') {
 4241         long which = 0;
 4242         long value = 0;
 4243         char *next;
 4244         if (*buf == ';') {
 4245         ++buf;
 4246         } else {
 4247         which = strtol(buf, &next, 10);
 4248         if (!PartS2L(buf, next) || (which < 0))
 4249             break;
 4250         buf = next;
 4251         if (*buf == ';')
 4252             ++buf;
 4253         }
 4254         if (*buf == ';') {
 4255         ++buf;
 4256         } else {
 4257         value = strtol(buf, &next, 10);
 4258         if (!PartS2L(buf, next) || (value < 0))
 4259             break;
 4260         buf = next;
 4261         if (*buf == ';')
 4262             ++buf;
 4263         }
 4264         TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
 4265         switch (which) {
 4266         case 0:
 4267         screen->colorBDMode = (value != 0);
 4268         break;
 4269         case 1:
 4270         screen->colorULMode = (value != 0);
 4271         break;
 4272         case 2:
 4273         screen->colorBLMode = (value != 0);
 4274         break;
 4275         case 3:
 4276         screen->colorRVMode = (value != 0);
 4277         break;
 4278 #if OPT_WIDE_ATTRS
 4279         case 4:
 4280         screen->colorITMode = (value != 0);
 4281         break;
 4282 #endif
 4283         default:
 4284         TRACE(("...unknown colorXXMode\n"));
 4285         break;
 4286         }
 4287     }
 4288     break;
 4289     case OSC_Reset(5):
 4290     ansi_colors = NUM_ANSI_COLORS;
 4291     /* FALLTHRU */
 4292     case OSC_Reset(4):
 4293     if (ResetAnsiColorRequest(xw, buf, ansi_colors))
 4294         xw->work.palette_changed = True;
 4295     break;
 4296 #endif
 4297     case OSC_TEXT_FG:
 4298     case OSC_TEXT_BG:
 4299     case OSC_TEXT_CURSOR:
 4300     case OSC_MOUSE_FG:
 4301     case OSC_MOUSE_BG:
 4302 #if OPT_HIGHLIGHT_COLOR
 4303     case OSC_HIGHLIGHT_BG:
 4304     case OSC_HIGHLIGHT_FG:
 4305 #endif
 4306 #if OPT_TEK4014
 4307     case OSC_TEK_FG:
 4308     case OSC_TEK_BG:
 4309     case OSC_TEK_CURSOR:
 4310 #endif
 4311     if (xw->misc.dynamicColors) {
 4312         ChangeColorsRequest(xw, mode, buf, final);
 4313     }
 4314     break;
 4315     case OSC_Reset(OSC_TEXT_FG):
 4316     case OSC_Reset(OSC_TEXT_BG):
 4317     case OSC_Reset(OSC_TEXT_CURSOR):
 4318     case OSC_Reset(OSC_MOUSE_FG):
 4319     case OSC_Reset(OSC_MOUSE_BG):
 4320 #if OPT_HIGHLIGHT_COLOR
 4321     case OSC_Reset(OSC_HIGHLIGHT_BG):
 4322     case OSC_Reset(OSC_HIGHLIGHT_FG):
 4323 #endif
 4324 #if OPT_TEK4014
 4325     case OSC_Reset(OSC_TEK_FG):
 4326     case OSC_Reset(OSC_TEK_BG):
 4327     case OSC_Reset(OSC_TEK_CURSOR):
 4328 #endif
 4329     if (xw->misc.dynamicColors) {
 4330         ResetColorsRequest(xw, mode);
 4331     }
 4332     break;
 4333 
 4334     case 22:
 4335     xtermSetupPointer(xw, buf);
 4336     break;
 4337 
 4338     case 30:
 4339     case 31:
 4340     /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
 4341     break;
 4342 
 4343 #ifdef ALLOWLOGGING
 4344     case 46:            /* new log file */
 4345 #ifdef ALLOWLOGFILECHANGES
 4346     /*
 4347      * Warning, enabling this feature allows people to overwrite
 4348      * arbitrary files accessible to the person running xterm.
 4349      */
 4350     if (strcmp(buf, "?")) {
 4351         char *bp;
 4352         if ((bp = x_strdup(buf)) != NULL) {
 4353         free(screen->logfile);
 4354         screen->logfile = bp;
 4355         break;
 4356         }
 4357     }
 4358 #endif
 4359     Bell(xw, XkbBI_Info, 0);
 4360     Bell(xw, XkbBI_Info, 0);
 4361     break;
 4362 #endif /* ALLOWLOGGING */
 4363 
 4364     case 50:
 4365 #if OPT_SHIFT_FONTS
 4366     if (*buf == '?') {
 4367         QueryFontRequest(xw, buf, final);
 4368     } else if (xw->misc.shift_fonts) {
 4369         ChangeFontRequest(xw, buf);
 4370     }
 4371 #endif /* OPT_SHIFT_FONTS */
 4372     break;
 4373     case 51:
 4374     /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
 4375     break;
 4376 
 4377 #if OPT_PASTE64
 4378     case 52:
 4379     ManipulateSelectionData(xw, screen, buf, final);
 4380     break;
 4381 #endif
 4382     /*
 4383      * One could write code to send back the display and host names,
 4384      * but that could potentially open a fairly nasty security hole.
 4385      */
 4386     default:
 4387     TRACE(("do_osc - unrecognized code\n"));
 4388     break;
 4389     }
 4390     unparse_end(xw);
 4391 }
 4392 
 4393 /*
 4394  * Parse one nibble of a hex byte from the OSC string.  We have removed the
 4395  * string-terminator (replacing it with a null), so the only other delimiter
 4396  * that is expected is semicolon.  Ignore other characters (Ray Neuman says
 4397  * "real" terminals accept commas in the string definitions).
 4398  */
 4399 static int
 4400 udk_value(const char **cp)
 4401 {
 4402     int result = -1;
 4403 
 4404     for (;;) {
 4405     int c;
 4406 
 4407     if ((c = **cp) != '\0')
 4408         *cp = *cp + 1;
 4409     if (c == ';' || c == '\0')
 4410         break;
 4411     if ((result = x_hex2int(c)) >= 0)
 4412         break;
 4413     }
 4414 
 4415     return result;
 4416 }
 4417 
 4418 void
 4419 reset_decudk(XtermWidget xw)
 4420 {
 4421     int n;
 4422     for (n = 0; n < MAX_UDK; n++) {
 4423     FreeAndNull(xw->work.user_keys[n].str);
 4424     xw->work.user_keys[n].len = 0;
 4425     }
 4426 }
 4427 
 4428 /*
 4429  * Parse the data for DECUDK (user-defined keys).
 4430  */
 4431 static void
 4432 parse_decudk(XtermWidget xw, const char *cp)
 4433 {
 4434     while (*cp) {
 4435     const char *base = cp;
 4436     char *str = malloc(strlen(cp) + 3);
 4437     unsigned key = 0;
 4438     int len = 0;
 4439 
 4440     if (str == NULL)
 4441         break;
 4442 
 4443     while (isdigit(CharOf(*cp)))
 4444         key = (key * 10) + (unsigned) (*cp++ - '0');
 4445 
 4446     if (*cp == '/') {
 4447         int lo, hi;
 4448 
 4449         cp++;
 4450         while ((hi = udk_value(&cp)) >= 0
 4451            && (lo = udk_value(&cp)) >= 0) {
 4452         str[len++] = (char) ((hi << 4) | lo);
 4453         }
 4454     }
 4455     if (len > 0 && key < MAX_UDK) {
 4456         str[len] = '\0';
 4457         free(xw->work.user_keys[key].str);
 4458         xw->work.user_keys[key].str = str;
 4459         xw->work.user_keys[key].len = len;
 4460         TRACE(("parse_decudk %d:%.*s\n", key, len, str));
 4461     } else {
 4462         free(str);
 4463     }
 4464     if (*cp == ';')
 4465         cp++;
 4466     if (cp == base)     /* badly-formed sequence - bail out */
 4467         break;
 4468     }
 4469 }
 4470 
 4471 /*
 4472  * Parse numeric parameters.  Normally we use a state machine to simplify
 4473  * interspersing with control characters, but have the string already.
 4474  */
 4475 static void
 4476 parse_ansi_params(ANSI *params, const char **string)
 4477 {
 4478     const char *cp = *string;
 4479     ParmType nparam = 0;
 4480     int last_empty = 1;
 4481 
 4482     memset(params, 0, sizeof(*params));
 4483     while (*cp != '\0') {
 4484     Char ch = CharOf(*cp++);
 4485 
 4486     if (isdigit(ch)) {
 4487         last_empty = 0;
 4488         if (nparam < NPARAM) {
 4489         params->a_param[nparam] =
 4490             (ParmType) ((params->a_param[nparam] * 10)
 4491                 + (ch - '0'));
 4492         }
 4493     } else if (ch == ';') {
 4494         last_empty = 1;
 4495         nparam++;
 4496     } else if (ch < 32) {
 4497         /* EMPTY */ ;
 4498     } else {
 4499         /* should be 0x30 to 0x7e */
 4500         params->a_final = ch;
 4501         break;
 4502     }
 4503     }
 4504 
 4505     *string = cp;
 4506     if (!last_empty)
 4507     nparam++;
 4508     if (nparam > NPARAM)
 4509     params->a_nparam = NPARAM;
 4510     else
 4511     params->a_nparam = nparam;
 4512 }
 4513 
 4514 #if OPT_TRACE
 4515 #define SOFT_WIDE 10
 4516 #define SOFT_HIGH 20
 4517 
 4518 static void
 4519 parse_decdld(ANSI *params, const char *string)
 4520 {
 4521     char DscsName[8];
 4522     int len;
 4523     int Pfn = params->a_param[0];
 4524     int Pcn = params->a_param[1];
 4525     int Pe = params->a_param[2];
 4526     int Pcmw = params->a_param[3];
 4527     int Pw = params->a_param[4];
 4528     int Pt = params->a_param[5];
 4529     int Pcmh = params->a_param[6];
 4530     int Pcss = params->a_param[7];
 4531 
 4532     int start_char = Pcn + 0x20;
 4533     int char_wide = ((Pcmw == 0)
 4534              ? (Pcss ? 6 : 10)
 4535              : (Pcmw > 4
 4536             ? Pcmw
 4537             : (Pcmw + 3)));
 4538     int char_high = ((Pcmh == 0)
 4539              ? ((Pcmw >= 2 && Pcmw <= 4)
 4540             ? 10
 4541             : 20)
 4542              : Pcmh);
 4543     Char ch;
 4544     Char bits[SOFT_HIGH][SOFT_WIDE];
 4545     Bool first = True;
 4546     Bool prior = False;
 4547     int row = 0, col = 0;
 4548 
 4549     TRACE(("Parsing DECDLD\n"));
 4550     TRACE(("  font number   %d\n", Pfn));
 4551     TRACE(("  starting char %d\n", Pcn));
 4552     TRACE(("  erase control %d\n", Pe));
 4553     TRACE(("  char-width    %d\n", Pcmw));
 4554     TRACE(("  font-width    %d\n", Pw));
 4555     TRACE(("  text/full     %d\n", Pt));
 4556     TRACE(("  char-height   %d\n", Pcmh));
 4557     TRACE(("  charset-size  %d\n", Pcss));
 4558 
 4559     if (Pfn > 1
 4560     || Pcn > 95
 4561     || Pe > 2
 4562     || Pcmw > 10
 4563     || Pcmw == 1
 4564     || Pt > 2
 4565     || Pcmh > 20
 4566     || Pcss > 1
 4567     || char_wide > SOFT_WIDE
 4568     || char_high > SOFT_HIGH) {
 4569     TRACE(("DECDLD illegal parameter\n"));
 4570     return;
 4571     }
 4572 
 4573     len = 0;
 4574     while (*string != '\0') {
 4575     ch = CharOf(*string++);
 4576     if (ch >= ANSI_SPA && ch <= 0x2f) {
 4577         if (len < 2)
 4578         DscsName[len++] = (char) ch;
 4579     } else if (ch >= 0x30 && ch <= 0x7e) {
 4580         DscsName[len++] = (char) ch;
 4581         break;
 4582     }
 4583     }
 4584     DscsName[len] = 0;
 4585     TRACE(("  Dscs name     '%s'\n", DscsName));
 4586 
 4587     TRACE(("  character matrix %dx%d\n", char_high, char_wide));
 4588     while (*string != '\0') {
 4589     if (first) {
 4590         TRACE(("Char %d:\n", start_char));
 4591         if (prior) {
 4592         for (row = 0; row < char_high; ++row) {
 4593             TRACE(("%.*s\n", char_wide, bits[row]));
 4594         }
 4595         }
 4596         prior = False;
 4597         first = False;
 4598         for (row = 0; row < char_high; ++row) {
 4599         for (col = 0; col < char_wide; ++col) {
 4600             bits[row][col] = '.';
 4601         }
 4602         }
 4603         row = col = 0;
 4604     }
 4605     ch = CharOf(*string++);
 4606     if (ch >= 0x3f && ch <= 0x7e) {
 4607         int n;
 4608 
 4609         ch = CharOf(ch - 0x3f);
 4610         for (n = 0; n < 6; ++n) {
 4611         bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
 4612         }
 4613         col += 1;
 4614         prior = True;
 4615     } else if (ch == '/') {
 4616         row += 6;
 4617         col = 0;
 4618     } else if (ch == ';') {
 4619         first = True;
 4620         ++start_char;
 4621     }
 4622     }
 4623 }
 4624 #else
 4625 #define parse_decdld(p,q)   /* nothing */
 4626 #endif
 4627 
 4628 #if OPT_DEC_RECTOPS
 4629 static const char *
 4630 skip_params(const char *cp)
 4631 {
 4632     while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
 4633     ++cp;
 4634     return cp;
 4635 }
 4636 
 4637 static int
 4638 parse_int_param(const char **cp)
 4639 {
 4640     int result = 0;
 4641     const char *s = *cp;
 4642     while (*s != '\0') {
 4643     if (*s == ';') {
 4644         ++s;
 4645         break;
 4646     } else if (*s >= '0' && *s <= '9') {
 4647         result = (result * 10) + (*s++ - '0');
 4648     } else {
 4649         s += strlen(s);
 4650     }
 4651     }
 4652     TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
 4653     *cp = s;
 4654     return result;
 4655 }
 4656 
 4657 static int
 4658 parse_chr_param(const char **cp)
 4659 {
 4660     int result = 0;
 4661     const char *s = *cp;
 4662     if (*s != '\0') {
 4663     if ((result = CharOf(*s++)) != 0) {
 4664         if (*s == ';') {
 4665         ++s;
 4666         } else if (*s != '\0') {
 4667         result = 0;
 4668         }
 4669     }
 4670     }
 4671     TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
 4672     *cp = s;
 4673     return result;
 4674 }
 4675 
 4676 static void
 4677 restore_DECCIR(XtermWidget xw, const char *cp)
 4678 {
 4679     TScreen *screen = TScreenOf(xw);
 4680     int value;
 4681 
 4682     /* row */
 4683     if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
 4684     return;
 4685     screen->cur_row = (value - 1);
 4686 
 4687     /* column */
 4688     if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
 4689     return;
 4690     screen->cur_col = (value - 1);
 4691 
 4692     /* page */
 4693     if (parse_int_param(&cp) != 1)
 4694     return;
 4695 
 4696     /* rendition */
 4697     if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
 4698     return;
 4699     UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
 4700     xw->flags |= (value & 8) ? INVERSE : 0;
 4701     xw->flags |= (value & 4) ? BLINK : 0;
 4702     xw->flags |= (value & 2) ? UNDERLINE : 0;
 4703     xw->flags |= (value & 1) ? BOLD : 0;
 4704 
 4705     /* attributes */
 4706     if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
 4707     return;
 4708     screen->protected_mode &= ~DEC_PROTECT;
 4709     screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
 4710 
 4711     /* flags */
 4712     if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
 4713     return;
 4714     screen->do_wrap = (value & 8) ? True : False;
 4715     screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
 4716     UIntClr(xw->flags, ORIGIN);
 4717     xw->flags |= (value & 1) ? ORIGIN : 0;
 4718 
 4719     if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
 4720     return;
 4721     screen->curgl = (Char) value;
 4722 
 4723     if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
 4724     return;
 4725     screen->curgr = (Char) value;
 4726 
 4727     /* character-set size */
 4728     if (parse_chr_param(&cp) != 0x4f)   /* works for xterm */
 4729     return;
 4730 
 4731     /* SCS designators */
 4732     for (value = 0; value < NUM_GSETS; ++value) {
 4733     if (*cp == '%') {
 4734         xtermDecodeSCS(xw, value, 0, '%', *++cp);
 4735     } else if (*cp != '\0') {
 4736         xtermDecodeSCS(xw, value, 0, '\0', *cp);
 4737     } else {
 4738         return;
 4739     }
 4740     cp++;
 4741     }
 4742 
 4743     TRACE(("...done DECCIR\n"));
 4744 }
 4745 
 4746 static void
 4747 restore_DECTABSR(XtermWidget xw, const char *cp)
 4748 {
 4749     int stop = 0;
 4750     Bool fail = False;
 4751 
 4752     TabZonk(xw->tabs);
 4753     while (*cp != '\0' && !fail) {
 4754     if ((*cp) >= '0' && (*cp) <= '9') {
 4755         stop = (stop * 10) + ((*cp) - '0');
 4756     } else if (*cp == '/') {
 4757         --stop;
 4758         if (OkTAB(stop)) {
 4759         TabSet(xw->tabs, stop);
 4760         stop = 0;
 4761         } else {
 4762         fail = True;
 4763         }
 4764     } else {
 4765         fail = True;
 4766     }
 4767     ++cp;
 4768     }
 4769     --stop;
 4770     if (OkTAB(stop))
 4771     TabSet(xw->tabs, stop);
 4772 
 4773     TRACE(("...done DECTABSR\n"));
 4774 }
 4775 #endif
 4776 
 4777 void
 4778 do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
 4779 {
 4780     TScreen *screen = TScreenOf(xw);
 4781     char reply[BUFSIZ];
 4782     const char *cp = (const char *) dcsbuf;
 4783     Bool okay;
 4784     ANSI params;
 4785 #if OPT_DEC_RECTOPS
 4786     char psarg = '0';
 4787 #endif
 4788 
 4789     TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
 4790 
 4791     if (dcslen != strlen(cp))
 4792     /* shouldn't have nulls in the string */
 4793     return;
 4794 
 4795     switch (*cp) {      /* intermediate character, or parameter */
 4796     case '$':           /* DECRQSS */
 4797     okay = True;
 4798 
 4799     cp++;
 4800     if (*cp == 'q') {
 4801         *reply = '\0';
 4802         cp++;
 4803         if (!strcmp(cp, "\"q")) {   /* DECSCA */
 4804         TRACE(("DECRQSS -> DECSCA\n"));
 4805         sprintf(reply, "%d%s",
 4806             (screen->protected_mode == DEC_PROTECT)
 4807             && (xw->flags & PROTECTED) ? 1 : 0,
 4808             cp);
 4809         } else if (!strcmp(cp, "\"p")) {    /* DECSCL */
 4810         if (screen->vtXX_level < 2) {
 4811             /* actually none of DECRQSS is valid for vt100's */
 4812             break;
 4813         }
 4814         TRACE(("DECRQSS -> DECSCL\n"));
 4815         sprintf(reply, "%d%s%s",
 4816             (screen->vtXX_level ?
 4817              screen->vtXX_level : 1) + 60,
 4818             (screen->vtXX_level >= 2)
 4819             ? (screen->control_eight_bits
 4820                ? ";0" : ";1")
 4821             : "",
 4822             cp);
 4823         } else if (!strcmp(cp, "r")) {  /* DECSTBM */
 4824         TRACE(("DECRQSS -> DECSTBM\n"));
 4825         sprintf(reply, "%d;%dr",
 4826             screen->top_marg + 1,
 4827             screen->bot_marg + 1);
 4828         } else if (!strcmp(cp, "s")) {  /* DECSLRM */
 4829         if (screen->vtXX_level >= 4) {  /* VT420 */
 4830             TRACE(("DECRQSS -> DECSLRM\n"));
 4831             sprintf(reply, "%d;%ds",
 4832                 screen->lft_marg + 1,
 4833                 screen->rgt_marg + 1);
 4834         } else {
 4835             okay = False;
 4836         }
 4837         } else if (!strcmp(cp, "m")) {  /* SGR */
 4838         TRACE(("DECRQSS -> SGR\n"));
 4839         xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
 4840         strcat(reply, "m");
 4841         } else if (!strcmp(cp, " q")) { /* DECSCUSR */
 4842         int code = STEADY_BLOCK;
 4843         if (isCursorUnderline(screen))
 4844             code = STEADY_UNDERLINE;
 4845         else if (isCursorBar(screen))
 4846             code = STEADY_BAR;
 4847 #if OPT_BLINK_CURS
 4848         if (screen->cursor_blink_esc != 0)
 4849             code -= 1;
 4850 #endif
 4851         TRACE(("reply DECSCUSR\n"));
 4852         sprintf(reply, "%d%s", code, cp);
 4853         } else if (!strcmp(cp, "t")) {  /* DECSLPP */
 4854         sprintf(reply, "%d%s",
 4855             ((screen->max_row > 24) ? screen->max_row : 24),
 4856             cp);
 4857         TRACE(("reply DECSLPP\n"));
 4858         } else if (!strcmp(cp, "$|")) { /* DECSCPP */
 4859         TRACE(("reply DECSCPP\n"));
 4860         sprintf(reply, "%d%s",
 4861             ((xw->flags & IN132COLUMNS) ? 132 : 80),
 4862             cp);
 4863         } else if (!strcmp(cp, "*|")) { /* DECSNLS */
 4864         TRACE(("reply DECSNLS\n"));
 4865         sprintf(reply, "%d%s",
 4866             screen->max_row + 1,
 4867             cp);
 4868         } else {
 4869         okay = False;
 4870         }
 4871 
 4872         unparseputc1(xw, ANSI_DCS);
 4873         unparseputc(xw, okay ? '1' : '0');
 4874         unparseputc(xw, '$');
 4875         unparseputc(xw, 'r');
 4876         cp = reply;
 4877         unparseputs(xw, cp);
 4878         unparseputc1(xw, ANSI_ST);
 4879     } else {
 4880         unparseputc(xw, ANSI_CAN);
 4881     }
 4882     break;
 4883     case '+':
 4884     cp++;
 4885     switch (*cp) {
 4886 #if OPT_TCAP_QUERY
 4887     case 'p':
 4888         if (AllowTcapOps(xw, etSetTcap)) {
 4889         set_termcap(xw, cp + 1);
 4890         }
 4891         break;
 4892     case 'q':
 4893         if (AllowTcapOps(xw, etGetTcap)) {
 4894         Bool fkey;
 4895         unsigned state;
 4896         int code;
 4897         const char *tmp;
 4898         const char *parsed = ++cp;
 4899 
 4900         code = xtermcapKeycode(xw, &parsed, &state, &fkey);
 4901 
 4902         unparseputc1(xw, ANSI_DCS);
 4903 
 4904         unparseputc(xw, code >= 0 ? '1' : '0');
 4905 
 4906         unparseputc(xw, '+');
 4907         unparseputc(xw, 'r');
 4908 
 4909         while (*cp != 0 && (code >= -1)) {
 4910             if (cp == parsed)
 4911             break;  /* no data found, error */
 4912 
 4913             for (tmp = cp; tmp != parsed; ++tmp)
 4914             unparseputc(xw, *tmp);
 4915 
 4916             if (code >= 0) {
 4917             unparseputc(xw, '=');
 4918             screen->tc_query_code = code;
 4919             screen->tc_query_fkey = fkey;
 4920 #if OPT_ISO_COLORS
 4921             /* XK_COLORS is a fake code for the "Co" entry (maximum
 4922              * number of colors) */
 4923             if (code == XK_COLORS) {
 4924                 unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
 4925             } else
 4926 #if OPT_DIRECT_COLOR
 4927             if (code == XK_RGB) {
 4928                 if (TScreenOf(xw)->direct_color && xw->has_rgb) {
 4929                 if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
 4930                     xw->rgb_widths[1] == xw->rgb_widths[2]) {
 4931                     unparseputn(xw, xw->rgb_widths[0]);
 4932                 } else {
 4933                     char temp[1024];
 4934                     sprintf(temp, "%d/%d/%d",
 4935                         xw->rgb_widths[0],
 4936                         xw->rgb_widths[1],
 4937                         xw->rgb_widths[2]);
 4938                     unparseputs(xw, temp);
 4939                 }
 4940                 } else {
 4941                 unparseputs(xw, "-1");
 4942                 }
 4943             } else
 4944 #endif
 4945 #endif
 4946             if (code == XK_TCAPNAME) {
 4947                 unparseputs(xw, resource.term_name);
 4948             } else {
 4949                 XKeyEvent event;
 4950                 memset(&event, 0, sizeof(event));
 4951                 event.state = state;
 4952                 Input(xw, &event, False);
 4953             }
 4954             screen->tc_query_code = -1;
 4955             } else {
 4956             break;  /* no match found, error */
 4957             }
 4958 
 4959             cp = parsed;
 4960             if (*parsed == ';') {
 4961             unparseputc(xw, *parsed++);
 4962             cp = parsed;
 4963             code = xtermcapKeycode(xw, &parsed, &state, &fkey);
 4964             }
 4965         }
 4966         unparseputc1(xw, ANSI_ST);
 4967         }
 4968         break;
 4969 #endif
 4970 #if OPT_XRES_QUERY
 4971     case 'Q':
 4972         ++cp;
 4973         if (AllowXResOps(xw)) {
 4974         Boolean first = True;
 4975         while (*cp != '\0') {
 4976             const char *parsed = 0;
 4977             const char *tmp;
 4978             char *name = x_decode_hex(cp, &parsed);
 4979             char *value;
 4980             char *result;
 4981             if (cp == parsed || name == NULL) {
 4982             free(name);
 4983             break;  /* no data found, error */
 4984             }
 4985             TRACE(("query-feature '%s'\n", name));
 4986             if ((value = vt100ResourceToString(xw, name)) != 0) {
 4987             okay = True;    /* valid */
 4988             } else {
 4989             okay = False;   /* invalid */
 4990             }
 4991             if (first) {
 4992             unparseputc1(xw, ANSI_DCS);
 4993             unparseputc(xw, okay ? '1' : '0');
 4994             unparseputc(xw, '+');
 4995             unparseputc(xw, 'R');
 4996             first = False;
 4997             }
 4998 
 4999             for (tmp = cp; tmp != parsed; ++tmp)
 5000             unparseputc(xw, *tmp);
 5001 
 5002             if (value != 0) {
 5003             unparseputc1(xw, '=');
 5004             result = x_encode_hex(value);
 5005             unparseputs(xw, result);
 5006             } else {
 5007             result = NULL;
 5008             }
 5009 
 5010             free(name);
 5011             free(value);
 5012             free(result);
 5013 
 5014             cp = parsed;
 5015             if (*parsed == ';') {
 5016             unparseputc(xw, *parsed++);
 5017             cp = parsed;
 5018             }
 5019         }
 5020         if (!first)
 5021             unparseputc1(xw, ANSI_ST);
 5022         }
 5023         break;
 5024 #endif
 5025     }
 5026     break;
 5027 #if OPT_DEC_RECTOPS
 5028     case '1':
 5029     /* FALLTHRU */
 5030     case '2':
 5031     if (*skip_params(cp) == '$') {
 5032         psarg = *cp++;
 5033         if ((*cp++ == '$')
 5034         && (*cp++ == 't')
 5035         && (screen->vtXX_level >= 3)) {
 5036         switch (psarg) {
 5037         case '1':
 5038             TRACE(("DECRSPS (DECCIR)\n"));
 5039             restore_DECCIR(xw, cp);
 5040             break;
 5041         case '2':
 5042             TRACE(("DECRSPS (DECTABSR)\n"));
 5043             restore_DECTABSR(xw, cp);
 5044             break;
 5045         }
 5046         }
 5047         break;
 5048     }
 5049 #endif
 5050     /* FALLTHRU */
 5051     default:
 5052     if (optRegisGraphics(screen) ||
 5053         optSixelGraphics(screen) ||
 5054         screen->vtXX_level >= 2) {  /* VT220 */
 5055         parse_ansi_params(&params, &cp);
 5056         switch (params.a_final) {
 5057         case 'p':       /* ReGIS */
 5058 #if OPT_REGIS_GRAPHICS
 5059         if (optRegisGraphics(screen)) {
 5060             parse_regis(xw, &params, cp);
 5061         }
 5062 #else
 5063         TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
 5064 #endif
 5065         break;
 5066         case 'q':       /* sixel */
 5067 #if OPT_SIXEL_GRAPHICS
 5068         if (optSixelGraphics(screen)) {
 5069             (void) parse_sixel(xw, &params, cp);
 5070         }
 5071 #else
 5072         TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
 5073 #endif
 5074         break;
 5075         case '|':       /* DECUDK */
 5076         if (screen->vtXX_level >= 2) {  /* VT220 */
 5077             if (params.a_param[0] == 0)
 5078             reset_decudk(xw);
 5079             parse_decudk(xw, cp);
 5080         }
 5081         break;
 5082         case L_CURL:    /* DECDLD */
 5083         if (screen->vtXX_level >= 2) {  /* VT220 */
 5084             parse_decdld(&params, cp);
 5085         }
 5086         break;
 5087         }
 5088     }
 5089     break;
 5090     }
 5091     unparse_end(xw);
 5092 }
 5093 
 5094 #if OPT_DEC_RECTOPS
 5095 enum {
 5096     mdUnknown = 0,
 5097     mdMaybeSet = 1,
 5098     mdMaybeReset = 2,
 5099     mdAlwaysSet = 3,
 5100     mdAlwaysReset = 4
 5101 };
 5102 
 5103 #define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
 5104 #define MdFlag(mode,flag) MdBool((mode) & (flag))
 5105 
 5106 /*
 5107  * Reply is the same format as the query, with pair of mode/value:
 5108  * 0 - not recognized
 5109  * 1 - set
 5110  * 2 - reset
 5111  * 3 - permanently set
 5112  * 4 - permanently reset
 5113  * Only one mode can be reported at a time.
 5114  */
 5115 void
 5116 do_ansi_rqm(XtermWidget xw, int nparams, int *params)
 5117 {
 5118     ANSI reply;
 5119     int count = 0;
 5120 
 5121     TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
 5122     memset(&reply, 0, sizeof(reply));
 5123 
 5124     if (nparams >= 1) {
 5125     int result = mdUnknown;
 5126 
 5127     /* DECRQM can only ask about one mode at a time */
 5128     switch (params[0]) {
 5129     case 1:     /* GATM */
 5130         result = mdAlwaysReset;
 5131         break;
 5132     case 2:
 5133         result = MdFlag(xw->keyboard.flags, MODE_KAM);
 5134         break;
 5135     case 3:     /* CRM */
 5136         result = mdMaybeReset;
 5137         break;
 5138     case 4:
 5139         result = MdFlag(xw->flags, INSERT);
 5140         break;
 5141     case 5:     /* SRTM */
 5142     case 7:     /* VEM */
 5143     case 10:        /* HEM */
 5144     case 11:        /* PUM */
 5145         result = mdAlwaysReset;
 5146         break;
 5147     case 12:
 5148         result = MdFlag(xw->keyboard.flags, MODE_SRM);
 5149         break;
 5150     case 13:        /* FEAM */
 5151     case 14:        /* FETM */
 5152     case 15:        /* MATM */
 5153     case 16:        /* TTM */
 5154     case 17:        /* SATM */
 5155     case 18:        /* TSM */
 5156     case 19:        /* EBM */
 5157         result = mdAlwaysReset;
 5158         break;
 5159     case 20:
 5160         result = MdFlag(xw->flags, LINEFEED);
 5161         break;
 5162     }
 5163     reply.a_param[count++] = (ParmType) params[0];
 5164     reply.a_param[count++] = (ParmType) result;
 5165     }
 5166     reply.a_type = ANSI_CSI;
 5167     reply.a_nparam = (ParmType) count;
 5168     reply.a_inters = '$';
 5169     reply.a_final = 'y';
 5170     unparseseq(xw, &reply);
 5171 }
 5172 
 5173 void
 5174 do_dec_rqm(XtermWidget xw, int nparams, int *params)
 5175 {
 5176     ANSI reply;
 5177     int count = 0;
 5178 
 5179     TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
 5180     memset(&reply, 0, sizeof(reply));
 5181 
 5182     if (nparams >= 1) {
 5183     TScreen *screen = TScreenOf(xw);
 5184     int result = mdUnknown;
 5185 
 5186     /* DECRQM can only ask about one mode at a time */
 5187     switch ((DECSET_codes) params[0]) {
 5188     case srm_DECCKM:
 5189         result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
 5190         break;
 5191     case srm_DECANM:    /* ANSI/VT52 mode      */
 5192 #if OPT_VT52_MODE
 5193         result = MdBool(screen->vtXX_level >= 1);
 5194 #else
 5195         result = mdMaybeSet;
 5196 #endif
 5197         break;
 5198     case srm_DECCOLM:
 5199         result = MdFlag(xw->flags, IN132COLUMNS);
 5200         break;
 5201     case srm_DECSCLM:   /* (slow scroll)        */
 5202         result = MdFlag(xw->flags, SMOOTHSCROLL);
 5203         break;
 5204     case srm_DECSCNM:
 5205         result = MdFlag(xw->flags, REVERSE_VIDEO);
 5206         break;
 5207     case srm_DECOM:
 5208         result = MdFlag(xw->flags, ORIGIN);
 5209         break;
 5210     case srm_DECAWM:
 5211         result = MdFlag(xw->flags, WRAPAROUND);
 5212         break;
 5213     case srm_DECARM:
 5214         result = mdAlwaysReset;
 5215         break;
 5216     case srm_X10_MOUSE: /* X10 mouse                    */
 5217         result = MdBool(screen->send_mouse_pos == X10_MOUSE);
 5218         break;
 5219 #if OPT_TOOLBAR
 5220     case srm_RXVT_TOOLBAR:
 5221         result = MdBool(resource.toolBar);
 5222         break;
 5223 #endif
 5224 #if OPT_BLINK_CURS
 5225     case srm_ATT610_BLINK:  /* AT&T 610: Start/stop blinking cursor */
 5226         result = MdBool(screen->cursor_blink_esc);
 5227         break;
 5228     case srm_CURSOR_BLINK_OPS:
 5229         switch (screen->cursor_blink) {
 5230         case cbTrue:
 5231         result = mdMaybeSet;
 5232         break;
 5233         case cbFalse:
 5234         result = mdMaybeReset;
 5235         break;
 5236         case cbAlways:
 5237         result = mdAlwaysSet;
 5238         break;
 5239         case cbLAST:
 5240         /* FALLTHRU */
 5241         case cbNever:
 5242         result = mdAlwaysReset;
 5243         break;
 5244         }
 5245         break;
 5246     case srm_XOR_CURSOR_BLINKS:
 5247         result = (screen->cursor_blink_xor
 5248               ? mdAlwaysSet
 5249               : mdAlwaysReset);
 5250         break;
 5251 #endif
 5252     case srm_DECPFF:    /* print form feed */
 5253         result = MdBool(PrinterOf(screen).printer_formfeed);
 5254         break;
 5255     case srm_DECPEX:    /* print extent */
 5256         result = MdBool(PrinterOf(screen).printer_extent);
 5257         break;
 5258     case srm_DECTCEM:   /* Show/hide cursor (VT200) */
 5259         result = MdBool(screen->cursor_set);
 5260         break;
 5261     case srm_RXVT_SCROLLBAR:
 5262         result = MdBool(screen->fullVwin.sb_info.width != OFF);
 5263         break;
 5264 #if OPT_SHIFT_FONTS
 5265     case srm_RXVT_FONTSIZE:
 5266         result = MdBool(xw->misc.shift_fonts);
 5267         break;
 5268 #endif
 5269 #if OPT_TEK4014
 5270     case srm_DECTEK:
 5271         result = MdBool(TEK4014_ACTIVE(xw));
 5272         break;
 5273 #endif
 5274     case srm_132COLS:
 5275         result = MdBool(screen->c132);
 5276         break;
 5277     case srm_CURSES_HACK:
 5278         result = MdBool(screen->curses);
 5279         break;
 5280     case srm_DECNRCM:   /* national charset (VT220) */
 5281         if (screen->vtXX_level >= 2) {
 5282         result = MdFlag(xw->flags, NATIONAL);
 5283         } else {
 5284         result = 0;
 5285         }
 5286         break;
 5287     case srm_MARGIN_BELL:   /* margin bell                  */
 5288         result = MdBool(screen->marginbell);
 5289         break;
 5290 #if OPT_PRINT_GRAPHICS
 5291     case srm_DECGEPM:   /* Graphics Expanded Print Mode */
 5292         result = MdBool(screen->graphics_expanded_print_mode);
 5293         break;
 5294 #endif
 5295     case srm_REVERSEWRAP:   /* reverse wraparound   */
 5296         if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
 5297         result = MdFlag(xw->flags, REVERSEWRAP);
 5298         break;
 5299 #if defined(ALLOWLOGGING)
 5300     case srm_ALLOWLOGGING:  /* logging              */
 5301         if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
 5302 #if defined(ALLOWLOGFILEONOFF)
 5303         result = MdBool(screen->logging);
 5304 #else
 5305         result = ((MdBool(screen->logging) == mdMaybeSet)
 5306               ? mdAlwaysSet
 5307               : mdAlwaysReset);
 5308 #endif
 5309         break;
 5310 #elif OPT_PRINT_GRAPHICS
 5311     case srm_DECGPBM:   /* Graphics Print Background Mode */
 5312         result = MdBool(screen->graphics_print_background_mode);
 5313         break;
 5314 #endif
 5315     case srm_OPT_ALTBUF_CURSOR: /* alternate buffer & cursor */
 5316         /* FALLTHRU */
 5317     case srm_OPT_ALTBUF:
 5318         result = MdBool(screen->whichBuf);
 5319         break;
 5320     case srm_ALTBUF:
 5321         if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
 5322         result = MdBool(screen->whichBuf);
 5323         break;
 5324     case srm_DECNKM:
 5325         result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
 5326         break;
 5327     case srm_DECBKM:
 5328         result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
 5329         break;
 5330     case srm_DECLRMM:
 5331         if (screen->vtXX_level >= 4) {  /* VT420 */
 5332         result = MdFlag(xw->flags, LEFT_RIGHT);
 5333         } else {
 5334         result = 0;
 5335         }
 5336         break;
 5337 #if OPT_SIXEL_GRAPHICS
 5338     case srm_DECSDM:
 5339         result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
 5340         break;
 5341 #endif
 5342     case srm_DECNCSM:
 5343         if (screen->vtXX_level >= 5) {  /* VT510 */
 5344         result = MdFlag(xw->flags, NOCLEAR_COLM);
 5345         } else {
 5346         result = 0;
 5347         }
 5348         break;
 5349     case srm_VT200_MOUSE:   /* xterm bogus sequence */
 5350         result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
 5351         break;
 5352     case srm_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */
 5353         result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
 5354         break;
 5355     case srm_BTN_EVENT_MOUSE:
 5356         result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
 5357         break;
 5358     case srm_ANY_EVENT_MOUSE:
 5359         result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
 5360         break;
 5361 #if OPT_FOCUS_EVENT
 5362     case srm_FOCUS_EVENT_MOUSE:
 5363         result = MdBool(screen->send_focus_pos);
 5364         break;
 5365 #endif
 5366     case srm_EXT_MODE_MOUSE:
 5367         /* FALLTHRU */
 5368     case srm_SGR_EXT_MODE_MOUSE:
 5369         /* FALLTHRU */
 5370     case srm_URXVT_EXT_MODE_MOUSE:
 5371         /* FALLTHRU */
 5372     case srm_PIXEL_POSITION_MOUSE:
 5373         result = MdBool(screen->extend_coords == params[0]);
 5374         break;
 5375     case srm_ALTERNATE_SCROLL:
 5376         result = MdBool(screen->alternateScroll);
 5377         break;
 5378     case srm_RXVT_SCROLL_TTY_OUTPUT:
 5379         result = MdBool(screen->scrollttyoutput);
 5380         break;
 5381     case srm_RXVT_SCROLL_TTY_KEYPRESS:
 5382         result = MdBool(screen->scrollkey);
 5383         break;
 5384     case srm_EIGHT_BIT_META:
 5385         result = MdBool(screen->eight_bit_meta);
 5386         break;
 5387 #if OPT_NUM_LOCK
 5388     case srm_REAL_NUMLOCK:
 5389         result = MdBool(xw->misc.real_NumLock);
 5390         break;
 5391     case srm_META_SENDS_ESC:
 5392         result = MdBool(screen->meta_sends_esc);
 5393         break;
 5394 #endif
 5395     case srm_DELETE_IS_DEL:
 5396         result = MdBool(xtermDeleteIsDEL(xw));
 5397         break;
 5398 #if OPT_NUM_LOCK
 5399     case srm_ALT_SENDS_ESC:
 5400         result = MdBool(screen->alt_sends_esc);
 5401         break;
 5402 #endif
 5403     case srm_KEEP_SELECTION:
 5404         result = MdBool(screen->keepSelection);
 5405         break;
 5406     case srm_SELECT_TO_CLIPBOARD:
 5407         result = MdBool(screen->selectToClipboard);
 5408         break;
 5409     case srm_BELL_IS_URGENT:
 5410         result = MdBool(screen->bellIsUrgent);
 5411         break;
 5412     case srm_POP_ON_BELL:
 5413         result = MdBool(screen->poponbell);
 5414         break;
 5415     case srm_KEEP_CLIPBOARD:
 5416         result = MdBool(screen->keepClipboard);
 5417         break;
 5418     case srm_ALLOW_ALTBUF:
 5419         result = MdBool(xw->misc.titeInhibit);
 5420         break;
 5421     case srm_SAVE_CURSOR:
 5422         result = MdBool(screen->sc[screen->whichBuf].saved);
 5423         break;
 5424 #if OPT_TCAP_FKEYS
 5425     case srm_TCAP_FKEYS:
 5426         result = MdBool(xw->keyboard.type == keyboardIsTermcap);
 5427         break;
 5428 #endif
 5429 #if OPT_SUN_FUNC_KEYS
 5430     case srm_SUN_FKEYS:
 5431         result = MdBool(xw->keyboard.type == keyboardIsSun);
 5432         break;
 5433 #endif
 5434 #if OPT_HP_FUNC_KEYS
 5435     case srm_HP_FKEYS:
 5436         result = MdBool(xw->keyboard.type == keyboardIsHP);
 5437         break;
 5438 #endif
 5439 #if OPT_SCO_FUNC_KEYS
 5440     case srm_SCO_FKEYS:
 5441         result = MdBool(xw->keyboard.type == keyboardIsSCO);
 5442         break;
 5443 #endif
 5444     case srm_LEGACY_FKEYS:
 5445         result = MdBool(xw->keyboard.type == keyboardIsLegacy);
 5446         break;
 5447 #if OPT_SUNPC_KBD
 5448     case srm_VT220_FKEYS:
 5449         result = MdBool(xw->keyboard.type == keyboardIsVT220);
 5450         break;
 5451 #endif
 5452 #if OPT_PASTE64 || OPT_READLINE
 5453     case srm_PASTE_IN_BRACKET:
 5454         result = MdBool(SCREEN_FLAG(screen, paste_brackets));
 5455         break;
 5456 #endif
 5457 #if OPT_READLINE
 5458     case srm_BUTTON1_MOVE_POINT:
 5459         result = MdBool(SCREEN_FLAG(screen, click1_moves));
 5460         break;
 5461     case srm_BUTTON2_MOVE_POINT:
 5462         result = MdBool(SCREEN_FLAG(screen, paste_moves));
 5463         break;
 5464     case srm_DBUTTON3_DELETE:
 5465         result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
 5466         break;
 5467     case srm_PASTE_QUOTE:
 5468         result = MdBool(SCREEN_FLAG(screen, paste_quotes));
 5469         break;
 5470     case srm_PASTE_LITERAL_NL:
 5471         result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
 5472         break;
 5473 #endif /* OPT_READLINE */
 5474 #if OPT_GRAPHICS
 5475     case srm_PRIVATE_COLOR_REGISTERS:
 5476         result = MdBool(screen->privatecolorregisters);
 5477         break;
 5478 #endif
 5479 #if OPT_SIXEL_GRAPHICS
 5480     case srm_SIXEL_SCROLLS_RIGHT:
 5481         result = MdBool(screen->sixel_scrolls_right);
 5482         break;
 5483 #endif
 5484     default:
 5485         TRACE(("DATA_ERROR: requested report for unknown private mode %d