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