misc.c revision 037a25dd
1037a25ddSmrg/* $XTermId: misc.c,v 1.743 2016/10/07 00:41:14 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4037a25ddSmrg * Copyright 1999-2015,2016 by Thomas E. Dickey
5d522f475Smrg *
6cd3331d0Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg#include <version.h>
56b7c89284Ssnj#include <main.h>
57d522f475Smrg#include <xterm.h>
58d522f475Smrg
59d522f475Smrg#include <sys/stat.h>
60d522f475Smrg#include <stdio.h>
613367019cSmrg#include <stdarg.h>
62d522f475Smrg#include <signal.h>
63d522f475Smrg#include <ctype.h>
64d522f475Smrg#include <pwd.h>
65d522f475Smrg#include <sys/wait.h>
66d522f475Smrg
67d522f475Smrg#include <X11/keysym.h>
68d522f475Smrg#include <X11/Xatom.h>
69d522f475Smrg#include <X11/cursorfont.h>
70d522f475Smrg#include <X11/Xlocale.h>
71d522f475Smrg
72d522f475Smrg#include <X11/Xmu/Error.h>
73d522f475Smrg#include <X11/Xmu/SysUtil.h>
74d522f475Smrg#include <X11/Xmu/WinUtil.h>
75d522f475Smrg#include <X11/Xmu/Xmu.h>
76d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
77d522f475Smrg#include <X11/Sunkeysym.h>
78d522f475Smrg#endif
79d522f475Smrg
803367019cSmrg#ifdef HAVE_LIBXPM
813367019cSmrg#include <X11/xpm.h>
823367019cSmrg#endif
833367019cSmrg
84d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
85d522f475Smrg#include <langinfo.h>
86d522f475Smrg#endif
87d522f475Smrg
88d522f475Smrg#include <xutf8.h>
89d522f475Smrg
90d522f475Smrg#include <data.h>
91d522f475Smrg#include <error.h>
92d522f475Smrg#include <menu.h>
93d522f475Smrg#include <fontutils.h>
94d522f475Smrg#include <xstrings.h>
95d522f475Smrg#include <xtermcap.h>
96d522f475Smrg#include <VTparse.h>
97fa3f02f3Smrg#include <graphics.h>
989a64e1c5Smrg#include <graphics_regis.h>
999a64e1c5Smrg#include <graphics_sixel.h>
100d522f475Smrg
101d522f475Smrg#include <assert.h>
102d522f475Smrg
103d522f475Smrg#if (XtSpecificationRelease < 6)
104d522f475Smrg#ifndef X_GETTIMEOFDAY
105d522f475Smrg#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0)
106d522f475Smrg#endif
107d522f475Smrg#endif
108d522f475Smrg
109d522f475Smrg#ifdef VMS
110d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
111d522f475Smrg#ifdef ALLOWLOGFILEEXEC
112d522f475Smrg#undef ALLOWLOGFILEEXEC
113d522f475Smrg#endif
114d522f475Smrg#endif /* VMS */
115d522f475Smrg
116d522f475Smrg#if OPT_TEK4014
117d522f475Smrg#define OUR_EVENT(event,Type) \
118d522f475Smrg		(event.type == Type && \
119d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
120d522f475Smrg		    (tekWidget && \
121d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
122d522f475Smrg#else
123d522f475Smrg#define OUR_EVENT(event,Type) \
124d522f475Smrg		(event.type == Type && \
125d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
126d522f475Smrg#endif
127d522f475Smrg
1283367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
129d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
130d522f475Smrg
1313367019cSmrgstatic char emptyString[] = "";
1323367019cSmrg
133d522f475Smrg#if OPT_EXEC_XTERM
134d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
135d522f475Smrg   error; adapted from libc docs */
136d522f475Smrgstatic char *
137d522f475SmrgReadlink(const char *filename)
138d522f475Smrg{
139d522f475Smrg    char *buf = NULL;
140cd3331d0Smrg    size_t size = 100;
141d522f475Smrg
142d522f475Smrg    for (;;) {
143037a25ddSmrg	int n;
144037a25ddSmrg	char *tmp = TypeRealloc(char, size, buf);
145037a25ddSmrg	if (tmp == NULL) {
146037a25ddSmrg	    free(buf);
147037a25ddSmrg	    return NULL;
148037a25ddSmrg	}
149037a25ddSmrg	buf = tmp;
150d522f475Smrg	memset(buf, 0, size);
151d522f475Smrg
152cd3331d0Smrg	n = (int) readlink(filename, buf, size);
153d522f475Smrg	if (n < 0) {
154d522f475Smrg	    free(buf);
155d522f475Smrg	    return NULL;
156d522f475Smrg	}
157d522f475Smrg
158d522f475Smrg	if ((unsigned) n < size) {
159d522f475Smrg	    return buf;
160d522f475Smrg	}
161d522f475Smrg
162d522f475Smrg	size *= 2;
163d522f475Smrg    }
164d522f475Smrg}
165d522f475Smrg#endif /* OPT_EXEC_XTERM */
166d522f475Smrg
167d522f475Smrgstatic void
168d522f475SmrgSleep(int msec)
169d522f475Smrg{
170d522f475Smrg    static struct timeval select_timeout;
171d522f475Smrg
172d522f475Smrg    select_timeout.tv_sec = 0;
173d522f475Smrg    select_timeout.tv_usec = msec * 1000;
174d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
175d522f475Smrg}
176d522f475Smrg
177d522f475Smrgstatic void
1783367019cSmrgselectwindow(XtermWidget xw, int flag)
179d522f475Smrg{
1803367019cSmrg    TScreen *screen = TScreenOf(xw);
1813367019cSmrg
182d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
183d522f475Smrg
184d522f475Smrg#if OPT_TEK4014
1853367019cSmrg    if (TEK4014_ACTIVE(xw)) {
186d522f475Smrg	if (!Ttoggled)
187d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
188d522f475Smrg	screen->select |= flag;
189d522f475Smrg	if (!Ttoggled)
190d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
191d522f475Smrg    } else
192d522f475Smrg#endif
193d522f475Smrg    {
1943367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
1953367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
1963367019cSmrg	if (input && input->xic)
1973367019cSmrg	    XSetICFocus(input->xic);
1983367019cSmrg#endif
199d522f475Smrg
200d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
201d522f475Smrg	    HideCursor();
202d522f475Smrg	screen->select |= flag;
203d522f475Smrg	if (screen->cursor_state)
204d522f475Smrg	    ShowCursor();
205d522f475Smrg    }
206cd3331d0Smrg    GetScrollLock(screen);
207d522f475Smrg}
208d522f475Smrg
209d522f475Smrgstatic void
2103367019cSmrgunselectwindow(XtermWidget xw, int flag)
211d522f475Smrg{
2123367019cSmrg    TScreen *screen = TScreenOf(xw);
2133367019cSmrg
214d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
215d522f475Smrg
2163367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
217d522f475Smrg	screen->hide_pointer = False;
2183367019cSmrg	xtermDisplayCursor(xw);
219d522f475Smrg    }
220d522f475Smrg
221d522f475Smrg    if (!screen->always_highlight) {
222d522f475Smrg#if OPT_TEK4014
2233367019cSmrg	if (TEK4014_ACTIVE(xw)) {
224d522f475Smrg	    if (!Ttoggled)
225d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
226d522f475Smrg	    screen->select &= ~flag;
227d522f475Smrg	    if (!Ttoggled)
228d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
229d522f475Smrg	} else
230d522f475Smrg#endif
231d522f475Smrg	{
2323367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
2333367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2343367019cSmrg	    if (input && input->xic)
2353367019cSmrg		XUnsetICFocus(input->xic);
2363367019cSmrg#endif
237d522f475Smrg
238d522f475Smrg	    screen->select &= ~flag;
239d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
240d522f475Smrg		HideCursor();
241d522f475Smrg	    if (screen->cursor_state)
242d522f475Smrg		ShowCursor();
243d522f475Smrg	}
244d522f475Smrg    }
245d522f475Smrg}
246d522f475Smrg
247d522f475Smrgstatic void
2489a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
249d522f475Smrg{
250d522f475Smrg    TScreen *screen = TScreenOf(xw);
251d522f475Smrg
252d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
253cd3331d0Smrg    TRACE_FOCUS(xw, ev);
254cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
255cd3331d0Smrg	ev->focus &&
256cd3331d0Smrg	!(screen->select & FOCUS))
2573367019cSmrg	selectwindow(xw, INWINDOW);
258d522f475Smrg}
259d522f475Smrg
260d522f475Smrgstatic void
2619a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
262d522f475Smrg{
263d522f475Smrg    TScreen *screen = TScreenOf(xw);
264d522f475Smrg
265d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
266cd3331d0Smrg    TRACE_FOCUS(xw, ev);
267cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
268cd3331d0Smrg	ev->focus &&
269cd3331d0Smrg	!(screen->select & FOCUS))
2703367019cSmrg	unselectwindow(xw, INWINDOW);
271d522f475Smrg}
272d522f475Smrg
273d522f475Smrg#ifndef XUrgencyHint
274d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
275d522f475Smrg#endif
276d522f475Smrg
277d522f475Smrgstatic void
278c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
279d522f475Smrg{
280c219fbebSmrg    TScreen *screen = TScreenOf(xw);
281c219fbebSmrg
282d522f475Smrg    if (screen->bellIsUrgent) {
283c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
284d522f475Smrg	if (h != 0) {
285c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
286d522f475Smrg		h->flags |= XUrgencyHint;
287d522f475Smrg	    } else {
288d522f475Smrg		h->flags &= ~XUrgencyHint;
289d522f475Smrg	    }
290c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
291d522f475Smrg	}
292d522f475Smrg    }
293d522f475Smrg}
294d522f475Smrg
295d522f475Smrgvoid
296d522f475Smrgdo_xevents(void)
297d522f475Smrg{
298d522f475Smrg    TScreen *screen = TScreenOf(term);
299d522f475Smrg
3003367019cSmrg    if (xtermAppPending()
301d522f475Smrg	||
302d522f475Smrg#if defined(VMS) || defined(__VMS)
303d522f475Smrg	screen->display->qlen > 0
304d522f475Smrg#else
305d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
306d522f475Smrg#endif
307d522f475Smrg	)
308d522f475Smrg	xevents();
309d522f475Smrg}
310d522f475Smrg
311d522f475Smrgvoid
312d522f475SmrgxtermDisplayCursor(XtermWidget xw)
313d522f475Smrg{
314d522f475Smrg    TScreen *screen = TScreenOf(xw);
315d522f475Smrg
316d522f475Smrg    if (screen->Vshow) {
317d522f475Smrg	if (screen->hide_pointer) {
318d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
319d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
320d522f475Smrg	} else {
321d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
322d522f475Smrg	    recolor_cursor(screen,
323d522f475Smrg			   screen->pointer_cursor,
324d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
325d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
326d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
327d522f475Smrg	}
328d522f475Smrg    }
329d522f475Smrg}
330d522f475Smrg
331d522f475Smrgvoid
332d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
333d522f475Smrg{
334d522f475Smrg    static int tried = -1;
335d522f475Smrg    TScreen *screen = TScreenOf(xw);
336d522f475Smrg
337d522f475Smrg#if OPT_TEK4014
338d522f475Smrg    if (TEK4014_SHOWN(xw))
339d522f475Smrg	enable = True;
340d522f475Smrg#endif
341d522f475Smrg
342d522f475Smrg    /*
343d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
344d522f475Smrg     * the mouse-mode:
345d522f475Smrg     */
346d522f475Smrg    if (!enable) {
347d522f475Smrg	switch (screen->pointer_mode) {
348d522f475Smrg	case pNever:
349d522f475Smrg	    enable = True;
350d522f475Smrg	    break;
351d522f475Smrg	case pNoMouse:
352d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
353d522f475Smrg		enable = True;
354d522f475Smrg	    break;
355d522f475Smrg	case pAlways:
3563367019cSmrg	case pFocused:
357d522f475Smrg	    break;
358d522f475Smrg	}
359d522f475Smrg    }
360d522f475Smrg
361d522f475Smrg    if (enable) {
362d522f475Smrg	if (screen->hide_pointer) {
363d522f475Smrg	    screen->hide_pointer = False;
364d522f475Smrg	    xtermDisplayCursor(xw);
365d522f475Smrg	    switch (screen->send_mouse_pos) {
366d522f475Smrg	    case ANY_EVENT_MOUSE:
367d522f475Smrg		break;
368d522f475Smrg	    default:
369d522f475Smrg		MotionOff(screen, xw);
370d522f475Smrg		break;
371d522f475Smrg	    }
372d522f475Smrg	}
373d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
374d522f475Smrg	if (screen->hidden_cursor == 0) {
375d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
376d522f475Smrg	}
377d522f475Smrg	if (screen->hidden_cursor == 0) {
378d522f475Smrg	    tried = 1;
379d522f475Smrg	} else {
380d522f475Smrg	    tried = 0;
381d522f475Smrg	    screen->hide_pointer = True;
382d522f475Smrg	    xtermDisplayCursor(xw);
383d522f475Smrg	    MotionOn(screen, xw);
384d522f475Smrg	}
385d522f475Smrg    }
386d522f475Smrg}
387d522f475Smrg
3883367019cSmrg#if OPT_TRACE
3893367019cSmrgstatic void
3909a64e1c5SmrgTraceExposeEvent(XEvent *arg)
3913367019cSmrg{
3923367019cSmrg    XExposeEvent *event = (XExposeEvent *) arg;
3933367019cSmrg
3943367019cSmrg    TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n",
3953367019cSmrg	   event->serial,
3963367019cSmrg	   event->count,
3973367019cSmrg	   event->y,
3983367019cSmrg	   event->x,
3993367019cSmrg	   event->height,
4003367019cSmrg	   event->width,
4013367019cSmrg	   event->window));
4023367019cSmrg}
4033367019cSmrg
4043367019cSmrg#else
4053367019cSmrg#define TraceExposeEvent(event)	/* nothing */
4063367019cSmrg#endif
4073367019cSmrg
4083367019cSmrg/* true if p contains q */
4093367019cSmrg#define ExposeContains(p,q) \
4103367019cSmrg	    ((p)->y <= (q)->y \
4113367019cSmrg	  && (p)->x <= (q)->x \
4123367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
4133367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
4143367019cSmrg
4153367019cSmrgstatic XtInputMask
4169a64e1c5SmrgmergeExposeEvents(XEvent *target)
4173367019cSmrg{
4183367019cSmrg    XEvent next_event;
419037a25ddSmrg    XExposeEvent *p;
4203367019cSmrg
4213367019cSmrg    TRACE(("pending Expose...?\n"));
4223367019cSmrg    TraceExposeEvent(target);
4233367019cSmrg    XtAppNextEvent(app_con, target);
4243367019cSmrg    p = (XExposeEvent *) target;
4253367019cSmrg
4263367019cSmrg    while (XtAppPending(app_con)
4273367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4283367019cSmrg	   && next_event.type == Expose) {
4293367019cSmrg	Boolean merge_this = False;
430037a25ddSmrg	XExposeEvent *q;
4313367019cSmrg
4323367019cSmrg	TraceExposeEvent(&next_event);
4333367019cSmrg	q = (XExposeEvent *) (&next_event);
4343367019cSmrg	XtAppNextEvent(app_con, &next_event);
4353367019cSmrg
4363367019cSmrg	/*
4373367019cSmrg	 * If either window is contained within the other, merge the events.
4383367019cSmrg	 * The traces show that there are also cases where a full repaint of
4393367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4403367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4413367019cSmrg	 * to skim several events ahead.
4423367019cSmrg	 */
4433367019cSmrg	if (p->window == q->window) {
4443367019cSmrg	    if (ExposeContains(p, q)) {
4453367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4463367019cSmrg		merge_this = True;
4473367019cSmrg		next_event = *target;
4483367019cSmrg	    } else if (ExposeContains(q, p)) {
4493367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4503367019cSmrg		merge_this = True;
4513367019cSmrg	    }
4523367019cSmrg	}
4533367019cSmrg	if (!merge_this) {
4543367019cSmrg	    XtDispatchEvent(target);
4553367019cSmrg	}
4563367019cSmrg	*target = next_event;
4573367019cSmrg    }
4583367019cSmrg    XtDispatchEvent(target);
4593367019cSmrg    return XtAppPending(app_con);
4603367019cSmrg}
4613367019cSmrg
4623367019cSmrg#if OPT_TRACE
4633367019cSmrgstatic void
4649a64e1c5SmrgTraceConfigureEvent(XEvent *arg)
4653367019cSmrg{
4663367019cSmrg    XConfigureEvent *event = (XConfigureEvent *) arg;
4673367019cSmrg
4683367019cSmrg    TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n",
4693367019cSmrg	   event->serial,
4703367019cSmrg	   event->y,
4713367019cSmrg	   event->x,
4723367019cSmrg	   event->height,
4733367019cSmrg	   event->width,
4743367019cSmrg	   event->window));
4753367019cSmrg}
4763367019cSmrg
4773367019cSmrg#else
4783367019cSmrg#define TraceConfigureEvent(event)	/* nothing */
4793367019cSmrg#endif
4803367019cSmrg
4813367019cSmrg/*
4823367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4833367019cSmrg * event.  Remove that from the queue so we can look further.
4843367019cSmrg *
4853367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4863367019cSmrg * that.  If the adjacent events are for different windows, process the older
4873367019cSmrg * event and update the event used for comparing windows.  If they are for the
4883367019cSmrg * same window, only the newer event is of interest.
4893367019cSmrg *
4903367019cSmrg * Finally, process the (remaining) configure-notify event.
4913367019cSmrg */
4923367019cSmrgstatic XtInputMask
4939a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4943367019cSmrg{
4953367019cSmrg    XEvent next_event;
496037a25ddSmrg    XConfigureEvent *p;
4973367019cSmrg
4983367019cSmrg    XtAppNextEvent(app_con, target);
4993367019cSmrg    p = (XConfigureEvent *) target;
5003367019cSmrg
5013367019cSmrg    TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no"));
5023367019cSmrg    TraceConfigureEvent(target);
5033367019cSmrg
5043367019cSmrg    if (XtAppPending(app_con)
5053367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
5063367019cSmrg	&& next_event.type == ConfigureNotify) {
5073367019cSmrg	Boolean merge_this = False;
508037a25ddSmrg	XConfigureEvent *q;
5093367019cSmrg
5103367019cSmrg	TraceConfigureEvent(&next_event);
5113367019cSmrg	XtAppNextEvent(app_con, &next_event);
5123367019cSmrg	q = (XConfigureEvent *) (&next_event);
5133367019cSmrg
5143367019cSmrg	if (p->window == q->window) {
5153367019cSmrg	    TRACE(("pending Configure...merged\n"));
5163367019cSmrg	    merge_this = True;
5173367019cSmrg	}
5183367019cSmrg	if (!merge_this) {
5193367019cSmrg	    TRACE(("pending Configure...skipped\n"));
5203367019cSmrg	    XtDispatchEvent(target);
5213367019cSmrg	}
5223367019cSmrg	*target = next_event;
5233367019cSmrg    }
5243367019cSmrg    XtDispatchEvent(target);
5253367019cSmrg    return XtAppPending(app_con);
5263367019cSmrg}
5273367019cSmrg
5283367019cSmrg/*
5293367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5303367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5313367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5323367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5333367019cSmrg * point.
5343367019cSmrg *
5353367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5363367019cSmrg */
5373367019cSmrgXtInputMask
5383367019cSmrgxtermAppPending(void)
5393367019cSmrg{
5403367019cSmrg    XtInputMask result = XtAppPending(app_con);
5413367019cSmrg    XEvent this_event;
542037a25ddSmrg    Boolean found = False;
5433367019cSmrg
5443367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
545037a25ddSmrg	found = True;
5463367019cSmrg	if (this_event.type == Expose) {
5473367019cSmrg	    result = mergeExposeEvents(&this_event);
548fa3f02f3Smrg	    TRACE(("got merged expose events\n"));
5493367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5503367019cSmrg	    result = mergeConfigureEvents(&this_event);
551fa3f02f3Smrg	    TRACE(("got merged configure notify events\n"));
5523367019cSmrg	} else {
5533367019cSmrg	    TRACE(("pending %s\n", visibleEventType(this_event.type)));
5543367019cSmrg	    break;
5553367019cSmrg	}
5563367019cSmrg    }
557037a25ddSmrg
558037a25ddSmrg    /*
559037a25ddSmrg     * With NetBSD, closing a shell results in closing the X input event
560037a25ddSmrg     * stream, which interferes with the "-hold" option.  Wait a short time in
561037a25ddSmrg     * this case, to avoid max'ing the CPU.
562037a25ddSmrg     */
563037a25ddSmrg    if (hold_screen && caught_intr && !found) {
564037a25ddSmrg	Sleep(10);
565037a25ddSmrg    }
5663367019cSmrg    return result;
5673367019cSmrg}
5683367019cSmrg
569d522f475Smrgvoid
570d522f475Smrgxevents(void)
571d522f475Smrg{
572d522f475Smrg    XtermWidget xw = term;
573d522f475Smrg    TScreen *screen = TScreenOf(xw);
574d522f475Smrg    XEvent event;
575d522f475Smrg    XtInputMask input_mask;
576d522f475Smrg
577d522f475Smrg    if (need_cleanup)
5783367019cSmrg	NormalExit();
579d522f475Smrg
580d522f475Smrg    if (screen->scroll_amt)
581d522f475Smrg	FlushScroll(xw);
582d522f475Smrg    /*
583d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
584d522f475Smrg     * will process the timeout and return without blockng on the
585cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
586d522f475Smrg     * with select().
587d522f475Smrg     */
5883367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
589cd3331d0Smrg	if (input_mask & XtIMTimer)
590cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
591d522f475Smrg#if OPT_SESSION_MGT
592cd3331d0Smrg	/*
593cd3331d0Smrg	 * Session management events are alternative input events. Deal with
594cd3331d0Smrg	 * them in the same way.
595cd3331d0Smrg	 */
596cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
597cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
598d522f475Smrg#endif
599cd3331d0Smrg	else
600cd3331d0Smrg	    break;
601cd3331d0Smrg    }
602d522f475Smrg
603d522f475Smrg    /*
604d522f475Smrg     * If there's no XEvents, don't wait around...
605d522f475Smrg     */
606d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
607d522f475Smrg	return;
608d522f475Smrg    do {
609d522f475Smrg	/*
610d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
611d522f475Smrg	 * We simply ignore all events except for those not passed down to
612d522f475Smrg	 * this function, e.g., those handled in in_put().
613d522f475Smrg	 */
614d522f475Smrg	if (screen->waitingForTrackInfo) {
615d522f475Smrg	    Sleep(10);
616d522f475Smrg	    return;
617d522f475Smrg	}
618d522f475Smrg	XtAppNextEvent(app_con, &event);
619d522f475Smrg	/*
620d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
621d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
622d522f475Smrg	 * looking at the event ourselves we make sure that we can
623d522f475Smrg	 * do the right thing.
624d522f475Smrg	 */
625d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
626d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
627d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
628d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
629d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
630d522f475Smrg#if OPT_DEC_LOCATOR
631d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
632d522f475Smrg#endif /* OPT_DEC_LOCATOR */
633d522f475Smrg		   )
634d522f475Smrg		   && event.xany.type == MotionNotify
635d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
636d522f475Smrg	    SendMousePosition(xw, &event);
637cb4a1343Smrg	    xtermShowPointer(xw, True);
638d522f475Smrg	    continue;
639d522f475Smrg	}
640d522f475Smrg
641cb4a1343Smrg	/*
642cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
643cb4a1343Smrg	 * mouse pointer back on.
644cb4a1343Smrg	 */
645cb4a1343Smrg	if (screen->hide_pointer) {
6463367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6473367019cSmrg		switch (event.xany.type) {
6483367019cSmrg		case MotionNotify:
6493367019cSmrg		    xtermShowPointer(xw, True);
6503367019cSmrg		    break;
6513367019cSmrg		}
6523367019cSmrg	    } else {
6533367019cSmrg		switch (event.xany.type) {
6543367019cSmrg		case KeyPress:
6553367019cSmrg		case KeyRelease:
6563367019cSmrg		case ButtonPress:
6573367019cSmrg		case ButtonRelease:
6583367019cSmrg		    /* also these... */
6593367019cSmrg		case Expose:
660037a25ddSmrg		case GraphicsExpose:
6613367019cSmrg		case NoExpose:
6623367019cSmrg		case PropertyNotify:
6633367019cSmrg		case ClientMessage:
6643367019cSmrg		    break;
6653367019cSmrg		default:
6663367019cSmrg		    xtermShowPointer(xw, True);
6673367019cSmrg		    break;
6683367019cSmrg		}
669cb4a1343Smrg	    }
670cb4a1343Smrg	}
671cb4a1343Smrg
672d522f475Smrg	if (!event.xany.send_event ||
673d522f475Smrg	    screen->allowSendEvents ||
674d522f475Smrg	    ((event.xany.type != KeyPress) &&
675d522f475Smrg	     (event.xany.type != KeyRelease) &&
676d522f475Smrg	     (event.xany.type != ButtonPress) &&
677d522f475Smrg	     (event.xany.type != ButtonRelease))) {
678d522f475Smrg
679d522f475Smrg	    XtDispatchEvent(&event);
680d522f475Smrg	}
6813367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
682d522f475Smrg}
683d522f475Smrg
684d522f475Smrgstatic Cursor
685d522f475Smrgmake_hidden_cursor(XtermWidget xw)
686d522f475Smrg{
687d522f475Smrg    TScreen *screen = TScreenOf(xw);
688d522f475Smrg    Cursor c;
689d522f475Smrg    Display *dpy = screen->display;
690d522f475Smrg    XFontStruct *fn;
691d522f475Smrg
692d522f475Smrg    static XColor dummy;
693d522f475Smrg
694d522f475Smrg    /*
695d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
696d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
697b7c89284Ssnj     * server insists on drawing _something_.
698d522f475Smrg     */
699d522f475Smrg    TRACE(("Ask for nil2 font\n"));
700d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
701d522f475Smrg	TRACE(("...Ask for fixed font\n"));
702b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
703d522f475Smrg    }
704d522f475Smrg
705d522f475Smrg    if (fn != 0) {
706d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
707d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
708d522f475Smrg	XFreeFont(dpy, fn);
709d522f475Smrg    } else {
710d522f475Smrg	c = 0;
711d522f475Smrg    }
712d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
713d522f475Smrg    return (c);
714d522f475Smrg}
715d522f475Smrg
716fa3f02f3Smrg/*
717fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
718fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
719fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
720fa3f02f3Smrg * until the window is initialized.
721fa3f02f3Smrg */
722fa3f02f3Smrgvoid
723037a25ddSmrginit_colored_cursor(Display *dpy)
724fa3f02f3Smrg{
725fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
72694644356Smrg    static const char theme[] = "index.theme";
72794644356Smrg    static const char pattern[] = "xtermXXXXXX";
728fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
729fa3f02f3Smrg
730fa3f02f3Smrg    xterm_cursor_theme = 0;
731037a25ddSmrg    /*
732037a25ddSmrg     * The environment variable overrides a (possible) resource Xcursor.theme
733037a25ddSmrg     */
734fa3f02f3Smrg    if (IsEmpty(env)) {
735037a25ddSmrg	env = XGetDefault(dpy, "Xcursor", "theme");
736037a25ddSmrg    }
737037a25ddSmrg    /*
738037a25ddSmrg     * If neither found, provide our own default theme.
739037a25ddSmrg     */
740037a25ddSmrg    if (IsEmpty(env)) {
741037a25ddSmrg	const char *tmp_dir;
742037a25ddSmrg	char *filename;
743037a25ddSmrg	size_t needed;
744037a25ddSmrg
745fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
746fa3f02f3Smrg	    tmp_dir = P_tmpdir;
747fa3f02f3Smrg	}
748fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
749fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
750fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
751fa3f02f3Smrg
752fa3f02f3Smrg#ifdef HAVE_MKDTEMP
753fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
754fa3f02f3Smrg#else
755fa3f02f3Smrg	    if (mktemp(filename) != 0
756fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
757fa3f02f3Smrg		xterm_cursor_theme = filename;
758fa3f02f3Smrg	    }
759fa3f02f3Smrg#endif
760fa3f02f3Smrg	    /*
761fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
762fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
763fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
764fa3f02f3Smrg	     */
765fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
766fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
767037a25ddSmrg		FILE *fp;
768037a25ddSmrg
769fa3f02f3Smrg		strcat(leaf, "/");
770fa3f02f3Smrg		strcat(leaf, theme);
771fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
772fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
773fa3f02f3Smrg		    fclose(fp);
774fa3f02f3Smrg		    *leaf = '\0';
775fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
776fa3f02f3Smrg		    *leaf = '/';
777fa3f02f3Smrg		}
778fa3f02f3Smrg		atexit(cleanup_colored_cursor);
779fa3f02f3Smrg	    }
780fa3f02f3Smrg	}
781fa3f02f3Smrg    }
782037a25ddSmrg#else
783037a25ddSmrg    (void) dpy;
784fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
785fa3f02f3Smrg}
786fa3f02f3Smrg
787fa3f02f3Smrg/*
788fa3f02f3Smrg * Once done, discard the file and directory holding it.
789fa3f02f3Smrg */
790fa3f02f3Smrgvoid
791fa3f02f3Smrgcleanup_colored_cursor(void)
792fa3f02f3Smrg{
793fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
794fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
795fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
796fa3f02f3Smrg	struct stat sb;
797fa3f02f3Smrg	if (!IsEmpty(my_path)
798fa3f02f3Smrg	    && stat(my_path, &sb) == 0
799fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
800fa3f02f3Smrg	    unlink(xterm_cursor_theme);
801fa3f02f3Smrg	    rmdir(my_path);
802fa3f02f3Smrg	    free(xterm_cursor_theme);
803fa3f02f3Smrg	    xterm_cursor_theme = 0;
804fa3f02f3Smrg	}
805fa3f02f3Smrg    }
806fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
807fa3f02f3Smrg}
808fa3f02f3Smrg
809d522f475SmrgCursor
810d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
811d522f475Smrg		    unsigned long fg,	/* pixel value */
812d522f475Smrg		    unsigned long bg)	/* pixel value */
813d522f475Smrg{
814d522f475Smrg    TScreen *screen = TScreenOf(term);
815d522f475Smrg    Cursor c;
816d522f475Smrg    Display *dpy = screen->display;
817d522f475Smrg
818d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
819d522f475Smrg    if (c != None) {
820d522f475Smrg	recolor_cursor(screen, c, fg, bg);
821d522f475Smrg    }
822d522f475Smrg    return (c);
823d522f475Smrg}
824d522f475Smrg
825d522f475Smrg/* ARGSUSED */
826d522f475Smrgvoid
827d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
8289a64e1c5Smrg		 XEvent *event,
829fa3f02f3Smrg		 String *params GCC_UNUSED,
830d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
831d522f475Smrg{
832cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
833cd3331d0Smrg    Input(term, &event->xkey, False);
834d522f475Smrg}
835d522f475Smrg
836d522f475Smrg/* ARGSUSED */
837d522f475Smrgvoid
838d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
8399a64e1c5Smrg			 XEvent *event,
840fa3f02f3Smrg			 String *params GCC_UNUSED,
841d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
842d522f475Smrg{
843cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
844cd3331d0Smrg    Input(term, &event->xkey, True);
845d522f475Smrg}
846d522f475Smrg
847d522f475Smrg/* ARGSUSED */
848d522f475Smrgvoid
849d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
8509a64e1c5Smrg		  XEvent *event GCC_UNUSED,
851fa3f02f3Smrg		  String *params,
852d522f475Smrg		  Cardinal *nparams)
853d522f475Smrg{
854d522f475Smrg
855d522f475Smrg    if (*nparams != 1)
856d522f475Smrg	return;
857d522f475Smrg
858d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
8590d92cbfdSchristos	const char *abcdef = "ABCDEF";
8600d92cbfdSchristos	const char *xxxxxx;
861cd3331d0Smrg	Char c;
862cd3331d0Smrg	UString p;
8630d92cbfdSchristos	unsigned value = 0;
8640d92cbfdSchristos
865cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
8660d92cbfdSchristos	     '\0'; p++) {
8670d92cbfdSchristos	    value *= 16;
868d522f475Smrg	    if (c >= '0' && c <= '9')
8690d92cbfdSchristos		value += (unsigned) (c - '0');
870fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
8710d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
872d522f475Smrg	    else
873d522f475Smrg		break;
874d522f475Smrg	}
8750d92cbfdSchristos	if (c == '\0') {
8760d92cbfdSchristos	    Char hexval[2];
8770d92cbfdSchristos	    hexval[0] = (Char) value;
8780d92cbfdSchristos	    hexval[1] = 0;
879b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
8800d92cbfdSchristos	}
881d522f475Smrg    } else {
882cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
883d522f475Smrg    }
884d522f475Smrg}
885d522f475Smrg
886d522f475Smrg#if OPT_EXEC_XTERM
887d522f475Smrg
888d522f475Smrg#ifndef PROCFS_ROOT
889d522f475Smrg#define PROCFS_ROOT "/proc"
890d522f475Smrg#endif
891d522f475Smrg
892037a25ddSmrg/*
893037a25ddSmrg * Determine the current working directory of the child so that we can
894037a25ddSmrg * spawn a new terminal in the same directory.
895037a25ddSmrg *
896037a25ddSmrg * If we cannot get the CWD of the child, just use our own.
897037a25ddSmrg */
898037a25ddSmrgchar *
899037a25ddSmrgProcGetCWD(pid_t pid)
900037a25ddSmrg{
901037a25ddSmrg    char *child_cwd = NULL;
902037a25ddSmrg
903037a25ddSmrg    if (pid) {
904037a25ddSmrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
905037a25ddSmrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
906037a25ddSmrg	child_cwd = Readlink(child_cwd_link);
907037a25ddSmrg    }
908037a25ddSmrg    return child_cwd;
909037a25ddSmrg}
910037a25ddSmrg
911d522f475Smrg/* ARGSUSED */
912d522f475Smrgvoid
913d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
9149a64e1c5Smrg		    XEvent *event GCC_UNUSED,
915fa3f02f3Smrg		    String *params,
916d522f475Smrg		    Cardinal *nparams)
917d522f475Smrg{
918cd3331d0Smrg    TScreen *screen = TScreenOf(term);
919d522f475Smrg    char *child_cwd = NULL;
920d522f475Smrg    char *child_exe;
921d522f475Smrg    pid_t pid;
922d522f475Smrg
923d522f475Smrg    /*
924d522f475Smrg     * Try to find the actual program which is running in the child process.
925d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
926d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
927d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
928d522f475Smrg     */
929d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
930d522f475Smrg    if (!child_exe) {
931cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
932cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
933d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
934d522f475Smrg	} else {
9353367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
936d522f475Smrg	}
937d522f475Smrg	if (child_exe == 0)
938d522f475Smrg	    return;
939d522f475Smrg    }
940d522f475Smrg
941037a25ddSmrg    child_cwd = ProcGetCWD(screen->pid);
942d522f475Smrg
943d522f475Smrg    /* The reaper will take care of cleaning up the child */
944d522f475Smrg    pid = fork();
945d522f475Smrg    if (pid == -1) {
9463367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
947d522f475Smrg    } else if (!pid) {
948d522f475Smrg	/* We are the child */
949d522f475Smrg	if (child_cwd) {
950cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
951d522f475Smrg	}
952d522f475Smrg
953d522f475Smrg	if (setuid(screen->uid) == -1
954d522f475Smrg	    || setgid(screen->gid) == -1) {
9553367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
956d522f475Smrg	} else {
9570d92cbfdSchristos	    unsigned myargc = *nparams + 1;
958d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
959d522f475Smrg
96094644356Smrg	    if (myargv != 0) {
96194644356Smrg		unsigned n = 0;
962d522f475Smrg
96394644356Smrg		myargv[n++] = child_exe;
964d522f475Smrg
96594644356Smrg		while (n < myargc) {
96694644356Smrg		    myargv[n++] = (char *) *params++;
96794644356Smrg		}
96894644356Smrg
96994644356Smrg		myargv[n] = 0;
97094644356Smrg		execv(child_exe, myargv);
97194644356Smrg	    }
972d522f475Smrg
973d522f475Smrg	    /* If we get here, we've failed */
9743367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
975d522f475Smrg	}
976d522f475Smrg	_exit(0);
977d522f475Smrg    }
9783367019cSmrg
9793367019cSmrg    /* We are the parent; clean up */
9803367019cSmrg    if (child_cwd)
9813367019cSmrg	free(child_cwd);
9823367019cSmrg    free(child_exe);
983d522f475Smrg}
984d522f475Smrg#endif /* OPT_EXEC_XTERM */
985d522f475Smrg
986d522f475Smrg/*
987d522f475Smrg * Rather than sending characters to the host, put them directly into our
988d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
989d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
990d522f475Smrg *
991d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
992d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
993d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
994d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
995d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
996d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
997d522f475Smrg */
998d522f475Smrg/* ARGSUSED */
999d522f475Smrgvoid
1000d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
10019a64e1c5Smrg		XEvent *event GCC_UNUSED,
1002fa3f02f3Smrg		String *params,
1003d522f475Smrg		Cardinal *param_count)
1004d522f475Smrg{
1005d522f475Smrg    if (*param_count == 1) {
1006cd3331d0Smrg	const char *value = params[0];
1007b7c89284Ssnj	int need = (int) strlen(value);
1008cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
1009cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
1010d522f475Smrg
1011d522f475Smrg	if (have - used + need < BUF_SIZE) {
1012d522f475Smrg
1013cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
1014d522f475Smrg
1015d522f475Smrg	    TRACE(("Interpret %s\n", value));
1016d522f475Smrg	    VTbuffer->update++;
1017d522f475Smrg	}
1018d522f475Smrg    }
1019d522f475Smrg}
1020d522f475Smrg
1021d522f475Smrg/*ARGSUSED*/
1022d522f475Smrgvoid
1023d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
1024d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10259a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1026fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1027d522f475Smrg{
1028d522f475Smrg    /* NOP since we handled it above */
1029d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
1030cd3331d0Smrg    TRACE_FOCUS(w, event);
1031d522f475Smrg}
1032d522f475Smrg
1033d522f475Smrg/*ARGSUSED*/
1034d522f475Smrgvoid
1035d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
1036d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10379a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1038fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1039d522f475Smrg{
1040d522f475Smrg    /* NOP since we handled it above */
1041d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1042cd3331d0Smrg    TRACE_FOCUS(w, event);
1043d522f475Smrg}
1044d522f475Smrg
1045d522f475Smrg/*ARGSUSED*/
1046d522f475Smrgvoid
1047d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1048d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10499a64e1c5Smrg		  XEvent *ev,
1050fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1051d522f475Smrg{
1052d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1053d522f475Smrg    XtermWidget xw = term;
1054d522f475Smrg    TScreen *screen = TScreenOf(xw);
1055d522f475Smrg
10563367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1057d522f475Smrg	   visibleEventType(event->type),
10583367019cSmrg	   visibleNotifyMode(event->mode),
10593367019cSmrg	   visibleNotifyDetail(event->detail)));
1060cd3331d0Smrg    TRACE_FOCUS(xw, event);
1061d522f475Smrg
1062d522f475Smrg    if (screen->quiet_grab
1063d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1064c219fbebSmrg	/* EMPTY */ ;
1065d522f475Smrg    } else if (event->type == FocusIn) {
106694644356Smrg	if (event->detail != NotifyPointer) {
106794644356Smrg	    setXUrgency(xw, False);
106894644356Smrg	}
1069d522f475Smrg
1070d522f475Smrg	/*
1071d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1072d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1073d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1074d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1075d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1076d522f475Smrg	 * delivered to the obscured window.
1077d522f475Smrg	 */
1078d522f475Smrg	if (event->detail == NotifyNonlinear
1079d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
10803367019cSmrg	    unselectwindow(xw, INWINDOW);
1081d522f475Smrg	}
10823367019cSmrg	selectwindow(xw,
1083d522f475Smrg		     ((event->detail == NotifyPointer)
1084d522f475Smrg		      ? INWINDOW
1085d522f475Smrg		      : FOCUS));
1086d522f475Smrg	SendFocusButton(xw, event);
1087d522f475Smrg    } else {
1088d522f475Smrg#if OPT_FOCUS_EVENT
1089d522f475Smrg	if (event->type == FocusOut) {
1090d522f475Smrg	    SendFocusButton(xw, event);
1091d522f475Smrg	}
1092d522f475Smrg#endif
1093d522f475Smrg	/*
1094d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1095d522f475Smrg	 * ignore.
1096d522f475Smrg	 */
1097d522f475Smrg	if (event->mode != NotifyGrab) {
10983367019cSmrg	    unselectwindow(xw,
1099d522f475Smrg			   ((event->detail == NotifyPointer)
1100d522f475Smrg			    ? INWINDOW
1101d522f475Smrg			    : FOCUS));
1102d522f475Smrg	}
1103d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1104cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1105d522f475Smrg	    ReverseVideo(xw);
1106d522f475Smrg	    screen->grabbedKbd = False;
1107d522f475Smrg	    update_securekbd();
1108d522f475Smrg	}
1109d522f475Smrg    }
1110d522f475Smrg}
1111d522f475Smrg
1112d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1113d522f475Smrg
1114b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1115b7c89284Ssnjstatic Atom
1116b7c89284SsnjAtomBell(XtermWidget xw, int which)
1117b7c89284Ssnj{
1118b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1119b7c89284Ssnj    static struct {
1120b7c89284Ssnj	int value;
1121b7c89284Ssnj	const char *name;
1122b7c89284Ssnj    } table[] = {
1123b7c89284Ssnj	DATA(Info),
1124b7c89284Ssnj	    DATA(MarginBell),
1125b7c89284Ssnj	    DATA(MinorError),
1126b7c89284Ssnj	    DATA(TerminalBell)
1127b7c89284Ssnj    };
1128b7c89284Ssnj    Cardinal n;
1129b7c89284Ssnj    Atom result = None;
1130b7c89284Ssnj
1131b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1132b7c89284Ssnj	if (table[n].value == which) {
1133cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1134b7c89284Ssnj	    break;
1135b7c89284Ssnj	}
1136b7c89284Ssnj    }
1137b7c89284Ssnj    return result;
1138b7c89284Ssnj}
1139b7c89284Ssnj#endif
1140b7c89284Ssnj
1141d522f475Smrgvoid
1142b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1143d522f475Smrg{
1144b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1145b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1146b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1147cd3331d0Smrg#endif
1148cd3331d0Smrg
1149cd3331d0Smrg    switch (which) {
1150cd3331d0Smrg    case XkbBI_Info:
1151cd3331d0Smrg    case XkbBI_MinorError:
1152cd3331d0Smrg    case XkbBI_MajorError:
1153cd3331d0Smrg    case XkbBI_TerminalBell:
1154cd3331d0Smrg	switch (screen->warningVolume) {
1155cd3331d0Smrg	case bvOff:
1156cd3331d0Smrg	    percent = -100;
1157cd3331d0Smrg	    break;
1158cd3331d0Smrg	case bvLow:
1159cd3331d0Smrg	    break;
1160cd3331d0Smrg	case bvHigh:
1161cd3331d0Smrg	    percent = 100;
1162cd3331d0Smrg	    break;
1163cd3331d0Smrg	}
1164cd3331d0Smrg	break;
1165cd3331d0Smrg    case XkbBI_MarginBell:
1166cd3331d0Smrg	switch (screen->marginVolume) {
1167cd3331d0Smrg	case bvOff:
1168cd3331d0Smrg	    percent = -100;
1169cd3331d0Smrg	    break;
1170cd3331d0Smrg	case bvLow:
1171cd3331d0Smrg	    break;
1172cd3331d0Smrg	case bvHigh:
1173cd3331d0Smrg	    percent = 100;
1174cd3331d0Smrg	    break;
1175cd3331d0Smrg	}
1176cd3331d0Smrg	break;
1177cd3331d0Smrg    default:
1178cd3331d0Smrg	break;
1179cd3331d0Smrg    }
1180cd3331d0Smrg
1181cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1182b7c89284Ssnj    if (tony != None) {
1183c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1184b7c89284Ssnj    } else
1185b7c89284Ssnj#endif
1186b7c89284Ssnj	XBell(screen->display, percent);
1187b7c89284Ssnj}
1188b7c89284Ssnj
1189b7c89284Ssnjvoid
1190cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1191b7c89284Ssnj{
1192b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1193d522f475Smrg    struct timeval curtime;
1194d522f475Smrg
1195b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1196b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1197d522f475Smrg	return;
1198d522f475Smrg    }
1199d522f475Smrg
1200c219fbebSmrg    setXUrgency(xw, True);
1201d522f475Smrg
1202d522f475Smrg    /* has enough time gone by that we are allowed to ring
1203d522f475Smrg       the bell again? */
1204d522f475Smrg    if (screen->bellSuppressTime) {
1205037a25ddSmrg	long now_msecs;
1206037a25ddSmrg
1207d522f475Smrg	if (screen->bellInProgress) {
1208d522f475Smrg	    do_xevents();
1209d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1210d522f475Smrg		return;
1211d522f475Smrg	    }
1212d522f475Smrg	}
1213d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1214d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1215d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1216d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1217d522f475Smrg	    return;
1218d522f475Smrg	}
1219d522f475Smrg	lastBellTime = now_msecs;
1220d522f475Smrg    }
1221d522f475Smrg
1222d522f475Smrg    if (screen->visualbell) {
1223d522f475Smrg	VisualBell();
1224d522f475Smrg    } else {
1225b7c89284Ssnj	xtermBell(xw, which, percent);
1226d522f475Smrg    }
1227d522f475Smrg
1228d522f475Smrg    if (screen->poponbell)
1229c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1230d522f475Smrg
1231d522f475Smrg    if (screen->bellSuppressTime) {
1232d522f475Smrg	/* now we change a property and wait for the notify event to come
1233d522f475Smrg	   back.  If the server is suspending operations while the bell
1234d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1235d522f475Smrg	   know when the previous bell has finished */
1236d522f475Smrg	Widget w = CURRENT_EMU();
1237d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1238d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1239d522f475Smrg	screen->bellInProgress = True;
1240d522f475Smrg    }
1241d522f475Smrg}
1242d522f475Smrg
1243d522f475Smrg#define VB_DELAY screen->visualBellDelay
1244d522f475Smrg
1245d522f475Smrgstatic void
1246fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1247d522f475Smrg{
12483367019cSmrg    int y = 0;
12493367019cSmrg    int x = 0;
12503367019cSmrg
12513367019cSmrg    if (screen->flash_line) {
12523367019cSmrg	y = CursorY(screen, screen->cur_row);
12533367019cSmrg	height = (unsigned) FontHeight(screen);
12543367019cSmrg    }
12553367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1256d522f475Smrg    XFlush(screen->display);
1257d522f475Smrg    Sleep(VB_DELAY);
12583367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1259d522f475Smrg}
1260d522f475Smrg
1261d522f475Smrgvoid
1262d522f475SmrgVisualBell(void)
1263d522f475Smrg{
1264d522f475Smrg    TScreen *screen = TScreenOf(term);
1265d522f475Smrg
1266d522f475Smrg    if (VB_DELAY > 0) {
1267d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1268d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1269d522f475Smrg	XGCValues gcval;
1270d522f475Smrg	GC visualGC;
1271d522f475Smrg
1272d522f475Smrg	gcval.function = GXxor;
1273d522f475Smrg	gcval.foreground = xorPixel;
1274d522f475Smrg	visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval);
1275d522f475Smrg#if OPT_TEK4014
1276d522f475Smrg	if (TEK4014_ACTIVE(term)) {
1277cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1278d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1279d522f475Smrg			TFullWidth(tekscr),
1280d522f475Smrg			TFullHeight(tekscr));
1281d522f475Smrg	} else
1282d522f475Smrg#endif
1283d522f475Smrg	{
1284d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1285d522f475Smrg			FullWidth(screen),
1286d522f475Smrg			FullHeight(screen));
1287d522f475Smrg	}
1288d522f475Smrg	XtReleaseGC((Widget) term, visualGC);
1289d522f475Smrg    }
1290d522f475Smrg}
1291d522f475Smrg
1292d522f475Smrg/* ARGSUSED */
1293d522f475Smrgvoid
1294d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1295d522f475Smrg			 XtPointer data GCC_UNUSED,
12969a64e1c5Smrg			 XEvent *ev,
1297fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1298d522f475Smrg{
1299d522f475Smrg    TScreen *screen = TScreenOf(term);
1300d522f475Smrg
1301d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1302d522f475Smrg	screen->bellInProgress = False;
1303d522f475Smrg    }
1304d522f475Smrg}
1305d522f475Smrg
13063367019cSmrgvoid
13073367019cSmrgxtermWarning(const char *fmt,...)
13083367019cSmrg{
13093367019cSmrg    int save_err = errno;
13103367019cSmrg    va_list ap;
13113367019cSmrg
1312fa3f02f3Smrg    TRACE(("xtermWarning fmt='%s'\n", fmt));
13133367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
13143367019cSmrg    va_start(ap, fmt);
13153367019cSmrg    vfprintf(stderr, fmt, ap);
13163367019cSmrg    (void) fflush(stderr);
13173367019cSmrg
13183367019cSmrg    va_end(ap);
13193367019cSmrg    errno = save_err;
13203367019cSmrg}
13213367019cSmrg
13223367019cSmrgvoid
13233367019cSmrgxtermPerror(const char *fmt,...)
13243367019cSmrg{
13253367019cSmrg    int save_err = errno;
13263367019cSmrg    char *msg = strerror(errno);
13273367019cSmrg    va_list ap;
13283367019cSmrg
1329fa3f02f3Smrg    TRACE(("xtermPerror fmt='%s', msg='%s'\n", fmt, NonNull(msg)));
13303367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
13313367019cSmrg    va_start(ap, fmt);
13323367019cSmrg    vfprintf(stderr, fmt, ap);
13333367019cSmrg    fprintf(stderr, ": %s\n", msg);
13343367019cSmrg    (void) fflush(stderr);
13353367019cSmrg
13363367019cSmrg    va_end(ap);
13373367019cSmrg    errno = save_err;
13383367019cSmrg}
13393367019cSmrg
1340d522f475SmrgWindow
1341c219fbebSmrgWMFrameWindow(XtermWidget xw)
1342d522f475Smrg{
1343d522f475Smrg    Window win_root, win_current, *children;
1344d522f475Smrg    Window win_parent = 0;
1345d522f475Smrg    unsigned int nchildren;
1346d522f475Smrg
1347c219fbebSmrg    win_current = XtWindow(xw);
1348d522f475Smrg
1349d522f475Smrg    /* find the parent which is child of root */
1350d522f475Smrg    do {
1351d522f475Smrg	if (win_parent)
1352d522f475Smrg	    win_current = win_parent;
1353c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1354d522f475Smrg		   win_current,
1355d522f475Smrg		   &win_root,
1356d522f475Smrg		   &win_parent,
1357d522f475Smrg		   &children,
1358d522f475Smrg		   &nchildren);
1359d522f475Smrg	XFree(children);
1360d522f475Smrg    } while (win_root != win_parent);
1361d522f475Smrg
1362d522f475Smrg    return win_current;
1363d522f475Smrg}
1364d522f475Smrg
1365d522f475Smrg#if OPT_DABBREV
1366d522f475Smrg/*
1367d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1368d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1369d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1370d522f475Smrg * expansions and ignores one of them if they are identical.
1371d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1372d522f475Smrg */
1373d522f475Smrg
1374d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1375d522f475Smrg
1376d522f475Smrgstatic int
1377fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1378d522f475Smrg{
1379b7c89284Ssnj    int result = -1;
1380b7c89284Ssnj    int firstLine = -(screen->savedlines);
1381d522f475Smrg
1382b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1383b7c89284Ssnj    while (cell->row >= firstLine) {
1384b7c89284Ssnj	if (--(cell->col) >= 0) {
1385b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1386b7c89284Ssnj	    break;
1387b7c89284Ssnj	}
1388b7c89284Ssnj	if (--(cell->row) < firstLine)
1389b7c89284Ssnj	    break;		/* ...there is no previous line */
1390b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1391b7c89284Ssnj	cell->col = MaxCols(screen);
1392b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1393b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1394d522f475Smrg	    break;
1395b7c89284Ssnj	}
1396d522f475Smrg    }
1397b7c89284Ssnj    return result;
1398d522f475Smrg}
1399d522f475Smrg
1400d522f475Smrgstatic char *
14019a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1402d522f475Smrg{
14039a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1404d522f475Smrg    char *abword;
1405d522f475Smrg    int c;
14069a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1407b7c89284Ssnj    char *result = 0;
1408d522f475Smrg
1409b7c89284Ssnj    abword = ab_end;
1410d522f475Smrg    *abword = '\0';		/* end of string marker */
1411d522f475Smrg
1412b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1413b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
14149a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1415b7c89284Ssnj	    *(--abword) = (char) c;
1416d522f475Smrg    }
1417d522f475Smrg
1418b7c89284Ssnj    if (c >= 0) {
1419b7c89284Ssnj	result = abword;
1420b7c89284Ssnj    } else if (abword != ab_end) {
1421b7c89284Ssnj	result = abword;
1422b7c89284Ssnj    }
1423b7c89284Ssnj
1424b7c89284Ssnj    if (result != 0) {
1425b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1426b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1427b7c89284Ssnj	    ;			/* skip preceding spaces */
1428b7c89284Ssnj	}
1429b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1430b7c89284Ssnj    }
1431b7c89284Ssnj    return result;
1432d522f475Smrg}
1433d522f475Smrg
1434d522f475Smrgstatic int
14359a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1436d522f475Smrg{
14379a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1438d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1439d522f475Smrg
1440b7c89284Ssnj    static CELL cell;
1441d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1442d522f475Smrg    static unsigned int expansions;
1443d522f475Smrg
1444d522f475Smrg    char *expansion;
1445d522f475Smrg    size_t hint_len;
1446b7c89284Ssnj    int result = 0;
1447b7c89284Ssnj    LineData *ld;
1448d522f475Smrg
1449d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1450d522f475Smrg	expansions = 0;
1451b7c89284Ssnj	cell.col = screen->cur_col;
1452b7c89284Ssnj	cell.row = screen->cur_row;
1453b7c89284Ssnj
1454b7c89284Ssnj	if (dabbrev_hint != 0)
1455b7c89284Ssnj	    free(dabbrev_hint);
1456b7c89284Ssnj
14579a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1458b7c89284Ssnj
1459b7c89284Ssnj	    if (lastexpansion != 0)
1460b7c89284Ssnj		free(lastexpansion);
1461b7c89284Ssnj
1462b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1463b7c89284Ssnj
1464b7c89284Ssnj		/* make own copy */
1465b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1466b7c89284Ssnj		    screen->dabbrev_working = True;
1467b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1468b7c89284Ssnj		}
1469cd3331d0Smrg	    } else {
1470cd3331d0Smrg		return result;
1471b7c89284Ssnj	    }
1472cd3331d0Smrg	} else {
1473cd3331d0Smrg	    return result;
1474d522f475Smrg	}
1475b7c89284Ssnj	if (!screen->dabbrev_working) {
1476b7c89284Ssnj	    if (lastexpansion != 0) {
1477b7c89284Ssnj		free(lastexpansion);
1478b7c89284Ssnj		lastexpansion = 0;
1479b7c89284Ssnj	    }
1480b7c89284Ssnj	    return result;
1481b7c89284Ssnj	}
1482d522f475Smrg    }
1483d522f475Smrg
1484cd3331d0Smrg    if (dabbrev_hint == 0)
1485cd3331d0Smrg	return result;
1486cd3331d0Smrg
1487d522f475Smrg    hint_len = strlen(dabbrev_hint);
1488d522f475Smrg    for (;;) {
14899a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1490d522f475Smrg	    if (expansions >= 2) {
1491d522f475Smrg		expansions = 0;
1492b7c89284Ssnj		cell.col = screen->cur_col;
1493b7c89284Ssnj		cell.row = screen->cur_row;
1494d522f475Smrg		continue;
1495d522f475Smrg	    }
1496d522f475Smrg	    break;
1497d522f475Smrg	}
1498d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1499d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1500d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1501d522f475Smrg	    break;
1502d522f475Smrg    }
1503d522f475Smrg
1504b7c89284Ssnj    if (expansion != 0) {
1505037a25ddSmrg	Char *copybuffer;
1506037a25ddSmrg	size_t del_cnt = strlen(lastexpansion) - hint_len;
1507037a25ddSmrg	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1508b7c89284Ssnj
1509b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1510b7c89284Ssnj	    /* delete previous expansion */
1511b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1512b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1513b7c89284Ssnj		    expansion + hint_len,
1514b7c89284Ssnj		    strlen(expansion) - hint_len);
1515cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1516b7c89284Ssnj	    /* v_write() just reset our flag */
1517b7c89284Ssnj	    screen->dabbrev_working = True;
1518b7c89284Ssnj	    free(copybuffer);
1519b7c89284Ssnj
1520b7c89284Ssnj	    free(lastexpansion);
1521b7c89284Ssnj
1522b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1523b7c89284Ssnj		result = 1;
1524b7c89284Ssnj		expansions++;
1525b7c89284Ssnj	    }
1526b7c89284Ssnj	}
1527b7c89284Ssnj    }
1528b7c89284Ssnj
1529b7c89284Ssnj    return result;
1530d522f475Smrg}
1531d522f475Smrg
1532d522f475Smrg/*ARGSUSED*/
1533d522f475Smrgvoid
1534b7c89284SsnjHandleDabbrevExpand(Widget w,
15359a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1536fa3f02f3Smrg		    String *params GCC_UNUSED,
1537d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1538d522f475Smrg{
1539b7c89284Ssnj    XtermWidget xw;
1540b7c89284Ssnj
1541cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1542b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
15439a64e1c5Smrg	if (!dabbrev_expand(xw))
1544cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1545d522f475Smrg    }
1546d522f475Smrg}
1547d522f475Smrg#endif /* OPT_DABBREV */
1548d522f475Smrg
1549d522f475Smrg#if OPT_MAXIMIZE
1550d522f475Smrg/*ARGSUSED*/
1551d522f475Smrgvoid
1552b7c89284SsnjHandleDeIconify(Widget w,
15539a64e1c5Smrg		XEvent *event GCC_UNUSED,
1554fa3f02f3Smrg		String *params GCC_UNUSED,
1555d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1556d522f475Smrg{
1557b7c89284Ssnj    XtermWidget xw;
1558b7c89284Ssnj
1559b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1560b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1561c219fbebSmrg	XMapWindow(screen->display, VShellWindow(xw));
1562d522f475Smrg    }
1563d522f475Smrg}
1564d522f475Smrg
1565d522f475Smrg/*ARGSUSED*/
1566d522f475Smrgvoid
1567b7c89284SsnjHandleIconify(Widget w,
15689a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1569fa3f02f3Smrg	      String *params GCC_UNUSED,
1570d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1571d522f475Smrg{
1572b7c89284Ssnj    XtermWidget xw;
1573b7c89284Ssnj
1574b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1575b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1576d522f475Smrg	XIconifyWindow(screen->display,
1577c219fbebSmrg		       VShellWindow(xw),
1578d522f475Smrg		       DefaultScreen(screen->display));
1579d522f475Smrg    }
1580d522f475Smrg}
1581d522f475Smrg
1582d522f475Smrgint
1583c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1584d522f475Smrg{
1585c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1586d522f475Smrg    XSizeHints hints;
1587d522f475Smrg    long supp = 0;
1588d522f475Smrg    Window root_win;
1589d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1590d522f475Smrg    int root_y = -1;
1591d522f475Smrg    unsigned root_border;
1592d522f475Smrg    unsigned root_depth;
15933367019cSmrg    int code;
1594d522f475Smrg
1595d522f475Smrg    if (XGetGeometry(screen->display,
1596c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1597d522f475Smrg		     &root_win,
1598d522f475Smrg		     &root_x,
1599d522f475Smrg		     &root_y,
1600d522f475Smrg		     width,
1601d522f475Smrg		     height,
1602d522f475Smrg		     &root_border,
1603d522f475Smrg		     &root_depth)) {
1604d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1605d522f475Smrg	       root_x,
1606d522f475Smrg	       root_y,
1607d522f475Smrg	       *width,
1608d522f475Smrg	       *height,
1609d522f475Smrg	       root_border));
1610d522f475Smrg
1611d522f475Smrg	*width -= (root_border * 2);
1612d522f475Smrg	*height -= (root_border * 2);
1613d522f475Smrg
1614d522f475Smrg	hints.flags = PMaxSize;
1615d522f475Smrg	if (XGetWMNormalHints(screen->display,
1616c219fbebSmrg			      VShellWindow(xw),
1617d522f475Smrg			      &hints,
1618d522f475Smrg			      &supp)
1619d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1620d522f475Smrg
1621d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1622d522f475Smrg		   hints.max_width,
1623d522f475Smrg		   hints.max_height));
1624d522f475Smrg
1625d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1626b7c89284Ssnj		*width = (unsigned) hints.max_width;
1627d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1628b7c89284Ssnj		*height = (unsigned) hints.max_height;
1629d522f475Smrg	}
16303367019cSmrg	code = 1;
16313367019cSmrg    } else {
16323367019cSmrg	*width = 0;
16333367019cSmrg	*height = 0;
16343367019cSmrg	code = 0;
1635d522f475Smrg    }
16363367019cSmrg    return code;
1637d522f475Smrg}
1638d522f475Smrg
1639d522f475Smrgvoid
1640c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1641d522f475Smrg{
1642c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1643d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1644d522f475Smrg    unsigned root_width, root_height;
16453367019cSmrg    Boolean success = False;
1646d522f475Smrg
16473367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
16483367019cSmrg	   maximize,
16493367019cSmrg	   (maximize
16503367019cSmrg	    ? "maximize"
16513367019cSmrg	    : "restore")));
1652d522f475Smrg
16533367019cSmrg    /*
16543367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
16553367019cSmrg     * as well as the estimated root-window size.
16563367019cSmrg     */
16573367019cSmrg    if (maximize
16583367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
16593367019cSmrg	&& xtermGetWinAttrs(screen->display,
16603367019cSmrg			    WMFrameWindow(xw),
16613367019cSmrg			    &wm_attrs)
16623367019cSmrg	&& xtermGetWinAttrs(screen->display,
16633367019cSmrg			    VShellWindow(xw),
16643367019cSmrg			    &vshell_attrs)) {
16653367019cSmrg
16663367019cSmrg	if (screen->restore_data != True
16673367019cSmrg	    || screen->restore_width != root_width
16683367019cSmrg	    || screen->restore_height != root_height) {
16693367019cSmrg	    screen->restore_data = True;
16703367019cSmrg	    screen->restore_x = wm_attrs.x + wm_attrs.border_width;
16713367019cSmrg	    screen->restore_y = wm_attrs.y + wm_attrs.border_width;
16723367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
16733367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
16743367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1675d522f475Smrg		   screen->restore_x,
1676d522f475Smrg		   screen->restore_y,
1677d522f475Smrg		   screen->restore_width,
1678d522f475Smrg		   screen->restore_height));
16793367019cSmrg	}
1680d522f475Smrg
16813367019cSmrg	/* subtract wm decoration dimensions */
16823367019cSmrg	root_width -= (unsigned) ((wm_attrs.width - vshell_attrs.width)
16833367019cSmrg				  + (wm_attrs.border_width * 2));
16843367019cSmrg	root_height -= (unsigned) ((wm_attrs.height - vshell_attrs.height)
16853367019cSmrg				   + (wm_attrs.border_width * 2));
16863367019cSmrg	success = True;
16873367019cSmrg    } else if (screen->restore_data) {
16883367019cSmrg	success = True;
16893367019cSmrg	maximize = 0;
16903367019cSmrg    }
16913367019cSmrg
16923367019cSmrg    if (success) {
16933367019cSmrg	switch (maximize) {
16943367019cSmrg	case 3:
16953367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
16963367019cSmrg	    break;
16973367019cSmrg	case 2:
16983367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
16993367019cSmrg	    break;
17003367019cSmrg	case 1:
17013367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
17023367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
17033367019cSmrg			      0 + wm_attrs.border_width,	/* x */
17043367019cSmrg			      0 + wm_attrs.border_width,	/* y */
17053367019cSmrg			      root_width,
17063367019cSmrg			      root_height);
17073367019cSmrg	    break;
17083367019cSmrg
17093367019cSmrg	default:
17103367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
17113367019cSmrg	    if (screen->restore_data) {
17123367019cSmrg		screen->restore_data = False;
17133367019cSmrg
17143367019cSmrg		TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
17153367019cSmrg		       screen->restore_x,
17163367019cSmrg		       screen->restore_y,
17173367019cSmrg		       screen->restore_width,
17183367019cSmrg		       screen->restore_height));
17193367019cSmrg
17203367019cSmrg		XMoveResizeWindow(screen->display,
17213367019cSmrg				  VShellWindow(xw),
17223367019cSmrg				  screen->restore_x,
17233367019cSmrg				  screen->restore_y,
17243367019cSmrg				  screen->restore_width,
17253367019cSmrg				  screen->restore_height);
17263367019cSmrg	    }
17273367019cSmrg	    break;
1728d522f475Smrg	}
1729d522f475Smrg    }
1730d522f475Smrg}
1731d522f475Smrg
1732d522f475Smrg/*ARGSUSED*/
1733d522f475Smrgvoid
1734b7c89284SsnjHandleMaximize(Widget w,
17359a64e1c5Smrg	       XEvent *event GCC_UNUSED,
1736fa3f02f3Smrg	       String *params GCC_UNUSED,
1737d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1738d522f475Smrg{
1739b7c89284Ssnj    XtermWidget xw;
1740b7c89284Ssnj
1741b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1742b7c89284Ssnj	RequestMaximize(xw, 1);
1743d522f475Smrg    }
1744d522f475Smrg}
1745d522f475Smrg
1746d522f475Smrg/*ARGSUSED*/
1747d522f475Smrgvoid
1748b7c89284SsnjHandleRestoreSize(Widget w,
17499a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1750fa3f02f3Smrg		  String *params GCC_UNUSED,
1751d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1752d522f475Smrg{
1753b7c89284Ssnj    XtermWidget xw;
1754b7c89284Ssnj
1755b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1756b7c89284Ssnj	RequestMaximize(xw, 0);
1757d522f475Smrg    }
1758d522f475Smrg}
1759d522f475Smrg#endif /* OPT_MAXIMIZE */
1760d522f475Smrg
1761d522f475Smrgvoid
1762d522f475SmrgRedraw(void)
1763d522f475Smrg{
1764d522f475Smrg    TScreen *screen = TScreenOf(term);
1765d522f475Smrg    XExposeEvent event;
1766d522f475Smrg
1767d522f475Smrg    TRACE(("Redraw\n"));
1768d522f475Smrg
1769d522f475Smrg    event.type = Expose;
1770d522f475Smrg    event.display = screen->display;
1771d522f475Smrg    event.x = 0;
1772d522f475Smrg    event.y = 0;
1773d522f475Smrg    event.count = 0;
1774d522f475Smrg
1775d522f475Smrg    if (VWindow(screen)) {
1776d522f475Smrg	event.window = VWindow(screen);
1777d522f475Smrg	event.width = term->core.width;
1778d522f475Smrg	event.height = term->core.height;
1779d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
17809a64e1c5Smrg						       (XEvent *) &event,
1781d522f475Smrg						       NULL);
1782d522f475Smrg	if (ScrollbarWidth(screen)) {
1783d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
17849a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
1785d522f475Smrg	}
1786d522f475Smrg    }
1787d522f475Smrg#if OPT_TEK4014
1788d522f475Smrg    if (TEK4014_SHOWN(term)) {
1789cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1790d522f475Smrg	event.window = TWindow(tekscr);
1791d522f475Smrg	event.width = tekWidget->core.width;
1792d522f475Smrg	event.height = tekWidget->core.height;
17939a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
1794d522f475Smrg    }
1795d522f475Smrg#endif
1796d522f475Smrg}
1797d522f475Smrg
1798d522f475Smrg#ifdef VMS
1799d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1800d522f475Smrg#else
1801d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1802d522f475Smrg#endif
1803d522f475Smrg
1804d522f475Smrgvoid
1805d522f475Smrgtimestamp_filename(char *dst, const char *src)
1806d522f475Smrg{
1807d522f475Smrg    time_t tstamp;
1808d522f475Smrg    struct tm *tstruct;
1809d522f475Smrg
1810d522f475Smrg    tstamp = time((time_t *) 0);
1811d522f475Smrg    tstruct = localtime(&tstamp);
1812d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1813d522f475Smrg	    src,
18143367019cSmrg	    (int) tstruct->tm_year + 1900,
1815d522f475Smrg	    tstruct->tm_mon + 1,
1816d522f475Smrg	    tstruct->tm_mday,
1817d522f475Smrg	    tstruct->tm_hour,
1818d522f475Smrg	    tstruct->tm_min,
1819d522f475Smrg	    tstruct->tm_sec);
1820d522f475Smrg}
1821d522f475Smrg
1822d522f475Smrgint
1823d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1824d522f475Smrg{
1825d522f475Smrg    int fd;
1826d522f475Smrg    struct stat sb;
1827d522f475Smrg
1828d522f475Smrg#ifdef VMS
1829d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1830d522f475Smrg	int the_error = errno;
18313367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
18323367019cSmrg		     path,
18333367019cSmrg		     the_error,
18343367019cSmrg		     SysErrorMsg(the_error));
1835d522f475Smrg	return -1;
1836d522f475Smrg    }
1837d522f475Smrg    chown(path, uid, gid);
1838d522f475Smrg#else
1839d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1840d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1841d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1842d522f475Smrg	int the_error = errno;
18433367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
18443367019cSmrg		     path,
18453367019cSmrg		     the_error,
18463367019cSmrg		     SysErrorMsg(the_error));
1847d522f475Smrg	return -1;
1848d522f475Smrg    }
1849d522f475Smrg#endif
1850d522f475Smrg
1851d522f475Smrg    /*
1852d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1853d522f475Smrg     * we do any damage, and that it is not world-writable.
1854d522f475Smrg     */
1855d522f475Smrg    if (fstat(fd, &sb) < 0
1856d522f475Smrg	|| sb.st_uid != uid
1857d522f475Smrg	|| (sb.st_mode & 022) != 0) {
18583367019cSmrg	xtermWarning("you do not own %s\n", path);
1859d522f475Smrg	close(fd);
1860d522f475Smrg	return -1;
1861d522f475Smrg    }
1862d522f475Smrg    return fd;
1863d522f475Smrg}
1864d522f475Smrg
1865d522f475Smrg#ifndef VMS
1866d522f475Smrg/*
1867d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1868d522f475Smrg * We could emulate this with careful use of access() and following
1869d522f475Smrg * symbolic links, but that is messy and has race conditions.
1870d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1871d522f475Smrg * being available.
1872d522f475Smrg *
1873d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1874d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1875d522f475Smrg * for the debug logs.
1876d522f475Smrg *
1877d522f475Smrg * Returns
1878d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1879d522f475Smrg *	-1 on error, e.g., cannot fork
1880d522f475Smrg *	 0 otherwise.
1881d522f475Smrg */
1882d522f475Smrgint
1883712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
1884d522f475Smrg{
1885d522f475Smrg    int fd;
1886d522f475Smrg    pid_t pid;
1887d522f475Smrg    int retval = 0;
1888d522f475Smrg    int childstat = 0;
1889d522f475Smrg#ifndef HAVE_WAITPID
1890d522f475Smrg    int waited;
18913367019cSmrg    void (*chldfunc) (int);
1892d522f475Smrg
1893d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1894d522f475Smrg#endif /* HAVE_WAITPID */
1895d522f475Smrg
1896d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1897d522f475Smrg	   (int) uid, (int) geteuid(),
1898d522f475Smrg	   (int) gid, (int) getegid(),
1899d522f475Smrg	   append,
1900d522f475Smrg	   pathname,
1901d522f475Smrg	   mode));
1902d522f475Smrg
1903d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1904d522f475Smrg	fd = open(pathname,
1905d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1906d522f475Smrg		  mode);
1907d522f475Smrg	if (fd >= 0)
1908d522f475Smrg	    close(fd);
1909d522f475Smrg	return (fd >= 0);
1910d522f475Smrg    }
1911d522f475Smrg
1912d522f475Smrg    pid = fork();
1913d522f475Smrg    switch (pid) {
1914d522f475Smrg    case 0:			/* child */
1915d522f475Smrg	if (setgid(gid) == -1
1916d522f475Smrg	    || setuid(uid) == -1) {
1917d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1918d522f475Smrg	    retval = 1;
1919d522f475Smrg	} else {
1920d522f475Smrg	    fd = open(pathname,
1921d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1922d522f475Smrg		      mode);
1923d522f475Smrg	    if (fd >= 0) {
1924d522f475Smrg		close(fd);
1925d522f475Smrg		retval = 0;
1926d522f475Smrg	    } else {
1927d522f475Smrg		retval = 1;
1928d522f475Smrg	    }
1929d522f475Smrg	}
1930d522f475Smrg	_exit(retval);
1931d522f475Smrg	/* NOTREACHED */
1932d522f475Smrg    case -1:			/* error */
1933d522f475Smrg	return retval;
1934d522f475Smrg    default:			/* parent */
1935d522f475Smrg#ifdef HAVE_WAITPID
1936d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1937d522f475Smrg#ifdef EINTR
1938d522f475Smrg	    if (errno == EINTR)
1939d522f475Smrg		continue;
1940d522f475Smrg#endif /* EINTR */
1941d522f475Smrg#ifdef ERESTARTSYS
1942d522f475Smrg	    if (errno == ERESTARTSYS)
1943d522f475Smrg		continue;
1944d522f475Smrg#endif /* ERESTARTSYS */
1945d522f475Smrg	    break;
1946d522f475Smrg	}
1947d522f475Smrg#else /* HAVE_WAITPID */
1948d522f475Smrg	waited = wait(&childstat);
1949d522f475Smrg	signal(SIGCHLD, chldfunc);
1950d522f475Smrg	/*
1951d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1952d522f475Smrg	   we might have missed the termination of our screen child.
1953d522f475Smrg	   If we can check for this possibility without hanging, do so.
1954d522f475Smrg	 */
1955d522f475Smrg	do
1956cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
19573367019cSmrg		NormalExit();
1958d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1959d522f475Smrg#endif /* HAVE_WAITPID */
1960d522f475Smrg#ifndef WIFEXITED
1961d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1962d522f475Smrg#endif
1963d522f475Smrg	if (WIFEXITED(childstat))
1964d522f475Smrg	    retval = 1;
1965d522f475Smrg	return retval;
1966d522f475Smrg    }
1967d522f475Smrg}
1968d522f475Smrg#endif /* !VMS */
1969d522f475Smrg
1970d522f475Smrgint
1971fa3f02f3SmrgxtermResetIds(TScreen *screen)
1972d522f475Smrg{
1973d522f475Smrg    int result = 0;
1974d522f475Smrg    if (setgid(screen->gid) == -1) {
19753367019cSmrg	xtermWarning("unable to reset group-id\n");
1976d522f475Smrg	result = -1;
1977d522f475Smrg    }
1978d522f475Smrg    if (setuid(screen->uid) == -1) {
19793367019cSmrg	xtermWarning("unable to reset user-id\n");
1980d522f475Smrg	result = -1;
1981d522f475Smrg    }
1982d522f475Smrg    return result;
1983d522f475Smrg}
1984d522f475Smrg
1985d522f475Smrg#ifdef ALLOWLOGGING
1986d522f475Smrg
1987d522f475Smrg/*
1988d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1989d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1990d522f475Smrg */
1991d522f475Smrg
1992d522f475Smrg#ifdef ALLOWLOGFILEEXEC
19933367019cSmrgstatic void
1994d522f475Smrglogpipe(int sig GCC_UNUSED)
1995d522f475Smrg{
1996cd3331d0Smrg    XtermWidget xw = term;
1997cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1998d522f475Smrg
19993367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2000d522f475Smrg#ifdef SYSV
2001d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2002d522f475Smrg#endif /* SYSV */
2003d522f475Smrg    if (screen->logging)
2004cd3331d0Smrg	CloseLog(xw);
2005d522f475Smrg}
2006d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2007d522f475Smrg
2008d522f475Smrgvoid
2009cd3331d0SmrgStartLog(XtermWidget xw)
2010d522f475Smrg{
2011d522f475Smrg    static char *log_default;
2012cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2013d522f475Smrg
2014d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2015d522f475Smrg	return;
2016d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
2017d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
2018d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2019d522f475Smrg			 0640);
2020d522f475Smrg    if (screen->logfd < 0)
2021d522f475Smrg	return;			/* open failed */
2022d522f475Smrg#else /*VMS */
2023d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
2024d522f475Smrg	if (screen->logfile)
2025d522f475Smrg	    free(screen->logfile);
2026d522f475Smrg	if (log_default == NULL) {
2027d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2028037a25ddSmrg	    const char form[] = "Xterm.log.%s%s.%d";
2029037a25ddSmrg	    char where[255 + 1];	/* Internet standard limit (RFC 1035):
2030d522f475Smrg					   ``To simplify implementations, the
2031d522f475Smrg					   total length of a domain name (i.e.,
2032d522f475Smrg					   label octets and label length
2033d522f475Smrg					   octets) is restricted to 255 octets
2034d522f475Smrg					   or less.'' */
2035037a25ddSmrg	    char when[LEN_TIMESTAMP];
2036037a25ddSmrg	    char formatted[sizeof(form) + sizeof(where) + sizeof(when) + 9];
2037d522f475Smrg	    time_t now;
2038d522f475Smrg	    struct tm *ltm;
2039d522f475Smrg
2040d522f475Smrg	    now = time((time_t *) 0);
2041d522f475Smrg	    ltm = (struct tm *) localtime(&now);
2042037a25ddSmrg	    if ((gethostname(where, sizeof(where)) == 0) &&
2043037a25ddSmrg		(strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0)) {
2044037a25ddSmrg		(void) sprintf(formatted, form, where, when, (int) getpid());
2045037a25ddSmrg	    } else {
2046037a25ddSmrg		return;
2047d522f475Smrg	    }
2048037a25ddSmrg	    if ((log_default = x_strdup(formatted)) == NULL) {
2049d522f475Smrg		return;
2050037a25ddSmrg	    }
2051d522f475Smrg#else
205294644356Smrg	    static const char log_def_name[] = "XtermLog.XXXXXX";
2053037a25ddSmrg	    if ((log_default = x_strdup(log_def_name)) == NULL) {
2054d522f475Smrg		return;
2055037a25ddSmrg	    }
2056d522f475Smrg	    mktemp(log_default);
2057d522f475Smrg#endif
2058d522f475Smrg	}
2059d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
2060d522f475Smrg	    return;
2061d522f475Smrg    }
2062d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
2063d522f475Smrg#ifdef ALLOWLOGFILEEXEC
2064d522f475Smrg	/*
2065d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
2066d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2067d522f475Smrg	 * done through escape sequences....  You have been warned.
2068d522f475Smrg	 */
2069d522f475Smrg	int pid;
2070d522f475Smrg	int p[2];
2071d522f475Smrg	static char *shell;
20723367019cSmrg	struct passwd pw;
20733367019cSmrg
20743367019cSmrg	if ((shell = x_getenv("SHELL")) == NULL) {
20753367019cSmrg
20763367019cSmrg	    if (x_getpwuid(screen->uid, &pw)) {
20773367019cSmrg		char *name = x_getlogin(screen->uid, &pw);
20783367019cSmrg		if (*(pw.pw_shell)) {
20793367019cSmrg		    shell = pw.pw_shell;
20803367019cSmrg		}
20813367019cSmrg		free(name);
20823367019cSmrg	    }
20833367019cSmrg	}
20843367019cSmrg
20853367019cSmrg	if (shell == 0) {
20863367019cSmrg	    static char dummy[] = "/bin/sh";
20873367019cSmrg	    shell = dummy;
20883367019cSmrg	}
20893367019cSmrg
20903367019cSmrg	if (access(shell, X_OK) != 0) {
20913367019cSmrg	    xtermPerror("Can't execute `%s'\n", shell);
20923367019cSmrg	    return;
20933367019cSmrg	}
2094d522f475Smrg
20953367019cSmrg	if (pipe(p) < 0) {
20963367019cSmrg	    xtermPerror("Can't make a pipe connection\n");
2097d522f475Smrg	    return;
20983367019cSmrg	} else if ((pid = fork()) < 0) {
20993367019cSmrg	    xtermPerror("Can't fork...\n");
21003367019cSmrg	    return;
21013367019cSmrg	}
2102d522f475Smrg	if (pid == 0) {		/* child */
2103d522f475Smrg	    /*
2104d522f475Smrg	     * Close our output (we won't be talking back to the
2105d522f475Smrg	     * parent), and redirect our child's output to the
2106d522f475Smrg	     * original stderr.
2107d522f475Smrg	     */
2108d522f475Smrg	    close(p[1]);
2109d522f475Smrg	    dup2(p[0], 0);
2110d522f475Smrg	    close(p[0]);
2111d522f475Smrg	    dup2(fileno(stderr), 1);
2112d522f475Smrg	    dup2(fileno(stderr), 2);
2113d522f475Smrg
2114d522f475Smrg	    close(fileno(stderr));
2115d522f475Smrg	    close(ConnectionNumber(screen->display));
2116d522f475Smrg	    close(screen->respond);
2117d522f475Smrg
2118d522f475Smrg	    signal(SIGHUP, SIG_DFL);
2119d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
2120d522f475Smrg
2121d522f475Smrg	    /* (this is redundant) */
2122d522f475Smrg	    if (xtermResetIds(screen) < 0)
2123d522f475Smrg		exit(ERROR_SETUID);
2124d522f475Smrg
21253367019cSmrg	    if (access(shell, X_OK) == 0) {
21263367019cSmrg		execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
21273367019cSmrg		xtermWarning("Can't exec `%s'\n", &screen->logfile[1]);
21283367019cSmrg	    } else {
21293367019cSmrg		xtermWarning("Can't execute `%s'\n", shell);
21303367019cSmrg	    }
2131d522f475Smrg	    exit(ERROR_LOGEXEC);
2132d522f475Smrg	}
2133d522f475Smrg	close(p[0]);
2134d522f475Smrg	screen->logfd = p[1];
2135d522f475Smrg	signal(SIGPIPE, logpipe);
2136d522f475Smrg#else
2137cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2138cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2139d522f475Smrg	return;
2140d522f475Smrg#endif
2141d522f475Smrg    } else {
2142d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2143d522f475Smrg					   screen->gid,
2144d522f475Smrg					   screen->logfile,
2145d522f475Smrg					   (log_default != 0))) < 0)
2146d522f475Smrg	    return;
2147d522f475Smrg    }
2148d522f475Smrg#endif /*VMS */
2149d522f475Smrg    screen->logstart = VTbuffer->next;
2150d522f475Smrg    screen->logging = True;
2151d522f475Smrg    update_logging();
2152d522f475Smrg}
2153d522f475Smrg
2154d522f475Smrgvoid
2155cd3331d0SmrgCloseLog(XtermWidget xw)
2156d522f475Smrg{
2157cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2158cd3331d0Smrg
2159d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2160d522f475Smrg	return;
2161cd3331d0Smrg    FlushLog(xw);
2162d522f475Smrg    close(screen->logfd);
2163d522f475Smrg    screen->logging = False;
2164d522f475Smrg    update_logging();
2165d522f475Smrg}
2166d522f475Smrg
2167d522f475Smrgvoid
2168cd3331d0SmrgFlushLog(XtermWidget xw)
2169d522f475Smrg{
2170cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2171cd3331d0Smrg
2172d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2173d522f475Smrg	Char *cp;
2174d522f475Smrg	int i;
2175d522f475Smrg
2176d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2177d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2178d522f475Smrg	if (!tt_new_output)
2179d522f475Smrg	    return;
2180d522f475Smrg	tt_new_output = False;
2181d522f475Smrg#endif /* VMS */
2182d522f475Smrg	cp = VTbuffer->next;
2183d522f475Smrg	if (screen->logstart != 0
2184cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2185cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2186d522f475Smrg	}
2187d522f475Smrg	screen->logstart = VTbuffer->next;
2188d522f475Smrg    }
2189d522f475Smrg}
2190d522f475Smrg
2191d522f475Smrg#endif /* ALLOWLOGGING */
2192d522f475Smrg
2193d522f475Smrg/***====================================================================***/
2194d522f475Smrg
2195fa3f02f3Smrgint
2196fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2197fa3f02f3Smrg{
2198fa3f02f3Smrg#define MYFMT "getVisualInfo \
2199fa3f02f3Smrgdepth %d, \
2200fa3f02f3Smrgtype %d (%s), \
2201fa3f02f3Smrgsize %d \
2202fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2203fa3f02f3Smrg#define MYARG \
2204fa3f02f3Smrg       vi->depth,\
2205fa3f02f3Smrg       vi->class,\
2206fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2207fa3f02f3Smrg       vi->colormap_size,\
2208fa3f02f3Smrg       vi->red_mask,\
2209fa3f02f3Smrg       vi->green_mask,\
2210fa3f02f3Smrg       vi->blue_mask
2211d522f475Smrg
2212fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2213fa3f02f3Smrg    Display *dpy = screen->display;
2214fa3f02f3Smrg    XVisualInfo myTemplate;
2215fa3f02f3Smrg
2216fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2217fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2218fa3f02f3Smrg								XDefaultScreen(dpy)));
2219fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2220fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2221fa3f02f3Smrg
2222fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2223fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2224fa3f02f3Smrg	    if (resource.reportColors) {
2225fa3f02f3Smrg		printf(MYFMT, MYARG);
2226fa3f02f3Smrg	    }
2227fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2228fa3f02f3Smrg	}
2229fa3f02f3Smrg    }
2230fa3f02f3Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0);
2231fa3f02f3Smrg#undef MYFMT
2232fa3f02f3Smrg#undef MYARG
2233fa3f02f3Smrg}
22343367019cSmrg
22359a64e1c5Smrg#if OPT_ISO_COLORS
22369a64e1c5Smrgstatic void
22379a64e1c5SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
22389a64e1c5Smrg{
22399a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
22409a64e1c5Smrg	XColor color;
22419a64e1c5Smrg	Colormap cmap = xw->core.colormap;
22429a64e1c5Smrg	char buffer[80];
22439a64e1c5Smrg
22449a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
22459a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
22469a64e1c5Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
22479a64e1c5Smrg	sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
22489a64e1c5Smrg		colornum,
22499a64e1c5Smrg		color.red,
22509a64e1c5Smrg		color.green,
22519a64e1c5Smrg		color.blue);
22529a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
22539a64e1c5Smrg	unparseputs(xw, buffer);
22549a64e1c5Smrg	unparseputc1(xw, final);
22559a64e1c5Smrg	unparse_end(xw);
22569a64e1c5Smrg    }
22579a64e1c5Smrg}
22589a64e1c5Smrg
2259fa3f02f3Smrgstatic void
2260fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2261fa3f02f3Smrg{
2262fa3f02f3Smrg    if (getVisualInfo(xw)) {
2263fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2264fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2265fa3f02f3Smrg    } else {
2266fa3f02f3Smrg	*typep = 0;
2267fa3f02f3Smrg	*sizep = 0;
2268fa3f02f3Smrg    }
22693367019cSmrg}
22703367019cSmrg
22713367019cSmrg#define MAX_COLORTABLE 4096
22723367019cSmrg
22733367019cSmrg/*
22743367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
22753367019cSmrg */
22763367019cSmrgstatic Boolean
22773367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
22783367019cSmrg{
22793367019cSmrg    Colormap cmap = xw->core.colormap;
22803367019cSmrg    TScreen *screen = TScreenOf(xw);
2281fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
22823367019cSmrg
2283fa3f02f3Smrg    if (!result
22843367019cSmrg	&& length != 0
22853367019cSmrg	&& length < MAX_COLORTABLE) {
22863367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2287037a25ddSmrg
22883367019cSmrg	if (screen->cmap_data != 0) {
2289037a25ddSmrg	    unsigned i;
2290037a25ddSmrg
22913367019cSmrg	    screen->cmap_size = length;
22923367019cSmrg
22933367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
22943367019cSmrg		screen->cmap_data[i].pixel = (unsigned long) i;
22953367019cSmrg	    }
22963367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
22973367019cSmrg					     cmap,
22983367019cSmrg					     screen->cmap_data,
22993367019cSmrg					     (int) screen->cmap_size) != 0);
23003367019cSmrg	}
23013367019cSmrg    }
2302d522f475Smrg    return result;
2303d522f475Smrg}
2304d522f475Smrg
2305d522f475Smrg/*
2306d522f475Smrg * Find closest color for "def" in "cmap".
2307d522f475Smrg * Set "def" to the resulting color.
2308d522f475Smrg *
2309d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2310d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2311d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2312d522f475Smrg *
2313d522f475Smrg * Return False if not able to find or allocate a color.
2314d522f475Smrg */
2315d522f475Smrgstatic Boolean
23169a64e1c5SmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def)
2317d522f475Smrg{
23183367019cSmrg    TScreen *screen = TScreenOf(xw);
2319d522f475Smrg    Boolean result = False;
23203367019cSmrg    unsigned cmap_type;
2321d522f475Smrg    unsigned cmap_size;
2322d522f475Smrg
2323fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2324d522f475Smrg
23253367019cSmrg    if ((cmap_type & 1) != 0) {
23263367019cSmrg
23273367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2328037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2329d522f475Smrg
2330d522f475Smrg	    if (tried != 0) {
2331037a25ddSmrg		unsigned attempts;
2332d522f475Smrg
2333d522f475Smrg		/*
2334d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2335d522f475Smrg		 * approximation to the requested color.
2336d522f475Smrg		 */
2337d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2338d522f475Smrg		    Boolean first = True;
2339037a25ddSmrg		    double bestRGB = 0.0;
2340037a25ddSmrg		    unsigned bestInx = 0;
2341037a25ddSmrg		    unsigned i;
2342d522f475Smrg
2343d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2344d522f475Smrg			if (!tried[bestInx]) {
2345037a25ddSmrg			    double diff, thisRGB = 0.0;
2346037a25ddSmrg
2347d522f475Smrg			    /*
2348d522f475Smrg			     * Look for the best match based on luminance.
2349d522f475Smrg			     * Measure this by the least-squares difference of
2350d522f475Smrg			     * the weighted R/G/B components from the color map
2351d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2352d522f475Smrg			     * component of the YIQ color space model for
2353d522f475Smrg			     * weights that correspond to the luminance.
2354d522f475Smrg			     */
2355d522f475Smrg#define AddColorWeight(weight, color) \
23563367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2357037a25ddSmrg			    thisRGB += diff * diff
2358d522f475Smrg
2359d522f475Smrg			    AddColorWeight(0.30, red);
2360d522f475Smrg			    AddColorWeight(0.61, green);
2361d522f475Smrg			    AddColorWeight(0.11, blue);
2362d522f475Smrg
2363d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2364d522f475Smrg				first = False;
2365d522f475Smrg				bestInx = i;
2366d522f475Smrg				bestRGB = thisRGB;
2367d522f475Smrg			    }
2368d522f475Smrg			}
2369d522f475Smrg		    }
23703367019cSmrg		    if (XAllocColor(screen->display, cmap,
23713367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
23723367019cSmrg			*def = screen->cmap_data[bestInx];
23733367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
23743367019cSmrg			       def->green, def->blue));
2375d522f475Smrg			result = True;
2376d522f475Smrg			break;
2377d522f475Smrg		    }
2378d522f475Smrg		    /*
2379d522f475Smrg		     * It failed - either the color map entry was readonly, or
2380d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2381d522f475Smrg		     * so we will ignore it
2382d522f475Smrg		     */
2383d522f475Smrg		    tried[bestInx] = True;
2384d522f475Smrg		}
2385d522f475Smrg		free(tried);
2386d522f475Smrg	    }
2387d522f475Smrg	}
2388d522f475Smrg    }
2389d522f475Smrg    return result;
2390d522f475Smrg}
2391d522f475Smrg
23923367019cSmrg#ifndef ULONG_MAX
23933367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
23943367019cSmrg#endif
23953367019cSmrg
23963367019cSmrg#define CheckColor(result, value) \
23973367019cSmrg	    result = 0; \
23983367019cSmrg	    if (value.red) \
23993367019cSmrg		result |= 1; \
24003367019cSmrg	    if (value.green) \
24013367019cSmrg		result |= 2; \
24023367019cSmrg	    if (value.blue) \
24033367019cSmrg		result |= 4
24043367019cSmrg
24053367019cSmrg#define SelectColor(state, value, result) \
24063367019cSmrg	switch (state) { \
24073367019cSmrg	default: \
24083367019cSmrg	case 1: \
24093367019cSmrg	    result = value.red; \
24103367019cSmrg	    break; \
24113367019cSmrg	case 2: \
24123367019cSmrg	    result = value.green; \
24133367019cSmrg	    break; \
24143367019cSmrg	case 4: \
24153367019cSmrg	    result = value.blue; \
24163367019cSmrg	    break; \
24173367019cSmrg	}
24183367019cSmrg
24193367019cSmrg/*
24203367019cSmrg * Check if the color map consists of values in exactly one of the red, green
24213367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
24223367019cSmrg * match.
24233367019cSmrg */
24243367019cSmrgstatic int
24259a64e1c5SmrgsimpleColors(XColor *colortable, unsigned length)
24263367019cSmrg{
24273367019cSmrg    unsigned n;
2428fa3f02f3Smrg    int state = 0;
24293367019cSmrg    int check;
24303367019cSmrg
24313367019cSmrg    for (n = 0; n < length; ++n) {
24323367019cSmrg	if (state > 0) {
24333367019cSmrg	    CheckColor(check, colortable[n]);
24343367019cSmrg	    if (check > 0 && check != state) {
24353367019cSmrg		state = 0;
24363367019cSmrg		break;
24373367019cSmrg	    }
2438fa3f02f3Smrg	} else {
2439fa3f02f3Smrg	    CheckColor(state, colortable[n]);
24403367019cSmrg	}
24413367019cSmrg    }
24423367019cSmrg    switch (state) {
24433367019cSmrg    case 1:
24443367019cSmrg    case 2:
24453367019cSmrg    case 4:
24463367019cSmrg	break;
24473367019cSmrg    default:
24483367019cSmrg	state = 0;
24493367019cSmrg	break;
24503367019cSmrg    }
24513367019cSmrg    return state;
24523367019cSmrg}
24533367019cSmrg
2454fa3f02f3Smrg/*
2455fa3f02f3Smrg * Shift the mask left or right to put its most significant bit at the 16-bit
2456fa3f02f3Smrg * mark.
2457fa3f02f3Smrg */
2458fa3f02f3Smrgstatic unsigned
2459fa3f02f3SmrgnormalizeMask(unsigned mask)
2460fa3f02f3Smrg{
2461fa3f02f3Smrg    while (mask < 0x8000) {
2462fa3f02f3Smrg	mask <<= 1;
2463fa3f02f3Smrg    }
2464fa3f02f3Smrg    while (mask >= 0x10000) {
2465fa3f02f3Smrg	mask >>= 1;
2466fa3f02f3Smrg    }
2467fa3f02f3Smrg    return mask;
2468fa3f02f3Smrg}
2469fa3f02f3Smrg
24703367019cSmrgstatic unsigned
24719a64e1c5SmrgsearchColors(XColor *colortable, unsigned mask, unsigned length, unsigned
2472fa3f02f3Smrg	     color, int state)
24733367019cSmrg{
24743367019cSmrg    unsigned result = 0;
24753367019cSmrg    unsigned n;
24763367019cSmrg    unsigned long best = ULONG_MAX;
24773367019cSmrg    unsigned value;
24783367019cSmrg
2479fa3f02f3Smrg    mask = normalizeMask(mask);
24803367019cSmrg    for (n = 0; n < length; ++n) {
2481037a25ddSmrg	unsigned long diff;
2482037a25ddSmrg
24833367019cSmrg	SelectColor(state, colortable[n], value);
2484fa3f02f3Smrg	diff = ((color & mask) - (value & mask));
24853367019cSmrg	diff *= diff;
24863367019cSmrg	if (diff < best) {
24873367019cSmrg#if 0
24883367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
24893367019cSmrg		   n, color,
24903367019cSmrg		   colortable[n].red,
24913367019cSmrg		   colortable[n].green,
24923367019cSmrg		   colortable[n].blue,
24933367019cSmrg		   diff));
24943367019cSmrg#endif
24953367019cSmrg	    result = n;
24963367019cSmrg	    best = diff;
24973367019cSmrg	}
24983367019cSmrg    }
24993367019cSmrg    SelectColor(state, colortable[result], value);
25003367019cSmrg    return value;
25013367019cSmrg}
25023367019cSmrg
25033367019cSmrg/*
25043367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
25053367019cSmrg *
25063367019cSmrg * According to
25073367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
25083367019cSmrg *
25093367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
25103367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
25113367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
25123367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
25133367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
25143367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
25153367019cSmrg *     actual RGB values allocated.
25163367019cSmrg *
25173367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
2518fa3f02f3Smrg * case, allocateClosestRGB() is useful for the dynamic display classes such as
25193367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
25203367019cSmrg * return regular RGB triples (unless a different scheme was used for
25213367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
25223367019cSmrg * is filled in with the colors that the server supports.
25233367019cSmrg *
25243367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
25253367019cSmrg * described.  For some TrueColor configurations it merely returns a close
25263367019cSmrg * approximation, but not the closest.
25273367019cSmrg */
25283367019cSmrgstatic Boolean
25299a64e1c5SmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def)
25303367019cSmrg{
25313367019cSmrg    XColor save = *def;
25323367019cSmrg    TScreen *screen = TScreenOf(xw);
25333367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
25343367019cSmrg
25353367019cSmrg    /*
2536fa3f02f3Smrg     * If this is a statically allocated display with too many items to store
2537fa3f02f3Smrg     * in our array, i.e., TrueColor, see if we can improve on the result by
2538fa3f02f3Smrg     * using the color values actually supported by the server.
25393367019cSmrg     */
25403367019cSmrg    if (result) {
25413367019cSmrg	unsigned cmap_type;
25423367019cSmrg	unsigned cmap_size;
25433367019cSmrg
2544fa3f02f3Smrg	getColormapInfo(xw, &cmap_type, &cmap_size);
25453367019cSmrg
2546fa3f02f3Smrg	if (cmap_type == TrueColor) {
25473367019cSmrg	    XColor temp = *def;
2548037a25ddSmrg	    int state;
25493367019cSmrg
25503367019cSmrg	    if (loadColorTable(xw, cmap_size)
25513367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
2552fa3f02f3Smrg#define SearchColors(which) \
2553fa3f02f3Smrg	temp.which = (unsigned short) searchColors(screen->cmap_data, \
2554fa3f02f3Smrg						   (unsigned) xw->visInfo->which##_mask,\
2555fa3f02f3Smrg						   cmap_size, \
2556fa3f02f3Smrg						   save.which, \
2557fa3f02f3Smrg						   state)
25583367019cSmrg		SearchColors(red);
25593367019cSmrg		SearchColors(green);
25603367019cSmrg		SearchColors(blue);
25613367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
25623367019cSmrg#if OPT_TRACE
25633367019cSmrg		    if (temp.red != save.red
25643367019cSmrg			|| temp.green != save.green
25653367019cSmrg			|| temp.blue != save.blue) {
25663367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
25673367019cSmrg			       save.red, save.green, save.blue,
25683367019cSmrg			       temp.red, temp.green, temp.blue));
25693367019cSmrg		    } else {
25703367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
25713367019cSmrg			       save.red, save.green, save.blue));
25723367019cSmrg		    }
25733367019cSmrg#endif
25743367019cSmrg		    *def = temp;
25753367019cSmrg		}
25763367019cSmrg	    }
25773367019cSmrg	}
25783367019cSmrg    }
25793367019cSmrg
25803367019cSmrg    return result;
25813367019cSmrg}
25823367019cSmrg
2583d522f475Smrg/*
2584d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2585d522f475Smrg * to 256.
2586d522f475Smrg *
2587d522f475Smrg * Returns
2588d522f475Smrg *	-1 on error
2589d522f475Smrg *	0 on no change
2590d522f475Smrg *	1 if a new color was allocated.
2591d522f475Smrg */
2592d522f475Smrgstatic int
2593d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2594d522f475Smrg		  ColorRes * res,
2595cd3331d0Smrg		  const char *spec)
2596d522f475Smrg{
2597d522f475Smrg    int result;
2598d522f475Smrg    XColor def;
2599d522f475Smrg
26003367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2601d522f475Smrg	if (
2602d522f475Smrg#if OPT_COLOR_RES
2603d522f475Smrg	       res->mode == True &&
2604d522f475Smrg#endif
2605d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
2606d522f475Smrg	    result = 0;
2607d522f475Smrg	} else {
2608d522f475Smrg	    result = 1;
2609d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
26103367019cSmrg	    res->red = def.red;
26113367019cSmrg	    res->green = def.green;
26123367019cSmrg	    res->blue = def.blue;
26133367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
26143367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
26153367019cSmrg		   def.red,
26163367019cSmrg		   def.green,
26173367019cSmrg		   def.blue,
26183367019cSmrg		   def.pixel));
2619d522f475Smrg#if OPT_COLOR_RES
2620d522f475Smrg	    if (!res->mode)
2621d522f475Smrg		result = 0;
2622d522f475Smrg	    res->mode = True;
2623d522f475Smrg#endif
2624d522f475Smrg	}
2625d522f475Smrg    } else {
2626d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2627d522f475Smrg	result = -1;
2628d522f475Smrg    }
2629d522f475Smrg    return (result);
2630d522f475Smrg}
2631d522f475Smrg
2632d522f475Smrg#if OPT_COLOR_RES
2633d522f475SmrgPixel
2634cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2635d522f475Smrg{
2636d522f475Smrg    Pixel result = 0;
2637d522f475Smrg
2638d522f475Smrg    if (res->mode) {
2639d522f475Smrg	result = res->value;
2640d522f475Smrg    } else {
2641d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2642cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2643d522f475Smrg
2644cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2645cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2646d522f475Smrg
2647cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2648cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2649d522f475Smrg		res->mode = -True;
26503367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
26513367019cSmrg			     NonNull(res->resource));
2652d522f475Smrg	    }
2653d522f475Smrg	    result = res->value;
2654d522f475Smrg	} else {
2655d522f475Smrg	    result = 0;
2656d522f475Smrg	}
2657d522f475Smrg    }
2658d522f475Smrg    return result;
2659d522f475Smrg}
2660d522f475Smrg#endif
2661d522f475Smrg
2662cd3331d0Smrgstatic int
2663cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2664cd3331d0Smrg{
2665cd3331d0Smrg    int code;
2666cd3331d0Smrg
2667cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2668cd3331d0Smrg	code = -1;
2669cd3331d0Smrg    } else {
2670cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2671cd3331d0Smrg
2672cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2673cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2674cd3331d0Smrg    }
2675cd3331d0Smrg    return code;
2676cd3331d0Smrg}
2677cd3331d0Smrg
2678cd3331d0Smrg/*
2679cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2680cd3331d0Smrg * values from the given buffer.
2681cd3331d0Smrg *
2682cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2683cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2684cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2685cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2686cd3331d0Smrg * 'first' set to the beginning of those indices.
2687cd3331d0Smrg *
2688cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2689cd3331d0Smrg */
2690d522f475Smrgstatic Bool
2691d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2692d522f475Smrg		       char *buf,
2693cd3331d0Smrg		       int first,
2694d522f475Smrg		       int final)
2695d522f475Smrg{
2696d522f475Smrg    int repaint = False;
2697d522f475Smrg    int code;
2698cd3331d0Smrg    int last = (MAXCOLORS - first);
2699d522f475Smrg
2700d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2701d522f475Smrg
2702d522f475Smrg    while (buf && *buf) {
2703037a25ddSmrg	int color;
2704037a25ddSmrg	char *name = strchr(buf, ';');
2705037a25ddSmrg
2706d522f475Smrg	if (name == NULL)
2707d522f475Smrg	    break;
2708d522f475Smrg	*name = '\0';
2709d522f475Smrg	name++;
2710d522f475Smrg	color = atoi(buf);
2711cd3331d0Smrg	if (color < 0 || color >= last)
2712cd3331d0Smrg	    break;		/* quit on any error */
2713d522f475Smrg	buf = strchr(name, ';');
2714d522f475Smrg	if (buf) {
2715d522f475Smrg	    *buf = '\0';
2716d522f475Smrg	    buf++;
2717d522f475Smrg	}
2718cd3331d0Smrg	if (!strcmp(name, "?")) {
2719cd3331d0Smrg	    ReportAnsiColorRequest(xw, color + first, final);
2720cd3331d0Smrg	} else {
2721cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
2722d522f475Smrg	    if (code < 0) {
2723d522f475Smrg		/* stop on any error */
2724d522f475Smrg		break;
2725d522f475Smrg	    } else if (code > 0) {
2726d522f475Smrg		repaint = True;
2727d522f475Smrg	    }
2728d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
2729d522f475Smrg	     * change style (dynamic colors).
2730d522f475Smrg	     */
2731d522f475Smrg	}
2732d522f475Smrg    }
2733d522f475Smrg
2734d522f475Smrg    return (repaint);
2735d522f475Smrg}
2736cd3331d0Smrg
2737cd3331d0Smrgstatic Bool
2738cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
2739cd3331d0Smrg{
2740cd3331d0Smrg    Bool repaint = False;
2741cd3331d0Smrg    int last = MAXCOLORS - start;
2742cd3331d0Smrg
2743cd3331d0Smrg    if (color >= 0 && color < last) {
2744cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
2745cd3331d0Smrg
2746cd3331d0Smrg	if (res->mode) {
2747cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
2748cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
2749cd3331d0Smrg		repaint = True;
2750cd3331d0Smrg	    }
2751cd3331d0Smrg	}
2752cd3331d0Smrg    }
2753cd3331d0Smrg    return repaint;
2754cd3331d0Smrg}
2755cd3331d0Smrg
2756cd3331d0Smrgint
2757cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
2758cd3331d0Smrg{
2759cd3331d0Smrg    int repaint = 0;
2760cd3331d0Smrg    int color;
2761cd3331d0Smrg
2762cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
2763cd3331d0Smrg    if (*buf != '\0') {
2764cd3331d0Smrg	/* reset specific colors */
2765cd3331d0Smrg	while (!IsEmpty(buf)) {
2766cd3331d0Smrg	    char *next;
2767cd3331d0Smrg
2768037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
2769037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
2770cd3331d0Smrg		break;		/* no number at all */
2771cd3331d0Smrg	    if (next != 0) {
2772cd3331d0Smrg		if (strchr(";", *next) == 0)
2773cd3331d0Smrg		    break;	/* unexpected delimiter */
2774cd3331d0Smrg		++next;
2775cd3331d0Smrg	    }
2776cd3331d0Smrg
2777cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2778cd3331d0Smrg		++repaint;
2779cd3331d0Smrg	    }
2780cd3331d0Smrg	    buf = next;
2781cd3331d0Smrg	}
2782cd3331d0Smrg    } else {
2783cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
2784cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
2785cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2786cd3331d0Smrg		++repaint;
2787cd3331d0Smrg	    }
2788cd3331d0Smrg	}
2789cd3331d0Smrg    }
2790cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
2791cd3331d0Smrg    return repaint;
2792cd3331d0Smrg}
2793d522f475Smrg#else
27943367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
27953367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
2796d522f475Smrg#endif /* OPT_ISO_COLORS */
2797d522f475Smrg
2798fa3f02f3SmrgBoolean
27999a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
2800fa3f02f3Smrg{
2801fa3f02f3Smrg    Colormap cmap = xw->core.colormap;
2802fa3f02f3Smrg
2803fa3f02f3Smrg    return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
2804fa3f02f3Smrg}
2805fa3f02f3Smrg
28063367019cSmrgstatic Boolean
28079a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
28083367019cSmrg{
28093367019cSmrg    Boolean result = False;
28103367019cSmrg    TScreen *screen = TScreenOf(xw);
28113367019cSmrg    Colormap cmap = xw->core.colormap;
28123367019cSmrg
2813fa3f02f3Smrg    if (XParseColor(screen->display, cmap, spec, def)) {
2814fa3f02f3Smrg	XColor save_def = *def;
2815fa3f02f3Smrg	if (resource.reportColors) {
2816fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
2817fa3f02f3Smrg		   def->red, def->green, def->blue,
2818fa3f02f3Smrg		   spec);
2819fa3f02f3Smrg	}
2820fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
2821fa3f02f3Smrg	    if (resource.reportColors) {
2822fa3f02f3Smrg		if (def->red != save_def.red ||
2823fa3f02f3Smrg		    def->green != save_def.green ||
2824fa3f02f3Smrg		    def->blue != save_def.blue) {
2825fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
2826fa3f02f3Smrg			   def->red, def->green, def->blue,
2827fa3f02f3Smrg			   spec);
2828fa3f02f3Smrg		}
2829fa3f02f3Smrg	    }
2830fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
2831fa3f02f3Smrg		   def->red, def->green, def->blue));
2832fa3f02f3Smrg	    result = True;
2833fa3f02f3Smrg	}
28343367019cSmrg    }
28353367019cSmrg    return result;
28363367019cSmrg}
28373367019cSmrg
28383367019cSmrg/*
28393367019cSmrg * This provides an approximation (the closest color from xterm's palette)
28403367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
28413367019cSmrg * because of the context in which it is used.
28423367019cSmrg */
28433367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
28443367019cSmrgint
28453367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
28463367019cSmrg{
28473367019cSmrg    int result = -1;
28483367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
28493367019cSmrg    int n;
28503367019cSmrg    int best_index = -1;
28513367019cSmrg    unsigned long best_value = 0;
28523367019cSmrg    unsigned long this_value;
28533367019cSmrg    long diff_red, diff_green, diff_blue;
28543367019cSmrg
28553367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
28563367019cSmrg
28573367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
28583367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
28593367019cSmrg
28603367019cSmrg	/* ensure that we have a value for each of the colors */
28613367019cSmrg	if (!res->mode) {
28623367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
28633367019cSmrg	}
28643367019cSmrg
28653367019cSmrg	/* find the closest match */
28663367019cSmrg	if (res->mode == True) {
28673367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
28683367019cSmrg		    res->value, res->red, res->green, res->blue));
28693367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
28703367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
28713367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
28723367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
28733367019cSmrg					  + (diff_green * diff_green)
28743367019cSmrg					  + (diff_blue * diff_blue));
28753367019cSmrg	    if (best_index < 0 || this_value < best_value) {
28763367019cSmrg		best_index = n;
28773367019cSmrg		best_value = this_value;
28783367019cSmrg	    }
28793367019cSmrg	}
28803367019cSmrg    }
28813367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
28823367019cSmrg    result = best_index;
28833367019cSmrg#else
28843367019cSmrg    (void) xw;
28853367019cSmrg    (void) find_red;
28863367019cSmrg    (void) find_green;
28873367019cSmrg    (void) find_blue;
28883367019cSmrg#endif
28893367019cSmrg    return result;
28903367019cSmrg}
28913367019cSmrg
2892d522f475Smrg#if OPT_PASTE64
2893d522f475Smrgstatic void
2894fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
2895d522f475Smrg{
2896d522f475Smrg#define PDATA(a,b) { a, #b }
2897d522f475Smrg    static struct {
2898d522f475Smrg	char given;
2899cd3331d0Smrg	String result;
2900d522f475Smrg    } table[] = {
2901d522f475Smrg	PDATA('s', SELECT),
2902d522f475Smrg	    PDATA('p', PRIMARY),
2903d522f475Smrg	    PDATA('c', CLIPBOARD),
2904d522f475Smrg	    PDATA('0', CUT_BUFFER0),
2905d522f475Smrg	    PDATA('1', CUT_BUFFER1),
2906d522f475Smrg	    PDATA('2', CUT_BUFFER2),
2907d522f475Smrg	    PDATA('3', CUT_BUFFER3),
2908d522f475Smrg	    PDATA('4', CUT_BUFFER4),
2909d522f475Smrg	    PDATA('5', CUT_BUFFER5),
2910d522f475Smrg	    PDATA('6', CUT_BUFFER6),
2911d522f475Smrg	    PDATA('7', CUT_BUFFER7),
2912d522f475Smrg    };
2913d522f475Smrg
2914cd3331d0Smrg    const char *base = buf;
2915d522f475Smrg    Cardinal j, n = 0;
2916d522f475Smrg
2917d522f475Smrg    TRACE(("Manipulate selection data\n"));
2918d522f475Smrg
2919d522f475Smrg    while (*buf != ';' && *buf != '\0') {
2920d522f475Smrg	++buf;
2921d522f475Smrg    }
2922d522f475Smrg
2923d522f475Smrg    if (*buf == ';') {
2924037a25ddSmrg	char *used;
2925037a25ddSmrg
2926d522f475Smrg	*buf++ = '\0';
2927d522f475Smrg
2928d522f475Smrg	if (*base == '\0')
2929d522f475Smrg	    base = "s0";
2930d522f475Smrg
29313367019cSmrg	if ((used = x_strdup(base)) != 0) {
2932037a25ddSmrg	    String *select_args;
2933037a25ddSmrg
29343367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
29353367019cSmrg		while (*base != '\0') {
29363367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
29373367019cSmrg			if (*base == table[j].given) {
29383367019cSmrg			    used[n] = *base;
29393367019cSmrg			    select_args[n++] = table[j].result;
29403367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
29413367019cSmrg			    break;
29423367019cSmrg			}
29433367019cSmrg		    }
29443367019cSmrg		    ++base;
29453367019cSmrg		}
29463367019cSmrg		used[n] = 0;
29473367019cSmrg
29483367019cSmrg		if (!strcmp(buf, "?")) {
29493367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
29503367019cSmrg			TRACE(("Getting selection\n"));
29513367019cSmrg			unparseputc1(xw, ANSI_OSC);
29523367019cSmrg			unparseputs(xw, "52");
29533367019cSmrg			unparseputc(xw, ';');
29543367019cSmrg
29553367019cSmrg			unparseputs(xw, used);
29563367019cSmrg			unparseputc(xw, ';');
29573367019cSmrg
29583367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
29593367019cSmrg			screen->base64_paste = n;
29603367019cSmrg			screen->base64_final = final;
29613367019cSmrg
29623367019cSmrg			/* terminator will be written in this call */
29633367019cSmrg			xtermGetSelection((Widget) xw,
2964fa3f02f3Smrg					  XtLastTimestampProcessed(TScreenOf(xw)->display),
29653367019cSmrg					  select_args, n,
29663367019cSmrg					  NULL);
296794644356Smrg			/*
296894644356Smrg			 * select_args is used via SelectionReceived, cannot
296994644356Smrg			 * free it here.
297094644356Smrg			 */
297194644356Smrg		    } else {
297294644356Smrg			free(select_args);
29733367019cSmrg		    }
29743367019cSmrg		} else {
29753367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
29763367019cSmrg			TRACE(("Setting selection with %s\n", buf));
29773367019cSmrg			ClearSelectionBuffer(screen);
29783367019cSmrg			while (*buf != '\0')
29793367019cSmrg			    AppendToSelectionBuffer(screen, CharOf(*buf++));
29803367019cSmrg			CompleteSelection(xw, select_args, n);
29813367019cSmrg		    }
298294644356Smrg		    free(select_args);
29833367019cSmrg		}
2984cd3331d0Smrg	    }
29853367019cSmrg	    free(used);
2986d522f475Smrg	}
2987d522f475Smrg    }
2988d522f475Smrg}
2989d522f475Smrg#endif /* OPT_PASTE64 */
2990d522f475Smrg
2991d522f475Smrg/***====================================================================***/
2992d522f475Smrg
2993cd3331d0Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
2994cd3331d0Smrg
2995d522f475Smrgstatic Bool
2996fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
2997d522f475Smrg{
2998cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2999d522f475Smrg    Bool result = False;
3000d522f475Smrg    Char *cp = *bufp;
3001d522f475Smrg    Char *next = cp;
3002d522f475Smrg
3003d522f475Smrg    (void) screen;
3004d522f475Smrg    (void) last;
3005d522f475Smrg
3006d522f475Smrg#if OPT_WIDE_CHARS
3007cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3008d522f475Smrg	PtyData data;
3009d522f475Smrg
30109a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3011d522f475Smrg	    if (data.utf_data != UCS_REPL
3012d522f475Smrg		&& (data.utf_data >= 128 ||
3013d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3014d522f475Smrg		next += (data.utf_size - 1);
3015d522f475Smrg		result = True;
3016d522f475Smrg	    } else {
3017d522f475Smrg		result = False;
3018d522f475Smrg	    }
3019d522f475Smrg	} else {
3020d522f475Smrg	    result = False;
3021d522f475Smrg	}
3022d522f475Smrg    } else
3023d522f475Smrg#endif
3024d522f475Smrg#if OPT_C1_PRINT
3025d522f475Smrg	if (screen->c1_printable
3026d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3027d522f475Smrg	result = True;
3028d522f475Smrg    } else
3029d522f475Smrg#endif
3030d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3031d522f475Smrg	result = True;
3032d522f475Smrg    }
3033d522f475Smrg    *bufp = next;
3034d522f475Smrg    return result;
3035d522f475Smrg}
3036d522f475Smrg
3037d522f475Smrg/***====================================================================***/
3038d522f475Smrg
3039d522f475Smrg/*
3040d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3041cd3331d0Smrg * array indices.  Compare with TermColors.
3042d522f475Smrg */
3043d522f475Smrgtypedef enum {
3044d522f475Smrg    OSC_TEXT_FG = 10
3045d522f475Smrg    ,OSC_TEXT_BG
3046d522f475Smrg    ,OSC_TEXT_CURSOR
3047d522f475Smrg    ,OSC_MOUSE_FG
3048d522f475Smrg    ,OSC_MOUSE_BG
3049d522f475Smrg#if OPT_TEK4014
3050d522f475Smrg    ,OSC_TEK_FG = 15
3051d522f475Smrg    ,OSC_TEK_BG
3052d522f475Smrg#endif
3053d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3054d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3055d522f475Smrg#endif
3056d522f475Smrg#if OPT_TEK4014
3057d522f475Smrg    ,OSC_TEK_CURSOR = 18
3058d522f475Smrg#endif
3059d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3060d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3061d522f475Smrg#endif
3062d522f475Smrg    ,OSC_NCOLORS
3063d522f475Smrg} OscTextColors;
3064d522f475Smrg
3065cd3331d0Smrg/*
3066cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3067cd3331d0Smrg */
3068cd3331d0Smrg#define OSC_RESET 100
3069cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3070cd3331d0Smrg
3071d522f475Smrgstatic Bool
3072d522f475SmrgGetOldColors(XtermWidget xw)
3073d522f475Smrg{
30749a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3075037a25ddSmrg	int i;
3076037a25ddSmrg
30779a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
30789a64e1c5Smrg	if (xw->work.oldColors == NULL) {
30793367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3080d522f475Smrg	    return (False);
3081d522f475Smrg	}
30829a64e1c5Smrg	xw->work.oldColors->which = 0;
3083d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
30849a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
30859a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3086d522f475Smrg	}
30879a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3088d522f475Smrg    }
3089d522f475Smrg    return (True);
3090d522f475Smrg}
3091d522f475Smrg
3092d522f475Smrgstatic int
3093d522f475SmrgoppositeColor(int n)
3094d522f475Smrg{
3095d522f475Smrg    switch (n) {
3096d522f475Smrg    case TEXT_FG:
3097d522f475Smrg	n = TEXT_BG;
3098d522f475Smrg	break;
3099d522f475Smrg    case TEXT_BG:
3100d522f475Smrg	n = TEXT_FG;
3101d522f475Smrg	break;
3102d522f475Smrg    case MOUSE_FG:
3103d522f475Smrg	n = MOUSE_BG;
3104d522f475Smrg	break;
3105d522f475Smrg    case MOUSE_BG:
3106d522f475Smrg	n = MOUSE_FG;
3107d522f475Smrg	break;
3108d522f475Smrg#if OPT_TEK4014
3109d522f475Smrg    case TEK_FG:
3110d522f475Smrg	n = TEK_BG;
3111d522f475Smrg	break;
3112d522f475Smrg    case TEK_BG:
3113d522f475Smrg	n = TEK_FG;
3114d522f475Smrg	break;
3115d522f475Smrg#endif
3116d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3117d522f475Smrg    case HIGHLIGHT_FG:
3118d522f475Smrg	n = HIGHLIGHT_BG;
3119d522f475Smrg	break;
3120d522f475Smrg    case HIGHLIGHT_BG:
3121d522f475Smrg	n = HIGHLIGHT_FG;
3122d522f475Smrg	break;
3123d522f475Smrg#endif
3124d522f475Smrg    default:
3125d522f475Smrg	break;
3126d522f475Smrg    }
3127d522f475Smrg    return n;
3128d522f475Smrg}
3129d522f475Smrg
3130d522f475Smrgstatic void
3131d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3132d522f475Smrg{
3133cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3134cd3331d0Smrg	XColor color;
3135cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3136cd3331d0Smrg	char buffer[80];
3137d522f475Smrg
3138cd3331d0Smrg	/*
3139cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3140cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3141cd3331d0Smrg	 * reporting the opposite color which would be used.
3142cd3331d0Smrg	 */
3143cd3331d0Smrg	int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
3144cd3331d0Smrg
3145cd3331d0Smrg	GetOldColors(xw);
31469a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3147cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
3148cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3149cd3331d0Smrg		color.red,
3150cd3331d0Smrg		color.green,
3151cd3331d0Smrg		color.blue);
3152712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
31539a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3154cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3155cd3331d0Smrg	unparseputs(xw, buffer);
3156cd3331d0Smrg	unparseputc1(xw, final);
3157cd3331d0Smrg	unparse_end(xw);
3158cd3331d0Smrg    }
3159d522f475Smrg}
3160d522f475Smrg
3161d522f475Smrgstatic Bool
3162d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
3163d522f475Smrg{
3164d522f475Smrg    int i;
3165d522f475Smrg
3166d522f475Smrg    /* if we were going to free old colors, this would be the place to
3167d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3168d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3169d522f475Smrg     * we could save some overhead this way.   The only case in which this
3170d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3171d522f475Smrg     * which case they can restart xterm
3172d522f475Smrg     */
3173d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3174d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
31759a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
31769a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
31779a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3178d522f475Smrg	    }
3179d522f475Smrg	    if (pNew->names[i]) {
31809a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3181d522f475Smrg	    }
31829a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3183d522f475Smrg	}
3184d522f475Smrg    }
3185d522f475Smrg    return (True);
3186d522f475Smrg}
3187d522f475Smrg
3188d522f475Smrg/*
3189d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3190d522f475Smrg * xterm is compiled.
3191d522f475Smrg */
3192d522f475Smrgstatic int
3193d522f475SmrgOscToColorIndex(OscTextColors mode)
3194d522f475Smrg{
3195d522f475Smrg    int result = 0;
3196d522f475Smrg
3197d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3198d522f475Smrg    switch (mode) {
3199d522f475Smrg	CASE(TEXT_FG);
3200d522f475Smrg	CASE(TEXT_BG);
3201d522f475Smrg	CASE(TEXT_CURSOR);
3202d522f475Smrg	CASE(MOUSE_FG);
3203d522f475Smrg	CASE(MOUSE_BG);
3204d522f475Smrg#if OPT_TEK4014
3205d522f475Smrg	CASE(TEK_FG);
3206d522f475Smrg	CASE(TEK_BG);
3207d522f475Smrg#endif
3208d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3209d522f475Smrg	CASE(HIGHLIGHT_BG);
3210d522f475Smrg	CASE(HIGHLIGHT_FG);
3211d522f475Smrg#endif
3212d522f475Smrg#if OPT_TEK4014
3213d522f475Smrg	CASE(TEK_CURSOR);
3214d522f475Smrg#endif
3215d522f475Smrg    case OSC_NCOLORS:
3216d522f475Smrg	break;
3217d522f475Smrg    }
3218d522f475Smrg    return result;
3219d522f475Smrg}
3220d522f475Smrg
3221d522f475Smrgstatic Bool
3222d522f475SmrgChangeColorsRequest(XtermWidget xw,
3223d522f475Smrg		    int start,
3224d522f475Smrg		    char *names,
3225d522f475Smrg		    int final)
3226d522f475Smrg{
3227d522f475Smrg    Bool result = False;
3228d522f475Smrg    ScrnColors newColors;
3229d522f475Smrg
3230d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3231d522f475Smrg
3232d522f475Smrg    if (GetOldColors(xw)) {
3233037a25ddSmrg	int i;
3234037a25ddSmrg
3235d522f475Smrg	newColors.which = 0;
3236d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3237d522f475Smrg	    newColors.names[i] = NULL;
3238d522f475Smrg	}
3239d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3240037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3241d522f475Smrg	    if (xw->misc.re_verse)
3242d522f475Smrg		ndx = oppositeColor(ndx);
3243d522f475Smrg
3244cd3331d0Smrg	    if (IsEmpty(names)) {
3245d522f475Smrg		newColors.names[ndx] = NULL;
3246d522f475Smrg	    } else {
3247037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3248037a25ddSmrg
3249d522f475Smrg		names = strchr(names, ';');
3250d522f475Smrg		if (names != NULL) {
3251d522f475Smrg		    *names++ = '\0';
3252d522f475Smrg		}
3253fa3f02f3Smrg		if (thisName != 0) {
3254fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3255fa3f02f3Smrg			ReportColorRequest(xw, ndx, final);
32569a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
32579a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3258fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3259fa3f02f3Smrg		    }
3260d522f475Smrg		}
3261d522f475Smrg	    }
3262d522f475Smrg	}
3263d522f475Smrg
3264d522f475Smrg	if (newColors.which != 0) {
3265d522f475Smrg	    ChangeColors(xw, &newColors);
3266d522f475Smrg	    UpdateOldColors(xw, &newColors);
3267d522f475Smrg	}
3268d522f475Smrg	result = True;
3269d522f475Smrg    }
3270d522f475Smrg    return result;
3271d522f475Smrg}
3272d522f475Smrg
3273cd3331d0Smrgstatic Bool
3274cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3275cd3331d0Smrg		   int code)
3276cd3331d0Smrg{
3277cd3331d0Smrg    Bool result = False;
3278cd3331d0Smrg
3279cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3280cd3331d0Smrg
3281cd3331d0Smrg#if OPT_COLOR_RES
3282cd3331d0Smrg    if (GetOldColors(xw)) {
3283037a25ddSmrg	ScrnColors newColors;
3284037a25ddSmrg	const char *thisName;
3285037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3286037a25ddSmrg
3287cd3331d0Smrg	if (xw->misc.re_verse)
3288cd3331d0Smrg	    ndx = oppositeColor(ndx);
3289cd3331d0Smrg
3290cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3291cd3331d0Smrg
3292cd3331d0Smrg	newColors.which = 0;
3293cd3331d0Smrg	newColors.names[ndx] = NULL;
3294cd3331d0Smrg
3295cd3331d0Smrg	if (thisName != 0
32969a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
32979a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3298cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3299cd3331d0Smrg
3300cd3331d0Smrg	    if (newColors.which != 0) {
3301cd3331d0Smrg		ChangeColors(xw, &newColors);
3302cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3303cd3331d0Smrg	    }
3304cd3331d0Smrg	}
3305cd3331d0Smrg	result = True;
3306cd3331d0Smrg    }
3307cd3331d0Smrg#endif
3308cd3331d0Smrg    return result;
3309cd3331d0Smrg}
3310cd3331d0Smrg
3311cd3331d0Smrg#if OPT_SHIFT_FONTS
3312cd3331d0Smrg/*
3313cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3314cd3331d0Smrg *
3315cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3316cd3331d0Smrg * the corresponding menu font entry.
3317cd3331d0Smrg */
3318cd3331d0Smrgstatic int
3319fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3320cd3331d0Smrg{
3321cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3322cd3331d0Smrg    int num = screen->menu_font_number;
3323cd3331d0Smrg    int rel = 0;
3324cd3331d0Smrg
3325cd3331d0Smrg    if (*++source == '+') {
3326cd3331d0Smrg	rel = 1;
3327cd3331d0Smrg	source++;
3328cd3331d0Smrg    } else if (*source == '-') {
3329cd3331d0Smrg	rel = -1;
3330cd3331d0Smrg	source++;
3331cd3331d0Smrg    }
3332cd3331d0Smrg
3333cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3334cd3331d0Smrg	int val = atoi(source);
3335cd3331d0Smrg	if (rel > 0)
3336cd3331d0Smrg	    rel = val;
3337cd3331d0Smrg	else if (rel < 0)
3338cd3331d0Smrg	    rel = -val;
3339cd3331d0Smrg	else
3340cd3331d0Smrg	    num = val;
3341cd3331d0Smrg    }
3342cd3331d0Smrg
3343cd3331d0Smrg    if (rel != 0) {
3344cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3345cd3331d0Smrg				     screen->menu_font_number, rel);
3346cd3331d0Smrg
3347cd3331d0Smrg    }
3348cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3349cd3331d0Smrg    *target = source;
3350cd3331d0Smrg    return num;
3351cd3331d0Smrg}
3352cd3331d0Smrg
3353cd3331d0Smrgstatic void
3354cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3355cd3331d0Smrg{
3356cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3357cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3358cd3331d0Smrg	Bool success = True;
3359cd3331d0Smrg	int num;
3360cb4a1343Smrg	String base = buf + 1;
3361cd3331d0Smrg	const char *name = 0;
3362cd3331d0Smrg
3363cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3364cd3331d0Smrg	if (num < 0
3365cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3366cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3367cd3331d0Smrg	    success = False;
3368cd3331d0Smrg	} else {
3369cd3331d0Smrg#if OPT_RENDERFONT
3370cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3371cd3331d0Smrg		name = getFaceName(xw, False);
3372cd3331d0Smrg	    } else
3373cd3331d0Smrg#endif
3374cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3375cd3331d0Smrg		success = False;
3376cd3331d0Smrg	    }
3377cd3331d0Smrg	}
3378cd3331d0Smrg
3379cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3380cd3331d0Smrg	unparseputs(xw, "50");
3381cd3331d0Smrg
3382cd3331d0Smrg	if (success) {
3383cd3331d0Smrg	    unparseputc(xw, ';');
3384cd3331d0Smrg	    if (buf >= base) {
3385cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3386cd3331d0Smrg		if (*buf != '\0') {
3387037a25ddSmrg		    char temp[10];
3388037a25ddSmrg
3389cd3331d0Smrg		    unparseputc(xw, '#');
3390cd3331d0Smrg		    sprintf(temp, "%d", num);
3391cd3331d0Smrg		    unparseputs(xw, temp);
3392cd3331d0Smrg		    if (*name != '\0')
3393cd3331d0Smrg			unparseputc(xw, ' ');
3394cd3331d0Smrg		}
3395cd3331d0Smrg	    }
3396cd3331d0Smrg	    unparseputs(xw, name);
3397cd3331d0Smrg	}
3398cd3331d0Smrg
3399cd3331d0Smrg	unparseputc1(xw, final);
3400cd3331d0Smrg	unparse_end(xw);
3401cd3331d0Smrg    }
3402cd3331d0Smrg}
3403cd3331d0Smrg
3404cd3331d0Smrgstatic void
3405cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3406cd3331d0Smrg{
3407cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3408cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3409cd3331d0Smrg	Bool success = True;
3410cd3331d0Smrg	int num;
3411cd3331d0Smrg	VTFontNames fonts;
3412cd3331d0Smrg	char *name;
3413cd3331d0Smrg
3414cd3331d0Smrg	/*
3415cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3416cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3417cd3331d0Smrg	 *
3418cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3419cd3331d0Smrg	 * to load the font entry.
3420cd3331d0Smrg	 */
3421cd3331d0Smrg	if (*buf == '#') {
3422cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3423cd3331d0Smrg
3424cd3331d0Smrg	    if (num < 0
3425cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3426cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3427cd3331d0Smrg		success = False;
3428cd3331d0Smrg	    } else {
3429cd3331d0Smrg		/*
3430cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3431cd3331d0Smrg		 * for a font specification within the control.
3432cd3331d0Smrg		 */
3433cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3434cd3331d0Smrg		    ++buf;
3435cd3331d0Smrg		}
3436cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3437cd3331d0Smrg		    ++buf;
3438cd3331d0Smrg		}
3439cd3331d0Smrg#if OPT_RENDERFONT
3440cd3331d0Smrg		if (UsingRenderFont(xw)) {
3441c219fbebSmrg		    /* EMPTY */
3442c219fbebSmrg		    /* there is only one font entry to load */
3443c219fbebSmrg		    ;
3444cd3331d0Smrg		} else
3445cd3331d0Smrg#endif
3446cd3331d0Smrg		{
3447cd3331d0Smrg		    /*
3448cd3331d0Smrg		     * Normally there is no font specified in the control.
3449cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3450cd3331d0Smrg		     */
3451cd3331d0Smrg		    if (*buf == '\0') {
3452cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3453cd3331d0Smrg			    success = False;
3454cd3331d0Smrg			}
3455cd3331d0Smrg		    }
3456cd3331d0Smrg		}
3457cd3331d0Smrg	    }
3458cd3331d0Smrg	} else {
3459cd3331d0Smrg	    num = screen->menu_font_number;
3460cd3331d0Smrg	}
3461cd3331d0Smrg	name = x_strtrim(buf);
346294644356Smrg	if (screen->EscapeFontName()) {
346394644356Smrg	    FREE_STRING(screen->EscapeFontName());
346494644356Smrg	    screen->EscapeFontName() = 0;
346594644356Smrg	}
3466cd3331d0Smrg	if (success && !IsEmpty(name)) {
3467cd3331d0Smrg#if OPT_RENDERFONT
3468cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3469cd3331d0Smrg		setFaceName(xw, name);
3470cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3471cd3331d0Smrg	    } else
3472cd3331d0Smrg#endif
3473cd3331d0Smrg	    {
3474cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3475cd3331d0Smrg		fonts.f_n = name;
3476cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
347794644356Smrg		if (num == screen->menu_font_number &&
347894644356Smrg		    num != fontMenu_fontescape) {
347994644356Smrg		    screen->EscapeFontName() = x_strdup(name);
348094644356Smrg		}
3481cd3331d0Smrg	    }
3482cd3331d0Smrg	} else {
3483cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3484cd3331d0Smrg	}
348594644356Smrg	update_font_escape();
3486cd3331d0Smrg	free(name);
3487cd3331d0Smrg    }
3488cd3331d0Smrg}
3489cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3490cd3331d0Smrg
3491d522f475Smrg/***====================================================================***/
3492d522f475Smrg
3493d522f475Smrgvoid
3494fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3495d522f475Smrg{
3496cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3497d522f475Smrg    int mode;
3498d522f475Smrg    Char *cp;
3499d522f475Smrg    int state = 0;
3500d522f475Smrg    char *buf = 0;
3501cd3331d0Smrg    char temp[2];
3502cd3331d0Smrg#if OPT_ISO_COLORS
3503cd3331d0Smrg    int ansi_colors = 0;
3504cd3331d0Smrg#endif
3505cd3331d0Smrg    Bool need_data = True;
3506fa3f02f3Smrg    Bool optional_data = False;
3507d522f475Smrg
3508d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3509d522f475Smrg
3510712a7ff4Smrg    (void) screen;
3511712a7ff4Smrg
3512d522f475Smrg    /*
3513d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3514d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3515d522f475Smrg     * with the same final character as the application sends to make this
3516d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3517d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3518d522f475Smrg     */
3519d522f475Smrg    mode = 0;
3520d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3521d522f475Smrg	switch (state) {
3522d522f475Smrg	case 0:
3523d522f475Smrg	    if (isdigit(*cp)) {
3524d522f475Smrg		mode = 10 * mode + (*cp - '0');
3525d522f475Smrg		if (mode > 65535) {
3526d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3527d522f475Smrg		    return;
3528d522f475Smrg		}
3529d522f475Smrg		break;
3530d522f475Smrg	    }
3531d522f475Smrg	    /* FALLTHRU */
3532d522f475Smrg	case 1:
3533d522f475Smrg	    if (*cp != ';') {
3534cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
3535cd3331d0Smrg		       (int) (cp - oscbuf)));
3536d522f475Smrg		return;
3537d522f475Smrg	    }
3538d522f475Smrg	    state = 2;
3539d522f475Smrg	    break;
3540d522f475Smrg	case 2:
3541d522f475Smrg	    buf = (char *) cp;
3542d522f475Smrg	    state = 3;
3543d522f475Smrg	    /* FALLTHRU */
3544d522f475Smrg	default:
3545cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
3546d522f475Smrg		switch (mode) {
3547d522f475Smrg		case 0:
3548d522f475Smrg		case 1:
3549d522f475Smrg		case 2:
3550d522f475Smrg		    break;
3551d522f475Smrg		default:
3552d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
3553d522f475Smrg			   CharOf(*cp),
3554cd3331d0Smrg			   (int) (cp - oscbuf)));
3555d522f475Smrg		    return;
3556d522f475Smrg		}
3557d522f475Smrg	    }
3558d522f475Smrg	}
3559d522f475Smrg    }
3560cd3331d0Smrg
35613367019cSmrg    /*
35623367019cSmrg     * Check if the palette changed and there are no more immediate changes
35633367019cSmrg     * that could be deferred to the next repaint.
35643367019cSmrg     */
35653367019cSmrg    if (xw->misc.palette_changed) {
35663367019cSmrg	switch (mode) {
35673367019cSmrg	case 3:		/* change X property */
35683367019cSmrg	case 30:		/* Konsole (unused) */
35693367019cSmrg	case 31:		/* Konsole (unused) */
35703367019cSmrg	case 50:		/* font operations */
35713367019cSmrg	case 51:		/* Emacs (unused) */
35723367019cSmrg#if OPT_PASTE64
35733367019cSmrg	case 52:		/* selection data */
35743367019cSmrg#endif
35753367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
35763367019cSmrg	    xw->misc.palette_changed = False;
35773367019cSmrg	    xtermRepaint(xw);
35783367019cSmrg	    break;
35793367019cSmrg	}
35803367019cSmrg    }
35813367019cSmrg
3582cd3331d0Smrg    /*
3583cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
3584cd3331d0Smrg     * a special case.
3585cd3331d0Smrg     */
3586cd3331d0Smrg    switch (mode) {
358794644356Smrg    case 50:
3588cd3331d0Smrg#if OPT_ISO_COLORS
3589cd3331d0Smrg    case OSC_Reset(4):
3590cd3331d0Smrg    case OSC_Reset(5):
3591fa3f02f3Smrg	need_data = False;
3592fa3f02f3Smrg	optional_data = True;
3593fa3f02f3Smrg	break;
3594cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3595cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3596cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3597cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3598cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3599cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3600cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3601cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3602cd3331d0Smrg#endif
3603cd3331d0Smrg#if OPT_TEK4014
3604cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3605cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3606cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3607cd3331d0Smrg#endif
3608cd3331d0Smrg	need_data = False;
3609cd3331d0Smrg	break;
3610cd3331d0Smrg#endif
3611cd3331d0Smrg    default:
3612cd3331d0Smrg	break;
3613cd3331d0Smrg    }
3614cd3331d0Smrg
3615cd3331d0Smrg    /*
3616cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
3617cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
3618cd3331d0Smrg     */
3619cd3331d0Smrg    if (IsEmpty(buf)) {
3620cd3331d0Smrg	if (need_data) {
3621cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
3622cd3331d0Smrg	    return;
3623cd3331d0Smrg	}
3624cd3331d0Smrg	temp[0] = '\0';
3625cd3331d0Smrg	buf = temp;
3626fa3f02f3Smrg    } else if (!need_data && !optional_data) {
3627fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
3628d522f475Smrg	return;
36290d92cbfdSchristos    }
3630d522f475Smrg
3631d522f475Smrg    switch (mode) {
3632d522f475Smrg    case 0:			/* new icon name and title */
3633b7c89284Ssnj	ChangeIconName(xw, buf);
3634b7c89284Ssnj	ChangeTitle(xw, buf);
3635d522f475Smrg	break;
3636d522f475Smrg
3637d522f475Smrg    case 1:			/* new icon name only */
3638b7c89284Ssnj	ChangeIconName(xw, buf);
3639d522f475Smrg	break;
3640d522f475Smrg
3641d522f475Smrg    case 2:			/* new title only */
3642b7c89284Ssnj	ChangeTitle(xw, buf);
3643d522f475Smrg	break;
3644d522f475Smrg
364522d8e007Schristos#ifdef notdef
3646d522f475Smrg    case 3:			/* change X property */
3647cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
36480d92cbfdSchristos	    ChangeXprop(buf);
3649d522f475Smrg	break;
365022d8e007Schristos#endif
3651d522f475Smrg#if OPT_ISO_COLORS
3652cd3331d0Smrg    case 5:
3653cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3654cd3331d0Smrg	/* FALLTHRU */
3655d522f475Smrg    case 4:
3656cd3331d0Smrg	if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
36573367019cSmrg	    xw->misc.palette_changed = True;
3658cd3331d0Smrg	break;
365994644356Smrg    case 6:
366094644356Smrg	/* FALLTHRU */
366194644356Smrg    case OSC_Reset(6):
366294644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
366394644356Smrg	while (*buf != '\0') {
366494644356Smrg	    long which = 0;
366594644356Smrg	    long value = 0;
366694644356Smrg	    char *next;
366794644356Smrg	    if (*buf == ';') {
366894644356Smrg		++buf;
366994644356Smrg	    } else {
367094644356Smrg		which = strtol(buf, &next, 10);
3671037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
367294644356Smrg		    break;
367394644356Smrg		buf = next;
367494644356Smrg		if (*buf == ';')
367594644356Smrg		    ++buf;
367694644356Smrg	    }
367794644356Smrg	    if (*buf == ';') {
367894644356Smrg		++buf;
367994644356Smrg	    } else {
368094644356Smrg		value = strtol(buf, &next, 10);
3681037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
368294644356Smrg		    break;
368394644356Smrg		buf = next;
368494644356Smrg		if (*buf == ';')
368594644356Smrg		    ++buf;
368694644356Smrg	    }
368794644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
368894644356Smrg	    switch (which) {
368994644356Smrg	    case 0:
369094644356Smrg		screen->colorBDMode = (value != 0);
369194644356Smrg		break;
369294644356Smrg	    case 1:
369394644356Smrg		screen->colorULMode = (value != 0);
369494644356Smrg		break;
369594644356Smrg	    case 2:
369694644356Smrg		screen->colorBLMode = (value != 0);
369794644356Smrg		break;
369894644356Smrg	    case 3:
369994644356Smrg		screen->colorRVMode = (value != 0);
370094644356Smrg		break;
370194644356Smrg#if OPT_WIDE_ATTRS
370294644356Smrg	    case 4:
370394644356Smrg		screen->colorITMode = (value != 0);
370494644356Smrg		break;
370594644356Smrg#endif
370694644356Smrg	    default:
370794644356Smrg		TRACE(("...unknown colorXXMode\n"));
370894644356Smrg		break;
370994644356Smrg	    }
371094644356Smrg	}
371194644356Smrg	break;
3712cd3331d0Smrg    case OSC_Reset(5):
3713cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3714cd3331d0Smrg	/* FALLTHRU */
3715cd3331d0Smrg    case OSC_Reset(4):
3716cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
37173367019cSmrg	    xw->misc.palette_changed = True;
3718d522f475Smrg	break;
3719d522f475Smrg#endif
3720d522f475Smrg    case OSC_TEXT_FG:
3721d522f475Smrg    case OSC_TEXT_BG:
3722d522f475Smrg    case OSC_TEXT_CURSOR:
3723d522f475Smrg    case OSC_MOUSE_FG:
3724d522f475Smrg    case OSC_MOUSE_BG:
3725d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3726d522f475Smrg    case OSC_HIGHLIGHT_BG:
3727cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
3728d522f475Smrg#endif
3729d522f475Smrg#if OPT_TEK4014
3730d522f475Smrg    case OSC_TEK_FG:
3731d522f475Smrg    case OSC_TEK_BG:
3732d522f475Smrg    case OSC_TEK_CURSOR:
3733d522f475Smrg#endif
3734cd3331d0Smrg	if (xw->misc.dynamicColors) {
3735d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
3736cd3331d0Smrg	}
3737cd3331d0Smrg	break;
3738cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3739cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3740cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3741cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3742cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3743cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3744cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3745cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3746cd3331d0Smrg#endif
3747cd3331d0Smrg#if OPT_TEK4014
3748cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3749cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3750cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3751cd3331d0Smrg#endif
3752cd3331d0Smrg	if (xw->misc.dynamicColors) {
3753cd3331d0Smrg	    ResetColorsRequest(xw, mode);
3754cd3331d0Smrg	}
3755d522f475Smrg	break;
3756d522f475Smrg
3757d522f475Smrg    case 30:
3758d522f475Smrg    case 31:
3759d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
3760d522f475Smrg	break;
3761d522f475Smrg
3762d522f475Smrg#ifdef ALLOWLOGGING
3763d522f475Smrg    case 46:			/* new log file */
3764d522f475Smrg#ifdef ALLOWLOGFILECHANGES
3765d522f475Smrg	/*
3766d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
3767d522f475Smrg	 * arbitrary files accessible to the person running xterm.
3768d522f475Smrg	 */
3769037a25ddSmrg	if (strcmp(buf, "?")) {
3770037a25ddSmrg	    char *bp;
3771037a25ddSmrg	    if ((bp = CastMallocN(char, strlen(buf))) != NULL) {
3772037a25ddSmrg		strcpy(bp, buf);
3773037a25ddSmrg		if (screen->logfile)
3774037a25ddSmrg		    free(screen->logfile);
3775037a25ddSmrg		screen->logfile = bp;
3776037a25ddSmrg		break;
3777037a25ddSmrg	    }
3778d522f475Smrg	}
3779d522f475Smrg#endif
3780cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3781cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3782d522f475Smrg	break;
3783d522f475Smrg#endif /* ALLOWLOGGING */
3784d522f475Smrg
3785d522f475Smrg    case 50:
3786d522f475Smrg#if OPT_SHIFT_FONTS
3787cd3331d0Smrg	if (*buf == '?') {
3788cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
3789cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
3790cd3331d0Smrg	    ChangeFontRequest(xw, buf);
3791d522f475Smrg	}
3792d522f475Smrg#endif /* OPT_SHIFT_FONTS */
3793d522f475Smrg	break;
3794d522f475Smrg    case 51:
3795d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
3796d522f475Smrg	break;
3797d522f475Smrg
3798d522f475Smrg#if OPT_PASTE64
3799d522f475Smrg    case 52:
3800cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
3801d522f475Smrg	break;
3802d522f475Smrg#endif
3803d522f475Smrg	/*
3804d522f475Smrg	 * One could write code to send back the display and host names,
3805d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
3806d522f475Smrg	 */
3807cd3331d0Smrg    default:
3808cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
3809cd3331d0Smrg	break;
3810d522f475Smrg    }
3811d522f475Smrg    unparse_end(xw);
3812d522f475Smrg}
3813d522f475Smrg
3814d522f475Smrg/*
3815d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
3816d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
3817d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
3818d522f475Smrg * "real" terminals accept commas in the string definitions).
3819d522f475Smrg */
3820d522f475Smrgstatic int
3821cd3331d0Smrgudk_value(const char **cp)
3822d522f475Smrg{
3823cd3331d0Smrg    int result = -1;
3824d522f475Smrg
3825d522f475Smrg    for (;;) {
3826037a25ddSmrg	int c;
3827037a25ddSmrg
3828d522f475Smrg	if ((c = **cp) != '\0')
3829d522f475Smrg	    *cp = *cp + 1;
3830d522f475Smrg	if (c == ';' || c == '\0')
3831cd3331d0Smrg	    break;
3832cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
3833cd3331d0Smrg	    break;
3834d522f475Smrg    }
3835cd3331d0Smrg
3836cd3331d0Smrg    return result;
3837d522f475Smrg}
3838d522f475Smrg
3839d522f475Smrgvoid
38409a64e1c5Smrgreset_decudk(XtermWidget xw)
3841d522f475Smrg{
3842d522f475Smrg    int n;
3843d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
38449a64e1c5Smrg	if (xw->work.user_keys[n].str != 0) {
38459a64e1c5Smrg	    free(xw->work.user_keys[n].str);
38469a64e1c5Smrg	    xw->work.user_keys[n].str = 0;
38479a64e1c5Smrg	    xw->work.user_keys[n].len = 0;
3848d522f475Smrg	}
3849d522f475Smrg    }
3850d522f475Smrg}
3851d522f475Smrg
3852d522f475Smrg/*
3853d522f475Smrg * Parse the data for DECUDK (user-defined keys).
3854d522f475Smrg */
3855d522f475Smrgstatic void
38569a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
3857d522f475Smrg{
3858d522f475Smrg    while (*cp) {
3859cd3331d0Smrg	const char *base = cp;
38603367019cSmrg	char *str = CastMallocN(char, strlen(cp) + 2);
3861d522f475Smrg	unsigned key = 0;
3862d522f475Smrg	int len = 0;
3863d522f475Smrg
386494644356Smrg	if (str == NULL)
386594644356Smrg	    break;
386694644356Smrg
3867d522f475Smrg	while (isdigit(CharOf(*cp)))
38680d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
3869037a25ddSmrg
3870d522f475Smrg	if (*cp == '/') {
3871037a25ddSmrg	    int lo, hi;
3872037a25ddSmrg
3873d522f475Smrg	    cp++;
3874d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
3875d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
38760d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
3877d522f475Smrg	    }
3878d522f475Smrg	}
3879d522f475Smrg	if (len > 0 && key < MAX_UDK) {
38803367019cSmrg	    str[len] = '\0';
38819a64e1c5Smrg	    if (xw->work.user_keys[key].str != 0)
38829a64e1c5Smrg		free(xw->work.user_keys[key].str);
38839a64e1c5Smrg	    xw->work.user_keys[key].str = str;
38849a64e1c5Smrg	    xw->work.user_keys[key].len = len;
3885d522f475Smrg	} else {
3886d522f475Smrg	    free(str);
3887d522f475Smrg	}
3888d522f475Smrg	if (*cp == ';')
3889d522f475Smrg	    cp++;
3890d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
3891d522f475Smrg	    break;
3892d522f475Smrg    }
3893d522f475Smrg}
3894d522f475Smrg
3895fa3f02f3Smrg/*
3896fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
3897fa3f02f3Smrg * interspersing with control characters, but have the string already.
3898fa3f02f3Smrg */
3899fa3f02f3Smrgstatic void
3900fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
3901fa3f02f3Smrg{
3902fa3f02f3Smrg    const char *cp = *string;
3903fa3f02f3Smrg    ParmType nparam = 0;
3904fa3f02f3Smrg    int last_empty = 1;
3905fa3f02f3Smrg
3906fa3f02f3Smrg    memset(params, 0, sizeof(*params));
3907fa3f02f3Smrg    while (*cp != '\0') {
3908fa3f02f3Smrg	Char ch = CharOf(*cp++);
3909fa3f02f3Smrg
3910fa3f02f3Smrg	if (isdigit(ch)) {
3911fa3f02f3Smrg	    last_empty = 0;
3912fa3f02f3Smrg	    if (nparam < NPARAM) {
3913fa3f02f3Smrg		params->a_param[nparam] =
3914fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
3915fa3f02f3Smrg				+ (ch - '0'));
3916fa3f02f3Smrg	    }
3917fa3f02f3Smrg	} else if (ch == ';') {
3918fa3f02f3Smrg	    last_empty = 1;
3919fa3f02f3Smrg	    nparam++;
3920fa3f02f3Smrg	} else if (ch < 32) {
3921fa3f02f3Smrg	    /* EMPTY */ ;
3922fa3f02f3Smrg	} else {
3923fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
3924fa3f02f3Smrg	    params->a_final = ch;
3925fa3f02f3Smrg	    break;
3926fa3f02f3Smrg	}
3927fa3f02f3Smrg    }
3928fa3f02f3Smrg
3929fa3f02f3Smrg    *string = cp;
3930fa3f02f3Smrg    if (!last_empty)
3931fa3f02f3Smrg	nparam++;
3932fa3f02f3Smrg    if (nparam > NPARAM)
3933fa3f02f3Smrg	params->a_nparam = NPARAM;
3934fa3f02f3Smrg    else
3935fa3f02f3Smrg	params->a_nparam = nparam;
3936fa3f02f3Smrg}
3937fa3f02f3Smrg
3938d522f475Smrg#if OPT_TRACE
3939d522f475Smrg#define SOFT_WIDE 10
3940d522f475Smrg#define SOFT_HIGH 20
3941d522f475Smrg
3942d522f475Smrgstatic void
3943fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
3944d522f475Smrg{
3945d522f475Smrg    char DscsName[8];
3946d522f475Smrg    int len;
3947d522f475Smrg    int Pfn = params->a_param[0];
3948d522f475Smrg    int Pcn = params->a_param[1];
3949d522f475Smrg    int Pe = params->a_param[2];
3950d522f475Smrg    int Pcmw = params->a_param[3];
3951d522f475Smrg    int Pw = params->a_param[4];
3952d522f475Smrg    int Pt = params->a_param[5];
3953d522f475Smrg    int Pcmh = params->a_param[6];
3954d522f475Smrg    int Pcss = params->a_param[7];
3955d522f475Smrg
3956d522f475Smrg    int start_char = Pcn + 0x20;
3957d522f475Smrg    int char_wide = ((Pcmw == 0)
3958d522f475Smrg		     ? (Pcss ? 6 : 10)
3959d522f475Smrg		     : (Pcmw > 4
3960d522f475Smrg			? Pcmw
3961d522f475Smrg			: (Pcmw + 3)));
3962d522f475Smrg    int char_high = ((Pcmh == 0)
39633367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
3964d522f475Smrg			? 10
3965d522f475Smrg			: 20)
3966d522f475Smrg		     : Pcmh);
3967d522f475Smrg    Char ch;
3968d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
3969d522f475Smrg    Bool first = True;
3970d522f475Smrg    Bool prior = False;
3971d522f475Smrg    int row = 0, col = 0;
3972d522f475Smrg
3973d522f475Smrg    TRACE(("Parsing DECDLD\n"));
3974d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
3975d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
3976d522f475Smrg    TRACE(("  erase control %d\n", Pe));
3977d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
3978d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
3979d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
3980d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
3981d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
3982d522f475Smrg
3983d522f475Smrg    if (Pfn > 1
3984d522f475Smrg	|| Pcn > 95
3985d522f475Smrg	|| Pe > 2
3986d522f475Smrg	|| Pcmw > 10
3987d522f475Smrg	|| Pcmw == 1
3988d522f475Smrg	|| Pt > 2
3989d522f475Smrg	|| Pcmh > 20
3990d522f475Smrg	|| Pcss > 1
3991d522f475Smrg	|| char_wide > SOFT_WIDE
3992d522f475Smrg	|| char_high > SOFT_HIGH) {
3993d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
3994d522f475Smrg	return;
3995d522f475Smrg    }
3996d522f475Smrg
3997d522f475Smrg    len = 0;
3998d522f475Smrg    while (*string != '\0') {
3999d522f475Smrg	ch = CharOf(*string++);
4000d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4001d522f475Smrg	    if (len < 2)
4002b7c89284Ssnj		DscsName[len++] = (char) ch;
4003d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4004b7c89284Ssnj	    DscsName[len++] = (char) ch;
4005d522f475Smrg	    break;
4006d522f475Smrg	}
4007d522f475Smrg    }
4008d522f475Smrg    DscsName[len] = 0;
4009d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4010d522f475Smrg
4011d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4012d522f475Smrg    while (*string != '\0') {
4013d522f475Smrg	if (first) {
4014d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4015d522f475Smrg	    if (prior) {
4016d522f475Smrg		for (row = 0; row < char_high; ++row) {
4017d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4018d522f475Smrg		}
4019d522f475Smrg	    }
4020d522f475Smrg	    prior = False;
4021d522f475Smrg	    first = False;
4022d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4023d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4024d522f475Smrg		    bits[row][col] = '.';
4025d522f475Smrg		}
4026d522f475Smrg	    }
4027d522f475Smrg	    row = col = 0;
4028d522f475Smrg	}
4029d522f475Smrg	ch = CharOf(*string++);
4030d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4031d522f475Smrg	    int n;
4032d522f475Smrg
4033b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4034d522f475Smrg	    for (n = 0; n < 6; ++n) {
4035b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4036d522f475Smrg	    }
4037d522f475Smrg	    col += 1;
4038d522f475Smrg	    prior = True;
4039d522f475Smrg	} else if (ch == '/') {
4040d522f475Smrg	    row += 6;
4041d522f475Smrg	    col = 0;
4042d522f475Smrg	} else if (ch == ';') {
4043d522f475Smrg	    first = True;
4044d522f475Smrg	    ++start_char;
4045d522f475Smrg	}
4046d522f475Smrg    }
4047d522f475Smrg}
4048d522f475Smrg#else
4049d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4050d522f475Smrg#endif
4051d522f475Smrg
4052d522f475Smrgvoid
4053fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4054d522f475Smrg{
4055cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4056d522f475Smrg    char reply[BUFSIZ];
4057cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4058d522f475Smrg    Bool okay;
4059d522f475Smrg    ANSI params;
4060d522f475Smrg
4061cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4062d522f475Smrg
4063d522f475Smrg    if (dcslen != strlen(cp))
4064d522f475Smrg	/* shouldn't have nulls in the string */
4065d522f475Smrg	return;
4066d522f475Smrg
4067d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4068d522f475Smrg    case '$':			/* DECRQSS */
4069d522f475Smrg	okay = True;
4070d522f475Smrg
4071d522f475Smrg	cp++;
4072d522f475Smrg	if (*cp++ == 'q') {
4073d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4074d522f475Smrg		sprintf(reply, "%d%s",
4075d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4076d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4077d522f475Smrg			cp);
4078d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
40793367019cSmrg		if (screen->vtXX_level < 2) {
40803367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
40813367019cSmrg		    break;
40823367019cSmrg		}
4083d522f475Smrg		sprintf(reply, "%d%s%s",
4084d522f475Smrg			(screen->vtXX_level ?
4085d522f475Smrg			 screen->vtXX_level : 1) + 60,
4086d522f475Smrg			(screen->vtXX_level >= 2)
4087d522f475Smrg			? (screen->control_eight_bits
4088d522f475Smrg			   ? ";0" : ";1")
4089d522f475Smrg			: "",
4090d522f475Smrg			cp);
4091d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4092d522f475Smrg		sprintf(reply, "%d;%dr",
4093d522f475Smrg			screen->top_marg + 1,
4094d522f475Smrg			screen->bot_marg + 1);
40953367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
40963367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
40973367019cSmrg		    sprintf(reply, "%d;%ds",
40983367019cSmrg			    screen->lft_marg + 1,
40993367019cSmrg			    screen->rgt_marg + 1);
4100037a25ddSmrg		} else {
4101037a25ddSmrg		    okay = False;
41023367019cSmrg		}
4103d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4104d522f475Smrg		strcpy(reply, "0");
4105d522f475Smrg		if (xw->flags & BOLD)
4106d522f475Smrg		    strcat(reply, ";1");
4107d522f475Smrg		if (xw->flags & UNDERLINE)
4108d522f475Smrg		    strcat(reply, ";4");
4109d522f475Smrg		if (xw->flags & BLINK)
4110d522f475Smrg		    strcat(reply, ";5");
4111d522f475Smrg		if (xw->flags & INVERSE)
4112d522f475Smrg		    strcat(reply, ";7");
4113d522f475Smrg		if (xw->flags & INVISIBLE)
4114d522f475Smrg		    strcat(reply, ";8");
4115b7c89284Ssnj#if OPT_256_COLORS || OPT_88_COLORS
4116b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4117d522f475Smrg		    if (xw->flags & FG_COLOR) {
4118d522f475Smrg			if (xw->cur_foreground >= 16)
4119d522f475Smrg			    sprintf(reply + strlen(reply),
4120d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
4121d522f475Smrg			else
4122d522f475Smrg			    sprintf(reply + strlen(reply),
4123d522f475Smrg				    ";%d%d",
4124d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
4125d522f475Smrg				    xw->cur_foreground >= 8 ?
4126d522f475Smrg				    xw->cur_foreground - 8 :
4127d522f475Smrg				    xw->cur_foreground);
4128d522f475Smrg		    }
4129d522f475Smrg		    if (xw->flags & BG_COLOR) {
4130d522f475Smrg			if (xw->cur_background >= 16)
4131d522f475Smrg			    sprintf(reply + strlen(reply),
4132d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
4133d522f475Smrg			else
4134d522f475Smrg			    sprintf(reply + strlen(reply),
4135d522f475Smrg				    ";%d%d",
4136d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
4137d522f475Smrg				    xw->cur_background >= 8 ?
4138d522f475Smrg				    xw->cur_background - 8 :
4139d522f475Smrg				    xw->cur_background);
4140d522f475Smrg		    }
4141d522f475Smrg		});
4142b7c89284Ssnj#elif OPT_ISO_COLORS
4143b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4144d522f475Smrg		    if (xw->flags & FG_COLOR)
4145d522f475Smrg			sprintf(reply + strlen(reply),
4146d522f475Smrg				";%d%d",
4147d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
4148d522f475Smrg				xw->cur_foreground >= 8 ?
4149d522f475Smrg				xw->cur_foreground - 8 :
4150d522f475Smrg				xw->cur_foreground);
4151d522f475Smrg		    if (xw->flags & BG_COLOR)
4152d522f475Smrg			sprintf(reply + strlen(reply),
4153d522f475Smrg				";%d%d",
4154d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
4155d522f475Smrg				xw->cur_background >= 8 ?
4156d522f475Smrg				xw->cur_background - 8 :
4157d522f475Smrg				xw->cur_background);
4158d522f475Smrg		});
4159b7c89284Ssnj#endif
4160d522f475Smrg		strcat(reply, "m");
4161712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
41623367019cSmrg		int code = STEADY_BLOCK;
41633367019cSmrg		if (isCursorUnderline(screen))
41643367019cSmrg		    code = STEADY_UNDERLINE;
41653367019cSmrg		else if (isCursorBar(screen))
41663367019cSmrg		    code = STEADY_BAR;
41673367019cSmrg#if OPT_BLINK_CURS
416894644356Smrg		if (screen->cursor_blink_esc != 0)
41693367019cSmrg		    code -= 1;
41703367019cSmrg#endif
41713367019cSmrg		sprintf(reply, "%d%s", code, cp);
4172d522f475Smrg	    } else
4173d522f475Smrg		okay = False;
4174d522f475Smrg
417522d8e007Schristos	    if (okay) {
41760d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
41773367019cSmrg		unparseputc(xw, '1');
41780d92cbfdSchristos		unparseputc(xw, '$');
41790d92cbfdSchristos		unparseputc(xw, 'r');
4180d522f475Smrg		cp = reply;
418122d8e007Schristos		unparseputs(xw, cp);
41820d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
41830d92cbfdSchristos	    } else {
41840d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
418522d8e007Schristos	    }
4186d522f475Smrg	} else {
4187d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4188d522f475Smrg	}
4189d522f475Smrg	break;
4190d522f475Smrg#if OPT_TCAP_QUERY
4191d522f475Smrg    case '+':
4192d522f475Smrg	cp++;
4193cd3331d0Smrg	switch (*cp) {
4194cd3331d0Smrg	case 'p':
4195cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4196cd3331d0Smrg		set_termcap(xw, cp + 1);
4197cd3331d0Smrg	    }
4198cd3331d0Smrg	    break;
4199cd3331d0Smrg	case 'q':
4200cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4201cd3331d0Smrg		Bool fkey;
4202cd3331d0Smrg		unsigned state;
4203cd3331d0Smrg		int code;
4204cd3331d0Smrg		const char *tmp;
4205cd3331d0Smrg		const char *parsed = ++cp;
4206d522f475Smrg
4207cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4208d522f475Smrg
4209cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4210b7c89284Ssnj
4211cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4212d522f475Smrg
4213cd3331d0Smrg		unparseputc(xw, '+');
4214cd3331d0Smrg		unparseputc(xw, 'r');
4215d522f475Smrg
4216cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4217cd3331d0Smrg		    if (cp == parsed)
4218cd3331d0Smrg			break;	/* no data found, error */
4219d522f475Smrg
4220cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4221cd3331d0Smrg			unparseputc(xw, *tmp);
4222d522f475Smrg
4223cd3331d0Smrg		    if (code >= 0) {
4224cd3331d0Smrg			unparseputc(xw, '=');
4225cd3331d0Smrg			screen->tc_query_code = code;
4226cd3331d0Smrg			screen->tc_query_fkey = fkey;
4227d522f475Smrg#if OPT_ISO_COLORS
4228cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4229cd3331d0Smrg			 * number of colors) */
4230cd3331d0Smrg			if (code == XK_COLORS) {
4231cd3331d0Smrg			    unparseputn(xw, NUM_ANSI_COLORS);
4232cd3331d0Smrg			} else
4233cd3331d0Smrg#endif
4234cd3331d0Smrg			if (code == XK_TCAPNAME) {
4235c219fbebSmrg			    unparseputs(xw, resource.term_name);
4236cd3331d0Smrg			} else {
4237cd3331d0Smrg			    XKeyEvent event;
4238cd3331d0Smrg			    event.state = state;
4239cd3331d0Smrg			    Input(xw, &event, False);
4240cd3331d0Smrg			}
4241cd3331d0Smrg			screen->tc_query_code = -1;
4242cd3331d0Smrg		    } else {
4243cd3331d0Smrg			break;	/* no match found, error */
4244d522f475Smrg		    }
4245d522f475Smrg
4246d522f475Smrg		    cp = parsed;
4247cd3331d0Smrg		    if (*parsed == ';') {
4248cd3331d0Smrg			unparseputc(xw, *parsed++);
4249cd3331d0Smrg			cp = parsed;
4250cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4251cd3331d0Smrg		    }
4252d522f475Smrg		}
4253cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4254d522f475Smrg	    }
4255cd3331d0Smrg	    break;
4256d522f475Smrg	}
4257d522f475Smrg	break;
4258d522f475Smrg#endif
4259d522f475Smrg    default:
4260fa3f02f3Smrg	if (screen->terminal_id == 125 ||
4261fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
42620d92cbfdSchristos	    parse_ansi_params(&params, &cp);
42630d92cbfdSchristos	    switch (params.a_final) {
4264fa3f02f3Smrg	    case 'p':
42659a64e1c5Smrg#if OPT_REGIS_GRAPHICS
4266fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4267fa3f02f3Smrg		    screen->terminal_id == 240 ||
4268fa3f02f3Smrg		    screen->terminal_id == 241 ||
4269fa3f02f3Smrg		    screen->terminal_id == 330 ||
4270fa3f02f3Smrg		    screen->terminal_id == 340) {
4271fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4272fa3f02f3Smrg		}
42739a64e1c5Smrg#else
42749a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
42759a64e1c5Smrg#endif
4276fa3f02f3Smrg		break;
4277fa3f02f3Smrg	    case 'q':
42789a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
4279fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4280fa3f02f3Smrg		    screen->terminal_id == 240 ||
4281fa3f02f3Smrg		    screen->terminal_id == 241 ||
4282fa3f02f3Smrg		    screen->terminal_id == 330 ||
42839a64e1c5Smrg		    screen->terminal_id == 340 ||
42849a64e1c5Smrg		    screen->terminal_id == 382) {
4285037a25ddSmrg		    (void) parse_sixel(xw, &params, cp);
4286fa3f02f3Smrg		}
42879a64e1c5Smrg#else
42889a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
4289fa3f02f3Smrg#endif
42909a64e1c5Smrg		break;
42910d92cbfdSchristos	    case '|':		/* DECUDK */
42929a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
42939a64e1c5Smrg		    if (params.a_param[0] == 0)
42949a64e1c5Smrg			reset_decudk(xw);
42959a64e1c5Smrg		    parse_decudk(xw, cp);
42969a64e1c5Smrg		}
42970d92cbfdSchristos		break;
429894644356Smrg	    case L_CURL:	/* DECDLD */
42999a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
43009a64e1c5Smrg		    parse_decdld(&params, cp);
43019a64e1c5Smrg		}
43020d92cbfdSchristos		break;
43030d92cbfdSchristos	    }
4304d522f475Smrg	}
4305d522f475Smrg	break;
4306d522f475Smrg    }
4307d522f475Smrg    unparse_end(xw);
4308d522f475Smrg}
4309d522f475Smrg
4310cb4a1343Smrg#if OPT_DEC_RECTOPS
4311cb4a1343Smrgenum {
4312cb4a1343Smrg    mdUnknown = 0,
4313cb4a1343Smrg    mdMaybeSet = 1,
4314cb4a1343Smrg    mdMaybeReset = 2,
4315cb4a1343Smrg    mdAlwaysSet = 3,
4316cb4a1343Smrg    mdAlwaysReset = 4
4317cb4a1343Smrg};
4318cb4a1343Smrg
4319cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
43203367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
4321cb4a1343Smrg
4322cb4a1343Smrg/*
4323cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
4324cb4a1343Smrg * 0 - not recognized
4325cb4a1343Smrg * 1 - set
4326cb4a1343Smrg * 2 - reset
4327cb4a1343Smrg * 3 - permanently set
4328cb4a1343Smrg * 4 - permanently reset
4329cb4a1343Smrg * Only one mode can be reported at a time.
4330cb4a1343Smrg */
4331cb4a1343Smrgvoid
4332cb4a1343Smrgdo_rpm(XtermWidget xw, int nparams, int *params)
4333cb4a1343Smrg{
4334cb4a1343Smrg    ANSI reply;
4335cb4a1343Smrg    int count = 0;
4336cb4a1343Smrg
4337cb4a1343Smrg    TRACE(("do_rpm %d:%d\n", nparams, params[0]));
4338cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4339037a25ddSmrg
4340cb4a1343Smrg    if (nparams >= 1) {
4341037a25ddSmrg	int result = 0;
4342037a25ddSmrg
4343cb4a1343Smrg	switch (params[0]) {
4344cb4a1343Smrg	case 1:		/* GATM */
4345cb4a1343Smrg	    result = mdAlwaysReset;
4346cb4a1343Smrg	    break;
4347cb4a1343Smrg	case 2:
4348cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
4349cb4a1343Smrg	    break;
4350cb4a1343Smrg	case 3:		/* CRM */
4351cb4a1343Smrg	    result = mdMaybeReset;
4352cb4a1343Smrg	    break;
4353cb4a1343Smrg	case 4:
4354cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
4355cb4a1343Smrg	    break;
4356cb4a1343Smrg	case 5:		/* SRTM */
4357cb4a1343Smrg	case 7:		/* VEM */
4358cb4a1343Smrg	case 10:		/* HEM */
4359cb4a1343Smrg	case 11:		/* PUM */
4360cb4a1343Smrg	    result = mdAlwaysReset;
4361cb4a1343Smrg	    break;
4362cb4a1343Smrg	case 12:
4363cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
4364cb4a1343Smrg	    break;
4365cb4a1343Smrg	case 13:		/* FEAM */
4366cb4a1343Smrg	case 14:		/* FETM */
4367cb4a1343Smrg	case 15:		/* MATM */
4368cb4a1343Smrg	case 16:		/* TTM */
4369cb4a1343Smrg	case 17:		/* SATM */
4370cb4a1343Smrg	case 18:		/* TSM */
4371cb4a1343Smrg	case 19:		/* EBM */
4372cb4a1343Smrg	    result = mdAlwaysReset;
4373cb4a1343Smrg	    break;
4374cb4a1343Smrg	case 20:
4375cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
4376cb4a1343Smrg	    break;
4377cb4a1343Smrg	}
4378cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4379cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4380cb4a1343Smrg    }
4381cb4a1343Smrg    reply.a_type = ANSI_CSI;
4382cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4383cb4a1343Smrg    reply.a_inters = '$';
4384cb4a1343Smrg    reply.a_final = 'y';
4385cb4a1343Smrg    unparseseq(xw, &reply);
4386cb4a1343Smrg}
4387cb4a1343Smrg
4388cb4a1343Smrgvoid
4389cb4a1343Smrgdo_decrpm(XtermWidget xw, int nparams, int *params)
4390cb4a1343Smrg{
4391cb4a1343Smrg    ANSI reply;
4392cb4a1343Smrg    int count = 0;
4393cb4a1343Smrg
4394cb4a1343Smrg    TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
4395cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4396037a25ddSmrg
4397cb4a1343Smrg    if (nparams >= 1) {
4398cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
4399037a25ddSmrg	int result = 0;
4400cb4a1343Smrg
4401cb4a1343Smrg	switch (params[0]) {
4402fa3f02f3Smrg	case srm_DECCKM:
4403cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
4404cb4a1343Smrg	    break;
4405fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
4406cb4a1343Smrg#if OPT_VT52_MODE
44073367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
4408cb4a1343Smrg#else
4409cb4a1343Smrg	    result = mdMaybeSet;
4410cb4a1343Smrg#endif
4411cb4a1343Smrg	    break;
4412fa3f02f3Smrg	case srm_DECCOLM:
4413cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
4414cb4a1343Smrg	    break;
4415fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
4416cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
4417cb4a1343Smrg	    break;
4418fa3f02f3Smrg	case srm_DECSCNM:
4419cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
4420cb4a1343Smrg	    break;
4421fa3f02f3Smrg	case srm_DECOM:
4422cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
4423cb4a1343Smrg	    break;
4424fa3f02f3Smrg	case srm_DECAWM:
4425cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
4426cb4a1343Smrg	    break;
4427fa3f02f3Smrg	case srm_DECARM:
4428cb4a1343Smrg	    result = mdAlwaysReset;
4429cb4a1343Smrg	    break;
4430fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
4431cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
4432cb4a1343Smrg	    break;
4433cb4a1343Smrg#if OPT_TOOLBAR
4434fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
4435cb4a1343Smrg	    result = MdBool(resource.toolBar);
4436cb4a1343Smrg	    break;
4437cb4a1343Smrg#endif
4438cb4a1343Smrg#if OPT_BLINK_CURS
4439fa3f02f3Smrg	case srm_ATT610_BLINK:	/* att610: Start/stop blinking cursor */
4440cb4a1343Smrg	    result = MdBool(screen->cursor_blink_res);
4441cb4a1343Smrg	    break;
4442cb4a1343Smrg#endif
4443fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
4444712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
4445cb4a1343Smrg	    break;
4446fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
4447712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
4448cb4a1343Smrg	    break;
4449fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
4450cb4a1343Smrg	    result = MdBool(screen->cursor_set);
4451cb4a1343Smrg	    break;
4452fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
4453cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
4454cb4a1343Smrg	    break;
4455cb4a1343Smrg#if OPT_SHIFT_FONTS
4456fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
4457cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
4458cb4a1343Smrg	    break;
4459cb4a1343Smrg#endif
4460cb4a1343Smrg#if OPT_TEK4014
4461fa3f02f3Smrg	case srm_DECTEK:
4462cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
4463cb4a1343Smrg	    break;
4464cb4a1343Smrg#endif
4465fa3f02f3Smrg	case srm_132COLS:
4466cb4a1343Smrg	    result = MdBool(screen->c132);
4467cb4a1343Smrg	    break;
4468fa3f02f3Smrg	case srm_CURSES_HACK:
4469cb4a1343Smrg	    result = MdBool(screen->curses);
4470cb4a1343Smrg	    break;
4471fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
4472cb4a1343Smrg	    result = MdFlag(xw->flags, NATIONAL);
4473cb4a1343Smrg	    break;
4474fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
4475cb4a1343Smrg	    result = MdBool(screen->marginbell);
4476cb4a1343Smrg	    break;
4477fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
4478cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSEWRAP);
4479cb4a1343Smrg	    break;
4480cb4a1343Smrg#ifdef ALLOWLOGGING
4481fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
4482cb4a1343Smrg#ifdef ALLOWLOGFILEONOFF
4483cb4a1343Smrg	    result = MdBool(screen->logging);
4484cb4a1343Smrg#endif /* ALLOWLOGFILEONOFF */
4485cb4a1343Smrg	    break;
4486cb4a1343Smrg#endif
4487fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
4488cb4a1343Smrg	    /* FALLTHRU */
4489fa3f02f3Smrg	case srm_OPT_ALTBUF:
4490cb4a1343Smrg	    /* FALLTHRU */
4491fa3f02f3Smrg	case srm_ALTBUF:
4492cb4a1343Smrg	    result = MdBool(screen->whichBuf);
4493cb4a1343Smrg	    break;
4494fa3f02f3Smrg	case srm_DECNKM:
4495cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
4496cb4a1343Smrg	    break;
4497fa3f02f3Smrg	case srm_DECBKM:
4498cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
4499cb4a1343Smrg	    break;
4500fa3f02f3Smrg	case srm_DECLRMM:
45013367019cSmrg	    result = MdFlag(xw->flags, LEFT_RIGHT);
45023367019cSmrg	    break;
4503fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
4504fa3f02f3Smrg	case srm_DECSDM:
4505fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
4506fa3f02f3Smrg	    break;
4507fa3f02f3Smrg#endif
4508fa3f02f3Smrg	case srm_DECNCSM:
45093367019cSmrg	    result = MdFlag(xw->flags, NOCLEAR_COLM);
45103367019cSmrg	    break;
4511fa3f02f3Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence         */
4512cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
4513cb4a1343Smrg	    break;
4514fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
4515cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
4516cb4a1343Smrg	    break;
4517fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
4518cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
4519cb4a1343Smrg	    break;
4520fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
4521cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
4522cb4a1343Smrg	    break;
4523cb4a1343Smrg#if OPT_FOCUS_EVENT
4524fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
4525cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
4526cb4a1343Smrg	    break;
4527cb4a1343Smrg#endif
4528fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
45293367019cSmrg	    /* FALLTHRU */
4530fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
45313367019cSmrg	    /* FALLTHRU */
4532fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
45333367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
45343367019cSmrg	    break;
4535fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
45363367019cSmrg	    result = MdBool(screen->alternateScroll);
4537cb4a1343Smrg	    break;
4538fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
4539cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
4540cb4a1343Smrg	    break;
4541fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
4542cb4a1343Smrg	    result = MdBool(screen->scrollkey);
4543cb4a1343Smrg	    break;
4544fa3f02f3Smrg	case srm_EIGHT_BIT_META:
45453367019cSmrg	    result = MdBool(screen->eight_bit_meta);
4546cb4a1343Smrg	    break;
4547cb4a1343Smrg#if OPT_NUM_LOCK
4548fa3f02f3Smrg	case srm_REAL_NUMLOCK:
4549cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
4550cb4a1343Smrg	    break;
4551fa3f02f3Smrg	case srm_META_SENDS_ESC:
4552cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
4553cb4a1343Smrg	    break;
4554cb4a1343Smrg#endif
4555fa3f02f3Smrg	case srm_DELETE_IS_DEL:
4556cb4a1343Smrg	    result = MdBool(screen->delete_is_del);
4557cb4a1343Smrg	    break;
4558cb4a1343Smrg#if OPT_NUM_LOCK
4559fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
4560cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
4561cb4a1343Smrg	    break;
4562cb4a1343Smrg#endif
4563fa3f02f3Smrg	case srm_KEEP_SELECTION:
4564cb4a1343Smrg	    result = MdBool(screen->keepSelection);
4565cb4a1343Smrg	    break;
4566fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
4567cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
4568cb4a1343Smrg	    break;
4569fa3f02f3Smrg	case srm_BELL_IS_URGENT:
4570cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
4571cb4a1343Smrg	    break;
4572fa3f02f3Smrg	case srm_POP_ON_BELL:
4573cb4a1343Smrg	    result = MdBool(screen->poponbell);
4574cb4a1343Smrg	    break;
4575fa3f02f3Smrg	case srm_TITE_INHIBIT:
4576cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
4577cb4a1343Smrg	    break;
4578cb4a1343Smrg#if OPT_TCAP_FKEYS
4579fa3f02f3Smrg	case srm_TCAP_FKEYS:
4580cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
4581cb4a1343Smrg	    break;
4582cb4a1343Smrg#endif
4583cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
4584fa3f02f3Smrg	case srm_SUN_FKEYS:
4585cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
4586cb4a1343Smrg	    break;
4587cb4a1343Smrg#endif
4588cb4a1343Smrg#if OPT_HP_FUNC_KEYS
4589fa3f02f3Smrg	case srm_HP_FKEYS:
4590cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
4591cb4a1343Smrg	    break;
4592cb4a1343Smrg#endif
4593cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
4594fa3f02f3Smrg	case srm_SCO_FKEYS:
4595cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
4596cb4a1343Smrg	    break;
4597cb4a1343Smrg#endif
4598fa3f02f3Smrg	case srm_LEGACY_FKEYS:
4599cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
4600cb4a1343Smrg	    break;
4601cb4a1343Smrg#if OPT_SUNPC_KBD
4602fa3f02f3Smrg	case srm_VT220_FKEYS:
4603cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
4604cb4a1343Smrg	    break;
4605cb4a1343Smrg#endif
4606cb4a1343Smrg#if OPT_READLINE
4607fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
4608cb4a1343Smrg	    result = MdBool(screen->click1_moves);
4609cb4a1343Smrg	    break;
4610fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
4611cb4a1343Smrg	    result = MdBool(screen->paste_moves);
4612cb4a1343Smrg	    break;
4613fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
4614cb4a1343Smrg	    result = MdBool(screen->dclick3_deletes);
4615cb4a1343Smrg	    break;
4616fa3f02f3Smrg	case srm_PASTE_IN_BRACKET:
4617cb4a1343Smrg	    result = MdBool(screen->paste_brackets);
4618cb4a1343Smrg	    break;
4619fa3f02f3Smrg	case srm_PASTE_QUOTE:
4620cb4a1343Smrg	    result = MdBool(screen->paste_quotes);
4621cb4a1343Smrg	    break;
4622fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
4623cb4a1343Smrg	    result = MdBool(screen->paste_literal_nl);
4624cb4a1343Smrg	    break;
4625cb4a1343Smrg#endif /* OPT_READLINE */
46269a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
46279a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
46289a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
46299a64e1c5Smrg	    break;
46309a64e1c5Smrg#endif
46319a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
46329a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
46339a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
46349a64e1c5Smrg	    break;
46359a64e1c5Smrg#endif
46369a64e1c5Smrg	default:
46379a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
46389a64e1c5Smrg		   params[0]));
4639cb4a1343Smrg	}
4640cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4641cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4642cb4a1343Smrg    }
4643cb4a1343Smrg    reply.a_type = ANSI_CSI;
4644cb4a1343Smrg    reply.a_pintro = '?';
4645cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4646cb4a1343Smrg    reply.a_inters = '$';
4647cb4a1343Smrg    reply.a_final = 'y';
4648cb4a1343Smrg    unparseseq(xw, &reply);
4649cb4a1343Smrg}
4650cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
4651cb4a1343Smrg
4652d522f475Smrgchar *
46539a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
4654d522f475Smrg{
4655d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
46569a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
46579a64e1c5Smrg	return xw->work.user_keys[keycode].str;
4658d522f475Smrg    }
4659d522f475Smrg    return 0;
4660d522f475Smrg}
4661d522f475Smrg
46623367019cSmrg#ifdef HAVE_LIBXPM
46633367019cSmrg
46643367019cSmrg#ifndef PIXMAP_ROOTDIR
46653367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
46663367019cSmrg#endif
46673367019cSmrg
46683367019cSmrgtypedef struct {
46693367019cSmrg    const char *name;
46703367019cSmrg    const char *const *data;
46713367019cSmrg} XPM_DATA;
46723367019cSmrg
46733367019cSmrgstatic char *
46743367019cSmrgx_find_icon(char **work, int *state, const char *suffix)
46753367019cSmrg{
46763367019cSmrg    const char *filename = resource.icon_hint;
46773367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
46783367019cSmrg    const char *larger = "_48x48";
46793367019cSmrg    char *result = 0;
46803367019cSmrg
46813367019cSmrg    if (*state >= 0) {
46823367019cSmrg	if ((*state & 1) == 0)
46833367019cSmrg	    suffix = "";
46843367019cSmrg	if ((*state & 2) == 0)
46853367019cSmrg	    larger = "";
46863367019cSmrg	if ((*state & 4) == 0) {
46873367019cSmrg	    prefix = "";
46883367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
46893367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
46903367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
46913367019cSmrg	    *state = -1;
46923367019cSmrg	} else if (*state >= 8) {
46933367019cSmrg	    *state = -1;
46943367019cSmrg	}
46953367019cSmrg    }
46963367019cSmrg
46973367019cSmrg    if (*state >= 0) {
4698037a25ddSmrg	size_t length;
4699037a25ddSmrg
47003367019cSmrg	if (*work) {
47013367019cSmrg	    free(*work);
47023367019cSmrg	    *work = 0;
47033367019cSmrg	}
47043367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
47053367019cSmrg	    strlen(suffix);
47063367019cSmrg	if ((result = malloc(length)) != 0) {
47073367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
47083367019cSmrg	    *work = result;
47093367019cSmrg	}
47103367019cSmrg	*state += 1;
47113367019cSmrg	TRACE(("x_find_icon %d:%s\n", *state, result));
47123367019cSmrg    }
47133367019cSmrg    return result;
47143367019cSmrg}
47153367019cSmrg
47163367019cSmrg#if OPT_BUILTIN_XPMS
47173367019cSmrgstatic const XPM_DATA *
47183367019cSmrgBuiltInXPM(const XPM_DATA * table, Cardinal length)
47193367019cSmrg{
47203367019cSmrg    const char *find = resource.icon_hint;
47213367019cSmrg    const XPM_DATA *result = 0;
47223367019cSmrg    if (!IsEmpty(find)) {
47233367019cSmrg	Cardinal n;
47243367019cSmrg	for (n = 0; n < length; ++n) {
47253367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
47263367019cSmrg		result = table + n;
47273367019cSmrg		break;
47283367019cSmrg	    }
47293367019cSmrg	}
47303367019cSmrg
47313367019cSmrg	/*
47323367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
47333367019cSmrg	 * which are all _HHxWW format.
47343367019cSmrg	 */
47353367019cSmrg	if (result == 0) {
47363367019cSmrg	    const char *base = table[0].name;
47373367019cSmrg	    const char *last = strchr(base, '_');
47383367019cSmrg	    if (last != 0
47393367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
47403367019cSmrg		result = table + length - 1;
47413367019cSmrg	    }
47423367019cSmrg	}
47433367019cSmrg    }
47443367019cSmrg    return result;
47453367019cSmrg}
47463367019cSmrg#endif /* OPT_BUILTIN_XPMS */
47473367019cSmrg
47483367019cSmrgtypedef enum {
47493367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
47503367019cSmrg    ,eHintNone
47513367019cSmrg    ,eHintSearch
47523367019cSmrg} ICON_HINT;
47533367019cSmrg
47543367019cSmrgstatic ICON_HINT
47553367019cSmrgwhich_icon_hint(void)
47563367019cSmrg{
47573367019cSmrg    ICON_HINT result = eHintDefault;
47583367019cSmrg    if (!IsEmpty(resource.icon_hint)) {
47593367019cSmrg	if (!x_strcasecmp(resource.icon_hint, "none")) {
47603367019cSmrg	    result = eHintNone;
47613367019cSmrg	} else {
47623367019cSmrg	    result = eHintSearch;
47633367019cSmrg	}
47643367019cSmrg    }
47653367019cSmrg    return result;
47663367019cSmrg}
47673367019cSmrg#endif /* HAVE_LIBXPM */
47683367019cSmrg
47693367019cSmrgint
47703367019cSmrggetVisualDepth(XtermWidget xw)
47713367019cSmrg{
47723367019cSmrg    int result = 0;
47733367019cSmrg
4774fa3f02f3Smrg    if (getVisualInfo(xw)) {
4775fa3f02f3Smrg	result = xw->visInfo->depth;
47763367019cSmrg    }
47773367019cSmrg    return result;
47783367019cSmrg}
47793367019cSmrg
47803367019cSmrg/*
47813367019cSmrg * WM_ICON_SIZE should be honored if possible.
47823367019cSmrg */
47833367019cSmrgvoid
47843367019cSmrgxtermLoadIcon(XtermWidget xw)
47853367019cSmrg{
47863367019cSmrg#ifdef HAVE_LIBXPM
47873367019cSmrg    Display *dpy = XtDisplay(xw);
47883367019cSmrg    Pixmap myIcon = 0;
47893367019cSmrg    Pixmap myMask = 0;
47903367019cSmrg    char *workname = 0;
47913367019cSmrg    ICON_HINT hint = which_icon_hint();
4792fa3f02f3Smrg#include <builtin_icons.h>
47933367019cSmrg
47943367019cSmrg    TRACE(("xtermLoadIcon %p:%s\n", (void *) xw, NonNull(resource.icon_hint)));
47953367019cSmrg
47963367019cSmrg    if (hint == eHintSearch) {
47973367019cSmrg	int state = 0;
47983367019cSmrg	while (x_find_icon(&workname, &state, ".xpm") != 0) {
47993367019cSmrg	    Pixmap resIcon = 0;
48003367019cSmrg	    Pixmap shapemask = 0;
48013367019cSmrg	    XpmAttributes attributes;
48023367019cSmrg
48033367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
48043367019cSmrg	    attributes.valuemask = XpmDepth;
48053367019cSmrg
48063367019cSmrg	    if (XpmReadFileToPixmap(dpy,
48073367019cSmrg				    DefaultRootWindow(dpy),
48083367019cSmrg				    workname,
48093367019cSmrg				    &resIcon,
48103367019cSmrg				    &shapemask,
48113367019cSmrg				    &attributes) == XpmSuccess) {
48123367019cSmrg		myIcon = resIcon;
48133367019cSmrg		myMask = shapemask;
48143367019cSmrg		TRACE(("...success\n"));
48153367019cSmrg		break;
48163367019cSmrg	    }
48173367019cSmrg	}
48183367019cSmrg    }
48193367019cSmrg
48203367019cSmrg    /*
48213367019cSmrg     * If no external file was found, look for the name in the built-in table.
48223367019cSmrg     * If that fails, just use the biggest mini-icon.
48233367019cSmrg     */
48243367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
48253367019cSmrg	char **data;
48263367019cSmrg#if OPT_BUILTIN_XPMS
48273367019cSmrg	const XPM_DATA *myData = 0;
48283367019cSmrg	myData = BuiltInXPM(mini_xterm_xpms, XtNumber(mini_xterm_xpms));
48293367019cSmrg	if (myData == 0)
48303367019cSmrg	    myData = BuiltInXPM(filled_xterm_xpms, XtNumber(filled_xterm_xpms));
48313367019cSmrg	if (myData == 0)
48323367019cSmrg	    myData = BuiltInXPM(xterm_color_xpms, XtNumber(xterm_color_xpms));
48333367019cSmrg	if (myData == 0)
48343367019cSmrg	    myData = BuiltInXPM(xterm_xpms, XtNumber(xterm_xpms));
48353367019cSmrg	if (myData == 0)
48363367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
483794644356Smrg	data = (char **) myData->data;
48383367019cSmrg#else
48393367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
48403367019cSmrg#endif
48413367019cSmrg	if (XpmCreatePixmapFromData(dpy,
48423367019cSmrg				    DefaultRootWindow(dpy),
48433367019cSmrg				    data,
48443367019cSmrg				    &myIcon, &myMask, 0) != 0) {
48453367019cSmrg	    myIcon = 0;
48463367019cSmrg	    myMask = 0;
48473367019cSmrg	}
48483367019cSmrg    }
48493367019cSmrg
48503367019cSmrg    if (myIcon != 0) {
48513367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
48523367019cSmrg	if (!hints)
48533367019cSmrg	    hints = XAllocWMHints();
48543367019cSmrg
48553367019cSmrg	if (hints) {
48563367019cSmrg	    hints->flags |= IconPixmapHint;
48573367019cSmrg	    hints->icon_pixmap = myIcon;
48583367019cSmrg	    if (myMask) {
48593367019cSmrg		hints->flags |= IconMaskHint;
48603367019cSmrg		hints->icon_mask = myMask;
48613367019cSmrg	    }
48623367019cSmrg
48633367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
48643367019cSmrg	    XFree(hints);
48653367019cSmrg	    TRACE(("...loaded icon\n"));
48663367019cSmrg	}
48673367019cSmrg    }
48683367019cSmrg
48693367019cSmrg    if (workname != 0)
48703367019cSmrg	free(workname);
48713367019cSmrg
48723367019cSmrg#else
48733367019cSmrg    (void) xw;
48743367019cSmrg#endif
48753367019cSmrg}
48763367019cSmrg
48773367019cSmrgvoid
4878cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
4879d522f475Smrg{
4880d522f475Smrg#if OPT_WIDE_CHARS
4881d522f475Smrg    static Char *converted;	/* NO_LEAKS */
4882d522f475Smrg#endif
4883d522f475Smrg
4884d522f475Smrg    Arg args[1];
4885cd3331d0Smrg    Boolean changed = True;
4886d522f475Smrg    Widget w = CURRENT_EMU();
4887d522f475Smrg    Widget top = SHELL_OF(w);
4888d522f475Smrg
4889cd3331d0Smrg    char *my_attr;
4890cd3331d0Smrg    char *name;
4891cd3331d0Smrg    size_t limit;
4892cd3331d0Smrg    Char *c1;
4893cd3331d0Smrg    Char *cp;
4894d522f475Smrg
4895b7c89284Ssnj    if (!AllowTitleOps(xw))
4896d522f475Smrg	return;
4897d522f475Smrg
4898cd3331d0Smrg    if (value == 0)
48993367019cSmrg	value = emptyString;
4900cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
4901cd3331d0Smrg	const char *temp;
4902cd3331d0Smrg	char *test;
4903cd3331d0Smrg
4904cd3331d0Smrg	value = x_decode_hex(value, &temp);
49053367019cSmrg	if (*temp != '\0') {
49063367019cSmrg	    free(value);
4907cd3331d0Smrg	    return;
49083367019cSmrg	}
4909cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
4910cd3331d0Smrg	    if (CharOf(*test) < 32) {
4911cd3331d0Smrg		*test = '\0';
4912cd3331d0Smrg		break;
4913cd3331d0Smrg	    }
4914cd3331d0Smrg	}
4915cd3331d0Smrg    }
4916cd3331d0Smrg
4917cd3331d0Smrg    c1 = (Char *) value;
4918cd3331d0Smrg    name = value;
4919cd3331d0Smrg    limit = strlen(name);
4920cd3331d0Smrg    my_attr = x_strdup(attribute);
4921cd3331d0Smrg
4922cd3331d0Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
4923cd3331d0Smrg
4924d522f475Smrg    /*
4925d522f475Smrg     * Ignore titles that are too long to be plausible requests.
4926d522f475Smrg     */
4927cd3331d0Smrg    if (limit > 0 && limit < 1024) {
4928d522f475Smrg
4929cd3331d0Smrg	/*
4930cd3331d0Smrg	 * After all decoding, overwrite nonprintable characters with '?'.
4931cd3331d0Smrg	 */
4932cd3331d0Smrg	for (cp = c1; *cp != 0; ++cp) {
4933cd3331d0Smrg	    Char *c2 = cp;
4934cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
4935cd3331d0Smrg		memset(c2, '?', (size_t) (cp + 1 - c2));
4936cd3331d0Smrg	    }
4937d522f475Smrg	}
4938d522f475Smrg
4939d522f475Smrg#if OPT_WIDE_CHARS
4940cd3331d0Smrg	/*
4941cd3331d0Smrg	 * If we're running in UTF-8 mode, and have not been told that the
4942cd3331d0Smrg	 * title string is in UTF-8, it is likely that non-ASCII text in the
4943cd3331d0Smrg	 * string will be rejected because it is not printable in the current
4944cd3331d0Smrg	 * locale.  So we convert it to UTF-8, allowing the X library to
4945cd3331d0Smrg	 * convert it back.
4946cd3331d0Smrg	 */
4947cd3331d0Smrg	if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) {
4948cd3331d0Smrg	    int n;
4949cd3331d0Smrg
4950cd3331d0Smrg	    for (n = 0; name[n] != '\0'; ++n) {
4951cd3331d0Smrg		if (CharOf(name[n]) > 127) {
4952cd3331d0Smrg		    if (converted != 0)
4953cd3331d0Smrg			free(converted);
4954cd3331d0Smrg		    if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
4955cd3331d0Smrg			Char *temp = converted;
4956cd3331d0Smrg			while (*name != 0) {
4957cd3331d0Smrg			    temp = convertToUTF8(temp, CharOf(*name));
4958cd3331d0Smrg			    ++name;
4959cd3331d0Smrg			}
4960cd3331d0Smrg			*temp = 0;
4961cd3331d0Smrg			name = (char *) converted;
4962cd3331d0Smrg			TRACE(("...converted{%s}\n", name));
4963d522f475Smrg		    }
4964cd3331d0Smrg		    break;
4965d522f475Smrg		}
4966d522f475Smrg	    }
4967d522f475Smrg	}
4968d522f475Smrg#endif
4969d522f475Smrg
4970d522f475Smrg#if OPT_SAME_NAME
4971cd3331d0Smrg	/* If the attribute isn't going to change, then don't bother... */
4972cd3331d0Smrg
4973cd3331d0Smrg	if (resource.sameName) {
4974cd3331d0Smrg	    char *buf = 0;
4975cd3331d0Smrg	    XtSetArg(args[0], my_attr, &buf);
4976cd3331d0Smrg	    XtGetValues(top, args, 1);
4977cd3331d0Smrg	    TRACE(("...comparing{%s}\n", buf));
4978cd3331d0Smrg	    if (buf != 0 && strcmp(name, buf) == 0)
4979cd3331d0Smrg		changed = False;
4980cd3331d0Smrg	}
4981d522f475Smrg#endif /* OPT_SAME_NAME */
4982d522f475Smrg
4983cd3331d0Smrg	if (changed) {
4984cd3331d0Smrg	    TRACE(("...updating %s\n", my_attr));
4985cd3331d0Smrg	    TRACE(("...value is %s\n", name));
4986cd3331d0Smrg	    XtSetArg(args[0], my_attr, name);
4987cd3331d0Smrg	    XtSetValues(top, args, 1);
4988d522f475Smrg
4989d522f475Smrg#if OPT_WIDE_CHARS
4990cd3331d0Smrg	    if (xtermEnvUTF8()) {
4991cd3331d0Smrg		Display *dpy = XtDisplay(xw);
4992cd3331d0Smrg		Atom my_atom;
4993cd3331d0Smrg
4994cd3331d0Smrg		const char *propname = (!strcmp(my_attr, XtNtitle)
4995cd3331d0Smrg					? "_NET_WM_NAME"
4996cd3331d0Smrg					: "_NET_WM_ICON_NAME");
4997cd3331d0Smrg		if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
4998cd3331d0Smrg		    if (IsSetUtf8Title(xw)) {
4999cd3331d0Smrg			TRACE(("...updating %s\n", propname));
5000cd3331d0Smrg			TRACE(("...value is %s\n", value));
5001c219fbebSmrg			XChangeProperty(dpy, VShellWindow(xw), my_atom,
5002cd3331d0Smrg					XA_UTF8_STRING(dpy), 8,
5003cd3331d0Smrg					PropModeReplace,
5004cd3331d0Smrg					(Char *) value,
5005cd3331d0Smrg					(int) strlen(value));
5006cd3331d0Smrg		    } else {
5007cd3331d0Smrg			TRACE(("...deleting %s\n", propname));
5008c219fbebSmrg			XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5009cd3331d0Smrg		    }
5010cd3331d0Smrg		}
5011d522f475Smrg	    }
5012cd3331d0Smrg#endif
5013d522f475Smrg	}
5014d522f475Smrg    }
5015037a25ddSmrg    if (IsTitleMode(xw, tmSetBase16) && (value != emptyString)) {
50163367019cSmrg	free(value);
50173367019cSmrg    }
50183367019cSmrg    free(my_attr);
50193367019cSmrg
5020cd3331d0Smrg    return;
5021d522f475Smrg}
5022d522f475Smrg
5023d522f475Smrgvoid
5024b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
5025d522f475Smrg{
5026cd3331d0Smrg    if (name == 0) {
50273367019cSmrg	name = emptyString;
50283367019cSmrg    }
50293367019cSmrg    if (!showZIconBeep(xw, name))
5030b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
5031d522f475Smrg}
5032d522f475Smrg
5033d522f475Smrgvoid
5034b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
5035d522f475Smrg{
5036b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
5037d522f475Smrg}
5038d522f475Smrg
5039712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
5040d522f475Smrg
5041d522f475Smrgvoid
5042d522f475SmrgChangeXprop(char *buf)
5043d522f475Smrg{
5044d522f475Smrg    Display *dpy = XtDisplay(toplevel);
5045d522f475Smrg    Window w = XtWindow(toplevel);
5046d522f475Smrg    XTextProperty text_prop;
5047d522f475Smrg    Atom aprop;
5048d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
5049d522f475Smrg
5050d522f475Smrg    if (pchEndPropName)
5051d522f475Smrg	*pchEndPropName = '\0';
5052d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
5053d522f475Smrg    if (pchEndPropName == NULL) {
5054d522f475Smrg	/* no "=value" given, so delete the property */
5055d522f475Smrg	XDeleteProperty(dpy, w, aprop);
5056d522f475Smrg    } else {
5057d522f475Smrg	text_prop.value = pchEndPropName + 1;
5058d522f475Smrg	text_prop.encoding = XA_STRING;
5059d522f475Smrg	text_prop.format = 8;
5060d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
5061d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
5062d522f475Smrg    }
5063d522f475Smrg}
5064d522f475Smrg
5065d522f475Smrg/***====================================================================***/
5066d522f475Smrg
5067d522f475Smrg/*
5068d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
5069d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
5070d522f475Smrg */
5071d522f475Smrgvoid
50729a64e1c5SmrgReverseOldColors(XtermWidget xw)
5073d522f475Smrg{
50749a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
5075d522f475Smrg    Pixel tmpPix;
5076d522f475Smrg    char *tmpName;
5077d522f475Smrg
5078d522f475Smrg    if (pOld) {
5079d522f475Smrg	/* change text cursor, if necesary */
5080d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
5081d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
5082d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
50839a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
5084d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
5085d522f475Smrg	    }
5086d522f475Smrg	    if (pOld->names[TEXT_BG]) {
5087d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
5088d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
5089d522f475Smrg		}
5090d522f475Smrg	    }
5091d522f475Smrg	}
5092d522f475Smrg
5093d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
5094d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
5095d522f475Smrg
5096d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
5097d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
5098d522f475Smrg
5099d522f475Smrg#if OPT_TEK4014
5100d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
5101d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
5102d522f475Smrg#endif
5103d522f475Smrg    }
5104d522f475Smrg    return;
5105d522f475Smrg}
5106d522f475Smrg
5107d522f475SmrgBool
5108d522f475SmrgAllocateTermColor(XtermWidget xw,
5109d522f475Smrg		  ScrnColors * pNew,
5110d522f475Smrg		  int ndx,
5111cd3331d0Smrg		  const char *name,
5112cd3331d0Smrg		  Bool always)
5113d522f475Smrg{
5114cd3331d0Smrg    Bool result = False;
5115d522f475Smrg
5116cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
5117cd3331d0Smrg	XColor def;
5118cd3331d0Smrg	char *newName;
5119cd3331d0Smrg
5120712a7ff4Smrg	result = True;
5121712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
5122712a7ff4Smrg	    def.pixel = xw->old_foreground;
5123712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
5124712a7ff4Smrg	    def.pixel = xw->old_background;
51253367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
5126712a7ff4Smrg	    result = False;
5127712a7ff4Smrg	}
5128712a7ff4Smrg
5129712a7ff4Smrg	if (result
5130cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
5131712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
5132cd3331d0Smrg		free(pNew->names[ndx]);
5133712a7ff4Smrg	    }
5134cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
5135cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
5136712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
5137712a7ff4Smrg		   ndx, newName, def.pixel));
5138cd3331d0Smrg	} else {
5139cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
5140712a7ff4Smrg	    result = False;
5141cd3331d0Smrg	}
5142cd3331d0Smrg    }
5143cd3331d0Smrg    return result;
5144d522f475Smrg}
5145d522f475Smrg/***====================================================================***/
5146d522f475Smrg
5147d522f475Smrg/* ARGSUSED */
5148d522f475Smrgvoid
5149cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
5150d522f475Smrg{
51513367019cSmrg    if_DEBUG({
51523367019cSmrg	xtermWarning(s, a);
51533367019cSmrg    });
5154d522f475Smrg}
5155d522f475Smrg
5156d522f475Smrgconst char *
5157d522f475SmrgSysErrorMsg(int code)
5158d522f475Smrg{
515994644356Smrg    static const char unknown[] = "unknown error";
5160d522f475Smrg    char *s = strerror(code);
5161d522f475Smrg    return s ? s : unknown;
5162d522f475Smrg}
5163d522f475Smrg
5164d522f475Smrgconst char *
5165d522f475SmrgSysReasonMsg(int code)
5166d522f475Smrg{
5167d522f475Smrg    /* *INDENT-OFF* */
5168d522f475Smrg    static const struct {
5169d522f475Smrg	int code;
5170d522f475Smrg	const char *name;
5171d522f475Smrg    } table[] = {
5172d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
5173d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
5174d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
5175d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
5176d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
5177d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
5178d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
5179d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
5180d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
5181d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
5182d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
5183d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
5184d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
5185d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
5186d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
5187d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
5188d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
5189d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
5190d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
5191d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
5192d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
5193d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
5194d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
5195d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
5196d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
5197d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
5198d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
5199d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
5200d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
5201d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
5202d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
5203d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
5204d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
5205d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
5206d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
5207d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
5208d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
5209d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
5210d522f475Smrg    };
5211d522f475Smrg    /* *INDENT-ON* */
5212d522f475Smrg
5213d522f475Smrg    Cardinal n;
5214d522f475Smrg    const char *result = "?";
5215d522f475Smrg
5216d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
5217d522f475Smrg	if (code == table[n].code) {
5218d522f475Smrg	    result = table[n].name;
5219d522f475Smrg	    break;
5220d522f475Smrg	}
5221d522f475Smrg    }
5222d522f475Smrg    return result;
5223d522f475Smrg}
5224d522f475Smrg
5225d522f475Smrgvoid
5226d522f475SmrgSysError(int code)
5227d522f475Smrg{
5228d522f475Smrg    int oerrno = errno;
5229d522f475Smrg
5230c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
5231d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
5232d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
5233d522f475Smrg
5234d522f475Smrg    Cleanup(code);
5235d522f475Smrg}
5236d522f475Smrg
5237d522f475Smrgvoid
52383367019cSmrgNormalExit(void)
5239d522f475Smrg{
5240d522f475Smrg    static Bool cleaning;
5241d522f475Smrg
5242d522f475Smrg    /*
5243d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
5244d522f475Smrg     */
52453367019cSmrg    if (cleaning) {
52463367019cSmrg	hold_screen = 0;
52473367019cSmrg	return;
52483367019cSmrg    }
5249d522f475Smrg
52503367019cSmrg    cleaning = True;
52513367019cSmrg    need_cleanup = False;
5252d522f475Smrg
52533367019cSmrg    if (hold_screen) {
52543367019cSmrg	hold_screen = 2;
52553367019cSmrg	while (hold_screen) {
52563367019cSmrg	    xevents();
52573367019cSmrg	    Sleep(10);
5258d522f475Smrg	}
52593367019cSmrg    }
5260d522f475Smrg#if OPT_SESSION_MGT
52613367019cSmrg    if (resource.sessionMgt) {
52623367019cSmrg	XtVaSetValues(toplevel,
52633367019cSmrg		      XtNjoinSession, False,
52643367019cSmrg		      (void *) 0);
5265d522f475Smrg    }
52663367019cSmrg#endif
52673367019cSmrg    Cleanup(0);
52683367019cSmrg}
52693367019cSmrg
52703367019cSmrg/*
52713367019cSmrg * cleanup by sending SIGHUP to client processes
52723367019cSmrg */
52733367019cSmrgvoid
52743367019cSmrgCleanup(int code)
52753367019cSmrg{
52763367019cSmrg    TScreen *screen = TScreenOf(term);
52773367019cSmrg
52783367019cSmrg    TRACE(("Cleanup %d\n", code));
5279d522f475Smrg
5280d522f475Smrg    if (screen->pid > 1) {
5281d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
5282d522f475Smrg    }
5283d522f475Smrg    Exit(code);
5284d522f475Smrg}
5285d522f475Smrg
5286fa3f02f3Smrg#ifndef S_IXOTH
5287fa3f02f3Smrg#define S_IXOTH 1
5288fa3f02f3Smrg#endif
5289fa3f02f3Smrg
5290fa3f02f3SmrgBoolean
5291fa3f02f3SmrgvalidProgram(const char *pathname)
5292fa3f02f3Smrg{
5293fa3f02f3Smrg    Boolean result = False;
5294fa3f02f3Smrg    struct stat sb;
5295fa3f02f3Smrg
5296fa3f02f3Smrg    if (!IsEmpty(pathname)
5297fa3f02f3Smrg	&& *pathname == '/'
5298fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
5299fa3f02f3Smrg	&& stat(pathname, &sb) == 0
5300fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
5301fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
5302fa3f02f3Smrg	result = True;
5303fa3f02f3Smrg    }
5304fa3f02f3Smrg    return result;
5305fa3f02f3Smrg}
5306fa3f02f3Smrg
5307d522f475Smrg#ifndef VMS
53083367019cSmrg#ifndef PATH_MAX
53093367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
53103367019cSmrg#endif
5311d522f475Smrgchar *
5312d522f475SmrgxtermFindShell(char *leaf, Bool warning)
5313d522f475Smrg{
53143367019cSmrg    char *s0;
5315d522f475Smrg    char *s;
5316d522f475Smrg    char *d;
5317d522f475Smrg    char *tmp;
5318d522f475Smrg    char *result = leaf;
53193367019cSmrg    Bool allocated = False;
5320d522f475Smrg
5321d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
53223367019cSmrg
53233367019cSmrg    if (!strncmp("./", result, (size_t) 2)
53243367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
53253367019cSmrg	size_t need = PATH_MAX;
53263367019cSmrg	size_t used = strlen(result) + 2;
53273367019cSmrg	char *buffer = malloc(used + need);
53283367019cSmrg	if (buffer != 0) {
53293367019cSmrg	    if (getcwd(buffer, need) != 0) {
53303367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
53313367019cSmrg		result = buffer;
53323367019cSmrg		allocated = True;
53333367019cSmrg	    } else {
53343367019cSmrg		free(buffer);
53353367019cSmrg	    }
53363367019cSmrg	}
53373367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
5338d522f475Smrg	/* find it in $PATH */
53393367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
53400d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
5341d522f475Smrg		Bool found = False;
5342d522f475Smrg		while (*s != '\0') {
5343d522f475Smrg		    strcpy(tmp, s);
5344d522f475Smrg		    for (d = tmp;; ++d) {
5345d522f475Smrg			if (*d == ':' || *d == '\0') {
5346d522f475Smrg			    int skip = (*d != '\0');
5347d522f475Smrg			    *d = '/';
5348d522f475Smrg			    strcpy(d + 1, leaf);
5349d522f475Smrg			    if (skip)
5350d522f475Smrg				++d;
5351d522f475Smrg			    s += (d - tmp);
5352fa3f02f3Smrg			    if (validProgram(tmp)) {
5353d522f475Smrg				result = x_strdup(tmp);
5354d522f475Smrg				found = True;
53553367019cSmrg				allocated = True;
5356d522f475Smrg			    }
5357d522f475Smrg			    break;
5358d522f475Smrg			}
5359d522f475Smrg		    }
5360d522f475Smrg		    if (found)
5361d522f475Smrg			break;
5362d522f475Smrg		}
5363d522f475Smrg		free(tmp);
5364d522f475Smrg	    }
53653367019cSmrg	    free(s0);
5366d522f475Smrg	}
5367d522f475Smrg    }
5368d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
5369fa3f02f3Smrg    if (!validProgram(result)) {
5370d522f475Smrg	if (warning)
53713367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
53723367019cSmrg	if (allocated)
53733367019cSmrg	    free(result);
5374d522f475Smrg	result = 0;
5375d522f475Smrg    }
53763367019cSmrg    /* be consistent, so that caller can always free the result */
53773367019cSmrg    if (result != 0 && !allocated)
53783367019cSmrg	result = x_strdup(result);
5379d522f475Smrg    return result;
5380d522f475Smrg}
5381d522f475Smrg#endif /* VMS */
5382d522f475Smrg
53830d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
5384d522f475Smrg
53853367019cSmrg/*
53863367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
53873367019cSmrg * This could happen on some older machines due to the uneven standardization
53883367019cSmrg * process for the two functions.
53893367019cSmrg *
53903367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
53913367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
53923367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
53933367019cSmrg * could copy environ.
53943367019cSmrg */
53953367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
53963367019cSmrg#undef HAVE_PUTENV
53973367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
53983367019cSmrg#undef HAVE_UNSETENV
53993367019cSmrg#endif
54003367019cSmrg
5401d522f475Smrg/*
5402d522f475Smrg * copy the environment before Setenv'ing.
5403d522f475Smrg */
5404d522f475Smrgvoid
5405d522f475SmrgxtermCopyEnv(char **oldenv)
5406d522f475Smrg{
54073367019cSmrg#ifdef HAVE_PUTENV
54083367019cSmrg    (void) oldenv;
54093367019cSmrg#else
5410d522f475Smrg    unsigned size;
5411d522f475Smrg    char **newenv;
5412d522f475Smrg
5413d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
5414d522f475Smrg	;
5415d522f475Smrg    }
5416d522f475Smrg
5417d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
5418d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
5419d522f475Smrg    environ = newenv;
54203367019cSmrg#endif
54213367019cSmrg}
54223367019cSmrg
54233367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
54243367019cSmrgstatic int
54253367019cSmrgfindEnv(const char *var, int *lengthp)
54263367019cSmrg{
54273367019cSmrg    char *test;
54283367019cSmrg    int envindex = 0;
54293367019cSmrg    size_t len = strlen(var);
54303367019cSmrg    int found = -1;
54313367019cSmrg
54323367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
54333367019cSmrg
54343367019cSmrg    while ((test = environ[envindex]) != NULL) {
54353367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
54363367019cSmrg	    found = envindex;
54373367019cSmrg	    break;
54383367019cSmrg	}
54393367019cSmrg	envindex++;
54403367019cSmrg    }
54413367019cSmrg    *lengthp = envindex;
54423367019cSmrg    return found;
5443d522f475Smrg}
54443367019cSmrg#endif
5445d522f475Smrg
5446d522f475Smrg/*
5447d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
5448d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
5449d522f475Smrg * This procedure assumes the memory for the first level of environ
5450d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
5451d522f475Smrg * to have to do a realloc().
5452d522f475Smrg */
5453d522f475Smrgvoid
5454cd3331d0SmrgxtermSetenv(const char *var, const char *value)
5455d522f475Smrg{
5456d522f475Smrg    if (value != 0) {
54573367019cSmrg#ifdef HAVE_PUTENV
54583367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
54593367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
54603367019cSmrg	if (both) {
54613367019cSmrg	    sprintf(both, "%s=%s", var, value);
54623367019cSmrg	    putenv(both);
54633367019cSmrg	}
54643367019cSmrg#else
5465d522f475Smrg	size_t len = strlen(var);
54663367019cSmrg	int envindex;
54673367019cSmrg	int found = findEnv(var, &envindex);
5468d522f475Smrg
5469d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
5470d522f475Smrg
5471d522f475Smrg	if (found < 0) {
5472d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
5473d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
5474d522f475Smrg
5475d522f475Smrg	    if (need > have) {
5476d522f475Smrg		char **newenv;
5477d522f475Smrg		newenv = TypeMallocN(char *, need);
5478d522f475Smrg		if (newenv == 0) {
54793367019cSmrg		    xtermWarning("Cannot increase environment\n");
5480d522f475Smrg		    return;
5481d522f475Smrg		}
5482d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
5483d522f475Smrg		free(environ);
5484d522f475Smrg		environ = newenv;
5485d522f475Smrg	    }
5486d522f475Smrg
5487d522f475Smrg	    found = envindex;
5488d522f475Smrg	    environ[found + 1] = NULL;
5489d522f475Smrg	    environ = environ;
5490d522f475Smrg	}
5491d522f475Smrg
5492d522f475Smrg	environ[found] = CastMallocN(char, 1 + len + strlen(value));
5493d522f475Smrg	if (environ[found] == 0) {
54943367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
5495d522f475Smrg	    return;
5496d522f475Smrg	}
5497d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
54983367019cSmrg#endif
5499d522f475Smrg    }
5500d522f475Smrg}
5501d522f475Smrg
55023367019cSmrgvoid
55033367019cSmrgxtermUnsetenv(const char *var)
55043367019cSmrg{
55053367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
55063367019cSmrg#ifdef HAVE_UNSETENV
55073367019cSmrg    unsetenv(var);
55083367019cSmrg#else
55093367019cSmrg    {
55103367019cSmrg	int ignore;
55113367019cSmrg	int item = findEnv(var, &ignore);
55123367019cSmrg	if (item >= 0) {
55133367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
55143367019cSmrg		++item;
55153367019cSmrg	    }
55163367019cSmrg	}
55173367019cSmrg    }
55183367019cSmrg#endif
55193367019cSmrg}
55203367019cSmrg
5521d522f475Smrg/*ARGSUSED*/
5522d522f475Smrgint
55239a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
5524d522f475Smrg{
55253367019cSmrg    xtermWarning("warning, error event received:\n");
5526d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
5527d522f475Smrg    Exit(ERROR_XERROR);
5528d522f475Smrg    return 0;			/* appease the compiler */
5529d522f475Smrg}
5530d522f475Smrg
5531712a7ff4Smrgvoid
5532712a7ff4Smrgice_error(IceConn iceConn)
5533712a7ff4Smrg{
5534712a7ff4Smrg    (void) iceConn;
5535712a7ff4Smrg
55363367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
55373367019cSmrg		 (long) getpid(), errno);
5538712a7ff4Smrg
5539712a7ff4Smrg    Exit(ERROR_ICEERROR);
5540712a7ff4Smrg}
5541712a7ff4Smrg
5542d522f475Smrg/*ARGSUSED*/
5543d522f475Smrgint
5544fa3f02f3Smrgxioerror(Display *dpy)
5545d522f475Smrg{
5546d522f475Smrg    int the_error = errno;
5547d522f475Smrg
55483367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
55493367019cSmrg		 the_error, SysErrorMsg(the_error),
55503367019cSmrg		 DisplayString(dpy));
5551d522f475Smrg
5552d522f475Smrg    Exit(ERROR_XIOERROR);
5553d522f475Smrg    return 0;			/* appease the compiler */
5554d522f475Smrg}
5555d522f475Smrg
5556d522f475Smrgvoid
5557d522f475Smrgxt_error(String message)
5558d522f475Smrg{
55593367019cSmrg    xtermWarning("Xt error: %s\n", message);
5560d522f475Smrg
5561d522f475Smrg    /*
5562d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
5563d522f475Smrg     */
5564d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
55653367019cSmrg	xtermWarning("DISPLAY is not set\n");
5566d522f475Smrg    }
5567d522f475Smrg    exit(1);
5568d522f475Smrg}
5569d522f475Smrg
5570d522f475Smrgint
5571d522f475SmrgXStrCmp(char *s1, char *s2)
5572d522f475Smrg{
5573d522f475Smrg    if (s1 && s2)
5574d522f475Smrg	return (strcmp(s1, s2));
5575d522f475Smrg    if (s1 && *s1)
5576d522f475Smrg	return (1);
5577d522f475Smrg    if (s2 && *s2)
5578d522f475Smrg	return (-1);
5579d522f475Smrg    return (0);
5580d522f475Smrg}
5581d522f475Smrg
5582d522f475Smrg#if OPT_TEK4014
5583d522f475Smrgstatic void
5584fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
5585d522f475Smrg{
5586d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
5587d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
5588d522f475Smrg    XWithdrawWindow(dpy, w, scr);
5589d522f475Smrg    return;
5590d522f475Smrg}
5591d522f475Smrg#endif
5592d522f475Smrg
5593d522f475Smrgvoid
5594d522f475Smrgset_vt_visibility(Bool on)
5595d522f475Smrg{
5596c219fbebSmrg    XtermWidget xw = term;
5597c219fbebSmrg    TScreen *screen = TScreenOf(xw);
5598d522f475Smrg
5599d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
5600d522f475Smrg    if (on) {
5601c219fbebSmrg	if (!screen->Vshow && xw) {
5602c219fbebSmrg	    VTInit(xw);
5603c219fbebSmrg	    XtMapWidget(XtParent(xw));
5604d522f475Smrg#if OPT_TOOLBAR
5605d522f475Smrg	    /* we need both of these during initialization */
5606c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
5607d522f475Smrg	    ShowToolbar(resource.toolBar);
5608d522f475Smrg#endif
5609d522f475Smrg	    screen->Vshow = True;
5610d522f475Smrg	}
5611d522f475Smrg    }
5612d522f475Smrg#if OPT_TEK4014
5613d522f475Smrg    else {
5614c219fbebSmrg	if (screen->Vshow && xw) {
5615c219fbebSmrg	    withdraw_window(XtDisplay(xw),
5616c219fbebSmrg			    VShellWindow(xw),
5617c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
5618d522f475Smrg	    screen->Vshow = False;
5619d522f475Smrg	}
5620d522f475Smrg    }
5621d522f475Smrg    set_vthide_sensitivity();
5622d522f475Smrg    set_tekhide_sensitivity();
5623d522f475Smrg    update_vttekmode();
5624d522f475Smrg    update_tekshow();
5625d522f475Smrg    update_vtshow();
5626d522f475Smrg#endif
5627d522f475Smrg    return;
5628d522f475Smrg}
5629d522f475Smrg
5630d522f475Smrg#if OPT_TEK4014
5631d522f475Smrgvoid
5632d522f475Smrgset_tek_visibility(Bool on)
5633d522f475Smrg{
5634d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
5635d522f475Smrg
5636d522f475Smrg    if (on) {
5637cd3331d0Smrg	if (!TEK4014_SHOWN(term)) {
5638cd3331d0Smrg	    if (tekWidget == 0) {
5639cd3331d0Smrg		TekInit();	/* will exit on failure */
5640cd3331d0Smrg	    }
5641cd3331d0Smrg	    if (tekWidget != 0) {
5642cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
5643cd3331d0Smrg		XtRealizeWidget(tekParent);
5644cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
5645d522f475Smrg#if OPT_TOOLBAR
5646cd3331d0Smrg		/* we need both of these during initialization */
5647cd3331d0Smrg		XtMapWidget(tekParent);
5648cd3331d0Smrg		XtMapWidget(tekWidget);
5649d522f475Smrg#endif
5650cd3331d0Smrg		XtOverrideTranslations(tekParent,
5651cd3331d0Smrg				       XtParseTranslationTable
5652cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
5653cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
5654cd3331d0Smrg				       XtWindow(tekParent),
5655cd3331d0Smrg				       &wm_delete_window, 1);
5656cd3331d0Smrg		TEK4014_SHOWN(term) = True;
5657cd3331d0Smrg	    }
5658d522f475Smrg	}
5659d522f475Smrg    } else {
5660d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
5661d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
5662d522f475Smrg			    TShellWindow,
5663d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
5664d522f475Smrg	    TEK4014_SHOWN(term) = False;
5665d522f475Smrg	}
5666d522f475Smrg    }
5667d522f475Smrg    set_tekhide_sensitivity();
5668d522f475Smrg    set_vthide_sensitivity();
5669d522f475Smrg    update_vtshow();
5670d522f475Smrg    update_tekshow();
5671d522f475Smrg    update_vttekmode();
5672d522f475Smrg    return;
5673d522f475Smrg}
5674d522f475Smrg
5675d522f475Smrgvoid
5676d522f475Smrgend_tek_mode(void)
5677d522f475Smrg{
5678cd3331d0Smrg    XtermWidget xw = term;
5679cd3331d0Smrg
5680cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
5681cd3331d0Smrg	FlushLog(xw);
5682d522f475Smrg	longjmp(Tekend, 1);
5683d522f475Smrg    }
5684d522f475Smrg    return;
5685d522f475Smrg}
5686d522f475Smrg
5687d522f475Smrgvoid
5688d522f475Smrgend_vt_mode(void)
5689d522f475Smrg{
5690cd3331d0Smrg    XtermWidget xw = term;
5691cd3331d0Smrg
5692cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
5693cd3331d0Smrg	FlushLog(xw);
5694cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
5695d522f475Smrg	longjmp(VTend, 1);
5696d522f475Smrg    }
5697d522f475Smrg    return;
5698d522f475Smrg}
5699d522f475Smrg
5700d522f475Smrgvoid
5701d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
5702d522f475Smrg{
5703d522f475Smrg    if (tovt) {
5704d522f475Smrg	if (tekRefreshList)
5705d522f475Smrg	    TekRefresh(tekWidget);
5706d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
5707d522f475Smrg    } else {
5708d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
5709d522f475Smrg    }
5710d522f475Smrg}
5711d522f475Smrg
5712d522f475Smrgvoid
5713d522f475Smrghide_vt_window(void)
5714d522f475Smrg{
5715d522f475Smrg    set_vt_visibility(False);
5716d522f475Smrg    if (!TEK4014_ACTIVE(term))
5717d522f475Smrg	switch_modes(False);	/* switch to tek mode */
5718d522f475Smrg}
5719d522f475Smrg
5720d522f475Smrgvoid
5721d522f475Smrghide_tek_window(void)
5722d522f475Smrg{
5723d522f475Smrg    set_tek_visibility(False);
5724d522f475Smrg    tekRefreshList = (TekLink *) 0;
5725d522f475Smrg    if (TEK4014_ACTIVE(term))
5726d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
5727d522f475Smrg}
5728d522f475Smrg#endif /* OPT_TEK4014 */
5729d522f475Smrg
5730d522f475Smrgstatic const char *
5731d522f475Smrgskip_punct(const char *s)
5732d522f475Smrg{
5733d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
5734d522f475Smrg	++s;
5735d522f475Smrg    }
5736d522f475Smrg    return s;
5737d522f475Smrg}
5738d522f475Smrg
5739d522f475Smrgstatic int
5740d522f475Smrgcmp_options(const void *a, const void *b)
5741d522f475Smrg{
5742d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
5743d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
5744d522f475Smrg    return strcmp(s1, s2);
5745d522f475Smrg}
5746d522f475Smrg
5747d522f475Smrgstatic int
5748d522f475Smrgcmp_resources(const void *a, const void *b)
5749d522f475Smrg{
5750d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
5751d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
5752d522f475Smrg}
5753d522f475Smrg
5754d522f475SmrgXrmOptionDescRec *
5755d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
5756d522f475Smrg{
5757d522f475Smrg    static XrmOptionDescRec *res_array = 0;
5758d522f475Smrg
5759d522f475Smrg#ifdef NO_LEAKS
5760cd3331d0Smrg    if (descs == 0) {
5761cd3331d0Smrg	if (res_array != 0) {
5762cd3331d0Smrg	    free(res_array);
5763cd3331d0Smrg	    res_array = 0;
5764cd3331d0Smrg	}
5765d522f475Smrg    } else
5766d522f475Smrg#endif
5767d522f475Smrg    if (res_array == 0) {
5768d522f475Smrg	Cardinal j;
5769d522f475Smrg
5770d522f475Smrg	/* make a sorted index to 'resources' */
5771d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
5772cd3331d0Smrg	if (res_array != 0) {
5773cd3331d0Smrg	    for (j = 0; j < res_count; j++)
5774cd3331d0Smrg		res_array[j] = descs[j];
5775cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
5776cd3331d0Smrg	}
5777d522f475Smrg    }
5778d522f475Smrg    return res_array;
5779d522f475Smrg}
5780d522f475Smrg
5781d522f475Smrg/*
5782d522f475Smrg * The first time this is called, construct sorted index to the main program's
5783d522f475Smrg * list of options, taking into account the on/off options which will be
5784d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
5785d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
5786d522f475Smrg */
5787d522f475SmrgOptionHelp *
5788d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
5789d522f475Smrg{
5790d522f475Smrg    static OptionHelp *opt_array = 0;
5791d522f475Smrg
5792d522f475Smrg#ifdef NO_LEAKS
5793d522f475Smrg    if (descs == 0 && opt_array != 0) {
5794d522f475Smrg	sortedOptDescs(descs, numDescs);
5795d522f475Smrg	free(opt_array);
5796d522f475Smrg	opt_array = 0;
5797d522f475Smrg	return 0;
5798d522f475Smrg    } else if (options == 0 || descs == 0) {
5799d522f475Smrg	return 0;
5800d522f475Smrg    }
5801d522f475Smrg#endif
5802d522f475Smrg
5803d522f475Smrg    if (opt_array == 0) {
5804cd3331d0Smrg	size_t opt_count, j;
5805d522f475Smrg#if OPT_TRACE
5806d522f475Smrg	Cardinal k;
5807d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
5808d522f475Smrg	int code;
5809cd3331d0Smrg	const char *mesg;
5810d522f475Smrg#else
5811d522f475Smrg	(void) descs;
5812d522f475Smrg	(void) numDescs;
5813d522f475Smrg#endif
5814d522f475Smrg
5815d522f475Smrg	/* count 'options' and make a sorted index to it */
5816d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
5817d522f475Smrg	    ;
5818d522f475Smrg	}
5819d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
5820d522f475Smrg	for (j = 0; j < opt_count; j++)
5821d522f475Smrg	    opt_array[j] = options[j];
5822d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
5823d522f475Smrg
5824d522f475Smrg	/* supply the "turn on/off" strings if needed */
5825d522f475Smrg#if OPT_TRACE
5826d522f475Smrg	for (j = 0; j < opt_count; j++) {
5827712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
5828c219fbebSmrg		char temp[80];
5829cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
5830d522f475Smrg		for (k = 0; k < numDescs; ++k) {
5831cd3331d0Smrg		    const char *value = res_array[k].value;
5832d522f475Smrg		    if (res_array[k].option[0] == '-') {
5833d522f475Smrg			code = -1;
5834d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
5835d522f475Smrg			code = 1;
5836d522f475Smrg		    } else {
5837d522f475Smrg			code = 0;
5838d522f475Smrg		    }
58393367019cSmrg		    sprintf(temp, "%.*s",
58403367019cSmrg			    (int) sizeof(temp) - 2,
58413367019cSmrg			    opt_array[j].desc);
5842c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
5843d522f475Smrg			code = -code;
5844d522f475Smrg		    if (code != 0
5845d522f475Smrg			&& res_array[k].value != 0
5846d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
5847d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
5848d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
5849d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
5850d522f475Smrg			    mesg = "turn on/off";
5851d522f475Smrg			} else {
5852d522f475Smrg			    mesg = "turn off/on";
5853d522f475Smrg			}
5854d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
5855712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
5856d522f475Smrg				char *s = CastMallocN(char,
5857d522f475Smrg						      strlen(mesg)
5858d522f475Smrg						      + 1
5859d522f475Smrg						      + strlen(opt_array[j].desc));
5860d522f475Smrg				if (s != 0) {
5861d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
5862d522f475Smrg				    opt_array[j].desc = s;
5863d522f475Smrg				}
5864d522f475Smrg			    } else {
5865d522f475Smrg				TRACE(("OOPS "));
5866d522f475Smrg			    }
5867d522f475Smrg			}
5868d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
5869d522f475Smrg			       mesg,
5870d522f475Smrg			       res_array[k].option,
5871d522f475Smrg			       res_array[k].value,
5872d522f475Smrg			       opt_array[j].opt,
5873d522f475Smrg			       opt_array[j].desc));
5874d522f475Smrg			break;
5875d522f475Smrg		    }
5876d522f475Smrg		}
5877d522f475Smrg	    }
5878d522f475Smrg	}
5879d522f475Smrg#endif
5880d522f475Smrg    }
5881d522f475Smrg    return opt_array;
5882d522f475Smrg}
5883d522f475Smrg
5884d522f475Smrg/*
5885d522f475Smrg * Report the character-type locale that xterm was started in.
5886d522f475Smrg */
58873367019cSmrgString
5888d522f475SmrgxtermEnvLocale(void)
5889d522f475Smrg{
58903367019cSmrg    static String result;
5891d522f475Smrg
5892d522f475Smrg    if (result == 0) {
5893d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
5894cd3331d0Smrg	    result = x_strdup("C");
5895cd3331d0Smrg	} else {
5896cd3331d0Smrg	    result = x_strdup(result);
5897d522f475Smrg	}
5898d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
5899d522f475Smrg    }
5900d522f475Smrg    return result;
5901d522f475Smrg}
5902d522f475Smrg
5903d522f475Smrgchar *
5904d522f475SmrgxtermEnvEncoding(void)
5905d522f475Smrg{
5906d522f475Smrg    static char *result;
5907d522f475Smrg
5908d522f475Smrg    if (result == 0) {
5909d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5910d522f475Smrg	result = nl_langinfo(CODESET);
5911d522f475Smrg#else
5912d522f475Smrg	char *locale = xtermEnvLocale();
5913d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
5914d522f475Smrg	    result = "ASCII";
5915d522f475Smrg	} else {
5916d522f475Smrg	    result = "ISO-8859-1";
5917d522f475Smrg	}
5918d522f475Smrg#endif
5919d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
5920d522f475Smrg    }
5921d522f475Smrg    return result;
5922d522f475Smrg}
5923d522f475Smrg
5924d522f475Smrg#if OPT_WIDE_CHARS
5925d522f475Smrg/*
5926d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
5927d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
5928d522f475Smrg * various library calls.
5929d522f475Smrg */
5930d522f475SmrgBool
5931d522f475SmrgxtermEnvUTF8(void)
5932d522f475Smrg{
5933d522f475Smrg    static Bool init = False;
5934d522f475Smrg    static Bool result = False;
5935d522f475Smrg
5936d522f475Smrg    if (!init) {
5937d522f475Smrg	init = True;
5938d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5939d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
5940d522f475Smrg#else
5941fa3f02f3Smrg	{
5942fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
5943fa3f02f3Smrg	    int n;
5944fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
5945fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
5946fa3f02f3Smrg	    }
5947fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
5948fa3f02f3Smrg		result = True;
5949fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
5950fa3f02f3Smrg		result = True;
5951fa3f02f3Smrg	    free(locale);
5952fa3f02f3Smrg	}
5953d522f475Smrg#endif
5954d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
5955d522f475Smrg    }
5956d522f475Smrg    return result;
5957d522f475Smrg}
5958d522f475Smrg#endif /* OPT_WIDE_CHARS */
5959d522f475Smrg
5960b7c89284Ssnj/*
5961b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
5962b7c89284Ssnj */
5963b7c89284SsnjXtermWidget
5964b7c89284SsnjgetXtermWidget(Widget w)
5965b7c89284Ssnj{
5966b7c89284Ssnj    XtermWidget xw;
5967b7c89284Ssnj
5968b7c89284Ssnj    if (w == 0) {
5969b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
5970b7c89284Ssnj	if (!IsXtermWidget(xw)) {
5971b7c89284Ssnj	    xw = 0;
5972b7c89284Ssnj	}
5973b7c89284Ssnj    } else if (IsXtermWidget(w)) {
5974b7c89284Ssnj	xw = (XtermWidget) w;
5975b7c89284Ssnj    } else {
5976b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
5977b7c89284Ssnj    }
5978b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
5979b7c89284Ssnj    return xw;
5980b7c89284Ssnj}
59813367019cSmrg
59823367019cSmrg#if OPT_SESSION_MGT
59833367019cSmrgstatic void
59843367019cSmrgdie_callback(Widget w GCC_UNUSED,
59853367019cSmrg	     XtPointer client_data GCC_UNUSED,
59863367019cSmrg	     XtPointer call_data GCC_UNUSED)
59873367019cSmrg{
59883367019cSmrg    NormalExit();
59893367019cSmrg}
59903367019cSmrg
59913367019cSmrgstatic void
59923367019cSmrgsave_callback(Widget w GCC_UNUSED,
59933367019cSmrg	      XtPointer client_data GCC_UNUSED,
59943367019cSmrg	      XtPointer call_data)
59953367019cSmrg{
59963367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
59973367019cSmrg    /* we have nothing to save */
59983367019cSmrg    token->save_success = True;
59993367019cSmrg}
60003367019cSmrg
60013367019cSmrgstatic void
60023367019cSmrgicewatch(IceConn iceConn,
60033367019cSmrg	 IcePointer clientData GCC_UNUSED,
60043367019cSmrg	 Bool opening,
60053367019cSmrg	 IcePointer * watchData GCC_UNUSED)
60063367019cSmrg{
60073367019cSmrg    if (opening) {
60083367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
60093367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
60103367019cSmrg    } else {
60113367019cSmrg	ice_fd = -1;
60123367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
60133367019cSmrg    }
60143367019cSmrg}
60153367019cSmrg
60163367019cSmrgvoid
60173367019cSmrgxtermOpenSession(void)
60183367019cSmrg{
60193367019cSmrg    if (resource.sessionMgt) {
60203367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
60213367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
60223367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
60233367019cSmrg    }
60243367019cSmrg}
60253367019cSmrg
60263367019cSmrgvoid
60273367019cSmrgxtermCloseSession(void)
60283367019cSmrg{
60293367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
60303367019cSmrg}
60313367019cSmrg#endif /* OPT_SESSION_MGT */
60323367019cSmrg
60333367019cSmrgWidget
60343367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
60353367019cSmrg		     String my_class,
60363367019cSmrg		     XrmOptionDescRec * options,
60373367019cSmrg		     Cardinal num_options,
60383367019cSmrg		     int *argc_in_out,
6039fa3f02f3Smrg		     String *argv_in_out,
6040fa3f02f3Smrg		     String *fallback_resources,
60413367019cSmrg		     WidgetClass widget_class,
60423367019cSmrg		     ArgList args,
60433367019cSmrg		     Cardinal num_args)
60443367019cSmrg{
60453367019cSmrg    Widget result;
60463367019cSmrg
60473367019cSmrg    XtSetErrorHandler(xt_error);
60483367019cSmrg#if OPT_SESSION_MGT
60493367019cSmrg    result = XtOpenApplication(app_context_return,
60503367019cSmrg			       my_class,
60513367019cSmrg			       options,
60523367019cSmrg			       num_options,
60533367019cSmrg			       argc_in_out,
60543367019cSmrg			       argv_in_out,
60553367019cSmrg			       fallback_resources,
60563367019cSmrg			       widget_class,
60573367019cSmrg			       args,
60583367019cSmrg			       num_args);
60593367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
60603367019cSmrg#else
60619a64e1c5Smrg    (void) widget_class;
60629a64e1c5Smrg    (void) args;
60639a64e1c5Smrg    (void) num_args;
60643367019cSmrg    result = XtAppInitialize(app_context_return,
60653367019cSmrg			     my_class,
60663367019cSmrg			     options,
60673367019cSmrg			     num_options,
60683367019cSmrg			     argc_in_out,
60693367019cSmrg			     argv_in_out,
60703367019cSmrg			     fallback_resources,
60713367019cSmrg			     NULL, 0);
60723367019cSmrg#endif /* OPT_SESSION_MGT */
6073037a25ddSmrg    init_colored_cursor(XtDisplay(result));
6074037a25ddSmrg
60753367019cSmrg    XtSetErrorHandler((XtErrorHandler) 0);
60763367019cSmrg
60773367019cSmrg    return result;
60783367019cSmrg}
60793367019cSmrg
60803367019cSmrgstatic int x11_errors;
60813367019cSmrg
60823367019cSmrgstatic int
60839a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
60843367019cSmrg{
60853367019cSmrg    (void) display;
60863367019cSmrg    (void) error_event;
60873367019cSmrg    ++x11_errors;
60883367019cSmrg    return 0;
60893367019cSmrg}
60903367019cSmrg
60913367019cSmrgBoolean
6092fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
60933367019cSmrg{
60943367019cSmrg    Boolean result = False;
60953367019cSmrg    Status code;
60963367019cSmrg
60973367019cSmrg    memset(attrs, 0, sizeof(*attrs));
60983367019cSmrg    if (win != None) {
60993367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
61003367019cSmrg	x11_errors = 0;
61013367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
61023367019cSmrg	XSetErrorHandler(save);
61033367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
61043367019cSmrg	if (result) {
61053367019cSmrg	    TRACE_WIN_ATTRS(attrs);
61063367019cSmrg	} else {
61073367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
61083367019cSmrg	}
61093367019cSmrg    }
61103367019cSmrg    return result;
61113367019cSmrg}
61123367019cSmrg
61133367019cSmrgBoolean
6114fa3f02f3SmrgxtermGetWinProp(Display *display,
61153367019cSmrg		Window win,
61163367019cSmrg		Atom property,
61173367019cSmrg		long long_offset,
61183367019cSmrg		long long_length,
61193367019cSmrg		Atom req_type,
61209a64e1c5Smrg		Atom *actual_type_return,
61213367019cSmrg		int *actual_format_return,
61223367019cSmrg		unsigned long *nitems_return,
61233367019cSmrg		unsigned long *bytes_after_return,
61243367019cSmrg		unsigned char **prop_return)
61253367019cSmrg{
61263367019cSmrg    Boolean result = True;
61273367019cSmrg
61283367019cSmrg    if (win != None) {
61293367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
61303367019cSmrg	x11_errors = 0;
61313367019cSmrg	if (XGetWindowProperty(display,
61323367019cSmrg			       win,
61333367019cSmrg			       property,
61343367019cSmrg			       long_offset,
61353367019cSmrg			       long_length,
61363367019cSmrg			       False,
61373367019cSmrg			       req_type,
61383367019cSmrg			       actual_type_return,
61393367019cSmrg			       actual_format_return,
61403367019cSmrg			       nitems_return,
61413367019cSmrg			       bytes_after_return,
61423367019cSmrg			       prop_return) == Success
61433367019cSmrg	    && x11_errors == 0) {
61443367019cSmrg	    result = True;
61453367019cSmrg	}
61463367019cSmrg	XSetErrorHandler(save);
61473367019cSmrg    }
61483367019cSmrg    return result;
61493367019cSmrg}
61503367019cSmrg
61513367019cSmrgvoid
61523367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
61533367019cSmrg{
61543367019cSmrg    Display *dpy = XtDisplay(toplevel);
61553367019cSmrg    XWindowAttributes attrs;
61563367019cSmrg
61573367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
61583367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
61593367019cSmrg	XtermWidget xw = term;
61603367019cSmrg	TScreen *screen = TScreenOf(xw);
61613367019cSmrg
61623367019cSmrg	XtRealizeWidget(toplevel);
61633367019cSmrg
61643367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
61653367019cSmrg	       XtWindow(toplevel),
61663367019cSmrg	       winToEmbedInto));
61673367019cSmrg	XReparentWindow(dpy,
61683367019cSmrg			XtWindow(toplevel),
61693367019cSmrg			winToEmbedInto, 0, 0);
61703367019cSmrg
61713367019cSmrg	screen->embed_high = (Dimension) attrs.height;
61723367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
61733367019cSmrg    }
61743367019cSmrg}
617594644356Smrg
617694644356Smrgvoid
617794644356Smrgfree_string(String value)
617894644356Smrg{
617994644356Smrg    free((void *) value);
618094644356Smrg}
6181