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