Home | History | Annotate | Line # | Download | only in dist
      1 /* $XTermId: misc.c,v 1.1107 2024/12/01 20:06:49 tom Exp $ */
      2 
      3 /*
      4  * Copyright 1999-2023,2024 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 #if USE_DOUBLE_BUFFER
    109 #include <X11/extensions/Xdbe.h>
    110 #endif
    111 
    112 #if OPT_WIDE_CHARS
    113 #include <wctype.h>
    114 #endif
    115 
    116 #if OPT_TEK4014
    117 #define OUR_EVENT(event,Type) \
    118 		(event.type == Type && \
    119 		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
    120 		    (tekWidget && \
    121 		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
    122 #else
    123 #define OUR_EVENT(event,Type) \
    124 		(event.type == Type && \
    125 		   (event.xcrossing.window == XtWindow(XtParent(xw))))
    126 #endif
    127 
    128 #define VB_DELAY    screen->visualBellDelay
    129 #define EVENT_DELAY TScreenOf(term)->nextEventDelay
    130 
    131 static Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
    132 static Cursor make_hidden_cursor(XtermWidget);
    133 
    134 #if OPT_EXEC_XTERM
    135 /* Like readlink(2), but returns a malloc()ed buffer, or NULL on
    136    error; adapted from libc docs */
    137 static char *
    138 Readlink(const char *filename)
    139 {
    140     char *buf = NULL;
    141     size_t size = 100;
    142 
    143     for (;;) {
    144 	int n;
    145 	char *tmp = TypeRealloc(char, size, buf);
    146 	if (tmp == NULL) {
    147 	    free(buf);
    148 	    return NULL;
    149 	}
    150 	buf = tmp;
    151 	memset(buf, 0, size);
    152 
    153 	n = (int) readlink(filename, buf, size);
    154 	if (n < 0) {
    155 	    free(buf);
    156 	    return NULL;
    157 	}
    158 
    159 	if ((unsigned) n < size) {
    160 	    return buf;
    161 	}
    162 
    163 	size *= 2;
    164     }
    165 }
    166 #endif /* OPT_EXEC_XTERM */
    167 
    168 static void
    169 Sleep(int msec)
    170 {
    171     static struct timeval select_timeout;
    172 
    173     select_timeout.tv_sec = 0;
    174     select_timeout.tv_usec = msec * 1000;
    175     select(0, NULL, NULL, NULL, &select_timeout);
    176 }
    177 
    178 static void
    179 selectwindow(XtermWidget xw, int flag)
    180 {
    181     TScreen *screen = TScreenOf(xw);
    182 
    183     TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
    184 
    185 #if OPT_TEK4014
    186     if (TEK4014_ACTIVE(xw)) {
    187 	if (!Ttoggled)
    188 	    TCursorToggle(tekWidget, TOGGLE);
    189 	screen->select |= flag;
    190 	if (!Ttoggled)
    191 	    TCursorToggle(tekWidget, TOGGLE);
    192     } else
    193 #endif
    194     {
    195 #if OPT_INPUT_METHOD
    196 	TInput *input = lookupTInput(xw, (Widget) xw);
    197 	if (input && input->xic)
    198 	    XSetICFocus(input->xic);
    199 #endif
    200 
    201 	if (screen->cursor_state && CursorMoved(screen))
    202 	    HideCursor(xw);
    203 	screen->select |= flag;
    204 	if (screen->cursor_state)
    205 	    ShowCursor(xw);
    206     }
    207     GetScrollLock(screen);
    208 }
    209 
    210 static void
    211 unselectwindow(XtermWidget xw, int flag)
    212 {
    213     TScreen *screen = TScreenOf(xw);
    214 
    215     TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
    216 
    217     if (screen->hide_pointer && screen->pointer_mode < pFocused) {
    218 	screen->hide_pointer = False;
    219 	xtermDisplayPointer(xw);
    220     }
    221 
    222     screen->select &= ~flag;
    223 
    224     if (!screen->always_highlight) {
    225 #if OPT_TEK4014
    226 	if (TEK4014_ACTIVE(xw)) {
    227 	    if (!Ttoggled)
    228 		TCursorToggle(tekWidget, TOGGLE);
    229 	    if (!Ttoggled)
    230 		TCursorToggle(tekWidget, TOGGLE);
    231 	} else
    232 #endif
    233 	{
    234 #if OPT_INPUT_METHOD
    235 	    TInput *input = lookupTInput(xw, (Widget) xw);
    236 	    if (input && input->xic)
    237 		XUnsetICFocus(input->xic);
    238 #endif
    239 
    240 	    if (screen->cursor_state && CursorMoved(screen))
    241 		HideCursor(xw);
    242 	    if (screen->cursor_state)
    243 		ShowCursor(xw);
    244 	}
    245     }
    246 }
    247 
    248 static void
    249 DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
    250 {
    251     TScreen *screen = TScreenOf(xw);
    252 
    253     TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
    254     TRACE_FOCUS(xw, ev);
    255     if (((ev->detail) != NotifyInferior) &&
    256 	ev->focus &&
    257 	!(screen->select & FOCUS))
    258 	selectwindow(xw, INWINDOW);
    259 }
    260 
    261 static void
    262 DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
    263 {
    264     TScreen *screen = TScreenOf(xw);
    265 
    266     TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
    267     TRACE_FOCUS(xw, ev);
    268     if (((ev->detail) != NotifyInferior) &&
    269 	ev->focus &&
    270 	!(screen->select & FOCUS))
    271 	unselectwindow(xw, INWINDOW);
    272 }
    273 
    274 #ifndef XUrgencyHint
    275 #define XUrgencyHint (1L << 8)	/* X11R5 does not define */
    276 #endif
    277 
    278 static void
    279 setXUrgency(XtermWidget xw, Bool enable)
    280 {
    281     TScreen *screen = TScreenOf(xw);
    282 
    283     if (screen->bellIsUrgent) {
    284 	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
    285 	if (h != NULL) {
    286 	    if (enable && !(screen->select & FOCUS)) {
    287 		h->flags |= XUrgencyHint;
    288 	    } else {
    289 		h->flags &= ~XUrgencyHint;
    290 	    }
    291 	    XSetWMHints(screen->display, VShellWindow(xw), h);
    292 	}
    293     }
    294 }
    295 
    296 void
    297 do_xevents(XtermWidget xw)
    298 {
    299     TScreen *screen = TScreenOf(xw);
    300 
    301     if (xtermAppPending()
    302 	|| GetBytesAvailable(screen->display) > 0) {
    303 	xevents(xw);
    304     }
    305 }
    306 
    307 void
    308 xtermDisplayPointer(XtermWidget xw)
    309 {
    310     TScreen *screen = TScreenOf(xw);
    311 
    312     if (screen->Vshow) {
    313 	if (screen->hide_pointer) {
    314 	    TRACE(("Display text pointer (hidden)\n"));
    315 	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
    316 	} else {
    317 	    TRACE(("Display text pointer (visible)\n"));
    318 	    recolor_cursor(screen,
    319 			   screen->pointer_cursor,
    320 			   T_COLOR(screen, MOUSE_FG),
    321 			   T_COLOR(screen, MOUSE_BG));
    322 	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
    323 	}
    324     }
    325 }
    326 
    327 void
    328 xtermShowPointer(XtermWidget xw, Bool enable)
    329 {
    330     static int tried = -1;
    331     TScreen *screen = TScreenOf(xw);
    332 
    333 #if OPT_TEK4014
    334     if (TEK4014_SHOWN(xw))
    335 	enable = True;
    336 #endif
    337 
    338     /*
    339      * Whether we actually hide the pointer depends on the pointer-mode and
    340      * the mouse-mode:
    341      */
    342     if (!enable) {
    343 	switch (screen->pointer_mode) {
    344 	case pNever:
    345 	    enable = True;
    346 	    break;
    347 	case pNoMouse:
    348 	    if (screen->send_mouse_pos != MOUSE_OFF)
    349 		enable = True;
    350 	    break;
    351 	case pAlways:
    352 	case pFocused:
    353 	    break;
    354 	}
    355     }
    356 
    357     if (enable) {
    358 	if (screen->hide_pointer) {
    359 	    screen->hide_pointer = False;
    360 	    xtermDisplayPointer(xw);
    361 	    switch (screen->send_mouse_pos) {
    362 	    case ANY_EVENT_MOUSE:
    363 		break;
    364 	    default:
    365 		MotionOff(screen, xw);
    366 		break;
    367 	    }
    368 	}
    369     } else if (!(screen->hide_pointer) && (tried <= 0)) {
    370 	if (screen->hidden_cursor == 0) {
    371 	    screen->hidden_cursor = make_hidden_cursor(xw);
    372 	}
    373 	if (screen->hidden_cursor == 0) {
    374 	    tried = 1;
    375 	} else {
    376 	    tried = 0;
    377 	    screen->hide_pointer = True;
    378 	    xtermDisplayPointer(xw);
    379 	    MotionOn(screen, xw);
    380 	}
    381     }
    382 }
    383 
    384 /* true if p contains q */
    385 #define ExposeContains(p,q) \
    386 	    ((p)->y <= (q)->y \
    387 	  && (p)->x <= (q)->x \
    388 	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
    389 	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
    390 
    391 static XtInputMask
    392 mergeExposeEvents(XEvent *target)
    393 {
    394     XEvent next_event;
    395     XExposeEvent *p;
    396 
    397     XtAppNextEvent(app_con, target);
    398     p = (XExposeEvent *) target;
    399 
    400     while (XtAppPending(app_con)
    401 	   && XtAppPeekEvent(app_con, &next_event)
    402 	   && next_event.type == Expose) {
    403 	Boolean merge_this = False;
    404 	XExposeEvent *q = (XExposeEvent *) (&next_event);
    405 
    406 	XtAppNextEvent(app_con, &next_event);
    407 	TRACE_EVENT("pending", &next_event, (String *) 0, NULL);
    408 
    409 	/*
    410 	 * If either window is contained within the other, merge the events.
    411 	 * The traces show that there are also cases where a full repaint of
    412 	 * a window is broken into 3 or more rectangles, which do not arrive
    413 	 * in the same instant.  We could merge those if xterm were modified
    414 	 * to skim several events ahead.
    415 	 */
    416 	if (p->window == q->window) {
    417 	    if (ExposeContains(p, q)) {
    418 		TRACE(("pending Expose...merged forward\n"));
    419 		merge_this = True;
    420 		next_event = *target;
    421 	    } else if (ExposeContains(q, p)) {
    422 		TRACE(("pending Expose...merged backward\n"));
    423 		merge_this = True;
    424 	    }
    425 	}
    426 	if (!merge_this) {
    427 	    XtDispatchEvent(target);
    428 	}
    429 	*target = next_event;
    430     }
    431     XtDispatchEvent(target);
    432     return XtAppPending(app_con);
    433 }
    434 
    435 /*
    436  * On entry, we have peeked at the event queue and see a configure-notify
    437  * event.  Remove that from the queue so we can look further.
    438  *
    439  * Then, as long as there is a configure-notify event in the queue, remove
    440  * that.  If the adjacent events are for different windows, process the older
    441  * event and update the event used for comparing windows.  If they are for the
    442  * same window, only the newer event is of interest.
    443  *
    444  * Finally, process the (remaining) configure-notify event.
    445  */
    446 static XtInputMask
    447 mergeConfigureEvents(XEvent *target)
    448 {
    449     XEvent next_event;
    450     XConfigureEvent *p;
    451 
    452     XtAppNextEvent(app_con, target);
    453     p = (XConfigureEvent *) target;
    454 
    455     if (XtAppPending(app_con)
    456 	&& XtAppPeekEvent(app_con, &next_event)
    457 	&& next_event.type == ConfigureNotify) {
    458 	Boolean merge_this = False;
    459 	XConfigureEvent *q = (XConfigureEvent *) (&next_event);
    460 
    461 	XtAppNextEvent(app_con, &next_event);
    462 	TRACE_EVENT("pending", &next_event, (String *) 0, NULL);
    463 
    464 	if (p->window == q->window) {
    465 	    TRACE(("pending Configure...merged\n"));
    466 	    merge_this = True;
    467 	}
    468 	if (!merge_this) {
    469 	    TRACE(("pending Configure...skipped\n"));
    470 	    XtDispatchEvent(target);
    471 	}
    472 	*target = next_event;
    473     }
    474     XtDispatchEvent(target);
    475     return XtAppPending(app_con);
    476 }
    477 
    478 #define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
    479 #define SameButtonEvent(a,b) ( \
    480 	SAME(a,b,type) && \
    481 	SAME(a,b,serial) && \
    482 	SAME(a,b,send_event) && \
    483 	SAME(a,b,display) && \
    484 	SAME(a,b,window) && \
    485 	SAME(a,b,root) && \
    486 	SAME(a,b,subwindow) && \
    487 	SAME(a,b,time) && \
    488 	SAME(a,b,x) && \
    489 	SAME(a,b,y) && \
    490 	SAME(a,b,x_root) && \
    491 	SAME(a,b,y_root) && \
    492 	SAME(a,b,state) && \
    493 	SAME(a,b,button) && \
    494 	SAME(a,b,same_screen))
    495 
    496 /*
    497  * Work around a bug in the X mouse code, which delivers duplicate events.
    498  */
    499 static XtInputMask
    500 mergeButtonEvents(XEvent *target)
    501 {
    502     XEvent next_event;
    503     XButtonEvent *p;
    504 
    505     XtAppNextEvent(app_con, target);
    506     p = (XButtonEvent *) target;
    507 
    508     if (XtAppPending(app_con)
    509 	&& XtAppPeekEvent(app_con, &next_event)
    510 	&& SameButtonEvent(target, &next_event)) {
    511 	Boolean merge_this = False;
    512 	XButtonEvent *q = (XButtonEvent *) (&next_event);
    513 
    514 	XtAppNextEvent(app_con, &next_event);
    515 	TRACE_EVENT("pending", &next_event, (String *) 0, NULL);
    516 
    517 	if (p->window == q->window) {
    518 	    TRACE(("pending ButtonEvent...merged\n"));
    519 	    merge_this = True;
    520 	}
    521 	if (!merge_this) {
    522 	    TRACE(("pending ButtonEvent...skipped\n"));
    523 	    XtDispatchEvent(target);
    524 	}
    525 	*target = next_event;
    526     }
    527     XtDispatchEvent(target);
    528     return XtAppPending(app_con);
    529 }
    530 
    531 /*
    532  * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
    533  * adjacent events because there could be other event-loop processing.  Absent
    534  * that limitation, it might be possible to scan ahead to find when the screen
    535  * would be completely updated, skipping unnecessary re-repainting before that
    536  * point.
    537  *
    538  * Note: all cases should allow doing XtAppNextEvent if result is true.
    539  */
    540 XtInputMask
    541 xtermAppPending(void)
    542 {
    543     XtInputMask result = XtAppPending(app_con);
    544     XEvent this_event;
    545     Boolean found = False;
    546 
    547     while (result && XtAppPeekEvent(app_con, &this_event)) {
    548 	found = True;
    549 	TRACE_EVENT("pending", &this_event, (String *) 0, NULL);
    550 	if (this_event.type == Expose) {
    551 	    result = mergeExposeEvents(&this_event);
    552 	} else if (this_event.type == ConfigureNotify) {
    553 	    result = mergeConfigureEvents(&this_event);
    554 	} else if (this_event.type == ButtonPress ||
    555 		   this_event.type == ButtonRelease) {
    556 	    result = mergeButtonEvents(&this_event);
    557 	} else {
    558 	    break;
    559 	}
    560     }
    561 
    562     /*
    563      * With NetBSD, closing a shell results in closing the X input event
    564      * stream, which interferes with the "-hold" option.  Wait a short time in
    565      * this case, to avoid max'ing the CPU.
    566      */
    567     if (hold_screen && caught_intr && !found) {
    568 	Sleep(EVENT_DELAY);
    569     }
    570     return result;
    571 }
    572 
    573 void
    574 xevents(XtermWidget xw)
    575 {
    576     TScreen *screen = TScreenOf(xw);
    577     XEvent event;
    578     XtInputMask input_mask;
    579 
    580     if (need_cleanup)
    581 	NormalExit();
    582 
    583     if (screen->scroll_amt)
    584 	FlushScroll(xw);
    585     /*
    586      * process timeouts, relying on the fact that XtAppProcessEvent
    587      * will process the timeout and return without blockng on the
    588      * XEvent queue.  Other sources i.e., the pty are handled elsewhere
    589      * with select().
    590      */
    591     while ((input_mask = xtermAppPending()) != 0) {
    592 	if (input_mask & XtIMTimer)
    593 	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
    594 #if OPT_SESSION_MGT
    595 	/*
    596 	 * Session management events are alternative input events. Deal with
    597 	 * them in the same way.
    598 	 */
    599 	else if (input_mask & XtIMAlternateInput)
    600 	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
    601 #endif
    602 	else
    603 	    break;
    604     }
    605 
    606     /*
    607      * If there are no XEvents, don't wait around...
    608      */
    609     if ((input_mask & XtIMXEvent) != XtIMXEvent)
    610 	return;
    611     do {
    612 	/*
    613 	 * This check makes xterm hang when in mouse hilite tracking mode.
    614 	 * We simply ignore all events except for those not passed down to
    615 	 * this function, e.g., those handled in in_put().
    616 	 */
    617 	if (screen->waitingForTrackInfo) {
    618 	    Sleep(EVENT_DELAY);
    619 	    return;
    620 	}
    621 	XtAppNextEvent(app_con, &event);
    622 	/*
    623 	 * Hack to get around problems with the toolkit throwing away
    624 	 * eventing during the exclusive grab of the menu popup.  By
    625 	 * looking at the event ourselves we make sure that we can
    626 	 * do the right thing.
    627 	 */
    628 	if (OUR_EVENT(event, EnterNotify)) {
    629 	    DoSpecialEnterNotify(xw, &event.xcrossing);
    630 	} else if (OUR_EVENT(event, LeaveNotify)) {
    631 	    DoSpecialLeaveNotify(xw, &event.xcrossing);
    632 	} else if (event.xany.type == MotionNotify
    633 		   && event.xcrossing.window == XtWindow(xw)) {
    634 	    switch (screen->send_mouse_pos) {
    635 	    case ANY_EVENT_MOUSE:
    636 #if OPT_DEC_LOCATOR
    637 	    case DEC_LOCATOR:
    638 #endif /* OPT_DEC_LOCATOR */
    639 		SendMousePosition(xw, &event);
    640 		xtermShowPointer(xw, True);
    641 		continue;
    642 	    case BTN_EVENT_MOUSE:
    643 		SendMousePosition(xw, &event);
    644 		xtermShowPointer(xw, True);
    645 	    }
    646 	}
    647 
    648 	/*
    649 	 * If the event is interesting (and not a keyboard event), turn the
    650 	 * mouse pointer back on.
    651 	 */
    652 	if (screen->hide_pointer) {
    653 	    if (screen->pointer_mode >= pFocused) {
    654 		switch (event.xany.type) {
    655 		case MotionNotify:
    656 		    xtermShowPointer(xw, True);
    657 		    break;
    658 		}
    659 	    } else {
    660 		switch (event.xany.type) {
    661 		case KeyPress:
    662 		case KeyRelease:
    663 		case ButtonPress:
    664 		case ButtonRelease:
    665 		    /* also these... */
    666 		case Expose:
    667 		case GraphicsExpose:
    668 		case NoExpose:
    669 		case PropertyNotify:
    670 		case ClientMessage:
    671 		    break;
    672 		default:
    673 		    xtermShowPointer(xw, True);
    674 		    break;
    675 		}
    676 	    }
    677 	}
    678 
    679 	if (!event.xany.send_event ||
    680 	    screen->allowSendEvents ||
    681 	    ((event.xany.type != KeyPress) &&
    682 	     (event.xany.type != KeyRelease) &&
    683 	     (event.xany.type != ButtonPress) &&
    684 	     (event.xany.type != ButtonRelease))) {
    685 
    686 	    if (event.xany.type == MappingNotify) {
    687 		XRefreshKeyboardMapping(&(event.xmapping));
    688 		VTInitModifiers(xw);
    689 	    }
    690 	    XtDispatchEvent(&event);
    691 	}
    692     } while (xtermAppPending() & XtIMXEvent);
    693 }
    694 
    695 static Cursor
    696 make_hidden_cursor(XtermWidget xw)
    697 {
    698     TScreen *screen = TScreenOf(xw);
    699     Cursor c;
    700     Display *dpy = screen->display;
    701     XFontStruct *fn;
    702 
    703     static XColor dummy;
    704 
    705     /*
    706      * Prefer nil2 (which is normally available) to "fixed" (which is supposed
    707      * to be "always" available), since it's a smaller glyph in case the
    708      * server insists on drawing _something_.
    709      */
    710     TRACE(("Ask for nil2 font\n"));
    711     if ((fn = xtermLoadQueryFont(xw, "nil2")) == NULL) {
    712 	TRACE(("...Ask for fixed font\n"));
    713 	fn = xtermLoadQueryFont(xw, DEFFONT);
    714     }
    715 
    716     if (fn != NULL) {
    717 	/* a space character seems to work as a cursor (dots are not needed) */
    718 	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
    719 	XFreeFont(dpy, fn);
    720     } else {
    721 	c = None;
    722     }
    723     TRACE(("XCreateGlyphCursor ->%#lx\n", c));
    724     return c;
    725 }
    726 
    727 /*
    728  * Xlib uses Xcursor to customize cursor coloring, which interferes with
    729  * xterm's pointerColor resource.  Work around this by providing our own
    730  * default theme.  Testing seems to show that we only have to provide this
    731  * until the window is initialized.
    732  */
    733 #ifdef HAVE_LIB_XCURSOR
    734 void
    735 init_colored_cursor(Display *dpy)
    736 {
    737     static const char theme[] = "index.theme";
    738     static const char pattern[] = "xtermXXXXXXXX";
    739     char *env = getenv("XCURSOR_THEME");
    740 
    741     xterm_cursor_theme = NULL;
    742     /*
    743      * The environment variable overrides a (possible) resource Xcursor.theme
    744      */
    745     if (IsEmpty(env)) {
    746 	env = XGetDefault(dpy, "Xcursor", "theme");
    747 	TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
    748     } else {
    749 	TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
    750     }
    751 
    752     /*
    753      * If neither found, provide our own default theme.
    754      */
    755     if (IsEmpty(env)) {
    756 	const char *tmp_dir;
    757 	char *filename;
    758 	size_t needed;
    759 
    760 	TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
    761 
    762 	if ((tmp_dir = getenv("TMPDIR")) == NULL) {
    763 	    tmp_dir = P_tmpdir;
    764 	}
    765 	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
    766 	if ((filename = malloc(needed)) != NULL) {
    767 	    sprintf(filename, "%s/%s", tmp_dir, pattern);
    768 
    769 #ifdef HAVE_MKDTEMP
    770 	    xterm_cursor_theme = mkdtemp(filename);
    771 #else
    772 	    if (MakeTemp(filename) != 0
    773 		&& mkdir(filename, 0700) == 0) {
    774 		xterm_cursor_theme = filename;
    775 	    }
    776 #endif
    777 	    if (xterm_cursor_theme != filename)
    778 		free(filename);
    779 	    /*
    780 	     * Actually, Xcursor does what _we_ want just by steering its
    781 	     * search path away from home.  We are setting up the complete
    782 	     * theme just in case the library ever acquires a maintainer.
    783 	     */
    784 	    if (xterm_cursor_theme != NULL) {
    785 		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
    786 		FILE *fp;
    787 
    788 		strcat(leaf, "/");
    789 		strcat(leaf, theme);
    790 
    791 		if ((fp = fopen(xterm_cursor_theme, "w")) != NULL) {
    792 		    fprintf(fp, "[Icon Theme]\n");
    793 		    fclose(fp);
    794 		    *leaf = '\0';
    795 		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
    796 		    *leaf = '/';
    797 
    798 		    TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
    799 			   xterm_cursor_theme));
    800 		    atexit(cleanup_colored_cursor);
    801 		} else {
    802 		    FreeAndNull(xterm_cursor_theme);
    803 		}
    804 	    }
    805 	}
    806     }
    807 }
    808 #endif /* HAVE_LIB_XCURSOR */
    809 
    810 /*
    811  * Once done, discard the file and directory holding it.
    812  */
    813 void
    814 cleanup_colored_cursor(void)
    815 {
    816 #ifdef HAVE_LIB_XCURSOR
    817     if (xterm_cursor_theme != NULL) {
    818 	char *my_path = getenv("XCURSOR_PATH");
    819 	struct stat sb;
    820 	if (!IsEmpty(my_path)
    821 	    && stat(my_path, &sb) == 0
    822 	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
    823 	    unlink(xterm_cursor_theme);
    824 	    rmdir(my_path);
    825 	}
    826 	FreeAndNull(xterm_cursor_theme);
    827     }
    828 #endif /* HAVE_LIB_XCURSOR */
    829 }
    830 
    831 Cursor
    832 make_colored_cursor(unsigned c_index,		/* index into font */
    833 		    unsigned long fg,	/* pixel value */
    834 		    unsigned long bg)	/* pixel value */
    835 {
    836     TScreen *screen = TScreenOf(term);
    837     Cursor c = None;
    838     Display *dpy = screen->display;
    839 
    840     TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
    841     if (!IsEmpty(screen->cursor_font_name)) {
    842 	static XTermFonts myFont;
    843 
    844 	/* adapted from XCreateFontCursor(), which hardcodes the font name */
    845 	TRACE(("loading cursor from alternate cursor font\n"));
    846 	myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
    847 	if (myFont.fs != NULL) {
    848 	    if (!xtermMissingChar(c_index, &myFont)
    849 		&& !xtermMissingChar(c_index + 1, &myFont)) {
    850 #define DATA(c) { 0UL, c, c, c, 0, 0 }
    851 		static XColor foreground = DATA(0);
    852 		static XColor background = DATA(65535);
    853 #undef DATA
    854 
    855 		/*
    856 		 * Cursor fonts follow each shape glyph with a mask glyph; so
    857 		 * that character position 0 contains a shape, 1 the mask for
    858 		 * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
    859 		 * contains defined names for each shape.
    860 		 */
    861 		c = XCreateGlyphCursor(dpy,
    862 				       myFont.fs->fid,	/* source_font */
    863 				       myFont.fs->fid,	/* mask_font */
    864 				       c_index + 0,	/* source_char */
    865 				       c_index + 1,	/* mask_char */
    866 				       &foreground,
    867 				       &background);
    868 	    }
    869 	    XFreeFont(dpy, myFont.fs);
    870 	}
    871 	if (c == None) {
    872 	    xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
    873 			 c_index, screen->cursor_font_name);
    874 	}
    875     }
    876     if (c == None)
    877 	c = XCreateFontCursor(dpy, c_index);
    878 
    879     if (c != None) {
    880 	recolor_cursor(screen, c, fg, bg);
    881     }
    882     return c;
    883 }
    884 
    885 /* adapted from <X11/cursorfont.h> */
    886 static int
    887 LookupCursorShape(const char *name)
    888 {
    889 #define DATA(name) { XC_##name, #name }
    890     static struct {
    891 	int code;
    892 	const char name[25];
    893     } table[] = {
    894 	DATA(X_cursor),
    895 	    DATA(arrow),
    896 	    DATA(based_arrow_down),
    897 	    DATA(based_arrow_up),
    898 	    DATA(boat),
    899 	    DATA(bogosity),
    900 	    DATA(bottom_left_corner),
    901 	    DATA(bottom_right_corner),
    902 	    DATA(bottom_side),
    903 	    DATA(bottom_tee),
    904 	    DATA(box_spiral),
    905 	    DATA(center_ptr),
    906 	    DATA(circle),
    907 	    DATA(clock),
    908 	    DATA(coffee_mug),
    909 	    DATA(cross),
    910 	    DATA(cross_reverse),
    911 	    DATA(crosshair),
    912 	    DATA(diamond_cross),
    913 	    DATA(dot),
    914 	    DATA(dotbox),
    915 	    DATA(double_arrow),
    916 	    DATA(draft_large),
    917 	    DATA(draft_small),
    918 	    DATA(draped_box),
    919 	    DATA(exchange),
    920 	    DATA(fleur),
    921 	    DATA(gobbler),
    922 	    DATA(gumby),
    923 	    DATA(hand1),
    924 	    DATA(hand2),
    925 	    DATA(heart),
    926 	    DATA(icon),
    927 	    DATA(iron_cross),
    928 	    DATA(left_ptr),
    929 	    DATA(left_side),
    930 	    DATA(left_tee),
    931 	    DATA(leftbutton),
    932 	    DATA(ll_angle),
    933 	    DATA(lr_angle),
    934 	    DATA(man),
    935 	    DATA(middlebutton),
    936 	    DATA(mouse),
    937 	    DATA(pencil),
    938 	    DATA(pirate),
    939 	    DATA(plus),
    940 	    DATA(question_arrow),
    941 	    DATA(right_ptr),
    942 	    DATA(right_side),
    943 	    DATA(right_tee),
    944 	    DATA(rightbutton),
    945 	    DATA(rtl_logo),
    946 	    DATA(sailboat),
    947 	    DATA(sb_down_arrow),
    948 	    DATA(sb_h_double_arrow),
    949 	    DATA(sb_left_arrow),
    950 	    DATA(sb_right_arrow),
    951 	    DATA(sb_up_arrow),
    952 	    DATA(sb_v_double_arrow),
    953 	    DATA(shuttle),
    954 	    DATA(sizing),
    955 	    DATA(spider),
    956 	    DATA(spraycan),
    957 	    DATA(star),
    958 	    DATA(target),
    959 	    DATA(tcross),
    960 	    DATA(top_left_arrow),
    961 	    DATA(top_left_corner),
    962 	    DATA(top_right_corner),
    963 	    DATA(top_side),
    964 	    DATA(top_tee),
    965 	    DATA(trek),
    966 	    DATA(ul_angle),
    967 	    DATA(umbrella),
    968 	    DATA(ur_angle),
    969 	    DATA(watch),
    970 	    DATA(xterm),
    971     };
    972 #undef DATA
    973     Cardinal j;
    974     int result = -1;
    975     if (!IsEmpty(name)) {
    976 	for (j = 0; j < XtNumber(table); ++j) {
    977 	    if (!strcmp(name, table[j].name)) {
    978 		result = table[j].code;
    979 		break;
    980 	    }
    981 	}
    982     }
    983     return result;
    984 }
    985 
    986 void
    987 xtermSetupPointer(XtermWidget xw, const char *theShape)
    988 {
    989     TScreen *screen = TScreenOf(xw);
    990     unsigned shape = XC_xterm;
    991     int other = LookupCursorShape(theShape);
    992     unsigned which;
    993 
    994     if (other >= 0 && other < XC_num_glyphs)
    995 	shape = (unsigned) other;
    996 
    997     TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
    998 	   NonNull(theShape)));
    999 
   1000     which = (unsigned) (shape / 2);
   1001     if (xw->work.pointer_cursors[which] == None) {
   1002 	TRACE(("creating text pointer cursor from shape %d\n", shape));
   1003 	xw->work.pointer_cursors[which] =
   1004 	    make_colored_cursor(shape,
   1005 				T_COLOR(screen, MOUSE_FG),
   1006 				T_COLOR(screen, MOUSE_BG));
   1007     } else {
   1008 	TRACE(("updating text pointer cursor for shape %d\n", shape));
   1009 	recolor_cursor(screen,
   1010 		       screen->pointer_cursor,
   1011 		       T_COLOR(screen, MOUSE_FG),
   1012 		       T_COLOR(screen, MOUSE_BG));
   1013     }
   1014     if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
   1015 	screen->pointer_cursor = xw->work.pointer_cursors[which];
   1016 	TRACE(("defining text pointer cursor with shape %d\n", shape));
   1017 	XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
   1018 	if (XtIsRealized((Widget) xw)) {
   1019 	    /* briefly override pointerMode after changing the pointer */
   1020 	    if (screen->pointer_mode != pNever)
   1021 		screen->hide_pointer = True;
   1022 	    xtermShowPointer(xw, True);
   1023 	}
   1024     }
   1025 }
   1026 
   1027 /* ARGSUSED */
   1028 void
   1029 HandleKeyPressed(Widget w GCC_UNUSED,
   1030 		 XEvent *event,
   1031 		 String *params GCC_UNUSED,
   1032 		 Cardinal *nparams GCC_UNUSED)
   1033 {
   1034     TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
   1035     Input(term, &event->xkey, False);
   1036 }
   1037 
   1038 /* ARGSUSED */
   1039 void
   1040 HandleEightBitKeyPressed(Widget w GCC_UNUSED,
   1041 			 XEvent *event,
   1042 			 String *params GCC_UNUSED,
   1043 			 Cardinal *nparams GCC_UNUSED)
   1044 {
   1045     TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
   1046     Input(term, &event->xkey, True);
   1047 }
   1048 
   1049 /* ARGSUSED */
   1050 void
   1051 HandleStringEvent(Widget w GCC_UNUSED,
   1052 		  XEvent *event GCC_UNUSED,
   1053 		  String *params,
   1054 		  Cardinal *nparams)
   1055 {
   1056 
   1057     if (*nparams != 1)
   1058 	return;
   1059 
   1060     if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
   1061 	const char *abcdef = "ABCDEF";
   1062 	const char *xxxxxx;
   1063 	Char c;
   1064 	UString p;
   1065 	unsigned value = 0;
   1066 
   1067 	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
   1068 	     '\0'; p++) {
   1069 	    value *= 16;
   1070 	    if (c >= '0' && c <= '9')
   1071 		value += (unsigned) (c - '0');
   1072 	    else if ((xxxxxx = (strchr) (abcdef, c)) != NULL)
   1073 		value += (unsigned) (xxxxxx - abcdef) + 10;
   1074 	    else
   1075 		break;
   1076 	}
   1077 	if (c == '\0') {
   1078 	    Char hexval[2];
   1079 	    hexval[0] = (Char) value;
   1080 	    hexval[1] = 0;
   1081 	    StringInput(term, hexval, (size_t) 1);
   1082 	}
   1083     } else {
   1084 	StringInput(term, (const Char *) *params, strlen(*params));
   1085     }
   1086 }
   1087 
   1088 #if OPT_EXEC_XTERM
   1089 
   1090 #ifndef PROCFS_ROOT
   1091 #define PROCFS_ROOT "/proc"
   1092 #endif
   1093 
   1094 /*
   1095  * Determine the current working directory of the child so that we can
   1096  * spawn a new terminal in the same directory.
   1097  *
   1098  * If we cannot get the CWD of the child, just use our own.
   1099  */
   1100 char *
   1101 ProcGetCWD(pid_t pid)
   1102 {
   1103     char *child_cwd = NULL;
   1104 
   1105     if (pid) {
   1106 	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
   1107 	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
   1108 	child_cwd = Readlink(child_cwd_link);
   1109     }
   1110     return child_cwd;
   1111 }
   1112 
   1113 /* ARGSUSED */
   1114 void
   1115 HandleSpawnTerminal(Widget w GCC_UNUSED,
   1116 		    XEvent *event GCC_UNUSED,
   1117 		    String *params,
   1118 		    Cardinal *nparams)
   1119 {
   1120     TScreen *screen = TScreenOf(term);
   1121     char *child_cwd = NULL;
   1122     char *child_exe;
   1123     pid_t pid;
   1124 
   1125     /*
   1126      * Try to find the actual program which is running in the child process.
   1127      * This works for Linux.  If we cannot find the program, fall back to the
   1128      * xterm program (which is usually adequate).  Give up if we are given only
   1129      * a relative path to xterm, since that would not always match $PATH.
   1130      */
   1131     child_exe = Readlink(PROCFS_ROOT "/self/exe");
   1132     if (!child_exe) {
   1133 	if (strncmp(ProgramName, "./", (size_t) 2)
   1134 	    && strncmp(ProgramName, "../", (size_t) 3)) {
   1135 	    child_exe = xtermFindShell(ProgramName, True);
   1136 	} else {
   1137 	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
   1138 	}
   1139 	if (child_exe == 0)
   1140 	    return;
   1141     }
   1142 
   1143     child_cwd = ProcGetCWD(screen->pid);
   1144 
   1145     /* The reaper will take care of cleaning up the child */
   1146     pid = fork();
   1147     if (pid == -1) {
   1148 	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
   1149     } else if (!pid) {
   1150 	/* We are the child */
   1151 	if (child_cwd) {
   1152 	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
   1153 	}
   1154 
   1155 	if (setuid(screen->uid) == -1
   1156 	    || setgid(screen->gid) == -1) {
   1157 	    xtermWarning("Cannot reset uid/gid\n");
   1158 	} else {
   1159 	    unsigned myargc = *nparams + 1;
   1160 	    char **myargv = TypeMallocN(char *, myargc + 1);
   1161 
   1162 	    if (myargv != 0) {
   1163 		unsigned n = 0;
   1164 
   1165 		myargv[n++] = child_exe;
   1166 
   1167 		while (n < myargc) {
   1168 		    myargv[n++] = (char *) *params++;
   1169 		}
   1170 
   1171 		myargv[n] = 0;
   1172 		execv(child_exe, myargv);
   1173 	    }
   1174 
   1175 	    /* If we get here, we've failed */
   1176 	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
   1177 	}
   1178 	_exit(0);
   1179     }
   1180 
   1181     /* We are the parent; clean up */
   1182     free(child_cwd);
   1183     free(child_exe);
   1184 }
   1185 #endif /* OPT_EXEC_XTERM */
   1186 
   1187 /*
   1188  * Rather than sending characters to the host, put them directly into our
   1189  * input queue.  That lets a user have access to any of the control sequences
   1190  * for a key binding.  This is the equivalent of local function key support.
   1191  *
   1192  * NOTE:  This code does not support the hexadecimal kludge used in
   1193  * HandleStringEvent because it prevents us from sending an arbitrary string
   1194  * (but it appears in a lot of examples - so we are stuck with it).  The
   1195  * standard string converter does recognize "\" for newline ("\n") and for
   1196  * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
   1197  * without a specialized converter.  (Don't try to use \000, though).
   1198  */
   1199 /* ARGSUSED */
   1200 void
   1201 HandleInterpret(Widget w GCC_UNUSED,
   1202 		XEvent *event GCC_UNUSED,
   1203 		String *params,
   1204 		Cardinal *param_count)
   1205 {
   1206     if (*param_count == 1) {
   1207 	const char *value = params[0];
   1208 	size_t need = strlen(value);
   1209 	size_t used = (size_t) (VTbuffer->next - VTbuffer->buffer);
   1210 	size_t have = (size_t) (VTbuffer->last - VTbuffer->buffer);
   1211 
   1212 	if ((have - used) + need < (size_t) BUF_SIZE) {
   1213 
   1214 	    fillPtyData(term, VTbuffer, value, strlen(value));
   1215 
   1216 	    TRACE(("Interpret %s\n", value));
   1217 	    VTbuffer->update++;
   1218 	}
   1219     }
   1220 }
   1221 
   1222 /*ARGSUSED*/
   1223 void
   1224 HandleEnterWindow(Widget w GCC_UNUSED,
   1225 		  XtPointer eventdata GCC_UNUSED,
   1226 		  XEvent *event GCC_UNUSED,
   1227 		  Boolean *cont GCC_UNUSED)
   1228 {
   1229     /* NOP since we handled it above */
   1230     TRACE(("HandleEnterWindow ignored\n"));
   1231     TRACE_FOCUS(w, event);
   1232 }
   1233 
   1234 /*ARGSUSED*/
   1235 void
   1236 HandleLeaveWindow(Widget w GCC_UNUSED,
   1237 		  XtPointer eventdata GCC_UNUSED,
   1238 		  XEvent *event GCC_UNUSED,
   1239 		  Boolean *cont GCC_UNUSED)
   1240 {
   1241     /* NOP since we handled it above */
   1242     TRACE(("HandleLeaveWindow ignored\n"));
   1243     TRACE_FOCUS(w, event);
   1244 }
   1245 
   1246 /*ARGSUSED*/
   1247 void
   1248 HandleFocusChange(Widget w GCC_UNUSED,
   1249 		  XtPointer eventdata GCC_UNUSED,
   1250 		  XEvent *ev,
   1251 		  Boolean *cont GCC_UNUSED)
   1252 {
   1253     XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
   1254     XtermWidget xw = term;
   1255     TScreen *screen = TScreenOf(xw);
   1256 
   1257     TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
   1258 	   visibleEventType(event->type),
   1259 	   visibleNotifyMode(event->mode),
   1260 	   visibleNotifyDetail(event->detail)));
   1261     TRACE_FOCUS(xw, event);
   1262 
   1263     if (screen->quiet_grab
   1264 	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
   1265 	/* EMPTY */ ;
   1266     } else if (event->type == FocusIn) {
   1267 	if (event->detail != NotifyPointer) {
   1268 	    setXUrgency(xw, False);
   1269 	}
   1270 
   1271 	/*
   1272 	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
   1273 	 * one of our windows.  Use this to reset a case where one xterm is
   1274 	 * partly obscuring another, and X gets (us) confused about whether the
   1275 	 * pointer was in the window.  In particular, this can happen if the
   1276 	 * user is resizing the obscuring window, causing some events to not be
   1277 	 * delivered to the obscured window.
   1278 	 */
   1279 	if (event->detail == NotifyNonlinear
   1280 	    && (screen->select & INWINDOW) != 0) {
   1281 	    unselectwindow(xw, INWINDOW);
   1282 	}
   1283 	selectwindow(xw,
   1284 		     ((event->detail == NotifyPointer)
   1285 		      ? INWINDOW
   1286 		      : FOCUS));
   1287 	SendFocusButton(xw, event);
   1288     } else {
   1289 #if OPT_FOCUS_EVENT
   1290 	if (event->type == FocusOut) {
   1291 	    SendFocusButton(xw, event);
   1292 	}
   1293 #endif
   1294 	/*
   1295 	 * XGrabKeyboard() will generate NotifyGrab event that we want to
   1296 	 * ignore.
   1297 	 */
   1298 	if (event->mode != NotifyGrab) {
   1299 	    unselectwindow(xw,
   1300 			   ((event->detail == NotifyPointer)
   1301 			    ? INWINDOW
   1302 			    : FOCUS));
   1303 	}
   1304 	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
   1305 	    Bell(xw, XkbBI_Info, 100);
   1306 	    ReverseVideo(xw);
   1307 	    screen->grabbedKbd = False;
   1308 	    update_securekbd();
   1309 	}
   1310     }
   1311 }
   1312 
   1313 static long lastBellTime;	/* in milliseconds */
   1314 
   1315 #if defined(HAVE_XKB_BELL_EXT)
   1316 static Atom
   1317 AtomBell(XtermWidget xw, int which)
   1318 {
   1319 #define DATA(name) { XkbBI_##name, XkbBN_##name }
   1320     static struct {
   1321 	int value;
   1322 	const char *name;
   1323     } table[] = {
   1324 	DATA(Info),
   1325 	    DATA(MarginBell),
   1326 	    DATA(MinorError),
   1327 	    DATA(TerminalBell)
   1328     };
   1329 #undef DATA
   1330     Cardinal n;
   1331     Atom result = None;
   1332 
   1333     for (n = 0; n < XtNumber(table); ++n) {
   1334 	if (table[n].value == which) {
   1335 	    result = CachedInternAtom(XtDisplay(xw), table[n].name);
   1336 	    break;
   1337 	}
   1338     }
   1339     return result;
   1340 }
   1341 #endif
   1342 
   1343 void
   1344 xtermBell(XtermWidget xw, int which, int percent)
   1345 {
   1346     TScreen *screen = TScreenOf(xw);
   1347 #if defined(HAVE_XKB_BELL_EXT)
   1348     Atom tony = AtomBell(xw, which);
   1349 #endif
   1350 
   1351     switch (which) {
   1352     case XkbBI_Info:
   1353     case XkbBI_MinorError:
   1354     case XkbBI_MajorError:
   1355     case XkbBI_TerminalBell:
   1356 	switch (screen->warningVolume) {
   1357 	case bvOff:
   1358 	    percent = -100;
   1359 	    break;
   1360 	case bvLow:
   1361 	    break;
   1362 	case bvHigh:
   1363 	    percent = 100;
   1364 	    break;
   1365 	}
   1366 	break;
   1367     case XkbBI_MarginBell:
   1368 	switch (screen->marginVolume) {
   1369 	case bvOff:
   1370 	    percent = -100;
   1371 	    break;
   1372 	case bvLow:
   1373 	    break;
   1374 	case bvHigh:
   1375 	    percent = 100;
   1376 	    break;
   1377 	}
   1378 	break;
   1379     default:
   1380 	break;
   1381     }
   1382 
   1383 #if defined(HAVE_XKB_BELL_EXT)
   1384     if (tony != None) {
   1385 	XkbBell(screen->display, VShellWindow(xw), percent, tony);
   1386     } else
   1387 #endif
   1388 	XBell(screen->display, percent);
   1389 }
   1390 
   1391 void
   1392 Bell(XtermWidget xw, int which, int percent)
   1393 {
   1394     TScreen *screen = TScreenOf(xw);
   1395     struct timeval curtime;
   1396 
   1397     TRACE(("BELL %d %d%%\n", which, percent));
   1398     if (!XtIsRealized((Widget) xw)) {
   1399 	return;
   1400     }
   1401 
   1402     setXUrgency(xw, True);
   1403 
   1404     /* has enough time gone by that we are allowed to ring
   1405        the bell again? */
   1406     if (screen->bellSuppressTime) {
   1407 	long now_msecs;
   1408 
   1409 	if (screen->bellInProgress) {
   1410 	    do_xevents(xw);
   1411 	    if (screen->bellInProgress) {	/* even after new events? */
   1412 		return;
   1413 	    }
   1414 	}
   1415 	X_GETTIMEOFDAY(&curtime);
   1416 	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
   1417 	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
   1418 	    now_msecs - lastBellTime < screen->bellSuppressTime) {
   1419 	    return;
   1420 	}
   1421 	lastBellTime = now_msecs;
   1422     }
   1423 
   1424     if (screen->visualbell) {
   1425 	VisualBell();
   1426     } else {
   1427 	xtermBell(xw, which, percent);
   1428     }
   1429 
   1430     if (screen->poponbell)
   1431 	XRaiseWindow(screen->display, VShellWindow(xw));
   1432 
   1433     if (screen->bellSuppressTime) {
   1434 	/* now we change a property and wait for the notify event to come
   1435 	   back.  If the server is suspending operations while the bell
   1436 	   is being emitted (problematic for audio bell), this lets us
   1437 	   know when the previous bell has finished */
   1438 	Widget w = CURRENT_EMU();
   1439 	XChangeProperty(XtDisplay(w), XtWindow(w),
   1440 			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
   1441 	screen->bellInProgress = True;
   1442     }
   1443 }
   1444 
   1445 static void
   1446 flashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
   1447 {
   1448     int y = 0;
   1449     int x = 0;
   1450 
   1451     if (screen->flash_line) {
   1452 	y = CursorY(screen, screen->cur_row);
   1453 	height = (unsigned) FontHeight(screen);
   1454     }
   1455     XFillRectangle(screen->display, window, visualGC, x, y, width, height);
   1456     XFlush(screen->display);
   1457     Sleep(VB_DELAY);
   1458     XFillRectangle(screen->display, window, visualGC, x, y, width, height);
   1459 }
   1460 
   1461 void
   1462 VisualBell(void)
   1463 {
   1464     XtermWidget xw = term;
   1465     TScreen *screen = TScreenOf(xw);
   1466 
   1467     if (VB_DELAY > 0) {
   1468 	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
   1469 			  T_COLOR(screen, TEXT_BG));
   1470 	XGCValues gcval;
   1471 	GC visualGC;
   1472 
   1473 	gcval.function = GXxor;
   1474 	gcval.foreground = xorPixel;
   1475 	visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
   1476 #if OPT_TEK4014
   1477 	if (TEK4014_ACTIVE(xw)) {
   1478 	    TekScreen *tekscr = TekScreenOf(tekWidget);
   1479 	    flashWindow(screen, TWindow(tekscr), visualGC,
   1480 			TFullWidth(tekscr),
   1481 			TFullHeight(tekscr));
   1482 	} else
   1483 #endif
   1484 	{
   1485 	    flashWindow(screen, VWindow(screen), visualGC,
   1486 			FullWidth(screen),
   1487 			FullHeight(screen));
   1488 	}
   1489 	XtReleaseGC((Widget) xw, visualGC);
   1490     }
   1491 }
   1492 
   1493 /* ARGSUSED */
   1494 void
   1495 HandleBellPropertyChange(Widget w GCC_UNUSED,
   1496 			 XtPointer data GCC_UNUSED,
   1497 			 XEvent *ev,
   1498 			 Boolean *more GCC_UNUSED)
   1499 {
   1500     TScreen *screen = TScreenOf(term);
   1501 
   1502     if (ev->xproperty.atom == XA_NOTICE) {
   1503 	screen->bellInProgress = False;
   1504     }
   1505 }
   1506 
   1507 void
   1508 xtermWarning(const char *fmt, ...)
   1509 {
   1510     int save_err = errno;
   1511     va_list ap;
   1512 
   1513     fflush(stdout);
   1514 
   1515 #if OPT_TRACE
   1516     va_start(ap, fmt);
   1517     Trace("xtermWarning: ");
   1518     TraceVA(fmt, ap);
   1519     va_end(ap);
   1520 #endif
   1521 
   1522     fprintf(stderr, "%s: ", ProgramName);
   1523     va_start(ap, fmt);
   1524     vfprintf(stderr, fmt, ap);
   1525     (void) fflush(stderr);
   1526 
   1527     va_end(ap);
   1528     errno = save_err;
   1529 }
   1530 
   1531 void
   1532 xtermPerror(const char *fmt, ...)
   1533 {
   1534     int save_err = errno;
   1535     const char *msg = strerror(errno);
   1536     va_list ap;
   1537 
   1538     fflush(stdout);
   1539 
   1540 #if OPT_TRACE
   1541     va_start(ap, fmt);
   1542     Trace("xtermPerror: ");
   1543     TraceVA(fmt, ap);
   1544     va_end(ap);
   1545 #endif
   1546 
   1547     fprintf(stderr, "%s: ", ProgramName);
   1548     va_start(ap, fmt);
   1549     vfprintf(stderr, fmt, ap);
   1550     fprintf(stderr, ": %s\n", msg);
   1551     (void) fflush(stderr);
   1552 
   1553     va_end(ap);
   1554     errno = save_err;
   1555 }
   1556 
   1557 Window
   1558 WMFrameWindow(XtermWidget xw)
   1559 {
   1560     Window win_root, win_current, *children;
   1561     Window win_parent = 0;
   1562     unsigned int nchildren;
   1563 
   1564     win_current = XtWindow(xw);
   1565 
   1566     /* find the parent which is child of root */
   1567     do {
   1568 	if (win_parent)
   1569 	    win_current = win_parent;
   1570 	XQueryTree(TScreenOf(xw)->display,
   1571 		   win_current,
   1572 		   &win_root,
   1573 		   &win_parent,
   1574 		   &children,
   1575 		   &nchildren);
   1576 	XFree(children);
   1577     } while (win_root != win_parent);
   1578 
   1579     return win_current;
   1580 }
   1581 
   1582 #if OPT_DABBREV
   1583 /*
   1584  * The following code implements `dynamic abbreviation' expansion a la
   1585  * Emacs.  It looks in the preceding visible screen and its scrollback
   1586  * to find expansions of a typed word.  It compares consecutive
   1587  * expansions and ignores one of them if they are identical.
   1588  * (Tomasz J. Cholewo, t.cholewo (at) ieee.org)
   1589  */
   1590 
   1591 #define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
   1592 
   1593 static int
   1594 dabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
   1595 {
   1596     int result = -1;
   1597     int firstLine = -(screen->savedlines);
   1598 
   1599     *ld = getLineData(screen, cell->row);
   1600     while (cell->row >= firstLine) {
   1601 	if (--(cell->col) >= 0) {
   1602 	    result = (int) (*ld)->charData[cell->col];
   1603 	    break;
   1604 	}
   1605 	if (--(cell->row) < firstLine)
   1606 	    break;		/* ...there is no previous line */
   1607 	*ld = getLineData(screen, cell->row);
   1608 	cell->col = MaxCols(screen);
   1609 	if (!LineTstWrapped(*ld)) {
   1610 	    result = ' ';	/* treat lines as separate */
   1611 	    break;
   1612 	}
   1613     }
   1614     return result;
   1615 }
   1616 
   1617 static char *
   1618 dabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
   1619 {
   1620     TScreen *screen = TScreenOf(xw);
   1621     char *abword;
   1622     int c;
   1623     char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
   1624     char *result = NULL;
   1625 
   1626     abword = ab_end;
   1627     *abword = '\0';		/* end of string marker */
   1628 
   1629     while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
   1630 	   IS_WORD_CONSTITUENT(c)) {
   1631 	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
   1632 	    *(--abword) = (char) c;
   1633     }
   1634 
   1635     if (c >= 0) {
   1636 	result = abword;
   1637     } else if (abword != ab_end) {
   1638 	result = abword;
   1639     }
   1640 
   1641     if (result != NULL) {
   1642 	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
   1643 	       !IS_WORD_CONSTITUENT(c)) {
   1644 	    ;			/* skip preceding spaces */
   1645 	}
   1646 	(cell->col)++;		/* can be | > screen->max_col| */
   1647     }
   1648     return result;
   1649 }
   1650 
   1651 static int
   1652 dabbrev_expand(XtermWidget xw)
   1653 {
   1654     TScreen *screen = TScreenOf(xw);
   1655     int pty = screen->respond;	/* file descriptor of pty */
   1656 
   1657     static CELL cell;
   1658     static char *dabbrev_hint = NULL, *lastexpansion = NULL;
   1659     static unsigned int expansions;
   1660 
   1661     char *expansion;
   1662     size_t hint_len;
   1663     int result = 0;
   1664     LineData *ld;
   1665 
   1666     if (!screen->dabbrev_working) {	/* initialize */
   1667 	expansions = 0;
   1668 	cell.col = screen->cur_col;
   1669 	cell.row = screen->cur_row;
   1670 
   1671 	free(dabbrev_hint);
   1672 
   1673 	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != NULL) {
   1674 
   1675 	    free(lastexpansion);
   1676 
   1677 	    if ((lastexpansion = strdup(dabbrev_hint)) != NULL) {
   1678 
   1679 		/* make own copy */
   1680 		if ((dabbrev_hint = strdup(dabbrev_hint)) != NULL) {
   1681 		    screen->dabbrev_working = True;
   1682 		    /* we are in the middle of dabbrev process */
   1683 		}
   1684 	    } else {
   1685 		return result;
   1686 	    }
   1687 	} else {
   1688 	    return result;
   1689 	}
   1690 	if (!screen->dabbrev_working) {
   1691 	    free(lastexpansion);
   1692 	    lastexpansion = NULL;
   1693 	    return result;
   1694 	}
   1695     }
   1696 
   1697     if (dabbrev_hint == NULL)
   1698 	return result;
   1699 
   1700     hint_len = strlen(dabbrev_hint);
   1701     for (;;) {
   1702 	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == NULL) {
   1703 	    if (expansions >= 2) {
   1704 		expansions = 0;
   1705 		cell.col = screen->cur_col;
   1706 		cell.row = screen->cur_row;
   1707 		continue;
   1708 	    }
   1709 	    break;
   1710 	}
   1711 	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
   1712 	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
   1713 	    strcmp(expansion, lastexpansion))	/* different from previous */
   1714 	    break;
   1715     }
   1716 
   1717     if (expansion != NULL) {
   1718 	Char *copybuffer;
   1719 	size_t del_cnt = strlen(lastexpansion) - hint_len;
   1720 	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
   1721 
   1722 	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != NULL) {
   1723 	    /* delete previous expansion */
   1724 	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
   1725 	    memmove(copybuffer + del_cnt,
   1726 		    expansion + hint_len,
   1727 		    strlen(expansion) - hint_len);
   1728 	    v_write(pty, copybuffer, buf_cnt);
   1729 	    /* v_write() just reset our flag */
   1730 	    screen->dabbrev_working = True;
   1731 	    free(copybuffer);
   1732 
   1733 	    free(lastexpansion);
   1734 
   1735 	    if ((lastexpansion = strdup(expansion)) != NULL) {
   1736 		result = 1;
   1737 		expansions++;
   1738 	    }
   1739 	}
   1740     }
   1741 
   1742     return result;
   1743 }
   1744 
   1745 /*ARGSUSED*/
   1746 void
   1747 HandleDabbrevExpand(Widget w,
   1748 		    XEvent *event GCC_UNUSED,
   1749 		    String *params GCC_UNUSED,
   1750 		    Cardinal *nparams GCC_UNUSED)
   1751 {
   1752     XtermWidget xw;
   1753 
   1754     TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
   1755     if ((xw = getXtermWidget(w)) != NULL) {
   1756 	if (!dabbrev_expand(xw))
   1757 	    Bell(xw, XkbBI_TerminalBell, 0);
   1758     }
   1759 }
   1760 #endif /* OPT_DABBREV */
   1761 
   1762 void
   1763 xtermDeiconify(XtermWidget xw)
   1764 {
   1765     TScreen *screen = TScreenOf(xw);
   1766     Display *dpy = screen->display;
   1767     Window target = VShellWindow(xw);
   1768     XEvent e;
   1769     Atom atom_state = CachedInternAtom(dpy, "_NET_ACTIVE_WINDOW");
   1770 
   1771     if (xtermIsIconified(xw)) {
   1772 	TRACE(("...de-iconify window %#lx\n", target));
   1773 	ResetHiddenHint(xw);
   1774 	XMapWindow(dpy, target);
   1775 
   1776 	memset(&e, 0, sizeof(e));
   1777 	e.xclient.type = ClientMessage;
   1778 	e.xclient.message_type = atom_state;
   1779 	e.xclient.display = dpy;
   1780 	e.xclient.window = target;
   1781 	e.xclient.format = 32;
   1782 	e.xclient.data.l[0] = 1;
   1783 	e.xclient.data.l[1] = CurrentTime;
   1784 
   1785 	XSendEvent(dpy, DefaultRootWindow(dpy), False,
   1786 		   SubstructureRedirectMask | SubstructureNotifyMask, &e);
   1787 	xevents(xw);
   1788     }
   1789 }
   1790 
   1791 void
   1792 xtermIconify(XtermWidget xw)
   1793 {
   1794     TScreen *screen = TScreenOf(xw);
   1795     Window target = VShellWindow(xw);
   1796 
   1797     if (!xtermIsIconified(xw)) {
   1798 	TRACE(("...iconify window %#lx\n", target));
   1799 	XIconifyWindow(screen->display,
   1800 		       target,
   1801 		       DefaultScreen(screen->display));
   1802 	xevents(xw);
   1803     }
   1804 }
   1805 
   1806 Boolean
   1807 xtermIsIconified(XtermWidget xw)
   1808 {
   1809     XWindowAttributes win_attrs;
   1810     TScreen *screen = TScreenOf(xw);
   1811     Window target = VShellWindow(xw);
   1812     Display *dpy = screen->display;
   1813     Boolean result = False;
   1814 
   1815     if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
   1816 	Atom actual_return_type;
   1817 	int actual_format_return = 0;
   1818 	unsigned long nitems_return = 0;
   1819 	unsigned long bytes_after_return = 0;
   1820 	unsigned char *prop_return = NULL;
   1821 	long long_length = 1024;
   1822 	Atom requested_type = XA_ATOM;
   1823 	Atom is_hidden = CachedInternAtom(dpy, "_NET_WM_STATE_HIDDEN");
   1824 	Atom wm_state = CachedInternAtom(dpy, "_NET_WM_STATE");
   1825 
   1826 	/* this works with non-EWMH */
   1827 	result = (win_attrs.map_state != IsViewable) ? True : False;
   1828 
   1829 	/* this is a convention used by some EWMH applications */
   1830 	if (xtermGetWinProp(dpy,
   1831 			    target,
   1832 			    wm_state,
   1833 			    0L,
   1834 			    long_length,
   1835 			    requested_type,
   1836 			    &actual_return_type,
   1837 			    &actual_format_return,
   1838 			    &nitems_return,
   1839 			    &bytes_after_return,
   1840 			    &prop_return)) {
   1841 	    if (prop_return != NULL
   1842 		&& actual_return_type == requested_type
   1843 		&& actual_format_return == 32) {
   1844 		unsigned long n;
   1845 		for (n = 0; n < nitems_return; ++n) {
   1846 		    unsigned long check = (((unsigned long *)
   1847 					    (void *) prop_return)[n]);
   1848 		    if (check == is_hidden) {
   1849 			result = True;
   1850 			break;
   1851 		    }
   1852 		}
   1853 		XFree(prop_return);
   1854 	    }
   1855 	}
   1856     }
   1857     TRACE(("...window %#lx is%s iconified\n",
   1858 	   target,
   1859 	   result ? "" : " not"));
   1860     return result;
   1861 }
   1862 
   1863 #if OPT_MAXIMIZE
   1864 /*ARGSUSED*/
   1865 void
   1866 HandleDeIconify(Widget w,
   1867 		XEvent *event GCC_UNUSED,
   1868 		String *params GCC_UNUSED,
   1869 		Cardinal *nparams GCC_UNUSED)
   1870 {
   1871     XtermWidget xw;
   1872 
   1873     if ((xw = getXtermWidget(w)) != NULL) {
   1874 	xtermDeiconify(xw);
   1875     }
   1876 }
   1877 
   1878 /*ARGSUSED*/
   1879 void
   1880 HandleIconify(Widget w,
   1881 	      XEvent *event GCC_UNUSED,
   1882 	      String *params GCC_UNUSED,
   1883 	      Cardinal *nparams GCC_UNUSED)
   1884 {
   1885     XtermWidget xw;
   1886 
   1887     if ((xw = getXtermWidget(w)) != NULL) {
   1888 	xtermIconify(xw);
   1889     }
   1890 }
   1891 
   1892 int
   1893 QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
   1894 {
   1895     TScreen *screen = TScreenOf(xw);
   1896     XSizeHints hints;
   1897     long supp = 0;
   1898     Window root_win;
   1899     int root_x = -1;		/* saved co-ordinates */
   1900     int root_y = -1;
   1901     unsigned root_border;
   1902     unsigned root_depth;
   1903     int code;
   1904 
   1905     if (XGetGeometry(screen->display,
   1906 		     RootWindowOfScreen(XtScreen(xw)),
   1907 		     &root_win,
   1908 		     &root_x,
   1909 		     &root_y,
   1910 		     width,
   1911 		     height,
   1912 		     &root_border,
   1913 		     &root_depth)) {
   1914 	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
   1915 	       root_x,
   1916 	       root_y,
   1917 	       *width,
   1918 	       *height,
   1919 	       root_border));
   1920 
   1921 	*width -= (root_border * 2);
   1922 	*height -= (root_border * 2);
   1923 
   1924 	hints.flags = PMaxSize;
   1925 	if (XGetWMNormalHints(screen->display,
   1926 			      VShellWindow(xw),
   1927 			      &hints,
   1928 			      &supp)
   1929 	    && (hints.flags & PMaxSize) != 0) {
   1930 
   1931 	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
   1932 		   hints.max_width,
   1933 		   hints.max_height));
   1934 
   1935 	    if ((unsigned) hints.max_width < *width)
   1936 		*width = (unsigned) hints.max_width;
   1937 	    if ((unsigned) hints.max_height < *height)
   1938 		*height = (unsigned) hints.max_height;
   1939 	}
   1940 	code = 1;
   1941     } else {
   1942 	*width = 0;
   1943 	*height = 0;
   1944 	code = 0;
   1945     }
   1946     return code;
   1947 }
   1948 
   1949 void
   1950 RequestMaximize(XtermWidget xw, int maximize)
   1951 {
   1952     TScreen *screen = TScreenOf(xw);
   1953     XWindowAttributes wm_attrs, vshell_attrs;
   1954     unsigned root_width = 0, root_height = 0;
   1955     Boolean success = False;
   1956 
   1957     TRACE(("RequestMaximize %d:%s\n",
   1958 	   maximize,
   1959 	   (maximize
   1960 	    ? "maximize"
   1961 	    : "restore")));
   1962 
   1963     /*
   1964      * Before any maximize, ensure that we can capture the current screensize
   1965      * as well as the estimated root-window size.
   1966      */
   1967     if (maximize
   1968 	&& QueryMaximize(xw, &root_width, &root_height)
   1969 	&& xtermGetWinAttrs(screen->display,
   1970 			    WMFrameWindow(xw),
   1971 			    &wm_attrs)
   1972 	&& xtermGetWinAttrs(screen->display,
   1973 			    VShellWindow(xw),
   1974 			    &vshell_attrs)) {
   1975 
   1976 	if (screen->restore_data != True
   1977 	    || screen->restore_width != root_width
   1978 	    || screen->restore_height != root_height) {
   1979 	    screen->restore_data = True;
   1980 	    screen->restore_x = wm_attrs.x;
   1981 	    screen->restore_y = wm_attrs.y;
   1982 	    screen->restore_width = (unsigned) vshell_attrs.width;
   1983 	    screen->restore_height = (unsigned) vshell_attrs.height;
   1984 	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
   1985 		   screen->restore_x,
   1986 		   screen->restore_y,
   1987 		   screen->restore_width,
   1988 		   screen->restore_height));
   1989 	}
   1990 
   1991 	/* subtract wm decoration dimensions */
   1992 	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
   1993 	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
   1994 	success = True;
   1995     } else if (screen->restore_data) {
   1996 	success = True;
   1997 	maximize = 0;
   1998     }
   1999 
   2000     if (success) {
   2001 	switch (maximize) {
   2002 	case 3:
   2003 	    FullScreen(xw, 3);	/* depends on EWMH */
   2004 	    break;
   2005 	case 2:
   2006 	    FullScreen(xw, 2);	/* depends on EWMH */
   2007 	    break;
   2008 	case 1:
   2009 	    FullScreen(xw, 0);	/* overrides any EWMH hint */
   2010 	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
   2011 		   0,
   2012 		   0,
   2013 		   root_width,
   2014 		   root_height));
   2015 	    XMoveResizeWindow(screen->display, VShellWindow(xw),
   2016 			      0,	/* x */
   2017 			      0,	/* y */
   2018 			      root_width,
   2019 			      root_height);
   2020 	    break;
   2021 
   2022 	default:
   2023 	    FullScreen(xw, 0);	/* reset any EWMH hint */
   2024 	    if (screen->restore_data) {
   2025 		screen->restore_data = False;
   2026 
   2027 		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
   2028 		       screen->restore_x,
   2029 		       screen->restore_y,
   2030 		       screen->restore_width,
   2031 		       screen->restore_height));
   2032 
   2033 		XMoveResizeWindow(screen->display,
   2034 				  VShellWindow(xw),
   2035 				  screen->restore_x,
   2036 				  screen->restore_y,
   2037 				  screen->restore_width,
   2038 				  screen->restore_height);
   2039 	    }
   2040 	    break;
   2041 	}
   2042     }
   2043 }
   2044 
   2045 /*ARGSUSED*/
   2046 void
   2047 HandleMaximize(Widget w,
   2048 	       XEvent *event GCC_UNUSED,
   2049 	       String *params GCC_UNUSED,
   2050 	       Cardinal *nparams GCC_UNUSED)
   2051 {
   2052     XtermWidget xw;
   2053 
   2054     if ((xw = getXtermWidget(w)) != NULL) {
   2055 	RequestMaximize(xw, 1);
   2056     }
   2057 }
   2058 
   2059 /*ARGSUSED*/
   2060 void
   2061 HandleRestoreSize(Widget w,
   2062 		  XEvent *event GCC_UNUSED,
   2063 		  String *params GCC_UNUSED,
   2064 		  Cardinal *nparams GCC_UNUSED)
   2065 {
   2066     XtermWidget xw;
   2067 
   2068     if ((xw = getXtermWidget(w)) != NULL) {
   2069 	RequestMaximize(xw, 0);
   2070     }
   2071 }
   2072 #endif /* OPT_MAXIMIZE */
   2073 
   2074 void
   2075 Redraw(void)
   2076 {
   2077     XtermWidget xw = term;
   2078     TScreen *screen = TScreenOf(xw);
   2079     XExposeEvent event;
   2080 
   2081     TRACE(("Redraw\n"));
   2082 
   2083     event.type = Expose;
   2084     event.display = screen->display;
   2085     event.x = 0;
   2086     event.y = 0;
   2087     event.count = 0;
   2088 
   2089     if (VWindow(screen)) {
   2090 	event.window = VWindow(screen);
   2091 	event.width = xw->core.width;
   2092 	event.height = xw->core.height;
   2093 	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
   2094 						     (XEvent *) &event,
   2095 						     NULL);
   2096 	if (ScrollbarWidth(screen)) {
   2097 	    (screen->scrollWidget->core.widget_class->core_class.expose)
   2098 		(screen->scrollWidget, (XEvent *) &event, NULL);
   2099 	}
   2100     }
   2101 #if OPT_TEK4014
   2102     if (TEK4014_SHOWN(xw)) {
   2103 	TekScreen *tekscr = TekScreenOf(tekWidget);
   2104 	event.window = TWindow(tekscr);
   2105 	event.width = tekWidget->core.width;
   2106 	event.height = tekWidget->core.height;
   2107 	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
   2108     }
   2109 #endif
   2110 }
   2111 
   2112 #define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
   2113 
   2114 void
   2115 timestamp_filename(char *dst, const char *src)
   2116 {
   2117     time_t tstamp;
   2118     struct tm *tstruct;
   2119 
   2120     tstamp = time((time_t *) 0);
   2121     tstruct = localtime(&tstamp);
   2122     sprintf(dst, TIMESTAMP_FMT,
   2123 	    src,
   2124 	    (int) tstruct->tm_year + 1900,
   2125 	    tstruct->tm_mon + 1,
   2126 	    tstruct->tm_mday,
   2127 	    tstruct->tm_hour,
   2128 	    tstruct->tm_min,
   2129 	    tstruct->tm_sec);
   2130 }
   2131 
   2132 #if OPT_SCREEN_DUMPS
   2133 FILE *
   2134 create_printfile(XtermWidget xw, const char *suffix)
   2135 {
   2136     TScreen *screen = TScreenOf(xw);
   2137     char fname[1024];
   2138     int fd;
   2139     FILE *fp;
   2140 
   2141 #if defined(HAVE_STRFTIME)
   2142     {
   2143 	char format[1024];
   2144 	time_t now;
   2145 	struct tm *ltm;
   2146 
   2147 	now = time((time_t *) 0);
   2148 	ltm = localtime(&now);
   2149 
   2150 	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
   2151 	if (strftime(fname, sizeof fname, format, ltm) == 0) {
   2152 	    sprintf(fname, "xterm%s", suffix);
   2153 	}
   2154     }
   2155 #else
   2156     sprintf(fname, "xterm%s", suffix);
   2157 #endif
   2158     fd = open_userfile(screen->uid, screen->gid, fname, False);
   2159     fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
   2160     return fp;
   2161 }
   2162 #endif /* OPT_SCREEN_DUMPS */
   2163 
   2164 #if OPT_SCREEN_DUMPS || defined(ALLOWLOGGING)
   2165 int
   2166 open_userfile(uid_t uid, gid_t gid, char *path, Bool append)
   2167 {
   2168     int fd;
   2169     struct stat sb;
   2170 
   2171     if ((access(path, F_OK) != 0 && (errno != ENOENT))
   2172 	|| (creat_as(uid, gid, append, path, 0644) <= 0)
   2173 	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
   2174 	int the_error = errno;
   2175 	xtermWarning("cannot open %s: %d:%s\n",
   2176 		     path,
   2177 		     the_error,
   2178 		     SysErrorMsg(the_error));
   2179 	return -1;
   2180     }
   2181 
   2182     /*
   2183      * Doublecheck that the user really owns the file that we've opened before
   2184      * we do any damage, and that it is not world-writable.
   2185      */
   2186     if (fstat(fd, &sb) < 0
   2187 	|| sb.st_uid != uid
   2188 	|| (sb.st_mode & 022) != 0) {
   2189 	xtermWarning("you do not own %s\n", path);
   2190 	close(fd);
   2191 	return -1;
   2192     }
   2193     return fd;
   2194 }
   2195 
   2196 /*
   2197  * Create a file only if we could with the permissions of the real user id.
   2198  * We could emulate this with careful use of access() and following
   2199  * symbolic links, but that is messy and has race conditions.
   2200  * Forking is messy, too, but we can't count on setreuid() or saved set-uids
   2201  * being available.
   2202  *
   2203  * Note: When called for user logging, we have ensured that the real and
   2204  * effective user ids are the same, so this remains as a convenience function
   2205  * for the debug logs.
   2206  *
   2207  * Returns
   2208  *	 1 if we can proceed to open the file in relative safety,
   2209  *	-1 on error, e.g., cannot fork
   2210  *	 0 otherwise.
   2211  */
   2212 int
   2213 creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
   2214 {
   2215     int fd;
   2216     pid_t pid;
   2217     int retval = 0;
   2218     int childstat = 0;
   2219 #ifndef HAVE_WAITPID
   2220     int waited;
   2221     void (*chldfunc) (int);
   2222 
   2223     chldfunc = signal(SIGCHLD, SIG_DFL);
   2224 #endif /* HAVE_WAITPID */
   2225 
   2226     TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
   2227 	   (int) uid, (int) geteuid(),
   2228 	   (int) gid, (int) getegid(),
   2229 	   append,
   2230 	   pathname,
   2231 	   mode));
   2232 
   2233     if (uid == geteuid() && gid == getegid()) {
   2234 	fd = open(pathname,
   2235 		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
   2236 		  mode);
   2237 	if (fd >= 0)
   2238 	    close(fd);
   2239 	return (fd >= 0);
   2240     }
   2241 
   2242     pid = fork();
   2243     switch (pid) {
   2244     case 0:			/* child */
   2245 	if (setgid(gid) == -1
   2246 	    || setuid(uid) == -1) {
   2247 	    /* we cannot report an error here via stderr, just quit */
   2248 	    retval = 1;
   2249 	} else {
   2250 	    fd = open(pathname,
   2251 		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
   2252 		      mode);
   2253 	    if (fd >= 0) {
   2254 		close(fd);
   2255 		retval = 0;
   2256 	    } else {
   2257 		retval = 1;
   2258 	    }
   2259 	}
   2260 	_exit(retval);
   2261 	/* NOTREACHED */
   2262     case -1:			/* error */
   2263 	return retval;
   2264     default:			/* parent */
   2265 #ifdef HAVE_WAITPID
   2266 	while (waitpid(pid, &childstat, 0) < 0) {
   2267 #ifdef EINTR
   2268 	    if (errno == EINTR)
   2269 		continue;
   2270 #endif /* EINTR */
   2271 #ifdef ERESTARTSYS
   2272 	    if (errno == ERESTARTSYS)
   2273 		continue;
   2274 #endif /* ERESTARTSYS */
   2275 	    break;
   2276 	}
   2277 #else /* HAVE_WAITPID */
   2278 	waited = wait(&childstat);
   2279 	signal(SIGCHLD, chldfunc);
   2280 	/*
   2281 	   Since we had the signal handler uninstalled for a while,
   2282 	   we might have missed the termination of our screen child.
   2283 	   If we can check for this possibility without hanging, do so.
   2284 	 */
   2285 	do
   2286 	    if (waited == TScreenOf(term)->pid)
   2287 		NormalExit();
   2288 	while ((waited = nonblocking_wait()) > 0) ;
   2289 #endif /* HAVE_WAITPID */
   2290 #ifndef WIFEXITED
   2291 #define WIFEXITED(status) ((status & 0xff) != 0)
   2292 #endif
   2293 	if (WIFEXITED(childstat))
   2294 	    retval = 1;
   2295 	return retval;
   2296     }
   2297 }
   2298 #endif /* OPT_SCREEN_DUMPS || defined(ALLOWLOGGING) */
   2299 
   2300 int
   2301 xtermResetIds(TScreen *screen)
   2302 {
   2303     int result = 0;
   2304     if (setgid(screen->gid) == -1) {
   2305 	xtermWarning("unable to reset group-id\n");
   2306 	result = -1;
   2307     }
   2308     if (setuid(screen->uid) == -1) {
   2309 	xtermWarning("unable to reset user-id\n");
   2310 	result = -1;
   2311     }
   2312     return result;
   2313 }
   2314 
   2315 #ifdef ALLOWLOGGING
   2316 
   2317 /*
   2318  * Logging is a security hole, since it allows a setuid program to write
   2319  * arbitrary data to an arbitrary file.  So it is disabled by default.
   2320  */
   2321 
   2322 #ifdef ALLOWLOGFILEEXEC
   2323 static void
   2324 handle_SIGPIPE(int sig GCC_UNUSED)
   2325 {
   2326     XtermWidget xw = term;
   2327     TScreen *screen = TScreenOf(xw);
   2328 
   2329     DEBUG_MSG("handle:logpipe\n");
   2330 #ifdef SYSV
   2331     (void) signal(SIGPIPE, SIG_IGN);
   2332 #endif /* SYSV */
   2333     if (screen->logging)
   2334 	CloseLog(xw);
   2335 }
   2336 
   2337 /*
   2338  * Open a command to pipe log data to it.
   2339  * Warning, enabling this "feature" allows arbitrary programs
   2340  * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
   2341  * done through escape sequences....  You have been warned.
   2342  */
   2343 static void
   2344 StartLogExec(TScreen *screen)
   2345 {
   2346     int pid;
   2347     int p[2];
   2348     static char *shell;
   2349     struct passwd pw;
   2350 
   2351     if ((shell = x_getenv("SHELL")) == NULL) {
   2352 
   2353 	if (x_getpwuid(screen->uid, &pw)) {
   2354 	    char *name = x_getlogin(screen->uid, &pw);
   2355 	    if (*(pw.pw_shell)) {
   2356 		shell = pw.pw_shell;
   2357 	    }
   2358 	    free(name);
   2359 	}
   2360     }
   2361 
   2362     if (shell == NULL) {
   2363 	static char dummy[] = "/bin/sh";
   2364 	shell = dummy;
   2365     }
   2366 
   2367     if (access(shell, X_OK) != 0) {
   2368 	xtermPerror("Can't execute `%s'\n", shell);
   2369 	return;
   2370     }
   2371 
   2372     if (pipe(p) < 0) {
   2373 	xtermPerror("Can't make a pipe connection\n");
   2374 	return;
   2375     } else if ((pid = fork()) < 0) {
   2376 	xtermPerror("Can't fork...\n");
   2377 	return;
   2378     }
   2379     if (pid == 0) {		/* child */
   2380 	/*
   2381 	 * Close our output (we won't be talking back to the
   2382 	 * parent), and redirect our child's output to the
   2383 	 * original stderr.
   2384 	 */
   2385 	close(p[1]);
   2386 	dup2(p[0], 0);
   2387 	close(p[0]);
   2388 	dup2(fileno(stderr), 1);
   2389 	dup2(fileno(stderr), 2);
   2390 
   2391 	close(fileno(stderr));
   2392 	close(ConnectionNumber(screen->display));
   2393 	close(screen->respond);
   2394 
   2395 	signal(SIGHUP, SIG_DFL);
   2396 	signal(SIGCHLD, SIG_DFL);
   2397 
   2398 	/* (this is redundant) */
   2399 	if (xtermResetIds(screen) < 0)
   2400 	    exit(ERROR_SETUID);
   2401 
   2402 	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
   2403 	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
   2404 	exit(ERROR_LOGEXEC);
   2405     }
   2406     close(p[0]);
   2407     screen->logfd = p[1];
   2408     signal(SIGPIPE, handle_SIGPIPE);
   2409 }
   2410 #endif /* ALLOWLOGFILEEXEC */
   2411 
   2412 /*
   2413  * Generate a path for a logfile if no default path is given.
   2414  */
   2415 static char *
   2416 GenerateLogPath(void)
   2417 {
   2418     static char *log_default = NULL;
   2419 
   2420     /* once opened we just reuse the same log name */
   2421     if (log_default)
   2422 	return (log_default);
   2423 
   2424 #if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
   2425     {
   2426 #define LEN_HOSTNAME 255
   2427 	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
   2428 	 * the total length of a domain name (i.e., label octets and label
   2429 	 * length octets) is restricted to 255 octets or less.''
   2430 	 */
   2431 #define LEN_GETPID 9
   2432 	/*
   2433 	 * This is arbitrary...
   2434 	 */
   2435 	const char form[] = "Xterm.log.%s%s.%lu";
   2436 	char where[LEN_HOSTNAME + 1];
   2437 	char when[LEN_TIMESTAMP];
   2438 	time_t now = time((time_t *) 0);
   2439 	struct tm *ltm = (struct tm *) localtime(&now);
   2440 
   2441 	if ((gethostname(where, sizeof(where)) == 0) &&
   2442 	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
   2443 	    ((log_default = (char *) malloc((sizeof(form)
   2444 					     + strlen(where)
   2445 					     + strlen(when)
   2446 					     + LEN_GETPID))) != NULL)) {
   2447 	    (void) sprintf(log_default,
   2448 			   form,
   2449 			   where, when,
   2450 			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
   2451 	}
   2452     }
   2453 #else
   2454     {
   2455 	static const char log_def_name[] = "XtermLog.XXXXXX";
   2456 	if ((log_default = x_strdup(log_def_name)) != NULL) {
   2457 	    MakeTemp(log_default);
   2458 	}
   2459     }
   2460 #endif
   2461 
   2462     return (log_default);
   2463 }
   2464 
   2465 void
   2466 StartLog(XtermWidget xw)
   2467 {
   2468     TScreen *screen = TScreenOf(xw);
   2469 
   2470     if (screen->logging || (screen->inhibit & I_LOG))
   2471 	return;
   2472 
   2473     /* if we weren't supplied with a logfile path, generate one */
   2474     if (IsEmpty(screen->logfile))
   2475 	screen->logfile = GenerateLogPath();
   2476 
   2477     /* give up if we were unable to allocate the filename */
   2478     if (!screen->logfile)
   2479 	return;
   2480 
   2481     if (*screen->logfile == '|') {	/* exec command */
   2482 #ifdef ALLOWLOGFILEEXEC
   2483 	StartLogExec(screen);
   2484 #else
   2485 	Bell(xw, XkbBI_Info, 0);
   2486 	Bell(xw, XkbBI_Info, 0);
   2487 	return;
   2488 #endif
   2489     } else if (strcmp(screen->logfile, "-") == 0) {
   2490 	screen->logfd = STDOUT_FILENO;
   2491     } else {
   2492 	if ((screen->logfd = open_userfile(screen->uid,
   2493 					   screen->gid,
   2494 					   screen->logfile,
   2495 					   True)) < 0)
   2496 	    return;
   2497     }
   2498     screen->logstart = VTbuffer->next;
   2499     screen->logging = True;
   2500     update_logging();
   2501 }
   2502 
   2503 void
   2504 CloseLog(XtermWidget xw)
   2505 {
   2506     TScreen *screen = TScreenOf(xw);
   2507 
   2508     if (!screen->logging || (screen->inhibit & I_LOG))
   2509 	return;
   2510     FlushLog(xw);
   2511     close(screen->logfd);
   2512     screen->logging = False;
   2513     update_logging();
   2514 }
   2515 
   2516 void
   2517 FlushLog(XtermWidget xw)
   2518 {
   2519     TScreen *screen = TScreenOf(xw);
   2520 
   2521     if (screen->logging && !(screen->inhibit & I_LOG)) {
   2522 	Char *cp;
   2523 	size_t i;
   2524 
   2525 	cp = VTbuffer->next;
   2526 	if (screen->logstart != NULL
   2527 	    && (i = (size_t) (cp - screen->logstart)) > 0) {
   2528 	    IGNORE_RC(write(screen->logfd, screen->logstart, i));
   2529 	}
   2530 	screen->logstart = VTbuffer->next;
   2531     }
   2532 }
   2533 
   2534 #endif /* ALLOWLOGGING */
   2535 
   2536 /***====================================================================***/
   2537 
   2538 static unsigned
   2539 maskToShift(unsigned long mask)
   2540 {
   2541     unsigned result = 0;
   2542     if (mask != 0) {
   2543 	while ((mask & 1) == 0) {
   2544 	    mask >>= 1;
   2545 	    ++result;
   2546 	}
   2547     }
   2548     return result;
   2549 }
   2550 
   2551 static unsigned
   2552 maskToWidth(unsigned long mask)
   2553 {
   2554     unsigned result = 0;
   2555     while (mask != 0) {
   2556 	if ((mask & 1) != 0)
   2557 	    ++result;
   2558 	mask >>= 1;
   2559     }
   2560     return result;
   2561 }
   2562 
   2563 XVisualInfo *
   2564 getVisualInfo(XtermWidget xw)
   2565 {
   2566 #define MYFMT "getVisualInfo \
   2567 depth %d, \
   2568 type %d (%s), \
   2569 size %d \
   2570 rgb masks (%04lx/%04lx/%04lx)\n"
   2571 #define MYARG \
   2572        vi->depth,\
   2573        vi->class,\
   2574        ((vi->class & 1) ? "dynamic" : "static"),\
   2575        vi->colormap_size,\
   2576        vi->red_mask,\
   2577        vi->green_mask,\
   2578        vi->blue_mask
   2579 
   2580     TScreen *screen = TScreenOf(xw);
   2581     Display *dpy = screen->display;
   2582     XVisualInfo myTemplate;
   2583 
   2584     if (xw->visInfo == NULL && xw->numVisuals == 0) {
   2585 	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
   2586 								XDefaultScreen(dpy)));
   2587 	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
   2588 				     &myTemplate, &xw->numVisuals);
   2589 
   2590 	if ((xw->visInfo != NULL) && (xw->numVisuals > 0)) {
   2591 	    XVisualInfo *vi = xw->visInfo;
   2592 	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
   2593 	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
   2594 	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
   2595 	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
   2596 	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
   2597 	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
   2598 
   2599 	    xw->has_rgb = ((vi->red_mask != 0) &&
   2600 			   (vi->green_mask != 0) &&
   2601 			   (vi->blue_mask != 0) &&
   2602 			   ((vi->red_mask & vi->green_mask) == 0) &&
   2603 			   ((vi->green_mask & vi->blue_mask) == 0) &&
   2604 			   ((vi->blue_mask & vi->red_mask) == 0) &&
   2605 			   xw->rgb_widths[0] <= (unsigned) vi->bits_per_rgb &&
   2606 			   xw->rgb_widths[1] <= (unsigned) vi->bits_per_rgb &&
   2607 			   xw->rgb_widths[2] <= (unsigned) vi->bits_per_rgb &&
   2608 			   (vi->class == TrueColor
   2609 			    || vi->class == DirectColor));
   2610 
   2611 	    if (resource.reportColors) {
   2612 		printf(MYFMT, MYARG);
   2613 	    }
   2614 	    TRACE((MYFMT, MYARG));
   2615 	    TRACE(("...shifts %u/%u/%u\n",
   2616 		   xw->rgb_shifts[0],
   2617 		   xw->rgb_shifts[1],
   2618 		   xw->rgb_shifts[2]));
   2619 	    TRACE(("...widths %u/%u/%u\n",
   2620 		   xw->rgb_widths[0],
   2621 		   xw->rgb_widths[1],
   2622 		   xw->rgb_widths[2]));
   2623 	}
   2624     }
   2625     return (xw->visInfo != NULL) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
   2626 #undef MYFMT
   2627 #undef MYARG
   2628 }
   2629 
   2630 #if OPT_ISO_COLORS
   2631 static Bool
   2632 ReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
   2633 {
   2634     Bool result = False;
   2635 
   2636     if (AllowColorOps(xw, ecGetAnsiColor)) {
   2637 	XColor color;
   2638 	char buffer[80];
   2639 
   2640 	TRACE(("ReportAnsiColorRequest %d\n", colornum));
   2641 	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
   2642 	(void) QueryOneColor(xw, &color);
   2643 	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
   2644 		opcode,
   2645 		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
   2646 		color.red,
   2647 		color.green,
   2648 		color.blue);
   2649 	unparseputc1(xw, ANSI_OSC);
   2650 	unparseputs(xw, buffer);
   2651 	unparseputc1(xw, final);
   2652 	result = True;
   2653     }
   2654     return result;
   2655 }
   2656 
   2657 static void
   2658 getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
   2659 {
   2660     if (getVisualInfo(xw)) {
   2661 	*typep = (unsigned) xw->visInfo->class;
   2662 	*sizep = (unsigned) xw->visInfo->colormap_size;
   2663     } else {
   2664 	*typep = 0;
   2665 	*sizep = 0;
   2666     }
   2667 }
   2668 
   2669 #define MAX_COLORTABLE 4096
   2670 
   2671 /*
   2672  * Make only one call to XQueryColors(), since it can be slow.
   2673  */
   2674 static Boolean
   2675 loadColorTable(XtermWidget xw, unsigned length)
   2676 {
   2677     Colormap cmap = xw->core.colormap;
   2678     TScreen *screen = TScreenOf(xw);
   2679     Boolean result = (screen->cmap_data != NULL);
   2680 
   2681     if (!result
   2682 	&& length != 0
   2683 	&& length < MAX_COLORTABLE) {
   2684 	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
   2685 
   2686 	if (screen->cmap_data != NULL) {
   2687 	    unsigned i;
   2688 	    unsigned shift;
   2689 
   2690 	    if (getVisualInfo(xw))
   2691 		shift = xw->rgb_shifts[2];
   2692 	    else
   2693 		shift = 0;
   2694 
   2695 	    screen->cmap_size = length;
   2696 
   2697 	    for (i = 0; i < screen->cmap_size; i++) {
   2698 		screen->cmap_data[i].pixel = (unsigned long) i << shift;
   2699 	    }
   2700 	    result = (Boolean) (XQueryColors(screen->display,
   2701 					     cmap,
   2702 					     screen->cmap_data,
   2703 					     (int) screen->cmap_size) != 0);
   2704 	}
   2705     }
   2706     return result;
   2707 }
   2708 
   2709 /***====================================================================***/
   2710 
   2711 /*
   2712  * Call this function with def->{red,green,blue} initialized, to obtain a pixel
   2713  * value.
   2714  */
   2715 Boolean
   2716 AllocOneColor(XtermWidget xw, XColor *def)
   2717 {
   2718     TScreen *screen = TScreenOf(xw);
   2719     Boolean result = True;
   2720 
   2721 #define MaskIt(name,nn) \
   2722 	((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
   2723 	             << xw->rgb_shifts[nn]) \
   2724 	 & xw->visInfo->name ##_mask)
   2725 
   2726 #define VisualIsRGB(xw) (getVisualInfo(xw) != NULL && xw->has_rgb && xw->visInfo->bits_per_rgb <= 8)
   2727 
   2728     if (VisualIsRGB(xw)) {
   2729 	def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
   2730     } else {
   2731 	Display *dpy = screen->display;
   2732 	if (!XAllocColor(dpy, xw->core.colormap, def)) {
   2733 	    /*
   2734 	     * Decide between foreground and background by a grayscale
   2735 	     * approximation.
   2736 	     */
   2737 	    int bright = def->red * 3 + def->green * 10 + def->blue;
   2738 	    int levels = 14 * 0x8000;
   2739 	    def->pixel = ((bright >= levels)
   2740 			  ? xw->dft_background
   2741 			  : xw->dft_foreground);
   2742 	    TRACE(("XAllocColor failed, for %04x/%04x/%04x: choose %08lx (%d vs %d)\n",
   2743 		   def->red, def->green, def->blue,
   2744 		   def->pixel, bright, levels));
   2745 	    result = False;
   2746 	}
   2747     }
   2748     return result;
   2749 }
   2750 
   2751 /***====================================================================***/
   2752 
   2753 /*
   2754  * Call this function with def->pixel set to the color that we want to convert
   2755  * to separate red/green/blue.
   2756  */
   2757 Boolean
   2758 QueryOneColor(XtermWidget xw, XColor *def)
   2759 {
   2760     Boolean result = True;
   2761 
   2762 #define UnMaskIt(name,nn) \
   2763 	((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
   2764 #define UnMaskIt2(name,nn) \
   2765 	(unsigned short)((((UnMaskIt(name,nn) << 8) \
   2766 			   |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
   2767 
   2768     if (VisualIsRGB(xw)) {
   2769 	/* *INDENT-EQLS* */
   2770 	def->red   = UnMaskIt2(red, 0);
   2771 	def->green = UnMaskIt2(green, 1);
   2772 	def->blue  = UnMaskIt2(blue, 2);
   2773     } else {
   2774 	Display *dpy = TScreenOf(xw)->display;
   2775 	if (!XQueryColor(dpy, xw->core.colormap, def)) {
   2776 	    TRACE(("XQueryColor failed, given %08lx\n", def->pixel));
   2777 	    result     = False;
   2778 	}
   2779     }
   2780     return result;
   2781 }
   2782 
   2783 /***====================================================================***/
   2784 
   2785 /*
   2786  * Find closest color for "def" in "cmap".
   2787  * Set "def" to the resulting color.
   2788  *
   2789  * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
   2790  * modified with ideas from David Tong's "noflash" library.
   2791  * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
   2792  *
   2793  * Return False if not able to find or allocate a color.
   2794  */
   2795 static Boolean
   2796 allocateClosestRGB(XtermWidget xw, XColor *def)
   2797 {
   2798     TScreen *screen = TScreenOf(xw);
   2799     Boolean result = False;
   2800     unsigned cmap_type;
   2801     unsigned cmap_size;
   2802 
   2803     getColormapInfo(xw, &cmap_type, &cmap_size);
   2804 
   2805     if ((cmap_type & 1) != 0) {
   2806 
   2807 	if (loadColorTable(xw, cmap_size)) {
   2808 	    char *tried = TypeCallocN(char, (size_t) cmap_size);
   2809 
   2810 	    if (tried != NULL) {
   2811 		unsigned attempts;
   2812 
   2813 		/*
   2814 		 * Try (possibly each entry in the color map) to find the best
   2815 		 * approximation to the requested color.
   2816 		 */
   2817 		for (attempts = 0; attempts < cmap_size; attempts++) {
   2818 		    Boolean first = True;
   2819 		    double bestRGB = 0.0;
   2820 		    unsigned bestInx = 0;
   2821 		    unsigned i;
   2822 
   2823 		    for (i = 0; i < cmap_size; i++) {
   2824 			if (!tried[bestInx]) {
   2825 			    double diff, thisRGB = 0.0;
   2826 
   2827 			    /*
   2828 			     * Look for the best match based on luminance.
   2829 			     * Measure this by the least-squares difference of
   2830 			     * the weighted R/G/B components from the color map
   2831 			     * versus the requested color.  Use the Y (luma)
   2832 			     * component of the YIQ color space model for
   2833 			     * weights that correspond to the luminance.
   2834 			     */
   2835 #define AddColorWeight(weight, color) \
   2836 			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
   2837 			    thisRGB += diff * diff
   2838 
   2839 			    AddColorWeight(0.30, red);
   2840 			    AddColorWeight(0.61, green);
   2841 			    AddColorWeight(0.11, blue);
   2842 
   2843 			    if (first || (thisRGB < bestRGB)) {
   2844 				first = False;
   2845 				bestInx = i;
   2846 				bestRGB = thisRGB;
   2847 			    }
   2848 			}
   2849 		    }
   2850 		    if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
   2851 			*def = screen->cmap_data[bestInx];
   2852 			TRACE(("...closest %x/%x/%x\n", def->red,
   2853 			       def->green, def->blue));
   2854 			result = True;
   2855 			break;
   2856 		    }
   2857 		    /*
   2858 		     * It failed - either the color map entry was readonly, or
   2859 		     * another client has allocated the entry.  Mark the entry
   2860 		     * so we will ignore it
   2861 		     */
   2862 		    tried[bestInx] = True;
   2863 		}
   2864 		free(tried);
   2865 	    }
   2866 	}
   2867     }
   2868     return result;
   2869 }
   2870 
   2871 #ifndef ULONG_MAX
   2872 #define ULONG_MAX (unsigned long)(~(0L))
   2873 #endif
   2874 
   2875 /*
   2876  * Allocate a color for the "ANSI" colors.  That actually includes colors up
   2877  * to 256.
   2878  *
   2879  * Returns
   2880  *	-1 on error
   2881  *	0 on no change
   2882  *	1 if a new color was allocated.
   2883  */
   2884 static int
   2885 AllocateAnsiColor(XtermWidget xw,
   2886 		  ColorRes * res,
   2887 		  const char *spec)
   2888 {
   2889     int result;
   2890     XColor def;
   2891 
   2892     if (xtermAllocColor(xw, &def, spec)) {
   2893 	if (res->mode == True &&
   2894 	    EQL_COLOR_RES(res, def.pixel)) {
   2895 	    result = 0;
   2896 	} else {
   2897 	    result = 1;
   2898 	    SET_COLOR_RES(res, def.pixel);
   2899 	    res->red = def.red;
   2900 	    res->green = def.green;
   2901 	    res->blue = def.blue;
   2902 	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
   2903 		   (int) (res - TScreenOf(xw)->Acolors), spec,
   2904 		   def.red,
   2905 		   def.green,
   2906 		   def.blue,
   2907 		   def.pixel));
   2908 	    if (!res->mode)
   2909 		result = 0;
   2910 	    res->mode = True;
   2911 	}
   2912     } else {
   2913 	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
   2914 	result = -1;
   2915     }
   2916     return (result);
   2917 }
   2918 
   2919 Pixel
   2920 xtermGetColorRes(XtermWidget xw, ColorRes * res)
   2921 {
   2922     Pixel result = 0;
   2923 
   2924     if (res->mode) {
   2925 	result = res->value;
   2926     } else {
   2927 	TRACE(("xtermGetColorRes for Acolors[%d]\n",
   2928 	       (int) (res - TScreenOf(xw)->Acolors)));
   2929 
   2930 	if (res >= TScreenOf(xw)->Acolors) {
   2931 	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
   2932 
   2933 	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
   2934 		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
   2935 		res->mode = -True;
   2936 		xtermWarning("Cannot allocate color \"%s\"\n",
   2937 			     NonNull(res->resource));
   2938 	    }
   2939 	    result = res->value;
   2940 	} else {
   2941 	    result = 0;
   2942 	}
   2943     }
   2944     return result;
   2945 }
   2946 
   2947 static int
   2948 ChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
   2949 {
   2950     int code;
   2951 
   2952     if (color < 0 || color >= MAXCOLORS) {
   2953 	code = -1;
   2954     } else {
   2955 	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
   2956 
   2957 	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
   2958 	code = AllocateAnsiColor(xw, res, name);
   2959     }
   2960     return code;
   2961 }
   2962 
   2963 /*
   2964  * Set or query entries in the Acolors[] array by parsing pairs of color/name
   2965  * values from the given buffer.
   2966  *
   2967  * The color can be any legal index into Acolors[], which consists of the
   2968  * 16/88/256 "ANSI" colors, followed by special color values for the various
   2969  * colorXX resources.  The indices for the special color values are not
   2970  * simple to work with, so an alternative is to use the calls which pass in
   2971  * 'first' set to the beginning of those indices.
   2972  *
   2973  * If the name is "?", report to the host the current value for the color.
   2974  */
   2975 static Bool
   2976 ChangeAnsiColorRequest(XtermWidget xw,
   2977 		       int opcode,
   2978 		       char *buf,
   2979 		       int first,
   2980 		       int final)
   2981 {
   2982     int repaint = False;
   2983     int code;
   2984     int last = (MAXCOLORS - first);
   2985     int queried = 0;
   2986 
   2987     TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
   2988 
   2989     while (buf && *buf) {
   2990 	int color;
   2991 	char *name = strchr(buf, ';');
   2992 
   2993 	if (name == NULL)
   2994 	    break;
   2995 	*name = '\0';
   2996 	name++;
   2997 	color = atoi(buf);
   2998 	if (color < 0 || color >= last)
   2999 	    break;		/* quit on any error */
   3000 	buf = strchr(name, ';');
   3001 	if (buf) {
   3002 	    *buf = '\0';
   3003 	    buf++;
   3004 	}
   3005 	if (!strcmp(name, "?")) {
   3006 	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
   3007 		++queried;
   3008 	} else {
   3009 	    code = ChangeOneAnsiColor(xw, color + first, name);
   3010 	    if (code < 0) {
   3011 		/* stop on any error */
   3012 		break;
   3013 	    } else if (code > 0) {
   3014 		repaint = True;
   3015 	    }
   3016 	    /* FIXME:  free old color somehow?  We aren't for the other color
   3017 	     * change style (dynamic colors).
   3018 	     */
   3019 	}
   3020     }
   3021     if (queried)
   3022 	unparse_end(xw);
   3023 
   3024     return (repaint);
   3025 }
   3026 
   3027 static Bool
   3028 ResetOneAnsiColor(XtermWidget xw, int color, int start)
   3029 {
   3030     Bool repaint = False;
   3031     int last = MAXCOLORS - start;
   3032 
   3033     if (color >= 0 && color < last) {
   3034 	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
   3035 
   3036 	if (res->mode) {
   3037 	    /* a color has been allocated for this slot - test further... */
   3038 	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
   3039 		repaint = True;
   3040 	    }
   3041 	}
   3042     }
   3043     return repaint;
   3044 }
   3045 
   3046 int
   3047 ResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
   3048 {
   3049     int repaint = 0;
   3050     int color;
   3051 
   3052     TRACE(("ResetAnsiColorRequest(%s)\n", buf));
   3053     if (*buf != '\0') {
   3054 	/* reset specific colors */
   3055 	while (!IsEmpty(buf)) {
   3056 	    char *next;
   3057 
   3058 	    color = (int) (strtol) (buf, &next, 10);
   3059 	    if (!PartS2L(buf, next) || (color < 0))
   3060 		break;		/* no number at all */
   3061 	    if (next != NULL) {
   3062 		if (strchr(";", *next) == NULL)
   3063 		    break;	/* unexpected delimiter */
   3064 		++next;
   3065 	    }
   3066 
   3067 	    if (ResetOneAnsiColor(xw, color, start)) {
   3068 		++repaint;
   3069 	    }
   3070 	    buf = next;
   3071 	}
   3072     } else {
   3073 	TRACE(("...resetting all %d colors\n", MAXCOLORS));
   3074 	for (color = 0; color < MAXCOLORS; ++color) {
   3075 	    if (ResetOneAnsiColor(xw, color, start)) {
   3076 		++repaint;
   3077 	    }
   3078 	}
   3079     }
   3080     TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
   3081     return repaint;
   3082 }
   3083 #else
   3084 #define allocateClosestRGB(xw, def) 0
   3085 #endif /* OPT_ISO_COLORS */
   3086 
   3087 Boolean
   3088 allocateBestRGB(XtermWidget xw, XColor *def)
   3089 {
   3090     (void) xw;
   3091     (void) def;
   3092     return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
   3093 }
   3094 
   3095 static Boolean
   3096 xtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
   3097 {
   3098     Boolean result = False;
   3099     TScreen *screen = TScreenOf(xw);
   3100     Colormap cmap = xw->core.colormap;
   3101     size_t have = strlen(spec);
   3102 
   3103     if (have == 0 || have > MAX_U_STRING) {
   3104 	if (resource.reportColors) {
   3105 	    printf("color  (ignored, length %lu)\n", (unsigned long) have);
   3106 	}
   3107     } else if (XParseColor(screen->display, cmap, spec, def)) {
   3108 	XColor save_def = *def;
   3109 	if (resource.reportColors) {
   3110 	    printf("color  %04x/%04x/%04x = \"%s\"\n",
   3111 		   def->red, def->green, def->blue,
   3112 		   spec);
   3113 	}
   3114 	if (allocateBestRGB(xw, def)) {
   3115 	    if (resource.reportColors) {
   3116 		if (def->red != save_def.red ||
   3117 		    def->green != save_def.green ||
   3118 		    def->blue != save_def.blue) {
   3119 		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
   3120 			   def->red, def->green, def->blue,
   3121 			   spec);
   3122 		}
   3123 	    }
   3124 	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
   3125 		   def->red, def->green, def->blue));
   3126 	    result = True;
   3127 	}
   3128     }
   3129     return result;
   3130 }
   3131 
   3132 /*
   3133  * This provides an approximation (the closest color from xterm's palette)
   3134  * rather than the "exact" color (whatever the display could provide, actually)
   3135  * because of the context in which it is used.
   3136  */
   3137 #define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
   3138 int
   3139 xtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
   3140 {
   3141     int result = -1;
   3142 #if OPT_ISO_COLORS
   3143     int n;
   3144     int best_index = -1;
   3145     unsigned long best_value = 0;
   3146     unsigned long this_value;
   3147     long diff_red, diff_green, diff_blue;
   3148 
   3149     TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
   3150 
   3151     for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
   3152 	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
   3153 
   3154 	/* ensure that we have a value for each of the colors */
   3155 	if (!res->mode) {
   3156 	    (void) AllocateAnsiColor(xw, res, res->resource);
   3157 	}
   3158 
   3159 	/* find the closest match */
   3160 	if (res->mode == True) {
   3161 	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
   3162 		    res->value, res->red, res->green, res->blue));
   3163 	    diff_red = ColorDiff(find_red, res->red);
   3164 	    diff_green = ColorDiff(find_green, res->green);
   3165 	    diff_blue = ColorDiff(find_blue, res->blue);
   3166 	    this_value = (unsigned long) ((diff_red * diff_red)
   3167 					  + (diff_green * diff_green)
   3168 					  + (diff_blue * diff_blue));
   3169 	    if (best_index < 0 || this_value < best_value) {
   3170 		best_index = n;
   3171 		best_value = this_value;
   3172 	    }
   3173 	}
   3174     }
   3175     TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
   3176     result = best_index;
   3177 
   3178 #else
   3179     (void) xw;
   3180     (void) find_red;
   3181     (void) find_green;
   3182     (void) find_blue;
   3183 #endif
   3184     return result;
   3185 }
   3186 
   3187 #if OPT_DIRECT_COLOR
   3188 int
   3189 getDirectColor(XtermWidget xw, int red, int green, int blue)
   3190 {
   3191     Pixel result = 0;
   3192 
   3193 #define getRGB(name,shift) \
   3194     do { \
   3195 	Pixel value = (Pixel) name & 0xff; \
   3196 	if (xw->rgb_widths[shift] < 8) { \
   3197 	    value >>= (int) (8 - xw->rgb_widths[shift]); \
   3198 	} \
   3199 	value <<= xw->rgb_shifts[shift]; \
   3200 	value &= xw->visInfo->name ##_mask; \
   3201 	result |= value; \
   3202     } while (0)
   3203 
   3204     getRGB(red, 0);
   3205     getRGB(green, 1);
   3206     getRGB(blue, 2);
   3207 
   3208 #undef getRGB
   3209 
   3210     return (int) result;
   3211 }
   3212 
   3213 static void
   3214 formatDirectColor(char *target, XtermWidget xw, unsigned value)
   3215 {
   3216     Pixel result[3];
   3217 
   3218 #define getRGB(name, shift) \
   3219     do { \
   3220 	result[shift] = value & xw->visInfo->name ## _mask; \
   3221 	result[shift] >>= xw->rgb_shifts[shift]; \
   3222 	if (xw->rgb_widths[shift] < 8) \
   3223 	    result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
   3224     } while(0)
   3225 
   3226     getRGB(red, 0);
   3227     getRGB(green, 1);
   3228     getRGB(blue, 2);
   3229 
   3230 #undef getRGB
   3231 
   3232     sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
   3233 }
   3234 #endif /* OPT_DIRECT_COLOR */
   3235 
   3236 #define fg2SGR(n) \
   3237 		(n) >= 8 ? 9 : 3, \
   3238 		(n) >= 8 ? (n) - 8 : (n)
   3239 #define bg2SGR(n) \
   3240 		(n) >= 8 ? 10 : 4, \
   3241 		(n) >= 8 ? (n) - 8 : (n)
   3242 
   3243 #define EndOf(s) (s) + strlen(s)
   3244 
   3245 char *
   3246 xtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
   3247 {
   3248     TScreen *screen = TScreenOf(xw);
   3249     char *msg = target;
   3250 
   3251     strcpy(target, "0");
   3252     if (attr & BOLD)
   3253 	strcat(msg, ";1");
   3254     if (attr & UNDERLINE)
   3255 	strcat(msg, ";4");
   3256     if (attr & BLINK)
   3257 	strcat(msg, ";5");
   3258     if (attr & INVERSE)
   3259 	strcat(msg, ";7");
   3260     if (attr & INVISIBLE)
   3261 	strcat(msg, ";8");
   3262 #if OPT_WIDE_ATTRS
   3263     if (attr & ATR_FAINT)
   3264 	strcat(msg, ";2");
   3265     if (attr & ATR_ITALIC)
   3266 	strcat(msg, ";3");
   3267     if (attr & ATR_STRIKEOUT)
   3268 	strcat(msg, ";9");
   3269     if (attr & ATR_DBL_UNDER)
   3270 	strcat(msg, ";21");
   3271 #endif
   3272 #if OPT_256_COLORS || OPT_88_COLORS
   3273     if_OPT_ISO_COLORS(screen, {
   3274 	if (attr & FG_COLOR) {
   3275 	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
   3276 		strcat(msg, ";38:2::");
   3277 		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
   3278 	    }) if (fg >= 16) {
   3279 		sprintf(EndOf(msg), ";38:5:%d", fg);
   3280 	    } else {
   3281 		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
   3282 	    }
   3283 	}
   3284 	if (attr & BG_COLOR) {
   3285 	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
   3286 		strcat(msg, ";48:2::");
   3287 		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
   3288 	    }) if (bg >= 16) {
   3289 		sprintf(EndOf(msg), ";48:5:%d", bg);
   3290 	    } else {
   3291 		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
   3292 	    }
   3293 	}
   3294     });
   3295 #elif OPT_ISO_COLORS
   3296     if_OPT_ISO_COLORS(screen, {
   3297 	if (attr & FG_COLOR) {
   3298 	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
   3299 	}
   3300 	if (attr & BG_COLOR) {
   3301 	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
   3302 	}
   3303     });
   3304 #else
   3305     (void) screen;
   3306     (void) fg;
   3307     (void) bg;
   3308 #endif
   3309     return target;
   3310 }
   3311 
   3312 #if OPT_PASTE64
   3313 static void
   3314 ManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
   3315 {
   3316 #define PDATA(a,b) { a, #b }
   3317     static struct {
   3318 	char given;
   3319 	String result;
   3320     } table[] = {
   3321 	PDATA('s', SELECT),
   3322 	    PDATA('p', PRIMARY),
   3323 	    PDATA('q', SECONDARY),
   3324 	    PDATA('c', CLIPBOARD),
   3325 	    PDATA('0', CUT_BUFFER0),
   3326 	    PDATA('1', CUT_BUFFER1),
   3327 	    PDATA('2', CUT_BUFFER2),
   3328 	    PDATA('3', CUT_BUFFER3),
   3329 	    PDATA('4', CUT_BUFFER4),
   3330 	    PDATA('5', CUT_BUFFER5),
   3331 	    PDATA('6', CUT_BUFFER6),
   3332 	    PDATA('7', CUT_BUFFER7),
   3333     };
   3334     char target_used[XtNumber(table)];
   3335 
   3336     const char *base = buf;
   3337     Cardinal j;
   3338     Cardinal num_targets = 0;
   3339 
   3340     TRACE(("Manipulate selection data\n"));
   3341 
   3342     memset(target_used, 0, sizeof(target_used));
   3343     while (*buf != ';' && *buf != '\0') {
   3344 	++buf;
   3345     }
   3346 
   3347     if (*buf == ';') {
   3348 	char select_code[XtNumber(table) + 1];
   3349 	String select_args[XtNumber(table) + 1];
   3350 
   3351 	*buf++ = '\0';
   3352 	if (*base == '\0')
   3353 	    base = "s0";
   3354 
   3355 	while (*base != '\0') {
   3356 	    for (j = 0; j < XtNumber(table); ++j) {
   3357 		if (*base == table[j].given) {
   3358 		    if (!target_used[j]) {
   3359 			target_used[j] = 1;
   3360 			select_code[num_targets] = *base;
   3361 			select_args[num_targets++] = table[j].result;
   3362 			TRACE(("atom[%d] %s\n", num_targets, table[j].result));
   3363 		    }
   3364 		    break;
   3365 		}
   3366 	    }
   3367 	    ++base;
   3368 	}
   3369 	select_code[num_targets] = '\0';
   3370 
   3371 	if (!strcmp(buf, "?")) {
   3372 	    if (AllowWindowOps(xw, ewGetSelection)) {
   3373 		TRACE(("Getting selection\n"));
   3374 		unparseputc1(xw, ANSI_OSC);
   3375 		unparseputs(xw, "52");
   3376 		unparseputc(xw, ';');
   3377 
   3378 		unparseputs(xw, select_code);
   3379 		unparseputc(xw, ';');
   3380 
   3381 		/* Tell xtermGetSelection data is base64 encoded */
   3382 		screen->base64_paste = num_targets;
   3383 		screen->base64_final = final;
   3384 
   3385 		screen->selection_time =
   3386 		    XtLastTimestampProcessed(TScreenOf(xw)->display);
   3387 
   3388 		/* terminator will be written in this call */
   3389 		xtermGetSelection((Widget) xw,
   3390 				  screen->selection_time,
   3391 				  select_args, num_targets,
   3392 				  NULL);
   3393 	    }
   3394 	} else {
   3395 	    if (AllowWindowOps(xw, ewSetSelection)) {
   3396 		char *old = buf;
   3397 
   3398 		TRACE(("Setting selection(%s) with %s\n", select_code, buf));
   3399 		screen->selection_time =
   3400 		    XtLastTimestampProcessed(TScreenOf(xw)->display);
   3401 
   3402 		for (j = 0; j < num_targets; ++j) {
   3403 		    buf = old;
   3404 		    ClearSelectionBuffer(screen, select_args[j]);
   3405 		    while (*buf != '\0') {
   3406 			AppendToSelectionBuffer(screen,
   3407 						CharOf(*buf++),
   3408 						select_args[j]);
   3409 		    }
   3410 		}
   3411 		CompleteSelection(xw, select_args, num_targets);
   3412 	    }
   3413 	}
   3414     }
   3415 }
   3416 #endif /* OPT_PASTE64 */
   3417 
   3418 /***====================================================================***/
   3419 
   3420 static Bool
   3421 xtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
   3422 {
   3423     TScreen *screen = TScreenOf(xw);
   3424     Bool result = False;
   3425     Char *cp = *bufp;
   3426     Char *next = cp;
   3427 
   3428     (void) screen;
   3429     (void) last;
   3430 
   3431 #if OPT_WIDE_CHARS
   3432     if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
   3433 	PtyData data;
   3434 
   3435 	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
   3436 	    if (!is_UCS_SPECIAL(data.utf_data)
   3437 		&& (data.utf_data >= 128 ||
   3438 		    ansi_table[data.utf_data] == CASE_PRINT)) {
   3439 		next += (data.utf_size - 1);
   3440 		result = True;
   3441 	    } else {
   3442 		result = False;
   3443 	    }
   3444 	} else {
   3445 	    result = False;
   3446 	}
   3447     } else
   3448 #endif
   3449 #if OPT_C1_PRINT
   3450 	if (screen->c1_printable
   3451 	    && (*cp >= 128 && *cp < 160)) {
   3452 	result = True;
   3453     } else
   3454 #endif
   3455     if (ansi_table[*cp] == CASE_PRINT) {
   3456 	result = True;
   3457     }
   3458     *bufp = next;
   3459     return result;
   3460 }
   3461 
   3462 /***====================================================================***/
   3463 
   3464 /*
   3465  * Enum corresponding to the actual OSC codes rather than the internal
   3466  * array indices.  Compare with TermColors.
   3467  */
   3468 typedef enum {
   3469     OSC_TEXT_FG = 10
   3470     ,OSC_TEXT_BG
   3471     ,OSC_TEXT_CURSOR
   3472     ,OSC_MOUSE_FG
   3473     ,OSC_MOUSE_BG
   3474 #if OPT_TEK4014
   3475     ,OSC_TEK_FG = 15
   3476     ,OSC_TEK_BG
   3477 #endif
   3478 #if OPT_HIGHLIGHT_COLOR
   3479     ,OSC_HIGHLIGHT_BG = 17
   3480 #endif
   3481 #if OPT_TEK4014
   3482     ,OSC_TEK_CURSOR = 18
   3483 #endif
   3484 #if OPT_HIGHLIGHT_COLOR
   3485     ,OSC_HIGHLIGHT_FG = 19
   3486 #endif
   3487     ,OSC_NCOLORS
   3488 } OscTextColors;
   3489 
   3490 /*
   3491  * Map codes to OSC controls that can reset colors.
   3492  */
   3493 #define OSC_RESET 100
   3494 #define OSC_Reset(code) (code) + OSC_RESET
   3495 
   3496 /*
   3497  * Other (non-color) OSC controls
   3498  */
   3499 typedef enum {
   3500     OSC_IconBoth = 0
   3501     ,OSC_IconOnly = 1
   3502     ,OSC_TitleOnly = 2
   3503     ,OSC_X_Property = 3
   3504     ,OSC_SetAnsiColor = 4
   3505     ,OSC_GetAnsiColors = 5
   3506     ,OSC_ColorMode = 6
   3507     ,OSC_SetupPointer = 22
   3508     ,OSC_Unused_30 = 30		/* Konsole (unused) */
   3509     ,OSC_Unused_31 = 31		/* Konsole (unused) */
   3510     ,OSC_NewLogFile = 46
   3511     ,OSC_FontOps = 50
   3512     ,OSC_Unused_51		/* Emacs (unused) */
   3513     ,OSC_SelectionData = 52
   3514     ,OSC_AllowedOps = 60
   3515     ,OSC_DisallowedOps = 61
   3516 } OscMiscOps;
   3517 
   3518 static Bool
   3519 GetOldColors(XtermWidget xw)
   3520 {
   3521     if (xw->work.oldColors == NULL) {
   3522 	int i;
   3523 
   3524 	xw->work.oldColors = TypeXtMalloc(ScrnColors);
   3525 	if (xw->work.oldColors == NULL) {
   3526 	    xtermWarning("allocation failure in GetOldColors\n");
   3527 	    return (False);
   3528 	}
   3529 	xw->work.oldColors->which = 0;
   3530 	for (i = 0; i < NCOLORS; i++) {
   3531 	    xw->work.oldColors->colors[i] = 0;
   3532 	    xw->work.oldColors->names[i] = NULL;
   3533 	}
   3534 	GetColors(xw, xw->work.oldColors);
   3535     }
   3536     return (True);
   3537 }
   3538 
   3539 static int
   3540 oppositeColor(XtermWidget xw, int n)
   3541 {
   3542     Boolean reversed = (xw->misc.re_verse);
   3543 
   3544     switch (n) {
   3545     case TEXT_FG:
   3546 	n = reversed ? TEXT_FG : TEXT_BG;
   3547 	break;
   3548     case TEXT_BG:
   3549 	n = reversed ? TEXT_BG : TEXT_FG;
   3550 	break;
   3551     case MOUSE_FG:
   3552 	n = MOUSE_BG;
   3553 	break;
   3554     case MOUSE_BG:
   3555 	n = MOUSE_FG;
   3556 	break;
   3557 #if OPT_TEK4014
   3558     case TEK_FG:
   3559 	n = reversed ? TEK_FG : TEK_BG;
   3560 	break;
   3561     case TEK_BG:
   3562 	n = reversed ? TEK_BG : TEK_FG;
   3563 	break;
   3564 #endif
   3565 #if OPT_HIGHLIGHT_COLOR
   3566     case HIGHLIGHT_FG:
   3567 	n = HIGHLIGHT_BG;
   3568 	break;
   3569     case HIGHLIGHT_BG:
   3570 	n = HIGHLIGHT_FG;
   3571 	break;
   3572 #endif
   3573     default:
   3574 	break;
   3575     }
   3576     return n;
   3577 }
   3578 
   3579 static Bool
   3580 ReportColorRequest(XtermWidget xw, int ndx, int final)
   3581 {
   3582     Bool result = False;
   3583 
   3584     if (AllowColorOps(xw, ecGetColor)) {
   3585 	XColor color;
   3586 	char buffer[80];
   3587 
   3588 	/*
   3589 	 * ChangeColorsRequest() has "always" chosen the opposite color when
   3590 	 * reverse-video is set.  Report this as the original color index, but
   3591 	 * reporting the opposite color which would be used.
   3592 	 */
   3593 	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
   3594 
   3595 	GetOldColors(xw);
   3596 	color.pixel = xw->work.oldColors->colors[ndx];
   3597 	(void) QueryOneColor(xw, &color);
   3598 	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
   3599 		color.red,
   3600 		color.green,
   3601 		color.blue);
   3602 	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
   3603 	       ndx, xw->work.oldColors->colors[ndx], buffer));
   3604 	unparseputc1(xw, ANSI_OSC);
   3605 	unparseputs(xw, buffer);
   3606 	unparseputc1(xw, final);
   3607 	result = True;
   3608     }
   3609     return result;
   3610 }
   3611 
   3612 static Bool
   3613 UpdateOldColors(XtermWidget xw, ScrnColors * pNew)
   3614 {
   3615     int i;
   3616 
   3617     /* if we were going to free old colors, this would be the place to
   3618      * do it.   I've decided not to (for now), because it seems likely
   3619      * that we'd have a small set of colors we use over and over, and that
   3620      * we could save some overhead this way.   The only case in which this
   3621      * (clearly) fails is if someone is trying a boatload of colors, in
   3622      * which case they can restart xterm
   3623      */
   3624     for (i = 0; i < NCOLORS; i++) {
   3625 	if (COLOR_DEFINED(pNew, i)) {
   3626 	    if (xw->work.oldColors->names[i] != NULL) {
   3627 		XtFree(xw->work.oldColors->names[i]);
   3628 		xw->work.oldColors->names[i] = NULL;
   3629 	    }
   3630 	    if (pNew->names[i]) {
   3631 		xw->work.oldColors->names[i] = pNew->names[i];
   3632 	    }
   3633 	    xw->work.oldColors->colors[i] = pNew->colors[i];
   3634 	}
   3635     }
   3636     return (True);
   3637 }
   3638 
   3639 /*
   3640  * OSC codes are constant, but the indices for the color arrays depend on how
   3641  * xterm is compiled.
   3642  */
   3643 static int
   3644 OscToColorIndex(OscTextColors mode)
   3645 {
   3646     int result = 0;
   3647 
   3648 #define CASE(name) case OSC_##name: result = name; break
   3649     switch (mode) {
   3650 	CASE(TEXT_FG);
   3651 	CASE(TEXT_BG);
   3652 	CASE(TEXT_CURSOR);
   3653 	CASE(MOUSE_FG);
   3654 	CASE(MOUSE_BG);
   3655 #if OPT_TEK4014
   3656 	CASE(TEK_FG);
   3657 	CASE(TEK_BG);
   3658 #endif
   3659 #if OPT_HIGHLIGHT_COLOR
   3660 	CASE(HIGHLIGHT_BG);
   3661 	CASE(HIGHLIGHT_FG);
   3662 #endif
   3663 #if OPT_TEK4014
   3664 	CASE(TEK_CURSOR);
   3665 #endif
   3666     case OSC_NCOLORS:
   3667 	break;
   3668     }
   3669 #undef CASE
   3670     return result;
   3671 }
   3672 
   3673 static Bool
   3674 ChangeColorsRequest(XtermWidget xw,
   3675 		    int start,
   3676 		    char *names,
   3677 		    int final)
   3678 {
   3679     Bool result = False;
   3680     ScrnColors newColors;
   3681 
   3682     TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
   3683 
   3684     if (GetOldColors(xw)) {
   3685 	int i;
   3686 	int queried = 0;
   3687 
   3688 	newColors.which = 0;
   3689 	for (i = 0; i < NCOLORS; i++) {
   3690 	    newColors.names[i] = NULL;
   3691 	}
   3692 	for (i = start; i < OSC_NCOLORS; i++) {
   3693 	    int ndx = OscToColorIndex((OscTextColors) i);
   3694 	    if (xw->misc.re_verse)
   3695 		ndx = oppositeColor(xw, ndx);
   3696 
   3697 	    if (IsEmpty(names)) {
   3698 		newColors.names[ndx] = NULL;
   3699 	    } else {
   3700 		char *thisName = ((names[0] == ';') ? NULL : names);
   3701 
   3702 		names = strchr(names, ';');
   3703 		if (names != NULL) {
   3704 		    *names++ = '\0';
   3705 		}
   3706 		if (thisName != NULL) {
   3707 		    if (!strcmp(thisName, "?")) {
   3708 			if (ReportColorRequest(xw, ndx, final))
   3709 			    ++queried;
   3710 		    } else if (!xw->work.oldColors->names[ndx]
   3711 			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
   3712 			AllocateTermColor(xw, &newColors, ndx, thisName, False);
   3713 		    }
   3714 		}
   3715 	    }
   3716 	}
   3717 
   3718 	if (newColors.which != 0) {
   3719 	    ChangeColors(xw, &newColors);
   3720 	    UpdateOldColors(xw, &newColors);
   3721 	} else if (queried) {
   3722 	    unparse_end(xw);
   3723 	}
   3724 	result = True;
   3725     }
   3726     return result;
   3727 }
   3728 
   3729 static Bool
   3730 ResetColorsRequest(XtermWidget xw,
   3731 		   int code)
   3732 {
   3733     Bool result = False;
   3734 
   3735     (void) xw;
   3736     (void) code;
   3737 
   3738     TRACE(("ResetColorsRequest code=%d\n", code));
   3739     if (GetOldColors(xw)) {
   3740 	ScrnColors newColors;
   3741 	const char *thisName;
   3742 	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
   3743 
   3744 	if (xw->misc.re_verse)
   3745 	    ndx = oppositeColor(xw, ndx);
   3746 
   3747 	thisName = xw->screen.Tcolors[ndx].resource;
   3748 
   3749 	newColors.which = 0;
   3750 	newColors.names[ndx] = NULL;
   3751 
   3752 	if (thisName != NULL
   3753 	    && xw->work.oldColors->names[ndx] != NULL
   3754 	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
   3755 	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
   3756 
   3757 	    if (newColors.which != 0) {
   3758 		ChangeColors(xw, &newColors);
   3759 		UpdateOldColors(xw, &newColors);
   3760 	    }
   3761 	}
   3762 	result = True;
   3763     }
   3764     return result;
   3765 }
   3766 
   3767 #if OPT_SHIFT_FONTS
   3768 /*
   3769  * Initially, 'source' points to '#' or '?'.
   3770  *
   3771  * Look for an optional sign and optional number.  If those are found, lookup
   3772  * the corresponding menu font entry.
   3773  */
   3774 static int
   3775 ParseShiftedFont(XtermWidget xw, String source, String *target)
   3776 {
   3777     TScreen *screen = TScreenOf(xw);
   3778     int num = screen->menu_font_number;
   3779     int rel = 0;
   3780 
   3781     if (*++source == '+') {
   3782 	rel = 1;
   3783 	source++;
   3784     } else if (*source == '-') {
   3785 	rel = -1;
   3786 	source++;
   3787     }
   3788 
   3789     if (isdigit(CharOf(*source))) {
   3790 	int val = atoi(source);
   3791 	if (rel > 0)
   3792 	    rel = val;
   3793 	else if (rel < 0)
   3794 	    rel = -val;
   3795 	else
   3796 	    num = val;
   3797     }
   3798 
   3799     if (rel != 0) {
   3800 	num = lookupRelativeFontSize(xw,
   3801 				     screen->menu_font_number, rel);
   3802 
   3803     }
   3804     TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
   3805     *target = source;
   3806     return num;
   3807 }
   3808 
   3809 static void
   3810 QueryFontRequest(XtermWidget xw, String buf, int final)
   3811 {
   3812     if (AllowFontOps(xw, efGetFont)) {
   3813 	TScreen *screen = TScreenOf(xw);
   3814 	Bool success = True;
   3815 	int num;
   3816 	String base = buf + 1;
   3817 	const char *name = NULL;
   3818 
   3819 	num = ParseShiftedFont(xw, buf, &buf);
   3820 	if (num < 0
   3821 	    || num > fontMenu_lastBuiltin) {
   3822 	    Bell(xw, XkbBI_MinorError, 0);
   3823 	    success = False;
   3824 	} else {
   3825 #if OPT_RENDERFONT
   3826 	    if (UsingRenderFont(xw)) {
   3827 		name = getFaceName(xw, False);
   3828 	    } else
   3829 #endif
   3830 	    if ((name = screen->MenuFontName(num)) == NULL) {
   3831 		success = False;
   3832 	    }
   3833 	}
   3834 
   3835 	unparseputc1(xw, ANSI_OSC);
   3836 	unparseputs(xw, "50");
   3837 
   3838 	if (success) {
   3839 	    unparseputc(xw, ';');
   3840 	    if (buf >= base) {
   3841 		/* identify the font-entry, unless it is the current one */
   3842 		if (*buf != '\0') {
   3843 		    char temp[10];
   3844 
   3845 		    unparseputc(xw, '#');
   3846 		    sprintf(temp, "%d", num);
   3847 		    unparseputs(xw, temp);
   3848 		    if (*name != '\0')
   3849 			unparseputc(xw, ' ');
   3850 		}
   3851 	    }
   3852 	    unparseputs(xw, name);
   3853 	}
   3854 
   3855 	unparseputc1(xw, final);
   3856 	unparse_end(xw);
   3857     }
   3858 }
   3859 
   3860 static void
   3861 ChangeFontRequest(XtermWidget xw, String buf)
   3862 {
   3863     if (AllowFontOps(xw, efSetFont)) {
   3864 	TScreen *screen = TScreenOf(xw);
   3865 	Bool success = True;
   3866 	int num;
   3867 	VTFontNames fonts;
   3868 	char *name;
   3869 
   3870 	/*
   3871 	 * If the font specification is a "#", followed by an optional sign and
   3872 	 * optional number, lookup the corresponding menu font entry.
   3873 	 *
   3874 	 * Further, if the "#", etc., is followed by a font name, use that
   3875 	 * to load the font entry.
   3876 	 */
   3877 	if (*buf == '#') {
   3878 	    num = ParseShiftedFont(xw, buf, &buf);
   3879 
   3880 	    if (num < 0
   3881 		|| num > fontMenu_lastBuiltin) {
   3882 		Bell(xw, XkbBI_MinorError, 0);
   3883 		success = False;
   3884 	    } else {
   3885 		/*
   3886 		 * Skip past the optional number, and any whitespace to look
   3887 		 * for a font specification within the control.
   3888 		 */
   3889 		while (isdigit(CharOf(*buf))) {
   3890 		    ++buf;
   3891 		}
   3892 		while (isspace(CharOf(*buf))) {
   3893 		    ++buf;
   3894 		}
   3895 #if OPT_RENDERFONT
   3896 		if (UsingRenderFont(xw)) {
   3897 		    /* EMPTY */
   3898 		    /* there is only one font entry to load */
   3899 		    ;
   3900 		} else
   3901 #endif
   3902 		{
   3903 		    /*
   3904 		     * Normally there is no font specified in the control.
   3905 		     * But if there is, simply overwrite the font entry.
   3906 		     */
   3907 		    if (*buf == '\0') {
   3908 			if ((buf = screen->MenuFontName(num)) == NULL) {
   3909 			    success = False;
   3910 			}
   3911 		    }
   3912 		}
   3913 	    }
   3914 	} else {
   3915 	    num = screen->menu_font_number;
   3916 	}
   3917 	name = x_strtrim(buf);
   3918 	if (screen->EscapeFontName()) {
   3919 	    FREE_STRING(screen->EscapeFontName());
   3920 	    screen->EscapeFontName() = NULL;
   3921 	}
   3922 	if (success && !IsEmpty(name)) {
   3923 #if OPT_RENDERFONT
   3924 	    if (UsingRenderFont(xw)) {
   3925 		setFaceName(xw, name);
   3926 		xtermUpdateFontInfo(xw, True);
   3927 	    } else
   3928 #endif
   3929 	    {
   3930 		memset(&fonts, 0, sizeof(fonts));
   3931 		fonts.f_n = name;
   3932 		if (SetVTFont(xw, num, True, &fonts)
   3933 		    && num == screen->menu_font_number
   3934 		    && num != fontMenu_fontescape) {
   3935 		    screen->EscapeFontName() = x_strdup(name);
   3936 		}
   3937 	    }
   3938 	} else {
   3939 	    Bell(xw, XkbBI_MinorError, 0);
   3940 	}
   3941 	update_font_escape();
   3942 	free(name);
   3943     }
   3944 }
   3945 #endif /* OPT_SHIFT_FONTS */
   3946 
   3947 /***====================================================================***/
   3948 
   3949 static void
   3950 report_allowed_ops(XtermWidget xw, int final)
   3951 {
   3952     TScreen *screen = TScreenOf(xw);
   3953     char delimiter = ';';
   3954 
   3955     unparseputc1(xw, ANSI_OSC);
   3956     unparseputn(xw, OSC_AllowedOps);
   3957 
   3958 #define CASE(name) \
   3959     if (screen->name) { \
   3960 	unparseputc(xw, delimiter); \
   3961 	unparseputs(xw, #name); \
   3962 	delimiter = ','; \
   3963     }
   3964     CASE(allowColorOps);
   3965     CASE(allowFontOps);
   3966     CASE(allowMouseOps);
   3967     CASE(allowPasteControls);
   3968     CASE(allowTcapOps);
   3969     CASE(allowTitleOps);
   3970     CASE(allowWindowOps);
   3971     (void) delimiter;
   3972 #undef CASE
   3973 
   3974     unparseputc1(xw, final);
   3975 }
   3976 
   3977 static void
   3978 report_disallowed_ops(XtermWidget xw, char *value, int final)
   3979 {
   3980     unparseputc1(xw, ANSI_OSC);
   3981     unparseputn(xw, OSC_DisallowedOps);
   3982     unparse_disallowed_ops(xw, value);
   3983     unparseputc1(xw, final);
   3984 }
   3985 
   3986 /***====================================================================***/
   3987 
   3988 void
   3989 do_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
   3990 {
   3991     TScreen *screen = TScreenOf(xw);
   3992     int mode;
   3993     Char *cp;
   3994     int state = 0;
   3995     char *buf = NULL;
   3996     char temp[20];
   3997 #if OPT_ISO_COLORS
   3998     int ansi_colors = 0;
   3999 #endif
   4000     Bool need_data = True;
   4001     Bool optional_data = False;
   4002 
   4003     TRACE(("do_osc %s\n", oscbuf));
   4004 
   4005     (void) screen;
   4006 
   4007     /*
   4008      * Lines should be of the form <OSC> number ; string <ST>, however
   4009      * older xterms can accept <BEL> as a final character.  We will respond
   4010      * with the same final character as the application sends to make this
   4011      * work better with shell scripts, which may have trouble reading an
   4012      * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
   4013      */
   4014     mode = 0;
   4015     for (cp = oscbuf; *cp != '\0'; cp++) {
   4016 	switch (state) {
   4017 	case 0:
   4018 	    if (isdigit(*cp)) {
   4019 		mode = 10 * mode + (*cp - '0');
   4020 		if (mode > 65535) {
   4021 		    TRACE(("do_osc found unknown mode %d\n", mode));
   4022 		    return;
   4023 		}
   4024 		break;
   4025 	    } else {
   4026 		switch (*cp) {
   4027 		case 'I':
   4028 		    xtermLoadIcon(xw, (char *) ++cp);
   4029 		    return;
   4030 		case 'l':
   4031 		    ChangeTitle(xw, (char *) ++cp);
   4032 		    return;
   4033 		case 'L':
   4034 		    ChangeIconName(xw, (char *) ++cp);
   4035 		    return;
   4036 		}
   4037 	    }
   4038 	    /* FALLTHRU */
   4039 	case 1:
   4040 	    if (*cp != ';') {
   4041 		TRACE(("do_osc did not find semicolon offset %lu\n",
   4042 		       (unsigned long) (cp - oscbuf)));
   4043 		return;
   4044 	    }
   4045 	    state = 2;
   4046 	    break;
   4047 	case 2:
   4048 	    buf = (char *) cp;
   4049 	    state = 3;
   4050 	    /* FALLTHRU */
   4051 	default:
   4052 	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
   4053 		switch (mode) {
   4054 		case 0:
   4055 		case 1:
   4056 		case 2:
   4057 		    break;
   4058 		default:
   4059 		    TRACE(("do_osc found nonprinting char %02X offset %lu\n",
   4060 			   CharOf(*cp),
   4061 			   (unsigned long) (cp - oscbuf)));
   4062 		    return;
   4063 		}
   4064 	    }
   4065 	}
   4066     }
   4067 
   4068     /*
   4069      * Check if the palette changed and there are no more immediate changes
   4070      * that could be deferred to the next repaint.
   4071      */
   4072     if (xw->work.palette_changed) {
   4073 	switch (mode) {
   4074 	case OSC_AllowedOps:
   4075 	case OSC_DisallowedOps:
   4076 	case OSC_FontOps:
   4077 	case OSC_NewLogFile:
   4078 	case OSC_SelectionData:
   4079 	case OSC_Unused_30:
   4080 	case OSC_Unused_31:
   4081 	case OSC_Unused_51:
   4082 	case OSC_X_Property:
   4083 	    TRACE(("forced repaint after palette changed\n"));
   4084 	    xw->work.palette_changed = False;
   4085 	    xtermRepaint(xw);
   4086 	    break;
   4087 	default:
   4088 	    xtermNeedSwap(xw, 1);
   4089 	    break;
   4090 	}
   4091     }
   4092 
   4093     /*
   4094      * Most OSC controls other than resets require data.  Handle the others as
   4095      * a special case.
   4096      */
   4097     switch (mode) {
   4098     case OSC_FontOps:
   4099 #if OPT_ISO_COLORS
   4100     case OSC_Reset(OSC_SetAnsiColor):
   4101     case OSC_Reset(OSC_GetAnsiColors):
   4102 	need_data = False;
   4103 	optional_data = True;
   4104 	break;
   4105     case OSC_Reset(OSC_TEXT_FG):
   4106     case OSC_Reset(OSC_TEXT_BG):
   4107     case OSC_Reset(OSC_TEXT_CURSOR):
   4108     case OSC_Reset(OSC_MOUSE_FG):
   4109     case OSC_Reset(OSC_MOUSE_BG):
   4110 #if OPT_HIGHLIGHT_COLOR
   4111     case OSC_Reset(OSC_HIGHLIGHT_BG):
   4112     case OSC_Reset(OSC_HIGHLIGHT_FG):
   4113 #endif
   4114 #if OPT_TEK4014
   4115     case OSC_Reset(OSC_TEK_FG):
   4116     case OSC_Reset(OSC_TEK_BG):
   4117     case OSC_Reset(OSC_TEK_CURSOR):
   4118 #endif
   4119     case OSC_AllowedOps:
   4120 	need_data = False;
   4121 	break;
   4122 #endif
   4123     default:
   4124 	break;
   4125     }
   4126 
   4127     /*
   4128      * Check if we have data when we want, and not when we do not want it.
   4129      * Either way, that is a malformed control sequence, and will be ignored.
   4130      */
   4131     if (IsEmpty(buf)) {
   4132 	if (need_data) {
   4133 	    switch (mode) {
   4134 	    case 0:
   4135 	    case 1:
   4136 	    case 2:
   4137 		buf = strcpy(temp, "xterm");
   4138 		break;
   4139 	    default:
   4140 		TRACE(("do_osc found no data\n"));
   4141 		return;
   4142 	    }
   4143 	} else {
   4144 	    temp[0] = '\0';
   4145 	    buf = temp;
   4146 	}
   4147     } else if (!need_data && !optional_data) {
   4148 	TRACE(("do_osc found unwanted data\n"));
   4149 	return;
   4150     }
   4151 
   4152     switch (mode) {
   4153     case OSC_IconBoth:		/* new icon name and title */
   4154 	ChangeIconName(xw, buf);
   4155 	ChangeTitle(xw, buf);
   4156 	break;
   4157 
   4158     case OSC_IconOnly:		/* new icon name only */
   4159 	ChangeIconName(xw, buf);
   4160 	break;
   4161 
   4162     case OSC_TitleOnly:	/* new title only */
   4163 	ChangeTitle(xw, buf);
   4164 	break;
   4165 
   4166 #ifdef notdef
   4167     case OSC_X_Property:	/* change X property */
   4168 	if (AllowWindowOps(xw, ewSetXprop))
   4169 	    ChangeXprop(buf);
   4170 	break;
   4171 #endif
   4172 #if OPT_ISO_COLORS
   4173     case OSC_GetAnsiColors:
   4174 	ansi_colors = NUM_ANSI_COLORS;
   4175 	/* FALLTHRU */
   4176     case OSC_SetAnsiColor:
   4177 	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
   4178 	    xw->work.palette_changed = True;
   4179 	break;
   4180     case OSC_ColorMode:
   4181 	/* FALLTHRU */
   4182     case OSC_Reset(OSC_ColorMode):
   4183 	TRACE(("parse colorXXMode:%s\n", buf));
   4184 	while (*buf != '\0') {
   4185 	    long which = 0;
   4186 	    long value = 0;
   4187 	    char *next;
   4188 	    if (*buf == ';') {
   4189 		++buf;
   4190 	    } else {
   4191 		which = strtol(buf, &next, 10);
   4192 		if (!PartS2L(buf, next) || (which < 0))
   4193 		    break;
   4194 		buf = next;
   4195 		if (*buf == ';')
   4196 		    ++buf;
   4197 	    }
   4198 	    if (*buf == ';') {
   4199 		++buf;
   4200 	    } else {
   4201 		value = strtol(buf, &next, 10);
   4202 		if (!PartS2L(buf, next) || (value < 0))
   4203 		    break;
   4204 		buf = next;
   4205 		if (*buf == ';')
   4206 		    ++buf;
   4207 	    }
   4208 	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
   4209 	    switch (which) {
   4210 	    case 0:
   4211 		screen->colorBDMode = (value != 0);
   4212 		break;
   4213 	    case 1:
   4214 		screen->colorULMode = (value != 0);
   4215 		break;
   4216 	    case 2:
   4217 		screen->colorBLMode = (value != 0);
   4218 		break;
   4219 	    case 3:
   4220 		screen->colorRVMode = (value != 0);
   4221 		break;
   4222 #if OPT_WIDE_ATTRS
   4223 	    case 4:
   4224 		screen->colorITMode = (value != 0);
   4225 		break;
   4226 #endif
   4227 	    default:
   4228 		TRACE(("...unknown colorXXMode\n"));
   4229 		break;
   4230 	    }
   4231 	}
   4232 	break;
   4233     case OSC_Reset(OSC_GetAnsiColors):
   4234 	ansi_colors = NUM_ANSI_COLORS;
   4235 	/* FALLTHRU */
   4236     case OSC_Reset(OSC_SetAnsiColor):
   4237 	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
   4238 	    xw->work.palette_changed = True;
   4239 	break;
   4240 #endif
   4241     case OSC_TEXT_FG:
   4242     case OSC_TEXT_BG:
   4243     case OSC_TEXT_CURSOR:
   4244     case OSC_MOUSE_FG:
   4245     case OSC_MOUSE_BG:
   4246 #if OPT_HIGHLIGHT_COLOR
   4247     case OSC_HIGHLIGHT_BG:
   4248     case OSC_HIGHLIGHT_FG:
   4249 #endif
   4250 #if OPT_TEK4014
   4251     case OSC_TEK_FG:
   4252     case OSC_TEK_BG:
   4253     case OSC_TEK_CURSOR:
   4254 #endif
   4255 	if (xw->misc.dynamicColors) {
   4256 	    ChangeColorsRequest(xw, mode, buf, final);
   4257 	}
   4258 	break;
   4259     case OSC_Reset(OSC_TEXT_FG):
   4260     case OSC_Reset(OSC_TEXT_BG):
   4261     case OSC_Reset(OSC_TEXT_CURSOR):
   4262     case OSC_Reset(OSC_MOUSE_FG):
   4263     case OSC_Reset(OSC_MOUSE_BG):
   4264 #if OPT_HIGHLIGHT_COLOR
   4265     case OSC_Reset(OSC_HIGHLIGHT_BG):
   4266     case OSC_Reset(OSC_HIGHLIGHT_FG):
   4267 #endif
   4268 #if OPT_TEK4014
   4269     case OSC_Reset(OSC_TEK_FG):
   4270     case OSC_Reset(OSC_TEK_BG):
   4271     case OSC_Reset(OSC_TEK_CURSOR):
   4272 #endif
   4273 	if (xw->misc.dynamicColors) {
   4274 	    ResetColorsRequest(xw, mode);
   4275 	}
   4276 	break;
   4277 
   4278     case OSC_SetupPointer:
   4279 	xtermSetupPointer(xw, buf);
   4280 	break;
   4281 
   4282 #ifdef ALLOWLOGGING
   4283     case OSC_NewLogFile:
   4284 #ifdef ALLOWLOGFILECHANGES
   4285 	/*
   4286 	 * Warning, enabling this feature allows people to overwrite
   4287 	 * arbitrary files accessible to the person running xterm.
   4288 	 */
   4289 	if (strcmp(buf, "?")) {
   4290 	    char *bp;
   4291 	    if ((bp = x_strdup(buf)) != NULL) {
   4292 		free(screen->logfile);
   4293 		screen->logfile = bp;
   4294 		break;
   4295 	    }
   4296 	}
   4297 #endif
   4298 	Bell(xw, XkbBI_Info, 0);
   4299 	Bell(xw, XkbBI_Info, 0);
   4300 	break;
   4301 #endif /* ALLOWLOGGING */
   4302 
   4303     case OSC_FontOps:
   4304 #if OPT_SHIFT_FONTS
   4305 	if (*buf == '?') {
   4306 	    QueryFontRequest(xw, buf, final);
   4307 	} else if (xw->misc.shift_fonts) {
   4308 	    ChangeFontRequest(xw, buf);
   4309 	}
   4310 #endif /* OPT_SHIFT_FONTS */
   4311 	break;
   4312 
   4313 #if OPT_PASTE64
   4314     case OSC_SelectionData:
   4315 	ManipulateSelectionData(xw, screen, buf, final);
   4316 	break;
   4317 #endif
   4318 
   4319     case OSC_AllowedOps:	/* XTQALLOWED */
   4320 	report_allowed_ops(xw, final);
   4321 	break;
   4322 
   4323     case OSC_DisallowedOps:	/* XTQDISALLOWED */
   4324 	report_disallowed_ops(xw, buf, final);
   4325 	break;
   4326 
   4327     case OSC_Unused_30:
   4328     case OSC_Unused_31:
   4329     case OSC_Unused_51:
   4330     default:
   4331 	TRACE(("do_osc - unrecognized code\n"));
   4332 	break;
   4333     }
   4334     unparse_end(xw);
   4335 }
   4336 
   4337 /*
   4338  * Parse one nibble of a hex byte from the OSC string.  We have removed the
   4339  * string-terminator (replacing it with a null), so the only other delimiter
   4340  * that is expected is semicolon.  Ignore other characters (Ray Neuman says
   4341  * "real" terminals accept commas in the string definitions).
   4342  */
   4343 static int
   4344 udk_value(const char **cp)
   4345 {
   4346     int result = -1;
   4347 
   4348     for (;;) {
   4349 	int c;
   4350 
   4351 	if ((c = **cp) != '\0')
   4352 	    *cp = *cp + 1;
   4353 	if (c == ';' || c == '\0')
   4354 	    break;
   4355 	if ((result = x_hex2int(c)) >= 0)
   4356 	    break;
   4357     }
   4358 
   4359     return result;
   4360 }
   4361 
   4362 void
   4363 reset_decudk(XtermWidget xw)
   4364 {
   4365     int n;
   4366     for (n = 0; n < MAX_UDK; n++) {
   4367 	FreeAndNull(xw->work.user_keys[n].str);
   4368 	xw->work.user_keys[n].len = 0;
   4369     }
   4370 }
   4371 
   4372 /*
   4373  * Parse the data for DECUDK (user-defined keys).
   4374  */
   4375 static void
   4376 parse_decudk(XtermWidget xw, const char *cp)
   4377 {
   4378     while (*cp) {
   4379 	const char *base = cp;
   4380 	char *str = malloc(strlen(cp) + 3);
   4381 	unsigned key = 0;
   4382 	int len = 0;
   4383 
   4384 	if (str == NULL)
   4385 	    break;
   4386 
   4387 	while (isdigit(CharOf(*cp)))
   4388 	    key = (key * 10) + (unsigned) (*cp++ - '0');
   4389 
   4390 	if (*cp == '/') {
   4391 	    int lo, hi;
   4392 
   4393 	    cp++;
   4394 	    while ((hi = udk_value(&cp)) >= 0
   4395 		   && (lo = udk_value(&cp)) >= 0) {
   4396 		str[len++] = (char) ((hi << 4) | lo);
   4397 	    }
   4398 	}
   4399 	if (len > 0 && key < MAX_UDK) {
   4400 	    str[len] = '\0';
   4401 	    free(xw->work.user_keys[key].str);
   4402 	    xw->work.user_keys[key].str = str;
   4403 	    xw->work.user_keys[key].len = len;
   4404 	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
   4405 	} else {
   4406 	    free(str);
   4407 	}
   4408 	if (*cp == ';')
   4409 	    cp++;
   4410 	if (cp == base)		/* badly-formed sequence - bail out */
   4411 	    break;
   4412     }
   4413 }
   4414 
   4415 /*
   4416  * Parse numeric parameters.  Normally we use a state machine to simplify
   4417  * interspersing with control characters, but have the string already.
   4418  */
   4419 void
   4420 parse_ansi_params(ANSI *params, const char **string)
   4421 {
   4422     const char *cp = *string;
   4423     ParmType nparam = 0;
   4424     int last_empty = 1;
   4425 
   4426     memset(params, 0, sizeof(*params));
   4427     while (*cp != '\0') {
   4428 	Char ch = CharOf(*cp++);
   4429 
   4430 	if (isdigit(ch)) {
   4431 	    last_empty = 0;
   4432 	    if (nparam < NPARAM) {
   4433 		params->a_param[nparam] =
   4434 		    (ParmType) ((params->a_param[nparam] * 10)
   4435 				+ (ch - '0'));
   4436 	    }
   4437 	} else if (ch == ';') {
   4438 	    last_empty = 1;
   4439 	    nparam++;
   4440 	} else if (ch < 32) {
   4441 	    /* EMPTY */ ;
   4442 	} else {
   4443 	    /* should be 0x30 to 0x7e */
   4444 	    params->a_final = ch;
   4445 	    break;
   4446 	}
   4447     }
   4448 
   4449     *string = cp;
   4450     if (!last_empty)
   4451 	nparam++;
   4452     if (nparam > NPARAM)
   4453 	params->a_nparam = NPARAM;
   4454     else
   4455 	params->a_nparam = nparam;
   4456 }
   4457 
   4458 #if OPT_TRACE
   4459 #define SOFT_WIDE 10
   4460 #define SOFT_HIGH 20
   4461 
   4462 static void
   4463 parse_decdld(ANSI *params, const char *string)
   4464 {
   4465     char DscsName[8];
   4466     int len;
   4467     int Pfn = params->a_param[0];
   4468     int Pcn = params->a_param[1];
   4469     int Pe = params->a_param[2];
   4470     int Pcmw = params->a_param[3];
   4471     int Pw = params->a_param[4];
   4472     int Pt = params->a_param[5];
   4473     int Pcmh = params->a_param[6];
   4474     int Pcss = params->a_param[7];
   4475 
   4476     int start_char = Pcn + 0x20;
   4477     int char_wide = ((Pcmw == 0)
   4478 		     ? (Pcss ? 6 : 10)
   4479 		     : (Pcmw > 4
   4480 			? Pcmw
   4481 			: (Pcmw + 3)));
   4482     int char_high = ((Pcmh == 0)
   4483 		     ? ((Pcmw >= 2 && Pcmw <= 4)
   4484 			? 10
   4485 			: 20)
   4486 		     : Pcmh);
   4487     Char ch;
   4488     Char bits[SOFT_HIGH][SOFT_WIDE];
   4489     Bool first = True;
   4490     Bool prior = False;
   4491     int row = 0, col = 0;
   4492 
   4493     TRACE(("Parsing DECDLD\n"));
   4494     TRACE(("  font number   %d\n", Pfn));
   4495     TRACE(("  starting char %d\n", Pcn));
   4496     TRACE(("  erase control %d\n", Pe));
   4497     TRACE(("  char-width    %d\n", Pcmw));
   4498     TRACE(("  font-width    %d\n", Pw));
   4499     TRACE(("  text/full     %d\n", Pt));
   4500     TRACE(("  char-height   %d\n", Pcmh));
   4501     TRACE(("  charset-size  %d\n", Pcss));
   4502 
   4503     if (Pfn > 1
   4504 	|| Pcn > 95
   4505 	|| Pe > 2
   4506 	|| Pcmw > 10
   4507 	|| Pcmw == 1
   4508 	|| Pt > 2
   4509 	|| Pcmh > 20
   4510 	|| Pcss > 1
   4511 	|| char_wide > SOFT_WIDE
   4512 	|| char_high > SOFT_HIGH) {
   4513 	TRACE(("DECDLD illegal parameter\n"));
   4514 	return;
   4515     }
   4516 
   4517     len = 0;
   4518     while (*string != '\0') {
   4519 	ch = CharOf(*string++);
   4520 	if (ch >= ANSI_SPA && ch <= 0x2f) {
   4521 	    if (len < 2)
   4522 		DscsName[len++] = (char) ch;
   4523 	} else if (ch >= 0x30 && ch <= 0x7e) {
   4524 	    DscsName[len++] = (char) ch;
   4525 	    break;
   4526 	}
   4527     }
   4528     DscsName[len] = 0;
   4529     TRACE(("  Dscs name     '%s'\n", DscsName));
   4530 
   4531     TRACE(("  character matrix %dx%d\n", char_high, char_wide));
   4532     while (*string != '\0') {
   4533 	if (first) {
   4534 	    TRACE(("Char %d:\n", start_char));
   4535 	    if (prior) {
   4536 		for (row = 0; row < char_high; ++row) {
   4537 		    TRACE(("%.*s\n", char_wide, bits[row]));
   4538 		}
   4539 	    }
   4540 	    prior = False;
   4541 	    first = False;
   4542 	    for (row = 0; row < char_high; ++row) {
   4543 		for (col = 0; col < char_wide; ++col) {
   4544 		    bits[row][col] = '.';
   4545 		}
   4546 	    }
   4547 	    row = col = 0;
   4548 	}
   4549 	ch = CharOf(*string++);
   4550 	if (ch >= 0x3f && ch <= 0x7e) {
   4551 	    int n;
   4552 
   4553 	    ch = CharOf(ch - 0x3f);
   4554 	    for (n = 0; n < 6; ++n) {
   4555 		bits[row + n][col] = CharOf((ch & xBIT(n)) ? '*' : '.');
   4556 	    }
   4557 	    col += 1;
   4558 	    prior = True;
   4559 	} else if (ch == '/') {
   4560 	    row += 6;
   4561 	    col = 0;
   4562 	} else if (ch == ';') {
   4563 	    first = True;
   4564 	    ++start_char;
   4565 	}
   4566     }
   4567 }
   4568 #else
   4569 #define parse_decdld(p,q)	/* nothing */
   4570 #endif
   4571 
   4572 static const char *
   4573 skip_params(const char *cp)
   4574 {
   4575     while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
   4576 	++cp;
   4577     return cp;
   4578 }
   4579 
   4580 #if OPT_MOD_FKEYS || OPT_DEC_RECTOPS || (OPT_VT525_COLORS && OPT_ISO_COLORS)
   4581 static int
   4582 parse_int_param(const char **cp)
   4583 {
   4584     Boolean found = False;
   4585     int result = 0;
   4586     const char *s = *cp;
   4587     while (*s != '\0') {
   4588 	if (*s == ';') {
   4589 	    ++s;
   4590 	    break;
   4591 	} else if (*s >= '0' && *s <= '9') {
   4592 	    result = (result * 10) + (*s++ - '0');
   4593 	    found = True;
   4594 	} else {
   4595 	    s += strlen(s);
   4596 	}
   4597     }
   4598     TRACE(("parse-int \"%s\" ->%d, %#x->\"%s\"\n", *cp, result, result, s));
   4599     *cp = s;
   4600     return found ? result : -1;
   4601 }
   4602 #endif
   4603 
   4604 #if OPT_DEC_RECTOPS
   4605 static int
   4606 parse_chr_param(const char **cp)
   4607 {
   4608     int result = 0;
   4609     const char *s = *cp;
   4610     if (*s != '\0') {
   4611 	if ((result = CharOf(*s++)) != 0) {
   4612 	    if (*s == ';') {
   4613 		++s;
   4614 	    } else if (*s != '\0') {
   4615 		result = 0;
   4616 	    }
   4617 	}
   4618     }
   4619     TRACE(("parse-chr %s ->%#x, %#x->%s\n", *cp, result, result, s));
   4620     *cp = s;
   4621     return result;
   4622 }
   4623 
   4624 #if OPT_TRACE
   4625 #define done_DECCIR()	do { TRACE(("...quit DECCIR @%d\n", __LINE__)); return; } while(0)
   4626 #else
   4627 #define done_DECCIR()	return
   4628 #endif
   4629 
   4630 static void
   4631 restore_DECCIR(XtermWidget xw, const char *cp)
   4632 {
   4633     TScreen *screen = TScreenOf(xw);
   4634     int value;
   4635 
   4636     /* row */
   4637     if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
   4638 	done_DECCIR();
   4639     screen->cur_row = (value - 1);
   4640 
   4641     /* column */
   4642     if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
   4643 	done_DECCIR();
   4644     screen->cur_col = (value - 1);
   4645 
   4646     /* page */
   4647     if (parse_int_param(&cp) != 1)
   4648 	done_DECCIR();
   4649 
   4650     /* rendition */
   4651     if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) {
   4652 	if (value & 0x10) {
   4653 	    /*
   4654 	     * VT420 is documented for bit 5 always reset; VT520/VT525 are not
   4655 	     * documented, but do use the bit for setting invisible mode.
   4656 	     */
   4657 	    if (screen->vtXX_level <= 4)
   4658 		done_DECCIR();
   4659 	} else if (!(value & 0x40)) {
   4660 	    done_DECCIR();
   4661 	}
   4662     }
   4663     UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
   4664     xw->flags |= (value & 16) ? INVISIBLE : 0;
   4665     xw->flags |= (value & 8) ? INVERSE : 0;
   4666     xw->flags |= (value & 4) ? BLINK : 0;
   4667     xw->flags |= (value & 2) ? UNDERLINE : 0;
   4668     xw->flags |= (value & 1) ? BOLD : 0;
   4669 
   4670     /* attributes */
   4671     if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
   4672 	done_DECCIR();
   4673     screen->protected_mode &= ~DEC_PROTECT;
   4674     screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
   4675 
   4676     /* flags */
   4677     if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
   4678 	done_DECCIR();
   4679     screen->do_wrap = (value & 8) ? True : False;
   4680     screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
   4681     UIntClr(xw->flags, ORIGIN);
   4682     xw->flags |= (value & 1) ? ORIGIN : 0;
   4683 
   4684     if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
   4685 	done_DECCIR();
   4686     screen->curgl = (Char) value;
   4687 
   4688     if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
   4689 	done_DECCIR();
   4690     screen->curgr = (Char) value;
   4691 
   4692     /* character-set size */
   4693     if (parse_chr_param(&cp) == 0xffff)		/* FIXME: limit SCS? */
   4694 	done_DECCIR();
   4695 
   4696     /* SCS designators */
   4697     for (value = 0; value < NUM_GSETS; ++value) {
   4698 	if (*cp == '\0') {
   4699 	    done_DECCIR();
   4700 	} else if (strchr("%&\"", *cp) != NULL) {
   4701 	    int prefix = *cp++;
   4702 	    xtermDecodeSCS(xw, value, 0, prefix, *cp);
   4703 	} else {
   4704 	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
   4705 	}
   4706 	cp++;
   4707     }
   4708 
   4709     TRACE(("...done DECCIR\n"));
   4710 }
   4711 
   4712 static void
   4713 restore_DECTABSR(XtermWidget xw, const char *cp)
   4714 {
   4715     int stop = 0;
   4716     Bool fail = False;
   4717 
   4718     TabZonk(xw->tabs);
   4719     while (*cp != '\0' && !fail) {
   4720 	if ((*cp) >= '0' && (*cp) <= '9') {
   4721 	    stop = (stop * 10) + ((*cp) - '0');
   4722 	} else if (*cp == '/') {
   4723 	    --stop;
   4724 	    if (OkTAB(stop)) {
   4725 		TabSet(xw->tabs, stop);
   4726 	    }
   4727 	    stop = 0;
   4728 	} else {
   4729 	    fail = True;
   4730 	}
   4731 	++cp;
   4732     }
   4733     --stop;
   4734     if (OkTAB(stop))
   4735 	TabSet(xw->tabs, stop);
   4736 
   4737     TRACE(("...done DECTABSR\n"));
   4738 }
   4739 #endif /* OPT_DEC_RECTOPS */
   4740 
   4741 /*
   4742  * VT510 and VT520 reference manual have the same explanation for Pn (params),
   4743  * but it does not agree with the possible values for Dscs because it refers
   4744  * to "ISO Latin-7" (ISO 8859-13 aka "Baltic Rim"), and omits ISO Greek
   4745  * (ISO 8859-7):
   4746  *
   4747  * ------------------------------------------------------------------------
   4748  * Pn  Meaning
   4749  * ------------------------------------------------------------------------
   4750  * 0   DEC, ISO Latin-1, ISO Latin-2
   4751  * 1   ISO Latin-5, ISO Latin-7, ISO Cyrillic, ISO Hebrew
   4752  * ------------------------------------------------------------------------
   4753  *
   4754  * versus
   4755  *
   4756  * ------------------------------------------------------------------------
   4757  * Dscs   Character Set
   4758  * ------------------------------------------------------------------------
   4759  * %5     DEC Supplemental
   4760  * "?     DEC Greek
   4761  * "4     DEC Hebrew
   4762  * %0     DEC Turkish
   4763  * &4     DEC Cyrillic
   4764  * <      User-preferred Supplemental
   4765  * A      ISO Latin-1 Supplemental
   4766  * B      ISO Latin-2 Supplemental
   4767  * F      ISO Greek Supplemental
   4768  * H      ISO Hebrew Supplemental
   4769  * M      ISO Latin-5 Supplemental
   4770  * L      ISO Latin-Cyrillic
   4771  * ------------------------------------------------------------------------
   4772  *
   4773  * DEC 070, page 5-123 explains that Pn ("Ps" in the text) selects 94 or 96
   4774  * character sets (0 or 1, respectively), and on the next page states that
   4775  * the valid combinations are 0 (DEC Supplemental) and 1 (ISO Latin-1
   4776  * supplemental).  The document comments in regard to LS0 that (applications)
   4777  * should not assume that they can use 96-character sets for G0, but that it
   4778  * is possible to do this using UPSS.
   4779  *
   4780  * The VT510/VT520 reference manuals under SCS Select Character Set show
   4781  * a list of 94- and 96-character sets with "DEC" and "NRCS" as 94-characters,
   4782  * and the "ISO" as 96-characters.  A few 94-character sets are added, based
   4783  * on testing VT520/VT525 that shows that DEC Special Graphics also is allowed.
   4784  */
   4785 static Bool
   4786 decode_upss(XtermWidget xw, const char *cp, char psarg, DECNRCM_codes * upss)
   4787 {
   4788     /* *INDENT-OFF* */
   4789     static const struct {
   4790 	DECNRCM_codes code;
   4791 	int params;	/* 0 for 94-characters, 1 for 96-characters */
   4792 	int prefix;
   4793 	int suffix;
   4794 	int min_level;
   4795 	int max_level;
   4796     } upss_table[] = {
   4797 	{ DFT_UPSS,               0,  '%', '5', 3, 9 },
   4798     	{ nrc_ASCII,              0,  0,   'A', 1, 9 },	/* undocumented */
   4799     	{ nrc_DEC_Spec_Graphic,   0,  0,   '0', 1, 9 },	/* undocumented */
   4800     	{ nrc_DEC_Technical,      0,  0,   '>', 3, 9 },	/* undocumented */
   4801 	{ nrc_DEC_Greek_Supp,     0,  '"', '?', 5, 9 },
   4802 	{ nrc_DEC_Hebrew_Supp,    0,  '"', '4', 5, 9 },
   4803 	{ nrc_DEC_Turkish_Supp,   0,  '%', '0', 5, 9 },
   4804 	{ nrc_DEC_Cyrillic,       0,  '&', '4', 5, 9 },
   4805 	{ ALT_UPSS,               1,  0,   'A', 3, 9 },
   4806 	{ nrc_ISO_Latin_2_Supp,   1,  0,   'B', 5, 9 },
   4807 	{ nrc_ISO_Greek_Supp,     1,  0,   'F', 5, 9 },
   4808 	{ nrc_ISO_Hebrew_Supp,    1,  0,   'H', 5, 9 },
   4809 	{ nrc_ISO_Latin_5_Supp,   1,  0,   'M', 5, 9 },
   4810 	{ nrc_ISO_Latin_Cyrillic, 1,  0,   'L', 5, 9 },
   4811     };
   4812     /* *INDENT-ON* */
   4813     TScreen *screen = TScreenOf(xw);
   4814     Bool result = False;
   4815 
   4816     *upss = nrc_ASCII;
   4817     if (screen->vtXX_level >= 3) {
   4818 	Cardinal n;
   4819 	for (n = 0; n < XtNumber(upss_table); ++n) {
   4820 	    if (((int) psarg - '0') != upss_table[n].params)
   4821 		continue;
   4822 
   4823 	    if (cp[1] == '\0') {
   4824 		if (upss_table[n].suffix != cp[0])
   4825 		    continue;
   4826 	    } else if (cp[2] == '\0') {
   4827 		if (upss_table[n].prefix != cp[0])
   4828 		    continue;
   4829 		if (upss_table[n].suffix != cp[1])
   4830 		    continue;
   4831 	    } else {
   4832 		continue;
   4833 	    }
   4834 
   4835 	    result = True;
   4836 	    *upss = upss_table[n].code;
   4837 	    if (*upss == DFT_UPSS) {
   4838 		TRACE(("DECAUPSS (default)\n"));
   4839 	    } else if (*upss == ALT_UPSS) {
   4840 		TRACE(("DECAUPSS (alternate)\n"));
   4841 	    }
   4842 	    break;
   4843 	}
   4844 	TRACE(("DECAUPSS %ssuccessful %s\n",
   4845 	       result ? "" : "not ", visibleScsCode(*upss)));
   4846     }
   4847     return result;
   4848 }
   4849 
   4850 void
   4851 do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
   4852 {
   4853     TScreen *screen = TScreenOf(xw);
   4854     char reply[BUFSIZ];
   4855     const char *cp = (const char *) dcsbuf;
   4856     Bool okay;
   4857     ANSI params;
   4858     char psarg = '0';
   4859 #if OPT_VT525_COLORS && OPT_ISO_COLORS
   4860     const char *cp2;
   4861 #endif
   4862 #if (OPT_VT525_COLORS && OPT_ISO_COLORS) || OPT_MOD_FKEYS
   4863     int ival;
   4864 #endif
   4865 
   4866     TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
   4867 
   4868     if (dcslen != strlen(cp))
   4869 	/* shouldn't have nulls in the string */
   4870 	return;
   4871 
   4872     switch (*cp) {		/* intermediate character, or parameter */
   4873     case '$':			/* DECRQSS */
   4874 	okay = True;
   4875 
   4876 	cp++;
   4877 	if (*cp == 'q') {
   4878 	    *reply = '\0';
   4879 	    cp++;
   4880 	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
   4881 		TRACE(("DECRQSS -> DECSCA\n"));
   4882 		sprintf(reply, "%d%s",
   4883 			(screen->protected_mode == DEC_PROTECT)
   4884 			&& (xw->flags & PROTECTED) ? 1 : 0,
   4885 			cp);
   4886 	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
   4887 		if (screen->vtXX_level < 2) {
   4888 		    /* actually none of DECRQSS is valid for vt100's */
   4889 		    break;
   4890 		}
   4891 		TRACE(("DECRQSS -> DECSCL\n"));
   4892 		sprintf(reply, "%d%s%s",
   4893 			(screen->vtXX_level ?
   4894 			 screen->vtXX_level : 1) + 60,
   4895 			(screen->control_eight_bits
   4896 			 ? ";0" : ";1"),
   4897 			cp);
   4898 	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
   4899 		TRACE(("DECRQSS -> DECSTBM\n"));
   4900 		sprintf(reply, "%d;%dr",
   4901 			screen->top_marg + 1,
   4902 			screen->bot_marg + 1);
   4903 	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
   4904 		if (screen->vtXX_level >= 4) {	/* VT420 */
   4905 		    TRACE(("DECRQSS -> DECSLRM\n"));
   4906 		    sprintf(reply, "%d;%ds",
   4907 			    screen->lft_marg + 1,
   4908 			    screen->rgt_marg + 1);
   4909 		} else {
   4910 		    okay = False;
   4911 		}
   4912 	    } else if (!strcmp(cp, "m")) {	/* SGR */
   4913 		TRACE(("DECRQSS -> SGR\n"));
   4914 		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
   4915 		strcat(reply, "m");
   4916 	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
   4917 		int code = STEADY_BLOCK;
   4918 		if (isCursorUnderline(screen))
   4919 		    code = STEADY_UNDERLINE;
   4920 		else if (isCursorBar(screen))
   4921 		    code = STEADY_BAR;
   4922 #if OPT_BLINK_CURS
   4923 		if (screen->cursor_blink_esc != 0)
   4924 		    code -= 1;
   4925 #endif
   4926 		TRACE(("reply DECSCUSR\n"));
   4927 		sprintf(reply, "%d%s", code, cp);
   4928 	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
   4929 		sprintf(reply, "%d%s",
   4930 			((screen->max_row > 24) ? screen->max_row : 24),
   4931 			cp);
   4932 		TRACE(("reply DECSLPP\n"));
   4933 	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
   4934 		TRACE(("reply DECSCPP\n"));
   4935 		sprintf(reply, "%d%s",
   4936 			((xw->flags & IN132COLUMNS) ? 132 : 80),
   4937 			cp);
   4938 	    } else
   4939 #if OPT_STATUS_LINE
   4940 	    if (!strcmp(cp, "$}")) {	/* DECSASD */
   4941 		TRACE(("reply DECSASD\n"));
   4942 		sprintf(reply, "%d%s",
   4943 			screen->status_active,
   4944 			cp);
   4945 	    } else if (!strcmp(cp, "$~")) {	/* DECSSDT */
   4946 		TRACE(("reply DECSASD\n"));
   4947 		sprintf(reply, "%d%s",
   4948 			screen->status_type,
   4949 			cp);
   4950 	    } else
   4951 #endif
   4952 #if OPT_DEC_RECTOPS
   4953 	    if (!strcmp(cp, "*x")) {	/* DECSACE */
   4954 		TRACE(("reply DECSACE\n"));
   4955 		sprintf(reply, "%d%s",
   4956 			screen->cur_decsace,
   4957 			cp);
   4958 	    } else
   4959 #endif
   4960 	    if (!strcmp(cp, "*|")) {	/* DECSNLS */
   4961 		TRACE(("reply DECSNLS\n"));
   4962 		sprintf(reply, "%d%s",
   4963 			screen->max_row + 1,
   4964 			cp);
   4965 	    } else
   4966 #if OPT_VT525_COLORS && OPT_ISO_COLORS
   4967 		if (screen->terminal_id == 525
   4968 		    && !strcmp((cp2 = skip_params(cp)), ",}")) {	/* DECATC */
   4969 		ival = parse_int_param(&cp);
   4970 		TRACE(("reply DECATC:%s\n", cp));
   4971 		if (ival >= 0 && ival < 16 && *cp2 == ',') {
   4972 		    sprintf(reply, "%d;%d;%d%s", ival,
   4973 			    screen->alt_colors[ival].fg,
   4974 			    screen->alt_colors[ival].bg,
   4975 			    cp2);
   4976 		} else {
   4977 		    okay = False;
   4978 		}
   4979 	    } else if (screen->terminal_id == 525
   4980 		       && !strcmp((cp2 = skip_params(cp)), "){")) {	/* DECSTGLT */
   4981 		TRACE(("reply DECSTGLT:%s\n", cp));
   4982 		sprintf(reply, "%d%s",
   4983 			3,	/* ANSI SGR color */
   4984 			cp);
   4985 	    } else if (screen->terminal_id == 525
   4986 		       && !strcmp((cp2 = skip_params(cp)), ",|")) {	/* DECAC */
   4987 		ival = parse_int_param(&cp);
   4988 		TRACE(("reply DECAC\n"));
   4989 		switch (ival) {
   4990 		case 1:	/* normal text */
   4991 		    sprintf(reply, "%d,%d%s",
   4992 			    screen->assigned_fg,
   4993 			    screen->assigned_bg,
   4994 			    cp2);
   4995 		    break;
   4996 		case 2:	/* window frame (not implemented) */
   4997 		    /* FALLTHRU */
   4998 		default:
   4999 		    okay = False;
   5000 		    break;
   5001 		}
   5002 	    } else
   5003 #endif
   5004 #if OPT_MOD_FKEYS
   5005 	    if (*cp == '>' && !strcmp(skip_params(1 + cp), "m")) {	/* XTQMODKEYS */
   5006 		++cp;
   5007 		okay = True;
   5008 		ival = parse_int_param(&cp);
   5009 #define GET_MOD_FKEYS(field) xw->keyboard.modify_now.field
   5010 #define FMT_MOD_FKEYS(field) sprintf(reply, ">%d;%dm", ival, GET_MOD_FKEYS(field))
   5011 		switch (ival) {
   5012 		case 0:
   5013 		    FMT_MOD_FKEYS(allow_keys);
   5014 		    break;
   5015 		case 1:
   5016 		    FMT_MOD_FKEYS(cursor_keys);
   5017 		    break;
   5018 		case 2:
   5019 		    FMT_MOD_FKEYS(function_keys);
   5020 		    break;
   5021 		case 4:
   5022 		    FMT_MOD_FKEYS(other_keys);
   5023 		    break;
   5024 		default:
   5025 		    okay = False;
   5026 		    break;
   5027 		}
   5028 	    } else
   5029 #endif
   5030 		/*
   5031 		 * This query returns the settings assuming the default value
   5032 		 * of DEF_TITLE_MODES, which is zero.  Someone could in
   5033 		 * principle alter that (so that some states could only be
   5034 		 * reached by removing rather than consistently by setting),
   5035 		 * but the default value could be discovered by resetting the
   5036 		 * title modes, querying the resulting reset state.
   5037 		 */
   5038 	    if (*cp == '>' && !strcmp(skip_params(1 + cp), "t")) {	/* XTSMTITLE */
   5039 		char buffer[80];
   5040 		int n;
   5041 
   5042 		++cp;
   5043 		okay = True;
   5044 		ival = parse_int_param(&cp);
   5045 		*buffer = '\0';
   5046 		if (ival == -1) {	/* DEFAULT */
   5047 		    for (n = 0; n <= MAX_TITLEMODE; ++n) {
   5048 			int check = xBIT(n);
   5049 			char *s = buffer + strlen(buffer);
   5050 			if (s != buffer)
   5051 			    *s++ = ';';
   5052 			sprintf(s, "%d",
   5053 				((check & screen->title_modes) != 0
   5054 				 ? 1
   5055 				 : 0));
   5056 		    }
   5057 		} else if (ival >= 0 && ival <= MAX_TITLEMODE) {
   5058 		    sprintf(buffer, "%d",
   5059 			    ((xBIT(ival) & screen->title_modes) != 0
   5060 			     ? 1
   5061 			     : 0));
   5062 		} else {
   5063 		    okay = False;
   5064 		}
   5065 		if (okay)
   5066 		    sprintf(reply, ">%st", buffer);
   5067 	    } else {
   5068 		okay = False;
   5069 	    }
   5070 
   5071 	    unparseputc1(xw, ANSI_DCS);
   5072 	    unparseputc(xw, okay ? '1' : '0');
   5073 	    unparseputc(xw, '$');
   5074 	    unparseputc(xw, 'r');
   5075 	    cp = reply;
   5076 	    unparseputs(xw, cp);
   5077 	    unparseputc1(xw, ANSI_ST);
   5078 	} else {
   5079 	    unparseputc(xw, ANSI_CAN);
   5080 	}
   5081 	break;
   5082     case '+':
   5083 	cp++;
   5084 	switch (*cp) {
   5085 #if OPT_TCAP_QUERY
   5086 	case 'p':		/* XTSETTCAP */
   5087 	    if (AllowTcapOps(xw, etSetTcap)) {
   5088 		set_termcap(xw, cp + 1);
   5089 	    }
   5090 	    break;
   5091 	case 'q':		/* XTGETTCAP */
   5092 	    if (AllowTcapOps(xw, etGetTcap)) {
   5093 		Bool fkey;
   5094 		unsigned state;
   5095 		int code;
   5096 		const char *tmp;
   5097 		const char *parsed = ++cp;
   5098 
   5099 		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
   5100 
   5101 		unparseputc1(xw, ANSI_DCS);
   5102 
   5103 		unparseputc(xw, code >= 0 ? '1' : '0');
   5104 
   5105 		unparseputc(xw, '+');
   5106 		unparseputc(xw, 'r');
   5107 
   5108 		while (*cp != 0 && (code >= -1)) {
   5109 		    if (cp == parsed)
   5110 			break;	/* no data found, error */
   5111 
   5112 		    for (tmp = cp; tmp != parsed; ++tmp)
   5113 			unparseputc(xw, *tmp);
   5114 
   5115 		    if (code >= 0) {
   5116 			unparseputc(xw, '=');
   5117 			screen->tc_query_code = code;
   5118 			screen->tc_query_fkey = fkey;
   5119 #if OPT_ISO_COLORS
   5120 			/* XK_COLORS is a fake code for the "Co" entry (maximum
   5121 			 * number of colors) */
   5122 			if (code == XK_COLORS) {
   5123 			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
   5124 			} else
   5125 #if OPT_DIRECT_COLOR
   5126 			if (code == XK_RGB) {
   5127 			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
   5128 				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
   5129 				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
   5130 				    unparseputn(xw, xw->rgb_widths[0]);
   5131 				} else {
   5132 				    char temp[1024];
   5133 				    sprintf(temp, "%u/%u/%u",
   5134 					    xw->rgb_widths[0],
   5135 					    xw->rgb_widths[1],
   5136 					    xw->rgb_widths[2]);
   5137 				    unparseputs(xw, temp);
   5138 				}
   5139 			    } else {
   5140 				unparseputs(xw, "-1");
   5141 			    }
   5142 			} else
   5143 #endif
   5144 #endif
   5145 			if (code == XK_TCAPNAME) {
   5146 			    unparseputs(xw, resource.term_name);
   5147 			} else {
   5148 			    XKeyEvent event;
   5149 			    memset(&event, 0, sizeof(event));
   5150 			    event.state = state;
   5151 			    Input(xw, &event, False);
   5152 			}
   5153 			screen->tc_query_code = -1;
   5154 		    } else {
   5155 			break;	/* no match found, error */
   5156 		    }
   5157 
   5158 		    cp = parsed;
   5159 		    if (*parsed == ';') {
   5160 			unparseputc(xw, *parsed++);
   5161 			cp = parsed;
   5162 			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
   5163 		    }
   5164 		}
   5165 		unparseputc1(xw, ANSI_ST);
   5166 	    }
   5167 	    break;
   5168 #endif
   5169 #if OPT_XRES_QUERY
   5170 	case 'Q':		/* XTGETXRES */
   5171 	    ++cp;
   5172 	    if (AllowXResOps(xw)) {
   5173 		Boolean first = True;
   5174 		okay = True;
   5175 		while (*cp != '\0' && okay) {
   5176 		    const char *parsed = NULL;
   5177 		    const char *tmp;
   5178 		    char *name = x_decode_hex(cp, &parsed);
   5179 		    char *value;
   5180 		    char *result;
   5181 		    if (cp == parsed || name == NULL) {
   5182 			free(name);
   5183 			break;	/* no data found, error */
   5184 		    }
   5185 		    if ((cp - parsed) > 1024) {
   5186 			free(name);
   5187 			break;	/* ignore improbable resource */
   5188 		    }
   5189 		    TRACE(("query-feature '%s'\n", name));
   5190 		    if ((value = vt100ResourceToString(xw, name)) != NULL) {
   5191 			okay = True;	/* valid */
   5192 		    } else {
   5193 			okay = False;	/* invalid */
   5194 		    }
   5195 		    if (first) {
   5196 			unparseputc1(xw, ANSI_DCS);
   5197 			unparseputc(xw, okay ? '1' : '0');
   5198 			unparseputc(xw, '+');
   5199 			unparseputc(xw, 'R');
   5200 			first = False;
   5201 		    }
   5202 
   5203 		    for (tmp = cp; tmp != parsed; ++tmp)
   5204 			unparseputc(xw, *tmp);
   5205 
   5206 		    if (value != NULL) {
   5207 			unparseputc1(xw, '=');
   5208 			result = x_encode_hex(value);
   5209 			unparseputs(xw, result);
   5210 		    } else {
   5211 			result = NULL;
   5212 		    }
   5213 
   5214 		    free(name);
   5215 		    free(value);
   5216 		    free(result);
   5217 
   5218 		    cp = parsed;
   5219 		    if (*parsed == ';') {
   5220 			unparseputc(xw, *parsed++);
   5221 			cp = parsed;
   5222 		    }
   5223 		}
   5224 		if (!first)
   5225 		    unparseputc1(xw, ANSI_ST);
   5226 	    }
   5227 	    break;
   5228 #endif
   5229 	}
   5230 	break;
   5231     case '0':
   5232 	/* FALLTHRU */
   5233     case '1':
   5234 	if (screen->vtXX_level >= 3 && *skip_params(cp) == '!') {
   5235 	    DECNRCM_codes upss;
   5236 	    psarg = *cp++;
   5237 	    if (*cp++ == '!' && *cp++ == 'u') {
   5238 #if OPT_WIDE_CHARS
   5239 		if (screen->wide_chars && screen->utf8_mode) {
   5240 		    ;		/* EMPTY */
   5241 		} else
   5242 #endif
   5243 		if (decode_upss(xw, cp, psarg, &upss)) {
   5244 		    screen->gsets_upss = upss;
   5245 		}
   5246 	    }
   5247 	    break;
   5248 	}
   5249 #if OPT_DEC_RECTOPS
   5250 	/* FALLTHRU */
   5251     case '2':
   5252 	if (*skip_params(cp) == '$') {
   5253 	    psarg = *cp++;
   5254 	    if ((*cp++ == '$')
   5255 		&& (*cp++ == 't')
   5256 		&& (screen->vtXX_level >= 3)) {
   5257 		switch (psarg) {
   5258 		case '1':
   5259 		    TRACE(("DECRSPS (DECCIR)\n"));
   5260 		    restore_DECCIR(xw, cp);
   5261 		    break;
   5262 		case '2':
   5263 		    TRACE(("DECRSPS (DECTABSR)\n"));
   5264 		    restore_DECTABSR(xw, cp);
   5265 		    break;
   5266 		}
   5267 	    }
   5268 	    break;
   5269 	}
   5270 #endif
   5271 	/* FALLTHRU */
   5272     default:
   5273 	if (optRegisGraphics(screen) ||
   5274 	    screen->vtXX_level >= 2) {	/* VT220 */
   5275 	    parse_ansi_params(&params, &cp);
   5276 	    switch (params.a_final) {
   5277 	    case 'p':		/* ReGIS */
   5278 #if OPT_REGIS_GRAPHICS
   5279 		if (optRegisGraphics(screen)) {
   5280 		    parse_regis(xw, &params, cp);
   5281 		}
   5282 #else
   5283 		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
   5284 #endif
   5285 		break;
   5286 	    case 'q':		/* sixel is done in charproc.c */
   5287 		break;
   5288 	    case '|':		/* DECUDK */
   5289 		if (screen->vtXX_level >= 2) {	/* VT220 */
   5290 		    if (params.a_param[0] == 0)
   5291 			reset_decudk(xw);
   5292 		    parse_decudk(xw, cp);
   5293 		}
   5294 		break;
   5295 	    case L_CURL:	/* DECDLD */
   5296 		if (screen->vtXX_level >= 2) {	/* VT220 */
   5297 		    parse_decdld(&params, cp);
   5298 		}
   5299 		break;
   5300 	    }
   5301 	}
   5302 	break;
   5303     }
   5304     unparse_end(xw);
   5305 }
   5306 
   5307 #if OPT_DEC_RECTOPS
   5308 enum {
   5309     mdUnknown = 0,
   5310     mdMaybeSet = 1,
   5311     mdMaybeReset = 2,
   5312     mdAlwaysSet = 3,
   5313     mdAlwaysReset = 4
   5314 };
   5315 
   5316 #define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
   5317 #define MdFlag(mode,flag) MdBool((mode) & (flag))
   5318 
   5319 /*
   5320  * Reply is the same format as the query, with pair of mode/value:
   5321  * 0 - not recognized
   5322  * 1 - set
   5323  * 2 - reset
   5324  * 3 - permanently set
   5325  * 4 - permanently reset
   5326  * Only one mode can be reported at a time.
   5327  */
   5328 void
   5329 do_ansi_rqm(XtermWidget xw, int nparams, int *params)
   5330 {
   5331     ANSI reply;
   5332     int count = 0;
   5333 
   5334     TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
   5335     memset(&reply, 0, sizeof(reply));
   5336 
   5337     if (nparams >= 1) {
   5338 	int result = mdUnknown;
   5339 
   5340 	/* DECRQM can only ask about one mode at a time */
   5341 	switch (params[0]) {
   5342 	case 1:		/* GATM */
   5343 	    result = mdAlwaysReset;
   5344 	    break;
   5345 	case 2:
   5346 	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
   5347 	    break;
   5348 	case 3:		/* CRM */
   5349 	    result = mdMaybeReset;
   5350 	    break;
   5351 	case 4:
   5352 	    result = MdFlag(xw->flags, INSERT);
   5353 	    break;
   5354 	case 5:		/* SRTM */
   5355 	case 7:		/* VEM */
   5356 	case 10:		/* HEM */
   5357 	case 11:		/* PUM */
   5358 	    result = mdAlwaysReset;
   5359 	    break;
   5360 	case 12:
   5361 	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
   5362 	    break;
   5363 	case 13:		/* FEAM */
   5364 	case 14:		/* FETM */
   5365 	case 15:		/* MATM */
   5366 	case 16:		/* TTM */
   5367 	case 17:		/* SATM */
   5368 	case 18:		/* TSM */
   5369 	case 19:		/* EBM */
   5370 	    result = mdAlwaysReset;
   5371 	    break;
   5372 	case 20:
   5373 	    result = MdFlag(xw->flags, LINEFEED);
   5374 	    break;
   5375 	}
   5376 	reply.a_param[count++] = (ParmType) params[0];
   5377 	reply.a_param[count++] = (ParmType) result;
   5378     }
   5379     reply.a_type = ANSI_CSI;
   5380     reply.a_nparam = (ParmType) count;
   5381     reply.a_inters = '$';
   5382     reply.a_final = 'y';
   5383     unparseseq(xw, &reply);
   5384 }
   5385 
   5386 void
   5387 do_dec_rqm(XtermWidget xw, int nparams, int *params)
   5388 {
   5389     ANSI reply;
   5390     int count = 0;
   5391 
   5392     TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
   5393     memset(&reply, 0, sizeof(reply));
   5394 
   5395     if (nparams >= 1) {
   5396 	TScreen *screen = TScreenOf(xw);
   5397 	int result = mdUnknown;
   5398 
   5399 	/* DECRQM can only ask about one mode at a time */
   5400 	switch ((DECSET_codes) params[0]) {
   5401 	case srm_DECCKM:
   5402 	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
   5403 	    break;
   5404 	case srm_DECANM:	/* ANSI/VT52 mode      */
   5405 #if OPT_VT52_MODE
   5406 	    result = MdBool(screen->vtXX_level >= 1);
   5407 #else
   5408 	    result = mdMaybeSet;
   5409 #endif
   5410 	    break;
   5411 	case srm_DECCOLM:
   5412 	    result = MdFlag(xw->flags, IN132COLUMNS);
   5413 	    break;
   5414 	case srm_DECSCLM:	/* (slow scroll)        */
   5415 	    result = MdFlag(xw->flags, SMOOTHSCROLL);
   5416 	    break;
   5417 	case srm_DECSCNM:
   5418 	    result = MdFlag(xw->flags, REVERSE_VIDEO);
   5419 	    break;
   5420 	case srm_DECOM:
   5421 	    result = MdFlag(xw->flags, ORIGIN);
   5422 	    break;
   5423 	case srm_DECAWM:
   5424 	    result = MdFlag(xw->flags, WRAPAROUND);
   5425 	    break;
   5426 	case srm_DECARM:
   5427 	    result = mdAlwaysReset;
   5428 	    break;
   5429 	case srm_X10_MOUSE:	/* X10 mouse                    */
   5430 	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
   5431 	    break;
   5432 #if OPT_TOOLBAR
   5433 	case srm_RXVT_TOOLBAR:
   5434 	    result = MdBool(resource.toolBar);
   5435 	    break;
   5436 #endif
   5437 #if OPT_BLINK_CURS
   5438 	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
   5439 	    result = MdBool(screen->cursor_blink_esc);
   5440 	    break;
   5441 	case srm_CURSOR_BLINK_OPS:
   5442 	    switch (screen->cursor_blink) {
   5443 	    case cbTrue:
   5444 		result = mdMaybeSet;
   5445 		break;
   5446 	    case cbFalse:
   5447 		result = mdMaybeReset;
   5448 		break;
   5449 	    case cbAlways:
   5450 		result = mdAlwaysSet;
   5451 		break;
   5452 	    case cbLAST:
   5453 		/* FALLTHRU */
   5454 	    case cbNever:
   5455 		result = mdAlwaysReset;
   5456 		break;
   5457 	    }
   5458 	    break;
   5459 	case srm_XOR_CURSOR_BLINKS:
   5460 	    result = (screen->cursor_blink_xor
   5461 		      ? mdAlwaysSet
   5462 		      : mdAlwaysReset);
   5463 	    break;
   5464 #endif
   5465 	case srm_DECPFF:	/* print form feed */
   5466 	    result = MdBool(PrinterOf(screen).printer_formfeed);
   5467 	    break;
   5468 	case srm_DECPEX:	/* print extent */
   5469 	    result = MdBool(PrinterOf(screen).printer_extent);
   5470 	    break;
   5471 	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
   5472 	    result = MdBool(screen->cursor_set);
   5473 	    break;
   5474 	case srm_RXVT_SCROLLBAR:
   5475 	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
   5476 	    break;
   5477 #if OPT_SHIFT_FONTS
   5478 	case srm_RXVT_FONTSIZE:
   5479 	    result = MdBool(xw->misc.shift_fonts);
   5480 	    break;
   5481 #endif
   5482 #if OPT_TEK4014
   5483 	case srm_DECTEK:
   5484 	    result = MdBool(TEK4014_ACTIVE(xw));
   5485 	    break;
   5486 #endif
   5487 	case srm_132COLS:
   5488 	    result = MdBool(screen->c132);
   5489 	    break;
   5490 	case srm_CURSES_HACK:
   5491 	    result = MdBool(screen->curses);
   5492 	    break;
   5493 	case srm_DECNRCM:	/* national charset (VT220) */
   5494 	    if (screen->vtXX_level >= 2) {
   5495 		result = MdFlag(xw->flags, NATIONAL);
   5496 	    } else {
   5497 		result = 0;
   5498 	    }
   5499 	    break;
   5500 	case srm_MARGIN_BELL:	/* margin bell                  */
   5501 	    result = MdBool(screen->marginbell);
   5502 	    break;
   5503 #if OPT_PRINT_GRAPHICS
   5504 	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
   5505 	    result = MdBool(screen->graphics_expanded_print_mode);
   5506 	    break;
   5507 #endif
   5508 	case srm_REVERSEWRAP:	/* reverse wraparound   */
   5509 	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
   5510 		result = MdFlag(xw->flags, REVERSEWRAP);
   5511 	    break;
   5512 	case srm_REVERSEWRAP2:	/* extended reverse wraparound   */
   5513 	    result = MdFlag(xw->flags, REVERSEWRAP2);
   5514 	    break;
   5515 #if defined(ALLOWLOGGING)
   5516 	case srm_ALLOWLOGGING:	/* logging              */
   5517 	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
   5518 #if defined(ALLOWLOGFILEONOFF)
   5519 		result = MdBool(screen->logging);
   5520 #else
   5521 		result = ((MdBool(screen->logging) == mdMaybeSet)
   5522 			  ? mdAlwaysSet
   5523 			  : mdAlwaysReset);
   5524 #endif
   5525 	    break;
   5526 #elif OPT_PRINT_GRAPHICS
   5527 	case srm_DECGPBM:	/* Graphics Print Background Mode */
   5528 	    result = MdBool(screen->graphics_print_background_mode);
   5529 	    break;
   5530 #endif
   5531 	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
   5532 	    /* FALLTHRU */
   5533 	case srm_OPT_ALTBUF:
   5534 	    result = MdBool(screen->whichBuf);
   5535 	    break;
   5536 	case srm_ALTBUF:
   5537 	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
   5538 		result = MdBool(screen->whichBuf);
   5539 	    break;
   5540 	case srm_DECNKM:
   5541 	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
   5542 	    break;
   5543 	case srm_DECBKM:
   5544 	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
   5545 	    break;
   5546 	case srm_DECLRMM:
   5547 	    if (screen->vtXX_level >= 4) {	/* VT420 */
   5548 		result = MdFlag(xw->flags, LEFT_RIGHT);
   5549 	    } else {
   5550 		result = 0;
   5551 	    }
   5552 	    break;
   5553 #if OPT_SIXEL_GRAPHICS
   5554 	case srm_DECSDM:
   5555 	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
   5556 	    break;
   5557 #endif
   5558 	case srm_DECNCSM:	/* no clearing screen on column change */
   5559 	    if (screen->vtXX_level >= 5) {	/* VT510 */
   5560 		result = MdFlag(xw->flags, NOCLEAR_COLM);
   5561 	    } else {
   5562 		result = 0;
   5563 	    }
   5564 	    break;
   5565 	case srm_VT200_MOUSE:	/* xterm bogus sequence */
   5566 	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
   5567 	    break;
   5568 	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
   5569 	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
   5570 	    break;
   5571 	case srm_BTN_EVENT_MOUSE:
   5572 	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
   5573 	    break;
   5574 	case srm_ANY_EVENT_MOUSE:
   5575 	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
   5576 	    break;
   5577 #if OPT_FOCUS_EVENT
   5578 	case srm_FOCUS_EVENT_MOUSE:
   5579 	    result = MdBool(screen->send_focus_pos);
   5580 	    break;
   5581 #endif
   5582 	case srm_EXT_MODE_MOUSE:
   5583 	    /* FALLTHRU */
   5584 	case srm_SGR_EXT_MODE_MOUSE:
   5585 	    /* FALLTHRU */
   5586 	case srm_URXVT_EXT_MODE_MOUSE:
   5587 	    /* FALLTHRU */
   5588 	case srm_PIXEL_POSITION_MOUSE:
   5589 	    result = MdBool(screen->extend_coords == params[0]);
   5590 	    break;
   5591 	case srm_ALTERNATE_SCROLL:
   5592 	    result = MdBool(screen->alternateScroll);
   5593 	    break;
   5594 	case srm_RXVT_SCROLL_TTY_OUTPUT:
   5595 	    result = MdBool(screen->scrollttyoutput);
   5596 	    break;
   5597 	case srm_RXVT_SCROLL_TTY_KEYPRESS:
   5598 	    result = MdBool(screen->scrollkey);
   5599 	    break;
   5600 	case srm_EIGHT_BIT_META:
   5601 	    result = MdBool(screen->eight_bit_meta);
   5602 	    break;
   5603 #if OPT_NUM_LOCK
   5604 	case srm_REAL_NUMLOCK:
   5605 	    result = MdBool(xw->misc.real_NumLock);
   5606 	    break;
   5607 	case srm_META_SENDS_ESC:
   5608 	    result = MdBool(screen->meta_sends_esc);
   5609 	    break;
   5610 #endif
   5611 	case srm_DELETE_IS_DEL:
   5612 	    result = MdBool(xtermDeleteIsDEL(xw));
   5613 	    break;
   5614 #if OPT_NUM_LOCK
   5615 	case srm_ALT_SENDS_ESC:
   5616 	    result = MdBool(screen->alt_sends_esc);
   5617 	    break;
   5618 #endif
   5619 	case srm_KEEP_SELECTION:
   5620 	    result = MdBool(screen->keepSelection);
   5621 	    break;
   5622 	case srm_SELECT_TO_CLIPBOARD:
   5623 	    result = MdBool(screen->selectToClipboard);
   5624 	    break;
   5625 	case srm_BELL_IS_URGENT:
   5626 	    result = MdBool(screen->bellIsUrgent);
   5627 	    break;
   5628 	case srm_POP_ON_BELL:
   5629 	    result = MdBool(screen->poponbell);
   5630 	    break;
   5631 	case srm_KEEP_CLIPBOARD:
   5632 	    result = MdBool(screen->keepClipboard);
   5633 	    break;
   5634 	case srm_ALLOW_ALTBUF:
   5635 	    result = MdBool(xw->misc.titeInhibit);
   5636 	    break;
   5637 	case srm_SAVE_CURSOR:
   5638 	    result = MdBool(screen->sc[screen->whichBuf].saved);
   5639 	    break;
   5640 	case srm_FAST_SCROLL:
   5641 	    result = MdBool(screen->fastscroll);
   5642 	    break;
   5643 #if OPT_TCAP_FKEYS
   5644 	case srm_TCAP_FKEYS:
   5645 	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
   5646 	    break;
   5647 #endif
   5648 #if OPT_SUN_FUNC_KEYS
   5649 	case srm_SUN_FKEYS:
   5650 	    result = MdBool(xw->keyboard.type == keyboardIsSun);
   5651 	    break;
   5652 #endif
   5653 #if OPT_HP_FUNC_KEYS
   5654 	case srm_HP_FKEYS:
   5655 	    result = MdBool(xw->keyboard.type == keyboardIsHP);
   5656 	    break;
   5657 #endif
   5658 #if OPT_SCO_FUNC_KEYS
   5659 	case srm_SCO_FKEYS:
   5660 	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
   5661 	    break;
   5662 #endif
   5663 	case srm_LEGACY_FKEYS:
   5664 	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
   5665 	    break;
   5666 #if OPT_SUNPC_KBD
   5667 	case srm_VT220_FKEYS:
   5668 	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
   5669 	    break;
   5670 #endif
   5671 #if OPT_PASTE64 || OPT_READLINE
   5672 	case srm_PASTE_IN_BRACKET:
   5673 	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
   5674 	    break;
   5675 #endif
   5676 #if OPT_READLINE
   5677 	case srm_BUTTON1_MOVE_POINT:
   5678 	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
   5679 	    break;
   5680 	case srm_BUTTON2_MOVE_POINT:
   5681 	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
   5682 	    break;
   5683 	case srm_DBUTTON3_DELETE:
   5684 	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
   5685 	    break;
   5686 	case srm_PASTE_QUOTE:
   5687 	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
   5688 	    break;
   5689 	case srm_PASTE_LITERAL_NL:
   5690 	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
   5691 	    break;
   5692 #endif /* OPT_READLINE */
   5693 #if OPT_GRAPHICS
   5694 	case srm_PRIVATE_COLOR_REGISTERS:
   5695 	    result = MdBool(screen->privatecolorregisters);
   5696 	    break;
   5697 #endif
   5698 #if OPT_SIXEL_GRAPHICS
   5699 	case srm_SIXEL_SCROLLS_RIGHT:
   5700 	    result = MdBool(screen->sixel_scrolls_right);
   5701 	    break;
   5702 #endif
   5703 	    /* the remainder are recognized but unimplemented */
   5704 	    /* VT3xx */
   5705 	case srm_DEC131TM:	/* vt330:VT131 transmit */
   5706 	case srm_DECEKEM:	/* vt330:edit key execution */
   5707 	case srm_DECHCCM:	/* vt320:Horizontal Cursor-Coupling Mode */
   5708 	case srm_DECKBUM:	/* vt330:Keyboard Usage mode */
   5709 	case srm_DECKKDM:	/* vt382:Kanji/Katakana */
   5710 	case srm_DECLTM:	/* vt330:line transmit */
   5711 	case srm_DECPCCM:	/* vt330:Page Cursor-Coupling Mode */
   5712 	case srm_DECVCCM:	/* vt330:Vertical Cursor-Coupling Mode */
   5713 	case srm_DECXRLM:	/* vt330:Transmit Rate Limiting */
   5714 #if !OPT_BLINK_CURS
   5715 	case srm_DECKANAM:	/* vt382:Katakana shift */
   5716 	case srm_DECSCFDM:	/* vt330:space compression field delimiter */
   5717 	case srm_DECTEM:	/* vt330:transmission execution */
   5718 #endif
   5719 #if !OPT_TOOLBAR
   5720 	case srm_DECEDM:	/* vt330:edit */
   5721 #endif
   5722 	    if (screen->vtXX_level >= 3)
   5723 		result = mdAlwaysReset;
   5724 	    break;
   5725 	    /* VT4xx */
   5726 	case srm_DECKPM:	/* vt420:Key Position Mode */
   5727 	    if (screen->vtXX_level >= 4)
   5728 		result = mdAlwaysReset;
   5729 	    break;
   5730 	    /* VT5xx */
   5731 	case srm_DECAAM:	/* vt510:auto answerback */
   5732 	case srm_DECARSM:	/* vt510:auto resize */
   5733 	case srm_DECATCBM:	/* vt520:alternate text color blink */
   5734 	case srm_DECATCUM:	/* vt520:alternate text color underline */
   5735 	case srm_DECBBSM:	/* vt520:bold and blink style */
   5736 	case srm_DECCANSM:	/* vt510:conceal answerback */
   5737 	case srm_DECCAPSLK:	/* vt510:Caps Lock Mode */
   5738 	case srm_DECCRTSM:	/* vt510:CRT save */
   5739 	case srm_DECECM:	/* vt520:erase color */
   5740 	case srm_DECESKM:	/* vt510:enable secondary keyboard language */
   5741 	case srm_DECFWM:	/* vt520:framed windows */
   5742 	case srm_DECHDPXM:	/* vt510:half duplex */
   5743 	case srm_DECHEM:	/* vt510:Hebrew encoding */
   5744 	case srm_DECHWUM:	/* vt520:host wake-up mode (CRT and energy saver) */
   5745 	case srm_DECIPEM:	/* vt510:IBM ProPrinter Emulation Mode */
   5746 	case srm_DECKLHIM:	/* vt510:ignore */
   5747 	case srm_DECMCM:	/* vt510:modem control */
   5748 	case srm_DECNAKB:	/* vt510:Greek/N-A Keyboard Mapping */
   5749 	case srm_DECNULM:	/* vt510:Ignoring Null Mode */
   5750 	case srm_DECNUMLK:	/* vt510:Num Lock Mode */
   5751 	case srm_DECOSCNM:	/* vt510:Overscan Mode */
   5752 	case srm_DECRLCM:	/* vt510:Right-to-Left Copy */
   5753 	case srm_DECRLM:	/* vt510:left-to-right */
   5754 	case srm_DECRPL:	/* vt520:Review Previous Lines */
   5755 #if !OPT_SHIFT_FONTS
   5756 	case srm_DECHEBM:	/* vt520:Hebrew keyboard mapping */
   5757 #endif
   5758 	    if (screen->vtXX_level >= 5)
   5759 		result = mdAlwaysReset;
   5760 	    break;
   5761 	default:
   5762 	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
   5763 		   params[0]));
   5764 	}
   5765 	reply.a_param[count++] = (ParmType) params[0];
   5766 	reply.a_param[count++] = (ParmType) result;
   5767 	TRACE(("DECRPM(%d) = %d\n", params[0], result));
   5768     }
   5769     reply.a_type = ANSI_CSI;
   5770     reply.a_pintro = '?';
   5771     reply.a_nparam = (ParmType) count;
   5772     reply.a_inters = '$';
   5773     reply.a_final = 'y';
   5774     unparseseq(xw, &reply);
   5775 }
   5776 #endif /* OPT_DEC_RECTOPS */
   5777 
   5778 char *
   5779 udk_lookup(XtermWidget xw, int keycode, int *len)
   5780 {
   5781     char *result = NULL;
   5782     if (keycode >= 0 && keycode < MAX_UDK) {
   5783 	*len = xw->work.user_keys[keycode].len;
   5784 	result = xw->work.user_keys[keycode].str;
   5785 	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
   5786     } else {
   5787 	TRACE(("udk_lookup(%d) = <null>\n", keycode));
   5788     }
   5789     return result;
   5790 }
   5791 
   5792 #if OPT_REPORT_ICONS
   5793 void
   5794 report_icons(const char *fmt, ...)
   5795 {
   5796     if (resource.reportIcons) {
   5797 	va_list ap;
   5798 	va_start(ap, fmt);
   5799 	vfprintf(stdout, fmt, ap);
   5800 	va_end(ap);
   5801 #if OPT_TRACE
   5802 	va_start(ap, fmt);
   5803 	TraceVA(fmt, ap);
   5804 	va_end(ap);
   5805 #endif
   5806     }
   5807 }
   5808 #endif
   5809 
   5810 #ifdef HAVE_LIBXPM
   5811 
   5812 #ifndef PIXMAP_ROOTDIR
   5813 #define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
   5814 #endif
   5815 
   5816 typedef struct {
   5817     const char *name;
   5818     const char *const *data;
   5819 } XPM_DATA;
   5820 
   5821 static char *
   5822 x_find_icon(char **work, int *state, const char *filename, const char *suffix)
   5823 {
   5824     const char *prefix = PIXMAP_ROOTDIR;
   5825     const char *larger = "_48x48";
   5826     char *result = NULL;
   5827 
   5828     if (*state >= 0) {
   5829 	if ((*state & 1) == 0)
   5830 	    suffix = "";
   5831 	if ((*state & 2) == 0)
   5832 	    larger = "";
   5833 	if ((*state & 4) == 0) {
   5834 	    prefix = "";
   5835 	} else if (!strncmp(filename, "/", (size_t) 1) ||
   5836 		   !strncmp(filename, "./", (size_t) 2) ||
   5837 		   !strncmp(filename, "../", (size_t) 3)) {
   5838 	    *state = -1;
   5839 	} else if (*state >= 8) {
   5840 	    *state = -1;
   5841 	}
   5842     }
   5843 
   5844     if (*state >= 0) {
   5845 	size_t length;
   5846 
   5847 	FreeAndNull(*work);
   5848 	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
   5849 	    strlen(suffix);
   5850 	if ((result = malloc(length)) != NULL) {
   5851 	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
   5852 	    *work = result;
   5853 	}
   5854 	*state += 1;
   5855     }
   5856     TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
   5857     return result;
   5858 }
   5859 
   5860 #if OPT_BUILTIN_XPMS
   5861 
   5862 static const XPM_DATA *
   5863 built_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
   5864 {
   5865     const XPM_DATA *result = NULL;
   5866     if (!IsEmpty(find)) {
   5867 	Cardinal n;
   5868 	for (n = 0; n < length; ++n) {
   5869 	    if (!x_strcasecmp(find, table[n].name)) {
   5870 		result = table + n;
   5871 		ReportIcons(("use builtin-icon %s\n", table[n].name));
   5872 		break;
   5873 	    }
   5874 	}
   5875 
   5876 	/*
   5877 	 * As a fallback, check if the icon name matches without the lengths,
   5878 	 * which are all _HHxWW format.
   5879 	 */
   5880 	if (result == NULL) {
   5881 	    const char *base = table[0].name;
   5882 	    const char *last = strchr(base, '_');
   5883 	    if (last != NULL
   5884 		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
   5885 		result = table + length - 1;
   5886 		ReportIcons(("use builtin-icon %s\n", table[0].name));
   5887 	    }
   5888 	}
   5889     }
   5890     return result;
   5891 }
   5892 #define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
   5893 #endif /* OPT_BUILTIN_XPMS */
   5894 
   5895 typedef enum {
   5896     eHintDefault = 0		/* use the largest builtin-icon */
   5897     ,eHintNone
   5898     ,eHintSearch
   5899 } ICON_HINT;
   5900 #endif /* HAVE_LIBXPM */
   5901 
   5902 int
   5903 getVisualDepth(XtermWidget xw)
   5904 {
   5905     int result = 0;
   5906 
   5907     if (getVisualInfo(xw)) {
   5908 	result = xw->visInfo->depth;
   5909     }
   5910     return result;
   5911 }
   5912 
   5913 /*
   5914  * WM_ICON_SIZE should be honored if possible.
   5915  */
   5916 void
   5917 xtermLoadIcon(XtermWidget xw, const char *icon_hint)
   5918 {
   5919 #ifdef HAVE_LIBXPM
   5920     Display *dpy = XtDisplay(xw);
   5921     Pixmap myIcon = 0;
   5922     Pixmap myMask = 0;
   5923     char *workname = NULL;
   5924     ICON_HINT hint = eHintDefault;
   5925 #include <builtin_icons.h>
   5926 
   5927     ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
   5928     if (!IsEmpty(icon_hint)) {
   5929 	if (!x_strcasecmp(icon_hint, "none")) {
   5930 	    hint = eHintNone;
   5931 	} else {
   5932 	    hint = eHintSearch;
   5933 	}
   5934     }
   5935 
   5936     if (hint == eHintSearch) {
   5937 	int state = 0;
   5938 	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != NULL) {
   5939 	    Pixmap resIcon = 0;
   5940 	    Pixmap shapemask = 0;
   5941 	    XpmAttributes attributes;
   5942 	    struct stat sb;
   5943 
   5944 	    attributes.depth = (unsigned) getVisualDepth(xw);
   5945 	    attributes.valuemask = XpmDepth;
   5946 
   5947 	    if (IsEmpty(workname)
   5948 		|| lstat(workname, &sb) != 0
   5949 		|| !S_ISREG(sb.st_mode)) {
   5950 		TRACE(("...failure (no such file)\n"));
   5951 	    } else {
   5952 		int rc = XpmReadFileToPixmap(dpy,
   5953 					     DefaultRootWindow(dpy),
   5954 					     workname,
   5955 					     &resIcon,
   5956 					     &shapemask,
   5957 					     &attributes);
   5958 		if (rc == XpmSuccess) {
   5959 		    myIcon = resIcon;
   5960 		    myMask = shapemask;
   5961 		    TRACE(("...success\n"));
   5962 		    ReportIcons(("found/loaded icon-file %s\n", workname));
   5963 		    break;
   5964 		} else {
   5965 		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
   5966 		}
   5967 	    }
   5968 	}
   5969     }
   5970 
   5971     /*
   5972      * If no external file was found, look for the name in the built-in table.
   5973      * If that fails, just use the biggest mini-icon.
   5974      */
   5975     if (myIcon == 0 && hint != eHintNone) {
   5976 	char **data;
   5977 #if OPT_BUILTIN_XPMS
   5978 	const XPM_DATA *myData = NULL;
   5979 	myData = BuiltInXPM(mini_xterm_xpms);
   5980 	if (myData == NULL)
   5981 	    myData = BuiltInXPM(filled_xterm_xpms);
   5982 	if (myData == NULL)
   5983 	    myData = BuiltInXPM(xterm_color_xpms);
   5984 	if (myData == NULL)
   5985 	    myData = BuiltInXPM(xterm_xpms);
   5986 	if (myData == NULL)
   5987 	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
   5988 	data = (char **) myData->data;
   5989 #else
   5990 	data = (char **) &mini_xterm_48x48_xpm;
   5991 #endif
   5992 	if (XpmCreatePixmapFromData(dpy,
   5993 				    DefaultRootWindow(dpy),
   5994 				    data,
   5995 				    &myIcon, &myMask, NULL) == 0) {
   5996 	    ReportIcons(("loaded built-in pixmap icon\n"));
   5997 	} else {
   5998 	    myIcon = 0;
   5999 	    myMask = 0;
   6000 	}
   6001     }
   6002 
   6003     if (myIcon != 0) {
   6004 	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
   6005 	if (!hints)
   6006 	    hints = XAllocWMHints();
   6007 
   6008 	if (hints) {
   6009 	    hints->flags |= IconPixmapHint;
   6010 	    hints->icon_pixmap = myIcon;
   6011 	    if (myMask) {
   6012 		hints->flags |= IconMaskHint;
   6013 		hints->icon_mask = myMask;
   6014 	    }
   6015 
   6016 	    XSetWMHints(dpy, VShellWindow(xw), hints);
   6017 	    XFree(hints);
   6018 	    ReportIcons(("updated window-manager hints\n"));
   6019 	}
   6020     }
   6021 
   6022     free(workname);
   6023 
   6024 #else
   6025     (void) xw;
   6026     (void) icon_hint;
   6027 #endif
   6028 }
   6029 
   6030 void
   6031 ChangeGroup(XtermWidget xw, const char *attribute, char *value)
   6032 {
   6033     Arg args[1];
   6034     Boolean changed = True;
   6035     Widget w = CURRENT_EMU();
   6036     Widget top = SHELL_OF(w);
   6037 
   6038     char *my_attr = NULL;
   6039     char *old_value = value;
   6040 #if OPT_WIDE_CHARS
   6041     Boolean titleIsUTF8;
   6042 #endif
   6043 
   6044     if (!AllowTitleOps(xw))
   6045 	return;
   6046 
   6047     /*
   6048      * Ignore empty or too-long requests.
   6049      */
   6050     if (value == NULL || strlen(value) > 1000)
   6051 	return;
   6052 
   6053     if (IsTitleMode(xw, tmSetBase16)) {
   6054 	const char *temp;
   6055 	char *test;
   6056 
   6057 	/* this allocates a new string, if no error is detected */
   6058 	value = x_decode_hex(value, &temp);
   6059 	if (value == NULL || *temp != '\0') {
   6060 	    free(value);
   6061 	    return;
   6062 	}
   6063 	for (test = value; *test != '\0'; ++test) {
   6064 	    if (CharOf(*test) < 32) {
   6065 		*test = '\0';
   6066 		break;
   6067 	    }
   6068 	}
   6069     }
   6070 #if OPT_WIDE_CHARS
   6071     /*
   6072      * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
   6073      * the WM_NAME property, rather than doing this directly.  That relies on
   6074      * the application to tell it if the format should be something other than
   6075      * STRING, i.e., by setting the XtNtitleEncoding resource.
   6076      *
   6077      * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
   6078      * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
   6079      * added UTF8_STRING (the documentation for that was discarded by an Xorg
   6080      * developer, although the source-code provides this feature).
   6081      *
   6082      * Since X11R5, if the X11 library fails to store a text property as
   6083      * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
   6084      * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
   6085      * case, limit the resulting characters to the printable ISO-8859-1 set.
   6086      */
   6087     titleIsUTF8 = isValidUTF8((Char *) value);
   6088     if (IsSetUtf8Title(xw) && titleIsUTF8) {
   6089 	char *testc = malloc(strlen(value) + 1);
   6090 	Char *nextc = (Char *) value;
   6091 	Boolean ok8bit = True;
   6092 
   6093 	if (testc != NULL) {
   6094 	    /*
   6095 	     * Check if the data fits in STRING.  Along the way, replace
   6096 	     * control characters.
   6097 	     */
   6098 	    Char *lastc = (Char *) testc;
   6099 	    while (*nextc != '\0') {
   6100 		unsigned ch;
   6101 		nextc = convertFromUTF8(nextc, &ch);
   6102 		if (ch > 255) {
   6103 		    ok8bit = False;
   6104 		} else if (!IsLatin1(ch)) {
   6105 		    ch = OnlyLatin1(ch);
   6106 		}
   6107 		*lastc++ = (Char) ch;
   6108 	    }
   6109 	    *lastc = '\0';
   6110 	    if (ok8bit) {
   6111 		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
   6112 		if (value != old_value)
   6113 		    free(value);
   6114 		value = testc;
   6115 		titleIsUTF8 = False;
   6116 	    } else {
   6117 		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
   6118 		       "\t%s\n", value));
   6119 		free(testc);
   6120 		nextc = (Char *) value;
   6121 		while (*nextc != '\0') {
   6122 		    unsigned ch;
   6123 		    Char *skip = convertFromUTF8(nextc, &ch);
   6124 		    if (iswcntrl((wint_t) ch)) {
   6125 			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
   6126 		    }
   6127 		    nextc = skip;
   6128 		}
   6129 	    }
   6130 	}
   6131     } else
   6132 #endif
   6133     {
   6134 	Char *c1 = (Char *) value;
   6135 
   6136 	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
   6137 	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
   6138 	    *c1 = (Char) OnlyLatin1(*c1);
   6139 	}
   6140     }
   6141 
   6142     my_attr = x_strdup(attribute);
   6143 
   6144     ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
   6145 
   6146 #if OPT_WIDE_CHARS
   6147     /*
   6148      * If we're running in UTF-8 mode, and have not been told that the
   6149      * title string is in UTF-8, it is likely that non-ASCII text in the
   6150      * string will be rejected because it is not printable in the current
   6151      * locale.  So we convert it to UTF-8, allowing the X library to
   6152      * convert it back.
   6153      */
   6154     TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
   6155     if (xtermEnvUTF8() && !titleIsUTF8) {
   6156 	size_t limit = strlen(value);
   6157 	Char *c1 = (Char *) value;
   6158 	int n;
   6159 
   6160 	for (n = 0; c1[n] != '\0'; ++n) {
   6161 	    if (c1[n] > 127) {
   6162 		Char *converted;
   6163 		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != NULL) {
   6164 		    Char *temp = converted;
   6165 		    while (*c1 != 0) {
   6166 			temp = convertToUTF8(temp, *c1++);
   6167 		    }
   6168 		    *temp = 0;
   6169 		    if (value != old_value)
   6170 			free(value);
   6171 		    value = (char *) converted;
   6172 		    ReportIcons(("...converted{%s}\n", value));
   6173 		}
   6174 		break;
   6175 	    }
   6176 	}
   6177     }
   6178 #endif
   6179 
   6180 #if OPT_SAME_NAME
   6181     /* If the attribute isn't going to change, then don't bother... */
   6182     if (resource.sameName) {
   6183 	char *buf = NULL;
   6184 	XtSetArg(args[0], my_attr, &buf);
   6185 	XtGetValues(top, args, 1);
   6186 	TRACE(("...comparing resource{%s} to new value{%s}\n",
   6187 	       NonNull(buf),
   6188 	       NonNull(value)));
   6189 	if (buf != NULL && strcmp(value, buf) == 0)
   6190 	    changed = False;
   6191     }
   6192 #endif /* OPT_SAME_NAME */
   6193 
   6194     if (changed) {
   6195 	ReportIcons(("...updating %s\n", my_attr));
   6196 	ReportIcons(("...value is %s\n", value));
   6197 	XtSetArg(args[0], my_attr, value);
   6198 	XtSetValues(top, args, 1);
   6199     }
   6200 #if OPT_WIDE_CHARS
   6201     if (xtermEnvUTF8()) {
   6202 	Display *dpy = XtDisplay(xw);
   6203 	const char *propname = (!strcmp(my_attr, XtNtitle)
   6204 				? "_NET_WM_NAME"
   6205 				: "_NET_WM_ICON_NAME");
   6206 	Atom my_atom = CachedInternAtom(dpy, propname);
   6207 
   6208 	if (my_atom != None) {
   6209 	    changed = True;
   6210 
   6211 	    if (IsSetUtf8Title(xw)) {
   6212 #if OPT_SAME_NAME
   6213 		if (resource.sameName) {
   6214 		    Atom actual_type;
   6215 		    Atom requested_type = XA_UTF8_STRING(dpy);
   6216 		    int actual_format = 0;
   6217 		    long long_length = 1024;
   6218 		    unsigned long nitems = 0;
   6219 		    unsigned long bytes_after = 0;
   6220 		    unsigned char *prop = NULL;
   6221 
   6222 		    if (xtermGetWinProp(dpy,
   6223 					VShellWindow(xw),
   6224 					my_atom,
   6225 					0L,
   6226 					long_length,
   6227 					requested_type,
   6228 					&actual_type,
   6229 					&actual_format,
   6230 					&nitems,
   6231 					&bytes_after,
   6232 					&prop)) {
   6233 			if (actual_type == requested_type
   6234 			    && actual_format == 8
   6235 			    && prop != NULL
   6236 			    && nitems == strlen(value)
   6237 			    && memcmp(value, prop, nitems) == 0) {
   6238 			    changed = False;
   6239 			}
   6240 			XFree(prop);
   6241 		    }
   6242 		}
   6243 #endif /* OPT_SAME_NAME */
   6244 		if (changed) {
   6245 		    ReportIcons(("...updating %s\n", propname));
   6246 		    ReportIcons(("...value is %s\n", value));
   6247 		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
   6248 				    XA_UTF8_STRING(dpy), 8,
   6249 				    PropModeReplace,
   6250 				    (Char *) value,
   6251 				    (int) strlen(value));
   6252 		}
   6253 	    } else {
   6254 		ReportIcons(("...deleting %s\n", propname));
   6255 		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
   6256 	    }
   6257 	}
   6258     }
   6259 #endif
   6260     if (value != old_value) {
   6261 	free(value);
   6262     }
   6263     free(my_attr);
   6264 
   6265     return;
   6266 }
   6267 
   6268 void
   6269 ChangeIconName(XtermWidget xw, char *name)
   6270 {
   6271     if (!showZIconBeep(xw, name))
   6272 	ChangeGroup(xw, XtNiconName, name);
   6273 }
   6274 
   6275 void
   6276 ChangeTitle(XtermWidget xw, char *name)
   6277 {
   6278     ChangeGroup(xw, XtNtitle, name);
   6279 }
   6280 
   6281 #define Strlen(s) strlen((const char *)(s))
   6282 
   6283 void
   6284 ChangeXprop(char *buf)
   6285 {
   6286     Display *dpy = XtDisplay(toplevel);
   6287     Window w = XtWindow(toplevel);
   6288     XTextProperty text_prop;
   6289     Atom aprop;
   6290     Char *pchEndPropName = (Char *) strchr(buf, '=');
   6291 
   6292     if (pchEndPropName)
   6293 	*pchEndPropName = '\0';
   6294     aprop = CachedInternAtom(dpy, buf);
   6295     if (pchEndPropName == NULL) {
   6296 	/* no "=value" given, so delete the property */
   6297 	XDeleteProperty(dpy, w, aprop);
   6298     } else {
   6299 	text_prop.value = pchEndPropName + 1;
   6300 	text_prop.encoding = XA_STRING;
   6301 	text_prop.format = 8;
   6302 	text_prop.nitems = Strlen(text_prop.value);
   6303 	XSetTextProperty(dpy, w, &text_prop, aprop);
   6304     }
   6305 }
   6306 
   6307 /***====================================================================***/
   6308 
   6309 /*
   6310  * This is part of ReverseVideo().  It reverses the data stored for the old
   6311  * "dynamic" colors that might have been retrieved using OSC 10-18.
   6312  */
   6313 void
   6314 ReverseOldColors(XtermWidget xw)
   6315 {
   6316     ScrnColors *pOld = xw->work.oldColors;
   6317     Pixel tmpPix;
   6318     char *tmpName;
   6319 
   6320     if (pOld) {
   6321 	/* change text cursor, if necessary */
   6322 	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
   6323 	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
   6324 	    if (pOld->names[TEXT_CURSOR]) {
   6325 		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
   6326 		pOld->names[TEXT_CURSOR] = NULL;
   6327 	    }
   6328 	    if (pOld->names[TEXT_BG]) {
   6329 		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != NULL) {
   6330 		    pOld->names[TEXT_CURSOR] = tmpName;
   6331 		}
   6332 	    }
   6333 	}
   6334 
   6335 	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
   6336 	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
   6337 
   6338 	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
   6339 	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
   6340 
   6341 #if OPT_TEK4014
   6342 	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
   6343 	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
   6344 #endif
   6345 	FreeMarkGCs(xw);
   6346     }
   6347     return;
   6348 }
   6349 
   6350 Bool
   6351 AllocateTermColor(XtermWidget xw,
   6352 		  ScrnColors * pNew,
   6353 		  int ndx,
   6354 		  const char *name,
   6355 		  Bool always)
   6356 {
   6357     Bool result = False;
   6358 
   6359     if (always || AllowColorOps(xw, ecSetColor)) {
   6360 	XColor def;
   6361 	char *newName;
   6362 
   6363 	result = True;
   6364 	if (!x_strcasecmp(name, XtDefaultForeground)) {
   6365 	    def.pixel = xw->old_foreground;
   6366 	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
   6367 	    def.pixel = xw->old_background;
   6368 	} else if (!xtermAllocColor(xw, &def, name)) {
   6369 	    result = False;
   6370 	}
   6371 
   6372 	if (result
   6373 	    && (newName = x_strdup(name)) != NULL) {
   6374 	    if (COLOR_DEFINED(pNew, ndx)) {
   6375 		free(pNew->names[ndx]);
   6376 	    }
   6377 	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
   6378 	    SET_COLOR_NAME(pNew, ndx, newName);
   6379 	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
   6380 		   ndx, newName, def.pixel));
   6381 	} else {
   6382 	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
   6383 	    result = False;
   6384 	}
   6385     }
   6386     return result;
   6387 }
   6388 /***====================================================================***/
   6389 
   6390 /* ARGSUSED */
   6391 void
   6392 Panic(const char *s GCC_UNUSED, int a GCC_UNUSED)
   6393 {
   6394     if_DEBUG({
   6395 	xtermWarning(s, a);
   6396     });
   6397 }
   6398 
   6399 const char *
   6400 SysErrorMsg(int code)
   6401 {
   6402     static const char unknown[] = "unknown error";
   6403     const char *s = strerror(code);
   6404     return s ? s : unknown;
   6405 }
   6406 
   6407 const char *
   6408 SysReasonMsg(int code)
   6409 {
   6410     /* *INDENT-OFF* */
   6411     static const struct {
   6412 	int code;
   6413 	const char *name;
   6414     } table[] = {
   6415 	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
   6416 	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
   6417 	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
   6418 	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
   6419 	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
   6420 	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
   6421 	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
   6422 	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
   6423 	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
   6424 	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
   6425 	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
   6426 	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
   6427 	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
   6428 	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
   6429 	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
   6430 	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
   6431 	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
   6432 	{ ERROR_FORK,		"spawn: fork() failed" },
   6433 	{ ERROR_EXEC,		"spawn: exec() failed" },
   6434 	{ ERROR_PTYS,		"get_pty: not enough ptys" },
   6435 	{ ERROR_PTY_EXEC,	"waiting for initial map" },
   6436 	{ ERROR_SETUID,		"spawn: setuid() failed" },
   6437 	{ ERROR_INIT,		"spawn: can't initialize window" },
   6438 	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
   6439 	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
   6440 	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
   6441 	{ ERROR_SELECT,		"in_put: select() failed" },
   6442 	{ ERROR_VINIT,		"VTInit: can't initialize window" },
   6443 	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
   6444 	{ ERROR_TSELECT,	"Tinput: select() failed" },
   6445 	{ ERROR_TINIT,		"TekInit: can't initialize window" },
   6446 	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
   6447 	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
   6448 	{ ERROR_XERROR,		"xerror: XError event" },
   6449 	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
   6450 	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
   6451 	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
   6452 	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
   6453     };
   6454     /* *INDENT-ON* */
   6455 
   6456     Cardinal n;
   6457     const char *result = "?";
   6458 
   6459     for (n = 0; n < XtNumber(table); ++n) {
   6460 	if (code == table[n].code) {
   6461 	    result = table[n].name;
   6462 	    break;
   6463 	}
   6464     }
   6465     return result;
   6466 }
   6467 
   6468 void
   6469 SysError(int code)
   6470 {
   6471     int oerrno = errno;
   6472 
   6473     fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
   6474     fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
   6475     fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
   6476 
   6477     Cleanup(code);
   6478 }
   6479 
   6480 void
   6481 NormalExit(void)
   6482 {
   6483     static Bool cleaning;
   6484 
   6485     /*
   6486      * Process "-hold" and session cleanup only for a normal exit.
   6487      */
   6488     if (cleaning) {
   6489 	hold_screen = 0;
   6490 	return;
   6491     }
   6492 
   6493     cleaning = True;
   6494     need_cleanup = False;
   6495 
   6496     if (hold_screen) {
   6497 	hold_screen = 2;
   6498 	while (hold_screen) {
   6499 	    xtermFlushDbe(term);
   6500 	    xevents(term);
   6501 	    Sleep(EVENT_DELAY);
   6502 	}
   6503     }
   6504 #if OPT_SESSION_MGT
   6505     if (resource.sessionMgt) {
   6506 	XtVaSetValues(toplevel,
   6507 		      XtNjoinSession, False,
   6508 		      (void *) 0);
   6509     }
   6510 #endif
   6511     Cleanup(0);
   6512 }
   6513 
   6514 #if USE_DOUBLE_BUFFER
   6515 void
   6516 xtermFlushDbe(XtermWidget xw)
   6517 {
   6518     TScreen *screen = TScreenOf(xw);
   6519     if (resource.buffered && screen->needSwap) {
   6520 	XdbeSwapInfo swap;
   6521 	swap.swap_window = VWindow(screen);
   6522 	swap.swap_action = XdbeCopied;
   6523 	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
   6524 	XFlush(XtDisplay(xw));
   6525 	screen->needSwap = 0;
   6526 	ScrollBarDrawThumb(xw, 2);
   6527 	X_GETTIMEOFDAY(&screen->buffered_at);
   6528     }
   6529 }
   6530 
   6531 void
   6532 xtermTimedDbe(XtermWidget xw)
   6533 {
   6534     if (resource.buffered) {
   6535 	TScreen *screen = TScreenOf(xw);
   6536 	struct timeval now;
   6537 	long elapsed;
   6538 	long limit = DbeMsecs(xw);
   6539 
   6540 	X_GETTIMEOFDAY(&now);
   6541 	if (screen->buffered_at.tv_sec) {
   6542 	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
   6543 		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
   6544 	} else {
   6545 	    elapsed = limit;
   6546 	}
   6547 	if (elapsed >= limit) {
   6548 	    xtermNeedSwap(xw, 1);
   6549 	    xtermFlushDbe(xw);
   6550 	}
   6551     }
   6552 }
   6553 #endif
   6554 
   6555 /*
   6556  * cleanup by sending SIGHUP to client processes
   6557  */
   6558 void
   6559 Cleanup(int code)
   6560 {
   6561     TScreen *screen = TScreenOf(term);
   6562 
   6563     TRACE(("Cleanup %d\n", code));
   6564 
   6565     if (screen->pid > 1) {
   6566 	(void) kill_process_group(screen->pid, SIGHUP);
   6567     }
   6568     Exit(code);
   6569 }
   6570 
   6571 #ifndef S_IXOTH
   6572 #define S_IXOTH 1
   6573 #endif
   6574 
   6575 Boolean
   6576 validProgram(const char *pathname)
   6577 {
   6578     Boolean result = False;
   6579     struct stat sb;
   6580 
   6581     if (!IsEmpty(pathname)
   6582 	&& *pathname == '/'
   6583 	&& strstr(pathname, "/..") == NULL
   6584 	&& stat(pathname, &sb) == 0
   6585 	&& (sb.st_mode & S_IFMT) == S_IFREG
   6586 	&& (sb.st_mode & S_IXOTH) != 0) {
   6587 	result = True;
   6588     }
   6589     return result;
   6590 }
   6591 
   6592 #ifndef PATH_MAX
   6593 #define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
   6594 #endif
   6595 char *
   6596 xtermFindShell(char *leaf, Bool warning)
   6597 {
   6598     char *s0;
   6599     char *s;
   6600     char *d;
   6601     char *tmp;
   6602     char *result = leaf;
   6603     Bool allocated = False;
   6604 
   6605     TRACE(("xtermFindShell(%s)\n", leaf));
   6606 
   6607     if (!strncmp("./", result, (size_t) 2)
   6608 	|| !strncmp("../", result, (size_t) 3)) {
   6609 	size_t need = PATH_MAX;
   6610 	size_t used = strlen(result) + 2;
   6611 	char *buffer = malloc(used + need);
   6612 	if (buffer != NULL) {
   6613 	    if (getcwd(buffer, need) != NULL) {
   6614 		sprintf(buffer + strlen(buffer), "/%s", result);
   6615 		result = buffer;
   6616 		allocated = True;
   6617 	    } else {
   6618 		free(buffer);
   6619 	    }
   6620 	}
   6621     } else if (*result != '\0' && strchr("+/-", *result) == NULL) {
   6622 	/* find it in $PATH */
   6623 	if ((s = s0 = x_getenv("PATH")) != NULL) {
   6624 	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != NULL) {
   6625 		Bool found = False;
   6626 		while (*s != '\0') {
   6627 		    strcpy(tmp, s);
   6628 		    for (d = tmp;; ++d) {
   6629 			if (*d == ':' || *d == '\0') {
   6630 			    int skip = (*d != '\0');
   6631 			    *d = '/';
   6632 			    strcpy(d + 1, leaf);
   6633 			    if (skip)
   6634 				++d;
   6635 			    s += (d - tmp);
   6636 			    if (validProgram(tmp)) {
   6637 				result = x_strdup(tmp);
   6638 				found = True;
   6639 				allocated = True;
   6640 			    }
   6641 			    break;
   6642 			}
   6643 		    }
   6644 		    if (found)
   6645 			break;
   6646 		}
   6647 		free(tmp);
   6648 	    }
   6649 	    free(s0);
   6650 	}
   6651     }
   6652     TRACE(("...xtermFindShell(%s)\n", result));
   6653     if (!validProgram(result)) {
   6654 	if (warning)
   6655 	    xtermWarning("No absolute path found for shell: %s\n", result);
   6656 	if (allocated)
   6657 	    free(result);
   6658 	result = NULL;
   6659     }
   6660     /* be consistent, so that caller can always free the result */
   6661     if (result != NULL && !allocated)
   6662 	result = x_strdup(result);
   6663     return result;
   6664 }
   6665 
   6666 #define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
   6667 
   6668 /*
   6669  * If we do not have unsetenv(), make consistent updates for environ[].
   6670  * This could happen on some older machines due to the uneven standardization
   6671  * process for the two functions.
   6672  *
   6673  * That is, putenv() makes a copy of environ, and some implementations do not
   6674  * update the environ pointer, so the fallback when unsetenv() is missing would
   6675  * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
   6676  * could copy environ.
   6677  */
   6678 #if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
   6679 #undef HAVE_PUTENV
   6680 #elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
   6681 #undef HAVE_UNSETENV
   6682 #endif
   6683 
   6684 /*
   6685  * copy the environment before Setenv'ing.
   6686  */
   6687 void
   6688 xtermCopyEnv(char **oldenv)
   6689 {
   6690 #ifdef HAVE_PUTENV
   6691     (void) oldenv;
   6692 #else
   6693     unsigned size;
   6694     char **newenv;
   6695 
   6696     for (size = 0; oldenv[size] != NULL; size++) {
   6697 	;
   6698     }
   6699 
   6700     newenv = TypeCallocN(char *, ENV_HUNK(size));
   6701     memmove(newenv, oldenv, size * sizeof(char *));
   6702     environ = newenv;
   6703 #endif
   6704 }
   6705 
   6706 #if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
   6707 static int
   6708 findEnv(const char *var, int *lengthp)
   6709 {
   6710     char *test;
   6711     int envindex = 0;
   6712     size_t len = strlen(var);
   6713     int found = -1;
   6714 
   6715     TRACE(("findEnv(%s=..)\n", var));
   6716 
   6717     while ((test = environ[envindex]) != NULL) {
   6718 	if (strncmp(test, var, len) == 0 && test[len] == '=') {
   6719 	    found = envindex;
   6720 	    break;
   6721 	}
   6722 	envindex++;
   6723     }
   6724     *lengthp = envindex;
   6725     return found;
   6726 }
   6727 #endif
   6728 
   6729 /*
   6730  * sets the value of var to be arg in the Unix 4.2 BSD environment env.
   6731  * Var should end with '=' (bindings are of the form "var=value").
   6732  * This procedure assumes the memory for the first level of environ
   6733  * was allocated using calloc, with enough extra room at the end so not
   6734  * to have to do a realloc().
   6735  */
   6736 void
   6737 xtermSetenv(const char *var, const char *value)
   6738 {
   6739     if (value != NULL) {
   6740 #ifdef HAVE_PUTENV
   6741 	char *both = malloc(2 + strlen(var) + strlen(value));
   6742 	TRACE(("xtermSetenv(%s=%s)\n", var, value));
   6743 	if (both) {
   6744 	    sprintf(both, "%s=%s", var, value);
   6745 	    putenv(both);
   6746 	}
   6747 #else
   6748 	size_t len = strlen(var);
   6749 	int envindex;
   6750 	int found = findEnv(var, &envindex);
   6751 
   6752 	TRACE(("xtermSetenv(%s=%s)\n", var, value));
   6753 
   6754 	if (found < 0) {
   6755 	    unsigned need = ENV_HUNK(envindex + 1);
   6756 	    unsigned have = ENV_HUNK(envindex);
   6757 
   6758 	    if (need > have) {
   6759 		char **newenv;
   6760 		newenv = TypeMallocN(char *, need);
   6761 		if (newenv == 0) {
   6762 		    xtermWarning("Cannot increase environment\n");
   6763 		    return;
   6764 		}
   6765 		memmove(newenv, environ, have * sizeof(*newenv));
   6766 		free(environ);
   6767 		environ = newenv;
   6768 	    }
   6769 
   6770 	    found = envindex;
   6771 	    environ[found + 1] = NULL;
   6772 	}
   6773 
   6774 	environ[found] = malloc(2 + len + strlen(value));
   6775 	if (environ[found] == 0) {
   6776 	    xtermWarning("Cannot allocate environment %s\n", var);
   6777 	    return;
   6778 	}
   6779 	sprintf(environ[found], "%s=%s", var, value);
   6780 #endif
   6781     }
   6782 }
   6783 
   6784 void
   6785 xtermUnsetenv(const char *var)
   6786 {
   6787     TRACE(("xtermUnsetenv(%s)\n", var));
   6788 #ifdef HAVE_UNSETENV
   6789     unsetenv(var);
   6790 #else
   6791     {
   6792 	int ignore;
   6793 	int item = findEnv(var, &ignore);
   6794 	if (item >= 0) {
   6795 	    while ((environ[item] = environ[item + 1]) != 0) {
   6796 		++item;
   6797 	    }
   6798 	}
   6799     }
   6800 #endif
   6801 }
   6802 
   6803 /*ARGSUSED*/
   6804 int
   6805 xerror(Display *d, XErrorEvent *ev)
   6806 {
   6807     xtermWarning("warning, error event received:\n");
   6808     TRACE_X_ERR(d, ev);
   6809     (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
   6810     Exit(ERROR_XERROR);
   6811     return 0;			/* appease the compiler */
   6812 }
   6813 
   6814 void
   6815 ice_error(IceConn iceConn)
   6816 {
   6817     (void) iceConn;
   6818 
   6819     xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
   6820 		 (long) getpid(), errno);
   6821 
   6822     Exit(ERROR_ICEERROR);
   6823 }
   6824 
   6825 /*ARGSUSED*/
   6826 int
   6827 xioerror(Display *dpy)
   6828 {
   6829     int the_error = errno;
   6830 
   6831     xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
   6832 		 the_error, SysErrorMsg(the_error),
   6833 		 DisplayString(dpy));
   6834 
   6835     Exit(ERROR_XIOERROR);
   6836     return 0;			/* appease the compiler */
   6837 }
   6838 
   6839 void
   6840 xt_error(String message)
   6841 {
   6842     xtermWarning("Xt error: %s\n", message);
   6843 
   6844     /*
   6845      * Check for the obvious - Xt does a poor job of reporting this.
   6846      */
   6847     if (x_getenv("DISPLAY") == NULL) {
   6848 	xtermWarning("DISPLAY is not set\n");
   6849     }
   6850     exit(ERROR_MISC);
   6851 }
   6852 
   6853 int
   6854 XStrCmp(char *s1, char *s2)
   6855 {
   6856     if (s1 && s2)
   6857 	return (strcmp(s1, s2));
   6858     if (s1 && *s1)
   6859 	return (1);
   6860     if (s2 && *s2)
   6861 	return (-1);
   6862     return (0);
   6863 }
   6864 
   6865 #if OPT_TEK4014
   6866 static void
   6867 withdraw_window(Display *dpy, Window w, int scr)
   6868 {
   6869     TRACE(("withdraw_window %#lx\n", (long) w));
   6870     (void) XmuUpdateMapHints(dpy, w, NULL);
   6871     XWithdrawWindow(dpy, w, scr);
   6872     return;
   6873 }
   6874 #endif
   6875 
   6876 void
   6877 set_vt_visibility(Bool on)
   6878 {
   6879     XtermWidget xw = term;
   6880     TScreen *screen = TScreenOf(xw);
   6881 
   6882     TRACE(("set_vt_visibility(%d)\n", on));
   6883 
   6884     if (on) {
   6885 	if (!screen->Vshow && xw) {
   6886 	    resource.notMapped = False;
   6887 	    VTInit(xw);
   6888 	    XtMapWidget(XtParent(xw));
   6889 #if OPT_TOOLBAR
   6890 	    /* we need both of these during initialization */
   6891 	    XtMapWidget(SHELL_OF(xw));
   6892 	    ShowToolbar(resource.toolBar);
   6893 #endif
   6894 	    screen->Vshow = True;
   6895 	}
   6896     }
   6897 #if OPT_TEK4014
   6898     else {
   6899 	if (screen->Vshow && xw) {
   6900 	    withdraw_window(XtDisplay(xw),
   6901 			    VShellWindow(xw),
   6902 			    XScreenNumberOfScreen(XtScreen(xw)));
   6903 	    screen->Vshow = False;
   6904 	}
   6905     }
   6906     set_vthide_sensitivity();
   6907     set_tekhide_sensitivity();
   6908     update_vttekmode();
   6909     update_tekshow();
   6910     update_vtshow();
   6911 #endif
   6912     return;
   6913 }
   6914 
   6915 #if OPT_TEK4014
   6916 void
   6917 set_tek_visibility(Bool on)
   6918 {
   6919     XtermWidget xw = term;
   6920 
   6921     TRACE(("set_tek_visibility(%d)\n", on));
   6922 
   6923     if (on) {
   6924 	if (!TEK4014_SHOWN(xw)) {
   6925 	    if (tekWidget == NULL) {
   6926 		TekInit();	/* will exit on failure */
   6927 	    }
   6928 	    if (tekWidget != NULL) {
   6929 		Widget tekParent = SHELL_OF(tekWidget);
   6930 		resource.notMapped = False;
   6931 		XtRealizeWidget(tekParent);
   6932 		XtMapWidget(XtParent(tekWidget));
   6933 #if OPT_TOOLBAR
   6934 		/* we need both of these during initialization */
   6935 		XtMapWidget(tekParent);
   6936 		XtMapWidget(tekWidget);
   6937 #endif
   6938 		XtOverrideTranslations(tekParent,
   6939 				       XtParseTranslationTable
   6940 				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
   6941 		(void) XSetWMProtocols(XtDisplay(tekParent),
   6942 				       XtWindow(tekParent),
   6943 				       &wm_delete_window, 1);
   6944 		TEK4014_SHOWN(xw) = True;
   6945 	    }
   6946 	}
   6947     } else {
   6948 	if (TEK4014_SHOWN(xw) && tekWidget) {
   6949 	    withdraw_window(XtDisplay(tekWidget),
   6950 			    TShellWindow,
   6951 			    XScreenNumberOfScreen(XtScreen(tekWidget)));
   6952 	    TEK4014_SHOWN(xw) = False;
   6953 	}
   6954     }
   6955     set_tekhide_sensitivity();
   6956     set_vthide_sensitivity();
   6957     update_vtshow();
   6958     update_tekshow();
   6959     update_vttekmode();
   6960     return;
   6961 }
   6962 
   6963 void
   6964 end_tek_mode(void)
   6965 {
   6966     XtermWidget xw = term;
   6967 
   6968     if (TEK4014_ACTIVE(xw)) {
   6969 	FlushLog(xw);
   6970 	TEK4014_ACTIVE(xw) = False;
   6971 	xtermSetWinSize(xw);
   6972 	longjmp(Tekend, 1);
   6973     }
   6974     return;
   6975 }
   6976 
   6977 void
   6978 end_vt_mode(void)
   6979 {
   6980     XtermWidget xw = term;
   6981 
   6982     if (!TEK4014_ACTIVE(xw)) {
   6983 	FlushLog(xw);
   6984 	set_tek_visibility(True);
   6985 	TEK4014_ACTIVE(xw) = True;
   6986 	TekSetWinSize(tekWidget);
   6987 	longjmp(VTend, 1);
   6988     }
   6989     return;
   6990 }
   6991 
   6992 void
   6993 switch_modes(Bool tovt)		/* if true, then become vt mode */
   6994 {
   6995     if (tovt) {
   6996 	if (tekRefreshList)
   6997 	    TekRefresh(tekWidget);
   6998 	end_tek_mode();		/* WARNING: this does a longjmp... */
   6999     } else {
   7000 	end_vt_mode();		/* WARNING: this does a longjmp... */
   7001     }
   7002 }
   7003 
   7004 void
   7005 hide_vt_window(void)
   7006 {
   7007     set_vt_visibility(False);
   7008     if (!TEK4014_ACTIVE(term))
   7009 	switch_modes(False);	/* switch to tek mode */
   7010 }
   7011 
   7012 void
   7013 hide_tek_window(void)
   7014 {
   7015     set_tek_visibility(False);
   7016     tekRefreshList = (TekLink *) 0;
   7017     if (TEK4014_ACTIVE(term))
   7018 	switch_modes(True);	/* does longjmp to vt mode */
   7019 }
   7020 #endif /* OPT_TEK4014 */
   7021 
   7022 static const char *
   7023 skip_punct(const char *s)
   7024 {
   7025     while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
   7026 	++s;
   7027     }
   7028     return s;
   7029 }
   7030 
   7031 static int
   7032 cmp_options(const void *a, const void *b)
   7033 {
   7034     const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
   7035     const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
   7036     return strcmp(s1, s2);
   7037 }
   7038 
   7039 static int
   7040 cmp_resources(const void *a, const void *b)
   7041 {
   7042     return strcmp(((const XrmOptionDescRec *) a)->option,
   7043 		  ((const XrmOptionDescRec *) b)->option);
   7044 }
   7045 
   7046 XrmOptionDescRec *
   7047 sortedOptDescs(const XrmOptionDescRec * descs, Cardinal res_count)
   7048 {
   7049     static XrmOptionDescRec *res_array = NULL;
   7050 
   7051 #ifdef NO_LEAKS
   7052     if (descs == NULL) {
   7053 	FreeAndNull(res_array);
   7054     } else
   7055 #endif
   7056     if (res_array == NULL) {
   7057 	Cardinal j;
   7058 
   7059 	/* make a sorted index to 'resources' */
   7060 	res_array = TypeCallocN(XrmOptionDescRec, res_count);
   7061 	if (res_array != NULL) {
   7062 	    for (j = 0; j < res_count; j++)
   7063 		res_array[j] = descs[j];
   7064 	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
   7065 	}
   7066     }
   7067     return res_array;
   7068 }
   7069 
   7070 /*
   7071  * The first time this is called, construct sorted index to the main program's
   7072  * list of options, taking into account the on/off options which will be
   7073  * compressed into one token.  It's a lot simpler to do it this way than
   7074  * maintain the list in sorted form with lots of ifdef's.
   7075  */
   7076 OptionHelp *
   7077 sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
   7078 {
   7079     static OptionHelp *opt_array = NULL;
   7080 
   7081 #ifdef NO_LEAKS
   7082     if (descs == NULL && opt_array != NULL) {
   7083 	sortedOptDescs(descs, numDescs);
   7084 	FreeAndNull(opt_array);
   7085 	return NULL;
   7086     } else if (options == NULL || descs == NULL) {
   7087 	return NULL;
   7088     }
   7089 #endif
   7090 
   7091     if (opt_array == NULL) {
   7092 	size_t opt_count, j;
   7093 #if OPT_TRACE
   7094 	Cardinal k;
   7095 	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
   7096 	int code;
   7097 	const char *mesg;
   7098 #else
   7099 	(void) descs;
   7100 	(void) numDescs;
   7101 #endif
   7102 
   7103 	/* count 'options' and make a sorted index to it */
   7104 	for (opt_count = 0; options[opt_count].opt != NULL; ++opt_count) {
   7105 	    ;
   7106 	}
   7107 	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
   7108 	for (j = 0; j < opt_count; j++)
   7109 	    opt_array[j] = options[j];
   7110 	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
   7111 
   7112 	/* supply the "turn on/off" strings if needed */
   7113 #if OPT_TRACE
   7114 	for (j = 0; j < opt_count; j++) {
   7115 	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
   7116 		char temp[80];
   7117 		const char *name = opt_array[j].opt + 3;
   7118 		for (k = 0; k < numDescs; ++k) {
   7119 		    const char *value = res_array[k].value;
   7120 		    if (res_array[k].option[0] == '-') {
   7121 			code = -1;
   7122 		    } else if (res_array[k].option[0] == '+') {
   7123 			code = 1;
   7124 		    } else {
   7125 			code = 0;
   7126 		    }
   7127 		    sprintf(temp, "%.*s",
   7128 			    (int) sizeof(temp) - 2,
   7129 			    opt_array[j].desc);
   7130 		    if (x_strindex(temp, "inhibit") != NULL)
   7131 			code = -code;
   7132 		    if (code != 0
   7133 			&& res_array[k].value != NULL
   7134 			&& !strcmp(name, res_array[k].option + 1)) {
   7135 			if (((code < 0) && !strcmp(value, "on"))
   7136 			    || ((code > 0) && !strcmp(value, "off"))
   7137 			    || ((code > 0) && !strcmp(value, "0"))) {
   7138 			    mesg = "turn on/off";
   7139 			} else {
   7140 			    mesg = "turn off/on";
   7141 			}
   7142 			TRACE(("%s: %s %s: %s (%s)\n",
   7143 			       mesg,
   7144 			       res_array[k].option,
   7145 			       res_array[k].value,
   7146 			       opt_array[j].opt,
   7147 			       opt_array[j].desc));
   7148 			break;
   7149 		    }
   7150 		}
   7151 	    }
   7152 	}
   7153 #endif
   7154     }
   7155     return opt_array;
   7156 }
   7157 
   7158 /*
   7159  * Report the character-type locale that xterm was started in.
   7160  */
   7161 String
   7162 xtermEnvLocale(void)
   7163 {
   7164     static String result;
   7165 
   7166     if (result == NULL) {
   7167 	if ((result = x_nonempty(setlocale(LC_CTYPE, NULL))) == NULL) {
   7168 	    result = x_strdup("C");
   7169 	} else {
   7170 	    result = x_strdup(result);
   7171 	}
   7172 	TRACE(("xtermEnvLocale ->%s\n", result));
   7173     }
   7174     return result;
   7175 }
   7176 
   7177 char *
   7178 xtermEnvEncoding(void)
   7179 {
   7180     static char *result;
   7181 
   7182     if (result == NULL) {
   7183 #ifdef HAVE_LANGINFO_CODESET
   7184 	result = nl_langinfo(CODESET);
   7185 #else
   7186 	const char *locale = xtermEnvLocale();
   7187 	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
   7188 	    result = x_strdup("ASCII");
   7189 	} else {
   7190 	    result = x_strdup("ISO-8859-1");
   7191 	}
   7192 #endif
   7193 	TRACE(("xtermEnvEncoding ->%s\n", result));
   7194     }
   7195     return result;
   7196 }
   7197 
   7198 #if OPT_WIDE_CHARS
   7199 /*
   7200  * Tell whether xterm was started in a locale that uses UTF-8 encoding for
   7201  * characters.  That environment is inherited by subprocesses and used in
   7202  * various library calls.
   7203  */
   7204 Bool
   7205 xtermEnvUTF8(void)
   7206 {
   7207     static Bool init = False;
   7208     static Bool result = False;
   7209 
   7210     if (!init) {
   7211 	init = True;
   7212 #ifdef HAVE_LANGINFO_CODESET
   7213 	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
   7214 #else
   7215 	{
   7216 	    char *locale = x_strdup(xtermEnvLocale());
   7217 	    int n;
   7218 	    for (n = 0; locale[n] != 0; ++n) {
   7219 		locale[n] = x_toupper(locale[n]);
   7220 	    }
   7221 	    if (strstr(locale, "UTF-8") != 0)
   7222 		result = True;
   7223 	    else if (strstr(locale, "UTF8") != 0)
   7224 		result = True;
   7225 	    free(locale);
   7226 	}
   7227 #endif
   7228 	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
   7229     }
   7230     return result;
   7231 }
   7232 #endif /* OPT_WIDE_CHARS */
   7233 
   7234 /*
   7235  * Check if the current widget, or any parent, is the VT100 "xterm" widget.
   7236  */
   7237 XtermWidget
   7238 getXtermWidget(Widget w)
   7239 {
   7240     XtermWidget xw;
   7241 
   7242     if (w == NULL) {
   7243 	xw = (XtermWidget) CURRENT_EMU();
   7244 	if (!IsXtermWidget(xw)) {
   7245 	    xw = NULL;
   7246 	}
   7247     } else if (IsXtermWidget(w)) {
   7248 	xw = (XtermWidget) w;
   7249     } else {
   7250 	xw = getXtermWidget(XtParent(w));
   7251     }
   7252     TRACE2(("getXtermWidget %p -> %p\n", w, xw));
   7253     return xw;
   7254 }
   7255 
   7256 #if OPT_SESSION_MGT
   7257 
   7258 #if OPT_TRACE
   7259 static void
   7260 trace_1_SM(const char *tag, String name)
   7261 {
   7262     Arg args[1];
   7263     char *buf = NULL;
   7264 
   7265     XtSetArg(args[0], name, &buf);
   7266     XtGetValues(toplevel, args, 1);
   7267 
   7268     if (strstr(name, "Path") || strstr(name, "Directory")) {
   7269 	TRACE(("%s %s: %s\n", tag, name, NonNull(buf)));
   7270     } else if (strstr(name, "Command")) {
   7271 	if (buf != NULL) {
   7272 	    char **vec = (char **) (void *) buf;
   7273 	    int n;
   7274 	    TRACE(("%s %s:\n", tag, name));
   7275 	    for (n = 0; vec[n] != NULL; ++n) {
   7276 		TRACE((" arg[%d] = %s\n", n, vec[n]));
   7277 	    }
   7278 	} else {
   7279 	    TRACE(("%s %s: %p\n", tag, name, buf));
   7280 	}
   7281     } else {
   7282 	TRACE(("%s %s: %p\n", tag, name, buf));
   7283     }
   7284 }
   7285 
   7286 static void
   7287 trace_SM_props(void)
   7288 {
   7289     /* *INDENT-OFF* */
   7290     static struct { String app, cls; } table[] = {
   7291 	{ XtNcurrentDirectory,	XtCCurrentDirectory },
   7292 	{ XtNdieCallback,	XtNdiscardCommand },
   7293 	{ XtCDiscardCommand,	XtNenvironment },
   7294 	{ XtCEnvironment,	XtNinteractCallback },
   7295 	{ XtNjoinSession,	XtCJoinSession },
   7296 	{ XtNprogramPath,	XtCProgramPath },
   7297 	{ XtNresignCommand,	XtCResignCommand },
   7298 	{ XtNrestartCommand,	XtCRestartCommand },
   7299 	{ XtNrestartStyle,	XtCRestartStyle },
   7300 	{ XtNsaveCallback,	XtNsaveCompleteCallback },
   7301 	{ XtNsessionID,		XtCSessionID },
   7302 	{ XtNshutdownCommand,	XtCShutdownCommand },
   7303     };
   7304     /* *INDENT-ON* */
   7305     Cardinal n;
   7306     TRACE(("Session properties:\n"));
   7307     for (n = 0; n < XtNumber(table); ++n) {
   7308 	trace_1_SM("app", table[n].app);
   7309 	trace_1_SM("cls", table[n].cls);
   7310     }
   7311 }
   7312 #define TRACE_SM_PROPS()	trace_SM_props()
   7313 #else
   7314 #define TRACE_SM_PROPS()	/* nothing */
   7315 #endif
   7316 
   7317 static void
   7318 die_callback(Widget w GCC_UNUSED,
   7319 	     XtPointer client_data GCC_UNUSED,
   7320 	     XtPointer call_data GCC_UNUSED)
   7321 {
   7322     TRACE(("die_callback client=%p, call=%p\n",
   7323 	   (void *) client_data,
   7324 	   (void *) call_data));
   7325     TRACE_SM_PROPS();
   7326     NormalExit();
   7327 }
   7328 
   7329 static void
   7330 save_callback(Widget w GCC_UNUSED,
   7331 	      XtPointer client_data GCC_UNUSED,
   7332 	      XtPointer call_data)
   7333 {
   7334     XtCheckpointToken token = (XtCheckpointToken) call_data;
   7335     TRACE(("save_callback:\n"));
   7336     TRACE(("... save_type            <-%d\n", token->save_type));
   7337     TRACE(("... interact_style       <-%d\n", token->interact_style));
   7338     TRACE(("... shutdown             <-%s\n", BtoS(token->shutdown)));
   7339     TRACE(("... fast                 <-%s\n", BtoS(token->fast)));
   7340     TRACE(("... cancel_shutdown      <-%s\n", BtoS(token->cancel_shutdown)));
   7341     TRACE(("... phase                <-%d\n", token->phase));
   7342     TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type));
   7343     TRACE(("... request_cancel       ->%s\n", BtoS(token->request_cancel)));
   7344     TRACE(("... request_next_phase   ->%s\n", BtoS(token->request_next_phase)));
   7345     TRACE(("... save_success         ->%s\n", BtoS(token->save_success)));
   7346     xtermUpdateRestartCommand(term);
   7347     /* we have nothing more to save */
   7348     token->save_success = True;
   7349 }
   7350 
   7351 static void
   7352 icewatch(IceConn iceConn,
   7353 	 IcePointer clientData GCC_UNUSED,
   7354 	 Bool opening,
   7355 	 IcePointer * watchData GCC_UNUSED)
   7356 {
   7357     if (opening) {
   7358 	ice_fd = IceConnectionNumber(iceConn);
   7359 	TRACE(("got IceConnectionNumber %d\n", ice_fd));
   7360     } else {
   7361 	ice_fd = -1;
   7362 	TRACE(("reset IceConnectionNumber\n"));
   7363     }
   7364 }
   7365 
   7366 void
   7367 xtermOpenSession(void)
   7368 {
   7369     if (resource.sessionMgt) {
   7370 	TRACE(("Enabling session-management callbacks\n"));
   7371 	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
   7372 	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
   7373 
   7374 	TRACE_SM_PROPS();
   7375     }
   7376 }
   7377 
   7378 void
   7379 xtermCloseSession(void)
   7380 {
   7381     IceRemoveConnectionWatch(icewatch, NULL);
   7382 }
   7383 
   7384 typedef enum {
   7385     B_ARG = 0,
   7386     I_ARG,
   7387     D_ARG,
   7388     S_ARG
   7389 } ParamType;
   7390 
   7391 #define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) }
   7392 #define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) }
   7393 #define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) }
   7394 #define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) }
   7395 
   7396 typedef struct {
   7397     const char name[30];
   7398     ParamType type;
   7399     Cardinal offset;
   7400 } FontParams;
   7401 
   7402 /* *INDENT-OFF* */
   7403 static const FontParams fontParams[] = {
   7404     Iarg(XtNinitialFont,     screen.menu_font_number),	/* "-fc" */
   7405     Barg(XtNallowBoldFonts,  screen.allowBoldFonts),	/* menu */
   7406 #if OPT_BOX_CHARS
   7407     Barg(XtNforceBoxChars,   screen.force_box_chars),	/* "-fbx" */
   7408     Barg(XtNforcePackedFont, screen.force_packed),	/* menu */
   7409 #endif
   7410 #if OPT_DEC_CHRSET
   7411     Barg(XtNfontDoublesize,  screen.font_doublesize),	/* menu */
   7412 #endif
   7413 #if OPT_WIDE_CHARS
   7414     Barg(XtNutf8Fonts,       screen.utf8_fonts),	/* menu */
   7415 #endif
   7416 #if OPT_RENDERFONT
   7417     Darg(XtNfaceSize,        misc.face_size[0]),	/* "-fs" */
   7418     Sarg(XtNfaceName,        misc.default_xft.f_n),	/* "-fa" */
   7419     Sarg(XtNrenderFont,      misc.render_font_s),	/* (resource) */
   7420 #endif
   7421 };
   7422 /* *INDENT-ON* */
   7423 
   7424 #define RESTART_PARAMS (int)(XtNumber(fontParams) * 2)
   7425 #define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset)
   7426 
   7427 /*
   7428  * If no widget is given, no value is used.
   7429  */
   7430 static char *
   7431 formatFontParam(char *result, XtermWidget xw, const FontParams * parameter)
   7432 {
   7433     sprintf(result, "%s*%s:", ProgramName, parameter->name);
   7434     if (xw != NULL) {
   7435 	char *next = result + strlen(result);
   7436 	switch (parameter->type) {
   7437 	case B_ARG:
   7438 	    sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset)
   7439 		    ? "true"
   7440 		    : "false");
   7441 	    break;
   7442 	case I_ARG:
   7443 	    sprintf(next, "%d", TypedPtr(int));
   7444 	    break;
   7445 	case D_ARG:
   7446 	    sprintf(next, "%.1f", TypedPtr(float));
   7447 	    break;
   7448 	case S_ARG:
   7449 	    strcpy(next, TypedPtr(char *));
   7450 #if OPT_RENDERFONT
   7451 	    if (!strcmp(parameter->name, XtNfaceName)) {
   7452 		if (IsEmpty(next)
   7453 		    && xw->work.render_font) {
   7454 		    strcpy(next, DEFFACENAME_AUTO);
   7455 		}
   7456 	    } else if (!strcmp(parameter->name, XtNrenderFont)) {
   7457 		if (xw->work.render_font == erDefault
   7458 		    && IsEmpty(xw->misc.default_xft.f_n)) {
   7459 		    strcpy(next, "DefaultOff");
   7460 		}
   7461 	    }
   7462 #endif
   7463 	    break;
   7464 	}
   7465     }
   7466     return result;
   7467 }
   7468 
   7469 #if OPT_TRACE
   7470 static void
   7471 dumpFontParams(XtermWidget xw)
   7472 {
   7473     char buffer[1024];
   7474     Cardinal n;
   7475 
   7476     TRACE(("FontParams:\n"));
   7477     for (n = 0; n < XtNumber(fontParams); ++n) {
   7478 	TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n)));
   7479     }
   7480 }
   7481 #else
   7482 #define dumpFontParams(xw)	/* nothing */
   7483 #endif
   7484 
   7485 static Boolean
   7486 findFontParams(int argc, char **argv)
   7487 {
   7488     Boolean result = False;
   7489 
   7490     if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) {
   7491 	int n;
   7492 
   7493 	for (n = 0; n < RESTART_PARAMS; ++n) {
   7494 	    int my_index = argc - restart_params - n - 1;
   7495 	    int my_param = (RESTART_PARAMS - n - 1) / 2;
   7496 	    char *actual = argv[my_index];
   7497 	    char expect[1024];
   7498 	    Boolean value = (Boolean) ((n % 2) == 0);
   7499 
   7500 	    result = False;
   7501 	    TRACE(("...index: %d\n", my_index));
   7502 	    TRACE(("...param: %d\n", my_param));
   7503 	    TRACE(("...actual %s\n", actual));
   7504 	    if (IsEmpty(actual))
   7505 		break;
   7506 
   7507 	    if (value) {
   7508 		formatFontParam(expect, NULL, fontParams + my_param);
   7509 	    } else {
   7510 		strcpy(expect, "-xrm");
   7511 	    }
   7512 
   7513 	    TRACE(("...expect %s\n", expect));
   7514 
   7515 	    if (value) {
   7516 		if (strlen(expect) >= strlen(actual))
   7517 		    break;
   7518 		if (strncmp(expect, actual, strlen(expect)))
   7519 		    break;
   7520 	    } else {
   7521 		if (strcmp(actual, expect))
   7522 		    break;
   7523 	    }
   7524 	    TRACE(("fixme/ok:%d\n", n));
   7525 	    result = True;
   7526 	}
   7527 	TRACE(("findFontParams: %s (tested %d of %d parameters)\n",
   7528 	       BtoS(result), n + 1, RESTART_PARAMS));
   7529     }
   7530     return result;
   7531 }
   7532 
   7533 static int
   7534 insertFontParams(XtermWidget xw, int *targetp, Bool first)
   7535 {
   7536     int changed = 0;
   7537     int n;
   7538     int target = *targetp;
   7539     char buffer[1024];
   7540     const char *option = "-xrm";
   7541 
   7542     for (n = 0; n < (int) XtNumber(fontParams); ++n) {
   7543 	formatFontParam(buffer, xw, fontParams + n);
   7544 	TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer));
   7545 	if (restart_command[target] == NULL)
   7546 	    restart_command[target] = x_strdup(option);
   7547 	++target;
   7548 	if (first) {
   7549 	    restart_command[target] = x_strdup(buffer);
   7550 	    ++changed;
   7551 	} else if (restart_command[target] == NULL
   7552 		   || strcmp(restart_command[target], buffer)) {
   7553 	    free(restart_command[target]);
   7554 	    restart_command[target] = x_strdup(buffer);
   7555 	    ++changed;
   7556 	}
   7557 	++target;
   7558     }
   7559     *targetp = target;
   7560     return changed;
   7561 }
   7562 
   7563 void
   7564 xtermUpdateRestartCommand(XtermWidget xw)
   7565 {
   7566     if (resource.sessionMgt) {
   7567 	Arg args[1];
   7568 	char **argv = NULL;
   7569 
   7570 	XtSetArg(args[0], XtNrestartCommand, &argv);
   7571 	XtGetValues(toplevel, args, 1);
   7572 	if (argv != NULL) {
   7573 	    static int my_params = 0;
   7574 
   7575 	    int changes = 0;
   7576 	    Boolean first = False;
   7577 	    int argc;
   7578 	    int want;
   7579 	    int source, target;
   7580 
   7581 	    TRACE(("xtermUpdateRestartCommand\n"));
   7582 	    dumpFontParams(xw);
   7583 	    for (argc = 0; argv[argc] != NULL; ++argc) {
   7584 		TRACE((" arg[%d] = %s\n", argc, argv[argc]));
   7585 		;
   7586 	    }
   7587 	    want = argc - (restart_params + RESTART_PARAMS);
   7588 
   7589 	    TRACE((" argc:           %d\n", argc));
   7590 	    TRACE((" restart_params: %d\n", restart_params));
   7591 	    TRACE((" want to insert: %d\n", want));
   7592 
   7593 	    /*
   7594 	     * If we already have the font-choice option, do not add it again.
   7595 	     */
   7596 	    if (findFontParams(argc, argv)) {
   7597 		my_params = (want);
   7598 	    } else {
   7599 		first = True;
   7600 		my_params = (argc - restart_params);
   7601 	    }
   7602 	    TRACE((" my_params:      %d\n", my_params));
   7603 
   7604 	    if (my_params > argc) {
   7605 		TRACE((" re-allocate restartCommand\n"));
   7606 		FreeAndNull(restart_command);
   7607 	    }
   7608 
   7609 	    if (restart_command == NULL) {
   7610 		int need = argc + RESTART_PARAMS + 1;
   7611 
   7612 		restart_command = TypeCallocN(char *, need);
   7613 
   7614 		TRACE(("..inserting font-parameters\n"));
   7615 		for (source = target = 0; source < argc; ++source) {
   7616 		    if (source == my_params) {
   7617 			changes += insertFontParams(xw, &target, first);
   7618 			if (!first) {
   7619 			    source += (RESTART_PARAMS - 1);
   7620 			    continue;
   7621 			}
   7622 		    }
   7623 		    if (argv[source] == NULL)
   7624 			break;
   7625 		    restart_command[target++] = x_strdup(argv[source]);
   7626 		}
   7627 		restart_command[target] = NULL;
   7628 	    } else {
   7629 		TRACE(("..replacing font-parameters\n"));
   7630 		target = my_params;
   7631 		changes += insertFontParams(xw, &target, first);
   7632 	    }
   7633 	    if (changes) {
   7634 		TRACE(("..%d parameters changed\n", changes));
   7635 		XtSetArg(args[0], XtNrestartCommand, restart_command);
   7636 		XtSetValues(toplevel, args, 1);
   7637 	    } else {
   7638 		TRACE(("..NO parameters changed\n"));
   7639 	    }
   7640 	}
   7641 	TRACE_SM_PROPS();
   7642     }
   7643 }
   7644 #endif /* OPT_SESSION_MGT */
   7645 
   7646 Widget
   7647 xtermOpenApplication(XtAppContext * app_context_return,
   7648 		     String my_class,
   7649 		     XrmOptionDescRec * options,
   7650 		     Cardinal num_options,
   7651 		     int *argc_in_out,
   7652 		     char **argv_in_out,
   7653 		     String *fallback_resources,
   7654 		     WidgetClass widget_class,
   7655 		     ArgList args,
   7656 		     Cardinal num_args)
   7657 {
   7658     Widget result;
   7659 
   7660     XtSetErrorHandler(xt_error);
   7661 #if OPT_SESSION_MGT
   7662     result = XtOpenApplication(app_context_return,
   7663 			       my_class,
   7664 			       options,
   7665 			       num_options,
   7666 			       argc_in_out,
   7667 			       argv_in_out,
   7668 			       fallback_resources,
   7669 			       widget_class,
   7670 			       args,
   7671 			       num_args);
   7672     IceAddConnectionWatch(icewatch, NULL);
   7673 #else
   7674     (void) widget_class;
   7675     (void) args;
   7676     (void) num_args;
   7677     result = XtAppInitialize(app_context_return,
   7678 			     my_class,
   7679 			     options,
   7680 			     num_options,
   7681 			     argc_in_out,
   7682 			     argv_in_out,
   7683 			     fallback_resources,
   7684 			     NULL, 0);
   7685 #endif /* OPT_SESSION_MGT */
   7686     XtSetErrorHandler(NULL);
   7687 
   7688     return result;
   7689 }
   7690 
   7691 /*
   7692  * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
   7693  * our own error-handler.
   7694  */
   7695 /* ARGSUSED */
   7696 int
   7697 ignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
   7698 {
   7699     return 1;
   7700 }
   7701 
   7702 static int x11_errors;
   7703 
   7704 static int
   7705 catch_x11_error(Display *display, XErrorEvent *error_event)
   7706 {
   7707     (void) display;
   7708     (void) error_event;
   7709     ++x11_errors;
   7710     return 0;
   7711 }
   7712 
   7713 Boolean
   7714 xtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
   7715 {
   7716     Boolean result = False;
   7717     Status code;
   7718 
   7719     memset(attrs, 0, sizeof(*attrs));
   7720     if (win != None) {
   7721 	XErrorHandler save = XSetErrorHandler(catch_x11_error);
   7722 	x11_errors = 0;
   7723 	code = XGetWindowAttributes(dpy, win, attrs);
   7724 	XSetErrorHandler(save);
   7725 	result = (Boolean) ((code != 0) && !x11_errors);
   7726 	if (result) {
   7727 	    TRACE_WIN_ATTRS(attrs);
   7728 	} else {
   7729 	    xtermWarning("invalid window-id %ld\n", (long) win);
   7730 	}
   7731     }
   7732     return result;
   7733 }
   7734 
   7735 Boolean
   7736 xtermGetWinProp(Display *display,
   7737 		Window win,
   7738 		Atom property,
   7739 		long long_offset,
   7740 		long long_length,
   7741 		Atom req_type,
   7742 		Atom *actual_type_return,
   7743 		int *actual_format_return,
   7744 		unsigned long *nitems_return,
   7745 		unsigned long *bytes_after_return,
   7746 		unsigned char **prop_return)
   7747 {
   7748     Boolean result = False;
   7749 
   7750     if (win != None) {
   7751 	XErrorHandler save = XSetErrorHandler(catch_x11_error);
   7752 	x11_errors = 0;
   7753 	if (XGetWindowProperty(display,
   7754 			       win,
   7755 			       property,
   7756 			       long_offset,
   7757 			       long_length,
   7758 			       False,
   7759 			       req_type,
   7760 			       actual_type_return,
   7761 			       actual_format_return,
   7762 			       nitems_return,
   7763 			       bytes_after_return,
   7764 			       prop_return) == Success
   7765 	    && x11_errors == 0) {
   7766 	    result = True;
   7767 	}
   7768 	XSetErrorHandler(save);
   7769     }
   7770     return result;
   7771 }
   7772 
   7773 void
   7774 xtermEmbedWindow(Window winToEmbedInto)
   7775 {
   7776     Display *dpy = XtDisplay(toplevel);
   7777     XWindowAttributes attrs;
   7778 
   7779     TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
   7780     if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
   7781 	XtermWidget xw = term;
   7782 	TScreen *screen = TScreenOf(xw);
   7783 
   7784 	XtRealizeWidget(toplevel);
   7785 
   7786 	TRACE(("...reparenting toplevel %#lx into %#lx\n",
   7787 	       XtWindow(toplevel),
   7788 	       winToEmbedInto));
   7789 	XReparentWindow(dpy,
   7790 			XtWindow(toplevel),
   7791 			winToEmbedInto, 0, 0);
   7792 
   7793 	screen->embed_high = (Dimension) attrs.height;
   7794 	screen->embed_wide = (Dimension) attrs.width;
   7795     }
   7796 }
   7797 
   7798 void
   7799 free_string(String value)
   7800 {
   7801     free((void *) value);
   7802 }
   7803 
   7804 /* Set tty's idea of window size, using the given file descriptor 'fd'. */
   7805 int
   7806 update_winsize(TScreen *screen, int rows, int cols, int height, int width)
   7807 {
   7808     int code = -1;
   7809 #ifdef TTYSIZE_STRUCT
   7810     static int last_rows = -1;
   7811     static int last_cols = -1;
   7812     static int last_high = -1;
   7813     static int last_wide = -1;
   7814 
   7815     TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n",
   7816 	   last_rows, last_cols, last_high, last_wide,
   7817 	   rows, cols, height, width));
   7818 
   7819     if (rows != last_rows
   7820 	|| cols != last_cols
   7821 	|| last_high != height
   7822 	|| last_wide != width) {
   7823 	TTYSIZE_STRUCT ts;
   7824 
   7825 	last_rows = rows;
   7826 	last_cols = cols;
   7827 	last_high = height;
   7828 	last_wide = width;
   7829 	setup_winsize(ts, rows, cols, height, width);
   7830 	TRACE_RC(code, SET_TTYSIZE(screen->respond, ts));
   7831 	trace_winsize(ts, "from SET_TTYSIZE");
   7832     }
   7833 #endif
   7834 
   7835     (void) rows;
   7836     (void) cols;
   7837     (void) height;
   7838     (void) width;
   7839 
   7840     return code;
   7841 }
   7842 
   7843 /*
   7844  * Update stty settings to match the values returned by dtterm window
   7845  * manipulation 18 and 19.
   7846  */
   7847 void
   7848 xtermSetWinSize(XtermWidget xw)
   7849 {
   7850 #if OPT_TEK4014
   7851     if (!TEK4014_ACTIVE(xw))
   7852 #endif
   7853 	if (XtIsRealized((Widget) xw)) {
   7854 	    TScreen *screen = TScreenOf(xw);
   7855 
   7856 	    TRACE(("xtermSetWinSize\n"));
   7857 	    update_winsize(screen,
   7858 			   MaxRows(screen),
   7859 			   MaxCols(screen),
   7860 			   Height(screen),
   7861 			   Width(screen));
   7862 	}
   7863 }
   7864 
   7865 static void
   7866 xtermInitTitle(TScreen *screen, int which)
   7867 {
   7868     TRACE(("xtermInitTitle #%d\n", which));
   7869     screen->saved_titles.data[which].iconName = NULL;
   7870     screen->saved_titles.data[which].windowName = NULL;
   7871 }
   7872 
   7873 /*
   7874  * Store/update an item on the title stack.
   7875  */
   7876 void
   7877 xtermPushTitle(TScreen *screen, int which, SaveTitle * item)
   7878 {
   7879     if (which-- <= 0) {
   7880 	which = screen->saved_titles.used++;
   7881 	screen->saved_titles.used %= MAX_SAVED_TITLES;
   7882     }
   7883     which %= MAX_SAVED_TITLES;
   7884     xtermFreeTitle(&screen->saved_titles.data[which]);
   7885     screen->saved_titles.data[which] = *item;
   7886     TRACE(("xtermPushTitle #%d: icon='%s', window='%s'\n", which,
   7887 	   NonNull(item->iconName),
   7888 	   NonNull(item->windowName)));
   7889 }
   7890 
   7891 /*
   7892  * Pop/retrieve an item from the title stack.
   7893  */
   7894 Boolean
   7895 xtermPopTitle(TScreen *screen, int which, SaveTitle * item)
   7896 {
   7897     Boolean result = True;
   7898     Boolean popped = False;
   7899 
   7900     if (which-- > 0) {
   7901 	which %= MAX_SAVED_TITLES;
   7902     } else if (screen->saved_titles.used > 0) {
   7903 	which = ((--(screen->saved_titles.used) + MAX_SAVED_TITLES) % MAX_SAVED_TITLES);
   7904 	popped = True;
   7905     } else {
   7906 	result = False;
   7907     }
   7908     if (result) {
   7909 	*item = screen->saved_titles.data[which];
   7910 	TRACE(("xtermPopTitle #%d: icon='%s', window='%s'\n", which,
   7911 	       NonNull(item->iconName),
   7912 	       NonNull(item->windowName)));
   7913 
   7914 	/* if the data is incomplete, try to get it from the next levels */
   7915 #define TryHigher(name) \
   7916 	if (item->name == NULL) { \
   7917 	    int n; \
   7918 	    for (n = 1; n < MAX_SAVED_TITLES; ++n) { \
   7919 		int nw = ((which - n) + MAX_SAVED_TITLES) % MAX_SAVED_TITLES; \
   7920 		if ((item->name = screen->saved_titles.data[nw].name) != NULL) { \
   7921 		    item->name = x_strdup(item->name); \
   7922 		    break; \
   7923 		} \
   7924 	    } \
   7925 	}
   7926 	TryHigher(iconName);
   7927 	TryHigher(windowName);
   7928 
   7929 	if (popped) {
   7930 	    xtermInitTitle(screen, which);
   7931 	}
   7932     }
   7933     return result;
   7934 }
   7935 
   7936 /*
   7937  * Discard data used for pushing or popping title.
   7938  */
   7939 void
   7940 xtermFreeTitle(SaveTitle * item)
   7941 {
   7942     TRACE(("xtermFreeTitle icon='%s', window='%s'\n",
   7943 	   NonNull(item->iconName),
   7944 	   NonNull(item->windowName)));
   7945     FreeAndNull(item->iconName);
   7946     FreeAndNull(item->windowName);
   7947 }
   7948 
   7949 #if OPT_XTERM_SGR
   7950 void
   7951 xtermReportTitleStack(XtermWidget xw)
   7952 {
   7953     TScreen *screen = TScreenOf(xw);
   7954     char reply[100];
   7955 
   7956     sprintf(reply, "%d;%d", screen->saved_titles.used, MAX_SAVED_TITLES);
   7957     unparseputc1(xw, ANSI_CSI);
   7958     unparseputs(xw, reply);
   7959     unparseputc(xw, '#');
   7960     unparseputc(xw, 'S');
   7961     unparse_end(xw);
   7962 }
   7963 
   7964 #if OPT_TRACE
   7965 static char *
   7966 traceIFlags(IFlags flags)
   7967 {
   7968     static char result[1000];
   7969     result[0] = '\0';
   7970 #define DATA(name) if (flags & name) { strcat(result, " " #name); }
   7971     DATA(INVERSE);
   7972     DATA(UNDERLINE);
   7973     DATA(BOLD);
   7974     DATA(BLINK);
   7975     DATA(INVISIBLE);
   7976     DATA(BG_COLOR);
   7977     DATA(FG_COLOR);
   7978 
   7979 #if OPT_WIDE_ATTRS
   7980     DATA(ATR_FAINT);
   7981     DATA(ATR_ITALIC);
   7982     DATA(ATR_STRIKEOUT);
   7983     DATA(ATR_DBL_UNDER);
   7984     DATA(ATR_DIRECT_FG);
   7985     DATA(ATR_DIRECT_BG);
   7986 #endif
   7987 #undef DATA
   7988     return result;
   7989 }
   7990 
   7991 static char *
   7992 traceIStack(unsigned flags)
   7993 {
   7994     static char result[1000];
   7995     result[0] = '\0';
   7996 #define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
   7997     DATA(INVERSE);
   7998     DATA(UNDERLINE);
   7999     DATA(BOLD);
   8000     DATA(BLINK);
   8001     DATA(INVISIBLE);
   8002 #if OPT_ISO_COLORS
   8003     DATA(BG_COLOR);
   8004     DATA(FG_COLOR);
   8005 #endif
   8006 
   8007 #if OPT_WIDE_ATTRS
   8008     DATA(ATR_FAINT);
   8009     DATA(ATR_ITALIC);
   8010     DATA(ATR_STRIKEOUT);
   8011     DATA(ATR_DBL_UNDER);
   8012     /* direct-colors are a special case of ISO-colors (see above) */
   8013 #endif
   8014 #undef DATA
   8015     return result;
   8016 }
   8017 #endif
   8018 
   8019 void
   8020 xtermPushSGR(XtermWidget xw, int value)
   8021 {
   8022     SavedSGR *s = &(xw->saved_sgr);
   8023 
   8024     TRACE(("xtermPushSGR %d mask %#x %s\n",
   8025 	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
   8026 
   8027     if (s->used < MAX_SAVED_SGR) {
   8028 	s->stack[s->used].mask = (IFlags) value;
   8029 #define PUSH_FLAG(name) \
   8030 	    s->stack[s->used].name = xw->name;\
   8031 	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
   8032 #define PUSH_DATA(name) \
   8033 	    s->stack[s->used].name = xw->name;\
   8034 	    TRACE(("...may pop %s %d\n", #name, xw->name))
   8035 	PUSH_FLAG(flags);
   8036 #if OPT_ISO_COLORS
   8037 	PUSH_DATA(sgr_foreground);
   8038 	PUSH_DATA(sgr_background);
   8039 	PUSH_DATA(sgr_38_xcolors);
   8040 #endif
   8041     }
   8042     s->used++;
   8043 }
   8044 
   8045 #define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
   8046 
   8047 void
   8048 xtermReportSGR(XtermWidget xw, XTermRect *value)
   8049 {
   8050     TScreen *screen = TScreenOf(xw);
   8051     char reply[BUFSIZ];
   8052     CellData working;
   8053     int row, col;
   8054     Boolean first = True;
   8055 
   8056     TRACE(("xtermReportSGR %d,%d - %d,%d\n",
   8057 	   value->top, value->left,
   8058 	   value->bottom, value->right));
   8059 
   8060     memset(&working, 0, sizeof(working));
   8061     for (row = value->top - 1; row < value->bottom; ++row) {
   8062 	LineData *ld = getLineData(screen, row);
   8063 	if (ld == NULL)
   8064 	    continue;
   8065 	for (col = value->left - 1; col < value->right; ++col) {
   8066 	    if (first) {
   8067 		first = False;
   8068 		saveCellData(screen, &working, 0, ld, NULL, col);
   8069 	    }
   8070 	    working.attribs &= ld->attribs[col];
   8071 #if OPT_ISO_COLORS
   8072 	    if (working.attribs & FG_COLOR
   8073 		&& GetCellColorFG(working.color)
   8074 		!= GetCellColorFG(ld->color[col])) {
   8075 		IAttrClr(working.attribs, FG_COLOR);
   8076 	    }
   8077 	    if (working.attribs & BG_COLOR
   8078 		&& GetCellColorBG(working.color)
   8079 		!= GetCellColorBG(ld->color[col])) {
   8080 		IAttrClr(working.attribs, BG_COLOR);
   8081 	    }
   8082 #endif
   8083 	}
   8084     }
   8085     xtermFormatSGR(xw, reply,
   8086 		   working.attribs,
   8087 		   GetCellColorFG(working.color),
   8088 		   GetCellColorBG(working.color));
   8089     unparseputc1(xw, ANSI_CSI);
   8090     unparseputs(xw, reply);
   8091     unparseputc(xw, 'm');
   8092     unparse_end(xw);
   8093 }
   8094 
   8095 void
   8096 xtermPopSGR(XtermWidget xw)
   8097 {
   8098     SavedSGR *s = &(xw->saved_sgr);
   8099 
   8100     TRACE(("xtermPopSGR %d\n", s->used));
   8101 
   8102     if (s->used > 0) {
   8103 	if (s->used-- <= MAX_SAVED_SGR) {
   8104 	    IFlags mask = s->stack[s->used].mask;
   8105 	    Boolean changed = False;
   8106 
   8107 	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
   8108 	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
   8109 	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
   8110 #define POP_FLAG(name) \
   8111 	    if (xBIT(ps##name - 1) & mask) { \
   8112 	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
   8113 		    changed = True; \
   8114 		    UIntClr(xw->flags, name); \
   8115 		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
   8116 		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
   8117 		} \
   8118 	    }
   8119 #define POP_FLAG2(name,part) \
   8120 	    if (xBIT(ps##name - 1) & mask) { \
   8121 	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
   8122 		    changed = True; \
   8123 		    UIntClr(xw->flags, part); \
   8124 		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
   8125 		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
   8126 		} \
   8127 	    }
   8128 #define POP_DATA(name,value) \
   8129 	    if (xBIT(ps##name - 1) & mask) { \
   8130 	        Bool always = False; \
   8131 	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
   8132 		    always = changed = True; \
   8133 		    UIntClr(xw->flags, name); \
   8134 		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
   8135 		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
   8136 		} \
   8137 		if (always || (xw->value != s->stack[s->used].value)) { \
   8138 		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
   8139 		    xw->value = s->stack[s->used].value; \
   8140 		    changed = True; \
   8141 		} \
   8142 	    }
   8143 	    POP_FLAG(BOLD);
   8144 	    POP_FLAG(UNDERLINE);
   8145 	    POP_FLAG(BLINK);
   8146 	    POP_FLAG(INVERSE);
   8147 	    POP_FLAG(INVISIBLE);
   8148 #if OPT_WIDE_ATTRS
   8149 	    if (xBIT(psATR_ITALIC - 1) & mask) {
   8150 		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
   8151 	    }
   8152 	    POP_FLAG(ATR_ITALIC);
   8153 	    POP_FLAG(ATR_FAINT);
   8154 	    POP_FLAG(ATR_STRIKEOUT);
   8155 	    POP_FLAG(ATR_DBL_UNDER);
   8156 #endif
   8157 #if OPT_ISO_COLORS
   8158 	    POP_DATA(FG_COLOR, sgr_foreground);
   8159 	    POP_DATA(BG_COLOR, sgr_background);
   8160 	    POP_DATA(BG_COLOR, sgr_38_xcolors);
   8161 #if OPT_DIRECT_COLOR
   8162 	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
   8163 	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
   8164 #endif
   8165 	    if (changed) {
   8166 		setExtendedColors(xw);
   8167 	    }
   8168 #else
   8169 	    (void) changed;
   8170 #endif
   8171 	}
   8172 #if OPT_ISO_COLORS
   8173 	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
   8174 	       traceIFlags(xw->flags),
   8175 	       xw->sgr_foreground,
   8176 	       xw->sgr_background,
   8177 	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
   8178 #else
   8179 	TRACE(("xtermP -> flags%s\n",
   8180 	       traceIFlags(xw->flags)));
   8181 #endif
   8182     }
   8183 }
   8184 
   8185 #if OPT_ISO_COLORS
   8186 static ColorSlot *
   8187 allocColorSlot(XtermWidget xw, int slot)
   8188 {
   8189     SavedColors *s = &(xw->saved_colors);
   8190     ColorSlot *result = NULL;
   8191 
   8192     if (slot >= 0 && slot < MAX_SAVED_SGR) {
   8193 	if (s->palettes[slot] == NULL) {
   8194 	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
   8195 						     sizeof(ColorSlot)
   8196 						     + (sizeof(ColorRes)
   8197 							* MAXCOLORS));
   8198 	}
   8199 	result = s->palettes[slot];
   8200     }
   8201     return result;
   8202 }
   8203 
   8204 static void
   8205 popOldColors(XtermWidget xw, ScrnColors * source)
   8206 {
   8207     Boolean changed = False;
   8208     ScrnColors *target = xw->work.oldColors;
   8209 
   8210     if (source->which != target->which) {
   8211 	changed = True;
   8212     } else {
   8213 	int n;
   8214 	for (n = 0; n < NCOLORS; ++n) {
   8215 	    if (COLOR_DEFINED(source, n)) {
   8216 		if (COLOR_DEFINED(target, n)) {
   8217 		    if (source->colors[n] != target->colors[n]) {
   8218 			changed = True;
   8219 			break;
   8220 		    }
   8221 		} else {
   8222 		    changed = True;
   8223 		    break;
   8224 		}
   8225 	    } else if (COLOR_DEFINED(target, n)) {
   8226 		changed = True;
   8227 		break;
   8228 	    }
   8229 	}
   8230     }
   8231     if (changed) {
   8232 	ChangeColors(xw, source);
   8233 	UpdateOldColors(xw, source);
   8234     }
   8235 }
   8236 #endif /* OPT_ISO_COLORS */
   8237 
   8238 #define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
   8239 #define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
   8240 
   8241 /*
   8242  * By default, a "push" increments the stack after copying to the current
   8243  * slot.  But a specific target allows one to copy into a specific slot.
   8244  */
   8245 void
   8246 xtermPushColors(XtermWidget xw, int value)
   8247 {
   8248 #if OPT_ISO_COLORS
   8249     SavedColors *s = &(xw->saved_colors);
   8250     int pushed = s->used;
   8251     int actual = (value <= 0) ? pushed : (value - 1);
   8252 
   8253     TRACE(("xtermPushColors %d:%d\n", actual, pushed));
   8254     if (actual < MAX_SAVED_SGR && actual >= 0) {
   8255 	TScreen *screen = TScreenOf(xw);
   8256 	ColorSlot *palette;
   8257 
   8258 	if ((palette = allocColorSlot(xw, actual)) != NULL) {
   8259 	    GetColors(xw, &(palette->base));
   8260 	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
   8261 	    if (value < 0) {
   8262 		s->used++;
   8263 		if (s->last < s->used)
   8264 		    s->last = s->used;
   8265 	    } else {
   8266 		s->used = value;
   8267 	    }
   8268 	}
   8269     }
   8270 #else
   8271     (void) xw;
   8272     (void) value;
   8273 #endif
   8274 }
   8275 
   8276 void
   8277 xtermPopColors(XtermWidget xw, int value)
   8278 {
   8279 #if OPT_ISO_COLORS
   8280     SavedColors *s = &(xw->saved_colors);
   8281     int popped = (s->used - 1);
   8282     int actual = (value <= 0) ? popped : (value - 1);
   8283 
   8284     TRACE(("xtermPopColors %d:%d\n", actual, popped));
   8285     if (actual < MAX_SAVED_SGR && actual >= 0) {
   8286 	TScreen *screen = TScreenOf(xw);
   8287 	ColorSlot *palette;
   8288 
   8289 	if ((palette = s->palettes[actual]) != NULL) {
   8290 	    Boolean changed = DiffColorSlot(screen->Acolors,
   8291 					    palette->ansi,
   8292 					    MAXCOLORS);
   8293 
   8294 	    GetOldColors(xw);
   8295 	    popOldColors(xw, &(palette->base));
   8296 	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
   8297 	    s->used = actual;
   8298 	    if (changed)
   8299 		xtermRepaint(xw);
   8300 	}
   8301     }
   8302 #else
   8303     (void) xw;
   8304     (void) value;
   8305 #endif
   8306 }
   8307 
   8308 void
   8309 xtermReportColors(XtermWidget xw)
   8310 {
   8311     ANSI reply;
   8312     SavedColors *s = &(xw->saved_colors);
   8313 
   8314     memset(&reply, 0, sizeof(reply));
   8315     reply.a_type = ANSI_CSI;
   8316     reply.a_pintro = '?';
   8317     reply.a_param[reply.a_nparam++] = (ParmType) s->used;
   8318     reply.a_param[reply.a_nparam++] = (ParmType) s->last;
   8319     reply.a_inters = '#';
   8320     reply.a_final = 'Q';
   8321     unparseseq(xw, &reply);
   8322 }
   8323 #endif /* OPT_XTERM_SGR */
   8324