misc.c revision 9a64e1c5
19a64e1c5Smrg/* $XTermId: misc.c,v 1.712 2014/05/26 14:45:58 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4fa3f02f3Smrg * Copyright 1999-2013,2014 by Thomas E. Dickey
5d522f475Smrg *
6cd3331d0Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg#include <version.h>
56b7c89284Ssnj#include <main.h>
57d522f475Smrg#include <xterm.h>
58d522f475Smrg
59d522f475Smrg#include <sys/stat.h>
60d522f475Smrg#include <stdio.h>
613367019cSmrg#include <stdarg.h>
62d522f475Smrg#include <signal.h>
63d522f475Smrg#include <ctype.h>
64d522f475Smrg#include <pwd.h>
65d522f475Smrg#include <sys/wait.h>
66d522f475Smrg
67d522f475Smrg#include <X11/keysym.h>
68d522f475Smrg#include <X11/Xatom.h>
69d522f475Smrg#include <X11/cursorfont.h>
70d522f475Smrg#include <X11/Xlocale.h>
71d522f475Smrg
72d522f475Smrg#include <X11/Xmu/Error.h>
73d522f475Smrg#include <X11/Xmu/SysUtil.h>
74d522f475Smrg#include <X11/Xmu/WinUtil.h>
75d522f475Smrg#include <X11/Xmu/Xmu.h>
76d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
77d522f475Smrg#include <X11/Sunkeysym.h>
78d522f475Smrg#endif
79d522f475Smrg
803367019cSmrg#ifdef HAVE_LIBXPM
813367019cSmrg#include <X11/xpm.h>
823367019cSmrg#endif
833367019cSmrg
84d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
85d522f475Smrg#include <langinfo.h>
86d522f475Smrg#endif
87d522f475Smrg
88d522f475Smrg#include <xutf8.h>
89d522f475Smrg
90d522f475Smrg#include <data.h>
91d522f475Smrg#include <error.h>
92d522f475Smrg#include <menu.h>
93d522f475Smrg#include <fontutils.h>
94d522f475Smrg#include <xstrings.h>
95d522f475Smrg#include <xtermcap.h>
96d522f475Smrg#include <VTparse.h>
97fa3f02f3Smrg#include <graphics.h>
989a64e1c5Smrg#include <graphics_regis.h>
999a64e1c5Smrg#include <graphics_sixel.h>
100d522f475Smrg
101d522f475Smrg#include <assert.h>
102d522f475Smrg
103d522f475Smrg#if (XtSpecificationRelease < 6)
104d522f475Smrg#ifndef X_GETTIMEOFDAY
105d522f475Smrg#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0)
106d522f475Smrg#endif
107d522f475Smrg#endif
108d522f475Smrg
109d522f475Smrg#ifdef VMS
110d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
111d522f475Smrg#ifdef ALLOWLOGFILEEXEC
112d522f475Smrg#undef ALLOWLOGFILEEXEC
113d522f475Smrg#endif
114d522f475Smrg#endif /* VMS */
115d522f475Smrg
116d522f475Smrg#if OPT_TEK4014
117d522f475Smrg#define OUR_EVENT(event,Type) \
118d522f475Smrg		(event.type == Type && \
119d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
120d522f475Smrg		    (tekWidget && \
121d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
122d522f475Smrg#else
123d522f475Smrg#define OUR_EVENT(event,Type) \
124d522f475Smrg		(event.type == Type && \
125d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
126d522f475Smrg#endif
127d522f475Smrg
1283367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
129d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
130d522f475Smrg
1313367019cSmrgstatic char emptyString[] = "";
1323367019cSmrg
133d522f475Smrg#if OPT_EXEC_XTERM
134d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
135d522f475Smrg   error; adapted from libc docs */
136d522f475Smrgstatic char *
137d522f475SmrgReadlink(const char *filename)
138d522f475Smrg{
139d522f475Smrg    char *buf = NULL;
140cd3331d0Smrg    size_t size = 100;
141d522f475Smrg    int n;
142d522f475Smrg
143d522f475Smrg    for (;;) {
144d522f475Smrg	buf = TypeRealloc(char, size, buf);
145d522f475Smrg	memset(buf, 0, size);
146d522f475Smrg
147cd3331d0Smrg	n = (int) readlink(filename, buf, size);
148d522f475Smrg	if (n < 0) {
149d522f475Smrg	    free(buf);
150d522f475Smrg	    return NULL;
151d522f475Smrg	}
152d522f475Smrg
153d522f475Smrg	if ((unsigned) n < size) {
154d522f475Smrg	    return buf;
155d522f475Smrg	}
156d522f475Smrg
157d522f475Smrg	size *= 2;
158d522f475Smrg    }
159d522f475Smrg}
160d522f475Smrg#endif /* OPT_EXEC_XTERM */
161d522f475Smrg
162d522f475Smrgstatic void
163d522f475SmrgSleep(int msec)
164d522f475Smrg{
165d522f475Smrg    static struct timeval select_timeout;
166d522f475Smrg
167d522f475Smrg    select_timeout.tv_sec = 0;
168d522f475Smrg    select_timeout.tv_usec = msec * 1000;
169d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
170d522f475Smrg}
171d522f475Smrg
172d522f475Smrgstatic void
1733367019cSmrgselectwindow(XtermWidget xw, int flag)
174d522f475Smrg{
1753367019cSmrg    TScreen *screen = TScreenOf(xw);
1763367019cSmrg
177d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
178d522f475Smrg
179d522f475Smrg#if OPT_TEK4014
1803367019cSmrg    if (TEK4014_ACTIVE(xw)) {
181d522f475Smrg	if (!Ttoggled)
182d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
183d522f475Smrg	screen->select |= flag;
184d522f475Smrg	if (!Ttoggled)
185d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
186d522f475Smrg    } else
187d522f475Smrg#endif
188d522f475Smrg    {
1893367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
1903367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
1913367019cSmrg	if (input && input->xic)
1923367019cSmrg	    XSetICFocus(input->xic);
1933367019cSmrg#endif
194d522f475Smrg
195d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
196d522f475Smrg	    HideCursor();
197d522f475Smrg	screen->select |= flag;
198d522f475Smrg	if (screen->cursor_state)
199d522f475Smrg	    ShowCursor();
200d522f475Smrg    }
201cd3331d0Smrg    GetScrollLock(screen);
202d522f475Smrg}
203d522f475Smrg
204d522f475Smrgstatic void
2053367019cSmrgunselectwindow(XtermWidget xw, int flag)
206d522f475Smrg{
2073367019cSmrg    TScreen *screen = TScreenOf(xw);
2083367019cSmrg
209d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
210d522f475Smrg
2113367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
212d522f475Smrg	screen->hide_pointer = False;
2133367019cSmrg	xtermDisplayCursor(xw);
214d522f475Smrg    }
215d522f475Smrg
216d522f475Smrg    if (!screen->always_highlight) {
217d522f475Smrg#if OPT_TEK4014
2183367019cSmrg	if (TEK4014_ACTIVE(xw)) {
219d522f475Smrg	    if (!Ttoggled)
220d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
221d522f475Smrg	    screen->select &= ~flag;
222d522f475Smrg	    if (!Ttoggled)
223d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
224d522f475Smrg	} else
225d522f475Smrg#endif
226d522f475Smrg	{
2273367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
2283367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2293367019cSmrg	    if (input && input->xic)
2303367019cSmrg		XUnsetICFocus(input->xic);
2313367019cSmrg#endif
232d522f475Smrg
233d522f475Smrg	    screen->select &= ~flag;
234d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
235d522f475Smrg		HideCursor();
236d522f475Smrg	    if (screen->cursor_state)
237d522f475Smrg		ShowCursor();
238d522f475Smrg	}
239d522f475Smrg    }
240d522f475Smrg}
241d522f475Smrg
242d522f475Smrgstatic void
2439a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
244d522f475Smrg{
245d522f475Smrg    TScreen *screen = TScreenOf(xw);
246d522f475Smrg
247d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
248cd3331d0Smrg    TRACE_FOCUS(xw, ev);
249cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
250cd3331d0Smrg	ev->focus &&
251cd3331d0Smrg	!(screen->select & FOCUS))
2523367019cSmrg	selectwindow(xw, INWINDOW);
253d522f475Smrg}
254d522f475Smrg
255d522f475Smrgstatic void
2569a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
257d522f475Smrg{
258d522f475Smrg    TScreen *screen = TScreenOf(xw);
259d522f475Smrg
260d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
261cd3331d0Smrg    TRACE_FOCUS(xw, ev);
262cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
263cd3331d0Smrg	ev->focus &&
264cd3331d0Smrg	!(screen->select & FOCUS))
2653367019cSmrg	unselectwindow(xw, INWINDOW);
266d522f475Smrg}
267d522f475Smrg
268d522f475Smrg#ifndef XUrgencyHint
269d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
270d522f475Smrg#endif
271d522f475Smrg
272d522f475Smrgstatic void
273c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
274d522f475Smrg{
275c219fbebSmrg    TScreen *screen = TScreenOf(xw);
276c219fbebSmrg
277d522f475Smrg    if (screen->bellIsUrgent) {
278c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
279d522f475Smrg	if (h != 0) {
280c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
281d522f475Smrg		h->flags |= XUrgencyHint;
282d522f475Smrg	    } else {
283d522f475Smrg		h->flags &= ~XUrgencyHint;
284d522f475Smrg	    }
285c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
286d522f475Smrg	}
287d522f475Smrg    }
288d522f475Smrg}
289d522f475Smrg
290d522f475Smrgvoid
291d522f475Smrgdo_xevents(void)
292d522f475Smrg{
293d522f475Smrg    TScreen *screen = TScreenOf(term);
294d522f475Smrg
2953367019cSmrg    if (xtermAppPending()
296d522f475Smrg	||
297d522f475Smrg#if defined(VMS) || defined(__VMS)
298d522f475Smrg	screen->display->qlen > 0
299d522f475Smrg#else
300d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
301d522f475Smrg#endif
302d522f475Smrg	)
303d522f475Smrg	xevents();
304d522f475Smrg}
305d522f475Smrg
306d522f475Smrgvoid
307d522f475SmrgxtermDisplayCursor(XtermWidget xw)
308d522f475Smrg{
309d522f475Smrg    TScreen *screen = TScreenOf(xw);
310d522f475Smrg
311d522f475Smrg    if (screen->Vshow) {
312d522f475Smrg	if (screen->hide_pointer) {
313d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
314d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
315d522f475Smrg	} else {
316d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
317d522f475Smrg	    recolor_cursor(screen,
318d522f475Smrg			   screen->pointer_cursor,
319d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
320d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
321d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
322d522f475Smrg	}
323d522f475Smrg    }
324d522f475Smrg}
325d522f475Smrg
326d522f475Smrgvoid
327d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
328d522f475Smrg{
329d522f475Smrg    static int tried = -1;
330d522f475Smrg    TScreen *screen = TScreenOf(xw);
331d522f475Smrg
332d522f475Smrg#if OPT_TEK4014
333d522f475Smrg    if (TEK4014_SHOWN(xw))
334d522f475Smrg	enable = True;
335d522f475Smrg#endif
336d522f475Smrg
337d522f475Smrg    /*
338d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
339d522f475Smrg     * the mouse-mode:
340d522f475Smrg     */
341d522f475Smrg    if (!enable) {
342d522f475Smrg	switch (screen->pointer_mode) {
343d522f475Smrg	case pNever:
344d522f475Smrg	    enable = True;
345d522f475Smrg	    break;
346d522f475Smrg	case pNoMouse:
347d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
348d522f475Smrg		enable = True;
349d522f475Smrg	    break;
350d522f475Smrg	case pAlways:
3513367019cSmrg	case pFocused:
352d522f475Smrg	    break;
353d522f475Smrg	}
354d522f475Smrg    }
355d522f475Smrg
356d522f475Smrg    if (enable) {
357d522f475Smrg	if (screen->hide_pointer) {
358d522f475Smrg	    screen->hide_pointer = False;
359d522f475Smrg	    xtermDisplayCursor(xw);
360d522f475Smrg	    switch (screen->send_mouse_pos) {
361d522f475Smrg	    case ANY_EVENT_MOUSE:
362d522f475Smrg		break;
363d522f475Smrg	    default:
364d522f475Smrg		MotionOff(screen, xw);
365d522f475Smrg		break;
366d522f475Smrg	    }
367d522f475Smrg	}
368d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
369d522f475Smrg	if (screen->hidden_cursor == 0) {
370d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
371d522f475Smrg	}
372d522f475Smrg	if (screen->hidden_cursor == 0) {
373d522f475Smrg	    tried = 1;
374d522f475Smrg	} else {
375d522f475Smrg	    tried = 0;
376d522f475Smrg	    screen->hide_pointer = True;
377d522f475Smrg	    xtermDisplayCursor(xw);
378d522f475Smrg	    MotionOn(screen, xw);
379d522f475Smrg	}
380d522f475Smrg    }
381d522f475Smrg}
382d522f475Smrg
3833367019cSmrg#if OPT_TRACE
3843367019cSmrgstatic void
3859a64e1c5SmrgTraceExposeEvent(XEvent *arg)
3863367019cSmrg{
3873367019cSmrg    XExposeEvent *event = (XExposeEvent *) arg;
3883367019cSmrg
3893367019cSmrg    TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n",
3903367019cSmrg	   event->serial,
3913367019cSmrg	   event->count,
3923367019cSmrg	   event->y,
3933367019cSmrg	   event->x,
3943367019cSmrg	   event->height,
3953367019cSmrg	   event->width,
3963367019cSmrg	   event->window));
3973367019cSmrg}
3983367019cSmrg
3993367019cSmrg#else
4003367019cSmrg#define TraceExposeEvent(event)	/* nothing */
4013367019cSmrg#endif
4023367019cSmrg
4033367019cSmrg/* true if p contains q */
4043367019cSmrg#define ExposeContains(p,q) \
4053367019cSmrg	    ((p)->y <= (q)->y \
4063367019cSmrg	  && (p)->x <= (q)->x \
4073367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
4083367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
4093367019cSmrg
4103367019cSmrgstatic XtInputMask
4119a64e1c5SmrgmergeExposeEvents(XEvent *target)
4123367019cSmrg{
4133367019cSmrg    XEvent next_event;
4143367019cSmrg    XExposeEvent *p, *q;
4153367019cSmrg
4163367019cSmrg    TRACE(("pending Expose...?\n"));
4173367019cSmrg    TraceExposeEvent(target);
4183367019cSmrg    XtAppNextEvent(app_con, target);
4193367019cSmrg    p = (XExposeEvent *) target;
4203367019cSmrg
4213367019cSmrg    while (XtAppPending(app_con)
4223367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4233367019cSmrg	   && next_event.type == Expose) {
4243367019cSmrg	Boolean merge_this = False;
4253367019cSmrg
4263367019cSmrg	TraceExposeEvent(&next_event);
4273367019cSmrg	q = (XExposeEvent *) (&next_event);
4283367019cSmrg	XtAppNextEvent(app_con, &next_event);
4293367019cSmrg
4303367019cSmrg	/*
4313367019cSmrg	 * If either window is contained within the other, merge the events.
4323367019cSmrg	 * The traces show that there are also cases where a full repaint of
4333367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4343367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4353367019cSmrg	 * to skim several events ahead.
4363367019cSmrg	 */
4373367019cSmrg	if (p->window == q->window) {
4383367019cSmrg	    if (ExposeContains(p, q)) {
4393367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4403367019cSmrg		merge_this = True;
4413367019cSmrg		next_event = *target;
4423367019cSmrg	    } else if (ExposeContains(q, p)) {
4433367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4443367019cSmrg		merge_this = True;
4453367019cSmrg	    }
4463367019cSmrg	}
4473367019cSmrg	if (!merge_this) {
4483367019cSmrg	    XtDispatchEvent(target);
4493367019cSmrg	}
4503367019cSmrg	*target = next_event;
4513367019cSmrg    }
4523367019cSmrg    XtDispatchEvent(target);
4533367019cSmrg    return XtAppPending(app_con);
4543367019cSmrg}
4553367019cSmrg
4563367019cSmrg#if OPT_TRACE
4573367019cSmrgstatic void
4589a64e1c5SmrgTraceConfigureEvent(XEvent *arg)
4593367019cSmrg{
4603367019cSmrg    XConfigureEvent *event = (XConfigureEvent *) arg;
4613367019cSmrg
4623367019cSmrg    TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n",
4633367019cSmrg	   event->serial,
4643367019cSmrg	   event->y,
4653367019cSmrg	   event->x,
4663367019cSmrg	   event->height,
4673367019cSmrg	   event->width,
4683367019cSmrg	   event->window));
4693367019cSmrg}
4703367019cSmrg
4713367019cSmrg#else
4723367019cSmrg#define TraceConfigureEvent(event)	/* nothing */
4733367019cSmrg#endif
4743367019cSmrg
4753367019cSmrg/*
4763367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4773367019cSmrg * event.  Remove that from the queue so we can look further.
4783367019cSmrg *
4793367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4803367019cSmrg * that.  If the adjacent events are for different windows, process the older
4813367019cSmrg * event and update the event used for comparing windows.  If they are for the
4823367019cSmrg * same window, only the newer event is of interest.
4833367019cSmrg *
4843367019cSmrg * Finally, process the (remaining) configure-notify event.
4853367019cSmrg */
4863367019cSmrgstatic XtInputMask
4879a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4883367019cSmrg{
4893367019cSmrg    XEvent next_event;
4903367019cSmrg    XConfigureEvent *p, *q;
4913367019cSmrg
4923367019cSmrg    XtAppNextEvent(app_con, target);
4933367019cSmrg    p = (XConfigureEvent *) target;
4943367019cSmrg
4953367019cSmrg    TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no"));
4963367019cSmrg    TraceConfigureEvent(target);
4973367019cSmrg
4983367019cSmrg    if (XtAppPending(app_con)
4993367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
5003367019cSmrg	&& next_event.type == ConfigureNotify) {
5013367019cSmrg	Boolean merge_this = False;
5023367019cSmrg
5033367019cSmrg	TraceConfigureEvent(&next_event);
5043367019cSmrg	XtAppNextEvent(app_con, &next_event);
5053367019cSmrg	q = (XConfigureEvent *) (&next_event);
5063367019cSmrg
5073367019cSmrg	if (p->window == q->window) {
5083367019cSmrg	    TRACE(("pending Configure...merged\n"));
5093367019cSmrg	    merge_this = True;
5103367019cSmrg	}
5113367019cSmrg	if (!merge_this) {
5123367019cSmrg	    TRACE(("pending Configure...skipped\n"));
5133367019cSmrg	    XtDispatchEvent(target);
5143367019cSmrg	}
5153367019cSmrg	*target = next_event;
5163367019cSmrg    }
5173367019cSmrg    XtDispatchEvent(target);
5183367019cSmrg    return XtAppPending(app_con);
5193367019cSmrg}
5203367019cSmrg
5213367019cSmrg/*
5223367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5233367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5243367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5253367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5263367019cSmrg * point.
5273367019cSmrg *
5283367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5293367019cSmrg */
5303367019cSmrgXtInputMask
5313367019cSmrgxtermAppPending(void)
5323367019cSmrg{
5333367019cSmrg    XtInputMask result = XtAppPending(app_con);
5343367019cSmrg    XEvent this_event;
5353367019cSmrg
5363367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
5373367019cSmrg	if (this_event.type == Expose) {
5383367019cSmrg	    result = mergeExposeEvents(&this_event);
539fa3f02f3Smrg	    TRACE(("got merged expose events\n"));
5403367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5413367019cSmrg	    result = mergeConfigureEvents(&this_event);
542fa3f02f3Smrg	    TRACE(("got merged configure notify events\n"));
5433367019cSmrg	} else {
5443367019cSmrg	    TRACE(("pending %s\n", visibleEventType(this_event.type)));
5453367019cSmrg	    break;
5463367019cSmrg	}
5473367019cSmrg    }
5483367019cSmrg    return result;
5493367019cSmrg}
5503367019cSmrg
551d522f475Smrgvoid
552d522f475Smrgxevents(void)
553d522f475Smrg{
554d522f475Smrg    XtermWidget xw = term;
555d522f475Smrg    TScreen *screen = TScreenOf(xw);
556d522f475Smrg    XEvent event;
557d522f475Smrg    XtInputMask input_mask;
558d522f475Smrg
559d522f475Smrg    if (need_cleanup)
5603367019cSmrg	NormalExit();
561d522f475Smrg
562d522f475Smrg    if (screen->scroll_amt)
563d522f475Smrg	FlushScroll(xw);
564d522f475Smrg    /*
565d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
566d522f475Smrg     * will process the timeout and return without blockng on the
567cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
568d522f475Smrg     * with select().
569d522f475Smrg     */
5703367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
571cd3331d0Smrg	if (input_mask & XtIMTimer)
572cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
573d522f475Smrg#if OPT_SESSION_MGT
574cd3331d0Smrg	/*
575cd3331d0Smrg	 * Session management events are alternative input events. Deal with
576cd3331d0Smrg	 * them in the same way.
577cd3331d0Smrg	 */
578cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
579cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
580d522f475Smrg#endif
581cd3331d0Smrg	else
582cd3331d0Smrg	    break;
583cd3331d0Smrg    }
584d522f475Smrg
585d522f475Smrg    /*
586d522f475Smrg     * If there's no XEvents, don't wait around...
587d522f475Smrg     */
588d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
589d522f475Smrg	return;
590d522f475Smrg    do {
591d522f475Smrg	/*
592d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
593d522f475Smrg	 * We simply ignore all events except for those not passed down to
594d522f475Smrg	 * this function, e.g., those handled in in_put().
595d522f475Smrg	 */
596d522f475Smrg	if (screen->waitingForTrackInfo) {
597d522f475Smrg	    Sleep(10);
598d522f475Smrg	    return;
599d522f475Smrg	}
600d522f475Smrg	XtAppNextEvent(app_con, &event);
601d522f475Smrg	/*
602d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
603d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
604d522f475Smrg	 * looking at the event ourselves we make sure that we can
605d522f475Smrg	 * do the right thing.
606d522f475Smrg	 */
607d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
608d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
609d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
610d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
611d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
612d522f475Smrg#if OPT_DEC_LOCATOR
613d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
614d522f475Smrg#endif /* OPT_DEC_LOCATOR */
615d522f475Smrg		   )
616d522f475Smrg		   && event.xany.type == MotionNotify
617d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
618d522f475Smrg	    SendMousePosition(xw, &event);
619cb4a1343Smrg	    xtermShowPointer(xw, True);
620d522f475Smrg	    continue;
621d522f475Smrg	}
622d522f475Smrg
623cb4a1343Smrg	/*
624cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
625cb4a1343Smrg	 * mouse pointer back on.
626cb4a1343Smrg	 */
627cb4a1343Smrg	if (screen->hide_pointer) {
6283367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6293367019cSmrg		switch (event.xany.type) {
6303367019cSmrg		case MotionNotify:
6313367019cSmrg		    xtermShowPointer(xw, True);
6323367019cSmrg		    break;
6333367019cSmrg		}
6343367019cSmrg	    } else {
6353367019cSmrg		switch (event.xany.type) {
6363367019cSmrg		case KeyPress:
6373367019cSmrg		case KeyRelease:
6383367019cSmrg		case ButtonPress:
6393367019cSmrg		case ButtonRelease:
6403367019cSmrg		    /* also these... */
6413367019cSmrg		case Expose:
6423367019cSmrg		case NoExpose:
6433367019cSmrg		case PropertyNotify:
6443367019cSmrg		case ClientMessage:
6453367019cSmrg		    break;
6463367019cSmrg		default:
6473367019cSmrg		    xtermShowPointer(xw, True);
6483367019cSmrg		    break;
6493367019cSmrg		}
650cb4a1343Smrg	    }
651cb4a1343Smrg	}
652cb4a1343Smrg
653d522f475Smrg	if (!event.xany.send_event ||
654d522f475Smrg	    screen->allowSendEvents ||
655d522f475Smrg	    ((event.xany.type != KeyPress) &&
656d522f475Smrg	     (event.xany.type != KeyRelease) &&
657d522f475Smrg	     (event.xany.type != ButtonPress) &&
658d522f475Smrg	     (event.xany.type != ButtonRelease))) {
659d522f475Smrg
660d522f475Smrg	    XtDispatchEvent(&event);
661d522f475Smrg	}
6623367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
663d522f475Smrg}
664d522f475Smrg
665d522f475Smrgstatic Cursor
666d522f475Smrgmake_hidden_cursor(XtermWidget xw)
667d522f475Smrg{
668d522f475Smrg    TScreen *screen = TScreenOf(xw);
669d522f475Smrg    Cursor c;
670d522f475Smrg    Display *dpy = screen->display;
671d522f475Smrg    XFontStruct *fn;
672d522f475Smrg
673d522f475Smrg    static XColor dummy;
674d522f475Smrg
675d522f475Smrg    /*
676d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
677d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
678b7c89284Ssnj     * server insists on drawing _something_.
679d522f475Smrg     */
680d522f475Smrg    TRACE(("Ask for nil2 font\n"));
681d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
682d522f475Smrg	TRACE(("...Ask for fixed font\n"));
683b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
684d522f475Smrg    }
685d522f475Smrg
686d522f475Smrg    if (fn != 0) {
687d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
688d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
689d522f475Smrg	XFreeFont(dpy, fn);
690d522f475Smrg    } else {
691d522f475Smrg	c = 0;
692d522f475Smrg    }
693d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
694d522f475Smrg    return (c);
695d522f475Smrg}
696d522f475Smrg
697fa3f02f3Smrg/*
698fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
699fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
700fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
701fa3f02f3Smrg * until the window is initialized.
702fa3f02f3Smrg */
703fa3f02f3Smrgvoid
704fa3f02f3Smrginit_colored_cursor(void)
705fa3f02f3Smrg{
706fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
707fa3f02f3Smrg    const char *theme = "index.theme";
708fa3f02f3Smrg    const char *pattern = "xtermXXXXXX";
709fa3f02f3Smrg    const char *tmp_dir;
710fa3f02f3Smrg    char *filename;
711fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
712fa3f02f3Smrg    size_t needed;
713fa3f02f3Smrg    FILE *fp;
714fa3f02f3Smrg
715fa3f02f3Smrg    xterm_cursor_theme = 0;
716fa3f02f3Smrg    if (IsEmpty(env)) {
717fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
718fa3f02f3Smrg	    tmp_dir = P_tmpdir;
719fa3f02f3Smrg	}
720fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
721fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
722fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
723fa3f02f3Smrg
724fa3f02f3Smrg#ifdef HAVE_MKDTEMP
725fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
726fa3f02f3Smrg#else
727fa3f02f3Smrg	    if (mktemp(filename) != 0
728fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
729fa3f02f3Smrg		xterm_cursor_theme = filename;
730fa3f02f3Smrg	    }
731fa3f02f3Smrg#endif
732fa3f02f3Smrg	    /*
733fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
734fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
735fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
736fa3f02f3Smrg	     */
737fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
738fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
739fa3f02f3Smrg		strcat(leaf, "/");
740fa3f02f3Smrg		strcat(leaf, theme);
741fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
742fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
743fa3f02f3Smrg		    fclose(fp);
744fa3f02f3Smrg		    *leaf = '\0';
745fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
746fa3f02f3Smrg		    *leaf = '/';
747fa3f02f3Smrg		}
748fa3f02f3Smrg		atexit(cleanup_colored_cursor);
749fa3f02f3Smrg	    }
750fa3f02f3Smrg	}
751fa3f02f3Smrg    }
752fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
753fa3f02f3Smrg}
754fa3f02f3Smrg
755fa3f02f3Smrg/*
756fa3f02f3Smrg * Once done, discard the file and directory holding it.
757fa3f02f3Smrg */
758fa3f02f3Smrgvoid
759fa3f02f3Smrgcleanup_colored_cursor(void)
760fa3f02f3Smrg{
761fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
762fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
763fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
764fa3f02f3Smrg	struct stat sb;
765fa3f02f3Smrg	if (!IsEmpty(my_path)
766fa3f02f3Smrg	    && stat(my_path, &sb) == 0
767fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
768fa3f02f3Smrg	    unlink(xterm_cursor_theme);
769fa3f02f3Smrg	    rmdir(my_path);
770fa3f02f3Smrg	    free(xterm_cursor_theme);
771fa3f02f3Smrg	    xterm_cursor_theme = 0;
772fa3f02f3Smrg	}
773fa3f02f3Smrg    }
774fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
775fa3f02f3Smrg}
776fa3f02f3Smrg
777d522f475SmrgCursor
778d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
779d522f475Smrg		    unsigned long fg,	/* pixel value */
780d522f475Smrg		    unsigned long bg)	/* pixel value */
781d522f475Smrg{
782d522f475Smrg    TScreen *screen = TScreenOf(term);
783d522f475Smrg    Cursor c;
784d522f475Smrg    Display *dpy = screen->display;
785d522f475Smrg
786d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
787d522f475Smrg    if (c != None) {
788d522f475Smrg	recolor_cursor(screen, c, fg, bg);
789d522f475Smrg    }
790d522f475Smrg    return (c);
791d522f475Smrg}
792d522f475Smrg
793d522f475Smrg/* ARGSUSED */
794d522f475Smrgvoid
795d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
7969a64e1c5Smrg		 XEvent *event,
797fa3f02f3Smrg		 String *params GCC_UNUSED,
798d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
799d522f475Smrg{
800cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
801cd3331d0Smrg    Input(term, &event->xkey, False);
802d522f475Smrg}
803d522f475Smrg
804d522f475Smrg/* ARGSUSED */
805d522f475Smrgvoid
806d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
8079a64e1c5Smrg			 XEvent *event,
808fa3f02f3Smrg			 String *params GCC_UNUSED,
809d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
810d522f475Smrg{
811cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
812cd3331d0Smrg    Input(term, &event->xkey, True);
813d522f475Smrg}
814d522f475Smrg
815d522f475Smrg/* ARGSUSED */
816d522f475Smrgvoid
817d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
8189a64e1c5Smrg		  XEvent *event GCC_UNUSED,
819fa3f02f3Smrg		  String *params,
820d522f475Smrg		  Cardinal *nparams)
821d522f475Smrg{
822d522f475Smrg
823d522f475Smrg    if (*nparams != 1)
824d522f475Smrg	return;
825d522f475Smrg
826d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
8270d92cbfdSchristos	const char *abcdef = "ABCDEF";
8280d92cbfdSchristos	const char *xxxxxx;
829cd3331d0Smrg	Char c;
830cd3331d0Smrg	UString p;
8310d92cbfdSchristos	unsigned value = 0;
8320d92cbfdSchristos
833cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
8340d92cbfdSchristos	     '\0'; p++) {
8350d92cbfdSchristos	    value *= 16;
836d522f475Smrg	    if (c >= '0' && c <= '9')
8370d92cbfdSchristos		value += (unsigned) (c - '0');
838fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
8390d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
840d522f475Smrg	    else
841d522f475Smrg		break;
842d522f475Smrg	}
8430d92cbfdSchristos	if (c == '\0') {
8440d92cbfdSchristos	    Char hexval[2];
8450d92cbfdSchristos	    hexval[0] = (Char) value;
8460d92cbfdSchristos	    hexval[1] = 0;
847b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
8480d92cbfdSchristos	}
849d522f475Smrg    } else {
850cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
851d522f475Smrg    }
852d522f475Smrg}
853d522f475Smrg
854d522f475Smrg#if OPT_EXEC_XTERM
855d522f475Smrg
856d522f475Smrg#ifndef PROCFS_ROOT
857d522f475Smrg#define PROCFS_ROOT "/proc"
858d522f475Smrg#endif
859d522f475Smrg
860d522f475Smrg/* ARGSUSED */
861d522f475Smrgvoid
862d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
8639a64e1c5Smrg		    XEvent *event GCC_UNUSED,
864fa3f02f3Smrg		    String *params,
865d522f475Smrg		    Cardinal *nparams)
866d522f475Smrg{
867cd3331d0Smrg    TScreen *screen = TScreenOf(term);
868d522f475Smrg    char *child_cwd = NULL;
869d522f475Smrg    char *child_exe;
870d522f475Smrg    pid_t pid;
871d522f475Smrg
872d522f475Smrg    /*
873d522f475Smrg     * Try to find the actual program which is running in the child process.
874d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
875d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
876d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
877d522f475Smrg     */
878d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
879d522f475Smrg    if (!child_exe) {
880cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
881cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
882d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
883d522f475Smrg	} else {
8843367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
885d522f475Smrg	}
886d522f475Smrg	if (child_exe == 0)
887d522f475Smrg	    return;
888d522f475Smrg    }
889d522f475Smrg
890d522f475Smrg    /*
891d522f475Smrg     * Determine the current working directory of the child so that we can
892d522f475Smrg     * spawn a new terminal in the same directory.
893d522f475Smrg     *
894d522f475Smrg     * If we cannot get the CWD of the child, just use our own.
895d522f475Smrg     */
896d522f475Smrg    if (screen->pid) {
897d522f475Smrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
898d522f475Smrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid);
899d522f475Smrg	child_cwd = Readlink(child_cwd_link);
900d522f475Smrg    }
901d522f475Smrg
902d522f475Smrg    /* The reaper will take care of cleaning up the child */
903d522f475Smrg    pid = fork();
904d522f475Smrg    if (pid == -1) {
9053367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
906d522f475Smrg    } else if (!pid) {
907d522f475Smrg	/* We are the child */
908d522f475Smrg	if (child_cwd) {
909cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
910d522f475Smrg	}
911d522f475Smrg
912d522f475Smrg	if (setuid(screen->uid) == -1
913d522f475Smrg	    || setgid(screen->gid) == -1) {
9143367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
915d522f475Smrg	} else {
9160d92cbfdSchristos	    unsigned myargc = *nparams + 1;
917d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
9180d92cbfdSchristos	    unsigned n = 0;
919d522f475Smrg
920d522f475Smrg	    myargv[n++] = child_exe;
921d522f475Smrg
922d522f475Smrg	    while (n < myargc) {
9233367019cSmrg		myargv[n++] = (char *) *params++;
924d522f475Smrg	    }
925d522f475Smrg
926d522f475Smrg	    myargv[n] = 0;
927d522f475Smrg	    execv(child_exe, myargv);
928d522f475Smrg
929d522f475Smrg	    /* If we get here, we've failed */
9303367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
931d522f475Smrg	}
932d522f475Smrg	_exit(0);
933d522f475Smrg    }
9343367019cSmrg
9353367019cSmrg    /* We are the parent; clean up */
9363367019cSmrg    if (child_cwd)
9373367019cSmrg	free(child_cwd);
9383367019cSmrg    free(child_exe);
939d522f475Smrg}
940d522f475Smrg#endif /* OPT_EXEC_XTERM */
941d522f475Smrg
942d522f475Smrg/*
943d522f475Smrg * Rather than sending characters to the host, put them directly into our
944d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
945d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
946d522f475Smrg *
947d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
948d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
949d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
950d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
951d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
952d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
953d522f475Smrg */
954d522f475Smrg/* ARGSUSED */
955d522f475Smrgvoid
956d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
9579a64e1c5Smrg		XEvent *event GCC_UNUSED,
958fa3f02f3Smrg		String *params,
959d522f475Smrg		Cardinal *param_count)
960d522f475Smrg{
961d522f475Smrg    if (*param_count == 1) {
962cd3331d0Smrg	const char *value = params[0];
963b7c89284Ssnj	int need = (int) strlen(value);
964cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
965cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
966d522f475Smrg
967d522f475Smrg	if (have - used + need < BUF_SIZE) {
968d522f475Smrg
969cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
970d522f475Smrg
971d522f475Smrg	    TRACE(("Interpret %s\n", value));
972d522f475Smrg	    VTbuffer->update++;
973d522f475Smrg	}
974d522f475Smrg    }
975d522f475Smrg}
976d522f475Smrg
977d522f475Smrg/*ARGSUSED*/
978d522f475Smrgvoid
979d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
980d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
9819a64e1c5Smrg		  XEvent *event GCC_UNUSED,
982fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
983d522f475Smrg{
984d522f475Smrg    /* NOP since we handled it above */
985d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
986cd3331d0Smrg    TRACE_FOCUS(w, event);
987d522f475Smrg}
988d522f475Smrg
989d522f475Smrg/*ARGSUSED*/
990d522f475Smrgvoid
991d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
992d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
9939a64e1c5Smrg		  XEvent *event GCC_UNUSED,
994fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
995d522f475Smrg{
996d522f475Smrg    /* NOP since we handled it above */
997d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
998cd3331d0Smrg    TRACE_FOCUS(w, event);
999d522f475Smrg}
1000d522f475Smrg
1001d522f475Smrg/*ARGSUSED*/
1002d522f475Smrgvoid
1003d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1004d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10059a64e1c5Smrg		  XEvent *ev,
1006fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1007d522f475Smrg{
1008d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1009d522f475Smrg    XtermWidget xw = term;
1010d522f475Smrg    TScreen *screen = TScreenOf(xw);
1011d522f475Smrg
10123367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1013d522f475Smrg	   visibleEventType(event->type),
10143367019cSmrg	   visibleNotifyMode(event->mode),
10153367019cSmrg	   visibleNotifyDetail(event->detail)));
1016cd3331d0Smrg    TRACE_FOCUS(xw, event);
1017d522f475Smrg
1018d522f475Smrg    if (screen->quiet_grab
1019d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1020c219fbebSmrg	/* EMPTY */ ;
10213367019cSmrg    } else if ((event->type == FocusIn || event->type == FocusOut)
10223367019cSmrg	       && event->detail == NotifyPointer) {
10233367019cSmrg	/*
10243367019cSmrg	 * NotifyPointer is sent to the window where the pointer is, and is
10253367019cSmrg	 * in addition to events sent to the old/new focus-windows.
10263367019cSmrg	 */
10273367019cSmrg	/* EMPTY */ ;
1028d522f475Smrg    } else if (event->type == FocusIn) {
1029c219fbebSmrg	setXUrgency(xw, False);
1030d522f475Smrg
1031d522f475Smrg	/*
1032d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1033d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1034d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1035d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1036d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1037d522f475Smrg	 * delivered to the obscured window.
1038d522f475Smrg	 */
1039d522f475Smrg	if (event->detail == NotifyNonlinear
1040d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
10413367019cSmrg	    unselectwindow(xw, INWINDOW);
1042d522f475Smrg	}
10433367019cSmrg	selectwindow(xw,
1044d522f475Smrg		     ((event->detail == NotifyPointer)
1045d522f475Smrg		      ? INWINDOW
1046d522f475Smrg		      : FOCUS));
1047d522f475Smrg	SendFocusButton(xw, event);
1048d522f475Smrg    } else {
1049d522f475Smrg#if OPT_FOCUS_EVENT
1050d522f475Smrg	if (event->type == FocusOut) {
1051d522f475Smrg	    SendFocusButton(xw, event);
1052d522f475Smrg	}
1053d522f475Smrg#endif
1054d522f475Smrg	/*
1055d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1056d522f475Smrg	 * ignore.
1057d522f475Smrg	 */
1058d522f475Smrg	if (event->mode != NotifyGrab) {
10593367019cSmrg	    unselectwindow(xw,
1060d522f475Smrg			   ((event->detail == NotifyPointer)
1061d522f475Smrg			    ? INWINDOW
1062d522f475Smrg			    : FOCUS));
1063d522f475Smrg	}
1064d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1065cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1066d522f475Smrg	    ReverseVideo(xw);
1067d522f475Smrg	    screen->grabbedKbd = False;
1068d522f475Smrg	    update_securekbd();
1069d522f475Smrg	}
1070d522f475Smrg    }
1071d522f475Smrg}
1072d522f475Smrg
1073d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1074d522f475Smrg
1075b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1076b7c89284Ssnjstatic Atom
1077b7c89284SsnjAtomBell(XtermWidget xw, int which)
1078b7c89284Ssnj{
1079b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1080b7c89284Ssnj    static struct {
1081b7c89284Ssnj	int value;
1082b7c89284Ssnj	const char *name;
1083b7c89284Ssnj    } table[] = {
1084b7c89284Ssnj	DATA(Info),
1085b7c89284Ssnj	    DATA(MarginBell),
1086b7c89284Ssnj	    DATA(MinorError),
1087b7c89284Ssnj	    DATA(TerminalBell)
1088b7c89284Ssnj    };
1089b7c89284Ssnj    Cardinal n;
1090b7c89284Ssnj    Atom result = None;
1091b7c89284Ssnj
1092b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1093b7c89284Ssnj	if (table[n].value == which) {
1094cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1095b7c89284Ssnj	    break;
1096b7c89284Ssnj	}
1097b7c89284Ssnj    }
1098b7c89284Ssnj    return result;
1099b7c89284Ssnj}
1100b7c89284Ssnj#endif
1101b7c89284Ssnj
1102d522f475Smrgvoid
1103b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1104d522f475Smrg{
1105b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1106b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1107b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1108cd3331d0Smrg#endif
1109cd3331d0Smrg
1110cd3331d0Smrg    switch (which) {
1111cd3331d0Smrg    case XkbBI_Info:
1112cd3331d0Smrg    case XkbBI_MinorError:
1113cd3331d0Smrg    case XkbBI_MajorError:
1114cd3331d0Smrg    case XkbBI_TerminalBell:
1115cd3331d0Smrg	switch (screen->warningVolume) {
1116cd3331d0Smrg	case bvOff:
1117cd3331d0Smrg	    percent = -100;
1118cd3331d0Smrg	    break;
1119cd3331d0Smrg	case bvLow:
1120cd3331d0Smrg	    break;
1121cd3331d0Smrg	case bvHigh:
1122cd3331d0Smrg	    percent = 100;
1123cd3331d0Smrg	    break;
1124cd3331d0Smrg	}
1125cd3331d0Smrg	break;
1126cd3331d0Smrg    case XkbBI_MarginBell:
1127cd3331d0Smrg	switch (screen->marginVolume) {
1128cd3331d0Smrg	case bvOff:
1129cd3331d0Smrg	    percent = -100;
1130cd3331d0Smrg	    break;
1131cd3331d0Smrg	case bvLow:
1132cd3331d0Smrg	    break;
1133cd3331d0Smrg	case bvHigh:
1134cd3331d0Smrg	    percent = 100;
1135cd3331d0Smrg	    break;
1136cd3331d0Smrg	}
1137cd3331d0Smrg	break;
1138cd3331d0Smrg    default:
1139cd3331d0Smrg	break;
1140cd3331d0Smrg    }
1141cd3331d0Smrg
1142cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1143b7c89284Ssnj    if (tony != None) {
1144c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1145b7c89284Ssnj    } else
1146b7c89284Ssnj#endif
1147b7c89284Ssnj	XBell(screen->display, percent);
1148b7c89284Ssnj}
1149b7c89284Ssnj
1150b7c89284Ssnjvoid
1151cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1152b7c89284Ssnj{
1153b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1154d522f475Smrg    struct timeval curtime;
1155d522f475Smrg    long now_msecs;
1156d522f475Smrg
1157b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1158b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1159d522f475Smrg	return;
1160d522f475Smrg    }
1161d522f475Smrg
1162c219fbebSmrg    setXUrgency(xw, True);
1163d522f475Smrg
1164d522f475Smrg    /* has enough time gone by that we are allowed to ring
1165d522f475Smrg       the bell again? */
1166d522f475Smrg    if (screen->bellSuppressTime) {
1167d522f475Smrg	if (screen->bellInProgress) {
1168d522f475Smrg	    do_xevents();
1169d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1170d522f475Smrg		return;
1171d522f475Smrg	    }
1172d522f475Smrg	}
1173d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1174d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1175d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1176d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1177d522f475Smrg	    return;
1178d522f475Smrg	}
1179d522f475Smrg	lastBellTime = now_msecs;
1180d522f475Smrg    }
1181d522f475Smrg
1182d522f475Smrg    if (screen->visualbell) {
1183d522f475Smrg	VisualBell();
1184d522f475Smrg    } else {
1185b7c89284Ssnj	xtermBell(xw, which, percent);
1186d522f475Smrg    }
1187d522f475Smrg
1188d522f475Smrg    if (screen->poponbell)
1189c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1190d522f475Smrg
1191d522f475Smrg    if (screen->bellSuppressTime) {
1192d522f475Smrg	/* now we change a property and wait for the notify event to come
1193d522f475Smrg	   back.  If the server is suspending operations while the bell
1194d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1195d522f475Smrg	   know when the previous bell has finished */
1196d522f475Smrg	Widget w = CURRENT_EMU();
1197d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1198d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1199d522f475Smrg	screen->bellInProgress = True;
1200d522f475Smrg    }
1201d522f475Smrg}
1202d522f475Smrg
1203d522f475Smrg#define VB_DELAY screen->visualBellDelay
1204d522f475Smrg
1205d522f475Smrgstatic void
1206fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1207d522f475Smrg{
12083367019cSmrg    int y = 0;
12093367019cSmrg    int x = 0;
12103367019cSmrg
12113367019cSmrg    if (screen->flash_line) {
12123367019cSmrg	y = CursorY(screen, screen->cur_row);
12133367019cSmrg	height = (unsigned) FontHeight(screen);
12143367019cSmrg    }
12153367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1216d522f475Smrg    XFlush(screen->display);
1217d522f475Smrg    Sleep(VB_DELAY);
12183367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1219d522f475Smrg}
1220d522f475Smrg
1221d522f475Smrgvoid
1222d522f475SmrgVisualBell(void)
1223d522f475Smrg{
1224d522f475Smrg    TScreen *screen = TScreenOf(term);
1225d522f475Smrg
1226d522f475Smrg    if (VB_DELAY > 0) {
1227d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1228d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1229d522f475Smrg	XGCValues gcval;
1230d522f475Smrg	GC visualGC;
1231d522f475Smrg
1232d522f475Smrg	gcval.function = GXxor;
1233d522f475Smrg	gcval.foreground = xorPixel;
1234d522f475Smrg	visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval);
1235d522f475Smrg#if OPT_TEK4014
1236d522f475Smrg	if (TEK4014_ACTIVE(term)) {
1237cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1238d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1239d522f475Smrg			TFullWidth(tekscr),
1240d522f475Smrg			TFullHeight(tekscr));
1241d522f475Smrg	} else
1242d522f475Smrg#endif
1243d522f475Smrg	{
1244d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1245d522f475Smrg			FullWidth(screen),
1246d522f475Smrg			FullHeight(screen));
1247d522f475Smrg	}
1248d522f475Smrg	XtReleaseGC((Widget) term, visualGC);
1249d522f475Smrg    }
1250d522f475Smrg}
1251d522f475Smrg
1252d522f475Smrg/* ARGSUSED */
1253d522f475Smrgvoid
1254d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1255d522f475Smrg			 XtPointer data GCC_UNUSED,
12569a64e1c5Smrg			 XEvent *ev,
1257fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1258d522f475Smrg{
1259d522f475Smrg    TScreen *screen = TScreenOf(term);
1260d522f475Smrg
1261d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1262d522f475Smrg	screen->bellInProgress = False;
1263d522f475Smrg    }
1264d522f475Smrg}
1265d522f475Smrg
12663367019cSmrgvoid
12673367019cSmrgxtermWarning(const char *fmt,...)
12683367019cSmrg{
12693367019cSmrg    int save_err = errno;
12703367019cSmrg    va_list ap;
12713367019cSmrg
1272fa3f02f3Smrg    TRACE(("xtermWarning fmt='%s'\n", fmt));
12733367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
12743367019cSmrg    va_start(ap, fmt);
12753367019cSmrg    vfprintf(stderr, fmt, ap);
12763367019cSmrg    (void) fflush(stderr);
12773367019cSmrg
12783367019cSmrg    va_end(ap);
12793367019cSmrg    errno = save_err;
12803367019cSmrg}
12813367019cSmrg
12823367019cSmrgvoid
12833367019cSmrgxtermPerror(const char *fmt,...)
12843367019cSmrg{
12853367019cSmrg    int save_err = errno;
12863367019cSmrg    char *msg = strerror(errno);
12873367019cSmrg    va_list ap;
12883367019cSmrg
1289fa3f02f3Smrg    TRACE(("xtermPerror fmt='%s', msg='%s'\n", fmt, NonNull(msg)));
12903367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
12913367019cSmrg    va_start(ap, fmt);
12923367019cSmrg    vfprintf(stderr, fmt, ap);
12933367019cSmrg    fprintf(stderr, ": %s\n", msg);
12943367019cSmrg    (void) fflush(stderr);
12953367019cSmrg
12963367019cSmrg    va_end(ap);
12973367019cSmrg    errno = save_err;
12983367019cSmrg}
12993367019cSmrg
1300d522f475SmrgWindow
1301c219fbebSmrgWMFrameWindow(XtermWidget xw)
1302d522f475Smrg{
1303d522f475Smrg    Window win_root, win_current, *children;
1304d522f475Smrg    Window win_parent = 0;
1305d522f475Smrg    unsigned int nchildren;
1306d522f475Smrg
1307c219fbebSmrg    win_current = XtWindow(xw);
1308d522f475Smrg
1309d522f475Smrg    /* find the parent which is child of root */
1310d522f475Smrg    do {
1311d522f475Smrg	if (win_parent)
1312d522f475Smrg	    win_current = win_parent;
1313c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1314d522f475Smrg		   win_current,
1315d522f475Smrg		   &win_root,
1316d522f475Smrg		   &win_parent,
1317d522f475Smrg		   &children,
1318d522f475Smrg		   &nchildren);
1319d522f475Smrg	XFree(children);
1320d522f475Smrg    } while (win_root != win_parent);
1321d522f475Smrg
1322d522f475Smrg    return win_current;
1323d522f475Smrg}
1324d522f475Smrg
1325d522f475Smrg#if OPT_DABBREV
1326d522f475Smrg/*
1327d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1328d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1329d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1330d522f475Smrg * expansions and ignores one of them if they are identical.
1331d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1332d522f475Smrg */
1333d522f475Smrg
1334d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1335d522f475Smrg
1336d522f475Smrgstatic int
1337fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1338d522f475Smrg{
1339b7c89284Ssnj    int result = -1;
1340b7c89284Ssnj    int firstLine = -(screen->savedlines);
1341d522f475Smrg
1342b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1343b7c89284Ssnj    while (cell->row >= firstLine) {
1344b7c89284Ssnj	if (--(cell->col) >= 0) {
1345b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1346b7c89284Ssnj	    break;
1347b7c89284Ssnj	}
1348b7c89284Ssnj	if (--(cell->row) < firstLine)
1349b7c89284Ssnj	    break;		/* ...there is no previous line */
1350b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1351b7c89284Ssnj	cell->col = MaxCols(screen);
1352b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1353b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1354d522f475Smrg	    break;
1355b7c89284Ssnj	}
1356d522f475Smrg    }
1357b7c89284Ssnj    return result;
1358d522f475Smrg}
1359d522f475Smrg
1360d522f475Smrgstatic char *
13619a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1362d522f475Smrg{
13639a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1364d522f475Smrg    char *abword;
1365d522f475Smrg    int c;
13669a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1367b7c89284Ssnj    char *result = 0;
1368d522f475Smrg
1369b7c89284Ssnj    abword = ab_end;
1370d522f475Smrg    *abword = '\0';		/* end of string marker */
1371d522f475Smrg
1372b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1373b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
13749a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1375b7c89284Ssnj	    *(--abword) = (char) c;
1376d522f475Smrg    }
1377d522f475Smrg
1378b7c89284Ssnj    if (c >= 0) {
1379b7c89284Ssnj	result = abword;
1380b7c89284Ssnj    } else if (abword != ab_end) {
1381b7c89284Ssnj	result = abword;
1382b7c89284Ssnj    }
1383b7c89284Ssnj
1384b7c89284Ssnj    if (result != 0) {
1385b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1386b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1387b7c89284Ssnj	    ;			/* skip preceding spaces */
1388b7c89284Ssnj	}
1389b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1390b7c89284Ssnj    }
1391b7c89284Ssnj    return result;
1392d522f475Smrg}
1393d522f475Smrg
1394d522f475Smrgstatic int
13959a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1396d522f475Smrg{
13979a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1398d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1399d522f475Smrg
1400b7c89284Ssnj    static CELL cell;
1401d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1402d522f475Smrg    static unsigned int expansions;
1403d522f475Smrg
1404d522f475Smrg    char *expansion;
1405d522f475Smrg    Char *copybuffer;
1406d522f475Smrg    size_t hint_len;
1407cd3331d0Smrg    size_t del_cnt;
1408cd3331d0Smrg    size_t buf_cnt;
1409b7c89284Ssnj    int result = 0;
1410b7c89284Ssnj    LineData *ld;
1411d522f475Smrg
1412d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1413d522f475Smrg	expansions = 0;
1414b7c89284Ssnj	cell.col = screen->cur_col;
1415b7c89284Ssnj	cell.row = screen->cur_row;
1416b7c89284Ssnj
1417b7c89284Ssnj	if (dabbrev_hint != 0)
1418b7c89284Ssnj	    free(dabbrev_hint);
1419b7c89284Ssnj
14209a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1421b7c89284Ssnj
1422b7c89284Ssnj	    if (lastexpansion != 0)
1423b7c89284Ssnj		free(lastexpansion);
1424b7c89284Ssnj
1425b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1426b7c89284Ssnj
1427b7c89284Ssnj		/* make own copy */
1428b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1429b7c89284Ssnj		    screen->dabbrev_working = True;
1430b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1431b7c89284Ssnj		}
1432cd3331d0Smrg	    } else {
1433cd3331d0Smrg		return result;
1434b7c89284Ssnj	    }
1435cd3331d0Smrg	} else {
1436cd3331d0Smrg	    return result;
1437d522f475Smrg	}
1438b7c89284Ssnj	if (!screen->dabbrev_working) {
1439b7c89284Ssnj	    if (lastexpansion != 0) {
1440b7c89284Ssnj		free(lastexpansion);
1441b7c89284Ssnj		lastexpansion = 0;
1442b7c89284Ssnj	    }
1443b7c89284Ssnj	    return result;
1444b7c89284Ssnj	}
1445d522f475Smrg    }
1446d522f475Smrg
1447cd3331d0Smrg    if (dabbrev_hint == 0)
1448cd3331d0Smrg	return result;
1449cd3331d0Smrg
1450d522f475Smrg    hint_len = strlen(dabbrev_hint);
1451d522f475Smrg    for (;;) {
14529a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1453d522f475Smrg	    if (expansions >= 2) {
1454d522f475Smrg		expansions = 0;
1455b7c89284Ssnj		cell.col = screen->cur_col;
1456b7c89284Ssnj		cell.row = screen->cur_row;
1457d522f475Smrg		continue;
1458d522f475Smrg	    }
1459d522f475Smrg	    break;
1460d522f475Smrg	}
1461d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1462d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1463d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1464d522f475Smrg	    break;
1465d522f475Smrg    }
1466d522f475Smrg
1467b7c89284Ssnj    if (expansion != 0) {
1468b7c89284Ssnj	del_cnt = strlen(lastexpansion) - hint_len;
1469b7c89284Ssnj	buf_cnt = del_cnt + strlen(expansion) - hint_len;
1470b7c89284Ssnj
1471b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1472b7c89284Ssnj	    /* delete previous expansion */
1473b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1474b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1475b7c89284Ssnj		    expansion + hint_len,
1476b7c89284Ssnj		    strlen(expansion) - hint_len);
1477cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1478b7c89284Ssnj	    /* v_write() just reset our flag */
1479b7c89284Ssnj	    screen->dabbrev_working = True;
1480b7c89284Ssnj	    free(copybuffer);
1481b7c89284Ssnj
1482b7c89284Ssnj	    free(lastexpansion);
1483b7c89284Ssnj
1484b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1485b7c89284Ssnj		result = 1;
1486b7c89284Ssnj		expansions++;
1487b7c89284Ssnj	    }
1488b7c89284Ssnj	}
1489b7c89284Ssnj    }
1490b7c89284Ssnj
1491b7c89284Ssnj    return result;
1492d522f475Smrg}
1493d522f475Smrg
1494d522f475Smrg/*ARGSUSED*/
1495d522f475Smrgvoid
1496b7c89284SsnjHandleDabbrevExpand(Widget w,
14979a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1498fa3f02f3Smrg		    String *params GCC_UNUSED,
1499d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1500d522f475Smrg{
1501b7c89284Ssnj    XtermWidget xw;
1502b7c89284Ssnj
1503cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1504b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
15059a64e1c5Smrg	if (!dabbrev_expand(xw))
1506cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1507d522f475Smrg    }
1508d522f475Smrg}
1509d522f475Smrg#endif /* OPT_DABBREV */
1510d522f475Smrg
1511d522f475Smrg#if OPT_MAXIMIZE
1512d522f475Smrg/*ARGSUSED*/
1513d522f475Smrgvoid
1514b7c89284SsnjHandleDeIconify(Widget w,
15159a64e1c5Smrg		XEvent *event GCC_UNUSED,
1516fa3f02f3Smrg		String *params GCC_UNUSED,
1517d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1518d522f475Smrg{
1519b7c89284Ssnj    XtermWidget xw;
1520b7c89284Ssnj
1521b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1522b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1523c219fbebSmrg	XMapWindow(screen->display, VShellWindow(xw));
1524d522f475Smrg    }
1525d522f475Smrg}
1526d522f475Smrg
1527d522f475Smrg/*ARGSUSED*/
1528d522f475Smrgvoid
1529b7c89284SsnjHandleIconify(Widget w,
15309a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1531fa3f02f3Smrg	      String *params GCC_UNUSED,
1532d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1533d522f475Smrg{
1534b7c89284Ssnj    XtermWidget xw;
1535b7c89284Ssnj
1536b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1537b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1538d522f475Smrg	XIconifyWindow(screen->display,
1539c219fbebSmrg		       VShellWindow(xw),
1540d522f475Smrg		       DefaultScreen(screen->display));
1541d522f475Smrg    }
1542d522f475Smrg}
1543d522f475Smrg
1544d522f475Smrgint
1545c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1546d522f475Smrg{
1547c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1548d522f475Smrg    XSizeHints hints;
1549d522f475Smrg    long supp = 0;
1550d522f475Smrg    Window root_win;
1551d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1552d522f475Smrg    int root_y = -1;
1553d522f475Smrg    unsigned root_border;
1554d522f475Smrg    unsigned root_depth;
15553367019cSmrg    int code;
1556d522f475Smrg
1557d522f475Smrg    if (XGetGeometry(screen->display,
1558c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1559d522f475Smrg		     &root_win,
1560d522f475Smrg		     &root_x,
1561d522f475Smrg		     &root_y,
1562d522f475Smrg		     width,
1563d522f475Smrg		     height,
1564d522f475Smrg		     &root_border,
1565d522f475Smrg		     &root_depth)) {
1566d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1567d522f475Smrg	       root_x,
1568d522f475Smrg	       root_y,
1569d522f475Smrg	       *width,
1570d522f475Smrg	       *height,
1571d522f475Smrg	       root_border));
1572d522f475Smrg
1573d522f475Smrg	*width -= (root_border * 2);
1574d522f475Smrg	*height -= (root_border * 2);
1575d522f475Smrg
1576d522f475Smrg	hints.flags = PMaxSize;
1577d522f475Smrg	if (XGetWMNormalHints(screen->display,
1578c219fbebSmrg			      VShellWindow(xw),
1579d522f475Smrg			      &hints,
1580d522f475Smrg			      &supp)
1581d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1582d522f475Smrg
1583d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1584d522f475Smrg		   hints.max_width,
1585d522f475Smrg		   hints.max_height));
1586d522f475Smrg
1587d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1588b7c89284Ssnj		*width = (unsigned) hints.max_width;
1589d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1590b7c89284Ssnj		*height = (unsigned) hints.max_height;
1591d522f475Smrg	}
15923367019cSmrg	code = 1;
15933367019cSmrg    } else {
15943367019cSmrg	*width = 0;
15953367019cSmrg	*height = 0;
15963367019cSmrg	code = 0;
1597d522f475Smrg    }
15983367019cSmrg    return code;
1599d522f475Smrg}
1600d522f475Smrg
1601d522f475Smrgvoid
1602c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1603d522f475Smrg{
1604c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1605d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1606d522f475Smrg    unsigned root_width, root_height;
16073367019cSmrg    Boolean success = False;
1608d522f475Smrg
16093367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
16103367019cSmrg	   maximize,
16113367019cSmrg	   (maximize
16123367019cSmrg	    ? "maximize"
16133367019cSmrg	    : "restore")));
1614d522f475Smrg
16153367019cSmrg    /*
16163367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
16173367019cSmrg     * as well as the estimated root-window size.
16183367019cSmrg     */
16193367019cSmrg    if (maximize
16203367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
16213367019cSmrg	&& xtermGetWinAttrs(screen->display,
16223367019cSmrg			    WMFrameWindow(xw),
16233367019cSmrg			    &wm_attrs)
16243367019cSmrg	&& xtermGetWinAttrs(screen->display,
16253367019cSmrg			    VShellWindow(xw),
16263367019cSmrg			    &vshell_attrs)) {
16273367019cSmrg
16283367019cSmrg	if (screen->restore_data != True
16293367019cSmrg	    || screen->restore_width != root_width
16303367019cSmrg	    || screen->restore_height != root_height) {
16313367019cSmrg	    screen->restore_data = True;
16323367019cSmrg	    screen->restore_x = wm_attrs.x + wm_attrs.border_width;
16333367019cSmrg	    screen->restore_y = wm_attrs.y + wm_attrs.border_width;
16343367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
16353367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
16363367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1637d522f475Smrg		   screen->restore_x,
1638d522f475Smrg		   screen->restore_y,
1639d522f475Smrg		   screen->restore_width,
1640d522f475Smrg		   screen->restore_height));
16413367019cSmrg	}
1642d522f475Smrg
16433367019cSmrg	/* subtract wm decoration dimensions */
16443367019cSmrg	root_width -= (unsigned) ((wm_attrs.width - vshell_attrs.width)
16453367019cSmrg				  + (wm_attrs.border_width * 2));
16463367019cSmrg	root_height -= (unsigned) ((wm_attrs.height - vshell_attrs.height)
16473367019cSmrg				   + (wm_attrs.border_width * 2));
16483367019cSmrg	success = True;
16493367019cSmrg    } else if (screen->restore_data) {
16503367019cSmrg	success = True;
16513367019cSmrg	maximize = 0;
16523367019cSmrg    }
16533367019cSmrg
16543367019cSmrg    if (success) {
16553367019cSmrg	switch (maximize) {
16563367019cSmrg	case 3:
16573367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
16583367019cSmrg	    break;
16593367019cSmrg	case 2:
16603367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
16613367019cSmrg	    break;
16623367019cSmrg	case 1:
16633367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
16643367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
16653367019cSmrg			      0 + wm_attrs.border_width,	/* x */
16663367019cSmrg			      0 + wm_attrs.border_width,	/* y */
16673367019cSmrg			      root_width,
16683367019cSmrg			      root_height);
16693367019cSmrg	    break;
16703367019cSmrg
16713367019cSmrg	default:
16723367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
16733367019cSmrg	    if (screen->restore_data) {
16743367019cSmrg		screen->restore_data = False;
16753367019cSmrg
16763367019cSmrg		TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
16773367019cSmrg		       screen->restore_x,
16783367019cSmrg		       screen->restore_y,
16793367019cSmrg		       screen->restore_width,
16803367019cSmrg		       screen->restore_height));
16813367019cSmrg
16823367019cSmrg		XMoveResizeWindow(screen->display,
16833367019cSmrg				  VShellWindow(xw),
16843367019cSmrg				  screen->restore_x,
16853367019cSmrg				  screen->restore_y,
16863367019cSmrg				  screen->restore_width,
16873367019cSmrg				  screen->restore_height);
16883367019cSmrg	    }
16893367019cSmrg	    break;
1690d522f475Smrg	}
1691d522f475Smrg    }
1692d522f475Smrg}
1693d522f475Smrg
1694d522f475Smrg/*ARGSUSED*/
1695d522f475Smrgvoid
1696b7c89284SsnjHandleMaximize(Widget w,
16979a64e1c5Smrg	       XEvent *event GCC_UNUSED,
1698fa3f02f3Smrg	       String *params GCC_UNUSED,
1699d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1700d522f475Smrg{
1701b7c89284Ssnj    XtermWidget xw;
1702b7c89284Ssnj
1703b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1704b7c89284Ssnj	RequestMaximize(xw, 1);
1705d522f475Smrg    }
1706d522f475Smrg}
1707d522f475Smrg
1708d522f475Smrg/*ARGSUSED*/
1709d522f475Smrgvoid
1710b7c89284SsnjHandleRestoreSize(Widget w,
17119a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1712fa3f02f3Smrg		  String *params GCC_UNUSED,
1713d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1714d522f475Smrg{
1715b7c89284Ssnj    XtermWidget xw;
1716b7c89284Ssnj
1717b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1718b7c89284Ssnj	RequestMaximize(xw, 0);
1719d522f475Smrg    }
1720d522f475Smrg}
1721d522f475Smrg#endif /* OPT_MAXIMIZE */
1722d522f475Smrg
1723d522f475Smrgvoid
1724d522f475SmrgRedraw(void)
1725d522f475Smrg{
1726d522f475Smrg    TScreen *screen = TScreenOf(term);
1727d522f475Smrg    XExposeEvent event;
1728d522f475Smrg
1729d522f475Smrg    TRACE(("Redraw\n"));
1730d522f475Smrg
1731d522f475Smrg    event.type = Expose;
1732d522f475Smrg    event.display = screen->display;
1733d522f475Smrg    event.x = 0;
1734d522f475Smrg    event.y = 0;
1735d522f475Smrg    event.count = 0;
1736d522f475Smrg
1737d522f475Smrg    if (VWindow(screen)) {
1738d522f475Smrg	event.window = VWindow(screen);
1739d522f475Smrg	event.width = term->core.width;
1740d522f475Smrg	event.height = term->core.height;
1741d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
17429a64e1c5Smrg						       (XEvent *) &event,
1743d522f475Smrg						       NULL);
1744d522f475Smrg	if (ScrollbarWidth(screen)) {
1745d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
17469a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
1747d522f475Smrg	}
1748d522f475Smrg    }
1749d522f475Smrg#if OPT_TEK4014
1750d522f475Smrg    if (TEK4014_SHOWN(term)) {
1751cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1752d522f475Smrg	event.window = TWindow(tekscr);
1753d522f475Smrg	event.width = tekWidget->core.width;
1754d522f475Smrg	event.height = tekWidget->core.height;
17559a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
1756d522f475Smrg    }
1757d522f475Smrg#endif
1758d522f475Smrg}
1759d522f475Smrg
1760d522f475Smrg#ifdef VMS
1761d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1762d522f475Smrg#else
1763d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1764d522f475Smrg#endif
1765d522f475Smrg
1766d522f475Smrgvoid
1767d522f475Smrgtimestamp_filename(char *dst, const char *src)
1768d522f475Smrg{
1769d522f475Smrg    time_t tstamp;
1770d522f475Smrg    struct tm *tstruct;
1771d522f475Smrg
1772d522f475Smrg    tstamp = time((time_t *) 0);
1773d522f475Smrg    tstruct = localtime(&tstamp);
1774d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1775d522f475Smrg	    src,
17763367019cSmrg	    (int) tstruct->tm_year + 1900,
1777d522f475Smrg	    tstruct->tm_mon + 1,
1778d522f475Smrg	    tstruct->tm_mday,
1779d522f475Smrg	    tstruct->tm_hour,
1780d522f475Smrg	    tstruct->tm_min,
1781d522f475Smrg	    tstruct->tm_sec);
1782d522f475Smrg}
1783d522f475Smrg
1784d522f475Smrgint
1785d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1786d522f475Smrg{
1787d522f475Smrg    int fd;
1788d522f475Smrg    struct stat sb;
1789d522f475Smrg
1790d522f475Smrg#ifdef VMS
1791d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1792d522f475Smrg	int the_error = errno;
17933367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
17943367019cSmrg		     path,
17953367019cSmrg		     the_error,
17963367019cSmrg		     SysErrorMsg(the_error));
1797d522f475Smrg	return -1;
1798d522f475Smrg    }
1799d522f475Smrg    chown(path, uid, gid);
1800d522f475Smrg#else
1801d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1802d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1803d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1804d522f475Smrg	int the_error = errno;
18053367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
18063367019cSmrg		     path,
18073367019cSmrg		     the_error,
18083367019cSmrg		     SysErrorMsg(the_error));
1809d522f475Smrg	return -1;
1810d522f475Smrg    }
1811d522f475Smrg#endif
1812d522f475Smrg
1813d522f475Smrg    /*
1814d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1815d522f475Smrg     * we do any damage, and that it is not world-writable.
1816d522f475Smrg     */
1817d522f475Smrg    if (fstat(fd, &sb) < 0
1818d522f475Smrg	|| sb.st_uid != uid
1819d522f475Smrg	|| (sb.st_mode & 022) != 0) {
18203367019cSmrg	xtermWarning("you do not own %s\n", path);
1821d522f475Smrg	close(fd);
1822d522f475Smrg	return -1;
1823d522f475Smrg    }
1824d522f475Smrg    return fd;
1825d522f475Smrg}
1826d522f475Smrg
1827d522f475Smrg#ifndef VMS
1828d522f475Smrg/*
1829d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1830d522f475Smrg * We could emulate this with careful use of access() and following
1831d522f475Smrg * symbolic links, but that is messy and has race conditions.
1832d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1833d522f475Smrg * being available.
1834d522f475Smrg *
1835d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1836d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1837d522f475Smrg * for the debug logs.
1838d522f475Smrg *
1839d522f475Smrg * Returns
1840d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1841d522f475Smrg *	-1 on error, e.g., cannot fork
1842d522f475Smrg *	 0 otherwise.
1843d522f475Smrg */
1844d522f475Smrgint
1845712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
1846d522f475Smrg{
1847d522f475Smrg    int fd;
1848d522f475Smrg    pid_t pid;
1849d522f475Smrg    int retval = 0;
1850d522f475Smrg    int childstat = 0;
1851d522f475Smrg#ifndef HAVE_WAITPID
1852d522f475Smrg    int waited;
18533367019cSmrg    void (*chldfunc) (int);
1854d522f475Smrg
1855d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1856d522f475Smrg#endif /* HAVE_WAITPID */
1857d522f475Smrg
1858d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1859d522f475Smrg	   (int) uid, (int) geteuid(),
1860d522f475Smrg	   (int) gid, (int) getegid(),
1861d522f475Smrg	   append,
1862d522f475Smrg	   pathname,
1863d522f475Smrg	   mode));
1864d522f475Smrg
1865d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1866d522f475Smrg	fd = open(pathname,
1867d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1868d522f475Smrg		  mode);
1869d522f475Smrg	if (fd >= 0)
1870d522f475Smrg	    close(fd);
1871d522f475Smrg	return (fd >= 0);
1872d522f475Smrg    }
1873d522f475Smrg
1874d522f475Smrg    pid = fork();
1875d522f475Smrg    switch (pid) {
1876d522f475Smrg    case 0:			/* child */
1877d522f475Smrg	if (setgid(gid) == -1
1878d522f475Smrg	    || setuid(uid) == -1) {
1879d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1880d522f475Smrg	    retval = 1;
1881d522f475Smrg	} else {
1882d522f475Smrg	    fd = open(pathname,
1883d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1884d522f475Smrg		      mode);
1885d522f475Smrg	    if (fd >= 0) {
1886d522f475Smrg		close(fd);
1887d522f475Smrg		retval = 0;
1888d522f475Smrg	    } else {
1889d522f475Smrg		retval = 1;
1890d522f475Smrg	    }
1891d522f475Smrg	}
1892d522f475Smrg	_exit(retval);
1893d522f475Smrg	/* NOTREACHED */
1894d522f475Smrg    case -1:			/* error */
1895d522f475Smrg	return retval;
1896d522f475Smrg    default:			/* parent */
1897d522f475Smrg#ifdef HAVE_WAITPID
1898d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1899d522f475Smrg#ifdef EINTR
1900d522f475Smrg	    if (errno == EINTR)
1901d522f475Smrg		continue;
1902d522f475Smrg#endif /* EINTR */
1903d522f475Smrg#ifdef ERESTARTSYS
1904d522f475Smrg	    if (errno == ERESTARTSYS)
1905d522f475Smrg		continue;
1906d522f475Smrg#endif /* ERESTARTSYS */
1907d522f475Smrg	    break;
1908d522f475Smrg	}
1909d522f475Smrg#else /* HAVE_WAITPID */
1910d522f475Smrg	waited = wait(&childstat);
1911d522f475Smrg	signal(SIGCHLD, chldfunc);
1912d522f475Smrg	/*
1913d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1914d522f475Smrg	   we might have missed the termination of our screen child.
1915d522f475Smrg	   If we can check for this possibility without hanging, do so.
1916d522f475Smrg	 */
1917d522f475Smrg	do
1918cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
19193367019cSmrg		NormalExit();
1920d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1921d522f475Smrg#endif /* HAVE_WAITPID */
1922d522f475Smrg#ifndef WIFEXITED
1923d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1924d522f475Smrg#endif
1925d522f475Smrg	if (WIFEXITED(childstat))
1926d522f475Smrg	    retval = 1;
1927d522f475Smrg	return retval;
1928d522f475Smrg    }
1929d522f475Smrg}
1930d522f475Smrg#endif /* !VMS */
1931d522f475Smrg
1932d522f475Smrgint
1933fa3f02f3SmrgxtermResetIds(TScreen *screen)
1934d522f475Smrg{
1935d522f475Smrg    int result = 0;
1936d522f475Smrg    if (setgid(screen->gid) == -1) {
19373367019cSmrg	xtermWarning("unable to reset group-id\n");
1938d522f475Smrg	result = -1;
1939d522f475Smrg    }
1940d522f475Smrg    if (setuid(screen->uid) == -1) {
19413367019cSmrg	xtermWarning("unable to reset user-id\n");
1942d522f475Smrg	result = -1;
1943d522f475Smrg    }
1944d522f475Smrg    return result;
1945d522f475Smrg}
1946d522f475Smrg
1947d522f475Smrg#ifdef ALLOWLOGGING
1948d522f475Smrg
1949d522f475Smrg/*
1950d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1951d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1952d522f475Smrg */
1953d522f475Smrg
1954d522f475Smrg#ifdef ALLOWLOGFILEEXEC
19553367019cSmrgstatic void
1956d522f475Smrglogpipe(int sig GCC_UNUSED)
1957d522f475Smrg{
1958cd3331d0Smrg    XtermWidget xw = term;
1959cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1960d522f475Smrg
19613367019cSmrg    DEBUG_MSG("handle:logpipe\n");
1962d522f475Smrg#ifdef SYSV
1963d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
1964d522f475Smrg#endif /* SYSV */
1965d522f475Smrg    if (screen->logging)
1966cd3331d0Smrg	CloseLog(xw);
1967d522f475Smrg}
1968d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1969d522f475Smrg
1970d522f475Smrgvoid
1971cd3331d0SmrgStartLog(XtermWidget xw)
1972d522f475Smrg{
1973d522f475Smrg    static char *log_default;
1974cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1975d522f475Smrg
1976d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
1977d522f475Smrg	return;
1978d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
1979d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
1980d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
1981d522f475Smrg			 0640);
1982d522f475Smrg    if (screen->logfd < 0)
1983d522f475Smrg	return;			/* open failed */
1984d522f475Smrg#else /*VMS */
1985d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
1986d522f475Smrg	if (screen->logfile)
1987d522f475Smrg	    free(screen->logfile);
1988d522f475Smrg	if (log_default == NULL) {
1989d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
1990d522f475Smrg	    char log_def_name[512];	/* see sprintf below */
1991d522f475Smrg	    char hostname[255 + 1];	/* Internet standard limit (RFC 1035):
1992d522f475Smrg					   ``To simplify implementations, the
1993d522f475Smrg					   total length of a domain name (i.e.,
1994d522f475Smrg					   label octets and label length
1995d522f475Smrg					   octets) is restricted to 255 octets
1996d522f475Smrg					   or less.'' */
1997d522f475Smrg	    char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1];
1998d522f475Smrg	    time_t now;
1999d522f475Smrg	    struct tm *ltm;
2000d522f475Smrg
2001d522f475Smrg	    now = time((time_t *) 0);
2002d522f475Smrg	    ltm = (struct tm *) localtime(&now);
2003d522f475Smrg	    if ((gethostname(hostname, sizeof(hostname)) == 0) &&
2004d522f475Smrg		(strftime(yyyy_mm_dd_hh_mm_ss,
2005d522f475Smrg			  sizeof(yyyy_mm_dd_hh_mm_ss),
2006d522f475Smrg			  "%Y.%m.%d.%H.%M.%S", ltm) > 0)) {
2007d522f475Smrg		(void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d",
2008d522f475Smrg			       hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid());
2009d522f475Smrg	    }
2010d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
2011d522f475Smrg		return;
2012d522f475Smrg#else
2013d522f475Smrg	    const char *log_def_name = "XtermLog.XXXXXX";
2014d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
2015d522f475Smrg		return;
2016d522f475Smrg
2017d522f475Smrg	    mktemp(log_default);
2018d522f475Smrg#endif
2019d522f475Smrg	}
2020d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
2021d522f475Smrg	    return;
2022d522f475Smrg    }
2023d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
2024d522f475Smrg#ifdef ALLOWLOGFILEEXEC
2025d522f475Smrg	/*
2026d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
2027d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2028d522f475Smrg	 * done through escape sequences....  You have been warned.
2029d522f475Smrg	 */
2030d522f475Smrg	int pid;
2031d522f475Smrg	int p[2];
2032d522f475Smrg	static char *shell;
20333367019cSmrg	struct passwd pw;
20343367019cSmrg
20353367019cSmrg	if ((shell = x_getenv("SHELL")) == NULL) {
20363367019cSmrg
20373367019cSmrg	    if (x_getpwuid(screen->uid, &pw)) {
20383367019cSmrg		char *name = x_getlogin(screen->uid, &pw);
20393367019cSmrg		if (*(pw.pw_shell)) {
20403367019cSmrg		    shell = pw.pw_shell;
20413367019cSmrg		}
20423367019cSmrg		free(name);
20433367019cSmrg	    }
20443367019cSmrg	}
20453367019cSmrg
20463367019cSmrg	if (shell == 0) {
20473367019cSmrg	    static char dummy[] = "/bin/sh";
20483367019cSmrg	    shell = dummy;
20493367019cSmrg	}
20503367019cSmrg
20513367019cSmrg	if (access(shell, X_OK) != 0) {
20523367019cSmrg	    xtermPerror("Can't execute `%s'\n", shell);
20533367019cSmrg	    return;
20543367019cSmrg	}
2055d522f475Smrg
20563367019cSmrg	if (pipe(p) < 0) {
20573367019cSmrg	    xtermPerror("Can't make a pipe connection\n");
2058d522f475Smrg	    return;
20593367019cSmrg	} else if ((pid = fork()) < 0) {
20603367019cSmrg	    xtermPerror("Can't fork...\n");
20613367019cSmrg	    return;
20623367019cSmrg	}
2063d522f475Smrg	if (pid == 0) {		/* child */
2064d522f475Smrg	    /*
2065d522f475Smrg	     * Close our output (we won't be talking back to the
2066d522f475Smrg	     * parent), and redirect our child's output to the
2067d522f475Smrg	     * original stderr.
2068d522f475Smrg	     */
2069d522f475Smrg	    close(p[1]);
2070d522f475Smrg	    dup2(p[0], 0);
2071d522f475Smrg	    close(p[0]);
2072d522f475Smrg	    dup2(fileno(stderr), 1);
2073d522f475Smrg	    dup2(fileno(stderr), 2);
2074d522f475Smrg
2075d522f475Smrg	    close(fileno(stderr));
2076d522f475Smrg	    close(ConnectionNumber(screen->display));
2077d522f475Smrg	    close(screen->respond);
2078d522f475Smrg
2079d522f475Smrg	    signal(SIGHUP, SIG_DFL);
2080d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
2081d522f475Smrg
2082d522f475Smrg	    /* (this is redundant) */
2083d522f475Smrg	    if (xtermResetIds(screen) < 0)
2084d522f475Smrg		exit(ERROR_SETUID);
2085d522f475Smrg
20863367019cSmrg	    if (access(shell, X_OK) == 0) {
20873367019cSmrg		execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
20883367019cSmrg		xtermWarning("Can't exec `%s'\n", &screen->logfile[1]);
20893367019cSmrg	    } else {
20903367019cSmrg		xtermWarning("Can't execute `%s'\n", shell);
20913367019cSmrg	    }
2092d522f475Smrg	    exit(ERROR_LOGEXEC);
2093d522f475Smrg	}
2094d522f475Smrg	close(p[0]);
2095d522f475Smrg	screen->logfd = p[1];
2096d522f475Smrg	signal(SIGPIPE, logpipe);
2097d522f475Smrg#else
2098cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2099cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2100d522f475Smrg	return;
2101d522f475Smrg#endif
2102d522f475Smrg    } else {
2103d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2104d522f475Smrg					   screen->gid,
2105d522f475Smrg					   screen->logfile,
2106d522f475Smrg					   (log_default != 0))) < 0)
2107d522f475Smrg	    return;
2108d522f475Smrg    }
2109d522f475Smrg#endif /*VMS */
2110d522f475Smrg    screen->logstart = VTbuffer->next;
2111d522f475Smrg    screen->logging = True;
2112d522f475Smrg    update_logging();
2113d522f475Smrg}
2114d522f475Smrg
2115d522f475Smrgvoid
2116cd3331d0SmrgCloseLog(XtermWidget xw)
2117d522f475Smrg{
2118cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2119cd3331d0Smrg
2120d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2121d522f475Smrg	return;
2122cd3331d0Smrg    FlushLog(xw);
2123d522f475Smrg    close(screen->logfd);
2124d522f475Smrg    screen->logging = False;
2125d522f475Smrg    update_logging();
2126d522f475Smrg}
2127d522f475Smrg
2128d522f475Smrgvoid
2129cd3331d0SmrgFlushLog(XtermWidget xw)
2130d522f475Smrg{
2131cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2132cd3331d0Smrg
2133d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2134d522f475Smrg	Char *cp;
2135d522f475Smrg	int i;
2136d522f475Smrg
2137d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2138d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2139d522f475Smrg	if (!tt_new_output)
2140d522f475Smrg	    return;
2141d522f475Smrg	tt_new_output = False;
2142d522f475Smrg#endif /* VMS */
2143d522f475Smrg	cp = VTbuffer->next;
2144d522f475Smrg	if (screen->logstart != 0
2145cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2146cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2147d522f475Smrg	}
2148d522f475Smrg	screen->logstart = VTbuffer->next;
2149d522f475Smrg    }
2150d522f475Smrg}
2151d522f475Smrg
2152d522f475Smrg#endif /* ALLOWLOGGING */
2153d522f475Smrg
2154d522f475Smrg/***====================================================================***/
2155d522f475Smrg
2156fa3f02f3Smrgint
2157fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2158fa3f02f3Smrg{
2159fa3f02f3Smrg#define MYFMT "getVisualInfo \
2160fa3f02f3Smrgdepth %d, \
2161fa3f02f3Smrgtype %d (%s), \
2162fa3f02f3Smrgsize %d \
2163fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2164fa3f02f3Smrg#define MYARG \
2165fa3f02f3Smrg       vi->depth,\
2166fa3f02f3Smrg       vi->class,\
2167fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2168fa3f02f3Smrg       vi->colormap_size,\
2169fa3f02f3Smrg       vi->red_mask,\
2170fa3f02f3Smrg       vi->green_mask,\
2171fa3f02f3Smrg       vi->blue_mask
2172d522f475Smrg
2173fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2174fa3f02f3Smrg    Display *dpy = screen->display;
2175fa3f02f3Smrg    XVisualInfo myTemplate;
2176fa3f02f3Smrg
2177fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2178fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2179fa3f02f3Smrg								XDefaultScreen(dpy)));
2180fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2181fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2182fa3f02f3Smrg
2183fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2184fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2185fa3f02f3Smrg	    if (resource.reportColors) {
2186fa3f02f3Smrg		printf(MYFMT, MYARG);
2187fa3f02f3Smrg	    }
2188fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2189fa3f02f3Smrg	}
2190fa3f02f3Smrg    }
2191fa3f02f3Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0);
2192fa3f02f3Smrg#undef MYFMT
2193fa3f02f3Smrg#undef MYARG
2194fa3f02f3Smrg}
21953367019cSmrg
21969a64e1c5Smrg#if OPT_ISO_COLORS
21979a64e1c5Smrgstatic void
21989a64e1c5SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
21999a64e1c5Smrg{
22009a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
22019a64e1c5Smrg	XColor color;
22029a64e1c5Smrg	Colormap cmap = xw->core.colormap;
22039a64e1c5Smrg	char buffer[80];
22049a64e1c5Smrg
22059a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
22069a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
22079a64e1c5Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
22089a64e1c5Smrg	sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
22099a64e1c5Smrg		colornum,
22109a64e1c5Smrg		color.red,
22119a64e1c5Smrg		color.green,
22129a64e1c5Smrg		color.blue);
22139a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
22149a64e1c5Smrg	unparseputs(xw, buffer);
22159a64e1c5Smrg	unparseputc1(xw, final);
22169a64e1c5Smrg	unparse_end(xw);
22179a64e1c5Smrg    }
22189a64e1c5Smrg}
22199a64e1c5Smrg
2220fa3f02f3Smrgstatic void
2221fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2222fa3f02f3Smrg{
2223fa3f02f3Smrg    if (getVisualInfo(xw)) {
2224fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2225fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2226fa3f02f3Smrg    } else {
2227fa3f02f3Smrg	*typep = 0;
2228fa3f02f3Smrg	*sizep = 0;
2229fa3f02f3Smrg    }
22303367019cSmrg}
22313367019cSmrg
22323367019cSmrg#define MAX_COLORTABLE 4096
22333367019cSmrg
22343367019cSmrg/*
22353367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
22363367019cSmrg */
22373367019cSmrgstatic Boolean
22383367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
22393367019cSmrg{
22403367019cSmrg    Colormap cmap = xw->core.colormap;
22413367019cSmrg    TScreen *screen = TScreenOf(xw);
22423367019cSmrg    unsigned i;
2243fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
22443367019cSmrg
2245fa3f02f3Smrg    if (!result
22463367019cSmrg	&& length != 0
22473367019cSmrg	&& length < MAX_COLORTABLE) {
22483367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
22493367019cSmrg	if (screen->cmap_data != 0) {
22503367019cSmrg	    screen->cmap_size = length;
22513367019cSmrg
22523367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
22533367019cSmrg		screen->cmap_data[i].pixel = (unsigned long) i;
22543367019cSmrg	    }
22553367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
22563367019cSmrg					     cmap,
22573367019cSmrg					     screen->cmap_data,
22583367019cSmrg					     (int) screen->cmap_size) != 0);
22593367019cSmrg	}
22603367019cSmrg    }
2261d522f475Smrg    return result;
2262d522f475Smrg}
2263d522f475Smrg
2264d522f475Smrg/*
2265d522f475Smrg * Find closest color for "def" in "cmap".
2266d522f475Smrg * Set "def" to the resulting color.
2267d522f475Smrg *
2268d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2269d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2270d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2271d522f475Smrg *
2272d522f475Smrg * Return False if not able to find or allocate a color.
2273d522f475Smrg */
2274d522f475Smrgstatic Boolean
22759a64e1c5SmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def)
2276d522f475Smrg{
22773367019cSmrg    TScreen *screen = TScreenOf(xw);
2278d522f475Smrg    Boolean result = False;
2279d522f475Smrg    char *tried;
2280d522f475Smrg    double diff, thisRGB, bestRGB;
2281d522f475Smrg    unsigned attempts;
2282d522f475Smrg    unsigned bestInx;
22833367019cSmrg    unsigned cmap_type;
2284d522f475Smrg    unsigned cmap_size;
2285d522f475Smrg    unsigned i;
2286d522f475Smrg
2287fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2288d522f475Smrg
22893367019cSmrg    if ((cmap_type & 1) != 0) {
22903367019cSmrg
22913367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2292d522f475Smrg
2293b7c89284Ssnj	    tried = TypeCallocN(char, (size_t) cmap_size);
2294d522f475Smrg	    if (tried != 0) {
2295d522f475Smrg
2296d522f475Smrg		/*
2297d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2298d522f475Smrg		 * approximation to the requested color.
2299d522f475Smrg		 */
2300d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2301d522f475Smrg		    Boolean first = True;
2302d522f475Smrg
2303d522f475Smrg		    bestRGB = 0.0;
2304d522f475Smrg		    bestInx = 0;
2305d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2306d522f475Smrg			if (!tried[bestInx]) {
2307d522f475Smrg			    /*
2308d522f475Smrg			     * Look for the best match based on luminance.
2309d522f475Smrg			     * Measure this by the least-squares difference of
2310d522f475Smrg			     * the weighted R/G/B components from the color map
2311d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2312d522f475Smrg			     * component of the YIQ color space model for
2313d522f475Smrg			     * weights that correspond to the luminance.
2314d522f475Smrg			     */
2315d522f475Smrg#define AddColorWeight(weight, color) \
23163367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2317d522f475Smrg			    thisRGB = diff * diff
2318d522f475Smrg
2319d522f475Smrg			    AddColorWeight(0.30, red);
2320d522f475Smrg			    AddColorWeight(0.61, green);
2321d522f475Smrg			    AddColorWeight(0.11, blue);
2322d522f475Smrg
2323d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2324d522f475Smrg				first = False;
2325d522f475Smrg				bestInx = i;
2326d522f475Smrg				bestRGB = thisRGB;
2327d522f475Smrg			    }
2328d522f475Smrg			}
2329d522f475Smrg		    }
23303367019cSmrg		    if (XAllocColor(screen->display, cmap,
23313367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
23323367019cSmrg			*def = screen->cmap_data[bestInx];
23333367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
23343367019cSmrg			       def->green, def->blue));
2335d522f475Smrg			result = True;
2336d522f475Smrg			break;
2337d522f475Smrg		    }
2338d522f475Smrg		    /*
2339d522f475Smrg		     * It failed - either the color map entry was readonly, or
2340d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2341d522f475Smrg		     * so we will ignore it
2342d522f475Smrg		     */
2343d522f475Smrg		    tried[bestInx] = True;
2344d522f475Smrg		}
2345d522f475Smrg		free(tried);
2346d522f475Smrg	    }
2347d522f475Smrg	}
2348d522f475Smrg    }
2349d522f475Smrg    return result;
2350d522f475Smrg}
2351d522f475Smrg
23523367019cSmrg#ifndef ULONG_MAX
23533367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
23543367019cSmrg#endif
23553367019cSmrg
23563367019cSmrg#define CheckColor(result, value) \
23573367019cSmrg	    result = 0; \
23583367019cSmrg	    if (value.red) \
23593367019cSmrg		result |= 1; \
23603367019cSmrg	    if (value.green) \
23613367019cSmrg		result |= 2; \
23623367019cSmrg	    if (value.blue) \
23633367019cSmrg		result |= 4
23643367019cSmrg
23653367019cSmrg#define SelectColor(state, value, result) \
23663367019cSmrg	switch (state) { \
23673367019cSmrg	default: \
23683367019cSmrg	case 1: \
23693367019cSmrg	    result = value.red; \
23703367019cSmrg	    break; \
23713367019cSmrg	case 2: \
23723367019cSmrg	    result = value.green; \
23733367019cSmrg	    break; \
23743367019cSmrg	case 4: \
23753367019cSmrg	    result = value.blue; \
23763367019cSmrg	    break; \
23773367019cSmrg	}
23783367019cSmrg
23793367019cSmrg/*
23803367019cSmrg * Check if the color map consists of values in exactly one of the red, green
23813367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
23823367019cSmrg * match.
23833367019cSmrg */
23843367019cSmrgstatic int
23859a64e1c5SmrgsimpleColors(XColor *colortable, unsigned length)
23863367019cSmrg{
23873367019cSmrg    unsigned n;
2388fa3f02f3Smrg    int state = 0;
23893367019cSmrg    int check;
23903367019cSmrg
23913367019cSmrg    for (n = 0; n < length; ++n) {
23923367019cSmrg	if (state > 0) {
23933367019cSmrg	    CheckColor(check, colortable[n]);
23943367019cSmrg	    if (check > 0 && check != state) {
23953367019cSmrg		state = 0;
23963367019cSmrg		break;
23973367019cSmrg	    }
2398fa3f02f3Smrg	} else {
2399fa3f02f3Smrg	    CheckColor(state, colortable[n]);
24003367019cSmrg	}
24013367019cSmrg    }
24023367019cSmrg    switch (state) {
24033367019cSmrg    case 1:
24043367019cSmrg    case 2:
24053367019cSmrg    case 4:
24063367019cSmrg	break;
24073367019cSmrg    default:
24083367019cSmrg	state = 0;
24093367019cSmrg	break;
24103367019cSmrg    }
24113367019cSmrg    return state;
24123367019cSmrg}
24133367019cSmrg
2414fa3f02f3Smrg/*
2415fa3f02f3Smrg * Shift the mask left or right to put its most significant bit at the 16-bit
2416fa3f02f3Smrg * mark.
2417fa3f02f3Smrg */
2418fa3f02f3Smrgstatic unsigned
2419fa3f02f3SmrgnormalizeMask(unsigned mask)
2420fa3f02f3Smrg{
2421fa3f02f3Smrg    while (mask < 0x8000) {
2422fa3f02f3Smrg	mask <<= 1;
2423fa3f02f3Smrg    }
2424fa3f02f3Smrg    while (mask >= 0x10000) {
2425fa3f02f3Smrg	mask >>= 1;
2426fa3f02f3Smrg    }
2427fa3f02f3Smrg    return mask;
2428fa3f02f3Smrg}
2429fa3f02f3Smrg
24303367019cSmrgstatic unsigned
24319a64e1c5SmrgsearchColors(XColor *colortable, unsigned mask, unsigned length, unsigned
2432fa3f02f3Smrg	     color, int state)
24333367019cSmrg{
24343367019cSmrg    unsigned result = 0;
24353367019cSmrg    unsigned n;
24363367019cSmrg    unsigned long best = ULONG_MAX;
24373367019cSmrg    unsigned long diff;
24383367019cSmrg    unsigned value;
24393367019cSmrg
2440fa3f02f3Smrg    mask = normalizeMask(mask);
24413367019cSmrg    for (n = 0; n < length; ++n) {
24423367019cSmrg	SelectColor(state, colortable[n], value);
2443fa3f02f3Smrg	diff = ((color & mask) - (value & mask));
24443367019cSmrg	diff *= diff;
24453367019cSmrg	if (diff < best) {
24463367019cSmrg#if 0
24473367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
24483367019cSmrg		   n, color,
24493367019cSmrg		   colortable[n].red,
24503367019cSmrg		   colortable[n].green,
24513367019cSmrg		   colortable[n].blue,
24523367019cSmrg		   diff));
24533367019cSmrg#endif
24543367019cSmrg	    result = n;
24553367019cSmrg	    best = diff;
24563367019cSmrg	}
24573367019cSmrg    }
24583367019cSmrg    SelectColor(state, colortable[result], value);
24593367019cSmrg    return value;
24603367019cSmrg}
24613367019cSmrg
24623367019cSmrg/*
24633367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
24643367019cSmrg *
24653367019cSmrg * According to
24663367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
24673367019cSmrg *
24683367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
24693367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
24703367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
24713367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
24723367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
24733367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
24743367019cSmrg *     actual RGB values allocated.
24753367019cSmrg *
24763367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
2477fa3f02f3Smrg * case, allocateClosestRGB() is useful for the dynamic display classes such as
24783367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
24793367019cSmrg * return regular RGB triples (unless a different scheme was used for
24803367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
24813367019cSmrg * is filled in with the colors that the server supports.
24823367019cSmrg *
24833367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
24843367019cSmrg * described.  For some TrueColor configurations it merely returns a close
24853367019cSmrg * approximation, but not the closest.
24863367019cSmrg */
24873367019cSmrgstatic Boolean
24889a64e1c5SmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def)
24893367019cSmrg{
24903367019cSmrg    XColor save = *def;
24913367019cSmrg    TScreen *screen = TScreenOf(xw);
24923367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
24933367019cSmrg
24943367019cSmrg    /*
2495fa3f02f3Smrg     * If this is a statically allocated display with too many items to store
2496fa3f02f3Smrg     * in our array, i.e., TrueColor, see if we can improve on the result by
2497fa3f02f3Smrg     * using the color values actually supported by the server.
24983367019cSmrg     */
24993367019cSmrg    if (result) {
25003367019cSmrg	unsigned cmap_type;
25013367019cSmrg	unsigned cmap_size;
25023367019cSmrg	int state;
25033367019cSmrg
2504fa3f02f3Smrg	getColormapInfo(xw, &cmap_type, &cmap_size);
25053367019cSmrg
2506fa3f02f3Smrg	if (cmap_type == TrueColor) {
25073367019cSmrg	    XColor temp = *def;
25083367019cSmrg
25093367019cSmrg	    if (loadColorTable(xw, cmap_size)
25103367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
2511fa3f02f3Smrg#define SearchColors(which) \
2512fa3f02f3Smrg	temp.which = (unsigned short) searchColors(screen->cmap_data, \
2513fa3f02f3Smrg						   (unsigned) xw->visInfo->which##_mask,\
2514fa3f02f3Smrg						   cmap_size, \
2515fa3f02f3Smrg						   save.which, \
2516fa3f02f3Smrg						   state)
25173367019cSmrg		SearchColors(red);
25183367019cSmrg		SearchColors(green);
25193367019cSmrg		SearchColors(blue);
25203367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
25213367019cSmrg#if OPT_TRACE
25223367019cSmrg		    if (temp.red != save.red
25233367019cSmrg			|| temp.green != save.green
25243367019cSmrg			|| temp.blue != save.blue) {
25253367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
25263367019cSmrg			       save.red, save.green, save.blue,
25273367019cSmrg			       temp.red, temp.green, temp.blue));
25283367019cSmrg		    } else {
25293367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
25303367019cSmrg			       save.red, save.green, save.blue));
25313367019cSmrg		    }
25323367019cSmrg#endif
25333367019cSmrg		    *def = temp;
25343367019cSmrg		}
25353367019cSmrg	    }
25363367019cSmrg	}
25373367019cSmrg    }
25383367019cSmrg
25393367019cSmrg    return result;
25403367019cSmrg}
25413367019cSmrg
2542d522f475Smrg/*
2543d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2544d522f475Smrg * to 256.
2545d522f475Smrg *
2546d522f475Smrg * Returns
2547d522f475Smrg *	-1 on error
2548d522f475Smrg *	0 on no change
2549d522f475Smrg *	1 if a new color was allocated.
2550d522f475Smrg */
2551d522f475Smrgstatic int
2552d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2553d522f475Smrg		  ColorRes * res,
2554cd3331d0Smrg		  const char *spec)
2555d522f475Smrg{
2556d522f475Smrg    int result;
2557d522f475Smrg    XColor def;
2558d522f475Smrg
25593367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2560d522f475Smrg	if (
2561d522f475Smrg#if OPT_COLOR_RES
2562d522f475Smrg	       res->mode == True &&
2563d522f475Smrg#endif
2564d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
2565d522f475Smrg	    result = 0;
2566d522f475Smrg	} else {
2567d522f475Smrg	    result = 1;
2568d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
25693367019cSmrg	    res->red = def.red;
25703367019cSmrg	    res->green = def.green;
25713367019cSmrg	    res->blue = def.blue;
25723367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
25733367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
25743367019cSmrg		   def.red,
25753367019cSmrg		   def.green,
25763367019cSmrg		   def.blue,
25773367019cSmrg		   def.pixel));
2578d522f475Smrg#if OPT_COLOR_RES
2579d522f475Smrg	    if (!res->mode)
2580d522f475Smrg		result = 0;
2581d522f475Smrg	    res->mode = True;
2582d522f475Smrg#endif
2583d522f475Smrg	}
2584d522f475Smrg    } else {
2585d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2586d522f475Smrg	result = -1;
2587d522f475Smrg    }
2588d522f475Smrg    return (result);
2589d522f475Smrg}
2590d522f475Smrg
2591d522f475Smrg#if OPT_COLOR_RES
2592d522f475SmrgPixel
2593cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2594d522f475Smrg{
2595d522f475Smrg    Pixel result = 0;
2596d522f475Smrg
2597d522f475Smrg    if (res->mode) {
2598d522f475Smrg	result = res->value;
2599d522f475Smrg    } else {
2600d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2601cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2602d522f475Smrg
2603cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2604cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2605d522f475Smrg
2606cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2607cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2608d522f475Smrg		res->mode = -True;
26093367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
26103367019cSmrg			     NonNull(res->resource));
2611d522f475Smrg	    }
2612d522f475Smrg	    result = res->value;
2613d522f475Smrg	} else {
2614d522f475Smrg	    result = 0;
2615d522f475Smrg	}
2616d522f475Smrg    }
2617d522f475Smrg    return result;
2618d522f475Smrg}
2619d522f475Smrg#endif
2620d522f475Smrg
2621cd3331d0Smrgstatic int
2622cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2623cd3331d0Smrg{
2624cd3331d0Smrg    int code;
2625cd3331d0Smrg
2626cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2627cd3331d0Smrg	code = -1;
2628cd3331d0Smrg    } else {
2629cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2630cd3331d0Smrg
2631cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2632cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2633cd3331d0Smrg    }
2634cd3331d0Smrg    return code;
2635cd3331d0Smrg}
2636cd3331d0Smrg
2637cd3331d0Smrg/*
2638cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2639cd3331d0Smrg * values from the given buffer.
2640cd3331d0Smrg *
2641cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2642cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2643cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2644cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2645cd3331d0Smrg * 'first' set to the beginning of those indices.
2646cd3331d0Smrg *
2647cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2648cd3331d0Smrg */
2649d522f475Smrgstatic Bool
2650d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2651d522f475Smrg		       char *buf,
2652cd3331d0Smrg		       int first,
2653d522f475Smrg		       int final)
2654d522f475Smrg{
2655d522f475Smrg    char *name;
2656d522f475Smrg    int color;
2657d522f475Smrg    int repaint = False;
2658d522f475Smrg    int code;
2659cd3331d0Smrg    int last = (MAXCOLORS - first);
2660d522f475Smrg
2661d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2662d522f475Smrg
2663d522f475Smrg    while (buf && *buf) {
2664d522f475Smrg	name = strchr(buf, ';');
2665d522f475Smrg	if (name == NULL)
2666d522f475Smrg	    break;
2667d522f475Smrg	*name = '\0';
2668d522f475Smrg	name++;
2669d522f475Smrg	color = atoi(buf);
2670cd3331d0Smrg	if (color < 0 || color >= last)
2671cd3331d0Smrg	    break;		/* quit on any error */
2672d522f475Smrg	buf = strchr(name, ';');
2673d522f475Smrg	if (buf) {
2674d522f475Smrg	    *buf = '\0';
2675d522f475Smrg	    buf++;
2676d522f475Smrg	}
2677cd3331d0Smrg	if (!strcmp(name, "?")) {
2678cd3331d0Smrg	    ReportAnsiColorRequest(xw, color + first, final);
2679cd3331d0Smrg	} else {
2680cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
2681d522f475Smrg	    if (code < 0) {
2682d522f475Smrg		/* stop on any error */
2683d522f475Smrg		break;
2684d522f475Smrg	    } else if (code > 0) {
2685d522f475Smrg		repaint = True;
2686d522f475Smrg	    }
2687d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
2688d522f475Smrg	     * change style (dynamic colors).
2689d522f475Smrg	     */
2690d522f475Smrg	}
2691d522f475Smrg    }
2692d522f475Smrg
2693d522f475Smrg    return (repaint);
2694d522f475Smrg}
2695cd3331d0Smrg
2696cd3331d0Smrgstatic Bool
2697cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
2698cd3331d0Smrg{
2699cd3331d0Smrg    Bool repaint = False;
2700cd3331d0Smrg    int last = MAXCOLORS - start;
2701cd3331d0Smrg
2702cd3331d0Smrg    if (color >= 0 && color < last) {
2703cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
2704cd3331d0Smrg
2705cd3331d0Smrg	if (res->mode) {
2706cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
2707cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
2708cd3331d0Smrg		repaint = True;
2709cd3331d0Smrg	    }
2710cd3331d0Smrg	}
2711cd3331d0Smrg    }
2712cd3331d0Smrg    return repaint;
2713cd3331d0Smrg}
2714cd3331d0Smrg
2715cd3331d0Smrgint
2716cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
2717cd3331d0Smrg{
2718cd3331d0Smrg    int repaint = 0;
2719cd3331d0Smrg    int color;
2720cd3331d0Smrg
2721cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
2722cd3331d0Smrg    if (*buf != '\0') {
2723cd3331d0Smrg	/* reset specific colors */
2724cd3331d0Smrg	while (!IsEmpty(buf)) {
2725cd3331d0Smrg	    char *next;
2726cd3331d0Smrg
2727cd3331d0Smrg	    color = (int) strtol(buf, &next, 10);
2728cd3331d0Smrg	    if ((next == buf) || (color < 0))
2729cd3331d0Smrg		break;		/* no number at all */
2730cd3331d0Smrg	    if (next != 0) {
2731cd3331d0Smrg		if (strchr(";", *next) == 0)
2732cd3331d0Smrg		    break;	/* unexpected delimiter */
2733cd3331d0Smrg		++next;
2734cd3331d0Smrg	    }
2735cd3331d0Smrg
2736cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2737cd3331d0Smrg		++repaint;
2738cd3331d0Smrg	    }
2739cd3331d0Smrg	    buf = next;
2740cd3331d0Smrg	}
2741cd3331d0Smrg    } else {
2742cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
2743cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
2744cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2745cd3331d0Smrg		++repaint;
2746cd3331d0Smrg	    }
2747cd3331d0Smrg	}
2748cd3331d0Smrg    }
2749cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
2750cd3331d0Smrg    return repaint;
2751cd3331d0Smrg}
2752d522f475Smrg#else
27533367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
27543367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
2755d522f475Smrg#endif /* OPT_ISO_COLORS */
2756d522f475Smrg
2757fa3f02f3SmrgBoolean
27589a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
2759fa3f02f3Smrg{
2760fa3f02f3Smrg    Colormap cmap = xw->core.colormap;
2761fa3f02f3Smrg
2762fa3f02f3Smrg    return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
2763fa3f02f3Smrg}
2764fa3f02f3Smrg
27653367019cSmrgstatic Boolean
27669a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
27673367019cSmrg{
27683367019cSmrg    Boolean result = False;
27693367019cSmrg    TScreen *screen = TScreenOf(xw);
27703367019cSmrg    Colormap cmap = xw->core.colormap;
27713367019cSmrg
2772fa3f02f3Smrg    if (XParseColor(screen->display, cmap, spec, def)) {
2773fa3f02f3Smrg	XColor save_def = *def;
2774fa3f02f3Smrg	if (resource.reportColors) {
2775fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
2776fa3f02f3Smrg		   def->red, def->green, def->blue,
2777fa3f02f3Smrg		   spec);
2778fa3f02f3Smrg	}
2779fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
2780fa3f02f3Smrg	    if (resource.reportColors) {
2781fa3f02f3Smrg		if (def->red != save_def.red ||
2782fa3f02f3Smrg		    def->green != save_def.green ||
2783fa3f02f3Smrg		    def->blue != save_def.blue) {
2784fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
2785fa3f02f3Smrg			   def->red, def->green, def->blue,
2786fa3f02f3Smrg			   spec);
2787fa3f02f3Smrg		}
2788fa3f02f3Smrg	    }
2789fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
2790fa3f02f3Smrg		   def->red, def->green, def->blue));
2791fa3f02f3Smrg	    result = True;
2792fa3f02f3Smrg	}
27933367019cSmrg    }
27943367019cSmrg    return result;
27953367019cSmrg}
27963367019cSmrg
27973367019cSmrg/*
27983367019cSmrg * This provides an approximation (the closest color from xterm's palette)
27993367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
28003367019cSmrg * because of the context in which it is used.
28013367019cSmrg */
28023367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
28033367019cSmrgint
28043367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
28053367019cSmrg{
28063367019cSmrg    int result = -1;
28073367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
28083367019cSmrg    int n;
28093367019cSmrg    int best_index = -1;
28103367019cSmrg    unsigned long best_value = 0;
28113367019cSmrg    unsigned long this_value;
28123367019cSmrg    long diff_red, diff_green, diff_blue;
28133367019cSmrg
28143367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
28153367019cSmrg
28163367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
28173367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
28183367019cSmrg
28193367019cSmrg	/* ensure that we have a value for each of the colors */
28203367019cSmrg	if (!res->mode) {
28213367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
28223367019cSmrg	}
28233367019cSmrg
28243367019cSmrg	/* find the closest match */
28253367019cSmrg	if (res->mode == True) {
28263367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
28273367019cSmrg		    res->value, res->red, res->green, res->blue));
28283367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
28293367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
28303367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
28313367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
28323367019cSmrg					  + (diff_green * diff_green)
28333367019cSmrg					  + (diff_blue * diff_blue));
28343367019cSmrg	    if (best_index < 0 || this_value < best_value) {
28353367019cSmrg		best_index = n;
28363367019cSmrg		best_value = this_value;
28373367019cSmrg	    }
28383367019cSmrg	}
28393367019cSmrg    }
28403367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
28413367019cSmrg    result = best_index;
28423367019cSmrg#else
28433367019cSmrg    (void) xw;
28443367019cSmrg    (void) find_red;
28453367019cSmrg    (void) find_green;
28463367019cSmrg    (void) find_blue;
28473367019cSmrg#endif
28483367019cSmrg    return result;
28493367019cSmrg}
28503367019cSmrg
2851d522f475Smrg#if OPT_PASTE64
2852d522f475Smrgstatic void
2853fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
2854d522f475Smrg{
2855d522f475Smrg#define PDATA(a,b) { a, #b }
2856d522f475Smrg    static struct {
2857d522f475Smrg	char given;
2858cd3331d0Smrg	String result;
2859d522f475Smrg    } table[] = {
2860d522f475Smrg	PDATA('s', SELECT),
2861d522f475Smrg	    PDATA('p', PRIMARY),
2862d522f475Smrg	    PDATA('c', CLIPBOARD),
2863d522f475Smrg	    PDATA('0', CUT_BUFFER0),
2864d522f475Smrg	    PDATA('1', CUT_BUFFER1),
2865d522f475Smrg	    PDATA('2', CUT_BUFFER2),
2866d522f475Smrg	    PDATA('3', CUT_BUFFER3),
2867d522f475Smrg	    PDATA('4', CUT_BUFFER4),
2868d522f475Smrg	    PDATA('5', CUT_BUFFER5),
2869d522f475Smrg	    PDATA('6', CUT_BUFFER6),
2870d522f475Smrg	    PDATA('7', CUT_BUFFER7),
2871d522f475Smrg    };
2872d522f475Smrg
2873cd3331d0Smrg    const char *base = buf;
28743367019cSmrg    char *used;
2875d522f475Smrg    Cardinal j, n = 0;
28763367019cSmrg    String *select_args;
2877d522f475Smrg
2878d522f475Smrg    TRACE(("Manipulate selection data\n"));
2879d522f475Smrg
2880d522f475Smrg    while (*buf != ';' && *buf != '\0') {
2881d522f475Smrg	++buf;
2882d522f475Smrg    }
2883d522f475Smrg
2884d522f475Smrg    if (*buf == ';') {
2885d522f475Smrg	*buf++ = '\0';
2886d522f475Smrg
2887d522f475Smrg	if (*base == '\0')
2888d522f475Smrg	    base = "s0";
2889d522f475Smrg
28903367019cSmrg	if ((used = x_strdup(base)) != 0) {
28913367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
28923367019cSmrg		while (*base != '\0') {
28933367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
28943367019cSmrg			if (*base == table[j].given) {
28953367019cSmrg			    used[n] = *base;
28963367019cSmrg			    select_args[n++] = table[j].result;
28973367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
28983367019cSmrg			    break;
28993367019cSmrg			}
29003367019cSmrg		    }
29013367019cSmrg		    ++base;
29023367019cSmrg		}
29033367019cSmrg		used[n] = 0;
29043367019cSmrg
29053367019cSmrg		if (!strcmp(buf, "?")) {
29063367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
29073367019cSmrg			TRACE(("Getting selection\n"));
29083367019cSmrg			unparseputc1(xw, ANSI_OSC);
29093367019cSmrg			unparseputs(xw, "52");
29103367019cSmrg			unparseputc(xw, ';');
29113367019cSmrg
29123367019cSmrg			unparseputs(xw, used);
29133367019cSmrg			unparseputc(xw, ';');
29143367019cSmrg
29153367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
29163367019cSmrg			screen->base64_paste = n;
29173367019cSmrg			screen->base64_final = final;
29183367019cSmrg
29193367019cSmrg			/* terminator will be written in this call */
29203367019cSmrg			xtermGetSelection((Widget) xw,
2921fa3f02f3Smrg					  XtLastTimestampProcessed(TScreenOf(xw)->display),
29223367019cSmrg					  select_args, n,
29233367019cSmrg					  NULL);
29243367019cSmrg		    }
29253367019cSmrg		} else {
29263367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
29273367019cSmrg			TRACE(("Setting selection with %s\n", buf));
29283367019cSmrg			ClearSelectionBuffer(screen);
29293367019cSmrg			while (*buf != '\0')
29303367019cSmrg			    AppendToSelectionBuffer(screen, CharOf(*buf++));
29313367019cSmrg			CompleteSelection(xw, select_args, n);
29323367019cSmrg		    }
29333367019cSmrg		}
29343367019cSmrg		free(select_args);
2935cd3331d0Smrg	    }
29363367019cSmrg	    free(used);
2937d522f475Smrg	}
2938d522f475Smrg    }
2939d522f475Smrg}
2940d522f475Smrg#endif /* OPT_PASTE64 */
2941d522f475Smrg
2942d522f475Smrg/***====================================================================***/
2943d522f475Smrg
2944cd3331d0Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
2945cd3331d0Smrg
2946d522f475Smrgstatic Bool
2947fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
2948d522f475Smrg{
2949cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2950d522f475Smrg    Bool result = False;
2951d522f475Smrg    Char *cp = *bufp;
2952d522f475Smrg    Char *next = cp;
2953d522f475Smrg
2954d522f475Smrg    (void) screen;
2955d522f475Smrg    (void) last;
2956d522f475Smrg
2957d522f475Smrg#if OPT_WIDE_CHARS
2958cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
2959d522f475Smrg	PtyData data;
2960d522f475Smrg
29619a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
2962d522f475Smrg	    if (data.utf_data != UCS_REPL
2963d522f475Smrg		&& (data.utf_data >= 128 ||
2964d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
2965d522f475Smrg		next += (data.utf_size - 1);
2966d522f475Smrg		result = True;
2967d522f475Smrg	    } else {
2968d522f475Smrg		result = False;
2969d522f475Smrg	    }
2970d522f475Smrg	} else {
2971d522f475Smrg	    result = False;
2972d522f475Smrg	}
2973d522f475Smrg    } else
2974d522f475Smrg#endif
2975d522f475Smrg#if OPT_C1_PRINT
2976d522f475Smrg	if (screen->c1_printable
2977d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
2978d522f475Smrg	result = True;
2979d522f475Smrg    } else
2980d522f475Smrg#endif
2981d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
2982d522f475Smrg	result = True;
2983d522f475Smrg    }
2984d522f475Smrg    *bufp = next;
2985d522f475Smrg    return result;
2986d522f475Smrg}
2987d522f475Smrg
2988d522f475Smrg/***====================================================================***/
2989d522f475Smrg
2990d522f475Smrg/*
2991d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
2992cd3331d0Smrg * array indices.  Compare with TermColors.
2993d522f475Smrg */
2994d522f475Smrgtypedef enum {
2995d522f475Smrg    OSC_TEXT_FG = 10
2996d522f475Smrg    ,OSC_TEXT_BG
2997d522f475Smrg    ,OSC_TEXT_CURSOR
2998d522f475Smrg    ,OSC_MOUSE_FG
2999d522f475Smrg    ,OSC_MOUSE_BG
3000d522f475Smrg#if OPT_TEK4014
3001d522f475Smrg    ,OSC_TEK_FG = 15
3002d522f475Smrg    ,OSC_TEK_BG
3003d522f475Smrg#endif
3004d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3005d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3006d522f475Smrg#endif
3007d522f475Smrg#if OPT_TEK4014
3008d522f475Smrg    ,OSC_TEK_CURSOR = 18
3009d522f475Smrg#endif
3010d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3011d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3012d522f475Smrg#endif
3013d522f475Smrg    ,OSC_NCOLORS
3014d522f475Smrg} OscTextColors;
3015d522f475Smrg
3016cd3331d0Smrg/*
3017cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3018cd3331d0Smrg */
3019cd3331d0Smrg#define OSC_RESET 100
3020cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3021cd3331d0Smrg
3022d522f475Smrgstatic Bool
3023d522f475SmrgGetOldColors(XtermWidget xw)
3024d522f475Smrg{
3025d522f475Smrg    int i;
30269a64e1c5Smrg    if (xw->work.oldColors == NULL) {
30279a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
30289a64e1c5Smrg	if (xw->work.oldColors == NULL) {
30293367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3030d522f475Smrg	    return (False);
3031d522f475Smrg	}
30329a64e1c5Smrg	xw->work.oldColors->which = 0;
3033d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
30349a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
30359a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3036d522f475Smrg	}
30379a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3038d522f475Smrg    }
3039d522f475Smrg    return (True);
3040d522f475Smrg}
3041d522f475Smrg
3042d522f475Smrgstatic int
3043d522f475SmrgoppositeColor(int n)
3044d522f475Smrg{
3045d522f475Smrg    switch (n) {
3046d522f475Smrg    case TEXT_FG:
3047d522f475Smrg	n = TEXT_BG;
3048d522f475Smrg	break;
3049d522f475Smrg    case TEXT_BG:
3050d522f475Smrg	n = TEXT_FG;
3051d522f475Smrg	break;
3052d522f475Smrg    case MOUSE_FG:
3053d522f475Smrg	n = MOUSE_BG;
3054d522f475Smrg	break;
3055d522f475Smrg    case MOUSE_BG:
3056d522f475Smrg	n = MOUSE_FG;
3057d522f475Smrg	break;
3058d522f475Smrg#if OPT_TEK4014
3059d522f475Smrg    case TEK_FG:
3060d522f475Smrg	n = TEK_BG;
3061d522f475Smrg	break;
3062d522f475Smrg    case TEK_BG:
3063d522f475Smrg	n = TEK_FG;
3064d522f475Smrg	break;
3065d522f475Smrg#endif
3066d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3067d522f475Smrg    case HIGHLIGHT_FG:
3068d522f475Smrg	n = HIGHLIGHT_BG;
3069d522f475Smrg	break;
3070d522f475Smrg    case HIGHLIGHT_BG:
3071d522f475Smrg	n = HIGHLIGHT_FG;
3072d522f475Smrg	break;
3073d522f475Smrg#endif
3074d522f475Smrg    default:
3075d522f475Smrg	break;
3076d522f475Smrg    }
3077d522f475Smrg    return n;
3078d522f475Smrg}
3079d522f475Smrg
3080d522f475Smrgstatic void
3081d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3082d522f475Smrg{
3083cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3084cd3331d0Smrg	XColor color;
3085cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3086cd3331d0Smrg	char buffer[80];
3087d522f475Smrg
3088cd3331d0Smrg	/*
3089cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3090cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3091cd3331d0Smrg	 * reporting the opposite color which would be used.
3092cd3331d0Smrg	 */
3093cd3331d0Smrg	int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
3094cd3331d0Smrg
3095cd3331d0Smrg	GetOldColors(xw);
30969a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3097cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
3098cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3099cd3331d0Smrg		color.red,
3100cd3331d0Smrg		color.green,
3101cd3331d0Smrg		color.blue);
3102712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
31039a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3104cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3105cd3331d0Smrg	unparseputs(xw, buffer);
3106cd3331d0Smrg	unparseputc1(xw, final);
3107cd3331d0Smrg	unparse_end(xw);
3108cd3331d0Smrg    }
3109d522f475Smrg}
3110d522f475Smrg
3111d522f475Smrgstatic Bool
3112d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
3113d522f475Smrg{
3114d522f475Smrg    int i;
3115d522f475Smrg
3116d522f475Smrg    /* if we were going to free old colors, this would be the place to
3117d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3118d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3119d522f475Smrg     * we could save some overhead this way.   The only case in which this
3120d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3121d522f475Smrg     * which case they can restart xterm
3122d522f475Smrg     */
3123d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3124d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
31259a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
31269a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
31279a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3128d522f475Smrg	    }
3129d522f475Smrg	    if (pNew->names[i]) {
31309a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3131d522f475Smrg	    }
31329a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3133d522f475Smrg	}
3134d522f475Smrg    }
3135d522f475Smrg    return (True);
3136d522f475Smrg}
3137d522f475Smrg
3138d522f475Smrg/*
3139d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3140d522f475Smrg * xterm is compiled.
3141d522f475Smrg */
3142d522f475Smrgstatic int
3143d522f475SmrgOscToColorIndex(OscTextColors mode)
3144d522f475Smrg{
3145d522f475Smrg    int result = 0;
3146d522f475Smrg
3147d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3148d522f475Smrg    switch (mode) {
3149d522f475Smrg	CASE(TEXT_FG);
3150d522f475Smrg	CASE(TEXT_BG);
3151d522f475Smrg	CASE(TEXT_CURSOR);
3152d522f475Smrg	CASE(MOUSE_FG);
3153d522f475Smrg	CASE(MOUSE_BG);
3154d522f475Smrg#if OPT_TEK4014
3155d522f475Smrg	CASE(TEK_FG);
3156d522f475Smrg	CASE(TEK_BG);
3157d522f475Smrg#endif
3158d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3159d522f475Smrg	CASE(HIGHLIGHT_BG);
3160d522f475Smrg	CASE(HIGHLIGHT_FG);
3161d522f475Smrg#endif
3162d522f475Smrg#if OPT_TEK4014
3163d522f475Smrg	CASE(TEK_CURSOR);
3164d522f475Smrg#endif
3165d522f475Smrg    case OSC_NCOLORS:
3166d522f475Smrg	break;
3167d522f475Smrg    }
3168d522f475Smrg    return result;
3169d522f475Smrg}
3170d522f475Smrg
3171d522f475Smrgstatic Bool
3172d522f475SmrgChangeColorsRequest(XtermWidget xw,
3173d522f475Smrg		    int start,
3174d522f475Smrg		    char *names,
3175d522f475Smrg		    int final)
3176d522f475Smrg{
3177d522f475Smrg    Bool result = False;
3178d522f475Smrg    char *thisName;
3179d522f475Smrg    ScrnColors newColors;
3180d522f475Smrg    int i, ndx;
3181d522f475Smrg
3182d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3183d522f475Smrg
3184d522f475Smrg    if (GetOldColors(xw)) {
3185d522f475Smrg	newColors.which = 0;
3186d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3187d522f475Smrg	    newColors.names[i] = NULL;
3188d522f475Smrg	}
3189d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3190d522f475Smrg	    ndx = OscToColorIndex((OscTextColors) i);
3191d522f475Smrg	    if (xw->misc.re_verse)
3192d522f475Smrg		ndx = oppositeColor(ndx);
3193d522f475Smrg
3194cd3331d0Smrg	    if (IsEmpty(names)) {
3195d522f475Smrg		newColors.names[ndx] = NULL;
3196d522f475Smrg	    } else {
3197d522f475Smrg		if (names[0] == ';')
3198d522f475Smrg		    thisName = NULL;
3199d522f475Smrg		else
3200d522f475Smrg		    thisName = names;
3201d522f475Smrg		names = strchr(names, ';');
3202d522f475Smrg		if (names != NULL) {
3203d522f475Smrg		    *names++ = '\0';
3204d522f475Smrg		}
3205fa3f02f3Smrg		if (thisName != 0) {
3206fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3207fa3f02f3Smrg			ReportColorRequest(xw, ndx, final);
32089a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
32099a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3210fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3211fa3f02f3Smrg		    }
3212d522f475Smrg		}
3213d522f475Smrg	    }
3214d522f475Smrg	}
3215d522f475Smrg
3216d522f475Smrg	if (newColors.which != 0) {
3217d522f475Smrg	    ChangeColors(xw, &newColors);
3218d522f475Smrg	    UpdateOldColors(xw, &newColors);
3219d522f475Smrg	}
3220d522f475Smrg	result = True;
3221d522f475Smrg    }
3222d522f475Smrg    return result;
3223d522f475Smrg}
3224d522f475Smrg
3225cd3331d0Smrgstatic Bool
3226cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3227cd3331d0Smrg		   int code)
3228cd3331d0Smrg{
3229cd3331d0Smrg    Bool result = False;
32309a64e1c5Smrg#if OPT_COLOR_RES
3231cd3331d0Smrg    const char *thisName;
3232cd3331d0Smrg    ScrnColors newColors;
3233cd3331d0Smrg    int ndx;
32349a64e1c5Smrg#endif
3235cd3331d0Smrg
3236cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3237cd3331d0Smrg
3238cd3331d0Smrg#if OPT_COLOR_RES
3239cd3331d0Smrg    if (GetOldColors(xw)) {
3240cd3331d0Smrg	ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3241cd3331d0Smrg	if (xw->misc.re_verse)
3242cd3331d0Smrg	    ndx = oppositeColor(ndx);
3243cd3331d0Smrg
3244cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3245cd3331d0Smrg
3246cd3331d0Smrg	newColors.which = 0;
3247cd3331d0Smrg	newColors.names[ndx] = NULL;
3248cd3331d0Smrg
3249cd3331d0Smrg	if (thisName != 0
32509a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
32519a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3252cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3253cd3331d0Smrg
3254cd3331d0Smrg	    if (newColors.which != 0) {
3255cd3331d0Smrg		ChangeColors(xw, &newColors);
3256cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3257cd3331d0Smrg	    }
3258cd3331d0Smrg	}
3259cd3331d0Smrg	result = True;
3260cd3331d0Smrg    }
3261cd3331d0Smrg#endif
3262cd3331d0Smrg    return result;
3263cd3331d0Smrg}
3264cd3331d0Smrg
3265cd3331d0Smrg#if OPT_SHIFT_FONTS
3266cd3331d0Smrg/*
3267cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3268cd3331d0Smrg *
3269cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3270cd3331d0Smrg * the corresponding menu font entry.
3271cd3331d0Smrg */
3272cd3331d0Smrgstatic int
3273fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3274cd3331d0Smrg{
3275cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3276cd3331d0Smrg    int num = screen->menu_font_number;
3277cd3331d0Smrg    int rel = 0;
3278cd3331d0Smrg
3279cd3331d0Smrg    if (*++source == '+') {
3280cd3331d0Smrg	rel = 1;
3281cd3331d0Smrg	source++;
3282cd3331d0Smrg    } else if (*source == '-') {
3283cd3331d0Smrg	rel = -1;
3284cd3331d0Smrg	source++;
3285cd3331d0Smrg    }
3286cd3331d0Smrg
3287cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3288cd3331d0Smrg	int val = atoi(source);
3289cd3331d0Smrg	if (rel > 0)
3290cd3331d0Smrg	    rel = val;
3291cd3331d0Smrg	else if (rel < 0)
3292cd3331d0Smrg	    rel = -val;
3293cd3331d0Smrg	else
3294cd3331d0Smrg	    num = val;
3295cd3331d0Smrg    }
3296cd3331d0Smrg
3297cd3331d0Smrg    if (rel != 0) {
3298cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3299cd3331d0Smrg				     screen->menu_font_number, rel);
3300cd3331d0Smrg
3301cd3331d0Smrg    }
3302cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3303cd3331d0Smrg    *target = source;
3304cd3331d0Smrg    return num;
3305cd3331d0Smrg}
3306cd3331d0Smrg
3307cd3331d0Smrgstatic void
3308cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3309cd3331d0Smrg{
3310cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3311cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3312cd3331d0Smrg	Bool success = True;
3313cd3331d0Smrg	int num;
3314cb4a1343Smrg	String base = buf + 1;
3315cd3331d0Smrg	const char *name = 0;
3316cd3331d0Smrg	char temp[10];
3317cd3331d0Smrg
3318cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3319cd3331d0Smrg	if (num < 0
3320cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3321cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3322cd3331d0Smrg	    success = False;
3323cd3331d0Smrg	} else {
3324cd3331d0Smrg#if OPT_RENDERFONT
3325cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3326cd3331d0Smrg		name = getFaceName(xw, False);
3327cd3331d0Smrg	    } else
3328cd3331d0Smrg#endif
3329cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3330cd3331d0Smrg		success = False;
3331cd3331d0Smrg	    }
3332cd3331d0Smrg	}
3333cd3331d0Smrg
3334cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3335cd3331d0Smrg	unparseputs(xw, "50");
3336cd3331d0Smrg
3337cd3331d0Smrg	if (success) {
3338cd3331d0Smrg	    unparseputc(xw, ';');
3339cd3331d0Smrg	    if (buf >= base) {
3340cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3341cd3331d0Smrg		if (*buf != '\0') {
3342cd3331d0Smrg		    unparseputc(xw, '#');
3343cd3331d0Smrg		    sprintf(temp, "%d", num);
3344cd3331d0Smrg		    unparseputs(xw, temp);
3345cd3331d0Smrg		    if (*name != '\0')
3346cd3331d0Smrg			unparseputc(xw, ' ');
3347cd3331d0Smrg		}
3348cd3331d0Smrg	    }
3349cd3331d0Smrg	    unparseputs(xw, name);
3350cd3331d0Smrg	}
3351cd3331d0Smrg
3352cd3331d0Smrg	unparseputc1(xw, final);
3353cd3331d0Smrg	unparse_end(xw);
3354cd3331d0Smrg    }
3355cd3331d0Smrg}
3356cd3331d0Smrg
3357cd3331d0Smrgstatic void
3358cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3359cd3331d0Smrg{
3360cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3361cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3362cd3331d0Smrg	Bool success = True;
3363cd3331d0Smrg	int num;
3364cd3331d0Smrg	VTFontNames fonts;
3365cd3331d0Smrg	char *name;
3366cd3331d0Smrg
3367cd3331d0Smrg	/*
3368cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3369cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3370cd3331d0Smrg	 *
3371cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3372cd3331d0Smrg	 * to load the font entry.
3373cd3331d0Smrg	 */
3374cd3331d0Smrg	if (*buf == '#') {
3375cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3376cd3331d0Smrg
3377cd3331d0Smrg	    if (num < 0
3378cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3379cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3380cd3331d0Smrg		success = False;
3381cd3331d0Smrg	    } else {
3382cd3331d0Smrg		/*
3383cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3384cd3331d0Smrg		 * for a font specification within the control.
3385cd3331d0Smrg		 */
3386cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3387cd3331d0Smrg		    ++buf;
3388cd3331d0Smrg		}
3389cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3390cd3331d0Smrg		    ++buf;
3391cd3331d0Smrg		}
3392cd3331d0Smrg#if OPT_RENDERFONT
3393cd3331d0Smrg		if (UsingRenderFont(xw)) {
3394c219fbebSmrg		    /* EMPTY */
3395c219fbebSmrg		    /* there is only one font entry to load */
3396c219fbebSmrg		    ;
3397cd3331d0Smrg		} else
3398cd3331d0Smrg#endif
3399cd3331d0Smrg		{
3400cd3331d0Smrg		    /*
3401cd3331d0Smrg		     * Normally there is no font specified in the control.
3402cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3403cd3331d0Smrg		     */
3404cd3331d0Smrg		    if (*buf == '\0') {
3405cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3406cd3331d0Smrg			    success = False;
3407cd3331d0Smrg			}
3408cd3331d0Smrg		    }
3409cd3331d0Smrg		}
3410cd3331d0Smrg	    }
3411cd3331d0Smrg	} else {
3412cd3331d0Smrg	    num = screen->menu_font_number;
3413cd3331d0Smrg	}
3414cd3331d0Smrg	name = x_strtrim(buf);
3415cd3331d0Smrg	if (success && !IsEmpty(name)) {
3416cd3331d0Smrg#if OPT_RENDERFONT
3417cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3418cd3331d0Smrg		setFaceName(xw, name);
3419cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3420cd3331d0Smrg	    } else
3421cd3331d0Smrg#endif
3422cd3331d0Smrg	    {
3423cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3424cd3331d0Smrg		fonts.f_n = name;
3425cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
3426cd3331d0Smrg	    }
3427cd3331d0Smrg	} else {
3428cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3429cd3331d0Smrg	}
3430cd3331d0Smrg	free(name);
3431cd3331d0Smrg    }
3432cd3331d0Smrg}
3433cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3434cd3331d0Smrg
3435d522f475Smrg/***====================================================================***/
3436d522f475Smrg
3437d522f475Smrgvoid
3438fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3439d522f475Smrg{
3440cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3441d522f475Smrg    int mode;
3442d522f475Smrg    Char *cp;
3443d522f475Smrg    int state = 0;
3444d522f475Smrg    char *buf = 0;
3445cd3331d0Smrg    char temp[2];
3446cd3331d0Smrg#if OPT_ISO_COLORS
3447cd3331d0Smrg    int ansi_colors = 0;
3448cd3331d0Smrg#endif
3449cd3331d0Smrg    Bool need_data = True;
3450fa3f02f3Smrg    Bool optional_data = False;
3451d522f475Smrg
3452d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3453d522f475Smrg
3454712a7ff4Smrg    (void) screen;
3455712a7ff4Smrg
3456d522f475Smrg    /*
3457d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3458d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3459d522f475Smrg     * with the same final character as the application sends to make this
3460d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3461d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3462d522f475Smrg     */
3463d522f475Smrg    mode = 0;
3464d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3465d522f475Smrg	switch (state) {
3466d522f475Smrg	case 0:
3467d522f475Smrg	    if (isdigit(*cp)) {
3468d522f475Smrg		mode = 10 * mode + (*cp - '0');
3469d522f475Smrg		if (mode > 65535) {
3470d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3471d522f475Smrg		    return;
3472d522f475Smrg		}
3473d522f475Smrg		break;
3474d522f475Smrg	    }
3475d522f475Smrg	    /* FALLTHRU */
3476d522f475Smrg	case 1:
3477d522f475Smrg	    if (*cp != ';') {
3478cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
3479cd3331d0Smrg		       (int) (cp - oscbuf)));
3480d522f475Smrg		return;
3481d522f475Smrg	    }
3482d522f475Smrg	    state = 2;
3483d522f475Smrg	    break;
3484d522f475Smrg	case 2:
3485d522f475Smrg	    buf = (char *) cp;
3486d522f475Smrg	    state = 3;
3487d522f475Smrg	    /* FALLTHRU */
3488d522f475Smrg	default:
3489cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
3490d522f475Smrg		switch (mode) {
3491d522f475Smrg		case 0:
3492d522f475Smrg		case 1:
3493d522f475Smrg		case 2:
3494d522f475Smrg		    break;
3495d522f475Smrg		default:
3496d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
3497d522f475Smrg			   CharOf(*cp),
3498cd3331d0Smrg			   (int) (cp - oscbuf)));
3499d522f475Smrg		    return;
3500d522f475Smrg		}
3501d522f475Smrg	    }
3502d522f475Smrg	}
3503d522f475Smrg    }
3504cd3331d0Smrg
35053367019cSmrg    /*
35063367019cSmrg     * Check if the palette changed and there are no more immediate changes
35073367019cSmrg     * that could be deferred to the next repaint.
35083367019cSmrg     */
35093367019cSmrg    if (xw->misc.palette_changed) {
35103367019cSmrg	switch (mode) {
35113367019cSmrg	case 3:		/* change X property */
35123367019cSmrg	case 30:		/* Konsole (unused) */
35133367019cSmrg	case 31:		/* Konsole (unused) */
35143367019cSmrg	case 50:		/* font operations */
35153367019cSmrg	case 51:		/* Emacs (unused) */
35163367019cSmrg#if OPT_PASTE64
35173367019cSmrg	case 52:		/* selection data */
35183367019cSmrg#endif
35193367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
35203367019cSmrg	    xw->misc.palette_changed = False;
35213367019cSmrg	    xtermRepaint(xw);
35223367019cSmrg	    break;
35233367019cSmrg	}
35243367019cSmrg    }
35253367019cSmrg
3526cd3331d0Smrg    /*
3527cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
3528cd3331d0Smrg     * a special case.
3529cd3331d0Smrg     */
3530cd3331d0Smrg    switch (mode) {
3531cd3331d0Smrg#if OPT_ISO_COLORS
3532cd3331d0Smrg    case OSC_Reset(4):
3533cd3331d0Smrg    case OSC_Reset(5):
3534fa3f02f3Smrg	need_data = False;
3535fa3f02f3Smrg	optional_data = True;
3536fa3f02f3Smrg	break;
3537cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3538cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3539cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3540cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3541cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3542cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3543cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3544cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3545cd3331d0Smrg#endif
3546cd3331d0Smrg#if OPT_TEK4014
3547cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3548cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3549cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3550cd3331d0Smrg#endif
3551cd3331d0Smrg	need_data = False;
3552cd3331d0Smrg	break;
3553cd3331d0Smrg#endif
3554cd3331d0Smrg    default:
3555cd3331d0Smrg	break;
3556cd3331d0Smrg    }
3557cd3331d0Smrg
3558cd3331d0Smrg    /*
3559cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
3560cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
3561cd3331d0Smrg     */
3562cd3331d0Smrg    if (IsEmpty(buf)) {
3563cd3331d0Smrg	if (need_data) {
3564cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
3565cd3331d0Smrg	    return;
3566cd3331d0Smrg	}
3567cd3331d0Smrg	temp[0] = '\0';
3568cd3331d0Smrg	buf = temp;
3569fa3f02f3Smrg    } else if (!need_data && !optional_data) {
3570fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
3571d522f475Smrg	return;
35720d92cbfdSchristos    }
3573d522f475Smrg
3574d522f475Smrg    switch (mode) {
3575d522f475Smrg    case 0:			/* new icon name and title */
3576b7c89284Ssnj	ChangeIconName(xw, buf);
3577b7c89284Ssnj	ChangeTitle(xw, buf);
3578d522f475Smrg	break;
3579d522f475Smrg
3580d522f475Smrg    case 1:			/* new icon name only */
3581b7c89284Ssnj	ChangeIconName(xw, buf);
3582d522f475Smrg	break;
3583d522f475Smrg
3584d522f475Smrg    case 2:			/* new title only */
3585b7c89284Ssnj	ChangeTitle(xw, buf);
3586d522f475Smrg	break;
3587d522f475Smrg
358822d8e007Schristos#ifdef notdef
3589d522f475Smrg    case 3:			/* change X property */
3590cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
35910d92cbfdSchristos	    ChangeXprop(buf);
3592d522f475Smrg	break;
359322d8e007Schristos#endif
3594d522f475Smrg#if OPT_ISO_COLORS
3595cd3331d0Smrg    case 5:
3596cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3597cd3331d0Smrg	/* FALLTHRU */
3598d522f475Smrg    case 4:
3599cd3331d0Smrg	if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
36003367019cSmrg	    xw->misc.palette_changed = True;
3601cd3331d0Smrg	break;
3602cd3331d0Smrg    case OSC_Reset(5):
3603cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3604cd3331d0Smrg	/* FALLTHRU */
3605cd3331d0Smrg    case OSC_Reset(4):
3606cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
36073367019cSmrg	    xw->misc.palette_changed = True;
3608d522f475Smrg	break;
3609d522f475Smrg#endif
3610d522f475Smrg    case OSC_TEXT_FG:
3611d522f475Smrg    case OSC_TEXT_BG:
3612d522f475Smrg    case OSC_TEXT_CURSOR:
3613d522f475Smrg    case OSC_MOUSE_FG:
3614d522f475Smrg    case OSC_MOUSE_BG:
3615d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3616d522f475Smrg    case OSC_HIGHLIGHT_BG:
3617cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
3618d522f475Smrg#endif
3619d522f475Smrg#if OPT_TEK4014
3620d522f475Smrg    case OSC_TEK_FG:
3621d522f475Smrg    case OSC_TEK_BG:
3622d522f475Smrg    case OSC_TEK_CURSOR:
3623d522f475Smrg#endif
3624cd3331d0Smrg	if (xw->misc.dynamicColors) {
3625d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
3626cd3331d0Smrg	}
3627cd3331d0Smrg	break;
3628cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3629cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3630cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3631cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3632cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3633cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3634cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3635cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3636cd3331d0Smrg#endif
3637cd3331d0Smrg#if OPT_TEK4014
3638cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3639cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3640cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3641cd3331d0Smrg#endif
3642cd3331d0Smrg	if (xw->misc.dynamicColors) {
3643cd3331d0Smrg	    ResetColorsRequest(xw, mode);
3644cd3331d0Smrg	}
3645d522f475Smrg	break;
3646d522f475Smrg
3647d522f475Smrg    case 30:
3648d522f475Smrg    case 31:
3649d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
3650d522f475Smrg	break;
3651d522f475Smrg
3652d522f475Smrg#ifdef ALLOWLOGGING
3653d522f475Smrg    case 46:			/* new log file */
3654d522f475Smrg#ifdef ALLOWLOGFILECHANGES
3655d522f475Smrg	/*
3656d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
3657d522f475Smrg	 * arbitrary files accessible to the person running xterm.
3658d522f475Smrg	 */
3659cd3331d0Smrg	if (strcmp(buf, "?")
3660d522f475Smrg	    && (cp = CastMallocN(char, strlen(buf)) != NULL)) {
3661d522f475Smrg	    strcpy(cp, buf);
3662d522f475Smrg	    if (screen->logfile)
3663d522f475Smrg		free(screen->logfile);
3664d522f475Smrg	    screen->logfile = cp;
3665d522f475Smrg	    break;
3666d522f475Smrg	}
3667d522f475Smrg#endif
3668cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3669cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3670d522f475Smrg	break;
3671d522f475Smrg#endif /* ALLOWLOGGING */
3672d522f475Smrg
3673d522f475Smrg    case 50:
3674d522f475Smrg#if OPT_SHIFT_FONTS
3675cd3331d0Smrg	if (*buf == '?') {
3676cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
3677cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
3678cd3331d0Smrg	    ChangeFontRequest(xw, buf);
3679d522f475Smrg	}
3680d522f475Smrg#endif /* OPT_SHIFT_FONTS */
3681d522f475Smrg	break;
3682d522f475Smrg    case 51:
3683d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
3684d522f475Smrg	break;
3685d522f475Smrg
3686d522f475Smrg#if OPT_PASTE64
3687d522f475Smrg    case 52:
3688cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
3689d522f475Smrg	break;
3690d522f475Smrg#endif
3691d522f475Smrg	/*
3692d522f475Smrg	 * One could write code to send back the display and host names,
3693d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
3694d522f475Smrg	 */
3695cd3331d0Smrg    default:
3696cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
3697cd3331d0Smrg	break;
3698d522f475Smrg    }
3699d522f475Smrg    unparse_end(xw);
3700d522f475Smrg}
3701d522f475Smrg
3702d522f475Smrg/*
3703d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
3704d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
3705d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
3706d522f475Smrg * "real" terminals accept commas in the string definitions).
3707d522f475Smrg */
3708d522f475Smrgstatic int
3709cd3331d0Smrgudk_value(const char **cp)
3710d522f475Smrg{
3711cd3331d0Smrg    int result = -1;
3712d522f475Smrg    int c;
3713d522f475Smrg
3714d522f475Smrg    for (;;) {
3715d522f475Smrg	if ((c = **cp) != '\0')
3716d522f475Smrg	    *cp = *cp + 1;
3717d522f475Smrg	if (c == ';' || c == '\0')
3718cd3331d0Smrg	    break;
3719cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
3720cd3331d0Smrg	    break;
3721d522f475Smrg    }
3722cd3331d0Smrg
3723cd3331d0Smrg    return result;
3724d522f475Smrg}
3725d522f475Smrg
3726d522f475Smrgvoid
37279a64e1c5Smrgreset_decudk(XtermWidget xw)
3728d522f475Smrg{
3729d522f475Smrg    int n;
3730d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
37319a64e1c5Smrg	if (xw->work.user_keys[n].str != 0) {
37329a64e1c5Smrg	    free(xw->work.user_keys[n].str);
37339a64e1c5Smrg	    xw->work.user_keys[n].str = 0;
37349a64e1c5Smrg	    xw->work.user_keys[n].len = 0;
3735d522f475Smrg	}
3736d522f475Smrg    }
3737d522f475Smrg}
3738d522f475Smrg
3739d522f475Smrg/*
3740d522f475Smrg * Parse the data for DECUDK (user-defined keys).
3741d522f475Smrg */
3742d522f475Smrgstatic void
37439a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
3744d522f475Smrg{
3745d522f475Smrg    while (*cp) {
3746cd3331d0Smrg	const char *base = cp;
37473367019cSmrg	char *str = CastMallocN(char, strlen(cp) + 2);
3748d522f475Smrg	unsigned key = 0;
3749d522f475Smrg	int lo, hi;
3750d522f475Smrg	int len = 0;
3751d522f475Smrg
3752d522f475Smrg	while (isdigit(CharOf(*cp)))
37530d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
3754d522f475Smrg	if (*cp == '/') {
3755d522f475Smrg	    cp++;
3756d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
3757d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
37580d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
3759d522f475Smrg	    }
3760d522f475Smrg	}
3761d522f475Smrg	if (len > 0 && key < MAX_UDK) {
37623367019cSmrg	    str[len] = '\0';
37639a64e1c5Smrg	    if (xw->work.user_keys[key].str != 0)
37649a64e1c5Smrg		free(xw->work.user_keys[key].str);
37659a64e1c5Smrg	    xw->work.user_keys[key].str = str;
37669a64e1c5Smrg	    xw->work.user_keys[key].len = len;
3767d522f475Smrg	} else {
3768d522f475Smrg	    free(str);
3769d522f475Smrg	}
3770d522f475Smrg	if (*cp == ';')
3771d522f475Smrg	    cp++;
3772d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
3773d522f475Smrg	    break;
3774d522f475Smrg    }
3775d522f475Smrg}
3776d522f475Smrg
3777fa3f02f3Smrg/*
3778fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
3779fa3f02f3Smrg * interspersing with control characters, but have the string already.
3780fa3f02f3Smrg */
3781fa3f02f3Smrgstatic void
3782fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
3783fa3f02f3Smrg{
3784fa3f02f3Smrg    const char *cp = *string;
3785fa3f02f3Smrg    ParmType nparam = 0;
3786fa3f02f3Smrg    int last_empty = 1;
3787fa3f02f3Smrg
3788fa3f02f3Smrg    memset(params, 0, sizeof(*params));
3789fa3f02f3Smrg    while (*cp != '\0') {
3790fa3f02f3Smrg	Char ch = CharOf(*cp++);
3791fa3f02f3Smrg
3792fa3f02f3Smrg	if (isdigit(ch)) {
3793fa3f02f3Smrg	    last_empty = 0;
3794fa3f02f3Smrg	    if (nparam < NPARAM) {
3795fa3f02f3Smrg		params->a_param[nparam] =
3796fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
3797fa3f02f3Smrg				+ (ch - '0'));
3798fa3f02f3Smrg	    }
3799fa3f02f3Smrg	} else if (ch == ';') {
3800fa3f02f3Smrg	    last_empty = 1;
3801fa3f02f3Smrg	    nparam++;
3802fa3f02f3Smrg	} else if (ch < 32) {
3803fa3f02f3Smrg	    /* EMPTY */ ;
3804fa3f02f3Smrg	} else {
3805fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
3806fa3f02f3Smrg	    params->a_final = ch;
3807fa3f02f3Smrg	    break;
3808fa3f02f3Smrg	}
3809fa3f02f3Smrg    }
3810fa3f02f3Smrg
3811fa3f02f3Smrg    *string = cp;
3812fa3f02f3Smrg    if (!last_empty)
3813fa3f02f3Smrg	nparam++;
3814fa3f02f3Smrg    if (nparam > NPARAM)
3815fa3f02f3Smrg	params->a_nparam = NPARAM;
3816fa3f02f3Smrg    else
3817fa3f02f3Smrg	params->a_nparam = nparam;
3818fa3f02f3Smrg}
3819fa3f02f3Smrg
3820d522f475Smrg#if OPT_TRACE
3821d522f475Smrg#define SOFT_WIDE 10
3822d522f475Smrg#define SOFT_HIGH 20
3823d522f475Smrg
3824d522f475Smrgstatic void
3825fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
3826d522f475Smrg{
3827d522f475Smrg    char DscsName[8];
3828d522f475Smrg    int len;
3829d522f475Smrg    int Pfn = params->a_param[0];
3830d522f475Smrg    int Pcn = params->a_param[1];
3831d522f475Smrg    int Pe = params->a_param[2];
3832d522f475Smrg    int Pcmw = params->a_param[3];
3833d522f475Smrg    int Pw = params->a_param[4];
3834d522f475Smrg    int Pt = params->a_param[5];
3835d522f475Smrg    int Pcmh = params->a_param[6];
3836d522f475Smrg    int Pcss = params->a_param[7];
3837d522f475Smrg
3838d522f475Smrg    int start_char = Pcn + 0x20;
3839d522f475Smrg    int char_wide = ((Pcmw == 0)
3840d522f475Smrg		     ? (Pcss ? 6 : 10)
3841d522f475Smrg		     : (Pcmw > 4
3842d522f475Smrg			? Pcmw
3843d522f475Smrg			: (Pcmw + 3)));
3844d522f475Smrg    int char_high = ((Pcmh == 0)
38453367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
3846d522f475Smrg			? 10
3847d522f475Smrg			: 20)
3848d522f475Smrg		     : Pcmh);
3849d522f475Smrg    Char ch;
3850d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
3851d522f475Smrg    Bool first = True;
3852d522f475Smrg    Bool prior = False;
3853d522f475Smrg    int row = 0, col = 0;
3854d522f475Smrg
3855d522f475Smrg    TRACE(("Parsing DECDLD\n"));
3856d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
3857d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
3858d522f475Smrg    TRACE(("  erase control %d\n", Pe));
3859d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
3860d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
3861d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
3862d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
3863d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
3864d522f475Smrg
3865d522f475Smrg    if (Pfn > 1
3866d522f475Smrg	|| Pcn > 95
3867d522f475Smrg	|| Pe > 2
3868d522f475Smrg	|| Pcmw > 10
3869d522f475Smrg	|| Pcmw == 1
3870d522f475Smrg	|| Pt > 2
3871d522f475Smrg	|| Pcmh > 20
3872d522f475Smrg	|| Pcss > 1
3873d522f475Smrg	|| char_wide > SOFT_WIDE
3874d522f475Smrg	|| char_high > SOFT_HIGH) {
3875d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
3876d522f475Smrg	return;
3877d522f475Smrg    }
3878d522f475Smrg
3879d522f475Smrg    len = 0;
3880d522f475Smrg    while (*string != '\0') {
3881d522f475Smrg	ch = CharOf(*string++);
3882d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
3883d522f475Smrg	    if (len < 2)
3884b7c89284Ssnj		DscsName[len++] = (char) ch;
3885d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
3886b7c89284Ssnj	    DscsName[len++] = (char) ch;
3887d522f475Smrg	    break;
3888d522f475Smrg	}
3889d522f475Smrg    }
3890d522f475Smrg    DscsName[len] = 0;
3891d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
3892d522f475Smrg
3893d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
3894d522f475Smrg    while (*string != '\0') {
3895d522f475Smrg	if (first) {
3896d522f475Smrg	    TRACE(("Char %d:\n", start_char));
3897d522f475Smrg	    if (prior) {
3898d522f475Smrg		for (row = 0; row < char_high; ++row) {
3899d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
3900d522f475Smrg		}
3901d522f475Smrg	    }
3902d522f475Smrg	    prior = False;
3903d522f475Smrg	    first = False;
3904d522f475Smrg	    for (row = 0; row < char_high; ++row) {
3905d522f475Smrg		for (col = 0; col < char_wide; ++col) {
3906d522f475Smrg		    bits[row][col] = '.';
3907d522f475Smrg		}
3908d522f475Smrg	    }
3909d522f475Smrg	    row = col = 0;
3910d522f475Smrg	}
3911d522f475Smrg	ch = CharOf(*string++);
3912d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
3913d522f475Smrg	    int n;
3914d522f475Smrg
3915b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
3916d522f475Smrg	    for (n = 0; n < 6; ++n) {
3917b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
3918d522f475Smrg	    }
3919d522f475Smrg	    col += 1;
3920d522f475Smrg	    prior = True;
3921d522f475Smrg	} else if (ch == '/') {
3922d522f475Smrg	    row += 6;
3923d522f475Smrg	    col = 0;
3924d522f475Smrg	} else if (ch == ';') {
3925d522f475Smrg	    first = True;
3926d522f475Smrg	    ++start_char;
3927d522f475Smrg	}
3928d522f475Smrg    }
3929d522f475Smrg}
3930d522f475Smrg#else
3931d522f475Smrg#define parse_decdld(p,q)	/* nothing */
3932d522f475Smrg#endif
3933d522f475Smrg
3934d522f475Smrgvoid
3935fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
3936d522f475Smrg{
3937cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3938d522f475Smrg    char reply[BUFSIZ];
3939cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
3940d522f475Smrg    Bool okay;
3941d522f475Smrg    ANSI params;
3942d522f475Smrg
3943cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
3944d522f475Smrg
3945d522f475Smrg    if (dcslen != strlen(cp))
3946d522f475Smrg	/* shouldn't have nulls in the string */
3947d522f475Smrg	return;
3948d522f475Smrg
3949d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
3950d522f475Smrg    case '$':			/* DECRQSS */
3951d522f475Smrg	okay = True;
3952d522f475Smrg
3953d522f475Smrg	cp++;
3954d522f475Smrg	if (*cp++ == 'q') {
3955d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
3956d522f475Smrg		sprintf(reply, "%d%s",
3957d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
3958d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
3959d522f475Smrg			cp);
3960d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
39613367019cSmrg		if (screen->vtXX_level < 2) {
39623367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
39633367019cSmrg		    break;
39643367019cSmrg		}
3965d522f475Smrg		sprintf(reply, "%d%s%s",
3966d522f475Smrg			(screen->vtXX_level ?
3967d522f475Smrg			 screen->vtXX_level : 1) + 60,
3968d522f475Smrg			(screen->vtXX_level >= 2)
3969d522f475Smrg			? (screen->control_eight_bits
3970d522f475Smrg			   ? ";0" : ";1")
3971d522f475Smrg			: "",
3972d522f475Smrg			cp);
3973d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
3974d522f475Smrg		sprintf(reply, "%d;%dr",
3975d522f475Smrg			screen->top_marg + 1,
3976d522f475Smrg			screen->bot_marg + 1);
39773367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
39783367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
39793367019cSmrg		    sprintf(reply, "%d;%ds",
39803367019cSmrg			    screen->lft_marg + 1,
39813367019cSmrg			    screen->rgt_marg + 1);
39823367019cSmrg		}
3983d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
3984d522f475Smrg		strcpy(reply, "0");
3985d522f475Smrg		if (xw->flags & BOLD)
3986d522f475Smrg		    strcat(reply, ";1");
3987d522f475Smrg		if (xw->flags & UNDERLINE)
3988d522f475Smrg		    strcat(reply, ";4");
3989d522f475Smrg		if (xw->flags & BLINK)
3990d522f475Smrg		    strcat(reply, ";5");
3991d522f475Smrg		if (xw->flags & INVERSE)
3992d522f475Smrg		    strcat(reply, ";7");
3993d522f475Smrg		if (xw->flags & INVISIBLE)
3994d522f475Smrg		    strcat(reply, ";8");
3995b7c89284Ssnj#if OPT_256_COLORS || OPT_88_COLORS
3996b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
3997d522f475Smrg		    if (xw->flags & FG_COLOR) {
3998d522f475Smrg			if (xw->cur_foreground >= 16)
3999d522f475Smrg			    sprintf(reply + strlen(reply),
4000d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
4001d522f475Smrg			else
4002d522f475Smrg			    sprintf(reply + strlen(reply),
4003d522f475Smrg				    ";%d%d",
4004d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
4005d522f475Smrg				    xw->cur_foreground >= 8 ?
4006d522f475Smrg				    xw->cur_foreground - 8 :
4007d522f475Smrg				    xw->cur_foreground);
4008d522f475Smrg		    }
4009d522f475Smrg		    if (xw->flags & BG_COLOR) {
4010d522f475Smrg			if (xw->cur_background >= 16)
4011d522f475Smrg			    sprintf(reply + strlen(reply),
4012d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
4013d522f475Smrg			else
4014d522f475Smrg			    sprintf(reply + strlen(reply),
4015d522f475Smrg				    ";%d%d",
4016d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
4017d522f475Smrg				    xw->cur_background >= 8 ?
4018d522f475Smrg				    xw->cur_background - 8 :
4019d522f475Smrg				    xw->cur_background);
4020d522f475Smrg		    }
4021d522f475Smrg		});
4022b7c89284Ssnj#elif OPT_ISO_COLORS
4023b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4024d522f475Smrg		    if (xw->flags & FG_COLOR)
4025d522f475Smrg			sprintf(reply + strlen(reply),
4026d522f475Smrg				";%d%d",
4027d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
4028d522f475Smrg				xw->cur_foreground >= 8 ?
4029d522f475Smrg				xw->cur_foreground - 8 :
4030d522f475Smrg				xw->cur_foreground);
4031d522f475Smrg		    if (xw->flags & BG_COLOR)
4032d522f475Smrg			sprintf(reply + strlen(reply),
4033d522f475Smrg				";%d%d",
4034d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
4035d522f475Smrg				xw->cur_background >= 8 ?
4036d522f475Smrg				xw->cur_background - 8 :
4037d522f475Smrg				xw->cur_background);
4038d522f475Smrg		});
4039b7c89284Ssnj#endif
4040d522f475Smrg		strcat(reply, "m");
4041712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
40423367019cSmrg		int code = STEADY_BLOCK;
40433367019cSmrg		if (isCursorUnderline(screen))
40443367019cSmrg		    code = STEADY_UNDERLINE;
40453367019cSmrg		else if (isCursorBar(screen))
40463367019cSmrg		    code = STEADY_BAR;
40473367019cSmrg#if OPT_BLINK_CURS
40483367019cSmrg		if (screen->cursor_blink_esc == 0)
40493367019cSmrg		    code -= 1;
40503367019cSmrg#endif
40513367019cSmrg		sprintf(reply, "%d%s", code, cp);
4052d522f475Smrg	    } else
4053d522f475Smrg		okay = False;
4054d522f475Smrg
405522d8e007Schristos	    if (okay) {
40560d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
40573367019cSmrg		unparseputc(xw, '1');
40580d92cbfdSchristos		unparseputc(xw, '$');
40590d92cbfdSchristos		unparseputc(xw, 'r');
4060d522f475Smrg		cp = reply;
406122d8e007Schristos		unparseputs(xw, cp);
40620d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
40630d92cbfdSchristos	    } else {
40640d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
406522d8e007Schristos	    }
4066d522f475Smrg	} else {
4067d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4068d522f475Smrg	}
4069d522f475Smrg	break;
4070d522f475Smrg#if OPT_TCAP_QUERY
4071d522f475Smrg    case '+':
4072d522f475Smrg	cp++;
4073cd3331d0Smrg	switch (*cp) {
4074cd3331d0Smrg	case 'p':
4075cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4076cd3331d0Smrg		set_termcap(xw, cp + 1);
4077cd3331d0Smrg	    }
4078cd3331d0Smrg	    break;
4079cd3331d0Smrg	case 'q':
4080cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4081cd3331d0Smrg		Bool fkey;
4082cd3331d0Smrg		unsigned state;
4083cd3331d0Smrg		int code;
4084cd3331d0Smrg		const char *tmp;
4085cd3331d0Smrg		const char *parsed = ++cp;
4086d522f475Smrg
4087cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4088d522f475Smrg
4089cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4090b7c89284Ssnj
4091cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4092d522f475Smrg
4093cd3331d0Smrg		unparseputc(xw, '+');
4094cd3331d0Smrg		unparseputc(xw, 'r');
4095d522f475Smrg
4096cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4097cd3331d0Smrg		    if (cp == parsed)
4098cd3331d0Smrg			break;	/* no data found, error */
4099d522f475Smrg
4100cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4101cd3331d0Smrg			unparseputc(xw, *tmp);
4102d522f475Smrg
4103cd3331d0Smrg		    if (code >= 0) {
4104cd3331d0Smrg			unparseputc(xw, '=');
4105cd3331d0Smrg			screen->tc_query_code = code;
4106cd3331d0Smrg			screen->tc_query_fkey = fkey;
4107d522f475Smrg#if OPT_ISO_COLORS
4108cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4109cd3331d0Smrg			 * number of colors) */
4110cd3331d0Smrg			if (code == XK_COLORS) {
4111cd3331d0Smrg			    unparseputn(xw, NUM_ANSI_COLORS);
4112cd3331d0Smrg			} else
4113cd3331d0Smrg#endif
4114cd3331d0Smrg			if (code == XK_TCAPNAME) {
4115c219fbebSmrg			    unparseputs(xw, resource.term_name);
4116cd3331d0Smrg			} else {
4117cd3331d0Smrg			    XKeyEvent event;
4118cd3331d0Smrg			    event.state = state;
4119cd3331d0Smrg			    Input(xw, &event, False);
4120cd3331d0Smrg			}
4121cd3331d0Smrg			screen->tc_query_code = -1;
4122cd3331d0Smrg		    } else {
4123cd3331d0Smrg			break;	/* no match found, error */
4124d522f475Smrg		    }
4125d522f475Smrg
4126d522f475Smrg		    cp = parsed;
4127cd3331d0Smrg		    if (*parsed == ';') {
4128cd3331d0Smrg			unparseputc(xw, *parsed++);
4129cd3331d0Smrg			cp = parsed;
4130cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4131cd3331d0Smrg		    }
4132d522f475Smrg		}
4133cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4134d522f475Smrg	    }
4135cd3331d0Smrg	    break;
4136d522f475Smrg	}
4137d522f475Smrg	break;
4138d522f475Smrg#endif
4139d522f475Smrg    default:
4140fa3f02f3Smrg	if (screen->terminal_id == 125 ||
4141fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
41420d92cbfdSchristos	    parse_ansi_params(&params, &cp);
41430d92cbfdSchristos	    switch (params.a_final) {
4144fa3f02f3Smrg	    case 'p':
41459a64e1c5Smrg#if OPT_REGIS_GRAPHICS
4146fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4147fa3f02f3Smrg		    screen->terminal_id == 240 ||
4148fa3f02f3Smrg		    screen->terminal_id == 241 ||
4149fa3f02f3Smrg		    screen->terminal_id == 330 ||
4150fa3f02f3Smrg		    screen->terminal_id == 340) {
4151fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4152fa3f02f3Smrg		}
41539a64e1c5Smrg#else
41549a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
41559a64e1c5Smrg#endif
4156fa3f02f3Smrg		break;
4157fa3f02f3Smrg	    case 'q':
41589a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
4159fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4160fa3f02f3Smrg		    screen->terminal_id == 240 ||
4161fa3f02f3Smrg		    screen->terminal_id == 241 ||
4162fa3f02f3Smrg		    screen->terminal_id == 330 ||
41639a64e1c5Smrg		    screen->terminal_id == 340 ||
41649a64e1c5Smrg		    screen->terminal_id == 382) {
4165fa3f02f3Smrg		    parse_sixel(xw, &params, cp);
4166fa3f02f3Smrg		}
41679a64e1c5Smrg#else
41689a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
4169fa3f02f3Smrg#endif
41709a64e1c5Smrg		break;
41710d92cbfdSchristos	    case '|':		/* DECUDK */
41729a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
41739a64e1c5Smrg		    if (params.a_param[0] == 0)
41749a64e1c5Smrg			reset_decudk(xw);
41759a64e1c5Smrg		    parse_decudk(xw, cp);
41769a64e1c5Smrg		}
41770d92cbfdSchristos		break;
41780d92cbfdSchristos	    case '{':		/* DECDLD (no '}' case though) */
41799a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
41809a64e1c5Smrg		    parse_decdld(&params, cp);
41819a64e1c5Smrg		}
41820d92cbfdSchristos		break;
41830d92cbfdSchristos	    }
4184d522f475Smrg	}
4185d522f475Smrg	break;
4186d522f475Smrg    }
4187d522f475Smrg    unparse_end(xw);
4188d522f475Smrg}
4189d522f475Smrg
4190cb4a1343Smrg#if OPT_DEC_RECTOPS
4191cb4a1343Smrgenum {
4192cb4a1343Smrg    mdUnknown = 0,
4193cb4a1343Smrg    mdMaybeSet = 1,
4194cb4a1343Smrg    mdMaybeReset = 2,
4195cb4a1343Smrg    mdAlwaysSet = 3,
4196cb4a1343Smrg    mdAlwaysReset = 4
4197cb4a1343Smrg};
4198cb4a1343Smrg
4199cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
42003367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
4201cb4a1343Smrg
4202cb4a1343Smrg/*
4203cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
4204cb4a1343Smrg * 0 - not recognized
4205cb4a1343Smrg * 1 - set
4206cb4a1343Smrg * 2 - reset
4207cb4a1343Smrg * 3 - permanently set
4208cb4a1343Smrg * 4 - permanently reset
4209cb4a1343Smrg * Only one mode can be reported at a time.
4210cb4a1343Smrg */
4211cb4a1343Smrgvoid
4212cb4a1343Smrgdo_rpm(XtermWidget xw, int nparams, int *params)
4213cb4a1343Smrg{
4214cb4a1343Smrg    ANSI reply;
4215cb4a1343Smrg    int result = 0;
4216cb4a1343Smrg    int count = 0;
4217cb4a1343Smrg
4218cb4a1343Smrg    TRACE(("do_rpm %d:%d\n", nparams, params[0]));
4219cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4220cb4a1343Smrg    if (nparams >= 1) {
4221cb4a1343Smrg	switch (params[0]) {
4222cb4a1343Smrg	case 1:		/* GATM */
4223cb4a1343Smrg	    result = mdAlwaysReset;
4224cb4a1343Smrg	    break;
4225cb4a1343Smrg	case 2:
4226cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
4227cb4a1343Smrg	    break;
4228cb4a1343Smrg	case 3:		/* CRM */
4229cb4a1343Smrg	    result = mdMaybeReset;
4230cb4a1343Smrg	    break;
4231cb4a1343Smrg	case 4:
4232cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
4233cb4a1343Smrg	    break;
4234cb4a1343Smrg	case 5:		/* SRTM */
4235cb4a1343Smrg	case 7:		/* VEM */
4236cb4a1343Smrg	case 10:		/* HEM */
4237cb4a1343Smrg	case 11:		/* PUM */
4238cb4a1343Smrg	    result = mdAlwaysReset;
4239cb4a1343Smrg	    break;
4240cb4a1343Smrg	case 12:
4241cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
4242cb4a1343Smrg	    break;
4243cb4a1343Smrg	case 13:		/* FEAM */
4244cb4a1343Smrg	case 14:		/* FETM */
4245cb4a1343Smrg	case 15:		/* MATM */
4246cb4a1343Smrg	case 16:		/* TTM */
4247cb4a1343Smrg	case 17:		/* SATM */
4248cb4a1343Smrg	case 18:		/* TSM */
4249cb4a1343Smrg	case 19:		/* EBM */
4250cb4a1343Smrg	    result = mdAlwaysReset;
4251cb4a1343Smrg	    break;
4252cb4a1343Smrg	case 20:
4253cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
4254cb4a1343Smrg	    break;
4255cb4a1343Smrg	}
4256cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4257cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4258cb4a1343Smrg    }
4259cb4a1343Smrg    reply.a_type = ANSI_CSI;
4260cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4261cb4a1343Smrg    reply.a_inters = '$';
4262cb4a1343Smrg    reply.a_final = 'y';
4263cb4a1343Smrg    unparseseq(xw, &reply);
4264cb4a1343Smrg}
4265cb4a1343Smrg
4266cb4a1343Smrgvoid
4267cb4a1343Smrgdo_decrpm(XtermWidget xw, int nparams, int *params)
4268cb4a1343Smrg{
4269cb4a1343Smrg    ANSI reply;
4270cb4a1343Smrg    int result = 0;
4271cb4a1343Smrg    int count = 0;
4272cb4a1343Smrg
4273cb4a1343Smrg    TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
4274cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4275cb4a1343Smrg    if (nparams >= 1) {
4276cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
4277cb4a1343Smrg
4278cb4a1343Smrg	switch (params[0]) {
4279fa3f02f3Smrg	case srm_DECCKM:
4280cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
4281cb4a1343Smrg	    break;
4282fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
4283cb4a1343Smrg#if OPT_VT52_MODE
42843367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
4285cb4a1343Smrg#else
4286cb4a1343Smrg	    result = mdMaybeSet;
4287cb4a1343Smrg#endif
4288cb4a1343Smrg	    break;
4289fa3f02f3Smrg	case srm_DECCOLM:
4290cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
4291cb4a1343Smrg	    break;
4292fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
4293cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
4294cb4a1343Smrg	    break;
4295fa3f02f3Smrg	case srm_DECSCNM:
4296cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
4297cb4a1343Smrg	    break;
4298fa3f02f3Smrg	case srm_DECOM:
4299cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
4300cb4a1343Smrg	    break;
4301fa3f02f3Smrg	case srm_DECAWM:
4302cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
4303cb4a1343Smrg	    break;
4304fa3f02f3Smrg	case srm_DECARM:
4305cb4a1343Smrg	    result = mdAlwaysReset;
4306cb4a1343Smrg	    break;
4307fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
4308cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
4309cb4a1343Smrg	    break;
4310cb4a1343Smrg#if OPT_TOOLBAR
4311fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
4312cb4a1343Smrg	    result = MdBool(resource.toolBar);
4313cb4a1343Smrg	    break;
4314cb4a1343Smrg#endif
4315cb4a1343Smrg#if OPT_BLINK_CURS
4316fa3f02f3Smrg	case srm_ATT610_BLINK:	/* att610: Start/stop blinking cursor */
4317cb4a1343Smrg	    result = MdBool(screen->cursor_blink_res);
4318cb4a1343Smrg	    break;
4319cb4a1343Smrg#endif
4320fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
4321712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
4322cb4a1343Smrg	    break;
4323fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
4324712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
4325cb4a1343Smrg	    break;
4326fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
4327cb4a1343Smrg	    result = MdBool(screen->cursor_set);
4328cb4a1343Smrg	    break;
4329fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
4330cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
4331cb4a1343Smrg	    break;
4332cb4a1343Smrg#if OPT_SHIFT_FONTS
4333fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
4334cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
4335cb4a1343Smrg	    break;
4336cb4a1343Smrg#endif
4337cb4a1343Smrg#if OPT_TEK4014
4338fa3f02f3Smrg	case srm_DECTEK:
4339cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
4340cb4a1343Smrg	    break;
4341cb4a1343Smrg#endif
4342fa3f02f3Smrg	case srm_132COLS:
4343cb4a1343Smrg	    result = MdBool(screen->c132);
4344cb4a1343Smrg	    break;
4345fa3f02f3Smrg	case srm_CURSES_HACK:
4346cb4a1343Smrg	    result = MdBool(screen->curses);
4347cb4a1343Smrg	    break;
4348fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
4349cb4a1343Smrg	    result = MdFlag(xw->flags, NATIONAL);
4350cb4a1343Smrg	    break;
4351fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
4352cb4a1343Smrg	    result = MdBool(screen->marginbell);
4353cb4a1343Smrg	    break;
4354fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
4355cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSEWRAP);
4356cb4a1343Smrg	    break;
4357cb4a1343Smrg#ifdef ALLOWLOGGING
4358fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
4359cb4a1343Smrg#ifdef ALLOWLOGFILEONOFF
4360cb4a1343Smrg	    result = MdBool(screen->logging);
4361cb4a1343Smrg#endif /* ALLOWLOGFILEONOFF */
4362cb4a1343Smrg	    break;
4363cb4a1343Smrg#endif
4364fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
4365cb4a1343Smrg	    /* FALLTHRU */
4366fa3f02f3Smrg	case srm_OPT_ALTBUF:
4367cb4a1343Smrg	    /* FALLTHRU */
4368fa3f02f3Smrg	case srm_ALTBUF:
4369cb4a1343Smrg	    result = MdBool(screen->whichBuf);
4370cb4a1343Smrg	    break;
4371fa3f02f3Smrg	case srm_DECNKM:
4372cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
4373cb4a1343Smrg	    break;
4374fa3f02f3Smrg	case srm_DECBKM:
4375cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
4376cb4a1343Smrg	    break;
4377fa3f02f3Smrg	case srm_DECLRMM:
43783367019cSmrg	    result = MdFlag(xw->flags, LEFT_RIGHT);
43793367019cSmrg	    break;
4380fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
4381fa3f02f3Smrg	case srm_DECSDM:
4382fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
4383fa3f02f3Smrg	    break;
4384fa3f02f3Smrg#endif
4385fa3f02f3Smrg	case srm_DECNCSM:
43863367019cSmrg	    result = MdFlag(xw->flags, NOCLEAR_COLM);
43873367019cSmrg	    break;
4388fa3f02f3Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence         */
4389cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
4390cb4a1343Smrg	    break;
4391fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
4392cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
4393cb4a1343Smrg	    break;
4394fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
4395cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
4396cb4a1343Smrg	    break;
4397fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
4398cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
4399cb4a1343Smrg	    break;
4400cb4a1343Smrg#if OPT_FOCUS_EVENT
4401fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
4402cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
4403cb4a1343Smrg	    break;
4404cb4a1343Smrg#endif
4405fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
44063367019cSmrg	    /* FALLTHRU */
4407fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
44083367019cSmrg	    /* FALLTHRU */
4409fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
44103367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
44113367019cSmrg	    break;
4412fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
44133367019cSmrg	    result = MdBool(screen->alternateScroll);
4414cb4a1343Smrg	    break;
4415fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
4416cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
4417cb4a1343Smrg	    break;
4418fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
4419cb4a1343Smrg	    result = MdBool(screen->scrollkey);
4420cb4a1343Smrg	    break;
4421fa3f02f3Smrg	case srm_EIGHT_BIT_META:
44223367019cSmrg	    result = MdBool(screen->eight_bit_meta);
4423cb4a1343Smrg	    break;
4424cb4a1343Smrg#if OPT_NUM_LOCK
4425fa3f02f3Smrg	case srm_REAL_NUMLOCK:
4426cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
4427cb4a1343Smrg	    break;
4428fa3f02f3Smrg	case srm_META_SENDS_ESC:
4429cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
4430cb4a1343Smrg	    break;
4431cb4a1343Smrg#endif
4432fa3f02f3Smrg	case srm_DELETE_IS_DEL:
4433cb4a1343Smrg	    result = MdBool(screen->delete_is_del);
4434cb4a1343Smrg	    break;
4435cb4a1343Smrg#if OPT_NUM_LOCK
4436fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
4437cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
4438cb4a1343Smrg	    break;
4439cb4a1343Smrg#endif
4440fa3f02f3Smrg	case srm_KEEP_SELECTION:
4441cb4a1343Smrg	    result = MdBool(screen->keepSelection);
4442cb4a1343Smrg	    break;
4443fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
4444cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
4445cb4a1343Smrg	    break;
4446fa3f02f3Smrg	case srm_BELL_IS_URGENT:
4447cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
4448cb4a1343Smrg	    break;
4449fa3f02f3Smrg	case srm_POP_ON_BELL:
4450cb4a1343Smrg	    result = MdBool(screen->poponbell);
4451cb4a1343Smrg	    break;
4452fa3f02f3Smrg	case srm_TITE_INHIBIT:
4453cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
4454cb4a1343Smrg	    break;
4455cb4a1343Smrg#if OPT_TCAP_FKEYS
4456fa3f02f3Smrg	case srm_TCAP_FKEYS:
4457cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
4458cb4a1343Smrg	    break;
4459cb4a1343Smrg#endif
4460cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
4461fa3f02f3Smrg	case srm_SUN_FKEYS:
4462cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
4463cb4a1343Smrg	    break;
4464cb4a1343Smrg#endif
4465cb4a1343Smrg#if OPT_HP_FUNC_KEYS
4466fa3f02f3Smrg	case srm_HP_FKEYS:
4467cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
4468cb4a1343Smrg	    break;
4469cb4a1343Smrg#endif
4470cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
4471fa3f02f3Smrg	case srm_SCO_FKEYS:
4472cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
4473cb4a1343Smrg	    break;
4474cb4a1343Smrg#endif
4475fa3f02f3Smrg	case srm_LEGACY_FKEYS:
4476cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
4477cb4a1343Smrg	    break;
4478cb4a1343Smrg#if OPT_SUNPC_KBD
4479fa3f02f3Smrg	case srm_VT220_FKEYS:
4480cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
4481cb4a1343Smrg	    break;
4482cb4a1343Smrg#endif
4483cb4a1343Smrg#if OPT_READLINE
4484fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
4485cb4a1343Smrg	    result = MdBool(screen->click1_moves);
4486cb4a1343Smrg	    break;
4487fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
4488cb4a1343Smrg	    result = MdBool(screen->paste_moves);
4489cb4a1343Smrg	    break;
4490fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
4491cb4a1343Smrg	    result = MdBool(screen->dclick3_deletes);
4492cb4a1343Smrg	    break;
4493fa3f02f3Smrg	case srm_PASTE_IN_BRACKET:
4494cb4a1343Smrg	    result = MdBool(screen->paste_brackets);
4495cb4a1343Smrg	    break;
4496fa3f02f3Smrg	case srm_PASTE_QUOTE:
4497cb4a1343Smrg	    result = MdBool(screen->paste_quotes);
4498cb4a1343Smrg	    break;
4499fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
4500cb4a1343Smrg	    result = MdBool(screen->paste_literal_nl);
4501cb4a1343Smrg	    break;
4502cb4a1343Smrg#endif /* OPT_READLINE */
45039a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
45049a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
45059a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
45069a64e1c5Smrg	    break;
45079a64e1c5Smrg#endif
45089a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
45099a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
45109a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
45119a64e1c5Smrg	    break;
45129a64e1c5Smrg#endif
45139a64e1c5Smrg	default:
45149a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
45159a64e1c5Smrg		   params[0]));
4516cb4a1343Smrg	}
4517cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4518cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4519cb4a1343Smrg    }
4520cb4a1343Smrg    reply.a_type = ANSI_CSI;
4521cb4a1343Smrg    reply.a_pintro = '?';
4522cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4523cb4a1343Smrg    reply.a_inters = '$';
4524cb4a1343Smrg    reply.a_final = 'y';
4525cb4a1343Smrg    unparseseq(xw, &reply);
4526cb4a1343Smrg}
4527cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
4528cb4a1343Smrg
4529d522f475Smrgchar *
45309a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
4531d522f475Smrg{
4532d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
45339a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
45349a64e1c5Smrg	return xw->work.user_keys[keycode].str;
4535d522f475Smrg    }
4536d522f475Smrg    return 0;
4537d522f475Smrg}
4538d522f475Smrg
45393367019cSmrg#ifdef HAVE_LIBXPM
45403367019cSmrg
45413367019cSmrg#ifndef PIXMAP_ROOTDIR
45423367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
45433367019cSmrg#endif
45443367019cSmrg
45453367019cSmrgtypedef struct {
45463367019cSmrg    const char *name;
45473367019cSmrg    const char *const *data;
45483367019cSmrg} XPM_DATA;
45493367019cSmrg
45503367019cSmrgstatic char *
45513367019cSmrgx_find_icon(char **work, int *state, const char *suffix)
45523367019cSmrg{
45533367019cSmrg    const char *filename = resource.icon_hint;
45543367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
45553367019cSmrg    const char *larger = "_48x48";
45563367019cSmrg    char *result = 0;
45573367019cSmrg    size_t length;
45583367019cSmrg
45593367019cSmrg    if (*state >= 0) {
45603367019cSmrg	if ((*state & 1) == 0)
45613367019cSmrg	    suffix = "";
45623367019cSmrg	if ((*state & 2) == 0)
45633367019cSmrg	    larger = "";
45643367019cSmrg	if ((*state & 4) == 0) {
45653367019cSmrg	    prefix = "";
45663367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
45673367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
45683367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
45693367019cSmrg	    *state = -1;
45703367019cSmrg	} else if (*state >= 8) {
45713367019cSmrg	    *state = -1;
45723367019cSmrg	}
45733367019cSmrg    }
45743367019cSmrg
45753367019cSmrg    if (*state >= 0) {
45763367019cSmrg	if (*work) {
45773367019cSmrg	    free(*work);
45783367019cSmrg	    *work = 0;
45793367019cSmrg	}
45803367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
45813367019cSmrg	    strlen(suffix);
45823367019cSmrg	if ((result = malloc(length)) != 0) {
45833367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
45843367019cSmrg	    *work = result;
45853367019cSmrg	}
45863367019cSmrg	*state += 1;
45873367019cSmrg	TRACE(("x_find_icon %d:%s\n", *state, result));
45883367019cSmrg    }
45893367019cSmrg    return result;
45903367019cSmrg}
45913367019cSmrg
45923367019cSmrg#if OPT_BUILTIN_XPMS
45933367019cSmrgstatic const XPM_DATA *
45943367019cSmrgBuiltInXPM(const XPM_DATA * table, Cardinal length)
45953367019cSmrg{
45963367019cSmrg    const char *find = resource.icon_hint;
45973367019cSmrg    const XPM_DATA *result = 0;
45983367019cSmrg    if (!IsEmpty(find)) {
45993367019cSmrg	Cardinal n;
46003367019cSmrg	for (n = 0; n < length; ++n) {
46013367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
46023367019cSmrg		result = table + n;
46033367019cSmrg		break;
46043367019cSmrg	    }
46053367019cSmrg	}
46063367019cSmrg
46073367019cSmrg	/*
46083367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
46093367019cSmrg	 * which are all _HHxWW format.
46103367019cSmrg	 */
46113367019cSmrg	if (result == 0) {
46123367019cSmrg	    const char *base = table[0].name;
46133367019cSmrg	    const char *last = strchr(base, '_');
46143367019cSmrg	    if (last != 0
46153367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
46163367019cSmrg		result = table + length - 1;
46173367019cSmrg	    }
46183367019cSmrg	}
46193367019cSmrg    }
46203367019cSmrg    return result;
46213367019cSmrg}
46223367019cSmrg#endif /* OPT_BUILTIN_XPMS */
46233367019cSmrg
46243367019cSmrgtypedef enum {
46253367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
46263367019cSmrg    ,eHintNone
46273367019cSmrg    ,eHintSearch
46283367019cSmrg} ICON_HINT;
46293367019cSmrg
46303367019cSmrgstatic ICON_HINT
46313367019cSmrgwhich_icon_hint(void)
46323367019cSmrg{
46333367019cSmrg    ICON_HINT result = eHintDefault;
46343367019cSmrg    if (!IsEmpty(resource.icon_hint)) {
46353367019cSmrg	if (!x_strcasecmp(resource.icon_hint, "none")) {
46363367019cSmrg	    result = eHintNone;
46373367019cSmrg	} else {
46383367019cSmrg	    result = eHintSearch;
46393367019cSmrg	}
46403367019cSmrg    }
46413367019cSmrg    return result;
46423367019cSmrg}
46433367019cSmrg#endif /* HAVE_LIBXPM */
46443367019cSmrg
46453367019cSmrgint
46463367019cSmrggetVisualDepth(XtermWidget xw)
46473367019cSmrg{
46483367019cSmrg    int result = 0;
46493367019cSmrg
4650fa3f02f3Smrg    if (getVisualInfo(xw)) {
4651fa3f02f3Smrg	result = xw->visInfo->depth;
46523367019cSmrg    }
46533367019cSmrg    return result;
46543367019cSmrg}
46553367019cSmrg
46563367019cSmrg/*
46573367019cSmrg * WM_ICON_SIZE should be honored if possible.
46583367019cSmrg */
46593367019cSmrgvoid
46603367019cSmrgxtermLoadIcon(XtermWidget xw)
46613367019cSmrg{
46623367019cSmrg#ifdef HAVE_LIBXPM
46633367019cSmrg    Display *dpy = XtDisplay(xw);
46643367019cSmrg    Pixmap myIcon = 0;
46653367019cSmrg    Pixmap myMask = 0;
46663367019cSmrg    char *workname = 0;
46673367019cSmrg    ICON_HINT hint = which_icon_hint();
4668fa3f02f3Smrg#include <builtin_icons.h>
46693367019cSmrg
46703367019cSmrg    TRACE(("xtermLoadIcon %p:%s\n", (void *) xw, NonNull(resource.icon_hint)));
46713367019cSmrg
46723367019cSmrg    if (hint == eHintSearch) {
46733367019cSmrg	int state = 0;
46743367019cSmrg	while (x_find_icon(&workname, &state, ".xpm") != 0) {
46753367019cSmrg	    Pixmap resIcon = 0;
46763367019cSmrg	    Pixmap shapemask = 0;
46773367019cSmrg	    XpmAttributes attributes;
46783367019cSmrg
46793367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
46803367019cSmrg	    attributes.valuemask = XpmDepth;
46813367019cSmrg
46823367019cSmrg	    if (XpmReadFileToPixmap(dpy,
46833367019cSmrg				    DefaultRootWindow(dpy),
46843367019cSmrg				    workname,
46853367019cSmrg				    &resIcon,
46863367019cSmrg				    &shapemask,
46873367019cSmrg				    &attributes) == XpmSuccess) {
46883367019cSmrg		myIcon = resIcon;
46893367019cSmrg		myMask = shapemask;
46903367019cSmrg		TRACE(("...success\n"));
46913367019cSmrg		break;
46923367019cSmrg	    }
46933367019cSmrg	}
46943367019cSmrg    }
46953367019cSmrg
46963367019cSmrg    /*
46973367019cSmrg     * If no external file was found, look for the name in the built-in table.
46983367019cSmrg     * If that fails, just use the biggest mini-icon.
46993367019cSmrg     */
47003367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
47013367019cSmrg	char **data;
47023367019cSmrg#if OPT_BUILTIN_XPMS
47033367019cSmrg	const XPM_DATA *myData = 0;
47043367019cSmrg	myData = BuiltInXPM(mini_xterm_xpms, XtNumber(mini_xterm_xpms));
47053367019cSmrg	if (myData == 0)
47063367019cSmrg	    myData = BuiltInXPM(filled_xterm_xpms, XtNumber(filled_xterm_xpms));
47073367019cSmrg	if (myData == 0)
47083367019cSmrg	    myData = BuiltInXPM(xterm_color_xpms, XtNumber(xterm_color_xpms));
47093367019cSmrg	if (myData == 0)
47103367019cSmrg	    myData = BuiltInXPM(xterm_xpms, XtNumber(xterm_xpms));
47113367019cSmrg	if (myData == 0)
47123367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
47133367019cSmrg	data = (char **) myData->data,
47143367019cSmrg#else
47153367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
47163367019cSmrg#endif
47173367019cSmrg	if (XpmCreatePixmapFromData(dpy,
47183367019cSmrg				    DefaultRootWindow(dpy),
47193367019cSmrg				    data,
47203367019cSmrg				    &myIcon, &myMask, 0) != 0) {
47213367019cSmrg	    myIcon = 0;
47223367019cSmrg	    myMask = 0;
47233367019cSmrg	}
47243367019cSmrg    }
47253367019cSmrg
47263367019cSmrg    if (myIcon != 0) {
47273367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
47283367019cSmrg	if (!hints)
47293367019cSmrg	    hints = XAllocWMHints();
47303367019cSmrg
47313367019cSmrg	if (hints) {
47323367019cSmrg	    hints->flags |= IconPixmapHint;
47333367019cSmrg	    hints->icon_pixmap = myIcon;
47343367019cSmrg	    if (myMask) {
47353367019cSmrg		hints->flags |= IconMaskHint;
47363367019cSmrg		hints->icon_mask = myMask;
47373367019cSmrg	    }
47383367019cSmrg
47393367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
47403367019cSmrg	    XFree(hints);
47413367019cSmrg	    TRACE(("...loaded icon\n"));
47423367019cSmrg	}
47433367019cSmrg    }
47443367019cSmrg
47453367019cSmrg    if (workname != 0)
47463367019cSmrg	free(workname);
47473367019cSmrg
47483367019cSmrg#else
47493367019cSmrg    (void) xw;
47503367019cSmrg#endif
47513367019cSmrg}
47523367019cSmrg
47533367019cSmrgvoid
4754cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
4755d522f475Smrg{
4756d522f475Smrg#if OPT_WIDE_CHARS
4757d522f475Smrg    static Char *converted;	/* NO_LEAKS */
4758d522f475Smrg#endif
4759d522f475Smrg
4760d522f475Smrg    Arg args[1];
4761cd3331d0Smrg    Boolean changed = True;
4762d522f475Smrg    Widget w = CURRENT_EMU();
4763d522f475Smrg    Widget top = SHELL_OF(w);
4764d522f475Smrg
4765cd3331d0Smrg    char *my_attr;
4766cd3331d0Smrg    char *name;
4767cd3331d0Smrg    size_t limit;
4768cd3331d0Smrg    Char *c1;
4769cd3331d0Smrg    Char *cp;
4770d522f475Smrg
4771b7c89284Ssnj    if (!AllowTitleOps(xw))
4772d522f475Smrg	return;
4773d522f475Smrg
4774cd3331d0Smrg    if (value == 0)
47753367019cSmrg	value = emptyString;
4776cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
4777cd3331d0Smrg	const char *temp;
4778cd3331d0Smrg	char *test;
4779cd3331d0Smrg
4780cd3331d0Smrg	value = x_decode_hex(value, &temp);
47813367019cSmrg	if (*temp != '\0') {
47823367019cSmrg	    free(value);
4783cd3331d0Smrg	    return;
47843367019cSmrg	}
4785cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
4786cd3331d0Smrg	    if (CharOf(*test) < 32) {
4787cd3331d0Smrg		*test = '\0';
4788cd3331d0Smrg		break;
4789cd3331d0Smrg	    }
4790cd3331d0Smrg	}
4791cd3331d0Smrg    }
4792cd3331d0Smrg
4793cd3331d0Smrg    c1 = (Char *) value;
4794cd3331d0Smrg    name = value;
4795cd3331d0Smrg    limit = strlen(name);
4796cd3331d0Smrg    my_attr = x_strdup(attribute);
4797cd3331d0Smrg
4798cd3331d0Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
4799cd3331d0Smrg
4800d522f475Smrg    /*
4801d522f475Smrg     * Ignore titles that are too long to be plausible requests.
4802d522f475Smrg     */
4803cd3331d0Smrg    if (limit > 0 && limit < 1024) {
4804d522f475Smrg
4805cd3331d0Smrg	/*
4806cd3331d0Smrg	 * After all decoding, overwrite nonprintable characters with '?'.
4807cd3331d0Smrg	 */
4808cd3331d0Smrg	for (cp = c1; *cp != 0; ++cp) {
4809cd3331d0Smrg	    Char *c2 = cp;
4810cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
4811cd3331d0Smrg		memset(c2, '?', (size_t) (cp + 1 - c2));
4812cd3331d0Smrg	    }
4813d522f475Smrg	}
4814d522f475Smrg
4815d522f475Smrg#if OPT_WIDE_CHARS
4816cd3331d0Smrg	/*
4817cd3331d0Smrg	 * If we're running in UTF-8 mode, and have not been told that the
4818cd3331d0Smrg	 * title string is in UTF-8, it is likely that non-ASCII text in the
4819cd3331d0Smrg	 * string will be rejected because it is not printable in the current
4820cd3331d0Smrg	 * locale.  So we convert it to UTF-8, allowing the X library to
4821cd3331d0Smrg	 * convert it back.
4822cd3331d0Smrg	 */
4823cd3331d0Smrg	if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) {
4824cd3331d0Smrg	    int n;
4825cd3331d0Smrg
4826cd3331d0Smrg	    for (n = 0; name[n] != '\0'; ++n) {
4827cd3331d0Smrg		if (CharOf(name[n]) > 127) {
4828cd3331d0Smrg		    if (converted != 0)
4829cd3331d0Smrg			free(converted);
4830cd3331d0Smrg		    if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
4831cd3331d0Smrg			Char *temp = converted;
4832cd3331d0Smrg			while (*name != 0) {
4833cd3331d0Smrg			    temp = convertToUTF8(temp, CharOf(*name));
4834cd3331d0Smrg			    ++name;
4835cd3331d0Smrg			}
4836cd3331d0Smrg			*temp = 0;
4837cd3331d0Smrg			name = (char *) converted;
4838cd3331d0Smrg			TRACE(("...converted{%s}\n", name));
4839d522f475Smrg		    }
4840cd3331d0Smrg		    break;
4841d522f475Smrg		}
4842d522f475Smrg	    }
4843d522f475Smrg	}
4844d522f475Smrg#endif
4845d522f475Smrg
4846d522f475Smrg#if OPT_SAME_NAME
4847cd3331d0Smrg	/* If the attribute isn't going to change, then don't bother... */
4848cd3331d0Smrg
4849cd3331d0Smrg	if (resource.sameName) {
4850cd3331d0Smrg	    char *buf = 0;
4851cd3331d0Smrg	    XtSetArg(args[0], my_attr, &buf);
4852cd3331d0Smrg	    XtGetValues(top, args, 1);
4853cd3331d0Smrg	    TRACE(("...comparing{%s}\n", buf));
4854cd3331d0Smrg	    if (buf != 0 && strcmp(name, buf) == 0)
4855cd3331d0Smrg		changed = False;
4856cd3331d0Smrg	}
4857d522f475Smrg#endif /* OPT_SAME_NAME */
4858d522f475Smrg
4859cd3331d0Smrg	if (changed) {
4860cd3331d0Smrg	    TRACE(("...updating %s\n", my_attr));
4861cd3331d0Smrg	    TRACE(("...value is %s\n", name));
4862cd3331d0Smrg	    XtSetArg(args[0], my_attr, name);
4863cd3331d0Smrg	    XtSetValues(top, args, 1);
4864d522f475Smrg
4865d522f475Smrg#if OPT_WIDE_CHARS
4866cd3331d0Smrg	    if (xtermEnvUTF8()) {
4867cd3331d0Smrg		Display *dpy = XtDisplay(xw);
4868cd3331d0Smrg		Atom my_atom;
4869cd3331d0Smrg
4870cd3331d0Smrg		const char *propname = (!strcmp(my_attr, XtNtitle)
4871cd3331d0Smrg					? "_NET_WM_NAME"
4872cd3331d0Smrg					: "_NET_WM_ICON_NAME");
4873cd3331d0Smrg		if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
4874cd3331d0Smrg		    if (IsSetUtf8Title(xw)) {
4875cd3331d0Smrg			TRACE(("...updating %s\n", propname));
4876cd3331d0Smrg			TRACE(("...value is %s\n", value));
4877c219fbebSmrg			XChangeProperty(dpy, VShellWindow(xw), my_atom,
4878cd3331d0Smrg					XA_UTF8_STRING(dpy), 8,
4879cd3331d0Smrg					PropModeReplace,
4880cd3331d0Smrg					(Char *) value,
4881cd3331d0Smrg					(int) strlen(value));
4882cd3331d0Smrg		    } else {
4883cd3331d0Smrg			TRACE(("...deleting %s\n", propname));
4884c219fbebSmrg			XDeleteProperty(dpy, VShellWindow(xw), my_atom);
4885cd3331d0Smrg		    }
4886cd3331d0Smrg		}
4887d522f475Smrg	    }
4888cd3331d0Smrg#endif
4889d522f475Smrg	}
4890d522f475Smrg    }
48913367019cSmrg    if (IsTitleMode(xw, tmSetBase16)) {
48923367019cSmrg	free(value);
48933367019cSmrg    }
48943367019cSmrg    free(my_attr);
48953367019cSmrg
4896cd3331d0Smrg    return;
4897d522f475Smrg}
4898d522f475Smrg
4899d522f475Smrgvoid
4900b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
4901d522f475Smrg{
4902cd3331d0Smrg    if (name == 0) {
49033367019cSmrg	name = emptyString;
49043367019cSmrg    }
49053367019cSmrg    if (!showZIconBeep(xw, name))
4906b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
4907d522f475Smrg}
4908d522f475Smrg
4909d522f475Smrgvoid
4910b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
4911d522f475Smrg{
4912b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
4913d522f475Smrg}
4914d522f475Smrg
4915712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
4916d522f475Smrg
4917d522f475Smrgvoid
4918d522f475SmrgChangeXprop(char *buf)
4919d522f475Smrg{
4920d522f475Smrg    Display *dpy = XtDisplay(toplevel);
4921d522f475Smrg    Window w = XtWindow(toplevel);
4922d522f475Smrg    XTextProperty text_prop;
4923d522f475Smrg    Atom aprop;
4924d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
4925d522f475Smrg
4926d522f475Smrg    if (pchEndPropName)
4927d522f475Smrg	*pchEndPropName = '\0';
4928d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
4929d522f475Smrg    if (pchEndPropName == NULL) {
4930d522f475Smrg	/* no "=value" given, so delete the property */
4931d522f475Smrg	XDeleteProperty(dpy, w, aprop);
4932d522f475Smrg    } else {
4933d522f475Smrg	text_prop.value = pchEndPropName + 1;
4934d522f475Smrg	text_prop.encoding = XA_STRING;
4935d522f475Smrg	text_prop.format = 8;
4936d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
4937d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
4938d522f475Smrg    }
4939d522f475Smrg}
4940d522f475Smrg
4941d522f475Smrg/***====================================================================***/
4942d522f475Smrg
4943d522f475Smrg/*
4944d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
4945d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
4946d522f475Smrg */
4947d522f475Smrgvoid
49489a64e1c5SmrgReverseOldColors(XtermWidget xw)
4949d522f475Smrg{
49509a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
4951d522f475Smrg    Pixel tmpPix;
4952d522f475Smrg    char *tmpName;
4953d522f475Smrg
4954d522f475Smrg    if (pOld) {
4955d522f475Smrg	/* change text cursor, if necesary */
4956d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
4957d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
4958d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
49599a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
4960d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
4961d522f475Smrg	    }
4962d522f475Smrg	    if (pOld->names[TEXT_BG]) {
4963d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
4964d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
4965d522f475Smrg		}
4966d522f475Smrg	    }
4967d522f475Smrg	}
4968d522f475Smrg
4969d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
4970d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
4971d522f475Smrg
4972d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
4973d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
4974d522f475Smrg
4975d522f475Smrg#if OPT_TEK4014
4976d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
4977d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
4978d522f475Smrg#endif
4979d522f475Smrg    }
4980d522f475Smrg    return;
4981d522f475Smrg}
4982d522f475Smrg
4983d522f475SmrgBool
4984d522f475SmrgAllocateTermColor(XtermWidget xw,
4985d522f475Smrg		  ScrnColors * pNew,
4986d522f475Smrg		  int ndx,
4987cd3331d0Smrg		  const char *name,
4988cd3331d0Smrg		  Bool always)
4989d522f475Smrg{
4990cd3331d0Smrg    Bool result = False;
4991d522f475Smrg
4992cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
4993cd3331d0Smrg	XColor def;
4994cd3331d0Smrg	char *newName;
4995cd3331d0Smrg
4996712a7ff4Smrg	result = True;
4997712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
4998712a7ff4Smrg	    def.pixel = xw->old_foreground;
4999712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
5000712a7ff4Smrg	    def.pixel = xw->old_background;
50013367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
5002712a7ff4Smrg	    result = False;
5003712a7ff4Smrg	}
5004712a7ff4Smrg
5005712a7ff4Smrg	if (result
5006cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
5007712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
5008cd3331d0Smrg		free(pNew->names[ndx]);
5009712a7ff4Smrg	    }
5010cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
5011cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
5012712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
5013712a7ff4Smrg		   ndx, newName, def.pixel));
5014cd3331d0Smrg	} else {
5015cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
5016712a7ff4Smrg	    result = False;
5017cd3331d0Smrg	}
5018cd3331d0Smrg    }
5019cd3331d0Smrg    return result;
5020d522f475Smrg}
5021d522f475Smrg/***====================================================================***/
5022d522f475Smrg
5023d522f475Smrg/* ARGSUSED */
5024d522f475Smrgvoid
5025cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
5026d522f475Smrg{
50273367019cSmrg    if_DEBUG({
50283367019cSmrg	xtermWarning(s, a);
50293367019cSmrg    });
5030d522f475Smrg}
5031d522f475Smrg
5032d522f475Smrgconst char *
5033d522f475SmrgSysErrorMsg(int code)
5034d522f475Smrg{
5035d522f475Smrg    static char unknown[] = "unknown error";
5036d522f475Smrg    char *s = strerror(code);
5037d522f475Smrg    return s ? s : unknown;
5038d522f475Smrg}
5039d522f475Smrg
5040d522f475Smrgconst char *
5041d522f475SmrgSysReasonMsg(int code)
5042d522f475Smrg{
5043d522f475Smrg    /* *INDENT-OFF* */
5044d522f475Smrg    static const struct {
5045d522f475Smrg	int code;
5046d522f475Smrg	const char *name;
5047d522f475Smrg    } table[] = {
5048d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
5049d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
5050d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
5051d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
5052d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
5053d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
5054d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
5055d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
5056d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
5057d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
5058d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
5059d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
5060d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
5061d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
5062d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
5063d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
5064d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
5065d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
5066d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
5067d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
5068d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
5069d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
5070d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
5071d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
5072d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
5073d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
5074d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
5075d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
5076d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
5077d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
5078d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
5079d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
5080d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
5081d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
5082d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
5083d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
5084d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
5085d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
5086d522f475Smrg    };
5087d522f475Smrg    /* *INDENT-ON* */
5088d522f475Smrg
5089d522f475Smrg    Cardinal n;
5090d522f475Smrg    const char *result = "?";
5091d522f475Smrg
5092d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
5093d522f475Smrg	if (code == table[n].code) {
5094d522f475Smrg	    result = table[n].name;
5095d522f475Smrg	    break;
5096d522f475Smrg	}
5097d522f475Smrg    }
5098d522f475Smrg    return result;
5099d522f475Smrg}
5100d522f475Smrg
5101d522f475Smrgvoid
5102d522f475SmrgSysError(int code)
5103d522f475Smrg{
5104d522f475Smrg    int oerrno = errno;
5105d522f475Smrg
5106c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
5107d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
5108d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
5109d522f475Smrg
5110d522f475Smrg    Cleanup(code);
5111d522f475Smrg}
5112d522f475Smrg
5113d522f475Smrgvoid
51143367019cSmrgNormalExit(void)
5115d522f475Smrg{
5116d522f475Smrg    static Bool cleaning;
5117d522f475Smrg
5118d522f475Smrg    /*
5119d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
5120d522f475Smrg     */
51213367019cSmrg    if (cleaning) {
51223367019cSmrg	hold_screen = 0;
51233367019cSmrg	return;
51243367019cSmrg    }
5125d522f475Smrg
51263367019cSmrg    cleaning = True;
51273367019cSmrg    need_cleanup = False;
5128d522f475Smrg
51293367019cSmrg    if (hold_screen) {
51303367019cSmrg	hold_screen = 2;
51313367019cSmrg	while (hold_screen) {
51323367019cSmrg	    xevents();
51333367019cSmrg	    Sleep(10);
5134d522f475Smrg	}
51353367019cSmrg    }
5136d522f475Smrg#if OPT_SESSION_MGT
51373367019cSmrg    if (resource.sessionMgt) {
51383367019cSmrg	XtVaSetValues(toplevel,
51393367019cSmrg		      XtNjoinSession, False,
51403367019cSmrg		      (void *) 0);
5141d522f475Smrg    }
51423367019cSmrg#endif
51433367019cSmrg    Cleanup(0);
51443367019cSmrg}
51453367019cSmrg
51463367019cSmrg/*
51473367019cSmrg * cleanup by sending SIGHUP to client processes
51483367019cSmrg */
51493367019cSmrgvoid
51503367019cSmrgCleanup(int code)
51513367019cSmrg{
51523367019cSmrg    TScreen *screen = TScreenOf(term);
51533367019cSmrg
51543367019cSmrg    TRACE(("Cleanup %d\n", code));
5155d522f475Smrg
5156d522f475Smrg    if (screen->pid > 1) {
5157d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
5158d522f475Smrg    }
5159d522f475Smrg    Exit(code);
5160d522f475Smrg}
5161d522f475Smrg
5162fa3f02f3Smrg#ifndef S_IXOTH
5163fa3f02f3Smrg#define S_IXOTH 1
5164fa3f02f3Smrg#endif
5165fa3f02f3Smrg
5166fa3f02f3SmrgBoolean
5167fa3f02f3SmrgvalidProgram(const char *pathname)
5168fa3f02f3Smrg{
5169fa3f02f3Smrg    Boolean result = False;
5170fa3f02f3Smrg    struct stat sb;
5171fa3f02f3Smrg
5172fa3f02f3Smrg    if (!IsEmpty(pathname)
5173fa3f02f3Smrg	&& *pathname == '/'
5174fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
5175fa3f02f3Smrg	&& stat(pathname, &sb) == 0
5176fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
5177fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
5178fa3f02f3Smrg	result = True;
5179fa3f02f3Smrg    }
5180fa3f02f3Smrg    return result;
5181fa3f02f3Smrg}
5182fa3f02f3Smrg
5183d522f475Smrg#ifndef VMS
51843367019cSmrg#ifndef PATH_MAX
51853367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
51863367019cSmrg#endif
5187d522f475Smrgchar *
5188d522f475SmrgxtermFindShell(char *leaf, Bool warning)
5189d522f475Smrg{
51903367019cSmrg    char *s0;
5191d522f475Smrg    char *s;
5192d522f475Smrg    char *d;
5193d522f475Smrg    char *tmp;
5194d522f475Smrg    char *result = leaf;
51953367019cSmrg    Bool allocated = False;
5196d522f475Smrg
5197d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
51983367019cSmrg
51993367019cSmrg    if (!strncmp("./", result, (size_t) 2)
52003367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
52013367019cSmrg	size_t need = PATH_MAX;
52023367019cSmrg	size_t used = strlen(result) + 2;
52033367019cSmrg	char *buffer = malloc(used + need);
52043367019cSmrg	if (buffer != 0) {
52053367019cSmrg	    if (getcwd(buffer, need) != 0) {
52063367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
52073367019cSmrg		result = buffer;
52083367019cSmrg		allocated = True;
52093367019cSmrg	    } else {
52103367019cSmrg		free(buffer);
52113367019cSmrg	    }
52123367019cSmrg	}
52133367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
5214d522f475Smrg	/* find it in $PATH */
52153367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
52160d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
5217d522f475Smrg		Bool found = False;
5218d522f475Smrg		while (*s != '\0') {
5219d522f475Smrg		    strcpy(tmp, s);
5220d522f475Smrg		    for (d = tmp;; ++d) {
5221d522f475Smrg			if (*d == ':' || *d == '\0') {
5222d522f475Smrg			    int skip = (*d != '\0');
5223d522f475Smrg			    *d = '/';
5224d522f475Smrg			    strcpy(d + 1, leaf);
5225d522f475Smrg			    if (skip)
5226d522f475Smrg				++d;
5227d522f475Smrg			    s += (d - tmp);
5228fa3f02f3Smrg			    if (validProgram(tmp)) {
5229d522f475Smrg				result = x_strdup(tmp);
5230d522f475Smrg				found = True;
52313367019cSmrg				allocated = True;
5232d522f475Smrg			    }
5233d522f475Smrg			    break;
5234d522f475Smrg			}
5235d522f475Smrg		    }
5236d522f475Smrg		    if (found)
5237d522f475Smrg			break;
5238d522f475Smrg		}
5239d522f475Smrg		free(tmp);
5240d522f475Smrg	    }
52413367019cSmrg	    free(s0);
5242d522f475Smrg	}
5243d522f475Smrg    }
5244d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
5245fa3f02f3Smrg    if (!validProgram(result)) {
5246d522f475Smrg	if (warning)
52473367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
52483367019cSmrg	if (allocated)
52493367019cSmrg	    free(result);
5250d522f475Smrg	result = 0;
5251d522f475Smrg    }
52523367019cSmrg    /* be consistent, so that caller can always free the result */
52533367019cSmrg    if (result != 0 && !allocated)
52543367019cSmrg	result = x_strdup(result);
5255d522f475Smrg    return result;
5256d522f475Smrg}
5257d522f475Smrg#endif /* VMS */
5258d522f475Smrg
52590d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
5260d522f475Smrg
52613367019cSmrg/*
52623367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
52633367019cSmrg * This could happen on some older machines due to the uneven standardization
52643367019cSmrg * process for the two functions.
52653367019cSmrg *
52663367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
52673367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
52683367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
52693367019cSmrg * could copy environ.
52703367019cSmrg */
52713367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
52723367019cSmrg#undef HAVE_PUTENV
52733367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
52743367019cSmrg#undef HAVE_UNSETENV
52753367019cSmrg#endif
52763367019cSmrg
5277d522f475Smrg/*
5278d522f475Smrg * copy the environment before Setenv'ing.
5279d522f475Smrg */
5280d522f475Smrgvoid
5281d522f475SmrgxtermCopyEnv(char **oldenv)
5282d522f475Smrg{
52833367019cSmrg#ifdef HAVE_PUTENV
52843367019cSmrg    (void) oldenv;
52853367019cSmrg#else
5286d522f475Smrg    unsigned size;
5287d522f475Smrg    char **newenv;
5288d522f475Smrg
5289d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
5290d522f475Smrg	;
5291d522f475Smrg    }
5292d522f475Smrg
5293d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
5294d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
5295d522f475Smrg    environ = newenv;
52963367019cSmrg#endif
52973367019cSmrg}
52983367019cSmrg
52993367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
53003367019cSmrgstatic int
53013367019cSmrgfindEnv(const char *var, int *lengthp)
53023367019cSmrg{
53033367019cSmrg    char *test;
53043367019cSmrg    int envindex = 0;
53053367019cSmrg    size_t len = strlen(var);
53063367019cSmrg    int found = -1;
53073367019cSmrg
53083367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
53093367019cSmrg
53103367019cSmrg    while ((test = environ[envindex]) != NULL) {
53113367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
53123367019cSmrg	    found = envindex;
53133367019cSmrg	    break;
53143367019cSmrg	}
53153367019cSmrg	envindex++;
53163367019cSmrg    }
53173367019cSmrg    *lengthp = envindex;
53183367019cSmrg    return found;
5319d522f475Smrg}
53203367019cSmrg#endif
5321d522f475Smrg
5322d522f475Smrg/*
5323d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
5324d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
5325d522f475Smrg * This procedure assumes the memory for the first level of environ
5326d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
5327d522f475Smrg * to have to do a realloc().
5328d522f475Smrg */
5329d522f475Smrgvoid
5330cd3331d0SmrgxtermSetenv(const char *var, const char *value)
5331d522f475Smrg{
5332d522f475Smrg    if (value != 0) {
53333367019cSmrg#ifdef HAVE_PUTENV
53343367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
53353367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
53363367019cSmrg	if (both) {
53373367019cSmrg	    sprintf(both, "%s=%s", var, value);
53383367019cSmrg	    putenv(both);
53393367019cSmrg	}
53403367019cSmrg#else
5341d522f475Smrg	size_t len = strlen(var);
53423367019cSmrg	int envindex;
53433367019cSmrg	int found = findEnv(var, &envindex);
5344d522f475Smrg
5345d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
5346d522f475Smrg
5347d522f475Smrg	if (found < 0) {
5348d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
5349d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
5350d522f475Smrg
5351d522f475Smrg	    if (need > have) {
5352d522f475Smrg		char **newenv;
5353d522f475Smrg		newenv = TypeMallocN(char *, need);
5354d522f475Smrg		if (newenv == 0) {
53553367019cSmrg		    xtermWarning("Cannot increase environment\n");
5356d522f475Smrg		    return;
5357d522f475Smrg		}
5358d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
5359d522f475Smrg		free(environ);
5360d522f475Smrg		environ = newenv;
5361d522f475Smrg	    }
5362d522f475Smrg
5363d522f475Smrg	    found = envindex;
5364d522f475Smrg	    environ[found + 1] = NULL;
5365d522f475Smrg	    environ = environ;
5366d522f475Smrg	}
5367d522f475Smrg
5368d522f475Smrg	environ[found] = CastMallocN(char, 1 + len + strlen(value));
5369d522f475Smrg	if (environ[found] == 0) {
53703367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
5371d522f475Smrg	    return;
5372d522f475Smrg	}
5373d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
53743367019cSmrg#endif
5375d522f475Smrg    }
5376d522f475Smrg}
5377d522f475Smrg
53783367019cSmrgvoid
53793367019cSmrgxtermUnsetenv(const char *var)
53803367019cSmrg{
53813367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
53823367019cSmrg#ifdef HAVE_UNSETENV
53833367019cSmrg    unsetenv(var);
53843367019cSmrg#else
53853367019cSmrg    {
53863367019cSmrg	int ignore;
53873367019cSmrg	int item = findEnv(var, &ignore);
53883367019cSmrg	if (item >= 0) {
53893367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
53903367019cSmrg		++item;
53913367019cSmrg	    }
53923367019cSmrg	}
53933367019cSmrg    }
53943367019cSmrg#endif
53953367019cSmrg}
53963367019cSmrg
5397d522f475Smrg/*ARGSUSED*/
5398d522f475Smrgint
53999a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
5400d522f475Smrg{
54013367019cSmrg    xtermWarning("warning, error event received:\n");
5402d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
5403d522f475Smrg    Exit(ERROR_XERROR);
5404d522f475Smrg    return 0;			/* appease the compiler */
5405d522f475Smrg}
5406d522f475Smrg
5407712a7ff4Smrgvoid
5408712a7ff4Smrgice_error(IceConn iceConn)
5409712a7ff4Smrg{
5410712a7ff4Smrg    (void) iceConn;
5411712a7ff4Smrg
54123367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
54133367019cSmrg		 (long) getpid(), errno);
5414712a7ff4Smrg
5415712a7ff4Smrg    Exit(ERROR_ICEERROR);
5416712a7ff4Smrg}
5417712a7ff4Smrg
5418d522f475Smrg/*ARGSUSED*/
5419d522f475Smrgint
5420fa3f02f3Smrgxioerror(Display *dpy)
5421d522f475Smrg{
5422d522f475Smrg    int the_error = errno;
5423d522f475Smrg
54243367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
54253367019cSmrg		 the_error, SysErrorMsg(the_error),
54263367019cSmrg		 DisplayString(dpy));
5427d522f475Smrg
5428d522f475Smrg    Exit(ERROR_XIOERROR);
5429d522f475Smrg    return 0;			/* appease the compiler */
5430d522f475Smrg}
5431d522f475Smrg
5432d522f475Smrgvoid
5433d522f475Smrgxt_error(String message)
5434d522f475Smrg{
54353367019cSmrg    xtermWarning("Xt error: %s\n", message);
5436d522f475Smrg
5437d522f475Smrg    /*
5438d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
5439d522f475Smrg     */
5440d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
54413367019cSmrg	xtermWarning("DISPLAY is not set\n");
5442d522f475Smrg    }
5443d522f475Smrg    exit(1);
5444d522f475Smrg}
5445d522f475Smrg
5446d522f475Smrgint
5447d522f475SmrgXStrCmp(char *s1, char *s2)
5448d522f475Smrg{
5449d522f475Smrg    if (s1 && s2)
5450d522f475Smrg	return (strcmp(s1, s2));
5451d522f475Smrg    if (s1 && *s1)
5452d522f475Smrg	return (1);
5453d522f475Smrg    if (s2 && *s2)
5454d522f475Smrg	return (-1);
5455d522f475Smrg    return (0);
5456d522f475Smrg}
5457d522f475Smrg
5458d522f475Smrg#if OPT_TEK4014
5459d522f475Smrgstatic void
5460fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
5461d522f475Smrg{
5462d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
5463d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
5464d522f475Smrg    XWithdrawWindow(dpy, w, scr);
5465d522f475Smrg    return;
5466d522f475Smrg}
5467d522f475Smrg#endif
5468d522f475Smrg
5469d522f475Smrgvoid
5470d522f475Smrgset_vt_visibility(Bool on)
5471d522f475Smrg{
5472c219fbebSmrg    XtermWidget xw = term;
5473c219fbebSmrg    TScreen *screen = TScreenOf(xw);
5474d522f475Smrg
5475d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
5476d522f475Smrg    if (on) {
5477c219fbebSmrg	if (!screen->Vshow && xw) {
5478c219fbebSmrg	    VTInit(xw);
5479c219fbebSmrg	    XtMapWidget(XtParent(xw));
5480d522f475Smrg#if OPT_TOOLBAR
5481d522f475Smrg	    /* we need both of these during initialization */
5482c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
5483d522f475Smrg	    ShowToolbar(resource.toolBar);
5484d522f475Smrg#endif
5485d522f475Smrg	    screen->Vshow = True;
5486d522f475Smrg	}
5487d522f475Smrg    }
5488d522f475Smrg#if OPT_TEK4014
5489d522f475Smrg    else {
5490c219fbebSmrg	if (screen->Vshow && xw) {
5491c219fbebSmrg	    withdraw_window(XtDisplay(xw),
5492c219fbebSmrg			    VShellWindow(xw),
5493c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
5494d522f475Smrg	    screen->Vshow = False;
5495d522f475Smrg	}
5496d522f475Smrg    }
5497d522f475Smrg    set_vthide_sensitivity();
5498d522f475Smrg    set_tekhide_sensitivity();
5499d522f475Smrg    update_vttekmode();
5500d522f475Smrg    update_tekshow();
5501d522f475Smrg    update_vtshow();
5502d522f475Smrg#endif
5503d522f475Smrg    return;
5504d522f475Smrg}
5505d522f475Smrg
5506d522f475Smrg#if OPT_TEK4014
5507d522f475Smrgvoid
5508d522f475Smrgset_tek_visibility(Bool on)
5509d522f475Smrg{
5510d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
5511d522f475Smrg
5512d522f475Smrg    if (on) {
5513cd3331d0Smrg	if (!TEK4014_SHOWN(term)) {
5514cd3331d0Smrg	    if (tekWidget == 0) {
5515cd3331d0Smrg		TekInit();	/* will exit on failure */
5516cd3331d0Smrg	    }
5517cd3331d0Smrg	    if (tekWidget != 0) {
5518cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
5519cd3331d0Smrg		XtRealizeWidget(tekParent);
5520cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
5521d522f475Smrg#if OPT_TOOLBAR
5522cd3331d0Smrg		/* we need both of these during initialization */
5523cd3331d0Smrg		XtMapWidget(tekParent);
5524cd3331d0Smrg		XtMapWidget(tekWidget);
5525d522f475Smrg#endif
5526cd3331d0Smrg		XtOverrideTranslations(tekParent,
5527cd3331d0Smrg				       XtParseTranslationTable
5528cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
5529cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
5530cd3331d0Smrg				       XtWindow(tekParent),
5531cd3331d0Smrg				       &wm_delete_window, 1);
5532cd3331d0Smrg		TEK4014_SHOWN(term) = True;
5533cd3331d0Smrg	    }
5534d522f475Smrg	}
5535d522f475Smrg    } else {
5536d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
5537d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
5538d522f475Smrg			    TShellWindow,
5539d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
5540d522f475Smrg	    TEK4014_SHOWN(term) = False;
5541d522f475Smrg	}
5542d522f475Smrg    }
5543d522f475Smrg    set_tekhide_sensitivity();
5544d522f475Smrg    set_vthide_sensitivity();
5545d522f475Smrg    update_vtshow();
5546d522f475Smrg    update_tekshow();
5547d522f475Smrg    update_vttekmode();
5548d522f475Smrg    return;
5549d522f475Smrg}
5550d522f475Smrg
5551d522f475Smrgvoid
5552d522f475Smrgend_tek_mode(void)
5553d522f475Smrg{
5554cd3331d0Smrg    XtermWidget xw = term;
5555cd3331d0Smrg
5556cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
5557cd3331d0Smrg	FlushLog(xw);
5558d522f475Smrg	longjmp(Tekend, 1);
5559d522f475Smrg    }
5560d522f475Smrg    return;
5561d522f475Smrg}
5562d522f475Smrg
5563d522f475Smrgvoid
5564d522f475Smrgend_vt_mode(void)
5565d522f475Smrg{
5566cd3331d0Smrg    XtermWidget xw = term;
5567cd3331d0Smrg
5568cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
5569cd3331d0Smrg	FlushLog(xw);
5570cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
5571d522f475Smrg	longjmp(VTend, 1);
5572d522f475Smrg    }
5573d522f475Smrg    return;
5574d522f475Smrg}
5575d522f475Smrg
5576d522f475Smrgvoid
5577d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
5578d522f475Smrg{
5579d522f475Smrg    if (tovt) {
5580d522f475Smrg	if (tekRefreshList)
5581d522f475Smrg	    TekRefresh(tekWidget);
5582d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
5583d522f475Smrg    } else {
5584d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
5585d522f475Smrg    }
5586d522f475Smrg}
5587d522f475Smrg
5588d522f475Smrgvoid
5589d522f475Smrghide_vt_window(void)
5590d522f475Smrg{
5591d522f475Smrg    set_vt_visibility(False);
5592d522f475Smrg    if (!TEK4014_ACTIVE(term))
5593d522f475Smrg	switch_modes(False);	/* switch to tek mode */
5594d522f475Smrg}
5595d522f475Smrg
5596d522f475Smrgvoid
5597d522f475Smrghide_tek_window(void)
5598d522f475Smrg{
5599d522f475Smrg    set_tek_visibility(False);
5600d522f475Smrg    tekRefreshList = (TekLink *) 0;
5601d522f475Smrg    if (TEK4014_ACTIVE(term))
5602d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
5603d522f475Smrg}
5604d522f475Smrg#endif /* OPT_TEK4014 */
5605d522f475Smrg
5606d522f475Smrgstatic const char *
5607d522f475Smrgskip_punct(const char *s)
5608d522f475Smrg{
5609d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
5610d522f475Smrg	++s;
5611d522f475Smrg    }
5612d522f475Smrg    return s;
5613d522f475Smrg}
5614d522f475Smrg
5615d522f475Smrgstatic int
5616d522f475Smrgcmp_options(const void *a, const void *b)
5617d522f475Smrg{
5618d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
5619d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
5620d522f475Smrg    return strcmp(s1, s2);
5621d522f475Smrg}
5622d522f475Smrg
5623d522f475Smrgstatic int
5624d522f475Smrgcmp_resources(const void *a, const void *b)
5625d522f475Smrg{
5626d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
5627d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
5628d522f475Smrg}
5629d522f475Smrg
5630d522f475SmrgXrmOptionDescRec *
5631d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
5632d522f475Smrg{
5633d522f475Smrg    static XrmOptionDescRec *res_array = 0;
5634d522f475Smrg
5635d522f475Smrg#ifdef NO_LEAKS
5636cd3331d0Smrg    if (descs == 0) {
5637cd3331d0Smrg	if (res_array != 0) {
5638cd3331d0Smrg	    free(res_array);
5639cd3331d0Smrg	    res_array = 0;
5640cd3331d0Smrg	}
5641d522f475Smrg    } else
5642d522f475Smrg#endif
5643d522f475Smrg    if (res_array == 0) {
5644d522f475Smrg	Cardinal j;
5645d522f475Smrg
5646d522f475Smrg	/* make a sorted index to 'resources' */
5647d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
5648cd3331d0Smrg	if (res_array != 0) {
5649cd3331d0Smrg	    for (j = 0; j < res_count; j++)
5650cd3331d0Smrg		res_array[j] = descs[j];
5651cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
5652cd3331d0Smrg	}
5653d522f475Smrg    }
5654d522f475Smrg    return res_array;
5655d522f475Smrg}
5656d522f475Smrg
5657d522f475Smrg/*
5658d522f475Smrg * The first time this is called, construct sorted index to the main program's
5659d522f475Smrg * list of options, taking into account the on/off options which will be
5660d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
5661d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
5662d522f475Smrg */
5663d522f475SmrgOptionHelp *
5664d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
5665d522f475Smrg{
5666d522f475Smrg    static OptionHelp *opt_array = 0;
5667d522f475Smrg
5668d522f475Smrg#ifdef NO_LEAKS
5669d522f475Smrg    if (descs == 0 && opt_array != 0) {
5670d522f475Smrg	sortedOptDescs(descs, numDescs);
5671d522f475Smrg	free(opt_array);
5672d522f475Smrg	opt_array = 0;
5673d522f475Smrg	return 0;
5674d522f475Smrg    } else if (options == 0 || descs == 0) {
5675d522f475Smrg	return 0;
5676d522f475Smrg    }
5677d522f475Smrg#endif
5678d522f475Smrg
5679d522f475Smrg    if (opt_array == 0) {
5680cd3331d0Smrg	size_t opt_count, j;
5681d522f475Smrg#if OPT_TRACE
5682d522f475Smrg	Cardinal k;
5683d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
5684d522f475Smrg	int code;
5685cd3331d0Smrg	const char *mesg;
5686d522f475Smrg#else
5687d522f475Smrg	(void) descs;
5688d522f475Smrg	(void) numDescs;
5689d522f475Smrg#endif
5690d522f475Smrg
5691d522f475Smrg	/* count 'options' and make a sorted index to it */
5692d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
5693d522f475Smrg	    ;
5694d522f475Smrg	}
5695d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
5696d522f475Smrg	for (j = 0; j < opt_count; j++)
5697d522f475Smrg	    opt_array[j] = options[j];
5698d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
5699d522f475Smrg
5700d522f475Smrg	/* supply the "turn on/off" strings if needed */
5701d522f475Smrg#if OPT_TRACE
5702d522f475Smrg	for (j = 0; j < opt_count; j++) {
5703712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
5704c219fbebSmrg		char temp[80];
5705cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
5706d522f475Smrg		for (k = 0; k < numDescs; ++k) {
5707cd3331d0Smrg		    const char *value = res_array[k].value;
5708d522f475Smrg		    if (res_array[k].option[0] == '-') {
5709d522f475Smrg			code = -1;
5710d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
5711d522f475Smrg			code = 1;
5712d522f475Smrg		    } else {
5713d522f475Smrg			code = 0;
5714d522f475Smrg		    }
57153367019cSmrg		    sprintf(temp, "%.*s",
57163367019cSmrg			    (int) sizeof(temp) - 2,
57173367019cSmrg			    opt_array[j].desc);
5718c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
5719d522f475Smrg			code = -code;
5720d522f475Smrg		    if (code != 0
5721d522f475Smrg			&& res_array[k].value != 0
5722d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
5723d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
5724d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
5725d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
5726d522f475Smrg			    mesg = "turn on/off";
5727d522f475Smrg			} else {
5728d522f475Smrg			    mesg = "turn off/on";
5729d522f475Smrg			}
5730d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
5731712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
5732d522f475Smrg				char *s = CastMallocN(char,
5733d522f475Smrg						      strlen(mesg)
5734d522f475Smrg						      + 1
5735d522f475Smrg						      + strlen(opt_array[j].desc));
5736d522f475Smrg				if (s != 0) {
5737d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
5738d522f475Smrg				    opt_array[j].desc = s;
5739d522f475Smrg				}
5740d522f475Smrg			    } else {
5741d522f475Smrg				TRACE(("OOPS "));
5742d522f475Smrg			    }
5743d522f475Smrg			}
5744d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
5745d522f475Smrg			       mesg,
5746d522f475Smrg			       res_array[k].option,
5747d522f475Smrg			       res_array[k].value,
5748d522f475Smrg			       opt_array[j].opt,
5749d522f475Smrg			       opt_array[j].desc));
5750d522f475Smrg			break;
5751d522f475Smrg		    }
5752d522f475Smrg		}
5753d522f475Smrg	    }
5754d522f475Smrg	}
5755d522f475Smrg#endif
5756d522f475Smrg    }
5757d522f475Smrg    return opt_array;
5758d522f475Smrg}
5759d522f475Smrg
5760d522f475Smrg/*
5761d522f475Smrg * Report the character-type locale that xterm was started in.
5762d522f475Smrg */
57633367019cSmrgString
5764d522f475SmrgxtermEnvLocale(void)
5765d522f475Smrg{
57663367019cSmrg    static String result;
5767d522f475Smrg
5768d522f475Smrg    if (result == 0) {
5769d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
5770cd3331d0Smrg	    result = x_strdup("C");
5771cd3331d0Smrg	} else {
5772cd3331d0Smrg	    result = x_strdup(result);
5773d522f475Smrg	}
5774d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
5775d522f475Smrg    }
5776d522f475Smrg    return result;
5777d522f475Smrg}
5778d522f475Smrg
5779d522f475Smrgchar *
5780d522f475SmrgxtermEnvEncoding(void)
5781d522f475Smrg{
5782d522f475Smrg    static char *result;
5783d522f475Smrg
5784d522f475Smrg    if (result == 0) {
5785d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5786d522f475Smrg	result = nl_langinfo(CODESET);
5787d522f475Smrg#else
5788d522f475Smrg	char *locale = xtermEnvLocale();
5789d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
5790d522f475Smrg	    result = "ASCII";
5791d522f475Smrg	} else {
5792d522f475Smrg	    result = "ISO-8859-1";
5793d522f475Smrg	}
5794d522f475Smrg#endif
5795d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
5796d522f475Smrg    }
5797d522f475Smrg    return result;
5798d522f475Smrg}
5799d522f475Smrg
5800d522f475Smrg#if OPT_WIDE_CHARS
5801d522f475Smrg/*
5802d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
5803d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
5804d522f475Smrg * various library calls.
5805d522f475Smrg */
5806d522f475SmrgBool
5807d522f475SmrgxtermEnvUTF8(void)
5808d522f475Smrg{
5809d522f475Smrg    static Bool init = False;
5810d522f475Smrg    static Bool result = False;
5811d522f475Smrg
5812d522f475Smrg    if (!init) {
5813d522f475Smrg	init = True;
5814d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5815d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
5816d522f475Smrg#else
5817fa3f02f3Smrg	{
5818fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
5819fa3f02f3Smrg	    int n;
5820fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
5821fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
5822fa3f02f3Smrg	    }
5823fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
5824fa3f02f3Smrg		result = True;
5825fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
5826fa3f02f3Smrg		result = True;
5827fa3f02f3Smrg	    free(locale);
5828fa3f02f3Smrg	}
5829d522f475Smrg#endif
5830d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
5831d522f475Smrg    }
5832d522f475Smrg    return result;
5833d522f475Smrg}
5834d522f475Smrg#endif /* OPT_WIDE_CHARS */
5835d522f475Smrg
5836b7c89284Ssnj/*
5837b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
5838b7c89284Ssnj */
5839b7c89284SsnjXtermWidget
5840b7c89284SsnjgetXtermWidget(Widget w)
5841b7c89284Ssnj{
5842b7c89284Ssnj    XtermWidget xw;
5843b7c89284Ssnj
5844b7c89284Ssnj    if (w == 0) {
5845b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
5846b7c89284Ssnj	if (!IsXtermWidget(xw)) {
5847b7c89284Ssnj	    xw = 0;
5848b7c89284Ssnj	}
5849b7c89284Ssnj    } else if (IsXtermWidget(w)) {
5850b7c89284Ssnj	xw = (XtermWidget) w;
5851b7c89284Ssnj    } else {
5852b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
5853b7c89284Ssnj    }
5854b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
5855b7c89284Ssnj    return xw;
5856b7c89284Ssnj}
58573367019cSmrg
58583367019cSmrg#if OPT_SESSION_MGT
58593367019cSmrgstatic void
58603367019cSmrgdie_callback(Widget w GCC_UNUSED,
58613367019cSmrg	     XtPointer client_data GCC_UNUSED,
58623367019cSmrg	     XtPointer call_data GCC_UNUSED)
58633367019cSmrg{
58643367019cSmrg    NormalExit();
58653367019cSmrg}
58663367019cSmrg
58673367019cSmrgstatic void
58683367019cSmrgsave_callback(Widget w GCC_UNUSED,
58693367019cSmrg	      XtPointer client_data GCC_UNUSED,
58703367019cSmrg	      XtPointer call_data)
58713367019cSmrg{
58723367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
58733367019cSmrg    /* we have nothing to save */
58743367019cSmrg    token->save_success = True;
58753367019cSmrg}
58763367019cSmrg
58773367019cSmrgstatic void
58783367019cSmrgicewatch(IceConn iceConn,
58793367019cSmrg	 IcePointer clientData GCC_UNUSED,
58803367019cSmrg	 Bool opening,
58813367019cSmrg	 IcePointer * watchData GCC_UNUSED)
58823367019cSmrg{
58833367019cSmrg    if (opening) {
58843367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
58853367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
58863367019cSmrg    } else {
58873367019cSmrg	ice_fd = -1;
58883367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
58893367019cSmrg    }
58903367019cSmrg}
58913367019cSmrg
58923367019cSmrgvoid
58933367019cSmrgxtermOpenSession(void)
58943367019cSmrg{
58953367019cSmrg    if (resource.sessionMgt) {
58963367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
58973367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
58983367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
58993367019cSmrg    }
59003367019cSmrg}
59013367019cSmrg
59023367019cSmrgvoid
59033367019cSmrgxtermCloseSession(void)
59043367019cSmrg{
59053367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
59063367019cSmrg}
59073367019cSmrg#endif /* OPT_SESSION_MGT */
59083367019cSmrg
59093367019cSmrgWidget
59103367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
59113367019cSmrg		     String my_class,
59123367019cSmrg		     XrmOptionDescRec * options,
59133367019cSmrg		     Cardinal num_options,
59143367019cSmrg		     int *argc_in_out,
5915fa3f02f3Smrg		     String *argv_in_out,
5916fa3f02f3Smrg		     String *fallback_resources,
59173367019cSmrg		     WidgetClass widget_class,
59183367019cSmrg		     ArgList args,
59193367019cSmrg		     Cardinal num_args)
59203367019cSmrg{
59213367019cSmrg    Widget result;
59223367019cSmrg
59233367019cSmrg    XtSetErrorHandler(xt_error);
59243367019cSmrg#if OPT_SESSION_MGT
59253367019cSmrg    result = XtOpenApplication(app_context_return,
59263367019cSmrg			       my_class,
59273367019cSmrg			       options,
59283367019cSmrg			       num_options,
59293367019cSmrg			       argc_in_out,
59303367019cSmrg			       argv_in_out,
59313367019cSmrg			       fallback_resources,
59323367019cSmrg			       widget_class,
59333367019cSmrg			       args,
59343367019cSmrg			       num_args);
59353367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
59363367019cSmrg#else
59379a64e1c5Smrg    (void) widget_class;
59389a64e1c5Smrg    (void) args;
59399a64e1c5Smrg    (void) num_args;
59403367019cSmrg    result = XtAppInitialize(app_context_return,
59413367019cSmrg			     my_class,
59423367019cSmrg			     options,
59433367019cSmrg			     num_options,
59443367019cSmrg			     argc_in_out,
59453367019cSmrg			     argv_in_out,
59463367019cSmrg			     fallback_resources,
59473367019cSmrg			     NULL, 0);
59483367019cSmrg#endif /* OPT_SESSION_MGT */
59493367019cSmrg    XtSetErrorHandler((XtErrorHandler) 0);
59503367019cSmrg
59513367019cSmrg    return result;
59523367019cSmrg}
59533367019cSmrg
59543367019cSmrgstatic int x11_errors;
59553367019cSmrg
59563367019cSmrgstatic int
59579a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
59583367019cSmrg{
59593367019cSmrg    (void) display;
59603367019cSmrg    (void) error_event;
59613367019cSmrg    ++x11_errors;
59623367019cSmrg    return 0;
59633367019cSmrg}
59643367019cSmrg
59653367019cSmrgBoolean
5966fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
59673367019cSmrg{
59683367019cSmrg    Boolean result = False;
59693367019cSmrg    Status code;
59703367019cSmrg
59713367019cSmrg    memset(attrs, 0, sizeof(*attrs));
59723367019cSmrg    if (win != None) {
59733367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
59743367019cSmrg	x11_errors = 0;
59753367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
59763367019cSmrg	XSetErrorHandler(save);
59773367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
59783367019cSmrg	if (result) {
59793367019cSmrg	    TRACE_WIN_ATTRS(attrs);
59803367019cSmrg	} else {
59813367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
59823367019cSmrg	}
59833367019cSmrg    }
59843367019cSmrg    return result;
59853367019cSmrg}
59863367019cSmrg
59873367019cSmrgBoolean
5988fa3f02f3SmrgxtermGetWinProp(Display *display,
59893367019cSmrg		Window win,
59903367019cSmrg		Atom property,
59913367019cSmrg		long long_offset,
59923367019cSmrg		long long_length,
59933367019cSmrg		Atom req_type,
59949a64e1c5Smrg		Atom *actual_type_return,
59953367019cSmrg		int *actual_format_return,
59963367019cSmrg		unsigned long *nitems_return,
59973367019cSmrg		unsigned long *bytes_after_return,
59983367019cSmrg		unsigned char **prop_return)
59993367019cSmrg{
60003367019cSmrg    Boolean result = True;
60013367019cSmrg
60023367019cSmrg    if (win != None) {
60033367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
60043367019cSmrg	x11_errors = 0;
60053367019cSmrg	if (XGetWindowProperty(display,
60063367019cSmrg			       win,
60073367019cSmrg			       property,
60083367019cSmrg			       long_offset,
60093367019cSmrg			       long_length,
60103367019cSmrg			       False,
60113367019cSmrg			       req_type,
60123367019cSmrg			       actual_type_return,
60133367019cSmrg			       actual_format_return,
60143367019cSmrg			       nitems_return,
60153367019cSmrg			       bytes_after_return,
60163367019cSmrg			       prop_return) == Success
60173367019cSmrg	    && x11_errors == 0) {
60183367019cSmrg	    result = True;
60193367019cSmrg	}
60203367019cSmrg	XSetErrorHandler(save);
60213367019cSmrg    }
60223367019cSmrg    return result;
60233367019cSmrg}
60243367019cSmrg
60253367019cSmrgvoid
60263367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
60273367019cSmrg{
60283367019cSmrg    Display *dpy = XtDisplay(toplevel);
60293367019cSmrg    XWindowAttributes attrs;
60303367019cSmrg
60313367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
60323367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
60333367019cSmrg	XtermWidget xw = term;
60343367019cSmrg	TScreen *screen = TScreenOf(xw);
60353367019cSmrg
60363367019cSmrg	XtRealizeWidget(toplevel);
60373367019cSmrg
60383367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
60393367019cSmrg	       XtWindow(toplevel),
60403367019cSmrg	       winToEmbedInto));
60413367019cSmrg	XReparentWindow(dpy,
60423367019cSmrg			XtWindow(toplevel),
60433367019cSmrg			winToEmbedInto, 0, 0);
60443367019cSmrg
60453367019cSmrg	screen->embed_high = (Dimension) attrs.height;
60463367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
60473367019cSmrg    }
60483367019cSmrg}
6049