misc.c revision 3367019c
13367019cSmrg/* $XTermId: misc.c,v 1.660 2013/05/26 21:16:20 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
43367019cSmrg * Copyright 1999-2012,2013 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 <xcharmouse.h>
95d522f475Smrg#include <xstrings.h>
96d522f475Smrg#include <xtermcap.h>
97d522f475Smrg#include <VTparse.h>
98d522f475Smrg
99d522f475Smrg#include <assert.h>
100d522f475Smrg
101d522f475Smrg#if (XtSpecificationRelease < 6)
102d522f475Smrg#ifndef X_GETTIMEOFDAY
103d522f475Smrg#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0)
104d522f475Smrg#endif
105d522f475Smrg#endif
106d522f475Smrg
107d522f475Smrg#ifdef VMS
108d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
109d522f475Smrg#ifdef ALLOWLOGFILEEXEC
110d522f475Smrg#undef ALLOWLOGFILEEXEC
111d522f475Smrg#endif
112d522f475Smrg#endif /* VMS */
113d522f475Smrg
114d522f475Smrg#if OPT_TEK4014
115d522f475Smrg#define OUR_EVENT(event,Type) \
116d522f475Smrg		(event.type == Type && \
117d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
118d522f475Smrg		    (tekWidget && \
119d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
120d522f475Smrg#else
121d522f475Smrg#define OUR_EVENT(event,Type) \
122d522f475Smrg		(event.type == Type && \
123d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
124d522f475Smrg#endif
125d522f475Smrg
1263367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
127d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
128d522f475Smrg
1293367019cSmrgstatic char emptyString[] = "";
1303367019cSmrg
131d522f475Smrg#if OPT_EXEC_XTERM
132d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
133d522f475Smrg   error; adapted from libc docs */
134d522f475Smrgstatic char *
135d522f475SmrgReadlink(const char *filename)
136d522f475Smrg{
137d522f475Smrg    char *buf = NULL;
138cd3331d0Smrg    size_t size = 100;
139d522f475Smrg    int n;
140d522f475Smrg
141d522f475Smrg    for (;;) {
142d522f475Smrg	buf = TypeRealloc(char, size, buf);
143d522f475Smrg	memset(buf, 0, size);
144d522f475Smrg
145cd3331d0Smrg	n = (int) readlink(filename, buf, size);
146d522f475Smrg	if (n < 0) {
147d522f475Smrg	    free(buf);
148d522f475Smrg	    return NULL;
149d522f475Smrg	}
150d522f475Smrg
151d522f475Smrg	if ((unsigned) n < size) {
152d522f475Smrg	    return buf;
153d522f475Smrg	}
154d522f475Smrg
155d522f475Smrg	size *= 2;
156d522f475Smrg    }
157d522f475Smrg}
158d522f475Smrg#endif /* OPT_EXEC_XTERM */
159d522f475Smrg
160d522f475Smrgstatic void
161d522f475SmrgSleep(int msec)
162d522f475Smrg{
163d522f475Smrg    static struct timeval select_timeout;
164d522f475Smrg
165d522f475Smrg    select_timeout.tv_sec = 0;
166d522f475Smrg    select_timeout.tv_usec = msec * 1000;
167d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
168d522f475Smrg}
169d522f475Smrg
170d522f475Smrgstatic void
1713367019cSmrgselectwindow(XtermWidget xw, int flag)
172d522f475Smrg{
1733367019cSmrg    TScreen *screen = TScreenOf(xw);
1743367019cSmrg
175d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
176d522f475Smrg
177d522f475Smrg#if OPT_TEK4014
1783367019cSmrg    if (TEK4014_ACTIVE(xw)) {
179d522f475Smrg	if (!Ttoggled)
180d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
181d522f475Smrg	screen->select |= flag;
182d522f475Smrg	if (!Ttoggled)
183d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
184d522f475Smrg    } else
185d522f475Smrg#endif
186d522f475Smrg    {
1873367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
1883367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
1893367019cSmrg	if (input && input->xic)
1903367019cSmrg	    XSetICFocus(input->xic);
1913367019cSmrg#endif
192d522f475Smrg
193d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
194d522f475Smrg	    HideCursor();
195d522f475Smrg	screen->select |= flag;
196d522f475Smrg	if (screen->cursor_state)
197d522f475Smrg	    ShowCursor();
198d522f475Smrg    }
199cd3331d0Smrg    GetScrollLock(screen);
200d522f475Smrg}
201d522f475Smrg
202d522f475Smrgstatic void
2033367019cSmrgunselectwindow(XtermWidget xw, int flag)
204d522f475Smrg{
2053367019cSmrg    TScreen *screen = TScreenOf(xw);
2063367019cSmrg
207d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
208d522f475Smrg
2093367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
210d522f475Smrg	screen->hide_pointer = False;
2113367019cSmrg	xtermDisplayCursor(xw);
212d522f475Smrg    }
213d522f475Smrg
214d522f475Smrg    if (!screen->always_highlight) {
215d522f475Smrg#if OPT_TEK4014
2163367019cSmrg	if (TEK4014_ACTIVE(xw)) {
217d522f475Smrg	    if (!Ttoggled)
218d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
219d522f475Smrg	    screen->select &= ~flag;
220d522f475Smrg	    if (!Ttoggled)
221d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
222d522f475Smrg	} else
223d522f475Smrg#endif
224d522f475Smrg	{
2253367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
2263367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2273367019cSmrg	    if (input && input->xic)
2283367019cSmrg		XUnsetICFocus(input->xic);
2293367019cSmrg#endif
230d522f475Smrg
231d522f475Smrg	    screen->select &= ~flag;
232d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
233d522f475Smrg		HideCursor();
234d522f475Smrg	    if (screen->cursor_state)
235d522f475Smrg		ShowCursor();
236d522f475Smrg	}
237d522f475Smrg    }
238d522f475Smrg}
239d522f475Smrg
240d522f475Smrgstatic void
241d522f475SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent * ev)
242d522f475Smrg{
243d522f475Smrg    TScreen *screen = TScreenOf(xw);
244d522f475Smrg
245d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
246cd3331d0Smrg    TRACE_FOCUS(xw, ev);
247cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
248cd3331d0Smrg	ev->focus &&
249cd3331d0Smrg	!(screen->select & FOCUS))
2503367019cSmrg	selectwindow(xw, INWINDOW);
251d522f475Smrg}
252d522f475Smrg
253d522f475Smrgstatic void
254d522f475SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent * ev)
255d522f475Smrg{
256d522f475Smrg    TScreen *screen = TScreenOf(xw);
257d522f475Smrg
258d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
259cd3331d0Smrg    TRACE_FOCUS(xw, ev);
260cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
261cd3331d0Smrg	ev->focus &&
262cd3331d0Smrg	!(screen->select & FOCUS))
2633367019cSmrg	unselectwindow(xw, INWINDOW);
264d522f475Smrg}
265d522f475Smrg
266d522f475Smrg#ifndef XUrgencyHint
267d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
268d522f475Smrg#endif
269d522f475Smrg
270d522f475Smrgstatic void
271c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
272d522f475Smrg{
273c219fbebSmrg    TScreen *screen = TScreenOf(xw);
274c219fbebSmrg
275d522f475Smrg    if (screen->bellIsUrgent) {
276c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
277d522f475Smrg	if (h != 0) {
278c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
279d522f475Smrg		h->flags |= XUrgencyHint;
280d522f475Smrg	    } else {
281d522f475Smrg		h->flags &= ~XUrgencyHint;
282d522f475Smrg	    }
283c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
284d522f475Smrg	}
285d522f475Smrg    }
286d522f475Smrg}
287d522f475Smrg
288d522f475Smrgvoid
289d522f475Smrgdo_xevents(void)
290d522f475Smrg{
291d522f475Smrg    TScreen *screen = TScreenOf(term);
292d522f475Smrg
2933367019cSmrg    if (xtermAppPending()
294d522f475Smrg	||
295d522f475Smrg#if defined(VMS) || defined(__VMS)
296d522f475Smrg	screen->display->qlen > 0
297d522f475Smrg#else
298d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
299d522f475Smrg#endif
300d522f475Smrg	)
301d522f475Smrg	xevents();
302d522f475Smrg}
303d522f475Smrg
304d522f475Smrgvoid
305d522f475SmrgxtermDisplayCursor(XtermWidget xw)
306d522f475Smrg{
307d522f475Smrg    TScreen *screen = TScreenOf(xw);
308d522f475Smrg
309d522f475Smrg    if (screen->Vshow) {
310d522f475Smrg	if (screen->hide_pointer) {
311d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
312d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
313d522f475Smrg	} else {
314d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
315d522f475Smrg	    recolor_cursor(screen,
316d522f475Smrg			   screen->pointer_cursor,
317d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
318d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
319d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
320d522f475Smrg	}
321d522f475Smrg    }
322d522f475Smrg}
323d522f475Smrg
324d522f475Smrgvoid
325d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
326d522f475Smrg{
327d522f475Smrg    static int tried = -1;
328d522f475Smrg    TScreen *screen = TScreenOf(xw);
329d522f475Smrg
330d522f475Smrg#if OPT_TEK4014
331d522f475Smrg    if (TEK4014_SHOWN(xw))
332d522f475Smrg	enable = True;
333d522f475Smrg#endif
334d522f475Smrg
335d522f475Smrg    /*
336d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
337d522f475Smrg     * the mouse-mode:
338d522f475Smrg     */
339d522f475Smrg    if (!enable) {
340d522f475Smrg	switch (screen->pointer_mode) {
341d522f475Smrg	case pNever:
342d522f475Smrg	    enable = True;
343d522f475Smrg	    break;
344d522f475Smrg	case pNoMouse:
345d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
346d522f475Smrg		enable = True;
347d522f475Smrg	    break;
348d522f475Smrg	case pAlways:
3493367019cSmrg	case pFocused:
350d522f475Smrg	    break;
351d522f475Smrg	}
352d522f475Smrg    }
353d522f475Smrg
354d522f475Smrg    if (enable) {
355d522f475Smrg	if (screen->hide_pointer) {
356d522f475Smrg	    screen->hide_pointer = False;
357d522f475Smrg	    xtermDisplayCursor(xw);
358d522f475Smrg	    switch (screen->send_mouse_pos) {
359d522f475Smrg	    case ANY_EVENT_MOUSE:
360d522f475Smrg		break;
361d522f475Smrg	    default:
362d522f475Smrg		MotionOff(screen, xw);
363d522f475Smrg		break;
364d522f475Smrg	    }
365d522f475Smrg	}
366d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
367d522f475Smrg	if (screen->hidden_cursor == 0) {
368d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
369d522f475Smrg	}
370d522f475Smrg	if (screen->hidden_cursor == 0) {
371d522f475Smrg	    tried = 1;
372d522f475Smrg	} else {
373d522f475Smrg	    tried = 0;
374d522f475Smrg	    screen->hide_pointer = True;
375d522f475Smrg	    xtermDisplayCursor(xw);
376d522f475Smrg	    MotionOn(screen, xw);
377d522f475Smrg	}
378d522f475Smrg    }
379d522f475Smrg}
380d522f475Smrg
3813367019cSmrg#if OPT_TRACE
3823367019cSmrgstatic void
3833367019cSmrgTraceExposeEvent(XEvent * arg)
3843367019cSmrg{
3853367019cSmrg    XExposeEvent *event = (XExposeEvent *) arg;
3863367019cSmrg
3873367019cSmrg    TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n",
3883367019cSmrg	   event->serial,
3893367019cSmrg	   event->count,
3903367019cSmrg	   event->y,
3913367019cSmrg	   event->x,
3923367019cSmrg	   event->height,
3933367019cSmrg	   event->width,
3943367019cSmrg	   event->window));
3953367019cSmrg}
3963367019cSmrg
3973367019cSmrg#else
3983367019cSmrg#define TraceExposeEvent(event)	/* nothing */
3993367019cSmrg#endif
4003367019cSmrg
4013367019cSmrg/* true if p contains q */
4023367019cSmrg#define ExposeContains(p,q) \
4033367019cSmrg	    ((p)->y <= (q)->y \
4043367019cSmrg	  && (p)->x <= (q)->x \
4053367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
4063367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
4073367019cSmrg
4083367019cSmrgstatic XtInputMask
4093367019cSmrgmergeExposeEvents(XEvent * target)
4103367019cSmrg{
4113367019cSmrg    XEvent next_event;
4123367019cSmrg    XExposeEvent *p, *q;
4133367019cSmrg
4143367019cSmrg    TRACE(("pending Expose...?\n"));
4153367019cSmrg    TraceExposeEvent(target);
4163367019cSmrg    XtAppNextEvent(app_con, target);
4173367019cSmrg    p = (XExposeEvent *) target;
4183367019cSmrg
4193367019cSmrg    while (XtAppPending(app_con)
4203367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4213367019cSmrg	   && next_event.type == Expose) {
4223367019cSmrg	Boolean merge_this = False;
4233367019cSmrg
4243367019cSmrg	TraceExposeEvent(&next_event);
4253367019cSmrg	q = (XExposeEvent *) (&next_event);
4263367019cSmrg	XtAppNextEvent(app_con, &next_event);
4273367019cSmrg
4283367019cSmrg	/*
4293367019cSmrg	 * If either window is contained within the other, merge the events.
4303367019cSmrg	 * The traces show that there are also cases where a full repaint of
4313367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4323367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4333367019cSmrg	 * to skim several events ahead.
4343367019cSmrg	 */
4353367019cSmrg	if (p->window == q->window) {
4363367019cSmrg	    if (ExposeContains(p, q)) {
4373367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4383367019cSmrg		merge_this = True;
4393367019cSmrg		next_event = *target;
4403367019cSmrg	    } else if (ExposeContains(q, p)) {
4413367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4423367019cSmrg		merge_this = True;
4433367019cSmrg	    }
4443367019cSmrg	}
4453367019cSmrg	if (!merge_this) {
4463367019cSmrg	    XtDispatchEvent(target);
4473367019cSmrg	}
4483367019cSmrg	*target = next_event;
4493367019cSmrg    }
4503367019cSmrg    XtDispatchEvent(target);
4513367019cSmrg    return XtAppPending(app_con);
4523367019cSmrg}
4533367019cSmrg
4543367019cSmrg#if OPT_TRACE
4553367019cSmrgstatic void
4563367019cSmrgTraceConfigureEvent(XEvent * arg)
4573367019cSmrg{
4583367019cSmrg    XConfigureEvent *event = (XConfigureEvent *) arg;
4593367019cSmrg
4603367019cSmrg    TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n",
4613367019cSmrg	   event->serial,
4623367019cSmrg	   event->y,
4633367019cSmrg	   event->x,
4643367019cSmrg	   event->height,
4653367019cSmrg	   event->width,
4663367019cSmrg	   event->window));
4673367019cSmrg}
4683367019cSmrg
4693367019cSmrg#else
4703367019cSmrg#define TraceConfigureEvent(event)	/* nothing */
4713367019cSmrg#endif
4723367019cSmrg
4733367019cSmrg/*
4743367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4753367019cSmrg * event.  Remove that from the queue so we can look further.
4763367019cSmrg *
4773367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4783367019cSmrg * that.  If the adjacent events are for different windows, process the older
4793367019cSmrg * event and update the event used for comparing windows.  If they are for the
4803367019cSmrg * same window, only the newer event is of interest.
4813367019cSmrg *
4823367019cSmrg * Finally, process the (remaining) configure-notify event.
4833367019cSmrg */
4843367019cSmrgstatic XtInputMask
4853367019cSmrgmergeConfigureEvents(XEvent * target)
4863367019cSmrg{
4873367019cSmrg    XEvent next_event;
4883367019cSmrg    XConfigureEvent *p, *q;
4893367019cSmrg
4903367019cSmrg    XtAppNextEvent(app_con, target);
4913367019cSmrg    p = (XConfigureEvent *) target;
4923367019cSmrg
4933367019cSmrg    TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no"));
4943367019cSmrg    TraceConfigureEvent(target);
4953367019cSmrg
4963367019cSmrg    if (XtAppPending(app_con)
4973367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
4983367019cSmrg	&& next_event.type == ConfigureNotify) {
4993367019cSmrg	Boolean merge_this = False;
5003367019cSmrg
5013367019cSmrg	TraceConfigureEvent(&next_event);
5023367019cSmrg	XtAppNextEvent(app_con, &next_event);
5033367019cSmrg	q = (XConfigureEvent *) (&next_event);
5043367019cSmrg
5053367019cSmrg	if (p->window == q->window) {
5063367019cSmrg	    TRACE(("pending Configure...merged\n"));
5073367019cSmrg	    merge_this = True;
5083367019cSmrg	}
5093367019cSmrg	if (!merge_this) {
5103367019cSmrg	    TRACE(("pending Configure...skipped\n"));
5113367019cSmrg	    XtDispatchEvent(target);
5123367019cSmrg	}
5133367019cSmrg	*target = next_event;
5143367019cSmrg    }
5153367019cSmrg    XtDispatchEvent(target);
5163367019cSmrg    return XtAppPending(app_con);
5173367019cSmrg}
5183367019cSmrg
5193367019cSmrg/*
5203367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5213367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5223367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5233367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5243367019cSmrg * point.
5253367019cSmrg *
5263367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5273367019cSmrg */
5283367019cSmrgXtInputMask
5293367019cSmrgxtermAppPending(void)
5303367019cSmrg{
5313367019cSmrg    XtInputMask result = XtAppPending(app_con);
5323367019cSmrg    XEvent this_event;
5333367019cSmrg
5343367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
5353367019cSmrg	if (this_event.type == Expose) {
5363367019cSmrg	    result = mergeExposeEvents(&this_event);
5373367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5383367019cSmrg	    result = mergeConfigureEvents(&this_event);
5393367019cSmrg	} else {
5403367019cSmrg	    TRACE(("pending %s\n", visibleEventType(this_event.type)));
5413367019cSmrg	    break;
5423367019cSmrg	}
5433367019cSmrg    }
5443367019cSmrg    return result;
5453367019cSmrg}
5463367019cSmrg
547d522f475Smrgvoid
548d522f475Smrgxevents(void)
549d522f475Smrg{
550d522f475Smrg    XtermWidget xw = term;
551d522f475Smrg    TScreen *screen = TScreenOf(xw);
552d522f475Smrg    XEvent event;
553d522f475Smrg    XtInputMask input_mask;
554d522f475Smrg
555d522f475Smrg    if (need_cleanup)
5563367019cSmrg	NormalExit();
557d522f475Smrg
558d522f475Smrg    if (screen->scroll_amt)
559d522f475Smrg	FlushScroll(xw);
560d522f475Smrg    /*
561d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
562d522f475Smrg     * will process the timeout and return without blockng on the
563cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
564d522f475Smrg     * with select().
565d522f475Smrg     */
5663367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
567cd3331d0Smrg	if (input_mask & XtIMTimer)
568cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
569d522f475Smrg#if OPT_SESSION_MGT
570cd3331d0Smrg	/*
571cd3331d0Smrg	 * Session management events are alternative input events. Deal with
572cd3331d0Smrg	 * them in the same way.
573cd3331d0Smrg	 */
574cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
575cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
576d522f475Smrg#endif
577cd3331d0Smrg	else
578cd3331d0Smrg	    break;
579cd3331d0Smrg    }
580d522f475Smrg
581d522f475Smrg    /*
582d522f475Smrg     * If there's no XEvents, don't wait around...
583d522f475Smrg     */
584d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
585d522f475Smrg	return;
586d522f475Smrg    do {
587d522f475Smrg	/*
588d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
589d522f475Smrg	 * We simply ignore all events except for those not passed down to
590d522f475Smrg	 * this function, e.g., those handled in in_put().
591d522f475Smrg	 */
592d522f475Smrg	if (screen->waitingForTrackInfo) {
593d522f475Smrg	    Sleep(10);
594d522f475Smrg	    return;
595d522f475Smrg	}
596d522f475Smrg	XtAppNextEvent(app_con, &event);
597d522f475Smrg	/*
598d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
599d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
600d522f475Smrg	 * looking at the event ourselves we make sure that we can
601d522f475Smrg	 * do the right thing.
602d522f475Smrg	 */
603d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
604d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
605d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
606d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
607d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
608d522f475Smrg#if OPT_DEC_LOCATOR
609d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
610d522f475Smrg#endif /* OPT_DEC_LOCATOR */
611d522f475Smrg		   )
612d522f475Smrg		   && event.xany.type == MotionNotify
613d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
614d522f475Smrg	    SendMousePosition(xw, &event);
615cb4a1343Smrg	    xtermShowPointer(xw, True);
616d522f475Smrg	    continue;
617d522f475Smrg	}
618d522f475Smrg
619cb4a1343Smrg	/*
620cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
621cb4a1343Smrg	 * mouse pointer back on.
622cb4a1343Smrg	 */
623cb4a1343Smrg	if (screen->hide_pointer) {
6243367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6253367019cSmrg		switch (event.xany.type) {
6263367019cSmrg		case MotionNotify:
6273367019cSmrg		    xtermShowPointer(xw, True);
6283367019cSmrg		    break;
6293367019cSmrg		}
6303367019cSmrg	    } else {
6313367019cSmrg		switch (event.xany.type) {
6323367019cSmrg		case KeyPress:
6333367019cSmrg		case KeyRelease:
6343367019cSmrg		case ButtonPress:
6353367019cSmrg		case ButtonRelease:
6363367019cSmrg		    /* also these... */
6373367019cSmrg		case Expose:
6383367019cSmrg		case NoExpose:
6393367019cSmrg		case PropertyNotify:
6403367019cSmrg		case ClientMessage:
6413367019cSmrg		    break;
6423367019cSmrg		default:
6433367019cSmrg		    xtermShowPointer(xw, True);
6443367019cSmrg		    break;
6453367019cSmrg		}
646cb4a1343Smrg	    }
647cb4a1343Smrg	}
648cb4a1343Smrg
649d522f475Smrg	if (!event.xany.send_event ||
650d522f475Smrg	    screen->allowSendEvents ||
651d522f475Smrg	    ((event.xany.type != KeyPress) &&
652d522f475Smrg	     (event.xany.type != KeyRelease) &&
653d522f475Smrg	     (event.xany.type != ButtonPress) &&
654d522f475Smrg	     (event.xany.type != ButtonRelease))) {
655d522f475Smrg
656d522f475Smrg	    XtDispatchEvent(&event);
657d522f475Smrg	}
6583367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
659d522f475Smrg}
660d522f475Smrg
661d522f475Smrgstatic Cursor
662d522f475Smrgmake_hidden_cursor(XtermWidget xw)
663d522f475Smrg{
664d522f475Smrg    TScreen *screen = TScreenOf(xw);
665d522f475Smrg    Cursor c;
666d522f475Smrg    Display *dpy = screen->display;
667d522f475Smrg    XFontStruct *fn;
668d522f475Smrg
669d522f475Smrg    static XColor dummy;
670d522f475Smrg
671d522f475Smrg    /*
672d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
673d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
674b7c89284Ssnj     * server insists on drawing _something_.
675d522f475Smrg     */
676d522f475Smrg    TRACE(("Ask for nil2 font\n"));
677d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
678d522f475Smrg	TRACE(("...Ask for fixed font\n"));
679b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
680d522f475Smrg    }
681d522f475Smrg
682d522f475Smrg    if (fn != 0) {
683d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
684d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
685d522f475Smrg	XFreeFont(dpy, fn);
686d522f475Smrg    } else {
687d522f475Smrg	c = 0;
688d522f475Smrg    }
689d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
690d522f475Smrg    return (c);
691d522f475Smrg}
692d522f475Smrg
693d522f475SmrgCursor
694d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
695d522f475Smrg		    unsigned long fg,	/* pixel value */
696d522f475Smrg		    unsigned long bg)	/* pixel value */
697d522f475Smrg{
698d522f475Smrg    TScreen *screen = TScreenOf(term);
699d522f475Smrg    Cursor c;
700d522f475Smrg    Display *dpy = screen->display;
701d522f475Smrg
702d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
703d522f475Smrg    if (c != None) {
704d522f475Smrg	recolor_cursor(screen, c, fg, bg);
705d522f475Smrg    }
706d522f475Smrg    return (c);
707d522f475Smrg}
708d522f475Smrg
709d522f475Smrg/* ARGSUSED */
710d522f475Smrgvoid
711d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
712d522f475Smrg		 XEvent * event,
713d522f475Smrg		 String * params GCC_UNUSED,
714d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
715d522f475Smrg{
716cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
717cd3331d0Smrg    Input(term, &event->xkey, False);
718d522f475Smrg}
719d522f475Smrg
720d522f475Smrg/* ARGSUSED */
721d522f475Smrgvoid
722d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
723d522f475Smrg			 XEvent * event,
724d522f475Smrg			 String * params GCC_UNUSED,
725d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
726d522f475Smrg{
727cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
728cd3331d0Smrg    Input(term, &event->xkey, True);
729d522f475Smrg}
730d522f475Smrg
731d522f475Smrg/* ARGSUSED */
732d522f475Smrgvoid
733d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
734d522f475Smrg		  XEvent * event GCC_UNUSED,
735d522f475Smrg		  String * params,
736d522f475Smrg		  Cardinal *nparams)
737d522f475Smrg{
738d522f475Smrg
739d522f475Smrg    if (*nparams != 1)
740d522f475Smrg	return;
741d522f475Smrg
742d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
7430d92cbfdSchristos	const char *abcdef = "ABCDEF";
7440d92cbfdSchristos	const char *xxxxxx;
745cd3331d0Smrg	Char c;
746cd3331d0Smrg	UString p;
7470d92cbfdSchristos	unsigned value = 0;
7480d92cbfdSchristos
749cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
7500d92cbfdSchristos	     '\0'; p++) {
7510d92cbfdSchristos	    value *= 16;
752d522f475Smrg	    if (c >= '0' && c <= '9')
7530d92cbfdSchristos		value += (unsigned) (c - '0');
7540d92cbfdSchristos	    else if ((xxxxxx = strchr(abcdef, c)) != 0)
7550d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
756d522f475Smrg	    else
757d522f475Smrg		break;
758d522f475Smrg	}
7590d92cbfdSchristos	if (c == '\0') {
7600d92cbfdSchristos	    Char hexval[2];
7610d92cbfdSchristos	    hexval[0] = (Char) value;
7620d92cbfdSchristos	    hexval[1] = 0;
763b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
7640d92cbfdSchristos	}
765d522f475Smrg    } else {
766cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
767d522f475Smrg    }
768d522f475Smrg}
769d522f475Smrg
770d522f475Smrg#if OPT_EXEC_XTERM
771d522f475Smrg
772d522f475Smrg#ifndef PROCFS_ROOT
773d522f475Smrg#define PROCFS_ROOT "/proc"
774d522f475Smrg#endif
775d522f475Smrg
776d522f475Smrg/* ARGSUSED */
777d522f475Smrgvoid
778d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
779d522f475Smrg		    XEvent * event GCC_UNUSED,
780d522f475Smrg		    String * params,
781d522f475Smrg		    Cardinal *nparams)
782d522f475Smrg{
783cd3331d0Smrg    TScreen *screen = TScreenOf(term);
784d522f475Smrg    char *child_cwd = NULL;
785d522f475Smrg    char *child_exe;
786d522f475Smrg    pid_t pid;
787d522f475Smrg
788d522f475Smrg    /*
789d522f475Smrg     * Try to find the actual program which is running in the child process.
790d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
791d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
792d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
793d522f475Smrg     */
794d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
795d522f475Smrg    if (!child_exe) {
796cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
797cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
798d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
799d522f475Smrg	} else {
8003367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
801d522f475Smrg	}
802d522f475Smrg	if (child_exe == 0)
803d522f475Smrg	    return;
804d522f475Smrg    }
805d522f475Smrg
806d522f475Smrg    /*
807d522f475Smrg     * Determine the current working directory of the child so that we can
808d522f475Smrg     * spawn a new terminal in the same directory.
809d522f475Smrg     *
810d522f475Smrg     * If we cannot get the CWD of the child, just use our own.
811d522f475Smrg     */
812d522f475Smrg    if (screen->pid) {
813d522f475Smrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
814d522f475Smrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid);
815d522f475Smrg	child_cwd = Readlink(child_cwd_link);
816d522f475Smrg    }
817d522f475Smrg
818d522f475Smrg    /* The reaper will take care of cleaning up the child */
819d522f475Smrg    pid = fork();
820d522f475Smrg    if (pid == -1) {
8213367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
822d522f475Smrg    } else if (!pid) {
823d522f475Smrg	/* We are the child */
824d522f475Smrg	if (child_cwd) {
825cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
826d522f475Smrg	}
827d522f475Smrg
828d522f475Smrg	if (setuid(screen->uid) == -1
829d522f475Smrg	    || setgid(screen->gid) == -1) {
8303367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
831d522f475Smrg	} else {
8320d92cbfdSchristos	    unsigned myargc = *nparams + 1;
833d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
8340d92cbfdSchristos	    unsigned n = 0;
835d522f475Smrg
836d522f475Smrg	    myargv[n++] = child_exe;
837d522f475Smrg
838d522f475Smrg	    while (n < myargc) {
8393367019cSmrg		myargv[n++] = (char *) *params++;
840d522f475Smrg	    }
841d522f475Smrg
842d522f475Smrg	    myargv[n] = 0;
843d522f475Smrg	    execv(child_exe, myargv);
844d522f475Smrg
845d522f475Smrg	    /* If we get here, we've failed */
8463367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
847d522f475Smrg	}
848d522f475Smrg	_exit(0);
849d522f475Smrg    }
8503367019cSmrg
8513367019cSmrg    /* We are the parent; clean up */
8523367019cSmrg    if (child_cwd)
8533367019cSmrg	free(child_cwd);
8543367019cSmrg    free(child_exe);
855d522f475Smrg}
856d522f475Smrg#endif /* OPT_EXEC_XTERM */
857d522f475Smrg
858d522f475Smrg/*
859d522f475Smrg * Rather than sending characters to the host, put them directly into our
860d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
861d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
862d522f475Smrg *
863d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
864d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
865d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
866d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
867d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
868d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
869d522f475Smrg */
870d522f475Smrg/* ARGSUSED */
871d522f475Smrgvoid
872d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
873d522f475Smrg		XEvent * event GCC_UNUSED,
874d522f475Smrg		String * params,
875d522f475Smrg		Cardinal *param_count)
876d522f475Smrg{
877d522f475Smrg    if (*param_count == 1) {
878cd3331d0Smrg	const char *value = params[0];
879b7c89284Ssnj	int need = (int) strlen(value);
880cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
881cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
882d522f475Smrg
883d522f475Smrg	if (have - used + need < BUF_SIZE) {
884d522f475Smrg
885cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
886d522f475Smrg
887d522f475Smrg	    TRACE(("Interpret %s\n", value));
888d522f475Smrg	    VTbuffer->update++;
889d522f475Smrg	}
890d522f475Smrg    }
891d522f475Smrg}
892d522f475Smrg
893d522f475Smrg/*ARGSUSED*/
894d522f475Smrgvoid
895d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
896d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
897d522f475Smrg		  XEvent * event GCC_UNUSED,
898d522f475Smrg		  Boolean * cont GCC_UNUSED)
899d522f475Smrg{
900d522f475Smrg    /* NOP since we handled it above */
901d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
902cd3331d0Smrg    TRACE_FOCUS(w, event);
903d522f475Smrg}
904d522f475Smrg
905d522f475Smrg/*ARGSUSED*/
906d522f475Smrgvoid
907d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
908d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
909d522f475Smrg		  XEvent * event GCC_UNUSED,
910d522f475Smrg		  Boolean * cont GCC_UNUSED)
911d522f475Smrg{
912d522f475Smrg    /* NOP since we handled it above */
913d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
914cd3331d0Smrg    TRACE_FOCUS(w, event);
915d522f475Smrg}
916d522f475Smrg
917d522f475Smrg/*ARGSUSED*/
918d522f475Smrgvoid
919d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
920d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
921d522f475Smrg		  XEvent * ev,
922d522f475Smrg		  Boolean * cont GCC_UNUSED)
923d522f475Smrg{
924d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
925d522f475Smrg    XtermWidget xw = term;
926d522f475Smrg    TScreen *screen = TScreenOf(xw);
927d522f475Smrg
9283367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
929d522f475Smrg	   visibleEventType(event->type),
9303367019cSmrg	   visibleNotifyMode(event->mode),
9313367019cSmrg	   visibleNotifyDetail(event->detail)));
932cd3331d0Smrg    TRACE_FOCUS(xw, event);
933d522f475Smrg
934d522f475Smrg    if (screen->quiet_grab
935d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
936c219fbebSmrg	/* EMPTY */ ;
9373367019cSmrg    } else if ((event->type == FocusIn || event->type == FocusOut)
9383367019cSmrg	       && event->detail == NotifyPointer) {
9393367019cSmrg	/*
9403367019cSmrg	 * NotifyPointer is sent to the window where the pointer is, and is
9413367019cSmrg	 * in addition to events sent to the old/new focus-windows.
9423367019cSmrg	 */
9433367019cSmrg	/* EMPTY */ ;
944d522f475Smrg    } else if (event->type == FocusIn) {
945c219fbebSmrg	setXUrgency(xw, False);
946d522f475Smrg
947d522f475Smrg	/*
948d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
949d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
950d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
951d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
952d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
953d522f475Smrg	 * delivered to the obscured window.
954d522f475Smrg	 */
955d522f475Smrg	if (event->detail == NotifyNonlinear
956d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
9573367019cSmrg	    unselectwindow(xw, INWINDOW);
958d522f475Smrg	}
9593367019cSmrg	selectwindow(xw,
960d522f475Smrg		     ((event->detail == NotifyPointer)
961d522f475Smrg		      ? INWINDOW
962d522f475Smrg		      : FOCUS));
963d522f475Smrg	SendFocusButton(xw, event);
964d522f475Smrg    } else {
965d522f475Smrg#if OPT_FOCUS_EVENT
966d522f475Smrg	if (event->type == FocusOut) {
967d522f475Smrg	    SendFocusButton(xw, event);
968d522f475Smrg	}
969d522f475Smrg#endif
970d522f475Smrg	/*
971d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
972d522f475Smrg	 * ignore.
973d522f475Smrg	 */
974d522f475Smrg	if (event->mode != NotifyGrab) {
9753367019cSmrg	    unselectwindow(xw,
976d522f475Smrg			   ((event->detail == NotifyPointer)
977d522f475Smrg			    ? INWINDOW
978d522f475Smrg			    : FOCUS));
979d522f475Smrg	}
980d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
981cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
982d522f475Smrg	    ReverseVideo(xw);
983d522f475Smrg	    screen->grabbedKbd = False;
984d522f475Smrg	    update_securekbd();
985d522f475Smrg	}
986d522f475Smrg    }
987d522f475Smrg}
988d522f475Smrg
989d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
990d522f475Smrg
991b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
992b7c89284Ssnjstatic Atom
993b7c89284SsnjAtomBell(XtermWidget xw, int which)
994b7c89284Ssnj{
995b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
996b7c89284Ssnj    static struct {
997b7c89284Ssnj	int value;
998b7c89284Ssnj	const char *name;
999b7c89284Ssnj    } table[] = {
1000b7c89284Ssnj	DATA(Info),
1001b7c89284Ssnj	    DATA(MarginBell),
1002b7c89284Ssnj	    DATA(MinorError),
1003b7c89284Ssnj	    DATA(TerminalBell)
1004b7c89284Ssnj    };
1005b7c89284Ssnj    Cardinal n;
1006b7c89284Ssnj    Atom result = None;
1007b7c89284Ssnj
1008b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1009b7c89284Ssnj	if (table[n].value == which) {
1010cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1011b7c89284Ssnj	    break;
1012b7c89284Ssnj	}
1013b7c89284Ssnj    }
1014b7c89284Ssnj    return result;
1015b7c89284Ssnj}
1016b7c89284Ssnj#endif
1017b7c89284Ssnj
1018d522f475Smrgvoid
1019b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1020d522f475Smrg{
1021b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1022b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1023b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1024cd3331d0Smrg#endif
1025cd3331d0Smrg
1026cd3331d0Smrg    switch (which) {
1027cd3331d0Smrg    case XkbBI_Info:
1028cd3331d0Smrg    case XkbBI_MinorError:
1029cd3331d0Smrg    case XkbBI_MajorError:
1030cd3331d0Smrg    case XkbBI_TerminalBell:
1031cd3331d0Smrg	switch (screen->warningVolume) {
1032cd3331d0Smrg	case bvOff:
1033cd3331d0Smrg	    percent = -100;
1034cd3331d0Smrg	    break;
1035cd3331d0Smrg	case bvLow:
1036cd3331d0Smrg	    break;
1037cd3331d0Smrg	case bvHigh:
1038cd3331d0Smrg	    percent = 100;
1039cd3331d0Smrg	    break;
1040cd3331d0Smrg	}
1041cd3331d0Smrg	break;
1042cd3331d0Smrg    case XkbBI_MarginBell:
1043cd3331d0Smrg	switch (screen->marginVolume) {
1044cd3331d0Smrg	case bvOff:
1045cd3331d0Smrg	    percent = -100;
1046cd3331d0Smrg	    break;
1047cd3331d0Smrg	case bvLow:
1048cd3331d0Smrg	    break;
1049cd3331d0Smrg	case bvHigh:
1050cd3331d0Smrg	    percent = 100;
1051cd3331d0Smrg	    break;
1052cd3331d0Smrg	}
1053cd3331d0Smrg	break;
1054cd3331d0Smrg    default:
1055cd3331d0Smrg	break;
1056cd3331d0Smrg    }
1057cd3331d0Smrg
1058cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1059b7c89284Ssnj    if (tony != None) {
1060c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1061b7c89284Ssnj    } else
1062b7c89284Ssnj#endif
1063b7c89284Ssnj	XBell(screen->display, percent);
1064b7c89284Ssnj}
1065b7c89284Ssnj
1066b7c89284Ssnjvoid
1067cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1068b7c89284Ssnj{
1069b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1070d522f475Smrg    struct timeval curtime;
1071d522f475Smrg    long now_msecs;
1072d522f475Smrg
1073b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1074b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1075d522f475Smrg	return;
1076d522f475Smrg    }
1077d522f475Smrg
1078c219fbebSmrg    setXUrgency(xw, True);
1079d522f475Smrg
1080d522f475Smrg    /* has enough time gone by that we are allowed to ring
1081d522f475Smrg       the bell again? */
1082d522f475Smrg    if (screen->bellSuppressTime) {
1083d522f475Smrg	if (screen->bellInProgress) {
1084d522f475Smrg	    do_xevents();
1085d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1086d522f475Smrg		return;
1087d522f475Smrg	    }
1088d522f475Smrg	}
1089d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1090d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1091d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1092d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1093d522f475Smrg	    return;
1094d522f475Smrg	}
1095d522f475Smrg	lastBellTime = now_msecs;
1096d522f475Smrg    }
1097d522f475Smrg
1098d522f475Smrg    if (screen->visualbell) {
1099d522f475Smrg	VisualBell();
1100d522f475Smrg    } else {
1101b7c89284Ssnj	xtermBell(xw, which, percent);
1102d522f475Smrg    }
1103d522f475Smrg
1104d522f475Smrg    if (screen->poponbell)
1105c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1106d522f475Smrg
1107d522f475Smrg    if (screen->bellSuppressTime) {
1108d522f475Smrg	/* now we change a property and wait for the notify event to come
1109d522f475Smrg	   back.  If the server is suspending operations while the bell
1110d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1111d522f475Smrg	   know when the previous bell has finished */
1112d522f475Smrg	Widget w = CURRENT_EMU();
1113d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1114d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1115d522f475Smrg	screen->bellInProgress = True;
1116d522f475Smrg    }
1117d522f475Smrg}
1118d522f475Smrg
1119d522f475Smrg#define VB_DELAY screen->visualBellDelay
1120d522f475Smrg
1121d522f475Smrgstatic void
1122d522f475SmrgflashWindow(TScreen * screen, Window window, GC visualGC, unsigned width, unsigned height)
1123d522f475Smrg{
11243367019cSmrg    int y = 0;
11253367019cSmrg    int x = 0;
11263367019cSmrg
11273367019cSmrg    if (screen->flash_line) {
11283367019cSmrg	y = CursorY(screen, screen->cur_row);
11293367019cSmrg	height = (unsigned) FontHeight(screen);
11303367019cSmrg    }
11313367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1132d522f475Smrg    XFlush(screen->display);
1133d522f475Smrg    Sleep(VB_DELAY);
11343367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1135d522f475Smrg}
1136d522f475Smrg
1137d522f475Smrgvoid
1138d522f475SmrgVisualBell(void)
1139d522f475Smrg{
1140d522f475Smrg    TScreen *screen = TScreenOf(term);
1141d522f475Smrg
1142d522f475Smrg    if (VB_DELAY > 0) {
1143d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1144d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1145d522f475Smrg	XGCValues gcval;
1146d522f475Smrg	GC visualGC;
1147d522f475Smrg
1148d522f475Smrg	gcval.function = GXxor;
1149d522f475Smrg	gcval.foreground = xorPixel;
1150d522f475Smrg	visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval);
1151d522f475Smrg#if OPT_TEK4014
1152d522f475Smrg	if (TEK4014_ACTIVE(term)) {
1153cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1154d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1155d522f475Smrg			TFullWidth(tekscr),
1156d522f475Smrg			TFullHeight(tekscr));
1157d522f475Smrg	} else
1158d522f475Smrg#endif
1159d522f475Smrg	{
1160d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1161d522f475Smrg			FullWidth(screen),
1162d522f475Smrg			FullHeight(screen));
1163d522f475Smrg	}
1164d522f475Smrg	XtReleaseGC((Widget) term, visualGC);
1165d522f475Smrg    }
1166d522f475Smrg}
1167d522f475Smrg
1168d522f475Smrg/* ARGSUSED */
1169d522f475Smrgvoid
1170d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1171d522f475Smrg			 XtPointer data GCC_UNUSED,
1172d522f475Smrg			 XEvent * ev,
1173d522f475Smrg			 Boolean * more GCC_UNUSED)
1174d522f475Smrg{
1175d522f475Smrg    TScreen *screen = TScreenOf(term);
1176d522f475Smrg
1177d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1178d522f475Smrg	screen->bellInProgress = False;
1179d522f475Smrg    }
1180d522f475Smrg}
1181d522f475Smrg
11823367019cSmrgvoid
11833367019cSmrgxtermWarning(const char *fmt,...)
11843367019cSmrg{
11853367019cSmrg    int save_err = errno;
11863367019cSmrg    va_list ap;
11873367019cSmrg
11883367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
11893367019cSmrg    va_start(ap, fmt);
11903367019cSmrg    vfprintf(stderr, fmt, ap);
11913367019cSmrg    (void) fflush(stderr);
11923367019cSmrg
11933367019cSmrg    va_end(ap);
11943367019cSmrg    errno = save_err;
11953367019cSmrg}
11963367019cSmrg
11973367019cSmrgvoid
11983367019cSmrgxtermPerror(const char *fmt,...)
11993367019cSmrg{
12003367019cSmrg    int save_err = errno;
12013367019cSmrg    char *msg = strerror(errno);
12023367019cSmrg    va_list ap;
12033367019cSmrg
12043367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
12053367019cSmrg    va_start(ap, fmt);
12063367019cSmrg    vfprintf(stderr, fmt, ap);
12073367019cSmrg    fprintf(stderr, ": %s\n", msg);
12083367019cSmrg    (void) fflush(stderr);
12093367019cSmrg
12103367019cSmrg    va_end(ap);
12113367019cSmrg    errno = save_err;
12123367019cSmrg}
12133367019cSmrg
1214d522f475SmrgWindow
1215c219fbebSmrgWMFrameWindow(XtermWidget xw)
1216d522f475Smrg{
1217d522f475Smrg    Window win_root, win_current, *children;
1218d522f475Smrg    Window win_parent = 0;
1219d522f475Smrg    unsigned int nchildren;
1220d522f475Smrg
1221c219fbebSmrg    win_current = XtWindow(xw);
1222d522f475Smrg
1223d522f475Smrg    /* find the parent which is child of root */
1224d522f475Smrg    do {
1225d522f475Smrg	if (win_parent)
1226d522f475Smrg	    win_current = win_parent;
1227c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1228d522f475Smrg		   win_current,
1229d522f475Smrg		   &win_root,
1230d522f475Smrg		   &win_parent,
1231d522f475Smrg		   &children,
1232d522f475Smrg		   &nchildren);
1233d522f475Smrg	XFree(children);
1234d522f475Smrg    } while (win_root != win_parent);
1235d522f475Smrg
1236d522f475Smrg    return win_current;
1237d522f475Smrg}
1238d522f475Smrg
1239d522f475Smrg#if OPT_DABBREV
1240d522f475Smrg/*
1241d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1242d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1243d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1244d522f475Smrg * expansions and ignores one of them if they are identical.
1245d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1246d522f475Smrg */
1247d522f475Smrg
1248d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1249d522f475Smrg#define MAXWLEN 1024		/* maximum word length as in tcsh */
1250d522f475Smrg
1251d522f475Smrgstatic int
1252b7c89284Ssnjdabbrev_prev_char(TScreen * screen, CELL * cell, LineData ** ld)
1253d522f475Smrg{
1254b7c89284Ssnj    int result = -1;
1255b7c89284Ssnj    int firstLine = -(screen->savedlines);
1256d522f475Smrg
1257b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1258b7c89284Ssnj    while (cell->row >= firstLine) {
1259b7c89284Ssnj	if (--(cell->col) >= 0) {
1260b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1261b7c89284Ssnj	    break;
1262b7c89284Ssnj	}
1263b7c89284Ssnj	if (--(cell->row) < firstLine)
1264b7c89284Ssnj	    break;		/* ...there is no previous line */
1265b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1266b7c89284Ssnj	cell->col = MaxCols(screen);
1267b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1268b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1269d522f475Smrg	    break;
1270b7c89284Ssnj	}
1271d522f475Smrg    }
1272b7c89284Ssnj    return result;
1273d522f475Smrg}
1274d522f475Smrg
1275d522f475Smrgstatic char *
1276b7c89284Ssnjdabbrev_prev_word(TScreen * screen, CELL * cell, LineData ** ld)
1277d522f475Smrg{
1278d522f475Smrg    static char ab[MAXWLEN];
1279b7c89284Ssnj
1280d522f475Smrg    char *abword;
1281d522f475Smrg    int c;
1282b7c89284Ssnj    char *ab_end = (ab + MAXWLEN - 1);
1283b7c89284Ssnj    char *result = 0;
1284d522f475Smrg
1285b7c89284Ssnj    abword = ab_end;
1286d522f475Smrg    *abword = '\0';		/* end of string marker */
1287d522f475Smrg
1288b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1289b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
1290d522f475Smrg	if (abword > ab)	/* store only |MAXWLEN| last chars */
1291b7c89284Ssnj	    *(--abword) = (char) c;
1292d522f475Smrg    }
1293d522f475Smrg
1294b7c89284Ssnj    if (c >= 0) {
1295b7c89284Ssnj	result = abword;
1296b7c89284Ssnj    } else if (abword != ab_end) {
1297b7c89284Ssnj	result = abword;
1298b7c89284Ssnj    }
1299b7c89284Ssnj
1300b7c89284Ssnj    if (result != 0) {
1301b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1302b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1303b7c89284Ssnj	    ;			/* skip preceding spaces */
1304b7c89284Ssnj	}
1305b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1306b7c89284Ssnj    }
1307b7c89284Ssnj    return result;
1308d522f475Smrg}
1309d522f475Smrg
1310d522f475Smrgstatic int
1311d522f475Smrgdabbrev_expand(TScreen * screen)
1312d522f475Smrg{
1313d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1314d522f475Smrg
1315b7c89284Ssnj    static CELL cell;
1316d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1317d522f475Smrg    static unsigned int expansions;
1318d522f475Smrg
1319d522f475Smrg    char *expansion;
1320d522f475Smrg    Char *copybuffer;
1321d522f475Smrg    size_t hint_len;
1322cd3331d0Smrg    size_t del_cnt;
1323cd3331d0Smrg    size_t buf_cnt;
1324b7c89284Ssnj    int result = 0;
1325b7c89284Ssnj    LineData *ld;
1326d522f475Smrg
1327d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1328d522f475Smrg	expansions = 0;
1329b7c89284Ssnj	cell.col = screen->cur_col;
1330b7c89284Ssnj	cell.row = screen->cur_row;
1331b7c89284Ssnj
1332b7c89284Ssnj	if (dabbrev_hint != 0)
1333b7c89284Ssnj	    free(dabbrev_hint);
1334b7c89284Ssnj
1335b7c89284Ssnj	if ((dabbrev_hint = dabbrev_prev_word(screen, &cell, &ld)) != 0) {
1336b7c89284Ssnj
1337b7c89284Ssnj	    if (lastexpansion != 0)
1338b7c89284Ssnj		free(lastexpansion);
1339b7c89284Ssnj
1340b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1341b7c89284Ssnj
1342b7c89284Ssnj		/* make own copy */
1343b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1344b7c89284Ssnj		    screen->dabbrev_working = True;
1345b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1346b7c89284Ssnj		}
1347cd3331d0Smrg	    } else {
1348cd3331d0Smrg		return result;
1349b7c89284Ssnj	    }
1350cd3331d0Smrg	} else {
1351cd3331d0Smrg	    return result;
1352d522f475Smrg	}
1353b7c89284Ssnj	if (!screen->dabbrev_working) {
1354b7c89284Ssnj	    if (lastexpansion != 0) {
1355b7c89284Ssnj		free(lastexpansion);
1356b7c89284Ssnj		lastexpansion = 0;
1357b7c89284Ssnj	    }
1358b7c89284Ssnj	    return result;
1359b7c89284Ssnj	}
1360d522f475Smrg    }
1361d522f475Smrg
1362cd3331d0Smrg    if (dabbrev_hint == 0)
1363cd3331d0Smrg	return result;
1364cd3331d0Smrg
1365d522f475Smrg    hint_len = strlen(dabbrev_hint);
1366d522f475Smrg    for (;;) {
1367b7c89284Ssnj	if ((expansion = dabbrev_prev_word(screen, &cell, &ld)) == 0) {
1368d522f475Smrg	    if (expansions >= 2) {
1369d522f475Smrg		expansions = 0;
1370b7c89284Ssnj		cell.col = screen->cur_col;
1371b7c89284Ssnj		cell.row = screen->cur_row;
1372d522f475Smrg		continue;
1373d522f475Smrg	    }
1374d522f475Smrg	    break;
1375d522f475Smrg	}
1376d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1377d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1378d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1379d522f475Smrg	    break;
1380d522f475Smrg    }
1381d522f475Smrg
1382b7c89284Ssnj    if (expansion != 0) {
1383b7c89284Ssnj	del_cnt = strlen(lastexpansion) - hint_len;
1384b7c89284Ssnj	buf_cnt = del_cnt + strlen(expansion) - hint_len;
1385b7c89284Ssnj
1386b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1387b7c89284Ssnj	    /* delete previous expansion */
1388b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1389b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1390b7c89284Ssnj		    expansion + hint_len,
1391b7c89284Ssnj		    strlen(expansion) - hint_len);
1392cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1393b7c89284Ssnj	    /* v_write() just reset our flag */
1394b7c89284Ssnj	    screen->dabbrev_working = True;
1395b7c89284Ssnj	    free(copybuffer);
1396b7c89284Ssnj
1397b7c89284Ssnj	    free(lastexpansion);
1398b7c89284Ssnj
1399b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1400b7c89284Ssnj		result = 1;
1401b7c89284Ssnj		expansions++;
1402b7c89284Ssnj	    }
1403b7c89284Ssnj	}
1404b7c89284Ssnj    }
1405b7c89284Ssnj
1406b7c89284Ssnj    return result;
1407d522f475Smrg}
1408d522f475Smrg
1409d522f475Smrg/*ARGSUSED*/
1410d522f475Smrgvoid
1411b7c89284SsnjHandleDabbrevExpand(Widget w,
1412d522f475Smrg		    XEvent * event GCC_UNUSED,
1413d522f475Smrg		    String * params GCC_UNUSED,
1414d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1415d522f475Smrg{
1416b7c89284Ssnj    XtermWidget xw;
1417b7c89284Ssnj
1418cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1419b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1420cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
1421d522f475Smrg	if (!dabbrev_expand(screen))
1422cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1423d522f475Smrg    }
1424d522f475Smrg}
1425d522f475Smrg#endif /* OPT_DABBREV */
1426d522f475Smrg
1427d522f475Smrg#if OPT_MAXIMIZE
1428d522f475Smrg/*ARGSUSED*/
1429d522f475Smrgvoid
1430b7c89284SsnjHandleDeIconify(Widget w,
1431d522f475Smrg		XEvent * event GCC_UNUSED,
1432d522f475Smrg		String * params GCC_UNUSED,
1433d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1434d522f475Smrg{
1435b7c89284Ssnj    XtermWidget xw;
1436b7c89284Ssnj
1437b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1438b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1439c219fbebSmrg	XMapWindow(screen->display, VShellWindow(xw));
1440d522f475Smrg    }
1441d522f475Smrg}
1442d522f475Smrg
1443d522f475Smrg/*ARGSUSED*/
1444d522f475Smrgvoid
1445b7c89284SsnjHandleIconify(Widget w,
1446d522f475Smrg	      XEvent * event GCC_UNUSED,
1447d522f475Smrg	      String * params GCC_UNUSED,
1448d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1449d522f475Smrg{
1450b7c89284Ssnj    XtermWidget xw;
1451b7c89284Ssnj
1452b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1453b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1454d522f475Smrg	XIconifyWindow(screen->display,
1455c219fbebSmrg		       VShellWindow(xw),
1456d522f475Smrg		       DefaultScreen(screen->display));
1457d522f475Smrg    }
1458d522f475Smrg}
1459d522f475Smrg
1460d522f475Smrgint
1461c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1462d522f475Smrg{
1463c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1464d522f475Smrg    XSizeHints hints;
1465d522f475Smrg    long supp = 0;
1466d522f475Smrg    Window root_win;
1467d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1468d522f475Smrg    int root_y = -1;
1469d522f475Smrg    unsigned root_border;
1470d522f475Smrg    unsigned root_depth;
14713367019cSmrg    int code;
1472d522f475Smrg
1473d522f475Smrg    if (XGetGeometry(screen->display,
1474c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1475d522f475Smrg		     &root_win,
1476d522f475Smrg		     &root_x,
1477d522f475Smrg		     &root_y,
1478d522f475Smrg		     width,
1479d522f475Smrg		     height,
1480d522f475Smrg		     &root_border,
1481d522f475Smrg		     &root_depth)) {
1482d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1483d522f475Smrg	       root_x,
1484d522f475Smrg	       root_y,
1485d522f475Smrg	       *width,
1486d522f475Smrg	       *height,
1487d522f475Smrg	       root_border));
1488d522f475Smrg
1489d522f475Smrg	*width -= (root_border * 2);
1490d522f475Smrg	*height -= (root_border * 2);
1491d522f475Smrg
1492d522f475Smrg	hints.flags = PMaxSize;
1493d522f475Smrg	if (XGetWMNormalHints(screen->display,
1494c219fbebSmrg			      VShellWindow(xw),
1495d522f475Smrg			      &hints,
1496d522f475Smrg			      &supp)
1497d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1498d522f475Smrg
1499d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1500d522f475Smrg		   hints.max_width,
1501d522f475Smrg		   hints.max_height));
1502d522f475Smrg
1503d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1504b7c89284Ssnj		*width = (unsigned) hints.max_width;
1505d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1506b7c89284Ssnj		*height = (unsigned) hints.max_height;
1507d522f475Smrg	}
15083367019cSmrg	code = 1;
15093367019cSmrg    } else {
15103367019cSmrg	*width = 0;
15113367019cSmrg	*height = 0;
15123367019cSmrg	code = 0;
1513d522f475Smrg    }
15143367019cSmrg    return code;
1515d522f475Smrg}
1516d522f475Smrg
1517d522f475Smrgvoid
1518c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1519d522f475Smrg{
1520c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1521d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1522d522f475Smrg    unsigned root_width, root_height;
15233367019cSmrg    Boolean success = False;
1524d522f475Smrg
15253367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
15263367019cSmrg	   maximize,
15273367019cSmrg	   (maximize
15283367019cSmrg	    ? "maximize"
15293367019cSmrg	    : "restore")));
1530d522f475Smrg
15313367019cSmrg    /*
15323367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
15333367019cSmrg     * as well as the estimated root-window size.
15343367019cSmrg     */
15353367019cSmrg    if (maximize
15363367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
15373367019cSmrg	&& xtermGetWinAttrs(screen->display,
15383367019cSmrg			    WMFrameWindow(xw),
15393367019cSmrg			    &wm_attrs)
15403367019cSmrg	&& xtermGetWinAttrs(screen->display,
15413367019cSmrg			    VShellWindow(xw),
15423367019cSmrg			    &vshell_attrs)) {
15433367019cSmrg
15443367019cSmrg	if (screen->restore_data != True
15453367019cSmrg	    || screen->restore_width != root_width
15463367019cSmrg	    || screen->restore_height != root_height) {
15473367019cSmrg	    screen->restore_data = True;
15483367019cSmrg	    screen->restore_x = wm_attrs.x + wm_attrs.border_width;
15493367019cSmrg	    screen->restore_y = wm_attrs.y + wm_attrs.border_width;
15503367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
15513367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
15523367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1553d522f475Smrg		   screen->restore_x,
1554d522f475Smrg		   screen->restore_y,
1555d522f475Smrg		   screen->restore_width,
1556d522f475Smrg		   screen->restore_height));
15573367019cSmrg	}
1558d522f475Smrg
15593367019cSmrg	/* subtract wm decoration dimensions */
15603367019cSmrg	root_width -= (unsigned) ((wm_attrs.width - vshell_attrs.width)
15613367019cSmrg				  + (wm_attrs.border_width * 2));
15623367019cSmrg	root_height -= (unsigned) ((wm_attrs.height - vshell_attrs.height)
15633367019cSmrg				   + (wm_attrs.border_width * 2));
15643367019cSmrg	success = True;
15653367019cSmrg    } else if (screen->restore_data) {
15663367019cSmrg	success = True;
15673367019cSmrg	maximize = 0;
15683367019cSmrg    }
15693367019cSmrg
15703367019cSmrg    if (success) {
15713367019cSmrg	switch (maximize) {
15723367019cSmrg	case 3:
15733367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
15743367019cSmrg	    break;
15753367019cSmrg	case 2:
15763367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
15773367019cSmrg	    break;
15783367019cSmrg	case 1:
15793367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
15803367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
15813367019cSmrg			      0 + wm_attrs.border_width,	/* x */
15823367019cSmrg			      0 + wm_attrs.border_width,	/* y */
15833367019cSmrg			      root_width,
15843367019cSmrg			      root_height);
15853367019cSmrg	    break;
15863367019cSmrg
15873367019cSmrg	default:
15883367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
15893367019cSmrg	    if (screen->restore_data) {
15903367019cSmrg		screen->restore_data = False;
15913367019cSmrg
15923367019cSmrg		TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
15933367019cSmrg		       screen->restore_x,
15943367019cSmrg		       screen->restore_y,
15953367019cSmrg		       screen->restore_width,
15963367019cSmrg		       screen->restore_height));
15973367019cSmrg
15983367019cSmrg		XMoveResizeWindow(screen->display,
15993367019cSmrg				  VShellWindow(xw),
16003367019cSmrg				  screen->restore_x,
16013367019cSmrg				  screen->restore_y,
16023367019cSmrg				  screen->restore_width,
16033367019cSmrg				  screen->restore_height);
16043367019cSmrg	    }
16053367019cSmrg	    break;
1606d522f475Smrg	}
1607d522f475Smrg    }
1608d522f475Smrg}
1609d522f475Smrg
1610d522f475Smrg/*ARGSUSED*/
1611d522f475Smrgvoid
1612b7c89284SsnjHandleMaximize(Widget w,
1613d522f475Smrg	       XEvent * event GCC_UNUSED,
1614d522f475Smrg	       String * params GCC_UNUSED,
1615d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1616d522f475Smrg{
1617b7c89284Ssnj    XtermWidget xw;
1618b7c89284Ssnj
1619b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1620b7c89284Ssnj	RequestMaximize(xw, 1);
1621d522f475Smrg    }
1622d522f475Smrg}
1623d522f475Smrg
1624d522f475Smrg/*ARGSUSED*/
1625d522f475Smrgvoid
1626b7c89284SsnjHandleRestoreSize(Widget w,
1627d522f475Smrg		  XEvent * event GCC_UNUSED,
1628d522f475Smrg		  String * params GCC_UNUSED,
1629d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1630d522f475Smrg{
1631b7c89284Ssnj    XtermWidget xw;
1632b7c89284Ssnj
1633b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1634b7c89284Ssnj	RequestMaximize(xw, 0);
1635d522f475Smrg    }
1636d522f475Smrg}
1637d522f475Smrg#endif /* OPT_MAXIMIZE */
1638d522f475Smrg
1639d522f475Smrgvoid
1640d522f475SmrgRedraw(void)
1641d522f475Smrg{
1642d522f475Smrg    TScreen *screen = TScreenOf(term);
1643d522f475Smrg    XExposeEvent event;
1644d522f475Smrg
1645d522f475Smrg    TRACE(("Redraw\n"));
1646d522f475Smrg
1647d522f475Smrg    event.type = Expose;
1648d522f475Smrg    event.display = screen->display;
1649d522f475Smrg    event.x = 0;
1650d522f475Smrg    event.y = 0;
1651d522f475Smrg    event.count = 0;
1652d522f475Smrg
1653d522f475Smrg    if (VWindow(screen)) {
1654d522f475Smrg	event.window = VWindow(screen);
1655d522f475Smrg	event.width = term->core.width;
1656d522f475Smrg	event.height = term->core.height;
1657d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
1658d522f475Smrg						       (XEvent *) & event,
1659d522f475Smrg						       NULL);
1660d522f475Smrg	if (ScrollbarWidth(screen)) {
1661d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
1662d522f475Smrg		(screen->scrollWidget, (XEvent *) & event, NULL);
1663d522f475Smrg	}
1664d522f475Smrg    }
1665d522f475Smrg#if OPT_TEK4014
1666d522f475Smrg    if (TEK4014_SHOWN(term)) {
1667cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1668d522f475Smrg	event.window = TWindow(tekscr);
1669d522f475Smrg	event.width = tekWidget->core.width;
1670d522f475Smrg	event.height = tekWidget->core.height;
1671d522f475Smrg	TekExpose((Widget) tekWidget, (XEvent *) & event, NULL);
1672d522f475Smrg    }
1673d522f475Smrg#endif
1674d522f475Smrg}
1675d522f475Smrg
1676d522f475Smrg#ifdef VMS
1677d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1678d522f475Smrg#else
1679d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1680d522f475Smrg#endif
1681d522f475Smrg
1682d522f475Smrgvoid
1683d522f475Smrgtimestamp_filename(char *dst, const char *src)
1684d522f475Smrg{
1685d522f475Smrg    time_t tstamp;
1686d522f475Smrg    struct tm *tstruct;
1687d522f475Smrg
1688d522f475Smrg    tstamp = time((time_t *) 0);
1689d522f475Smrg    tstruct = localtime(&tstamp);
1690d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1691d522f475Smrg	    src,
16923367019cSmrg	    (int) tstruct->tm_year + 1900,
1693d522f475Smrg	    tstruct->tm_mon + 1,
1694d522f475Smrg	    tstruct->tm_mday,
1695d522f475Smrg	    tstruct->tm_hour,
1696d522f475Smrg	    tstruct->tm_min,
1697d522f475Smrg	    tstruct->tm_sec);
1698d522f475Smrg}
1699d522f475Smrg
1700d522f475Smrgint
1701d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1702d522f475Smrg{
1703d522f475Smrg    int fd;
1704d522f475Smrg    struct stat sb;
1705d522f475Smrg
1706d522f475Smrg#ifdef VMS
1707d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1708d522f475Smrg	int the_error = errno;
17093367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
17103367019cSmrg		     path,
17113367019cSmrg		     the_error,
17123367019cSmrg		     SysErrorMsg(the_error));
1713d522f475Smrg	return -1;
1714d522f475Smrg    }
1715d522f475Smrg    chown(path, uid, gid);
1716d522f475Smrg#else
1717d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1718d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1719d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1720d522f475Smrg	int the_error = errno;
17213367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
17223367019cSmrg		     path,
17233367019cSmrg		     the_error,
17243367019cSmrg		     SysErrorMsg(the_error));
1725d522f475Smrg	return -1;
1726d522f475Smrg    }
1727d522f475Smrg#endif
1728d522f475Smrg
1729d522f475Smrg    /*
1730d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1731d522f475Smrg     * we do any damage, and that it is not world-writable.
1732d522f475Smrg     */
1733d522f475Smrg    if (fstat(fd, &sb) < 0
1734d522f475Smrg	|| sb.st_uid != uid
1735d522f475Smrg	|| (sb.st_mode & 022) != 0) {
17363367019cSmrg	xtermWarning("you do not own %s\n", path);
1737d522f475Smrg	close(fd);
1738d522f475Smrg	return -1;
1739d522f475Smrg    }
1740d522f475Smrg    return fd;
1741d522f475Smrg}
1742d522f475Smrg
1743d522f475Smrg#ifndef VMS
1744d522f475Smrg/*
1745d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1746d522f475Smrg * We could emulate this with careful use of access() and following
1747d522f475Smrg * symbolic links, but that is messy and has race conditions.
1748d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1749d522f475Smrg * being available.
1750d522f475Smrg *
1751d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1752d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1753d522f475Smrg * for the debug logs.
1754d522f475Smrg *
1755d522f475Smrg * Returns
1756d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1757d522f475Smrg *	-1 on error, e.g., cannot fork
1758d522f475Smrg *	 0 otherwise.
1759d522f475Smrg */
1760d522f475Smrgint
1761712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
1762d522f475Smrg{
1763d522f475Smrg    int fd;
1764d522f475Smrg    pid_t pid;
1765d522f475Smrg    int retval = 0;
1766d522f475Smrg    int childstat = 0;
1767d522f475Smrg#ifndef HAVE_WAITPID
1768d522f475Smrg    int waited;
17693367019cSmrg    void (*chldfunc) (int);
1770d522f475Smrg
1771d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1772d522f475Smrg#endif /* HAVE_WAITPID */
1773d522f475Smrg
1774d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1775d522f475Smrg	   (int) uid, (int) geteuid(),
1776d522f475Smrg	   (int) gid, (int) getegid(),
1777d522f475Smrg	   append,
1778d522f475Smrg	   pathname,
1779d522f475Smrg	   mode));
1780d522f475Smrg
1781d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1782d522f475Smrg	fd = open(pathname,
1783d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1784d522f475Smrg		  mode);
1785d522f475Smrg	if (fd >= 0)
1786d522f475Smrg	    close(fd);
1787d522f475Smrg	return (fd >= 0);
1788d522f475Smrg    }
1789d522f475Smrg
1790d522f475Smrg    pid = fork();
1791d522f475Smrg    switch (pid) {
1792d522f475Smrg    case 0:			/* child */
1793d522f475Smrg	if (setgid(gid) == -1
1794d522f475Smrg	    || setuid(uid) == -1) {
1795d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1796d522f475Smrg	    retval = 1;
1797d522f475Smrg	} else {
1798d522f475Smrg	    fd = open(pathname,
1799d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1800d522f475Smrg		      mode);
1801d522f475Smrg	    if (fd >= 0) {
1802d522f475Smrg		close(fd);
1803d522f475Smrg		retval = 0;
1804d522f475Smrg	    } else {
1805d522f475Smrg		retval = 1;
1806d522f475Smrg	    }
1807d522f475Smrg	}
1808d522f475Smrg	_exit(retval);
1809d522f475Smrg	/* NOTREACHED */
1810d522f475Smrg    case -1:			/* error */
1811d522f475Smrg	return retval;
1812d522f475Smrg    default:			/* parent */
1813d522f475Smrg#ifdef HAVE_WAITPID
1814d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1815d522f475Smrg#ifdef EINTR
1816d522f475Smrg	    if (errno == EINTR)
1817d522f475Smrg		continue;
1818d522f475Smrg#endif /* EINTR */
1819d522f475Smrg#ifdef ERESTARTSYS
1820d522f475Smrg	    if (errno == ERESTARTSYS)
1821d522f475Smrg		continue;
1822d522f475Smrg#endif /* ERESTARTSYS */
1823d522f475Smrg	    break;
1824d522f475Smrg	}
1825d522f475Smrg#else /* HAVE_WAITPID */
1826d522f475Smrg	waited = wait(&childstat);
1827d522f475Smrg	signal(SIGCHLD, chldfunc);
1828d522f475Smrg	/*
1829d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1830d522f475Smrg	   we might have missed the termination of our screen child.
1831d522f475Smrg	   If we can check for this possibility without hanging, do so.
1832d522f475Smrg	 */
1833d522f475Smrg	do
1834cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
18353367019cSmrg		NormalExit();
1836d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1837d522f475Smrg#endif /* HAVE_WAITPID */
1838d522f475Smrg#ifndef WIFEXITED
1839d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1840d522f475Smrg#endif
1841d522f475Smrg	if (WIFEXITED(childstat))
1842d522f475Smrg	    retval = 1;
1843d522f475Smrg	return retval;
1844d522f475Smrg    }
1845d522f475Smrg}
1846d522f475Smrg#endif /* !VMS */
1847d522f475Smrg
1848d522f475Smrgint
1849d522f475SmrgxtermResetIds(TScreen * screen)
1850d522f475Smrg{
1851d522f475Smrg    int result = 0;
1852d522f475Smrg    if (setgid(screen->gid) == -1) {
18533367019cSmrg	xtermWarning("unable to reset group-id\n");
1854d522f475Smrg	result = -1;
1855d522f475Smrg    }
1856d522f475Smrg    if (setuid(screen->uid) == -1) {
18573367019cSmrg	xtermWarning("unable to reset user-id\n");
1858d522f475Smrg	result = -1;
1859d522f475Smrg    }
1860d522f475Smrg    return result;
1861d522f475Smrg}
1862d522f475Smrg
1863d522f475Smrg#ifdef ALLOWLOGGING
1864d522f475Smrg
1865d522f475Smrg/*
1866d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1867d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1868d522f475Smrg */
1869d522f475Smrg
1870d522f475Smrg#ifdef ALLOWLOGFILEEXEC
18713367019cSmrgstatic void
1872d522f475Smrglogpipe(int sig GCC_UNUSED)
1873d522f475Smrg{
1874cd3331d0Smrg    XtermWidget xw = term;
1875cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1876d522f475Smrg
18773367019cSmrg    DEBUG_MSG("handle:logpipe\n");
1878d522f475Smrg#ifdef SYSV
1879d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
1880d522f475Smrg#endif /* SYSV */
1881d522f475Smrg    if (screen->logging)
1882cd3331d0Smrg	CloseLog(xw);
1883d522f475Smrg}
1884d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1885d522f475Smrg
1886d522f475Smrgvoid
1887cd3331d0SmrgStartLog(XtermWidget xw)
1888d522f475Smrg{
1889d522f475Smrg    static char *log_default;
1890cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1891d522f475Smrg
1892d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
1893d522f475Smrg	return;
1894d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
1895d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
1896d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
1897d522f475Smrg			 0640);
1898d522f475Smrg    if (screen->logfd < 0)
1899d522f475Smrg	return;			/* open failed */
1900d522f475Smrg#else /*VMS */
1901d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
1902d522f475Smrg	if (screen->logfile)
1903d522f475Smrg	    free(screen->logfile);
1904d522f475Smrg	if (log_default == NULL) {
1905d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
1906d522f475Smrg	    char log_def_name[512];	/* see sprintf below */
1907d522f475Smrg	    char hostname[255 + 1];	/* Internet standard limit (RFC 1035):
1908d522f475Smrg					   ``To simplify implementations, the
1909d522f475Smrg					   total length of a domain name (i.e.,
1910d522f475Smrg					   label octets and label length
1911d522f475Smrg					   octets) is restricted to 255 octets
1912d522f475Smrg					   or less.'' */
1913d522f475Smrg	    char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1];
1914d522f475Smrg	    time_t now;
1915d522f475Smrg	    struct tm *ltm;
1916d522f475Smrg
1917d522f475Smrg	    now = time((time_t *) 0);
1918d522f475Smrg	    ltm = (struct tm *) localtime(&now);
1919d522f475Smrg	    if ((gethostname(hostname, sizeof(hostname)) == 0) &&
1920d522f475Smrg		(strftime(yyyy_mm_dd_hh_mm_ss,
1921d522f475Smrg			  sizeof(yyyy_mm_dd_hh_mm_ss),
1922d522f475Smrg			  "%Y.%m.%d.%H.%M.%S", ltm) > 0)) {
1923d522f475Smrg		(void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d",
1924d522f475Smrg			       hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid());
1925d522f475Smrg	    }
1926d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
1927d522f475Smrg		return;
1928d522f475Smrg#else
1929d522f475Smrg	    const char *log_def_name = "XtermLog.XXXXXX";
1930d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
1931d522f475Smrg		return;
1932d522f475Smrg
1933d522f475Smrg	    mktemp(log_default);
1934d522f475Smrg#endif
1935d522f475Smrg	}
1936d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
1937d522f475Smrg	    return;
1938d522f475Smrg    }
1939d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
1940d522f475Smrg#ifdef ALLOWLOGFILEEXEC
1941d522f475Smrg	/*
1942d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
1943d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
1944d522f475Smrg	 * done through escape sequences....  You have been warned.
1945d522f475Smrg	 */
1946d522f475Smrg	int pid;
1947d522f475Smrg	int p[2];
1948d522f475Smrg	static char *shell;
19493367019cSmrg	struct passwd pw;
19503367019cSmrg
19513367019cSmrg	if ((shell = x_getenv("SHELL")) == NULL) {
19523367019cSmrg
19533367019cSmrg	    if (x_getpwuid(screen->uid, &pw)) {
19543367019cSmrg		char *name = x_getlogin(screen->uid, &pw);
19553367019cSmrg		if (*(pw.pw_shell)) {
19563367019cSmrg		    shell = pw.pw_shell;
19573367019cSmrg		}
19583367019cSmrg		free(name);
19593367019cSmrg	    }
19603367019cSmrg	}
19613367019cSmrg
19623367019cSmrg	if (shell == 0) {
19633367019cSmrg	    static char dummy[] = "/bin/sh";
19643367019cSmrg	    shell = dummy;
19653367019cSmrg	}
19663367019cSmrg
19673367019cSmrg	if (access(shell, X_OK) != 0) {
19683367019cSmrg	    xtermPerror("Can't execute `%s'\n", shell);
19693367019cSmrg	    return;
19703367019cSmrg	}
1971d522f475Smrg
19723367019cSmrg	if (pipe(p) < 0) {
19733367019cSmrg	    xtermPerror("Can't make a pipe connection\n");
1974d522f475Smrg	    return;
19753367019cSmrg	} else if ((pid = fork()) < 0) {
19763367019cSmrg	    xtermPerror("Can't fork...\n");
19773367019cSmrg	    return;
19783367019cSmrg	}
1979d522f475Smrg	if (pid == 0) {		/* child */
1980d522f475Smrg	    /*
1981d522f475Smrg	     * Close our output (we won't be talking back to the
1982d522f475Smrg	     * parent), and redirect our child's output to the
1983d522f475Smrg	     * original stderr.
1984d522f475Smrg	     */
1985d522f475Smrg	    close(p[1]);
1986d522f475Smrg	    dup2(p[0], 0);
1987d522f475Smrg	    close(p[0]);
1988d522f475Smrg	    dup2(fileno(stderr), 1);
1989d522f475Smrg	    dup2(fileno(stderr), 2);
1990d522f475Smrg
1991d522f475Smrg	    close(fileno(stderr));
1992d522f475Smrg	    close(ConnectionNumber(screen->display));
1993d522f475Smrg	    close(screen->respond);
1994d522f475Smrg
1995d522f475Smrg	    signal(SIGHUP, SIG_DFL);
1996d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
1997d522f475Smrg
1998d522f475Smrg	    /* (this is redundant) */
1999d522f475Smrg	    if (xtermResetIds(screen) < 0)
2000d522f475Smrg		exit(ERROR_SETUID);
2001d522f475Smrg
20023367019cSmrg	    if (access(shell, X_OK) == 0) {
20033367019cSmrg		execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
20043367019cSmrg		xtermWarning("Can't exec `%s'\n", &screen->logfile[1]);
20053367019cSmrg	    } else {
20063367019cSmrg		xtermWarning("Can't execute `%s'\n", shell);
20073367019cSmrg	    }
2008d522f475Smrg	    exit(ERROR_LOGEXEC);
2009d522f475Smrg	}
2010d522f475Smrg	close(p[0]);
2011d522f475Smrg	screen->logfd = p[1];
2012d522f475Smrg	signal(SIGPIPE, logpipe);
2013d522f475Smrg#else
2014cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2015cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2016d522f475Smrg	return;
2017d522f475Smrg#endif
2018d522f475Smrg    } else {
2019d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2020d522f475Smrg					   screen->gid,
2021d522f475Smrg					   screen->logfile,
2022d522f475Smrg					   (log_default != 0))) < 0)
2023d522f475Smrg	    return;
2024d522f475Smrg    }
2025d522f475Smrg#endif /*VMS */
2026d522f475Smrg    screen->logstart = VTbuffer->next;
2027d522f475Smrg    screen->logging = True;
2028d522f475Smrg    update_logging();
2029d522f475Smrg}
2030d522f475Smrg
2031d522f475Smrgvoid
2032cd3331d0SmrgCloseLog(XtermWidget xw)
2033d522f475Smrg{
2034cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2035cd3331d0Smrg
2036d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2037d522f475Smrg	return;
2038cd3331d0Smrg    FlushLog(xw);
2039d522f475Smrg    close(screen->logfd);
2040d522f475Smrg    screen->logging = False;
2041d522f475Smrg    update_logging();
2042d522f475Smrg}
2043d522f475Smrg
2044d522f475Smrgvoid
2045cd3331d0SmrgFlushLog(XtermWidget xw)
2046d522f475Smrg{
2047cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2048cd3331d0Smrg
2049d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2050d522f475Smrg	Char *cp;
2051d522f475Smrg	int i;
2052d522f475Smrg
2053d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2054d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2055d522f475Smrg	if (!tt_new_output)
2056d522f475Smrg	    return;
2057d522f475Smrg	tt_new_output = False;
2058d522f475Smrg#endif /* VMS */
2059d522f475Smrg	cp = VTbuffer->next;
2060d522f475Smrg	if (screen->logstart != 0
2061cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2062cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2063d522f475Smrg	}
2064d522f475Smrg	screen->logstart = VTbuffer->next;
2065d522f475Smrg    }
2066d522f475Smrg}
2067d522f475Smrg
2068d522f475Smrg#endif /* ALLOWLOGGING */
2069d522f475Smrg
2070d522f475Smrg/***====================================================================***/
2071d522f475Smrg
2072d522f475Smrg#if OPT_ISO_COLORS
2073d522f475Smrgstatic void
2074d522f475SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
2075d522f475Smrg{
2076cd3331d0Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
2077cd3331d0Smrg	XColor color;
2078cd3331d0Smrg	Colormap cmap = xw->core.colormap;
2079cd3331d0Smrg	char buffer[80];
2080cd3331d0Smrg
2081cd3331d0Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
2082cd3331d0Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2083cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
2084cd3331d0Smrg	sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
2085cd3331d0Smrg		colornum,
2086cd3331d0Smrg		color.red,
2087cd3331d0Smrg		color.green,
2088cd3331d0Smrg		color.blue);
2089cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
2090cd3331d0Smrg	unparseputs(xw, buffer);
2091cd3331d0Smrg	unparseputc1(xw, final);
2092cd3331d0Smrg	unparse_end(xw);
2093cd3331d0Smrg    }
2094d522f475Smrg}
2095d522f475Smrg
20963367019cSmrgstatic void
20973367019cSmrggetColormapInfo(Display * display, unsigned *typep, unsigned *sizep)
2098d522f475Smrg{
2099d522f475Smrg    int numFound;
2100d522f475Smrg    XVisualInfo myTemplate, *visInfoPtr;
2101d522f475Smrg
2102d522f475Smrg    myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(display,
2103d522f475Smrg							    XDefaultScreen(display)));
2104d522f475Smrg    visInfoPtr = XGetVisualInfo(display, (long) VisualIDMask,
2105d522f475Smrg				&myTemplate, &numFound);
21063367019cSmrg    *typep = (numFound >= 1) ? (unsigned) visInfoPtr->class : 0;
21073367019cSmrg    *sizep = (numFound >= 1) ? (unsigned) visInfoPtr->colormap_size : 0;
2108d522f475Smrg
2109d522f475Smrg    XFree((char *) visInfoPtr);
21103367019cSmrg
21113367019cSmrg    TRACE(("getColormapInfo type %d (%s), size %d\n",
21123367019cSmrg	   *typep, ((*typep & 1) ? "dynamic" : "static"), *sizep));
21133367019cSmrg}
21143367019cSmrg
21153367019cSmrg#define MAX_COLORTABLE 4096
21163367019cSmrg
21173367019cSmrg/*
21183367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
21193367019cSmrg */
21203367019cSmrgstatic Boolean
21213367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
21223367019cSmrg{
21233367019cSmrg    Colormap cmap = xw->core.colormap;
21243367019cSmrg    TScreen *screen = TScreenOf(xw);
21253367019cSmrg    unsigned i;
21263367019cSmrg    Boolean result = False;
21273367019cSmrg
21283367019cSmrg    if (screen->cmap_data == 0
21293367019cSmrg	&& length != 0
21303367019cSmrg	&& length < MAX_COLORTABLE) {
21313367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
21323367019cSmrg	if (screen->cmap_data != 0) {
21333367019cSmrg	    screen->cmap_size = length;
21343367019cSmrg
21353367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
21363367019cSmrg		screen->cmap_data[i].pixel = (unsigned long) i;
21373367019cSmrg	    }
21383367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
21393367019cSmrg					     cmap,
21403367019cSmrg					     screen->cmap_data,
21413367019cSmrg					     (int) screen->cmap_size) != 0);
21423367019cSmrg	}
21433367019cSmrg    }
2144d522f475Smrg    return result;
2145d522f475Smrg}
2146d522f475Smrg
2147d522f475Smrg/*
2148d522f475Smrg * Find closest color for "def" in "cmap".
2149d522f475Smrg * Set "def" to the resulting color.
2150d522f475Smrg *
2151d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2152d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2153d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2154d522f475Smrg *
2155d522f475Smrg * These provide some introduction:
2156d522f475Smrg *	http://en.wikipedia.org/wiki/YIQ
2157d522f475Smrg *		for an introduction to YIQ weights.
2158d522f475Smrg *	http://en.wikipedia.org/wiki/Luminance_(video)
2159d522f475Smrg *		for a discussion of luma.
2160d522f475Smrg *	http://en.wikipedia.org/wiki/YUV
2161d522f475Smrg *
2162d522f475Smrg * Return False if not able to find or allocate a color.
2163d522f475Smrg */
2164d522f475Smrgstatic Boolean
21653367019cSmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor * def)
2166d522f475Smrg{
21673367019cSmrg    TScreen *screen = TScreenOf(xw);
2168d522f475Smrg    Boolean result = False;
2169d522f475Smrg    char *tried;
2170d522f475Smrg    double diff, thisRGB, bestRGB;
2171d522f475Smrg    unsigned attempts;
2172d522f475Smrg    unsigned bestInx;
21733367019cSmrg    unsigned cmap_type;
2174d522f475Smrg    unsigned cmap_size;
2175d522f475Smrg    unsigned i;
2176d522f475Smrg
21773367019cSmrg    getColormapInfo(screen->display, &cmap_type, &cmap_size);
2178d522f475Smrg
21793367019cSmrg    if ((cmap_type & 1) != 0) {
21803367019cSmrg
21813367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2182d522f475Smrg
2183b7c89284Ssnj	    tried = TypeCallocN(char, (size_t) cmap_size);
2184d522f475Smrg	    if (tried != 0) {
2185d522f475Smrg
2186d522f475Smrg		/*
2187d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2188d522f475Smrg		 * approximation to the requested color.
2189d522f475Smrg		 */
2190d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2191d522f475Smrg		    Boolean first = True;
2192d522f475Smrg
2193d522f475Smrg		    bestRGB = 0.0;
2194d522f475Smrg		    bestInx = 0;
2195d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2196d522f475Smrg			if (!tried[bestInx]) {
2197d522f475Smrg			    /*
2198d522f475Smrg			     * Look for the best match based on luminance.
2199d522f475Smrg			     * Measure this by the least-squares difference of
2200d522f475Smrg			     * the weighted R/G/B components from the color map
2201d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2202d522f475Smrg			     * component of the YIQ color space model for
2203d522f475Smrg			     * weights that correspond to the luminance.
2204d522f475Smrg			     */
2205d522f475Smrg#define AddColorWeight(weight, color) \
22063367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2207d522f475Smrg			    thisRGB = diff * diff
2208d522f475Smrg
2209d522f475Smrg			    AddColorWeight(0.30, red);
2210d522f475Smrg			    AddColorWeight(0.61, green);
2211d522f475Smrg			    AddColorWeight(0.11, blue);
2212d522f475Smrg
2213d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2214d522f475Smrg				first = False;
2215d522f475Smrg				bestInx = i;
2216d522f475Smrg				bestRGB = thisRGB;
2217d522f475Smrg			    }
2218d522f475Smrg			}
2219d522f475Smrg		    }
22203367019cSmrg		    if (XAllocColor(screen->display, cmap,
22213367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
22223367019cSmrg			*def = screen->cmap_data[bestInx];
22233367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
22243367019cSmrg			       def->green, def->blue));
2225d522f475Smrg			result = True;
2226d522f475Smrg			break;
2227d522f475Smrg		    }
2228d522f475Smrg		    /*
2229d522f475Smrg		     * It failed - either the color map entry was readonly, or
2230d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2231d522f475Smrg		     * so we will ignore it
2232d522f475Smrg		     */
2233d522f475Smrg		    tried[bestInx] = True;
2234d522f475Smrg		}
2235d522f475Smrg		free(tried);
2236d522f475Smrg	    }
2237d522f475Smrg	}
2238d522f475Smrg    }
2239d522f475Smrg    return result;
2240d522f475Smrg}
2241d522f475Smrg
22423367019cSmrg#ifndef ULONG_MAX
22433367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
22443367019cSmrg#endif
22453367019cSmrg
22463367019cSmrg#define CheckColor(result, value) \
22473367019cSmrg	    result = 0; \
22483367019cSmrg	    if (value.red) \
22493367019cSmrg		result |= 1; \
22503367019cSmrg	    if (value.green) \
22513367019cSmrg		result |= 2; \
22523367019cSmrg	    if (value.blue) \
22533367019cSmrg		result |= 4
22543367019cSmrg
22553367019cSmrg#define SelectColor(state, value, result) \
22563367019cSmrg	switch (state) { \
22573367019cSmrg	default: \
22583367019cSmrg	case 1: \
22593367019cSmrg	    result = value.red; \
22603367019cSmrg	    break; \
22613367019cSmrg	case 2: \
22623367019cSmrg	    result = value.green; \
22633367019cSmrg	    break; \
22643367019cSmrg	case 4: \
22653367019cSmrg	    result = value.blue; \
22663367019cSmrg	    break; \
22673367019cSmrg	}
22683367019cSmrg
22693367019cSmrg/*
22703367019cSmrg * Check if the color map consists of values in exactly one of the red, green
22713367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
22723367019cSmrg * match.
22733367019cSmrg */
22743367019cSmrgstatic int
22753367019cSmrgsimpleColors(XColor * colortable, unsigned length)
22763367019cSmrg{
22773367019cSmrg    unsigned n;
22783367019cSmrg    int state = -1;
22793367019cSmrg    int check;
22803367019cSmrg
22813367019cSmrg    for (n = 0; n < length; ++n) {
22823367019cSmrg	if (state == -1) {
22833367019cSmrg	    CheckColor(state, colortable[n]);
22843367019cSmrg	    if (state == 0)
22853367019cSmrg		state = -1;
22863367019cSmrg	}
22873367019cSmrg	if (state > 0) {
22883367019cSmrg	    CheckColor(check, colortable[n]);
22893367019cSmrg	    if (check > 0 && check != state) {
22903367019cSmrg		state = 0;
22913367019cSmrg		break;
22923367019cSmrg	    }
22933367019cSmrg	}
22943367019cSmrg    }
22953367019cSmrg    switch (state) {
22963367019cSmrg    case 1:
22973367019cSmrg    case 2:
22983367019cSmrg    case 4:
22993367019cSmrg	break;
23003367019cSmrg    default:
23013367019cSmrg	state = 0;
23023367019cSmrg	break;
23033367019cSmrg    }
23043367019cSmrg    return state;
23053367019cSmrg}
23063367019cSmrg
23073367019cSmrgstatic unsigned
23083367019cSmrgsearchColors(XColor * colortable, unsigned length, unsigned color, int state)
23093367019cSmrg{
23103367019cSmrg    unsigned result = 0;
23113367019cSmrg    unsigned n;
23123367019cSmrg    unsigned long best = ULONG_MAX;
23133367019cSmrg    unsigned long diff;
23143367019cSmrg    unsigned value;
23153367019cSmrg
23163367019cSmrg    for (n = 0; n < length; ++n) {
23173367019cSmrg	SelectColor(state, colortable[n], value);
23183367019cSmrg	diff = (color - value);
23193367019cSmrg	diff *= diff;
23203367019cSmrg	if (diff < best) {
23213367019cSmrg#if 0
23223367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
23233367019cSmrg		   n, color,
23243367019cSmrg		   colortable[n].red,
23253367019cSmrg		   colortable[n].green,
23263367019cSmrg		   colortable[n].blue,
23273367019cSmrg		   diff));
23283367019cSmrg#endif
23293367019cSmrg	    result = n;
23303367019cSmrg	    best = diff;
23313367019cSmrg	}
23323367019cSmrg    }
23333367019cSmrg    SelectColor(state, colortable[result], value);
23343367019cSmrg    return value;
23353367019cSmrg}
23363367019cSmrg
23373367019cSmrg/*
23383367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
23393367019cSmrg *
23403367019cSmrg * According to
23413367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
23423367019cSmrg *
23433367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
23443367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
23453367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
23463367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
23473367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
23483367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
23493367019cSmrg *     actual RGB values allocated.
23503367019cSmrg *
23513367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
23523367019cSmrg * case, allocateClosesRGB() is useful for the dynamic display classes such as
23533367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
23543367019cSmrg * return regular RGB triples (unless a different scheme was used for
23553367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
23563367019cSmrg * is filled in with the colors that the server supports.
23573367019cSmrg *
23583367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
23593367019cSmrg * described.  For some TrueColor configurations it merely returns a close
23603367019cSmrg * approximation, but not the closest.
23613367019cSmrg */
23623367019cSmrgstatic Boolean
23633367019cSmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor * def)
23643367019cSmrg{
23653367019cSmrg    XColor save = *def;
23663367019cSmrg    TScreen *screen = TScreenOf(xw);
23673367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
23683367019cSmrg
23693367019cSmrg    /*
23703367019cSmrg     * If this is a statically allocated display, e.g., TrueColor, see if we
23713367019cSmrg     * can improve on the result by using the color values actually supported
23723367019cSmrg     * by the server.
23733367019cSmrg     */
23743367019cSmrg    if (result) {
23753367019cSmrg	unsigned cmap_type;
23763367019cSmrg	unsigned cmap_size;
23773367019cSmrg	int state;
23783367019cSmrg
23793367019cSmrg	getColormapInfo(screen->display, &cmap_type, &cmap_size);
23803367019cSmrg
23813367019cSmrg	if ((cmap_type & 1) == 0) {
23823367019cSmrg	    XColor temp = *def;
23833367019cSmrg
23843367019cSmrg	    if (loadColorTable(xw, cmap_size)
23853367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
23863367019cSmrg#define SearchColors(which) temp.which = (unsigned short) searchColors(screen->cmap_data, cmap_size, save.which, state)
23873367019cSmrg		SearchColors(red);
23883367019cSmrg		SearchColors(green);
23893367019cSmrg		SearchColors(blue);
23903367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
23913367019cSmrg#if OPT_TRACE
23923367019cSmrg		    if (temp.red != save.red
23933367019cSmrg			|| temp.green != save.green
23943367019cSmrg			|| temp.blue != save.blue) {
23953367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
23963367019cSmrg			       save.red, save.green, save.blue,
23973367019cSmrg			       temp.red, temp.green, temp.blue));
23983367019cSmrg		    } else {
23993367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
24003367019cSmrg			       save.red, save.green, save.blue));
24013367019cSmrg		    }
24023367019cSmrg#endif
24033367019cSmrg		    *def = temp;
24043367019cSmrg		}
24053367019cSmrg	    }
24063367019cSmrg	}
24073367019cSmrg    }
24083367019cSmrg
24093367019cSmrg    return result;
24103367019cSmrg}
24113367019cSmrg
2412d522f475Smrg/*
2413d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2414d522f475Smrg * to 256.
2415d522f475Smrg *
2416d522f475Smrg * Returns
2417d522f475Smrg *	-1 on error
2418d522f475Smrg *	0 on no change
2419d522f475Smrg *	1 if a new color was allocated.
2420d522f475Smrg */
2421d522f475Smrgstatic int
2422d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2423d522f475Smrg		  ColorRes * res,
2424cd3331d0Smrg		  const char *spec)
2425d522f475Smrg{
2426d522f475Smrg    int result;
2427d522f475Smrg    XColor def;
2428d522f475Smrg
24293367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2430d522f475Smrg	if (
2431d522f475Smrg#if OPT_COLOR_RES
2432d522f475Smrg	       res->mode == True &&
2433d522f475Smrg#endif
2434d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
2435d522f475Smrg	    result = 0;
2436d522f475Smrg	} else {
2437d522f475Smrg	    result = 1;
2438d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
24393367019cSmrg	    res->red = def.red;
24403367019cSmrg	    res->green = def.green;
24413367019cSmrg	    res->blue = def.blue;
24423367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
24433367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
24443367019cSmrg		   def.red,
24453367019cSmrg		   def.green,
24463367019cSmrg		   def.blue,
24473367019cSmrg		   def.pixel));
2448d522f475Smrg#if OPT_COLOR_RES
2449d522f475Smrg	    if (!res->mode)
2450d522f475Smrg		result = 0;
2451d522f475Smrg	    res->mode = True;
2452d522f475Smrg#endif
2453d522f475Smrg	}
2454d522f475Smrg    } else {
2455d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2456d522f475Smrg	result = -1;
2457d522f475Smrg    }
2458d522f475Smrg    return (result);
2459d522f475Smrg}
2460d522f475Smrg
2461d522f475Smrg#if OPT_COLOR_RES
2462d522f475SmrgPixel
2463cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2464d522f475Smrg{
2465d522f475Smrg    Pixel result = 0;
2466d522f475Smrg
2467d522f475Smrg    if (res->mode) {
2468d522f475Smrg	result = res->value;
2469d522f475Smrg    } else {
2470d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2471cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2472d522f475Smrg
2473cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2474cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2475d522f475Smrg
2476cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2477cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2478d522f475Smrg		res->mode = -True;
24793367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
24803367019cSmrg			     NonNull(res->resource));
2481d522f475Smrg	    }
2482d522f475Smrg	    result = res->value;
2483d522f475Smrg	} else {
2484d522f475Smrg	    result = 0;
2485d522f475Smrg	}
2486d522f475Smrg    }
2487d522f475Smrg    return result;
2488d522f475Smrg}
2489d522f475Smrg#endif
2490d522f475Smrg
2491cd3331d0Smrgstatic int
2492cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2493cd3331d0Smrg{
2494cd3331d0Smrg    int code;
2495cd3331d0Smrg
2496cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2497cd3331d0Smrg	code = -1;
2498cd3331d0Smrg    } else {
2499cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2500cd3331d0Smrg
2501cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2502cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2503cd3331d0Smrg    }
2504cd3331d0Smrg    return code;
2505cd3331d0Smrg}
2506cd3331d0Smrg
2507cd3331d0Smrg/*
2508cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2509cd3331d0Smrg * values from the given buffer.
2510cd3331d0Smrg *
2511cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2512cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2513cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2514cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2515cd3331d0Smrg * 'first' set to the beginning of those indices.
2516cd3331d0Smrg *
2517cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2518cd3331d0Smrg */
2519d522f475Smrgstatic Bool
2520d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2521d522f475Smrg		       char *buf,
2522cd3331d0Smrg		       int first,
2523d522f475Smrg		       int final)
2524d522f475Smrg{
2525d522f475Smrg    char *name;
2526d522f475Smrg    int color;
2527d522f475Smrg    int repaint = False;
2528d522f475Smrg    int code;
2529cd3331d0Smrg    int last = (MAXCOLORS - first);
2530d522f475Smrg
2531d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2532d522f475Smrg
2533d522f475Smrg    while (buf && *buf) {
2534d522f475Smrg	name = strchr(buf, ';');
2535d522f475Smrg	if (name == NULL)
2536d522f475Smrg	    break;
2537d522f475Smrg	*name = '\0';
2538d522f475Smrg	name++;
2539d522f475Smrg	color = atoi(buf);
2540cd3331d0Smrg	if (color < 0 || color >= last)
2541cd3331d0Smrg	    break;		/* quit on any error */
2542d522f475Smrg	buf = strchr(name, ';');
2543d522f475Smrg	if (buf) {
2544d522f475Smrg	    *buf = '\0';
2545d522f475Smrg	    buf++;
2546d522f475Smrg	}
2547cd3331d0Smrg	if (!strcmp(name, "?")) {
2548cd3331d0Smrg	    ReportAnsiColorRequest(xw, color + first, final);
2549cd3331d0Smrg	} else {
2550cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
2551d522f475Smrg	    if (code < 0) {
2552d522f475Smrg		/* stop on any error */
2553d522f475Smrg		break;
2554d522f475Smrg	    } else if (code > 0) {
2555d522f475Smrg		repaint = True;
2556d522f475Smrg	    }
2557d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
2558d522f475Smrg	     * change style (dynamic colors).
2559d522f475Smrg	     */
2560d522f475Smrg	}
2561d522f475Smrg    }
2562d522f475Smrg
2563d522f475Smrg    return (repaint);
2564d522f475Smrg}
2565cd3331d0Smrg
2566cd3331d0Smrgstatic Bool
2567cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
2568cd3331d0Smrg{
2569cd3331d0Smrg    Bool repaint = False;
2570cd3331d0Smrg    int last = MAXCOLORS - start;
2571cd3331d0Smrg
2572cd3331d0Smrg    if (color >= 0 && color < last) {
2573cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
2574cd3331d0Smrg
2575cd3331d0Smrg	if (res->mode) {
2576cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
2577cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
2578cd3331d0Smrg		repaint = True;
2579cd3331d0Smrg	    }
2580cd3331d0Smrg	}
2581cd3331d0Smrg    }
2582cd3331d0Smrg    return repaint;
2583cd3331d0Smrg}
2584cd3331d0Smrg
2585cd3331d0Smrgint
2586cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
2587cd3331d0Smrg{
2588cd3331d0Smrg    int repaint = 0;
2589cd3331d0Smrg    int color;
2590cd3331d0Smrg
2591cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
2592cd3331d0Smrg    if (*buf != '\0') {
2593cd3331d0Smrg	/* reset specific colors */
2594cd3331d0Smrg	while (!IsEmpty(buf)) {
2595cd3331d0Smrg	    char *next;
2596cd3331d0Smrg
2597cd3331d0Smrg	    color = (int) strtol(buf, &next, 10);
2598cd3331d0Smrg	    if ((next == buf) || (color < 0))
2599cd3331d0Smrg		break;		/* no number at all */
2600cd3331d0Smrg	    if (next != 0) {
2601cd3331d0Smrg		if (strchr(";", *next) == 0)
2602cd3331d0Smrg		    break;	/* unexpected delimiter */
2603cd3331d0Smrg		++next;
2604cd3331d0Smrg	    }
2605cd3331d0Smrg
2606cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2607cd3331d0Smrg		++repaint;
2608cd3331d0Smrg	    }
2609cd3331d0Smrg	    buf = next;
2610cd3331d0Smrg	}
2611cd3331d0Smrg    } else {
2612cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
2613cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
2614cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2615cd3331d0Smrg		++repaint;
2616cd3331d0Smrg	    }
2617cd3331d0Smrg	}
2618cd3331d0Smrg    }
2619cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
2620cd3331d0Smrg    return repaint;
2621cd3331d0Smrg}
2622d522f475Smrg#else
26233367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
26243367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
2625d522f475Smrg#endif /* OPT_ISO_COLORS */
2626d522f475Smrg
26273367019cSmrgstatic Boolean
26283367019cSmrgxtermAllocColor(XtermWidget xw, XColor * def, const char *spec)
26293367019cSmrg{
26303367019cSmrg    Boolean result = False;
26313367019cSmrg    TScreen *screen = TScreenOf(xw);
26323367019cSmrg    Colormap cmap = xw->core.colormap;
26333367019cSmrg
26343367019cSmrg    if (XParseColor(screen->display, cmap, spec, def)
26353367019cSmrg	&& (allocateExactRGB(xw, cmap, def)
26363367019cSmrg	    || allocateClosestRGB(xw, cmap, def))) {
26373367019cSmrg	TRACE(("xtermAllocColor -> %x/%x/%x\n",
26383367019cSmrg	       def->red, def->green, def->blue));
26393367019cSmrg	result = True;
26403367019cSmrg    }
26413367019cSmrg    return result;
26423367019cSmrg}
26433367019cSmrg
26443367019cSmrg/*
26453367019cSmrg * This provides an approximation (the closest color from xterm's palette)
26463367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
26473367019cSmrg * because of the context in which it is used.
26483367019cSmrg */
26493367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
26503367019cSmrgint
26513367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
26523367019cSmrg{
26533367019cSmrg    int result = -1;
26543367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
26553367019cSmrg    int n;
26563367019cSmrg    int best_index = -1;
26573367019cSmrg    unsigned long best_value = 0;
26583367019cSmrg    unsigned long this_value;
26593367019cSmrg    long diff_red, diff_green, diff_blue;
26603367019cSmrg
26613367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
26623367019cSmrg
26633367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
26643367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
26653367019cSmrg
26663367019cSmrg	/* ensure that we have a value for each of the colors */
26673367019cSmrg	if (!res->mode) {
26683367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
26693367019cSmrg	}
26703367019cSmrg
26713367019cSmrg	/* find the closest match */
26723367019cSmrg	if (res->mode == True) {
26733367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
26743367019cSmrg		    res->value, res->red, res->green, res->blue));
26753367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
26763367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
26773367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
26783367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
26793367019cSmrg					  + (diff_green * diff_green)
26803367019cSmrg					  + (diff_blue * diff_blue));
26813367019cSmrg	    if (best_index < 0 || this_value < best_value) {
26823367019cSmrg		best_index = n;
26833367019cSmrg		best_value = this_value;
26843367019cSmrg	    }
26853367019cSmrg	}
26863367019cSmrg    }
26873367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
26883367019cSmrg    result = best_index;
26893367019cSmrg#else
26903367019cSmrg    (void) xw;
26913367019cSmrg    (void) find_red;
26923367019cSmrg    (void) find_green;
26933367019cSmrg    (void) find_blue;
26943367019cSmrg#endif
26953367019cSmrg    return result;
26963367019cSmrg}
26973367019cSmrg
2698d522f475Smrg#if OPT_PASTE64
2699d522f475Smrgstatic void
2700d522f475SmrgManipulateSelectionData(XtermWidget xw, TScreen * screen, char *buf, int final)
2701d522f475Smrg{
2702d522f475Smrg#define PDATA(a,b) { a, #b }
2703d522f475Smrg    static struct {
2704d522f475Smrg	char given;
2705cd3331d0Smrg	String result;
2706d522f475Smrg    } table[] = {
2707d522f475Smrg	PDATA('s', SELECT),
2708d522f475Smrg	    PDATA('p', PRIMARY),
2709d522f475Smrg	    PDATA('c', CLIPBOARD),
2710d522f475Smrg	    PDATA('0', CUT_BUFFER0),
2711d522f475Smrg	    PDATA('1', CUT_BUFFER1),
2712d522f475Smrg	    PDATA('2', CUT_BUFFER2),
2713d522f475Smrg	    PDATA('3', CUT_BUFFER3),
2714d522f475Smrg	    PDATA('4', CUT_BUFFER4),
2715d522f475Smrg	    PDATA('5', CUT_BUFFER5),
2716d522f475Smrg	    PDATA('6', CUT_BUFFER6),
2717d522f475Smrg	    PDATA('7', CUT_BUFFER7),
2718d522f475Smrg    };
2719d522f475Smrg
2720cd3331d0Smrg    const char *base = buf;
27213367019cSmrg    char *used;
2722d522f475Smrg    Cardinal j, n = 0;
27233367019cSmrg    String *select_args;
2724d522f475Smrg
2725d522f475Smrg    TRACE(("Manipulate selection data\n"));
2726d522f475Smrg
2727d522f475Smrg    while (*buf != ';' && *buf != '\0') {
2728d522f475Smrg	++buf;
2729d522f475Smrg    }
2730d522f475Smrg
2731d522f475Smrg    if (*buf == ';') {
2732d522f475Smrg	*buf++ = '\0';
2733d522f475Smrg
2734d522f475Smrg	if (*base == '\0')
2735d522f475Smrg	    base = "s0";
2736d522f475Smrg
27373367019cSmrg	if ((used = x_strdup(base)) != 0) {
27383367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
27393367019cSmrg		while (*base != '\0') {
27403367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
27413367019cSmrg			if (*base == table[j].given) {
27423367019cSmrg			    used[n] = *base;
27433367019cSmrg			    select_args[n++] = table[j].result;
27443367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
27453367019cSmrg			    break;
27463367019cSmrg			}
27473367019cSmrg		    }
27483367019cSmrg		    ++base;
27493367019cSmrg		}
27503367019cSmrg		used[n] = 0;
27513367019cSmrg
27523367019cSmrg		if (!strcmp(buf, "?")) {
27533367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
27543367019cSmrg			TRACE(("Getting selection\n"));
27553367019cSmrg			unparseputc1(xw, ANSI_OSC);
27563367019cSmrg			unparseputs(xw, "52");
27573367019cSmrg			unparseputc(xw, ';');
27583367019cSmrg
27593367019cSmrg			unparseputs(xw, used);
27603367019cSmrg			unparseputc(xw, ';');
27613367019cSmrg
27623367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
27633367019cSmrg			screen->base64_paste = n;
27643367019cSmrg			screen->base64_final = final;
27653367019cSmrg
27663367019cSmrg			/* terminator will be written in this call */
27673367019cSmrg			xtermGetSelection((Widget) xw,
27683367019cSmrg					  (Time) 0,
27693367019cSmrg					  select_args, n,
27703367019cSmrg					  NULL);
27713367019cSmrg		    }
27723367019cSmrg		} else {
27733367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
27743367019cSmrg			TRACE(("Setting selection with %s\n", buf));
27753367019cSmrg			ClearSelectionBuffer(screen);
27763367019cSmrg			while (*buf != '\0')
27773367019cSmrg			    AppendToSelectionBuffer(screen, CharOf(*buf++));
27783367019cSmrg			CompleteSelection(xw, select_args, n);
27793367019cSmrg		    }
27803367019cSmrg		}
27813367019cSmrg		free(select_args);
2782cd3331d0Smrg	    }
27833367019cSmrg	    free(used);
2784d522f475Smrg	}
2785d522f475Smrg    }
2786d522f475Smrg}
2787d522f475Smrg#endif /* OPT_PASTE64 */
2788d522f475Smrg
2789d522f475Smrg/***====================================================================***/
2790d522f475Smrg
2791cd3331d0Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
2792cd3331d0Smrg
2793d522f475Smrgstatic Bool
2794cd3331d0SmrgxtermIsPrintable(XtermWidget xw, Char ** bufp, Char * last)
2795d522f475Smrg{
2796cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2797d522f475Smrg    Bool result = False;
2798d522f475Smrg    Char *cp = *bufp;
2799d522f475Smrg    Char *next = cp;
2800d522f475Smrg
2801d522f475Smrg    (void) screen;
2802d522f475Smrg    (void) last;
2803d522f475Smrg
2804d522f475Smrg#if OPT_WIDE_CHARS
2805cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
2806d522f475Smrg	PtyData data;
2807d522f475Smrg
2808d522f475Smrg	if (decodeUtf8(fakePtyData(&data, cp, last))) {
2809d522f475Smrg	    if (data.utf_data != UCS_REPL
2810d522f475Smrg		&& (data.utf_data >= 128 ||
2811d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
2812d522f475Smrg		next += (data.utf_size - 1);
2813d522f475Smrg		result = True;
2814d522f475Smrg	    } else {
2815d522f475Smrg		result = False;
2816d522f475Smrg	    }
2817d522f475Smrg	} else {
2818d522f475Smrg	    result = False;
2819d522f475Smrg	}
2820d522f475Smrg    } else
2821d522f475Smrg#endif
2822d522f475Smrg#if OPT_C1_PRINT
2823d522f475Smrg	if (screen->c1_printable
2824d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
2825d522f475Smrg	result = True;
2826d522f475Smrg    } else
2827d522f475Smrg#endif
2828d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
2829d522f475Smrg	result = True;
2830d522f475Smrg    }
2831d522f475Smrg    *bufp = next;
2832d522f475Smrg    return result;
2833d522f475Smrg}
2834d522f475Smrg
2835d522f475Smrg/***====================================================================***/
2836d522f475Smrg
2837d522f475Smrg/*
2838d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
2839cd3331d0Smrg * array indices.  Compare with TermColors.
2840d522f475Smrg */
2841d522f475Smrgtypedef enum {
2842d522f475Smrg    OSC_TEXT_FG = 10
2843d522f475Smrg    ,OSC_TEXT_BG
2844d522f475Smrg    ,OSC_TEXT_CURSOR
2845d522f475Smrg    ,OSC_MOUSE_FG
2846d522f475Smrg    ,OSC_MOUSE_BG
2847d522f475Smrg#if OPT_TEK4014
2848d522f475Smrg    ,OSC_TEK_FG = 15
2849d522f475Smrg    ,OSC_TEK_BG
2850d522f475Smrg#endif
2851d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2852d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
2853d522f475Smrg#endif
2854d522f475Smrg#if OPT_TEK4014
2855d522f475Smrg    ,OSC_TEK_CURSOR = 18
2856d522f475Smrg#endif
2857d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2858d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
2859d522f475Smrg#endif
2860d522f475Smrg    ,OSC_NCOLORS
2861d522f475Smrg} OscTextColors;
2862d522f475Smrg
2863cd3331d0Smrg/*
2864cd3331d0Smrg * Map codes to OSC controls that can reset colors.
2865cd3331d0Smrg */
2866cd3331d0Smrg#define OSC_RESET 100
2867cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
2868cd3331d0Smrg
2869d522f475Smrgstatic ScrnColors *pOldColors = NULL;
2870d522f475Smrg
2871d522f475Smrgstatic Bool
2872d522f475SmrgGetOldColors(XtermWidget xw)
2873d522f475Smrg{
2874d522f475Smrg    int i;
2875d522f475Smrg    if (pOldColors == NULL) {
2876c219fbebSmrg	pOldColors = TypeXtMalloc(ScrnColors);
2877d522f475Smrg	if (pOldColors == NULL) {
28783367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
2879d522f475Smrg	    return (False);
2880d522f475Smrg	}
2881d522f475Smrg	pOldColors->which = 0;
2882d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
2883d522f475Smrg	    pOldColors->colors[i] = 0;
2884d522f475Smrg	    pOldColors->names[i] = NULL;
2885d522f475Smrg	}
2886d522f475Smrg	GetColors(xw, pOldColors);
2887d522f475Smrg    }
2888d522f475Smrg    return (True);
2889d522f475Smrg}
2890d522f475Smrg
2891d522f475Smrgstatic int
2892d522f475SmrgoppositeColor(int n)
2893d522f475Smrg{
2894d522f475Smrg    switch (n) {
2895d522f475Smrg    case TEXT_FG:
2896d522f475Smrg	n = TEXT_BG;
2897d522f475Smrg	break;
2898d522f475Smrg    case TEXT_BG:
2899d522f475Smrg	n = TEXT_FG;
2900d522f475Smrg	break;
2901d522f475Smrg    case MOUSE_FG:
2902d522f475Smrg	n = MOUSE_BG;
2903d522f475Smrg	break;
2904d522f475Smrg    case MOUSE_BG:
2905d522f475Smrg	n = MOUSE_FG;
2906d522f475Smrg	break;
2907d522f475Smrg#if OPT_TEK4014
2908d522f475Smrg    case TEK_FG:
2909d522f475Smrg	n = TEK_BG;
2910d522f475Smrg	break;
2911d522f475Smrg    case TEK_BG:
2912d522f475Smrg	n = TEK_FG;
2913d522f475Smrg	break;
2914d522f475Smrg#endif
2915d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2916d522f475Smrg    case HIGHLIGHT_FG:
2917d522f475Smrg	n = HIGHLIGHT_BG;
2918d522f475Smrg	break;
2919d522f475Smrg    case HIGHLIGHT_BG:
2920d522f475Smrg	n = HIGHLIGHT_FG;
2921d522f475Smrg	break;
2922d522f475Smrg#endif
2923d522f475Smrg    default:
2924d522f475Smrg	break;
2925d522f475Smrg    }
2926d522f475Smrg    return n;
2927d522f475Smrg}
2928d522f475Smrg
2929d522f475Smrgstatic void
2930d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
2931d522f475Smrg{
2932cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
2933cd3331d0Smrg	XColor color;
2934cd3331d0Smrg	Colormap cmap = xw->core.colormap;
2935cd3331d0Smrg	char buffer[80];
2936d522f475Smrg
2937cd3331d0Smrg	/*
2938cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
2939cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
2940cd3331d0Smrg	 * reporting the opposite color which would be used.
2941cd3331d0Smrg	 */
2942cd3331d0Smrg	int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
2943cd3331d0Smrg
2944cd3331d0Smrg	GetOldColors(xw);
2945cd3331d0Smrg	color.pixel = pOldColors->colors[ndx];
2946cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
2947cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
2948cd3331d0Smrg		color.red,
2949cd3331d0Smrg		color.green,
2950cd3331d0Smrg		color.blue);
2951712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
2952cd3331d0Smrg	       ndx, pOldColors->colors[ndx], buffer));
2953cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
2954cd3331d0Smrg	unparseputs(xw, buffer);
2955cd3331d0Smrg	unparseputc1(xw, final);
2956cd3331d0Smrg	unparse_end(xw);
2957cd3331d0Smrg    }
2958d522f475Smrg}
2959d522f475Smrg
2960d522f475Smrgstatic Bool
2961d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
2962d522f475Smrg{
2963d522f475Smrg    int i;
2964d522f475Smrg
2965d522f475Smrg    /* if we were going to free old colors, this would be the place to
2966d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
2967d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
2968d522f475Smrg     * we could save some overhead this way.   The only case in which this
2969d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
2970d522f475Smrg     * which case they can restart xterm
2971d522f475Smrg     */
2972d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
2973d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
2974d522f475Smrg	    if (pOldColors->names[i] != NULL) {
2975d522f475Smrg		XtFree(pOldColors->names[i]);
2976d522f475Smrg		pOldColors->names[i] = NULL;
2977d522f475Smrg	    }
2978d522f475Smrg	    if (pNew->names[i]) {
2979d522f475Smrg		pOldColors->names[i] = pNew->names[i];
2980d522f475Smrg	    }
2981d522f475Smrg	    pOldColors->colors[i] = pNew->colors[i];
2982d522f475Smrg	}
2983d522f475Smrg    }
2984d522f475Smrg    return (True);
2985d522f475Smrg}
2986d522f475Smrg
2987d522f475Smrg/*
2988d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
2989d522f475Smrg * xterm is compiled.
2990d522f475Smrg */
2991d522f475Smrgstatic int
2992d522f475SmrgOscToColorIndex(OscTextColors mode)
2993d522f475Smrg{
2994d522f475Smrg    int result = 0;
2995d522f475Smrg
2996d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
2997d522f475Smrg    switch (mode) {
2998d522f475Smrg	CASE(TEXT_FG);
2999d522f475Smrg	CASE(TEXT_BG);
3000d522f475Smrg	CASE(TEXT_CURSOR);
3001d522f475Smrg	CASE(MOUSE_FG);
3002d522f475Smrg	CASE(MOUSE_BG);
3003d522f475Smrg#if OPT_TEK4014
3004d522f475Smrg	CASE(TEK_FG);
3005d522f475Smrg	CASE(TEK_BG);
3006d522f475Smrg#endif
3007d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3008d522f475Smrg	CASE(HIGHLIGHT_BG);
3009d522f475Smrg	CASE(HIGHLIGHT_FG);
3010d522f475Smrg#endif
3011d522f475Smrg#if OPT_TEK4014
3012d522f475Smrg	CASE(TEK_CURSOR);
3013d522f475Smrg#endif
3014d522f475Smrg    case OSC_NCOLORS:
3015d522f475Smrg	break;
3016d522f475Smrg    }
3017d522f475Smrg    return result;
3018d522f475Smrg}
3019d522f475Smrg
3020d522f475Smrgstatic Bool
3021d522f475SmrgChangeColorsRequest(XtermWidget xw,
3022d522f475Smrg		    int start,
3023d522f475Smrg		    char *names,
3024d522f475Smrg		    int final)
3025d522f475Smrg{
3026d522f475Smrg    Bool result = False;
3027d522f475Smrg    char *thisName;
3028d522f475Smrg    ScrnColors newColors;
3029d522f475Smrg    int i, ndx;
3030d522f475Smrg
3031d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3032d522f475Smrg
3033d522f475Smrg    if (GetOldColors(xw)) {
3034d522f475Smrg	newColors.which = 0;
3035d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3036d522f475Smrg	    newColors.names[i] = NULL;
3037d522f475Smrg	}
3038d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3039d522f475Smrg	    ndx = OscToColorIndex((OscTextColors) i);
3040d522f475Smrg	    if (xw->misc.re_verse)
3041d522f475Smrg		ndx = oppositeColor(ndx);
3042d522f475Smrg
3043cd3331d0Smrg	    if (IsEmpty(names)) {
3044d522f475Smrg		newColors.names[ndx] = NULL;
3045d522f475Smrg	    } else {
3046d522f475Smrg		if (names[0] == ';')
3047d522f475Smrg		    thisName = NULL;
3048d522f475Smrg		else
3049d522f475Smrg		    thisName = names;
3050d522f475Smrg		names = strchr(names, ';');
3051d522f475Smrg		if (names != NULL) {
3052d522f475Smrg		    *names++ = '\0';
3053d522f475Smrg		}
3054d522f475Smrg		if (thisName != 0 && !strcmp(thisName, "?")) {
3055d522f475Smrg		    ReportColorRequest(xw, ndx, final);
3056d522f475Smrg		} else if (!pOldColors->names[ndx]
3057d522f475Smrg			   || (thisName
3058d522f475Smrg			       && strcmp(thisName, pOldColors->names[ndx]))) {
3059cd3331d0Smrg		    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3060d522f475Smrg		}
3061d522f475Smrg	    }
3062d522f475Smrg	}
3063d522f475Smrg
3064d522f475Smrg	if (newColors.which != 0) {
3065d522f475Smrg	    ChangeColors(xw, &newColors);
3066d522f475Smrg	    UpdateOldColors(xw, &newColors);
3067d522f475Smrg	}
3068d522f475Smrg	result = True;
3069d522f475Smrg    }
3070d522f475Smrg    return result;
3071d522f475Smrg}
3072d522f475Smrg
3073cd3331d0Smrgstatic Bool
3074cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3075cd3331d0Smrg		   int code)
3076cd3331d0Smrg{
3077cd3331d0Smrg    Bool result = False;
3078cd3331d0Smrg    const char *thisName;
3079cd3331d0Smrg    ScrnColors newColors;
3080cd3331d0Smrg    int ndx;
3081cd3331d0Smrg
3082cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3083cd3331d0Smrg
3084cd3331d0Smrg#if OPT_COLOR_RES
3085cd3331d0Smrg    if (GetOldColors(xw)) {
3086cd3331d0Smrg	ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3087cd3331d0Smrg	if (xw->misc.re_verse)
3088cd3331d0Smrg	    ndx = oppositeColor(ndx);
3089cd3331d0Smrg
3090cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3091cd3331d0Smrg
3092cd3331d0Smrg	newColors.which = 0;
3093cd3331d0Smrg	newColors.names[ndx] = NULL;
3094cd3331d0Smrg
3095cd3331d0Smrg	if (thisName != 0
3096cd3331d0Smrg	    && pOldColors->names[ndx] != 0
3097cd3331d0Smrg	    && strcmp(thisName, pOldColors->names[ndx])) {
3098cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3099cd3331d0Smrg
3100cd3331d0Smrg	    if (newColors.which != 0) {
3101cd3331d0Smrg		ChangeColors(xw, &newColors);
3102cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3103cd3331d0Smrg	    }
3104cd3331d0Smrg	}
3105cd3331d0Smrg	result = True;
3106cd3331d0Smrg    }
3107cd3331d0Smrg#endif
3108cd3331d0Smrg    return result;
3109cd3331d0Smrg}
3110cd3331d0Smrg
3111cd3331d0Smrg#if OPT_SHIFT_FONTS
3112cd3331d0Smrg/*
3113cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3114cd3331d0Smrg *
3115cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3116cd3331d0Smrg * the corresponding menu font entry.
3117cd3331d0Smrg */
3118cd3331d0Smrgstatic int
3119cb4a1343SmrgParseShiftedFont(XtermWidget xw, String source, String * target)
3120cd3331d0Smrg{
3121cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3122cd3331d0Smrg    int num = screen->menu_font_number;
3123cd3331d0Smrg    int rel = 0;
3124cd3331d0Smrg
3125cd3331d0Smrg    if (*++source == '+') {
3126cd3331d0Smrg	rel = 1;
3127cd3331d0Smrg	source++;
3128cd3331d0Smrg    } else if (*source == '-') {
3129cd3331d0Smrg	rel = -1;
3130cd3331d0Smrg	source++;
3131cd3331d0Smrg    }
3132cd3331d0Smrg
3133cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3134cd3331d0Smrg	int val = atoi(source);
3135cd3331d0Smrg	if (rel > 0)
3136cd3331d0Smrg	    rel = val;
3137cd3331d0Smrg	else if (rel < 0)
3138cd3331d0Smrg	    rel = -val;
3139cd3331d0Smrg	else
3140cd3331d0Smrg	    num = val;
3141cd3331d0Smrg    }
3142cd3331d0Smrg
3143cd3331d0Smrg    if (rel != 0) {
3144cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3145cd3331d0Smrg				     screen->menu_font_number, rel);
3146cd3331d0Smrg
3147cd3331d0Smrg    }
3148cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3149cd3331d0Smrg    *target = source;
3150cd3331d0Smrg    return num;
3151cd3331d0Smrg}
3152cd3331d0Smrg
3153cd3331d0Smrgstatic void
3154cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3155cd3331d0Smrg{
3156cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3157cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3158cd3331d0Smrg	Bool success = True;
3159cd3331d0Smrg	int num;
3160cb4a1343Smrg	String base = buf + 1;
3161cd3331d0Smrg	const char *name = 0;
3162cd3331d0Smrg	char temp[10];
3163cd3331d0Smrg
3164cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3165cd3331d0Smrg	if (num < 0
3166cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3167cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3168cd3331d0Smrg	    success = False;
3169cd3331d0Smrg	} else {
3170cd3331d0Smrg#if OPT_RENDERFONT
3171cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3172cd3331d0Smrg		name = getFaceName(xw, False);
3173cd3331d0Smrg	    } else
3174cd3331d0Smrg#endif
3175cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3176cd3331d0Smrg		success = False;
3177cd3331d0Smrg	    }
3178cd3331d0Smrg	}
3179cd3331d0Smrg
3180cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3181cd3331d0Smrg	unparseputs(xw, "50");
3182cd3331d0Smrg
3183cd3331d0Smrg	if (success) {
3184cd3331d0Smrg	    unparseputc(xw, ';');
3185cd3331d0Smrg	    if (buf >= base) {
3186cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3187cd3331d0Smrg		if (*buf != '\0') {
3188cd3331d0Smrg		    unparseputc(xw, '#');
3189cd3331d0Smrg		    sprintf(temp, "%d", num);
3190cd3331d0Smrg		    unparseputs(xw, temp);
3191cd3331d0Smrg		    if (*name != '\0')
3192cd3331d0Smrg			unparseputc(xw, ' ');
3193cd3331d0Smrg		}
3194cd3331d0Smrg	    }
3195cd3331d0Smrg	    unparseputs(xw, name);
3196cd3331d0Smrg	}
3197cd3331d0Smrg
3198cd3331d0Smrg	unparseputc1(xw, final);
3199cd3331d0Smrg	unparse_end(xw);
3200cd3331d0Smrg    }
3201cd3331d0Smrg}
3202cd3331d0Smrg
3203cd3331d0Smrgstatic void
3204cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3205cd3331d0Smrg{
3206cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3207cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3208cd3331d0Smrg	Bool success = True;
3209cd3331d0Smrg	int num;
3210cd3331d0Smrg	VTFontNames fonts;
3211cd3331d0Smrg	char *name;
3212cd3331d0Smrg
3213cd3331d0Smrg	/*
3214cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3215cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3216cd3331d0Smrg	 *
3217cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3218cd3331d0Smrg	 * to load the font entry.
3219cd3331d0Smrg	 */
3220cd3331d0Smrg	if (*buf == '#') {
3221cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3222cd3331d0Smrg
3223cd3331d0Smrg	    if (num < 0
3224cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3225cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3226cd3331d0Smrg		success = False;
3227cd3331d0Smrg	    } else {
3228cd3331d0Smrg		/*
3229cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3230cd3331d0Smrg		 * for a font specification within the control.
3231cd3331d0Smrg		 */
3232cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3233cd3331d0Smrg		    ++buf;
3234cd3331d0Smrg		}
3235cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3236cd3331d0Smrg		    ++buf;
3237cd3331d0Smrg		}
3238cd3331d0Smrg#if OPT_RENDERFONT
3239cd3331d0Smrg		if (UsingRenderFont(xw)) {
3240c219fbebSmrg		    /* EMPTY */
3241c219fbebSmrg		    /* there is only one font entry to load */
3242c219fbebSmrg		    ;
3243cd3331d0Smrg		} else
3244cd3331d0Smrg#endif
3245cd3331d0Smrg		{
3246cd3331d0Smrg		    /*
3247cd3331d0Smrg		     * Normally there is no font specified in the control.
3248cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3249cd3331d0Smrg		     */
3250cd3331d0Smrg		    if (*buf == '\0') {
3251cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3252cd3331d0Smrg			    success = False;
3253cd3331d0Smrg			}
3254cd3331d0Smrg		    }
3255cd3331d0Smrg		}
3256cd3331d0Smrg	    }
3257cd3331d0Smrg	} else {
3258cd3331d0Smrg	    num = screen->menu_font_number;
3259cd3331d0Smrg	}
3260cd3331d0Smrg	name = x_strtrim(buf);
3261cd3331d0Smrg	if (success && !IsEmpty(name)) {
3262cd3331d0Smrg#if OPT_RENDERFONT
3263cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3264cd3331d0Smrg		setFaceName(xw, name);
3265cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3266cd3331d0Smrg	    } else
3267cd3331d0Smrg#endif
3268cd3331d0Smrg	    {
3269cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3270cd3331d0Smrg		fonts.f_n = name;
3271cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
3272cd3331d0Smrg	    }
3273cd3331d0Smrg	} else {
3274cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3275cd3331d0Smrg	}
3276cd3331d0Smrg	free(name);
3277cd3331d0Smrg    }
3278cd3331d0Smrg}
3279cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3280cd3331d0Smrg
3281d522f475Smrg/***====================================================================***/
3282d522f475Smrg
3283d522f475Smrgvoid
3284cd3331d0Smrgdo_osc(XtermWidget xw, Char * oscbuf, size_t len, int final)
3285d522f475Smrg{
3286cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3287d522f475Smrg    int mode;
3288d522f475Smrg    Char *cp;
3289d522f475Smrg    int state = 0;
3290d522f475Smrg    char *buf = 0;
3291cd3331d0Smrg    char temp[2];
3292cd3331d0Smrg#if OPT_ISO_COLORS
3293cd3331d0Smrg    int ansi_colors = 0;
3294cd3331d0Smrg#endif
3295cd3331d0Smrg    Bool need_data = True;
3296d522f475Smrg
3297d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3298d522f475Smrg
3299712a7ff4Smrg    (void) screen;
3300712a7ff4Smrg
3301d522f475Smrg    /*
3302d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3303d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3304d522f475Smrg     * with the same final character as the application sends to make this
3305d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3306d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3307d522f475Smrg     */
3308d522f475Smrg    mode = 0;
3309d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3310d522f475Smrg	switch (state) {
3311d522f475Smrg	case 0:
3312d522f475Smrg	    if (isdigit(*cp)) {
3313d522f475Smrg		mode = 10 * mode + (*cp - '0');
3314d522f475Smrg		if (mode > 65535) {
3315d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3316d522f475Smrg		    return;
3317d522f475Smrg		}
3318d522f475Smrg		break;
3319d522f475Smrg	    }
3320d522f475Smrg	    /* FALLTHRU */
3321d522f475Smrg	case 1:
3322d522f475Smrg	    if (*cp != ';') {
3323cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
3324cd3331d0Smrg		       (int) (cp - oscbuf)));
3325d522f475Smrg		return;
3326d522f475Smrg	    }
3327d522f475Smrg	    state = 2;
3328d522f475Smrg	    break;
3329d522f475Smrg	case 2:
3330d522f475Smrg	    buf = (char *) cp;
3331d522f475Smrg	    state = 3;
3332d522f475Smrg	    /* FALLTHRU */
3333d522f475Smrg	default:
3334cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
3335d522f475Smrg		switch (mode) {
3336d522f475Smrg		case 0:
3337d522f475Smrg		case 1:
3338d522f475Smrg		case 2:
3339d522f475Smrg		    break;
3340d522f475Smrg		default:
3341d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
3342d522f475Smrg			   CharOf(*cp),
3343cd3331d0Smrg			   (int) (cp - oscbuf)));
3344d522f475Smrg		    return;
3345d522f475Smrg		}
3346d522f475Smrg	    }
3347d522f475Smrg	}
3348d522f475Smrg    }
3349cd3331d0Smrg
33503367019cSmrg    /*
33513367019cSmrg     * Check if the palette changed and there are no more immediate changes
33523367019cSmrg     * that could be deferred to the next repaint.
33533367019cSmrg     */
33543367019cSmrg    if (xw->misc.palette_changed) {
33553367019cSmrg	switch (mode) {
33563367019cSmrg	case 3:		/* change X property */
33573367019cSmrg	case 30:		/* Konsole (unused) */
33583367019cSmrg	case 31:		/* Konsole (unused) */
33593367019cSmrg	case 50:		/* font operations */
33603367019cSmrg	case 51:		/* Emacs (unused) */
33613367019cSmrg#if OPT_PASTE64
33623367019cSmrg	case 52:		/* selection data */
33633367019cSmrg#endif
33643367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
33653367019cSmrg	    xw->misc.palette_changed = False;
33663367019cSmrg	    xtermRepaint(xw);
33673367019cSmrg	    break;
33683367019cSmrg	}
33693367019cSmrg    }
33703367019cSmrg
3371cd3331d0Smrg    /*
3372cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
3373cd3331d0Smrg     * a special case.
3374cd3331d0Smrg     */
3375cd3331d0Smrg    switch (mode) {
3376cd3331d0Smrg#if OPT_ISO_COLORS
3377cd3331d0Smrg    case OSC_Reset(4):
3378cd3331d0Smrg    case OSC_Reset(5):
3379cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3380cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3381cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3382cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3383cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3384cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3385cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3386cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3387cd3331d0Smrg#endif
3388cd3331d0Smrg#if OPT_TEK4014
3389cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3390cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3391cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3392cd3331d0Smrg#endif
3393cd3331d0Smrg	need_data = False;
3394cd3331d0Smrg	break;
3395cd3331d0Smrg#endif
3396cd3331d0Smrg    default:
3397cd3331d0Smrg	break;
3398cd3331d0Smrg    }
3399cd3331d0Smrg
3400cd3331d0Smrg    /*
3401cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
3402cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
3403cd3331d0Smrg     */
3404cd3331d0Smrg    if (IsEmpty(buf)) {
3405cd3331d0Smrg	if (need_data) {
3406cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
3407cd3331d0Smrg	    return;
3408cd3331d0Smrg	}
3409cd3331d0Smrg	temp[0] = '\0';
3410cd3331d0Smrg	buf = temp;
3411cd3331d0Smrg    } else if (!need_data) {
3412cd3331d0Smrg	TRACE(("do_osc found found unwanted data\n"));
3413d522f475Smrg	return;
34140d92cbfdSchristos    }
3415d522f475Smrg
3416d522f475Smrg    switch (mode) {
3417d522f475Smrg    case 0:			/* new icon name and title */
3418b7c89284Ssnj	ChangeIconName(xw, buf);
3419b7c89284Ssnj	ChangeTitle(xw, buf);
3420d522f475Smrg	break;
3421d522f475Smrg
3422d522f475Smrg    case 1:			/* new icon name only */
3423b7c89284Ssnj	ChangeIconName(xw, buf);
3424d522f475Smrg	break;
3425d522f475Smrg
3426d522f475Smrg    case 2:			/* new title only */
3427b7c89284Ssnj	ChangeTitle(xw, buf);
3428d522f475Smrg	break;
3429d522f475Smrg
343022d8e007Schristos#ifdef notdef
3431d522f475Smrg    case 3:			/* change X property */
3432cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
34330d92cbfdSchristos	    ChangeXprop(buf);
3434d522f475Smrg	break;
343522d8e007Schristos#endif
3436d522f475Smrg#if OPT_ISO_COLORS
3437cd3331d0Smrg    case 5:
3438cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3439cd3331d0Smrg	/* FALLTHRU */
3440d522f475Smrg    case 4:
3441cd3331d0Smrg	if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
34423367019cSmrg	    xw->misc.palette_changed = True;
3443cd3331d0Smrg	break;
3444cd3331d0Smrg    case OSC_Reset(5):
3445cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3446cd3331d0Smrg	/* FALLTHRU */
3447cd3331d0Smrg    case OSC_Reset(4):
3448cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
34493367019cSmrg	    xw->misc.palette_changed = True;
3450d522f475Smrg	break;
3451d522f475Smrg#endif
3452d522f475Smrg    case OSC_TEXT_FG:
3453d522f475Smrg    case OSC_TEXT_BG:
3454d522f475Smrg    case OSC_TEXT_CURSOR:
3455d522f475Smrg    case OSC_MOUSE_FG:
3456d522f475Smrg    case OSC_MOUSE_BG:
3457d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3458d522f475Smrg    case OSC_HIGHLIGHT_BG:
3459cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
3460d522f475Smrg#endif
3461d522f475Smrg#if OPT_TEK4014
3462d522f475Smrg    case OSC_TEK_FG:
3463d522f475Smrg    case OSC_TEK_BG:
3464d522f475Smrg    case OSC_TEK_CURSOR:
3465d522f475Smrg#endif
3466cd3331d0Smrg	if (xw->misc.dynamicColors) {
3467d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
3468cd3331d0Smrg	}
3469cd3331d0Smrg	break;
3470cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3471cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3472cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3473cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3474cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3475cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3476cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3477cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3478cd3331d0Smrg#endif
3479cd3331d0Smrg#if OPT_TEK4014
3480cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3481cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3482cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3483cd3331d0Smrg#endif
3484cd3331d0Smrg	if (xw->misc.dynamicColors) {
3485cd3331d0Smrg	    ResetColorsRequest(xw, mode);
3486cd3331d0Smrg	}
3487d522f475Smrg	break;
3488d522f475Smrg
3489d522f475Smrg    case 30:
3490d522f475Smrg    case 31:
3491d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
3492d522f475Smrg	break;
3493d522f475Smrg
3494d522f475Smrg#ifdef ALLOWLOGGING
3495d522f475Smrg    case 46:			/* new log file */
3496d522f475Smrg#ifdef ALLOWLOGFILECHANGES
3497d522f475Smrg	/*
3498d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
3499d522f475Smrg	 * arbitrary files accessible to the person running xterm.
3500d522f475Smrg	 */
3501cd3331d0Smrg	if (strcmp(buf, "?")
3502d522f475Smrg	    && (cp = CastMallocN(char, strlen(buf)) != NULL)) {
3503d522f475Smrg	    strcpy(cp, buf);
3504d522f475Smrg	    if (screen->logfile)
3505d522f475Smrg		free(screen->logfile);
3506d522f475Smrg	    screen->logfile = cp;
3507d522f475Smrg	    break;
3508d522f475Smrg	}
3509d522f475Smrg#endif
3510cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3511cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3512d522f475Smrg	break;
3513d522f475Smrg#endif /* ALLOWLOGGING */
3514d522f475Smrg
3515d522f475Smrg    case 50:
3516d522f475Smrg#if OPT_SHIFT_FONTS
3517cd3331d0Smrg	if (*buf == '?') {
3518cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
3519cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
3520cd3331d0Smrg	    ChangeFontRequest(xw, buf);
3521d522f475Smrg	}
3522d522f475Smrg#endif /* OPT_SHIFT_FONTS */
3523d522f475Smrg	break;
3524d522f475Smrg    case 51:
3525d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
3526d522f475Smrg	break;
3527d522f475Smrg
3528d522f475Smrg#if OPT_PASTE64
3529d522f475Smrg    case 52:
3530cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
3531d522f475Smrg	break;
3532d522f475Smrg#endif
3533d522f475Smrg	/*
3534d522f475Smrg	 * One could write code to send back the display and host names,
3535d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
3536d522f475Smrg	 */
3537cd3331d0Smrg    default:
3538cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
3539cd3331d0Smrg	break;
3540d522f475Smrg    }
3541d522f475Smrg    unparse_end(xw);
3542d522f475Smrg}
3543d522f475Smrg
3544d522f475Smrg#ifdef SunXK_F36
3545d522f475Smrg#define MAX_UDK 37
3546d522f475Smrg#else
3547d522f475Smrg#define MAX_UDK 35
3548d522f475Smrg#endif
3549d522f475Smrgstatic struct {
3550d522f475Smrg    char *str;
3551d522f475Smrg    int len;
3552d522f475Smrg} user_keys[MAX_UDK];
3553d522f475Smrg
3554d522f475Smrg/*
3555d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
3556d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
3557d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
3558d522f475Smrg * "real" terminals accept commas in the string definitions).
3559d522f475Smrg */
3560d522f475Smrgstatic int
3561cd3331d0Smrgudk_value(const char **cp)
3562d522f475Smrg{
3563cd3331d0Smrg    int result = -1;
3564d522f475Smrg    int c;
3565d522f475Smrg
3566d522f475Smrg    for (;;) {
3567d522f475Smrg	if ((c = **cp) != '\0')
3568d522f475Smrg	    *cp = *cp + 1;
3569d522f475Smrg	if (c == ';' || c == '\0')
3570cd3331d0Smrg	    break;
3571cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
3572cd3331d0Smrg	    break;
3573d522f475Smrg    }
3574cd3331d0Smrg
3575cd3331d0Smrg    return result;
3576d522f475Smrg}
3577d522f475Smrg
3578d522f475Smrgvoid
3579d522f475Smrgreset_decudk(void)
3580d522f475Smrg{
3581d522f475Smrg    int n;
3582d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
3583d522f475Smrg	if (user_keys[n].str != 0) {
3584d522f475Smrg	    free(user_keys[n].str);
3585d522f475Smrg	    user_keys[n].str = 0;
3586d522f475Smrg	    user_keys[n].len = 0;
3587d522f475Smrg	}
3588d522f475Smrg    }
3589d522f475Smrg}
3590d522f475Smrg
3591d522f475Smrg/*
3592d522f475Smrg * Parse the data for DECUDK (user-defined keys).
3593d522f475Smrg */
3594d522f475Smrgstatic void
3595cd3331d0Smrgparse_decudk(const char *cp)
3596d522f475Smrg{
3597d522f475Smrg    while (*cp) {
3598cd3331d0Smrg	const char *base = cp;
35993367019cSmrg	char *str = CastMallocN(char, strlen(cp) + 2);
3600d522f475Smrg	unsigned key = 0;
3601d522f475Smrg	int lo, hi;
3602d522f475Smrg	int len = 0;
3603d522f475Smrg
3604d522f475Smrg	while (isdigit(CharOf(*cp)))
36050d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
3606d522f475Smrg	if (*cp == '/') {
3607d522f475Smrg	    cp++;
3608d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
3609d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
36100d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
3611d522f475Smrg	    }
3612d522f475Smrg	}
3613d522f475Smrg	if (len > 0 && key < MAX_UDK) {
36143367019cSmrg	    str[len] = '\0';
3615d522f475Smrg	    if (user_keys[key].str != 0)
3616d522f475Smrg		free(user_keys[key].str);
3617d522f475Smrg	    user_keys[key].str = str;
3618d522f475Smrg	    user_keys[key].len = len;
3619d522f475Smrg	} else {
3620d522f475Smrg	    free(str);
3621d522f475Smrg	}
3622d522f475Smrg	if (*cp == ';')
3623d522f475Smrg	    cp++;
3624d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
3625d522f475Smrg	    break;
3626d522f475Smrg    }
3627d522f475Smrg}
3628d522f475Smrg
3629d522f475Smrg#if OPT_TRACE
3630d522f475Smrg#define SOFT_WIDE 10
3631d522f475Smrg#define SOFT_HIGH 20
3632d522f475Smrg
3633d522f475Smrgstatic void
3634cd3331d0Smrgparse_decdld(ANSI * params, const char *string)
3635d522f475Smrg{
3636d522f475Smrg    char DscsName[8];
3637d522f475Smrg    int len;
3638d522f475Smrg    int Pfn = params->a_param[0];
3639d522f475Smrg    int Pcn = params->a_param[1];
3640d522f475Smrg    int Pe = params->a_param[2];
3641d522f475Smrg    int Pcmw = params->a_param[3];
3642d522f475Smrg    int Pw = params->a_param[4];
3643d522f475Smrg    int Pt = params->a_param[5];
3644d522f475Smrg    int Pcmh = params->a_param[6];
3645d522f475Smrg    int Pcss = params->a_param[7];
3646d522f475Smrg
3647d522f475Smrg    int start_char = Pcn + 0x20;
3648d522f475Smrg    int char_wide = ((Pcmw == 0)
3649d522f475Smrg		     ? (Pcss ? 6 : 10)
3650d522f475Smrg		     : (Pcmw > 4
3651d522f475Smrg			? Pcmw
3652d522f475Smrg			: (Pcmw + 3)));
3653d522f475Smrg    int char_high = ((Pcmh == 0)
36543367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
3655d522f475Smrg			? 10
3656d522f475Smrg			: 20)
3657d522f475Smrg		     : Pcmh);
3658d522f475Smrg    Char ch;
3659d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
3660d522f475Smrg    Bool first = True;
3661d522f475Smrg    Bool prior = False;
3662d522f475Smrg    int row = 0, col = 0;
3663d522f475Smrg
3664d522f475Smrg    TRACE(("Parsing DECDLD\n"));
3665d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
3666d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
3667d522f475Smrg    TRACE(("  erase control %d\n", Pe));
3668d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
3669d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
3670d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
3671d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
3672d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
3673d522f475Smrg
3674d522f475Smrg    if (Pfn > 1
3675d522f475Smrg	|| Pcn > 95
3676d522f475Smrg	|| Pe > 2
3677d522f475Smrg	|| Pcmw > 10
3678d522f475Smrg	|| Pcmw == 1
3679d522f475Smrg	|| Pt > 2
3680d522f475Smrg	|| Pcmh > 20
3681d522f475Smrg	|| Pcss > 1
3682d522f475Smrg	|| char_wide > SOFT_WIDE
3683d522f475Smrg	|| char_high > SOFT_HIGH) {
3684d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
3685d522f475Smrg	return;
3686d522f475Smrg    }
3687d522f475Smrg
3688d522f475Smrg    len = 0;
3689d522f475Smrg    while (*string != '\0') {
3690d522f475Smrg	ch = CharOf(*string++);
3691d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
3692d522f475Smrg	    if (len < 2)
3693b7c89284Ssnj		DscsName[len++] = (char) ch;
3694d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
3695b7c89284Ssnj	    DscsName[len++] = (char) ch;
3696d522f475Smrg	    break;
3697d522f475Smrg	}
3698d522f475Smrg    }
3699d522f475Smrg    DscsName[len] = 0;
3700d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
3701d522f475Smrg
3702d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
3703d522f475Smrg    while (*string != '\0') {
3704d522f475Smrg	if (first) {
3705d522f475Smrg	    TRACE(("Char %d:\n", start_char));
3706d522f475Smrg	    if (prior) {
3707d522f475Smrg		for (row = 0; row < char_high; ++row) {
3708d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
3709d522f475Smrg		}
3710d522f475Smrg	    }
3711d522f475Smrg	    prior = False;
3712d522f475Smrg	    first = False;
3713d522f475Smrg	    for (row = 0; row < char_high; ++row) {
3714d522f475Smrg		for (col = 0; col < char_wide; ++col) {
3715d522f475Smrg		    bits[row][col] = '.';
3716d522f475Smrg		}
3717d522f475Smrg	    }
3718d522f475Smrg	    row = col = 0;
3719d522f475Smrg	}
3720d522f475Smrg	ch = CharOf(*string++);
3721d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
3722d522f475Smrg	    int n;
3723d522f475Smrg
3724b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
3725d522f475Smrg	    for (n = 0; n < 6; ++n) {
3726b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
3727d522f475Smrg	    }
3728d522f475Smrg	    col += 1;
3729d522f475Smrg	    prior = True;
3730d522f475Smrg	} else if (ch == '/') {
3731d522f475Smrg	    row += 6;
3732d522f475Smrg	    col = 0;
3733d522f475Smrg	} else if (ch == ';') {
3734d522f475Smrg	    first = True;
3735d522f475Smrg	    ++start_char;
3736d522f475Smrg	}
3737d522f475Smrg    }
3738d522f475Smrg}
3739d522f475Smrg#else
3740d522f475Smrg#define parse_decdld(p,q)	/* nothing */
3741d522f475Smrg#endif
3742d522f475Smrg
3743d522f475Smrg/*
3744d522f475Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
3745d522f475Smrg * interspersing with control characters, but have the string already.
3746d522f475Smrg */
3747d522f475Smrgstatic void
3748cd3331d0Smrgparse_ansi_params(ANSI * params, const char **string)
3749d522f475Smrg{
3750cd3331d0Smrg    const char *cp = *string;
3751b7c89284Ssnj    ParmType nparam = 0;
3752d522f475Smrg
3753d522f475Smrg    memset(params, 0, sizeof(*params));
3754d522f475Smrg    while (*cp != '\0') {
3755d522f475Smrg	Char ch = CharOf(*cp++);
3756d522f475Smrg
3757d522f475Smrg	if (isdigit(ch)) {
3758d522f475Smrg	    if (nparam < NPARAM) {
3759b7c89284Ssnj		params->a_param[nparam] =
3760b7c89284Ssnj		    (ParmType) ((params->a_param[nparam] * 10)
3761b7c89284Ssnj				+ (ch - '0'));
3762d522f475Smrg	    }
3763d522f475Smrg	} else if (ch == ';') {
3764d522f475Smrg	    if (++nparam < NPARAM)
3765d522f475Smrg		params->a_nparam = nparam;
3766d522f475Smrg	} else if (ch < 32) {
3767c219fbebSmrg	    /* EMPTY */ ;
3768d522f475Smrg	} else {
3769d522f475Smrg	    /* should be 0x30 to 0x7e */
3770d522f475Smrg	    params->a_final = ch;
3771d522f475Smrg	    break;
3772d522f475Smrg	}
3773d522f475Smrg    }
3774d522f475Smrg    *string = cp;
3775d522f475Smrg}
3776d522f475Smrg
3777d522f475Smrgvoid
3778d522f475Smrgdo_dcs(XtermWidget xw, Char * dcsbuf, size_t dcslen)
3779d522f475Smrg{
3780cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3781d522f475Smrg    char reply[BUFSIZ];
3782cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
3783d522f475Smrg    Bool okay;
3784d522f475Smrg    ANSI params;
3785d522f475Smrg
3786cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
3787d522f475Smrg
3788d522f475Smrg    if (dcslen != strlen(cp))
3789d522f475Smrg	/* shouldn't have nulls in the string */
3790d522f475Smrg	return;
3791d522f475Smrg
3792d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
3793d522f475Smrg    case '$':			/* DECRQSS */
3794d522f475Smrg	okay = True;
3795d522f475Smrg
3796d522f475Smrg	cp++;
3797d522f475Smrg	if (*cp++ == 'q') {
3798d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
3799d522f475Smrg		sprintf(reply, "%d%s",
3800d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
3801d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
3802d522f475Smrg			cp);
3803d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
38043367019cSmrg		if (screen->vtXX_level < 2) {
38053367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
38063367019cSmrg		    break;
38073367019cSmrg		}
3808d522f475Smrg		sprintf(reply, "%d%s%s",
3809d522f475Smrg			(screen->vtXX_level ?
3810d522f475Smrg			 screen->vtXX_level : 1) + 60,
3811d522f475Smrg			(screen->vtXX_level >= 2)
3812d522f475Smrg			? (screen->control_eight_bits
3813d522f475Smrg			   ? ";0" : ";1")
3814d522f475Smrg			: "",
3815d522f475Smrg			cp);
3816d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
3817d522f475Smrg		sprintf(reply, "%d;%dr",
3818d522f475Smrg			screen->top_marg + 1,
3819d522f475Smrg			screen->bot_marg + 1);
38203367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
38213367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
38223367019cSmrg		    sprintf(reply, "%d;%ds",
38233367019cSmrg			    screen->lft_marg + 1,
38243367019cSmrg			    screen->rgt_marg + 1);
38253367019cSmrg		}
3826d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
3827d522f475Smrg		strcpy(reply, "0");
3828d522f475Smrg		if (xw->flags & BOLD)
3829d522f475Smrg		    strcat(reply, ";1");
3830d522f475Smrg		if (xw->flags & UNDERLINE)
3831d522f475Smrg		    strcat(reply, ";4");
3832d522f475Smrg		if (xw->flags & BLINK)
3833d522f475Smrg		    strcat(reply, ";5");
3834d522f475Smrg		if (xw->flags & INVERSE)
3835d522f475Smrg		    strcat(reply, ";7");
3836d522f475Smrg		if (xw->flags & INVISIBLE)
3837d522f475Smrg		    strcat(reply, ";8");
3838b7c89284Ssnj#if OPT_256_COLORS || OPT_88_COLORS
3839b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
3840d522f475Smrg		    if (xw->flags & FG_COLOR) {
3841d522f475Smrg			if (xw->cur_foreground >= 16)
3842d522f475Smrg			    sprintf(reply + strlen(reply),
3843d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
3844d522f475Smrg			else
3845d522f475Smrg			    sprintf(reply + strlen(reply),
3846d522f475Smrg				    ";%d%d",
3847d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
3848d522f475Smrg				    xw->cur_foreground >= 8 ?
3849d522f475Smrg				    xw->cur_foreground - 8 :
3850d522f475Smrg				    xw->cur_foreground);
3851d522f475Smrg		    }
3852d522f475Smrg		    if (xw->flags & BG_COLOR) {
3853d522f475Smrg			if (xw->cur_background >= 16)
3854d522f475Smrg			    sprintf(reply + strlen(reply),
3855d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
3856d522f475Smrg			else
3857d522f475Smrg			    sprintf(reply + strlen(reply),
3858d522f475Smrg				    ";%d%d",
3859d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
3860d522f475Smrg				    xw->cur_background >= 8 ?
3861d522f475Smrg				    xw->cur_background - 8 :
3862d522f475Smrg				    xw->cur_background);
3863d522f475Smrg		    }
3864d522f475Smrg		});
3865b7c89284Ssnj#elif OPT_ISO_COLORS
3866b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
3867d522f475Smrg		    if (xw->flags & FG_COLOR)
3868d522f475Smrg			sprintf(reply + strlen(reply),
3869d522f475Smrg				";%d%d",
3870d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
3871d522f475Smrg				xw->cur_foreground >= 8 ?
3872d522f475Smrg				xw->cur_foreground - 8 :
3873d522f475Smrg				xw->cur_foreground);
3874d522f475Smrg		    if (xw->flags & BG_COLOR)
3875d522f475Smrg			sprintf(reply + strlen(reply),
3876d522f475Smrg				";%d%d",
3877d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
3878d522f475Smrg				xw->cur_background >= 8 ?
3879d522f475Smrg				xw->cur_background - 8 :
3880d522f475Smrg				xw->cur_background);
3881d522f475Smrg		});
3882b7c89284Ssnj#endif
3883d522f475Smrg		strcat(reply, "m");
3884712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
38853367019cSmrg		int code = STEADY_BLOCK;
38863367019cSmrg		if (isCursorUnderline(screen))
38873367019cSmrg		    code = STEADY_UNDERLINE;
38883367019cSmrg		else if (isCursorBar(screen))
38893367019cSmrg		    code = STEADY_BAR;
38903367019cSmrg#if OPT_BLINK_CURS
38913367019cSmrg		if (screen->cursor_blink_esc == 0)
38923367019cSmrg		    code -= 1;
38933367019cSmrg#endif
38943367019cSmrg		sprintf(reply, "%d%s", code, cp);
3895d522f475Smrg	    } else
3896d522f475Smrg		okay = False;
3897d522f475Smrg
389822d8e007Schristos	    if (okay) {
38990d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
39003367019cSmrg		unparseputc(xw, '1');
39010d92cbfdSchristos		unparseputc(xw, '$');
39020d92cbfdSchristos		unparseputc(xw, 'r');
3903d522f475Smrg		cp = reply;
390422d8e007Schristos		unparseputs(xw, cp);
39050d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
39060d92cbfdSchristos	    } else {
39070d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
390822d8e007Schristos	    }
3909d522f475Smrg	} else {
3910d522f475Smrg	    unparseputc(xw, ANSI_CAN);
3911d522f475Smrg	}
3912d522f475Smrg	break;
3913d522f475Smrg#if OPT_TCAP_QUERY
3914d522f475Smrg    case '+':
3915d522f475Smrg	cp++;
3916cd3331d0Smrg	switch (*cp) {
3917cd3331d0Smrg	case 'p':
3918cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
3919cd3331d0Smrg		set_termcap(xw, cp + 1);
3920cd3331d0Smrg	    }
3921cd3331d0Smrg	    break;
3922cd3331d0Smrg	case 'q':
3923cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
3924cd3331d0Smrg		Bool fkey;
3925cd3331d0Smrg		unsigned state;
3926cd3331d0Smrg		int code;
3927cd3331d0Smrg		const char *tmp;
3928cd3331d0Smrg		const char *parsed = ++cp;
3929d522f475Smrg
3930cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
3931d522f475Smrg
3932cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
3933b7c89284Ssnj
3934cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
3935d522f475Smrg
3936cd3331d0Smrg		unparseputc(xw, '+');
3937cd3331d0Smrg		unparseputc(xw, 'r');
3938d522f475Smrg
3939cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
3940cd3331d0Smrg		    if (cp == parsed)
3941cd3331d0Smrg			break;	/* no data found, error */
3942d522f475Smrg
3943cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
3944cd3331d0Smrg			unparseputc(xw, *tmp);
3945d522f475Smrg
3946cd3331d0Smrg		    if (code >= 0) {
3947cd3331d0Smrg			unparseputc(xw, '=');
3948cd3331d0Smrg			screen->tc_query_code = code;
3949cd3331d0Smrg			screen->tc_query_fkey = fkey;
3950d522f475Smrg#if OPT_ISO_COLORS
3951cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
3952cd3331d0Smrg			 * number of colors) */
3953cd3331d0Smrg			if (code == XK_COLORS) {
3954cd3331d0Smrg			    unparseputn(xw, NUM_ANSI_COLORS);
3955cd3331d0Smrg			} else
3956cd3331d0Smrg#endif
3957cd3331d0Smrg			if (code == XK_TCAPNAME) {
3958c219fbebSmrg			    unparseputs(xw, resource.term_name);
3959cd3331d0Smrg			} else {
3960cd3331d0Smrg			    XKeyEvent event;
3961cd3331d0Smrg			    event.state = state;
3962cd3331d0Smrg			    Input(xw, &event, False);
3963cd3331d0Smrg			}
3964cd3331d0Smrg			screen->tc_query_code = -1;
3965cd3331d0Smrg		    } else {
3966cd3331d0Smrg			break;	/* no match found, error */
3967d522f475Smrg		    }
3968d522f475Smrg
3969d522f475Smrg		    cp = parsed;
3970cd3331d0Smrg		    if (*parsed == ';') {
3971cd3331d0Smrg			unparseputc(xw, *parsed++);
3972cd3331d0Smrg			cp = parsed;
3973cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
3974cd3331d0Smrg		    }
3975d522f475Smrg		}
3976cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
3977d522f475Smrg	    }
3978cd3331d0Smrg	    break;
3979d522f475Smrg	}
3980d522f475Smrg	break;
3981d522f475Smrg#endif
3982d522f475Smrg    default:
39833367019cSmrg	if (screen->vtXX_level >= 2) {	/* VT220 */
39840d92cbfdSchristos	    parse_ansi_params(&params, &cp);
39850d92cbfdSchristos	    switch (params.a_final) {
39860d92cbfdSchristos	    case '|':		/* DECUDK */
39870d92cbfdSchristos		if (params.a_param[0] == 0)
39880d92cbfdSchristos		    reset_decudk();
39890d92cbfdSchristos		parse_decudk(cp);
39900d92cbfdSchristos		break;
39910d92cbfdSchristos	    case '{':		/* DECDLD (no '}' case though) */
39920d92cbfdSchristos		parse_decdld(&params, cp);
39930d92cbfdSchristos		break;
39940d92cbfdSchristos	    }
3995d522f475Smrg	}
3996d522f475Smrg	break;
3997d522f475Smrg    }
3998d522f475Smrg    unparse_end(xw);
3999d522f475Smrg}
4000d522f475Smrg
4001cb4a1343Smrg#if OPT_DEC_RECTOPS
4002cb4a1343Smrgenum {
4003cb4a1343Smrg    mdUnknown = 0,
4004cb4a1343Smrg    mdMaybeSet = 1,
4005cb4a1343Smrg    mdMaybeReset = 2,
4006cb4a1343Smrg    mdAlwaysSet = 3,
4007cb4a1343Smrg    mdAlwaysReset = 4
4008cb4a1343Smrg};
4009cb4a1343Smrg
4010cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
40113367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
4012cb4a1343Smrg
4013cb4a1343Smrg/*
4014cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
4015cb4a1343Smrg * 0 - not recognized
4016cb4a1343Smrg * 1 - set
4017cb4a1343Smrg * 2 - reset
4018cb4a1343Smrg * 3 - permanently set
4019cb4a1343Smrg * 4 - permanently reset
4020cb4a1343Smrg * Only one mode can be reported at a time.
4021cb4a1343Smrg */
4022cb4a1343Smrgvoid
4023cb4a1343Smrgdo_rpm(XtermWidget xw, int nparams, int *params)
4024cb4a1343Smrg{
4025cb4a1343Smrg    ANSI reply;
4026cb4a1343Smrg    int result = 0;
4027cb4a1343Smrg    int count = 0;
4028cb4a1343Smrg
4029cb4a1343Smrg    TRACE(("do_rpm %d:%d\n", nparams, params[0]));
4030cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4031cb4a1343Smrg    if (nparams >= 1) {
4032cb4a1343Smrg	switch (params[0]) {
4033cb4a1343Smrg	case 1:		/* GATM */
4034cb4a1343Smrg	    result = mdAlwaysReset;
4035cb4a1343Smrg	    break;
4036cb4a1343Smrg	case 2:
4037cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
4038cb4a1343Smrg	    break;
4039cb4a1343Smrg	case 3:		/* CRM */
4040cb4a1343Smrg	    result = mdMaybeReset;
4041cb4a1343Smrg	    break;
4042cb4a1343Smrg	case 4:
4043cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
4044cb4a1343Smrg	    break;
4045cb4a1343Smrg	case 5:		/* SRTM */
4046cb4a1343Smrg	case 7:		/* VEM */
4047cb4a1343Smrg	case 10:		/* HEM */
4048cb4a1343Smrg	case 11:		/* PUM */
4049cb4a1343Smrg	    result = mdAlwaysReset;
4050cb4a1343Smrg	    break;
4051cb4a1343Smrg	case 12:
4052cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
4053cb4a1343Smrg	    break;
4054cb4a1343Smrg	case 13:		/* FEAM */
4055cb4a1343Smrg	case 14:		/* FETM */
4056cb4a1343Smrg	case 15:		/* MATM */
4057cb4a1343Smrg	case 16:		/* TTM */
4058cb4a1343Smrg	case 17:		/* SATM */
4059cb4a1343Smrg	case 18:		/* TSM */
4060cb4a1343Smrg	case 19:		/* EBM */
4061cb4a1343Smrg	    result = mdAlwaysReset;
4062cb4a1343Smrg	    break;
4063cb4a1343Smrg	case 20:
4064cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
4065cb4a1343Smrg	    break;
4066cb4a1343Smrg	}
4067cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4068cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4069cb4a1343Smrg    }
4070cb4a1343Smrg    reply.a_type = ANSI_CSI;
4071cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4072cb4a1343Smrg    reply.a_inters = '$';
4073cb4a1343Smrg    reply.a_final = 'y';
4074cb4a1343Smrg    unparseseq(xw, &reply);
4075cb4a1343Smrg}
4076cb4a1343Smrg
4077cb4a1343Smrgvoid
4078cb4a1343Smrgdo_decrpm(XtermWidget xw, int nparams, int *params)
4079cb4a1343Smrg{
4080cb4a1343Smrg    ANSI reply;
4081cb4a1343Smrg    int result = 0;
4082cb4a1343Smrg    int count = 0;
4083cb4a1343Smrg
4084cb4a1343Smrg    TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
4085cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4086cb4a1343Smrg    if (nparams >= 1) {
4087cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
4088cb4a1343Smrg
4089cb4a1343Smrg	switch (params[0]) {
4090cb4a1343Smrg	case 1:		/* DECCKM                       */
4091cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
4092cb4a1343Smrg	    break;
4093cb4a1343Smrg	case 2:		/* DECANM - ANSI/VT52 mode      */
4094cb4a1343Smrg#if OPT_VT52_MODE
40953367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
4096cb4a1343Smrg#else
4097cb4a1343Smrg	    result = mdMaybeSet;
4098cb4a1343Smrg#endif
4099cb4a1343Smrg	    break;
4100cb4a1343Smrg	case 3:		/* DECCOLM                      */
4101cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
4102cb4a1343Smrg	    break;
4103cb4a1343Smrg	case 4:		/* DECSCLM (slow scroll)        */
4104cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
4105cb4a1343Smrg	    break;
4106cb4a1343Smrg	case 5:		/* DECSCNM                      */
4107cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
4108cb4a1343Smrg	    break;
4109cb4a1343Smrg	case 6:		/* DECOM                        */
4110cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
4111cb4a1343Smrg	    break;
4112cb4a1343Smrg	case 7:		/* DECAWM                       */
4113cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
4114cb4a1343Smrg	    break;
4115cb4a1343Smrg	case 8:		/* DECARM                       */
4116cb4a1343Smrg	    result = mdAlwaysReset;
4117cb4a1343Smrg	    break;
4118cb4a1343Smrg	case SET_X10_MOUSE:	/* X10 mouse                    */
4119cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
4120cb4a1343Smrg	    break;
4121cb4a1343Smrg#if OPT_TOOLBAR
4122cb4a1343Smrg	case 10:		/* rxvt */
4123cb4a1343Smrg	    result = MdBool(resource.toolBar);
4124cb4a1343Smrg	    break;
4125cb4a1343Smrg#endif
4126cb4a1343Smrg#if OPT_BLINK_CURS
4127cb4a1343Smrg	case 12:		/* att610: Start/stop blinking cursor */
4128cb4a1343Smrg	    result = MdBool(screen->cursor_blink_res);
4129cb4a1343Smrg	    break;
4130cb4a1343Smrg#endif
4131cb4a1343Smrg	case 18:		/* DECPFF: print form feed */
4132712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
4133cb4a1343Smrg	    break;
4134cb4a1343Smrg	case 19:		/* DECPEX: print extent */
4135712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
4136cb4a1343Smrg	    break;
4137cb4a1343Smrg	case 25:		/* DECTCEM: Show/hide cursor (VT200) */
4138cb4a1343Smrg	    result = MdBool(screen->cursor_set);
4139cb4a1343Smrg	    break;
4140cb4a1343Smrg	case 30:		/* rxvt */
4141cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
4142cb4a1343Smrg	    break;
4143cb4a1343Smrg#if OPT_SHIFT_FONTS
4144cb4a1343Smrg	case 35:		/* rxvt */
4145cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
4146cb4a1343Smrg	    break;
4147cb4a1343Smrg#endif
4148cb4a1343Smrg#if OPT_TEK4014
4149cb4a1343Smrg	case 38:		/* DECTEK                       */
4150cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
4151cb4a1343Smrg	    break;
4152cb4a1343Smrg#endif
4153cb4a1343Smrg	case 40:		/* 132 column mode              */
4154cb4a1343Smrg	    result = MdBool(screen->c132);
4155cb4a1343Smrg	    break;
4156cb4a1343Smrg	case 41:		/* curses hack                  */
4157cb4a1343Smrg	    result = MdBool(screen->curses);
4158cb4a1343Smrg	    break;
4159cb4a1343Smrg	case 42:		/* DECNRCM national charset (VT220) */
4160cb4a1343Smrg	    result = MdFlag(xw->flags, NATIONAL);
4161cb4a1343Smrg	    break;
4162cb4a1343Smrg	case 44:		/* margin bell                  */
4163cb4a1343Smrg	    result = MdBool(screen->marginbell);
4164cb4a1343Smrg	    break;
4165cb4a1343Smrg	case 45:		/* reverse wraparound   */
4166cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSEWRAP);
4167cb4a1343Smrg	    break;
4168cb4a1343Smrg#ifdef ALLOWLOGGING
4169cb4a1343Smrg	case 46:		/* logging              */
4170cb4a1343Smrg#ifdef ALLOWLOGFILEONOFF
4171cb4a1343Smrg	    result = MdBool(screen->logging);
4172cb4a1343Smrg#endif /* ALLOWLOGFILEONOFF */
4173cb4a1343Smrg	    break;
4174cb4a1343Smrg#endif
4175cb4a1343Smrg	case 1049:		/* alternate buffer & cursor */
4176cb4a1343Smrg	    /* FALLTHRU */
4177cb4a1343Smrg	case 1047:
4178cb4a1343Smrg	    /* FALLTHRU */
4179cb4a1343Smrg	case 47:		/* alternate buffer */
4180cb4a1343Smrg	    result = MdBool(screen->whichBuf);
4181cb4a1343Smrg	    break;
4182cb4a1343Smrg	case 66:		/* DECNKM */
4183cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
4184cb4a1343Smrg	    break;
4185cb4a1343Smrg	case 67:		/* DECBKM */
4186cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
4187cb4a1343Smrg	    break;
41883367019cSmrg	case 69:		/* DECLRMM */
41893367019cSmrg	    result = MdFlag(xw->flags, LEFT_RIGHT);
41903367019cSmrg	    break;
41913367019cSmrg	case 95:		/* DECNCSM */
41923367019cSmrg	    result = MdFlag(xw->flags, NOCLEAR_COLM);
41933367019cSmrg	    break;
4194cb4a1343Smrg	case SET_VT200_MOUSE:	/* xterm bogus sequence         */
4195cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
4196cb4a1343Smrg	    break;
4197cb4a1343Smrg	case SET_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
4198cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
4199cb4a1343Smrg	    break;
4200cb4a1343Smrg	case SET_BTN_EVENT_MOUSE:
4201cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
4202cb4a1343Smrg	    break;
4203cb4a1343Smrg	case SET_ANY_EVENT_MOUSE:
4204cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
4205cb4a1343Smrg	    break;
4206cb4a1343Smrg#if OPT_FOCUS_EVENT
4207cb4a1343Smrg	case SET_FOCUS_EVENT_MOUSE:
4208cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
4209cb4a1343Smrg	    break;
4210cb4a1343Smrg#endif
4211cb4a1343Smrg	case SET_EXT_MODE_MOUSE:
42123367019cSmrg	    /* FALLTHRU */
42133367019cSmrg	case SET_SGR_EXT_MODE_MOUSE:
42143367019cSmrg	    /* FALLTHRU */
42153367019cSmrg	case SET_URXVT_EXT_MODE_MOUSE:
42163367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
42173367019cSmrg	    break;
42183367019cSmrg	case SET_ALTERNATE_SCROLL:
42193367019cSmrg	    result = MdBool(screen->alternateScroll);
4220cb4a1343Smrg	    break;
4221cb4a1343Smrg	case 1010:		/* rxvt */
4222cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
4223cb4a1343Smrg	    break;
4224cb4a1343Smrg	case 1011:		/* rxvt */
4225cb4a1343Smrg	    result = MdBool(screen->scrollkey);
4226cb4a1343Smrg	    break;
4227cb4a1343Smrg	case 1034:
42283367019cSmrg	    result = MdBool(screen->eight_bit_meta);
4229cb4a1343Smrg	    break;
4230cb4a1343Smrg#if OPT_NUM_LOCK
4231cb4a1343Smrg	case 1035:
4232cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
4233cb4a1343Smrg	    break;
4234cb4a1343Smrg	case 1036:
4235cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
4236cb4a1343Smrg	    break;
4237cb4a1343Smrg#endif
4238cb4a1343Smrg	case 1037:
4239cb4a1343Smrg	    result = MdBool(screen->delete_is_del);
4240cb4a1343Smrg	    break;
4241cb4a1343Smrg#if OPT_NUM_LOCK
4242cb4a1343Smrg	case 1039:
4243cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
4244cb4a1343Smrg	    break;
4245cb4a1343Smrg#endif
4246cb4a1343Smrg	case 1040:
4247cb4a1343Smrg	    result = MdBool(screen->keepSelection);
4248cb4a1343Smrg	    break;
4249cb4a1343Smrg	case 1041:
4250cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
4251cb4a1343Smrg	    break;
4252cb4a1343Smrg	case 1042:
4253cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
4254cb4a1343Smrg	    break;
4255cb4a1343Smrg	case 1043:
4256cb4a1343Smrg	    result = MdBool(screen->poponbell);
4257cb4a1343Smrg	    break;
4258cb4a1343Smrg	case 1048:
4259cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
4260cb4a1343Smrg	    break;
4261cb4a1343Smrg#if OPT_TCAP_FKEYS
4262cb4a1343Smrg	case 1050:
4263cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
4264cb4a1343Smrg	    break;
4265cb4a1343Smrg#endif
4266cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
4267cb4a1343Smrg	case 1051:
4268cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
4269cb4a1343Smrg	    break;
4270cb4a1343Smrg#endif
4271cb4a1343Smrg#if OPT_HP_FUNC_KEYS
4272cb4a1343Smrg	case 1052:
4273cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
4274cb4a1343Smrg	    break;
4275cb4a1343Smrg#endif
4276cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
4277cb4a1343Smrg	case 1053:
4278cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
4279cb4a1343Smrg	    break;
4280cb4a1343Smrg#endif
4281cb4a1343Smrg	case 1060:
4282cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
4283cb4a1343Smrg	    break;
4284cb4a1343Smrg#if OPT_SUNPC_KBD
4285cb4a1343Smrg	case 1061:
4286cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
4287cb4a1343Smrg	    break;
4288cb4a1343Smrg#endif
4289cb4a1343Smrg#if OPT_READLINE
4290cb4a1343Smrg	case SET_BUTTON1_MOVE_POINT:
4291cb4a1343Smrg	    result = MdBool(screen->click1_moves);
4292cb4a1343Smrg	    break;
4293cb4a1343Smrg	case SET_BUTTON2_MOVE_POINT:
4294cb4a1343Smrg	    result = MdBool(screen->paste_moves);
4295cb4a1343Smrg	    break;
4296cb4a1343Smrg	case SET_DBUTTON3_DELETE:
4297cb4a1343Smrg	    result = MdBool(screen->dclick3_deletes);
4298cb4a1343Smrg	    break;
4299cb4a1343Smrg	case SET_PASTE_IN_BRACKET:
4300cb4a1343Smrg	    result = MdBool(screen->paste_brackets);
4301cb4a1343Smrg	    break;
4302cb4a1343Smrg	case SET_PASTE_QUOTE:
4303cb4a1343Smrg	    result = MdBool(screen->paste_quotes);
4304cb4a1343Smrg	    break;
4305cb4a1343Smrg	case SET_PASTE_LITERAL_NL:
4306cb4a1343Smrg	    result = MdBool(screen->paste_literal_nl);
4307cb4a1343Smrg	    break;
4308cb4a1343Smrg#endif /* OPT_READLINE */
4309cb4a1343Smrg	}
4310cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4311cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4312cb4a1343Smrg    }
4313cb4a1343Smrg    reply.a_type = ANSI_CSI;
4314cb4a1343Smrg    reply.a_pintro = '?';
4315cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4316cb4a1343Smrg    reply.a_inters = '$';
4317cb4a1343Smrg    reply.a_final = 'y';
4318cb4a1343Smrg    unparseseq(xw, &reply);
4319cb4a1343Smrg}
4320cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
4321cb4a1343Smrg
4322d522f475Smrgchar *
4323d522f475Smrgudk_lookup(int keycode, int *len)
4324d522f475Smrg{
4325d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
4326d522f475Smrg	*len = user_keys[keycode].len;
4327d522f475Smrg	return user_keys[keycode].str;
4328d522f475Smrg    }
4329d522f475Smrg    return 0;
4330d522f475Smrg}
4331d522f475Smrg
43323367019cSmrg#ifdef HAVE_LIBXPM
43333367019cSmrg
43343367019cSmrg#ifndef PIXMAP_ROOTDIR
43353367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
43363367019cSmrg#endif
43373367019cSmrg
43383367019cSmrgtypedef struct {
43393367019cSmrg    const char *name;
43403367019cSmrg    const char *const *data;
43413367019cSmrg} XPM_DATA;
43423367019cSmrg
43433367019cSmrgstatic char *
43443367019cSmrgx_find_icon(char **work, int *state, const char *suffix)
43453367019cSmrg{
43463367019cSmrg    const char *filename = resource.icon_hint;
43473367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
43483367019cSmrg    const char *larger = "_48x48";
43493367019cSmrg    char *result = 0;
43503367019cSmrg    size_t length;
43513367019cSmrg
43523367019cSmrg    if (*state >= 0) {
43533367019cSmrg	if ((*state & 1) == 0)
43543367019cSmrg	    suffix = "";
43553367019cSmrg	if ((*state & 2) == 0)
43563367019cSmrg	    larger = "";
43573367019cSmrg	if ((*state & 4) == 0) {
43583367019cSmrg	    prefix = "";
43593367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
43603367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
43613367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
43623367019cSmrg	    *state = -1;
43633367019cSmrg	} else if (*state >= 8) {
43643367019cSmrg	    *state = -1;
43653367019cSmrg	}
43663367019cSmrg    }
43673367019cSmrg
43683367019cSmrg    if (*state >= 0) {
43693367019cSmrg	if (*work) {
43703367019cSmrg	    free(*work);
43713367019cSmrg	    *work = 0;
43723367019cSmrg	}
43733367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
43743367019cSmrg	    strlen(suffix);
43753367019cSmrg	if ((result = malloc(length)) != 0) {
43763367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
43773367019cSmrg	    *work = result;
43783367019cSmrg	}
43793367019cSmrg	*state += 1;
43803367019cSmrg	TRACE(("x_find_icon %d:%s\n", *state, result));
43813367019cSmrg    }
43823367019cSmrg    return result;
43833367019cSmrg}
43843367019cSmrg
43853367019cSmrg#if OPT_BUILTIN_XPMS
43863367019cSmrgstatic const XPM_DATA *
43873367019cSmrgBuiltInXPM(const XPM_DATA * table, Cardinal length)
43883367019cSmrg{
43893367019cSmrg    const char *find = resource.icon_hint;
43903367019cSmrg    const XPM_DATA *result = 0;
43913367019cSmrg    if (!IsEmpty(find)) {
43923367019cSmrg	Cardinal n;
43933367019cSmrg	for (n = 0; n < length; ++n) {
43943367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
43953367019cSmrg		result = table + n;
43963367019cSmrg		break;
43973367019cSmrg	    }
43983367019cSmrg	}
43993367019cSmrg
44003367019cSmrg	/*
44013367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
44023367019cSmrg	 * which are all _HHxWW format.
44033367019cSmrg	 */
44043367019cSmrg	if (result == 0) {
44053367019cSmrg	    const char *base = table[0].name;
44063367019cSmrg	    const char *last = strchr(base, '_');
44073367019cSmrg	    if (last != 0
44083367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
44093367019cSmrg		result = table + length - 1;
44103367019cSmrg	    }
44113367019cSmrg	}
44123367019cSmrg    }
44133367019cSmrg    return result;
44143367019cSmrg}
44153367019cSmrg#endif /* OPT_BUILTIN_XPMS */
44163367019cSmrg
44173367019cSmrgtypedef enum {
44183367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
44193367019cSmrg    ,eHintNone
44203367019cSmrg    ,eHintSearch
44213367019cSmrg} ICON_HINT;
44223367019cSmrg
44233367019cSmrgstatic ICON_HINT
44243367019cSmrgwhich_icon_hint(void)
44253367019cSmrg{
44263367019cSmrg    ICON_HINT result = eHintDefault;
44273367019cSmrg    if (!IsEmpty(resource.icon_hint)) {
44283367019cSmrg	if (!x_strcasecmp(resource.icon_hint, "none")) {
44293367019cSmrg	    result = eHintNone;
44303367019cSmrg	} else {
44313367019cSmrg	    result = eHintSearch;
44323367019cSmrg	}
44333367019cSmrg    }
44343367019cSmrg    return result;
44353367019cSmrg}
44363367019cSmrg#endif /* HAVE_LIBXPM */
44373367019cSmrg
44383367019cSmrgint
44393367019cSmrggetVisualDepth(XtermWidget xw)
44403367019cSmrg{
44413367019cSmrg    Display *display = TScreenOf(xw)->display;
44423367019cSmrg    XVisualInfo myTemplate, *visInfoPtr;
44433367019cSmrg    int numFound;
44443367019cSmrg    int result = 0;
44453367019cSmrg
44463367019cSmrg    myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(display,
44473367019cSmrg							    XDefaultScreen(display)));
44483367019cSmrg    visInfoPtr = XGetVisualInfo(display, (long) VisualIDMask,
44493367019cSmrg				&myTemplate, &numFound);
44503367019cSmrg    if (visInfoPtr != 0) {
44513367019cSmrg	if (numFound != 0) {
44523367019cSmrg	    result = visInfoPtr->depth;
44533367019cSmrg	}
44543367019cSmrg	XFree(visInfoPtr);
44553367019cSmrg    }
44563367019cSmrg    return result;
44573367019cSmrg}
44583367019cSmrg
44593367019cSmrg/*
44603367019cSmrg * WM_ICON_SIZE should be honored if possible.
44613367019cSmrg */
44623367019cSmrgvoid
44633367019cSmrgxtermLoadIcon(XtermWidget xw)
44643367019cSmrg{
44653367019cSmrg#ifdef HAVE_LIBXPM
44663367019cSmrg    Display *dpy = XtDisplay(xw);
44673367019cSmrg    Pixmap myIcon = 0;
44683367019cSmrg    Pixmap myMask = 0;
44693367019cSmrg    char *workname = 0;
44703367019cSmrg    ICON_HINT hint = which_icon_hint();
44713367019cSmrg#if OPT_BUILTIN_XPMS
44723367019cSmrg#include <icons/mini.xterm.xpms>
44733367019cSmrg#include <icons/filled-xterm.xpms>
44743367019cSmrg#include <icons/xterm.xpms>
44753367019cSmrg#include <icons/xterm-color.xpms>
44763367019cSmrg#else
44773367019cSmrg#include <icons/mini.xterm_48x48.xpm>
44783367019cSmrg#endif
44793367019cSmrg
44803367019cSmrg    TRACE(("xtermLoadIcon %p:%s\n", (void *) xw, NonNull(resource.icon_hint)));
44813367019cSmrg
44823367019cSmrg    if (hint == eHintSearch) {
44833367019cSmrg	int state = 0;
44843367019cSmrg	while (x_find_icon(&workname, &state, ".xpm") != 0) {
44853367019cSmrg	    Pixmap resIcon = 0;
44863367019cSmrg	    Pixmap shapemask = 0;
44873367019cSmrg	    XpmAttributes attributes;
44883367019cSmrg
44893367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
44903367019cSmrg	    attributes.valuemask = XpmDepth;
44913367019cSmrg
44923367019cSmrg	    if (XpmReadFileToPixmap(dpy,
44933367019cSmrg				    DefaultRootWindow(dpy),
44943367019cSmrg				    workname,
44953367019cSmrg				    &resIcon,
44963367019cSmrg				    &shapemask,
44973367019cSmrg				    &attributes) == XpmSuccess) {
44983367019cSmrg		myIcon = resIcon;
44993367019cSmrg		myMask = shapemask;
45003367019cSmrg		TRACE(("...success\n"));
45013367019cSmrg		break;
45023367019cSmrg	    }
45033367019cSmrg	}
45043367019cSmrg    }
45053367019cSmrg
45063367019cSmrg    /*
45073367019cSmrg     * If no external file was found, look for the name in the built-in table.
45083367019cSmrg     * If that fails, just use the biggest mini-icon.
45093367019cSmrg     */
45103367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
45113367019cSmrg	char **data;
45123367019cSmrg#if OPT_BUILTIN_XPMS
45133367019cSmrg	const XPM_DATA *myData = 0;
45143367019cSmrg	myData = BuiltInXPM(mini_xterm_xpms, XtNumber(mini_xterm_xpms));
45153367019cSmrg	if (myData == 0)
45163367019cSmrg	    myData = BuiltInXPM(filled_xterm_xpms, XtNumber(filled_xterm_xpms));
45173367019cSmrg	if (myData == 0)
45183367019cSmrg	    myData = BuiltInXPM(xterm_color_xpms, XtNumber(xterm_color_xpms));
45193367019cSmrg	if (myData == 0)
45203367019cSmrg	    myData = BuiltInXPM(xterm_xpms, XtNumber(xterm_xpms));
45213367019cSmrg	if (myData == 0)
45223367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
45233367019cSmrg	data = (char **) myData->data,
45243367019cSmrg#else
45253367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
45263367019cSmrg#endif
45273367019cSmrg	if (XpmCreatePixmapFromData(dpy,
45283367019cSmrg				    DefaultRootWindow(dpy),
45293367019cSmrg				    data,
45303367019cSmrg				    &myIcon, &myMask, 0) != 0) {
45313367019cSmrg	    myIcon = 0;
45323367019cSmrg	    myMask = 0;
45333367019cSmrg	}
45343367019cSmrg    }
45353367019cSmrg
45363367019cSmrg    if (myIcon != 0) {
45373367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
45383367019cSmrg	if (!hints)
45393367019cSmrg	    hints = XAllocWMHints();
45403367019cSmrg
45413367019cSmrg	if (hints) {
45423367019cSmrg	    hints->flags |= IconPixmapHint;
45433367019cSmrg	    hints->icon_pixmap = myIcon;
45443367019cSmrg	    if (myMask) {
45453367019cSmrg		hints->flags |= IconMaskHint;
45463367019cSmrg		hints->icon_mask = myMask;
45473367019cSmrg	    }
45483367019cSmrg
45493367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
45503367019cSmrg	    XFree(hints);
45513367019cSmrg	    TRACE(("...loaded icon\n"));
45523367019cSmrg	}
45533367019cSmrg    }
45543367019cSmrg
45553367019cSmrg    if (workname != 0)
45563367019cSmrg	free(workname);
45573367019cSmrg
45583367019cSmrg#else
45593367019cSmrg    (void) xw;
45603367019cSmrg#endif
45613367019cSmrg}
45623367019cSmrg
45633367019cSmrgvoid
4564cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
4565d522f475Smrg{
4566d522f475Smrg#if OPT_WIDE_CHARS
4567d522f475Smrg    static Char *converted;	/* NO_LEAKS */
4568d522f475Smrg#endif
4569d522f475Smrg
4570d522f475Smrg    Arg args[1];
4571cd3331d0Smrg    Boolean changed = True;
4572d522f475Smrg    Widget w = CURRENT_EMU();
4573d522f475Smrg    Widget top = SHELL_OF(w);
4574d522f475Smrg
4575cd3331d0Smrg    char *my_attr;
4576cd3331d0Smrg    char *name;
4577cd3331d0Smrg    size_t limit;
4578cd3331d0Smrg    Char *c1;
4579cd3331d0Smrg    Char *cp;
4580d522f475Smrg
4581b7c89284Ssnj    if (!AllowTitleOps(xw))
4582d522f475Smrg	return;
4583d522f475Smrg
4584cd3331d0Smrg    if (value == 0)
45853367019cSmrg	value = emptyString;
4586cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
4587cd3331d0Smrg	const char *temp;
4588cd3331d0Smrg	char *test;
4589cd3331d0Smrg
4590cd3331d0Smrg	value = x_decode_hex(value, &temp);
45913367019cSmrg	if (*temp != '\0') {
45923367019cSmrg	    free(value);
4593cd3331d0Smrg	    return;
45943367019cSmrg	}
4595cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
4596cd3331d0Smrg	    if (CharOf(*test) < 32) {
4597cd3331d0Smrg		*test = '\0';
4598cd3331d0Smrg		break;
4599cd3331d0Smrg	    }
4600cd3331d0Smrg	}
4601cd3331d0Smrg    }
4602cd3331d0Smrg
4603cd3331d0Smrg    c1 = (Char *) value;
4604cd3331d0Smrg    name = value;
4605cd3331d0Smrg    limit = strlen(name);
4606cd3331d0Smrg    my_attr = x_strdup(attribute);
4607cd3331d0Smrg
4608cd3331d0Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
4609cd3331d0Smrg
4610d522f475Smrg    /*
4611d522f475Smrg     * Ignore titles that are too long to be plausible requests.
4612d522f475Smrg     */
4613cd3331d0Smrg    if (limit > 0 && limit < 1024) {
4614d522f475Smrg
4615cd3331d0Smrg	/*
4616cd3331d0Smrg	 * After all decoding, overwrite nonprintable characters with '?'.
4617cd3331d0Smrg	 */
4618cd3331d0Smrg	for (cp = c1; *cp != 0; ++cp) {
4619cd3331d0Smrg	    Char *c2 = cp;
4620cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
4621cd3331d0Smrg		memset(c2, '?', (size_t) (cp + 1 - c2));
4622cd3331d0Smrg	    }
4623d522f475Smrg	}
4624d522f475Smrg
4625d522f475Smrg#if OPT_WIDE_CHARS
4626cd3331d0Smrg	/*
4627cd3331d0Smrg	 * If we're running in UTF-8 mode, and have not been told that the
4628cd3331d0Smrg	 * title string is in UTF-8, it is likely that non-ASCII text in the
4629cd3331d0Smrg	 * string will be rejected because it is not printable in the current
4630cd3331d0Smrg	 * locale.  So we convert it to UTF-8, allowing the X library to
4631cd3331d0Smrg	 * convert it back.
4632cd3331d0Smrg	 */
4633cd3331d0Smrg	if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) {
4634cd3331d0Smrg	    int n;
4635cd3331d0Smrg
4636cd3331d0Smrg	    for (n = 0; name[n] != '\0'; ++n) {
4637cd3331d0Smrg		if (CharOf(name[n]) > 127) {
4638cd3331d0Smrg		    if (converted != 0)
4639cd3331d0Smrg			free(converted);
4640cd3331d0Smrg		    if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
4641cd3331d0Smrg			Char *temp = converted;
4642cd3331d0Smrg			while (*name != 0) {
4643cd3331d0Smrg			    temp = convertToUTF8(temp, CharOf(*name));
4644cd3331d0Smrg			    ++name;
4645cd3331d0Smrg			}
4646cd3331d0Smrg			*temp = 0;
4647cd3331d0Smrg			name = (char *) converted;
4648cd3331d0Smrg			TRACE(("...converted{%s}\n", name));
4649d522f475Smrg		    }
4650cd3331d0Smrg		    break;
4651d522f475Smrg		}
4652d522f475Smrg	    }
4653d522f475Smrg	}
4654d522f475Smrg#endif
4655d522f475Smrg
4656d522f475Smrg#if OPT_SAME_NAME
4657cd3331d0Smrg	/* If the attribute isn't going to change, then don't bother... */
4658cd3331d0Smrg
4659cd3331d0Smrg	if (resource.sameName) {
4660cd3331d0Smrg	    char *buf = 0;
4661cd3331d0Smrg	    XtSetArg(args[0], my_attr, &buf);
4662cd3331d0Smrg	    XtGetValues(top, args, 1);
4663cd3331d0Smrg	    TRACE(("...comparing{%s}\n", buf));
4664cd3331d0Smrg	    if (buf != 0 && strcmp(name, buf) == 0)
4665cd3331d0Smrg		changed = False;
4666cd3331d0Smrg	}
4667d522f475Smrg#endif /* OPT_SAME_NAME */
4668d522f475Smrg
4669cd3331d0Smrg	if (changed) {
4670cd3331d0Smrg	    TRACE(("...updating %s\n", my_attr));
4671cd3331d0Smrg	    TRACE(("...value is %s\n", name));
4672cd3331d0Smrg	    XtSetArg(args[0], my_attr, name);
4673cd3331d0Smrg	    XtSetValues(top, args, 1);
4674d522f475Smrg
4675d522f475Smrg#if OPT_WIDE_CHARS
4676cd3331d0Smrg	    if (xtermEnvUTF8()) {
4677cd3331d0Smrg		Display *dpy = XtDisplay(xw);
4678cd3331d0Smrg		Atom my_atom;
4679cd3331d0Smrg
4680cd3331d0Smrg		const char *propname = (!strcmp(my_attr, XtNtitle)
4681cd3331d0Smrg					? "_NET_WM_NAME"
4682cd3331d0Smrg					: "_NET_WM_ICON_NAME");
4683cd3331d0Smrg		if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
4684cd3331d0Smrg		    if (IsSetUtf8Title(xw)) {
4685cd3331d0Smrg			TRACE(("...updating %s\n", propname));
4686cd3331d0Smrg			TRACE(("...value is %s\n", value));
4687c219fbebSmrg			XChangeProperty(dpy, VShellWindow(xw), my_atom,
4688cd3331d0Smrg					XA_UTF8_STRING(dpy), 8,
4689cd3331d0Smrg					PropModeReplace,
4690cd3331d0Smrg					(Char *) value,
4691cd3331d0Smrg					(int) strlen(value));
4692cd3331d0Smrg		    } else {
4693cd3331d0Smrg			TRACE(("...deleting %s\n", propname));
4694c219fbebSmrg			XDeleteProperty(dpy, VShellWindow(xw), my_atom);
4695cd3331d0Smrg		    }
4696cd3331d0Smrg		}
4697d522f475Smrg	    }
4698cd3331d0Smrg#endif
4699d522f475Smrg	}
4700d522f475Smrg    }
47013367019cSmrg    if (IsTitleMode(xw, tmSetBase16)) {
47023367019cSmrg	free(value);
47033367019cSmrg    }
47043367019cSmrg    free(my_attr);
47053367019cSmrg
4706cd3331d0Smrg    return;
4707d522f475Smrg}
4708d522f475Smrg
4709d522f475Smrgvoid
4710b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
4711d522f475Smrg{
4712cd3331d0Smrg    if (name == 0) {
47133367019cSmrg	name = emptyString;
47143367019cSmrg    }
47153367019cSmrg    if (!showZIconBeep(xw, name))
4716b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
4717d522f475Smrg}
4718d522f475Smrg
4719d522f475Smrgvoid
4720b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
4721d522f475Smrg{
4722b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
4723d522f475Smrg}
4724d522f475Smrg
4725712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
4726d522f475Smrg
4727d522f475Smrgvoid
4728d522f475SmrgChangeXprop(char *buf)
4729d522f475Smrg{
4730d522f475Smrg    Display *dpy = XtDisplay(toplevel);
4731d522f475Smrg    Window w = XtWindow(toplevel);
4732d522f475Smrg    XTextProperty text_prop;
4733d522f475Smrg    Atom aprop;
4734d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
4735d522f475Smrg
4736d522f475Smrg    if (pchEndPropName)
4737d522f475Smrg	*pchEndPropName = '\0';
4738d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
4739d522f475Smrg    if (pchEndPropName == NULL) {
4740d522f475Smrg	/* no "=value" given, so delete the property */
4741d522f475Smrg	XDeleteProperty(dpy, w, aprop);
4742d522f475Smrg    } else {
4743d522f475Smrg	text_prop.value = pchEndPropName + 1;
4744d522f475Smrg	text_prop.encoding = XA_STRING;
4745d522f475Smrg	text_prop.format = 8;
4746d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
4747d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
4748d522f475Smrg    }
4749d522f475Smrg}
4750d522f475Smrg
4751d522f475Smrg/***====================================================================***/
4752d522f475Smrg
4753d522f475Smrg/*
4754d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
4755d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
4756d522f475Smrg */
4757d522f475Smrgvoid
4758d522f475SmrgReverseOldColors(void)
4759d522f475Smrg{
4760d522f475Smrg    ScrnColors *pOld = pOldColors;
4761d522f475Smrg    Pixel tmpPix;
4762d522f475Smrg    char *tmpName;
4763d522f475Smrg
4764d522f475Smrg    if (pOld) {
4765d522f475Smrg	/* change text cursor, if necesary */
4766d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
4767d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
4768d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
4769d522f475Smrg		XtFree(pOldColors->names[TEXT_CURSOR]);
4770d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
4771d522f475Smrg	    }
4772d522f475Smrg	    if (pOld->names[TEXT_BG]) {
4773d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
4774d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
4775d522f475Smrg		}
4776d522f475Smrg	    }
4777d522f475Smrg	}
4778d522f475Smrg
4779d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
4780d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
4781d522f475Smrg
4782d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
4783d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
4784d522f475Smrg
4785d522f475Smrg#if OPT_TEK4014
4786d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
4787d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
4788d522f475Smrg#endif
4789d522f475Smrg    }
4790d522f475Smrg    return;
4791d522f475Smrg}
4792d522f475Smrg
4793d522f475SmrgBool
4794d522f475SmrgAllocateTermColor(XtermWidget xw,
4795d522f475Smrg		  ScrnColors * pNew,
4796d522f475Smrg		  int ndx,
4797cd3331d0Smrg		  const char *name,
4798cd3331d0Smrg		  Bool always)
4799d522f475Smrg{
4800cd3331d0Smrg    Bool result = False;
4801d522f475Smrg
4802cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
4803cd3331d0Smrg	XColor def;
4804cd3331d0Smrg	char *newName;
4805cd3331d0Smrg
4806712a7ff4Smrg	result = True;
4807712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
4808712a7ff4Smrg	    def.pixel = xw->old_foreground;
4809712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
4810712a7ff4Smrg	    def.pixel = xw->old_background;
48113367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
4812712a7ff4Smrg	    result = False;
4813712a7ff4Smrg	}
4814712a7ff4Smrg
4815712a7ff4Smrg	if (result
4816cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
4817712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
4818cd3331d0Smrg		free(pNew->names[ndx]);
4819712a7ff4Smrg	    }
4820cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
4821cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
4822712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
4823712a7ff4Smrg		   ndx, newName, def.pixel));
4824cd3331d0Smrg	} else {
4825cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
4826712a7ff4Smrg	    result = False;
4827cd3331d0Smrg	}
4828cd3331d0Smrg    }
4829cd3331d0Smrg    return result;
4830d522f475Smrg}
4831d522f475Smrg/***====================================================================***/
4832d522f475Smrg
4833d522f475Smrg/* ARGSUSED */
4834d522f475Smrgvoid
4835cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
4836d522f475Smrg{
48373367019cSmrg    if_DEBUG({
48383367019cSmrg	xtermWarning(s, a);
48393367019cSmrg    });
4840d522f475Smrg}
4841d522f475Smrg
4842d522f475Smrgconst char *
4843d522f475SmrgSysErrorMsg(int code)
4844d522f475Smrg{
4845d522f475Smrg    static char unknown[] = "unknown error";
4846d522f475Smrg    char *s = strerror(code);
4847d522f475Smrg    return s ? s : unknown;
4848d522f475Smrg}
4849d522f475Smrg
4850d522f475Smrgconst char *
4851d522f475SmrgSysReasonMsg(int code)
4852d522f475Smrg{
4853d522f475Smrg    /* *INDENT-OFF* */
4854d522f475Smrg    static const struct {
4855d522f475Smrg	int code;
4856d522f475Smrg	const char *name;
4857d522f475Smrg    } table[] = {
4858d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
4859d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
4860d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
4861d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
4862d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
4863d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
4864d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
4865d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
4866d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
4867d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
4868d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
4869d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
4870d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
4871d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
4872d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
4873d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
4874d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
4875d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
4876d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
4877d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
4878d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
4879d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
4880d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
4881d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
4882d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
4883d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
4884d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
4885d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
4886d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
4887d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
4888d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
4889d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
4890d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
4891d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
4892d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
4893d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
4894d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
4895d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
4896d522f475Smrg    };
4897d522f475Smrg    /* *INDENT-ON* */
4898d522f475Smrg
4899d522f475Smrg    Cardinal n;
4900d522f475Smrg    const char *result = "?";
4901d522f475Smrg
4902d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
4903d522f475Smrg	if (code == table[n].code) {
4904d522f475Smrg	    result = table[n].name;
4905d522f475Smrg	    break;
4906d522f475Smrg	}
4907d522f475Smrg    }
4908d522f475Smrg    return result;
4909d522f475Smrg}
4910d522f475Smrg
4911d522f475Smrgvoid
4912d522f475SmrgSysError(int code)
4913d522f475Smrg{
4914d522f475Smrg    int oerrno = errno;
4915d522f475Smrg
4916c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
4917d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
4918d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
4919d522f475Smrg
4920d522f475Smrg    Cleanup(code);
4921d522f475Smrg}
4922d522f475Smrg
4923d522f475Smrgvoid
49243367019cSmrgNormalExit(void)
4925d522f475Smrg{
4926d522f475Smrg    static Bool cleaning;
4927d522f475Smrg
4928d522f475Smrg    /*
4929d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
4930d522f475Smrg     */
49313367019cSmrg    if (cleaning) {
49323367019cSmrg	hold_screen = 0;
49333367019cSmrg	return;
49343367019cSmrg    }
4935d522f475Smrg
49363367019cSmrg    cleaning = True;
49373367019cSmrg    need_cleanup = False;
4938d522f475Smrg
49393367019cSmrg    if (hold_screen) {
49403367019cSmrg	hold_screen = 2;
49413367019cSmrg	while (hold_screen) {
49423367019cSmrg	    xevents();
49433367019cSmrg	    Sleep(10);
4944d522f475Smrg	}
49453367019cSmrg    }
4946d522f475Smrg#if OPT_SESSION_MGT
49473367019cSmrg    if (resource.sessionMgt) {
49483367019cSmrg	XtVaSetValues(toplevel,
49493367019cSmrg		      XtNjoinSession, False,
49503367019cSmrg		      (void *) 0);
4951d522f475Smrg    }
49523367019cSmrg#endif
49533367019cSmrg    Cleanup(0);
49543367019cSmrg}
49553367019cSmrg
49563367019cSmrg/*
49573367019cSmrg * cleanup by sending SIGHUP to client processes
49583367019cSmrg */
49593367019cSmrgvoid
49603367019cSmrgCleanup(int code)
49613367019cSmrg{
49623367019cSmrg    TScreen *screen = TScreenOf(term);
49633367019cSmrg
49643367019cSmrg    TRACE(("Cleanup %d\n", code));
4965d522f475Smrg
4966d522f475Smrg    if (screen->pid > 1) {
4967d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
4968d522f475Smrg    }
4969d522f475Smrg    Exit(code);
4970d522f475Smrg}
4971d522f475Smrg
4972d522f475Smrg#ifndef VMS
49733367019cSmrg#ifndef PATH_MAX
49743367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
49753367019cSmrg#endif
4976d522f475Smrgchar *
4977d522f475SmrgxtermFindShell(char *leaf, Bool warning)
4978d522f475Smrg{
49793367019cSmrg    char *s0;
4980d522f475Smrg    char *s;
4981d522f475Smrg    char *d;
4982d522f475Smrg    char *tmp;
4983d522f475Smrg    char *result = leaf;
49843367019cSmrg    Bool allocated = False;
4985d522f475Smrg
4986d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
49873367019cSmrg
49883367019cSmrg    if (!strncmp("./", result, (size_t) 2)
49893367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
49903367019cSmrg	size_t need = PATH_MAX;
49913367019cSmrg	size_t used = strlen(result) + 2;
49923367019cSmrg	char *buffer = malloc(used + need);
49933367019cSmrg	if (buffer != 0) {
49943367019cSmrg	    if (getcwd(buffer, need) != 0) {
49953367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
49963367019cSmrg		result = buffer;
49973367019cSmrg		allocated = True;
49983367019cSmrg	    } else {
49993367019cSmrg		free(buffer);
50003367019cSmrg	    }
50013367019cSmrg	}
50023367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
5003d522f475Smrg	/* find it in $PATH */
50043367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
50050d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
5006d522f475Smrg		Bool found = False;
5007d522f475Smrg		while (*s != '\0') {
5008d522f475Smrg		    strcpy(tmp, s);
5009d522f475Smrg		    for (d = tmp;; ++d) {
5010d522f475Smrg			if (*d == ':' || *d == '\0') {
5011d522f475Smrg			    int skip = (*d != '\0');
5012d522f475Smrg			    *d = '/';
5013d522f475Smrg			    strcpy(d + 1, leaf);
5014d522f475Smrg			    if (skip)
5015d522f475Smrg				++d;
5016d522f475Smrg			    s += (d - tmp);
5017d522f475Smrg			    if (*tmp == '/'
5018d522f475Smrg				&& strstr(tmp, "..") == 0
5019d522f475Smrg				&& access(tmp, X_OK) == 0) {
5020d522f475Smrg				result = x_strdup(tmp);
5021d522f475Smrg				found = True;
50223367019cSmrg				allocated = True;
5023d522f475Smrg			    }
5024d522f475Smrg			    break;
5025d522f475Smrg			}
5026d522f475Smrg		    }
5027d522f475Smrg		    if (found)
5028d522f475Smrg			break;
5029d522f475Smrg		}
5030d522f475Smrg		free(tmp);
5031d522f475Smrg	    }
50323367019cSmrg	    free(s0);
5033d522f475Smrg	}
5034d522f475Smrg    }
5035d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
5036d522f475Smrg    if (*result != '/'
5037d522f475Smrg	|| strstr(result, "..") != 0
5038d522f475Smrg	|| access(result, X_OK) != 0) {
5039d522f475Smrg	if (warning)
50403367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
50413367019cSmrg	if (allocated)
50423367019cSmrg	    free(result);
5043d522f475Smrg	result = 0;
5044d522f475Smrg    }
50453367019cSmrg    /* be consistent, so that caller can always free the result */
50463367019cSmrg    if (result != 0 && !allocated)
50473367019cSmrg	result = x_strdup(result);
5048d522f475Smrg    return result;
5049d522f475Smrg}
5050d522f475Smrg#endif /* VMS */
5051d522f475Smrg
50520d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
5053d522f475Smrg
50543367019cSmrg/*
50553367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
50563367019cSmrg * This could happen on some older machines due to the uneven standardization
50573367019cSmrg * process for the two functions.
50583367019cSmrg *
50593367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
50603367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
50613367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
50623367019cSmrg * could copy environ.
50633367019cSmrg */
50643367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
50653367019cSmrg#undef HAVE_PUTENV
50663367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
50673367019cSmrg#undef HAVE_UNSETENV
50683367019cSmrg#endif
50693367019cSmrg
5070d522f475Smrg/*
5071d522f475Smrg * copy the environment before Setenv'ing.
5072d522f475Smrg */
5073d522f475Smrgvoid
5074d522f475SmrgxtermCopyEnv(char **oldenv)
5075d522f475Smrg{
50763367019cSmrg#ifdef HAVE_PUTENV
50773367019cSmrg    (void) oldenv;
50783367019cSmrg#else
5079d522f475Smrg    unsigned size;
5080d522f475Smrg    char **newenv;
5081d522f475Smrg
5082d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
5083d522f475Smrg	;
5084d522f475Smrg    }
5085d522f475Smrg
5086d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
5087d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
5088d522f475Smrg    environ = newenv;
50893367019cSmrg#endif
50903367019cSmrg}
50913367019cSmrg
50923367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
50933367019cSmrgstatic int
50943367019cSmrgfindEnv(const char *var, int *lengthp)
50953367019cSmrg{
50963367019cSmrg    char *test;
50973367019cSmrg    int envindex = 0;
50983367019cSmrg    size_t len = strlen(var);
50993367019cSmrg    int found = -1;
51003367019cSmrg
51013367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
51023367019cSmrg
51033367019cSmrg    while ((test = environ[envindex]) != NULL) {
51043367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
51053367019cSmrg	    found = envindex;
51063367019cSmrg	    break;
51073367019cSmrg	}
51083367019cSmrg	envindex++;
51093367019cSmrg    }
51103367019cSmrg    *lengthp = envindex;
51113367019cSmrg    return found;
5112d522f475Smrg}
51133367019cSmrg#endif
5114d522f475Smrg
5115d522f475Smrg/*
5116d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
5117d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
5118d522f475Smrg * This procedure assumes the memory for the first level of environ
5119d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
5120d522f475Smrg * to have to do a realloc().
5121d522f475Smrg */
5122d522f475Smrgvoid
5123cd3331d0SmrgxtermSetenv(const char *var, const char *value)
5124d522f475Smrg{
5125d522f475Smrg    if (value != 0) {
51263367019cSmrg#ifdef HAVE_PUTENV
51273367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
51283367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
51293367019cSmrg	if (both) {
51303367019cSmrg	    sprintf(both, "%s=%s", var, value);
51313367019cSmrg	    putenv(both);
51323367019cSmrg	}
51333367019cSmrg#else
5134d522f475Smrg	size_t len = strlen(var);
51353367019cSmrg	int envindex;
51363367019cSmrg	int found = findEnv(var, &envindex);
5137d522f475Smrg
5138d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
5139d522f475Smrg
5140d522f475Smrg	if (found < 0) {
5141d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
5142d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
5143d522f475Smrg
5144d522f475Smrg	    if (need > have) {
5145d522f475Smrg		char **newenv;
5146d522f475Smrg		newenv = TypeMallocN(char *, need);
5147d522f475Smrg		if (newenv == 0) {
51483367019cSmrg		    xtermWarning("Cannot increase environment\n");
5149d522f475Smrg		    return;
5150d522f475Smrg		}
5151d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
5152d522f475Smrg		free(environ);
5153d522f475Smrg		environ = newenv;
5154d522f475Smrg	    }
5155d522f475Smrg
5156d522f475Smrg	    found = envindex;
5157d522f475Smrg	    environ[found + 1] = NULL;
5158d522f475Smrg	    environ = environ;
5159d522f475Smrg	}
5160d522f475Smrg
5161d522f475Smrg	environ[found] = CastMallocN(char, 1 + len + strlen(value));
5162d522f475Smrg	if (environ[found] == 0) {
51633367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
5164d522f475Smrg	    return;
5165d522f475Smrg	}
5166d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
51673367019cSmrg#endif
5168d522f475Smrg    }
5169d522f475Smrg}
5170d522f475Smrg
51713367019cSmrgvoid
51723367019cSmrgxtermUnsetenv(const char *var)
51733367019cSmrg{
51743367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
51753367019cSmrg#ifdef HAVE_UNSETENV
51763367019cSmrg    unsetenv(var);
51773367019cSmrg#else
51783367019cSmrg    {
51793367019cSmrg	int ignore;
51803367019cSmrg	int item = findEnv(var, &ignore);
51813367019cSmrg	if (item >= 0) {
51823367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
51833367019cSmrg		++item;
51843367019cSmrg	    }
51853367019cSmrg	}
51863367019cSmrg    }
51873367019cSmrg#endif
51883367019cSmrg}
51893367019cSmrg
5190d522f475Smrg/*ARGSUSED*/
5191d522f475Smrgint
5192d522f475Smrgxerror(Display * d, XErrorEvent * ev)
5193d522f475Smrg{
51943367019cSmrg    xtermWarning("warning, error event received:\n");
5195d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
5196d522f475Smrg    Exit(ERROR_XERROR);
5197d522f475Smrg    return 0;			/* appease the compiler */
5198d522f475Smrg}
5199d522f475Smrg
5200712a7ff4Smrgvoid
5201712a7ff4Smrgice_error(IceConn iceConn)
5202712a7ff4Smrg{
5203712a7ff4Smrg    (void) iceConn;
5204712a7ff4Smrg
52053367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
52063367019cSmrg		 (long) getpid(), errno);
5207712a7ff4Smrg
5208712a7ff4Smrg    Exit(ERROR_ICEERROR);
5209712a7ff4Smrg}
5210712a7ff4Smrg
5211d522f475Smrg/*ARGSUSED*/
5212d522f475Smrgint
5213d522f475Smrgxioerror(Display * dpy)
5214d522f475Smrg{
5215d522f475Smrg    int the_error = errno;
5216d522f475Smrg
52173367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
52183367019cSmrg		 the_error, SysErrorMsg(the_error),
52193367019cSmrg		 DisplayString(dpy));
5220d522f475Smrg
5221d522f475Smrg    Exit(ERROR_XIOERROR);
5222d522f475Smrg    return 0;			/* appease the compiler */
5223d522f475Smrg}
5224d522f475Smrg
5225d522f475Smrgvoid
5226d522f475Smrgxt_error(String message)
5227d522f475Smrg{
52283367019cSmrg    xtermWarning("Xt error: %s\n", message);
5229d522f475Smrg
5230d522f475Smrg    /*
5231d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
5232d522f475Smrg     */
5233d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
52343367019cSmrg	xtermWarning("DISPLAY is not set\n");
5235d522f475Smrg    }
5236d522f475Smrg    exit(1);
5237d522f475Smrg}
5238d522f475Smrg
5239d522f475Smrgint
5240d522f475SmrgXStrCmp(char *s1, char *s2)
5241d522f475Smrg{
5242d522f475Smrg    if (s1 && s2)
5243d522f475Smrg	return (strcmp(s1, s2));
5244d522f475Smrg    if (s1 && *s1)
5245d522f475Smrg	return (1);
5246d522f475Smrg    if (s2 && *s2)
5247d522f475Smrg	return (-1);
5248d522f475Smrg    return (0);
5249d522f475Smrg}
5250d522f475Smrg
5251d522f475Smrg#if OPT_TEK4014
5252d522f475Smrgstatic void
5253d522f475Smrgwithdraw_window(Display * dpy, Window w, int scr)
5254d522f475Smrg{
5255d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
5256d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
5257d522f475Smrg    XWithdrawWindow(dpy, w, scr);
5258d522f475Smrg    return;
5259d522f475Smrg}
5260d522f475Smrg#endif
5261d522f475Smrg
5262d522f475Smrgvoid
5263d522f475Smrgset_vt_visibility(Bool on)
5264d522f475Smrg{
5265c219fbebSmrg    XtermWidget xw = term;
5266c219fbebSmrg    TScreen *screen = TScreenOf(xw);
5267d522f475Smrg
5268d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
5269d522f475Smrg    if (on) {
5270c219fbebSmrg	if (!screen->Vshow && xw) {
5271c219fbebSmrg	    VTInit(xw);
5272c219fbebSmrg	    XtMapWidget(XtParent(xw));
5273d522f475Smrg#if OPT_TOOLBAR
5274d522f475Smrg	    /* we need both of these during initialization */
5275c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
5276d522f475Smrg	    ShowToolbar(resource.toolBar);
5277d522f475Smrg#endif
5278d522f475Smrg	    screen->Vshow = True;
5279d522f475Smrg	}
5280d522f475Smrg    }
5281d522f475Smrg#if OPT_TEK4014
5282d522f475Smrg    else {
5283c219fbebSmrg	if (screen->Vshow && xw) {
5284c219fbebSmrg	    withdraw_window(XtDisplay(xw),
5285c219fbebSmrg			    VShellWindow(xw),
5286c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
5287d522f475Smrg	    screen->Vshow = False;
5288d522f475Smrg	}
5289d522f475Smrg    }
5290d522f475Smrg    set_vthide_sensitivity();
5291d522f475Smrg    set_tekhide_sensitivity();
5292d522f475Smrg    update_vttekmode();
5293d522f475Smrg    update_tekshow();
5294d522f475Smrg    update_vtshow();
5295d522f475Smrg#endif
5296d522f475Smrg    return;
5297d522f475Smrg}
5298d522f475Smrg
5299d522f475Smrg#if OPT_TEK4014
5300d522f475Smrgvoid
5301d522f475Smrgset_tek_visibility(Bool on)
5302d522f475Smrg{
5303d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
5304d522f475Smrg
5305d522f475Smrg    if (on) {
5306cd3331d0Smrg	if (!TEK4014_SHOWN(term)) {
5307cd3331d0Smrg	    if (tekWidget == 0) {
5308cd3331d0Smrg		TekInit();	/* will exit on failure */
5309cd3331d0Smrg	    }
5310cd3331d0Smrg	    if (tekWidget != 0) {
5311cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
5312cd3331d0Smrg		XtRealizeWidget(tekParent);
5313cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
5314d522f475Smrg#if OPT_TOOLBAR
5315cd3331d0Smrg		/* we need both of these during initialization */
5316cd3331d0Smrg		XtMapWidget(tekParent);
5317cd3331d0Smrg		XtMapWidget(tekWidget);
5318d522f475Smrg#endif
5319cd3331d0Smrg		XtOverrideTranslations(tekParent,
5320cd3331d0Smrg				       XtParseTranslationTable
5321cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
5322cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
5323cd3331d0Smrg				       XtWindow(tekParent),
5324cd3331d0Smrg				       &wm_delete_window, 1);
5325cd3331d0Smrg		TEK4014_SHOWN(term) = True;
5326cd3331d0Smrg	    }
5327d522f475Smrg	}
5328d522f475Smrg    } else {
5329d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
5330d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
5331d522f475Smrg			    TShellWindow,
5332d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
5333d522f475Smrg	    TEK4014_SHOWN(term) = False;
5334d522f475Smrg	}
5335d522f475Smrg    }
5336d522f475Smrg    set_tekhide_sensitivity();
5337d522f475Smrg    set_vthide_sensitivity();
5338d522f475Smrg    update_vtshow();
5339d522f475Smrg    update_tekshow();
5340d522f475Smrg    update_vttekmode();
5341d522f475Smrg    return;
5342d522f475Smrg}
5343d522f475Smrg
5344d522f475Smrgvoid
5345d522f475Smrgend_tek_mode(void)
5346d522f475Smrg{
5347cd3331d0Smrg    XtermWidget xw = term;
5348cd3331d0Smrg
5349cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
5350cd3331d0Smrg	FlushLog(xw);
5351d522f475Smrg	longjmp(Tekend, 1);
5352d522f475Smrg    }
5353d522f475Smrg    return;
5354d522f475Smrg}
5355d522f475Smrg
5356d522f475Smrgvoid
5357d522f475Smrgend_vt_mode(void)
5358d522f475Smrg{
5359cd3331d0Smrg    XtermWidget xw = term;
5360cd3331d0Smrg
5361cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
5362cd3331d0Smrg	FlushLog(xw);
5363cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
5364d522f475Smrg	longjmp(VTend, 1);
5365d522f475Smrg    }
5366d522f475Smrg    return;
5367d522f475Smrg}
5368d522f475Smrg
5369d522f475Smrgvoid
5370d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
5371d522f475Smrg{
5372d522f475Smrg    if (tovt) {
5373d522f475Smrg	if (tekRefreshList)
5374d522f475Smrg	    TekRefresh(tekWidget);
5375d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
5376d522f475Smrg    } else {
5377d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
5378d522f475Smrg    }
5379d522f475Smrg}
5380d522f475Smrg
5381d522f475Smrgvoid
5382d522f475Smrghide_vt_window(void)
5383d522f475Smrg{
5384d522f475Smrg    set_vt_visibility(False);
5385d522f475Smrg    if (!TEK4014_ACTIVE(term))
5386d522f475Smrg	switch_modes(False);	/* switch to tek mode */
5387d522f475Smrg}
5388d522f475Smrg
5389d522f475Smrgvoid
5390d522f475Smrghide_tek_window(void)
5391d522f475Smrg{
5392d522f475Smrg    set_tek_visibility(False);
5393d522f475Smrg    tekRefreshList = (TekLink *) 0;
5394d522f475Smrg    if (TEK4014_ACTIVE(term))
5395d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
5396d522f475Smrg}
5397d522f475Smrg#endif /* OPT_TEK4014 */
5398d522f475Smrg
5399d522f475Smrgstatic const char *
5400d522f475Smrgskip_punct(const char *s)
5401d522f475Smrg{
5402d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
5403d522f475Smrg	++s;
5404d522f475Smrg    }
5405d522f475Smrg    return s;
5406d522f475Smrg}
5407d522f475Smrg
5408d522f475Smrgstatic int
5409d522f475Smrgcmp_options(const void *a, const void *b)
5410d522f475Smrg{
5411d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
5412d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
5413d522f475Smrg    return strcmp(s1, s2);
5414d522f475Smrg}
5415d522f475Smrg
5416d522f475Smrgstatic int
5417d522f475Smrgcmp_resources(const void *a, const void *b)
5418d522f475Smrg{
5419d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
5420d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
5421d522f475Smrg}
5422d522f475Smrg
5423d522f475SmrgXrmOptionDescRec *
5424d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
5425d522f475Smrg{
5426d522f475Smrg    static XrmOptionDescRec *res_array = 0;
5427d522f475Smrg
5428d522f475Smrg#ifdef NO_LEAKS
5429cd3331d0Smrg    if (descs == 0) {
5430cd3331d0Smrg	if (res_array != 0) {
5431cd3331d0Smrg	    free(res_array);
5432cd3331d0Smrg	    res_array = 0;
5433cd3331d0Smrg	}
5434d522f475Smrg    } else
5435d522f475Smrg#endif
5436d522f475Smrg    if (res_array == 0) {
5437d522f475Smrg	Cardinal j;
5438d522f475Smrg
5439d522f475Smrg	/* make a sorted index to 'resources' */
5440d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
5441cd3331d0Smrg	if (res_array != 0) {
5442cd3331d0Smrg	    for (j = 0; j < res_count; j++)
5443cd3331d0Smrg		res_array[j] = descs[j];
5444cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
5445cd3331d0Smrg	}
5446d522f475Smrg    }
5447d522f475Smrg    return res_array;
5448d522f475Smrg}
5449d522f475Smrg
5450d522f475Smrg/*
5451d522f475Smrg * The first time this is called, construct sorted index to the main program's
5452d522f475Smrg * list of options, taking into account the on/off options which will be
5453d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
5454d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
5455d522f475Smrg */
5456d522f475SmrgOptionHelp *
5457d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
5458d522f475Smrg{
5459d522f475Smrg    static OptionHelp *opt_array = 0;
5460d522f475Smrg
5461d522f475Smrg#ifdef NO_LEAKS
5462d522f475Smrg    if (descs == 0 && opt_array != 0) {
5463d522f475Smrg	sortedOptDescs(descs, numDescs);
5464d522f475Smrg	free(opt_array);
5465d522f475Smrg	opt_array = 0;
5466d522f475Smrg	return 0;
5467d522f475Smrg    } else if (options == 0 || descs == 0) {
5468d522f475Smrg	return 0;
5469d522f475Smrg    }
5470d522f475Smrg#endif
5471d522f475Smrg
5472d522f475Smrg    if (opt_array == 0) {
5473cd3331d0Smrg	size_t opt_count, j;
5474d522f475Smrg#if OPT_TRACE
5475d522f475Smrg	Cardinal k;
5476d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
5477d522f475Smrg	int code;
5478cd3331d0Smrg	const char *mesg;
5479d522f475Smrg#else
5480d522f475Smrg	(void) descs;
5481d522f475Smrg	(void) numDescs;
5482d522f475Smrg#endif
5483d522f475Smrg
5484d522f475Smrg	/* count 'options' and make a sorted index to it */
5485d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
5486d522f475Smrg	    ;
5487d522f475Smrg	}
5488d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
5489d522f475Smrg	for (j = 0; j < opt_count; j++)
5490d522f475Smrg	    opt_array[j] = options[j];
5491d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
5492d522f475Smrg
5493d522f475Smrg	/* supply the "turn on/off" strings if needed */
5494d522f475Smrg#if OPT_TRACE
5495d522f475Smrg	for (j = 0; j < opt_count; j++) {
5496712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
5497c219fbebSmrg		char temp[80];
5498cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
5499d522f475Smrg		for (k = 0; k < numDescs; ++k) {
5500cd3331d0Smrg		    const char *value = res_array[k].value;
5501d522f475Smrg		    if (res_array[k].option[0] == '-') {
5502d522f475Smrg			code = -1;
5503d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
5504d522f475Smrg			code = 1;
5505d522f475Smrg		    } else {
5506d522f475Smrg			code = 0;
5507d522f475Smrg		    }
55083367019cSmrg		    sprintf(temp, "%.*s",
55093367019cSmrg			    (int) sizeof(temp) - 2,
55103367019cSmrg			    opt_array[j].desc);
5511c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
5512d522f475Smrg			code = -code;
5513d522f475Smrg		    if (code != 0
5514d522f475Smrg			&& res_array[k].value != 0
5515d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
5516d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
5517d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
5518d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
5519d522f475Smrg			    mesg = "turn on/off";
5520d522f475Smrg			} else {
5521d522f475Smrg			    mesg = "turn off/on";
5522d522f475Smrg			}
5523d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
5524712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
5525d522f475Smrg				char *s = CastMallocN(char,
5526d522f475Smrg						      strlen(mesg)
5527d522f475Smrg						      + 1
5528d522f475Smrg						      + strlen(opt_array[j].desc));
5529d522f475Smrg				if (s != 0) {
5530d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
5531d522f475Smrg				    opt_array[j].desc = s;
5532d522f475Smrg				}
5533d522f475Smrg			    } else {
5534d522f475Smrg				TRACE(("OOPS "));
5535d522f475Smrg			    }
5536d522f475Smrg			}
5537d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
5538d522f475Smrg			       mesg,
5539d522f475Smrg			       res_array[k].option,
5540d522f475Smrg			       res_array[k].value,
5541d522f475Smrg			       opt_array[j].opt,
5542d522f475Smrg			       opt_array[j].desc));
5543d522f475Smrg			break;
5544d522f475Smrg		    }
5545d522f475Smrg		}
5546d522f475Smrg	    }
5547d522f475Smrg	}
5548d522f475Smrg#endif
5549d522f475Smrg    }
5550d522f475Smrg    return opt_array;
5551d522f475Smrg}
5552d522f475Smrg
5553d522f475Smrg/*
5554d522f475Smrg * Report the character-type locale that xterm was started in.
5555d522f475Smrg */
55563367019cSmrgString
5557d522f475SmrgxtermEnvLocale(void)
5558d522f475Smrg{
55593367019cSmrg    static String result;
5560d522f475Smrg
5561d522f475Smrg    if (result == 0) {
5562d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
5563cd3331d0Smrg	    result = x_strdup("C");
5564cd3331d0Smrg	} else {
5565cd3331d0Smrg	    result = x_strdup(result);
5566d522f475Smrg	}
5567d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
5568d522f475Smrg    }
5569d522f475Smrg    return result;
5570d522f475Smrg}
5571d522f475Smrg
5572d522f475Smrgchar *
5573d522f475SmrgxtermEnvEncoding(void)
5574d522f475Smrg{
5575d522f475Smrg    static char *result;
5576d522f475Smrg
5577d522f475Smrg    if (result == 0) {
5578d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5579d522f475Smrg	result = nl_langinfo(CODESET);
5580d522f475Smrg#else
5581d522f475Smrg	char *locale = xtermEnvLocale();
5582d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
5583d522f475Smrg	    result = "ASCII";
5584d522f475Smrg	} else {
5585d522f475Smrg	    result = "ISO-8859-1";
5586d522f475Smrg	}
5587d522f475Smrg#endif
5588d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
5589d522f475Smrg    }
5590d522f475Smrg    return result;
5591d522f475Smrg}
5592d522f475Smrg
5593d522f475Smrg#if OPT_WIDE_CHARS
5594d522f475Smrg/*
5595d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
5596d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
5597d522f475Smrg * various library calls.
5598d522f475Smrg */
5599d522f475SmrgBool
5600d522f475SmrgxtermEnvUTF8(void)
5601d522f475Smrg{
5602d522f475Smrg    static Bool init = False;
5603d522f475Smrg    static Bool result = False;
5604d522f475Smrg
5605d522f475Smrg    if (!init) {
5606d522f475Smrg	init = True;
5607d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5608d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
5609d522f475Smrg#else
5610d522f475Smrg	result = (strstr(xtermEnvLocale(), "UTF-8") != NULL);
5611d522f475Smrg#endif
5612d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
5613d522f475Smrg    }
5614d522f475Smrg    return result;
5615d522f475Smrg}
5616d522f475Smrg#endif /* OPT_WIDE_CHARS */
5617d522f475Smrg
5618b7c89284Ssnj/*
5619b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
5620b7c89284Ssnj */
5621b7c89284SsnjXtermWidget
5622b7c89284SsnjgetXtermWidget(Widget w)
5623b7c89284Ssnj{
5624b7c89284Ssnj    XtermWidget xw;
5625b7c89284Ssnj
5626b7c89284Ssnj    if (w == 0) {
5627b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
5628b7c89284Ssnj	if (!IsXtermWidget(xw)) {
5629b7c89284Ssnj	    xw = 0;
5630b7c89284Ssnj	}
5631b7c89284Ssnj    } else if (IsXtermWidget(w)) {
5632b7c89284Ssnj	xw = (XtermWidget) w;
5633b7c89284Ssnj    } else {
5634b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
5635b7c89284Ssnj    }
5636b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
5637b7c89284Ssnj    return xw;
5638b7c89284Ssnj}
56393367019cSmrg
56403367019cSmrg#if OPT_SESSION_MGT
56413367019cSmrgstatic void
56423367019cSmrgdie_callback(Widget w GCC_UNUSED,
56433367019cSmrg	     XtPointer client_data GCC_UNUSED,
56443367019cSmrg	     XtPointer call_data GCC_UNUSED)
56453367019cSmrg{
56463367019cSmrg    NormalExit();
56473367019cSmrg}
56483367019cSmrg
56493367019cSmrgstatic void
56503367019cSmrgsave_callback(Widget w GCC_UNUSED,
56513367019cSmrg	      XtPointer client_data GCC_UNUSED,
56523367019cSmrg	      XtPointer call_data)
56533367019cSmrg{
56543367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
56553367019cSmrg    /* we have nothing to save */
56563367019cSmrg    token->save_success = True;
56573367019cSmrg}
56583367019cSmrg
56593367019cSmrgstatic void
56603367019cSmrgicewatch(IceConn iceConn,
56613367019cSmrg	 IcePointer clientData GCC_UNUSED,
56623367019cSmrg	 Bool opening,
56633367019cSmrg	 IcePointer * watchData GCC_UNUSED)
56643367019cSmrg{
56653367019cSmrg    if (opening) {
56663367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
56673367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
56683367019cSmrg    } else {
56693367019cSmrg	ice_fd = -1;
56703367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
56713367019cSmrg    }
56723367019cSmrg}
56733367019cSmrg
56743367019cSmrgvoid
56753367019cSmrgxtermOpenSession(void)
56763367019cSmrg{
56773367019cSmrg    if (resource.sessionMgt) {
56783367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
56793367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
56803367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
56813367019cSmrg    }
56823367019cSmrg}
56833367019cSmrg
56843367019cSmrgvoid
56853367019cSmrgxtermCloseSession(void)
56863367019cSmrg{
56873367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
56883367019cSmrg}
56893367019cSmrg#endif /* OPT_SESSION_MGT */
56903367019cSmrg
56913367019cSmrgWidget
56923367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
56933367019cSmrg		     String my_class,
56943367019cSmrg		     XrmOptionDescRec * options,
56953367019cSmrg		     Cardinal num_options,
56963367019cSmrg		     int *argc_in_out,
56973367019cSmrg		     String * argv_in_out,
56983367019cSmrg		     String * fallback_resources,
56993367019cSmrg		     WidgetClass widget_class,
57003367019cSmrg		     ArgList args,
57013367019cSmrg		     Cardinal num_args)
57023367019cSmrg{
57033367019cSmrg    Widget result;
57043367019cSmrg
57053367019cSmrg    XtSetErrorHandler(xt_error);
57063367019cSmrg#if OPT_SESSION_MGT
57073367019cSmrg    result = XtOpenApplication(app_context_return,
57083367019cSmrg			       my_class,
57093367019cSmrg			       options,
57103367019cSmrg			       num_options,
57113367019cSmrg			       argc_in_out,
57123367019cSmrg			       argv_in_out,
57133367019cSmrg			       fallback_resources,
57143367019cSmrg			       widget_class,
57153367019cSmrg			       args,
57163367019cSmrg			       num_args);
57173367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
57183367019cSmrg#else
57193367019cSmrg    result = XtAppInitialize(app_context_return,
57203367019cSmrg			     my_class,
57213367019cSmrg			     options,
57223367019cSmrg			     num_options,
57233367019cSmrg			     argc_in_out,
57243367019cSmrg			     argv_in_out,
57253367019cSmrg			     fallback_resources,
57263367019cSmrg			     NULL, 0);
57273367019cSmrg#endif /* OPT_SESSION_MGT */
57283367019cSmrg    XtSetErrorHandler((XtErrorHandler) 0);
57293367019cSmrg
57303367019cSmrg    return result;
57313367019cSmrg}
57323367019cSmrg
57333367019cSmrgstatic int x11_errors;
57343367019cSmrg
57353367019cSmrgstatic int
57363367019cSmrgcatch_x11_error(Display * display, XErrorEvent * error_event)
57373367019cSmrg{
57383367019cSmrg    (void) display;
57393367019cSmrg    (void) error_event;
57403367019cSmrg    ++x11_errors;
57413367019cSmrg    return 0;
57423367019cSmrg}
57433367019cSmrg
57443367019cSmrgBoolean
57453367019cSmrgxtermGetWinAttrs(Display * dpy, Window win, XWindowAttributes * attrs)
57463367019cSmrg{
57473367019cSmrg    Boolean result = False;
57483367019cSmrg    Status code;
57493367019cSmrg
57503367019cSmrg    memset(attrs, 0, sizeof(*attrs));
57513367019cSmrg    if (win != None) {
57523367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
57533367019cSmrg	x11_errors = 0;
57543367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
57553367019cSmrg	XSetErrorHandler(save);
57563367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
57573367019cSmrg	if (result) {
57583367019cSmrg	    TRACE_WIN_ATTRS(attrs);
57593367019cSmrg	} else {
57603367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
57613367019cSmrg	}
57623367019cSmrg    }
57633367019cSmrg    return result;
57643367019cSmrg}
57653367019cSmrg
57663367019cSmrgBoolean
57673367019cSmrgxtermGetWinProp(Display * display,
57683367019cSmrg		Window win,
57693367019cSmrg		Atom property,
57703367019cSmrg		long long_offset,
57713367019cSmrg		long long_length,
57723367019cSmrg		Atom req_type,
57733367019cSmrg		Atom * actual_type_return,
57743367019cSmrg		int *actual_format_return,
57753367019cSmrg		unsigned long *nitems_return,
57763367019cSmrg		unsigned long *bytes_after_return,
57773367019cSmrg		unsigned char **prop_return)
57783367019cSmrg{
57793367019cSmrg    Boolean result = True;
57803367019cSmrg
57813367019cSmrg    if (win != None) {
57823367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
57833367019cSmrg	x11_errors = 0;
57843367019cSmrg	if (XGetWindowProperty(display,
57853367019cSmrg			       win,
57863367019cSmrg			       property,
57873367019cSmrg			       long_offset,
57883367019cSmrg			       long_length,
57893367019cSmrg			       False,
57903367019cSmrg			       req_type,
57913367019cSmrg			       actual_type_return,
57923367019cSmrg			       actual_format_return,
57933367019cSmrg			       nitems_return,
57943367019cSmrg			       bytes_after_return,
57953367019cSmrg			       prop_return) == Success
57963367019cSmrg	    && x11_errors == 0) {
57973367019cSmrg	    result = True;
57983367019cSmrg	}
57993367019cSmrg	XSetErrorHandler(save);
58003367019cSmrg    }
58013367019cSmrg    return result;
58023367019cSmrg}
58033367019cSmrg
58043367019cSmrgvoid
58053367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
58063367019cSmrg{
58073367019cSmrg    Display *dpy = XtDisplay(toplevel);
58083367019cSmrg    XWindowAttributes attrs;
58093367019cSmrg
58103367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
58113367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
58123367019cSmrg	XtermWidget xw = term;
58133367019cSmrg	TScreen *screen = TScreenOf(xw);
58143367019cSmrg
58153367019cSmrg	XtRealizeWidget(toplevel);
58163367019cSmrg
58173367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
58183367019cSmrg	       XtWindow(toplevel),
58193367019cSmrg	       winToEmbedInto));
58203367019cSmrg	XReparentWindow(dpy,
58213367019cSmrg			XtWindow(toplevel),
58223367019cSmrg			winToEmbedInto, 0, 0);
58233367019cSmrg
58243367019cSmrg	screen->embed_high = (Dimension) attrs.height;
58253367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
58263367019cSmrg    }
58273367019cSmrg}
5828