misc.c revision dfb07bc7
1dfb07bc7Smrg/* $XTermId: misc.c,v 1.757 2017/06/20 08:52:18 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4dfb07bc7Smrg * Copyright 1999-2016,2017 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>
58dfb07bc7Smrg#include <xterm_io.h>
59d522f475Smrg
60d522f475Smrg#include <sys/stat.h>
61d522f475Smrg#include <stdio.h>
623367019cSmrg#include <stdarg.h>
63d522f475Smrg#include <signal.h>
64d522f475Smrg#include <ctype.h>
65d522f475Smrg#include <pwd.h>
66d522f475Smrg#include <sys/wait.h>
67d522f475Smrg
68d522f475Smrg#include <X11/keysym.h>
69d522f475Smrg#include <X11/Xatom.h>
70d522f475Smrg#include <X11/cursorfont.h>
71d522f475Smrg#include <X11/Xlocale.h>
72d522f475Smrg
73d522f475Smrg#include <X11/Xmu/Error.h>
74d522f475Smrg#include <X11/Xmu/SysUtil.h>
75d522f475Smrg#include <X11/Xmu/WinUtil.h>
76d522f475Smrg#include <X11/Xmu/Xmu.h>
77d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
78d522f475Smrg#include <X11/Sunkeysym.h>
79d522f475Smrg#endif
80d522f475Smrg
813367019cSmrg#ifdef HAVE_LIBXPM
823367019cSmrg#include <X11/xpm.h>
833367019cSmrg#endif
843367019cSmrg
85d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
86d522f475Smrg#include <langinfo.h>
87d522f475Smrg#endif
88d522f475Smrg
89d522f475Smrg#include <xutf8.h>
90d522f475Smrg
91d522f475Smrg#include <data.h>
92d522f475Smrg#include <error.h>
93d522f475Smrg#include <menu.h>
94d522f475Smrg#include <fontutils.h>
95d522f475Smrg#include <xstrings.h>
96d522f475Smrg#include <xtermcap.h>
97d522f475Smrg#include <VTparse.h>
98fa3f02f3Smrg#include <graphics.h>
999a64e1c5Smrg#include <graphics_regis.h>
1009a64e1c5Smrg#include <graphics_sixel.h>
101d522f475Smrg
102d522f475Smrg#include <assert.h>
103d522f475Smrg
104d522f475Smrg#if (XtSpecificationRelease < 6)
105d522f475Smrg#ifndef X_GETTIMEOFDAY
106d522f475Smrg#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0)
107d522f475Smrg#endif
108d522f475Smrg#endif
109d522f475Smrg
110d522f475Smrg#ifdef VMS
111d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
112d522f475Smrg#ifdef ALLOWLOGFILEEXEC
113d522f475Smrg#undef ALLOWLOGFILEEXEC
114d522f475Smrg#endif
115d522f475Smrg#endif /* VMS */
116d522f475Smrg
117d522f475Smrg#if OPT_TEK4014
118d522f475Smrg#define OUR_EVENT(event,Type) \
119d522f475Smrg		(event.type == Type && \
120d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
121d522f475Smrg		    (tekWidget && \
122d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
123d522f475Smrg#else
124d522f475Smrg#define OUR_EVENT(event,Type) \
125d522f475Smrg		(event.type == Type && \
126d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
127d522f475Smrg#endif
128d522f475Smrg
1293367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
130d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
131d522f475Smrg
1323367019cSmrgstatic char emptyString[] = "";
1333367019cSmrg
134d522f475Smrg#if OPT_EXEC_XTERM
135d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
136d522f475Smrg   error; adapted from libc docs */
137d522f475Smrgstatic char *
138d522f475SmrgReadlink(const char *filename)
139d522f475Smrg{
140d522f475Smrg    char *buf = NULL;
141cd3331d0Smrg    size_t size = 100;
142d522f475Smrg
143d522f475Smrg    for (;;) {
144037a25ddSmrg	int n;
145037a25ddSmrg	char *tmp = TypeRealloc(char, size, buf);
146037a25ddSmrg	if (tmp == NULL) {
147037a25ddSmrg	    free(buf);
148037a25ddSmrg	    return NULL;
149037a25ddSmrg	}
150037a25ddSmrg	buf = tmp;
151d522f475Smrg	memset(buf, 0, size);
152d522f475Smrg
153cd3331d0Smrg	n = (int) readlink(filename, buf, size);
154d522f475Smrg	if (n < 0) {
155d522f475Smrg	    free(buf);
156d522f475Smrg	    return NULL;
157d522f475Smrg	}
158d522f475Smrg
159d522f475Smrg	if ((unsigned) n < size) {
160d522f475Smrg	    return buf;
161d522f475Smrg	}
162d522f475Smrg
163d522f475Smrg	size *= 2;
164d522f475Smrg    }
165d522f475Smrg}
166d522f475Smrg#endif /* OPT_EXEC_XTERM */
167d522f475Smrg
168d522f475Smrgstatic void
169d522f475SmrgSleep(int msec)
170d522f475Smrg{
171d522f475Smrg    static struct timeval select_timeout;
172d522f475Smrg
173d522f475Smrg    select_timeout.tv_sec = 0;
174d522f475Smrg    select_timeout.tv_usec = msec * 1000;
175d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
176d522f475Smrg}
177d522f475Smrg
178d522f475Smrgstatic void
1793367019cSmrgselectwindow(XtermWidget xw, int flag)
180d522f475Smrg{
1813367019cSmrg    TScreen *screen = TScreenOf(xw);
1823367019cSmrg
183d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
184d522f475Smrg
185d522f475Smrg#if OPT_TEK4014
1863367019cSmrg    if (TEK4014_ACTIVE(xw)) {
187d522f475Smrg	if (!Ttoggled)
188d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
189d522f475Smrg	screen->select |= flag;
190d522f475Smrg	if (!Ttoggled)
191d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
192d522f475Smrg    } else
193d522f475Smrg#endif
194d522f475Smrg    {
1953367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
1963367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
1973367019cSmrg	if (input && input->xic)
1983367019cSmrg	    XSetICFocus(input->xic);
1993367019cSmrg#endif
200d522f475Smrg
201d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
202d522f475Smrg	    HideCursor();
203d522f475Smrg	screen->select |= flag;
204d522f475Smrg	if (screen->cursor_state)
205d522f475Smrg	    ShowCursor();
206d522f475Smrg    }
207cd3331d0Smrg    GetScrollLock(screen);
208d522f475Smrg}
209d522f475Smrg
210d522f475Smrgstatic void
2113367019cSmrgunselectwindow(XtermWidget xw, int flag)
212d522f475Smrg{
2133367019cSmrg    TScreen *screen = TScreenOf(xw);
2143367019cSmrg
215d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
216d522f475Smrg
2173367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
218d522f475Smrg	screen->hide_pointer = False;
2193367019cSmrg	xtermDisplayCursor(xw);
220d522f475Smrg    }
221d522f475Smrg
222d522f475Smrg    if (!screen->always_highlight) {
223d522f475Smrg#if OPT_TEK4014
2243367019cSmrg	if (TEK4014_ACTIVE(xw)) {
225d522f475Smrg	    if (!Ttoggled)
226d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
227d522f475Smrg	    screen->select &= ~flag;
228d522f475Smrg	    if (!Ttoggled)
229d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
230d522f475Smrg	} else
231d522f475Smrg#endif
232d522f475Smrg	{
2333367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
2343367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2353367019cSmrg	    if (input && input->xic)
2363367019cSmrg		XUnsetICFocus(input->xic);
2373367019cSmrg#endif
238d522f475Smrg
239d522f475Smrg	    screen->select &= ~flag;
240d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
241d522f475Smrg		HideCursor();
242d522f475Smrg	    if (screen->cursor_state)
243d522f475Smrg		ShowCursor();
244d522f475Smrg	}
245d522f475Smrg    }
246d522f475Smrg}
247d522f475Smrg
248d522f475Smrgstatic void
2499a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
250d522f475Smrg{
251d522f475Smrg    TScreen *screen = TScreenOf(xw);
252d522f475Smrg
253d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
254cd3331d0Smrg    TRACE_FOCUS(xw, ev);
255cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
256cd3331d0Smrg	ev->focus &&
257cd3331d0Smrg	!(screen->select & FOCUS))
2583367019cSmrg	selectwindow(xw, INWINDOW);
259d522f475Smrg}
260d522f475Smrg
261d522f475Smrgstatic void
2629a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
263d522f475Smrg{
264d522f475Smrg    TScreen *screen = TScreenOf(xw);
265d522f475Smrg
266d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
267cd3331d0Smrg    TRACE_FOCUS(xw, ev);
268cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
269cd3331d0Smrg	ev->focus &&
270cd3331d0Smrg	!(screen->select & FOCUS))
2713367019cSmrg	unselectwindow(xw, INWINDOW);
272d522f475Smrg}
273d522f475Smrg
274d522f475Smrg#ifndef XUrgencyHint
275d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
276d522f475Smrg#endif
277d522f475Smrg
278d522f475Smrgstatic void
279c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
280d522f475Smrg{
281c219fbebSmrg    TScreen *screen = TScreenOf(xw);
282c219fbebSmrg
283d522f475Smrg    if (screen->bellIsUrgent) {
284c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
285d522f475Smrg	if (h != 0) {
286c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
287d522f475Smrg		h->flags |= XUrgencyHint;
288d522f475Smrg	    } else {
289d522f475Smrg		h->flags &= ~XUrgencyHint;
290d522f475Smrg	    }
291c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
292d522f475Smrg	}
293d522f475Smrg    }
294d522f475Smrg}
295d522f475Smrg
296d522f475Smrgvoid
297d522f475Smrgdo_xevents(void)
298d522f475Smrg{
299d522f475Smrg    TScreen *screen = TScreenOf(term);
300d522f475Smrg
3013367019cSmrg    if (xtermAppPending()
302d522f475Smrg	||
303d522f475Smrg#if defined(VMS) || defined(__VMS)
304d522f475Smrg	screen->display->qlen > 0
305d522f475Smrg#else
306d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
307d522f475Smrg#endif
308d522f475Smrg	)
309d522f475Smrg	xevents();
310d522f475Smrg}
311d522f475Smrg
312d522f475Smrgvoid
313d522f475SmrgxtermDisplayCursor(XtermWidget xw)
314d522f475Smrg{
315d522f475Smrg    TScreen *screen = TScreenOf(xw);
316d522f475Smrg
317d522f475Smrg    if (screen->Vshow) {
318d522f475Smrg	if (screen->hide_pointer) {
319d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
320d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
321d522f475Smrg	} else {
322d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
323d522f475Smrg	    recolor_cursor(screen,
324d522f475Smrg			   screen->pointer_cursor,
325d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
326d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
327d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
328d522f475Smrg	}
329d522f475Smrg    }
330d522f475Smrg}
331d522f475Smrg
332d522f475Smrgvoid
333d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
334d522f475Smrg{
335d522f475Smrg    static int tried = -1;
336d522f475Smrg    TScreen *screen = TScreenOf(xw);
337d522f475Smrg
338d522f475Smrg#if OPT_TEK4014
339d522f475Smrg    if (TEK4014_SHOWN(xw))
340d522f475Smrg	enable = True;
341d522f475Smrg#endif
342d522f475Smrg
343d522f475Smrg    /*
344d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
345d522f475Smrg     * the mouse-mode:
346d522f475Smrg     */
347d522f475Smrg    if (!enable) {
348d522f475Smrg	switch (screen->pointer_mode) {
349d522f475Smrg	case pNever:
350d522f475Smrg	    enable = True;
351d522f475Smrg	    break;
352d522f475Smrg	case pNoMouse:
353d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
354d522f475Smrg		enable = True;
355d522f475Smrg	    break;
356d522f475Smrg	case pAlways:
3573367019cSmrg	case pFocused:
358d522f475Smrg	    break;
359d522f475Smrg	}
360d522f475Smrg    }
361d522f475Smrg
362d522f475Smrg    if (enable) {
363d522f475Smrg	if (screen->hide_pointer) {
364d522f475Smrg	    screen->hide_pointer = False;
365d522f475Smrg	    xtermDisplayCursor(xw);
366d522f475Smrg	    switch (screen->send_mouse_pos) {
367d522f475Smrg	    case ANY_EVENT_MOUSE:
368d522f475Smrg		break;
369d522f475Smrg	    default:
370d522f475Smrg		MotionOff(screen, xw);
371d522f475Smrg		break;
372d522f475Smrg	    }
373d522f475Smrg	}
374d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
375d522f475Smrg	if (screen->hidden_cursor == 0) {
376d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
377d522f475Smrg	}
378d522f475Smrg	if (screen->hidden_cursor == 0) {
379d522f475Smrg	    tried = 1;
380d522f475Smrg	} else {
381d522f475Smrg	    tried = 0;
382d522f475Smrg	    screen->hide_pointer = True;
383d522f475Smrg	    xtermDisplayCursor(xw);
384d522f475Smrg	    MotionOn(screen, xw);
385d522f475Smrg	}
386d522f475Smrg    }
387d522f475Smrg}
388d522f475Smrg
3893367019cSmrg#if OPT_TRACE
3903367019cSmrgstatic void
3919a64e1c5SmrgTraceExposeEvent(XEvent *arg)
3923367019cSmrg{
3933367019cSmrg    XExposeEvent *event = (XExposeEvent *) arg;
3943367019cSmrg
3953367019cSmrg    TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n",
3963367019cSmrg	   event->serial,
3973367019cSmrg	   event->count,
3983367019cSmrg	   event->y,
3993367019cSmrg	   event->x,
4003367019cSmrg	   event->height,
4013367019cSmrg	   event->width,
4023367019cSmrg	   event->window));
4033367019cSmrg}
4043367019cSmrg
4053367019cSmrg#else
4063367019cSmrg#define TraceExposeEvent(event)	/* nothing */
4073367019cSmrg#endif
4083367019cSmrg
4093367019cSmrg/* true if p contains q */
4103367019cSmrg#define ExposeContains(p,q) \
4113367019cSmrg	    ((p)->y <= (q)->y \
4123367019cSmrg	  && (p)->x <= (q)->x \
4133367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
4143367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
4153367019cSmrg
4163367019cSmrgstatic XtInputMask
4179a64e1c5SmrgmergeExposeEvents(XEvent *target)
4183367019cSmrg{
4193367019cSmrg    XEvent next_event;
420037a25ddSmrg    XExposeEvent *p;
4213367019cSmrg
4223367019cSmrg    TRACE(("pending Expose...?\n"));
4233367019cSmrg    TraceExposeEvent(target);
4243367019cSmrg    XtAppNextEvent(app_con, target);
4253367019cSmrg    p = (XExposeEvent *) target;
4263367019cSmrg
4273367019cSmrg    while (XtAppPending(app_con)
4283367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4293367019cSmrg	   && next_event.type == Expose) {
4303367019cSmrg	Boolean merge_this = False;
431037a25ddSmrg	XExposeEvent *q;
4323367019cSmrg
4333367019cSmrg	TraceExposeEvent(&next_event);
4343367019cSmrg	q = (XExposeEvent *) (&next_event);
4353367019cSmrg	XtAppNextEvent(app_con, &next_event);
4363367019cSmrg
4373367019cSmrg	/*
4383367019cSmrg	 * If either window is contained within the other, merge the events.
4393367019cSmrg	 * The traces show that there are also cases where a full repaint of
4403367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4413367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4423367019cSmrg	 * to skim several events ahead.
4433367019cSmrg	 */
4443367019cSmrg	if (p->window == q->window) {
4453367019cSmrg	    if (ExposeContains(p, q)) {
4463367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4473367019cSmrg		merge_this = True;
4483367019cSmrg		next_event = *target;
4493367019cSmrg	    } else if (ExposeContains(q, p)) {
4503367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4513367019cSmrg		merge_this = True;
4523367019cSmrg	    }
4533367019cSmrg	}
4543367019cSmrg	if (!merge_this) {
4553367019cSmrg	    XtDispatchEvent(target);
4563367019cSmrg	}
4573367019cSmrg	*target = next_event;
4583367019cSmrg    }
4593367019cSmrg    XtDispatchEvent(target);
4603367019cSmrg    return XtAppPending(app_con);
4613367019cSmrg}
4623367019cSmrg
4633367019cSmrg#if OPT_TRACE
4643367019cSmrgstatic void
4659a64e1c5SmrgTraceConfigureEvent(XEvent *arg)
4663367019cSmrg{
4673367019cSmrg    XConfigureEvent *event = (XConfigureEvent *) arg;
4683367019cSmrg
4693367019cSmrg    TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n",
4703367019cSmrg	   event->serial,
4713367019cSmrg	   event->y,
4723367019cSmrg	   event->x,
4733367019cSmrg	   event->height,
4743367019cSmrg	   event->width,
4753367019cSmrg	   event->window));
4763367019cSmrg}
4773367019cSmrg
4783367019cSmrg#else
4793367019cSmrg#define TraceConfigureEvent(event)	/* nothing */
4803367019cSmrg#endif
4813367019cSmrg
4823367019cSmrg/*
4833367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4843367019cSmrg * event.  Remove that from the queue so we can look further.
4853367019cSmrg *
4863367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4873367019cSmrg * that.  If the adjacent events are for different windows, process the older
4883367019cSmrg * event and update the event used for comparing windows.  If they are for the
4893367019cSmrg * same window, only the newer event is of interest.
4903367019cSmrg *
4913367019cSmrg * Finally, process the (remaining) configure-notify event.
4923367019cSmrg */
4933367019cSmrgstatic XtInputMask
4949a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4953367019cSmrg{
4963367019cSmrg    XEvent next_event;
497037a25ddSmrg    XConfigureEvent *p;
4983367019cSmrg
4993367019cSmrg    XtAppNextEvent(app_con, target);
5003367019cSmrg    p = (XConfigureEvent *) target;
5013367019cSmrg
5023367019cSmrg    TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no"));
5033367019cSmrg    TraceConfigureEvent(target);
5043367019cSmrg
5053367019cSmrg    if (XtAppPending(app_con)
5063367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
5073367019cSmrg	&& next_event.type == ConfigureNotify) {
5083367019cSmrg	Boolean merge_this = False;
509037a25ddSmrg	XConfigureEvent *q;
5103367019cSmrg
5113367019cSmrg	TraceConfigureEvent(&next_event);
5123367019cSmrg	XtAppNextEvent(app_con, &next_event);
5133367019cSmrg	q = (XConfigureEvent *) (&next_event);
5143367019cSmrg
5153367019cSmrg	if (p->window == q->window) {
5163367019cSmrg	    TRACE(("pending Configure...merged\n"));
5173367019cSmrg	    merge_this = True;
5183367019cSmrg	}
5193367019cSmrg	if (!merge_this) {
5203367019cSmrg	    TRACE(("pending Configure...skipped\n"));
5213367019cSmrg	    XtDispatchEvent(target);
5223367019cSmrg	}
5233367019cSmrg	*target = next_event;
5243367019cSmrg    }
5253367019cSmrg    XtDispatchEvent(target);
5263367019cSmrg    return XtAppPending(app_con);
5273367019cSmrg}
5283367019cSmrg
5293367019cSmrg/*
5303367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5313367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5323367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5333367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5343367019cSmrg * point.
5353367019cSmrg *
5363367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5373367019cSmrg */
5383367019cSmrgXtInputMask
5393367019cSmrgxtermAppPending(void)
5403367019cSmrg{
5413367019cSmrg    XtInputMask result = XtAppPending(app_con);
5423367019cSmrg    XEvent this_event;
543037a25ddSmrg    Boolean found = False;
5443367019cSmrg
5453367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
546037a25ddSmrg	found = True;
5473367019cSmrg	if (this_event.type == Expose) {
5483367019cSmrg	    result = mergeExposeEvents(&this_event);
549fa3f02f3Smrg	    TRACE(("got merged expose events\n"));
5503367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5513367019cSmrg	    result = mergeConfigureEvents(&this_event);
552fa3f02f3Smrg	    TRACE(("got merged configure notify events\n"));
5533367019cSmrg	} else {
5543367019cSmrg	    TRACE(("pending %s\n", visibleEventType(this_event.type)));
5553367019cSmrg	    break;
5563367019cSmrg	}
5573367019cSmrg    }
558037a25ddSmrg
559037a25ddSmrg    /*
560037a25ddSmrg     * With NetBSD, closing a shell results in closing the X input event
561037a25ddSmrg     * stream, which interferes with the "-hold" option.  Wait a short time in
562037a25ddSmrg     * this case, to avoid max'ing the CPU.
563037a25ddSmrg     */
564037a25ddSmrg    if (hold_screen && caught_intr && !found) {
565037a25ddSmrg	Sleep(10);
566037a25ddSmrg    }
5673367019cSmrg    return result;
5683367019cSmrg}
5693367019cSmrg
570d522f475Smrgvoid
571d522f475Smrgxevents(void)
572d522f475Smrg{
573d522f475Smrg    XtermWidget xw = term;
574d522f475Smrg    TScreen *screen = TScreenOf(xw);
575d522f475Smrg    XEvent event;
576d522f475Smrg    XtInputMask input_mask;
577d522f475Smrg
578d522f475Smrg    if (need_cleanup)
5793367019cSmrg	NormalExit();
580d522f475Smrg
581d522f475Smrg    if (screen->scroll_amt)
582d522f475Smrg	FlushScroll(xw);
583d522f475Smrg    /*
584d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
585d522f475Smrg     * will process the timeout and return without blockng on the
586cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
587d522f475Smrg     * with select().
588d522f475Smrg     */
5893367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
590cd3331d0Smrg	if (input_mask & XtIMTimer)
591cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
592d522f475Smrg#if OPT_SESSION_MGT
593cd3331d0Smrg	/*
594cd3331d0Smrg	 * Session management events are alternative input events. Deal with
595cd3331d0Smrg	 * them in the same way.
596cd3331d0Smrg	 */
597cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
598cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
599d522f475Smrg#endif
600cd3331d0Smrg	else
601cd3331d0Smrg	    break;
602cd3331d0Smrg    }
603d522f475Smrg
604d522f475Smrg    /*
605d522f475Smrg     * If there's no XEvents, don't wait around...
606d522f475Smrg     */
607d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
608d522f475Smrg	return;
609d522f475Smrg    do {
610d522f475Smrg	/*
611d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
612d522f475Smrg	 * We simply ignore all events except for those not passed down to
613d522f475Smrg	 * this function, e.g., those handled in in_put().
614d522f475Smrg	 */
615d522f475Smrg	if (screen->waitingForTrackInfo) {
616d522f475Smrg	    Sleep(10);
617d522f475Smrg	    return;
618d522f475Smrg	}
619d522f475Smrg	XtAppNextEvent(app_con, &event);
620d522f475Smrg	/*
621d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
622d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
623d522f475Smrg	 * looking at the event ourselves we make sure that we can
624d522f475Smrg	 * do the right thing.
625d522f475Smrg	 */
626d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
627d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
628d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
629d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
630d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
631d522f475Smrg#if OPT_DEC_LOCATOR
632d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
633d522f475Smrg#endif /* OPT_DEC_LOCATOR */
634d522f475Smrg		   )
635d522f475Smrg		   && event.xany.type == MotionNotify
636d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
637d522f475Smrg	    SendMousePosition(xw, &event);
638cb4a1343Smrg	    xtermShowPointer(xw, True);
639d522f475Smrg	    continue;
640d522f475Smrg	}
641d522f475Smrg
642cb4a1343Smrg	/*
643cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
644cb4a1343Smrg	 * mouse pointer back on.
645cb4a1343Smrg	 */
646cb4a1343Smrg	if (screen->hide_pointer) {
6473367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6483367019cSmrg		switch (event.xany.type) {
6493367019cSmrg		case MotionNotify:
6503367019cSmrg		    xtermShowPointer(xw, True);
6513367019cSmrg		    break;
6523367019cSmrg		}
6533367019cSmrg	    } else {
6543367019cSmrg		switch (event.xany.type) {
6553367019cSmrg		case KeyPress:
6563367019cSmrg		case KeyRelease:
6573367019cSmrg		case ButtonPress:
6583367019cSmrg		case ButtonRelease:
6593367019cSmrg		    /* also these... */
6603367019cSmrg		case Expose:
661037a25ddSmrg		case GraphicsExpose:
6623367019cSmrg		case NoExpose:
6633367019cSmrg		case PropertyNotify:
6643367019cSmrg		case ClientMessage:
6653367019cSmrg		    break;
6663367019cSmrg		default:
6673367019cSmrg		    xtermShowPointer(xw, True);
6683367019cSmrg		    break;
6693367019cSmrg		}
670cb4a1343Smrg	    }
671cb4a1343Smrg	}
672cb4a1343Smrg
673d522f475Smrg	if (!event.xany.send_event ||
674d522f475Smrg	    screen->allowSendEvents ||
675d522f475Smrg	    ((event.xany.type != KeyPress) &&
676d522f475Smrg	     (event.xany.type != KeyRelease) &&
677d522f475Smrg	     (event.xany.type != ButtonPress) &&
678d522f475Smrg	     (event.xany.type != ButtonRelease))) {
679d522f475Smrg
680d522f475Smrg	    XtDispatchEvent(&event);
681d522f475Smrg	}
6823367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
683d522f475Smrg}
684d522f475Smrg
685d522f475Smrgstatic Cursor
686d522f475Smrgmake_hidden_cursor(XtermWidget xw)
687d522f475Smrg{
688d522f475Smrg    TScreen *screen = TScreenOf(xw);
689d522f475Smrg    Cursor c;
690d522f475Smrg    Display *dpy = screen->display;
691d522f475Smrg    XFontStruct *fn;
692d522f475Smrg
693d522f475Smrg    static XColor dummy;
694d522f475Smrg
695d522f475Smrg    /*
696d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
697d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
698b7c89284Ssnj     * server insists on drawing _something_.
699d522f475Smrg     */
700d522f475Smrg    TRACE(("Ask for nil2 font\n"));
701d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
702d522f475Smrg	TRACE(("...Ask for fixed font\n"));
703b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
704d522f475Smrg    }
705d522f475Smrg
706d522f475Smrg    if (fn != 0) {
707d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
708d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
709d522f475Smrg	XFreeFont(dpy, fn);
710d522f475Smrg    } else {
711d522f475Smrg	c = 0;
712d522f475Smrg    }
713d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
714d522f475Smrg    return (c);
715d522f475Smrg}
716d522f475Smrg
717fa3f02f3Smrg/*
718fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
719fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
720fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
721fa3f02f3Smrg * until the window is initialized.
722fa3f02f3Smrg */
723fa3f02f3Smrgvoid
724037a25ddSmrginit_colored_cursor(Display *dpy)
725fa3f02f3Smrg{
726fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
72794644356Smrg    static const char theme[] = "index.theme";
72894644356Smrg    static const char pattern[] = "xtermXXXXXX";
729fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
730fa3f02f3Smrg
731fa3f02f3Smrg    xterm_cursor_theme = 0;
732037a25ddSmrg    /*
733037a25ddSmrg     * The environment variable overrides a (possible) resource Xcursor.theme
734037a25ddSmrg     */
735fa3f02f3Smrg    if (IsEmpty(env)) {
736037a25ddSmrg	env = XGetDefault(dpy, "Xcursor", "theme");
737037a25ddSmrg    }
738037a25ddSmrg    /*
739037a25ddSmrg     * If neither found, provide our own default theme.
740037a25ddSmrg     */
741037a25ddSmrg    if (IsEmpty(env)) {
742037a25ddSmrg	const char *tmp_dir;
743037a25ddSmrg	char *filename;
744037a25ddSmrg	size_t needed;
745037a25ddSmrg
746fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
747fa3f02f3Smrg	    tmp_dir = P_tmpdir;
748fa3f02f3Smrg	}
749fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
750fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
751fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
752fa3f02f3Smrg
753fa3f02f3Smrg#ifdef HAVE_MKDTEMP
754fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
755fa3f02f3Smrg#else
756fa3f02f3Smrg	    if (mktemp(filename) != 0
757fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
758fa3f02f3Smrg		xterm_cursor_theme = filename;
759fa3f02f3Smrg	    }
760fa3f02f3Smrg#endif
761fa3f02f3Smrg	    /*
762fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
763fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
764fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
765fa3f02f3Smrg	     */
766fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
767fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
768037a25ddSmrg		FILE *fp;
769037a25ddSmrg
770fa3f02f3Smrg		strcat(leaf, "/");
771fa3f02f3Smrg		strcat(leaf, theme);
772fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
773fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
774fa3f02f3Smrg		    fclose(fp);
775fa3f02f3Smrg		    *leaf = '\0';
776fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
777fa3f02f3Smrg		    *leaf = '/';
778fa3f02f3Smrg		}
779fa3f02f3Smrg		atexit(cleanup_colored_cursor);
780fa3f02f3Smrg	    }
781fa3f02f3Smrg	}
782fa3f02f3Smrg    }
783037a25ddSmrg#else
784037a25ddSmrg    (void) dpy;
785fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
786fa3f02f3Smrg}
787fa3f02f3Smrg
788fa3f02f3Smrg/*
789fa3f02f3Smrg * Once done, discard the file and directory holding it.
790fa3f02f3Smrg */
791fa3f02f3Smrgvoid
792fa3f02f3Smrgcleanup_colored_cursor(void)
793fa3f02f3Smrg{
794fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
795fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
796fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
797fa3f02f3Smrg	struct stat sb;
798fa3f02f3Smrg	if (!IsEmpty(my_path)
799fa3f02f3Smrg	    && stat(my_path, &sb) == 0
800fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
801fa3f02f3Smrg	    unlink(xterm_cursor_theme);
802fa3f02f3Smrg	    rmdir(my_path);
803fa3f02f3Smrg	    free(xterm_cursor_theme);
804fa3f02f3Smrg	    xterm_cursor_theme = 0;
805fa3f02f3Smrg	}
806fa3f02f3Smrg    }
807fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
808fa3f02f3Smrg}
809fa3f02f3Smrg
810d522f475SmrgCursor
811d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
812d522f475Smrg		    unsigned long fg,	/* pixel value */
813d522f475Smrg		    unsigned long bg)	/* pixel value */
814d522f475Smrg{
815d522f475Smrg    TScreen *screen = TScreenOf(term);
816d522f475Smrg    Cursor c;
817d522f475Smrg    Display *dpy = screen->display;
818d522f475Smrg
819d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
820d522f475Smrg    if (c != None) {
821d522f475Smrg	recolor_cursor(screen, c, fg, bg);
822d522f475Smrg    }
823d522f475Smrg    return (c);
824d522f475Smrg}
825d522f475Smrg
826d522f475Smrg/* ARGSUSED */
827d522f475Smrgvoid
828d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
8299a64e1c5Smrg		 XEvent *event,
830fa3f02f3Smrg		 String *params GCC_UNUSED,
831d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
832d522f475Smrg{
833cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
834cd3331d0Smrg    Input(term, &event->xkey, False);
835d522f475Smrg}
836d522f475Smrg
837d522f475Smrg/* ARGSUSED */
838d522f475Smrgvoid
839d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
8409a64e1c5Smrg			 XEvent *event,
841fa3f02f3Smrg			 String *params GCC_UNUSED,
842d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
843d522f475Smrg{
844cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
845cd3331d0Smrg    Input(term, &event->xkey, True);
846d522f475Smrg}
847d522f475Smrg
848d522f475Smrg/* ARGSUSED */
849d522f475Smrgvoid
850d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
8519a64e1c5Smrg		  XEvent *event GCC_UNUSED,
852fa3f02f3Smrg		  String *params,
853d522f475Smrg		  Cardinal *nparams)
854d522f475Smrg{
855d522f475Smrg
856d522f475Smrg    if (*nparams != 1)
857d522f475Smrg	return;
858d522f475Smrg
859d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
8600d92cbfdSchristos	const char *abcdef = "ABCDEF";
8610d92cbfdSchristos	const char *xxxxxx;
862cd3331d0Smrg	Char c;
863cd3331d0Smrg	UString p;
8640d92cbfdSchristos	unsigned value = 0;
8650d92cbfdSchristos
866cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
8670d92cbfdSchristos	     '\0'; p++) {
8680d92cbfdSchristos	    value *= 16;
869d522f475Smrg	    if (c >= '0' && c <= '9')
8700d92cbfdSchristos		value += (unsigned) (c - '0');
871fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
8720d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
873d522f475Smrg	    else
874d522f475Smrg		break;
875d522f475Smrg	}
8760d92cbfdSchristos	if (c == '\0') {
8770d92cbfdSchristos	    Char hexval[2];
8780d92cbfdSchristos	    hexval[0] = (Char) value;
8790d92cbfdSchristos	    hexval[1] = 0;
880b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
8810d92cbfdSchristos	}
882d522f475Smrg    } else {
883cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
884d522f475Smrg    }
885d522f475Smrg}
886d522f475Smrg
887d522f475Smrg#if OPT_EXEC_XTERM
888d522f475Smrg
889d522f475Smrg#ifndef PROCFS_ROOT
890d522f475Smrg#define PROCFS_ROOT "/proc"
891d522f475Smrg#endif
892d522f475Smrg
893037a25ddSmrg/*
894037a25ddSmrg * Determine the current working directory of the child so that we can
895037a25ddSmrg * spawn a new terminal in the same directory.
896037a25ddSmrg *
897037a25ddSmrg * If we cannot get the CWD of the child, just use our own.
898037a25ddSmrg */
899037a25ddSmrgchar *
900037a25ddSmrgProcGetCWD(pid_t pid)
901037a25ddSmrg{
902037a25ddSmrg    char *child_cwd = NULL;
903037a25ddSmrg
904037a25ddSmrg    if (pid) {
905037a25ddSmrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
906037a25ddSmrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
907037a25ddSmrg	child_cwd = Readlink(child_cwd_link);
908037a25ddSmrg    }
909037a25ddSmrg    return child_cwd;
910037a25ddSmrg}
911037a25ddSmrg
912d522f475Smrg/* ARGSUSED */
913d522f475Smrgvoid
914d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
9159a64e1c5Smrg		    XEvent *event GCC_UNUSED,
916fa3f02f3Smrg		    String *params,
917d522f475Smrg		    Cardinal *nparams)
918d522f475Smrg{
919cd3331d0Smrg    TScreen *screen = TScreenOf(term);
920d522f475Smrg    char *child_cwd = NULL;
921d522f475Smrg    char *child_exe;
922d522f475Smrg    pid_t pid;
923d522f475Smrg
924d522f475Smrg    /*
925d522f475Smrg     * Try to find the actual program which is running in the child process.
926d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
927d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
928d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
929d522f475Smrg     */
930d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
931d522f475Smrg    if (!child_exe) {
932cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
933cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
934d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
935d522f475Smrg	} else {
9363367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
937d522f475Smrg	}
938d522f475Smrg	if (child_exe == 0)
939d522f475Smrg	    return;
940d522f475Smrg    }
941d522f475Smrg
942037a25ddSmrg    child_cwd = ProcGetCWD(screen->pid);
943d522f475Smrg
944d522f475Smrg    /* The reaper will take care of cleaning up the child */
945d522f475Smrg    pid = fork();
946d522f475Smrg    if (pid == -1) {
9473367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
948d522f475Smrg    } else if (!pid) {
949d522f475Smrg	/* We are the child */
950d522f475Smrg	if (child_cwd) {
951cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
952d522f475Smrg	}
953d522f475Smrg
954d522f475Smrg	if (setuid(screen->uid) == -1
955d522f475Smrg	    || setgid(screen->gid) == -1) {
9563367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
957d522f475Smrg	} else {
9580d92cbfdSchristos	    unsigned myargc = *nparams + 1;
959d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
960d522f475Smrg
96194644356Smrg	    if (myargv != 0) {
96294644356Smrg		unsigned n = 0;
963d522f475Smrg
96494644356Smrg		myargv[n++] = child_exe;
965d522f475Smrg
96694644356Smrg		while (n < myargc) {
96794644356Smrg		    myargv[n++] = (char *) *params++;
96894644356Smrg		}
96994644356Smrg
97094644356Smrg		myargv[n] = 0;
97194644356Smrg		execv(child_exe, myargv);
97294644356Smrg	    }
973d522f475Smrg
974d522f475Smrg	    /* If we get here, we've failed */
9753367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
976d522f475Smrg	}
977d522f475Smrg	_exit(0);
978d522f475Smrg    }
9793367019cSmrg
9803367019cSmrg    /* We are the parent; clean up */
9813367019cSmrg    if (child_cwd)
9823367019cSmrg	free(child_cwd);
9833367019cSmrg    free(child_exe);
984d522f475Smrg}
985d522f475Smrg#endif /* OPT_EXEC_XTERM */
986d522f475Smrg
987d522f475Smrg/*
988d522f475Smrg * Rather than sending characters to the host, put them directly into our
989d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
990d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
991d522f475Smrg *
992d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
993d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
994d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
995d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
996d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
997d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
998d522f475Smrg */
999d522f475Smrg/* ARGSUSED */
1000d522f475Smrgvoid
1001d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
10029a64e1c5Smrg		XEvent *event GCC_UNUSED,
1003fa3f02f3Smrg		String *params,
1004d522f475Smrg		Cardinal *param_count)
1005d522f475Smrg{
1006d522f475Smrg    if (*param_count == 1) {
1007cd3331d0Smrg	const char *value = params[0];
1008b7c89284Ssnj	int need = (int) strlen(value);
1009cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
1010cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
1011d522f475Smrg
1012d522f475Smrg	if (have - used + need < BUF_SIZE) {
1013d522f475Smrg
1014cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
1015d522f475Smrg
1016d522f475Smrg	    TRACE(("Interpret %s\n", value));
1017d522f475Smrg	    VTbuffer->update++;
1018d522f475Smrg	}
1019d522f475Smrg    }
1020d522f475Smrg}
1021d522f475Smrg
1022d522f475Smrg/*ARGSUSED*/
1023d522f475Smrgvoid
1024d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
1025d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10269a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1027fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1028d522f475Smrg{
1029d522f475Smrg    /* NOP since we handled it above */
1030d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
1031cd3331d0Smrg    TRACE_FOCUS(w, event);
1032d522f475Smrg}
1033d522f475Smrg
1034d522f475Smrg/*ARGSUSED*/
1035d522f475Smrgvoid
1036d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
1037d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10389a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1039fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1040d522f475Smrg{
1041d522f475Smrg    /* NOP since we handled it above */
1042d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1043cd3331d0Smrg    TRACE_FOCUS(w, event);
1044d522f475Smrg}
1045d522f475Smrg
1046d522f475Smrg/*ARGSUSED*/
1047d522f475Smrgvoid
1048d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1049d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10509a64e1c5Smrg		  XEvent *ev,
1051fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1052d522f475Smrg{
1053d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1054d522f475Smrg    XtermWidget xw = term;
1055d522f475Smrg    TScreen *screen = TScreenOf(xw);
1056d522f475Smrg
10573367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1058d522f475Smrg	   visibleEventType(event->type),
10593367019cSmrg	   visibleNotifyMode(event->mode),
10603367019cSmrg	   visibleNotifyDetail(event->detail)));
1061cd3331d0Smrg    TRACE_FOCUS(xw, event);
1062d522f475Smrg
1063d522f475Smrg    if (screen->quiet_grab
1064d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1065c219fbebSmrg	/* EMPTY */ ;
1066d522f475Smrg    } else if (event->type == FocusIn) {
106794644356Smrg	if (event->detail != NotifyPointer) {
106894644356Smrg	    setXUrgency(xw, False);
106994644356Smrg	}
1070d522f475Smrg
1071d522f475Smrg	/*
1072d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1073d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1074d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1075d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1076d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1077d522f475Smrg	 * delivered to the obscured window.
1078d522f475Smrg	 */
1079d522f475Smrg	if (event->detail == NotifyNonlinear
1080d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
10813367019cSmrg	    unselectwindow(xw, INWINDOW);
1082d522f475Smrg	}
10833367019cSmrg	selectwindow(xw,
1084d522f475Smrg		     ((event->detail == NotifyPointer)
1085d522f475Smrg		      ? INWINDOW
1086d522f475Smrg		      : FOCUS));
1087d522f475Smrg	SendFocusButton(xw, event);
1088d522f475Smrg    } else {
1089d522f475Smrg#if OPT_FOCUS_EVENT
1090d522f475Smrg	if (event->type == FocusOut) {
1091d522f475Smrg	    SendFocusButton(xw, event);
1092d522f475Smrg	}
1093d522f475Smrg#endif
1094d522f475Smrg	/*
1095d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1096d522f475Smrg	 * ignore.
1097d522f475Smrg	 */
1098d522f475Smrg	if (event->mode != NotifyGrab) {
10993367019cSmrg	    unselectwindow(xw,
1100d522f475Smrg			   ((event->detail == NotifyPointer)
1101d522f475Smrg			    ? INWINDOW
1102d522f475Smrg			    : FOCUS));
1103d522f475Smrg	}
1104d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1105cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1106d522f475Smrg	    ReverseVideo(xw);
1107d522f475Smrg	    screen->grabbedKbd = False;
1108d522f475Smrg	    update_securekbd();
1109d522f475Smrg	}
1110d522f475Smrg    }
1111d522f475Smrg}
1112d522f475Smrg
1113d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1114d522f475Smrg
1115b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1116b7c89284Ssnjstatic Atom
1117b7c89284SsnjAtomBell(XtermWidget xw, int which)
1118b7c89284Ssnj{
1119b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1120b7c89284Ssnj    static struct {
1121b7c89284Ssnj	int value;
1122b7c89284Ssnj	const char *name;
1123b7c89284Ssnj    } table[] = {
1124b7c89284Ssnj	DATA(Info),
1125b7c89284Ssnj	    DATA(MarginBell),
1126b7c89284Ssnj	    DATA(MinorError),
1127b7c89284Ssnj	    DATA(TerminalBell)
1128b7c89284Ssnj    };
1129b7c89284Ssnj    Cardinal n;
1130b7c89284Ssnj    Atom result = None;
1131b7c89284Ssnj
1132b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1133b7c89284Ssnj	if (table[n].value == which) {
1134cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1135b7c89284Ssnj	    break;
1136b7c89284Ssnj	}
1137b7c89284Ssnj    }
1138b7c89284Ssnj    return result;
1139b7c89284Ssnj}
1140b7c89284Ssnj#endif
1141b7c89284Ssnj
1142d522f475Smrgvoid
1143b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1144d522f475Smrg{
1145b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1146b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1147b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1148cd3331d0Smrg#endif
1149cd3331d0Smrg
1150cd3331d0Smrg    switch (which) {
1151cd3331d0Smrg    case XkbBI_Info:
1152cd3331d0Smrg    case XkbBI_MinorError:
1153cd3331d0Smrg    case XkbBI_MajorError:
1154cd3331d0Smrg    case XkbBI_TerminalBell:
1155cd3331d0Smrg	switch (screen->warningVolume) {
1156cd3331d0Smrg	case bvOff:
1157cd3331d0Smrg	    percent = -100;
1158cd3331d0Smrg	    break;
1159cd3331d0Smrg	case bvLow:
1160cd3331d0Smrg	    break;
1161cd3331d0Smrg	case bvHigh:
1162cd3331d0Smrg	    percent = 100;
1163cd3331d0Smrg	    break;
1164cd3331d0Smrg	}
1165cd3331d0Smrg	break;
1166cd3331d0Smrg    case XkbBI_MarginBell:
1167cd3331d0Smrg	switch (screen->marginVolume) {
1168cd3331d0Smrg	case bvOff:
1169cd3331d0Smrg	    percent = -100;
1170cd3331d0Smrg	    break;
1171cd3331d0Smrg	case bvLow:
1172cd3331d0Smrg	    break;
1173cd3331d0Smrg	case bvHigh:
1174cd3331d0Smrg	    percent = 100;
1175cd3331d0Smrg	    break;
1176cd3331d0Smrg	}
1177cd3331d0Smrg	break;
1178cd3331d0Smrg    default:
1179cd3331d0Smrg	break;
1180cd3331d0Smrg    }
1181cd3331d0Smrg
1182cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1183b7c89284Ssnj    if (tony != None) {
1184c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1185b7c89284Ssnj    } else
1186b7c89284Ssnj#endif
1187b7c89284Ssnj	XBell(screen->display, percent);
1188b7c89284Ssnj}
1189b7c89284Ssnj
1190b7c89284Ssnjvoid
1191cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1192b7c89284Ssnj{
1193b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1194d522f475Smrg    struct timeval curtime;
1195d522f475Smrg
1196b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1197b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1198d522f475Smrg	return;
1199d522f475Smrg    }
1200d522f475Smrg
1201c219fbebSmrg    setXUrgency(xw, True);
1202d522f475Smrg
1203d522f475Smrg    /* has enough time gone by that we are allowed to ring
1204d522f475Smrg       the bell again? */
1205d522f475Smrg    if (screen->bellSuppressTime) {
1206037a25ddSmrg	long now_msecs;
1207037a25ddSmrg
1208d522f475Smrg	if (screen->bellInProgress) {
1209d522f475Smrg	    do_xevents();
1210d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1211d522f475Smrg		return;
1212d522f475Smrg	    }
1213d522f475Smrg	}
1214d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1215d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1216d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1217d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1218d522f475Smrg	    return;
1219d522f475Smrg	}
1220d522f475Smrg	lastBellTime = now_msecs;
1221d522f475Smrg    }
1222d522f475Smrg
1223d522f475Smrg    if (screen->visualbell) {
1224d522f475Smrg	VisualBell();
1225d522f475Smrg    } else {
1226b7c89284Ssnj	xtermBell(xw, which, percent);
1227d522f475Smrg    }
1228d522f475Smrg
1229d522f475Smrg    if (screen->poponbell)
1230c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1231d522f475Smrg
1232d522f475Smrg    if (screen->bellSuppressTime) {
1233d522f475Smrg	/* now we change a property and wait for the notify event to come
1234d522f475Smrg	   back.  If the server is suspending operations while the bell
1235d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1236d522f475Smrg	   know when the previous bell has finished */
1237d522f475Smrg	Widget w = CURRENT_EMU();
1238d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1239d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1240d522f475Smrg	screen->bellInProgress = True;
1241d522f475Smrg    }
1242d522f475Smrg}
1243d522f475Smrg
1244d522f475Smrg#define VB_DELAY screen->visualBellDelay
1245d522f475Smrg
1246d522f475Smrgstatic void
1247fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1248d522f475Smrg{
12493367019cSmrg    int y = 0;
12503367019cSmrg    int x = 0;
12513367019cSmrg
12523367019cSmrg    if (screen->flash_line) {
12533367019cSmrg	y = CursorY(screen, screen->cur_row);
12543367019cSmrg	height = (unsigned) FontHeight(screen);
12553367019cSmrg    }
12563367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1257d522f475Smrg    XFlush(screen->display);
1258d522f475Smrg    Sleep(VB_DELAY);
12593367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1260d522f475Smrg}
1261d522f475Smrg
1262d522f475Smrgvoid
1263d522f475SmrgVisualBell(void)
1264d522f475Smrg{
1265d522f475Smrg    TScreen *screen = TScreenOf(term);
1266d522f475Smrg
1267d522f475Smrg    if (VB_DELAY > 0) {
1268d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1269d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1270d522f475Smrg	XGCValues gcval;
1271d522f475Smrg	GC visualGC;
1272d522f475Smrg
1273d522f475Smrg	gcval.function = GXxor;
1274d522f475Smrg	gcval.foreground = xorPixel;
1275d522f475Smrg	visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval);
1276d522f475Smrg#if OPT_TEK4014
1277d522f475Smrg	if (TEK4014_ACTIVE(term)) {
1278cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1279d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1280d522f475Smrg			TFullWidth(tekscr),
1281d522f475Smrg			TFullHeight(tekscr));
1282d522f475Smrg	} else
1283d522f475Smrg#endif
1284d522f475Smrg	{
1285d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1286d522f475Smrg			FullWidth(screen),
1287d522f475Smrg			FullHeight(screen));
1288d522f475Smrg	}
1289d522f475Smrg	XtReleaseGC((Widget) term, visualGC);
1290d522f475Smrg    }
1291d522f475Smrg}
1292d522f475Smrg
1293d522f475Smrg/* ARGSUSED */
1294d522f475Smrgvoid
1295d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1296d522f475Smrg			 XtPointer data GCC_UNUSED,
12979a64e1c5Smrg			 XEvent *ev,
1298fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1299d522f475Smrg{
1300d522f475Smrg    TScreen *screen = TScreenOf(term);
1301d522f475Smrg
1302d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1303d522f475Smrg	screen->bellInProgress = False;
1304d522f475Smrg    }
1305d522f475Smrg}
1306d522f475Smrg
13073367019cSmrgvoid
13083367019cSmrgxtermWarning(const char *fmt,...)
13093367019cSmrg{
13103367019cSmrg    int save_err = errno;
13113367019cSmrg    va_list ap;
13123367019cSmrg
1313dfb07bc7Smrg    fflush(stdout);
1314fa3f02f3Smrg    TRACE(("xtermWarning fmt='%s'\n", fmt));
13153367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
13163367019cSmrg    va_start(ap, fmt);
13173367019cSmrg    vfprintf(stderr, fmt, ap);
13183367019cSmrg    (void) fflush(stderr);
13193367019cSmrg
13203367019cSmrg    va_end(ap);
13213367019cSmrg    errno = save_err;
13223367019cSmrg}
13233367019cSmrg
13243367019cSmrgvoid
13253367019cSmrgxtermPerror(const char *fmt,...)
13263367019cSmrg{
13273367019cSmrg    int save_err = errno;
13283367019cSmrg    char *msg = strerror(errno);
13293367019cSmrg    va_list ap;
13303367019cSmrg
1331dfb07bc7Smrg    fflush(stdout);
1332fa3f02f3Smrg    TRACE(("xtermPerror fmt='%s', msg='%s'\n", fmt, NonNull(msg)));
13333367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
13343367019cSmrg    va_start(ap, fmt);
13353367019cSmrg    vfprintf(stderr, fmt, ap);
13363367019cSmrg    fprintf(stderr, ": %s\n", msg);
13373367019cSmrg    (void) fflush(stderr);
13383367019cSmrg
13393367019cSmrg    va_end(ap);
13403367019cSmrg    errno = save_err;
13413367019cSmrg}
13423367019cSmrg
1343d522f475SmrgWindow
1344c219fbebSmrgWMFrameWindow(XtermWidget xw)
1345d522f475Smrg{
1346d522f475Smrg    Window win_root, win_current, *children;
1347d522f475Smrg    Window win_parent = 0;
1348d522f475Smrg    unsigned int nchildren;
1349d522f475Smrg
1350c219fbebSmrg    win_current = XtWindow(xw);
1351d522f475Smrg
1352d522f475Smrg    /* find the parent which is child of root */
1353d522f475Smrg    do {
1354d522f475Smrg	if (win_parent)
1355d522f475Smrg	    win_current = win_parent;
1356c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1357d522f475Smrg		   win_current,
1358d522f475Smrg		   &win_root,
1359d522f475Smrg		   &win_parent,
1360d522f475Smrg		   &children,
1361d522f475Smrg		   &nchildren);
1362d522f475Smrg	XFree(children);
1363d522f475Smrg    } while (win_root != win_parent);
1364d522f475Smrg
1365d522f475Smrg    return win_current;
1366d522f475Smrg}
1367d522f475Smrg
1368d522f475Smrg#if OPT_DABBREV
1369d522f475Smrg/*
1370d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1371d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1372d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1373d522f475Smrg * expansions and ignores one of them if they are identical.
1374d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1375d522f475Smrg */
1376d522f475Smrg
1377d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1378d522f475Smrg
1379d522f475Smrgstatic int
1380fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1381d522f475Smrg{
1382b7c89284Ssnj    int result = -1;
1383b7c89284Ssnj    int firstLine = -(screen->savedlines);
1384d522f475Smrg
1385b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1386b7c89284Ssnj    while (cell->row >= firstLine) {
1387b7c89284Ssnj	if (--(cell->col) >= 0) {
1388b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1389b7c89284Ssnj	    break;
1390b7c89284Ssnj	}
1391b7c89284Ssnj	if (--(cell->row) < firstLine)
1392b7c89284Ssnj	    break;		/* ...there is no previous line */
1393b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1394b7c89284Ssnj	cell->col = MaxCols(screen);
1395b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1396b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1397d522f475Smrg	    break;
1398b7c89284Ssnj	}
1399d522f475Smrg    }
1400b7c89284Ssnj    return result;
1401d522f475Smrg}
1402d522f475Smrg
1403d522f475Smrgstatic char *
14049a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1405d522f475Smrg{
14069a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1407d522f475Smrg    char *abword;
1408d522f475Smrg    int c;
14099a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1410b7c89284Ssnj    char *result = 0;
1411d522f475Smrg
1412b7c89284Ssnj    abword = ab_end;
1413d522f475Smrg    *abword = '\0';		/* end of string marker */
1414d522f475Smrg
1415b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1416b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
14179a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1418b7c89284Ssnj	    *(--abword) = (char) c;
1419d522f475Smrg    }
1420d522f475Smrg
1421b7c89284Ssnj    if (c >= 0) {
1422b7c89284Ssnj	result = abword;
1423b7c89284Ssnj    } else if (abword != ab_end) {
1424b7c89284Ssnj	result = abword;
1425b7c89284Ssnj    }
1426b7c89284Ssnj
1427b7c89284Ssnj    if (result != 0) {
1428b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1429b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1430b7c89284Ssnj	    ;			/* skip preceding spaces */
1431b7c89284Ssnj	}
1432b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1433b7c89284Ssnj    }
1434b7c89284Ssnj    return result;
1435d522f475Smrg}
1436d522f475Smrg
1437d522f475Smrgstatic int
14389a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1439d522f475Smrg{
14409a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1441d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1442d522f475Smrg
1443b7c89284Ssnj    static CELL cell;
1444d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1445d522f475Smrg    static unsigned int expansions;
1446d522f475Smrg
1447d522f475Smrg    char *expansion;
1448d522f475Smrg    size_t hint_len;
1449b7c89284Ssnj    int result = 0;
1450b7c89284Ssnj    LineData *ld;
1451d522f475Smrg
1452d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1453d522f475Smrg	expansions = 0;
1454b7c89284Ssnj	cell.col = screen->cur_col;
1455b7c89284Ssnj	cell.row = screen->cur_row;
1456b7c89284Ssnj
1457b7c89284Ssnj	if (dabbrev_hint != 0)
1458b7c89284Ssnj	    free(dabbrev_hint);
1459b7c89284Ssnj
14609a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1461b7c89284Ssnj
1462b7c89284Ssnj	    if (lastexpansion != 0)
1463b7c89284Ssnj		free(lastexpansion);
1464b7c89284Ssnj
1465b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1466b7c89284Ssnj
1467b7c89284Ssnj		/* make own copy */
1468b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1469b7c89284Ssnj		    screen->dabbrev_working = True;
1470b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1471b7c89284Ssnj		}
1472cd3331d0Smrg	    } else {
1473cd3331d0Smrg		return result;
1474b7c89284Ssnj	    }
1475cd3331d0Smrg	} else {
1476cd3331d0Smrg	    return result;
1477d522f475Smrg	}
1478b7c89284Ssnj	if (!screen->dabbrev_working) {
1479b7c89284Ssnj	    if (lastexpansion != 0) {
1480b7c89284Ssnj		free(lastexpansion);
1481b7c89284Ssnj		lastexpansion = 0;
1482b7c89284Ssnj	    }
1483b7c89284Ssnj	    return result;
1484b7c89284Ssnj	}
1485d522f475Smrg    }
1486d522f475Smrg
1487cd3331d0Smrg    if (dabbrev_hint == 0)
1488cd3331d0Smrg	return result;
1489cd3331d0Smrg
1490d522f475Smrg    hint_len = strlen(dabbrev_hint);
1491d522f475Smrg    for (;;) {
14929a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1493d522f475Smrg	    if (expansions >= 2) {
1494d522f475Smrg		expansions = 0;
1495b7c89284Ssnj		cell.col = screen->cur_col;
1496b7c89284Ssnj		cell.row = screen->cur_row;
1497d522f475Smrg		continue;
1498d522f475Smrg	    }
1499d522f475Smrg	    break;
1500d522f475Smrg	}
1501d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1502d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1503d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1504d522f475Smrg	    break;
1505d522f475Smrg    }
1506d522f475Smrg
1507b7c89284Ssnj    if (expansion != 0) {
1508037a25ddSmrg	Char *copybuffer;
1509037a25ddSmrg	size_t del_cnt = strlen(lastexpansion) - hint_len;
1510037a25ddSmrg	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1511b7c89284Ssnj
1512b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1513b7c89284Ssnj	    /* delete previous expansion */
1514b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1515b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1516b7c89284Ssnj		    expansion + hint_len,
1517b7c89284Ssnj		    strlen(expansion) - hint_len);
1518cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1519b7c89284Ssnj	    /* v_write() just reset our flag */
1520b7c89284Ssnj	    screen->dabbrev_working = True;
1521b7c89284Ssnj	    free(copybuffer);
1522b7c89284Ssnj
1523b7c89284Ssnj	    free(lastexpansion);
1524b7c89284Ssnj
1525b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1526b7c89284Ssnj		result = 1;
1527b7c89284Ssnj		expansions++;
1528b7c89284Ssnj	    }
1529b7c89284Ssnj	}
1530b7c89284Ssnj    }
1531b7c89284Ssnj
1532b7c89284Ssnj    return result;
1533d522f475Smrg}
1534d522f475Smrg
1535d522f475Smrg/*ARGSUSED*/
1536d522f475Smrgvoid
1537b7c89284SsnjHandleDabbrevExpand(Widget w,
15389a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1539fa3f02f3Smrg		    String *params GCC_UNUSED,
1540d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1541d522f475Smrg{
1542b7c89284Ssnj    XtermWidget xw;
1543b7c89284Ssnj
1544cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1545b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
15469a64e1c5Smrg	if (!dabbrev_expand(xw))
1547cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1548d522f475Smrg    }
1549d522f475Smrg}
1550d522f475Smrg#endif /* OPT_DABBREV */
1551d522f475Smrg
1552d522f475Smrg#if OPT_MAXIMIZE
1553d522f475Smrg/*ARGSUSED*/
1554d522f475Smrgvoid
1555b7c89284SsnjHandleDeIconify(Widget w,
15569a64e1c5Smrg		XEvent *event GCC_UNUSED,
1557fa3f02f3Smrg		String *params GCC_UNUSED,
1558d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1559d522f475Smrg{
1560b7c89284Ssnj    XtermWidget xw;
1561b7c89284Ssnj
1562b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1563b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1564c219fbebSmrg	XMapWindow(screen->display, VShellWindow(xw));
1565d522f475Smrg    }
1566d522f475Smrg}
1567d522f475Smrg
1568d522f475Smrg/*ARGSUSED*/
1569d522f475Smrgvoid
1570b7c89284SsnjHandleIconify(Widget w,
15719a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1572fa3f02f3Smrg	      String *params GCC_UNUSED,
1573d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1574d522f475Smrg{
1575b7c89284Ssnj    XtermWidget xw;
1576b7c89284Ssnj
1577b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1578b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1579d522f475Smrg	XIconifyWindow(screen->display,
1580c219fbebSmrg		       VShellWindow(xw),
1581d522f475Smrg		       DefaultScreen(screen->display));
1582d522f475Smrg    }
1583d522f475Smrg}
1584d522f475Smrg
1585d522f475Smrgint
1586c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1587d522f475Smrg{
1588c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1589d522f475Smrg    XSizeHints hints;
1590d522f475Smrg    long supp = 0;
1591d522f475Smrg    Window root_win;
1592d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1593d522f475Smrg    int root_y = -1;
1594d522f475Smrg    unsigned root_border;
1595d522f475Smrg    unsigned root_depth;
15963367019cSmrg    int code;
1597d522f475Smrg
1598d522f475Smrg    if (XGetGeometry(screen->display,
1599c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1600d522f475Smrg		     &root_win,
1601d522f475Smrg		     &root_x,
1602d522f475Smrg		     &root_y,
1603d522f475Smrg		     width,
1604d522f475Smrg		     height,
1605d522f475Smrg		     &root_border,
1606d522f475Smrg		     &root_depth)) {
1607d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1608d522f475Smrg	       root_x,
1609d522f475Smrg	       root_y,
1610d522f475Smrg	       *width,
1611d522f475Smrg	       *height,
1612d522f475Smrg	       root_border));
1613d522f475Smrg
1614d522f475Smrg	*width -= (root_border * 2);
1615d522f475Smrg	*height -= (root_border * 2);
1616d522f475Smrg
1617d522f475Smrg	hints.flags = PMaxSize;
1618d522f475Smrg	if (XGetWMNormalHints(screen->display,
1619c219fbebSmrg			      VShellWindow(xw),
1620d522f475Smrg			      &hints,
1621d522f475Smrg			      &supp)
1622d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1623d522f475Smrg
1624d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1625d522f475Smrg		   hints.max_width,
1626d522f475Smrg		   hints.max_height));
1627d522f475Smrg
1628d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1629b7c89284Ssnj		*width = (unsigned) hints.max_width;
1630d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1631b7c89284Ssnj		*height = (unsigned) hints.max_height;
1632d522f475Smrg	}
16333367019cSmrg	code = 1;
16343367019cSmrg    } else {
16353367019cSmrg	*width = 0;
16363367019cSmrg	*height = 0;
16373367019cSmrg	code = 0;
1638d522f475Smrg    }
16393367019cSmrg    return code;
1640d522f475Smrg}
1641d522f475Smrg
1642d522f475Smrgvoid
1643c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1644d522f475Smrg{
1645c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1646d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1647d522f475Smrg    unsigned root_width, root_height;
16483367019cSmrg    Boolean success = False;
1649d522f475Smrg
16503367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
16513367019cSmrg	   maximize,
16523367019cSmrg	   (maximize
16533367019cSmrg	    ? "maximize"
16543367019cSmrg	    : "restore")));
1655d522f475Smrg
16563367019cSmrg    /*
16573367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
16583367019cSmrg     * as well as the estimated root-window size.
16593367019cSmrg     */
16603367019cSmrg    if (maximize
16613367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
16623367019cSmrg	&& xtermGetWinAttrs(screen->display,
16633367019cSmrg			    WMFrameWindow(xw),
16643367019cSmrg			    &wm_attrs)
16653367019cSmrg	&& xtermGetWinAttrs(screen->display,
16663367019cSmrg			    VShellWindow(xw),
16673367019cSmrg			    &vshell_attrs)) {
16683367019cSmrg
16693367019cSmrg	if (screen->restore_data != True
16703367019cSmrg	    || screen->restore_width != root_width
16713367019cSmrg	    || screen->restore_height != root_height) {
16723367019cSmrg	    screen->restore_data = True;
16733367019cSmrg	    screen->restore_x = wm_attrs.x + wm_attrs.border_width;
16743367019cSmrg	    screen->restore_y = wm_attrs.y + wm_attrs.border_width;
16753367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
16763367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
16773367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1678d522f475Smrg		   screen->restore_x,
1679d522f475Smrg		   screen->restore_y,
1680d522f475Smrg		   screen->restore_width,
1681d522f475Smrg		   screen->restore_height));
16823367019cSmrg	}
1683d522f475Smrg
16843367019cSmrg	/* subtract wm decoration dimensions */
16853367019cSmrg	root_width -= (unsigned) ((wm_attrs.width - vshell_attrs.width)
16863367019cSmrg				  + (wm_attrs.border_width * 2));
16873367019cSmrg	root_height -= (unsigned) ((wm_attrs.height - vshell_attrs.height)
16883367019cSmrg				   + (wm_attrs.border_width * 2));
16893367019cSmrg	success = True;
16903367019cSmrg    } else if (screen->restore_data) {
16913367019cSmrg	success = True;
16923367019cSmrg	maximize = 0;
16933367019cSmrg    }
16943367019cSmrg
16953367019cSmrg    if (success) {
16963367019cSmrg	switch (maximize) {
16973367019cSmrg	case 3:
16983367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
16993367019cSmrg	    break;
17003367019cSmrg	case 2:
17013367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
17023367019cSmrg	    break;
17033367019cSmrg	case 1:
17043367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
17053367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
17063367019cSmrg			      0 + wm_attrs.border_width,	/* x */
17073367019cSmrg			      0 + wm_attrs.border_width,	/* y */
17083367019cSmrg			      root_width,
17093367019cSmrg			      root_height);
17103367019cSmrg	    break;
17113367019cSmrg
17123367019cSmrg	default:
17133367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
17143367019cSmrg	    if (screen->restore_data) {
17153367019cSmrg		screen->restore_data = False;
17163367019cSmrg
17173367019cSmrg		TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
17183367019cSmrg		       screen->restore_x,
17193367019cSmrg		       screen->restore_y,
17203367019cSmrg		       screen->restore_width,
17213367019cSmrg		       screen->restore_height));
17223367019cSmrg
17233367019cSmrg		XMoveResizeWindow(screen->display,
17243367019cSmrg				  VShellWindow(xw),
17253367019cSmrg				  screen->restore_x,
17263367019cSmrg				  screen->restore_y,
17273367019cSmrg				  screen->restore_width,
17283367019cSmrg				  screen->restore_height);
17293367019cSmrg	    }
17303367019cSmrg	    break;
1731d522f475Smrg	}
1732d522f475Smrg    }
1733d522f475Smrg}
1734d522f475Smrg
1735d522f475Smrg/*ARGSUSED*/
1736d522f475Smrgvoid
1737b7c89284SsnjHandleMaximize(Widget w,
17389a64e1c5Smrg	       XEvent *event GCC_UNUSED,
1739fa3f02f3Smrg	       String *params GCC_UNUSED,
1740d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1741d522f475Smrg{
1742b7c89284Ssnj    XtermWidget xw;
1743b7c89284Ssnj
1744b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1745b7c89284Ssnj	RequestMaximize(xw, 1);
1746d522f475Smrg    }
1747d522f475Smrg}
1748d522f475Smrg
1749d522f475Smrg/*ARGSUSED*/
1750d522f475Smrgvoid
1751b7c89284SsnjHandleRestoreSize(Widget w,
17529a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1753fa3f02f3Smrg		  String *params GCC_UNUSED,
1754d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1755d522f475Smrg{
1756b7c89284Ssnj    XtermWidget xw;
1757b7c89284Ssnj
1758b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1759b7c89284Ssnj	RequestMaximize(xw, 0);
1760d522f475Smrg    }
1761d522f475Smrg}
1762d522f475Smrg#endif /* OPT_MAXIMIZE */
1763d522f475Smrg
1764d522f475Smrgvoid
1765d522f475SmrgRedraw(void)
1766d522f475Smrg{
1767d522f475Smrg    TScreen *screen = TScreenOf(term);
1768d522f475Smrg    XExposeEvent event;
1769d522f475Smrg
1770d522f475Smrg    TRACE(("Redraw\n"));
1771d522f475Smrg
1772d522f475Smrg    event.type = Expose;
1773d522f475Smrg    event.display = screen->display;
1774d522f475Smrg    event.x = 0;
1775d522f475Smrg    event.y = 0;
1776d522f475Smrg    event.count = 0;
1777d522f475Smrg
1778d522f475Smrg    if (VWindow(screen)) {
1779d522f475Smrg	event.window = VWindow(screen);
1780d522f475Smrg	event.width = term->core.width;
1781d522f475Smrg	event.height = term->core.height;
1782d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
17839a64e1c5Smrg						       (XEvent *) &event,
1784d522f475Smrg						       NULL);
1785d522f475Smrg	if (ScrollbarWidth(screen)) {
1786d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
17879a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
1788d522f475Smrg	}
1789d522f475Smrg    }
1790d522f475Smrg#if OPT_TEK4014
1791d522f475Smrg    if (TEK4014_SHOWN(term)) {
1792cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1793d522f475Smrg	event.window = TWindow(tekscr);
1794d522f475Smrg	event.width = tekWidget->core.width;
1795d522f475Smrg	event.height = tekWidget->core.height;
17969a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
1797d522f475Smrg    }
1798d522f475Smrg#endif
1799d522f475Smrg}
1800d522f475Smrg
1801d522f475Smrg#ifdef VMS
1802d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1803d522f475Smrg#else
1804d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1805d522f475Smrg#endif
1806d522f475Smrg
1807d522f475Smrgvoid
1808d522f475Smrgtimestamp_filename(char *dst, const char *src)
1809d522f475Smrg{
1810d522f475Smrg    time_t tstamp;
1811d522f475Smrg    struct tm *tstruct;
1812d522f475Smrg
1813d522f475Smrg    tstamp = time((time_t *) 0);
1814d522f475Smrg    tstruct = localtime(&tstamp);
1815d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1816d522f475Smrg	    src,
18173367019cSmrg	    (int) tstruct->tm_year + 1900,
1818d522f475Smrg	    tstruct->tm_mon + 1,
1819d522f475Smrg	    tstruct->tm_mday,
1820d522f475Smrg	    tstruct->tm_hour,
1821d522f475Smrg	    tstruct->tm_min,
1822d522f475Smrg	    tstruct->tm_sec);
1823d522f475Smrg}
1824d522f475Smrg
1825d522f475Smrgint
1826d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1827d522f475Smrg{
1828d522f475Smrg    int fd;
1829d522f475Smrg    struct stat sb;
1830d522f475Smrg
1831d522f475Smrg#ifdef VMS
1832d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1833d522f475Smrg	int the_error = errno;
18343367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
18353367019cSmrg		     path,
18363367019cSmrg		     the_error,
18373367019cSmrg		     SysErrorMsg(the_error));
1838d522f475Smrg	return -1;
1839d522f475Smrg    }
1840d522f475Smrg    chown(path, uid, gid);
1841d522f475Smrg#else
1842d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1843d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1844d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1845d522f475Smrg	int the_error = errno;
18463367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
18473367019cSmrg		     path,
18483367019cSmrg		     the_error,
18493367019cSmrg		     SysErrorMsg(the_error));
1850d522f475Smrg	return -1;
1851d522f475Smrg    }
1852d522f475Smrg#endif
1853d522f475Smrg
1854d522f475Smrg    /*
1855d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1856d522f475Smrg     * we do any damage, and that it is not world-writable.
1857d522f475Smrg     */
1858d522f475Smrg    if (fstat(fd, &sb) < 0
1859d522f475Smrg	|| sb.st_uid != uid
1860d522f475Smrg	|| (sb.st_mode & 022) != 0) {
18613367019cSmrg	xtermWarning("you do not own %s\n", path);
1862d522f475Smrg	close(fd);
1863d522f475Smrg	return -1;
1864d522f475Smrg    }
1865d522f475Smrg    return fd;
1866d522f475Smrg}
1867d522f475Smrg
1868d522f475Smrg#ifndef VMS
1869d522f475Smrg/*
1870d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1871d522f475Smrg * We could emulate this with careful use of access() and following
1872d522f475Smrg * symbolic links, but that is messy and has race conditions.
1873d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1874d522f475Smrg * being available.
1875d522f475Smrg *
1876d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1877d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1878d522f475Smrg * for the debug logs.
1879d522f475Smrg *
1880d522f475Smrg * Returns
1881d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1882d522f475Smrg *	-1 on error, e.g., cannot fork
1883d522f475Smrg *	 0 otherwise.
1884d522f475Smrg */
1885d522f475Smrgint
1886712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
1887d522f475Smrg{
1888d522f475Smrg    int fd;
1889d522f475Smrg    pid_t pid;
1890d522f475Smrg    int retval = 0;
1891d522f475Smrg    int childstat = 0;
1892d522f475Smrg#ifndef HAVE_WAITPID
1893d522f475Smrg    int waited;
18943367019cSmrg    void (*chldfunc) (int);
1895d522f475Smrg
1896d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1897d522f475Smrg#endif /* HAVE_WAITPID */
1898d522f475Smrg
1899d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1900d522f475Smrg	   (int) uid, (int) geteuid(),
1901d522f475Smrg	   (int) gid, (int) getegid(),
1902d522f475Smrg	   append,
1903d522f475Smrg	   pathname,
1904d522f475Smrg	   mode));
1905d522f475Smrg
1906d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1907d522f475Smrg	fd = open(pathname,
1908d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1909d522f475Smrg		  mode);
1910d522f475Smrg	if (fd >= 0)
1911d522f475Smrg	    close(fd);
1912d522f475Smrg	return (fd >= 0);
1913d522f475Smrg    }
1914d522f475Smrg
1915d522f475Smrg    pid = fork();
1916d522f475Smrg    switch (pid) {
1917d522f475Smrg    case 0:			/* child */
1918d522f475Smrg	if (setgid(gid) == -1
1919d522f475Smrg	    || setuid(uid) == -1) {
1920d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1921d522f475Smrg	    retval = 1;
1922d522f475Smrg	} else {
1923d522f475Smrg	    fd = open(pathname,
1924d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1925d522f475Smrg		      mode);
1926d522f475Smrg	    if (fd >= 0) {
1927d522f475Smrg		close(fd);
1928d522f475Smrg		retval = 0;
1929d522f475Smrg	    } else {
1930d522f475Smrg		retval = 1;
1931d522f475Smrg	    }
1932d522f475Smrg	}
1933d522f475Smrg	_exit(retval);
1934d522f475Smrg	/* NOTREACHED */
1935d522f475Smrg    case -1:			/* error */
1936d522f475Smrg	return retval;
1937d522f475Smrg    default:			/* parent */
1938d522f475Smrg#ifdef HAVE_WAITPID
1939d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1940d522f475Smrg#ifdef EINTR
1941d522f475Smrg	    if (errno == EINTR)
1942d522f475Smrg		continue;
1943d522f475Smrg#endif /* EINTR */
1944d522f475Smrg#ifdef ERESTARTSYS
1945d522f475Smrg	    if (errno == ERESTARTSYS)
1946d522f475Smrg		continue;
1947d522f475Smrg#endif /* ERESTARTSYS */
1948d522f475Smrg	    break;
1949d522f475Smrg	}
1950d522f475Smrg#else /* HAVE_WAITPID */
1951d522f475Smrg	waited = wait(&childstat);
1952d522f475Smrg	signal(SIGCHLD, chldfunc);
1953d522f475Smrg	/*
1954d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1955d522f475Smrg	   we might have missed the termination of our screen child.
1956d522f475Smrg	   If we can check for this possibility without hanging, do so.
1957d522f475Smrg	 */
1958d522f475Smrg	do
1959cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
19603367019cSmrg		NormalExit();
1961d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1962d522f475Smrg#endif /* HAVE_WAITPID */
1963d522f475Smrg#ifndef WIFEXITED
1964d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1965d522f475Smrg#endif
1966d522f475Smrg	if (WIFEXITED(childstat))
1967d522f475Smrg	    retval = 1;
1968d522f475Smrg	return retval;
1969d522f475Smrg    }
1970d522f475Smrg}
1971d522f475Smrg#endif /* !VMS */
1972d522f475Smrg
1973d522f475Smrgint
1974fa3f02f3SmrgxtermResetIds(TScreen *screen)
1975d522f475Smrg{
1976d522f475Smrg    int result = 0;
1977d522f475Smrg    if (setgid(screen->gid) == -1) {
19783367019cSmrg	xtermWarning("unable to reset group-id\n");
1979d522f475Smrg	result = -1;
1980d522f475Smrg    }
1981d522f475Smrg    if (setuid(screen->uid) == -1) {
19823367019cSmrg	xtermWarning("unable to reset user-id\n");
1983d522f475Smrg	result = -1;
1984d522f475Smrg    }
1985d522f475Smrg    return result;
1986d522f475Smrg}
1987d522f475Smrg
1988d522f475Smrg#ifdef ALLOWLOGGING
1989d522f475Smrg
1990d522f475Smrg/*
1991d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1992d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1993d522f475Smrg */
1994d522f475Smrg
1995d522f475Smrg#ifdef ALLOWLOGFILEEXEC
19963367019cSmrgstatic void
1997d522f475Smrglogpipe(int sig GCC_UNUSED)
1998d522f475Smrg{
1999cd3331d0Smrg    XtermWidget xw = term;
2000cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2001d522f475Smrg
20023367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2003d522f475Smrg#ifdef SYSV
2004d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2005d522f475Smrg#endif /* SYSV */
2006d522f475Smrg    if (screen->logging)
2007cd3331d0Smrg	CloseLog(xw);
2008d522f475Smrg}
2009d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2010d522f475Smrg
2011d522f475Smrgvoid
2012cd3331d0SmrgStartLog(XtermWidget xw)
2013d522f475Smrg{
2014d522f475Smrg    static char *log_default;
2015cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2016d522f475Smrg
2017d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2018d522f475Smrg	return;
2019d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
2020d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
2021d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2022d522f475Smrg			 0640);
2023d522f475Smrg    if (screen->logfd < 0)
2024d522f475Smrg	return;			/* open failed */
2025d522f475Smrg#else /*VMS */
2026d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
2027d522f475Smrg	if (screen->logfile)
2028d522f475Smrg	    free(screen->logfile);
2029d522f475Smrg	if (log_default == NULL) {
2030d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2031037a25ddSmrg	    const char form[] = "Xterm.log.%s%s.%d";
2032037a25ddSmrg	    char where[255 + 1];	/* Internet standard limit (RFC 1035):
2033d522f475Smrg					   ``To simplify implementations, the
2034d522f475Smrg					   total length of a domain name (i.e.,
2035d522f475Smrg					   label octets and label length
2036d522f475Smrg					   octets) is restricted to 255 octets
2037d522f475Smrg					   or less.'' */
2038037a25ddSmrg	    char when[LEN_TIMESTAMP];
2039037a25ddSmrg	    char formatted[sizeof(form) + sizeof(where) + sizeof(when) + 9];
2040d522f475Smrg	    time_t now;
2041d522f475Smrg	    struct tm *ltm;
2042d522f475Smrg
2043d522f475Smrg	    now = time((time_t *) 0);
2044d522f475Smrg	    ltm = (struct tm *) localtime(&now);
2045037a25ddSmrg	    if ((gethostname(where, sizeof(where)) == 0) &&
2046037a25ddSmrg		(strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0)) {
2047037a25ddSmrg		(void) sprintf(formatted, form, where, when, (int) getpid());
2048037a25ddSmrg	    } else {
2049037a25ddSmrg		return;
2050d522f475Smrg	    }
2051037a25ddSmrg	    if ((log_default = x_strdup(formatted)) == NULL) {
2052d522f475Smrg		return;
2053037a25ddSmrg	    }
2054d522f475Smrg#else
205594644356Smrg	    static const char log_def_name[] = "XtermLog.XXXXXX";
2056037a25ddSmrg	    if ((log_default = x_strdup(log_def_name)) == NULL) {
2057d522f475Smrg		return;
2058037a25ddSmrg	    }
2059d522f475Smrg	    mktemp(log_default);
2060d522f475Smrg#endif
2061d522f475Smrg	}
2062d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
2063d522f475Smrg	    return;
2064d522f475Smrg    }
2065d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
2066d522f475Smrg#ifdef ALLOWLOGFILEEXEC
2067d522f475Smrg	/*
2068d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
2069d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2070d522f475Smrg	 * done through escape sequences....  You have been warned.
2071d522f475Smrg	 */
2072d522f475Smrg	int pid;
2073d522f475Smrg	int p[2];
2074d522f475Smrg	static char *shell;
20753367019cSmrg	struct passwd pw;
20763367019cSmrg
20773367019cSmrg	if ((shell = x_getenv("SHELL")) == NULL) {
20783367019cSmrg
20793367019cSmrg	    if (x_getpwuid(screen->uid, &pw)) {
20803367019cSmrg		char *name = x_getlogin(screen->uid, &pw);
20813367019cSmrg		if (*(pw.pw_shell)) {
20823367019cSmrg		    shell = pw.pw_shell;
20833367019cSmrg		}
20843367019cSmrg		free(name);
20853367019cSmrg	    }
20863367019cSmrg	}
20873367019cSmrg
20883367019cSmrg	if (shell == 0) {
20893367019cSmrg	    static char dummy[] = "/bin/sh";
20903367019cSmrg	    shell = dummy;
20913367019cSmrg	}
20923367019cSmrg
20933367019cSmrg	if (access(shell, X_OK) != 0) {
20943367019cSmrg	    xtermPerror("Can't execute `%s'\n", shell);
20953367019cSmrg	    return;
20963367019cSmrg	}
2097d522f475Smrg
20983367019cSmrg	if (pipe(p) < 0) {
20993367019cSmrg	    xtermPerror("Can't make a pipe connection\n");
2100d522f475Smrg	    return;
21013367019cSmrg	} else if ((pid = fork()) < 0) {
21023367019cSmrg	    xtermPerror("Can't fork...\n");
21033367019cSmrg	    return;
21043367019cSmrg	}
2105d522f475Smrg	if (pid == 0) {		/* child */
2106d522f475Smrg	    /*
2107d522f475Smrg	     * Close our output (we won't be talking back to the
2108d522f475Smrg	     * parent), and redirect our child's output to the
2109d522f475Smrg	     * original stderr.
2110d522f475Smrg	     */
2111d522f475Smrg	    close(p[1]);
2112d522f475Smrg	    dup2(p[0], 0);
2113d522f475Smrg	    close(p[0]);
2114d522f475Smrg	    dup2(fileno(stderr), 1);
2115d522f475Smrg	    dup2(fileno(stderr), 2);
2116d522f475Smrg
2117d522f475Smrg	    close(fileno(stderr));
2118d522f475Smrg	    close(ConnectionNumber(screen->display));
2119d522f475Smrg	    close(screen->respond);
2120d522f475Smrg
2121d522f475Smrg	    signal(SIGHUP, SIG_DFL);
2122d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
2123d522f475Smrg
2124d522f475Smrg	    /* (this is redundant) */
2125d522f475Smrg	    if (xtermResetIds(screen) < 0)
2126d522f475Smrg		exit(ERROR_SETUID);
2127d522f475Smrg
21283367019cSmrg	    if (access(shell, X_OK) == 0) {
21293367019cSmrg		execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
21303367019cSmrg		xtermWarning("Can't exec `%s'\n", &screen->logfile[1]);
21313367019cSmrg	    } else {
21323367019cSmrg		xtermWarning("Can't execute `%s'\n", shell);
21333367019cSmrg	    }
2134d522f475Smrg	    exit(ERROR_LOGEXEC);
2135d522f475Smrg	}
2136d522f475Smrg	close(p[0]);
2137d522f475Smrg	screen->logfd = p[1];
2138d522f475Smrg	signal(SIGPIPE, logpipe);
2139d522f475Smrg#else
2140cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2141cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2142d522f475Smrg	return;
2143d522f475Smrg#endif
2144d522f475Smrg    } else {
2145d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2146d522f475Smrg					   screen->gid,
2147d522f475Smrg					   screen->logfile,
2148d522f475Smrg					   (log_default != 0))) < 0)
2149d522f475Smrg	    return;
2150d522f475Smrg    }
2151d522f475Smrg#endif /*VMS */
2152d522f475Smrg    screen->logstart = VTbuffer->next;
2153d522f475Smrg    screen->logging = True;
2154d522f475Smrg    update_logging();
2155d522f475Smrg}
2156d522f475Smrg
2157d522f475Smrgvoid
2158cd3331d0SmrgCloseLog(XtermWidget xw)
2159d522f475Smrg{
2160cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2161cd3331d0Smrg
2162d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2163d522f475Smrg	return;
2164cd3331d0Smrg    FlushLog(xw);
2165d522f475Smrg    close(screen->logfd);
2166d522f475Smrg    screen->logging = False;
2167d522f475Smrg    update_logging();
2168d522f475Smrg}
2169d522f475Smrg
2170d522f475Smrgvoid
2171cd3331d0SmrgFlushLog(XtermWidget xw)
2172d522f475Smrg{
2173cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2174cd3331d0Smrg
2175d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2176d522f475Smrg	Char *cp;
2177d522f475Smrg	int i;
2178d522f475Smrg
2179d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2180d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2181d522f475Smrg	if (!tt_new_output)
2182d522f475Smrg	    return;
2183d522f475Smrg	tt_new_output = False;
2184d522f475Smrg#endif /* VMS */
2185d522f475Smrg	cp = VTbuffer->next;
2186d522f475Smrg	if (screen->logstart != 0
2187cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2188cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2189d522f475Smrg	}
2190d522f475Smrg	screen->logstart = VTbuffer->next;
2191d522f475Smrg    }
2192d522f475Smrg}
2193d522f475Smrg
2194d522f475Smrg#endif /* ALLOWLOGGING */
2195d522f475Smrg
2196d522f475Smrg/***====================================================================***/
2197d522f475Smrg
2198fa3f02f3Smrgint
2199fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2200fa3f02f3Smrg{
2201fa3f02f3Smrg#define MYFMT "getVisualInfo \
2202fa3f02f3Smrgdepth %d, \
2203fa3f02f3Smrgtype %d (%s), \
2204fa3f02f3Smrgsize %d \
2205fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2206fa3f02f3Smrg#define MYARG \
2207fa3f02f3Smrg       vi->depth,\
2208fa3f02f3Smrg       vi->class,\
2209fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2210fa3f02f3Smrg       vi->colormap_size,\
2211fa3f02f3Smrg       vi->red_mask,\
2212fa3f02f3Smrg       vi->green_mask,\
2213fa3f02f3Smrg       vi->blue_mask
2214d522f475Smrg
2215fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2216fa3f02f3Smrg    Display *dpy = screen->display;
2217fa3f02f3Smrg    XVisualInfo myTemplate;
2218fa3f02f3Smrg
2219fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2220fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2221fa3f02f3Smrg								XDefaultScreen(dpy)));
2222fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2223fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2224fa3f02f3Smrg
2225fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2226fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2227fa3f02f3Smrg	    if (resource.reportColors) {
2228fa3f02f3Smrg		printf(MYFMT, MYARG);
2229fa3f02f3Smrg	    }
2230fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2231fa3f02f3Smrg	}
2232fa3f02f3Smrg    }
2233fa3f02f3Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0);
2234fa3f02f3Smrg#undef MYFMT
2235fa3f02f3Smrg#undef MYARG
2236fa3f02f3Smrg}
22373367019cSmrg
22389a64e1c5Smrg#if OPT_ISO_COLORS
22399a64e1c5Smrgstatic void
22409a64e1c5SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
22419a64e1c5Smrg{
22429a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
22439a64e1c5Smrg	XColor color;
22449a64e1c5Smrg	Colormap cmap = xw->core.colormap;
22459a64e1c5Smrg	char buffer[80];
22469a64e1c5Smrg
22479a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
22489a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
22499a64e1c5Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
22509a64e1c5Smrg	sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
22519a64e1c5Smrg		colornum,
22529a64e1c5Smrg		color.red,
22539a64e1c5Smrg		color.green,
22549a64e1c5Smrg		color.blue);
22559a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
22569a64e1c5Smrg	unparseputs(xw, buffer);
22579a64e1c5Smrg	unparseputc1(xw, final);
22589a64e1c5Smrg	unparse_end(xw);
22599a64e1c5Smrg    }
22609a64e1c5Smrg}
22619a64e1c5Smrg
2262fa3f02f3Smrgstatic void
2263fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2264fa3f02f3Smrg{
2265fa3f02f3Smrg    if (getVisualInfo(xw)) {
2266fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2267fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2268fa3f02f3Smrg    } else {
2269fa3f02f3Smrg	*typep = 0;
2270fa3f02f3Smrg	*sizep = 0;
2271fa3f02f3Smrg    }
22723367019cSmrg}
22733367019cSmrg
22743367019cSmrg#define MAX_COLORTABLE 4096
22753367019cSmrg
22763367019cSmrg/*
22773367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
22783367019cSmrg */
22793367019cSmrgstatic Boolean
22803367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
22813367019cSmrg{
22823367019cSmrg    Colormap cmap = xw->core.colormap;
22833367019cSmrg    TScreen *screen = TScreenOf(xw);
2284fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
22853367019cSmrg
2286fa3f02f3Smrg    if (!result
22873367019cSmrg	&& length != 0
22883367019cSmrg	&& length < MAX_COLORTABLE) {
22893367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2290037a25ddSmrg
22913367019cSmrg	if (screen->cmap_data != 0) {
2292037a25ddSmrg	    unsigned i;
2293037a25ddSmrg
22943367019cSmrg	    screen->cmap_size = length;
22953367019cSmrg
22963367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
22973367019cSmrg		screen->cmap_data[i].pixel = (unsigned long) i;
22983367019cSmrg	    }
22993367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
23003367019cSmrg					     cmap,
23013367019cSmrg					     screen->cmap_data,
23023367019cSmrg					     (int) screen->cmap_size) != 0);
23033367019cSmrg	}
23043367019cSmrg    }
2305d522f475Smrg    return result;
2306d522f475Smrg}
2307d522f475Smrg
2308d522f475Smrg/*
2309d522f475Smrg * Find closest color for "def" in "cmap".
2310d522f475Smrg * Set "def" to the resulting color.
2311d522f475Smrg *
2312d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2313d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2314d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2315d522f475Smrg *
2316d522f475Smrg * Return False if not able to find or allocate a color.
2317d522f475Smrg */
2318d522f475Smrgstatic Boolean
23199a64e1c5SmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def)
2320d522f475Smrg{
23213367019cSmrg    TScreen *screen = TScreenOf(xw);
2322d522f475Smrg    Boolean result = False;
23233367019cSmrg    unsigned cmap_type;
2324d522f475Smrg    unsigned cmap_size;
2325d522f475Smrg
2326fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2327d522f475Smrg
23283367019cSmrg    if ((cmap_type & 1) != 0) {
23293367019cSmrg
23303367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2331037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2332d522f475Smrg
2333d522f475Smrg	    if (tried != 0) {
2334037a25ddSmrg		unsigned attempts;
2335d522f475Smrg
2336d522f475Smrg		/*
2337d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2338d522f475Smrg		 * approximation to the requested color.
2339d522f475Smrg		 */
2340d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2341d522f475Smrg		    Boolean first = True;
2342037a25ddSmrg		    double bestRGB = 0.0;
2343037a25ddSmrg		    unsigned bestInx = 0;
2344037a25ddSmrg		    unsigned i;
2345d522f475Smrg
2346d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2347d522f475Smrg			if (!tried[bestInx]) {
2348037a25ddSmrg			    double diff, thisRGB = 0.0;
2349037a25ddSmrg
2350d522f475Smrg			    /*
2351d522f475Smrg			     * Look for the best match based on luminance.
2352d522f475Smrg			     * Measure this by the least-squares difference of
2353d522f475Smrg			     * the weighted R/G/B components from the color map
2354d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2355d522f475Smrg			     * component of the YIQ color space model for
2356d522f475Smrg			     * weights that correspond to the luminance.
2357d522f475Smrg			     */
2358d522f475Smrg#define AddColorWeight(weight, color) \
23593367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2360037a25ddSmrg			    thisRGB += diff * diff
2361d522f475Smrg
2362d522f475Smrg			    AddColorWeight(0.30, red);
2363d522f475Smrg			    AddColorWeight(0.61, green);
2364d522f475Smrg			    AddColorWeight(0.11, blue);
2365d522f475Smrg
2366d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2367d522f475Smrg				first = False;
2368d522f475Smrg				bestInx = i;
2369d522f475Smrg				bestRGB = thisRGB;
2370d522f475Smrg			    }
2371d522f475Smrg			}
2372d522f475Smrg		    }
23733367019cSmrg		    if (XAllocColor(screen->display, cmap,
23743367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
23753367019cSmrg			*def = screen->cmap_data[bestInx];
23763367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
23773367019cSmrg			       def->green, def->blue));
2378d522f475Smrg			result = True;
2379d522f475Smrg			break;
2380d522f475Smrg		    }
2381d522f475Smrg		    /*
2382d522f475Smrg		     * It failed - either the color map entry was readonly, or
2383d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2384d522f475Smrg		     * so we will ignore it
2385d522f475Smrg		     */
2386d522f475Smrg		    tried[bestInx] = True;
2387d522f475Smrg		}
2388d522f475Smrg		free(tried);
2389d522f475Smrg	    }
2390d522f475Smrg	}
2391d522f475Smrg    }
2392d522f475Smrg    return result;
2393d522f475Smrg}
2394d522f475Smrg
23953367019cSmrg#ifndef ULONG_MAX
23963367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
23973367019cSmrg#endif
23983367019cSmrg
23993367019cSmrg#define CheckColor(result, value) \
24003367019cSmrg	    result = 0; \
24013367019cSmrg	    if (value.red) \
24023367019cSmrg		result |= 1; \
24033367019cSmrg	    if (value.green) \
24043367019cSmrg		result |= 2; \
24053367019cSmrg	    if (value.blue) \
24063367019cSmrg		result |= 4
24073367019cSmrg
24083367019cSmrg#define SelectColor(state, value, result) \
24093367019cSmrg	switch (state) { \
24103367019cSmrg	default: \
24113367019cSmrg	case 1: \
24123367019cSmrg	    result = value.red; \
24133367019cSmrg	    break; \
24143367019cSmrg	case 2: \
24153367019cSmrg	    result = value.green; \
24163367019cSmrg	    break; \
24173367019cSmrg	case 4: \
24183367019cSmrg	    result = value.blue; \
24193367019cSmrg	    break; \
24203367019cSmrg	}
24213367019cSmrg
24223367019cSmrg/*
24233367019cSmrg * Check if the color map consists of values in exactly one of the red, green
24243367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
24253367019cSmrg * match.
24263367019cSmrg */
24273367019cSmrgstatic int
24289a64e1c5SmrgsimpleColors(XColor *colortable, unsigned length)
24293367019cSmrg{
24303367019cSmrg    unsigned n;
2431fa3f02f3Smrg    int state = 0;
24323367019cSmrg    int check;
24333367019cSmrg
24343367019cSmrg    for (n = 0; n < length; ++n) {
24353367019cSmrg	if (state > 0) {
24363367019cSmrg	    CheckColor(check, colortable[n]);
24373367019cSmrg	    if (check > 0 && check != state) {
24383367019cSmrg		state = 0;
24393367019cSmrg		break;
24403367019cSmrg	    }
2441fa3f02f3Smrg	} else {
2442fa3f02f3Smrg	    CheckColor(state, colortable[n]);
24433367019cSmrg	}
24443367019cSmrg    }
24453367019cSmrg    switch (state) {
24463367019cSmrg    case 1:
24473367019cSmrg    case 2:
24483367019cSmrg    case 4:
24493367019cSmrg	break;
24503367019cSmrg    default:
24513367019cSmrg	state = 0;
24523367019cSmrg	break;
24533367019cSmrg    }
24543367019cSmrg    return state;
24553367019cSmrg}
24563367019cSmrg
2457fa3f02f3Smrg/*
2458fa3f02f3Smrg * Shift the mask left or right to put its most significant bit at the 16-bit
2459fa3f02f3Smrg * mark.
2460fa3f02f3Smrg */
2461fa3f02f3Smrgstatic unsigned
2462fa3f02f3SmrgnormalizeMask(unsigned mask)
2463fa3f02f3Smrg{
2464fa3f02f3Smrg    while (mask < 0x8000) {
2465fa3f02f3Smrg	mask <<= 1;
2466fa3f02f3Smrg    }
2467fa3f02f3Smrg    while (mask >= 0x10000) {
2468fa3f02f3Smrg	mask >>= 1;
2469fa3f02f3Smrg    }
2470fa3f02f3Smrg    return mask;
2471fa3f02f3Smrg}
2472fa3f02f3Smrg
24733367019cSmrgstatic unsigned
24749a64e1c5SmrgsearchColors(XColor *colortable, unsigned mask, unsigned length, unsigned
2475fa3f02f3Smrg	     color, int state)
24763367019cSmrg{
24773367019cSmrg    unsigned result = 0;
24783367019cSmrg    unsigned n;
24793367019cSmrg    unsigned long best = ULONG_MAX;
24803367019cSmrg    unsigned value;
24813367019cSmrg
2482fa3f02f3Smrg    mask = normalizeMask(mask);
24833367019cSmrg    for (n = 0; n < length; ++n) {
2484037a25ddSmrg	unsigned long diff;
2485037a25ddSmrg
24863367019cSmrg	SelectColor(state, colortable[n], value);
2487fa3f02f3Smrg	diff = ((color & mask) - (value & mask));
24883367019cSmrg	diff *= diff;
24893367019cSmrg	if (diff < best) {
24903367019cSmrg#if 0
24913367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
24923367019cSmrg		   n, color,
24933367019cSmrg		   colortable[n].red,
24943367019cSmrg		   colortable[n].green,
24953367019cSmrg		   colortable[n].blue,
24963367019cSmrg		   diff));
24973367019cSmrg#endif
24983367019cSmrg	    result = n;
24993367019cSmrg	    best = diff;
25003367019cSmrg	}
25013367019cSmrg    }
25023367019cSmrg    SelectColor(state, colortable[result], value);
25033367019cSmrg    return value;
25043367019cSmrg}
25053367019cSmrg
25063367019cSmrg/*
25073367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
25083367019cSmrg *
25093367019cSmrg * According to
25103367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
25113367019cSmrg *
25123367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
25133367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
25143367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
25153367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
25163367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
25173367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
25183367019cSmrg *     actual RGB values allocated.
25193367019cSmrg *
25203367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
2521fa3f02f3Smrg * case, allocateClosestRGB() is useful for the dynamic display classes such as
25223367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
25233367019cSmrg * return regular RGB triples (unless a different scheme was used for
25243367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
25253367019cSmrg * is filled in with the colors that the server supports.
25263367019cSmrg *
25273367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
25283367019cSmrg * described.  For some TrueColor configurations it merely returns a close
25293367019cSmrg * approximation, but not the closest.
25303367019cSmrg */
25313367019cSmrgstatic Boolean
25329a64e1c5SmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def)
25333367019cSmrg{
25343367019cSmrg    XColor save = *def;
25353367019cSmrg    TScreen *screen = TScreenOf(xw);
25363367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
25373367019cSmrg
25383367019cSmrg    /*
2539fa3f02f3Smrg     * If this is a statically allocated display with too many items to store
2540fa3f02f3Smrg     * in our array, i.e., TrueColor, see if we can improve on the result by
2541fa3f02f3Smrg     * using the color values actually supported by the server.
25423367019cSmrg     */
25433367019cSmrg    if (result) {
25443367019cSmrg	unsigned cmap_type;
25453367019cSmrg	unsigned cmap_size;
25463367019cSmrg
2547fa3f02f3Smrg	getColormapInfo(xw, &cmap_type, &cmap_size);
25483367019cSmrg
2549fa3f02f3Smrg	if (cmap_type == TrueColor) {
25503367019cSmrg	    XColor temp = *def;
2551037a25ddSmrg	    int state;
25523367019cSmrg
25533367019cSmrg	    if (loadColorTable(xw, cmap_size)
25543367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
2555fa3f02f3Smrg#define SearchColors(which) \
2556fa3f02f3Smrg	temp.which = (unsigned short) searchColors(screen->cmap_data, \
2557fa3f02f3Smrg						   (unsigned) xw->visInfo->which##_mask,\
2558fa3f02f3Smrg						   cmap_size, \
2559fa3f02f3Smrg						   save.which, \
2560fa3f02f3Smrg						   state)
25613367019cSmrg		SearchColors(red);
25623367019cSmrg		SearchColors(green);
25633367019cSmrg		SearchColors(blue);
25643367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
25653367019cSmrg#if OPT_TRACE
25663367019cSmrg		    if (temp.red != save.red
25673367019cSmrg			|| temp.green != save.green
25683367019cSmrg			|| temp.blue != save.blue) {
25693367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
25703367019cSmrg			       save.red, save.green, save.blue,
25713367019cSmrg			       temp.red, temp.green, temp.blue));
25723367019cSmrg		    } else {
25733367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
25743367019cSmrg			       save.red, save.green, save.blue));
25753367019cSmrg		    }
25763367019cSmrg#endif
25773367019cSmrg		    *def = temp;
25783367019cSmrg		}
25793367019cSmrg	    }
25803367019cSmrg	}
25813367019cSmrg    }
25823367019cSmrg
25833367019cSmrg    return result;
25843367019cSmrg}
25853367019cSmrg
2586d522f475Smrg/*
2587d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2588d522f475Smrg * to 256.
2589d522f475Smrg *
2590d522f475Smrg * Returns
2591d522f475Smrg *	-1 on error
2592d522f475Smrg *	0 on no change
2593d522f475Smrg *	1 if a new color was allocated.
2594d522f475Smrg */
2595d522f475Smrgstatic int
2596d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2597d522f475Smrg		  ColorRes * res,
2598cd3331d0Smrg		  const char *spec)
2599d522f475Smrg{
2600d522f475Smrg    int result;
2601d522f475Smrg    XColor def;
2602d522f475Smrg
26033367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2604d522f475Smrg	if (
2605d522f475Smrg#if OPT_COLOR_RES
2606d522f475Smrg	       res->mode == True &&
2607d522f475Smrg#endif
2608d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
2609d522f475Smrg	    result = 0;
2610d522f475Smrg	} else {
2611d522f475Smrg	    result = 1;
2612d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
26133367019cSmrg	    res->red = def.red;
26143367019cSmrg	    res->green = def.green;
26153367019cSmrg	    res->blue = def.blue;
26163367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
26173367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
26183367019cSmrg		   def.red,
26193367019cSmrg		   def.green,
26203367019cSmrg		   def.blue,
26213367019cSmrg		   def.pixel));
2622d522f475Smrg#if OPT_COLOR_RES
2623d522f475Smrg	    if (!res->mode)
2624d522f475Smrg		result = 0;
2625d522f475Smrg	    res->mode = True;
2626d522f475Smrg#endif
2627d522f475Smrg	}
2628d522f475Smrg    } else {
2629d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2630d522f475Smrg	result = -1;
2631d522f475Smrg    }
2632d522f475Smrg    return (result);
2633d522f475Smrg}
2634d522f475Smrg
2635d522f475Smrg#if OPT_COLOR_RES
2636d522f475SmrgPixel
2637cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2638d522f475Smrg{
2639d522f475Smrg    Pixel result = 0;
2640d522f475Smrg
2641d522f475Smrg    if (res->mode) {
2642d522f475Smrg	result = res->value;
2643d522f475Smrg    } else {
2644d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2645cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2646d522f475Smrg
2647cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2648cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2649d522f475Smrg
2650cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2651cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2652d522f475Smrg		res->mode = -True;
26533367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
26543367019cSmrg			     NonNull(res->resource));
2655d522f475Smrg	    }
2656d522f475Smrg	    result = res->value;
2657d522f475Smrg	} else {
2658d522f475Smrg	    result = 0;
2659d522f475Smrg	}
2660d522f475Smrg    }
2661d522f475Smrg    return result;
2662d522f475Smrg}
2663d522f475Smrg#endif
2664d522f475Smrg
2665cd3331d0Smrgstatic int
2666cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2667cd3331d0Smrg{
2668cd3331d0Smrg    int code;
2669cd3331d0Smrg
2670cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2671cd3331d0Smrg	code = -1;
2672cd3331d0Smrg    } else {
2673cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2674cd3331d0Smrg
2675cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2676cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2677cd3331d0Smrg    }
2678cd3331d0Smrg    return code;
2679cd3331d0Smrg}
2680cd3331d0Smrg
2681cd3331d0Smrg/*
2682cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2683cd3331d0Smrg * values from the given buffer.
2684cd3331d0Smrg *
2685cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2686cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2687cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2688cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2689cd3331d0Smrg * 'first' set to the beginning of those indices.
2690cd3331d0Smrg *
2691cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2692cd3331d0Smrg */
2693d522f475Smrgstatic Bool
2694d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2695d522f475Smrg		       char *buf,
2696cd3331d0Smrg		       int first,
2697d522f475Smrg		       int final)
2698d522f475Smrg{
2699d522f475Smrg    int repaint = False;
2700d522f475Smrg    int code;
2701cd3331d0Smrg    int last = (MAXCOLORS - first);
2702d522f475Smrg
2703d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2704d522f475Smrg
2705d522f475Smrg    while (buf && *buf) {
2706037a25ddSmrg	int color;
2707037a25ddSmrg	char *name = strchr(buf, ';');
2708037a25ddSmrg
2709d522f475Smrg	if (name == NULL)
2710d522f475Smrg	    break;
2711d522f475Smrg	*name = '\0';
2712d522f475Smrg	name++;
2713d522f475Smrg	color = atoi(buf);
2714cd3331d0Smrg	if (color < 0 || color >= last)
2715cd3331d0Smrg	    break;		/* quit on any error */
2716d522f475Smrg	buf = strchr(name, ';');
2717d522f475Smrg	if (buf) {
2718d522f475Smrg	    *buf = '\0';
2719d522f475Smrg	    buf++;
2720d522f475Smrg	}
2721cd3331d0Smrg	if (!strcmp(name, "?")) {
2722cd3331d0Smrg	    ReportAnsiColorRequest(xw, color + first, final);
2723cd3331d0Smrg	} else {
2724cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
2725d522f475Smrg	    if (code < 0) {
2726d522f475Smrg		/* stop on any error */
2727d522f475Smrg		break;
2728d522f475Smrg	    } else if (code > 0) {
2729d522f475Smrg		repaint = True;
2730d522f475Smrg	    }
2731d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
2732d522f475Smrg	     * change style (dynamic colors).
2733d522f475Smrg	     */
2734d522f475Smrg	}
2735d522f475Smrg    }
2736d522f475Smrg
2737d522f475Smrg    return (repaint);
2738d522f475Smrg}
2739cd3331d0Smrg
2740cd3331d0Smrgstatic Bool
2741cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
2742cd3331d0Smrg{
2743cd3331d0Smrg    Bool repaint = False;
2744cd3331d0Smrg    int last = MAXCOLORS - start;
2745cd3331d0Smrg
2746cd3331d0Smrg    if (color >= 0 && color < last) {
2747cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
2748cd3331d0Smrg
2749cd3331d0Smrg	if (res->mode) {
2750cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
2751cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
2752cd3331d0Smrg		repaint = True;
2753cd3331d0Smrg	    }
2754cd3331d0Smrg	}
2755cd3331d0Smrg    }
2756cd3331d0Smrg    return repaint;
2757cd3331d0Smrg}
2758cd3331d0Smrg
2759cd3331d0Smrgint
2760cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
2761cd3331d0Smrg{
2762cd3331d0Smrg    int repaint = 0;
2763cd3331d0Smrg    int color;
2764cd3331d0Smrg
2765cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
2766cd3331d0Smrg    if (*buf != '\0') {
2767cd3331d0Smrg	/* reset specific colors */
2768cd3331d0Smrg	while (!IsEmpty(buf)) {
2769cd3331d0Smrg	    char *next;
2770cd3331d0Smrg
2771037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
2772037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
2773cd3331d0Smrg		break;		/* no number at all */
2774cd3331d0Smrg	    if (next != 0) {
2775cd3331d0Smrg		if (strchr(";", *next) == 0)
2776cd3331d0Smrg		    break;	/* unexpected delimiter */
2777cd3331d0Smrg		++next;
2778cd3331d0Smrg	    }
2779cd3331d0Smrg
2780cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2781cd3331d0Smrg		++repaint;
2782cd3331d0Smrg	    }
2783cd3331d0Smrg	    buf = next;
2784cd3331d0Smrg	}
2785cd3331d0Smrg    } else {
2786cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
2787cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
2788cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2789cd3331d0Smrg		++repaint;
2790cd3331d0Smrg	    }
2791cd3331d0Smrg	}
2792cd3331d0Smrg    }
2793cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
2794cd3331d0Smrg    return repaint;
2795cd3331d0Smrg}
2796d522f475Smrg#else
27973367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
27983367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
2799d522f475Smrg#endif /* OPT_ISO_COLORS */
2800d522f475Smrg
2801fa3f02f3SmrgBoolean
28029a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
2803fa3f02f3Smrg{
2804fa3f02f3Smrg    Colormap cmap = xw->core.colormap;
2805fa3f02f3Smrg
2806fa3f02f3Smrg    return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
2807fa3f02f3Smrg}
2808fa3f02f3Smrg
28093367019cSmrgstatic Boolean
28109a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
28113367019cSmrg{
28123367019cSmrg    Boolean result = False;
28133367019cSmrg    TScreen *screen = TScreenOf(xw);
28143367019cSmrg    Colormap cmap = xw->core.colormap;
28153367019cSmrg
2816fa3f02f3Smrg    if (XParseColor(screen->display, cmap, spec, def)) {
2817fa3f02f3Smrg	XColor save_def = *def;
2818fa3f02f3Smrg	if (resource.reportColors) {
2819fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
2820fa3f02f3Smrg		   def->red, def->green, def->blue,
2821fa3f02f3Smrg		   spec);
2822fa3f02f3Smrg	}
2823fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
2824fa3f02f3Smrg	    if (resource.reportColors) {
2825fa3f02f3Smrg		if (def->red != save_def.red ||
2826fa3f02f3Smrg		    def->green != save_def.green ||
2827fa3f02f3Smrg		    def->blue != save_def.blue) {
2828fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
2829fa3f02f3Smrg			   def->red, def->green, def->blue,
2830fa3f02f3Smrg			   spec);
2831fa3f02f3Smrg		}
2832fa3f02f3Smrg	    }
2833fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
2834fa3f02f3Smrg		   def->red, def->green, def->blue));
2835fa3f02f3Smrg	    result = True;
2836fa3f02f3Smrg	}
28373367019cSmrg    }
28383367019cSmrg    return result;
28393367019cSmrg}
28403367019cSmrg
28413367019cSmrg/*
28423367019cSmrg * This provides an approximation (the closest color from xterm's palette)
28433367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
28443367019cSmrg * because of the context in which it is used.
28453367019cSmrg */
28463367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
28473367019cSmrgint
28483367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
28493367019cSmrg{
28503367019cSmrg    int result = -1;
28513367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
28523367019cSmrg    int n;
28533367019cSmrg    int best_index = -1;
28543367019cSmrg    unsigned long best_value = 0;
28553367019cSmrg    unsigned long this_value;
28563367019cSmrg    long diff_red, diff_green, diff_blue;
28573367019cSmrg
28583367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
28593367019cSmrg
28603367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
28613367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
28623367019cSmrg
28633367019cSmrg	/* ensure that we have a value for each of the colors */
28643367019cSmrg	if (!res->mode) {
28653367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
28663367019cSmrg	}
28673367019cSmrg
28683367019cSmrg	/* find the closest match */
28693367019cSmrg	if (res->mode == True) {
28703367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
28713367019cSmrg		    res->value, res->red, res->green, res->blue));
28723367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
28733367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
28743367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
28753367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
28763367019cSmrg					  + (diff_green * diff_green)
28773367019cSmrg					  + (diff_blue * diff_blue));
28783367019cSmrg	    if (best_index < 0 || this_value < best_value) {
28793367019cSmrg		best_index = n;
28803367019cSmrg		best_value = this_value;
28813367019cSmrg	    }
28823367019cSmrg	}
28833367019cSmrg    }
28843367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
28853367019cSmrg    result = best_index;
28863367019cSmrg#else
28873367019cSmrg    (void) xw;
28883367019cSmrg    (void) find_red;
28893367019cSmrg    (void) find_green;
28903367019cSmrg    (void) find_blue;
28913367019cSmrg#endif
28923367019cSmrg    return result;
28933367019cSmrg}
28943367019cSmrg
2895d522f475Smrg#if OPT_PASTE64
2896d522f475Smrgstatic void
2897fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
2898d522f475Smrg{
2899d522f475Smrg#define PDATA(a,b) { a, #b }
2900d522f475Smrg    static struct {
2901d522f475Smrg	char given;
2902cd3331d0Smrg	String result;
2903d522f475Smrg    } table[] = {
2904d522f475Smrg	PDATA('s', SELECT),
2905d522f475Smrg	    PDATA('p', PRIMARY),
2906d522f475Smrg	    PDATA('c', CLIPBOARD),
2907d522f475Smrg	    PDATA('0', CUT_BUFFER0),
2908d522f475Smrg	    PDATA('1', CUT_BUFFER1),
2909d522f475Smrg	    PDATA('2', CUT_BUFFER2),
2910d522f475Smrg	    PDATA('3', CUT_BUFFER3),
2911d522f475Smrg	    PDATA('4', CUT_BUFFER4),
2912d522f475Smrg	    PDATA('5', CUT_BUFFER5),
2913d522f475Smrg	    PDATA('6', CUT_BUFFER6),
2914d522f475Smrg	    PDATA('7', CUT_BUFFER7),
2915d522f475Smrg    };
2916d522f475Smrg
2917cd3331d0Smrg    const char *base = buf;
2918d522f475Smrg    Cardinal j, n = 0;
2919d522f475Smrg
2920d522f475Smrg    TRACE(("Manipulate selection data\n"));
2921d522f475Smrg
2922d522f475Smrg    while (*buf != ';' && *buf != '\0') {
2923d522f475Smrg	++buf;
2924d522f475Smrg    }
2925d522f475Smrg
2926d522f475Smrg    if (*buf == ';') {
2927037a25ddSmrg	char *used;
2928037a25ddSmrg
2929d522f475Smrg	*buf++ = '\0';
2930d522f475Smrg
2931d522f475Smrg	if (*base == '\0')
2932d522f475Smrg	    base = "s0";
2933d522f475Smrg
29343367019cSmrg	if ((used = x_strdup(base)) != 0) {
2935037a25ddSmrg	    String *select_args;
2936037a25ddSmrg
29373367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
29383367019cSmrg		while (*base != '\0') {
29393367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
29403367019cSmrg			if (*base == table[j].given) {
29413367019cSmrg			    used[n] = *base;
29423367019cSmrg			    select_args[n++] = table[j].result;
29433367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
29443367019cSmrg			    break;
29453367019cSmrg			}
29463367019cSmrg		    }
29473367019cSmrg		    ++base;
29483367019cSmrg		}
29493367019cSmrg		used[n] = 0;
29503367019cSmrg
29513367019cSmrg		if (!strcmp(buf, "?")) {
29523367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
29533367019cSmrg			TRACE(("Getting selection\n"));
29543367019cSmrg			unparseputc1(xw, ANSI_OSC);
29553367019cSmrg			unparseputs(xw, "52");
29563367019cSmrg			unparseputc(xw, ';');
29573367019cSmrg
29583367019cSmrg			unparseputs(xw, used);
29593367019cSmrg			unparseputc(xw, ';');
29603367019cSmrg
29613367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
29623367019cSmrg			screen->base64_paste = n;
29633367019cSmrg			screen->base64_final = final;
29643367019cSmrg
2965dfb07bc7Smrg			screen->selection_time =
2966dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
2967dfb07bc7Smrg
29683367019cSmrg			/* terminator will be written in this call */
29693367019cSmrg			xtermGetSelection((Widget) xw,
2970dfb07bc7Smrg					  screen->selection_time,
29713367019cSmrg					  select_args, n,
29723367019cSmrg					  NULL);
297394644356Smrg			/*
297494644356Smrg			 * select_args is used via SelectionReceived, cannot
297594644356Smrg			 * free it here.
297694644356Smrg			 */
297794644356Smrg		    } else {
297894644356Smrg			free(select_args);
29793367019cSmrg		    }
29803367019cSmrg		} else {
29813367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
29823367019cSmrg			TRACE(("Setting selection with %s\n", buf));
2983dfb07bc7Smrg			screen->selection_time =
2984dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
29853367019cSmrg			ClearSelectionBuffer(screen);
29863367019cSmrg			while (*buf != '\0')
29873367019cSmrg			    AppendToSelectionBuffer(screen, CharOf(*buf++));
29883367019cSmrg			CompleteSelection(xw, select_args, n);
29893367019cSmrg		    }
299094644356Smrg		    free(select_args);
29913367019cSmrg		}
2992cd3331d0Smrg	    }
29933367019cSmrg	    free(used);
2994d522f475Smrg	}
2995d522f475Smrg    }
2996d522f475Smrg}
2997d522f475Smrg#endif /* OPT_PASTE64 */
2998d522f475Smrg
2999d522f475Smrg/***====================================================================***/
3000d522f475Smrg
3001cd3331d0Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
3002cd3331d0Smrg
3003d522f475Smrgstatic Bool
3004fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3005d522f475Smrg{
3006cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3007d522f475Smrg    Bool result = False;
3008d522f475Smrg    Char *cp = *bufp;
3009d522f475Smrg    Char *next = cp;
3010d522f475Smrg
3011d522f475Smrg    (void) screen;
3012d522f475Smrg    (void) last;
3013d522f475Smrg
3014d522f475Smrg#if OPT_WIDE_CHARS
3015cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3016d522f475Smrg	PtyData data;
3017d522f475Smrg
30189a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3019d522f475Smrg	    if (data.utf_data != UCS_REPL
3020d522f475Smrg		&& (data.utf_data >= 128 ||
3021d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3022d522f475Smrg		next += (data.utf_size - 1);
3023d522f475Smrg		result = True;
3024d522f475Smrg	    } else {
3025d522f475Smrg		result = False;
3026d522f475Smrg	    }
3027d522f475Smrg	} else {
3028d522f475Smrg	    result = False;
3029d522f475Smrg	}
3030d522f475Smrg    } else
3031d522f475Smrg#endif
3032d522f475Smrg#if OPT_C1_PRINT
3033d522f475Smrg	if (screen->c1_printable
3034d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3035d522f475Smrg	result = True;
3036d522f475Smrg    } else
3037d522f475Smrg#endif
3038d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3039d522f475Smrg	result = True;
3040d522f475Smrg    }
3041d522f475Smrg    *bufp = next;
3042d522f475Smrg    return result;
3043d522f475Smrg}
3044d522f475Smrg
3045d522f475Smrg/***====================================================================***/
3046d522f475Smrg
3047d522f475Smrg/*
3048d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3049cd3331d0Smrg * array indices.  Compare with TermColors.
3050d522f475Smrg */
3051d522f475Smrgtypedef enum {
3052d522f475Smrg    OSC_TEXT_FG = 10
3053d522f475Smrg    ,OSC_TEXT_BG
3054d522f475Smrg    ,OSC_TEXT_CURSOR
3055d522f475Smrg    ,OSC_MOUSE_FG
3056d522f475Smrg    ,OSC_MOUSE_BG
3057d522f475Smrg#if OPT_TEK4014
3058d522f475Smrg    ,OSC_TEK_FG = 15
3059d522f475Smrg    ,OSC_TEK_BG
3060d522f475Smrg#endif
3061d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3062d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3063d522f475Smrg#endif
3064d522f475Smrg#if OPT_TEK4014
3065d522f475Smrg    ,OSC_TEK_CURSOR = 18
3066d522f475Smrg#endif
3067d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3068d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3069d522f475Smrg#endif
3070d522f475Smrg    ,OSC_NCOLORS
3071d522f475Smrg} OscTextColors;
3072d522f475Smrg
3073cd3331d0Smrg/*
3074cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3075cd3331d0Smrg */
3076cd3331d0Smrg#define OSC_RESET 100
3077cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3078cd3331d0Smrg
3079d522f475Smrgstatic Bool
3080d522f475SmrgGetOldColors(XtermWidget xw)
3081d522f475Smrg{
30829a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3083037a25ddSmrg	int i;
3084037a25ddSmrg
30859a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
30869a64e1c5Smrg	if (xw->work.oldColors == NULL) {
30873367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3088d522f475Smrg	    return (False);
3089d522f475Smrg	}
30909a64e1c5Smrg	xw->work.oldColors->which = 0;
3091d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
30929a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
30939a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3094d522f475Smrg	}
30959a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3096d522f475Smrg    }
3097d522f475Smrg    return (True);
3098d522f475Smrg}
3099d522f475Smrg
3100d522f475Smrgstatic int
3101d522f475SmrgoppositeColor(int n)
3102d522f475Smrg{
3103d522f475Smrg    switch (n) {
3104d522f475Smrg    case TEXT_FG:
3105d522f475Smrg	n = TEXT_BG;
3106d522f475Smrg	break;
3107d522f475Smrg    case TEXT_BG:
3108d522f475Smrg	n = TEXT_FG;
3109d522f475Smrg	break;
3110d522f475Smrg    case MOUSE_FG:
3111d522f475Smrg	n = MOUSE_BG;
3112d522f475Smrg	break;
3113d522f475Smrg    case MOUSE_BG:
3114d522f475Smrg	n = MOUSE_FG;
3115d522f475Smrg	break;
3116d522f475Smrg#if OPT_TEK4014
3117d522f475Smrg    case TEK_FG:
3118d522f475Smrg	n = TEK_BG;
3119d522f475Smrg	break;
3120d522f475Smrg    case TEK_BG:
3121d522f475Smrg	n = TEK_FG;
3122d522f475Smrg	break;
3123d522f475Smrg#endif
3124d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3125d522f475Smrg    case HIGHLIGHT_FG:
3126d522f475Smrg	n = HIGHLIGHT_BG;
3127d522f475Smrg	break;
3128d522f475Smrg    case HIGHLIGHT_BG:
3129d522f475Smrg	n = HIGHLIGHT_FG;
3130d522f475Smrg	break;
3131d522f475Smrg#endif
3132d522f475Smrg    default:
3133d522f475Smrg	break;
3134d522f475Smrg    }
3135d522f475Smrg    return n;
3136d522f475Smrg}
3137d522f475Smrg
3138d522f475Smrgstatic void
3139d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3140d522f475Smrg{
3141cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3142cd3331d0Smrg	XColor color;
3143cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3144cd3331d0Smrg	char buffer[80];
3145d522f475Smrg
3146cd3331d0Smrg	/*
3147cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3148cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3149cd3331d0Smrg	 * reporting the opposite color which would be used.
3150cd3331d0Smrg	 */
3151cd3331d0Smrg	int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
3152cd3331d0Smrg
3153cd3331d0Smrg	GetOldColors(xw);
31549a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3155cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
3156cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3157cd3331d0Smrg		color.red,
3158cd3331d0Smrg		color.green,
3159cd3331d0Smrg		color.blue);
3160712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
31619a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3162cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3163cd3331d0Smrg	unparseputs(xw, buffer);
3164cd3331d0Smrg	unparseputc1(xw, final);
3165cd3331d0Smrg	unparse_end(xw);
3166cd3331d0Smrg    }
3167d522f475Smrg}
3168d522f475Smrg
3169d522f475Smrgstatic Bool
3170d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
3171d522f475Smrg{
3172d522f475Smrg    int i;
3173d522f475Smrg
3174d522f475Smrg    /* if we were going to free old colors, this would be the place to
3175d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3176d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3177d522f475Smrg     * we could save some overhead this way.   The only case in which this
3178d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3179d522f475Smrg     * which case they can restart xterm
3180d522f475Smrg     */
3181d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3182d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
31839a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
31849a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
31859a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3186d522f475Smrg	    }
3187d522f475Smrg	    if (pNew->names[i]) {
31889a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3189d522f475Smrg	    }
31909a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3191d522f475Smrg	}
3192d522f475Smrg    }
3193d522f475Smrg    return (True);
3194d522f475Smrg}
3195d522f475Smrg
3196d522f475Smrg/*
3197d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3198d522f475Smrg * xterm is compiled.
3199d522f475Smrg */
3200d522f475Smrgstatic int
3201d522f475SmrgOscToColorIndex(OscTextColors mode)
3202d522f475Smrg{
3203d522f475Smrg    int result = 0;
3204d522f475Smrg
3205d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3206d522f475Smrg    switch (mode) {
3207d522f475Smrg	CASE(TEXT_FG);
3208d522f475Smrg	CASE(TEXT_BG);
3209d522f475Smrg	CASE(TEXT_CURSOR);
3210d522f475Smrg	CASE(MOUSE_FG);
3211d522f475Smrg	CASE(MOUSE_BG);
3212d522f475Smrg#if OPT_TEK4014
3213d522f475Smrg	CASE(TEK_FG);
3214d522f475Smrg	CASE(TEK_BG);
3215d522f475Smrg#endif
3216d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3217d522f475Smrg	CASE(HIGHLIGHT_BG);
3218d522f475Smrg	CASE(HIGHLIGHT_FG);
3219d522f475Smrg#endif
3220d522f475Smrg#if OPT_TEK4014
3221d522f475Smrg	CASE(TEK_CURSOR);
3222d522f475Smrg#endif
3223d522f475Smrg    case OSC_NCOLORS:
3224d522f475Smrg	break;
3225d522f475Smrg    }
3226d522f475Smrg    return result;
3227d522f475Smrg}
3228d522f475Smrg
3229d522f475Smrgstatic Bool
3230d522f475SmrgChangeColorsRequest(XtermWidget xw,
3231d522f475Smrg		    int start,
3232d522f475Smrg		    char *names,
3233d522f475Smrg		    int final)
3234d522f475Smrg{
3235d522f475Smrg    Bool result = False;
3236d522f475Smrg    ScrnColors newColors;
3237d522f475Smrg
3238d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3239d522f475Smrg
3240d522f475Smrg    if (GetOldColors(xw)) {
3241037a25ddSmrg	int i;
3242037a25ddSmrg
3243d522f475Smrg	newColors.which = 0;
3244d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3245d522f475Smrg	    newColors.names[i] = NULL;
3246d522f475Smrg	}
3247d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3248037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3249d522f475Smrg	    if (xw->misc.re_verse)
3250d522f475Smrg		ndx = oppositeColor(ndx);
3251d522f475Smrg
3252cd3331d0Smrg	    if (IsEmpty(names)) {
3253d522f475Smrg		newColors.names[ndx] = NULL;
3254d522f475Smrg	    } else {
3255037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3256037a25ddSmrg
3257d522f475Smrg		names = strchr(names, ';');
3258d522f475Smrg		if (names != NULL) {
3259d522f475Smrg		    *names++ = '\0';
3260d522f475Smrg		}
3261fa3f02f3Smrg		if (thisName != 0) {
3262fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3263fa3f02f3Smrg			ReportColorRequest(xw, ndx, final);
32649a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
32659a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3266fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3267fa3f02f3Smrg		    }
3268d522f475Smrg		}
3269d522f475Smrg	    }
3270d522f475Smrg	}
3271d522f475Smrg
3272d522f475Smrg	if (newColors.which != 0) {
3273d522f475Smrg	    ChangeColors(xw, &newColors);
3274d522f475Smrg	    UpdateOldColors(xw, &newColors);
3275d522f475Smrg	}
3276d522f475Smrg	result = True;
3277d522f475Smrg    }
3278d522f475Smrg    return result;
3279d522f475Smrg}
3280d522f475Smrg
3281cd3331d0Smrgstatic Bool
3282cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3283cd3331d0Smrg		   int code)
3284cd3331d0Smrg{
3285cd3331d0Smrg    Bool result = False;
3286cd3331d0Smrg
3287dfb07bc7Smrg    (void) xw;
3288dfb07bc7Smrg    (void) code;
3289dfb07bc7Smrg
3290cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3291cd3331d0Smrg
3292cd3331d0Smrg#if OPT_COLOR_RES
3293cd3331d0Smrg    if (GetOldColors(xw)) {
3294037a25ddSmrg	ScrnColors newColors;
3295037a25ddSmrg	const char *thisName;
3296037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3297037a25ddSmrg
3298cd3331d0Smrg	if (xw->misc.re_verse)
3299cd3331d0Smrg	    ndx = oppositeColor(ndx);
3300cd3331d0Smrg
3301cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3302cd3331d0Smrg
3303cd3331d0Smrg	newColors.which = 0;
3304cd3331d0Smrg	newColors.names[ndx] = NULL;
3305cd3331d0Smrg
3306cd3331d0Smrg	if (thisName != 0
33079a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
33089a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3309cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3310cd3331d0Smrg
3311cd3331d0Smrg	    if (newColors.which != 0) {
3312cd3331d0Smrg		ChangeColors(xw, &newColors);
3313cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3314cd3331d0Smrg	    }
3315cd3331d0Smrg	}
3316cd3331d0Smrg	result = True;
3317cd3331d0Smrg    }
3318cd3331d0Smrg#endif
3319cd3331d0Smrg    return result;
3320cd3331d0Smrg}
3321cd3331d0Smrg
3322cd3331d0Smrg#if OPT_SHIFT_FONTS
3323cd3331d0Smrg/*
3324cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3325cd3331d0Smrg *
3326cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3327cd3331d0Smrg * the corresponding menu font entry.
3328cd3331d0Smrg */
3329cd3331d0Smrgstatic int
3330fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3331cd3331d0Smrg{
3332cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3333cd3331d0Smrg    int num = screen->menu_font_number;
3334cd3331d0Smrg    int rel = 0;
3335cd3331d0Smrg
3336cd3331d0Smrg    if (*++source == '+') {
3337cd3331d0Smrg	rel = 1;
3338cd3331d0Smrg	source++;
3339cd3331d0Smrg    } else if (*source == '-') {
3340cd3331d0Smrg	rel = -1;
3341cd3331d0Smrg	source++;
3342cd3331d0Smrg    }
3343cd3331d0Smrg
3344cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3345cd3331d0Smrg	int val = atoi(source);
3346cd3331d0Smrg	if (rel > 0)
3347cd3331d0Smrg	    rel = val;
3348cd3331d0Smrg	else if (rel < 0)
3349cd3331d0Smrg	    rel = -val;
3350cd3331d0Smrg	else
3351cd3331d0Smrg	    num = val;
3352cd3331d0Smrg    }
3353cd3331d0Smrg
3354cd3331d0Smrg    if (rel != 0) {
3355cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3356cd3331d0Smrg				     screen->menu_font_number, rel);
3357cd3331d0Smrg
3358cd3331d0Smrg    }
3359cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3360cd3331d0Smrg    *target = source;
3361cd3331d0Smrg    return num;
3362cd3331d0Smrg}
3363cd3331d0Smrg
3364cd3331d0Smrgstatic void
3365cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3366cd3331d0Smrg{
3367cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3368cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3369cd3331d0Smrg	Bool success = True;
3370cd3331d0Smrg	int num;
3371cb4a1343Smrg	String base = buf + 1;
3372cd3331d0Smrg	const char *name = 0;
3373cd3331d0Smrg
3374cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3375cd3331d0Smrg	if (num < 0
3376cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3377cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3378cd3331d0Smrg	    success = False;
3379cd3331d0Smrg	} else {
3380cd3331d0Smrg#if OPT_RENDERFONT
3381cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3382cd3331d0Smrg		name = getFaceName(xw, False);
3383cd3331d0Smrg	    } else
3384cd3331d0Smrg#endif
3385cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3386cd3331d0Smrg		success = False;
3387cd3331d0Smrg	    }
3388cd3331d0Smrg	}
3389cd3331d0Smrg
3390cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3391cd3331d0Smrg	unparseputs(xw, "50");
3392cd3331d0Smrg
3393cd3331d0Smrg	if (success) {
3394cd3331d0Smrg	    unparseputc(xw, ';');
3395cd3331d0Smrg	    if (buf >= base) {
3396cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3397cd3331d0Smrg		if (*buf != '\0') {
3398037a25ddSmrg		    char temp[10];
3399037a25ddSmrg
3400cd3331d0Smrg		    unparseputc(xw, '#');
3401cd3331d0Smrg		    sprintf(temp, "%d", num);
3402cd3331d0Smrg		    unparseputs(xw, temp);
3403cd3331d0Smrg		    if (*name != '\0')
3404cd3331d0Smrg			unparseputc(xw, ' ');
3405cd3331d0Smrg		}
3406cd3331d0Smrg	    }
3407cd3331d0Smrg	    unparseputs(xw, name);
3408cd3331d0Smrg	}
3409cd3331d0Smrg
3410cd3331d0Smrg	unparseputc1(xw, final);
3411cd3331d0Smrg	unparse_end(xw);
3412cd3331d0Smrg    }
3413cd3331d0Smrg}
3414cd3331d0Smrg
3415cd3331d0Smrgstatic void
3416cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3417cd3331d0Smrg{
3418cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3419cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3420cd3331d0Smrg	Bool success = True;
3421cd3331d0Smrg	int num;
3422cd3331d0Smrg	VTFontNames fonts;
3423cd3331d0Smrg	char *name;
3424cd3331d0Smrg
3425cd3331d0Smrg	/*
3426cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3427cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3428cd3331d0Smrg	 *
3429cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3430cd3331d0Smrg	 * to load the font entry.
3431cd3331d0Smrg	 */
3432cd3331d0Smrg	if (*buf == '#') {
3433cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3434cd3331d0Smrg
3435cd3331d0Smrg	    if (num < 0
3436cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3437cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3438cd3331d0Smrg		success = False;
3439cd3331d0Smrg	    } else {
3440cd3331d0Smrg		/*
3441cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3442cd3331d0Smrg		 * for a font specification within the control.
3443cd3331d0Smrg		 */
3444cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3445cd3331d0Smrg		    ++buf;
3446cd3331d0Smrg		}
3447cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3448cd3331d0Smrg		    ++buf;
3449cd3331d0Smrg		}
3450cd3331d0Smrg#if OPT_RENDERFONT
3451cd3331d0Smrg		if (UsingRenderFont(xw)) {
3452c219fbebSmrg		    /* EMPTY */
3453c219fbebSmrg		    /* there is only one font entry to load */
3454c219fbebSmrg		    ;
3455cd3331d0Smrg		} else
3456cd3331d0Smrg#endif
3457cd3331d0Smrg		{
3458cd3331d0Smrg		    /*
3459cd3331d0Smrg		     * Normally there is no font specified in the control.
3460cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3461cd3331d0Smrg		     */
3462cd3331d0Smrg		    if (*buf == '\0') {
3463cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3464cd3331d0Smrg			    success = False;
3465cd3331d0Smrg			}
3466cd3331d0Smrg		    }
3467cd3331d0Smrg		}
3468cd3331d0Smrg	    }
3469cd3331d0Smrg	} else {
3470cd3331d0Smrg	    num = screen->menu_font_number;
3471cd3331d0Smrg	}
3472cd3331d0Smrg	name = x_strtrim(buf);
347394644356Smrg	if (screen->EscapeFontName()) {
347494644356Smrg	    FREE_STRING(screen->EscapeFontName());
347594644356Smrg	    screen->EscapeFontName() = 0;
347694644356Smrg	}
3477cd3331d0Smrg	if (success && !IsEmpty(name)) {
3478cd3331d0Smrg#if OPT_RENDERFONT
3479cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3480cd3331d0Smrg		setFaceName(xw, name);
3481cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3482cd3331d0Smrg	    } else
3483cd3331d0Smrg#endif
3484cd3331d0Smrg	    {
3485cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3486cd3331d0Smrg		fonts.f_n = name;
3487cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
348894644356Smrg		if (num == screen->menu_font_number &&
348994644356Smrg		    num != fontMenu_fontescape) {
349094644356Smrg		    screen->EscapeFontName() = x_strdup(name);
349194644356Smrg		}
3492cd3331d0Smrg	    }
3493cd3331d0Smrg	} else {
3494cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3495cd3331d0Smrg	}
349694644356Smrg	update_font_escape();
3497cd3331d0Smrg	free(name);
3498cd3331d0Smrg    }
3499cd3331d0Smrg}
3500cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3501cd3331d0Smrg
3502d522f475Smrg/***====================================================================***/
3503d522f475Smrg
3504d522f475Smrgvoid
3505fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3506d522f475Smrg{
3507cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3508d522f475Smrg    int mode;
3509d522f475Smrg    Char *cp;
3510d522f475Smrg    int state = 0;
3511d522f475Smrg    char *buf = 0;
3512cd3331d0Smrg    char temp[2];
3513cd3331d0Smrg#if OPT_ISO_COLORS
3514cd3331d0Smrg    int ansi_colors = 0;
3515cd3331d0Smrg#endif
3516cd3331d0Smrg    Bool need_data = True;
3517fa3f02f3Smrg    Bool optional_data = False;
3518d522f475Smrg
3519d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3520d522f475Smrg
3521712a7ff4Smrg    (void) screen;
3522712a7ff4Smrg
3523d522f475Smrg    /*
3524d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3525d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3526d522f475Smrg     * with the same final character as the application sends to make this
3527d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3528d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3529d522f475Smrg     */
3530d522f475Smrg    mode = 0;
3531d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3532d522f475Smrg	switch (state) {
3533d522f475Smrg	case 0:
3534d522f475Smrg	    if (isdigit(*cp)) {
3535d522f475Smrg		mode = 10 * mode + (*cp - '0');
3536d522f475Smrg		if (mode > 65535) {
3537d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3538d522f475Smrg		    return;
3539d522f475Smrg		}
3540d522f475Smrg		break;
3541d522f475Smrg	    }
3542d522f475Smrg	    /* FALLTHRU */
3543d522f475Smrg	case 1:
3544d522f475Smrg	    if (*cp != ';') {
3545cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
3546cd3331d0Smrg		       (int) (cp - oscbuf)));
3547d522f475Smrg		return;
3548d522f475Smrg	    }
3549d522f475Smrg	    state = 2;
3550d522f475Smrg	    break;
3551d522f475Smrg	case 2:
3552d522f475Smrg	    buf = (char *) cp;
3553d522f475Smrg	    state = 3;
3554d522f475Smrg	    /* FALLTHRU */
3555d522f475Smrg	default:
3556cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
3557d522f475Smrg		switch (mode) {
3558d522f475Smrg		case 0:
3559d522f475Smrg		case 1:
3560d522f475Smrg		case 2:
3561d522f475Smrg		    break;
3562d522f475Smrg		default:
3563d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
3564d522f475Smrg			   CharOf(*cp),
3565cd3331d0Smrg			   (int) (cp - oscbuf)));
3566d522f475Smrg		    return;
3567d522f475Smrg		}
3568d522f475Smrg	    }
3569d522f475Smrg	}
3570d522f475Smrg    }
3571cd3331d0Smrg
35723367019cSmrg    /*
35733367019cSmrg     * Check if the palette changed and there are no more immediate changes
35743367019cSmrg     * that could be deferred to the next repaint.
35753367019cSmrg     */
3576dfb07bc7Smrg    if (xw->work.palette_changed) {
35773367019cSmrg	switch (mode) {
35783367019cSmrg	case 3:		/* change X property */
35793367019cSmrg	case 30:		/* Konsole (unused) */
35803367019cSmrg	case 31:		/* Konsole (unused) */
35813367019cSmrg	case 50:		/* font operations */
35823367019cSmrg	case 51:		/* Emacs (unused) */
35833367019cSmrg#if OPT_PASTE64
35843367019cSmrg	case 52:		/* selection data */
35853367019cSmrg#endif
35863367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
3587dfb07bc7Smrg	    xw->work.palette_changed = False;
35883367019cSmrg	    xtermRepaint(xw);
35893367019cSmrg	    break;
35903367019cSmrg	}
35913367019cSmrg    }
35923367019cSmrg
3593cd3331d0Smrg    /*
3594cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
3595cd3331d0Smrg     * a special case.
3596cd3331d0Smrg     */
3597cd3331d0Smrg    switch (mode) {
359894644356Smrg    case 50:
3599cd3331d0Smrg#if OPT_ISO_COLORS
3600cd3331d0Smrg    case OSC_Reset(4):
3601cd3331d0Smrg    case OSC_Reset(5):
3602fa3f02f3Smrg	need_data = False;
3603fa3f02f3Smrg	optional_data = True;
3604fa3f02f3Smrg	break;
3605cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3606cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3607cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3608cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3609cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3610cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3611cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3612cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3613cd3331d0Smrg#endif
3614cd3331d0Smrg#if OPT_TEK4014
3615cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3616cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3617cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3618cd3331d0Smrg#endif
3619cd3331d0Smrg	need_data = False;
3620cd3331d0Smrg	break;
3621cd3331d0Smrg#endif
3622cd3331d0Smrg    default:
3623cd3331d0Smrg	break;
3624cd3331d0Smrg    }
3625cd3331d0Smrg
3626cd3331d0Smrg    /*
3627cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
3628cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
3629cd3331d0Smrg     */
3630cd3331d0Smrg    if (IsEmpty(buf)) {
3631cd3331d0Smrg	if (need_data) {
3632cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
3633cd3331d0Smrg	    return;
3634cd3331d0Smrg	}
3635cd3331d0Smrg	temp[0] = '\0';
3636cd3331d0Smrg	buf = temp;
3637fa3f02f3Smrg    } else if (!need_data && !optional_data) {
3638fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
3639d522f475Smrg	return;
36400d92cbfdSchristos    }
3641d522f475Smrg
3642d522f475Smrg    switch (mode) {
3643d522f475Smrg    case 0:			/* new icon name and title */
3644b7c89284Ssnj	ChangeIconName(xw, buf);
3645b7c89284Ssnj	ChangeTitle(xw, buf);
3646d522f475Smrg	break;
3647d522f475Smrg
3648d522f475Smrg    case 1:			/* new icon name only */
3649b7c89284Ssnj	ChangeIconName(xw, buf);
3650d522f475Smrg	break;
3651d522f475Smrg
3652d522f475Smrg    case 2:			/* new title only */
3653b7c89284Ssnj	ChangeTitle(xw, buf);
3654d522f475Smrg	break;
3655d522f475Smrg
365622d8e007Schristos#ifdef notdef
3657d522f475Smrg    case 3:			/* change X property */
3658cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
36590d92cbfdSchristos	    ChangeXprop(buf);
3660d522f475Smrg	break;
366122d8e007Schristos#endif
3662d522f475Smrg#if OPT_ISO_COLORS
3663cd3331d0Smrg    case 5:
3664cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3665cd3331d0Smrg	/* FALLTHRU */
3666d522f475Smrg    case 4:
3667cd3331d0Smrg	if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
3668dfb07bc7Smrg	    xw->work.palette_changed = True;
3669cd3331d0Smrg	break;
367094644356Smrg    case 6:
367194644356Smrg	/* FALLTHRU */
367294644356Smrg    case OSC_Reset(6):
367394644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
367494644356Smrg	while (*buf != '\0') {
367594644356Smrg	    long which = 0;
367694644356Smrg	    long value = 0;
367794644356Smrg	    char *next;
367894644356Smrg	    if (*buf == ';') {
367994644356Smrg		++buf;
368094644356Smrg	    } else {
368194644356Smrg		which = strtol(buf, &next, 10);
3682037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
368394644356Smrg		    break;
368494644356Smrg		buf = next;
368594644356Smrg		if (*buf == ';')
368694644356Smrg		    ++buf;
368794644356Smrg	    }
368894644356Smrg	    if (*buf == ';') {
368994644356Smrg		++buf;
369094644356Smrg	    } else {
369194644356Smrg		value = strtol(buf, &next, 10);
3692dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
369394644356Smrg		    break;
369494644356Smrg		buf = next;
369594644356Smrg		if (*buf == ';')
369694644356Smrg		    ++buf;
369794644356Smrg	    }
369894644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
369994644356Smrg	    switch (which) {
370094644356Smrg	    case 0:
370194644356Smrg		screen->colorBDMode = (value != 0);
370294644356Smrg		break;
370394644356Smrg	    case 1:
370494644356Smrg		screen->colorULMode = (value != 0);
370594644356Smrg		break;
370694644356Smrg	    case 2:
370794644356Smrg		screen->colorBLMode = (value != 0);
370894644356Smrg		break;
370994644356Smrg	    case 3:
371094644356Smrg		screen->colorRVMode = (value != 0);
371194644356Smrg		break;
371294644356Smrg#if OPT_WIDE_ATTRS
371394644356Smrg	    case 4:
371494644356Smrg		screen->colorITMode = (value != 0);
371594644356Smrg		break;
371694644356Smrg#endif
371794644356Smrg	    default:
371894644356Smrg		TRACE(("...unknown colorXXMode\n"));
371994644356Smrg		break;
372094644356Smrg	    }
372194644356Smrg	}
372294644356Smrg	break;
3723cd3331d0Smrg    case OSC_Reset(5):
3724cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3725cd3331d0Smrg	/* FALLTHRU */
3726cd3331d0Smrg    case OSC_Reset(4):
3727cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
3728dfb07bc7Smrg	    xw->work.palette_changed = True;
3729d522f475Smrg	break;
3730d522f475Smrg#endif
3731d522f475Smrg    case OSC_TEXT_FG:
3732d522f475Smrg    case OSC_TEXT_BG:
3733d522f475Smrg    case OSC_TEXT_CURSOR:
3734d522f475Smrg    case OSC_MOUSE_FG:
3735d522f475Smrg    case OSC_MOUSE_BG:
3736d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3737d522f475Smrg    case OSC_HIGHLIGHT_BG:
3738cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
3739d522f475Smrg#endif
3740d522f475Smrg#if OPT_TEK4014
3741d522f475Smrg    case OSC_TEK_FG:
3742d522f475Smrg    case OSC_TEK_BG:
3743d522f475Smrg    case OSC_TEK_CURSOR:
3744d522f475Smrg#endif
3745cd3331d0Smrg	if (xw->misc.dynamicColors) {
3746d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
3747cd3331d0Smrg	}
3748cd3331d0Smrg	break;
3749cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3750cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3751cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3752cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3753cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3754cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3755cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3756cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3757cd3331d0Smrg#endif
3758cd3331d0Smrg#if OPT_TEK4014
3759cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3760cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3761cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3762cd3331d0Smrg#endif
3763cd3331d0Smrg	if (xw->misc.dynamicColors) {
3764cd3331d0Smrg	    ResetColorsRequest(xw, mode);
3765cd3331d0Smrg	}
3766d522f475Smrg	break;
3767d522f475Smrg
3768d522f475Smrg    case 30:
3769d522f475Smrg    case 31:
3770d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
3771d522f475Smrg	break;
3772d522f475Smrg
3773d522f475Smrg#ifdef ALLOWLOGGING
3774d522f475Smrg    case 46:			/* new log file */
3775d522f475Smrg#ifdef ALLOWLOGFILECHANGES
3776d522f475Smrg	/*
3777d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
3778d522f475Smrg	 * arbitrary files accessible to the person running xterm.
3779d522f475Smrg	 */
3780037a25ddSmrg	if (strcmp(buf, "?")) {
3781037a25ddSmrg	    char *bp;
3782dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
3783037a25ddSmrg		if (screen->logfile)
3784037a25ddSmrg		    free(screen->logfile);
3785037a25ddSmrg		screen->logfile = bp;
3786037a25ddSmrg		break;
3787037a25ddSmrg	    }
3788d522f475Smrg	}
3789d522f475Smrg#endif
3790cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3791cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3792d522f475Smrg	break;
3793d522f475Smrg#endif /* ALLOWLOGGING */
3794d522f475Smrg
3795d522f475Smrg    case 50:
3796d522f475Smrg#if OPT_SHIFT_FONTS
3797cd3331d0Smrg	if (*buf == '?') {
3798cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
3799cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
3800cd3331d0Smrg	    ChangeFontRequest(xw, buf);
3801d522f475Smrg	}
3802d522f475Smrg#endif /* OPT_SHIFT_FONTS */
3803d522f475Smrg	break;
3804d522f475Smrg    case 51:
3805d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
3806d522f475Smrg	break;
3807d522f475Smrg
3808d522f475Smrg#if OPT_PASTE64
3809d522f475Smrg    case 52:
3810cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
3811d522f475Smrg	break;
3812d522f475Smrg#endif
3813d522f475Smrg	/*
3814d522f475Smrg	 * One could write code to send back the display and host names,
3815d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
3816d522f475Smrg	 */
3817cd3331d0Smrg    default:
3818cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
3819cd3331d0Smrg	break;
3820d522f475Smrg    }
3821d522f475Smrg    unparse_end(xw);
3822d522f475Smrg}
3823d522f475Smrg
3824d522f475Smrg/*
3825d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
3826d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
3827d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
3828d522f475Smrg * "real" terminals accept commas in the string definitions).
3829d522f475Smrg */
3830d522f475Smrgstatic int
3831cd3331d0Smrgudk_value(const char **cp)
3832d522f475Smrg{
3833cd3331d0Smrg    int result = -1;
3834d522f475Smrg
3835d522f475Smrg    for (;;) {
3836037a25ddSmrg	int c;
3837037a25ddSmrg
3838d522f475Smrg	if ((c = **cp) != '\0')
3839d522f475Smrg	    *cp = *cp + 1;
3840d522f475Smrg	if (c == ';' || c == '\0')
3841cd3331d0Smrg	    break;
3842cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
3843cd3331d0Smrg	    break;
3844d522f475Smrg    }
3845cd3331d0Smrg
3846cd3331d0Smrg    return result;
3847d522f475Smrg}
3848d522f475Smrg
3849d522f475Smrgvoid
38509a64e1c5Smrgreset_decudk(XtermWidget xw)
3851d522f475Smrg{
3852d522f475Smrg    int n;
3853d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
38549a64e1c5Smrg	if (xw->work.user_keys[n].str != 0) {
38559a64e1c5Smrg	    free(xw->work.user_keys[n].str);
38569a64e1c5Smrg	    xw->work.user_keys[n].str = 0;
38579a64e1c5Smrg	    xw->work.user_keys[n].len = 0;
3858d522f475Smrg	}
3859d522f475Smrg    }
3860d522f475Smrg}
3861d522f475Smrg
3862d522f475Smrg/*
3863d522f475Smrg * Parse the data for DECUDK (user-defined keys).
3864d522f475Smrg */
3865d522f475Smrgstatic void
38669a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
3867d522f475Smrg{
3868d522f475Smrg    while (*cp) {
3869cd3331d0Smrg	const char *base = cp;
3870dfb07bc7Smrg	char *str = TextAlloc(strlen(cp) + 2);
3871d522f475Smrg	unsigned key = 0;
3872d522f475Smrg	int len = 0;
3873d522f475Smrg
387494644356Smrg	if (str == NULL)
387594644356Smrg	    break;
387694644356Smrg
3877d522f475Smrg	while (isdigit(CharOf(*cp)))
38780d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
3879037a25ddSmrg
3880d522f475Smrg	if (*cp == '/') {
3881037a25ddSmrg	    int lo, hi;
3882037a25ddSmrg
3883d522f475Smrg	    cp++;
3884d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
3885d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
38860d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
3887d522f475Smrg	    }
3888d522f475Smrg	}
3889d522f475Smrg	if (len > 0 && key < MAX_UDK) {
38903367019cSmrg	    str[len] = '\0';
38919a64e1c5Smrg	    if (xw->work.user_keys[key].str != 0)
38929a64e1c5Smrg		free(xw->work.user_keys[key].str);
38939a64e1c5Smrg	    xw->work.user_keys[key].str = str;
38949a64e1c5Smrg	    xw->work.user_keys[key].len = len;
3895d522f475Smrg	} else {
3896d522f475Smrg	    free(str);
3897d522f475Smrg	}
3898d522f475Smrg	if (*cp == ';')
3899d522f475Smrg	    cp++;
3900d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
3901d522f475Smrg	    break;
3902d522f475Smrg    }
3903d522f475Smrg}
3904d522f475Smrg
3905fa3f02f3Smrg/*
3906fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
3907fa3f02f3Smrg * interspersing with control characters, but have the string already.
3908fa3f02f3Smrg */
3909fa3f02f3Smrgstatic void
3910fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
3911fa3f02f3Smrg{
3912fa3f02f3Smrg    const char *cp = *string;
3913fa3f02f3Smrg    ParmType nparam = 0;
3914fa3f02f3Smrg    int last_empty = 1;
3915fa3f02f3Smrg
3916fa3f02f3Smrg    memset(params, 0, sizeof(*params));
3917fa3f02f3Smrg    while (*cp != '\0') {
3918fa3f02f3Smrg	Char ch = CharOf(*cp++);
3919fa3f02f3Smrg
3920fa3f02f3Smrg	if (isdigit(ch)) {
3921fa3f02f3Smrg	    last_empty = 0;
3922fa3f02f3Smrg	    if (nparam < NPARAM) {
3923fa3f02f3Smrg		params->a_param[nparam] =
3924fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
3925fa3f02f3Smrg				+ (ch - '0'));
3926fa3f02f3Smrg	    }
3927fa3f02f3Smrg	} else if (ch == ';') {
3928fa3f02f3Smrg	    last_empty = 1;
3929fa3f02f3Smrg	    nparam++;
3930fa3f02f3Smrg	} else if (ch < 32) {
3931fa3f02f3Smrg	    /* EMPTY */ ;
3932fa3f02f3Smrg	} else {
3933fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
3934fa3f02f3Smrg	    params->a_final = ch;
3935fa3f02f3Smrg	    break;
3936fa3f02f3Smrg	}
3937fa3f02f3Smrg    }
3938fa3f02f3Smrg
3939fa3f02f3Smrg    *string = cp;
3940fa3f02f3Smrg    if (!last_empty)
3941fa3f02f3Smrg	nparam++;
3942fa3f02f3Smrg    if (nparam > NPARAM)
3943fa3f02f3Smrg	params->a_nparam = NPARAM;
3944fa3f02f3Smrg    else
3945fa3f02f3Smrg	params->a_nparam = nparam;
3946fa3f02f3Smrg}
3947fa3f02f3Smrg
3948d522f475Smrg#if OPT_TRACE
3949d522f475Smrg#define SOFT_WIDE 10
3950d522f475Smrg#define SOFT_HIGH 20
3951d522f475Smrg
3952d522f475Smrgstatic void
3953fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
3954d522f475Smrg{
3955d522f475Smrg    char DscsName[8];
3956d522f475Smrg    int len;
3957d522f475Smrg    int Pfn = params->a_param[0];
3958d522f475Smrg    int Pcn = params->a_param[1];
3959d522f475Smrg    int Pe = params->a_param[2];
3960d522f475Smrg    int Pcmw = params->a_param[3];
3961d522f475Smrg    int Pw = params->a_param[4];
3962d522f475Smrg    int Pt = params->a_param[5];
3963d522f475Smrg    int Pcmh = params->a_param[6];
3964d522f475Smrg    int Pcss = params->a_param[7];
3965d522f475Smrg
3966d522f475Smrg    int start_char = Pcn + 0x20;
3967d522f475Smrg    int char_wide = ((Pcmw == 0)
3968d522f475Smrg		     ? (Pcss ? 6 : 10)
3969d522f475Smrg		     : (Pcmw > 4
3970d522f475Smrg			? Pcmw
3971d522f475Smrg			: (Pcmw + 3)));
3972d522f475Smrg    int char_high = ((Pcmh == 0)
39733367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
3974d522f475Smrg			? 10
3975d522f475Smrg			: 20)
3976d522f475Smrg		     : Pcmh);
3977d522f475Smrg    Char ch;
3978d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
3979d522f475Smrg    Bool first = True;
3980d522f475Smrg    Bool prior = False;
3981d522f475Smrg    int row = 0, col = 0;
3982d522f475Smrg
3983d522f475Smrg    TRACE(("Parsing DECDLD\n"));
3984d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
3985d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
3986d522f475Smrg    TRACE(("  erase control %d\n", Pe));
3987d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
3988d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
3989d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
3990d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
3991d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
3992d522f475Smrg
3993d522f475Smrg    if (Pfn > 1
3994d522f475Smrg	|| Pcn > 95
3995d522f475Smrg	|| Pe > 2
3996d522f475Smrg	|| Pcmw > 10
3997d522f475Smrg	|| Pcmw == 1
3998d522f475Smrg	|| Pt > 2
3999d522f475Smrg	|| Pcmh > 20
4000d522f475Smrg	|| Pcss > 1
4001d522f475Smrg	|| char_wide > SOFT_WIDE
4002d522f475Smrg	|| char_high > SOFT_HIGH) {
4003d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4004d522f475Smrg	return;
4005d522f475Smrg    }
4006d522f475Smrg
4007d522f475Smrg    len = 0;
4008d522f475Smrg    while (*string != '\0') {
4009d522f475Smrg	ch = CharOf(*string++);
4010d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4011d522f475Smrg	    if (len < 2)
4012b7c89284Ssnj		DscsName[len++] = (char) ch;
4013d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4014b7c89284Ssnj	    DscsName[len++] = (char) ch;
4015d522f475Smrg	    break;
4016d522f475Smrg	}
4017d522f475Smrg    }
4018d522f475Smrg    DscsName[len] = 0;
4019d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4020d522f475Smrg
4021d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4022d522f475Smrg    while (*string != '\0') {
4023d522f475Smrg	if (first) {
4024d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4025d522f475Smrg	    if (prior) {
4026d522f475Smrg		for (row = 0; row < char_high; ++row) {
4027d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4028d522f475Smrg		}
4029d522f475Smrg	    }
4030d522f475Smrg	    prior = False;
4031d522f475Smrg	    first = False;
4032d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4033d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4034d522f475Smrg		    bits[row][col] = '.';
4035d522f475Smrg		}
4036d522f475Smrg	    }
4037d522f475Smrg	    row = col = 0;
4038d522f475Smrg	}
4039d522f475Smrg	ch = CharOf(*string++);
4040d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4041d522f475Smrg	    int n;
4042d522f475Smrg
4043b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4044d522f475Smrg	    for (n = 0; n < 6; ++n) {
4045b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4046d522f475Smrg	    }
4047d522f475Smrg	    col += 1;
4048d522f475Smrg	    prior = True;
4049d522f475Smrg	} else if (ch == '/') {
4050d522f475Smrg	    row += 6;
4051d522f475Smrg	    col = 0;
4052d522f475Smrg	} else if (ch == ';') {
4053d522f475Smrg	    first = True;
4054d522f475Smrg	    ++start_char;
4055d522f475Smrg	}
4056d522f475Smrg    }
4057d522f475Smrg}
4058d522f475Smrg#else
4059d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4060d522f475Smrg#endif
4061d522f475Smrg
4062d522f475Smrgvoid
4063fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4064d522f475Smrg{
4065cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4066d522f475Smrg    char reply[BUFSIZ];
4067cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4068d522f475Smrg    Bool okay;
4069d522f475Smrg    ANSI params;
4070d522f475Smrg
4071cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4072d522f475Smrg
4073d522f475Smrg    if (dcslen != strlen(cp))
4074d522f475Smrg	/* shouldn't have nulls in the string */
4075d522f475Smrg	return;
4076d522f475Smrg
4077d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4078d522f475Smrg    case '$':			/* DECRQSS */
4079d522f475Smrg	okay = True;
4080d522f475Smrg
4081d522f475Smrg	cp++;
4082d522f475Smrg	if (*cp++ == 'q') {
4083d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4084d522f475Smrg		sprintf(reply, "%d%s",
4085d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4086d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4087d522f475Smrg			cp);
4088d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
40893367019cSmrg		if (screen->vtXX_level < 2) {
40903367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
40913367019cSmrg		    break;
40923367019cSmrg		}
4093d522f475Smrg		sprintf(reply, "%d%s%s",
4094d522f475Smrg			(screen->vtXX_level ?
4095d522f475Smrg			 screen->vtXX_level : 1) + 60,
4096d522f475Smrg			(screen->vtXX_level >= 2)
4097d522f475Smrg			? (screen->control_eight_bits
4098d522f475Smrg			   ? ";0" : ";1")
4099d522f475Smrg			: "",
4100d522f475Smrg			cp);
4101d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4102d522f475Smrg		sprintf(reply, "%d;%dr",
4103d522f475Smrg			screen->top_marg + 1,
4104d522f475Smrg			screen->bot_marg + 1);
41053367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
41063367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
41073367019cSmrg		    sprintf(reply, "%d;%ds",
41083367019cSmrg			    screen->lft_marg + 1,
41093367019cSmrg			    screen->rgt_marg + 1);
4110037a25ddSmrg		} else {
4111037a25ddSmrg		    okay = False;
41123367019cSmrg		}
4113d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4114d522f475Smrg		strcpy(reply, "0");
4115d522f475Smrg		if (xw->flags & BOLD)
4116d522f475Smrg		    strcat(reply, ";1");
4117d522f475Smrg		if (xw->flags & UNDERLINE)
4118d522f475Smrg		    strcat(reply, ";4");
4119d522f475Smrg		if (xw->flags & BLINK)
4120d522f475Smrg		    strcat(reply, ";5");
4121d522f475Smrg		if (xw->flags & INVERSE)
4122d522f475Smrg		    strcat(reply, ";7");
4123d522f475Smrg		if (xw->flags & INVISIBLE)
4124d522f475Smrg		    strcat(reply, ";8");
4125b7c89284Ssnj#if OPT_256_COLORS || OPT_88_COLORS
4126b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4127d522f475Smrg		    if (xw->flags & FG_COLOR) {
4128d522f475Smrg			if (xw->cur_foreground >= 16)
4129d522f475Smrg			    sprintf(reply + strlen(reply),
4130d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
4131d522f475Smrg			else
4132d522f475Smrg			    sprintf(reply + strlen(reply),
4133d522f475Smrg				    ";%d%d",
4134d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
4135d522f475Smrg				    xw->cur_foreground >= 8 ?
4136d522f475Smrg				    xw->cur_foreground - 8 :
4137d522f475Smrg				    xw->cur_foreground);
4138d522f475Smrg		    }
4139d522f475Smrg		    if (xw->flags & BG_COLOR) {
4140d522f475Smrg			if (xw->cur_background >= 16)
4141d522f475Smrg			    sprintf(reply + strlen(reply),
4142d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
4143d522f475Smrg			else
4144d522f475Smrg			    sprintf(reply + strlen(reply),
4145d522f475Smrg				    ";%d%d",
4146d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
4147d522f475Smrg				    xw->cur_background >= 8 ?
4148d522f475Smrg				    xw->cur_background - 8 :
4149d522f475Smrg				    xw->cur_background);
4150d522f475Smrg		    }
4151d522f475Smrg		});
4152b7c89284Ssnj#elif OPT_ISO_COLORS
4153b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4154d522f475Smrg		    if (xw->flags & FG_COLOR)
4155d522f475Smrg			sprintf(reply + strlen(reply),
4156d522f475Smrg				";%d%d",
4157d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
4158d522f475Smrg				xw->cur_foreground >= 8 ?
4159d522f475Smrg				xw->cur_foreground - 8 :
4160d522f475Smrg				xw->cur_foreground);
4161d522f475Smrg		    if (xw->flags & BG_COLOR)
4162d522f475Smrg			sprintf(reply + strlen(reply),
4163d522f475Smrg				";%d%d",
4164d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
4165d522f475Smrg				xw->cur_background >= 8 ?
4166d522f475Smrg				xw->cur_background - 8 :
4167d522f475Smrg				xw->cur_background);
4168d522f475Smrg		});
4169b7c89284Ssnj#endif
4170d522f475Smrg		strcat(reply, "m");
4171712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
41723367019cSmrg		int code = STEADY_BLOCK;
41733367019cSmrg		if (isCursorUnderline(screen))
41743367019cSmrg		    code = STEADY_UNDERLINE;
41753367019cSmrg		else if (isCursorBar(screen))
41763367019cSmrg		    code = STEADY_BAR;
41773367019cSmrg#if OPT_BLINK_CURS
417894644356Smrg		if (screen->cursor_blink_esc != 0)
41793367019cSmrg		    code -= 1;
41803367019cSmrg#endif
41813367019cSmrg		sprintf(reply, "%d%s", code, cp);
4182d522f475Smrg	    } else
4183d522f475Smrg		okay = False;
4184d522f475Smrg
418522d8e007Schristos	    if (okay) {
41860d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
41873367019cSmrg		unparseputc(xw, '1');
41880d92cbfdSchristos		unparseputc(xw, '$');
41890d92cbfdSchristos		unparseputc(xw, 'r');
4190d522f475Smrg		cp = reply;
419122d8e007Schristos		unparseputs(xw, cp);
41920d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
41930d92cbfdSchristos	    } else {
41940d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
419522d8e007Schristos	    }
4196d522f475Smrg	} else {
4197d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4198d522f475Smrg	}
4199d522f475Smrg	break;
4200d522f475Smrg#if OPT_TCAP_QUERY
4201d522f475Smrg    case '+':
4202d522f475Smrg	cp++;
4203cd3331d0Smrg	switch (*cp) {
4204cd3331d0Smrg	case 'p':
4205cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4206cd3331d0Smrg		set_termcap(xw, cp + 1);
4207cd3331d0Smrg	    }
4208cd3331d0Smrg	    break;
4209cd3331d0Smrg	case 'q':
4210cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4211cd3331d0Smrg		Bool fkey;
4212cd3331d0Smrg		unsigned state;
4213cd3331d0Smrg		int code;
4214cd3331d0Smrg		const char *tmp;
4215cd3331d0Smrg		const char *parsed = ++cp;
4216d522f475Smrg
4217cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4218d522f475Smrg
4219cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4220b7c89284Ssnj
4221cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4222d522f475Smrg
4223cd3331d0Smrg		unparseputc(xw, '+');
4224cd3331d0Smrg		unparseputc(xw, 'r');
4225d522f475Smrg
4226cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4227cd3331d0Smrg		    if (cp == parsed)
4228cd3331d0Smrg			break;	/* no data found, error */
4229d522f475Smrg
4230cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4231cd3331d0Smrg			unparseputc(xw, *tmp);
4232d522f475Smrg
4233cd3331d0Smrg		    if (code >= 0) {
4234cd3331d0Smrg			unparseputc(xw, '=');
4235cd3331d0Smrg			screen->tc_query_code = code;
4236cd3331d0Smrg			screen->tc_query_fkey = fkey;
4237d522f475Smrg#if OPT_ISO_COLORS
4238cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4239cd3331d0Smrg			 * number of colors) */
4240cd3331d0Smrg			if (code == XK_COLORS) {
4241cd3331d0Smrg			    unparseputn(xw, NUM_ANSI_COLORS);
4242cd3331d0Smrg			} else
4243cd3331d0Smrg#endif
4244cd3331d0Smrg			if (code == XK_TCAPNAME) {
4245c219fbebSmrg			    unparseputs(xw, resource.term_name);
4246cd3331d0Smrg			} else {
4247cd3331d0Smrg			    XKeyEvent event;
4248cd3331d0Smrg			    event.state = state;
4249cd3331d0Smrg			    Input(xw, &event, False);
4250cd3331d0Smrg			}
4251cd3331d0Smrg			screen->tc_query_code = -1;
4252cd3331d0Smrg		    } else {
4253cd3331d0Smrg			break;	/* no match found, error */
4254d522f475Smrg		    }
4255d522f475Smrg
4256d522f475Smrg		    cp = parsed;
4257cd3331d0Smrg		    if (*parsed == ';') {
4258cd3331d0Smrg			unparseputc(xw, *parsed++);
4259cd3331d0Smrg			cp = parsed;
4260cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4261cd3331d0Smrg		    }
4262d522f475Smrg		}
4263cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4264d522f475Smrg	    }
4265cd3331d0Smrg	    break;
4266d522f475Smrg	}
4267d522f475Smrg	break;
4268d522f475Smrg#endif
4269d522f475Smrg    default:
4270fa3f02f3Smrg	if (screen->terminal_id == 125 ||
4271fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
42720d92cbfdSchristos	    parse_ansi_params(&params, &cp);
42730d92cbfdSchristos	    switch (params.a_final) {
4274fa3f02f3Smrg	    case 'p':
42759a64e1c5Smrg#if OPT_REGIS_GRAPHICS
4276fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4277fa3f02f3Smrg		    screen->terminal_id == 240 ||
4278fa3f02f3Smrg		    screen->terminal_id == 241 ||
4279fa3f02f3Smrg		    screen->terminal_id == 330 ||
4280fa3f02f3Smrg		    screen->terminal_id == 340) {
4281fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4282fa3f02f3Smrg		}
42839a64e1c5Smrg#else
42849a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
42859a64e1c5Smrg#endif
4286fa3f02f3Smrg		break;
4287fa3f02f3Smrg	    case 'q':
42889a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
4289fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4290fa3f02f3Smrg		    screen->terminal_id == 240 ||
4291fa3f02f3Smrg		    screen->terminal_id == 241 ||
4292fa3f02f3Smrg		    screen->terminal_id == 330 ||
42939a64e1c5Smrg		    screen->terminal_id == 340 ||
42949a64e1c5Smrg		    screen->terminal_id == 382) {
4295037a25ddSmrg		    (void) parse_sixel(xw, &params, cp);
4296fa3f02f3Smrg		}
42979a64e1c5Smrg#else
42989a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
4299fa3f02f3Smrg#endif
43009a64e1c5Smrg		break;
43010d92cbfdSchristos	    case '|':		/* DECUDK */
43029a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
43039a64e1c5Smrg		    if (params.a_param[0] == 0)
43049a64e1c5Smrg			reset_decudk(xw);
43059a64e1c5Smrg		    parse_decudk(xw, cp);
43069a64e1c5Smrg		}
43070d92cbfdSchristos		break;
430894644356Smrg	    case L_CURL:	/* DECDLD */
43099a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
43109a64e1c5Smrg		    parse_decdld(&params, cp);
43119a64e1c5Smrg		}
43120d92cbfdSchristos		break;
43130d92cbfdSchristos	    }
4314d522f475Smrg	}
4315d522f475Smrg	break;
4316d522f475Smrg    }
4317d522f475Smrg    unparse_end(xw);
4318d522f475Smrg}
4319d522f475Smrg
4320cb4a1343Smrg#if OPT_DEC_RECTOPS
4321cb4a1343Smrgenum {
4322cb4a1343Smrg    mdUnknown = 0,
4323cb4a1343Smrg    mdMaybeSet = 1,
4324cb4a1343Smrg    mdMaybeReset = 2,
4325cb4a1343Smrg    mdAlwaysSet = 3,
4326cb4a1343Smrg    mdAlwaysReset = 4
4327cb4a1343Smrg};
4328cb4a1343Smrg
4329cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
43303367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
4331cb4a1343Smrg
4332cb4a1343Smrg/*
4333cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
4334cb4a1343Smrg * 0 - not recognized
4335cb4a1343Smrg * 1 - set
4336cb4a1343Smrg * 2 - reset
4337cb4a1343Smrg * 3 - permanently set
4338cb4a1343Smrg * 4 - permanently reset
4339cb4a1343Smrg * Only one mode can be reported at a time.
4340cb4a1343Smrg */
4341cb4a1343Smrgvoid
4342cb4a1343Smrgdo_rpm(XtermWidget xw, int nparams, int *params)
4343cb4a1343Smrg{
4344cb4a1343Smrg    ANSI reply;
4345cb4a1343Smrg    int count = 0;
4346cb4a1343Smrg
4347cb4a1343Smrg    TRACE(("do_rpm %d:%d\n", nparams, params[0]));
4348cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4349037a25ddSmrg
4350cb4a1343Smrg    if (nparams >= 1) {
4351037a25ddSmrg	int result = 0;
4352037a25ddSmrg
4353cb4a1343Smrg	switch (params[0]) {
4354cb4a1343Smrg	case 1:		/* GATM */
4355cb4a1343Smrg	    result = mdAlwaysReset;
4356cb4a1343Smrg	    break;
4357cb4a1343Smrg	case 2:
4358cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
4359cb4a1343Smrg	    break;
4360cb4a1343Smrg	case 3:		/* CRM */
4361cb4a1343Smrg	    result = mdMaybeReset;
4362cb4a1343Smrg	    break;
4363cb4a1343Smrg	case 4:
4364cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
4365cb4a1343Smrg	    break;
4366cb4a1343Smrg	case 5:		/* SRTM */
4367cb4a1343Smrg	case 7:		/* VEM */
4368cb4a1343Smrg	case 10:		/* HEM */
4369cb4a1343Smrg	case 11:		/* PUM */
4370cb4a1343Smrg	    result = mdAlwaysReset;
4371cb4a1343Smrg	    break;
4372cb4a1343Smrg	case 12:
4373cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
4374cb4a1343Smrg	    break;
4375cb4a1343Smrg	case 13:		/* FEAM */
4376cb4a1343Smrg	case 14:		/* FETM */
4377cb4a1343Smrg	case 15:		/* MATM */
4378cb4a1343Smrg	case 16:		/* TTM */
4379cb4a1343Smrg	case 17:		/* SATM */
4380cb4a1343Smrg	case 18:		/* TSM */
4381cb4a1343Smrg	case 19:		/* EBM */
4382cb4a1343Smrg	    result = mdAlwaysReset;
4383cb4a1343Smrg	    break;
4384cb4a1343Smrg	case 20:
4385cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
4386cb4a1343Smrg	    break;
4387cb4a1343Smrg	}
4388cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4389cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4390cb4a1343Smrg    }
4391cb4a1343Smrg    reply.a_type = ANSI_CSI;
4392cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4393cb4a1343Smrg    reply.a_inters = '$';
4394cb4a1343Smrg    reply.a_final = 'y';
4395cb4a1343Smrg    unparseseq(xw, &reply);
4396cb4a1343Smrg}
4397cb4a1343Smrg
4398cb4a1343Smrgvoid
4399cb4a1343Smrgdo_decrpm(XtermWidget xw, int nparams, int *params)
4400cb4a1343Smrg{
4401cb4a1343Smrg    ANSI reply;
4402cb4a1343Smrg    int count = 0;
4403cb4a1343Smrg
4404cb4a1343Smrg    TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
4405cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4406037a25ddSmrg
4407cb4a1343Smrg    if (nparams >= 1) {
4408cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
4409037a25ddSmrg	int result = 0;
4410cb4a1343Smrg
4411cb4a1343Smrg	switch (params[0]) {
4412fa3f02f3Smrg	case srm_DECCKM:
4413cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
4414cb4a1343Smrg	    break;
4415fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
4416cb4a1343Smrg#if OPT_VT52_MODE
44173367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
4418cb4a1343Smrg#else
4419cb4a1343Smrg	    result = mdMaybeSet;
4420cb4a1343Smrg#endif
4421cb4a1343Smrg	    break;
4422fa3f02f3Smrg	case srm_DECCOLM:
4423cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
4424cb4a1343Smrg	    break;
4425fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
4426cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
4427cb4a1343Smrg	    break;
4428fa3f02f3Smrg	case srm_DECSCNM:
4429cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
4430cb4a1343Smrg	    break;
4431fa3f02f3Smrg	case srm_DECOM:
4432cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
4433cb4a1343Smrg	    break;
4434fa3f02f3Smrg	case srm_DECAWM:
4435cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
4436cb4a1343Smrg	    break;
4437fa3f02f3Smrg	case srm_DECARM:
4438cb4a1343Smrg	    result = mdAlwaysReset;
4439cb4a1343Smrg	    break;
4440fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
4441cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
4442cb4a1343Smrg	    break;
4443cb4a1343Smrg#if OPT_TOOLBAR
4444fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
4445cb4a1343Smrg	    result = MdBool(resource.toolBar);
4446cb4a1343Smrg	    break;
4447cb4a1343Smrg#endif
4448cb4a1343Smrg#if OPT_BLINK_CURS
4449fa3f02f3Smrg	case srm_ATT610_BLINK:	/* att610: Start/stop blinking cursor */
4450cb4a1343Smrg	    result = MdBool(screen->cursor_blink_res);
4451cb4a1343Smrg	    break;
4452cb4a1343Smrg#endif
4453fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
4454712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
4455cb4a1343Smrg	    break;
4456fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
4457712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
4458cb4a1343Smrg	    break;
4459fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
4460cb4a1343Smrg	    result = MdBool(screen->cursor_set);
4461cb4a1343Smrg	    break;
4462fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
4463cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
4464cb4a1343Smrg	    break;
4465cb4a1343Smrg#if OPT_SHIFT_FONTS
4466fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
4467cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
4468cb4a1343Smrg	    break;
4469cb4a1343Smrg#endif
4470cb4a1343Smrg#if OPT_TEK4014
4471fa3f02f3Smrg	case srm_DECTEK:
4472cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
4473cb4a1343Smrg	    break;
4474cb4a1343Smrg#endif
4475fa3f02f3Smrg	case srm_132COLS:
4476cb4a1343Smrg	    result = MdBool(screen->c132);
4477cb4a1343Smrg	    break;
4478fa3f02f3Smrg	case srm_CURSES_HACK:
4479cb4a1343Smrg	    result = MdBool(screen->curses);
4480cb4a1343Smrg	    break;
4481fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
4482cb4a1343Smrg	    result = MdFlag(xw->flags, NATIONAL);
4483cb4a1343Smrg	    break;
4484fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
4485cb4a1343Smrg	    result = MdBool(screen->marginbell);
4486cb4a1343Smrg	    break;
4487fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
4488cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSEWRAP);
4489cb4a1343Smrg	    break;
4490cb4a1343Smrg#ifdef ALLOWLOGGING
4491fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
4492cb4a1343Smrg#ifdef ALLOWLOGFILEONOFF
4493cb4a1343Smrg	    result = MdBool(screen->logging);
4494cb4a1343Smrg#endif /* ALLOWLOGFILEONOFF */
4495cb4a1343Smrg	    break;
4496cb4a1343Smrg#endif
4497fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
4498cb4a1343Smrg	    /* FALLTHRU */
4499fa3f02f3Smrg	case srm_OPT_ALTBUF:
4500cb4a1343Smrg	    /* FALLTHRU */
4501fa3f02f3Smrg	case srm_ALTBUF:
4502cb4a1343Smrg	    result = MdBool(screen->whichBuf);
4503cb4a1343Smrg	    break;
4504fa3f02f3Smrg	case srm_DECNKM:
4505cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
4506cb4a1343Smrg	    break;
4507fa3f02f3Smrg	case srm_DECBKM:
4508cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
4509cb4a1343Smrg	    break;
4510fa3f02f3Smrg	case srm_DECLRMM:
45113367019cSmrg	    result = MdFlag(xw->flags, LEFT_RIGHT);
45123367019cSmrg	    break;
4513fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
4514fa3f02f3Smrg	case srm_DECSDM:
4515fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
4516fa3f02f3Smrg	    break;
4517fa3f02f3Smrg#endif
4518fa3f02f3Smrg	case srm_DECNCSM:
45193367019cSmrg	    result = MdFlag(xw->flags, NOCLEAR_COLM);
45203367019cSmrg	    break;
4521fa3f02f3Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence         */
4522cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
4523cb4a1343Smrg	    break;
4524fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
4525cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
4526cb4a1343Smrg	    break;
4527fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
4528cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
4529cb4a1343Smrg	    break;
4530fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
4531cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
4532cb4a1343Smrg	    break;
4533cb4a1343Smrg#if OPT_FOCUS_EVENT
4534fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
4535cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
4536cb4a1343Smrg	    break;
4537cb4a1343Smrg#endif
4538fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
45393367019cSmrg	    /* FALLTHRU */
4540fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
45413367019cSmrg	    /* FALLTHRU */
4542fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
45433367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
45443367019cSmrg	    break;
4545fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
45463367019cSmrg	    result = MdBool(screen->alternateScroll);
4547cb4a1343Smrg	    break;
4548fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
4549cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
4550cb4a1343Smrg	    break;
4551fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
4552cb4a1343Smrg	    result = MdBool(screen->scrollkey);
4553cb4a1343Smrg	    break;
4554fa3f02f3Smrg	case srm_EIGHT_BIT_META:
45553367019cSmrg	    result = MdBool(screen->eight_bit_meta);
4556cb4a1343Smrg	    break;
4557cb4a1343Smrg#if OPT_NUM_LOCK
4558fa3f02f3Smrg	case srm_REAL_NUMLOCK:
4559cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
4560cb4a1343Smrg	    break;
4561fa3f02f3Smrg	case srm_META_SENDS_ESC:
4562cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
4563cb4a1343Smrg	    break;
4564cb4a1343Smrg#endif
4565fa3f02f3Smrg	case srm_DELETE_IS_DEL:
4566cb4a1343Smrg	    result = MdBool(screen->delete_is_del);
4567cb4a1343Smrg	    break;
4568cb4a1343Smrg#if OPT_NUM_LOCK
4569fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
4570cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
4571cb4a1343Smrg	    break;
4572cb4a1343Smrg#endif
4573fa3f02f3Smrg	case srm_KEEP_SELECTION:
4574cb4a1343Smrg	    result = MdBool(screen->keepSelection);
4575cb4a1343Smrg	    break;
4576fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
4577cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
4578cb4a1343Smrg	    break;
4579fa3f02f3Smrg	case srm_BELL_IS_URGENT:
4580cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
4581cb4a1343Smrg	    break;
4582fa3f02f3Smrg	case srm_POP_ON_BELL:
4583cb4a1343Smrg	    result = MdBool(screen->poponbell);
4584cb4a1343Smrg	    break;
4585fa3f02f3Smrg	case srm_TITE_INHIBIT:
4586cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
4587cb4a1343Smrg	    break;
4588cb4a1343Smrg#if OPT_TCAP_FKEYS
4589fa3f02f3Smrg	case srm_TCAP_FKEYS:
4590cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
4591cb4a1343Smrg	    break;
4592cb4a1343Smrg#endif
4593cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
4594fa3f02f3Smrg	case srm_SUN_FKEYS:
4595cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
4596cb4a1343Smrg	    break;
4597cb4a1343Smrg#endif
4598cb4a1343Smrg#if OPT_HP_FUNC_KEYS
4599fa3f02f3Smrg	case srm_HP_FKEYS:
4600cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
4601cb4a1343Smrg	    break;
4602cb4a1343Smrg#endif
4603cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
4604fa3f02f3Smrg	case srm_SCO_FKEYS:
4605cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
4606cb4a1343Smrg	    break;
4607cb4a1343Smrg#endif
4608fa3f02f3Smrg	case srm_LEGACY_FKEYS:
4609cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
4610cb4a1343Smrg	    break;
4611cb4a1343Smrg#if OPT_SUNPC_KBD
4612fa3f02f3Smrg	case srm_VT220_FKEYS:
4613cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
4614cb4a1343Smrg	    break;
4615cb4a1343Smrg#endif
4616cb4a1343Smrg#if OPT_READLINE
4617fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
4618cb4a1343Smrg	    result = MdBool(screen->click1_moves);
4619cb4a1343Smrg	    break;
4620fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
4621cb4a1343Smrg	    result = MdBool(screen->paste_moves);
4622cb4a1343Smrg	    break;
4623fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
4624cb4a1343Smrg	    result = MdBool(screen->dclick3_deletes);
4625cb4a1343Smrg	    break;
4626fa3f02f3Smrg	case srm_PASTE_IN_BRACKET:
4627cb4a1343Smrg	    result = MdBool(screen->paste_brackets);
4628cb4a1343Smrg	    break;
4629fa3f02f3Smrg	case srm_PASTE_QUOTE:
4630cb4a1343Smrg	    result = MdBool(screen->paste_quotes);
4631cb4a1343Smrg	    break;
4632fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
4633cb4a1343Smrg	    result = MdBool(screen->paste_literal_nl);
4634cb4a1343Smrg	    break;
4635cb4a1343Smrg#endif /* OPT_READLINE */
46369a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
46379a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
46389a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
46399a64e1c5Smrg	    break;
46409a64e1c5Smrg#endif
46419a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
46429a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
46439a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
46449a64e1c5Smrg	    break;
46459a64e1c5Smrg#endif
46469a64e1c5Smrg	default:
46479a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
46489a64e1c5Smrg		   params[0]));
4649cb4a1343Smrg	}
4650cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4651cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4652cb4a1343Smrg    }
4653cb4a1343Smrg    reply.a_type = ANSI_CSI;
4654cb4a1343Smrg    reply.a_pintro = '?';
4655cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4656cb4a1343Smrg    reply.a_inters = '$';
4657cb4a1343Smrg    reply.a_final = 'y';
4658cb4a1343Smrg    unparseseq(xw, &reply);
4659cb4a1343Smrg}
4660cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
4661cb4a1343Smrg
4662d522f475Smrgchar *
46639a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
4664d522f475Smrg{
4665d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
46669a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
46679a64e1c5Smrg	return xw->work.user_keys[keycode].str;
4668d522f475Smrg    }
4669d522f475Smrg    return 0;
4670d522f475Smrg}
4671d522f475Smrg
46723367019cSmrg#ifdef HAVE_LIBXPM
46733367019cSmrg
46743367019cSmrg#ifndef PIXMAP_ROOTDIR
46753367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
46763367019cSmrg#endif
46773367019cSmrg
46783367019cSmrgtypedef struct {
46793367019cSmrg    const char *name;
46803367019cSmrg    const char *const *data;
46813367019cSmrg} XPM_DATA;
46823367019cSmrg
46833367019cSmrgstatic char *
46843367019cSmrgx_find_icon(char **work, int *state, const char *suffix)
46853367019cSmrg{
46863367019cSmrg    const char *filename = resource.icon_hint;
46873367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
46883367019cSmrg    const char *larger = "_48x48";
46893367019cSmrg    char *result = 0;
46903367019cSmrg
46913367019cSmrg    if (*state >= 0) {
46923367019cSmrg	if ((*state & 1) == 0)
46933367019cSmrg	    suffix = "";
46943367019cSmrg	if ((*state & 2) == 0)
46953367019cSmrg	    larger = "";
46963367019cSmrg	if ((*state & 4) == 0) {
46973367019cSmrg	    prefix = "";
46983367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
46993367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
47003367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
47013367019cSmrg	    *state = -1;
47023367019cSmrg	} else if (*state >= 8) {
47033367019cSmrg	    *state = -1;
47043367019cSmrg	}
47053367019cSmrg    }
47063367019cSmrg
47073367019cSmrg    if (*state >= 0) {
4708037a25ddSmrg	size_t length;
4709037a25ddSmrg
47103367019cSmrg	if (*work) {
47113367019cSmrg	    free(*work);
47123367019cSmrg	    *work = 0;
47133367019cSmrg	}
47143367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
47153367019cSmrg	    strlen(suffix);
47163367019cSmrg	if ((result = malloc(length)) != 0) {
47173367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
47183367019cSmrg	    *work = result;
47193367019cSmrg	}
47203367019cSmrg	*state += 1;
47213367019cSmrg	TRACE(("x_find_icon %d:%s\n", *state, result));
47223367019cSmrg    }
47233367019cSmrg    return result;
47243367019cSmrg}
47253367019cSmrg
47263367019cSmrg#if OPT_BUILTIN_XPMS
47273367019cSmrgstatic const XPM_DATA *
47283367019cSmrgBuiltInXPM(const XPM_DATA * table, Cardinal length)
47293367019cSmrg{
47303367019cSmrg    const char *find = resource.icon_hint;
47313367019cSmrg    const XPM_DATA *result = 0;
47323367019cSmrg    if (!IsEmpty(find)) {
47333367019cSmrg	Cardinal n;
47343367019cSmrg	for (n = 0; n < length; ++n) {
47353367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
47363367019cSmrg		result = table + n;
47373367019cSmrg		break;
47383367019cSmrg	    }
47393367019cSmrg	}
47403367019cSmrg
47413367019cSmrg	/*
47423367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
47433367019cSmrg	 * which are all _HHxWW format.
47443367019cSmrg	 */
47453367019cSmrg	if (result == 0) {
47463367019cSmrg	    const char *base = table[0].name;
47473367019cSmrg	    const char *last = strchr(base, '_');
47483367019cSmrg	    if (last != 0
47493367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
47503367019cSmrg		result = table + length - 1;
47513367019cSmrg	    }
47523367019cSmrg	}
47533367019cSmrg    }
47543367019cSmrg    return result;
47553367019cSmrg}
47563367019cSmrg#endif /* OPT_BUILTIN_XPMS */
47573367019cSmrg
47583367019cSmrgtypedef enum {
47593367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
47603367019cSmrg    ,eHintNone
47613367019cSmrg    ,eHintSearch
47623367019cSmrg} ICON_HINT;
47633367019cSmrg
47643367019cSmrgstatic ICON_HINT
47653367019cSmrgwhich_icon_hint(void)
47663367019cSmrg{
47673367019cSmrg    ICON_HINT result = eHintDefault;
47683367019cSmrg    if (!IsEmpty(resource.icon_hint)) {
47693367019cSmrg	if (!x_strcasecmp(resource.icon_hint, "none")) {
47703367019cSmrg	    result = eHintNone;
47713367019cSmrg	} else {
47723367019cSmrg	    result = eHintSearch;
47733367019cSmrg	}
47743367019cSmrg    }
47753367019cSmrg    return result;
47763367019cSmrg}
47773367019cSmrg#endif /* HAVE_LIBXPM */
47783367019cSmrg
47793367019cSmrgint
47803367019cSmrggetVisualDepth(XtermWidget xw)
47813367019cSmrg{
47823367019cSmrg    int result = 0;
47833367019cSmrg
4784fa3f02f3Smrg    if (getVisualInfo(xw)) {
4785fa3f02f3Smrg	result = xw->visInfo->depth;
47863367019cSmrg    }
47873367019cSmrg    return result;
47883367019cSmrg}
47893367019cSmrg
47903367019cSmrg/*
47913367019cSmrg * WM_ICON_SIZE should be honored if possible.
47923367019cSmrg */
47933367019cSmrgvoid
47943367019cSmrgxtermLoadIcon(XtermWidget xw)
47953367019cSmrg{
47963367019cSmrg#ifdef HAVE_LIBXPM
47973367019cSmrg    Display *dpy = XtDisplay(xw);
47983367019cSmrg    Pixmap myIcon = 0;
47993367019cSmrg    Pixmap myMask = 0;
48003367019cSmrg    char *workname = 0;
48013367019cSmrg    ICON_HINT hint = which_icon_hint();
4802fa3f02f3Smrg#include <builtin_icons.h>
48033367019cSmrg
48043367019cSmrg    TRACE(("xtermLoadIcon %p:%s\n", (void *) xw, NonNull(resource.icon_hint)));
48053367019cSmrg
48063367019cSmrg    if (hint == eHintSearch) {
48073367019cSmrg	int state = 0;
48083367019cSmrg	while (x_find_icon(&workname, &state, ".xpm") != 0) {
48093367019cSmrg	    Pixmap resIcon = 0;
48103367019cSmrg	    Pixmap shapemask = 0;
48113367019cSmrg	    XpmAttributes attributes;
48123367019cSmrg
48133367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
48143367019cSmrg	    attributes.valuemask = XpmDepth;
48153367019cSmrg
48163367019cSmrg	    if (XpmReadFileToPixmap(dpy,
48173367019cSmrg				    DefaultRootWindow(dpy),
48183367019cSmrg				    workname,
48193367019cSmrg				    &resIcon,
48203367019cSmrg				    &shapemask,
48213367019cSmrg				    &attributes) == XpmSuccess) {
48223367019cSmrg		myIcon = resIcon;
48233367019cSmrg		myMask = shapemask;
48243367019cSmrg		TRACE(("...success\n"));
48253367019cSmrg		break;
48263367019cSmrg	    }
48273367019cSmrg	}
48283367019cSmrg    }
48293367019cSmrg
48303367019cSmrg    /*
48313367019cSmrg     * If no external file was found, look for the name in the built-in table.
48323367019cSmrg     * If that fails, just use the biggest mini-icon.
48333367019cSmrg     */
48343367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
48353367019cSmrg	char **data;
48363367019cSmrg#if OPT_BUILTIN_XPMS
48373367019cSmrg	const XPM_DATA *myData = 0;
48383367019cSmrg	myData = BuiltInXPM(mini_xterm_xpms, XtNumber(mini_xterm_xpms));
48393367019cSmrg	if (myData == 0)
48403367019cSmrg	    myData = BuiltInXPM(filled_xterm_xpms, XtNumber(filled_xterm_xpms));
48413367019cSmrg	if (myData == 0)
48423367019cSmrg	    myData = BuiltInXPM(xterm_color_xpms, XtNumber(xterm_color_xpms));
48433367019cSmrg	if (myData == 0)
48443367019cSmrg	    myData = BuiltInXPM(xterm_xpms, XtNumber(xterm_xpms));
48453367019cSmrg	if (myData == 0)
48463367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
484794644356Smrg	data = (char **) myData->data;
48483367019cSmrg#else
48493367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
48503367019cSmrg#endif
48513367019cSmrg	if (XpmCreatePixmapFromData(dpy,
48523367019cSmrg				    DefaultRootWindow(dpy),
48533367019cSmrg				    data,
48543367019cSmrg				    &myIcon, &myMask, 0) != 0) {
48553367019cSmrg	    myIcon = 0;
48563367019cSmrg	    myMask = 0;
48573367019cSmrg	}
48583367019cSmrg    }
48593367019cSmrg
48603367019cSmrg    if (myIcon != 0) {
48613367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
48623367019cSmrg	if (!hints)
48633367019cSmrg	    hints = XAllocWMHints();
48643367019cSmrg
48653367019cSmrg	if (hints) {
48663367019cSmrg	    hints->flags |= IconPixmapHint;
48673367019cSmrg	    hints->icon_pixmap = myIcon;
48683367019cSmrg	    if (myMask) {
48693367019cSmrg		hints->flags |= IconMaskHint;
48703367019cSmrg		hints->icon_mask = myMask;
48713367019cSmrg	    }
48723367019cSmrg
48733367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
48743367019cSmrg	    XFree(hints);
48753367019cSmrg	    TRACE(("...loaded icon\n"));
48763367019cSmrg	}
48773367019cSmrg    }
48783367019cSmrg
48793367019cSmrg    if (workname != 0)
48803367019cSmrg	free(workname);
48813367019cSmrg
48823367019cSmrg#else
48833367019cSmrg    (void) xw;
48843367019cSmrg#endif
48853367019cSmrg}
48863367019cSmrg
48873367019cSmrgvoid
4888cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
4889d522f475Smrg{
4890d522f475Smrg#if OPT_WIDE_CHARS
4891d522f475Smrg    static Char *converted;	/* NO_LEAKS */
4892d522f475Smrg#endif
4893d522f475Smrg
4894d522f475Smrg    Arg args[1];
4895cd3331d0Smrg    Boolean changed = True;
4896d522f475Smrg    Widget w = CURRENT_EMU();
4897d522f475Smrg    Widget top = SHELL_OF(w);
4898d522f475Smrg
4899cd3331d0Smrg    char *my_attr;
4900cd3331d0Smrg    char *name;
4901cd3331d0Smrg    size_t limit;
4902cd3331d0Smrg    Char *c1;
4903cd3331d0Smrg    Char *cp;
4904d522f475Smrg
4905b7c89284Ssnj    if (!AllowTitleOps(xw))
4906d522f475Smrg	return;
4907d522f475Smrg
4908cd3331d0Smrg    if (value == 0)
49093367019cSmrg	value = emptyString;
4910cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
4911cd3331d0Smrg	const char *temp;
4912cd3331d0Smrg	char *test;
4913cd3331d0Smrg
4914cd3331d0Smrg	value = x_decode_hex(value, &temp);
49153367019cSmrg	if (*temp != '\0') {
49163367019cSmrg	    free(value);
4917cd3331d0Smrg	    return;
49183367019cSmrg	}
4919cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
4920cd3331d0Smrg	    if (CharOf(*test) < 32) {
4921cd3331d0Smrg		*test = '\0';
4922cd3331d0Smrg		break;
4923cd3331d0Smrg	    }
4924cd3331d0Smrg	}
4925cd3331d0Smrg    }
4926cd3331d0Smrg
4927cd3331d0Smrg    c1 = (Char *) value;
4928cd3331d0Smrg    name = value;
4929cd3331d0Smrg    limit = strlen(name);
4930cd3331d0Smrg    my_attr = x_strdup(attribute);
4931cd3331d0Smrg
4932cd3331d0Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
4933cd3331d0Smrg
4934d522f475Smrg    /*
4935d522f475Smrg     * Ignore titles that are too long to be plausible requests.
4936d522f475Smrg     */
4937cd3331d0Smrg    if (limit > 0 && limit < 1024) {
4938d522f475Smrg
4939cd3331d0Smrg	/*
4940cd3331d0Smrg	 * After all decoding, overwrite nonprintable characters with '?'.
4941cd3331d0Smrg	 */
4942cd3331d0Smrg	for (cp = c1; *cp != 0; ++cp) {
4943cd3331d0Smrg	    Char *c2 = cp;
4944cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
4945cd3331d0Smrg		memset(c2, '?', (size_t) (cp + 1 - c2));
4946cd3331d0Smrg	    }
4947d522f475Smrg	}
4948d522f475Smrg
4949d522f475Smrg#if OPT_WIDE_CHARS
4950cd3331d0Smrg	/*
4951cd3331d0Smrg	 * If we're running in UTF-8 mode, and have not been told that the
4952cd3331d0Smrg	 * title string is in UTF-8, it is likely that non-ASCII text in the
4953cd3331d0Smrg	 * string will be rejected because it is not printable in the current
4954cd3331d0Smrg	 * locale.  So we convert it to UTF-8, allowing the X library to
4955cd3331d0Smrg	 * convert it back.
4956cd3331d0Smrg	 */
4957cd3331d0Smrg	if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) {
4958cd3331d0Smrg	    int n;
4959cd3331d0Smrg
4960cd3331d0Smrg	    for (n = 0; name[n] != '\0'; ++n) {
4961cd3331d0Smrg		if (CharOf(name[n]) > 127) {
4962cd3331d0Smrg		    if (converted != 0)
4963cd3331d0Smrg			free(converted);
4964cd3331d0Smrg		    if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
4965cd3331d0Smrg			Char *temp = converted;
4966cd3331d0Smrg			while (*name != 0) {
4967cd3331d0Smrg			    temp = convertToUTF8(temp, CharOf(*name));
4968cd3331d0Smrg			    ++name;
4969cd3331d0Smrg			}
4970cd3331d0Smrg			*temp = 0;
4971cd3331d0Smrg			name = (char *) converted;
4972cd3331d0Smrg			TRACE(("...converted{%s}\n", name));
4973d522f475Smrg		    }
4974cd3331d0Smrg		    break;
4975d522f475Smrg		}
4976d522f475Smrg	    }
4977d522f475Smrg	}
4978d522f475Smrg#endif
4979d522f475Smrg
4980d522f475Smrg#if OPT_SAME_NAME
4981cd3331d0Smrg	/* If the attribute isn't going to change, then don't bother... */
4982cd3331d0Smrg
4983cd3331d0Smrg	if (resource.sameName) {
4984cd3331d0Smrg	    char *buf = 0;
4985cd3331d0Smrg	    XtSetArg(args[0], my_attr, &buf);
4986cd3331d0Smrg	    XtGetValues(top, args, 1);
4987cd3331d0Smrg	    TRACE(("...comparing{%s}\n", buf));
4988cd3331d0Smrg	    if (buf != 0 && strcmp(name, buf) == 0)
4989cd3331d0Smrg		changed = False;
4990cd3331d0Smrg	}
4991d522f475Smrg#endif /* OPT_SAME_NAME */
4992d522f475Smrg
4993cd3331d0Smrg	if (changed) {
4994cd3331d0Smrg	    TRACE(("...updating %s\n", my_attr));
4995cd3331d0Smrg	    TRACE(("...value is %s\n", name));
4996cd3331d0Smrg	    XtSetArg(args[0], my_attr, name);
4997cd3331d0Smrg	    XtSetValues(top, args, 1);
4998d522f475Smrg
4999d522f475Smrg#if OPT_WIDE_CHARS
5000cd3331d0Smrg	    if (xtermEnvUTF8()) {
5001cd3331d0Smrg		Display *dpy = XtDisplay(xw);
5002cd3331d0Smrg		Atom my_atom;
5003cd3331d0Smrg
5004cd3331d0Smrg		const char *propname = (!strcmp(my_attr, XtNtitle)
5005cd3331d0Smrg					? "_NET_WM_NAME"
5006cd3331d0Smrg					: "_NET_WM_ICON_NAME");
5007cd3331d0Smrg		if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
5008cd3331d0Smrg		    if (IsSetUtf8Title(xw)) {
5009cd3331d0Smrg			TRACE(("...updating %s\n", propname));
5010cd3331d0Smrg			TRACE(("...value is %s\n", value));
5011c219fbebSmrg			XChangeProperty(dpy, VShellWindow(xw), my_atom,
5012cd3331d0Smrg					XA_UTF8_STRING(dpy), 8,
5013cd3331d0Smrg					PropModeReplace,
5014cd3331d0Smrg					(Char *) value,
5015cd3331d0Smrg					(int) strlen(value));
5016cd3331d0Smrg		    } else {
5017cd3331d0Smrg			TRACE(("...deleting %s\n", propname));
5018c219fbebSmrg			XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5019cd3331d0Smrg		    }
5020cd3331d0Smrg		}
5021d522f475Smrg	    }
5022cd3331d0Smrg#endif
5023d522f475Smrg	}
5024d522f475Smrg    }
5025037a25ddSmrg    if (IsTitleMode(xw, tmSetBase16) && (value != emptyString)) {
50263367019cSmrg	free(value);
50273367019cSmrg    }
50283367019cSmrg    free(my_attr);
50293367019cSmrg
5030cd3331d0Smrg    return;
5031d522f475Smrg}
5032d522f475Smrg
5033d522f475Smrgvoid
5034b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
5035d522f475Smrg{
5036cd3331d0Smrg    if (name == 0) {
50373367019cSmrg	name = emptyString;
50383367019cSmrg    }
50393367019cSmrg    if (!showZIconBeep(xw, name))
5040b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
5041d522f475Smrg}
5042d522f475Smrg
5043d522f475Smrgvoid
5044b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
5045d522f475Smrg{
5046b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
5047d522f475Smrg}
5048d522f475Smrg
5049712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
5050d522f475Smrg
5051d522f475Smrgvoid
5052d522f475SmrgChangeXprop(char *buf)
5053d522f475Smrg{
5054d522f475Smrg    Display *dpy = XtDisplay(toplevel);
5055d522f475Smrg    Window w = XtWindow(toplevel);
5056d522f475Smrg    XTextProperty text_prop;
5057d522f475Smrg    Atom aprop;
5058d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
5059d522f475Smrg
5060d522f475Smrg    if (pchEndPropName)
5061d522f475Smrg	*pchEndPropName = '\0';
5062d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
5063d522f475Smrg    if (pchEndPropName == NULL) {
5064d522f475Smrg	/* no "=value" given, so delete the property */
5065d522f475Smrg	XDeleteProperty(dpy, w, aprop);
5066d522f475Smrg    } else {
5067d522f475Smrg	text_prop.value = pchEndPropName + 1;
5068d522f475Smrg	text_prop.encoding = XA_STRING;
5069d522f475Smrg	text_prop.format = 8;
5070d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
5071d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
5072d522f475Smrg    }
5073d522f475Smrg}
5074d522f475Smrg
5075d522f475Smrg/***====================================================================***/
5076d522f475Smrg
5077d522f475Smrg/*
5078d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
5079d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
5080d522f475Smrg */
5081d522f475Smrgvoid
50829a64e1c5SmrgReverseOldColors(XtermWidget xw)
5083d522f475Smrg{
50849a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
5085d522f475Smrg    Pixel tmpPix;
5086d522f475Smrg    char *tmpName;
5087d522f475Smrg
5088d522f475Smrg    if (pOld) {
5089d522f475Smrg	/* change text cursor, if necesary */
5090d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
5091d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
5092d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
50939a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
5094d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
5095d522f475Smrg	    }
5096d522f475Smrg	    if (pOld->names[TEXT_BG]) {
5097d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
5098d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
5099d522f475Smrg		}
5100d522f475Smrg	    }
5101d522f475Smrg	}
5102d522f475Smrg
5103d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
5104d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
5105d522f475Smrg
5106d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
5107d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
5108d522f475Smrg
5109d522f475Smrg#if OPT_TEK4014
5110d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
5111d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
5112d522f475Smrg#endif
5113d522f475Smrg    }
5114d522f475Smrg    return;
5115d522f475Smrg}
5116d522f475Smrg
5117d522f475SmrgBool
5118d522f475SmrgAllocateTermColor(XtermWidget xw,
5119d522f475Smrg		  ScrnColors * pNew,
5120d522f475Smrg		  int ndx,
5121cd3331d0Smrg		  const char *name,
5122cd3331d0Smrg		  Bool always)
5123d522f475Smrg{
5124cd3331d0Smrg    Bool result = False;
5125d522f475Smrg
5126cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
5127cd3331d0Smrg	XColor def;
5128cd3331d0Smrg	char *newName;
5129cd3331d0Smrg
5130712a7ff4Smrg	result = True;
5131712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
5132712a7ff4Smrg	    def.pixel = xw->old_foreground;
5133712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
5134712a7ff4Smrg	    def.pixel = xw->old_background;
51353367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
5136712a7ff4Smrg	    result = False;
5137712a7ff4Smrg	}
5138712a7ff4Smrg
5139712a7ff4Smrg	if (result
5140cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
5141712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
5142cd3331d0Smrg		free(pNew->names[ndx]);
5143712a7ff4Smrg	    }
5144cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
5145cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
5146712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
5147712a7ff4Smrg		   ndx, newName, def.pixel));
5148cd3331d0Smrg	} else {
5149cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
5150712a7ff4Smrg	    result = False;
5151cd3331d0Smrg	}
5152cd3331d0Smrg    }
5153cd3331d0Smrg    return result;
5154d522f475Smrg}
5155d522f475Smrg/***====================================================================***/
5156d522f475Smrg
5157d522f475Smrg/* ARGSUSED */
5158d522f475Smrgvoid
5159cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
5160d522f475Smrg{
51613367019cSmrg    if_DEBUG({
51623367019cSmrg	xtermWarning(s, a);
51633367019cSmrg    });
5164d522f475Smrg}
5165d522f475Smrg
5166d522f475Smrgconst char *
5167d522f475SmrgSysErrorMsg(int code)
5168d522f475Smrg{
516994644356Smrg    static const char unknown[] = "unknown error";
5170d522f475Smrg    char *s = strerror(code);
5171d522f475Smrg    return s ? s : unknown;
5172d522f475Smrg}
5173d522f475Smrg
5174d522f475Smrgconst char *
5175d522f475SmrgSysReasonMsg(int code)
5176d522f475Smrg{
5177d522f475Smrg    /* *INDENT-OFF* */
5178d522f475Smrg    static const struct {
5179d522f475Smrg	int code;
5180d522f475Smrg	const char *name;
5181d522f475Smrg    } table[] = {
5182d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
5183d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
5184d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
5185d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
5186d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
5187d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
5188d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
5189d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
5190d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
5191d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
5192d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
5193d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
5194d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
5195d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
5196d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
5197d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
5198d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
5199d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
5200d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
5201d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
5202d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
5203d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
5204d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
5205d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
5206d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
5207d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
5208d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
5209d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
5210d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
5211d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
5212d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
5213d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
5214d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
5215d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
5216d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
5217d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
5218d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
5219d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
5220d522f475Smrg    };
5221d522f475Smrg    /* *INDENT-ON* */
5222d522f475Smrg
5223d522f475Smrg    Cardinal n;
5224d522f475Smrg    const char *result = "?";
5225d522f475Smrg
5226d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
5227d522f475Smrg	if (code == table[n].code) {
5228d522f475Smrg	    result = table[n].name;
5229d522f475Smrg	    break;
5230d522f475Smrg	}
5231d522f475Smrg    }
5232d522f475Smrg    return result;
5233d522f475Smrg}
5234d522f475Smrg
5235d522f475Smrgvoid
5236d522f475SmrgSysError(int code)
5237d522f475Smrg{
5238d522f475Smrg    int oerrno = errno;
5239d522f475Smrg
5240c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
5241d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
5242d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
5243d522f475Smrg
5244d522f475Smrg    Cleanup(code);
5245d522f475Smrg}
5246d522f475Smrg
5247d522f475Smrgvoid
52483367019cSmrgNormalExit(void)
5249d522f475Smrg{
5250d522f475Smrg    static Bool cleaning;
5251d522f475Smrg
5252d522f475Smrg    /*
5253d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
5254d522f475Smrg     */
52553367019cSmrg    if (cleaning) {
52563367019cSmrg	hold_screen = 0;
52573367019cSmrg	return;
52583367019cSmrg    }
5259d522f475Smrg
52603367019cSmrg    cleaning = True;
52613367019cSmrg    need_cleanup = False;
5262d522f475Smrg
52633367019cSmrg    if (hold_screen) {
52643367019cSmrg	hold_screen = 2;
52653367019cSmrg	while (hold_screen) {
52663367019cSmrg	    xevents();
52673367019cSmrg	    Sleep(10);
5268d522f475Smrg	}
52693367019cSmrg    }
5270d522f475Smrg#if OPT_SESSION_MGT
52713367019cSmrg    if (resource.sessionMgt) {
52723367019cSmrg	XtVaSetValues(toplevel,
52733367019cSmrg		      XtNjoinSession, False,
52743367019cSmrg		      (void *) 0);
5275d522f475Smrg    }
52763367019cSmrg#endif
52773367019cSmrg    Cleanup(0);
52783367019cSmrg}
52793367019cSmrg
52803367019cSmrg/*
52813367019cSmrg * cleanup by sending SIGHUP to client processes
52823367019cSmrg */
52833367019cSmrgvoid
52843367019cSmrgCleanup(int code)
52853367019cSmrg{
52863367019cSmrg    TScreen *screen = TScreenOf(term);
52873367019cSmrg
52883367019cSmrg    TRACE(("Cleanup %d\n", code));
5289d522f475Smrg
5290d522f475Smrg    if (screen->pid > 1) {
5291d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
5292d522f475Smrg    }
5293d522f475Smrg    Exit(code);
5294d522f475Smrg}
5295d522f475Smrg
5296fa3f02f3Smrg#ifndef S_IXOTH
5297fa3f02f3Smrg#define S_IXOTH 1
5298fa3f02f3Smrg#endif
5299fa3f02f3Smrg
5300fa3f02f3SmrgBoolean
5301fa3f02f3SmrgvalidProgram(const char *pathname)
5302fa3f02f3Smrg{
5303fa3f02f3Smrg    Boolean result = False;
5304fa3f02f3Smrg    struct stat sb;
5305fa3f02f3Smrg
5306fa3f02f3Smrg    if (!IsEmpty(pathname)
5307fa3f02f3Smrg	&& *pathname == '/'
5308fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
5309fa3f02f3Smrg	&& stat(pathname, &sb) == 0
5310fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
5311fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
5312fa3f02f3Smrg	result = True;
5313fa3f02f3Smrg    }
5314fa3f02f3Smrg    return result;
5315fa3f02f3Smrg}
5316fa3f02f3Smrg
5317d522f475Smrg#ifndef VMS
53183367019cSmrg#ifndef PATH_MAX
53193367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
53203367019cSmrg#endif
5321d522f475Smrgchar *
5322d522f475SmrgxtermFindShell(char *leaf, Bool warning)
5323d522f475Smrg{
53243367019cSmrg    char *s0;
5325d522f475Smrg    char *s;
5326d522f475Smrg    char *d;
5327d522f475Smrg    char *tmp;
5328d522f475Smrg    char *result = leaf;
53293367019cSmrg    Bool allocated = False;
5330d522f475Smrg
5331d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
53323367019cSmrg
53333367019cSmrg    if (!strncmp("./", result, (size_t) 2)
53343367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
53353367019cSmrg	size_t need = PATH_MAX;
53363367019cSmrg	size_t used = strlen(result) + 2;
53373367019cSmrg	char *buffer = malloc(used + need);
53383367019cSmrg	if (buffer != 0) {
53393367019cSmrg	    if (getcwd(buffer, need) != 0) {
53403367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
53413367019cSmrg		result = buffer;
53423367019cSmrg		allocated = True;
53433367019cSmrg	    } else {
53443367019cSmrg		free(buffer);
53453367019cSmrg	    }
53463367019cSmrg	}
53473367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
5348d522f475Smrg	/* find it in $PATH */
53493367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
53500d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
5351d522f475Smrg		Bool found = False;
5352d522f475Smrg		while (*s != '\0') {
5353d522f475Smrg		    strcpy(tmp, s);
5354d522f475Smrg		    for (d = tmp;; ++d) {
5355d522f475Smrg			if (*d == ':' || *d == '\0') {
5356d522f475Smrg			    int skip = (*d != '\0');
5357d522f475Smrg			    *d = '/';
5358d522f475Smrg			    strcpy(d + 1, leaf);
5359d522f475Smrg			    if (skip)
5360d522f475Smrg				++d;
5361d522f475Smrg			    s += (d - tmp);
5362fa3f02f3Smrg			    if (validProgram(tmp)) {
5363d522f475Smrg				result = x_strdup(tmp);
5364d522f475Smrg				found = True;
53653367019cSmrg				allocated = True;
5366d522f475Smrg			    }
5367d522f475Smrg			    break;
5368d522f475Smrg			}
5369d522f475Smrg		    }
5370d522f475Smrg		    if (found)
5371d522f475Smrg			break;
5372d522f475Smrg		}
5373d522f475Smrg		free(tmp);
5374d522f475Smrg	    }
53753367019cSmrg	    free(s0);
5376d522f475Smrg	}
5377d522f475Smrg    }
5378d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
5379fa3f02f3Smrg    if (!validProgram(result)) {
5380d522f475Smrg	if (warning)
53813367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
53823367019cSmrg	if (allocated)
53833367019cSmrg	    free(result);
5384d522f475Smrg	result = 0;
5385d522f475Smrg    }
53863367019cSmrg    /* be consistent, so that caller can always free the result */
53873367019cSmrg    if (result != 0 && !allocated)
53883367019cSmrg	result = x_strdup(result);
5389d522f475Smrg    return result;
5390d522f475Smrg}
5391d522f475Smrg#endif /* VMS */
5392d522f475Smrg
53930d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
5394d522f475Smrg
53953367019cSmrg/*
53963367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
53973367019cSmrg * This could happen on some older machines due to the uneven standardization
53983367019cSmrg * process for the two functions.
53993367019cSmrg *
54003367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
54013367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
54023367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
54033367019cSmrg * could copy environ.
54043367019cSmrg */
54053367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
54063367019cSmrg#undef HAVE_PUTENV
54073367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
54083367019cSmrg#undef HAVE_UNSETENV
54093367019cSmrg#endif
54103367019cSmrg
5411d522f475Smrg/*
5412d522f475Smrg * copy the environment before Setenv'ing.
5413d522f475Smrg */
5414d522f475Smrgvoid
5415d522f475SmrgxtermCopyEnv(char **oldenv)
5416d522f475Smrg{
54173367019cSmrg#ifdef HAVE_PUTENV
54183367019cSmrg    (void) oldenv;
54193367019cSmrg#else
5420d522f475Smrg    unsigned size;
5421d522f475Smrg    char **newenv;
5422d522f475Smrg
5423d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
5424d522f475Smrg	;
5425d522f475Smrg    }
5426d522f475Smrg
5427d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
5428d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
5429d522f475Smrg    environ = newenv;
54303367019cSmrg#endif
54313367019cSmrg}
54323367019cSmrg
54333367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
54343367019cSmrgstatic int
54353367019cSmrgfindEnv(const char *var, int *lengthp)
54363367019cSmrg{
54373367019cSmrg    char *test;
54383367019cSmrg    int envindex = 0;
54393367019cSmrg    size_t len = strlen(var);
54403367019cSmrg    int found = -1;
54413367019cSmrg
54423367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
54433367019cSmrg
54443367019cSmrg    while ((test = environ[envindex]) != NULL) {
54453367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
54463367019cSmrg	    found = envindex;
54473367019cSmrg	    break;
54483367019cSmrg	}
54493367019cSmrg	envindex++;
54503367019cSmrg    }
54513367019cSmrg    *lengthp = envindex;
54523367019cSmrg    return found;
5453d522f475Smrg}
54543367019cSmrg#endif
5455d522f475Smrg
5456d522f475Smrg/*
5457d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
5458d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
5459d522f475Smrg * This procedure assumes the memory for the first level of environ
5460d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
5461d522f475Smrg * to have to do a realloc().
5462d522f475Smrg */
5463d522f475Smrgvoid
5464cd3331d0SmrgxtermSetenv(const char *var, const char *value)
5465d522f475Smrg{
5466d522f475Smrg    if (value != 0) {
54673367019cSmrg#ifdef HAVE_PUTENV
54683367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
54693367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
54703367019cSmrg	if (both) {
54713367019cSmrg	    sprintf(both, "%s=%s", var, value);
54723367019cSmrg	    putenv(both);
54733367019cSmrg	}
54743367019cSmrg#else
5475d522f475Smrg	size_t len = strlen(var);
54763367019cSmrg	int envindex;
54773367019cSmrg	int found = findEnv(var, &envindex);
5478d522f475Smrg
5479d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
5480d522f475Smrg
5481d522f475Smrg	if (found < 0) {
5482d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
5483d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
5484d522f475Smrg
5485d522f475Smrg	    if (need > have) {
5486d522f475Smrg		char **newenv;
5487d522f475Smrg		newenv = TypeMallocN(char *, need);
5488d522f475Smrg		if (newenv == 0) {
54893367019cSmrg		    xtermWarning("Cannot increase environment\n");
5490d522f475Smrg		    return;
5491d522f475Smrg		}
5492d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
5493d522f475Smrg		free(environ);
5494d522f475Smrg		environ = newenv;
5495d522f475Smrg	    }
5496d522f475Smrg
5497d522f475Smrg	    found = envindex;
5498d522f475Smrg	    environ[found + 1] = NULL;
5499d522f475Smrg	    environ = environ;
5500d522f475Smrg	}
5501d522f475Smrg
5502dfb07bc7Smrg	environ[found] = TextAlloc(1 + len + strlen(value));
5503d522f475Smrg	if (environ[found] == 0) {
55043367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
5505d522f475Smrg	    return;
5506d522f475Smrg	}
5507d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
55083367019cSmrg#endif
5509d522f475Smrg    }
5510d522f475Smrg}
5511d522f475Smrg
55123367019cSmrgvoid
55133367019cSmrgxtermUnsetenv(const char *var)
55143367019cSmrg{
55153367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
55163367019cSmrg#ifdef HAVE_UNSETENV
55173367019cSmrg    unsetenv(var);
55183367019cSmrg#else
55193367019cSmrg    {
55203367019cSmrg	int ignore;
55213367019cSmrg	int item = findEnv(var, &ignore);
55223367019cSmrg	if (item >= 0) {
55233367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
55243367019cSmrg		++item;
55253367019cSmrg	    }
55263367019cSmrg	}
55273367019cSmrg    }
55283367019cSmrg#endif
55293367019cSmrg}
55303367019cSmrg
5531d522f475Smrg/*ARGSUSED*/
5532d522f475Smrgint
55339a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
5534d522f475Smrg{
55353367019cSmrg    xtermWarning("warning, error event received:\n");
5536d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
5537d522f475Smrg    Exit(ERROR_XERROR);
5538d522f475Smrg    return 0;			/* appease the compiler */
5539d522f475Smrg}
5540d522f475Smrg
5541712a7ff4Smrgvoid
5542712a7ff4Smrgice_error(IceConn iceConn)
5543712a7ff4Smrg{
5544712a7ff4Smrg    (void) iceConn;
5545712a7ff4Smrg
55463367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
55473367019cSmrg		 (long) getpid(), errno);
5548712a7ff4Smrg
5549712a7ff4Smrg    Exit(ERROR_ICEERROR);
5550712a7ff4Smrg}
5551712a7ff4Smrg
5552d522f475Smrg/*ARGSUSED*/
5553d522f475Smrgint
5554fa3f02f3Smrgxioerror(Display *dpy)
5555d522f475Smrg{
5556d522f475Smrg    int the_error = errno;
5557d522f475Smrg
55583367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
55593367019cSmrg		 the_error, SysErrorMsg(the_error),
55603367019cSmrg		 DisplayString(dpy));
5561d522f475Smrg
5562d522f475Smrg    Exit(ERROR_XIOERROR);
5563d522f475Smrg    return 0;			/* appease the compiler */
5564d522f475Smrg}
5565d522f475Smrg
5566d522f475Smrgvoid
5567d522f475Smrgxt_error(String message)
5568d522f475Smrg{
55693367019cSmrg    xtermWarning("Xt error: %s\n", message);
5570d522f475Smrg
5571d522f475Smrg    /*
5572d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
5573d522f475Smrg     */
5574d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
55753367019cSmrg	xtermWarning("DISPLAY is not set\n");
5576d522f475Smrg    }
5577d522f475Smrg    exit(1);
5578d522f475Smrg}
5579d522f475Smrg
5580d522f475Smrgint
5581d522f475SmrgXStrCmp(char *s1, char *s2)
5582d522f475Smrg{
5583d522f475Smrg    if (s1 && s2)
5584d522f475Smrg	return (strcmp(s1, s2));
5585d522f475Smrg    if (s1 && *s1)
5586d522f475Smrg	return (1);
5587d522f475Smrg    if (s2 && *s2)
5588d522f475Smrg	return (-1);
5589d522f475Smrg    return (0);
5590d522f475Smrg}
5591d522f475Smrg
5592d522f475Smrg#if OPT_TEK4014
5593d522f475Smrgstatic void
5594fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
5595d522f475Smrg{
5596d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
5597d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
5598d522f475Smrg    XWithdrawWindow(dpy, w, scr);
5599d522f475Smrg    return;
5600d522f475Smrg}
5601d522f475Smrg#endif
5602d522f475Smrg
5603d522f475Smrgvoid
5604d522f475Smrgset_vt_visibility(Bool on)
5605d522f475Smrg{
5606c219fbebSmrg    XtermWidget xw = term;
5607c219fbebSmrg    TScreen *screen = TScreenOf(xw);
5608d522f475Smrg
5609d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
5610d522f475Smrg    if (on) {
5611c219fbebSmrg	if (!screen->Vshow && xw) {
5612c219fbebSmrg	    VTInit(xw);
5613c219fbebSmrg	    XtMapWidget(XtParent(xw));
5614d522f475Smrg#if OPT_TOOLBAR
5615d522f475Smrg	    /* we need both of these during initialization */
5616c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
5617d522f475Smrg	    ShowToolbar(resource.toolBar);
5618d522f475Smrg#endif
5619d522f475Smrg	    screen->Vshow = True;
5620d522f475Smrg	}
5621d522f475Smrg    }
5622d522f475Smrg#if OPT_TEK4014
5623d522f475Smrg    else {
5624c219fbebSmrg	if (screen->Vshow && xw) {
5625c219fbebSmrg	    withdraw_window(XtDisplay(xw),
5626c219fbebSmrg			    VShellWindow(xw),
5627c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
5628d522f475Smrg	    screen->Vshow = False;
5629d522f475Smrg	}
5630d522f475Smrg    }
5631d522f475Smrg    set_vthide_sensitivity();
5632d522f475Smrg    set_tekhide_sensitivity();
5633d522f475Smrg    update_vttekmode();
5634d522f475Smrg    update_tekshow();
5635d522f475Smrg    update_vtshow();
5636d522f475Smrg#endif
5637d522f475Smrg    return;
5638d522f475Smrg}
5639d522f475Smrg
5640d522f475Smrg#if OPT_TEK4014
5641d522f475Smrgvoid
5642d522f475Smrgset_tek_visibility(Bool on)
5643d522f475Smrg{
5644d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
5645d522f475Smrg
5646d522f475Smrg    if (on) {
5647cd3331d0Smrg	if (!TEK4014_SHOWN(term)) {
5648cd3331d0Smrg	    if (tekWidget == 0) {
5649cd3331d0Smrg		TekInit();	/* will exit on failure */
5650cd3331d0Smrg	    }
5651cd3331d0Smrg	    if (tekWidget != 0) {
5652cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
5653cd3331d0Smrg		XtRealizeWidget(tekParent);
5654cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
5655d522f475Smrg#if OPT_TOOLBAR
5656cd3331d0Smrg		/* we need both of these during initialization */
5657cd3331d0Smrg		XtMapWidget(tekParent);
5658cd3331d0Smrg		XtMapWidget(tekWidget);
5659d522f475Smrg#endif
5660cd3331d0Smrg		XtOverrideTranslations(tekParent,
5661cd3331d0Smrg				       XtParseTranslationTable
5662cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
5663cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
5664cd3331d0Smrg				       XtWindow(tekParent),
5665cd3331d0Smrg				       &wm_delete_window, 1);
5666cd3331d0Smrg		TEK4014_SHOWN(term) = True;
5667cd3331d0Smrg	    }
5668d522f475Smrg	}
5669d522f475Smrg    } else {
5670d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
5671d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
5672d522f475Smrg			    TShellWindow,
5673d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
5674d522f475Smrg	    TEK4014_SHOWN(term) = False;
5675d522f475Smrg	}
5676d522f475Smrg    }
5677d522f475Smrg    set_tekhide_sensitivity();
5678d522f475Smrg    set_vthide_sensitivity();
5679d522f475Smrg    update_vtshow();
5680d522f475Smrg    update_tekshow();
5681d522f475Smrg    update_vttekmode();
5682d522f475Smrg    return;
5683d522f475Smrg}
5684d522f475Smrg
5685d522f475Smrgvoid
5686d522f475Smrgend_tek_mode(void)
5687d522f475Smrg{
5688cd3331d0Smrg    XtermWidget xw = term;
5689cd3331d0Smrg
5690cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
5691cd3331d0Smrg	FlushLog(xw);
5692dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
5693dfb07bc7Smrg	xtermSetWinSize(xw);
5694d522f475Smrg	longjmp(Tekend, 1);
5695d522f475Smrg    }
5696d522f475Smrg    return;
5697d522f475Smrg}
5698d522f475Smrg
5699d522f475Smrgvoid
5700d522f475Smrgend_vt_mode(void)
5701d522f475Smrg{
5702cd3331d0Smrg    XtermWidget xw = term;
5703cd3331d0Smrg
5704cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
5705cd3331d0Smrg	FlushLog(xw);
5706cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
5707dfb07bc7Smrg	TekSetWinSize(tekWidget);
5708d522f475Smrg	longjmp(VTend, 1);
5709d522f475Smrg    }
5710d522f475Smrg    return;
5711d522f475Smrg}
5712d522f475Smrg
5713d522f475Smrgvoid
5714d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
5715d522f475Smrg{
5716d522f475Smrg    if (tovt) {
5717d522f475Smrg	if (tekRefreshList)
5718d522f475Smrg	    TekRefresh(tekWidget);
5719d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
5720d522f475Smrg    } else {
5721d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
5722d522f475Smrg    }
5723d522f475Smrg}
5724d522f475Smrg
5725d522f475Smrgvoid
5726d522f475Smrghide_vt_window(void)
5727d522f475Smrg{
5728d522f475Smrg    set_vt_visibility(False);
5729d522f475Smrg    if (!TEK4014_ACTIVE(term))
5730d522f475Smrg	switch_modes(False);	/* switch to tek mode */
5731d522f475Smrg}
5732d522f475Smrg
5733d522f475Smrgvoid
5734d522f475Smrghide_tek_window(void)
5735d522f475Smrg{
5736d522f475Smrg    set_tek_visibility(False);
5737d522f475Smrg    tekRefreshList = (TekLink *) 0;
5738d522f475Smrg    if (TEK4014_ACTIVE(term))
5739d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
5740d522f475Smrg}
5741d522f475Smrg#endif /* OPT_TEK4014 */
5742d522f475Smrg
5743d522f475Smrgstatic const char *
5744d522f475Smrgskip_punct(const char *s)
5745d522f475Smrg{
5746d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
5747d522f475Smrg	++s;
5748d522f475Smrg    }
5749d522f475Smrg    return s;
5750d522f475Smrg}
5751d522f475Smrg
5752d522f475Smrgstatic int
5753d522f475Smrgcmp_options(const void *a, const void *b)
5754d522f475Smrg{
5755d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
5756d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
5757d522f475Smrg    return strcmp(s1, s2);
5758d522f475Smrg}
5759d522f475Smrg
5760d522f475Smrgstatic int
5761d522f475Smrgcmp_resources(const void *a, const void *b)
5762d522f475Smrg{
5763d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
5764d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
5765d522f475Smrg}
5766d522f475Smrg
5767d522f475SmrgXrmOptionDescRec *
5768d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
5769d522f475Smrg{
5770d522f475Smrg    static XrmOptionDescRec *res_array = 0;
5771d522f475Smrg
5772d522f475Smrg#ifdef NO_LEAKS
5773cd3331d0Smrg    if (descs == 0) {
5774cd3331d0Smrg	if (res_array != 0) {
5775cd3331d0Smrg	    free(res_array);
5776cd3331d0Smrg	    res_array = 0;
5777cd3331d0Smrg	}
5778d522f475Smrg    } else
5779d522f475Smrg#endif
5780d522f475Smrg    if (res_array == 0) {
5781d522f475Smrg	Cardinal j;
5782d522f475Smrg
5783d522f475Smrg	/* make a sorted index to 'resources' */
5784d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
5785cd3331d0Smrg	if (res_array != 0) {
5786cd3331d0Smrg	    for (j = 0; j < res_count; j++)
5787cd3331d0Smrg		res_array[j] = descs[j];
5788cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
5789cd3331d0Smrg	}
5790d522f475Smrg    }
5791d522f475Smrg    return res_array;
5792d522f475Smrg}
5793d522f475Smrg
5794d522f475Smrg/*
5795d522f475Smrg * The first time this is called, construct sorted index to the main program's
5796d522f475Smrg * list of options, taking into account the on/off options which will be
5797d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
5798d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
5799d522f475Smrg */
5800d522f475SmrgOptionHelp *
5801d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
5802d522f475Smrg{
5803d522f475Smrg    static OptionHelp *opt_array = 0;
5804d522f475Smrg
5805d522f475Smrg#ifdef NO_LEAKS
5806d522f475Smrg    if (descs == 0 && opt_array != 0) {
5807d522f475Smrg	sortedOptDescs(descs, numDescs);
5808d522f475Smrg	free(opt_array);
5809d522f475Smrg	opt_array = 0;
5810d522f475Smrg	return 0;
5811d522f475Smrg    } else if (options == 0 || descs == 0) {
5812d522f475Smrg	return 0;
5813d522f475Smrg    }
5814d522f475Smrg#endif
5815d522f475Smrg
5816d522f475Smrg    if (opt_array == 0) {
5817cd3331d0Smrg	size_t opt_count, j;
5818d522f475Smrg#if OPT_TRACE
5819d522f475Smrg	Cardinal k;
5820d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
5821d522f475Smrg	int code;
5822cd3331d0Smrg	const char *mesg;
5823d522f475Smrg#else
5824d522f475Smrg	(void) descs;
5825d522f475Smrg	(void) numDescs;
5826d522f475Smrg#endif
5827d522f475Smrg
5828d522f475Smrg	/* count 'options' and make a sorted index to it */
5829d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
5830d522f475Smrg	    ;
5831d522f475Smrg	}
5832d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
5833d522f475Smrg	for (j = 0; j < opt_count; j++)
5834d522f475Smrg	    opt_array[j] = options[j];
5835d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
5836d522f475Smrg
5837d522f475Smrg	/* supply the "turn on/off" strings if needed */
5838d522f475Smrg#if OPT_TRACE
5839d522f475Smrg	for (j = 0; j < opt_count; j++) {
5840712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
5841c219fbebSmrg		char temp[80];
5842cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
5843d522f475Smrg		for (k = 0; k < numDescs; ++k) {
5844cd3331d0Smrg		    const char *value = res_array[k].value;
5845d522f475Smrg		    if (res_array[k].option[0] == '-') {
5846d522f475Smrg			code = -1;
5847d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
5848d522f475Smrg			code = 1;
5849d522f475Smrg		    } else {
5850d522f475Smrg			code = 0;
5851d522f475Smrg		    }
58523367019cSmrg		    sprintf(temp, "%.*s",
58533367019cSmrg			    (int) sizeof(temp) - 2,
58543367019cSmrg			    opt_array[j].desc);
5855c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
5856d522f475Smrg			code = -code;
5857d522f475Smrg		    if (code != 0
5858d522f475Smrg			&& res_array[k].value != 0
5859d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
5860d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
5861d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
5862d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
5863d522f475Smrg			    mesg = "turn on/off";
5864d522f475Smrg			} else {
5865d522f475Smrg			    mesg = "turn off/on";
5866d522f475Smrg			}
5867d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
5868712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
5869dfb07bc7Smrg				char *s = TextAlloc(strlen(mesg)
5870dfb07bc7Smrg						    + 1
5871dfb07bc7Smrg						    + strlen(opt_array[j].desc));
5872d522f475Smrg				if (s != 0) {
5873d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
5874d522f475Smrg				    opt_array[j].desc = s;
5875d522f475Smrg				}
5876d522f475Smrg			    } else {
5877d522f475Smrg				TRACE(("OOPS "));
5878d522f475Smrg			    }
5879d522f475Smrg			}
5880d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
5881d522f475Smrg			       mesg,
5882d522f475Smrg			       res_array[k].option,
5883d522f475Smrg			       res_array[k].value,
5884d522f475Smrg			       opt_array[j].opt,
5885d522f475Smrg			       opt_array[j].desc));
5886d522f475Smrg			break;
5887d522f475Smrg		    }
5888d522f475Smrg		}
5889d522f475Smrg	    }
5890d522f475Smrg	}
5891d522f475Smrg#endif
5892d522f475Smrg    }
5893d522f475Smrg    return opt_array;
5894d522f475Smrg}
5895d522f475Smrg
5896d522f475Smrg/*
5897d522f475Smrg * Report the character-type locale that xterm was started in.
5898d522f475Smrg */
58993367019cSmrgString
5900d522f475SmrgxtermEnvLocale(void)
5901d522f475Smrg{
59023367019cSmrg    static String result;
5903d522f475Smrg
5904d522f475Smrg    if (result == 0) {
5905d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
5906cd3331d0Smrg	    result = x_strdup("C");
5907cd3331d0Smrg	} else {
5908cd3331d0Smrg	    result = x_strdup(result);
5909d522f475Smrg	}
5910d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
5911d522f475Smrg    }
5912d522f475Smrg    return result;
5913d522f475Smrg}
5914d522f475Smrg
5915d522f475Smrgchar *
5916d522f475SmrgxtermEnvEncoding(void)
5917d522f475Smrg{
5918d522f475Smrg    static char *result;
5919d522f475Smrg
5920d522f475Smrg    if (result == 0) {
5921d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5922d522f475Smrg	result = nl_langinfo(CODESET);
5923d522f475Smrg#else
5924d522f475Smrg	char *locale = xtermEnvLocale();
5925d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
5926d522f475Smrg	    result = "ASCII";
5927d522f475Smrg	} else {
5928d522f475Smrg	    result = "ISO-8859-1";
5929d522f475Smrg	}
5930d522f475Smrg#endif
5931d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
5932d522f475Smrg    }
5933d522f475Smrg    return result;
5934d522f475Smrg}
5935d522f475Smrg
5936d522f475Smrg#if OPT_WIDE_CHARS
5937d522f475Smrg/*
5938d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
5939d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
5940d522f475Smrg * various library calls.
5941d522f475Smrg */
5942d522f475SmrgBool
5943d522f475SmrgxtermEnvUTF8(void)
5944d522f475Smrg{
5945d522f475Smrg    static Bool init = False;
5946d522f475Smrg    static Bool result = False;
5947d522f475Smrg
5948d522f475Smrg    if (!init) {
5949d522f475Smrg	init = True;
5950d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5951d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
5952d522f475Smrg#else
5953fa3f02f3Smrg	{
5954fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
5955fa3f02f3Smrg	    int n;
5956fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
5957fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
5958fa3f02f3Smrg	    }
5959fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
5960fa3f02f3Smrg		result = True;
5961fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
5962fa3f02f3Smrg		result = True;
5963fa3f02f3Smrg	    free(locale);
5964fa3f02f3Smrg	}
5965d522f475Smrg#endif
5966d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
5967d522f475Smrg    }
5968d522f475Smrg    return result;
5969d522f475Smrg}
5970d522f475Smrg#endif /* OPT_WIDE_CHARS */
5971d522f475Smrg
5972b7c89284Ssnj/*
5973b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
5974b7c89284Ssnj */
5975b7c89284SsnjXtermWidget
5976b7c89284SsnjgetXtermWidget(Widget w)
5977b7c89284Ssnj{
5978b7c89284Ssnj    XtermWidget xw;
5979b7c89284Ssnj
5980b7c89284Ssnj    if (w == 0) {
5981b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
5982b7c89284Ssnj	if (!IsXtermWidget(xw)) {
5983b7c89284Ssnj	    xw = 0;
5984b7c89284Ssnj	}
5985b7c89284Ssnj    } else if (IsXtermWidget(w)) {
5986b7c89284Ssnj	xw = (XtermWidget) w;
5987b7c89284Ssnj    } else {
5988b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
5989b7c89284Ssnj    }
5990b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
5991b7c89284Ssnj    return xw;
5992b7c89284Ssnj}
59933367019cSmrg
59943367019cSmrg#if OPT_SESSION_MGT
59953367019cSmrgstatic void
59963367019cSmrgdie_callback(Widget w GCC_UNUSED,
59973367019cSmrg	     XtPointer client_data GCC_UNUSED,
59983367019cSmrg	     XtPointer call_data GCC_UNUSED)
59993367019cSmrg{
60003367019cSmrg    NormalExit();
60013367019cSmrg}
60023367019cSmrg
60033367019cSmrgstatic void
60043367019cSmrgsave_callback(Widget w GCC_UNUSED,
60053367019cSmrg	      XtPointer client_data GCC_UNUSED,
60063367019cSmrg	      XtPointer call_data)
60073367019cSmrg{
60083367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
60093367019cSmrg    /* we have nothing to save */
60103367019cSmrg    token->save_success = True;
60113367019cSmrg}
60123367019cSmrg
60133367019cSmrgstatic void
60143367019cSmrgicewatch(IceConn iceConn,
60153367019cSmrg	 IcePointer clientData GCC_UNUSED,
60163367019cSmrg	 Bool opening,
60173367019cSmrg	 IcePointer * watchData GCC_UNUSED)
60183367019cSmrg{
60193367019cSmrg    if (opening) {
60203367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
60213367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
60223367019cSmrg    } else {
60233367019cSmrg	ice_fd = -1;
60243367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
60253367019cSmrg    }
60263367019cSmrg}
60273367019cSmrg
60283367019cSmrgvoid
60293367019cSmrgxtermOpenSession(void)
60303367019cSmrg{
60313367019cSmrg    if (resource.sessionMgt) {
60323367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
60333367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
60343367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
60353367019cSmrg    }
60363367019cSmrg}
60373367019cSmrg
60383367019cSmrgvoid
60393367019cSmrgxtermCloseSession(void)
60403367019cSmrg{
60413367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
60423367019cSmrg}
60433367019cSmrg#endif /* OPT_SESSION_MGT */
60443367019cSmrg
60453367019cSmrgWidget
60463367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
60473367019cSmrg		     String my_class,
60483367019cSmrg		     XrmOptionDescRec * options,
60493367019cSmrg		     Cardinal num_options,
60503367019cSmrg		     int *argc_in_out,
6051fa3f02f3Smrg		     String *argv_in_out,
6052fa3f02f3Smrg		     String *fallback_resources,
60533367019cSmrg		     WidgetClass widget_class,
60543367019cSmrg		     ArgList args,
60553367019cSmrg		     Cardinal num_args)
60563367019cSmrg{
60573367019cSmrg    Widget result;
60583367019cSmrg
60593367019cSmrg    XtSetErrorHandler(xt_error);
60603367019cSmrg#if OPT_SESSION_MGT
60613367019cSmrg    result = XtOpenApplication(app_context_return,
60623367019cSmrg			       my_class,
60633367019cSmrg			       options,
60643367019cSmrg			       num_options,
60653367019cSmrg			       argc_in_out,
60663367019cSmrg			       argv_in_out,
60673367019cSmrg			       fallback_resources,
60683367019cSmrg			       widget_class,
60693367019cSmrg			       args,
60703367019cSmrg			       num_args);
60713367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
60723367019cSmrg#else
60739a64e1c5Smrg    (void) widget_class;
60749a64e1c5Smrg    (void) args;
60759a64e1c5Smrg    (void) num_args;
60763367019cSmrg    result = XtAppInitialize(app_context_return,
60773367019cSmrg			     my_class,
60783367019cSmrg			     options,
60793367019cSmrg			     num_options,
60803367019cSmrg			     argc_in_out,
60813367019cSmrg			     argv_in_out,
60823367019cSmrg			     fallback_resources,
60833367019cSmrg			     NULL, 0);
60843367019cSmrg#endif /* OPT_SESSION_MGT */
6085037a25ddSmrg    init_colored_cursor(XtDisplay(result));
6086037a25ddSmrg
60873367019cSmrg    XtSetErrorHandler((XtErrorHandler) 0);
60883367019cSmrg
60893367019cSmrg    return result;
60903367019cSmrg}
60913367019cSmrg
60923367019cSmrgstatic int x11_errors;
60933367019cSmrg
60943367019cSmrgstatic int
60959a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
60963367019cSmrg{
60973367019cSmrg    (void) display;
60983367019cSmrg    (void) error_event;
60993367019cSmrg    ++x11_errors;
61003367019cSmrg    return 0;
61013367019cSmrg}
61023367019cSmrg
61033367019cSmrgBoolean
6104fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
61053367019cSmrg{
61063367019cSmrg    Boolean result = False;
61073367019cSmrg    Status code;
61083367019cSmrg
61093367019cSmrg    memset(attrs, 0, sizeof(*attrs));
61103367019cSmrg    if (win != None) {
61113367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
61123367019cSmrg	x11_errors = 0;
61133367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
61143367019cSmrg	XSetErrorHandler(save);
61153367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
61163367019cSmrg	if (result) {
61173367019cSmrg	    TRACE_WIN_ATTRS(attrs);
61183367019cSmrg	} else {
61193367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
61203367019cSmrg	}
61213367019cSmrg    }
61223367019cSmrg    return result;
61233367019cSmrg}
61243367019cSmrg
61253367019cSmrgBoolean
6126fa3f02f3SmrgxtermGetWinProp(Display *display,
61273367019cSmrg		Window win,
61283367019cSmrg		Atom property,
61293367019cSmrg		long long_offset,
61303367019cSmrg		long long_length,
61313367019cSmrg		Atom req_type,
61329a64e1c5Smrg		Atom *actual_type_return,
61333367019cSmrg		int *actual_format_return,
61343367019cSmrg		unsigned long *nitems_return,
61353367019cSmrg		unsigned long *bytes_after_return,
61363367019cSmrg		unsigned char **prop_return)
61373367019cSmrg{
61383367019cSmrg    Boolean result = True;
61393367019cSmrg
61403367019cSmrg    if (win != None) {
61413367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
61423367019cSmrg	x11_errors = 0;
61433367019cSmrg	if (XGetWindowProperty(display,
61443367019cSmrg			       win,
61453367019cSmrg			       property,
61463367019cSmrg			       long_offset,
61473367019cSmrg			       long_length,
61483367019cSmrg			       False,
61493367019cSmrg			       req_type,
61503367019cSmrg			       actual_type_return,
61513367019cSmrg			       actual_format_return,
61523367019cSmrg			       nitems_return,
61533367019cSmrg			       bytes_after_return,
61543367019cSmrg			       prop_return) == Success
61553367019cSmrg	    && x11_errors == 0) {
61563367019cSmrg	    result = True;
61573367019cSmrg	}
61583367019cSmrg	XSetErrorHandler(save);
61593367019cSmrg    }
61603367019cSmrg    return result;
61613367019cSmrg}
61623367019cSmrg
61633367019cSmrgvoid
61643367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
61653367019cSmrg{
61663367019cSmrg    Display *dpy = XtDisplay(toplevel);
61673367019cSmrg    XWindowAttributes attrs;
61683367019cSmrg
61693367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
61703367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
61713367019cSmrg	XtermWidget xw = term;
61723367019cSmrg	TScreen *screen = TScreenOf(xw);
61733367019cSmrg
61743367019cSmrg	XtRealizeWidget(toplevel);
61753367019cSmrg
61763367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
61773367019cSmrg	       XtWindow(toplevel),
61783367019cSmrg	       winToEmbedInto));
61793367019cSmrg	XReparentWindow(dpy,
61803367019cSmrg			XtWindow(toplevel),
61813367019cSmrg			winToEmbedInto, 0, 0);
61823367019cSmrg
61833367019cSmrg	screen->embed_high = (Dimension) attrs.height;
61843367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
61853367019cSmrg    }
61863367019cSmrg}
618794644356Smrg
618894644356Smrgvoid
618994644356Smrgfree_string(String value)
619094644356Smrg{
619194644356Smrg    free((void *) value);
619294644356Smrg}
6193dfb07bc7Smrg
6194dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
6195dfb07bc7Smrgvoid
6196dfb07bc7Smrgupdate_winsize(int fd, int rows, int cols, int height, int width)
6197dfb07bc7Smrg{
6198dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
6199dfb07bc7Smrg    TTYSIZE_STRUCT ts;
6200dfb07bc7Smrg    int code;
6201dfb07bc7Smrg
6202dfb07bc7Smrg    setup_winsize(ts, rows, cols, height, width);
6203dfb07bc7Smrg    TRACE_RC(code, SET_TTYSIZE(fd, ts));
6204dfb07bc7Smrg    trace_winsize(ts, "from SET_TTYSIZE");
6205dfb07bc7Smrg    (void) code;
6206dfb07bc7Smrg#endif
6207dfb07bc7Smrg
6208dfb07bc7Smrg    (void) rows;
6209dfb07bc7Smrg    (void) cols;
6210dfb07bc7Smrg    (void) height;
6211dfb07bc7Smrg    (void) width;
6212dfb07bc7Smrg}
6213dfb07bc7Smrg
6214dfb07bc7Smrg/*
6215dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
6216dfb07bc7Smrg * manipulation 18 and 19.
6217dfb07bc7Smrg */
6218dfb07bc7Smrgvoid
6219dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
6220dfb07bc7Smrg{
6221dfb07bc7Smrg#if OPT_TEK4014
6222dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
6223dfb07bc7Smrg#endif
6224dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
6225dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
6226dfb07bc7Smrg
6227dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
6228dfb07bc7Smrg	    update_winsize(screen->respond,
6229dfb07bc7Smrg			   MaxRows(screen),
6230dfb07bc7Smrg			   MaxCols(screen),
6231dfb07bc7Smrg			   Height(screen),
6232dfb07bc7Smrg			   Width(screen));
6233dfb07bc7Smrg	}
6234dfb07bc7Smrg}
6235