"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/misc.c" (7 Jan 2023, 190536 Bytes) of package /linux/misc/xterm-379.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: 377_vs_379.

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