misc.c revision 94644356
194644356Smrg/* $XTermId: misc.c,v 1.726 2015/04/10 08:27:17 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
494644356Smrg * Copyright 1999-2014,2015 by Thomas E. Dickey
5d522f475Smrg *
6cd3331d0Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg#include <version.h>
56b7c89284Ssnj#include <main.h>
57d522f475Smrg#include <xterm.h>
58d522f475Smrg
59d522f475Smrg#include <sys/stat.h>
60d522f475Smrg#include <stdio.h>
613367019cSmrg#include <stdarg.h>
62d522f475Smrg#include <signal.h>
63d522f475Smrg#include <ctype.h>
64d522f475Smrg#include <pwd.h>
65d522f475Smrg#include <sys/wait.h>
66d522f475Smrg
67d522f475Smrg#include <X11/keysym.h>
68d522f475Smrg#include <X11/Xatom.h>
69d522f475Smrg#include <X11/cursorfont.h>
70d522f475Smrg#include <X11/Xlocale.h>
71d522f475Smrg
72d522f475Smrg#include <X11/Xmu/Error.h>
73d522f475Smrg#include <X11/Xmu/SysUtil.h>
74d522f475Smrg#include <X11/Xmu/WinUtil.h>
75d522f475Smrg#include <X11/Xmu/Xmu.h>
76d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
77d522f475Smrg#include <X11/Sunkeysym.h>
78d522f475Smrg#endif
79d522f475Smrg
803367019cSmrg#ifdef HAVE_LIBXPM
813367019cSmrg#include <X11/xpm.h>
823367019cSmrg#endif
833367019cSmrg
84d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
85d522f475Smrg#include <langinfo.h>
86d522f475Smrg#endif
87d522f475Smrg
88d522f475Smrg#include <xutf8.h>
89d522f475Smrg
90d522f475Smrg#include <data.h>
91d522f475Smrg#include <error.h>
92d522f475Smrg#include <menu.h>
93d522f475Smrg#include <fontutils.h>
94d522f475Smrg#include <xstrings.h>
95d522f475Smrg#include <xtermcap.h>
96d522f475Smrg#include <VTparse.h>
97fa3f02f3Smrg#include <graphics.h>
989a64e1c5Smrg#include <graphics_regis.h>
999a64e1c5Smrg#include <graphics_sixel.h>
100d522f475Smrg
101d522f475Smrg#include <assert.h>
102d522f475Smrg
103d522f475Smrg#if (XtSpecificationRelease < 6)
104d522f475Smrg#ifndef X_GETTIMEOFDAY
105d522f475Smrg#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0)
106d522f475Smrg#endif
107d522f475Smrg#endif
108d522f475Smrg
109d522f475Smrg#ifdef VMS
110d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
111d522f475Smrg#ifdef ALLOWLOGFILEEXEC
112d522f475Smrg#undef ALLOWLOGFILEEXEC
113d522f475Smrg#endif
114d522f475Smrg#endif /* VMS */
115d522f475Smrg
116d522f475Smrg#if OPT_TEK4014
117d522f475Smrg#define OUR_EVENT(event,Type) \
118d522f475Smrg		(event.type == Type && \
119d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
120d522f475Smrg		    (tekWidget && \
121d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
122d522f475Smrg#else
123d522f475Smrg#define OUR_EVENT(event,Type) \
124d522f475Smrg		(event.type == Type && \
125d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
126d522f475Smrg#endif
127d522f475Smrg
1283367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
129d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
130d522f475Smrg
1313367019cSmrgstatic char emptyString[] = "";
1323367019cSmrg
133d522f475Smrg#if OPT_EXEC_XTERM
134d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
135d522f475Smrg   error; adapted from libc docs */
136d522f475Smrgstatic char *
137d522f475SmrgReadlink(const char *filename)
138d522f475Smrg{
139d522f475Smrg    char *buf = NULL;
140cd3331d0Smrg    size_t size = 100;
141d522f475Smrg    int n;
142d522f475Smrg
143d522f475Smrg    for (;;) {
144d522f475Smrg	buf = TypeRealloc(char, size, buf);
145d522f475Smrg	memset(buf, 0, size);
146d522f475Smrg
147cd3331d0Smrg	n = (int) readlink(filename, buf, size);
148d522f475Smrg	if (n < 0) {
149d522f475Smrg	    free(buf);
150d522f475Smrg	    return NULL;
151d522f475Smrg	}
152d522f475Smrg
153d522f475Smrg	if ((unsigned) n < size) {
154d522f475Smrg	    return buf;
155d522f475Smrg	}
156d522f475Smrg
157d522f475Smrg	size *= 2;
158d522f475Smrg    }
159d522f475Smrg}
160d522f475Smrg#endif /* OPT_EXEC_XTERM */
161d522f475Smrg
162d522f475Smrgstatic void
163d522f475SmrgSleep(int msec)
164d522f475Smrg{
165d522f475Smrg    static struct timeval select_timeout;
166d522f475Smrg
167d522f475Smrg    select_timeout.tv_sec = 0;
168d522f475Smrg    select_timeout.tv_usec = msec * 1000;
169d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
170d522f475Smrg}
171d522f475Smrg
172d522f475Smrgstatic void
1733367019cSmrgselectwindow(XtermWidget xw, int flag)
174d522f475Smrg{
1753367019cSmrg    TScreen *screen = TScreenOf(xw);
1763367019cSmrg
177d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
178d522f475Smrg
179d522f475Smrg#if OPT_TEK4014
1803367019cSmrg    if (TEK4014_ACTIVE(xw)) {
181d522f475Smrg	if (!Ttoggled)
182d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
183d522f475Smrg	screen->select |= flag;
184d522f475Smrg	if (!Ttoggled)
185d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
186d522f475Smrg    } else
187d522f475Smrg#endif
188d522f475Smrg    {
1893367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
1903367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
1913367019cSmrg	if (input && input->xic)
1923367019cSmrg	    XSetICFocus(input->xic);
1933367019cSmrg#endif
194d522f475Smrg
195d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
196d522f475Smrg	    HideCursor();
197d522f475Smrg	screen->select |= flag;
198d522f475Smrg	if (screen->cursor_state)
199d522f475Smrg	    ShowCursor();
200d522f475Smrg    }
201cd3331d0Smrg    GetScrollLock(screen);
202d522f475Smrg}
203d522f475Smrg
204d522f475Smrgstatic void
2053367019cSmrgunselectwindow(XtermWidget xw, int flag)
206d522f475Smrg{
2073367019cSmrg    TScreen *screen = TScreenOf(xw);
2083367019cSmrg
209d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
210d522f475Smrg
2113367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
212d522f475Smrg	screen->hide_pointer = False;
2133367019cSmrg	xtermDisplayCursor(xw);
214d522f475Smrg    }
215d522f475Smrg
216d522f475Smrg    if (!screen->always_highlight) {
217d522f475Smrg#if OPT_TEK4014
2183367019cSmrg	if (TEK4014_ACTIVE(xw)) {
219d522f475Smrg	    if (!Ttoggled)
220d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
221d522f475Smrg	    screen->select &= ~flag;
222d522f475Smrg	    if (!Ttoggled)
223d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
224d522f475Smrg	} else
225d522f475Smrg#endif
226d522f475Smrg	{
2273367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
2283367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2293367019cSmrg	    if (input && input->xic)
2303367019cSmrg		XUnsetICFocus(input->xic);
2313367019cSmrg#endif
232d522f475Smrg
233d522f475Smrg	    screen->select &= ~flag;
234d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
235d522f475Smrg		HideCursor();
236d522f475Smrg	    if (screen->cursor_state)
237d522f475Smrg		ShowCursor();
238d522f475Smrg	}
239d522f475Smrg    }
240d522f475Smrg}
241d522f475Smrg
242d522f475Smrgstatic void
2439a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
244d522f475Smrg{
245d522f475Smrg    TScreen *screen = TScreenOf(xw);
246d522f475Smrg
247d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
248cd3331d0Smrg    TRACE_FOCUS(xw, ev);
249cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
250cd3331d0Smrg	ev->focus &&
251cd3331d0Smrg	!(screen->select & FOCUS))
2523367019cSmrg	selectwindow(xw, INWINDOW);
253d522f475Smrg}
254d522f475Smrg
255d522f475Smrgstatic void
2569a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
257d522f475Smrg{
258d522f475Smrg    TScreen *screen = TScreenOf(xw);
259d522f475Smrg
260d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
261cd3331d0Smrg    TRACE_FOCUS(xw, ev);
262cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
263cd3331d0Smrg	ev->focus &&
264cd3331d0Smrg	!(screen->select & FOCUS))
2653367019cSmrg	unselectwindow(xw, INWINDOW);
266d522f475Smrg}
267d522f475Smrg
268d522f475Smrg#ifndef XUrgencyHint
269d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
270d522f475Smrg#endif
271d522f475Smrg
272d522f475Smrgstatic void
273c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
274d522f475Smrg{
275c219fbebSmrg    TScreen *screen = TScreenOf(xw);
276c219fbebSmrg
277d522f475Smrg    if (screen->bellIsUrgent) {
278c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
279d522f475Smrg	if (h != 0) {
280c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
281d522f475Smrg		h->flags |= XUrgencyHint;
282d522f475Smrg	    } else {
283d522f475Smrg		h->flags &= ~XUrgencyHint;
284d522f475Smrg	    }
285c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
286d522f475Smrg	}
287d522f475Smrg    }
288d522f475Smrg}
289d522f475Smrg
290d522f475Smrgvoid
291d522f475Smrgdo_xevents(void)
292d522f475Smrg{
293d522f475Smrg    TScreen *screen = TScreenOf(term);
294d522f475Smrg
2953367019cSmrg    if (xtermAppPending()
296d522f475Smrg	||
297d522f475Smrg#if defined(VMS) || defined(__VMS)
298d522f475Smrg	screen->display->qlen > 0
299d522f475Smrg#else
300d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
301d522f475Smrg#endif
302d522f475Smrg	)
303d522f475Smrg	xevents();
304d522f475Smrg}
305d522f475Smrg
306d522f475Smrgvoid
307d522f475SmrgxtermDisplayCursor(XtermWidget xw)
308d522f475Smrg{
309d522f475Smrg    TScreen *screen = TScreenOf(xw);
310d522f475Smrg
311d522f475Smrg    if (screen->Vshow) {
312d522f475Smrg	if (screen->hide_pointer) {
313d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
314d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
315d522f475Smrg	} else {
316d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
317d522f475Smrg	    recolor_cursor(screen,
318d522f475Smrg			   screen->pointer_cursor,
319d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
320d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
321d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
322d522f475Smrg	}
323d522f475Smrg    }
324d522f475Smrg}
325d522f475Smrg
326d522f475Smrgvoid
327d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
328d522f475Smrg{
329d522f475Smrg    static int tried = -1;
330d522f475Smrg    TScreen *screen = TScreenOf(xw);
331d522f475Smrg
332d522f475Smrg#if OPT_TEK4014
333d522f475Smrg    if (TEK4014_SHOWN(xw))
334d522f475Smrg	enable = True;
335d522f475Smrg#endif
336d522f475Smrg
337d522f475Smrg    /*
338d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
339d522f475Smrg     * the mouse-mode:
340d522f475Smrg     */
341d522f475Smrg    if (!enable) {
342d522f475Smrg	switch (screen->pointer_mode) {
343d522f475Smrg	case pNever:
344d522f475Smrg	    enable = True;
345d522f475Smrg	    break;
346d522f475Smrg	case pNoMouse:
347d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
348d522f475Smrg		enable = True;
349d522f475Smrg	    break;
350d522f475Smrg	case pAlways:
3513367019cSmrg	case pFocused:
352d522f475Smrg	    break;
353d522f475Smrg	}
354d522f475Smrg    }
355d522f475Smrg
356d522f475Smrg    if (enable) {
357d522f475Smrg	if (screen->hide_pointer) {
358d522f475Smrg	    screen->hide_pointer = False;
359d522f475Smrg	    xtermDisplayCursor(xw);
360d522f475Smrg	    switch (screen->send_mouse_pos) {
361d522f475Smrg	    case ANY_EVENT_MOUSE:
362d522f475Smrg		break;
363d522f475Smrg	    default:
364d522f475Smrg		MotionOff(screen, xw);
365d522f475Smrg		break;
366d522f475Smrg	    }
367d522f475Smrg	}
368d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
369d522f475Smrg	if (screen->hidden_cursor == 0) {
370d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
371d522f475Smrg	}
372d522f475Smrg	if (screen->hidden_cursor == 0) {
373d522f475Smrg	    tried = 1;
374d522f475Smrg	} else {
375d522f475Smrg	    tried = 0;
376d522f475Smrg	    screen->hide_pointer = True;
377d522f475Smrg	    xtermDisplayCursor(xw);
378d522f475Smrg	    MotionOn(screen, xw);
379d522f475Smrg	}
380d522f475Smrg    }
381d522f475Smrg}
382d522f475Smrg
3833367019cSmrg#if OPT_TRACE
3843367019cSmrgstatic void
3859a64e1c5SmrgTraceExposeEvent(XEvent *arg)
3863367019cSmrg{
3873367019cSmrg    XExposeEvent *event = (XExposeEvent *) arg;
3883367019cSmrg
3893367019cSmrg    TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n",
3903367019cSmrg	   event->serial,
3913367019cSmrg	   event->count,
3923367019cSmrg	   event->y,
3933367019cSmrg	   event->x,
3943367019cSmrg	   event->height,
3953367019cSmrg	   event->width,
3963367019cSmrg	   event->window));
3973367019cSmrg}
3983367019cSmrg
3993367019cSmrg#else
4003367019cSmrg#define TraceExposeEvent(event)	/* nothing */
4013367019cSmrg#endif
4023367019cSmrg
4033367019cSmrg/* true if p contains q */
4043367019cSmrg#define ExposeContains(p,q) \
4053367019cSmrg	    ((p)->y <= (q)->y \
4063367019cSmrg	  && (p)->x <= (q)->x \
4073367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
4083367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
4093367019cSmrg
4103367019cSmrgstatic XtInputMask
4119a64e1c5SmrgmergeExposeEvents(XEvent *target)
4123367019cSmrg{
4133367019cSmrg    XEvent next_event;
4143367019cSmrg    XExposeEvent *p, *q;
4153367019cSmrg
4163367019cSmrg    TRACE(("pending Expose...?\n"));
4173367019cSmrg    TraceExposeEvent(target);
4183367019cSmrg    XtAppNextEvent(app_con, target);
4193367019cSmrg    p = (XExposeEvent *) target;
4203367019cSmrg
4213367019cSmrg    while (XtAppPending(app_con)
4223367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4233367019cSmrg	   && next_event.type == Expose) {
4243367019cSmrg	Boolean merge_this = False;
4253367019cSmrg
4263367019cSmrg	TraceExposeEvent(&next_event);
4273367019cSmrg	q = (XExposeEvent *) (&next_event);
4283367019cSmrg	XtAppNextEvent(app_con, &next_event);
4293367019cSmrg
4303367019cSmrg	/*
4313367019cSmrg	 * If either window is contained within the other, merge the events.
4323367019cSmrg	 * The traces show that there are also cases where a full repaint of
4333367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4343367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4353367019cSmrg	 * to skim several events ahead.
4363367019cSmrg	 */
4373367019cSmrg	if (p->window == q->window) {
4383367019cSmrg	    if (ExposeContains(p, q)) {
4393367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4403367019cSmrg		merge_this = True;
4413367019cSmrg		next_event = *target;
4423367019cSmrg	    } else if (ExposeContains(q, p)) {
4433367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4443367019cSmrg		merge_this = True;
4453367019cSmrg	    }
4463367019cSmrg	}
4473367019cSmrg	if (!merge_this) {
4483367019cSmrg	    XtDispatchEvent(target);
4493367019cSmrg	}
4503367019cSmrg	*target = next_event;
4513367019cSmrg    }
4523367019cSmrg    XtDispatchEvent(target);
4533367019cSmrg    return XtAppPending(app_con);
4543367019cSmrg}
4553367019cSmrg
4563367019cSmrg#if OPT_TRACE
4573367019cSmrgstatic void
4589a64e1c5SmrgTraceConfigureEvent(XEvent *arg)
4593367019cSmrg{
4603367019cSmrg    XConfigureEvent *event = (XConfigureEvent *) arg;
4613367019cSmrg
4623367019cSmrg    TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n",
4633367019cSmrg	   event->serial,
4643367019cSmrg	   event->y,
4653367019cSmrg	   event->x,
4663367019cSmrg	   event->height,
4673367019cSmrg	   event->width,
4683367019cSmrg	   event->window));
4693367019cSmrg}
4703367019cSmrg
4713367019cSmrg#else
4723367019cSmrg#define TraceConfigureEvent(event)	/* nothing */
4733367019cSmrg#endif
4743367019cSmrg
4753367019cSmrg/*
4763367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4773367019cSmrg * event.  Remove that from the queue so we can look further.
4783367019cSmrg *
4793367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4803367019cSmrg * that.  If the adjacent events are for different windows, process the older
4813367019cSmrg * event and update the event used for comparing windows.  If they are for the
4823367019cSmrg * same window, only the newer event is of interest.
4833367019cSmrg *
4843367019cSmrg * Finally, process the (remaining) configure-notify event.
4853367019cSmrg */
4863367019cSmrgstatic XtInputMask
4879a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4883367019cSmrg{
4893367019cSmrg    XEvent next_event;
4903367019cSmrg    XConfigureEvent *p, *q;
4913367019cSmrg
4923367019cSmrg    XtAppNextEvent(app_con, target);
4933367019cSmrg    p = (XConfigureEvent *) target;
4943367019cSmrg
4953367019cSmrg    TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no"));
4963367019cSmrg    TraceConfigureEvent(target);
4973367019cSmrg
4983367019cSmrg    if (XtAppPending(app_con)
4993367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
5003367019cSmrg	&& next_event.type == ConfigureNotify) {
5013367019cSmrg	Boolean merge_this = False;
5023367019cSmrg
5033367019cSmrg	TraceConfigureEvent(&next_event);
5043367019cSmrg	XtAppNextEvent(app_con, &next_event);
5053367019cSmrg	q = (XConfigureEvent *) (&next_event);
5063367019cSmrg
5073367019cSmrg	if (p->window == q->window) {
5083367019cSmrg	    TRACE(("pending Configure...merged\n"));
5093367019cSmrg	    merge_this = True;
5103367019cSmrg	}
5113367019cSmrg	if (!merge_this) {
5123367019cSmrg	    TRACE(("pending Configure...skipped\n"));
5133367019cSmrg	    XtDispatchEvent(target);
5143367019cSmrg	}
5153367019cSmrg	*target = next_event;
5163367019cSmrg    }
5173367019cSmrg    XtDispatchEvent(target);
5183367019cSmrg    return XtAppPending(app_con);
5193367019cSmrg}
5203367019cSmrg
5213367019cSmrg/*
5223367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5233367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5243367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5253367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5263367019cSmrg * point.
5273367019cSmrg *
5283367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5293367019cSmrg */
5303367019cSmrgXtInputMask
5313367019cSmrgxtermAppPending(void)
5323367019cSmrg{
5333367019cSmrg    XtInputMask result = XtAppPending(app_con);
5343367019cSmrg    XEvent this_event;
5353367019cSmrg
5363367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
5373367019cSmrg	if (this_event.type == Expose) {
5383367019cSmrg	    result = mergeExposeEvents(&this_event);
539fa3f02f3Smrg	    TRACE(("got merged expose events\n"));
5403367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5413367019cSmrg	    result = mergeConfigureEvents(&this_event);
542fa3f02f3Smrg	    TRACE(("got merged configure notify events\n"));
5433367019cSmrg	} else {
5443367019cSmrg	    TRACE(("pending %s\n", visibleEventType(this_event.type)));
5453367019cSmrg	    break;
5463367019cSmrg	}
5473367019cSmrg    }
5483367019cSmrg    return result;
5493367019cSmrg}
5503367019cSmrg
551d522f475Smrgvoid
552d522f475Smrgxevents(void)
553d522f475Smrg{
554d522f475Smrg    XtermWidget xw = term;
555d522f475Smrg    TScreen *screen = TScreenOf(xw);
556d522f475Smrg    XEvent event;
557d522f475Smrg    XtInputMask input_mask;
558d522f475Smrg
559d522f475Smrg    if (need_cleanup)
5603367019cSmrg	NormalExit();
561d522f475Smrg
562d522f475Smrg    if (screen->scroll_amt)
563d522f475Smrg	FlushScroll(xw);
564d522f475Smrg    /*
565d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
566d522f475Smrg     * will process the timeout and return without blockng on the
567cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
568d522f475Smrg     * with select().
569d522f475Smrg     */
5703367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
571cd3331d0Smrg	if (input_mask & XtIMTimer)
572cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
573d522f475Smrg#if OPT_SESSION_MGT
574cd3331d0Smrg	/*
575cd3331d0Smrg	 * Session management events are alternative input events. Deal with
576cd3331d0Smrg	 * them in the same way.
577cd3331d0Smrg	 */
578cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
579cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
580d522f475Smrg#endif
581cd3331d0Smrg	else
582cd3331d0Smrg	    break;
583cd3331d0Smrg    }
584d522f475Smrg
585d522f475Smrg    /*
586d522f475Smrg     * If there's no XEvents, don't wait around...
587d522f475Smrg     */
588d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
589d522f475Smrg	return;
590d522f475Smrg    do {
591d522f475Smrg	/*
592d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
593d522f475Smrg	 * We simply ignore all events except for those not passed down to
594d522f475Smrg	 * this function, e.g., those handled in in_put().
595d522f475Smrg	 */
596d522f475Smrg	if (screen->waitingForTrackInfo) {
597d522f475Smrg	    Sleep(10);
598d522f475Smrg	    return;
599d522f475Smrg	}
600d522f475Smrg	XtAppNextEvent(app_con, &event);
601d522f475Smrg	/*
602d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
603d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
604d522f475Smrg	 * looking at the event ourselves we make sure that we can
605d522f475Smrg	 * do the right thing.
606d522f475Smrg	 */
607d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
608d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
609d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
610d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
611d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
612d522f475Smrg#if OPT_DEC_LOCATOR
613d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
614d522f475Smrg#endif /* OPT_DEC_LOCATOR */
615d522f475Smrg		   )
616d522f475Smrg		   && event.xany.type == MotionNotify
617d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
618d522f475Smrg	    SendMousePosition(xw, &event);
619cb4a1343Smrg	    xtermShowPointer(xw, True);
620d522f475Smrg	    continue;
621d522f475Smrg	}
622d522f475Smrg
623cb4a1343Smrg	/*
624cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
625cb4a1343Smrg	 * mouse pointer back on.
626cb4a1343Smrg	 */
627cb4a1343Smrg	if (screen->hide_pointer) {
6283367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6293367019cSmrg		switch (event.xany.type) {
6303367019cSmrg		case MotionNotify:
6313367019cSmrg		    xtermShowPointer(xw, True);
6323367019cSmrg		    break;
6333367019cSmrg		}
6343367019cSmrg	    } else {
6353367019cSmrg		switch (event.xany.type) {
6363367019cSmrg		case KeyPress:
6373367019cSmrg		case KeyRelease:
6383367019cSmrg		case ButtonPress:
6393367019cSmrg		case ButtonRelease:
6403367019cSmrg		    /* also these... */
6413367019cSmrg		case Expose:
6423367019cSmrg		case NoExpose:
6433367019cSmrg		case PropertyNotify:
6443367019cSmrg		case ClientMessage:
6453367019cSmrg		    break;
6463367019cSmrg		default:
6473367019cSmrg		    xtermShowPointer(xw, True);
6483367019cSmrg		    break;
6493367019cSmrg		}
650cb4a1343Smrg	    }
651cb4a1343Smrg	}
652cb4a1343Smrg
653d522f475Smrg	if (!event.xany.send_event ||
654d522f475Smrg	    screen->allowSendEvents ||
655d522f475Smrg	    ((event.xany.type != KeyPress) &&
656d522f475Smrg	     (event.xany.type != KeyRelease) &&
657d522f475Smrg	     (event.xany.type != ButtonPress) &&
658d522f475Smrg	     (event.xany.type != ButtonRelease))) {
659d522f475Smrg
660d522f475Smrg	    XtDispatchEvent(&event);
661d522f475Smrg	}
6623367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
663d522f475Smrg}
664d522f475Smrg
665d522f475Smrgstatic Cursor
666d522f475Smrgmake_hidden_cursor(XtermWidget xw)
667d522f475Smrg{
668d522f475Smrg    TScreen *screen = TScreenOf(xw);
669d522f475Smrg    Cursor c;
670d522f475Smrg    Display *dpy = screen->display;
671d522f475Smrg    XFontStruct *fn;
672d522f475Smrg
673d522f475Smrg    static XColor dummy;
674d522f475Smrg
675d522f475Smrg    /*
676d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
677d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
678b7c89284Ssnj     * server insists on drawing _something_.
679d522f475Smrg     */
680d522f475Smrg    TRACE(("Ask for nil2 font\n"));
681d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
682d522f475Smrg	TRACE(("...Ask for fixed font\n"));
683b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
684d522f475Smrg    }
685d522f475Smrg
686d522f475Smrg    if (fn != 0) {
687d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
688d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
689d522f475Smrg	XFreeFont(dpy, fn);
690d522f475Smrg    } else {
691d522f475Smrg	c = 0;
692d522f475Smrg    }
693d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
694d522f475Smrg    return (c);
695d522f475Smrg}
696d522f475Smrg
697fa3f02f3Smrg/*
698fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
699fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
700fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
701fa3f02f3Smrg * until the window is initialized.
702fa3f02f3Smrg */
703fa3f02f3Smrgvoid
704fa3f02f3Smrginit_colored_cursor(void)
705fa3f02f3Smrg{
706fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
70794644356Smrg    static const char theme[] = "index.theme";
70894644356Smrg    static const char pattern[] = "xtermXXXXXX";
709fa3f02f3Smrg    const char *tmp_dir;
710fa3f02f3Smrg    char *filename;
711fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
712fa3f02f3Smrg    size_t needed;
713fa3f02f3Smrg    FILE *fp;
714fa3f02f3Smrg
715fa3f02f3Smrg    xterm_cursor_theme = 0;
716fa3f02f3Smrg    if (IsEmpty(env)) {
717fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
718fa3f02f3Smrg	    tmp_dir = P_tmpdir;
719fa3f02f3Smrg	}
720fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
721fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
722fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
723fa3f02f3Smrg
724fa3f02f3Smrg#ifdef HAVE_MKDTEMP
725fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
726fa3f02f3Smrg#else
727fa3f02f3Smrg	    if (mktemp(filename) != 0
728fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
729fa3f02f3Smrg		xterm_cursor_theme = filename;
730fa3f02f3Smrg	    }
731fa3f02f3Smrg#endif
732fa3f02f3Smrg	    /*
733fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
734fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
735fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
736fa3f02f3Smrg	     */
737fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
738fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
739fa3f02f3Smrg		strcat(leaf, "/");
740fa3f02f3Smrg		strcat(leaf, theme);
741fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
742fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
743fa3f02f3Smrg		    fclose(fp);
744fa3f02f3Smrg		    *leaf = '\0';
745fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
746fa3f02f3Smrg		    *leaf = '/';
747fa3f02f3Smrg		}
748fa3f02f3Smrg		atexit(cleanup_colored_cursor);
749fa3f02f3Smrg	    }
750fa3f02f3Smrg	}
751fa3f02f3Smrg    }
752fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
753fa3f02f3Smrg}
754fa3f02f3Smrg
755fa3f02f3Smrg/*
756fa3f02f3Smrg * Once done, discard the file and directory holding it.
757fa3f02f3Smrg */
758fa3f02f3Smrgvoid
759fa3f02f3Smrgcleanup_colored_cursor(void)
760fa3f02f3Smrg{
761fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
762fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
763fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
764fa3f02f3Smrg	struct stat sb;
765fa3f02f3Smrg	if (!IsEmpty(my_path)
766fa3f02f3Smrg	    && stat(my_path, &sb) == 0
767fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
768fa3f02f3Smrg	    unlink(xterm_cursor_theme);
769fa3f02f3Smrg	    rmdir(my_path);
770fa3f02f3Smrg	    free(xterm_cursor_theme);
771fa3f02f3Smrg	    xterm_cursor_theme = 0;
772fa3f02f3Smrg	}
773fa3f02f3Smrg    }
774fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
775fa3f02f3Smrg}
776fa3f02f3Smrg
777d522f475SmrgCursor
778d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
779d522f475Smrg		    unsigned long fg,	/* pixel value */
780d522f475Smrg		    unsigned long bg)	/* pixel value */
781d522f475Smrg{
782d522f475Smrg    TScreen *screen = TScreenOf(term);
783d522f475Smrg    Cursor c;
784d522f475Smrg    Display *dpy = screen->display;
785d522f475Smrg
786d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
787d522f475Smrg    if (c != None) {
788d522f475Smrg	recolor_cursor(screen, c, fg, bg);
789d522f475Smrg    }
790d522f475Smrg    return (c);
791d522f475Smrg}
792d522f475Smrg
793d522f475Smrg/* ARGSUSED */
794d522f475Smrgvoid
795d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
7969a64e1c5Smrg		 XEvent *event,
797fa3f02f3Smrg		 String *params GCC_UNUSED,
798d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
799d522f475Smrg{
800cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
801cd3331d0Smrg    Input(term, &event->xkey, False);
802d522f475Smrg}
803d522f475Smrg
804d522f475Smrg/* ARGSUSED */
805d522f475Smrgvoid
806d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
8079a64e1c5Smrg			 XEvent *event,
808fa3f02f3Smrg			 String *params GCC_UNUSED,
809d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
810d522f475Smrg{
811cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
812cd3331d0Smrg    Input(term, &event->xkey, True);
813d522f475Smrg}
814d522f475Smrg
815d522f475Smrg/* ARGSUSED */
816d522f475Smrgvoid
817d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
8189a64e1c5Smrg		  XEvent *event GCC_UNUSED,
819fa3f02f3Smrg		  String *params,
820d522f475Smrg		  Cardinal *nparams)
821d522f475Smrg{
822d522f475Smrg
823d522f475Smrg    if (*nparams != 1)
824d522f475Smrg	return;
825d522f475Smrg
826d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
8270d92cbfdSchristos	const char *abcdef = "ABCDEF";
8280d92cbfdSchristos	const char *xxxxxx;
829cd3331d0Smrg	Char c;
830cd3331d0Smrg	UString p;
8310d92cbfdSchristos	unsigned value = 0;
8320d92cbfdSchristos
833cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
8340d92cbfdSchristos	     '\0'; p++) {
8350d92cbfdSchristos	    value *= 16;
836d522f475Smrg	    if (c >= '0' && c <= '9')
8370d92cbfdSchristos		value += (unsigned) (c - '0');
838fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
8390d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
840d522f475Smrg	    else
841d522f475Smrg		break;
842d522f475Smrg	}
8430d92cbfdSchristos	if (c == '\0') {
8440d92cbfdSchristos	    Char hexval[2];
8450d92cbfdSchristos	    hexval[0] = (Char) value;
8460d92cbfdSchristos	    hexval[1] = 0;
847b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
8480d92cbfdSchristos	}
849d522f475Smrg    } else {
850cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
851d522f475Smrg    }
852d522f475Smrg}
853d522f475Smrg
854d522f475Smrg#if OPT_EXEC_XTERM
855d522f475Smrg
856d522f475Smrg#ifndef PROCFS_ROOT
857d522f475Smrg#define PROCFS_ROOT "/proc"
858d522f475Smrg#endif
859d522f475Smrg
860d522f475Smrg/* ARGSUSED */
861d522f475Smrgvoid
862d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
8639a64e1c5Smrg		    XEvent *event GCC_UNUSED,
864fa3f02f3Smrg		    String *params,
865d522f475Smrg		    Cardinal *nparams)
866d522f475Smrg{
867cd3331d0Smrg    TScreen *screen = TScreenOf(term);
868d522f475Smrg    char *child_cwd = NULL;
869d522f475Smrg    char *child_exe;
870d522f475Smrg    pid_t pid;
871d522f475Smrg
872d522f475Smrg    /*
873d522f475Smrg     * Try to find the actual program which is running in the child process.
874d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
875d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
876d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
877d522f475Smrg     */
878d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
879d522f475Smrg    if (!child_exe) {
880cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
881cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
882d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
883d522f475Smrg	} else {
8843367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
885d522f475Smrg	}
886d522f475Smrg	if (child_exe == 0)
887d522f475Smrg	    return;
888d522f475Smrg    }
889d522f475Smrg
890d522f475Smrg    /*
891d522f475Smrg     * Determine the current working directory of the child so that we can
892d522f475Smrg     * spawn a new terminal in the same directory.
893d522f475Smrg     *
894d522f475Smrg     * If we cannot get the CWD of the child, just use our own.
895d522f475Smrg     */
896d522f475Smrg    if (screen->pid) {
897d522f475Smrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
898d522f475Smrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid);
899d522f475Smrg	child_cwd = Readlink(child_cwd_link);
900d522f475Smrg    }
901d522f475Smrg
902d522f475Smrg    /* The reaper will take care of cleaning up the child */
903d522f475Smrg    pid = fork();
904d522f475Smrg    if (pid == -1) {
9053367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
906d522f475Smrg    } else if (!pid) {
907d522f475Smrg	/* We are the child */
908d522f475Smrg	if (child_cwd) {
909cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
910d522f475Smrg	}
911d522f475Smrg
912d522f475Smrg	if (setuid(screen->uid) == -1
913d522f475Smrg	    || setgid(screen->gid) == -1) {
9143367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
915d522f475Smrg	} else {
9160d92cbfdSchristos	    unsigned myargc = *nparams + 1;
917d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
918d522f475Smrg
91994644356Smrg	    if (myargv != 0) {
92094644356Smrg		unsigned n = 0;
921d522f475Smrg
92294644356Smrg		myargv[n++] = child_exe;
923d522f475Smrg
92494644356Smrg		while (n < myargc) {
92594644356Smrg		    myargv[n++] = (char *) *params++;
92694644356Smrg		}
92794644356Smrg
92894644356Smrg		myargv[n] = 0;
92994644356Smrg		execv(child_exe, myargv);
93094644356Smrg	    }
931d522f475Smrg
932d522f475Smrg	    /* If we get here, we've failed */
9333367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
934d522f475Smrg	}
935d522f475Smrg	_exit(0);
936d522f475Smrg    }
9373367019cSmrg
9383367019cSmrg    /* We are the parent; clean up */
9393367019cSmrg    if (child_cwd)
9403367019cSmrg	free(child_cwd);
9413367019cSmrg    free(child_exe);
942d522f475Smrg}
943d522f475Smrg#endif /* OPT_EXEC_XTERM */
944d522f475Smrg
945d522f475Smrg/*
946d522f475Smrg * Rather than sending characters to the host, put them directly into our
947d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
948d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
949d522f475Smrg *
950d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
951d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
952d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
953d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
954d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
955d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
956d522f475Smrg */
957d522f475Smrg/* ARGSUSED */
958d522f475Smrgvoid
959d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
9609a64e1c5Smrg		XEvent *event GCC_UNUSED,
961fa3f02f3Smrg		String *params,
962d522f475Smrg		Cardinal *param_count)
963d522f475Smrg{
964d522f475Smrg    if (*param_count == 1) {
965cd3331d0Smrg	const char *value = params[0];
966b7c89284Ssnj	int need = (int) strlen(value);
967cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
968cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
969d522f475Smrg
970d522f475Smrg	if (have - used + need < BUF_SIZE) {
971d522f475Smrg
972cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
973d522f475Smrg
974d522f475Smrg	    TRACE(("Interpret %s\n", value));
975d522f475Smrg	    VTbuffer->update++;
976d522f475Smrg	}
977d522f475Smrg    }
978d522f475Smrg}
979d522f475Smrg
980d522f475Smrg/*ARGSUSED*/
981d522f475Smrgvoid
982d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
983d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
9849a64e1c5Smrg		  XEvent *event GCC_UNUSED,
985fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
986d522f475Smrg{
987d522f475Smrg    /* NOP since we handled it above */
988d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
989cd3331d0Smrg    TRACE_FOCUS(w, event);
990d522f475Smrg}
991d522f475Smrg
992d522f475Smrg/*ARGSUSED*/
993d522f475Smrgvoid
994d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
995d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
9969a64e1c5Smrg		  XEvent *event GCC_UNUSED,
997fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
998d522f475Smrg{
999d522f475Smrg    /* NOP since we handled it above */
1000d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1001cd3331d0Smrg    TRACE_FOCUS(w, event);
1002d522f475Smrg}
1003d522f475Smrg
1004d522f475Smrg/*ARGSUSED*/
1005d522f475Smrgvoid
1006d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1007d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10089a64e1c5Smrg		  XEvent *ev,
1009fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1010d522f475Smrg{
1011d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1012d522f475Smrg    XtermWidget xw = term;
1013d522f475Smrg    TScreen *screen = TScreenOf(xw);
1014d522f475Smrg
10153367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1016d522f475Smrg	   visibleEventType(event->type),
10173367019cSmrg	   visibleNotifyMode(event->mode),
10183367019cSmrg	   visibleNotifyDetail(event->detail)));
1019cd3331d0Smrg    TRACE_FOCUS(xw, event);
1020d522f475Smrg
1021d522f475Smrg    if (screen->quiet_grab
1022d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1023c219fbebSmrg	/* EMPTY */ ;
1024d522f475Smrg    } else if (event->type == FocusIn) {
102594644356Smrg	if (event->detail != NotifyPointer) {
102694644356Smrg	    setXUrgency(xw, False);
102794644356Smrg	}
1028d522f475Smrg
1029d522f475Smrg	/*
1030d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1031d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1032d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1033d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1034d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1035d522f475Smrg	 * delivered to the obscured window.
1036d522f475Smrg	 */
1037d522f475Smrg	if (event->detail == NotifyNonlinear
1038d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
10393367019cSmrg	    unselectwindow(xw, INWINDOW);
1040d522f475Smrg	}
10413367019cSmrg	selectwindow(xw,
1042d522f475Smrg		     ((event->detail == NotifyPointer)
1043d522f475Smrg		      ? INWINDOW
1044d522f475Smrg		      : FOCUS));
1045d522f475Smrg	SendFocusButton(xw, event);
1046d522f475Smrg    } else {
1047d522f475Smrg#if OPT_FOCUS_EVENT
1048d522f475Smrg	if (event->type == FocusOut) {
1049d522f475Smrg	    SendFocusButton(xw, event);
1050d522f475Smrg	}
1051d522f475Smrg#endif
1052d522f475Smrg	/*
1053d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1054d522f475Smrg	 * ignore.
1055d522f475Smrg	 */
1056d522f475Smrg	if (event->mode != NotifyGrab) {
10573367019cSmrg	    unselectwindow(xw,
1058d522f475Smrg			   ((event->detail == NotifyPointer)
1059d522f475Smrg			    ? INWINDOW
1060d522f475Smrg			    : FOCUS));
1061d522f475Smrg	}
1062d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1063cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1064d522f475Smrg	    ReverseVideo(xw);
1065d522f475Smrg	    screen->grabbedKbd = False;
1066d522f475Smrg	    update_securekbd();
1067d522f475Smrg	}
1068d522f475Smrg    }
1069d522f475Smrg}
1070d522f475Smrg
1071d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1072d522f475Smrg
1073b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1074b7c89284Ssnjstatic Atom
1075b7c89284SsnjAtomBell(XtermWidget xw, int which)
1076b7c89284Ssnj{
1077b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1078b7c89284Ssnj    static struct {
1079b7c89284Ssnj	int value;
1080b7c89284Ssnj	const char *name;
1081b7c89284Ssnj    } table[] = {
1082b7c89284Ssnj	DATA(Info),
1083b7c89284Ssnj	    DATA(MarginBell),
1084b7c89284Ssnj	    DATA(MinorError),
1085b7c89284Ssnj	    DATA(TerminalBell)
1086b7c89284Ssnj    };
1087b7c89284Ssnj    Cardinal n;
1088b7c89284Ssnj    Atom result = None;
1089b7c89284Ssnj
1090b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1091b7c89284Ssnj	if (table[n].value == which) {
1092cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1093b7c89284Ssnj	    break;
1094b7c89284Ssnj	}
1095b7c89284Ssnj    }
1096b7c89284Ssnj    return result;
1097b7c89284Ssnj}
1098b7c89284Ssnj#endif
1099b7c89284Ssnj
1100d522f475Smrgvoid
1101b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1102d522f475Smrg{
1103b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1104b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1105b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1106cd3331d0Smrg#endif
1107cd3331d0Smrg
1108cd3331d0Smrg    switch (which) {
1109cd3331d0Smrg    case XkbBI_Info:
1110cd3331d0Smrg    case XkbBI_MinorError:
1111cd3331d0Smrg    case XkbBI_MajorError:
1112cd3331d0Smrg    case XkbBI_TerminalBell:
1113cd3331d0Smrg	switch (screen->warningVolume) {
1114cd3331d0Smrg	case bvOff:
1115cd3331d0Smrg	    percent = -100;
1116cd3331d0Smrg	    break;
1117cd3331d0Smrg	case bvLow:
1118cd3331d0Smrg	    break;
1119cd3331d0Smrg	case bvHigh:
1120cd3331d0Smrg	    percent = 100;
1121cd3331d0Smrg	    break;
1122cd3331d0Smrg	}
1123cd3331d0Smrg	break;
1124cd3331d0Smrg    case XkbBI_MarginBell:
1125cd3331d0Smrg	switch (screen->marginVolume) {
1126cd3331d0Smrg	case bvOff:
1127cd3331d0Smrg	    percent = -100;
1128cd3331d0Smrg	    break;
1129cd3331d0Smrg	case bvLow:
1130cd3331d0Smrg	    break;
1131cd3331d0Smrg	case bvHigh:
1132cd3331d0Smrg	    percent = 100;
1133cd3331d0Smrg	    break;
1134cd3331d0Smrg	}
1135cd3331d0Smrg	break;
1136cd3331d0Smrg    default:
1137cd3331d0Smrg	break;
1138cd3331d0Smrg    }
1139cd3331d0Smrg
1140cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1141b7c89284Ssnj    if (tony != None) {
1142c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1143b7c89284Ssnj    } else
1144b7c89284Ssnj#endif
1145b7c89284Ssnj	XBell(screen->display, percent);
1146b7c89284Ssnj}
1147b7c89284Ssnj
1148b7c89284Ssnjvoid
1149cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1150b7c89284Ssnj{
1151b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1152d522f475Smrg    struct timeval curtime;
1153d522f475Smrg    long now_msecs;
1154d522f475Smrg
1155b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1156b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1157d522f475Smrg	return;
1158d522f475Smrg    }
1159d522f475Smrg
1160c219fbebSmrg    setXUrgency(xw, True);
1161d522f475Smrg
1162d522f475Smrg    /* has enough time gone by that we are allowed to ring
1163d522f475Smrg       the bell again? */
1164d522f475Smrg    if (screen->bellSuppressTime) {
1165d522f475Smrg	if (screen->bellInProgress) {
1166d522f475Smrg	    do_xevents();
1167d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1168d522f475Smrg		return;
1169d522f475Smrg	    }
1170d522f475Smrg	}
1171d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1172d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1173d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1174d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1175d522f475Smrg	    return;
1176d522f475Smrg	}
1177d522f475Smrg	lastBellTime = now_msecs;
1178d522f475Smrg    }
1179d522f475Smrg
1180d522f475Smrg    if (screen->visualbell) {
1181d522f475Smrg	VisualBell();
1182d522f475Smrg    } else {
1183b7c89284Ssnj	xtermBell(xw, which, percent);
1184d522f475Smrg    }
1185d522f475Smrg
1186d522f475Smrg    if (screen->poponbell)
1187c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1188d522f475Smrg
1189d522f475Smrg    if (screen->bellSuppressTime) {
1190d522f475Smrg	/* now we change a property and wait for the notify event to come
1191d522f475Smrg	   back.  If the server is suspending operations while the bell
1192d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1193d522f475Smrg	   know when the previous bell has finished */
1194d522f475Smrg	Widget w = CURRENT_EMU();
1195d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1196d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1197d522f475Smrg	screen->bellInProgress = True;
1198d522f475Smrg    }
1199d522f475Smrg}
1200d522f475Smrg
1201d522f475Smrg#define VB_DELAY screen->visualBellDelay
1202d522f475Smrg
1203d522f475Smrgstatic void
1204fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1205d522f475Smrg{
12063367019cSmrg    int y = 0;
12073367019cSmrg    int x = 0;
12083367019cSmrg
12093367019cSmrg    if (screen->flash_line) {
12103367019cSmrg	y = CursorY(screen, screen->cur_row);
12113367019cSmrg	height = (unsigned) FontHeight(screen);
12123367019cSmrg    }
12133367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1214d522f475Smrg    XFlush(screen->display);
1215d522f475Smrg    Sleep(VB_DELAY);
12163367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1217d522f475Smrg}
1218d522f475Smrg
1219d522f475Smrgvoid
1220d522f475SmrgVisualBell(void)
1221d522f475Smrg{
1222d522f475Smrg    TScreen *screen = TScreenOf(term);
1223d522f475Smrg
1224d522f475Smrg    if (VB_DELAY > 0) {
1225d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1226d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1227d522f475Smrg	XGCValues gcval;
1228d522f475Smrg	GC visualGC;
1229d522f475Smrg
1230d522f475Smrg	gcval.function = GXxor;
1231d522f475Smrg	gcval.foreground = xorPixel;
1232d522f475Smrg	visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval);
1233d522f475Smrg#if OPT_TEK4014
1234d522f475Smrg	if (TEK4014_ACTIVE(term)) {
1235cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1236d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1237d522f475Smrg			TFullWidth(tekscr),
1238d522f475Smrg			TFullHeight(tekscr));
1239d522f475Smrg	} else
1240d522f475Smrg#endif
1241d522f475Smrg	{
1242d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1243d522f475Smrg			FullWidth(screen),
1244d522f475Smrg			FullHeight(screen));
1245d522f475Smrg	}
1246d522f475Smrg	XtReleaseGC((Widget) term, visualGC);
1247d522f475Smrg    }
1248d522f475Smrg}
1249d522f475Smrg
1250d522f475Smrg/* ARGSUSED */
1251d522f475Smrgvoid
1252d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1253d522f475Smrg			 XtPointer data GCC_UNUSED,
12549a64e1c5Smrg			 XEvent *ev,
1255fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1256d522f475Smrg{
1257d522f475Smrg    TScreen *screen = TScreenOf(term);
1258d522f475Smrg
1259d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1260d522f475Smrg	screen->bellInProgress = False;
1261d522f475Smrg    }
1262d522f475Smrg}
1263d522f475Smrg
12643367019cSmrgvoid
12653367019cSmrgxtermWarning(const char *fmt,...)
12663367019cSmrg{
12673367019cSmrg    int save_err = errno;
12683367019cSmrg    va_list ap;
12693367019cSmrg
1270fa3f02f3Smrg    TRACE(("xtermWarning fmt='%s'\n", fmt));
12713367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
12723367019cSmrg    va_start(ap, fmt);
12733367019cSmrg    vfprintf(stderr, fmt, ap);
12743367019cSmrg    (void) fflush(stderr);
12753367019cSmrg
12763367019cSmrg    va_end(ap);
12773367019cSmrg    errno = save_err;
12783367019cSmrg}
12793367019cSmrg
12803367019cSmrgvoid
12813367019cSmrgxtermPerror(const char *fmt,...)
12823367019cSmrg{
12833367019cSmrg    int save_err = errno;
12843367019cSmrg    char *msg = strerror(errno);
12853367019cSmrg    va_list ap;
12863367019cSmrg
1287fa3f02f3Smrg    TRACE(("xtermPerror fmt='%s', msg='%s'\n", fmt, NonNull(msg)));
12883367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
12893367019cSmrg    va_start(ap, fmt);
12903367019cSmrg    vfprintf(stderr, fmt, ap);
12913367019cSmrg    fprintf(stderr, ": %s\n", msg);
12923367019cSmrg    (void) fflush(stderr);
12933367019cSmrg
12943367019cSmrg    va_end(ap);
12953367019cSmrg    errno = save_err;
12963367019cSmrg}
12973367019cSmrg
1298d522f475SmrgWindow
1299c219fbebSmrgWMFrameWindow(XtermWidget xw)
1300d522f475Smrg{
1301d522f475Smrg    Window win_root, win_current, *children;
1302d522f475Smrg    Window win_parent = 0;
1303d522f475Smrg    unsigned int nchildren;
1304d522f475Smrg
1305c219fbebSmrg    win_current = XtWindow(xw);
1306d522f475Smrg
1307d522f475Smrg    /* find the parent which is child of root */
1308d522f475Smrg    do {
1309d522f475Smrg	if (win_parent)
1310d522f475Smrg	    win_current = win_parent;
1311c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1312d522f475Smrg		   win_current,
1313d522f475Smrg		   &win_root,
1314d522f475Smrg		   &win_parent,
1315d522f475Smrg		   &children,
1316d522f475Smrg		   &nchildren);
1317d522f475Smrg	XFree(children);
1318d522f475Smrg    } while (win_root != win_parent);
1319d522f475Smrg
1320d522f475Smrg    return win_current;
1321d522f475Smrg}
1322d522f475Smrg
1323d522f475Smrg#if OPT_DABBREV
1324d522f475Smrg/*
1325d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1326d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1327d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1328d522f475Smrg * expansions and ignores one of them if they are identical.
1329d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1330d522f475Smrg */
1331d522f475Smrg
1332d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1333d522f475Smrg
1334d522f475Smrgstatic int
1335fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1336d522f475Smrg{
1337b7c89284Ssnj    int result = -1;
1338b7c89284Ssnj    int firstLine = -(screen->savedlines);
1339d522f475Smrg
1340b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1341b7c89284Ssnj    while (cell->row >= firstLine) {
1342b7c89284Ssnj	if (--(cell->col) >= 0) {
1343b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1344b7c89284Ssnj	    break;
1345b7c89284Ssnj	}
1346b7c89284Ssnj	if (--(cell->row) < firstLine)
1347b7c89284Ssnj	    break;		/* ...there is no previous line */
1348b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1349b7c89284Ssnj	cell->col = MaxCols(screen);
1350b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1351b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1352d522f475Smrg	    break;
1353b7c89284Ssnj	}
1354d522f475Smrg    }
1355b7c89284Ssnj    return result;
1356d522f475Smrg}
1357d522f475Smrg
1358d522f475Smrgstatic char *
13599a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1360d522f475Smrg{
13619a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1362d522f475Smrg    char *abword;
1363d522f475Smrg    int c;
13649a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1365b7c89284Ssnj    char *result = 0;
1366d522f475Smrg
1367b7c89284Ssnj    abword = ab_end;
1368d522f475Smrg    *abword = '\0';		/* end of string marker */
1369d522f475Smrg
1370b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1371b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
13729a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1373b7c89284Ssnj	    *(--abword) = (char) c;
1374d522f475Smrg    }
1375d522f475Smrg
1376b7c89284Ssnj    if (c >= 0) {
1377b7c89284Ssnj	result = abword;
1378b7c89284Ssnj    } else if (abword != ab_end) {
1379b7c89284Ssnj	result = abword;
1380b7c89284Ssnj    }
1381b7c89284Ssnj
1382b7c89284Ssnj    if (result != 0) {
1383b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1384b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1385b7c89284Ssnj	    ;			/* skip preceding spaces */
1386b7c89284Ssnj	}
1387b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1388b7c89284Ssnj    }
1389b7c89284Ssnj    return result;
1390d522f475Smrg}
1391d522f475Smrg
1392d522f475Smrgstatic int
13939a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1394d522f475Smrg{
13959a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1396d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1397d522f475Smrg
1398b7c89284Ssnj    static CELL cell;
1399d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1400d522f475Smrg    static unsigned int expansions;
1401d522f475Smrg
1402d522f475Smrg    char *expansion;
1403d522f475Smrg    Char *copybuffer;
1404d522f475Smrg    size_t hint_len;
1405cd3331d0Smrg    size_t del_cnt;
1406cd3331d0Smrg    size_t buf_cnt;
1407b7c89284Ssnj    int result = 0;
1408b7c89284Ssnj    LineData *ld;
1409d522f475Smrg
1410d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1411d522f475Smrg	expansions = 0;
1412b7c89284Ssnj	cell.col = screen->cur_col;
1413b7c89284Ssnj	cell.row = screen->cur_row;
1414b7c89284Ssnj
1415b7c89284Ssnj	if (dabbrev_hint != 0)
1416b7c89284Ssnj	    free(dabbrev_hint);
1417b7c89284Ssnj
14189a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1419b7c89284Ssnj
1420b7c89284Ssnj	    if (lastexpansion != 0)
1421b7c89284Ssnj		free(lastexpansion);
1422b7c89284Ssnj
1423b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1424b7c89284Ssnj
1425b7c89284Ssnj		/* make own copy */
1426b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1427b7c89284Ssnj		    screen->dabbrev_working = True;
1428b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1429b7c89284Ssnj		}
1430cd3331d0Smrg	    } else {
1431cd3331d0Smrg		return result;
1432b7c89284Ssnj	    }
1433cd3331d0Smrg	} else {
1434cd3331d0Smrg	    return result;
1435d522f475Smrg	}
1436b7c89284Ssnj	if (!screen->dabbrev_working) {
1437b7c89284Ssnj	    if (lastexpansion != 0) {
1438b7c89284Ssnj		free(lastexpansion);
1439b7c89284Ssnj		lastexpansion = 0;
1440b7c89284Ssnj	    }
1441b7c89284Ssnj	    return result;
1442b7c89284Ssnj	}
1443d522f475Smrg    }
1444d522f475Smrg
1445cd3331d0Smrg    if (dabbrev_hint == 0)
1446cd3331d0Smrg	return result;
1447cd3331d0Smrg
1448d522f475Smrg    hint_len = strlen(dabbrev_hint);
1449d522f475Smrg    for (;;) {
14509a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1451d522f475Smrg	    if (expansions >= 2) {
1452d522f475Smrg		expansions = 0;
1453b7c89284Ssnj		cell.col = screen->cur_col;
1454b7c89284Ssnj		cell.row = screen->cur_row;
1455d522f475Smrg		continue;
1456d522f475Smrg	    }
1457d522f475Smrg	    break;
1458d522f475Smrg	}
1459d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1460d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1461d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1462d522f475Smrg	    break;
1463d522f475Smrg    }
1464d522f475Smrg
1465b7c89284Ssnj    if (expansion != 0) {
1466b7c89284Ssnj	del_cnt = strlen(lastexpansion) - hint_len;
1467b7c89284Ssnj	buf_cnt = del_cnt + strlen(expansion) - hint_len;
1468b7c89284Ssnj
1469b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1470b7c89284Ssnj	    /* delete previous expansion */
1471b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1472b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1473b7c89284Ssnj		    expansion + hint_len,
1474b7c89284Ssnj		    strlen(expansion) - hint_len);
1475cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1476b7c89284Ssnj	    /* v_write() just reset our flag */
1477b7c89284Ssnj	    screen->dabbrev_working = True;
1478b7c89284Ssnj	    free(copybuffer);
1479b7c89284Ssnj
1480b7c89284Ssnj	    free(lastexpansion);
1481b7c89284Ssnj
1482b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1483b7c89284Ssnj		result = 1;
1484b7c89284Ssnj		expansions++;
1485b7c89284Ssnj	    }
1486b7c89284Ssnj	}
1487b7c89284Ssnj    }
1488b7c89284Ssnj
1489b7c89284Ssnj    return result;
1490d522f475Smrg}
1491d522f475Smrg
1492d522f475Smrg/*ARGSUSED*/
1493d522f475Smrgvoid
1494b7c89284SsnjHandleDabbrevExpand(Widget w,
14959a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1496fa3f02f3Smrg		    String *params GCC_UNUSED,
1497d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1498d522f475Smrg{
1499b7c89284Ssnj    XtermWidget xw;
1500b7c89284Ssnj
1501cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1502b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
15039a64e1c5Smrg	if (!dabbrev_expand(xw))
1504cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1505d522f475Smrg    }
1506d522f475Smrg}
1507d522f475Smrg#endif /* OPT_DABBREV */
1508d522f475Smrg
1509d522f475Smrg#if OPT_MAXIMIZE
1510d522f475Smrg/*ARGSUSED*/
1511d522f475Smrgvoid
1512b7c89284SsnjHandleDeIconify(Widget w,
15139a64e1c5Smrg		XEvent *event GCC_UNUSED,
1514fa3f02f3Smrg		String *params GCC_UNUSED,
1515d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1516d522f475Smrg{
1517b7c89284Ssnj    XtermWidget xw;
1518b7c89284Ssnj
1519b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1520b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1521c219fbebSmrg	XMapWindow(screen->display, VShellWindow(xw));
1522d522f475Smrg    }
1523d522f475Smrg}
1524d522f475Smrg
1525d522f475Smrg/*ARGSUSED*/
1526d522f475Smrgvoid
1527b7c89284SsnjHandleIconify(Widget w,
15289a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1529fa3f02f3Smrg	      String *params GCC_UNUSED,
1530d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1531d522f475Smrg{
1532b7c89284Ssnj    XtermWidget xw;
1533b7c89284Ssnj
1534b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1535b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1536d522f475Smrg	XIconifyWindow(screen->display,
1537c219fbebSmrg		       VShellWindow(xw),
1538d522f475Smrg		       DefaultScreen(screen->display));
1539d522f475Smrg    }
1540d522f475Smrg}
1541d522f475Smrg
1542d522f475Smrgint
1543c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1544d522f475Smrg{
1545c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1546d522f475Smrg    XSizeHints hints;
1547d522f475Smrg    long supp = 0;
1548d522f475Smrg    Window root_win;
1549d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1550d522f475Smrg    int root_y = -1;
1551d522f475Smrg    unsigned root_border;
1552d522f475Smrg    unsigned root_depth;
15533367019cSmrg    int code;
1554d522f475Smrg
1555d522f475Smrg    if (XGetGeometry(screen->display,
1556c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1557d522f475Smrg		     &root_win,
1558d522f475Smrg		     &root_x,
1559d522f475Smrg		     &root_y,
1560d522f475Smrg		     width,
1561d522f475Smrg		     height,
1562d522f475Smrg		     &root_border,
1563d522f475Smrg		     &root_depth)) {
1564d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1565d522f475Smrg	       root_x,
1566d522f475Smrg	       root_y,
1567d522f475Smrg	       *width,
1568d522f475Smrg	       *height,
1569d522f475Smrg	       root_border));
1570d522f475Smrg
1571d522f475Smrg	*width -= (root_border * 2);
1572d522f475Smrg	*height -= (root_border * 2);
1573d522f475Smrg
1574d522f475Smrg	hints.flags = PMaxSize;
1575d522f475Smrg	if (XGetWMNormalHints(screen->display,
1576c219fbebSmrg			      VShellWindow(xw),
1577d522f475Smrg			      &hints,
1578d522f475Smrg			      &supp)
1579d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1580d522f475Smrg
1581d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1582d522f475Smrg		   hints.max_width,
1583d522f475Smrg		   hints.max_height));
1584d522f475Smrg
1585d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1586b7c89284Ssnj		*width = (unsigned) hints.max_width;
1587d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1588b7c89284Ssnj		*height = (unsigned) hints.max_height;
1589d522f475Smrg	}
15903367019cSmrg	code = 1;
15913367019cSmrg    } else {
15923367019cSmrg	*width = 0;
15933367019cSmrg	*height = 0;
15943367019cSmrg	code = 0;
1595d522f475Smrg    }
15963367019cSmrg    return code;
1597d522f475Smrg}
1598d522f475Smrg
1599d522f475Smrgvoid
1600c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1601d522f475Smrg{
1602c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1603d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1604d522f475Smrg    unsigned root_width, root_height;
16053367019cSmrg    Boolean success = False;
1606d522f475Smrg
16073367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
16083367019cSmrg	   maximize,
16093367019cSmrg	   (maximize
16103367019cSmrg	    ? "maximize"
16113367019cSmrg	    : "restore")));
1612d522f475Smrg
16133367019cSmrg    /*
16143367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
16153367019cSmrg     * as well as the estimated root-window size.
16163367019cSmrg     */
16173367019cSmrg    if (maximize
16183367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
16193367019cSmrg	&& xtermGetWinAttrs(screen->display,
16203367019cSmrg			    WMFrameWindow(xw),
16213367019cSmrg			    &wm_attrs)
16223367019cSmrg	&& xtermGetWinAttrs(screen->display,
16233367019cSmrg			    VShellWindow(xw),
16243367019cSmrg			    &vshell_attrs)) {
16253367019cSmrg
16263367019cSmrg	if (screen->restore_data != True
16273367019cSmrg	    || screen->restore_width != root_width
16283367019cSmrg	    || screen->restore_height != root_height) {
16293367019cSmrg	    screen->restore_data = True;
16303367019cSmrg	    screen->restore_x = wm_attrs.x + wm_attrs.border_width;
16313367019cSmrg	    screen->restore_y = wm_attrs.y + wm_attrs.border_width;
16323367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
16333367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
16343367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1635d522f475Smrg		   screen->restore_x,
1636d522f475Smrg		   screen->restore_y,
1637d522f475Smrg		   screen->restore_width,
1638d522f475Smrg		   screen->restore_height));
16393367019cSmrg	}
1640d522f475Smrg
16413367019cSmrg	/* subtract wm decoration dimensions */
16423367019cSmrg	root_width -= (unsigned) ((wm_attrs.width - vshell_attrs.width)
16433367019cSmrg				  + (wm_attrs.border_width * 2));
16443367019cSmrg	root_height -= (unsigned) ((wm_attrs.height - vshell_attrs.height)
16453367019cSmrg				   + (wm_attrs.border_width * 2));
16463367019cSmrg	success = True;
16473367019cSmrg    } else if (screen->restore_data) {
16483367019cSmrg	success = True;
16493367019cSmrg	maximize = 0;
16503367019cSmrg    }
16513367019cSmrg
16523367019cSmrg    if (success) {
16533367019cSmrg	switch (maximize) {
16543367019cSmrg	case 3:
16553367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
16563367019cSmrg	    break;
16573367019cSmrg	case 2:
16583367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
16593367019cSmrg	    break;
16603367019cSmrg	case 1:
16613367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
16623367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
16633367019cSmrg			      0 + wm_attrs.border_width,	/* x */
16643367019cSmrg			      0 + wm_attrs.border_width,	/* y */
16653367019cSmrg			      root_width,
16663367019cSmrg			      root_height);
16673367019cSmrg	    break;
16683367019cSmrg
16693367019cSmrg	default:
16703367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
16713367019cSmrg	    if (screen->restore_data) {
16723367019cSmrg		screen->restore_data = False;
16733367019cSmrg
16743367019cSmrg		TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
16753367019cSmrg		       screen->restore_x,
16763367019cSmrg		       screen->restore_y,
16773367019cSmrg		       screen->restore_width,
16783367019cSmrg		       screen->restore_height));
16793367019cSmrg
16803367019cSmrg		XMoveResizeWindow(screen->display,
16813367019cSmrg				  VShellWindow(xw),
16823367019cSmrg				  screen->restore_x,
16833367019cSmrg				  screen->restore_y,
16843367019cSmrg				  screen->restore_width,
16853367019cSmrg				  screen->restore_height);
16863367019cSmrg	    }
16873367019cSmrg	    break;
1688d522f475Smrg	}
1689d522f475Smrg    }
1690d522f475Smrg}
1691d522f475Smrg
1692d522f475Smrg/*ARGSUSED*/
1693d522f475Smrgvoid
1694b7c89284SsnjHandleMaximize(Widget w,
16959a64e1c5Smrg	       XEvent *event GCC_UNUSED,
1696fa3f02f3Smrg	       String *params GCC_UNUSED,
1697d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1698d522f475Smrg{
1699b7c89284Ssnj    XtermWidget xw;
1700b7c89284Ssnj
1701b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1702b7c89284Ssnj	RequestMaximize(xw, 1);
1703d522f475Smrg    }
1704d522f475Smrg}
1705d522f475Smrg
1706d522f475Smrg/*ARGSUSED*/
1707d522f475Smrgvoid
1708b7c89284SsnjHandleRestoreSize(Widget w,
17099a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1710fa3f02f3Smrg		  String *params GCC_UNUSED,
1711d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1712d522f475Smrg{
1713b7c89284Ssnj    XtermWidget xw;
1714b7c89284Ssnj
1715b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1716b7c89284Ssnj	RequestMaximize(xw, 0);
1717d522f475Smrg    }
1718d522f475Smrg}
1719d522f475Smrg#endif /* OPT_MAXIMIZE */
1720d522f475Smrg
1721d522f475Smrgvoid
1722d522f475SmrgRedraw(void)
1723d522f475Smrg{
1724d522f475Smrg    TScreen *screen = TScreenOf(term);
1725d522f475Smrg    XExposeEvent event;
1726d522f475Smrg
1727d522f475Smrg    TRACE(("Redraw\n"));
1728d522f475Smrg
1729d522f475Smrg    event.type = Expose;
1730d522f475Smrg    event.display = screen->display;
1731d522f475Smrg    event.x = 0;
1732d522f475Smrg    event.y = 0;
1733d522f475Smrg    event.count = 0;
1734d522f475Smrg
1735d522f475Smrg    if (VWindow(screen)) {
1736d522f475Smrg	event.window = VWindow(screen);
1737d522f475Smrg	event.width = term->core.width;
1738d522f475Smrg	event.height = term->core.height;
1739d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
17409a64e1c5Smrg						       (XEvent *) &event,
1741d522f475Smrg						       NULL);
1742d522f475Smrg	if (ScrollbarWidth(screen)) {
1743d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
17449a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
1745d522f475Smrg	}
1746d522f475Smrg    }
1747d522f475Smrg#if OPT_TEK4014
1748d522f475Smrg    if (TEK4014_SHOWN(term)) {
1749cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1750d522f475Smrg	event.window = TWindow(tekscr);
1751d522f475Smrg	event.width = tekWidget->core.width;
1752d522f475Smrg	event.height = tekWidget->core.height;
17539a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
1754d522f475Smrg    }
1755d522f475Smrg#endif
1756d522f475Smrg}
1757d522f475Smrg
1758d522f475Smrg#ifdef VMS
1759d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1760d522f475Smrg#else
1761d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1762d522f475Smrg#endif
1763d522f475Smrg
1764d522f475Smrgvoid
1765d522f475Smrgtimestamp_filename(char *dst, const char *src)
1766d522f475Smrg{
1767d522f475Smrg    time_t tstamp;
1768d522f475Smrg    struct tm *tstruct;
1769d522f475Smrg
1770d522f475Smrg    tstamp = time((time_t *) 0);
1771d522f475Smrg    tstruct = localtime(&tstamp);
1772d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1773d522f475Smrg	    src,
17743367019cSmrg	    (int) tstruct->tm_year + 1900,
1775d522f475Smrg	    tstruct->tm_mon + 1,
1776d522f475Smrg	    tstruct->tm_mday,
1777d522f475Smrg	    tstruct->tm_hour,
1778d522f475Smrg	    tstruct->tm_min,
1779d522f475Smrg	    tstruct->tm_sec);
1780d522f475Smrg}
1781d522f475Smrg
1782d522f475Smrgint
1783d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1784d522f475Smrg{
1785d522f475Smrg    int fd;
1786d522f475Smrg    struct stat sb;
1787d522f475Smrg
1788d522f475Smrg#ifdef VMS
1789d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1790d522f475Smrg	int the_error = errno;
17913367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
17923367019cSmrg		     path,
17933367019cSmrg		     the_error,
17943367019cSmrg		     SysErrorMsg(the_error));
1795d522f475Smrg	return -1;
1796d522f475Smrg    }
1797d522f475Smrg    chown(path, uid, gid);
1798d522f475Smrg#else
1799d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1800d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1801d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1802d522f475Smrg	int the_error = errno;
18033367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
18043367019cSmrg		     path,
18053367019cSmrg		     the_error,
18063367019cSmrg		     SysErrorMsg(the_error));
1807d522f475Smrg	return -1;
1808d522f475Smrg    }
1809d522f475Smrg#endif
1810d522f475Smrg
1811d522f475Smrg    /*
1812d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1813d522f475Smrg     * we do any damage, and that it is not world-writable.
1814d522f475Smrg     */
1815d522f475Smrg    if (fstat(fd, &sb) < 0
1816d522f475Smrg	|| sb.st_uid != uid
1817d522f475Smrg	|| (sb.st_mode & 022) != 0) {
18183367019cSmrg	xtermWarning("you do not own %s\n", path);
1819d522f475Smrg	close(fd);
1820d522f475Smrg	return -1;
1821d522f475Smrg    }
1822d522f475Smrg    return fd;
1823d522f475Smrg}
1824d522f475Smrg
1825d522f475Smrg#ifndef VMS
1826d522f475Smrg/*
1827d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1828d522f475Smrg * We could emulate this with careful use of access() and following
1829d522f475Smrg * symbolic links, but that is messy and has race conditions.
1830d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1831d522f475Smrg * being available.
1832d522f475Smrg *
1833d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1834d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1835d522f475Smrg * for the debug logs.
1836d522f475Smrg *
1837d522f475Smrg * Returns
1838d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1839d522f475Smrg *	-1 on error, e.g., cannot fork
1840d522f475Smrg *	 0 otherwise.
1841d522f475Smrg */
1842d522f475Smrgint
1843712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
1844d522f475Smrg{
1845d522f475Smrg    int fd;
1846d522f475Smrg    pid_t pid;
1847d522f475Smrg    int retval = 0;
1848d522f475Smrg    int childstat = 0;
1849d522f475Smrg#ifndef HAVE_WAITPID
1850d522f475Smrg    int waited;
18513367019cSmrg    void (*chldfunc) (int);
1852d522f475Smrg
1853d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1854d522f475Smrg#endif /* HAVE_WAITPID */
1855d522f475Smrg
1856d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1857d522f475Smrg	   (int) uid, (int) geteuid(),
1858d522f475Smrg	   (int) gid, (int) getegid(),
1859d522f475Smrg	   append,
1860d522f475Smrg	   pathname,
1861d522f475Smrg	   mode));
1862d522f475Smrg
1863d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1864d522f475Smrg	fd = open(pathname,
1865d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1866d522f475Smrg		  mode);
1867d522f475Smrg	if (fd >= 0)
1868d522f475Smrg	    close(fd);
1869d522f475Smrg	return (fd >= 0);
1870d522f475Smrg    }
1871d522f475Smrg
1872d522f475Smrg    pid = fork();
1873d522f475Smrg    switch (pid) {
1874d522f475Smrg    case 0:			/* child */
1875d522f475Smrg	if (setgid(gid) == -1
1876d522f475Smrg	    || setuid(uid) == -1) {
1877d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1878d522f475Smrg	    retval = 1;
1879d522f475Smrg	} else {
1880d522f475Smrg	    fd = open(pathname,
1881d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1882d522f475Smrg		      mode);
1883d522f475Smrg	    if (fd >= 0) {
1884d522f475Smrg		close(fd);
1885d522f475Smrg		retval = 0;
1886d522f475Smrg	    } else {
1887d522f475Smrg		retval = 1;
1888d522f475Smrg	    }
1889d522f475Smrg	}
1890d522f475Smrg	_exit(retval);
1891d522f475Smrg	/* NOTREACHED */
1892d522f475Smrg    case -1:			/* error */
1893d522f475Smrg	return retval;
1894d522f475Smrg    default:			/* parent */
1895d522f475Smrg#ifdef HAVE_WAITPID
1896d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1897d522f475Smrg#ifdef EINTR
1898d522f475Smrg	    if (errno == EINTR)
1899d522f475Smrg		continue;
1900d522f475Smrg#endif /* EINTR */
1901d522f475Smrg#ifdef ERESTARTSYS
1902d522f475Smrg	    if (errno == ERESTARTSYS)
1903d522f475Smrg		continue;
1904d522f475Smrg#endif /* ERESTARTSYS */
1905d522f475Smrg	    break;
1906d522f475Smrg	}
1907d522f475Smrg#else /* HAVE_WAITPID */
1908d522f475Smrg	waited = wait(&childstat);
1909d522f475Smrg	signal(SIGCHLD, chldfunc);
1910d522f475Smrg	/*
1911d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1912d522f475Smrg	   we might have missed the termination of our screen child.
1913d522f475Smrg	   If we can check for this possibility without hanging, do so.
1914d522f475Smrg	 */
1915d522f475Smrg	do
1916cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
19173367019cSmrg		NormalExit();
1918d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1919d522f475Smrg#endif /* HAVE_WAITPID */
1920d522f475Smrg#ifndef WIFEXITED
1921d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1922d522f475Smrg#endif
1923d522f475Smrg	if (WIFEXITED(childstat))
1924d522f475Smrg	    retval = 1;
1925d522f475Smrg	return retval;
1926d522f475Smrg    }
1927d522f475Smrg}
1928d522f475Smrg#endif /* !VMS */
1929d522f475Smrg
1930d522f475Smrgint
1931fa3f02f3SmrgxtermResetIds(TScreen *screen)
1932d522f475Smrg{
1933d522f475Smrg    int result = 0;
1934d522f475Smrg    if (setgid(screen->gid) == -1) {
19353367019cSmrg	xtermWarning("unable to reset group-id\n");
1936d522f475Smrg	result = -1;
1937d522f475Smrg    }
1938d522f475Smrg    if (setuid(screen->uid) == -1) {
19393367019cSmrg	xtermWarning("unable to reset user-id\n");
1940d522f475Smrg	result = -1;
1941d522f475Smrg    }
1942d522f475Smrg    return result;
1943d522f475Smrg}
1944d522f475Smrg
1945d522f475Smrg#ifdef ALLOWLOGGING
1946d522f475Smrg
1947d522f475Smrg/*
1948d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1949d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1950d522f475Smrg */
1951d522f475Smrg
1952d522f475Smrg#ifdef ALLOWLOGFILEEXEC
19533367019cSmrgstatic void
1954d522f475Smrglogpipe(int sig GCC_UNUSED)
1955d522f475Smrg{
1956cd3331d0Smrg    XtermWidget xw = term;
1957cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1958d522f475Smrg
19593367019cSmrg    DEBUG_MSG("handle:logpipe\n");
1960d522f475Smrg#ifdef SYSV
1961d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
1962d522f475Smrg#endif /* SYSV */
1963d522f475Smrg    if (screen->logging)
1964cd3331d0Smrg	CloseLog(xw);
1965d522f475Smrg}
1966d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1967d522f475Smrg
1968d522f475Smrgvoid
1969cd3331d0SmrgStartLog(XtermWidget xw)
1970d522f475Smrg{
1971d522f475Smrg    static char *log_default;
1972cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1973d522f475Smrg
1974d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
1975d522f475Smrg	return;
1976d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
1977d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
1978d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
1979d522f475Smrg			 0640);
1980d522f475Smrg    if (screen->logfd < 0)
1981d522f475Smrg	return;			/* open failed */
1982d522f475Smrg#else /*VMS */
1983d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
1984d522f475Smrg	if (screen->logfile)
1985d522f475Smrg	    free(screen->logfile);
1986d522f475Smrg	if (log_default == NULL) {
1987d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
1988d522f475Smrg	    char log_def_name[512];	/* see sprintf below */
1989d522f475Smrg	    char hostname[255 + 1];	/* Internet standard limit (RFC 1035):
1990d522f475Smrg					   ``To simplify implementations, the
1991d522f475Smrg					   total length of a domain name (i.e.,
1992d522f475Smrg					   label octets and label length
1993d522f475Smrg					   octets) is restricted to 255 octets
1994d522f475Smrg					   or less.'' */
1995d522f475Smrg	    char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1];
1996d522f475Smrg	    time_t now;
1997d522f475Smrg	    struct tm *ltm;
1998d522f475Smrg
1999d522f475Smrg	    now = time((time_t *) 0);
2000d522f475Smrg	    ltm = (struct tm *) localtime(&now);
2001d522f475Smrg	    if ((gethostname(hostname, sizeof(hostname)) == 0) &&
2002d522f475Smrg		(strftime(yyyy_mm_dd_hh_mm_ss,
2003d522f475Smrg			  sizeof(yyyy_mm_dd_hh_mm_ss),
2004d522f475Smrg			  "%Y.%m.%d.%H.%M.%S", ltm) > 0)) {
2005d522f475Smrg		(void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d",
2006d522f475Smrg			       hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid());
2007d522f475Smrg	    }
2008d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
2009d522f475Smrg		return;
2010d522f475Smrg#else
201194644356Smrg	    static const char log_def_name[] = "XtermLog.XXXXXX";
2012d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
2013d522f475Smrg		return;
2014d522f475Smrg
2015d522f475Smrg	    mktemp(log_default);
2016d522f475Smrg#endif
2017d522f475Smrg	}
2018d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
2019d522f475Smrg	    return;
2020d522f475Smrg    }
2021d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
2022d522f475Smrg#ifdef ALLOWLOGFILEEXEC
2023d522f475Smrg	/*
2024d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
2025d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2026d522f475Smrg	 * done through escape sequences....  You have been warned.
2027d522f475Smrg	 */
2028d522f475Smrg	int pid;
2029d522f475Smrg	int p[2];
2030d522f475Smrg	static char *shell;
20313367019cSmrg	struct passwd pw;
20323367019cSmrg
20333367019cSmrg	if ((shell = x_getenv("SHELL")) == NULL) {
20343367019cSmrg
20353367019cSmrg	    if (x_getpwuid(screen->uid, &pw)) {
20363367019cSmrg		char *name = x_getlogin(screen->uid, &pw);
20373367019cSmrg		if (*(pw.pw_shell)) {
20383367019cSmrg		    shell = pw.pw_shell;
20393367019cSmrg		}
20403367019cSmrg		free(name);
20413367019cSmrg	    }
20423367019cSmrg	}
20433367019cSmrg
20443367019cSmrg	if (shell == 0) {
20453367019cSmrg	    static char dummy[] = "/bin/sh";
20463367019cSmrg	    shell = dummy;
20473367019cSmrg	}
20483367019cSmrg
20493367019cSmrg	if (access(shell, X_OK) != 0) {
20503367019cSmrg	    xtermPerror("Can't execute `%s'\n", shell);
20513367019cSmrg	    return;
20523367019cSmrg	}
2053d522f475Smrg
20543367019cSmrg	if (pipe(p) < 0) {
20553367019cSmrg	    xtermPerror("Can't make a pipe connection\n");
2056d522f475Smrg	    return;
20573367019cSmrg	} else if ((pid = fork()) < 0) {
20583367019cSmrg	    xtermPerror("Can't fork...\n");
20593367019cSmrg	    return;
20603367019cSmrg	}
2061d522f475Smrg	if (pid == 0) {		/* child */
2062d522f475Smrg	    /*
2063d522f475Smrg	     * Close our output (we won't be talking back to the
2064d522f475Smrg	     * parent), and redirect our child's output to the
2065d522f475Smrg	     * original stderr.
2066d522f475Smrg	     */
2067d522f475Smrg	    close(p[1]);
2068d522f475Smrg	    dup2(p[0], 0);
2069d522f475Smrg	    close(p[0]);
2070d522f475Smrg	    dup2(fileno(stderr), 1);
2071d522f475Smrg	    dup2(fileno(stderr), 2);
2072d522f475Smrg
2073d522f475Smrg	    close(fileno(stderr));
2074d522f475Smrg	    close(ConnectionNumber(screen->display));
2075d522f475Smrg	    close(screen->respond);
2076d522f475Smrg
2077d522f475Smrg	    signal(SIGHUP, SIG_DFL);
2078d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
2079d522f475Smrg
2080d522f475Smrg	    /* (this is redundant) */
2081d522f475Smrg	    if (xtermResetIds(screen) < 0)
2082d522f475Smrg		exit(ERROR_SETUID);
2083d522f475Smrg
20843367019cSmrg	    if (access(shell, X_OK) == 0) {
20853367019cSmrg		execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
20863367019cSmrg		xtermWarning("Can't exec `%s'\n", &screen->logfile[1]);
20873367019cSmrg	    } else {
20883367019cSmrg		xtermWarning("Can't execute `%s'\n", shell);
20893367019cSmrg	    }
2090d522f475Smrg	    exit(ERROR_LOGEXEC);
2091d522f475Smrg	}
2092d522f475Smrg	close(p[0]);
2093d522f475Smrg	screen->logfd = p[1];
2094d522f475Smrg	signal(SIGPIPE, logpipe);
2095d522f475Smrg#else
2096cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2097cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2098d522f475Smrg	return;
2099d522f475Smrg#endif
2100d522f475Smrg    } else {
2101d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2102d522f475Smrg					   screen->gid,
2103d522f475Smrg					   screen->logfile,
2104d522f475Smrg					   (log_default != 0))) < 0)
2105d522f475Smrg	    return;
2106d522f475Smrg    }
2107d522f475Smrg#endif /*VMS */
2108d522f475Smrg    screen->logstart = VTbuffer->next;
2109d522f475Smrg    screen->logging = True;
2110d522f475Smrg    update_logging();
2111d522f475Smrg}
2112d522f475Smrg
2113d522f475Smrgvoid
2114cd3331d0SmrgCloseLog(XtermWidget xw)
2115d522f475Smrg{
2116cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2117cd3331d0Smrg
2118d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2119d522f475Smrg	return;
2120cd3331d0Smrg    FlushLog(xw);
2121d522f475Smrg    close(screen->logfd);
2122d522f475Smrg    screen->logging = False;
2123d522f475Smrg    update_logging();
2124d522f475Smrg}
2125d522f475Smrg
2126d522f475Smrgvoid
2127cd3331d0SmrgFlushLog(XtermWidget xw)
2128d522f475Smrg{
2129cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2130cd3331d0Smrg
2131d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2132d522f475Smrg	Char *cp;
2133d522f475Smrg	int i;
2134d522f475Smrg
2135d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2136d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2137d522f475Smrg	if (!tt_new_output)
2138d522f475Smrg	    return;
2139d522f475Smrg	tt_new_output = False;
2140d522f475Smrg#endif /* VMS */
2141d522f475Smrg	cp = VTbuffer->next;
2142d522f475Smrg	if (screen->logstart != 0
2143cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2144cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2145d522f475Smrg	}
2146d522f475Smrg	screen->logstart = VTbuffer->next;
2147d522f475Smrg    }
2148d522f475Smrg}
2149d522f475Smrg
2150d522f475Smrg#endif /* ALLOWLOGGING */
2151d522f475Smrg
2152d522f475Smrg/***====================================================================***/
2153d522f475Smrg
2154fa3f02f3Smrgint
2155fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2156fa3f02f3Smrg{
2157fa3f02f3Smrg#define MYFMT "getVisualInfo \
2158fa3f02f3Smrgdepth %d, \
2159fa3f02f3Smrgtype %d (%s), \
2160fa3f02f3Smrgsize %d \
2161fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2162fa3f02f3Smrg#define MYARG \
2163fa3f02f3Smrg       vi->depth,\
2164fa3f02f3Smrg       vi->class,\
2165fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2166fa3f02f3Smrg       vi->colormap_size,\
2167fa3f02f3Smrg       vi->red_mask,\
2168fa3f02f3Smrg       vi->green_mask,\
2169fa3f02f3Smrg       vi->blue_mask
2170d522f475Smrg
2171fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2172fa3f02f3Smrg    Display *dpy = screen->display;
2173fa3f02f3Smrg    XVisualInfo myTemplate;
2174fa3f02f3Smrg
2175fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2176fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2177fa3f02f3Smrg								XDefaultScreen(dpy)));
2178fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2179fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2180fa3f02f3Smrg
2181fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2182fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2183fa3f02f3Smrg	    if (resource.reportColors) {
2184fa3f02f3Smrg		printf(MYFMT, MYARG);
2185fa3f02f3Smrg	    }
2186fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2187fa3f02f3Smrg	}
2188fa3f02f3Smrg    }
2189fa3f02f3Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0);
2190fa3f02f3Smrg#undef MYFMT
2191fa3f02f3Smrg#undef MYARG
2192fa3f02f3Smrg}
21933367019cSmrg
21949a64e1c5Smrg#if OPT_ISO_COLORS
21959a64e1c5Smrgstatic void
21969a64e1c5SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
21979a64e1c5Smrg{
21989a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
21999a64e1c5Smrg	XColor color;
22009a64e1c5Smrg	Colormap cmap = xw->core.colormap;
22019a64e1c5Smrg	char buffer[80];
22029a64e1c5Smrg
22039a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
22049a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
22059a64e1c5Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
22069a64e1c5Smrg	sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
22079a64e1c5Smrg		colornum,
22089a64e1c5Smrg		color.red,
22099a64e1c5Smrg		color.green,
22109a64e1c5Smrg		color.blue);
22119a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
22129a64e1c5Smrg	unparseputs(xw, buffer);
22139a64e1c5Smrg	unparseputc1(xw, final);
22149a64e1c5Smrg	unparse_end(xw);
22159a64e1c5Smrg    }
22169a64e1c5Smrg}
22179a64e1c5Smrg
2218fa3f02f3Smrgstatic void
2219fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2220fa3f02f3Smrg{
2221fa3f02f3Smrg    if (getVisualInfo(xw)) {
2222fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2223fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2224fa3f02f3Smrg    } else {
2225fa3f02f3Smrg	*typep = 0;
2226fa3f02f3Smrg	*sizep = 0;
2227fa3f02f3Smrg    }
22283367019cSmrg}
22293367019cSmrg
22303367019cSmrg#define MAX_COLORTABLE 4096
22313367019cSmrg
22323367019cSmrg/*
22333367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
22343367019cSmrg */
22353367019cSmrgstatic Boolean
22363367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
22373367019cSmrg{
22383367019cSmrg    Colormap cmap = xw->core.colormap;
22393367019cSmrg    TScreen *screen = TScreenOf(xw);
22403367019cSmrg    unsigned i;
2241fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
22423367019cSmrg
2243fa3f02f3Smrg    if (!result
22443367019cSmrg	&& length != 0
22453367019cSmrg	&& length < MAX_COLORTABLE) {
22463367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
22473367019cSmrg	if (screen->cmap_data != 0) {
22483367019cSmrg	    screen->cmap_size = length;
22493367019cSmrg
22503367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
22513367019cSmrg		screen->cmap_data[i].pixel = (unsigned long) i;
22523367019cSmrg	    }
22533367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
22543367019cSmrg					     cmap,
22553367019cSmrg					     screen->cmap_data,
22563367019cSmrg					     (int) screen->cmap_size) != 0);
22573367019cSmrg	}
22583367019cSmrg    }
2259d522f475Smrg    return result;
2260d522f475Smrg}
2261d522f475Smrg
2262d522f475Smrg/*
2263d522f475Smrg * Find closest color for "def" in "cmap".
2264d522f475Smrg * Set "def" to the resulting color.
2265d522f475Smrg *
2266d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2267d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2268d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2269d522f475Smrg *
2270d522f475Smrg * Return False if not able to find or allocate a color.
2271d522f475Smrg */
2272d522f475Smrgstatic Boolean
22739a64e1c5SmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def)
2274d522f475Smrg{
22753367019cSmrg    TScreen *screen = TScreenOf(xw);
2276d522f475Smrg    Boolean result = False;
2277d522f475Smrg    char *tried;
2278d522f475Smrg    double diff, thisRGB, bestRGB;
2279d522f475Smrg    unsigned attempts;
2280d522f475Smrg    unsigned bestInx;
22813367019cSmrg    unsigned cmap_type;
2282d522f475Smrg    unsigned cmap_size;
2283d522f475Smrg    unsigned i;
2284d522f475Smrg
2285fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2286d522f475Smrg
22873367019cSmrg    if ((cmap_type & 1) != 0) {
22883367019cSmrg
22893367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2290d522f475Smrg
2291b7c89284Ssnj	    tried = TypeCallocN(char, (size_t) cmap_size);
2292d522f475Smrg	    if (tried != 0) {
2293d522f475Smrg
2294d522f475Smrg		/*
2295d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2296d522f475Smrg		 * approximation to the requested color.
2297d522f475Smrg		 */
2298d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2299d522f475Smrg		    Boolean first = True;
2300d522f475Smrg
2301d522f475Smrg		    bestRGB = 0.0;
2302d522f475Smrg		    bestInx = 0;
2303d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2304d522f475Smrg			if (!tried[bestInx]) {
2305d522f475Smrg			    /*
2306d522f475Smrg			     * Look for the best match based on luminance.
2307d522f475Smrg			     * Measure this by the least-squares difference of
2308d522f475Smrg			     * the weighted R/G/B components from the color map
2309d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2310d522f475Smrg			     * component of the YIQ color space model for
2311d522f475Smrg			     * weights that correspond to the luminance.
2312d522f475Smrg			     */
2313d522f475Smrg#define AddColorWeight(weight, color) \
23143367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2315d522f475Smrg			    thisRGB = diff * diff
2316d522f475Smrg
2317d522f475Smrg			    AddColorWeight(0.30, red);
2318d522f475Smrg			    AddColorWeight(0.61, green);
2319d522f475Smrg			    AddColorWeight(0.11, blue);
2320d522f475Smrg
2321d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2322d522f475Smrg				first = False;
2323d522f475Smrg				bestInx = i;
2324d522f475Smrg				bestRGB = thisRGB;
2325d522f475Smrg			    }
2326d522f475Smrg			}
2327d522f475Smrg		    }
23283367019cSmrg		    if (XAllocColor(screen->display, cmap,
23293367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
23303367019cSmrg			*def = screen->cmap_data[bestInx];
23313367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
23323367019cSmrg			       def->green, def->blue));
2333d522f475Smrg			result = True;
2334d522f475Smrg			break;
2335d522f475Smrg		    }
2336d522f475Smrg		    /*
2337d522f475Smrg		     * It failed - either the color map entry was readonly, or
2338d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2339d522f475Smrg		     * so we will ignore it
2340d522f475Smrg		     */
2341d522f475Smrg		    tried[bestInx] = True;
2342d522f475Smrg		}
2343d522f475Smrg		free(tried);
2344d522f475Smrg	    }
2345d522f475Smrg	}
2346d522f475Smrg    }
2347d522f475Smrg    return result;
2348d522f475Smrg}
2349d522f475Smrg
23503367019cSmrg#ifndef ULONG_MAX
23513367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
23523367019cSmrg#endif
23533367019cSmrg
23543367019cSmrg#define CheckColor(result, value) \
23553367019cSmrg	    result = 0; \
23563367019cSmrg	    if (value.red) \
23573367019cSmrg		result |= 1; \
23583367019cSmrg	    if (value.green) \
23593367019cSmrg		result |= 2; \
23603367019cSmrg	    if (value.blue) \
23613367019cSmrg		result |= 4
23623367019cSmrg
23633367019cSmrg#define SelectColor(state, value, result) \
23643367019cSmrg	switch (state) { \
23653367019cSmrg	default: \
23663367019cSmrg	case 1: \
23673367019cSmrg	    result = value.red; \
23683367019cSmrg	    break; \
23693367019cSmrg	case 2: \
23703367019cSmrg	    result = value.green; \
23713367019cSmrg	    break; \
23723367019cSmrg	case 4: \
23733367019cSmrg	    result = value.blue; \
23743367019cSmrg	    break; \
23753367019cSmrg	}
23763367019cSmrg
23773367019cSmrg/*
23783367019cSmrg * Check if the color map consists of values in exactly one of the red, green
23793367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
23803367019cSmrg * match.
23813367019cSmrg */
23823367019cSmrgstatic int
23839a64e1c5SmrgsimpleColors(XColor *colortable, unsigned length)
23843367019cSmrg{
23853367019cSmrg    unsigned n;
2386fa3f02f3Smrg    int state = 0;
23873367019cSmrg    int check;
23883367019cSmrg
23893367019cSmrg    for (n = 0; n < length; ++n) {
23903367019cSmrg	if (state > 0) {
23913367019cSmrg	    CheckColor(check, colortable[n]);
23923367019cSmrg	    if (check > 0 && check != state) {
23933367019cSmrg		state = 0;
23943367019cSmrg		break;
23953367019cSmrg	    }
2396fa3f02f3Smrg	} else {
2397fa3f02f3Smrg	    CheckColor(state, colortable[n]);
23983367019cSmrg	}
23993367019cSmrg    }
24003367019cSmrg    switch (state) {
24013367019cSmrg    case 1:
24023367019cSmrg    case 2:
24033367019cSmrg    case 4:
24043367019cSmrg	break;
24053367019cSmrg    default:
24063367019cSmrg	state = 0;
24073367019cSmrg	break;
24083367019cSmrg    }
24093367019cSmrg    return state;
24103367019cSmrg}
24113367019cSmrg
2412fa3f02f3Smrg/*
2413fa3f02f3Smrg * Shift the mask left or right to put its most significant bit at the 16-bit
2414fa3f02f3Smrg * mark.
2415fa3f02f3Smrg */
2416fa3f02f3Smrgstatic unsigned
2417fa3f02f3SmrgnormalizeMask(unsigned mask)
2418fa3f02f3Smrg{
2419fa3f02f3Smrg    while (mask < 0x8000) {
2420fa3f02f3Smrg	mask <<= 1;
2421fa3f02f3Smrg    }
2422fa3f02f3Smrg    while (mask >= 0x10000) {
2423fa3f02f3Smrg	mask >>= 1;
2424fa3f02f3Smrg    }
2425fa3f02f3Smrg    return mask;
2426fa3f02f3Smrg}
2427fa3f02f3Smrg
24283367019cSmrgstatic unsigned
24299a64e1c5SmrgsearchColors(XColor *colortable, unsigned mask, unsigned length, unsigned
2430fa3f02f3Smrg	     color, int state)
24313367019cSmrg{
24323367019cSmrg    unsigned result = 0;
24333367019cSmrg    unsigned n;
24343367019cSmrg    unsigned long best = ULONG_MAX;
24353367019cSmrg    unsigned long diff;
24363367019cSmrg    unsigned value;
24373367019cSmrg
2438fa3f02f3Smrg    mask = normalizeMask(mask);
24393367019cSmrg    for (n = 0; n < length; ++n) {
24403367019cSmrg	SelectColor(state, colortable[n], value);
2441fa3f02f3Smrg	diff = ((color & mask) - (value & mask));
24423367019cSmrg	diff *= diff;
24433367019cSmrg	if (diff < best) {
24443367019cSmrg#if 0
24453367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
24463367019cSmrg		   n, color,
24473367019cSmrg		   colortable[n].red,
24483367019cSmrg		   colortable[n].green,
24493367019cSmrg		   colortable[n].blue,
24503367019cSmrg		   diff));
24513367019cSmrg#endif
24523367019cSmrg	    result = n;
24533367019cSmrg	    best = diff;
24543367019cSmrg	}
24553367019cSmrg    }
24563367019cSmrg    SelectColor(state, colortable[result], value);
24573367019cSmrg    return value;
24583367019cSmrg}
24593367019cSmrg
24603367019cSmrg/*
24613367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
24623367019cSmrg *
24633367019cSmrg * According to
24643367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
24653367019cSmrg *
24663367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
24673367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
24683367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
24693367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
24703367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
24713367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
24723367019cSmrg *     actual RGB values allocated.
24733367019cSmrg *
24743367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
2475fa3f02f3Smrg * case, allocateClosestRGB() is useful for the dynamic display classes such as
24763367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
24773367019cSmrg * return regular RGB triples (unless a different scheme was used for
24783367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
24793367019cSmrg * is filled in with the colors that the server supports.
24803367019cSmrg *
24813367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
24823367019cSmrg * described.  For some TrueColor configurations it merely returns a close
24833367019cSmrg * approximation, but not the closest.
24843367019cSmrg */
24853367019cSmrgstatic Boolean
24869a64e1c5SmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def)
24873367019cSmrg{
24883367019cSmrg    XColor save = *def;
24893367019cSmrg    TScreen *screen = TScreenOf(xw);
24903367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
24913367019cSmrg
24923367019cSmrg    /*
2493fa3f02f3Smrg     * If this is a statically allocated display with too many items to store
2494fa3f02f3Smrg     * in our array, i.e., TrueColor, see if we can improve on the result by
2495fa3f02f3Smrg     * using the color values actually supported by the server.
24963367019cSmrg     */
24973367019cSmrg    if (result) {
24983367019cSmrg	unsigned cmap_type;
24993367019cSmrg	unsigned cmap_size;
25003367019cSmrg	int state;
25013367019cSmrg
2502fa3f02f3Smrg	getColormapInfo(xw, &cmap_type, &cmap_size);
25033367019cSmrg
2504fa3f02f3Smrg	if (cmap_type == TrueColor) {
25053367019cSmrg	    XColor temp = *def;
25063367019cSmrg
25073367019cSmrg	    if (loadColorTable(xw, cmap_size)
25083367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
2509fa3f02f3Smrg#define SearchColors(which) \
2510fa3f02f3Smrg	temp.which = (unsigned short) searchColors(screen->cmap_data, \
2511fa3f02f3Smrg						   (unsigned) xw->visInfo->which##_mask,\
2512fa3f02f3Smrg						   cmap_size, \
2513fa3f02f3Smrg						   save.which, \
2514fa3f02f3Smrg						   state)
25153367019cSmrg		SearchColors(red);
25163367019cSmrg		SearchColors(green);
25173367019cSmrg		SearchColors(blue);
25183367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
25193367019cSmrg#if OPT_TRACE
25203367019cSmrg		    if (temp.red != save.red
25213367019cSmrg			|| temp.green != save.green
25223367019cSmrg			|| temp.blue != save.blue) {
25233367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
25243367019cSmrg			       save.red, save.green, save.blue,
25253367019cSmrg			       temp.red, temp.green, temp.blue));
25263367019cSmrg		    } else {
25273367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
25283367019cSmrg			       save.red, save.green, save.blue));
25293367019cSmrg		    }
25303367019cSmrg#endif
25313367019cSmrg		    *def = temp;
25323367019cSmrg		}
25333367019cSmrg	    }
25343367019cSmrg	}
25353367019cSmrg    }
25363367019cSmrg
25373367019cSmrg    return result;
25383367019cSmrg}
25393367019cSmrg
2540d522f475Smrg/*
2541d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2542d522f475Smrg * to 256.
2543d522f475Smrg *
2544d522f475Smrg * Returns
2545d522f475Smrg *	-1 on error
2546d522f475Smrg *	0 on no change
2547d522f475Smrg *	1 if a new color was allocated.
2548d522f475Smrg */
2549d522f475Smrgstatic int
2550d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2551d522f475Smrg		  ColorRes * res,
2552cd3331d0Smrg		  const char *spec)
2553d522f475Smrg{
2554d522f475Smrg    int result;
2555d522f475Smrg    XColor def;
2556d522f475Smrg
25573367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2558d522f475Smrg	if (
2559d522f475Smrg#if OPT_COLOR_RES
2560d522f475Smrg	       res->mode == True &&
2561d522f475Smrg#endif
2562d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
2563d522f475Smrg	    result = 0;
2564d522f475Smrg	} else {
2565d522f475Smrg	    result = 1;
2566d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
25673367019cSmrg	    res->red = def.red;
25683367019cSmrg	    res->green = def.green;
25693367019cSmrg	    res->blue = def.blue;
25703367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
25713367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
25723367019cSmrg		   def.red,
25733367019cSmrg		   def.green,
25743367019cSmrg		   def.blue,
25753367019cSmrg		   def.pixel));
2576d522f475Smrg#if OPT_COLOR_RES
2577d522f475Smrg	    if (!res->mode)
2578d522f475Smrg		result = 0;
2579d522f475Smrg	    res->mode = True;
2580d522f475Smrg#endif
2581d522f475Smrg	}
2582d522f475Smrg    } else {
2583d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2584d522f475Smrg	result = -1;
2585d522f475Smrg    }
2586d522f475Smrg    return (result);
2587d522f475Smrg}
2588d522f475Smrg
2589d522f475Smrg#if OPT_COLOR_RES
2590d522f475SmrgPixel
2591cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2592d522f475Smrg{
2593d522f475Smrg    Pixel result = 0;
2594d522f475Smrg
2595d522f475Smrg    if (res->mode) {
2596d522f475Smrg	result = res->value;
2597d522f475Smrg    } else {
2598d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2599cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2600d522f475Smrg
2601cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2602cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2603d522f475Smrg
2604cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2605cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2606d522f475Smrg		res->mode = -True;
26073367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
26083367019cSmrg			     NonNull(res->resource));
2609d522f475Smrg	    }
2610d522f475Smrg	    result = res->value;
2611d522f475Smrg	} else {
2612d522f475Smrg	    result = 0;
2613d522f475Smrg	}
2614d522f475Smrg    }
2615d522f475Smrg    return result;
2616d522f475Smrg}
2617d522f475Smrg#endif
2618d522f475Smrg
2619cd3331d0Smrgstatic int
2620cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2621cd3331d0Smrg{
2622cd3331d0Smrg    int code;
2623cd3331d0Smrg
2624cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2625cd3331d0Smrg	code = -1;
2626cd3331d0Smrg    } else {
2627cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2628cd3331d0Smrg
2629cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2630cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2631cd3331d0Smrg    }
2632cd3331d0Smrg    return code;
2633cd3331d0Smrg}
2634cd3331d0Smrg
2635cd3331d0Smrg/*
2636cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2637cd3331d0Smrg * values from the given buffer.
2638cd3331d0Smrg *
2639cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2640cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2641cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2642cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2643cd3331d0Smrg * 'first' set to the beginning of those indices.
2644cd3331d0Smrg *
2645cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2646cd3331d0Smrg */
2647d522f475Smrgstatic Bool
2648d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2649d522f475Smrg		       char *buf,
2650cd3331d0Smrg		       int first,
2651d522f475Smrg		       int final)
2652d522f475Smrg{
2653d522f475Smrg    char *name;
2654d522f475Smrg    int color;
2655d522f475Smrg    int repaint = False;
2656d522f475Smrg    int code;
2657cd3331d0Smrg    int last = (MAXCOLORS - first);
2658d522f475Smrg
2659d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2660d522f475Smrg
2661d522f475Smrg    while (buf && *buf) {
2662d522f475Smrg	name = strchr(buf, ';');
2663d522f475Smrg	if (name == NULL)
2664d522f475Smrg	    break;
2665d522f475Smrg	*name = '\0';
2666d522f475Smrg	name++;
2667d522f475Smrg	color = atoi(buf);
2668cd3331d0Smrg	if (color < 0 || color >= last)
2669cd3331d0Smrg	    break;		/* quit on any error */
2670d522f475Smrg	buf = strchr(name, ';');
2671d522f475Smrg	if (buf) {
2672d522f475Smrg	    *buf = '\0';
2673d522f475Smrg	    buf++;
2674d522f475Smrg	}
2675cd3331d0Smrg	if (!strcmp(name, "?")) {
2676cd3331d0Smrg	    ReportAnsiColorRequest(xw, color + first, final);
2677cd3331d0Smrg	} else {
2678cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
2679d522f475Smrg	    if (code < 0) {
2680d522f475Smrg		/* stop on any error */
2681d522f475Smrg		break;
2682d522f475Smrg	    } else if (code > 0) {
2683d522f475Smrg		repaint = True;
2684d522f475Smrg	    }
2685d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
2686d522f475Smrg	     * change style (dynamic colors).
2687d522f475Smrg	     */
2688d522f475Smrg	}
2689d522f475Smrg    }
2690d522f475Smrg
2691d522f475Smrg    return (repaint);
2692d522f475Smrg}
2693cd3331d0Smrg
2694cd3331d0Smrgstatic Bool
2695cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
2696cd3331d0Smrg{
2697cd3331d0Smrg    Bool repaint = False;
2698cd3331d0Smrg    int last = MAXCOLORS - start;
2699cd3331d0Smrg
2700cd3331d0Smrg    if (color >= 0 && color < last) {
2701cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
2702cd3331d0Smrg
2703cd3331d0Smrg	if (res->mode) {
2704cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
2705cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
2706cd3331d0Smrg		repaint = True;
2707cd3331d0Smrg	    }
2708cd3331d0Smrg	}
2709cd3331d0Smrg    }
2710cd3331d0Smrg    return repaint;
2711cd3331d0Smrg}
2712cd3331d0Smrg
2713cd3331d0Smrgint
2714cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
2715cd3331d0Smrg{
2716cd3331d0Smrg    int repaint = 0;
2717cd3331d0Smrg    int color;
2718cd3331d0Smrg
2719cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
2720cd3331d0Smrg    if (*buf != '\0') {
2721cd3331d0Smrg	/* reset specific colors */
2722cd3331d0Smrg	while (!IsEmpty(buf)) {
2723cd3331d0Smrg	    char *next;
2724cd3331d0Smrg
2725cd3331d0Smrg	    color = (int) strtol(buf, &next, 10);
2726cd3331d0Smrg	    if ((next == buf) || (color < 0))
2727cd3331d0Smrg		break;		/* no number at all */
2728cd3331d0Smrg	    if (next != 0) {
2729cd3331d0Smrg		if (strchr(";", *next) == 0)
2730cd3331d0Smrg		    break;	/* unexpected delimiter */
2731cd3331d0Smrg		++next;
2732cd3331d0Smrg	    }
2733cd3331d0Smrg
2734cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2735cd3331d0Smrg		++repaint;
2736cd3331d0Smrg	    }
2737cd3331d0Smrg	    buf = next;
2738cd3331d0Smrg	}
2739cd3331d0Smrg    } else {
2740cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
2741cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
2742cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2743cd3331d0Smrg		++repaint;
2744cd3331d0Smrg	    }
2745cd3331d0Smrg	}
2746cd3331d0Smrg    }
2747cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
2748cd3331d0Smrg    return repaint;
2749cd3331d0Smrg}
2750d522f475Smrg#else
27513367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
27523367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
2753d522f475Smrg#endif /* OPT_ISO_COLORS */
2754d522f475Smrg
2755fa3f02f3SmrgBoolean
27569a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
2757fa3f02f3Smrg{
2758fa3f02f3Smrg    Colormap cmap = xw->core.colormap;
2759fa3f02f3Smrg
2760fa3f02f3Smrg    return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
2761fa3f02f3Smrg}
2762fa3f02f3Smrg
27633367019cSmrgstatic Boolean
27649a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
27653367019cSmrg{
27663367019cSmrg    Boolean result = False;
27673367019cSmrg    TScreen *screen = TScreenOf(xw);
27683367019cSmrg    Colormap cmap = xw->core.colormap;
27693367019cSmrg
2770fa3f02f3Smrg    if (XParseColor(screen->display, cmap, spec, def)) {
2771fa3f02f3Smrg	XColor save_def = *def;
2772fa3f02f3Smrg	if (resource.reportColors) {
2773fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
2774fa3f02f3Smrg		   def->red, def->green, def->blue,
2775fa3f02f3Smrg		   spec);
2776fa3f02f3Smrg	}
2777fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
2778fa3f02f3Smrg	    if (resource.reportColors) {
2779fa3f02f3Smrg		if (def->red != save_def.red ||
2780fa3f02f3Smrg		    def->green != save_def.green ||
2781fa3f02f3Smrg		    def->blue != save_def.blue) {
2782fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
2783fa3f02f3Smrg			   def->red, def->green, def->blue,
2784fa3f02f3Smrg			   spec);
2785fa3f02f3Smrg		}
2786fa3f02f3Smrg	    }
2787fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
2788fa3f02f3Smrg		   def->red, def->green, def->blue));
2789fa3f02f3Smrg	    result = True;
2790fa3f02f3Smrg	}
27913367019cSmrg    }
27923367019cSmrg    return result;
27933367019cSmrg}
27943367019cSmrg
27953367019cSmrg/*
27963367019cSmrg * This provides an approximation (the closest color from xterm's palette)
27973367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
27983367019cSmrg * because of the context in which it is used.
27993367019cSmrg */
28003367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
28013367019cSmrgint
28023367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
28033367019cSmrg{
28043367019cSmrg    int result = -1;
28053367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
28063367019cSmrg    int n;
28073367019cSmrg    int best_index = -1;
28083367019cSmrg    unsigned long best_value = 0;
28093367019cSmrg    unsigned long this_value;
28103367019cSmrg    long diff_red, diff_green, diff_blue;
28113367019cSmrg
28123367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
28133367019cSmrg
28143367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
28153367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
28163367019cSmrg
28173367019cSmrg	/* ensure that we have a value for each of the colors */
28183367019cSmrg	if (!res->mode) {
28193367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
28203367019cSmrg	}
28213367019cSmrg
28223367019cSmrg	/* find the closest match */
28233367019cSmrg	if (res->mode == True) {
28243367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
28253367019cSmrg		    res->value, res->red, res->green, res->blue));
28263367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
28273367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
28283367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
28293367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
28303367019cSmrg					  + (diff_green * diff_green)
28313367019cSmrg					  + (diff_blue * diff_blue));
28323367019cSmrg	    if (best_index < 0 || this_value < best_value) {
28333367019cSmrg		best_index = n;
28343367019cSmrg		best_value = this_value;
28353367019cSmrg	    }
28363367019cSmrg	}
28373367019cSmrg    }
28383367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
28393367019cSmrg    result = best_index;
28403367019cSmrg#else
28413367019cSmrg    (void) xw;
28423367019cSmrg    (void) find_red;
28433367019cSmrg    (void) find_green;
28443367019cSmrg    (void) find_blue;
28453367019cSmrg#endif
28463367019cSmrg    return result;
28473367019cSmrg}
28483367019cSmrg
2849d522f475Smrg#if OPT_PASTE64
2850d522f475Smrgstatic void
2851fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
2852d522f475Smrg{
2853d522f475Smrg#define PDATA(a,b) { a, #b }
2854d522f475Smrg    static struct {
2855d522f475Smrg	char given;
2856cd3331d0Smrg	String result;
2857d522f475Smrg    } table[] = {
2858d522f475Smrg	PDATA('s', SELECT),
2859d522f475Smrg	    PDATA('p', PRIMARY),
2860d522f475Smrg	    PDATA('c', CLIPBOARD),
2861d522f475Smrg	    PDATA('0', CUT_BUFFER0),
2862d522f475Smrg	    PDATA('1', CUT_BUFFER1),
2863d522f475Smrg	    PDATA('2', CUT_BUFFER2),
2864d522f475Smrg	    PDATA('3', CUT_BUFFER3),
2865d522f475Smrg	    PDATA('4', CUT_BUFFER4),
2866d522f475Smrg	    PDATA('5', CUT_BUFFER5),
2867d522f475Smrg	    PDATA('6', CUT_BUFFER6),
2868d522f475Smrg	    PDATA('7', CUT_BUFFER7),
2869d522f475Smrg    };
2870d522f475Smrg
2871cd3331d0Smrg    const char *base = buf;
28723367019cSmrg    char *used;
2873d522f475Smrg    Cardinal j, n = 0;
28743367019cSmrg    String *select_args;
2875d522f475Smrg
2876d522f475Smrg    TRACE(("Manipulate selection data\n"));
2877d522f475Smrg
2878d522f475Smrg    while (*buf != ';' && *buf != '\0') {
2879d522f475Smrg	++buf;
2880d522f475Smrg    }
2881d522f475Smrg
2882d522f475Smrg    if (*buf == ';') {
2883d522f475Smrg	*buf++ = '\0';
2884d522f475Smrg
2885d522f475Smrg	if (*base == '\0')
2886d522f475Smrg	    base = "s0";
2887d522f475Smrg
28883367019cSmrg	if ((used = x_strdup(base)) != 0) {
28893367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
28903367019cSmrg		while (*base != '\0') {
28913367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
28923367019cSmrg			if (*base == table[j].given) {
28933367019cSmrg			    used[n] = *base;
28943367019cSmrg			    select_args[n++] = table[j].result;
28953367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
28963367019cSmrg			    break;
28973367019cSmrg			}
28983367019cSmrg		    }
28993367019cSmrg		    ++base;
29003367019cSmrg		}
29013367019cSmrg		used[n] = 0;
29023367019cSmrg
29033367019cSmrg		if (!strcmp(buf, "?")) {
29043367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
29053367019cSmrg			TRACE(("Getting selection\n"));
29063367019cSmrg			unparseputc1(xw, ANSI_OSC);
29073367019cSmrg			unparseputs(xw, "52");
29083367019cSmrg			unparseputc(xw, ';');
29093367019cSmrg
29103367019cSmrg			unparseputs(xw, used);
29113367019cSmrg			unparseputc(xw, ';');
29123367019cSmrg
29133367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
29143367019cSmrg			screen->base64_paste = n;
29153367019cSmrg			screen->base64_final = final;
29163367019cSmrg
29173367019cSmrg			/* terminator will be written in this call */
29183367019cSmrg			xtermGetSelection((Widget) xw,
2919fa3f02f3Smrg					  XtLastTimestampProcessed(TScreenOf(xw)->display),
29203367019cSmrg					  select_args, n,
29213367019cSmrg					  NULL);
292294644356Smrg			/*
292394644356Smrg			 * select_args is used via SelectionReceived, cannot
292494644356Smrg			 * free it here.
292594644356Smrg			 */
292694644356Smrg		    } else {
292794644356Smrg			free(select_args);
29283367019cSmrg		    }
29293367019cSmrg		} else {
29303367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
29313367019cSmrg			TRACE(("Setting selection with %s\n", buf));
29323367019cSmrg			ClearSelectionBuffer(screen);
29333367019cSmrg			while (*buf != '\0')
29343367019cSmrg			    AppendToSelectionBuffer(screen, CharOf(*buf++));
29353367019cSmrg			CompleteSelection(xw, select_args, n);
29363367019cSmrg		    }
293794644356Smrg		    free(select_args);
29383367019cSmrg		}
2939cd3331d0Smrg	    }
29403367019cSmrg	    free(used);
2941d522f475Smrg	}
2942d522f475Smrg    }
2943d522f475Smrg}
2944d522f475Smrg#endif /* OPT_PASTE64 */
2945d522f475Smrg
2946d522f475Smrg/***====================================================================***/
2947d522f475Smrg
2948cd3331d0Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
2949cd3331d0Smrg
2950d522f475Smrgstatic Bool
2951fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
2952d522f475Smrg{
2953cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2954d522f475Smrg    Bool result = False;
2955d522f475Smrg    Char *cp = *bufp;
2956d522f475Smrg    Char *next = cp;
2957d522f475Smrg
2958d522f475Smrg    (void) screen;
2959d522f475Smrg    (void) last;
2960d522f475Smrg
2961d522f475Smrg#if OPT_WIDE_CHARS
2962cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
2963d522f475Smrg	PtyData data;
2964d522f475Smrg
29659a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
2966d522f475Smrg	    if (data.utf_data != UCS_REPL
2967d522f475Smrg		&& (data.utf_data >= 128 ||
2968d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
2969d522f475Smrg		next += (data.utf_size - 1);
2970d522f475Smrg		result = True;
2971d522f475Smrg	    } else {
2972d522f475Smrg		result = False;
2973d522f475Smrg	    }
2974d522f475Smrg	} else {
2975d522f475Smrg	    result = False;
2976d522f475Smrg	}
2977d522f475Smrg    } else
2978d522f475Smrg#endif
2979d522f475Smrg#if OPT_C1_PRINT
2980d522f475Smrg	if (screen->c1_printable
2981d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
2982d522f475Smrg	result = True;
2983d522f475Smrg    } else
2984d522f475Smrg#endif
2985d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
2986d522f475Smrg	result = True;
2987d522f475Smrg    }
2988d522f475Smrg    *bufp = next;
2989d522f475Smrg    return result;
2990d522f475Smrg}
2991d522f475Smrg
2992d522f475Smrg/***====================================================================***/
2993d522f475Smrg
2994d522f475Smrg/*
2995d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
2996cd3331d0Smrg * array indices.  Compare with TermColors.
2997d522f475Smrg */
2998d522f475Smrgtypedef enum {
2999d522f475Smrg    OSC_TEXT_FG = 10
3000d522f475Smrg    ,OSC_TEXT_BG
3001d522f475Smrg    ,OSC_TEXT_CURSOR
3002d522f475Smrg    ,OSC_MOUSE_FG
3003d522f475Smrg    ,OSC_MOUSE_BG
3004d522f475Smrg#if OPT_TEK4014
3005d522f475Smrg    ,OSC_TEK_FG = 15
3006d522f475Smrg    ,OSC_TEK_BG
3007d522f475Smrg#endif
3008d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3009d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3010d522f475Smrg#endif
3011d522f475Smrg#if OPT_TEK4014
3012d522f475Smrg    ,OSC_TEK_CURSOR = 18
3013d522f475Smrg#endif
3014d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3015d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3016d522f475Smrg#endif
3017d522f475Smrg    ,OSC_NCOLORS
3018d522f475Smrg} OscTextColors;
3019d522f475Smrg
3020cd3331d0Smrg/*
3021cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3022cd3331d0Smrg */
3023cd3331d0Smrg#define OSC_RESET 100
3024cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3025cd3331d0Smrg
3026d522f475Smrgstatic Bool
3027d522f475SmrgGetOldColors(XtermWidget xw)
3028d522f475Smrg{
3029d522f475Smrg    int i;
30309a64e1c5Smrg    if (xw->work.oldColors == NULL) {
30319a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
30329a64e1c5Smrg	if (xw->work.oldColors == NULL) {
30333367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3034d522f475Smrg	    return (False);
3035d522f475Smrg	}
30369a64e1c5Smrg	xw->work.oldColors->which = 0;
3037d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
30389a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
30399a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3040d522f475Smrg	}
30419a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3042d522f475Smrg    }
3043d522f475Smrg    return (True);
3044d522f475Smrg}
3045d522f475Smrg
3046d522f475Smrgstatic int
3047d522f475SmrgoppositeColor(int n)
3048d522f475Smrg{
3049d522f475Smrg    switch (n) {
3050d522f475Smrg    case TEXT_FG:
3051d522f475Smrg	n = TEXT_BG;
3052d522f475Smrg	break;
3053d522f475Smrg    case TEXT_BG:
3054d522f475Smrg	n = TEXT_FG;
3055d522f475Smrg	break;
3056d522f475Smrg    case MOUSE_FG:
3057d522f475Smrg	n = MOUSE_BG;
3058d522f475Smrg	break;
3059d522f475Smrg    case MOUSE_BG:
3060d522f475Smrg	n = MOUSE_FG;
3061d522f475Smrg	break;
3062d522f475Smrg#if OPT_TEK4014
3063d522f475Smrg    case TEK_FG:
3064d522f475Smrg	n = TEK_BG;
3065d522f475Smrg	break;
3066d522f475Smrg    case TEK_BG:
3067d522f475Smrg	n = TEK_FG;
3068d522f475Smrg	break;
3069d522f475Smrg#endif
3070d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3071d522f475Smrg    case HIGHLIGHT_FG:
3072d522f475Smrg	n = HIGHLIGHT_BG;
3073d522f475Smrg	break;
3074d522f475Smrg    case HIGHLIGHT_BG:
3075d522f475Smrg	n = HIGHLIGHT_FG;
3076d522f475Smrg	break;
3077d522f475Smrg#endif
3078d522f475Smrg    default:
3079d522f475Smrg	break;
3080d522f475Smrg    }
3081d522f475Smrg    return n;
3082d522f475Smrg}
3083d522f475Smrg
3084d522f475Smrgstatic void
3085d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3086d522f475Smrg{
3087cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3088cd3331d0Smrg	XColor color;
3089cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3090cd3331d0Smrg	char buffer[80];
3091d522f475Smrg
3092cd3331d0Smrg	/*
3093cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3094cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3095cd3331d0Smrg	 * reporting the opposite color which would be used.
3096cd3331d0Smrg	 */
3097cd3331d0Smrg	int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
3098cd3331d0Smrg
3099cd3331d0Smrg	GetOldColors(xw);
31009a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3101cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
3102cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3103cd3331d0Smrg		color.red,
3104cd3331d0Smrg		color.green,
3105cd3331d0Smrg		color.blue);
3106712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
31079a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3108cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3109cd3331d0Smrg	unparseputs(xw, buffer);
3110cd3331d0Smrg	unparseputc1(xw, final);
3111cd3331d0Smrg	unparse_end(xw);
3112cd3331d0Smrg    }
3113d522f475Smrg}
3114d522f475Smrg
3115d522f475Smrgstatic Bool
3116d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
3117d522f475Smrg{
3118d522f475Smrg    int i;
3119d522f475Smrg
3120d522f475Smrg    /* if we were going to free old colors, this would be the place to
3121d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3122d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3123d522f475Smrg     * we could save some overhead this way.   The only case in which this
3124d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3125d522f475Smrg     * which case they can restart xterm
3126d522f475Smrg     */
3127d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3128d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
31299a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
31309a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
31319a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3132d522f475Smrg	    }
3133d522f475Smrg	    if (pNew->names[i]) {
31349a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3135d522f475Smrg	    }
31369a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3137d522f475Smrg	}
3138d522f475Smrg    }
3139d522f475Smrg    return (True);
3140d522f475Smrg}
3141d522f475Smrg
3142d522f475Smrg/*
3143d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3144d522f475Smrg * xterm is compiled.
3145d522f475Smrg */
3146d522f475Smrgstatic int
3147d522f475SmrgOscToColorIndex(OscTextColors mode)
3148d522f475Smrg{
3149d522f475Smrg    int result = 0;
3150d522f475Smrg
3151d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3152d522f475Smrg    switch (mode) {
3153d522f475Smrg	CASE(TEXT_FG);
3154d522f475Smrg	CASE(TEXT_BG);
3155d522f475Smrg	CASE(TEXT_CURSOR);
3156d522f475Smrg	CASE(MOUSE_FG);
3157d522f475Smrg	CASE(MOUSE_BG);
3158d522f475Smrg#if OPT_TEK4014
3159d522f475Smrg	CASE(TEK_FG);
3160d522f475Smrg	CASE(TEK_BG);
3161d522f475Smrg#endif
3162d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3163d522f475Smrg	CASE(HIGHLIGHT_BG);
3164d522f475Smrg	CASE(HIGHLIGHT_FG);
3165d522f475Smrg#endif
3166d522f475Smrg#if OPT_TEK4014
3167d522f475Smrg	CASE(TEK_CURSOR);
3168d522f475Smrg#endif
3169d522f475Smrg    case OSC_NCOLORS:
3170d522f475Smrg	break;
3171d522f475Smrg    }
3172d522f475Smrg    return result;
3173d522f475Smrg}
3174d522f475Smrg
3175d522f475Smrgstatic Bool
3176d522f475SmrgChangeColorsRequest(XtermWidget xw,
3177d522f475Smrg		    int start,
3178d522f475Smrg		    char *names,
3179d522f475Smrg		    int final)
3180d522f475Smrg{
3181d522f475Smrg    Bool result = False;
3182d522f475Smrg    char *thisName;
3183d522f475Smrg    ScrnColors newColors;
3184d522f475Smrg    int i, ndx;
3185d522f475Smrg
3186d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3187d522f475Smrg
3188d522f475Smrg    if (GetOldColors(xw)) {
3189d522f475Smrg	newColors.which = 0;
3190d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3191d522f475Smrg	    newColors.names[i] = NULL;
3192d522f475Smrg	}
3193d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3194d522f475Smrg	    ndx = OscToColorIndex((OscTextColors) i);
3195d522f475Smrg	    if (xw->misc.re_verse)
3196d522f475Smrg		ndx = oppositeColor(ndx);
3197d522f475Smrg
3198cd3331d0Smrg	    if (IsEmpty(names)) {
3199d522f475Smrg		newColors.names[ndx] = NULL;
3200d522f475Smrg	    } else {
3201d522f475Smrg		if (names[0] == ';')
3202d522f475Smrg		    thisName = NULL;
3203d522f475Smrg		else
3204d522f475Smrg		    thisName = names;
3205d522f475Smrg		names = strchr(names, ';');
3206d522f475Smrg		if (names != NULL) {
3207d522f475Smrg		    *names++ = '\0';
3208d522f475Smrg		}
3209fa3f02f3Smrg		if (thisName != 0) {
3210fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3211fa3f02f3Smrg			ReportColorRequest(xw, ndx, final);
32129a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
32139a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3214fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3215fa3f02f3Smrg		    }
3216d522f475Smrg		}
3217d522f475Smrg	    }
3218d522f475Smrg	}
3219d522f475Smrg
3220d522f475Smrg	if (newColors.which != 0) {
3221d522f475Smrg	    ChangeColors(xw, &newColors);
3222d522f475Smrg	    UpdateOldColors(xw, &newColors);
3223d522f475Smrg	}
3224d522f475Smrg	result = True;
3225d522f475Smrg    }
3226d522f475Smrg    return result;
3227d522f475Smrg}
3228d522f475Smrg
3229cd3331d0Smrgstatic Bool
3230cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3231cd3331d0Smrg		   int code)
3232cd3331d0Smrg{
3233cd3331d0Smrg    Bool result = False;
32349a64e1c5Smrg#if OPT_COLOR_RES
3235cd3331d0Smrg    const char *thisName;
3236cd3331d0Smrg    ScrnColors newColors;
3237cd3331d0Smrg    int ndx;
32389a64e1c5Smrg#endif
3239cd3331d0Smrg
3240cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3241cd3331d0Smrg
3242cd3331d0Smrg#if OPT_COLOR_RES
3243cd3331d0Smrg    if (GetOldColors(xw)) {
3244cd3331d0Smrg	ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3245cd3331d0Smrg	if (xw->misc.re_verse)
3246cd3331d0Smrg	    ndx = oppositeColor(ndx);
3247cd3331d0Smrg
3248cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3249cd3331d0Smrg
3250cd3331d0Smrg	newColors.which = 0;
3251cd3331d0Smrg	newColors.names[ndx] = NULL;
3252cd3331d0Smrg
3253cd3331d0Smrg	if (thisName != 0
32549a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
32559a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3256cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3257cd3331d0Smrg
3258cd3331d0Smrg	    if (newColors.which != 0) {
3259cd3331d0Smrg		ChangeColors(xw, &newColors);
3260cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3261cd3331d0Smrg	    }
3262cd3331d0Smrg	}
3263cd3331d0Smrg	result = True;
3264cd3331d0Smrg    }
3265cd3331d0Smrg#endif
3266cd3331d0Smrg    return result;
3267cd3331d0Smrg}
3268cd3331d0Smrg
3269cd3331d0Smrg#if OPT_SHIFT_FONTS
3270cd3331d0Smrg/*
3271cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3272cd3331d0Smrg *
3273cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3274cd3331d0Smrg * the corresponding menu font entry.
3275cd3331d0Smrg */
3276cd3331d0Smrgstatic int
3277fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3278cd3331d0Smrg{
3279cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3280cd3331d0Smrg    int num = screen->menu_font_number;
3281cd3331d0Smrg    int rel = 0;
3282cd3331d0Smrg
3283cd3331d0Smrg    if (*++source == '+') {
3284cd3331d0Smrg	rel = 1;
3285cd3331d0Smrg	source++;
3286cd3331d0Smrg    } else if (*source == '-') {
3287cd3331d0Smrg	rel = -1;
3288cd3331d0Smrg	source++;
3289cd3331d0Smrg    }
3290cd3331d0Smrg
3291cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3292cd3331d0Smrg	int val = atoi(source);
3293cd3331d0Smrg	if (rel > 0)
3294cd3331d0Smrg	    rel = val;
3295cd3331d0Smrg	else if (rel < 0)
3296cd3331d0Smrg	    rel = -val;
3297cd3331d0Smrg	else
3298cd3331d0Smrg	    num = val;
3299cd3331d0Smrg    }
3300cd3331d0Smrg
3301cd3331d0Smrg    if (rel != 0) {
3302cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3303cd3331d0Smrg				     screen->menu_font_number, rel);
3304cd3331d0Smrg
3305cd3331d0Smrg    }
3306cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3307cd3331d0Smrg    *target = source;
3308cd3331d0Smrg    return num;
3309cd3331d0Smrg}
3310cd3331d0Smrg
3311cd3331d0Smrgstatic void
3312cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3313cd3331d0Smrg{
3314cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3315cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3316cd3331d0Smrg	Bool success = True;
3317cd3331d0Smrg	int num;
3318cb4a1343Smrg	String base = buf + 1;
3319cd3331d0Smrg	const char *name = 0;
3320cd3331d0Smrg	char temp[10];
3321cd3331d0Smrg
3322cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3323cd3331d0Smrg	if (num < 0
3324cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3325cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3326cd3331d0Smrg	    success = False;
3327cd3331d0Smrg	} else {
3328cd3331d0Smrg#if OPT_RENDERFONT
3329cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3330cd3331d0Smrg		name = getFaceName(xw, False);
3331cd3331d0Smrg	    } else
3332cd3331d0Smrg#endif
3333cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3334cd3331d0Smrg		success = False;
3335cd3331d0Smrg	    }
3336cd3331d0Smrg	}
3337cd3331d0Smrg
3338cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3339cd3331d0Smrg	unparseputs(xw, "50");
3340cd3331d0Smrg
3341cd3331d0Smrg	if (success) {
3342cd3331d0Smrg	    unparseputc(xw, ';');
3343cd3331d0Smrg	    if (buf >= base) {
3344cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3345cd3331d0Smrg		if (*buf != '\0') {
3346cd3331d0Smrg		    unparseputc(xw, '#');
3347cd3331d0Smrg		    sprintf(temp, "%d", num);
3348cd3331d0Smrg		    unparseputs(xw, temp);
3349cd3331d0Smrg		    if (*name != '\0')
3350cd3331d0Smrg			unparseputc(xw, ' ');
3351cd3331d0Smrg		}
3352cd3331d0Smrg	    }
3353cd3331d0Smrg	    unparseputs(xw, name);
3354cd3331d0Smrg	}
3355cd3331d0Smrg
3356cd3331d0Smrg	unparseputc1(xw, final);
3357cd3331d0Smrg	unparse_end(xw);
3358cd3331d0Smrg    }
3359cd3331d0Smrg}
3360cd3331d0Smrg
3361cd3331d0Smrgstatic void
3362cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3363cd3331d0Smrg{
3364cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3365cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3366cd3331d0Smrg	Bool success = True;
3367cd3331d0Smrg	int num;
3368cd3331d0Smrg	VTFontNames fonts;
3369cd3331d0Smrg	char *name;
3370cd3331d0Smrg
3371cd3331d0Smrg	/*
3372cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3373cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3374cd3331d0Smrg	 *
3375cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3376cd3331d0Smrg	 * to load the font entry.
3377cd3331d0Smrg	 */
3378cd3331d0Smrg	if (*buf == '#') {
3379cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3380cd3331d0Smrg
3381cd3331d0Smrg	    if (num < 0
3382cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3383cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3384cd3331d0Smrg		success = False;
3385cd3331d0Smrg	    } else {
3386cd3331d0Smrg		/*
3387cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3388cd3331d0Smrg		 * for a font specification within the control.
3389cd3331d0Smrg		 */
3390cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3391cd3331d0Smrg		    ++buf;
3392cd3331d0Smrg		}
3393cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3394cd3331d0Smrg		    ++buf;
3395cd3331d0Smrg		}
3396cd3331d0Smrg#if OPT_RENDERFONT
3397cd3331d0Smrg		if (UsingRenderFont(xw)) {
3398c219fbebSmrg		    /* EMPTY */
3399c219fbebSmrg		    /* there is only one font entry to load */
3400c219fbebSmrg		    ;
3401cd3331d0Smrg		} else
3402cd3331d0Smrg#endif
3403cd3331d0Smrg		{
3404cd3331d0Smrg		    /*
3405cd3331d0Smrg		     * Normally there is no font specified in the control.
3406cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3407cd3331d0Smrg		     */
3408cd3331d0Smrg		    if (*buf == '\0') {
3409cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3410cd3331d0Smrg			    success = False;
3411cd3331d0Smrg			}
3412cd3331d0Smrg		    }
3413cd3331d0Smrg		}
3414cd3331d0Smrg	    }
3415cd3331d0Smrg	} else {
3416cd3331d0Smrg	    num = screen->menu_font_number;
3417cd3331d0Smrg	}
3418cd3331d0Smrg	name = x_strtrim(buf);
341994644356Smrg	if (screen->EscapeFontName()) {
342094644356Smrg	    FREE_STRING(screen->EscapeFontName());
342194644356Smrg	    screen->EscapeFontName() = 0;
342294644356Smrg	}
3423cd3331d0Smrg	if (success && !IsEmpty(name)) {
3424cd3331d0Smrg#if OPT_RENDERFONT
3425cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3426cd3331d0Smrg		setFaceName(xw, name);
3427cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3428cd3331d0Smrg	    } else
3429cd3331d0Smrg#endif
3430cd3331d0Smrg	    {
3431cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3432cd3331d0Smrg		fonts.f_n = name;
3433cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
343494644356Smrg		if (num == screen->menu_font_number &&
343594644356Smrg		    num != fontMenu_fontescape) {
343694644356Smrg		    screen->EscapeFontName() = x_strdup(name);
343794644356Smrg		}
3438cd3331d0Smrg	    }
3439cd3331d0Smrg	} else {
3440cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3441cd3331d0Smrg	}
344294644356Smrg	update_font_escape();
3443cd3331d0Smrg	free(name);
3444cd3331d0Smrg    }
3445cd3331d0Smrg}
3446cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3447cd3331d0Smrg
3448d522f475Smrg/***====================================================================***/
3449d522f475Smrg
3450d522f475Smrgvoid
3451fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3452d522f475Smrg{
3453cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3454d522f475Smrg    int mode;
3455d522f475Smrg    Char *cp;
3456d522f475Smrg    int state = 0;
3457d522f475Smrg    char *buf = 0;
3458cd3331d0Smrg    char temp[2];
3459cd3331d0Smrg#if OPT_ISO_COLORS
3460cd3331d0Smrg    int ansi_colors = 0;
3461cd3331d0Smrg#endif
3462cd3331d0Smrg    Bool need_data = True;
3463fa3f02f3Smrg    Bool optional_data = False;
3464d522f475Smrg
3465d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3466d522f475Smrg
3467712a7ff4Smrg    (void) screen;
3468712a7ff4Smrg
3469d522f475Smrg    /*
3470d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3471d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3472d522f475Smrg     * with the same final character as the application sends to make this
3473d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3474d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3475d522f475Smrg     */
3476d522f475Smrg    mode = 0;
3477d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3478d522f475Smrg	switch (state) {
3479d522f475Smrg	case 0:
3480d522f475Smrg	    if (isdigit(*cp)) {
3481d522f475Smrg		mode = 10 * mode + (*cp - '0');
3482d522f475Smrg		if (mode > 65535) {
3483d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3484d522f475Smrg		    return;
3485d522f475Smrg		}
3486d522f475Smrg		break;
3487d522f475Smrg	    }
3488d522f475Smrg	    /* FALLTHRU */
3489d522f475Smrg	case 1:
3490d522f475Smrg	    if (*cp != ';') {
3491cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
3492cd3331d0Smrg		       (int) (cp - oscbuf)));
3493d522f475Smrg		return;
3494d522f475Smrg	    }
3495d522f475Smrg	    state = 2;
3496d522f475Smrg	    break;
3497d522f475Smrg	case 2:
3498d522f475Smrg	    buf = (char *) cp;
3499d522f475Smrg	    state = 3;
3500d522f475Smrg	    /* FALLTHRU */
3501d522f475Smrg	default:
3502cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
3503d522f475Smrg		switch (mode) {
3504d522f475Smrg		case 0:
3505d522f475Smrg		case 1:
3506d522f475Smrg		case 2:
3507d522f475Smrg		    break;
3508d522f475Smrg		default:
3509d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
3510d522f475Smrg			   CharOf(*cp),
3511cd3331d0Smrg			   (int) (cp - oscbuf)));
3512d522f475Smrg		    return;
3513d522f475Smrg		}
3514d522f475Smrg	    }
3515d522f475Smrg	}
3516d522f475Smrg    }
3517cd3331d0Smrg
35183367019cSmrg    /*
35193367019cSmrg     * Check if the palette changed and there are no more immediate changes
35203367019cSmrg     * that could be deferred to the next repaint.
35213367019cSmrg     */
35223367019cSmrg    if (xw->misc.palette_changed) {
35233367019cSmrg	switch (mode) {
35243367019cSmrg	case 3:		/* change X property */
35253367019cSmrg	case 30:		/* Konsole (unused) */
35263367019cSmrg	case 31:		/* Konsole (unused) */
35273367019cSmrg	case 50:		/* font operations */
35283367019cSmrg	case 51:		/* Emacs (unused) */
35293367019cSmrg#if OPT_PASTE64
35303367019cSmrg	case 52:		/* selection data */
35313367019cSmrg#endif
35323367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
35333367019cSmrg	    xw->misc.palette_changed = False;
35343367019cSmrg	    xtermRepaint(xw);
35353367019cSmrg	    break;
35363367019cSmrg	}
35373367019cSmrg    }
35383367019cSmrg
3539cd3331d0Smrg    /*
3540cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
3541cd3331d0Smrg     * a special case.
3542cd3331d0Smrg     */
3543cd3331d0Smrg    switch (mode) {
354494644356Smrg    case 50:
3545cd3331d0Smrg#if OPT_ISO_COLORS
3546cd3331d0Smrg    case OSC_Reset(4):
3547cd3331d0Smrg    case OSC_Reset(5):
3548fa3f02f3Smrg	need_data = False;
3549fa3f02f3Smrg	optional_data = True;
3550fa3f02f3Smrg	break;
3551cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3552cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3553cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3554cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3555cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3556cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3557cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3558cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3559cd3331d0Smrg#endif
3560cd3331d0Smrg#if OPT_TEK4014
3561cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3562cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3563cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3564cd3331d0Smrg#endif
3565cd3331d0Smrg	need_data = False;
3566cd3331d0Smrg	break;
3567cd3331d0Smrg#endif
3568cd3331d0Smrg    default:
3569cd3331d0Smrg	break;
3570cd3331d0Smrg    }
3571cd3331d0Smrg
3572cd3331d0Smrg    /*
3573cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
3574cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
3575cd3331d0Smrg     */
3576cd3331d0Smrg    if (IsEmpty(buf)) {
3577cd3331d0Smrg	if (need_data) {
3578cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
3579cd3331d0Smrg	    return;
3580cd3331d0Smrg	}
3581cd3331d0Smrg	temp[0] = '\0';
3582cd3331d0Smrg	buf = temp;
3583fa3f02f3Smrg    } else if (!need_data && !optional_data) {
3584fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
3585d522f475Smrg	return;
35860d92cbfdSchristos    }
3587d522f475Smrg
3588d522f475Smrg    switch (mode) {
3589d522f475Smrg    case 0:			/* new icon name and title */
3590b7c89284Ssnj	ChangeIconName(xw, buf);
3591b7c89284Ssnj	ChangeTitle(xw, buf);
3592d522f475Smrg	break;
3593d522f475Smrg
3594d522f475Smrg    case 1:			/* new icon name only */
3595b7c89284Ssnj	ChangeIconName(xw, buf);
3596d522f475Smrg	break;
3597d522f475Smrg
3598d522f475Smrg    case 2:			/* new title only */
3599b7c89284Ssnj	ChangeTitle(xw, buf);
3600d522f475Smrg	break;
3601d522f475Smrg
360222d8e007Schristos#ifdef notdef
3603d522f475Smrg    case 3:			/* change X property */
3604cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
36050d92cbfdSchristos	    ChangeXprop(buf);
3606d522f475Smrg	break;
360722d8e007Schristos#endif
3608d522f475Smrg#if OPT_ISO_COLORS
3609cd3331d0Smrg    case 5:
3610cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3611cd3331d0Smrg	/* FALLTHRU */
3612d522f475Smrg    case 4:
3613cd3331d0Smrg	if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
36143367019cSmrg	    xw->misc.palette_changed = True;
3615cd3331d0Smrg	break;
361694644356Smrg    case 6:
361794644356Smrg	/* FALLTHRU */
361894644356Smrg    case OSC_Reset(6):
361994644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
362094644356Smrg	while (*buf != '\0') {
362194644356Smrg	    long which = 0;
362294644356Smrg	    long value = 0;
362394644356Smrg	    char *next;
362494644356Smrg	    if (*buf == ';') {
362594644356Smrg		++buf;
362694644356Smrg	    } else {
362794644356Smrg		which = strtol(buf, &next, 10);
362894644356Smrg		if (next == 0)
362994644356Smrg		    break;
363094644356Smrg		buf = next;
363194644356Smrg		if (*buf == ';')
363294644356Smrg		    ++buf;
363394644356Smrg	    }
363494644356Smrg	    if (*buf == ';') {
363594644356Smrg		++buf;
363694644356Smrg	    } else {
363794644356Smrg		value = strtol(buf, &next, 10);
363894644356Smrg		if (next == 0)
363994644356Smrg		    break;
364094644356Smrg		buf = next;
364194644356Smrg		if (*buf == ';')
364294644356Smrg		    ++buf;
364394644356Smrg	    }
364494644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
364594644356Smrg	    switch (which) {
364694644356Smrg	    case 0:
364794644356Smrg		screen->colorBDMode = (value != 0);
364894644356Smrg		break;
364994644356Smrg	    case 1:
365094644356Smrg		screen->colorULMode = (value != 0);
365194644356Smrg		break;
365294644356Smrg	    case 2:
365394644356Smrg		screen->colorBLMode = (value != 0);
365494644356Smrg		break;
365594644356Smrg	    case 3:
365694644356Smrg		screen->colorRVMode = (value != 0);
365794644356Smrg		break;
365894644356Smrg#if OPT_WIDE_ATTRS
365994644356Smrg	    case 4:
366094644356Smrg		screen->colorITMode = (value != 0);
366194644356Smrg		break;
366294644356Smrg#endif
366394644356Smrg	    default:
366494644356Smrg		TRACE(("...unknown colorXXMode\n"));
366594644356Smrg		break;
366694644356Smrg	    }
366794644356Smrg	}
366894644356Smrg	break;
3669cd3331d0Smrg    case OSC_Reset(5):
3670cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3671cd3331d0Smrg	/* FALLTHRU */
3672cd3331d0Smrg    case OSC_Reset(4):
3673cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
36743367019cSmrg	    xw->misc.palette_changed = True;
3675d522f475Smrg	break;
3676d522f475Smrg#endif
3677d522f475Smrg    case OSC_TEXT_FG:
3678d522f475Smrg    case OSC_TEXT_BG:
3679d522f475Smrg    case OSC_TEXT_CURSOR:
3680d522f475Smrg    case OSC_MOUSE_FG:
3681d522f475Smrg    case OSC_MOUSE_BG:
3682d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3683d522f475Smrg    case OSC_HIGHLIGHT_BG:
3684cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
3685d522f475Smrg#endif
3686d522f475Smrg#if OPT_TEK4014
3687d522f475Smrg    case OSC_TEK_FG:
3688d522f475Smrg    case OSC_TEK_BG:
3689d522f475Smrg    case OSC_TEK_CURSOR:
3690d522f475Smrg#endif
3691cd3331d0Smrg	if (xw->misc.dynamicColors) {
3692d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
3693cd3331d0Smrg	}
3694cd3331d0Smrg	break;
3695cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3696cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3697cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3698cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3699cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3700cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3701cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3702cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3703cd3331d0Smrg#endif
3704cd3331d0Smrg#if OPT_TEK4014
3705cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3706cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3707cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3708cd3331d0Smrg#endif
3709cd3331d0Smrg	if (xw->misc.dynamicColors) {
3710cd3331d0Smrg	    ResetColorsRequest(xw, mode);
3711cd3331d0Smrg	}
3712d522f475Smrg	break;
3713d522f475Smrg
3714d522f475Smrg    case 30:
3715d522f475Smrg    case 31:
3716d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
3717d522f475Smrg	break;
3718d522f475Smrg
3719d522f475Smrg#ifdef ALLOWLOGGING
3720d522f475Smrg    case 46:			/* new log file */
3721d522f475Smrg#ifdef ALLOWLOGFILECHANGES
3722d522f475Smrg	/*
3723d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
3724d522f475Smrg	 * arbitrary files accessible to the person running xterm.
3725d522f475Smrg	 */
3726cd3331d0Smrg	if (strcmp(buf, "?")
3727d522f475Smrg	    && (cp = CastMallocN(char, strlen(buf)) != NULL)) {
3728d522f475Smrg	    strcpy(cp, buf);
3729d522f475Smrg	    if (screen->logfile)
3730d522f475Smrg		free(screen->logfile);
3731d522f475Smrg	    screen->logfile = cp;
3732d522f475Smrg	    break;
3733d522f475Smrg	}
3734d522f475Smrg#endif
3735cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3736cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3737d522f475Smrg	break;
3738d522f475Smrg#endif /* ALLOWLOGGING */
3739d522f475Smrg
3740d522f475Smrg    case 50:
3741d522f475Smrg#if OPT_SHIFT_FONTS
3742cd3331d0Smrg	if (*buf == '?') {
3743cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
3744cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
3745cd3331d0Smrg	    ChangeFontRequest(xw, buf);
3746d522f475Smrg	}
3747d522f475Smrg#endif /* OPT_SHIFT_FONTS */
3748d522f475Smrg	break;
3749d522f475Smrg    case 51:
3750d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
3751d522f475Smrg	break;
3752d522f475Smrg
3753d522f475Smrg#if OPT_PASTE64
3754d522f475Smrg    case 52:
3755cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
3756d522f475Smrg	break;
3757d522f475Smrg#endif
3758d522f475Smrg	/*
3759d522f475Smrg	 * One could write code to send back the display and host names,
3760d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
3761d522f475Smrg	 */
3762cd3331d0Smrg    default:
3763cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
3764cd3331d0Smrg	break;
3765d522f475Smrg    }
3766d522f475Smrg    unparse_end(xw);
3767d522f475Smrg}
3768d522f475Smrg
3769d522f475Smrg/*
3770d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
3771d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
3772d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
3773d522f475Smrg * "real" terminals accept commas in the string definitions).
3774d522f475Smrg */
3775d522f475Smrgstatic int
3776cd3331d0Smrgudk_value(const char **cp)
3777d522f475Smrg{
3778cd3331d0Smrg    int result = -1;
3779d522f475Smrg    int c;
3780d522f475Smrg
3781d522f475Smrg    for (;;) {
3782d522f475Smrg	if ((c = **cp) != '\0')
3783d522f475Smrg	    *cp = *cp + 1;
3784d522f475Smrg	if (c == ';' || c == '\0')
3785cd3331d0Smrg	    break;
3786cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
3787cd3331d0Smrg	    break;
3788d522f475Smrg    }
3789cd3331d0Smrg
3790cd3331d0Smrg    return result;
3791d522f475Smrg}
3792d522f475Smrg
3793d522f475Smrgvoid
37949a64e1c5Smrgreset_decudk(XtermWidget xw)
3795d522f475Smrg{
3796d522f475Smrg    int n;
3797d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
37989a64e1c5Smrg	if (xw->work.user_keys[n].str != 0) {
37999a64e1c5Smrg	    free(xw->work.user_keys[n].str);
38009a64e1c5Smrg	    xw->work.user_keys[n].str = 0;
38019a64e1c5Smrg	    xw->work.user_keys[n].len = 0;
3802d522f475Smrg	}
3803d522f475Smrg    }
3804d522f475Smrg}
3805d522f475Smrg
3806d522f475Smrg/*
3807d522f475Smrg * Parse the data for DECUDK (user-defined keys).
3808d522f475Smrg */
3809d522f475Smrgstatic void
38109a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
3811d522f475Smrg{
3812d522f475Smrg    while (*cp) {
3813cd3331d0Smrg	const char *base = cp;
38143367019cSmrg	char *str = CastMallocN(char, strlen(cp) + 2);
3815d522f475Smrg	unsigned key = 0;
3816d522f475Smrg	int lo, hi;
3817d522f475Smrg	int len = 0;
3818d522f475Smrg
381994644356Smrg	if (str == NULL)
382094644356Smrg	    break;
382194644356Smrg
3822d522f475Smrg	while (isdigit(CharOf(*cp)))
38230d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
3824d522f475Smrg	if (*cp == '/') {
3825d522f475Smrg	    cp++;
3826d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
3827d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
38280d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
3829d522f475Smrg	    }
3830d522f475Smrg	}
3831d522f475Smrg	if (len > 0 && key < MAX_UDK) {
38323367019cSmrg	    str[len] = '\0';
38339a64e1c5Smrg	    if (xw->work.user_keys[key].str != 0)
38349a64e1c5Smrg		free(xw->work.user_keys[key].str);
38359a64e1c5Smrg	    xw->work.user_keys[key].str = str;
38369a64e1c5Smrg	    xw->work.user_keys[key].len = len;
3837d522f475Smrg	} else {
3838d522f475Smrg	    free(str);
3839d522f475Smrg	}
3840d522f475Smrg	if (*cp == ';')
3841d522f475Smrg	    cp++;
3842d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
3843d522f475Smrg	    break;
3844d522f475Smrg    }
3845d522f475Smrg}
3846d522f475Smrg
3847fa3f02f3Smrg/*
3848fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
3849fa3f02f3Smrg * interspersing with control characters, but have the string already.
3850fa3f02f3Smrg */
3851fa3f02f3Smrgstatic void
3852fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
3853fa3f02f3Smrg{
3854fa3f02f3Smrg    const char *cp = *string;
3855fa3f02f3Smrg    ParmType nparam = 0;
3856fa3f02f3Smrg    int last_empty = 1;
3857fa3f02f3Smrg
3858fa3f02f3Smrg    memset(params, 0, sizeof(*params));
3859fa3f02f3Smrg    while (*cp != '\0') {
3860fa3f02f3Smrg	Char ch = CharOf(*cp++);
3861fa3f02f3Smrg
3862fa3f02f3Smrg	if (isdigit(ch)) {
3863fa3f02f3Smrg	    last_empty = 0;
3864fa3f02f3Smrg	    if (nparam < NPARAM) {
3865fa3f02f3Smrg		params->a_param[nparam] =
3866fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
3867fa3f02f3Smrg				+ (ch - '0'));
3868fa3f02f3Smrg	    }
3869fa3f02f3Smrg	} else if (ch == ';') {
3870fa3f02f3Smrg	    last_empty = 1;
3871fa3f02f3Smrg	    nparam++;
3872fa3f02f3Smrg	} else if (ch < 32) {
3873fa3f02f3Smrg	    /* EMPTY */ ;
3874fa3f02f3Smrg	} else {
3875fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
3876fa3f02f3Smrg	    params->a_final = ch;
3877fa3f02f3Smrg	    break;
3878fa3f02f3Smrg	}
3879fa3f02f3Smrg    }
3880fa3f02f3Smrg
3881fa3f02f3Smrg    *string = cp;
3882fa3f02f3Smrg    if (!last_empty)
3883fa3f02f3Smrg	nparam++;
3884fa3f02f3Smrg    if (nparam > NPARAM)
3885fa3f02f3Smrg	params->a_nparam = NPARAM;
3886fa3f02f3Smrg    else
3887fa3f02f3Smrg	params->a_nparam = nparam;
3888fa3f02f3Smrg}
3889fa3f02f3Smrg
3890d522f475Smrg#if OPT_TRACE
3891d522f475Smrg#define SOFT_WIDE 10
3892d522f475Smrg#define SOFT_HIGH 20
3893d522f475Smrg
3894d522f475Smrgstatic void
3895fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
3896d522f475Smrg{
3897d522f475Smrg    char DscsName[8];
3898d522f475Smrg    int len;
3899d522f475Smrg    int Pfn = params->a_param[0];
3900d522f475Smrg    int Pcn = params->a_param[1];
3901d522f475Smrg    int Pe = params->a_param[2];
3902d522f475Smrg    int Pcmw = params->a_param[3];
3903d522f475Smrg    int Pw = params->a_param[4];
3904d522f475Smrg    int Pt = params->a_param[5];
3905d522f475Smrg    int Pcmh = params->a_param[6];
3906d522f475Smrg    int Pcss = params->a_param[7];
3907d522f475Smrg
3908d522f475Smrg    int start_char = Pcn + 0x20;
3909d522f475Smrg    int char_wide = ((Pcmw == 0)
3910d522f475Smrg		     ? (Pcss ? 6 : 10)
3911d522f475Smrg		     : (Pcmw > 4
3912d522f475Smrg			? Pcmw
3913d522f475Smrg			: (Pcmw + 3)));
3914d522f475Smrg    int char_high = ((Pcmh == 0)
39153367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
3916d522f475Smrg			? 10
3917d522f475Smrg			: 20)
3918d522f475Smrg		     : Pcmh);
3919d522f475Smrg    Char ch;
3920d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
3921d522f475Smrg    Bool first = True;
3922d522f475Smrg    Bool prior = False;
3923d522f475Smrg    int row = 0, col = 0;
3924d522f475Smrg
3925d522f475Smrg    TRACE(("Parsing DECDLD\n"));
3926d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
3927d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
3928d522f475Smrg    TRACE(("  erase control %d\n", Pe));
3929d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
3930d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
3931d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
3932d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
3933d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
3934d522f475Smrg
3935d522f475Smrg    if (Pfn > 1
3936d522f475Smrg	|| Pcn > 95
3937d522f475Smrg	|| Pe > 2
3938d522f475Smrg	|| Pcmw > 10
3939d522f475Smrg	|| Pcmw == 1
3940d522f475Smrg	|| Pt > 2
3941d522f475Smrg	|| Pcmh > 20
3942d522f475Smrg	|| Pcss > 1
3943d522f475Smrg	|| char_wide > SOFT_WIDE
3944d522f475Smrg	|| char_high > SOFT_HIGH) {
3945d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
3946d522f475Smrg	return;
3947d522f475Smrg    }
3948d522f475Smrg
3949d522f475Smrg    len = 0;
3950d522f475Smrg    while (*string != '\0') {
3951d522f475Smrg	ch = CharOf(*string++);
3952d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
3953d522f475Smrg	    if (len < 2)
3954b7c89284Ssnj		DscsName[len++] = (char) ch;
3955d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
3956b7c89284Ssnj	    DscsName[len++] = (char) ch;
3957d522f475Smrg	    break;
3958d522f475Smrg	}
3959d522f475Smrg    }
3960d522f475Smrg    DscsName[len] = 0;
3961d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
3962d522f475Smrg
3963d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
3964d522f475Smrg    while (*string != '\0') {
3965d522f475Smrg	if (first) {
3966d522f475Smrg	    TRACE(("Char %d:\n", start_char));
3967d522f475Smrg	    if (prior) {
3968d522f475Smrg		for (row = 0; row < char_high; ++row) {
3969d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
3970d522f475Smrg		}
3971d522f475Smrg	    }
3972d522f475Smrg	    prior = False;
3973d522f475Smrg	    first = False;
3974d522f475Smrg	    for (row = 0; row < char_high; ++row) {
3975d522f475Smrg		for (col = 0; col < char_wide; ++col) {
3976d522f475Smrg		    bits[row][col] = '.';
3977d522f475Smrg		}
3978d522f475Smrg	    }
3979d522f475Smrg	    row = col = 0;
3980d522f475Smrg	}
3981d522f475Smrg	ch = CharOf(*string++);
3982d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
3983d522f475Smrg	    int n;
3984d522f475Smrg
3985b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
3986d522f475Smrg	    for (n = 0; n < 6; ++n) {
3987b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
3988d522f475Smrg	    }
3989d522f475Smrg	    col += 1;
3990d522f475Smrg	    prior = True;
3991d522f475Smrg	} else if (ch == '/') {
3992d522f475Smrg	    row += 6;
3993d522f475Smrg	    col = 0;
3994d522f475Smrg	} else if (ch == ';') {
3995d522f475Smrg	    first = True;
3996d522f475Smrg	    ++start_char;
3997d522f475Smrg	}
3998d522f475Smrg    }
3999d522f475Smrg}
4000d522f475Smrg#else
4001d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4002d522f475Smrg#endif
4003d522f475Smrg
4004d522f475Smrgvoid
4005fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4006d522f475Smrg{
4007cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4008d522f475Smrg    char reply[BUFSIZ];
4009cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4010d522f475Smrg    Bool okay;
4011d522f475Smrg    ANSI params;
4012d522f475Smrg
4013cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4014d522f475Smrg
4015d522f475Smrg    if (dcslen != strlen(cp))
4016d522f475Smrg	/* shouldn't have nulls in the string */
4017d522f475Smrg	return;
4018d522f475Smrg
4019d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4020d522f475Smrg    case '$':			/* DECRQSS */
4021d522f475Smrg	okay = True;
4022d522f475Smrg
4023d522f475Smrg	cp++;
4024d522f475Smrg	if (*cp++ == 'q') {
4025d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4026d522f475Smrg		sprintf(reply, "%d%s",
4027d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4028d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4029d522f475Smrg			cp);
4030d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
40313367019cSmrg		if (screen->vtXX_level < 2) {
40323367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
40333367019cSmrg		    break;
40343367019cSmrg		}
4035d522f475Smrg		sprintf(reply, "%d%s%s",
4036d522f475Smrg			(screen->vtXX_level ?
4037d522f475Smrg			 screen->vtXX_level : 1) + 60,
4038d522f475Smrg			(screen->vtXX_level >= 2)
4039d522f475Smrg			? (screen->control_eight_bits
4040d522f475Smrg			   ? ";0" : ";1")
4041d522f475Smrg			: "",
4042d522f475Smrg			cp);
4043d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4044d522f475Smrg		sprintf(reply, "%d;%dr",
4045d522f475Smrg			screen->top_marg + 1,
4046d522f475Smrg			screen->bot_marg + 1);
40473367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
40483367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
40493367019cSmrg		    sprintf(reply, "%d;%ds",
40503367019cSmrg			    screen->lft_marg + 1,
40513367019cSmrg			    screen->rgt_marg + 1);
40523367019cSmrg		}
4053d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4054d522f475Smrg		strcpy(reply, "0");
4055d522f475Smrg		if (xw->flags & BOLD)
4056d522f475Smrg		    strcat(reply, ";1");
4057d522f475Smrg		if (xw->flags & UNDERLINE)
4058d522f475Smrg		    strcat(reply, ";4");
4059d522f475Smrg		if (xw->flags & BLINK)
4060d522f475Smrg		    strcat(reply, ";5");
4061d522f475Smrg		if (xw->flags & INVERSE)
4062d522f475Smrg		    strcat(reply, ";7");
4063d522f475Smrg		if (xw->flags & INVISIBLE)
4064d522f475Smrg		    strcat(reply, ";8");
4065b7c89284Ssnj#if OPT_256_COLORS || OPT_88_COLORS
4066b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4067d522f475Smrg		    if (xw->flags & FG_COLOR) {
4068d522f475Smrg			if (xw->cur_foreground >= 16)
4069d522f475Smrg			    sprintf(reply + strlen(reply),
4070d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
4071d522f475Smrg			else
4072d522f475Smrg			    sprintf(reply + strlen(reply),
4073d522f475Smrg				    ";%d%d",
4074d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
4075d522f475Smrg				    xw->cur_foreground >= 8 ?
4076d522f475Smrg				    xw->cur_foreground - 8 :
4077d522f475Smrg				    xw->cur_foreground);
4078d522f475Smrg		    }
4079d522f475Smrg		    if (xw->flags & BG_COLOR) {
4080d522f475Smrg			if (xw->cur_background >= 16)
4081d522f475Smrg			    sprintf(reply + strlen(reply),
4082d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
4083d522f475Smrg			else
4084d522f475Smrg			    sprintf(reply + strlen(reply),
4085d522f475Smrg				    ";%d%d",
4086d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
4087d522f475Smrg				    xw->cur_background >= 8 ?
4088d522f475Smrg				    xw->cur_background - 8 :
4089d522f475Smrg				    xw->cur_background);
4090d522f475Smrg		    }
4091d522f475Smrg		});
4092b7c89284Ssnj#elif OPT_ISO_COLORS
4093b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4094d522f475Smrg		    if (xw->flags & FG_COLOR)
4095d522f475Smrg			sprintf(reply + strlen(reply),
4096d522f475Smrg				";%d%d",
4097d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
4098d522f475Smrg				xw->cur_foreground >= 8 ?
4099d522f475Smrg				xw->cur_foreground - 8 :
4100d522f475Smrg				xw->cur_foreground);
4101d522f475Smrg		    if (xw->flags & BG_COLOR)
4102d522f475Smrg			sprintf(reply + strlen(reply),
4103d522f475Smrg				";%d%d",
4104d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
4105d522f475Smrg				xw->cur_background >= 8 ?
4106d522f475Smrg				xw->cur_background - 8 :
4107d522f475Smrg				xw->cur_background);
4108d522f475Smrg		});
4109b7c89284Ssnj#endif
4110d522f475Smrg		strcat(reply, "m");
4111712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
41123367019cSmrg		int code = STEADY_BLOCK;
41133367019cSmrg		if (isCursorUnderline(screen))
41143367019cSmrg		    code = STEADY_UNDERLINE;
41153367019cSmrg		else if (isCursorBar(screen))
41163367019cSmrg		    code = STEADY_BAR;
41173367019cSmrg#if OPT_BLINK_CURS
411894644356Smrg		if (screen->cursor_blink_esc != 0)
41193367019cSmrg		    code -= 1;
41203367019cSmrg#endif
41213367019cSmrg		sprintf(reply, "%d%s", code, cp);
4122d522f475Smrg	    } else
4123d522f475Smrg		okay = False;
4124d522f475Smrg
412522d8e007Schristos	    if (okay) {
41260d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
41273367019cSmrg		unparseputc(xw, '1');
41280d92cbfdSchristos		unparseputc(xw, '$');
41290d92cbfdSchristos		unparseputc(xw, 'r');
4130d522f475Smrg		cp = reply;
413122d8e007Schristos		unparseputs(xw, cp);
41320d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
41330d92cbfdSchristos	    } else {
41340d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
413522d8e007Schristos	    }
4136d522f475Smrg	} else {
4137d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4138d522f475Smrg	}
4139d522f475Smrg	break;
4140d522f475Smrg#if OPT_TCAP_QUERY
4141d522f475Smrg    case '+':
4142d522f475Smrg	cp++;
4143cd3331d0Smrg	switch (*cp) {
4144cd3331d0Smrg	case 'p':
4145cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4146cd3331d0Smrg		set_termcap(xw, cp + 1);
4147cd3331d0Smrg	    }
4148cd3331d0Smrg	    break;
4149cd3331d0Smrg	case 'q':
4150cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4151cd3331d0Smrg		Bool fkey;
4152cd3331d0Smrg		unsigned state;
4153cd3331d0Smrg		int code;
4154cd3331d0Smrg		const char *tmp;
4155cd3331d0Smrg		const char *parsed = ++cp;
4156d522f475Smrg
4157cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4158d522f475Smrg
4159cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4160b7c89284Ssnj
4161cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4162d522f475Smrg
4163cd3331d0Smrg		unparseputc(xw, '+');
4164cd3331d0Smrg		unparseputc(xw, 'r');
4165d522f475Smrg
4166cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4167cd3331d0Smrg		    if (cp == parsed)
4168cd3331d0Smrg			break;	/* no data found, error */
4169d522f475Smrg
4170cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4171cd3331d0Smrg			unparseputc(xw, *tmp);
4172d522f475Smrg
4173cd3331d0Smrg		    if (code >= 0) {
4174cd3331d0Smrg			unparseputc(xw, '=');
4175cd3331d0Smrg			screen->tc_query_code = code;
4176cd3331d0Smrg			screen->tc_query_fkey = fkey;
4177d522f475Smrg#if OPT_ISO_COLORS
4178cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4179cd3331d0Smrg			 * number of colors) */
4180cd3331d0Smrg			if (code == XK_COLORS) {
4181cd3331d0Smrg			    unparseputn(xw, NUM_ANSI_COLORS);
4182cd3331d0Smrg			} else
4183cd3331d0Smrg#endif
4184cd3331d0Smrg			if (code == XK_TCAPNAME) {
4185c219fbebSmrg			    unparseputs(xw, resource.term_name);
4186cd3331d0Smrg			} else {
4187cd3331d0Smrg			    XKeyEvent event;
4188cd3331d0Smrg			    event.state = state;
4189cd3331d0Smrg			    Input(xw, &event, False);
4190cd3331d0Smrg			}
4191cd3331d0Smrg			screen->tc_query_code = -1;
4192cd3331d0Smrg		    } else {
4193cd3331d0Smrg			break;	/* no match found, error */
4194d522f475Smrg		    }
4195d522f475Smrg
4196d522f475Smrg		    cp = parsed;
4197cd3331d0Smrg		    if (*parsed == ';') {
4198cd3331d0Smrg			unparseputc(xw, *parsed++);
4199cd3331d0Smrg			cp = parsed;
4200cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4201cd3331d0Smrg		    }
4202d522f475Smrg		}
4203cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4204d522f475Smrg	    }
4205cd3331d0Smrg	    break;
4206d522f475Smrg	}
4207d522f475Smrg	break;
4208d522f475Smrg#endif
4209d522f475Smrg    default:
4210fa3f02f3Smrg	if (screen->terminal_id == 125 ||
4211fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
42120d92cbfdSchristos	    parse_ansi_params(&params, &cp);
42130d92cbfdSchristos	    switch (params.a_final) {
4214fa3f02f3Smrg	    case 'p':
42159a64e1c5Smrg#if OPT_REGIS_GRAPHICS
4216fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4217fa3f02f3Smrg		    screen->terminal_id == 240 ||
4218fa3f02f3Smrg		    screen->terminal_id == 241 ||
4219fa3f02f3Smrg		    screen->terminal_id == 330 ||
4220fa3f02f3Smrg		    screen->terminal_id == 340) {
4221fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4222fa3f02f3Smrg		}
42239a64e1c5Smrg#else
42249a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
42259a64e1c5Smrg#endif
4226fa3f02f3Smrg		break;
4227fa3f02f3Smrg	    case 'q':
42289a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
4229fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4230fa3f02f3Smrg		    screen->terminal_id == 240 ||
4231fa3f02f3Smrg		    screen->terminal_id == 241 ||
4232fa3f02f3Smrg		    screen->terminal_id == 330 ||
42339a64e1c5Smrg		    screen->terminal_id == 340 ||
42349a64e1c5Smrg		    screen->terminal_id == 382) {
4235fa3f02f3Smrg		    parse_sixel(xw, &params, cp);
4236fa3f02f3Smrg		}
42379a64e1c5Smrg#else
42389a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
4239fa3f02f3Smrg#endif
42409a64e1c5Smrg		break;
42410d92cbfdSchristos	    case '|':		/* DECUDK */
42429a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
42439a64e1c5Smrg		    if (params.a_param[0] == 0)
42449a64e1c5Smrg			reset_decudk(xw);
42459a64e1c5Smrg		    parse_decudk(xw, cp);
42469a64e1c5Smrg		}
42470d92cbfdSchristos		break;
424894644356Smrg	    case L_CURL:	/* DECDLD */
42499a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
42509a64e1c5Smrg		    parse_decdld(&params, cp);
42519a64e1c5Smrg		}
42520d92cbfdSchristos		break;
42530d92cbfdSchristos	    }
4254d522f475Smrg	}
4255d522f475Smrg	break;
4256d522f475Smrg    }
4257d522f475Smrg    unparse_end(xw);
4258d522f475Smrg}
4259d522f475Smrg
4260cb4a1343Smrg#if OPT_DEC_RECTOPS
4261cb4a1343Smrgenum {
4262cb4a1343Smrg    mdUnknown = 0,
4263cb4a1343Smrg    mdMaybeSet = 1,
4264cb4a1343Smrg    mdMaybeReset = 2,
4265cb4a1343Smrg    mdAlwaysSet = 3,
4266cb4a1343Smrg    mdAlwaysReset = 4
4267cb4a1343Smrg};
4268cb4a1343Smrg
4269cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
42703367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
4271cb4a1343Smrg
4272cb4a1343Smrg/*
4273cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
4274cb4a1343Smrg * 0 - not recognized
4275cb4a1343Smrg * 1 - set
4276cb4a1343Smrg * 2 - reset
4277cb4a1343Smrg * 3 - permanently set
4278cb4a1343Smrg * 4 - permanently reset
4279cb4a1343Smrg * Only one mode can be reported at a time.
4280cb4a1343Smrg */
4281cb4a1343Smrgvoid
4282cb4a1343Smrgdo_rpm(XtermWidget xw, int nparams, int *params)
4283cb4a1343Smrg{
4284cb4a1343Smrg    ANSI reply;
4285cb4a1343Smrg    int result = 0;
4286cb4a1343Smrg    int count = 0;
4287cb4a1343Smrg
4288cb4a1343Smrg    TRACE(("do_rpm %d:%d\n", nparams, params[0]));
4289cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4290cb4a1343Smrg    if (nparams >= 1) {
4291cb4a1343Smrg	switch (params[0]) {
4292cb4a1343Smrg	case 1:		/* GATM */
4293cb4a1343Smrg	    result = mdAlwaysReset;
4294cb4a1343Smrg	    break;
4295cb4a1343Smrg	case 2:
4296cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
4297cb4a1343Smrg	    break;
4298cb4a1343Smrg	case 3:		/* CRM */
4299cb4a1343Smrg	    result = mdMaybeReset;
4300cb4a1343Smrg	    break;
4301cb4a1343Smrg	case 4:
4302cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
4303cb4a1343Smrg	    break;
4304cb4a1343Smrg	case 5:		/* SRTM */
4305cb4a1343Smrg	case 7:		/* VEM */
4306cb4a1343Smrg	case 10:		/* HEM */
4307cb4a1343Smrg	case 11:		/* PUM */
4308cb4a1343Smrg	    result = mdAlwaysReset;
4309cb4a1343Smrg	    break;
4310cb4a1343Smrg	case 12:
4311cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
4312cb4a1343Smrg	    break;
4313cb4a1343Smrg	case 13:		/* FEAM */
4314cb4a1343Smrg	case 14:		/* FETM */
4315cb4a1343Smrg	case 15:		/* MATM */
4316cb4a1343Smrg	case 16:		/* TTM */
4317cb4a1343Smrg	case 17:		/* SATM */
4318cb4a1343Smrg	case 18:		/* TSM */
4319cb4a1343Smrg	case 19:		/* EBM */
4320cb4a1343Smrg	    result = mdAlwaysReset;
4321cb4a1343Smrg	    break;
4322cb4a1343Smrg	case 20:
4323cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
4324cb4a1343Smrg	    break;
4325cb4a1343Smrg	}
4326cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4327cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4328cb4a1343Smrg    }
4329cb4a1343Smrg    reply.a_type = ANSI_CSI;
4330cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4331cb4a1343Smrg    reply.a_inters = '$';
4332cb4a1343Smrg    reply.a_final = 'y';
4333cb4a1343Smrg    unparseseq(xw, &reply);
4334cb4a1343Smrg}
4335cb4a1343Smrg
4336cb4a1343Smrgvoid
4337cb4a1343Smrgdo_decrpm(XtermWidget xw, int nparams, int *params)
4338cb4a1343Smrg{
4339cb4a1343Smrg    ANSI reply;
4340cb4a1343Smrg    int result = 0;
4341cb4a1343Smrg    int count = 0;
4342cb4a1343Smrg
4343cb4a1343Smrg    TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
4344cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4345cb4a1343Smrg    if (nparams >= 1) {
4346cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
4347cb4a1343Smrg
4348cb4a1343Smrg	switch (params[0]) {
4349fa3f02f3Smrg	case srm_DECCKM:
4350cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
4351cb4a1343Smrg	    break;
4352fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
4353cb4a1343Smrg#if OPT_VT52_MODE
43543367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
4355cb4a1343Smrg#else
4356cb4a1343Smrg	    result = mdMaybeSet;
4357cb4a1343Smrg#endif
4358cb4a1343Smrg	    break;
4359fa3f02f3Smrg	case srm_DECCOLM:
4360cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
4361cb4a1343Smrg	    break;
4362fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
4363cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
4364cb4a1343Smrg	    break;
4365fa3f02f3Smrg	case srm_DECSCNM:
4366cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
4367cb4a1343Smrg	    break;
4368fa3f02f3Smrg	case srm_DECOM:
4369cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
4370cb4a1343Smrg	    break;
4371fa3f02f3Smrg	case srm_DECAWM:
4372cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
4373cb4a1343Smrg	    break;
4374fa3f02f3Smrg	case srm_DECARM:
4375cb4a1343Smrg	    result = mdAlwaysReset;
4376cb4a1343Smrg	    break;
4377fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
4378cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
4379cb4a1343Smrg	    break;
4380cb4a1343Smrg#if OPT_TOOLBAR
4381fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
4382cb4a1343Smrg	    result = MdBool(resource.toolBar);
4383cb4a1343Smrg	    break;
4384cb4a1343Smrg#endif
4385cb4a1343Smrg#if OPT_BLINK_CURS
4386fa3f02f3Smrg	case srm_ATT610_BLINK:	/* att610: Start/stop blinking cursor */
4387cb4a1343Smrg	    result = MdBool(screen->cursor_blink_res);
4388cb4a1343Smrg	    break;
4389cb4a1343Smrg#endif
4390fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
4391712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
4392cb4a1343Smrg	    break;
4393fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
4394712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
4395cb4a1343Smrg	    break;
4396fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
4397cb4a1343Smrg	    result = MdBool(screen->cursor_set);
4398cb4a1343Smrg	    break;
4399fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
4400cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
4401cb4a1343Smrg	    break;
4402cb4a1343Smrg#if OPT_SHIFT_FONTS
4403fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
4404cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
4405cb4a1343Smrg	    break;
4406cb4a1343Smrg#endif
4407cb4a1343Smrg#if OPT_TEK4014
4408fa3f02f3Smrg	case srm_DECTEK:
4409cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
4410cb4a1343Smrg	    break;
4411cb4a1343Smrg#endif
4412fa3f02f3Smrg	case srm_132COLS:
4413cb4a1343Smrg	    result = MdBool(screen->c132);
4414cb4a1343Smrg	    break;
4415fa3f02f3Smrg	case srm_CURSES_HACK:
4416cb4a1343Smrg	    result = MdBool(screen->curses);
4417cb4a1343Smrg	    break;
4418fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
4419cb4a1343Smrg	    result = MdFlag(xw->flags, NATIONAL);
4420cb4a1343Smrg	    break;
4421fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
4422cb4a1343Smrg	    result = MdBool(screen->marginbell);
4423cb4a1343Smrg	    break;
4424fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
4425cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSEWRAP);
4426cb4a1343Smrg	    break;
4427cb4a1343Smrg#ifdef ALLOWLOGGING
4428fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
4429cb4a1343Smrg#ifdef ALLOWLOGFILEONOFF
4430cb4a1343Smrg	    result = MdBool(screen->logging);
4431cb4a1343Smrg#endif /* ALLOWLOGFILEONOFF */
4432cb4a1343Smrg	    break;
4433cb4a1343Smrg#endif
4434fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
4435cb4a1343Smrg	    /* FALLTHRU */
4436fa3f02f3Smrg	case srm_OPT_ALTBUF:
4437cb4a1343Smrg	    /* FALLTHRU */
4438fa3f02f3Smrg	case srm_ALTBUF:
4439cb4a1343Smrg	    result = MdBool(screen->whichBuf);
4440cb4a1343Smrg	    break;
4441fa3f02f3Smrg	case srm_DECNKM:
4442cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
4443cb4a1343Smrg	    break;
4444fa3f02f3Smrg	case srm_DECBKM:
4445cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
4446cb4a1343Smrg	    break;
4447fa3f02f3Smrg	case srm_DECLRMM:
44483367019cSmrg	    result = MdFlag(xw->flags, LEFT_RIGHT);
44493367019cSmrg	    break;
4450fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
4451fa3f02f3Smrg	case srm_DECSDM:
4452fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
4453fa3f02f3Smrg	    break;
4454fa3f02f3Smrg#endif
4455fa3f02f3Smrg	case srm_DECNCSM:
44563367019cSmrg	    result = MdFlag(xw->flags, NOCLEAR_COLM);
44573367019cSmrg	    break;
4458fa3f02f3Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence         */
4459cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
4460cb4a1343Smrg	    break;
4461fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
4462cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
4463cb4a1343Smrg	    break;
4464fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
4465cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
4466cb4a1343Smrg	    break;
4467fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
4468cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
4469cb4a1343Smrg	    break;
4470cb4a1343Smrg#if OPT_FOCUS_EVENT
4471fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
4472cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
4473cb4a1343Smrg	    break;
4474cb4a1343Smrg#endif
4475fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
44763367019cSmrg	    /* FALLTHRU */
4477fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
44783367019cSmrg	    /* FALLTHRU */
4479fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
44803367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
44813367019cSmrg	    break;
4482fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
44833367019cSmrg	    result = MdBool(screen->alternateScroll);
4484cb4a1343Smrg	    break;
4485fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
4486cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
4487cb4a1343Smrg	    break;
4488fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
4489cb4a1343Smrg	    result = MdBool(screen->scrollkey);
4490cb4a1343Smrg	    break;
4491fa3f02f3Smrg	case srm_EIGHT_BIT_META:
44923367019cSmrg	    result = MdBool(screen->eight_bit_meta);
4493cb4a1343Smrg	    break;
4494cb4a1343Smrg#if OPT_NUM_LOCK
4495fa3f02f3Smrg	case srm_REAL_NUMLOCK:
4496cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
4497cb4a1343Smrg	    break;
4498fa3f02f3Smrg	case srm_META_SENDS_ESC:
4499cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
4500cb4a1343Smrg	    break;
4501cb4a1343Smrg#endif
4502fa3f02f3Smrg	case srm_DELETE_IS_DEL:
4503cb4a1343Smrg	    result = MdBool(screen->delete_is_del);
4504cb4a1343Smrg	    break;
4505cb4a1343Smrg#if OPT_NUM_LOCK
4506fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
4507cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
4508cb4a1343Smrg	    break;
4509cb4a1343Smrg#endif
4510fa3f02f3Smrg	case srm_KEEP_SELECTION:
4511cb4a1343Smrg	    result = MdBool(screen->keepSelection);
4512cb4a1343Smrg	    break;
4513fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
4514cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
4515cb4a1343Smrg	    break;
4516fa3f02f3Smrg	case srm_BELL_IS_URGENT:
4517cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
4518cb4a1343Smrg	    break;
4519fa3f02f3Smrg	case srm_POP_ON_BELL:
4520cb4a1343Smrg	    result = MdBool(screen->poponbell);
4521cb4a1343Smrg	    break;
4522fa3f02f3Smrg	case srm_TITE_INHIBIT:
4523cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
4524cb4a1343Smrg	    break;
4525cb4a1343Smrg#if OPT_TCAP_FKEYS
4526fa3f02f3Smrg	case srm_TCAP_FKEYS:
4527cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
4528cb4a1343Smrg	    break;
4529cb4a1343Smrg#endif
4530cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
4531fa3f02f3Smrg	case srm_SUN_FKEYS:
4532cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
4533cb4a1343Smrg	    break;
4534cb4a1343Smrg#endif
4535cb4a1343Smrg#if OPT_HP_FUNC_KEYS
4536fa3f02f3Smrg	case srm_HP_FKEYS:
4537cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
4538cb4a1343Smrg	    break;
4539cb4a1343Smrg#endif
4540cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
4541fa3f02f3Smrg	case srm_SCO_FKEYS:
4542cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
4543cb4a1343Smrg	    break;
4544cb4a1343Smrg#endif
4545fa3f02f3Smrg	case srm_LEGACY_FKEYS:
4546cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
4547cb4a1343Smrg	    break;
4548cb4a1343Smrg#if OPT_SUNPC_KBD
4549fa3f02f3Smrg	case srm_VT220_FKEYS:
4550cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
4551cb4a1343Smrg	    break;
4552cb4a1343Smrg#endif
4553cb4a1343Smrg#if OPT_READLINE
4554fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
4555cb4a1343Smrg	    result = MdBool(screen->click1_moves);
4556cb4a1343Smrg	    break;
4557fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
4558cb4a1343Smrg	    result = MdBool(screen->paste_moves);
4559cb4a1343Smrg	    break;
4560fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
4561cb4a1343Smrg	    result = MdBool(screen->dclick3_deletes);
4562cb4a1343Smrg	    break;
4563fa3f02f3Smrg	case srm_PASTE_IN_BRACKET:
4564cb4a1343Smrg	    result = MdBool(screen->paste_brackets);
4565cb4a1343Smrg	    break;
4566fa3f02f3Smrg	case srm_PASTE_QUOTE:
4567cb4a1343Smrg	    result = MdBool(screen->paste_quotes);
4568cb4a1343Smrg	    break;
4569fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
4570cb4a1343Smrg	    result = MdBool(screen->paste_literal_nl);
4571cb4a1343Smrg	    break;
4572cb4a1343Smrg#endif /* OPT_READLINE */
45739a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
45749a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
45759a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
45769a64e1c5Smrg	    break;
45779a64e1c5Smrg#endif
45789a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
45799a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
45809a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
45819a64e1c5Smrg	    break;
45829a64e1c5Smrg#endif
45839a64e1c5Smrg	default:
45849a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
45859a64e1c5Smrg		   params[0]));
4586cb4a1343Smrg	}
4587cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4588cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4589cb4a1343Smrg    }
4590cb4a1343Smrg    reply.a_type = ANSI_CSI;
4591cb4a1343Smrg    reply.a_pintro = '?';
4592cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4593cb4a1343Smrg    reply.a_inters = '$';
4594cb4a1343Smrg    reply.a_final = 'y';
4595cb4a1343Smrg    unparseseq(xw, &reply);
4596cb4a1343Smrg}
4597cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
4598cb4a1343Smrg
4599d522f475Smrgchar *
46009a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
4601d522f475Smrg{
4602d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
46039a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
46049a64e1c5Smrg	return xw->work.user_keys[keycode].str;
4605d522f475Smrg    }
4606d522f475Smrg    return 0;
4607d522f475Smrg}
4608d522f475Smrg
46093367019cSmrg#ifdef HAVE_LIBXPM
46103367019cSmrg
46113367019cSmrg#ifndef PIXMAP_ROOTDIR
46123367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
46133367019cSmrg#endif
46143367019cSmrg
46153367019cSmrgtypedef struct {
46163367019cSmrg    const char *name;
46173367019cSmrg    const char *const *data;
46183367019cSmrg} XPM_DATA;
46193367019cSmrg
46203367019cSmrgstatic char *
46213367019cSmrgx_find_icon(char **work, int *state, const char *suffix)
46223367019cSmrg{
46233367019cSmrg    const char *filename = resource.icon_hint;
46243367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
46253367019cSmrg    const char *larger = "_48x48";
46263367019cSmrg    char *result = 0;
46273367019cSmrg    size_t length;
46283367019cSmrg
46293367019cSmrg    if (*state >= 0) {
46303367019cSmrg	if ((*state & 1) == 0)
46313367019cSmrg	    suffix = "";
46323367019cSmrg	if ((*state & 2) == 0)
46333367019cSmrg	    larger = "";
46343367019cSmrg	if ((*state & 4) == 0) {
46353367019cSmrg	    prefix = "";
46363367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
46373367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
46383367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
46393367019cSmrg	    *state = -1;
46403367019cSmrg	} else if (*state >= 8) {
46413367019cSmrg	    *state = -1;
46423367019cSmrg	}
46433367019cSmrg    }
46443367019cSmrg
46453367019cSmrg    if (*state >= 0) {
46463367019cSmrg	if (*work) {
46473367019cSmrg	    free(*work);
46483367019cSmrg	    *work = 0;
46493367019cSmrg	}
46503367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
46513367019cSmrg	    strlen(suffix);
46523367019cSmrg	if ((result = malloc(length)) != 0) {
46533367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
46543367019cSmrg	    *work = result;
46553367019cSmrg	}
46563367019cSmrg	*state += 1;
46573367019cSmrg	TRACE(("x_find_icon %d:%s\n", *state, result));
46583367019cSmrg    }
46593367019cSmrg    return result;
46603367019cSmrg}
46613367019cSmrg
46623367019cSmrg#if OPT_BUILTIN_XPMS
46633367019cSmrgstatic const XPM_DATA *
46643367019cSmrgBuiltInXPM(const XPM_DATA * table, Cardinal length)
46653367019cSmrg{
46663367019cSmrg    const char *find = resource.icon_hint;
46673367019cSmrg    const XPM_DATA *result = 0;
46683367019cSmrg    if (!IsEmpty(find)) {
46693367019cSmrg	Cardinal n;
46703367019cSmrg	for (n = 0; n < length; ++n) {
46713367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
46723367019cSmrg		result = table + n;
46733367019cSmrg		break;
46743367019cSmrg	    }
46753367019cSmrg	}
46763367019cSmrg
46773367019cSmrg	/*
46783367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
46793367019cSmrg	 * which are all _HHxWW format.
46803367019cSmrg	 */
46813367019cSmrg	if (result == 0) {
46823367019cSmrg	    const char *base = table[0].name;
46833367019cSmrg	    const char *last = strchr(base, '_');
46843367019cSmrg	    if (last != 0
46853367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
46863367019cSmrg		result = table + length - 1;
46873367019cSmrg	    }
46883367019cSmrg	}
46893367019cSmrg    }
46903367019cSmrg    return result;
46913367019cSmrg}
46923367019cSmrg#endif /* OPT_BUILTIN_XPMS */
46933367019cSmrg
46943367019cSmrgtypedef enum {
46953367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
46963367019cSmrg    ,eHintNone
46973367019cSmrg    ,eHintSearch
46983367019cSmrg} ICON_HINT;
46993367019cSmrg
47003367019cSmrgstatic ICON_HINT
47013367019cSmrgwhich_icon_hint(void)
47023367019cSmrg{
47033367019cSmrg    ICON_HINT result = eHintDefault;
47043367019cSmrg    if (!IsEmpty(resource.icon_hint)) {
47053367019cSmrg	if (!x_strcasecmp(resource.icon_hint, "none")) {
47063367019cSmrg	    result = eHintNone;
47073367019cSmrg	} else {
47083367019cSmrg	    result = eHintSearch;
47093367019cSmrg	}
47103367019cSmrg    }
47113367019cSmrg    return result;
47123367019cSmrg}
47133367019cSmrg#endif /* HAVE_LIBXPM */
47143367019cSmrg
47153367019cSmrgint
47163367019cSmrggetVisualDepth(XtermWidget xw)
47173367019cSmrg{
47183367019cSmrg    int result = 0;
47193367019cSmrg
4720fa3f02f3Smrg    if (getVisualInfo(xw)) {
4721fa3f02f3Smrg	result = xw->visInfo->depth;
47223367019cSmrg    }
47233367019cSmrg    return result;
47243367019cSmrg}
47253367019cSmrg
47263367019cSmrg/*
47273367019cSmrg * WM_ICON_SIZE should be honored if possible.
47283367019cSmrg */
47293367019cSmrgvoid
47303367019cSmrgxtermLoadIcon(XtermWidget xw)
47313367019cSmrg{
47323367019cSmrg#ifdef HAVE_LIBXPM
47333367019cSmrg    Display *dpy = XtDisplay(xw);
47343367019cSmrg    Pixmap myIcon = 0;
47353367019cSmrg    Pixmap myMask = 0;
47363367019cSmrg    char *workname = 0;
47373367019cSmrg    ICON_HINT hint = which_icon_hint();
4738fa3f02f3Smrg#include <builtin_icons.h>
47393367019cSmrg
47403367019cSmrg    TRACE(("xtermLoadIcon %p:%s\n", (void *) xw, NonNull(resource.icon_hint)));
47413367019cSmrg
47423367019cSmrg    if (hint == eHintSearch) {
47433367019cSmrg	int state = 0;
47443367019cSmrg	while (x_find_icon(&workname, &state, ".xpm") != 0) {
47453367019cSmrg	    Pixmap resIcon = 0;
47463367019cSmrg	    Pixmap shapemask = 0;
47473367019cSmrg	    XpmAttributes attributes;
47483367019cSmrg
47493367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
47503367019cSmrg	    attributes.valuemask = XpmDepth;
47513367019cSmrg
47523367019cSmrg	    if (XpmReadFileToPixmap(dpy,
47533367019cSmrg				    DefaultRootWindow(dpy),
47543367019cSmrg				    workname,
47553367019cSmrg				    &resIcon,
47563367019cSmrg				    &shapemask,
47573367019cSmrg				    &attributes) == XpmSuccess) {
47583367019cSmrg		myIcon = resIcon;
47593367019cSmrg		myMask = shapemask;
47603367019cSmrg		TRACE(("...success\n"));
47613367019cSmrg		break;
47623367019cSmrg	    }
47633367019cSmrg	}
47643367019cSmrg    }
47653367019cSmrg
47663367019cSmrg    /*
47673367019cSmrg     * If no external file was found, look for the name in the built-in table.
47683367019cSmrg     * If that fails, just use the biggest mini-icon.
47693367019cSmrg     */
47703367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
47713367019cSmrg	char **data;
47723367019cSmrg#if OPT_BUILTIN_XPMS
47733367019cSmrg	const XPM_DATA *myData = 0;
47743367019cSmrg	myData = BuiltInXPM(mini_xterm_xpms, XtNumber(mini_xterm_xpms));
47753367019cSmrg	if (myData == 0)
47763367019cSmrg	    myData = BuiltInXPM(filled_xterm_xpms, XtNumber(filled_xterm_xpms));
47773367019cSmrg	if (myData == 0)
47783367019cSmrg	    myData = BuiltInXPM(xterm_color_xpms, XtNumber(xterm_color_xpms));
47793367019cSmrg	if (myData == 0)
47803367019cSmrg	    myData = BuiltInXPM(xterm_xpms, XtNumber(xterm_xpms));
47813367019cSmrg	if (myData == 0)
47823367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
478394644356Smrg	data = (char **) myData->data;
47843367019cSmrg#else
47853367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
47863367019cSmrg#endif
47873367019cSmrg	if (XpmCreatePixmapFromData(dpy,
47883367019cSmrg				    DefaultRootWindow(dpy),
47893367019cSmrg				    data,
47903367019cSmrg				    &myIcon, &myMask, 0) != 0) {
47913367019cSmrg	    myIcon = 0;
47923367019cSmrg	    myMask = 0;
47933367019cSmrg	}
47943367019cSmrg    }
47953367019cSmrg
47963367019cSmrg    if (myIcon != 0) {
47973367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
47983367019cSmrg	if (!hints)
47993367019cSmrg	    hints = XAllocWMHints();
48003367019cSmrg
48013367019cSmrg	if (hints) {
48023367019cSmrg	    hints->flags |= IconPixmapHint;
48033367019cSmrg	    hints->icon_pixmap = myIcon;
48043367019cSmrg	    if (myMask) {
48053367019cSmrg		hints->flags |= IconMaskHint;
48063367019cSmrg		hints->icon_mask = myMask;
48073367019cSmrg	    }
48083367019cSmrg
48093367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
48103367019cSmrg	    XFree(hints);
48113367019cSmrg	    TRACE(("...loaded icon\n"));
48123367019cSmrg	}
48133367019cSmrg    }
48143367019cSmrg
48153367019cSmrg    if (workname != 0)
48163367019cSmrg	free(workname);
48173367019cSmrg
48183367019cSmrg#else
48193367019cSmrg    (void) xw;
48203367019cSmrg#endif
48213367019cSmrg}
48223367019cSmrg
48233367019cSmrgvoid
4824cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
4825d522f475Smrg{
4826d522f475Smrg#if OPT_WIDE_CHARS
4827d522f475Smrg    static Char *converted;	/* NO_LEAKS */
4828d522f475Smrg#endif
4829d522f475Smrg
4830d522f475Smrg    Arg args[1];
4831cd3331d0Smrg    Boolean changed = True;
4832d522f475Smrg    Widget w = CURRENT_EMU();
4833d522f475Smrg    Widget top = SHELL_OF(w);
4834d522f475Smrg
4835cd3331d0Smrg    char *my_attr;
4836cd3331d0Smrg    char *name;
4837cd3331d0Smrg    size_t limit;
4838cd3331d0Smrg    Char *c1;
4839cd3331d0Smrg    Char *cp;
4840d522f475Smrg
4841b7c89284Ssnj    if (!AllowTitleOps(xw))
4842d522f475Smrg	return;
4843d522f475Smrg
4844cd3331d0Smrg    if (value == 0)
48453367019cSmrg	value = emptyString;
4846cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
4847cd3331d0Smrg	const char *temp;
4848cd3331d0Smrg	char *test;
4849cd3331d0Smrg
4850cd3331d0Smrg	value = x_decode_hex(value, &temp);
48513367019cSmrg	if (*temp != '\0') {
48523367019cSmrg	    free(value);
4853cd3331d0Smrg	    return;
48543367019cSmrg	}
4855cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
4856cd3331d0Smrg	    if (CharOf(*test) < 32) {
4857cd3331d0Smrg		*test = '\0';
4858cd3331d0Smrg		break;
4859cd3331d0Smrg	    }
4860cd3331d0Smrg	}
4861cd3331d0Smrg    }
4862cd3331d0Smrg
4863cd3331d0Smrg    c1 = (Char *) value;
4864cd3331d0Smrg    name = value;
4865cd3331d0Smrg    limit = strlen(name);
4866cd3331d0Smrg    my_attr = x_strdup(attribute);
4867cd3331d0Smrg
4868cd3331d0Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
4869cd3331d0Smrg
4870d522f475Smrg    /*
4871d522f475Smrg     * Ignore titles that are too long to be plausible requests.
4872d522f475Smrg     */
4873cd3331d0Smrg    if (limit > 0 && limit < 1024) {
4874d522f475Smrg
4875cd3331d0Smrg	/*
4876cd3331d0Smrg	 * After all decoding, overwrite nonprintable characters with '?'.
4877cd3331d0Smrg	 */
4878cd3331d0Smrg	for (cp = c1; *cp != 0; ++cp) {
4879cd3331d0Smrg	    Char *c2 = cp;
4880cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
4881cd3331d0Smrg		memset(c2, '?', (size_t) (cp + 1 - c2));
4882cd3331d0Smrg	    }
4883d522f475Smrg	}
4884d522f475Smrg
4885d522f475Smrg#if OPT_WIDE_CHARS
4886cd3331d0Smrg	/*
4887cd3331d0Smrg	 * If we're running in UTF-8 mode, and have not been told that the
4888cd3331d0Smrg	 * title string is in UTF-8, it is likely that non-ASCII text in the
4889cd3331d0Smrg	 * string will be rejected because it is not printable in the current
4890cd3331d0Smrg	 * locale.  So we convert it to UTF-8, allowing the X library to
4891cd3331d0Smrg	 * convert it back.
4892cd3331d0Smrg	 */
4893cd3331d0Smrg	if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) {
4894cd3331d0Smrg	    int n;
4895cd3331d0Smrg
4896cd3331d0Smrg	    for (n = 0; name[n] != '\0'; ++n) {
4897cd3331d0Smrg		if (CharOf(name[n]) > 127) {
4898cd3331d0Smrg		    if (converted != 0)
4899cd3331d0Smrg			free(converted);
4900cd3331d0Smrg		    if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
4901cd3331d0Smrg			Char *temp = converted;
4902cd3331d0Smrg			while (*name != 0) {
4903cd3331d0Smrg			    temp = convertToUTF8(temp, CharOf(*name));
4904cd3331d0Smrg			    ++name;
4905cd3331d0Smrg			}
4906cd3331d0Smrg			*temp = 0;
4907cd3331d0Smrg			name = (char *) converted;
4908cd3331d0Smrg			TRACE(("...converted{%s}\n", name));
4909d522f475Smrg		    }
4910cd3331d0Smrg		    break;
4911d522f475Smrg		}
4912d522f475Smrg	    }
4913d522f475Smrg	}
4914d522f475Smrg#endif
4915d522f475Smrg
4916d522f475Smrg#if OPT_SAME_NAME
4917cd3331d0Smrg	/* If the attribute isn't going to change, then don't bother... */
4918cd3331d0Smrg
4919cd3331d0Smrg	if (resource.sameName) {
4920cd3331d0Smrg	    char *buf = 0;
4921cd3331d0Smrg	    XtSetArg(args[0], my_attr, &buf);
4922cd3331d0Smrg	    XtGetValues(top, args, 1);
4923cd3331d0Smrg	    TRACE(("...comparing{%s}\n", buf));
4924cd3331d0Smrg	    if (buf != 0 && strcmp(name, buf) == 0)
4925cd3331d0Smrg		changed = False;
4926cd3331d0Smrg	}
4927d522f475Smrg#endif /* OPT_SAME_NAME */
4928d522f475Smrg
4929cd3331d0Smrg	if (changed) {
4930cd3331d0Smrg	    TRACE(("...updating %s\n", my_attr));
4931cd3331d0Smrg	    TRACE(("...value is %s\n", name));
4932cd3331d0Smrg	    XtSetArg(args[0], my_attr, name);
4933cd3331d0Smrg	    XtSetValues(top, args, 1);
4934d522f475Smrg
4935d522f475Smrg#if OPT_WIDE_CHARS
4936cd3331d0Smrg	    if (xtermEnvUTF8()) {
4937cd3331d0Smrg		Display *dpy = XtDisplay(xw);
4938cd3331d0Smrg		Atom my_atom;
4939cd3331d0Smrg
4940cd3331d0Smrg		const char *propname = (!strcmp(my_attr, XtNtitle)
4941cd3331d0Smrg					? "_NET_WM_NAME"
4942cd3331d0Smrg					: "_NET_WM_ICON_NAME");
4943cd3331d0Smrg		if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
4944cd3331d0Smrg		    if (IsSetUtf8Title(xw)) {
4945cd3331d0Smrg			TRACE(("...updating %s\n", propname));
4946cd3331d0Smrg			TRACE(("...value is %s\n", value));
4947c219fbebSmrg			XChangeProperty(dpy, VShellWindow(xw), my_atom,
4948cd3331d0Smrg					XA_UTF8_STRING(dpy), 8,
4949cd3331d0Smrg					PropModeReplace,
4950cd3331d0Smrg					(Char *) value,
4951cd3331d0Smrg					(int) strlen(value));
4952cd3331d0Smrg		    } else {
4953cd3331d0Smrg			TRACE(("...deleting %s\n", propname));
4954c219fbebSmrg			XDeleteProperty(dpy, VShellWindow(xw), my_atom);
4955cd3331d0Smrg		    }
4956cd3331d0Smrg		}
4957d522f475Smrg	    }
4958cd3331d0Smrg#endif
4959d522f475Smrg	}
4960d522f475Smrg    }
49613367019cSmrg    if (IsTitleMode(xw, tmSetBase16)) {
49623367019cSmrg	free(value);
49633367019cSmrg    }
49643367019cSmrg    free(my_attr);
49653367019cSmrg
4966cd3331d0Smrg    return;
4967d522f475Smrg}
4968d522f475Smrg
4969d522f475Smrgvoid
4970b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
4971d522f475Smrg{
4972cd3331d0Smrg    if (name == 0) {
49733367019cSmrg	name = emptyString;
49743367019cSmrg    }
49753367019cSmrg    if (!showZIconBeep(xw, name))
4976b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
4977d522f475Smrg}
4978d522f475Smrg
4979d522f475Smrgvoid
4980b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
4981d522f475Smrg{
4982b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
4983d522f475Smrg}
4984d522f475Smrg
4985712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
4986d522f475Smrg
4987d522f475Smrgvoid
4988d522f475SmrgChangeXprop(char *buf)
4989d522f475Smrg{
4990d522f475Smrg    Display *dpy = XtDisplay(toplevel);
4991d522f475Smrg    Window w = XtWindow(toplevel);
4992d522f475Smrg    XTextProperty text_prop;
4993d522f475Smrg    Atom aprop;
4994d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
4995d522f475Smrg
4996d522f475Smrg    if (pchEndPropName)
4997d522f475Smrg	*pchEndPropName = '\0';
4998d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
4999d522f475Smrg    if (pchEndPropName == NULL) {
5000d522f475Smrg	/* no "=value" given, so delete the property */
5001d522f475Smrg	XDeleteProperty(dpy, w, aprop);
5002d522f475Smrg    } else {
5003d522f475Smrg	text_prop.value = pchEndPropName + 1;
5004d522f475Smrg	text_prop.encoding = XA_STRING;
5005d522f475Smrg	text_prop.format = 8;
5006d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
5007d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
5008d522f475Smrg    }
5009d522f475Smrg}
5010d522f475Smrg
5011d522f475Smrg/***====================================================================***/
5012d522f475Smrg
5013d522f475Smrg/*
5014d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
5015d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
5016d522f475Smrg */
5017d522f475Smrgvoid
50189a64e1c5SmrgReverseOldColors(XtermWidget xw)
5019d522f475Smrg{
50209a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
5021d522f475Smrg    Pixel tmpPix;
5022d522f475Smrg    char *tmpName;
5023d522f475Smrg
5024d522f475Smrg    if (pOld) {
5025d522f475Smrg	/* change text cursor, if necesary */
5026d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
5027d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
5028d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
50299a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
5030d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
5031d522f475Smrg	    }
5032d522f475Smrg	    if (pOld->names[TEXT_BG]) {
5033d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
5034d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
5035d522f475Smrg		}
5036d522f475Smrg	    }
5037d522f475Smrg	}
5038d522f475Smrg
5039d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
5040d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
5041d522f475Smrg
5042d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
5043d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
5044d522f475Smrg
5045d522f475Smrg#if OPT_TEK4014
5046d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
5047d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
5048d522f475Smrg#endif
5049d522f475Smrg    }
5050d522f475Smrg    return;
5051d522f475Smrg}
5052d522f475Smrg
5053d522f475SmrgBool
5054d522f475SmrgAllocateTermColor(XtermWidget xw,
5055d522f475Smrg		  ScrnColors * pNew,
5056d522f475Smrg		  int ndx,
5057cd3331d0Smrg		  const char *name,
5058cd3331d0Smrg		  Bool always)
5059d522f475Smrg{
5060cd3331d0Smrg    Bool result = False;
5061d522f475Smrg
5062cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
5063cd3331d0Smrg	XColor def;
5064cd3331d0Smrg	char *newName;
5065cd3331d0Smrg
5066712a7ff4Smrg	result = True;
5067712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
5068712a7ff4Smrg	    def.pixel = xw->old_foreground;
5069712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
5070712a7ff4Smrg	    def.pixel = xw->old_background;
50713367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
5072712a7ff4Smrg	    result = False;
5073712a7ff4Smrg	}
5074712a7ff4Smrg
5075712a7ff4Smrg	if (result
5076cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
5077712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
5078cd3331d0Smrg		free(pNew->names[ndx]);
5079712a7ff4Smrg	    }
5080cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
5081cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
5082712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
5083712a7ff4Smrg		   ndx, newName, def.pixel));
5084cd3331d0Smrg	} else {
5085cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
5086712a7ff4Smrg	    result = False;
5087cd3331d0Smrg	}
5088cd3331d0Smrg    }
5089cd3331d0Smrg    return result;
5090d522f475Smrg}
5091d522f475Smrg/***====================================================================***/
5092d522f475Smrg
5093d522f475Smrg/* ARGSUSED */
5094d522f475Smrgvoid
5095cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
5096d522f475Smrg{
50973367019cSmrg    if_DEBUG({
50983367019cSmrg	xtermWarning(s, a);
50993367019cSmrg    });
5100d522f475Smrg}
5101d522f475Smrg
5102d522f475Smrgconst char *
5103d522f475SmrgSysErrorMsg(int code)
5104d522f475Smrg{
510594644356Smrg    static const char unknown[] = "unknown error";
5106d522f475Smrg    char *s = strerror(code);
5107d522f475Smrg    return s ? s : unknown;
5108d522f475Smrg}
5109d522f475Smrg
5110d522f475Smrgconst char *
5111d522f475SmrgSysReasonMsg(int code)
5112d522f475Smrg{
5113d522f475Smrg    /* *INDENT-OFF* */
5114d522f475Smrg    static const struct {
5115d522f475Smrg	int code;
5116d522f475Smrg	const char *name;
5117d522f475Smrg    } table[] = {
5118d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
5119d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
5120d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
5121d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
5122d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
5123d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
5124d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
5125d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
5126d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
5127d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
5128d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
5129d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
5130d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
5131d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
5132d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
5133d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
5134d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
5135d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
5136d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
5137d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
5138d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
5139d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
5140d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
5141d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
5142d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
5143d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
5144d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
5145d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
5146d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
5147d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
5148d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
5149d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
5150d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
5151d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
5152d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
5153d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
5154d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
5155d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
5156d522f475Smrg    };
5157d522f475Smrg    /* *INDENT-ON* */
5158d522f475Smrg
5159d522f475Smrg    Cardinal n;
5160d522f475Smrg    const char *result = "?";
5161d522f475Smrg
5162d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
5163d522f475Smrg	if (code == table[n].code) {
5164d522f475Smrg	    result = table[n].name;
5165d522f475Smrg	    break;
5166d522f475Smrg	}
5167d522f475Smrg    }
5168d522f475Smrg    return result;
5169d522f475Smrg}
5170d522f475Smrg
5171d522f475Smrgvoid
5172d522f475SmrgSysError(int code)
5173d522f475Smrg{
5174d522f475Smrg    int oerrno = errno;
5175d522f475Smrg
5176c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
5177d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
5178d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
5179d522f475Smrg
5180d522f475Smrg    Cleanup(code);
5181d522f475Smrg}
5182d522f475Smrg
5183d522f475Smrgvoid
51843367019cSmrgNormalExit(void)
5185d522f475Smrg{
5186d522f475Smrg    static Bool cleaning;
5187d522f475Smrg
5188d522f475Smrg    /*
5189d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
5190d522f475Smrg     */
51913367019cSmrg    if (cleaning) {
51923367019cSmrg	hold_screen = 0;
51933367019cSmrg	return;
51943367019cSmrg    }
5195d522f475Smrg
51963367019cSmrg    cleaning = True;
51973367019cSmrg    need_cleanup = False;
5198d522f475Smrg
51993367019cSmrg    if (hold_screen) {
52003367019cSmrg	hold_screen = 2;
52013367019cSmrg	while (hold_screen) {
52023367019cSmrg	    xevents();
52033367019cSmrg	    Sleep(10);
5204d522f475Smrg	}
52053367019cSmrg    }
5206d522f475Smrg#if OPT_SESSION_MGT
52073367019cSmrg    if (resource.sessionMgt) {
52083367019cSmrg	XtVaSetValues(toplevel,
52093367019cSmrg		      XtNjoinSession, False,
52103367019cSmrg		      (void *) 0);
5211d522f475Smrg    }
52123367019cSmrg#endif
52133367019cSmrg    Cleanup(0);
52143367019cSmrg}
52153367019cSmrg
52163367019cSmrg/*
52173367019cSmrg * cleanup by sending SIGHUP to client processes
52183367019cSmrg */
52193367019cSmrgvoid
52203367019cSmrgCleanup(int code)
52213367019cSmrg{
52223367019cSmrg    TScreen *screen = TScreenOf(term);
52233367019cSmrg
52243367019cSmrg    TRACE(("Cleanup %d\n", code));
5225d522f475Smrg
5226d522f475Smrg    if (screen->pid > 1) {
5227d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
5228d522f475Smrg    }
5229d522f475Smrg    Exit(code);
5230d522f475Smrg}
5231d522f475Smrg
5232fa3f02f3Smrg#ifndef S_IXOTH
5233fa3f02f3Smrg#define S_IXOTH 1
5234fa3f02f3Smrg#endif
5235fa3f02f3Smrg
5236fa3f02f3SmrgBoolean
5237fa3f02f3SmrgvalidProgram(const char *pathname)
5238fa3f02f3Smrg{
5239fa3f02f3Smrg    Boolean result = False;
5240fa3f02f3Smrg    struct stat sb;
5241fa3f02f3Smrg
5242fa3f02f3Smrg    if (!IsEmpty(pathname)
5243fa3f02f3Smrg	&& *pathname == '/'
5244fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
5245fa3f02f3Smrg	&& stat(pathname, &sb) == 0
5246fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
5247fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
5248fa3f02f3Smrg	result = True;
5249fa3f02f3Smrg    }
5250fa3f02f3Smrg    return result;
5251fa3f02f3Smrg}
5252fa3f02f3Smrg
5253d522f475Smrg#ifndef VMS
52543367019cSmrg#ifndef PATH_MAX
52553367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
52563367019cSmrg#endif
5257d522f475Smrgchar *
5258d522f475SmrgxtermFindShell(char *leaf, Bool warning)
5259d522f475Smrg{
52603367019cSmrg    char *s0;
5261d522f475Smrg    char *s;
5262d522f475Smrg    char *d;
5263d522f475Smrg    char *tmp;
5264d522f475Smrg    char *result = leaf;
52653367019cSmrg    Bool allocated = False;
5266d522f475Smrg
5267d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
52683367019cSmrg
52693367019cSmrg    if (!strncmp("./", result, (size_t) 2)
52703367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
52713367019cSmrg	size_t need = PATH_MAX;
52723367019cSmrg	size_t used = strlen(result) + 2;
52733367019cSmrg	char *buffer = malloc(used + need);
52743367019cSmrg	if (buffer != 0) {
52753367019cSmrg	    if (getcwd(buffer, need) != 0) {
52763367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
52773367019cSmrg		result = buffer;
52783367019cSmrg		allocated = True;
52793367019cSmrg	    } else {
52803367019cSmrg		free(buffer);
52813367019cSmrg	    }
52823367019cSmrg	}
52833367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
5284d522f475Smrg	/* find it in $PATH */
52853367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
52860d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
5287d522f475Smrg		Bool found = False;
5288d522f475Smrg		while (*s != '\0') {
5289d522f475Smrg		    strcpy(tmp, s);
5290d522f475Smrg		    for (d = tmp;; ++d) {
5291d522f475Smrg			if (*d == ':' || *d == '\0') {
5292d522f475Smrg			    int skip = (*d != '\0');
5293d522f475Smrg			    *d = '/';
5294d522f475Smrg			    strcpy(d + 1, leaf);
5295d522f475Smrg			    if (skip)
5296d522f475Smrg				++d;
5297d522f475Smrg			    s += (d - tmp);
5298fa3f02f3Smrg			    if (validProgram(tmp)) {
5299d522f475Smrg				result = x_strdup(tmp);
5300d522f475Smrg				found = True;
53013367019cSmrg				allocated = True;
5302d522f475Smrg			    }
5303d522f475Smrg			    break;
5304d522f475Smrg			}
5305d522f475Smrg		    }
5306d522f475Smrg		    if (found)
5307d522f475Smrg			break;
5308d522f475Smrg		}
5309d522f475Smrg		free(tmp);
5310d522f475Smrg	    }
53113367019cSmrg	    free(s0);
5312d522f475Smrg	}
5313d522f475Smrg    }
5314d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
5315fa3f02f3Smrg    if (!validProgram(result)) {
5316d522f475Smrg	if (warning)
53173367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
53183367019cSmrg	if (allocated)
53193367019cSmrg	    free(result);
5320d522f475Smrg	result = 0;
5321d522f475Smrg    }
53223367019cSmrg    /* be consistent, so that caller can always free the result */
53233367019cSmrg    if (result != 0 && !allocated)
53243367019cSmrg	result = x_strdup(result);
5325d522f475Smrg    return result;
5326d522f475Smrg}
5327d522f475Smrg#endif /* VMS */
5328d522f475Smrg
53290d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
5330d522f475Smrg
53313367019cSmrg/*
53323367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
53333367019cSmrg * This could happen on some older machines due to the uneven standardization
53343367019cSmrg * process for the two functions.
53353367019cSmrg *
53363367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
53373367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
53383367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
53393367019cSmrg * could copy environ.
53403367019cSmrg */
53413367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
53423367019cSmrg#undef HAVE_PUTENV
53433367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
53443367019cSmrg#undef HAVE_UNSETENV
53453367019cSmrg#endif
53463367019cSmrg
5347d522f475Smrg/*
5348d522f475Smrg * copy the environment before Setenv'ing.
5349d522f475Smrg */
5350d522f475Smrgvoid
5351d522f475SmrgxtermCopyEnv(char **oldenv)
5352d522f475Smrg{
53533367019cSmrg#ifdef HAVE_PUTENV
53543367019cSmrg    (void) oldenv;
53553367019cSmrg#else
5356d522f475Smrg    unsigned size;
5357d522f475Smrg    char **newenv;
5358d522f475Smrg
5359d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
5360d522f475Smrg	;
5361d522f475Smrg    }
5362d522f475Smrg
5363d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
5364d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
5365d522f475Smrg    environ = newenv;
53663367019cSmrg#endif
53673367019cSmrg}
53683367019cSmrg
53693367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
53703367019cSmrgstatic int
53713367019cSmrgfindEnv(const char *var, int *lengthp)
53723367019cSmrg{
53733367019cSmrg    char *test;
53743367019cSmrg    int envindex = 0;
53753367019cSmrg    size_t len = strlen(var);
53763367019cSmrg    int found = -1;
53773367019cSmrg
53783367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
53793367019cSmrg
53803367019cSmrg    while ((test = environ[envindex]) != NULL) {
53813367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
53823367019cSmrg	    found = envindex;
53833367019cSmrg	    break;
53843367019cSmrg	}
53853367019cSmrg	envindex++;
53863367019cSmrg    }
53873367019cSmrg    *lengthp = envindex;
53883367019cSmrg    return found;
5389d522f475Smrg}
53903367019cSmrg#endif
5391d522f475Smrg
5392d522f475Smrg/*
5393d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
5394d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
5395d522f475Smrg * This procedure assumes the memory for the first level of environ
5396d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
5397d522f475Smrg * to have to do a realloc().
5398d522f475Smrg */
5399d522f475Smrgvoid
5400cd3331d0SmrgxtermSetenv(const char *var, const char *value)
5401d522f475Smrg{
5402d522f475Smrg    if (value != 0) {
54033367019cSmrg#ifdef HAVE_PUTENV
54043367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
54053367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
54063367019cSmrg	if (both) {
54073367019cSmrg	    sprintf(both, "%s=%s", var, value);
54083367019cSmrg	    putenv(both);
54093367019cSmrg	}
54103367019cSmrg#else
5411d522f475Smrg	size_t len = strlen(var);
54123367019cSmrg	int envindex;
54133367019cSmrg	int found = findEnv(var, &envindex);
5414d522f475Smrg
5415d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
5416d522f475Smrg
5417d522f475Smrg	if (found < 0) {
5418d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
5419d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
5420d522f475Smrg
5421d522f475Smrg	    if (need > have) {
5422d522f475Smrg		char **newenv;
5423d522f475Smrg		newenv = TypeMallocN(char *, need);
5424d522f475Smrg		if (newenv == 0) {
54253367019cSmrg		    xtermWarning("Cannot increase environment\n");
5426d522f475Smrg		    return;
5427d522f475Smrg		}
5428d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
5429d522f475Smrg		free(environ);
5430d522f475Smrg		environ = newenv;
5431d522f475Smrg	    }
5432d522f475Smrg
5433d522f475Smrg	    found = envindex;
5434d522f475Smrg	    environ[found + 1] = NULL;
5435d522f475Smrg	    environ = environ;
5436d522f475Smrg	}
5437d522f475Smrg
5438d522f475Smrg	environ[found] = CastMallocN(char, 1 + len + strlen(value));
5439d522f475Smrg	if (environ[found] == 0) {
54403367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
5441d522f475Smrg	    return;
5442d522f475Smrg	}
5443d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
54443367019cSmrg#endif
5445d522f475Smrg    }
5446d522f475Smrg}
5447d522f475Smrg
54483367019cSmrgvoid
54493367019cSmrgxtermUnsetenv(const char *var)
54503367019cSmrg{
54513367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
54523367019cSmrg#ifdef HAVE_UNSETENV
54533367019cSmrg    unsetenv(var);
54543367019cSmrg#else
54553367019cSmrg    {
54563367019cSmrg	int ignore;
54573367019cSmrg	int item = findEnv(var, &ignore);
54583367019cSmrg	if (item >= 0) {
54593367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
54603367019cSmrg		++item;
54613367019cSmrg	    }
54623367019cSmrg	}
54633367019cSmrg    }
54643367019cSmrg#endif
54653367019cSmrg}
54663367019cSmrg
5467d522f475Smrg/*ARGSUSED*/
5468d522f475Smrgint
54699a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
5470d522f475Smrg{
54713367019cSmrg    xtermWarning("warning, error event received:\n");
5472d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
5473d522f475Smrg    Exit(ERROR_XERROR);
5474d522f475Smrg    return 0;			/* appease the compiler */
5475d522f475Smrg}
5476d522f475Smrg
5477712a7ff4Smrgvoid
5478712a7ff4Smrgice_error(IceConn iceConn)
5479712a7ff4Smrg{
5480712a7ff4Smrg    (void) iceConn;
5481712a7ff4Smrg
54823367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
54833367019cSmrg		 (long) getpid(), errno);
5484712a7ff4Smrg
5485712a7ff4Smrg    Exit(ERROR_ICEERROR);
5486712a7ff4Smrg}
5487712a7ff4Smrg
5488d522f475Smrg/*ARGSUSED*/
5489d522f475Smrgint
5490fa3f02f3Smrgxioerror(Display *dpy)
5491d522f475Smrg{
5492d522f475Smrg    int the_error = errno;
5493d522f475Smrg
54943367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
54953367019cSmrg		 the_error, SysErrorMsg(the_error),
54963367019cSmrg		 DisplayString(dpy));
5497d522f475Smrg
5498d522f475Smrg    Exit(ERROR_XIOERROR);
5499d522f475Smrg    return 0;			/* appease the compiler */
5500d522f475Smrg}
5501d522f475Smrg
5502d522f475Smrgvoid
5503d522f475Smrgxt_error(String message)
5504d522f475Smrg{
55053367019cSmrg    xtermWarning("Xt error: %s\n", message);
5506d522f475Smrg
5507d522f475Smrg    /*
5508d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
5509d522f475Smrg     */
5510d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
55113367019cSmrg	xtermWarning("DISPLAY is not set\n");
5512d522f475Smrg    }
5513d522f475Smrg    exit(1);
5514d522f475Smrg}
5515d522f475Smrg
5516d522f475Smrgint
5517d522f475SmrgXStrCmp(char *s1, char *s2)
5518d522f475Smrg{
5519d522f475Smrg    if (s1 && s2)
5520d522f475Smrg	return (strcmp(s1, s2));
5521d522f475Smrg    if (s1 && *s1)
5522d522f475Smrg	return (1);
5523d522f475Smrg    if (s2 && *s2)
5524d522f475Smrg	return (-1);
5525d522f475Smrg    return (0);
5526d522f475Smrg}
5527d522f475Smrg
5528d522f475Smrg#if OPT_TEK4014
5529d522f475Smrgstatic void
5530fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
5531d522f475Smrg{
5532d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
5533d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
5534d522f475Smrg    XWithdrawWindow(dpy, w, scr);
5535d522f475Smrg    return;
5536d522f475Smrg}
5537d522f475Smrg#endif
5538d522f475Smrg
5539d522f475Smrgvoid
5540d522f475Smrgset_vt_visibility(Bool on)
5541d522f475Smrg{
5542c219fbebSmrg    XtermWidget xw = term;
5543c219fbebSmrg    TScreen *screen = TScreenOf(xw);
5544d522f475Smrg
5545d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
5546d522f475Smrg    if (on) {
5547c219fbebSmrg	if (!screen->Vshow && xw) {
5548c219fbebSmrg	    VTInit(xw);
5549c219fbebSmrg	    XtMapWidget(XtParent(xw));
5550d522f475Smrg#if OPT_TOOLBAR
5551d522f475Smrg	    /* we need both of these during initialization */
5552c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
5553d522f475Smrg	    ShowToolbar(resource.toolBar);
5554d522f475Smrg#endif
5555d522f475Smrg	    screen->Vshow = True;
5556d522f475Smrg	}
5557d522f475Smrg    }
5558d522f475Smrg#if OPT_TEK4014
5559d522f475Smrg    else {
5560c219fbebSmrg	if (screen->Vshow && xw) {
5561c219fbebSmrg	    withdraw_window(XtDisplay(xw),
5562c219fbebSmrg			    VShellWindow(xw),
5563c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
5564d522f475Smrg	    screen->Vshow = False;
5565d522f475Smrg	}
5566d522f475Smrg    }
5567d522f475Smrg    set_vthide_sensitivity();
5568d522f475Smrg    set_tekhide_sensitivity();
5569d522f475Smrg    update_vttekmode();
5570d522f475Smrg    update_tekshow();
5571d522f475Smrg    update_vtshow();
5572d522f475Smrg#endif
5573d522f475Smrg    return;
5574d522f475Smrg}
5575d522f475Smrg
5576d522f475Smrg#if OPT_TEK4014
5577d522f475Smrgvoid
5578d522f475Smrgset_tek_visibility(Bool on)
5579d522f475Smrg{
5580d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
5581d522f475Smrg
5582d522f475Smrg    if (on) {
5583cd3331d0Smrg	if (!TEK4014_SHOWN(term)) {
5584cd3331d0Smrg	    if (tekWidget == 0) {
5585cd3331d0Smrg		TekInit();	/* will exit on failure */
5586cd3331d0Smrg	    }
5587cd3331d0Smrg	    if (tekWidget != 0) {
5588cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
5589cd3331d0Smrg		XtRealizeWidget(tekParent);
5590cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
5591d522f475Smrg#if OPT_TOOLBAR
5592cd3331d0Smrg		/* we need both of these during initialization */
5593cd3331d0Smrg		XtMapWidget(tekParent);
5594cd3331d0Smrg		XtMapWidget(tekWidget);
5595d522f475Smrg#endif
5596cd3331d0Smrg		XtOverrideTranslations(tekParent,
5597cd3331d0Smrg				       XtParseTranslationTable
5598cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
5599cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
5600cd3331d0Smrg				       XtWindow(tekParent),
5601cd3331d0Smrg				       &wm_delete_window, 1);
5602cd3331d0Smrg		TEK4014_SHOWN(term) = True;
5603cd3331d0Smrg	    }
5604d522f475Smrg	}
5605d522f475Smrg    } else {
5606d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
5607d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
5608d522f475Smrg			    TShellWindow,
5609d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
5610d522f475Smrg	    TEK4014_SHOWN(term) = False;
5611d522f475Smrg	}
5612d522f475Smrg    }
5613d522f475Smrg    set_tekhide_sensitivity();
5614d522f475Smrg    set_vthide_sensitivity();
5615d522f475Smrg    update_vtshow();
5616d522f475Smrg    update_tekshow();
5617d522f475Smrg    update_vttekmode();
5618d522f475Smrg    return;
5619d522f475Smrg}
5620d522f475Smrg
5621d522f475Smrgvoid
5622d522f475Smrgend_tek_mode(void)
5623d522f475Smrg{
5624cd3331d0Smrg    XtermWidget xw = term;
5625cd3331d0Smrg
5626cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
5627cd3331d0Smrg	FlushLog(xw);
5628d522f475Smrg	longjmp(Tekend, 1);
5629d522f475Smrg    }
5630d522f475Smrg    return;
5631d522f475Smrg}
5632d522f475Smrg
5633d522f475Smrgvoid
5634d522f475Smrgend_vt_mode(void)
5635d522f475Smrg{
5636cd3331d0Smrg    XtermWidget xw = term;
5637cd3331d0Smrg
5638cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
5639cd3331d0Smrg	FlushLog(xw);
5640cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
5641d522f475Smrg	longjmp(VTend, 1);
5642d522f475Smrg    }
5643d522f475Smrg    return;
5644d522f475Smrg}
5645d522f475Smrg
5646d522f475Smrgvoid
5647d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
5648d522f475Smrg{
5649d522f475Smrg    if (tovt) {
5650d522f475Smrg	if (tekRefreshList)
5651d522f475Smrg	    TekRefresh(tekWidget);
5652d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
5653d522f475Smrg    } else {
5654d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
5655d522f475Smrg    }
5656d522f475Smrg}
5657d522f475Smrg
5658d522f475Smrgvoid
5659d522f475Smrghide_vt_window(void)
5660d522f475Smrg{
5661d522f475Smrg    set_vt_visibility(False);
5662d522f475Smrg    if (!TEK4014_ACTIVE(term))
5663d522f475Smrg	switch_modes(False);	/* switch to tek mode */
5664d522f475Smrg}
5665d522f475Smrg
5666d522f475Smrgvoid
5667d522f475Smrghide_tek_window(void)
5668d522f475Smrg{
5669d522f475Smrg    set_tek_visibility(False);
5670d522f475Smrg    tekRefreshList = (TekLink *) 0;
5671d522f475Smrg    if (TEK4014_ACTIVE(term))
5672d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
5673d522f475Smrg}
5674d522f475Smrg#endif /* OPT_TEK4014 */
5675d522f475Smrg
5676d522f475Smrgstatic const char *
5677d522f475Smrgskip_punct(const char *s)
5678d522f475Smrg{
5679d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
5680d522f475Smrg	++s;
5681d522f475Smrg    }
5682d522f475Smrg    return s;
5683d522f475Smrg}
5684d522f475Smrg
5685d522f475Smrgstatic int
5686d522f475Smrgcmp_options(const void *a, const void *b)
5687d522f475Smrg{
5688d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
5689d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
5690d522f475Smrg    return strcmp(s1, s2);
5691d522f475Smrg}
5692d522f475Smrg
5693d522f475Smrgstatic int
5694d522f475Smrgcmp_resources(const void *a, const void *b)
5695d522f475Smrg{
5696d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
5697d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
5698d522f475Smrg}
5699d522f475Smrg
5700d522f475SmrgXrmOptionDescRec *
5701d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
5702d522f475Smrg{
5703d522f475Smrg    static XrmOptionDescRec *res_array = 0;
5704d522f475Smrg
5705d522f475Smrg#ifdef NO_LEAKS
5706cd3331d0Smrg    if (descs == 0) {
5707cd3331d0Smrg	if (res_array != 0) {
5708cd3331d0Smrg	    free(res_array);
5709cd3331d0Smrg	    res_array = 0;
5710cd3331d0Smrg	}
5711d522f475Smrg    } else
5712d522f475Smrg#endif
5713d522f475Smrg    if (res_array == 0) {
5714d522f475Smrg	Cardinal j;
5715d522f475Smrg
5716d522f475Smrg	/* make a sorted index to 'resources' */
5717d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
5718cd3331d0Smrg	if (res_array != 0) {
5719cd3331d0Smrg	    for (j = 0; j < res_count; j++)
5720cd3331d0Smrg		res_array[j] = descs[j];
5721cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
5722cd3331d0Smrg	}
5723d522f475Smrg    }
5724d522f475Smrg    return res_array;
5725d522f475Smrg}
5726d522f475Smrg
5727d522f475Smrg/*
5728d522f475Smrg * The first time this is called, construct sorted index to the main program's
5729d522f475Smrg * list of options, taking into account the on/off options which will be
5730d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
5731d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
5732d522f475Smrg */
5733d522f475SmrgOptionHelp *
5734d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
5735d522f475Smrg{
5736d522f475Smrg    static OptionHelp *opt_array = 0;
5737d522f475Smrg
5738d522f475Smrg#ifdef NO_LEAKS
5739d522f475Smrg    if (descs == 0 && opt_array != 0) {
5740d522f475Smrg	sortedOptDescs(descs, numDescs);
5741d522f475Smrg	free(opt_array);
5742d522f475Smrg	opt_array = 0;
5743d522f475Smrg	return 0;
5744d522f475Smrg    } else if (options == 0 || descs == 0) {
5745d522f475Smrg	return 0;
5746d522f475Smrg    }
5747d522f475Smrg#endif
5748d522f475Smrg
5749d522f475Smrg    if (opt_array == 0) {
5750cd3331d0Smrg	size_t opt_count, j;
5751d522f475Smrg#if OPT_TRACE
5752d522f475Smrg	Cardinal k;
5753d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
5754d522f475Smrg	int code;
5755cd3331d0Smrg	const char *mesg;
5756d522f475Smrg#else
5757d522f475Smrg	(void) descs;
5758d522f475Smrg	(void) numDescs;
5759d522f475Smrg#endif
5760d522f475Smrg
5761d522f475Smrg	/* count 'options' and make a sorted index to it */
5762d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
5763d522f475Smrg	    ;
5764d522f475Smrg	}
5765d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
5766d522f475Smrg	for (j = 0; j < opt_count; j++)
5767d522f475Smrg	    opt_array[j] = options[j];
5768d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
5769d522f475Smrg
5770d522f475Smrg	/* supply the "turn on/off" strings if needed */
5771d522f475Smrg#if OPT_TRACE
5772d522f475Smrg	for (j = 0; j < opt_count; j++) {
5773712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
5774c219fbebSmrg		char temp[80];
5775cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
5776d522f475Smrg		for (k = 0; k < numDescs; ++k) {
5777cd3331d0Smrg		    const char *value = res_array[k].value;
5778d522f475Smrg		    if (res_array[k].option[0] == '-') {
5779d522f475Smrg			code = -1;
5780d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
5781d522f475Smrg			code = 1;
5782d522f475Smrg		    } else {
5783d522f475Smrg			code = 0;
5784d522f475Smrg		    }
57853367019cSmrg		    sprintf(temp, "%.*s",
57863367019cSmrg			    (int) sizeof(temp) - 2,
57873367019cSmrg			    opt_array[j].desc);
5788c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
5789d522f475Smrg			code = -code;
5790d522f475Smrg		    if (code != 0
5791d522f475Smrg			&& res_array[k].value != 0
5792d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
5793d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
5794d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
5795d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
5796d522f475Smrg			    mesg = "turn on/off";
5797d522f475Smrg			} else {
5798d522f475Smrg			    mesg = "turn off/on";
5799d522f475Smrg			}
5800d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
5801712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
5802d522f475Smrg				char *s = CastMallocN(char,
5803d522f475Smrg						      strlen(mesg)
5804d522f475Smrg						      + 1
5805d522f475Smrg						      + strlen(opt_array[j].desc));
5806d522f475Smrg				if (s != 0) {
5807d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
5808d522f475Smrg				    opt_array[j].desc = s;
5809d522f475Smrg				}
5810d522f475Smrg			    } else {
5811d522f475Smrg				TRACE(("OOPS "));
5812d522f475Smrg			    }
5813d522f475Smrg			}
5814d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
5815d522f475Smrg			       mesg,
5816d522f475Smrg			       res_array[k].option,
5817d522f475Smrg			       res_array[k].value,
5818d522f475Smrg			       opt_array[j].opt,
5819d522f475Smrg			       opt_array[j].desc));
5820d522f475Smrg			break;
5821d522f475Smrg		    }
5822d522f475Smrg		}
5823d522f475Smrg	    }
5824d522f475Smrg	}
5825d522f475Smrg#endif
5826d522f475Smrg    }
5827d522f475Smrg    return opt_array;
5828d522f475Smrg}
5829d522f475Smrg
5830d522f475Smrg/*
5831d522f475Smrg * Report the character-type locale that xterm was started in.
5832d522f475Smrg */
58333367019cSmrgString
5834d522f475SmrgxtermEnvLocale(void)
5835d522f475Smrg{
58363367019cSmrg    static String result;
5837d522f475Smrg
5838d522f475Smrg    if (result == 0) {
5839d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
5840cd3331d0Smrg	    result = x_strdup("C");
5841cd3331d0Smrg	} else {
5842cd3331d0Smrg	    result = x_strdup(result);
5843d522f475Smrg	}
5844d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
5845d522f475Smrg    }
5846d522f475Smrg    return result;
5847d522f475Smrg}
5848d522f475Smrg
5849d522f475Smrgchar *
5850d522f475SmrgxtermEnvEncoding(void)
5851d522f475Smrg{
5852d522f475Smrg    static char *result;
5853d522f475Smrg
5854d522f475Smrg    if (result == 0) {
5855d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5856d522f475Smrg	result = nl_langinfo(CODESET);
5857d522f475Smrg#else
5858d522f475Smrg	char *locale = xtermEnvLocale();
5859d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
5860d522f475Smrg	    result = "ASCII";
5861d522f475Smrg	} else {
5862d522f475Smrg	    result = "ISO-8859-1";
5863d522f475Smrg	}
5864d522f475Smrg#endif
5865d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
5866d522f475Smrg    }
5867d522f475Smrg    return result;
5868d522f475Smrg}
5869d522f475Smrg
5870d522f475Smrg#if OPT_WIDE_CHARS
5871d522f475Smrg/*
5872d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
5873d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
5874d522f475Smrg * various library calls.
5875d522f475Smrg */
5876d522f475SmrgBool
5877d522f475SmrgxtermEnvUTF8(void)
5878d522f475Smrg{
5879d522f475Smrg    static Bool init = False;
5880d522f475Smrg    static Bool result = False;
5881d522f475Smrg
5882d522f475Smrg    if (!init) {
5883d522f475Smrg	init = True;
5884d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5885d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
5886d522f475Smrg#else
5887fa3f02f3Smrg	{
5888fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
5889fa3f02f3Smrg	    int n;
5890fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
5891fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
5892fa3f02f3Smrg	    }
5893fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
5894fa3f02f3Smrg		result = True;
5895fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
5896fa3f02f3Smrg		result = True;
5897fa3f02f3Smrg	    free(locale);
5898fa3f02f3Smrg	}
5899d522f475Smrg#endif
5900d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
5901d522f475Smrg    }
5902d522f475Smrg    return result;
5903d522f475Smrg}
5904d522f475Smrg#endif /* OPT_WIDE_CHARS */
5905d522f475Smrg
5906b7c89284Ssnj/*
5907b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
5908b7c89284Ssnj */
5909b7c89284SsnjXtermWidget
5910b7c89284SsnjgetXtermWidget(Widget w)
5911b7c89284Ssnj{
5912b7c89284Ssnj    XtermWidget xw;
5913b7c89284Ssnj
5914b7c89284Ssnj    if (w == 0) {
5915b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
5916b7c89284Ssnj	if (!IsXtermWidget(xw)) {
5917b7c89284Ssnj	    xw = 0;
5918b7c89284Ssnj	}
5919b7c89284Ssnj    } else if (IsXtermWidget(w)) {
5920b7c89284Ssnj	xw = (XtermWidget) w;
5921b7c89284Ssnj    } else {
5922b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
5923b7c89284Ssnj    }
5924b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
5925b7c89284Ssnj    return xw;
5926b7c89284Ssnj}
59273367019cSmrg
59283367019cSmrg#if OPT_SESSION_MGT
59293367019cSmrgstatic void
59303367019cSmrgdie_callback(Widget w GCC_UNUSED,
59313367019cSmrg	     XtPointer client_data GCC_UNUSED,
59323367019cSmrg	     XtPointer call_data GCC_UNUSED)
59333367019cSmrg{
59343367019cSmrg    NormalExit();
59353367019cSmrg}
59363367019cSmrg
59373367019cSmrgstatic void
59383367019cSmrgsave_callback(Widget w GCC_UNUSED,
59393367019cSmrg	      XtPointer client_data GCC_UNUSED,
59403367019cSmrg	      XtPointer call_data)
59413367019cSmrg{
59423367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
59433367019cSmrg    /* we have nothing to save */
59443367019cSmrg    token->save_success = True;
59453367019cSmrg}
59463367019cSmrg
59473367019cSmrgstatic void
59483367019cSmrgicewatch(IceConn iceConn,
59493367019cSmrg	 IcePointer clientData GCC_UNUSED,
59503367019cSmrg	 Bool opening,
59513367019cSmrg	 IcePointer * watchData GCC_UNUSED)
59523367019cSmrg{
59533367019cSmrg    if (opening) {
59543367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
59553367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
59563367019cSmrg    } else {
59573367019cSmrg	ice_fd = -1;
59583367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
59593367019cSmrg    }
59603367019cSmrg}
59613367019cSmrg
59623367019cSmrgvoid
59633367019cSmrgxtermOpenSession(void)
59643367019cSmrg{
59653367019cSmrg    if (resource.sessionMgt) {
59663367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
59673367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
59683367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
59693367019cSmrg    }
59703367019cSmrg}
59713367019cSmrg
59723367019cSmrgvoid
59733367019cSmrgxtermCloseSession(void)
59743367019cSmrg{
59753367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
59763367019cSmrg}
59773367019cSmrg#endif /* OPT_SESSION_MGT */
59783367019cSmrg
59793367019cSmrgWidget
59803367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
59813367019cSmrg		     String my_class,
59823367019cSmrg		     XrmOptionDescRec * options,
59833367019cSmrg		     Cardinal num_options,
59843367019cSmrg		     int *argc_in_out,
5985fa3f02f3Smrg		     String *argv_in_out,
5986fa3f02f3Smrg		     String *fallback_resources,
59873367019cSmrg		     WidgetClass widget_class,
59883367019cSmrg		     ArgList args,
59893367019cSmrg		     Cardinal num_args)
59903367019cSmrg{
59913367019cSmrg    Widget result;
59923367019cSmrg
59933367019cSmrg    XtSetErrorHandler(xt_error);
59943367019cSmrg#if OPT_SESSION_MGT
59953367019cSmrg    result = XtOpenApplication(app_context_return,
59963367019cSmrg			       my_class,
59973367019cSmrg			       options,
59983367019cSmrg			       num_options,
59993367019cSmrg			       argc_in_out,
60003367019cSmrg			       argv_in_out,
60013367019cSmrg			       fallback_resources,
60023367019cSmrg			       widget_class,
60033367019cSmrg			       args,
60043367019cSmrg			       num_args);
60053367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
60063367019cSmrg#else
60079a64e1c5Smrg    (void) widget_class;
60089a64e1c5Smrg    (void) args;
60099a64e1c5Smrg    (void) num_args;
60103367019cSmrg    result = XtAppInitialize(app_context_return,
60113367019cSmrg			     my_class,
60123367019cSmrg			     options,
60133367019cSmrg			     num_options,
60143367019cSmrg			     argc_in_out,
60153367019cSmrg			     argv_in_out,
60163367019cSmrg			     fallback_resources,
60173367019cSmrg			     NULL, 0);
60183367019cSmrg#endif /* OPT_SESSION_MGT */
60193367019cSmrg    XtSetErrorHandler((XtErrorHandler) 0);
60203367019cSmrg
60213367019cSmrg    return result;
60223367019cSmrg}
60233367019cSmrg
60243367019cSmrgstatic int x11_errors;
60253367019cSmrg
60263367019cSmrgstatic int
60279a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
60283367019cSmrg{
60293367019cSmrg    (void) display;
60303367019cSmrg    (void) error_event;
60313367019cSmrg    ++x11_errors;
60323367019cSmrg    return 0;
60333367019cSmrg}
60343367019cSmrg
60353367019cSmrgBoolean
6036fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
60373367019cSmrg{
60383367019cSmrg    Boolean result = False;
60393367019cSmrg    Status code;
60403367019cSmrg
60413367019cSmrg    memset(attrs, 0, sizeof(*attrs));
60423367019cSmrg    if (win != None) {
60433367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
60443367019cSmrg	x11_errors = 0;
60453367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
60463367019cSmrg	XSetErrorHandler(save);
60473367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
60483367019cSmrg	if (result) {
60493367019cSmrg	    TRACE_WIN_ATTRS(attrs);
60503367019cSmrg	} else {
60513367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
60523367019cSmrg	}
60533367019cSmrg    }
60543367019cSmrg    return result;
60553367019cSmrg}
60563367019cSmrg
60573367019cSmrgBoolean
6058fa3f02f3SmrgxtermGetWinProp(Display *display,
60593367019cSmrg		Window win,
60603367019cSmrg		Atom property,
60613367019cSmrg		long long_offset,
60623367019cSmrg		long long_length,
60633367019cSmrg		Atom req_type,
60649a64e1c5Smrg		Atom *actual_type_return,
60653367019cSmrg		int *actual_format_return,
60663367019cSmrg		unsigned long *nitems_return,
60673367019cSmrg		unsigned long *bytes_after_return,
60683367019cSmrg		unsigned char **prop_return)
60693367019cSmrg{
60703367019cSmrg    Boolean result = True;
60713367019cSmrg
60723367019cSmrg    if (win != None) {
60733367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
60743367019cSmrg	x11_errors = 0;
60753367019cSmrg	if (XGetWindowProperty(display,
60763367019cSmrg			       win,
60773367019cSmrg			       property,
60783367019cSmrg			       long_offset,
60793367019cSmrg			       long_length,
60803367019cSmrg			       False,
60813367019cSmrg			       req_type,
60823367019cSmrg			       actual_type_return,
60833367019cSmrg			       actual_format_return,
60843367019cSmrg			       nitems_return,
60853367019cSmrg			       bytes_after_return,
60863367019cSmrg			       prop_return) == Success
60873367019cSmrg	    && x11_errors == 0) {
60883367019cSmrg	    result = True;
60893367019cSmrg	}
60903367019cSmrg	XSetErrorHandler(save);
60913367019cSmrg    }
60923367019cSmrg    return result;
60933367019cSmrg}
60943367019cSmrg
60953367019cSmrgvoid
60963367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
60973367019cSmrg{
60983367019cSmrg    Display *dpy = XtDisplay(toplevel);
60993367019cSmrg    XWindowAttributes attrs;
61003367019cSmrg
61013367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
61023367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
61033367019cSmrg	XtermWidget xw = term;
61043367019cSmrg	TScreen *screen = TScreenOf(xw);
61053367019cSmrg
61063367019cSmrg	XtRealizeWidget(toplevel);
61073367019cSmrg
61083367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
61093367019cSmrg	       XtWindow(toplevel),
61103367019cSmrg	       winToEmbedInto));
61113367019cSmrg	XReparentWindow(dpy,
61123367019cSmrg			XtWindow(toplevel),
61133367019cSmrg			winToEmbedInto, 0, 0);
61143367019cSmrg
61153367019cSmrg	screen->embed_high = (Dimension) attrs.height;
61163367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
61173367019cSmrg    }
61183367019cSmrg}
611994644356Smrg
612094644356Smrgvoid
612194644356Smrgfree_string(String value)
612294644356Smrg{
612394644356Smrg    free((void *) value);
612494644356Smrg}
6125