misc.c revision fa3f02f3
1fa3f02f3Smrg/* $XTermId: misc.c,v 1.703 2014/03/07 01:43:32 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4fa3f02f3Smrg * Copyright 1999-2013,2014 by Thomas E. Dickey
5d522f475Smrg *
6cd3331d0Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg#include <version.h>
56b7c89284Ssnj#include <main.h>
57d522f475Smrg#include <xterm.h>
58d522f475Smrg
59d522f475Smrg#include <sys/stat.h>
60d522f475Smrg#include <stdio.h>
613367019cSmrg#include <stdarg.h>
62d522f475Smrg#include <signal.h>
63d522f475Smrg#include <ctype.h>
64d522f475Smrg#include <pwd.h>
65d522f475Smrg#include <sys/wait.h>
66d522f475Smrg
67d522f475Smrg#include <X11/keysym.h>
68d522f475Smrg#include <X11/Xatom.h>
69d522f475Smrg#include <X11/cursorfont.h>
70d522f475Smrg#include <X11/Xlocale.h>
71d522f475Smrg
72d522f475Smrg#include <X11/Xmu/Error.h>
73d522f475Smrg#include <X11/Xmu/SysUtil.h>
74d522f475Smrg#include <X11/Xmu/WinUtil.h>
75d522f475Smrg#include <X11/Xmu/Xmu.h>
76d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
77d522f475Smrg#include <X11/Sunkeysym.h>
78d522f475Smrg#endif
79d522f475Smrg
803367019cSmrg#ifdef HAVE_LIBXPM
813367019cSmrg#include <X11/xpm.h>
823367019cSmrg#endif
833367019cSmrg
84d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
85d522f475Smrg#include <langinfo.h>
86d522f475Smrg#endif
87d522f475Smrg
88d522f475Smrg#include <xutf8.h>
89d522f475Smrg
90d522f475Smrg#include <data.h>
91d522f475Smrg#include <error.h>
92d522f475Smrg#include <menu.h>
93d522f475Smrg#include <fontutils.h>
94d522f475Smrg#include <xstrings.h>
95d522f475Smrg#include <xtermcap.h>
96d522f475Smrg#include <VTparse.h>
97fa3f02f3Smrg#include <graphics.h>
98d522f475Smrg
99d522f475Smrg#include <assert.h>
100d522f475Smrg
101d522f475Smrg#if (XtSpecificationRelease < 6)
102d522f475Smrg#ifndef X_GETTIMEOFDAY
103d522f475Smrg#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0)
104d522f475Smrg#endif
105d522f475Smrg#endif
106d522f475Smrg
107d522f475Smrg#ifdef VMS
108d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
109d522f475Smrg#ifdef ALLOWLOGFILEEXEC
110d522f475Smrg#undef ALLOWLOGFILEEXEC
111d522f475Smrg#endif
112d522f475Smrg#endif /* VMS */
113d522f475Smrg
114d522f475Smrg#if OPT_TEK4014
115d522f475Smrg#define OUR_EVENT(event,Type) \
116d522f475Smrg		(event.type == Type && \
117d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
118d522f475Smrg		    (tekWidget && \
119d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
120d522f475Smrg#else
121d522f475Smrg#define OUR_EVENT(event,Type) \
122d522f475Smrg		(event.type == Type && \
123d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
124d522f475Smrg#endif
125d522f475Smrg
1263367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
127d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
128d522f475Smrg
1293367019cSmrgstatic char emptyString[] = "";
1303367019cSmrg
131d522f475Smrg#if OPT_EXEC_XTERM
132d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
133d522f475Smrg   error; adapted from libc docs */
134d522f475Smrgstatic char *
135d522f475SmrgReadlink(const char *filename)
136d522f475Smrg{
137d522f475Smrg    char *buf = NULL;
138cd3331d0Smrg    size_t size = 100;
139d522f475Smrg    int n;
140d522f475Smrg
141d522f475Smrg    for (;;) {
142d522f475Smrg	buf = TypeRealloc(char, size, buf);
143d522f475Smrg	memset(buf, 0, size);
144d522f475Smrg
145cd3331d0Smrg	n = (int) readlink(filename, buf, size);
146d522f475Smrg	if (n < 0) {
147d522f475Smrg	    free(buf);
148d522f475Smrg	    return NULL;
149d522f475Smrg	}
150d522f475Smrg
151d522f475Smrg	if ((unsigned) n < size) {
152d522f475Smrg	    return buf;
153d522f475Smrg	}
154d522f475Smrg
155d522f475Smrg	size *= 2;
156d522f475Smrg    }
157d522f475Smrg}
158d522f475Smrg#endif /* OPT_EXEC_XTERM */
159d522f475Smrg
160d522f475Smrgstatic void
161d522f475SmrgSleep(int msec)
162d522f475Smrg{
163d522f475Smrg    static struct timeval select_timeout;
164d522f475Smrg
165d522f475Smrg    select_timeout.tv_sec = 0;
166d522f475Smrg    select_timeout.tv_usec = msec * 1000;
167d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
168d522f475Smrg}
169d522f475Smrg
170d522f475Smrgstatic void
1713367019cSmrgselectwindow(XtermWidget xw, int flag)
172d522f475Smrg{
1733367019cSmrg    TScreen *screen = TScreenOf(xw);
1743367019cSmrg
175d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
176d522f475Smrg
177d522f475Smrg#if OPT_TEK4014
1783367019cSmrg    if (TEK4014_ACTIVE(xw)) {
179d522f475Smrg	if (!Ttoggled)
180d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
181d522f475Smrg	screen->select |= flag;
182d522f475Smrg	if (!Ttoggled)
183d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
184d522f475Smrg    } else
185d522f475Smrg#endif
186d522f475Smrg    {
1873367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
1883367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
1893367019cSmrg	if (input && input->xic)
1903367019cSmrg	    XSetICFocus(input->xic);
1913367019cSmrg#endif
192d522f475Smrg
193d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
194d522f475Smrg	    HideCursor();
195d522f475Smrg	screen->select |= flag;
196d522f475Smrg	if (screen->cursor_state)
197d522f475Smrg	    ShowCursor();
198d522f475Smrg    }
199cd3331d0Smrg    GetScrollLock(screen);
200d522f475Smrg}
201d522f475Smrg
202d522f475Smrgstatic void
2033367019cSmrgunselectwindow(XtermWidget xw, int flag)
204d522f475Smrg{
2053367019cSmrg    TScreen *screen = TScreenOf(xw);
2063367019cSmrg
207d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
208d522f475Smrg
2093367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
210d522f475Smrg	screen->hide_pointer = False;
2113367019cSmrg	xtermDisplayCursor(xw);
212d522f475Smrg    }
213d522f475Smrg
214d522f475Smrg    if (!screen->always_highlight) {
215d522f475Smrg#if OPT_TEK4014
2163367019cSmrg	if (TEK4014_ACTIVE(xw)) {
217d522f475Smrg	    if (!Ttoggled)
218d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
219d522f475Smrg	    screen->select &= ~flag;
220d522f475Smrg	    if (!Ttoggled)
221d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
222d522f475Smrg	} else
223d522f475Smrg#endif
224d522f475Smrg	{
2253367019cSmrg#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD
2263367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2273367019cSmrg	    if (input && input->xic)
2283367019cSmrg		XUnsetICFocus(input->xic);
2293367019cSmrg#endif
230d522f475Smrg
231d522f475Smrg	    screen->select &= ~flag;
232d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
233d522f475Smrg		HideCursor();
234d522f475Smrg	    if (screen->cursor_state)
235d522f475Smrg		ShowCursor();
236d522f475Smrg	}
237d522f475Smrg    }
238d522f475Smrg}
239d522f475Smrg
240d522f475Smrgstatic void
241d522f475SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent * ev)
242d522f475Smrg{
243d522f475Smrg    TScreen *screen = TScreenOf(xw);
244d522f475Smrg
245d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
246cd3331d0Smrg    TRACE_FOCUS(xw, ev);
247cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
248cd3331d0Smrg	ev->focus &&
249cd3331d0Smrg	!(screen->select & FOCUS))
2503367019cSmrg	selectwindow(xw, INWINDOW);
251d522f475Smrg}
252d522f475Smrg
253d522f475Smrgstatic void
254d522f475SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent * ev)
255d522f475Smrg{
256d522f475Smrg    TScreen *screen = TScreenOf(xw);
257d522f475Smrg
258d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
259cd3331d0Smrg    TRACE_FOCUS(xw, ev);
260cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
261cd3331d0Smrg	ev->focus &&
262cd3331d0Smrg	!(screen->select & FOCUS))
2633367019cSmrg	unselectwindow(xw, INWINDOW);
264d522f475Smrg}
265d522f475Smrg
266d522f475Smrg#ifndef XUrgencyHint
267d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
268d522f475Smrg#endif
269d522f475Smrg
270d522f475Smrgstatic void
271c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
272d522f475Smrg{
273c219fbebSmrg    TScreen *screen = TScreenOf(xw);
274c219fbebSmrg
275d522f475Smrg    if (screen->bellIsUrgent) {
276c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
277d522f475Smrg	if (h != 0) {
278c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
279d522f475Smrg		h->flags |= XUrgencyHint;
280d522f475Smrg	    } else {
281d522f475Smrg		h->flags &= ~XUrgencyHint;
282d522f475Smrg	    }
283c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
284d522f475Smrg	}
285d522f475Smrg    }
286d522f475Smrg}
287d522f475Smrg
288d522f475Smrgvoid
289d522f475Smrgdo_xevents(void)
290d522f475Smrg{
291d522f475Smrg    TScreen *screen = TScreenOf(term);
292d522f475Smrg
2933367019cSmrg    if (xtermAppPending()
294d522f475Smrg	||
295d522f475Smrg#if defined(VMS) || defined(__VMS)
296d522f475Smrg	screen->display->qlen > 0
297d522f475Smrg#else
298d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
299d522f475Smrg#endif
300d522f475Smrg	)
301d522f475Smrg	xevents();
302d522f475Smrg}
303d522f475Smrg
304d522f475Smrgvoid
305d522f475SmrgxtermDisplayCursor(XtermWidget xw)
306d522f475Smrg{
307d522f475Smrg    TScreen *screen = TScreenOf(xw);
308d522f475Smrg
309d522f475Smrg    if (screen->Vshow) {
310d522f475Smrg	if (screen->hide_pointer) {
311d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
312d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
313d522f475Smrg	} else {
314d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
315d522f475Smrg	    recolor_cursor(screen,
316d522f475Smrg			   screen->pointer_cursor,
317d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
318d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
319d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
320d522f475Smrg	}
321d522f475Smrg    }
322d522f475Smrg}
323d522f475Smrg
324d522f475Smrgvoid
325d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
326d522f475Smrg{
327d522f475Smrg    static int tried = -1;
328d522f475Smrg    TScreen *screen = TScreenOf(xw);
329d522f475Smrg
330d522f475Smrg#if OPT_TEK4014
331d522f475Smrg    if (TEK4014_SHOWN(xw))
332d522f475Smrg	enable = True;
333d522f475Smrg#endif
334d522f475Smrg
335d522f475Smrg    /*
336d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
337d522f475Smrg     * the mouse-mode:
338d522f475Smrg     */
339d522f475Smrg    if (!enable) {
340d522f475Smrg	switch (screen->pointer_mode) {
341d522f475Smrg	case pNever:
342d522f475Smrg	    enable = True;
343d522f475Smrg	    break;
344d522f475Smrg	case pNoMouse:
345d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
346d522f475Smrg		enable = True;
347d522f475Smrg	    break;
348d522f475Smrg	case pAlways:
3493367019cSmrg	case pFocused:
350d522f475Smrg	    break;
351d522f475Smrg	}
352d522f475Smrg    }
353d522f475Smrg
354d522f475Smrg    if (enable) {
355d522f475Smrg	if (screen->hide_pointer) {
356d522f475Smrg	    screen->hide_pointer = False;
357d522f475Smrg	    xtermDisplayCursor(xw);
358d522f475Smrg	    switch (screen->send_mouse_pos) {
359d522f475Smrg	    case ANY_EVENT_MOUSE:
360d522f475Smrg		break;
361d522f475Smrg	    default:
362d522f475Smrg		MotionOff(screen, xw);
363d522f475Smrg		break;
364d522f475Smrg	    }
365d522f475Smrg	}
366d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
367d522f475Smrg	if (screen->hidden_cursor == 0) {
368d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
369d522f475Smrg	}
370d522f475Smrg	if (screen->hidden_cursor == 0) {
371d522f475Smrg	    tried = 1;
372d522f475Smrg	} else {
373d522f475Smrg	    tried = 0;
374d522f475Smrg	    screen->hide_pointer = True;
375d522f475Smrg	    xtermDisplayCursor(xw);
376d522f475Smrg	    MotionOn(screen, xw);
377d522f475Smrg	}
378d522f475Smrg    }
379d522f475Smrg}
380d522f475Smrg
3813367019cSmrg#if OPT_TRACE
3823367019cSmrgstatic void
3833367019cSmrgTraceExposeEvent(XEvent * arg)
3843367019cSmrg{
3853367019cSmrg    XExposeEvent *event = (XExposeEvent *) arg;
3863367019cSmrg
3873367019cSmrg    TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n",
3883367019cSmrg	   event->serial,
3893367019cSmrg	   event->count,
3903367019cSmrg	   event->y,
3913367019cSmrg	   event->x,
3923367019cSmrg	   event->height,
3933367019cSmrg	   event->width,
3943367019cSmrg	   event->window));
3953367019cSmrg}
3963367019cSmrg
3973367019cSmrg#else
3983367019cSmrg#define TraceExposeEvent(event)	/* nothing */
3993367019cSmrg#endif
4003367019cSmrg
4013367019cSmrg/* true if p contains q */
4023367019cSmrg#define ExposeContains(p,q) \
4033367019cSmrg	    ((p)->y <= (q)->y \
4043367019cSmrg	  && (p)->x <= (q)->x \
4053367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
4063367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
4073367019cSmrg
4083367019cSmrgstatic XtInputMask
4093367019cSmrgmergeExposeEvents(XEvent * target)
4103367019cSmrg{
4113367019cSmrg    XEvent next_event;
4123367019cSmrg    XExposeEvent *p, *q;
4133367019cSmrg
4143367019cSmrg    TRACE(("pending Expose...?\n"));
4153367019cSmrg    TraceExposeEvent(target);
4163367019cSmrg    XtAppNextEvent(app_con, target);
4173367019cSmrg    p = (XExposeEvent *) target;
4183367019cSmrg
4193367019cSmrg    while (XtAppPending(app_con)
4203367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4213367019cSmrg	   && next_event.type == Expose) {
4223367019cSmrg	Boolean merge_this = False;
4233367019cSmrg
4243367019cSmrg	TraceExposeEvent(&next_event);
4253367019cSmrg	q = (XExposeEvent *) (&next_event);
4263367019cSmrg	XtAppNextEvent(app_con, &next_event);
4273367019cSmrg
4283367019cSmrg	/*
4293367019cSmrg	 * If either window is contained within the other, merge the events.
4303367019cSmrg	 * The traces show that there are also cases where a full repaint of
4313367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4323367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4333367019cSmrg	 * to skim several events ahead.
4343367019cSmrg	 */
4353367019cSmrg	if (p->window == q->window) {
4363367019cSmrg	    if (ExposeContains(p, q)) {
4373367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4383367019cSmrg		merge_this = True;
4393367019cSmrg		next_event = *target;
4403367019cSmrg	    } else if (ExposeContains(q, p)) {
4413367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4423367019cSmrg		merge_this = True;
4433367019cSmrg	    }
4443367019cSmrg	}
4453367019cSmrg	if (!merge_this) {
4463367019cSmrg	    XtDispatchEvent(target);
4473367019cSmrg	}
4483367019cSmrg	*target = next_event;
4493367019cSmrg    }
4503367019cSmrg    XtDispatchEvent(target);
4513367019cSmrg    return XtAppPending(app_con);
4523367019cSmrg}
4533367019cSmrg
4543367019cSmrg#if OPT_TRACE
4553367019cSmrgstatic void
4563367019cSmrgTraceConfigureEvent(XEvent * arg)
4573367019cSmrg{
4583367019cSmrg    XConfigureEvent *event = (XConfigureEvent *) arg;
4593367019cSmrg
4603367019cSmrg    TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n",
4613367019cSmrg	   event->serial,
4623367019cSmrg	   event->y,
4633367019cSmrg	   event->x,
4643367019cSmrg	   event->height,
4653367019cSmrg	   event->width,
4663367019cSmrg	   event->window));
4673367019cSmrg}
4683367019cSmrg
4693367019cSmrg#else
4703367019cSmrg#define TraceConfigureEvent(event)	/* nothing */
4713367019cSmrg#endif
4723367019cSmrg
4733367019cSmrg/*
4743367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4753367019cSmrg * event.  Remove that from the queue so we can look further.
4763367019cSmrg *
4773367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4783367019cSmrg * that.  If the adjacent events are for different windows, process the older
4793367019cSmrg * event and update the event used for comparing windows.  If they are for the
4803367019cSmrg * same window, only the newer event is of interest.
4813367019cSmrg *
4823367019cSmrg * Finally, process the (remaining) configure-notify event.
4833367019cSmrg */
4843367019cSmrgstatic XtInputMask
4853367019cSmrgmergeConfigureEvents(XEvent * target)
4863367019cSmrg{
4873367019cSmrg    XEvent next_event;
4883367019cSmrg    XConfigureEvent *p, *q;
4893367019cSmrg
4903367019cSmrg    XtAppNextEvent(app_con, target);
4913367019cSmrg    p = (XConfigureEvent *) target;
4923367019cSmrg
4933367019cSmrg    TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no"));
4943367019cSmrg    TraceConfigureEvent(target);
4953367019cSmrg
4963367019cSmrg    if (XtAppPending(app_con)
4973367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
4983367019cSmrg	&& next_event.type == ConfigureNotify) {
4993367019cSmrg	Boolean merge_this = False;
5003367019cSmrg
5013367019cSmrg	TraceConfigureEvent(&next_event);
5023367019cSmrg	XtAppNextEvent(app_con, &next_event);
5033367019cSmrg	q = (XConfigureEvent *) (&next_event);
5043367019cSmrg
5053367019cSmrg	if (p->window == q->window) {
5063367019cSmrg	    TRACE(("pending Configure...merged\n"));
5073367019cSmrg	    merge_this = True;
5083367019cSmrg	}
5093367019cSmrg	if (!merge_this) {
5103367019cSmrg	    TRACE(("pending Configure...skipped\n"));
5113367019cSmrg	    XtDispatchEvent(target);
5123367019cSmrg	}
5133367019cSmrg	*target = next_event;
5143367019cSmrg    }
5153367019cSmrg    XtDispatchEvent(target);
5163367019cSmrg    return XtAppPending(app_con);
5173367019cSmrg}
5183367019cSmrg
5193367019cSmrg/*
5203367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5213367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5223367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5233367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5243367019cSmrg * point.
5253367019cSmrg *
5263367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5273367019cSmrg */
5283367019cSmrgXtInputMask
5293367019cSmrgxtermAppPending(void)
5303367019cSmrg{
5313367019cSmrg    XtInputMask result = XtAppPending(app_con);
5323367019cSmrg    XEvent this_event;
5333367019cSmrg
5343367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
5353367019cSmrg	if (this_event.type == Expose) {
5363367019cSmrg	    result = mergeExposeEvents(&this_event);
537fa3f02f3Smrg	    TRACE(("got merged expose events\n"));
5383367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5393367019cSmrg	    result = mergeConfigureEvents(&this_event);
540fa3f02f3Smrg	    TRACE(("got merged configure notify events\n"));
5413367019cSmrg	} else {
5423367019cSmrg	    TRACE(("pending %s\n", visibleEventType(this_event.type)));
5433367019cSmrg	    break;
5443367019cSmrg	}
5453367019cSmrg    }
5463367019cSmrg    return result;
5473367019cSmrg}
5483367019cSmrg
549d522f475Smrgvoid
550d522f475Smrgxevents(void)
551d522f475Smrg{
552d522f475Smrg    XtermWidget xw = term;
553d522f475Smrg    TScreen *screen = TScreenOf(xw);
554d522f475Smrg    XEvent event;
555d522f475Smrg    XtInputMask input_mask;
556d522f475Smrg
557d522f475Smrg    if (need_cleanup)
5583367019cSmrg	NormalExit();
559d522f475Smrg
560d522f475Smrg    if (screen->scroll_amt)
561d522f475Smrg	FlushScroll(xw);
562d522f475Smrg    /*
563d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
564d522f475Smrg     * will process the timeout and return without blockng on the
565cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
566d522f475Smrg     * with select().
567d522f475Smrg     */
5683367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
569cd3331d0Smrg	if (input_mask & XtIMTimer)
570cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
571d522f475Smrg#if OPT_SESSION_MGT
572cd3331d0Smrg	/*
573cd3331d0Smrg	 * Session management events are alternative input events. Deal with
574cd3331d0Smrg	 * them in the same way.
575cd3331d0Smrg	 */
576cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
577cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
578d522f475Smrg#endif
579cd3331d0Smrg	else
580cd3331d0Smrg	    break;
581cd3331d0Smrg    }
582d522f475Smrg
583d522f475Smrg    /*
584d522f475Smrg     * If there's no XEvents, don't wait around...
585d522f475Smrg     */
586d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
587d522f475Smrg	return;
588d522f475Smrg    do {
589d522f475Smrg	/*
590d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
591d522f475Smrg	 * We simply ignore all events except for those not passed down to
592d522f475Smrg	 * this function, e.g., those handled in in_put().
593d522f475Smrg	 */
594d522f475Smrg	if (screen->waitingForTrackInfo) {
595d522f475Smrg	    Sleep(10);
596d522f475Smrg	    return;
597d522f475Smrg	}
598d522f475Smrg	XtAppNextEvent(app_con, &event);
599d522f475Smrg	/*
600d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
601d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
602d522f475Smrg	 * looking at the event ourselves we make sure that we can
603d522f475Smrg	 * do the right thing.
604d522f475Smrg	 */
605d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
606d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
607d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
608d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
609d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
610d522f475Smrg#if OPT_DEC_LOCATOR
611d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
612d522f475Smrg#endif /* OPT_DEC_LOCATOR */
613d522f475Smrg		   )
614d522f475Smrg		   && event.xany.type == MotionNotify
615d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
616d522f475Smrg	    SendMousePosition(xw, &event);
617cb4a1343Smrg	    xtermShowPointer(xw, True);
618d522f475Smrg	    continue;
619d522f475Smrg	}
620d522f475Smrg
621cb4a1343Smrg	/*
622cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
623cb4a1343Smrg	 * mouse pointer back on.
624cb4a1343Smrg	 */
625cb4a1343Smrg	if (screen->hide_pointer) {
6263367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6273367019cSmrg		switch (event.xany.type) {
6283367019cSmrg		case MotionNotify:
6293367019cSmrg		    xtermShowPointer(xw, True);
6303367019cSmrg		    break;
6313367019cSmrg		}
6323367019cSmrg	    } else {
6333367019cSmrg		switch (event.xany.type) {
6343367019cSmrg		case KeyPress:
6353367019cSmrg		case KeyRelease:
6363367019cSmrg		case ButtonPress:
6373367019cSmrg		case ButtonRelease:
6383367019cSmrg		    /* also these... */
6393367019cSmrg		case Expose:
6403367019cSmrg		case NoExpose:
6413367019cSmrg		case PropertyNotify:
6423367019cSmrg		case ClientMessage:
6433367019cSmrg		    break;
6443367019cSmrg		default:
6453367019cSmrg		    xtermShowPointer(xw, True);
6463367019cSmrg		    break;
6473367019cSmrg		}
648cb4a1343Smrg	    }
649cb4a1343Smrg	}
650cb4a1343Smrg
651d522f475Smrg	if (!event.xany.send_event ||
652d522f475Smrg	    screen->allowSendEvents ||
653d522f475Smrg	    ((event.xany.type != KeyPress) &&
654d522f475Smrg	     (event.xany.type != KeyRelease) &&
655d522f475Smrg	     (event.xany.type != ButtonPress) &&
656d522f475Smrg	     (event.xany.type != ButtonRelease))) {
657d522f475Smrg
658d522f475Smrg	    XtDispatchEvent(&event);
659d522f475Smrg	}
6603367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
661d522f475Smrg}
662d522f475Smrg
663d522f475Smrgstatic Cursor
664d522f475Smrgmake_hidden_cursor(XtermWidget xw)
665d522f475Smrg{
666d522f475Smrg    TScreen *screen = TScreenOf(xw);
667d522f475Smrg    Cursor c;
668d522f475Smrg    Display *dpy = screen->display;
669d522f475Smrg    XFontStruct *fn;
670d522f475Smrg
671d522f475Smrg    static XColor dummy;
672d522f475Smrg
673d522f475Smrg    /*
674d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
675d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
676b7c89284Ssnj     * server insists on drawing _something_.
677d522f475Smrg     */
678d522f475Smrg    TRACE(("Ask for nil2 font\n"));
679d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
680d522f475Smrg	TRACE(("...Ask for fixed font\n"));
681b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
682d522f475Smrg    }
683d522f475Smrg
684d522f475Smrg    if (fn != 0) {
685d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
686d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
687d522f475Smrg	XFreeFont(dpy, fn);
688d522f475Smrg    } else {
689d522f475Smrg	c = 0;
690d522f475Smrg    }
691d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
692d522f475Smrg    return (c);
693d522f475Smrg}
694d522f475Smrg
695fa3f02f3Smrg/*
696fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
697fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
698fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
699fa3f02f3Smrg * until the window is initialized.
700fa3f02f3Smrg */
701fa3f02f3Smrgvoid
702fa3f02f3Smrginit_colored_cursor(void)
703fa3f02f3Smrg{
704fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
705fa3f02f3Smrg    const char *theme = "index.theme";
706fa3f02f3Smrg    const char *pattern = "xtermXXXXXX";
707fa3f02f3Smrg    const char *tmp_dir;
708fa3f02f3Smrg    char *filename;
709fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
710fa3f02f3Smrg    size_t needed;
711fa3f02f3Smrg    FILE *fp;
712fa3f02f3Smrg
713fa3f02f3Smrg    xterm_cursor_theme = 0;
714fa3f02f3Smrg    if (IsEmpty(env)) {
715fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
716fa3f02f3Smrg	    tmp_dir = P_tmpdir;
717fa3f02f3Smrg	}
718fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
719fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
720fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
721fa3f02f3Smrg
722fa3f02f3Smrg#ifdef HAVE_MKDTEMP
723fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
724fa3f02f3Smrg#else
725fa3f02f3Smrg	    if (mktemp(filename) != 0
726fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
727fa3f02f3Smrg		xterm_cursor_theme = filename;
728fa3f02f3Smrg	    }
729fa3f02f3Smrg#endif
730fa3f02f3Smrg	    /*
731fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
732fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
733fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
734fa3f02f3Smrg	     */
735fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
736fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
737fa3f02f3Smrg		strcat(leaf, "/");
738fa3f02f3Smrg		strcat(leaf, theme);
739fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
740fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
741fa3f02f3Smrg		    fclose(fp);
742fa3f02f3Smrg		    *leaf = '\0';
743fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
744fa3f02f3Smrg		    *leaf = '/';
745fa3f02f3Smrg		}
746fa3f02f3Smrg		atexit(cleanup_colored_cursor);
747fa3f02f3Smrg	    }
748fa3f02f3Smrg	}
749fa3f02f3Smrg    }
750fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
751fa3f02f3Smrg}
752fa3f02f3Smrg
753fa3f02f3Smrg/*
754fa3f02f3Smrg * Once done, discard the file and directory holding it.
755fa3f02f3Smrg */
756fa3f02f3Smrgvoid
757fa3f02f3Smrgcleanup_colored_cursor(void)
758fa3f02f3Smrg{
759fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
760fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
761fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
762fa3f02f3Smrg	struct stat sb;
763fa3f02f3Smrg	if (!IsEmpty(my_path)
764fa3f02f3Smrg	    && stat(my_path, &sb) == 0
765fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
766fa3f02f3Smrg	    unlink(xterm_cursor_theme);
767fa3f02f3Smrg	    rmdir(my_path);
768fa3f02f3Smrg	    free(xterm_cursor_theme);
769fa3f02f3Smrg	    xterm_cursor_theme = 0;
770fa3f02f3Smrg	}
771fa3f02f3Smrg    }
772fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
773fa3f02f3Smrg}
774fa3f02f3Smrg
775d522f475SmrgCursor
776d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
777d522f475Smrg		    unsigned long fg,	/* pixel value */
778d522f475Smrg		    unsigned long bg)	/* pixel value */
779d522f475Smrg{
780d522f475Smrg    TScreen *screen = TScreenOf(term);
781d522f475Smrg    Cursor c;
782d522f475Smrg    Display *dpy = screen->display;
783d522f475Smrg
784d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
785d522f475Smrg    if (c != None) {
786d522f475Smrg	recolor_cursor(screen, c, fg, bg);
787d522f475Smrg    }
788d522f475Smrg    return (c);
789d522f475Smrg}
790d522f475Smrg
791d522f475Smrg/* ARGSUSED */
792d522f475Smrgvoid
793d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
794d522f475Smrg		 XEvent * event,
795fa3f02f3Smrg		 String *params GCC_UNUSED,
796d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
797d522f475Smrg{
798cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
799cd3331d0Smrg    Input(term, &event->xkey, False);
800d522f475Smrg}
801d522f475Smrg
802d522f475Smrg/* ARGSUSED */
803d522f475Smrgvoid
804d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
805d522f475Smrg			 XEvent * event,
806fa3f02f3Smrg			 String *params GCC_UNUSED,
807d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
808d522f475Smrg{
809cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
810cd3331d0Smrg    Input(term, &event->xkey, True);
811d522f475Smrg}
812d522f475Smrg
813d522f475Smrg/* ARGSUSED */
814d522f475Smrgvoid
815d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
816d522f475Smrg		  XEvent * event GCC_UNUSED,
817fa3f02f3Smrg		  String *params,
818d522f475Smrg		  Cardinal *nparams)
819d522f475Smrg{
820d522f475Smrg
821d522f475Smrg    if (*nparams != 1)
822d522f475Smrg	return;
823d522f475Smrg
824d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
8250d92cbfdSchristos	const char *abcdef = "ABCDEF";
8260d92cbfdSchristos	const char *xxxxxx;
827cd3331d0Smrg	Char c;
828cd3331d0Smrg	UString p;
8290d92cbfdSchristos	unsigned value = 0;
8300d92cbfdSchristos
831cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
8320d92cbfdSchristos	     '\0'; p++) {
8330d92cbfdSchristos	    value *= 16;
834d522f475Smrg	    if (c >= '0' && c <= '9')
8350d92cbfdSchristos		value += (unsigned) (c - '0');
836fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
8370d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
838d522f475Smrg	    else
839d522f475Smrg		break;
840d522f475Smrg	}
8410d92cbfdSchristos	if (c == '\0') {
8420d92cbfdSchristos	    Char hexval[2];
8430d92cbfdSchristos	    hexval[0] = (Char) value;
8440d92cbfdSchristos	    hexval[1] = 0;
845b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
8460d92cbfdSchristos	}
847d522f475Smrg    } else {
848cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
849d522f475Smrg    }
850d522f475Smrg}
851d522f475Smrg
852d522f475Smrg#if OPT_EXEC_XTERM
853d522f475Smrg
854d522f475Smrg#ifndef PROCFS_ROOT
855d522f475Smrg#define PROCFS_ROOT "/proc"
856d522f475Smrg#endif
857d522f475Smrg
858d522f475Smrg/* ARGSUSED */
859d522f475Smrgvoid
860d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
861d522f475Smrg		    XEvent * event GCC_UNUSED,
862fa3f02f3Smrg		    String *params,
863d522f475Smrg		    Cardinal *nparams)
864d522f475Smrg{
865cd3331d0Smrg    TScreen *screen = TScreenOf(term);
866d522f475Smrg    char *child_cwd = NULL;
867d522f475Smrg    char *child_exe;
868d522f475Smrg    pid_t pid;
869d522f475Smrg
870d522f475Smrg    /*
871d522f475Smrg     * Try to find the actual program which is running in the child process.
872d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
873d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
874d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
875d522f475Smrg     */
876d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
877d522f475Smrg    if (!child_exe) {
878cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
879cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
880d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
881d522f475Smrg	} else {
8823367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
883d522f475Smrg	}
884d522f475Smrg	if (child_exe == 0)
885d522f475Smrg	    return;
886d522f475Smrg    }
887d522f475Smrg
888d522f475Smrg    /*
889d522f475Smrg     * Determine the current working directory of the child so that we can
890d522f475Smrg     * spawn a new terminal in the same directory.
891d522f475Smrg     *
892d522f475Smrg     * If we cannot get the CWD of the child, just use our own.
893d522f475Smrg     */
894d522f475Smrg    if (screen->pid) {
895d522f475Smrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
896d522f475Smrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid);
897d522f475Smrg	child_cwd = Readlink(child_cwd_link);
898d522f475Smrg    }
899d522f475Smrg
900d522f475Smrg    /* The reaper will take care of cleaning up the child */
901d522f475Smrg    pid = fork();
902d522f475Smrg    if (pid == -1) {
9033367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
904d522f475Smrg    } else if (!pid) {
905d522f475Smrg	/* We are the child */
906d522f475Smrg	if (child_cwd) {
907cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
908d522f475Smrg	}
909d522f475Smrg
910d522f475Smrg	if (setuid(screen->uid) == -1
911d522f475Smrg	    || setgid(screen->gid) == -1) {
9123367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
913d522f475Smrg	} else {
9140d92cbfdSchristos	    unsigned myargc = *nparams + 1;
915d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
9160d92cbfdSchristos	    unsigned n = 0;
917d522f475Smrg
918d522f475Smrg	    myargv[n++] = child_exe;
919d522f475Smrg
920d522f475Smrg	    while (n < myargc) {
9213367019cSmrg		myargv[n++] = (char *) *params++;
922d522f475Smrg	    }
923d522f475Smrg
924d522f475Smrg	    myargv[n] = 0;
925d522f475Smrg	    execv(child_exe, myargv);
926d522f475Smrg
927d522f475Smrg	    /* If we get here, we've failed */
9283367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
929d522f475Smrg	}
930d522f475Smrg	_exit(0);
931d522f475Smrg    }
9323367019cSmrg
9333367019cSmrg    /* We are the parent; clean up */
9343367019cSmrg    if (child_cwd)
9353367019cSmrg	free(child_cwd);
9363367019cSmrg    free(child_exe);
937d522f475Smrg}
938d522f475Smrg#endif /* OPT_EXEC_XTERM */
939d522f475Smrg
940d522f475Smrg/*
941d522f475Smrg * Rather than sending characters to the host, put them directly into our
942d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
943d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
944d522f475Smrg *
945d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
946d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
947d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
948d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
949d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
950d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
951d522f475Smrg */
952d522f475Smrg/* ARGSUSED */
953d522f475Smrgvoid
954d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
955d522f475Smrg		XEvent * event GCC_UNUSED,
956fa3f02f3Smrg		String *params,
957d522f475Smrg		Cardinal *param_count)
958d522f475Smrg{
959d522f475Smrg    if (*param_count == 1) {
960cd3331d0Smrg	const char *value = params[0];
961b7c89284Ssnj	int need = (int) strlen(value);
962cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
963cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
964d522f475Smrg
965d522f475Smrg	if (have - used + need < BUF_SIZE) {
966d522f475Smrg
967cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
968d522f475Smrg
969d522f475Smrg	    TRACE(("Interpret %s\n", value));
970d522f475Smrg	    VTbuffer->update++;
971d522f475Smrg	}
972d522f475Smrg    }
973d522f475Smrg}
974d522f475Smrg
975d522f475Smrg/*ARGSUSED*/
976d522f475Smrgvoid
977d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
978d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
979d522f475Smrg		  XEvent * event GCC_UNUSED,
980fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
981d522f475Smrg{
982d522f475Smrg    /* NOP since we handled it above */
983d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
984cd3331d0Smrg    TRACE_FOCUS(w, event);
985d522f475Smrg}
986d522f475Smrg
987d522f475Smrg/*ARGSUSED*/
988d522f475Smrgvoid
989d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
990d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
991d522f475Smrg		  XEvent * event GCC_UNUSED,
992fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
993d522f475Smrg{
994d522f475Smrg    /* NOP since we handled it above */
995d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
996cd3331d0Smrg    TRACE_FOCUS(w, event);
997d522f475Smrg}
998d522f475Smrg
999d522f475Smrg/*ARGSUSED*/
1000d522f475Smrgvoid
1001d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1002d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
1003d522f475Smrg		  XEvent * ev,
1004fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1005d522f475Smrg{
1006d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1007d522f475Smrg    XtermWidget xw = term;
1008d522f475Smrg    TScreen *screen = TScreenOf(xw);
1009d522f475Smrg
10103367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1011d522f475Smrg	   visibleEventType(event->type),
10123367019cSmrg	   visibleNotifyMode(event->mode),
10133367019cSmrg	   visibleNotifyDetail(event->detail)));
1014cd3331d0Smrg    TRACE_FOCUS(xw, event);
1015d522f475Smrg
1016d522f475Smrg    if (screen->quiet_grab
1017d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1018c219fbebSmrg	/* EMPTY */ ;
10193367019cSmrg    } else if ((event->type == FocusIn || event->type == FocusOut)
10203367019cSmrg	       && event->detail == NotifyPointer) {
10213367019cSmrg	/*
10223367019cSmrg	 * NotifyPointer is sent to the window where the pointer is, and is
10233367019cSmrg	 * in addition to events sent to the old/new focus-windows.
10243367019cSmrg	 */
10253367019cSmrg	/* EMPTY */ ;
1026d522f475Smrg    } else if (event->type == FocusIn) {
1027c219fbebSmrg	setXUrgency(xw, False);
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,
1254d522f475Smrg			 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#define MAXWLEN 1024		/* maximum word length as in tcsh */
1334d522f475Smrg
1335d522f475Smrgstatic int
1336fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1337d522f475Smrg{
1338b7c89284Ssnj    int result = -1;
1339b7c89284Ssnj    int firstLine = -(screen->savedlines);
1340d522f475Smrg
1341b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1342b7c89284Ssnj    while (cell->row >= firstLine) {
1343b7c89284Ssnj	if (--(cell->col) >= 0) {
1344b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1345b7c89284Ssnj	    break;
1346b7c89284Ssnj	}
1347b7c89284Ssnj	if (--(cell->row) < firstLine)
1348b7c89284Ssnj	    break;		/* ...there is no previous line */
1349b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1350b7c89284Ssnj	cell->col = MaxCols(screen);
1351b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1352b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1353d522f475Smrg	    break;
1354b7c89284Ssnj	}
1355d522f475Smrg    }
1356b7c89284Ssnj    return result;
1357d522f475Smrg}
1358d522f475Smrg
1359d522f475Smrgstatic char *
1360fa3f02f3Smrgdabbrev_prev_word(TScreen *screen, CELL *cell, LineData **ld)
1361d522f475Smrg{
1362d522f475Smrg    static char ab[MAXWLEN];
1363b7c89284Ssnj
1364d522f475Smrg    char *abword;
1365d522f475Smrg    int c;
1366b7c89284Ssnj    char *ab_end = (ab + MAXWLEN - 1);
1367b7c89284Ssnj    char *result = 0;
1368d522f475Smrg
1369b7c89284Ssnj    abword = ab_end;
1370d522f475Smrg    *abword = '\0';		/* end of string marker */
1371d522f475Smrg
1372b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1373b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
1374d522f475Smrg	if (abword > ab)	/* store only |MAXWLEN| last chars */
1375b7c89284Ssnj	    *(--abword) = (char) c;
1376d522f475Smrg    }
1377d522f475Smrg
1378b7c89284Ssnj    if (c >= 0) {
1379b7c89284Ssnj	result = abword;
1380b7c89284Ssnj    } else if (abword != ab_end) {
1381b7c89284Ssnj	result = abword;
1382b7c89284Ssnj    }
1383b7c89284Ssnj
1384b7c89284Ssnj    if (result != 0) {
1385b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1386b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1387b7c89284Ssnj	    ;			/* skip preceding spaces */
1388b7c89284Ssnj	}
1389b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1390b7c89284Ssnj    }
1391b7c89284Ssnj    return result;
1392d522f475Smrg}
1393d522f475Smrg
1394d522f475Smrgstatic int
1395fa3f02f3Smrgdabbrev_expand(TScreen *screen)
1396d522f475Smrg{
1397d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1398d522f475Smrg
1399b7c89284Ssnj    static CELL cell;
1400d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1401d522f475Smrg    static unsigned int expansions;
1402d522f475Smrg
1403d522f475Smrg    char *expansion;
1404d522f475Smrg    Char *copybuffer;
1405d522f475Smrg    size_t hint_len;
1406cd3331d0Smrg    size_t del_cnt;
1407cd3331d0Smrg    size_t buf_cnt;
1408b7c89284Ssnj    int result = 0;
1409b7c89284Ssnj    LineData *ld;
1410d522f475Smrg
1411d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1412d522f475Smrg	expansions = 0;
1413b7c89284Ssnj	cell.col = screen->cur_col;
1414b7c89284Ssnj	cell.row = screen->cur_row;
1415b7c89284Ssnj
1416b7c89284Ssnj	if (dabbrev_hint != 0)
1417b7c89284Ssnj	    free(dabbrev_hint);
1418b7c89284Ssnj
1419b7c89284Ssnj	if ((dabbrev_hint = dabbrev_prev_word(screen, &cell, &ld)) != 0) {
1420b7c89284Ssnj
1421b7c89284Ssnj	    if (lastexpansion != 0)
1422b7c89284Ssnj		free(lastexpansion);
1423b7c89284Ssnj
1424b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1425b7c89284Ssnj
1426b7c89284Ssnj		/* make own copy */
1427b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1428b7c89284Ssnj		    screen->dabbrev_working = True;
1429b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1430b7c89284Ssnj		}
1431cd3331d0Smrg	    } else {
1432cd3331d0Smrg		return result;
1433b7c89284Ssnj	    }
1434cd3331d0Smrg	} else {
1435cd3331d0Smrg	    return result;
1436d522f475Smrg	}
1437b7c89284Ssnj	if (!screen->dabbrev_working) {
1438b7c89284Ssnj	    if (lastexpansion != 0) {
1439b7c89284Ssnj		free(lastexpansion);
1440b7c89284Ssnj		lastexpansion = 0;
1441b7c89284Ssnj	    }
1442b7c89284Ssnj	    return result;
1443b7c89284Ssnj	}
1444d522f475Smrg    }
1445d522f475Smrg
1446cd3331d0Smrg    if (dabbrev_hint == 0)
1447cd3331d0Smrg	return result;
1448cd3331d0Smrg
1449d522f475Smrg    hint_len = strlen(dabbrev_hint);
1450d522f475Smrg    for (;;) {
1451b7c89284Ssnj	if ((expansion = dabbrev_prev_word(screen, &cell, &ld)) == 0) {
1452d522f475Smrg	    if (expansions >= 2) {
1453d522f475Smrg		expansions = 0;
1454b7c89284Ssnj		cell.col = screen->cur_col;
1455b7c89284Ssnj		cell.row = screen->cur_row;
1456d522f475Smrg		continue;
1457d522f475Smrg	    }
1458d522f475Smrg	    break;
1459d522f475Smrg	}
1460d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1461d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1462d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1463d522f475Smrg	    break;
1464d522f475Smrg    }
1465d522f475Smrg
1466b7c89284Ssnj    if (expansion != 0) {
1467b7c89284Ssnj	del_cnt = strlen(lastexpansion) - hint_len;
1468b7c89284Ssnj	buf_cnt = del_cnt + strlen(expansion) - hint_len;
1469b7c89284Ssnj
1470b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1471b7c89284Ssnj	    /* delete previous expansion */
1472b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1473b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1474b7c89284Ssnj		    expansion + hint_len,
1475b7c89284Ssnj		    strlen(expansion) - hint_len);
1476cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1477b7c89284Ssnj	    /* v_write() just reset our flag */
1478b7c89284Ssnj	    screen->dabbrev_working = True;
1479b7c89284Ssnj	    free(copybuffer);
1480b7c89284Ssnj
1481b7c89284Ssnj	    free(lastexpansion);
1482b7c89284Ssnj
1483b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1484b7c89284Ssnj		result = 1;
1485b7c89284Ssnj		expansions++;
1486b7c89284Ssnj	    }
1487b7c89284Ssnj	}
1488b7c89284Ssnj    }
1489b7c89284Ssnj
1490b7c89284Ssnj    return result;
1491d522f475Smrg}
1492d522f475Smrg
1493d522f475Smrg/*ARGSUSED*/
1494d522f475Smrgvoid
1495b7c89284SsnjHandleDabbrevExpand(Widget w,
1496d522f475Smrg		    XEvent * event GCC_UNUSED,
1497fa3f02f3Smrg		    String *params GCC_UNUSED,
1498d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1499d522f475Smrg{
1500b7c89284Ssnj    XtermWidget xw;
1501b7c89284Ssnj
1502cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1503b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1504cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
1505d522f475Smrg	if (!dabbrev_expand(screen))
1506cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1507d522f475Smrg    }
1508d522f475Smrg}
1509d522f475Smrg#endif /* OPT_DABBREV */
1510d522f475Smrg
1511d522f475Smrg#if OPT_MAXIMIZE
1512d522f475Smrg/*ARGSUSED*/
1513d522f475Smrgvoid
1514b7c89284SsnjHandleDeIconify(Widget w,
1515d522f475Smrg		XEvent * event GCC_UNUSED,
1516fa3f02f3Smrg		String *params GCC_UNUSED,
1517d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1518d522f475Smrg{
1519b7c89284Ssnj    XtermWidget xw;
1520b7c89284Ssnj
1521b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1522b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1523c219fbebSmrg	XMapWindow(screen->display, VShellWindow(xw));
1524d522f475Smrg    }
1525d522f475Smrg}
1526d522f475Smrg
1527d522f475Smrg/*ARGSUSED*/
1528d522f475Smrgvoid
1529b7c89284SsnjHandleIconify(Widget w,
1530d522f475Smrg	      XEvent * event GCC_UNUSED,
1531fa3f02f3Smrg	      String *params GCC_UNUSED,
1532d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1533d522f475Smrg{
1534b7c89284Ssnj    XtermWidget xw;
1535b7c89284Ssnj
1536b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1537b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1538d522f475Smrg	XIconifyWindow(screen->display,
1539c219fbebSmrg		       VShellWindow(xw),
1540d522f475Smrg		       DefaultScreen(screen->display));
1541d522f475Smrg    }
1542d522f475Smrg}
1543d522f475Smrg
1544d522f475Smrgint
1545c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1546d522f475Smrg{
1547c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1548d522f475Smrg    XSizeHints hints;
1549d522f475Smrg    long supp = 0;
1550d522f475Smrg    Window root_win;
1551d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1552d522f475Smrg    int root_y = -1;
1553d522f475Smrg    unsigned root_border;
1554d522f475Smrg    unsigned root_depth;
15553367019cSmrg    int code;
1556d522f475Smrg
1557d522f475Smrg    if (XGetGeometry(screen->display,
1558c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1559d522f475Smrg		     &root_win,
1560d522f475Smrg		     &root_x,
1561d522f475Smrg		     &root_y,
1562d522f475Smrg		     width,
1563d522f475Smrg		     height,
1564d522f475Smrg		     &root_border,
1565d522f475Smrg		     &root_depth)) {
1566d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1567d522f475Smrg	       root_x,
1568d522f475Smrg	       root_y,
1569d522f475Smrg	       *width,
1570d522f475Smrg	       *height,
1571d522f475Smrg	       root_border));
1572d522f475Smrg
1573d522f475Smrg	*width -= (root_border * 2);
1574d522f475Smrg	*height -= (root_border * 2);
1575d522f475Smrg
1576d522f475Smrg	hints.flags = PMaxSize;
1577d522f475Smrg	if (XGetWMNormalHints(screen->display,
1578c219fbebSmrg			      VShellWindow(xw),
1579d522f475Smrg			      &hints,
1580d522f475Smrg			      &supp)
1581d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1582d522f475Smrg
1583d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1584d522f475Smrg		   hints.max_width,
1585d522f475Smrg		   hints.max_height));
1586d522f475Smrg
1587d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1588b7c89284Ssnj		*width = (unsigned) hints.max_width;
1589d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1590b7c89284Ssnj		*height = (unsigned) hints.max_height;
1591d522f475Smrg	}
15923367019cSmrg	code = 1;
15933367019cSmrg    } else {
15943367019cSmrg	*width = 0;
15953367019cSmrg	*height = 0;
15963367019cSmrg	code = 0;
1597d522f475Smrg    }
15983367019cSmrg    return code;
1599d522f475Smrg}
1600d522f475Smrg
1601d522f475Smrgvoid
1602c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1603d522f475Smrg{
1604c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1605d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1606d522f475Smrg    unsigned root_width, root_height;
16073367019cSmrg    Boolean success = False;
1608d522f475Smrg
16093367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
16103367019cSmrg	   maximize,
16113367019cSmrg	   (maximize
16123367019cSmrg	    ? "maximize"
16133367019cSmrg	    : "restore")));
1614d522f475Smrg
16153367019cSmrg    /*
16163367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
16173367019cSmrg     * as well as the estimated root-window size.
16183367019cSmrg     */
16193367019cSmrg    if (maximize
16203367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
16213367019cSmrg	&& xtermGetWinAttrs(screen->display,
16223367019cSmrg			    WMFrameWindow(xw),
16233367019cSmrg			    &wm_attrs)
16243367019cSmrg	&& xtermGetWinAttrs(screen->display,
16253367019cSmrg			    VShellWindow(xw),
16263367019cSmrg			    &vshell_attrs)) {
16273367019cSmrg
16283367019cSmrg	if (screen->restore_data != True
16293367019cSmrg	    || screen->restore_width != root_width
16303367019cSmrg	    || screen->restore_height != root_height) {
16313367019cSmrg	    screen->restore_data = True;
16323367019cSmrg	    screen->restore_x = wm_attrs.x + wm_attrs.border_width;
16333367019cSmrg	    screen->restore_y = wm_attrs.y + wm_attrs.border_width;
16343367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
16353367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
16363367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1637d522f475Smrg		   screen->restore_x,
1638d522f475Smrg		   screen->restore_y,
1639d522f475Smrg		   screen->restore_width,
1640d522f475Smrg		   screen->restore_height));
16413367019cSmrg	}
1642d522f475Smrg
16433367019cSmrg	/* subtract wm decoration dimensions */
16443367019cSmrg	root_width -= (unsigned) ((wm_attrs.width - vshell_attrs.width)
16453367019cSmrg				  + (wm_attrs.border_width * 2));
16463367019cSmrg	root_height -= (unsigned) ((wm_attrs.height - vshell_attrs.height)
16473367019cSmrg				   + (wm_attrs.border_width * 2));
16483367019cSmrg	success = True;
16493367019cSmrg    } else if (screen->restore_data) {
16503367019cSmrg	success = True;
16513367019cSmrg	maximize = 0;
16523367019cSmrg    }
16533367019cSmrg
16543367019cSmrg    if (success) {
16553367019cSmrg	switch (maximize) {
16563367019cSmrg	case 3:
16573367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
16583367019cSmrg	    break;
16593367019cSmrg	case 2:
16603367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
16613367019cSmrg	    break;
16623367019cSmrg	case 1:
16633367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
16643367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
16653367019cSmrg			      0 + wm_attrs.border_width,	/* x */
16663367019cSmrg			      0 + wm_attrs.border_width,	/* y */
16673367019cSmrg			      root_width,
16683367019cSmrg			      root_height);
16693367019cSmrg	    break;
16703367019cSmrg
16713367019cSmrg	default:
16723367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
16733367019cSmrg	    if (screen->restore_data) {
16743367019cSmrg		screen->restore_data = False;
16753367019cSmrg
16763367019cSmrg		TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
16773367019cSmrg		       screen->restore_x,
16783367019cSmrg		       screen->restore_y,
16793367019cSmrg		       screen->restore_width,
16803367019cSmrg		       screen->restore_height));
16813367019cSmrg
16823367019cSmrg		XMoveResizeWindow(screen->display,
16833367019cSmrg				  VShellWindow(xw),
16843367019cSmrg				  screen->restore_x,
16853367019cSmrg				  screen->restore_y,
16863367019cSmrg				  screen->restore_width,
16873367019cSmrg				  screen->restore_height);
16883367019cSmrg	    }
16893367019cSmrg	    break;
1690d522f475Smrg	}
1691d522f475Smrg    }
1692d522f475Smrg}
1693d522f475Smrg
1694d522f475Smrg/*ARGSUSED*/
1695d522f475Smrgvoid
1696b7c89284SsnjHandleMaximize(Widget w,
1697d522f475Smrg	       XEvent * event GCC_UNUSED,
1698fa3f02f3Smrg	       String *params GCC_UNUSED,
1699d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1700d522f475Smrg{
1701b7c89284Ssnj    XtermWidget xw;
1702b7c89284Ssnj
1703b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1704b7c89284Ssnj	RequestMaximize(xw, 1);
1705d522f475Smrg    }
1706d522f475Smrg}
1707d522f475Smrg
1708d522f475Smrg/*ARGSUSED*/
1709d522f475Smrgvoid
1710b7c89284SsnjHandleRestoreSize(Widget w,
1711d522f475Smrg		  XEvent * event GCC_UNUSED,
1712fa3f02f3Smrg		  String *params GCC_UNUSED,
1713d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1714d522f475Smrg{
1715b7c89284Ssnj    XtermWidget xw;
1716b7c89284Ssnj
1717b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1718b7c89284Ssnj	RequestMaximize(xw, 0);
1719d522f475Smrg    }
1720d522f475Smrg}
1721d522f475Smrg#endif /* OPT_MAXIMIZE */
1722d522f475Smrg
1723d522f475Smrgvoid
1724d522f475SmrgRedraw(void)
1725d522f475Smrg{
1726d522f475Smrg    TScreen *screen = TScreenOf(term);
1727d522f475Smrg    XExposeEvent event;
1728d522f475Smrg
1729d522f475Smrg    TRACE(("Redraw\n"));
1730d522f475Smrg
1731d522f475Smrg    event.type = Expose;
1732d522f475Smrg    event.display = screen->display;
1733d522f475Smrg    event.x = 0;
1734d522f475Smrg    event.y = 0;
1735d522f475Smrg    event.count = 0;
1736d522f475Smrg
1737d522f475Smrg    if (VWindow(screen)) {
1738d522f475Smrg	event.window = VWindow(screen);
1739d522f475Smrg	event.width = term->core.width;
1740d522f475Smrg	event.height = term->core.height;
1741d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
1742d522f475Smrg						       (XEvent *) & event,
1743d522f475Smrg						       NULL);
1744d522f475Smrg	if (ScrollbarWidth(screen)) {
1745d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
1746d522f475Smrg		(screen->scrollWidget, (XEvent *) & event, NULL);
1747d522f475Smrg	}
1748d522f475Smrg    }
1749d522f475Smrg#if OPT_TEK4014
1750d522f475Smrg    if (TEK4014_SHOWN(term)) {
1751cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1752d522f475Smrg	event.window = TWindow(tekscr);
1753d522f475Smrg	event.width = tekWidget->core.width;
1754d522f475Smrg	event.height = tekWidget->core.height;
1755d522f475Smrg	TekExpose((Widget) tekWidget, (XEvent *) & event, NULL);
1756d522f475Smrg    }
1757d522f475Smrg#endif
1758d522f475Smrg}
1759d522f475Smrg
1760d522f475Smrg#ifdef VMS
1761d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1762d522f475Smrg#else
1763d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1764d522f475Smrg#endif
1765d522f475Smrg
1766d522f475Smrgvoid
1767d522f475Smrgtimestamp_filename(char *dst, const char *src)
1768d522f475Smrg{
1769d522f475Smrg    time_t tstamp;
1770d522f475Smrg    struct tm *tstruct;
1771d522f475Smrg
1772d522f475Smrg    tstamp = time((time_t *) 0);
1773d522f475Smrg    tstruct = localtime(&tstamp);
1774d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1775d522f475Smrg	    src,
17763367019cSmrg	    (int) tstruct->tm_year + 1900,
1777d522f475Smrg	    tstruct->tm_mon + 1,
1778d522f475Smrg	    tstruct->tm_mday,
1779d522f475Smrg	    tstruct->tm_hour,
1780d522f475Smrg	    tstruct->tm_min,
1781d522f475Smrg	    tstruct->tm_sec);
1782d522f475Smrg}
1783d522f475Smrg
1784d522f475Smrgint
1785d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1786d522f475Smrg{
1787d522f475Smrg    int fd;
1788d522f475Smrg    struct stat sb;
1789d522f475Smrg
1790d522f475Smrg#ifdef VMS
1791d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1792d522f475Smrg	int the_error = errno;
17933367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
17943367019cSmrg		     path,
17953367019cSmrg		     the_error,
17963367019cSmrg		     SysErrorMsg(the_error));
1797d522f475Smrg	return -1;
1798d522f475Smrg    }
1799d522f475Smrg    chown(path, uid, gid);
1800d522f475Smrg#else
1801d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1802d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1803d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1804d522f475Smrg	int the_error = errno;
18053367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
18063367019cSmrg		     path,
18073367019cSmrg		     the_error,
18083367019cSmrg		     SysErrorMsg(the_error));
1809d522f475Smrg	return -1;
1810d522f475Smrg    }
1811d522f475Smrg#endif
1812d522f475Smrg
1813d522f475Smrg    /*
1814d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1815d522f475Smrg     * we do any damage, and that it is not world-writable.
1816d522f475Smrg     */
1817d522f475Smrg    if (fstat(fd, &sb) < 0
1818d522f475Smrg	|| sb.st_uid != uid
1819d522f475Smrg	|| (sb.st_mode & 022) != 0) {
18203367019cSmrg	xtermWarning("you do not own %s\n", path);
1821d522f475Smrg	close(fd);
1822d522f475Smrg	return -1;
1823d522f475Smrg    }
1824d522f475Smrg    return fd;
1825d522f475Smrg}
1826d522f475Smrg
1827d522f475Smrg#ifndef VMS
1828d522f475Smrg/*
1829d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1830d522f475Smrg * We could emulate this with careful use of access() and following
1831d522f475Smrg * symbolic links, but that is messy and has race conditions.
1832d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1833d522f475Smrg * being available.
1834d522f475Smrg *
1835d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1836d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1837d522f475Smrg * for the debug logs.
1838d522f475Smrg *
1839d522f475Smrg * Returns
1840d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1841d522f475Smrg *	-1 on error, e.g., cannot fork
1842d522f475Smrg *	 0 otherwise.
1843d522f475Smrg */
1844d522f475Smrgint
1845712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
1846d522f475Smrg{
1847d522f475Smrg    int fd;
1848d522f475Smrg    pid_t pid;
1849d522f475Smrg    int retval = 0;
1850d522f475Smrg    int childstat = 0;
1851d522f475Smrg#ifndef HAVE_WAITPID
1852d522f475Smrg    int waited;
18533367019cSmrg    void (*chldfunc) (int);
1854d522f475Smrg
1855d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1856d522f475Smrg#endif /* HAVE_WAITPID */
1857d522f475Smrg
1858d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1859d522f475Smrg	   (int) uid, (int) geteuid(),
1860d522f475Smrg	   (int) gid, (int) getegid(),
1861d522f475Smrg	   append,
1862d522f475Smrg	   pathname,
1863d522f475Smrg	   mode));
1864d522f475Smrg
1865d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1866d522f475Smrg	fd = open(pathname,
1867d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1868d522f475Smrg		  mode);
1869d522f475Smrg	if (fd >= 0)
1870d522f475Smrg	    close(fd);
1871d522f475Smrg	return (fd >= 0);
1872d522f475Smrg    }
1873d522f475Smrg
1874d522f475Smrg    pid = fork();
1875d522f475Smrg    switch (pid) {
1876d522f475Smrg    case 0:			/* child */
1877d522f475Smrg	if (setgid(gid) == -1
1878d522f475Smrg	    || setuid(uid) == -1) {
1879d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1880d522f475Smrg	    retval = 1;
1881d522f475Smrg	} else {
1882d522f475Smrg	    fd = open(pathname,
1883d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1884d522f475Smrg		      mode);
1885d522f475Smrg	    if (fd >= 0) {
1886d522f475Smrg		close(fd);
1887d522f475Smrg		retval = 0;
1888d522f475Smrg	    } else {
1889d522f475Smrg		retval = 1;
1890d522f475Smrg	    }
1891d522f475Smrg	}
1892d522f475Smrg	_exit(retval);
1893d522f475Smrg	/* NOTREACHED */
1894d522f475Smrg    case -1:			/* error */
1895d522f475Smrg	return retval;
1896d522f475Smrg    default:			/* parent */
1897d522f475Smrg#ifdef HAVE_WAITPID
1898d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1899d522f475Smrg#ifdef EINTR
1900d522f475Smrg	    if (errno == EINTR)
1901d522f475Smrg		continue;
1902d522f475Smrg#endif /* EINTR */
1903d522f475Smrg#ifdef ERESTARTSYS
1904d522f475Smrg	    if (errno == ERESTARTSYS)
1905d522f475Smrg		continue;
1906d522f475Smrg#endif /* ERESTARTSYS */
1907d522f475Smrg	    break;
1908d522f475Smrg	}
1909d522f475Smrg#else /* HAVE_WAITPID */
1910d522f475Smrg	waited = wait(&childstat);
1911d522f475Smrg	signal(SIGCHLD, chldfunc);
1912d522f475Smrg	/*
1913d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1914d522f475Smrg	   we might have missed the termination of our screen child.
1915d522f475Smrg	   If we can check for this possibility without hanging, do so.
1916d522f475Smrg	 */
1917d522f475Smrg	do
1918cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
19193367019cSmrg		NormalExit();
1920d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1921d522f475Smrg#endif /* HAVE_WAITPID */
1922d522f475Smrg#ifndef WIFEXITED
1923d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1924d522f475Smrg#endif
1925d522f475Smrg	if (WIFEXITED(childstat))
1926d522f475Smrg	    retval = 1;
1927d522f475Smrg	return retval;
1928d522f475Smrg    }
1929d522f475Smrg}
1930d522f475Smrg#endif /* !VMS */
1931d522f475Smrg
1932d522f475Smrgint
1933fa3f02f3SmrgxtermResetIds(TScreen *screen)
1934d522f475Smrg{
1935d522f475Smrg    int result = 0;
1936d522f475Smrg    if (setgid(screen->gid) == -1) {
19373367019cSmrg	xtermWarning("unable to reset group-id\n");
1938d522f475Smrg	result = -1;
1939d522f475Smrg    }
1940d522f475Smrg    if (setuid(screen->uid) == -1) {
19413367019cSmrg	xtermWarning("unable to reset user-id\n");
1942d522f475Smrg	result = -1;
1943d522f475Smrg    }
1944d522f475Smrg    return result;
1945d522f475Smrg}
1946d522f475Smrg
1947d522f475Smrg#ifdef ALLOWLOGGING
1948d522f475Smrg
1949d522f475Smrg/*
1950d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1951d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1952d522f475Smrg */
1953d522f475Smrg
1954d522f475Smrg#ifdef ALLOWLOGFILEEXEC
19553367019cSmrgstatic void
1956d522f475Smrglogpipe(int sig GCC_UNUSED)
1957d522f475Smrg{
1958cd3331d0Smrg    XtermWidget xw = term;
1959cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1960d522f475Smrg
19613367019cSmrg    DEBUG_MSG("handle:logpipe\n");
1962d522f475Smrg#ifdef SYSV
1963d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
1964d522f475Smrg#endif /* SYSV */
1965d522f475Smrg    if (screen->logging)
1966cd3331d0Smrg	CloseLog(xw);
1967d522f475Smrg}
1968d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1969d522f475Smrg
1970d522f475Smrgvoid
1971cd3331d0SmrgStartLog(XtermWidget xw)
1972d522f475Smrg{
1973d522f475Smrg    static char *log_default;
1974cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1975d522f475Smrg
1976d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
1977d522f475Smrg	return;
1978d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
1979d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
1980d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
1981d522f475Smrg			 0640);
1982d522f475Smrg    if (screen->logfd < 0)
1983d522f475Smrg	return;			/* open failed */
1984d522f475Smrg#else /*VMS */
1985d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
1986d522f475Smrg	if (screen->logfile)
1987d522f475Smrg	    free(screen->logfile);
1988d522f475Smrg	if (log_default == NULL) {
1989d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
1990d522f475Smrg	    char log_def_name[512];	/* see sprintf below */
1991d522f475Smrg	    char hostname[255 + 1];	/* Internet standard limit (RFC 1035):
1992d522f475Smrg					   ``To simplify implementations, the
1993d522f475Smrg					   total length of a domain name (i.e.,
1994d522f475Smrg					   label octets and label length
1995d522f475Smrg					   octets) is restricted to 255 octets
1996d522f475Smrg					   or less.'' */
1997d522f475Smrg	    char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1];
1998d522f475Smrg	    time_t now;
1999d522f475Smrg	    struct tm *ltm;
2000d522f475Smrg
2001d522f475Smrg	    now = time((time_t *) 0);
2002d522f475Smrg	    ltm = (struct tm *) localtime(&now);
2003d522f475Smrg	    if ((gethostname(hostname, sizeof(hostname)) == 0) &&
2004d522f475Smrg		(strftime(yyyy_mm_dd_hh_mm_ss,
2005d522f475Smrg			  sizeof(yyyy_mm_dd_hh_mm_ss),
2006d522f475Smrg			  "%Y.%m.%d.%H.%M.%S", ltm) > 0)) {
2007d522f475Smrg		(void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d",
2008d522f475Smrg			       hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid());
2009d522f475Smrg	    }
2010d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
2011d522f475Smrg		return;
2012d522f475Smrg#else
2013d522f475Smrg	    const char *log_def_name = "XtermLog.XXXXXX";
2014d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
2015d522f475Smrg		return;
2016d522f475Smrg
2017d522f475Smrg	    mktemp(log_default);
2018d522f475Smrg#endif
2019d522f475Smrg	}
2020d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
2021d522f475Smrg	    return;
2022d522f475Smrg    }
2023d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
2024d522f475Smrg#ifdef ALLOWLOGFILEEXEC
2025d522f475Smrg	/*
2026d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
2027d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2028d522f475Smrg	 * done through escape sequences....  You have been warned.
2029d522f475Smrg	 */
2030d522f475Smrg	int pid;
2031d522f475Smrg	int p[2];
2032d522f475Smrg	static char *shell;
20333367019cSmrg	struct passwd pw;
20343367019cSmrg
20353367019cSmrg	if ((shell = x_getenv("SHELL")) == NULL) {
20363367019cSmrg
20373367019cSmrg	    if (x_getpwuid(screen->uid, &pw)) {
20383367019cSmrg		char *name = x_getlogin(screen->uid, &pw);
20393367019cSmrg		if (*(pw.pw_shell)) {
20403367019cSmrg		    shell = pw.pw_shell;
20413367019cSmrg		}
20423367019cSmrg		free(name);
20433367019cSmrg	    }
20443367019cSmrg	}
20453367019cSmrg
20463367019cSmrg	if (shell == 0) {
20473367019cSmrg	    static char dummy[] = "/bin/sh";
20483367019cSmrg	    shell = dummy;
20493367019cSmrg	}
20503367019cSmrg
20513367019cSmrg	if (access(shell, X_OK) != 0) {
20523367019cSmrg	    xtermPerror("Can't execute `%s'\n", shell);
20533367019cSmrg	    return;
20543367019cSmrg	}
2055d522f475Smrg
20563367019cSmrg	if (pipe(p) < 0) {
20573367019cSmrg	    xtermPerror("Can't make a pipe connection\n");
2058d522f475Smrg	    return;
20593367019cSmrg	} else if ((pid = fork()) < 0) {
20603367019cSmrg	    xtermPerror("Can't fork...\n");
20613367019cSmrg	    return;
20623367019cSmrg	}
2063d522f475Smrg	if (pid == 0) {		/* child */
2064d522f475Smrg	    /*
2065d522f475Smrg	     * Close our output (we won't be talking back to the
2066d522f475Smrg	     * parent), and redirect our child's output to the
2067d522f475Smrg	     * original stderr.
2068d522f475Smrg	     */
2069d522f475Smrg	    close(p[1]);
2070d522f475Smrg	    dup2(p[0], 0);
2071d522f475Smrg	    close(p[0]);
2072d522f475Smrg	    dup2(fileno(stderr), 1);
2073d522f475Smrg	    dup2(fileno(stderr), 2);
2074d522f475Smrg
2075d522f475Smrg	    close(fileno(stderr));
2076d522f475Smrg	    close(ConnectionNumber(screen->display));
2077d522f475Smrg	    close(screen->respond);
2078d522f475Smrg
2079d522f475Smrg	    signal(SIGHUP, SIG_DFL);
2080d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
2081d522f475Smrg
2082d522f475Smrg	    /* (this is redundant) */
2083d522f475Smrg	    if (xtermResetIds(screen) < 0)
2084d522f475Smrg		exit(ERROR_SETUID);
2085d522f475Smrg
20863367019cSmrg	    if (access(shell, X_OK) == 0) {
20873367019cSmrg		execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
20883367019cSmrg		xtermWarning("Can't exec `%s'\n", &screen->logfile[1]);
20893367019cSmrg	    } else {
20903367019cSmrg		xtermWarning("Can't execute `%s'\n", shell);
20913367019cSmrg	    }
2092d522f475Smrg	    exit(ERROR_LOGEXEC);
2093d522f475Smrg	}
2094d522f475Smrg	close(p[0]);
2095d522f475Smrg	screen->logfd = p[1];
2096d522f475Smrg	signal(SIGPIPE, logpipe);
2097d522f475Smrg#else
2098cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2099cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2100d522f475Smrg	return;
2101d522f475Smrg#endif
2102d522f475Smrg    } else {
2103d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2104d522f475Smrg					   screen->gid,
2105d522f475Smrg					   screen->logfile,
2106d522f475Smrg					   (log_default != 0))) < 0)
2107d522f475Smrg	    return;
2108d522f475Smrg    }
2109d522f475Smrg#endif /*VMS */
2110d522f475Smrg    screen->logstart = VTbuffer->next;
2111d522f475Smrg    screen->logging = True;
2112d522f475Smrg    update_logging();
2113d522f475Smrg}
2114d522f475Smrg
2115d522f475Smrgvoid
2116cd3331d0SmrgCloseLog(XtermWidget xw)
2117d522f475Smrg{
2118cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2119cd3331d0Smrg
2120d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2121d522f475Smrg	return;
2122cd3331d0Smrg    FlushLog(xw);
2123d522f475Smrg    close(screen->logfd);
2124d522f475Smrg    screen->logging = False;
2125d522f475Smrg    update_logging();
2126d522f475Smrg}
2127d522f475Smrg
2128d522f475Smrgvoid
2129cd3331d0SmrgFlushLog(XtermWidget xw)
2130d522f475Smrg{
2131cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2132cd3331d0Smrg
2133d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2134d522f475Smrg	Char *cp;
2135d522f475Smrg	int i;
2136d522f475Smrg
2137d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2138d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2139d522f475Smrg	if (!tt_new_output)
2140d522f475Smrg	    return;
2141d522f475Smrg	tt_new_output = False;
2142d522f475Smrg#endif /* VMS */
2143d522f475Smrg	cp = VTbuffer->next;
2144d522f475Smrg	if (screen->logstart != 0
2145cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2146cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2147d522f475Smrg	}
2148d522f475Smrg	screen->logstart = VTbuffer->next;
2149d522f475Smrg    }
2150d522f475Smrg}
2151d522f475Smrg
2152d522f475Smrg#endif /* ALLOWLOGGING */
2153d522f475Smrg
2154d522f475Smrg/***====================================================================***/
2155d522f475Smrg
2156d522f475Smrg#if OPT_ISO_COLORS
2157d522f475Smrgstatic void
2158d522f475SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
2159d522f475Smrg{
2160cd3331d0Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
2161cd3331d0Smrg	XColor color;
2162cd3331d0Smrg	Colormap cmap = xw->core.colormap;
2163cd3331d0Smrg	char buffer[80];
2164cd3331d0Smrg
2165cd3331d0Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
2166cd3331d0Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2167cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
2168cd3331d0Smrg	sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
2169cd3331d0Smrg		colornum,
2170cd3331d0Smrg		color.red,
2171cd3331d0Smrg		color.green,
2172cd3331d0Smrg		color.blue);
2173cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
2174cd3331d0Smrg	unparseputs(xw, buffer);
2175cd3331d0Smrg	unparseputc1(xw, final);
2176cd3331d0Smrg	unparse_end(xw);
2177cd3331d0Smrg    }
2178d522f475Smrg}
2179d522f475Smrg
2180fa3f02f3Smrgint
2181fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2182fa3f02f3Smrg{
2183fa3f02f3Smrg#define MYFMT "getVisualInfo \
2184fa3f02f3Smrgdepth %d, \
2185fa3f02f3Smrgtype %d (%s), \
2186fa3f02f3Smrgsize %d \
2187fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2188fa3f02f3Smrg#define MYARG \
2189fa3f02f3Smrg       vi->depth,\
2190fa3f02f3Smrg       vi->class,\
2191fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2192fa3f02f3Smrg       vi->colormap_size,\
2193fa3f02f3Smrg       vi->red_mask,\
2194fa3f02f3Smrg       vi->green_mask,\
2195fa3f02f3Smrg       vi->blue_mask
2196d522f475Smrg
2197fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2198fa3f02f3Smrg    Display *dpy = screen->display;
2199fa3f02f3Smrg    XVisualInfo myTemplate;
2200fa3f02f3Smrg
2201fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2202fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2203fa3f02f3Smrg								XDefaultScreen(dpy)));
2204fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2205fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2206fa3f02f3Smrg
2207fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2208fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2209fa3f02f3Smrg	    if (resource.reportColors) {
2210fa3f02f3Smrg		printf(MYFMT, MYARG);
2211fa3f02f3Smrg	    }
2212fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2213fa3f02f3Smrg	}
2214fa3f02f3Smrg    }
2215fa3f02f3Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0);
2216fa3f02f3Smrg#undef MYFMT
2217fa3f02f3Smrg#undef MYARG
2218fa3f02f3Smrg}
22193367019cSmrg
2220fa3f02f3Smrgstatic void
2221fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2222fa3f02f3Smrg{
2223fa3f02f3Smrg    if (getVisualInfo(xw)) {
2224fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2225fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2226fa3f02f3Smrg    } else {
2227fa3f02f3Smrg	*typep = 0;
2228fa3f02f3Smrg	*sizep = 0;
2229fa3f02f3Smrg    }
22303367019cSmrg}
22313367019cSmrg
22323367019cSmrg#define MAX_COLORTABLE 4096
22333367019cSmrg
22343367019cSmrg/*
22353367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
22363367019cSmrg */
22373367019cSmrgstatic Boolean
22383367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
22393367019cSmrg{
22403367019cSmrg    Colormap cmap = xw->core.colormap;
22413367019cSmrg    TScreen *screen = TScreenOf(xw);
22423367019cSmrg    unsigned i;
2243fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
22443367019cSmrg
2245fa3f02f3Smrg    if (!result
22463367019cSmrg	&& length != 0
22473367019cSmrg	&& length < MAX_COLORTABLE) {
22483367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
22493367019cSmrg	if (screen->cmap_data != 0) {
22503367019cSmrg	    screen->cmap_size = length;
22513367019cSmrg
22523367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
22533367019cSmrg		screen->cmap_data[i].pixel = (unsigned long) i;
22543367019cSmrg	    }
22553367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
22563367019cSmrg					     cmap,
22573367019cSmrg					     screen->cmap_data,
22583367019cSmrg					     (int) screen->cmap_size) != 0);
22593367019cSmrg	}
22603367019cSmrg    }
2261d522f475Smrg    return result;
2262d522f475Smrg}
2263d522f475Smrg
2264d522f475Smrg/*
2265d522f475Smrg * Find closest color for "def" in "cmap".
2266d522f475Smrg * Set "def" to the resulting color.
2267d522f475Smrg *
2268d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2269d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2270d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2271d522f475Smrg *
2272d522f475Smrg * These provide some introduction:
2273d522f475Smrg *	http://en.wikipedia.org/wiki/YIQ
2274d522f475Smrg *		for an introduction to YIQ weights.
2275d522f475Smrg *	http://en.wikipedia.org/wiki/Luminance_(video)
2276d522f475Smrg *		for a discussion of luma.
2277d522f475Smrg *	http://en.wikipedia.org/wiki/YUV
2278d522f475Smrg *
2279d522f475Smrg * Return False if not able to find or allocate a color.
2280d522f475Smrg */
2281d522f475Smrgstatic Boolean
22823367019cSmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor * def)
2283d522f475Smrg{
22843367019cSmrg    TScreen *screen = TScreenOf(xw);
2285d522f475Smrg    Boolean result = False;
2286d522f475Smrg    char *tried;
2287d522f475Smrg    double diff, thisRGB, bestRGB;
2288d522f475Smrg    unsigned attempts;
2289d522f475Smrg    unsigned bestInx;
22903367019cSmrg    unsigned cmap_type;
2291d522f475Smrg    unsigned cmap_size;
2292d522f475Smrg    unsigned i;
2293d522f475Smrg
2294fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2295d522f475Smrg
22963367019cSmrg    if ((cmap_type & 1) != 0) {
22973367019cSmrg
22983367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2299d522f475Smrg
2300b7c89284Ssnj	    tried = TypeCallocN(char, (size_t) cmap_size);
2301d522f475Smrg	    if (tried != 0) {
2302d522f475Smrg
2303d522f475Smrg		/*
2304d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2305d522f475Smrg		 * approximation to the requested color.
2306d522f475Smrg		 */
2307d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2308d522f475Smrg		    Boolean first = True;
2309d522f475Smrg
2310d522f475Smrg		    bestRGB = 0.0;
2311d522f475Smrg		    bestInx = 0;
2312d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2313d522f475Smrg			if (!tried[bestInx]) {
2314d522f475Smrg			    /*
2315d522f475Smrg			     * Look for the best match based on luminance.
2316d522f475Smrg			     * Measure this by the least-squares difference of
2317d522f475Smrg			     * the weighted R/G/B components from the color map
2318d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2319d522f475Smrg			     * component of the YIQ color space model for
2320d522f475Smrg			     * weights that correspond to the luminance.
2321d522f475Smrg			     */
2322d522f475Smrg#define AddColorWeight(weight, color) \
23233367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2324d522f475Smrg			    thisRGB = diff * diff
2325d522f475Smrg
2326d522f475Smrg			    AddColorWeight(0.30, red);
2327d522f475Smrg			    AddColorWeight(0.61, green);
2328d522f475Smrg			    AddColorWeight(0.11, blue);
2329d522f475Smrg
2330d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2331d522f475Smrg				first = False;
2332d522f475Smrg				bestInx = i;
2333d522f475Smrg				bestRGB = thisRGB;
2334d522f475Smrg			    }
2335d522f475Smrg			}
2336d522f475Smrg		    }
23373367019cSmrg		    if (XAllocColor(screen->display, cmap,
23383367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
23393367019cSmrg			*def = screen->cmap_data[bestInx];
23403367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
23413367019cSmrg			       def->green, def->blue));
2342d522f475Smrg			result = True;
2343d522f475Smrg			break;
2344d522f475Smrg		    }
2345d522f475Smrg		    /*
2346d522f475Smrg		     * It failed - either the color map entry was readonly, or
2347d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2348d522f475Smrg		     * so we will ignore it
2349d522f475Smrg		     */
2350d522f475Smrg		    tried[bestInx] = True;
2351d522f475Smrg		}
2352d522f475Smrg		free(tried);
2353d522f475Smrg	    }
2354d522f475Smrg	}
2355d522f475Smrg    }
2356d522f475Smrg    return result;
2357d522f475Smrg}
2358d522f475Smrg
23593367019cSmrg#ifndef ULONG_MAX
23603367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
23613367019cSmrg#endif
23623367019cSmrg
23633367019cSmrg#define CheckColor(result, value) \
23643367019cSmrg	    result = 0; \
23653367019cSmrg	    if (value.red) \
23663367019cSmrg		result |= 1; \
23673367019cSmrg	    if (value.green) \
23683367019cSmrg		result |= 2; \
23693367019cSmrg	    if (value.blue) \
23703367019cSmrg		result |= 4
23713367019cSmrg
23723367019cSmrg#define SelectColor(state, value, result) \
23733367019cSmrg	switch (state) { \
23743367019cSmrg	default: \
23753367019cSmrg	case 1: \
23763367019cSmrg	    result = value.red; \
23773367019cSmrg	    break; \
23783367019cSmrg	case 2: \
23793367019cSmrg	    result = value.green; \
23803367019cSmrg	    break; \
23813367019cSmrg	case 4: \
23823367019cSmrg	    result = value.blue; \
23833367019cSmrg	    break; \
23843367019cSmrg	}
23853367019cSmrg
23863367019cSmrg/*
23873367019cSmrg * Check if the color map consists of values in exactly one of the red, green
23883367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
23893367019cSmrg * match.
23903367019cSmrg */
23913367019cSmrgstatic int
23923367019cSmrgsimpleColors(XColor * colortable, unsigned length)
23933367019cSmrg{
23943367019cSmrg    unsigned n;
2395fa3f02f3Smrg    int state = 0;
23963367019cSmrg    int check;
23973367019cSmrg
23983367019cSmrg    for (n = 0; n < length; ++n) {
23993367019cSmrg	if (state > 0) {
24003367019cSmrg	    CheckColor(check, colortable[n]);
24013367019cSmrg	    if (check > 0 && check != state) {
24023367019cSmrg		state = 0;
24033367019cSmrg		break;
24043367019cSmrg	    }
2405fa3f02f3Smrg	} else {
2406fa3f02f3Smrg	    CheckColor(state, colortable[n]);
24073367019cSmrg	}
24083367019cSmrg    }
24093367019cSmrg    switch (state) {
24103367019cSmrg    case 1:
24113367019cSmrg    case 2:
24123367019cSmrg    case 4:
24133367019cSmrg	break;
24143367019cSmrg    default:
24153367019cSmrg	state = 0;
24163367019cSmrg	break;
24173367019cSmrg    }
24183367019cSmrg    return state;
24193367019cSmrg}
24203367019cSmrg
2421fa3f02f3Smrg/*
2422fa3f02f3Smrg * Shift the mask left or right to put its most significant bit at the 16-bit
2423fa3f02f3Smrg * mark.
2424fa3f02f3Smrg */
2425fa3f02f3Smrgstatic unsigned
2426fa3f02f3SmrgnormalizeMask(unsigned mask)
2427fa3f02f3Smrg{
2428fa3f02f3Smrg    while (mask < 0x8000) {
2429fa3f02f3Smrg	mask <<= 1;
2430fa3f02f3Smrg    }
2431fa3f02f3Smrg    while (mask >= 0x10000) {
2432fa3f02f3Smrg	mask >>= 1;
2433fa3f02f3Smrg    }
2434fa3f02f3Smrg    return mask;
2435fa3f02f3Smrg}
2436fa3f02f3Smrg
24373367019cSmrgstatic unsigned
2438fa3f02f3SmrgsearchColors(XColor * colortable, unsigned mask, unsigned length, unsigned
2439fa3f02f3Smrg	     color, int state)
24403367019cSmrg{
24413367019cSmrg    unsigned result = 0;
24423367019cSmrg    unsigned n;
24433367019cSmrg    unsigned long best = ULONG_MAX;
24443367019cSmrg    unsigned long diff;
24453367019cSmrg    unsigned value;
24463367019cSmrg
2447fa3f02f3Smrg    mask = normalizeMask(mask);
24483367019cSmrg    for (n = 0; n < length; ++n) {
24493367019cSmrg	SelectColor(state, colortable[n], value);
2450fa3f02f3Smrg	diff = ((color & mask) - (value & mask));
24513367019cSmrg	diff *= diff;
24523367019cSmrg	if (diff < best) {
24533367019cSmrg#if 0
24543367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
24553367019cSmrg		   n, color,
24563367019cSmrg		   colortable[n].red,
24573367019cSmrg		   colortable[n].green,
24583367019cSmrg		   colortable[n].blue,
24593367019cSmrg		   diff));
24603367019cSmrg#endif
24613367019cSmrg	    result = n;
24623367019cSmrg	    best = diff;
24633367019cSmrg	}
24643367019cSmrg    }
24653367019cSmrg    SelectColor(state, colortable[result], value);
24663367019cSmrg    return value;
24673367019cSmrg}
24683367019cSmrg
24693367019cSmrg/*
24703367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
24713367019cSmrg *
24723367019cSmrg * According to
24733367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
24743367019cSmrg *
24753367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
24763367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
24773367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
24783367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
24793367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
24803367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
24813367019cSmrg *     actual RGB values allocated.
24823367019cSmrg *
24833367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
2484fa3f02f3Smrg * case, allocateClosestRGB() is useful for the dynamic display classes such as
24853367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
24863367019cSmrg * return regular RGB triples (unless a different scheme was used for
24873367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
24883367019cSmrg * is filled in with the colors that the server supports.
24893367019cSmrg *
24903367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
24913367019cSmrg * described.  For some TrueColor configurations it merely returns a close
24923367019cSmrg * approximation, but not the closest.
24933367019cSmrg */
24943367019cSmrgstatic Boolean
24953367019cSmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor * def)
24963367019cSmrg{
24973367019cSmrg    XColor save = *def;
24983367019cSmrg    TScreen *screen = TScreenOf(xw);
24993367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
25003367019cSmrg
25013367019cSmrg    /*
2502fa3f02f3Smrg     * If this is a statically allocated display with too many items to store
2503fa3f02f3Smrg     * in our array, i.e., TrueColor, see if we can improve on the result by
2504fa3f02f3Smrg     * using the color values actually supported by the server.
25053367019cSmrg     */
25063367019cSmrg    if (result) {
25073367019cSmrg	unsigned cmap_type;
25083367019cSmrg	unsigned cmap_size;
25093367019cSmrg	int state;
25103367019cSmrg
2511fa3f02f3Smrg	getColormapInfo(xw, &cmap_type, &cmap_size);
25123367019cSmrg
2513fa3f02f3Smrg	if (cmap_type == TrueColor) {
25143367019cSmrg	    XColor temp = *def;
25153367019cSmrg
25163367019cSmrg	    if (loadColorTable(xw, cmap_size)
25173367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
2518fa3f02f3Smrg#define SearchColors(which) \
2519fa3f02f3Smrg	temp.which = (unsigned short) searchColors(screen->cmap_data, \
2520fa3f02f3Smrg						   (unsigned) xw->visInfo->which##_mask,\
2521fa3f02f3Smrg						   cmap_size, \
2522fa3f02f3Smrg						   save.which, \
2523fa3f02f3Smrg						   state)
25243367019cSmrg		SearchColors(red);
25253367019cSmrg		SearchColors(green);
25263367019cSmrg		SearchColors(blue);
25273367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
25283367019cSmrg#if OPT_TRACE
25293367019cSmrg		    if (temp.red != save.red
25303367019cSmrg			|| temp.green != save.green
25313367019cSmrg			|| temp.blue != save.blue) {
25323367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
25333367019cSmrg			       save.red, save.green, save.blue,
25343367019cSmrg			       temp.red, temp.green, temp.blue));
25353367019cSmrg		    } else {
25363367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
25373367019cSmrg			       save.red, save.green, save.blue));
25383367019cSmrg		    }
25393367019cSmrg#endif
25403367019cSmrg		    *def = temp;
25413367019cSmrg		}
25423367019cSmrg	    }
25433367019cSmrg	}
25443367019cSmrg    }
25453367019cSmrg
25463367019cSmrg    return result;
25473367019cSmrg}
25483367019cSmrg
2549d522f475Smrg/*
2550d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2551d522f475Smrg * to 256.
2552d522f475Smrg *
2553d522f475Smrg * Returns
2554d522f475Smrg *	-1 on error
2555d522f475Smrg *	0 on no change
2556d522f475Smrg *	1 if a new color was allocated.
2557d522f475Smrg */
2558d522f475Smrgstatic int
2559d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2560d522f475Smrg		  ColorRes * res,
2561cd3331d0Smrg		  const char *spec)
2562d522f475Smrg{
2563d522f475Smrg    int result;
2564d522f475Smrg    XColor def;
2565d522f475Smrg
25663367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2567d522f475Smrg	if (
2568d522f475Smrg#if OPT_COLOR_RES
2569d522f475Smrg	       res->mode == True &&
2570d522f475Smrg#endif
2571d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
2572d522f475Smrg	    result = 0;
2573d522f475Smrg	} else {
2574d522f475Smrg	    result = 1;
2575d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
25763367019cSmrg	    res->red = def.red;
25773367019cSmrg	    res->green = def.green;
25783367019cSmrg	    res->blue = def.blue;
25793367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
25803367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
25813367019cSmrg		   def.red,
25823367019cSmrg		   def.green,
25833367019cSmrg		   def.blue,
25843367019cSmrg		   def.pixel));
2585d522f475Smrg#if OPT_COLOR_RES
2586d522f475Smrg	    if (!res->mode)
2587d522f475Smrg		result = 0;
2588d522f475Smrg	    res->mode = True;
2589d522f475Smrg#endif
2590d522f475Smrg	}
2591d522f475Smrg    } else {
2592d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2593d522f475Smrg	result = -1;
2594d522f475Smrg    }
2595d522f475Smrg    return (result);
2596d522f475Smrg}
2597d522f475Smrg
2598d522f475Smrg#if OPT_COLOR_RES
2599d522f475SmrgPixel
2600cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2601d522f475Smrg{
2602d522f475Smrg    Pixel result = 0;
2603d522f475Smrg
2604d522f475Smrg    if (res->mode) {
2605d522f475Smrg	result = res->value;
2606d522f475Smrg    } else {
2607d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2608cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2609d522f475Smrg
2610cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2611cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2612d522f475Smrg
2613cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2614cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2615d522f475Smrg		res->mode = -True;
26163367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
26173367019cSmrg			     NonNull(res->resource));
2618d522f475Smrg	    }
2619d522f475Smrg	    result = res->value;
2620d522f475Smrg	} else {
2621d522f475Smrg	    result = 0;
2622d522f475Smrg	}
2623d522f475Smrg    }
2624d522f475Smrg    return result;
2625d522f475Smrg}
2626d522f475Smrg#endif
2627d522f475Smrg
2628cd3331d0Smrgstatic int
2629cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2630cd3331d0Smrg{
2631cd3331d0Smrg    int code;
2632cd3331d0Smrg
2633cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2634cd3331d0Smrg	code = -1;
2635cd3331d0Smrg    } else {
2636cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2637cd3331d0Smrg
2638cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2639cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2640cd3331d0Smrg    }
2641cd3331d0Smrg    return code;
2642cd3331d0Smrg}
2643cd3331d0Smrg
2644cd3331d0Smrg/*
2645cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2646cd3331d0Smrg * values from the given buffer.
2647cd3331d0Smrg *
2648cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2649cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2650cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2651cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2652cd3331d0Smrg * 'first' set to the beginning of those indices.
2653cd3331d0Smrg *
2654cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2655cd3331d0Smrg */
2656d522f475Smrgstatic Bool
2657d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2658d522f475Smrg		       char *buf,
2659cd3331d0Smrg		       int first,
2660d522f475Smrg		       int final)
2661d522f475Smrg{
2662d522f475Smrg    char *name;
2663d522f475Smrg    int color;
2664d522f475Smrg    int repaint = False;
2665d522f475Smrg    int code;
2666cd3331d0Smrg    int last = (MAXCOLORS - first);
2667d522f475Smrg
2668d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2669d522f475Smrg
2670d522f475Smrg    while (buf && *buf) {
2671d522f475Smrg	name = strchr(buf, ';');
2672d522f475Smrg	if (name == NULL)
2673d522f475Smrg	    break;
2674d522f475Smrg	*name = '\0';
2675d522f475Smrg	name++;
2676d522f475Smrg	color = atoi(buf);
2677cd3331d0Smrg	if (color < 0 || color >= last)
2678cd3331d0Smrg	    break;		/* quit on any error */
2679d522f475Smrg	buf = strchr(name, ';');
2680d522f475Smrg	if (buf) {
2681d522f475Smrg	    *buf = '\0';
2682d522f475Smrg	    buf++;
2683d522f475Smrg	}
2684cd3331d0Smrg	if (!strcmp(name, "?")) {
2685cd3331d0Smrg	    ReportAnsiColorRequest(xw, color + first, final);
2686cd3331d0Smrg	} else {
2687cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
2688d522f475Smrg	    if (code < 0) {
2689d522f475Smrg		/* stop on any error */
2690d522f475Smrg		break;
2691d522f475Smrg	    } else if (code > 0) {
2692d522f475Smrg		repaint = True;
2693d522f475Smrg	    }
2694d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
2695d522f475Smrg	     * change style (dynamic colors).
2696d522f475Smrg	     */
2697d522f475Smrg	}
2698d522f475Smrg    }
2699d522f475Smrg
2700d522f475Smrg    return (repaint);
2701d522f475Smrg}
2702cd3331d0Smrg
2703cd3331d0Smrgstatic Bool
2704cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
2705cd3331d0Smrg{
2706cd3331d0Smrg    Bool repaint = False;
2707cd3331d0Smrg    int last = MAXCOLORS - start;
2708cd3331d0Smrg
2709cd3331d0Smrg    if (color >= 0 && color < last) {
2710cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
2711cd3331d0Smrg
2712cd3331d0Smrg	if (res->mode) {
2713cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
2714cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
2715cd3331d0Smrg		repaint = True;
2716cd3331d0Smrg	    }
2717cd3331d0Smrg	}
2718cd3331d0Smrg    }
2719cd3331d0Smrg    return repaint;
2720cd3331d0Smrg}
2721cd3331d0Smrg
2722cd3331d0Smrgint
2723cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
2724cd3331d0Smrg{
2725cd3331d0Smrg    int repaint = 0;
2726cd3331d0Smrg    int color;
2727cd3331d0Smrg
2728cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
2729cd3331d0Smrg    if (*buf != '\0') {
2730cd3331d0Smrg	/* reset specific colors */
2731cd3331d0Smrg	while (!IsEmpty(buf)) {
2732cd3331d0Smrg	    char *next;
2733cd3331d0Smrg
2734cd3331d0Smrg	    color = (int) strtol(buf, &next, 10);
2735cd3331d0Smrg	    if ((next == buf) || (color < 0))
2736cd3331d0Smrg		break;		/* no number at all */
2737cd3331d0Smrg	    if (next != 0) {
2738cd3331d0Smrg		if (strchr(";", *next) == 0)
2739cd3331d0Smrg		    break;	/* unexpected delimiter */
2740cd3331d0Smrg		++next;
2741cd3331d0Smrg	    }
2742cd3331d0Smrg
2743cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2744cd3331d0Smrg		++repaint;
2745cd3331d0Smrg	    }
2746cd3331d0Smrg	    buf = next;
2747cd3331d0Smrg	}
2748cd3331d0Smrg    } else {
2749cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
2750cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
2751cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2752cd3331d0Smrg		++repaint;
2753cd3331d0Smrg	    }
2754cd3331d0Smrg	}
2755cd3331d0Smrg    }
2756cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
2757cd3331d0Smrg    return repaint;
2758cd3331d0Smrg}
2759d522f475Smrg#else
27603367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
27613367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
2762d522f475Smrg#endif /* OPT_ISO_COLORS */
2763d522f475Smrg
2764fa3f02f3SmrgBoolean
2765fa3f02f3SmrgallocateBestRGB(XtermWidget xw, XColor * def)
2766fa3f02f3Smrg{
2767fa3f02f3Smrg    Colormap cmap = xw->core.colormap;
2768fa3f02f3Smrg
2769fa3f02f3Smrg    return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
2770fa3f02f3Smrg}
2771fa3f02f3Smrg
27723367019cSmrgstatic Boolean
27733367019cSmrgxtermAllocColor(XtermWidget xw, XColor * def, const char *spec)
27743367019cSmrg{
27753367019cSmrg    Boolean result = False;
27763367019cSmrg    TScreen *screen = TScreenOf(xw);
27773367019cSmrg    Colormap cmap = xw->core.colormap;
27783367019cSmrg
2779fa3f02f3Smrg    if (XParseColor(screen->display, cmap, spec, def)) {
2780fa3f02f3Smrg	XColor save_def = *def;
2781fa3f02f3Smrg	if (resource.reportColors) {
2782fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
2783fa3f02f3Smrg		   def->red, def->green, def->blue,
2784fa3f02f3Smrg		   spec);
2785fa3f02f3Smrg	}
2786fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
2787fa3f02f3Smrg	    if (resource.reportColors) {
2788fa3f02f3Smrg		if (def->red != save_def.red ||
2789fa3f02f3Smrg		    def->green != save_def.green ||
2790fa3f02f3Smrg		    def->blue != save_def.blue) {
2791fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
2792fa3f02f3Smrg			   def->red, def->green, def->blue,
2793fa3f02f3Smrg			   spec);
2794fa3f02f3Smrg		}
2795fa3f02f3Smrg	    }
2796fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
2797fa3f02f3Smrg		   def->red, def->green, def->blue));
2798fa3f02f3Smrg	    result = True;
2799fa3f02f3Smrg	}
28003367019cSmrg    }
28013367019cSmrg    return result;
28023367019cSmrg}
28033367019cSmrg
28043367019cSmrg/*
28053367019cSmrg * This provides an approximation (the closest color from xterm's palette)
28063367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
28073367019cSmrg * because of the context in which it is used.
28083367019cSmrg */
28093367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
28103367019cSmrgint
28113367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
28123367019cSmrg{
28133367019cSmrg    int result = -1;
28143367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
28153367019cSmrg    int n;
28163367019cSmrg    int best_index = -1;
28173367019cSmrg    unsigned long best_value = 0;
28183367019cSmrg    unsigned long this_value;
28193367019cSmrg    long diff_red, diff_green, diff_blue;
28203367019cSmrg
28213367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
28223367019cSmrg
28233367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
28243367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
28253367019cSmrg
28263367019cSmrg	/* ensure that we have a value for each of the colors */
28273367019cSmrg	if (!res->mode) {
28283367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
28293367019cSmrg	}
28303367019cSmrg
28313367019cSmrg	/* find the closest match */
28323367019cSmrg	if (res->mode == True) {
28333367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
28343367019cSmrg		    res->value, res->red, res->green, res->blue));
28353367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
28363367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
28373367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
28383367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
28393367019cSmrg					  + (diff_green * diff_green)
28403367019cSmrg					  + (diff_blue * diff_blue));
28413367019cSmrg	    if (best_index < 0 || this_value < best_value) {
28423367019cSmrg		best_index = n;
28433367019cSmrg		best_value = this_value;
28443367019cSmrg	    }
28453367019cSmrg	}
28463367019cSmrg    }
28473367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
28483367019cSmrg    result = best_index;
28493367019cSmrg#else
28503367019cSmrg    (void) xw;
28513367019cSmrg    (void) find_red;
28523367019cSmrg    (void) find_green;
28533367019cSmrg    (void) find_blue;
28543367019cSmrg#endif
28553367019cSmrg    return result;
28563367019cSmrg}
28573367019cSmrg
2858d522f475Smrg#if OPT_PASTE64
2859d522f475Smrgstatic void
2860fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
2861d522f475Smrg{
2862d522f475Smrg#define PDATA(a,b) { a, #b }
2863d522f475Smrg    static struct {
2864d522f475Smrg	char given;
2865cd3331d0Smrg	String result;
2866d522f475Smrg    } table[] = {
2867d522f475Smrg	PDATA('s', SELECT),
2868d522f475Smrg	    PDATA('p', PRIMARY),
2869d522f475Smrg	    PDATA('c', CLIPBOARD),
2870d522f475Smrg	    PDATA('0', CUT_BUFFER0),
2871d522f475Smrg	    PDATA('1', CUT_BUFFER1),
2872d522f475Smrg	    PDATA('2', CUT_BUFFER2),
2873d522f475Smrg	    PDATA('3', CUT_BUFFER3),
2874d522f475Smrg	    PDATA('4', CUT_BUFFER4),
2875d522f475Smrg	    PDATA('5', CUT_BUFFER5),
2876d522f475Smrg	    PDATA('6', CUT_BUFFER6),
2877d522f475Smrg	    PDATA('7', CUT_BUFFER7),
2878d522f475Smrg    };
2879d522f475Smrg
2880cd3331d0Smrg    const char *base = buf;
28813367019cSmrg    char *used;
2882d522f475Smrg    Cardinal j, n = 0;
28833367019cSmrg    String *select_args;
2884d522f475Smrg
2885d522f475Smrg    TRACE(("Manipulate selection data\n"));
2886d522f475Smrg
2887d522f475Smrg    while (*buf != ';' && *buf != '\0') {
2888d522f475Smrg	++buf;
2889d522f475Smrg    }
2890d522f475Smrg
2891d522f475Smrg    if (*buf == ';') {
2892d522f475Smrg	*buf++ = '\0';
2893d522f475Smrg
2894d522f475Smrg	if (*base == '\0')
2895d522f475Smrg	    base = "s0";
2896d522f475Smrg
28973367019cSmrg	if ((used = x_strdup(base)) != 0) {
28983367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
28993367019cSmrg		while (*base != '\0') {
29003367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
29013367019cSmrg			if (*base == table[j].given) {
29023367019cSmrg			    used[n] = *base;
29033367019cSmrg			    select_args[n++] = table[j].result;
29043367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
29053367019cSmrg			    break;
29063367019cSmrg			}
29073367019cSmrg		    }
29083367019cSmrg		    ++base;
29093367019cSmrg		}
29103367019cSmrg		used[n] = 0;
29113367019cSmrg
29123367019cSmrg		if (!strcmp(buf, "?")) {
29133367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
29143367019cSmrg			TRACE(("Getting selection\n"));
29153367019cSmrg			unparseputc1(xw, ANSI_OSC);
29163367019cSmrg			unparseputs(xw, "52");
29173367019cSmrg			unparseputc(xw, ';');
29183367019cSmrg
29193367019cSmrg			unparseputs(xw, used);
29203367019cSmrg			unparseputc(xw, ';');
29213367019cSmrg
29223367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
29233367019cSmrg			screen->base64_paste = n;
29243367019cSmrg			screen->base64_final = final;
29253367019cSmrg
29263367019cSmrg			/* terminator will be written in this call */
29273367019cSmrg			xtermGetSelection((Widget) xw,
2928fa3f02f3Smrg					  XtLastTimestampProcessed(TScreenOf(xw)->display),
29293367019cSmrg					  select_args, n,
29303367019cSmrg					  NULL);
29313367019cSmrg		    }
29323367019cSmrg		} else {
29333367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
29343367019cSmrg			TRACE(("Setting selection with %s\n", buf));
29353367019cSmrg			ClearSelectionBuffer(screen);
29363367019cSmrg			while (*buf != '\0')
29373367019cSmrg			    AppendToSelectionBuffer(screen, CharOf(*buf++));
29383367019cSmrg			CompleteSelection(xw, select_args, n);
29393367019cSmrg		    }
29403367019cSmrg		}
29413367019cSmrg		free(select_args);
2942cd3331d0Smrg	    }
29433367019cSmrg	    free(used);
2944d522f475Smrg	}
2945d522f475Smrg    }
2946d522f475Smrg}
2947d522f475Smrg#endif /* OPT_PASTE64 */
2948d522f475Smrg
2949d522f475Smrg/***====================================================================***/
2950d522f475Smrg
2951cd3331d0Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
2952cd3331d0Smrg
2953d522f475Smrgstatic Bool
2954fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
2955d522f475Smrg{
2956cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2957d522f475Smrg    Bool result = False;
2958d522f475Smrg    Char *cp = *bufp;
2959d522f475Smrg    Char *next = cp;
2960d522f475Smrg
2961d522f475Smrg    (void) screen;
2962d522f475Smrg    (void) last;
2963d522f475Smrg
2964d522f475Smrg#if OPT_WIDE_CHARS
2965cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
2966d522f475Smrg	PtyData data;
2967d522f475Smrg
2968d522f475Smrg	if (decodeUtf8(fakePtyData(&data, cp, last))) {
2969d522f475Smrg	    if (data.utf_data != UCS_REPL
2970d522f475Smrg		&& (data.utf_data >= 128 ||
2971d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
2972d522f475Smrg		next += (data.utf_size - 1);
2973d522f475Smrg		result = True;
2974d522f475Smrg	    } else {
2975d522f475Smrg		result = False;
2976d522f475Smrg	    }
2977d522f475Smrg	} else {
2978d522f475Smrg	    result = False;
2979d522f475Smrg	}
2980d522f475Smrg    } else
2981d522f475Smrg#endif
2982d522f475Smrg#if OPT_C1_PRINT
2983d522f475Smrg	if (screen->c1_printable
2984d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
2985d522f475Smrg	result = True;
2986d522f475Smrg    } else
2987d522f475Smrg#endif
2988d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
2989d522f475Smrg	result = True;
2990d522f475Smrg    }
2991d522f475Smrg    *bufp = next;
2992d522f475Smrg    return result;
2993d522f475Smrg}
2994d522f475Smrg
2995d522f475Smrg/***====================================================================***/
2996d522f475Smrg
2997d522f475Smrg/*
2998d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
2999cd3331d0Smrg * array indices.  Compare with TermColors.
3000d522f475Smrg */
3001d522f475Smrgtypedef enum {
3002d522f475Smrg    OSC_TEXT_FG = 10
3003d522f475Smrg    ,OSC_TEXT_BG
3004d522f475Smrg    ,OSC_TEXT_CURSOR
3005d522f475Smrg    ,OSC_MOUSE_FG
3006d522f475Smrg    ,OSC_MOUSE_BG
3007d522f475Smrg#if OPT_TEK4014
3008d522f475Smrg    ,OSC_TEK_FG = 15
3009d522f475Smrg    ,OSC_TEK_BG
3010d522f475Smrg#endif
3011d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3012d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3013d522f475Smrg#endif
3014d522f475Smrg#if OPT_TEK4014
3015d522f475Smrg    ,OSC_TEK_CURSOR = 18
3016d522f475Smrg#endif
3017d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3018d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3019d522f475Smrg#endif
3020d522f475Smrg    ,OSC_NCOLORS
3021d522f475Smrg} OscTextColors;
3022d522f475Smrg
3023cd3331d0Smrg/*
3024cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3025cd3331d0Smrg */
3026cd3331d0Smrg#define OSC_RESET 100
3027cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3028cd3331d0Smrg
3029d522f475Smrgstatic ScrnColors *pOldColors = NULL;
3030d522f475Smrg
3031d522f475Smrgstatic Bool
3032d522f475SmrgGetOldColors(XtermWidget xw)
3033d522f475Smrg{
3034d522f475Smrg    int i;
3035d522f475Smrg    if (pOldColors == NULL) {
3036c219fbebSmrg	pOldColors = TypeXtMalloc(ScrnColors);
3037d522f475Smrg	if (pOldColors == NULL) {
30383367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3039d522f475Smrg	    return (False);
3040d522f475Smrg	}
3041d522f475Smrg	pOldColors->which = 0;
3042d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3043d522f475Smrg	    pOldColors->colors[i] = 0;
3044d522f475Smrg	    pOldColors->names[i] = NULL;
3045d522f475Smrg	}
3046d522f475Smrg	GetColors(xw, pOldColors);
3047d522f475Smrg    }
3048d522f475Smrg    return (True);
3049d522f475Smrg}
3050d522f475Smrg
3051d522f475Smrgstatic int
3052d522f475SmrgoppositeColor(int n)
3053d522f475Smrg{
3054d522f475Smrg    switch (n) {
3055d522f475Smrg    case TEXT_FG:
3056d522f475Smrg	n = TEXT_BG;
3057d522f475Smrg	break;
3058d522f475Smrg    case TEXT_BG:
3059d522f475Smrg	n = TEXT_FG;
3060d522f475Smrg	break;
3061d522f475Smrg    case MOUSE_FG:
3062d522f475Smrg	n = MOUSE_BG;
3063d522f475Smrg	break;
3064d522f475Smrg    case MOUSE_BG:
3065d522f475Smrg	n = MOUSE_FG;
3066d522f475Smrg	break;
3067d522f475Smrg#if OPT_TEK4014
3068d522f475Smrg    case TEK_FG:
3069d522f475Smrg	n = TEK_BG;
3070d522f475Smrg	break;
3071d522f475Smrg    case TEK_BG:
3072d522f475Smrg	n = TEK_FG;
3073d522f475Smrg	break;
3074d522f475Smrg#endif
3075d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3076d522f475Smrg    case HIGHLIGHT_FG:
3077d522f475Smrg	n = HIGHLIGHT_BG;
3078d522f475Smrg	break;
3079d522f475Smrg    case HIGHLIGHT_BG:
3080d522f475Smrg	n = HIGHLIGHT_FG;
3081d522f475Smrg	break;
3082d522f475Smrg#endif
3083d522f475Smrg    default:
3084d522f475Smrg	break;
3085d522f475Smrg    }
3086d522f475Smrg    return n;
3087d522f475Smrg}
3088d522f475Smrg
3089d522f475Smrgstatic void
3090d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3091d522f475Smrg{
3092cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3093cd3331d0Smrg	XColor color;
3094cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3095cd3331d0Smrg	char buffer[80];
3096d522f475Smrg
3097cd3331d0Smrg	/*
3098cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3099cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3100cd3331d0Smrg	 * reporting the opposite color which would be used.
3101cd3331d0Smrg	 */
3102cd3331d0Smrg	int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
3103cd3331d0Smrg
3104cd3331d0Smrg	GetOldColors(xw);
3105cd3331d0Smrg	color.pixel = pOldColors->colors[ndx];
3106cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
3107cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3108cd3331d0Smrg		color.red,
3109cd3331d0Smrg		color.green,
3110cd3331d0Smrg		color.blue);
3111712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
3112cd3331d0Smrg	       ndx, pOldColors->colors[ndx], buffer));
3113cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3114cd3331d0Smrg	unparseputs(xw, buffer);
3115cd3331d0Smrg	unparseputc1(xw, final);
3116cd3331d0Smrg	unparse_end(xw);
3117cd3331d0Smrg    }
3118d522f475Smrg}
3119d522f475Smrg
3120d522f475Smrgstatic Bool
3121d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
3122d522f475Smrg{
3123d522f475Smrg    int i;
3124d522f475Smrg
3125d522f475Smrg    /* if we were going to free old colors, this would be the place to
3126d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3127d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3128d522f475Smrg     * we could save some overhead this way.   The only case in which this
3129d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3130d522f475Smrg     * which case they can restart xterm
3131d522f475Smrg     */
3132d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3133d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
3134d522f475Smrg	    if (pOldColors->names[i] != NULL) {
3135d522f475Smrg		XtFree(pOldColors->names[i]);
3136d522f475Smrg		pOldColors->names[i] = NULL;
3137d522f475Smrg	    }
3138d522f475Smrg	    if (pNew->names[i]) {
3139d522f475Smrg		pOldColors->names[i] = pNew->names[i];
3140d522f475Smrg	    }
3141d522f475Smrg	    pOldColors->colors[i] = pNew->colors[i];
3142d522f475Smrg	}
3143d522f475Smrg    }
3144d522f475Smrg    return (True);
3145d522f475Smrg}
3146d522f475Smrg
3147d522f475Smrg/*
3148d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3149d522f475Smrg * xterm is compiled.
3150d522f475Smrg */
3151d522f475Smrgstatic int
3152d522f475SmrgOscToColorIndex(OscTextColors mode)
3153d522f475Smrg{
3154d522f475Smrg    int result = 0;
3155d522f475Smrg
3156d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3157d522f475Smrg    switch (mode) {
3158d522f475Smrg	CASE(TEXT_FG);
3159d522f475Smrg	CASE(TEXT_BG);
3160d522f475Smrg	CASE(TEXT_CURSOR);
3161d522f475Smrg	CASE(MOUSE_FG);
3162d522f475Smrg	CASE(MOUSE_BG);
3163d522f475Smrg#if OPT_TEK4014
3164d522f475Smrg	CASE(TEK_FG);
3165d522f475Smrg	CASE(TEK_BG);
3166d522f475Smrg#endif
3167d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3168d522f475Smrg	CASE(HIGHLIGHT_BG);
3169d522f475Smrg	CASE(HIGHLIGHT_FG);
3170d522f475Smrg#endif
3171d522f475Smrg#if OPT_TEK4014
3172d522f475Smrg	CASE(TEK_CURSOR);
3173d522f475Smrg#endif
3174d522f475Smrg    case OSC_NCOLORS:
3175d522f475Smrg	break;
3176d522f475Smrg    }
3177d522f475Smrg    return result;
3178d522f475Smrg}
3179d522f475Smrg
3180d522f475Smrgstatic Bool
3181d522f475SmrgChangeColorsRequest(XtermWidget xw,
3182d522f475Smrg		    int start,
3183d522f475Smrg		    char *names,
3184d522f475Smrg		    int final)
3185d522f475Smrg{
3186d522f475Smrg    Bool result = False;
3187d522f475Smrg    char *thisName;
3188d522f475Smrg    ScrnColors newColors;
3189d522f475Smrg    int i, ndx;
3190d522f475Smrg
3191d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3192d522f475Smrg
3193d522f475Smrg    if (GetOldColors(xw)) {
3194d522f475Smrg	newColors.which = 0;
3195d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3196d522f475Smrg	    newColors.names[i] = NULL;
3197d522f475Smrg	}
3198d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3199d522f475Smrg	    ndx = OscToColorIndex((OscTextColors) i);
3200d522f475Smrg	    if (xw->misc.re_verse)
3201d522f475Smrg		ndx = oppositeColor(ndx);
3202d522f475Smrg
3203cd3331d0Smrg	    if (IsEmpty(names)) {
3204d522f475Smrg		newColors.names[ndx] = NULL;
3205d522f475Smrg	    } else {
3206d522f475Smrg		if (names[0] == ';')
3207d522f475Smrg		    thisName = NULL;
3208d522f475Smrg		else
3209d522f475Smrg		    thisName = names;
3210d522f475Smrg		names = strchr(names, ';');
3211d522f475Smrg		if (names != NULL) {
3212d522f475Smrg		    *names++ = '\0';
3213d522f475Smrg		}
3214fa3f02f3Smrg		if (thisName != 0) {
3215fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3216fa3f02f3Smrg			ReportColorRequest(xw, ndx, final);
3217fa3f02f3Smrg		    } else if (!pOldColors->names[ndx]
3218fa3f02f3Smrg			       || strcmp(thisName, pOldColors->names[ndx])) {
3219fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3220fa3f02f3Smrg		    }
3221d522f475Smrg		}
3222d522f475Smrg	    }
3223d522f475Smrg	}
3224d522f475Smrg
3225d522f475Smrg	if (newColors.which != 0) {
3226d522f475Smrg	    ChangeColors(xw, &newColors);
3227d522f475Smrg	    UpdateOldColors(xw, &newColors);
3228d522f475Smrg	}
3229d522f475Smrg	result = True;
3230d522f475Smrg    }
3231d522f475Smrg    return result;
3232d522f475Smrg}
3233d522f475Smrg
3234cd3331d0Smrgstatic Bool
3235cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3236cd3331d0Smrg		   int code)
3237cd3331d0Smrg{
3238cd3331d0Smrg    Bool result = False;
3239cd3331d0Smrg    const char *thisName;
3240cd3331d0Smrg    ScrnColors newColors;
3241cd3331d0Smrg    int ndx;
3242cd3331d0Smrg
3243cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3244cd3331d0Smrg
3245cd3331d0Smrg#if OPT_COLOR_RES
3246cd3331d0Smrg    if (GetOldColors(xw)) {
3247cd3331d0Smrg	ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3248cd3331d0Smrg	if (xw->misc.re_verse)
3249cd3331d0Smrg	    ndx = oppositeColor(ndx);
3250cd3331d0Smrg
3251cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3252cd3331d0Smrg
3253cd3331d0Smrg	newColors.which = 0;
3254cd3331d0Smrg	newColors.names[ndx] = NULL;
3255cd3331d0Smrg
3256cd3331d0Smrg	if (thisName != 0
3257cd3331d0Smrg	    && pOldColors->names[ndx] != 0
3258cd3331d0Smrg	    && strcmp(thisName, pOldColors->names[ndx])) {
3259cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3260cd3331d0Smrg
3261cd3331d0Smrg	    if (newColors.which != 0) {
3262cd3331d0Smrg		ChangeColors(xw, &newColors);
3263cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3264cd3331d0Smrg	    }
3265cd3331d0Smrg	}
3266cd3331d0Smrg	result = True;
3267cd3331d0Smrg    }
3268cd3331d0Smrg#endif
3269cd3331d0Smrg    return result;
3270cd3331d0Smrg}
3271cd3331d0Smrg
3272cd3331d0Smrg#if OPT_SHIFT_FONTS
3273cd3331d0Smrg/*
3274cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3275cd3331d0Smrg *
3276cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3277cd3331d0Smrg * the corresponding menu font entry.
3278cd3331d0Smrg */
3279cd3331d0Smrgstatic int
3280fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3281cd3331d0Smrg{
3282cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3283cd3331d0Smrg    int num = screen->menu_font_number;
3284cd3331d0Smrg    int rel = 0;
3285cd3331d0Smrg
3286cd3331d0Smrg    if (*++source == '+') {
3287cd3331d0Smrg	rel = 1;
3288cd3331d0Smrg	source++;
3289cd3331d0Smrg    } else if (*source == '-') {
3290cd3331d0Smrg	rel = -1;
3291cd3331d0Smrg	source++;
3292cd3331d0Smrg    }
3293cd3331d0Smrg
3294cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3295cd3331d0Smrg	int val = atoi(source);
3296cd3331d0Smrg	if (rel > 0)
3297cd3331d0Smrg	    rel = val;
3298cd3331d0Smrg	else if (rel < 0)
3299cd3331d0Smrg	    rel = -val;
3300cd3331d0Smrg	else
3301cd3331d0Smrg	    num = val;
3302cd3331d0Smrg    }
3303cd3331d0Smrg
3304cd3331d0Smrg    if (rel != 0) {
3305cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3306cd3331d0Smrg				     screen->menu_font_number, rel);
3307cd3331d0Smrg
3308cd3331d0Smrg    }
3309cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3310cd3331d0Smrg    *target = source;
3311cd3331d0Smrg    return num;
3312cd3331d0Smrg}
3313cd3331d0Smrg
3314cd3331d0Smrgstatic void
3315cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3316cd3331d0Smrg{
3317cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3318cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3319cd3331d0Smrg	Bool success = True;
3320cd3331d0Smrg	int num;
3321cb4a1343Smrg	String base = buf + 1;
3322cd3331d0Smrg	const char *name = 0;
3323cd3331d0Smrg	char temp[10];
3324cd3331d0Smrg
3325cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3326cd3331d0Smrg	if (num < 0
3327cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3328cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3329cd3331d0Smrg	    success = False;
3330cd3331d0Smrg	} else {
3331cd3331d0Smrg#if OPT_RENDERFONT
3332cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3333cd3331d0Smrg		name = getFaceName(xw, False);
3334cd3331d0Smrg	    } else
3335cd3331d0Smrg#endif
3336cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3337cd3331d0Smrg		success = False;
3338cd3331d0Smrg	    }
3339cd3331d0Smrg	}
3340cd3331d0Smrg
3341cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3342cd3331d0Smrg	unparseputs(xw, "50");
3343cd3331d0Smrg
3344cd3331d0Smrg	if (success) {
3345cd3331d0Smrg	    unparseputc(xw, ';');
3346cd3331d0Smrg	    if (buf >= base) {
3347cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3348cd3331d0Smrg		if (*buf != '\0') {
3349cd3331d0Smrg		    unparseputc(xw, '#');
3350cd3331d0Smrg		    sprintf(temp, "%d", num);
3351cd3331d0Smrg		    unparseputs(xw, temp);
3352cd3331d0Smrg		    if (*name != '\0')
3353cd3331d0Smrg			unparseputc(xw, ' ');
3354cd3331d0Smrg		}
3355cd3331d0Smrg	    }
3356cd3331d0Smrg	    unparseputs(xw, name);
3357cd3331d0Smrg	}
3358cd3331d0Smrg
3359cd3331d0Smrg	unparseputc1(xw, final);
3360cd3331d0Smrg	unparse_end(xw);
3361cd3331d0Smrg    }
3362cd3331d0Smrg}
3363cd3331d0Smrg
3364cd3331d0Smrgstatic void
3365cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3366cd3331d0Smrg{
3367cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3368cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3369cd3331d0Smrg	Bool success = True;
3370cd3331d0Smrg	int num;
3371cd3331d0Smrg	VTFontNames fonts;
3372cd3331d0Smrg	char *name;
3373cd3331d0Smrg
3374cd3331d0Smrg	/*
3375cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3376cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3377cd3331d0Smrg	 *
3378cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3379cd3331d0Smrg	 * to load the font entry.
3380cd3331d0Smrg	 */
3381cd3331d0Smrg	if (*buf == '#') {
3382cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3383cd3331d0Smrg
3384cd3331d0Smrg	    if (num < 0
3385cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3386cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3387cd3331d0Smrg		success = False;
3388cd3331d0Smrg	    } else {
3389cd3331d0Smrg		/*
3390cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3391cd3331d0Smrg		 * for a font specification within the control.
3392cd3331d0Smrg		 */
3393cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3394cd3331d0Smrg		    ++buf;
3395cd3331d0Smrg		}
3396cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3397cd3331d0Smrg		    ++buf;
3398cd3331d0Smrg		}
3399cd3331d0Smrg#if OPT_RENDERFONT
3400cd3331d0Smrg		if (UsingRenderFont(xw)) {
3401c219fbebSmrg		    /* EMPTY */
3402c219fbebSmrg		    /* there is only one font entry to load */
3403c219fbebSmrg		    ;
3404cd3331d0Smrg		} else
3405cd3331d0Smrg#endif
3406cd3331d0Smrg		{
3407cd3331d0Smrg		    /*
3408cd3331d0Smrg		     * Normally there is no font specified in the control.
3409cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3410cd3331d0Smrg		     */
3411cd3331d0Smrg		    if (*buf == '\0') {
3412cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3413cd3331d0Smrg			    success = False;
3414cd3331d0Smrg			}
3415cd3331d0Smrg		    }
3416cd3331d0Smrg		}
3417cd3331d0Smrg	    }
3418cd3331d0Smrg	} else {
3419cd3331d0Smrg	    num = screen->menu_font_number;
3420cd3331d0Smrg	}
3421cd3331d0Smrg	name = x_strtrim(buf);
3422cd3331d0Smrg	if (success && !IsEmpty(name)) {
3423cd3331d0Smrg#if OPT_RENDERFONT
3424cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3425cd3331d0Smrg		setFaceName(xw, name);
3426cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3427cd3331d0Smrg	    } else
3428cd3331d0Smrg#endif
3429cd3331d0Smrg	    {
3430cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3431cd3331d0Smrg		fonts.f_n = name;
3432cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
3433cd3331d0Smrg	    }
3434cd3331d0Smrg	} else {
3435cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3436cd3331d0Smrg	}
3437cd3331d0Smrg	free(name);
3438cd3331d0Smrg    }
3439cd3331d0Smrg}
3440cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3441cd3331d0Smrg
3442d522f475Smrg/***====================================================================***/
3443d522f475Smrg
3444d522f475Smrgvoid
3445fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3446d522f475Smrg{
3447cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3448d522f475Smrg    int mode;
3449d522f475Smrg    Char *cp;
3450d522f475Smrg    int state = 0;
3451d522f475Smrg    char *buf = 0;
3452cd3331d0Smrg    char temp[2];
3453cd3331d0Smrg#if OPT_ISO_COLORS
3454cd3331d0Smrg    int ansi_colors = 0;
3455cd3331d0Smrg#endif
3456cd3331d0Smrg    Bool need_data = True;
3457fa3f02f3Smrg    Bool optional_data = False;
3458d522f475Smrg
3459d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3460d522f475Smrg
3461712a7ff4Smrg    (void) screen;
3462712a7ff4Smrg
3463d522f475Smrg    /*
3464d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3465d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3466d522f475Smrg     * with the same final character as the application sends to make this
3467d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3468d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3469d522f475Smrg     */
3470d522f475Smrg    mode = 0;
3471d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3472d522f475Smrg	switch (state) {
3473d522f475Smrg	case 0:
3474d522f475Smrg	    if (isdigit(*cp)) {
3475d522f475Smrg		mode = 10 * mode + (*cp - '0');
3476d522f475Smrg		if (mode > 65535) {
3477d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3478d522f475Smrg		    return;
3479d522f475Smrg		}
3480d522f475Smrg		break;
3481d522f475Smrg	    }
3482d522f475Smrg	    /* FALLTHRU */
3483d522f475Smrg	case 1:
3484d522f475Smrg	    if (*cp != ';') {
3485cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
3486cd3331d0Smrg		       (int) (cp - oscbuf)));
3487d522f475Smrg		return;
3488d522f475Smrg	    }
3489d522f475Smrg	    state = 2;
3490d522f475Smrg	    break;
3491d522f475Smrg	case 2:
3492d522f475Smrg	    buf = (char *) cp;
3493d522f475Smrg	    state = 3;
3494d522f475Smrg	    /* FALLTHRU */
3495d522f475Smrg	default:
3496cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
3497d522f475Smrg		switch (mode) {
3498d522f475Smrg		case 0:
3499d522f475Smrg		case 1:
3500d522f475Smrg		case 2:
3501d522f475Smrg		    break;
3502d522f475Smrg		default:
3503d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
3504d522f475Smrg			   CharOf(*cp),
3505cd3331d0Smrg			   (int) (cp - oscbuf)));
3506d522f475Smrg		    return;
3507d522f475Smrg		}
3508d522f475Smrg	    }
3509d522f475Smrg	}
3510d522f475Smrg    }
3511cd3331d0Smrg
35123367019cSmrg    /*
35133367019cSmrg     * Check if the palette changed and there are no more immediate changes
35143367019cSmrg     * that could be deferred to the next repaint.
35153367019cSmrg     */
35163367019cSmrg    if (xw->misc.palette_changed) {
35173367019cSmrg	switch (mode) {
35183367019cSmrg	case 3:		/* change X property */
35193367019cSmrg	case 30:		/* Konsole (unused) */
35203367019cSmrg	case 31:		/* Konsole (unused) */
35213367019cSmrg	case 50:		/* font operations */
35223367019cSmrg	case 51:		/* Emacs (unused) */
35233367019cSmrg#if OPT_PASTE64
35243367019cSmrg	case 52:		/* selection data */
35253367019cSmrg#endif
35263367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
35273367019cSmrg	    xw->misc.palette_changed = False;
35283367019cSmrg	    xtermRepaint(xw);
35293367019cSmrg	    break;
35303367019cSmrg	}
35313367019cSmrg    }
35323367019cSmrg
3533cd3331d0Smrg    /*
3534cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
3535cd3331d0Smrg     * a special case.
3536cd3331d0Smrg     */
3537cd3331d0Smrg    switch (mode) {
3538cd3331d0Smrg#if OPT_ISO_COLORS
3539cd3331d0Smrg    case OSC_Reset(4):
3540cd3331d0Smrg    case OSC_Reset(5):
3541fa3f02f3Smrg	need_data = False;
3542fa3f02f3Smrg	optional_data = True;
3543fa3f02f3Smrg	break;
3544cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3545cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3546cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3547cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3548cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3549cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3550cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3551cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3552cd3331d0Smrg#endif
3553cd3331d0Smrg#if OPT_TEK4014
3554cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3555cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3556cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3557cd3331d0Smrg#endif
3558cd3331d0Smrg	need_data = False;
3559cd3331d0Smrg	break;
3560cd3331d0Smrg#endif
3561cd3331d0Smrg    default:
3562cd3331d0Smrg	break;
3563cd3331d0Smrg    }
3564cd3331d0Smrg
3565cd3331d0Smrg    /*
3566cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
3567cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
3568cd3331d0Smrg     */
3569cd3331d0Smrg    if (IsEmpty(buf)) {
3570cd3331d0Smrg	if (need_data) {
3571cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
3572cd3331d0Smrg	    return;
3573cd3331d0Smrg	}
3574cd3331d0Smrg	temp[0] = '\0';
3575cd3331d0Smrg	buf = temp;
3576fa3f02f3Smrg    } else if (!need_data && !optional_data) {
3577fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
3578d522f475Smrg	return;
35790d92cbfdSchristos    }
3580d522f475Smrg
3581d522f475Smrg    switch (mode) {
3582d522f475Smrg    case 0:			/* new icon name and title */
3583b7c89284Ssnj	ChangeIconName(xw, buf);
3584b7c89284Ssnj	ChangeTitle(xw, buf);
3585d522f475Smrg	break;
3586d522f475Smrg
3587d522f475Smrg    case 1:			/* new icon name only */
3588b7c89284Ssnj	ChangeIconName(xw, buf);
3589d522f475Smrg	break;
3590d522f475Smrg
3591d522f475Smrg    case 2:			/* new title only */
3592b7c89284Ssnj	ChangeTitle(xw, buf);
3593d522f475Smrg	break;
3594d522f475Smrg
359522d8e007Schristos#ifdef notdef
3596d522f475Smrg    case 3:			/* change X property */
3597cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
35980d92cbfdSchristos	    ChangeXprop(buf);
3599d522f475Smrg	break;
360022d8e007Schristos#endif
3601d522f475Smrg#if OPT_ISO_COLORS
3602cd3331d0Smrg    case 5:
3603cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3604cd3331d0Smrg	/* FALLTHRU */
3605d522f475Smrg    case 4:
3606cd3331d0Smrg	if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
36073367019cSmrg	    xw->misc.palette_changed = True;
3608cd3331d0Smrg	break;
3609cd3331d0Smrg    case OSC_Reset(5):
3610cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
3611cd3331d0Smrg	/* FALLTHRU */
3612cd3331d0Smrg    case OSC_Reset(4):
3613cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
36143367019cSmrg	    xw->misc.palette_changed = True;
3615d522f475Smrg	break;
3616d522f475Smrg#endif
3617d522f475Smrg    case OSC_TEXT_FG:
3618d522f475Smrg    case OSC_TEXT_BG:
3619d522f475Smrg    case OSC_TEXT_CURSOR:
3620d522f475Smrg    case OSC_MOUSE_FG:
3621d522f475Smrg    case OSC_MOUSE_BG:
3622d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3623d522f475Smrg    case OSC_HIGHLIGHT_BG:
3624cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
3625d522f475Smrg#endif
3626d522f475Smrg#if OPT_TEK4014
3627d522f475Smrg    case OSC_TEK_FG:
3628d522f475Smrg    case OSC_TEK_BG:
3629d522f475Smrg    case OSC_TEK_CURSOR:
3630d522f475Smrg#endif
3631cd3331d0Smrg	if (xw->misc.dynamicColors) {
3632d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
3633cd3331d0Smrg	}
3634cd3331d0Smrg	break;
3635cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
3636cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
3637cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
3638cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
3639cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
3640cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
3641cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
3642cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
3643cd3331d0Smrg#endif
3644cd3331d0Smrg#if OPT_TEK4014
3645cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
3646cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
3647cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
3648cd3331d0Smrg#endif
3649cd3331d0Smrg	if (xw->misc.dynamicColors) {
3650cd3331d0Smrg	    ResetColorsRequest(xw, mode);
3651cd3331d0Smrg	}
3652d522f475Smrg	break;
3653d522f475Smrg
3654d522f475Smrg    case 30:
3655d522f475Smrg    case 31:
3656d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
3657d522f475Smrg	break;
3658d522f475Smrg
3659d522f475Smrg#ifdef ALLOWLOGGING
3660d522f475Smrg    case 46:			/* new log file */
3661d522f475Smrg#ifdef ALLOWLOGFILECHANGES
3662d522f475Smrg	/*
3663d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
3664d522f475Smrg	 * arbitrary files accessible to the person running xterm.
3665d522f475Smrg	 */
3666cd3331d0Smrg	if (strcmp(buf, "?")
3667d522f475Smrg	    && (cp = CastMallocN(char, strlen(buf)) != NULL)) {
3668d522f475Smrg	    strcpy(cp, buf);
3669d522f475Smrg	    if (screen->logfile)
3670d522f475Smrg		free(screen->logfile);
3671d522f475Smrg	    screen->logfile = cp;
3672d522f475Smrg	    break;
3673d522f475Smrg	}
3674d522f475Smrg#endif
3675cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3676cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
3677d522f475Smrg	break;
3678d522f475Smrg#endif /* ALLOWLOGGING */
3679d522f475Smrg
3680d522f475Smrg    case 50:
3681d522f475Smrg#if OPT_SHIFT_FONTS
3682cd3331d0Smrg	if (*buf == '?') {
3683cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
3684cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
3685cd3331d0Smrg	    ChangeFontRequest(xw, buf);
3686d522f475Smrg	}
3687d522f475Smrg#endif /* OPT_SHIFT_FONTS */
3688d522f475Smrg	break;
3689d522f475Smrg    case 51:
3690d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
3691d522f475Smrg	break;
3692d522f475Smrg
3693d522f475Smrg#if OPT_PASTE64
3694d522f475Smrg    case 52:
3695cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
3696d522f475Smrg	break;
3697d522f475Smrg#endif
3698d522f475Smrg	/*
3699d522f475Smrg	 * One could write code to send back the display and host names,
3700d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
3701d522f475Smrg	 */
3702cd3331d0Smrg    default:
3703cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
3704cd3331d0Smrg	break;
3705d522f475Smrg    }
3706d522f475Smrg    unparse_end(xw);
3707d522f475Smrg}
3708d522f475Smrg
3709d522f475Smrg#ifdef SunXK_F36
3710d522f475Smrg#define MAX_UDK 37
3711d522f475Smrg#else
3712d522f475Smrg#define MAX_UDK 35
3713d522f475Smrg#endif
3714d522f475Smrgstatic struct {
3715d522f475Smrg    char *str;
3716d522f475Smrg    int len;
3717d522f475Smrg} user_keys[MAX_UDK];
3718d522f475Smrg
3719d522f475Smrg/*
3720d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
3721d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
3722d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
3723d522f475Smrg * "real" terminals accept commas in the string definitions).
3724d522f475Smrg */
3725d522f475Smrgstatic int
3726cd3331d0Smrgudk_value(const char **cp)
3727d522f475Smrg{
3728cd3331d0Smrg    int result = -1;
3729d522f475Smrg    int c;
3730d522f475Smrg
3731d522f475Smrg    for (;;) {
3732d522f475Smrg	if ((c = **cp) != '\0')
3733d522f475Smrg	    *cp = *cp + 1;
3734d522f475Smrg	if (c == ';' || c == '\0')
3735cd3331d0Smrg	    break;
3736cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
3737cd3331d0Smrg	    break;
3738d522f475Smrg    }
3739cd3331d0Smrg
3740cd3331d0Smrg    return result;
3741d522f475Smrg}
3742d522f475Smrg
3743d522f475Smrgvoid
3744d522f475Smrgreset_decudk(void)
3745d522f475Smrg{
3746d522f475Smrg    int n;
3747d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
3748d522f475Smrg	if (user_keys[n].str != 0) {
3749d522f475Smrg	    free(user_keys[n].str);
3750d522f475Smrg	    user_keys[n].str = 0;
3751d522f475Smrg	    user_keys[n].len = 0;
3752d522f475Smrg	}
3753d522f475Smrg    }
3754d522f475Smrg}
3755d522f475Smrg
3756d522f475Smrg/*
3757d522f475Smrg * Parse the data for DECUDK (user-defined keys).
3758d522f475Smrg */
3759d522f475Smrgstatic void
3760cd3331d0Smrgparse_decudk(const char *cp)
3761d522f475Smrg{
3762d522f475Smrg    while (*cp) {
3763cd3331d0Smrg	const char *base = cp;
37643367019cSmrg	char *str = CastMallocN(char, strlen(cp) + 2);
3765d522f475Smrg	unsigned key = 0;
3766d522f475Smrg	int lo, hi;
3767d522f475Smrg	int len = 0;
3768d522f475Smrg
3769d522f475Smrg	while (isdigit(CharOf(*cp)))
37700d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
3771d522f475Smrg	if (*cp == '/') {
3772d522f475Smrg	    cp++;
3773d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
3774d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
37750d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
3776d522f475Smrg	    }
3777d522f475Smrg	}
3778d522f475Smrg	if (len > 0 && key < MAX_UDK) {
37793367019cSmrg	    str[len] = '\0';
3780d522f475Smrg	    if (user_keys[key].str != 0)
3781d522f475Smrg		free(user_keys[key].str);
3782d522f475Smrg	    user_keys[key].str = str;
3783d522f475Smrg	    user_keys[key].len = len;
3784d522f475Smrg	} else {
3785d522f475Smrg	    free(str);
3786d522f475Smrg	}
3787d522f475Smrg	if (*cp == ';')
3788d522f475Smrg	    cp++;
3789d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
3790d522f475Smrg	    break;
3791d522f475Smrg    }
3792d522f475Smrg}
3793d522f475Smrg
3794fa3f02f3Smrg/*
3795fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
3796fa3f02f3Smrg * interspersing with control characters, but have the string already.
3797fa3f02f3Smrg */
3798fa3f02f3Smrgstatic void
3799fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
3800fa3f02f3Smrg{
3801fa3f02f3Smrg    const char *cp = *string;
3802fa3f02f3Smrg    ParmType nparam = 0;
3803fa3f02f3Smrg    int last_empty = 1;
3804fa3f02f3Smrg
3805fa3f02f3Smrg    memset(params, 0, sizeof(*params));
3806fa3f02f3Smrg    while (*cp != '\0') {
3807fa3f02f3Smrg	Char ch = CharOf(*cp++);
3808fa3f02f3Smrg
3809fa3f02f3Smrg	if (isdigit(ch)) {
3810fa3f02f3Smrg	    last_empty = 0;
3811fa3f02f3Smrg	    if (nparam < NPARAM) {
3812fa3f02f3Smrg		params->a_param[nparam] =
3813fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
3814fa3f02f3Smrg				+ (ch - '0'));
3815fa3f02f3Smrg	    }
3816fa3f02f3Smrg	} else if (ch == ';') {
3817fa3f02f3Smrg	    last_empty = 1;
3818fa3f02f3Smrg	    nparam++;
3819fa3f02f3Smrg	} else if (ch < 32) {
3820fa3f02f3Smrg	    /* EMPTY */ ;
3821fa3f02f3Smrg	} else {
3822fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
3823fa3f02f3Smrg	    params->a_final = ch;
3824fa3f02f3Smrg	    break;
3825fa3f02f3Smrg	}
3826fa3f02f3Smrg    }
3827fa3f02f3Smrg
3828fa3f02f3Smrg    *string = cp;
3829fa3f02f3Smrg    if (!last_empty)
3830fa3f02f3Smrg	nparam++;
3831fa3f02f3Smrg    if (nparam > NPARAM)
3832fa3f02f3Smrg	params->a_nparam = NPARAM;
3833fa3f02f3Smrg    else
3834fa3f02f3Smrg	params->a_nparam = nparam;
3835fa3f02f3Smrg}
3836fa3f02f3Smrg
3837d522f475Smrg#if OPT_TRACE
3838d522f475Smrg#define SOFT_WIDE 10
3839d522f475Smrg#define SOFT_HIGH 20
3840d522f475Smrg
3841d522f475Smrgstatic void
3842fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
3843d522f475Smrg{
3844d522f475Smrg    char DscsName[8];
3845d522f475Smrg    int len;
3846d522f475Smrg    int Pfn = params->a_param[0];
3847d522f475Smrg    int Pcn = params->a_param[1];
3848d522f475Smrg    int Pe = params->a_param[2];
3849d522f475Smrg    int Pcmw = params->a_param[3];
3850d522f475Smrg    int Pw = params->a_param[4];
3851d522f475Smrg    int Pt = params->a_param[5];
3852d522f475Smrg    int Pcmh = params->a_param[6];
3853d522f475Smrg    int Pcss = params->a_param[7];
3854d522f475Smrg
3855d522f475Smrg    int start_char = Pcn + 0x20;
3856d522f475Smrg    int char_wide = ((Pcmw == 0)
3857d522f475Smrg		     ? (Pcss ? 6 : 10)
3858d522f475Smrg		     : (Pcmw > 4
3859d522f475Smrg			? Pcmw
3860d522f475Smrg			: (Pcmw + 3)));
3861d522f475Smrg    int char_high = ((Pcmh == 0)
38623367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
3863d522f475Smrg			? 10
3864d522f475Smrg			: 20)
3865d522f475Smrg		     : Pcmh);
3866d522f475Smrg    Char ch;
3867d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
3868d522f475Smrg    Bool first = True;
3869d522f475Smrg    Bool prior = False;
3870d522f475Smrg    int row = 0, col = 0;
3871d522f475Smrg
3872d522f475Smrg    TRACE(("Parsing DECDLD\n"));
3873d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
3874d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
3875d522f475Smrg    TRACE(("  erase control %d\n", Pe));
3876d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
3877d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
3878d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
3879d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
3880d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
3881d522f475Smrg
3882d522f475Smrg    if (Pfn > 1
3883d522f475Smrg	|| Pcn > 95
3884d522f475Smrg	|| Pe > 2
3885d522f475Smrg	|| Pcmw > 10
3886d522f475Smrg	|| Pcmw == 1
3887d522f475Smrg	|| Pt > 2
3888d522f475Smrg	|| Pcmh > 20
3889d522f475Smrg	|| Pcss > 1
3890d522f475Smrg	|| char_wide > SOFT_WIDE
3891d522f475Smrg	|| char_high > SOFT_HIGH) {
3892d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
3893d522f475Smrg	return;
3894d522f475Smrg    }
3895d522f475Smrg
3896d522f475Smrg    len = 0;
3897d522f475Smrg    while (*string != '\0') {
3898d522f475Smrg	ch = CharOf(*string++);
3899d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
3900d522f475Smrg	    if (len < 2)
3901b7c89284Ssnj		DscsName[len++] = (char) ch;
3902d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
3903b7c89284Ssnj	    DscsName[len++] = (char) ch;
3904d522f475Smrg	    break;
3905d522f475Smrg	}
3906d522f475Smrg    }
3907d522f475Smrg    DscsName[len] = 0;
3908d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
3909d522f475Smrg
3910d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
3911d522f475Smrg    while (*string != '\0') {
3912d522f475Smrg	if (first) {
3913d522f475Smrg	    TRACE(("Char %d:\n", start_char));
3914d522f475Smrg	    if (prior) {
3915d522f475Smrg		for (row = 0; row < char_high; ++row) {
3916d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
3917d522f475Smrg		}
3918d522f475Smrg	    }
3919d522f475Smrg	    prior = False;
3920d522f475Smrg	    first = False;
3921d522f475Smrg	    for (row = 0; row < char_high; ++row) {
3922d522f475Smrg		for (col = 0; col < char_wide; ++col) {
3923d522f475Smrg		    bits[row][col] = '.';
3924d522f475Smrg		}
3925d522f475Smrg	    }
3926d522f475Smrg	    row = col = 0;
3927d522f475Smrg	}
3928d522f475Smrg	ch = CharOf(*string++);
3929d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
3930d522f475Smrg	    int n;
3931d522f475Smrg
3932b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
3933d522f475Smrg	    for (n = 0; n < 6; ++n) {
3934b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
3935d522f475Smrg	    }
3936d522f475Smrg	    col += 1;
3937d522f475Smrg	    prior = True;
3938d522f475Smrg	} else if (ch == '/') {
3939d522f475Smrg	    row += 6;
3940d522f475Smrg	    col = 0;
3941d522f475Smrg	} else if (ch == ';') {
3942d522f475Smrg	    first = True;
3943d522f475Smrg	    ++start_char;
3944d522f475Smrg	}
3945d522f475Smrg    }
3946d522f475Smrg}
3947d522f475Smrg#else
3948d522f475Smrg#define parse_decdld(p,q)	/* nothing */
3949d522f475Smrg#endif
3950d522f475Smrg
3951d522f475Smrgvoid
3952fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
3953d522f475Smrg{
3954cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3955d522f475Smrg    char reply[BUFSIZ];
3956cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
3957d522f475Smrg    Bool okay;
3958d522f475Smrg    ANSI params;
3959d522f475Smrg
3960cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
3961d522f475Smrg
3962d522f475Smrg    if (dcslen != strlen(cp))
3963d522f475Smrg	/* shouldn't have nulls in the string */
3964d522f475Smrg	return;
3965d522f475Smrg
3966d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
3967d522f475Smrg    case '$':			/* DECRQSS */
3968d522f475Smrg	okay = True;
3969d522f475Smrg
3970d522f475Smrg	cp++;
3971d522f475Smrg	if (*cp++ == 'q') {
3972d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
3973d522f475Smrg		sprintf(reply, "%d%s",
3974d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
3975d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
3976d522f475Smrg			cp);
3977d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
39783367019cSmrg		if (screen->vtXX_level < 2) {
39793367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
39803367019cSmrg		    break;
39813367019cSmrg		}
3982d522f475Smrg		sprintf(reply, "%d%s%s",
3983d522f475Smrg			(screen->vtXX_level ?
3984d522f475Smrg			 screen->vtXX_level : 1) + 60,
3985d522f475Smrg			(screen->vtXX_level >= 2)
3986d522f475Smrg			? (screen->control_eight_bits
3987d522f475Smrg			   ? ";0" : ";1")
3988d522f475Smrg			: "",
3989d522f475Smrg			cp);
3990d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
3991d522f475Smrg		sprintf(reply, "%d;%dr",
3992d522f475Smrg			screen->top_marg + 1,
3993d522f475Smrg			screen->bot_marg + 1);
39943367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
39953367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
39963367019cSmrg		    sprintf(reply, "%d;%ds",
39973367019cSmrg			    screen->lft_marg + 1,
39983367019cSmrg			    screen->rgt_marg + 1);
39993367019cSmrg		}
4000d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4001d522f475Smrg		strcpy(reply, "0");
4002d522f475Smrg		if (xw->flags & BOLD)
4003d522f475Smrg		    strcat(reply, ";1");
4004d522f475Smrg		if (xw->flags & UNDERLINE)
4005d522f475Smrg		    strcat(reply, ";4");
4006d522f475Smrg		if (xw->flags & BLINK)
4007d522f475Smrg		    strcat(reply, ";5");
4008d522f475Smrg		if (xw->flags & INVERSE)
4009d522f475Smrg		    strcat(reply, ";7");
4010d522f475Smrg		if (xw->flags & INVISIBLE)
4011d522f475Smrg		    strcat(reply, ";8");
4012b7c89284Ssnj#if OPT_256_COLORS || OPT_88_COLORS
4013b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4014d522f475Smrg		    if (xw->flags & FG_COLOR) {
4015d522f475Smrg			if (xw->cur_foreground >= 16)
4016d522f475Smrg			    sprintf(reply + strlen(reply),
4017d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
4018d522f475Smrg			else
4019d522f475Smrg			    sprintf(reply + strlen(reply),
4020d522f475Smrg				    ";%d%d",
4021d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
4022d522f475Smrg				    xw->cur_foreground >= 8 ?
4023d522f475Smrg				    xw->cur_foreground - 8 :
4024d522f475Smrg				    xw->cur_foreground);
4025d522f475Smrg		    }
4026d522f475Smrg		    if (xw->flags & BG_COLOR) {
4027d522f475Smrg			if (xw->cur_background >= 16)
4028d522f475Smrg			    sprintf(reply + strlen(reply),
4029d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
4030d522f475Smrg			else
4031d522f475Smrg			    sprintf(reply + strlen(reply),
4032d522f475Smrg				    ";%d%d",
4033d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
4034d522f475Smrg				    xw->cur_background >= 8 ?
4035d522f475Smrg				    xw->cur_background - 8 :
4036d522f475Smrg				    xw->cur_background);
4037d522f475Smrg		    }
4038d522f475Smrg		});
4039b7c89284Ssnj#elif OPT_ISO_COLORS
4040b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
4041d522f475Smrg		    if (xw->flags & FG_COLOR)
4042d522f475Smrg			sprintf(reply + strlen(reply),
4043d522f475Smrg				";%d%d",
4044d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
4045d522f475Smrg				xw->cur_foreground >= 8 ?
4046d522f475Smrg				xw->cur_foreground - 8 :
4047d522f475Smrg				xw->cur_foreground);
4048d522f475Smrg		    if (xw->flags & BG_COLOR)
4049d522f475Smrg			sprintf(reply + strlen(reply),
4050d522f475Smrg				";%d%d",
4051d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
4052d522f475Smrg				xw->cur_background >= 8 ?
4053d522f475Smrg				xw->cur_background - 8 :
4054d522f475Smrg				xw->cur_background);
4055d522f475Smrg		});
4056b7c89284Ssnj#endif
4057d522f475Smrg		strcat(reply, "m");
4058712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
40593367019cSmrg		int code = STEADY_BLOCK;
40603367019cSmrg		if (isCursorUnderline(screen))
40613367019cSmrg		    code = STEADY_UNDERLINE;
40623367019cSmrg		else if (isCursorBar(screen))
40633367019cSmrg		    code = STEADY_BAR;
40643367019cSmrg#if OPT_BLINK_CURS
40653367019cSmrg		if (screen->cursor_blink_esc == 0)
40663367019cSmrg		    code -= 1;
40673367019cSmrg#endif
40683367019cSmrg		sprintf(reply, "%d%s", code, cp);
4069d522f475Smrg	    } else
4070d522f475Smrg		okay = False;
4071d522f475Smrg
407222d8e007Schristos	    if (okay) {
40730d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
40743367019cSmrg		unparseputc(xw, '1');
40750d92cbfdSchristos		unparseputc(xw, '$');
40760d92cbfdSchristos		unparseputc(xw, 'r');
4077d522f475Smrg		cp = reply;
407822d8e007Schristos		unparseputs(xw, cp);
40790d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
40800d92cbfdSchristos	    } else {
40810d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
408222d8e007Schristos	    }
4083d522f475Smrg	} else {
4084d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4085d522f475Smrg	}
4086d522f475Smrg	break;
4087d522f475Smrg#if OPT_TCAP_QUERY
4088d522f475Smrg    case '+':
4089d522f475Smrg	cp++;
4090cd3331d0Smrg	switch (*cp) {
4091cd3331d0Smrg	case 'p':
4092cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4093cd3331d0Smrg		set_termcap(xw, cp + 1);
4094cd3331d0Smrg	    }
4095cd3331d0Smrg	    break;
4096cd3331d0Smrg	case 'q':
4097cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4098cd3331d0Smrg		Bool fkey;
4099cd3331d0Smrg		unsigned state;
4100cd3331d0Smrg		int code;
4101cd3331d0Smrg		const char *tmp;
4102cd3331d0Smrg		const char *parsed = ++cp;
4103d522f475Smrg
4104cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4105d522f475Smrg
4106cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4107b7c89284Ssnj
4108cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4109d522f475Smrg
4110cd3331d0Smrg		unparseputc(xw, '+');
4111cd3331d0Smrg		unparseputc(xw, 'r');
4112d522f475Smrg
4113cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4114cd3331d0Smrg		    if (cp == parsed)
4115cd3331d0Smrg			break;	/* no data found, error */
4116d522f475Smrg
4117cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4118cd3331d0Smrg			unparseputc(xw, *tmp);
4119d522f475Smrg
4120cd3331d0Smrg		    if (code >= 0) {
4121cd3331d0Smrg			unparseputc(xw, '=');
4122cd3331d0Smrg			screen->tc_query_code = code;
4123cd3331d0Smrg			screen->tc_query_fkey = fkey;
4124d522f475Smrg#if OPT_ISO_COLORS
4125cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4126cd3331d0Smrg			 * number of colors) */
4127cd3331d0Smrg			if (code == XK_COLORS) {
4128cd3331d0Smrg			    unparseputn(xw, NUM_ANSI_COLORS);
4129cd3331d0Smrg			} else
4130cd3331d0Smrg#endif
4131cd3331d0Smrg			if (code == XK_TCAPNAME) {
4132c219fbebSmrg			    unparseputs(xw, resource.term_name);
4133cd3331d0Smrg			} else {
4134cd3331d0Smrg			    XKeyEvent event;
4135cd3331d0Smrg			    event.state = state;
4136cd3331d0Smrg			    Input(xw, &event, False);
4137cd3331d0Smrg			}
4138cd3331d0Smrg			screen->tc_query_code = -1;
4139cd3331d0Smrg		    } else {
4140cd3331d0Smrg			break;	/* no match found, error */
4141d522f475Smrg		    }
4142d522f475Smrg
4143d522f475Smrg		    cp = parsed;
4144cd3331d0Smrg		    if (*parsed == ';') {
4145cd3331d0Smrg			unparseputc(xw, *parsed++);
4146cd3331d0Smrg			cp = parsed;
4147cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4148cd3331d0Smrg		    }
4149d522f475Smrg		}
4150cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4151d522f475Smrg	    }
4152cd3331d0Smrg	    break;
4153d522f475Smrg	}
4154d522f475Smrg	break;
4155d522f475Smrg#endif
4156d522f475Smrg    default:
4157fa3f02f3Smrg	if (screen->terminal_id == 125 ||
4158fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
41590d92cbfdSchristos	    parse_ansi_params(&params, &cp);
41600d92cbfdSchristos	    switch (params.a_final) {
4161fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
4162fa3f02f3Smrg	    case 'p':
4163fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4164fa3f02f3Smrg		    screen->terminal_id == 240 ||
4165fa3f02f3Smrg		    screen->terminal_id == 241 ||
4166fa3f02f3Smrg		    screen->terminal_id == 330 ||
4167fa3f02f3Smrg		    screen->terminal_id == 340) {
4168fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4169fa3f02f3Smrg		}
4170fa3f02f3Smrg		break;
4171fa3f02f3Smrg	    case 'q':
4172fa3f02f3Smrg		if (screen->terminal_id == 125 ||
4173fa3f02f3Smrg		    screen->terminal_id == 240 ||
4174fa3f02f3Smrg		    screen->terminal_id == 241 ||
4175fa3f02f3Smrg		    screen->terminal_id == 330 ||
4176fa3f02f3Smrg		    screen->terminal_id == 340) {
4177fa3f02f3Smrg		    parse_sixel(xw, &params, cp);
4178fa3f02f3Smrg		}
4179fa3f02f3Smrg		break;
4180fa3f02f3Smrg#endif
41810d92cbfdSchristos	    case '|':		/* DECUDK */
41820d92cbfdSchristos		if (params.a_param[0] == 0)
41830d92cbfdSchristos		    reset_decudk();
41840d92cbfdSchristos		parse_decudk(cp);
41850d92cbfdSchristos		break;
41860d92cbfdSchristos	    case '{':		/* DECDLD (no '}' case though) */
41870d92cbfdSchristos		parse_decdld(&params, cp);
41880d92cbfdSchristos		break;
41890d92cbfdSchristos	    }
4190d522f475Smrg	}
4191d522f475Smrg	break;
4192d522f475Smrg    }
4193d522f475Smrg    unparse_end(xw);
4194d522f475Smrg}
4195d522f475Smrg
4196cb4a1343Smrg#if OPT_DEC_RECTOPS
4197cb4a1343Smrgenum {
4198cb4a1343Smrg    mdUnknown = 0,
4199cb4a1343Smrg    mdMaybeSet = 1,
4200cb4a1343Smrg    mdMaybeReset = 2,
4201cb4a1343Smrg    mdAlwaysSet = 3,
4202cb4a1343Smrg    mdAlwaysReset = 4
4203cb4a1343Smrg};
4204cb4a1343Smrg
4205cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
42063367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
4207cb4a1343Smrg
4208cb4a1343Smrg/*
4209cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
4210cb4a1343Smrg * 0 - not recognized
4211cb4a1343Smrg * 1 - set
4212cb4a1343Smrg * 2 - reset
4213cb4a1343Smrg * 3 - permanently set
4214cb4a1343Smrg * 4 - permanently reset
4215cb4a1343Smrg * Only one mode can be reported at a time.
4216cb4a1343Smrg */
4217cb4a1343Smrgvoid
4218cb4a1343Smrgdo_rpm(XtermWidget xw, int nparams, int *params)
4219cb4a1343Smrg{
4220cb4a1343Smrg    ANSI reply;
4221cb4a1343Smrg    int result = 0;
4222cb4a1343Smrg    int count = 0;
4223cb4a1343Smrg
4224cb4a1343Smrg    TRACE(("do_rpm %d:%d\n", nparams, params[0]));
4225cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4226cb4a1343Smrg    if (nparams >= 1) {
4227cb4a1343Smrg	switch (params[0]) {
4228cb4a1343Smrg	case 1:		/* GATM */
4229cb4a1343Smrg	    result = mdAlwaysReset;
4230cb4a1343Smrg	    break;
4231cb4a1343Smrg	case 2:
4232cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
4233cb4a1343Smrg	    break;
4234cb4a1343Smrg	case 3:		/* CRM */
4235cb4a1343Smrg	    result = mdMaybeReset;
4236cb4a1343Smrg	    break;
4237cb4a1343Smrg	case 4:
4238cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
4239cb4a1343Smrg	    break;
4240cb4a1343Smrg	case 5:		/* SRTM */
4241cb4a1343Smrg	case 7:		/* VEM */
4242cb4a1343Smrg	case 10:		/* HEM */
4243cb4a1343Smrg	case 11:		/* PUM */
4244cb4a1343Smrg	    result = mdAlwaysReset;
4245cb4a1343Smrg	    break;
4246cb4a1343Smrg	case 12:
4247cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
4248cb4a1343Smrg	    break;
4249cb4a1343Smrg	case 13:		/* FEAM */
4250cb4a1343Smrg	case 14:		/* FETM */
4251cb4a1343Smrg	case 15:		/* MATM */
4252cb4a1343Smrg	case 16:		/* TTM */
4253cb4a1343Smrg	case 17:		/* SATM */
4254cb4a1343Smrg	case 18:		/* TSM */
4255cb4a1343Smrg	case 19:		/* EBM */
4256cb4a1343Smrg	    result = mdAlwaysReset;
4257cb4a1343Smrg	    break;
4258cb4a1343Smrg	case 20:
4259cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
4260cb4a1343Smrg	    break;
4261cb4a1343Smrg	}
4262cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4263cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4264cb4a1343Smrg    }
4265cb4a1343Smrg    reply.a_type = ANSI_CSI;
4266cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4267cb4a1343Smrg    reply.a_inters = '$';
4268cb4a1343Smrg    reply.a_final = 'y';
4269cb4a1343Smrg    unparseseq(xw, &reply);
4270cb4a1343Smrg}
4271cb4a1343Smrg
4272cb4a1343Smrgvoid
4273cb4a1343Smrgdo_decrpm(XtermWidget xw, int nparams, int *params)
4274cb4a1343Smrg{
4275cb4a1343Smrg    ANSI reply;
4276cb4a1343Smrg    int result = 0;
4277cb4a1343Smrg    int count = 0;
4278cb4a1343Smrg
4279cb4a1343Smrg    TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
4280cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4281cb4a1343Smrg    if (nparams >= 1) {
4282cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
4283cb4a1343Smrg
4284cb4a1343Smrg	switch (params[0]) {
4285fa3f02f3Smrg	case srm_DECCKM:
4286cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
4287cb4a1343Smrg	    break;
4288fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
4289cb4a1343Smrg#if OPT_VT52_MODE
42903367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
4291cb4a1343Smrg#else
4292cb4a1343Smrg	    result = mdMaybeSet;
4293cb4a1343Smrg#endif
4294cb4a1343Smrg	    break;
4295fa3f02f3Smrg	case srm_DECCOLM:
4296cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
4297cb4a1343Smrg	    break;
4298fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
4299cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
4300cb4a1343Smrg	    break;
4301fa3f02f3Smrg	case srm_DECSCNM:
4302cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
4303cb4a1343Smrg	    break;
4304fa3f02f3Smrg	case srm_DECOM:
4305cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
4306cb4a1343Smrg	    break;
4307fa3f02f3Smrg	case srm_DECAWM:
4308cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
4309cb4a1343Smrg	    break;
4310fa3f02f3Smrg	case srm_DECARM:
4311cb4a1343Smrg	    result = mdAlwaysReset;
4312cb4a1343Smrg	    break;
4313fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
4314cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
4315cb4a1343Smrg	    break;
4316cb4a1343Smrg#if OPT_TOOLBAR
4317fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
4318cb4a1343Smrg	    result = MdBool(resource.toolBar);
4319cb4a1343Smrg	    break;
4320cb4a1343Smrg#endif
4321cb4a1343Smrg#if OPT_BLINK_CURS
4322fa3f02f3Smrg	case srm_ATT610_BLINK:	/* att610: Start/stop blinking cursor */
4323cb4a1343Smrg	    result = MdBool(screen->cursor_blink_res);
4324cb4a1343Smrg	    break;
4325cb4a1343Smrg#endif
4326fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
4327712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
4328cb4a1343Smrg	    break;
4329fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
4330712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
4331cb4a1343Smrg	    break;
4332fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
4333cb4a1343Smrg	    result = MdBool(screen->cursor_set);
4334cb4a1343Smrg	    break;
4335fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
4336cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
4337cb4a1343Smrg	    break;
4338cb4a1343Smrg#if OPT_SHIFT_FONTS
4339fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
4340cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
4341cb4a1343Smrg	    break;
4342cb4a1343Smrg#endif
4343cb4a1343Smrg#if OPT_TEK4014
4344fa3f02f3Smrg	case srm_DECTEK:
4345cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
4346cb4a1343Smrg	    break;
4347cb4a1343Smrg#endif
4348fa3f02f3Smrg	case srm_132COLS:
4349cb4a1343Smrg	    result = MdBool(screen->c132);
4350cb4a1343Smrg	    break;
4351fa3f02f3Smrg	case srm_CURSES_HACK:
4352cb4a1343Smrg	    result = MdBool(screen->curses);
4353cb4a1343Smrg	    break;
4354fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
4355cb4a1343Smrg	    result = MdFlag(xw->flags, NATIONAL);
4356cb4a1343Smrg	    break;
4357fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
4358cb4a1343Smrg	    result = MdBool(screen->marginbell);
4359cb4a1343Smrg	    break;
4360fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
4361cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSEWRAP);
4362cb4a1343Smrg	    break;
4363cb4a1343Smrg#ifdef ALLOWLOGGING
4364fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
4365cb4a1343Smrg#ifdef ALLOWLOGFILEONOFF
4366cb4a1343Smrg	    result = MdBool(screen->logging);
4367cb4a1343Smrg#endif /* ALLOWLOGFILEONOFF */
4368cb4a1343Smrg	    break;
4369cb4a1343Smrg#endif
4370fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
4371cb4a1343Smrg	    /* FALLTHRU */
4372fa3f02f3Smrg	case srm_OPT_ALTBUF:
4373cb4a1343Smrg	    /* FALLTHRU */
4374fa3f02f3Smrg	case srm_ALTBUF:
4375cb4a1343Smrg	    result = MdBool(screen->whichBuf);
4376cb4a1343Smrg	    break;
4377fa3f02f3Smrg	case srm_DECNKM:
4378cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
4379cb4a1343Smrg	    break;
4380fa3f02f3Smrg	case srm_DECBKM:
4381cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
4382cb4a1343Smrg	    break;
4383fa3f02f3Smrg	case srm_DECLRMM:
43843367019cSmrg	    result = MdFlag(xw->flags, LEFT_RIGHT);
43853367019cSmrg	    break;
4386fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
4387fa3f02f3Smrg	case srm_DECSDM:
4388fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
4389fa3f02f3Smrg	    break;
4390fa3f02f3Smrg#endif
4391fa3f02f3Smrg	case srm_DECNCSM:
43923367019cSmrg	    result = MdFlag(xw->flags, NOCLEAR_COLM);
43933367019cSmrg	    break;
4394fa3f02f3Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence         */
4395cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
4396cb4a1343Smrg	    break;
4397fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
4398cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
4399cb4a1343Smrg	    break;
4400fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
4401cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
4402cb4a1343Smrg	    break;
4403fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
4404cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
4405cb4a1343Smrg	    break;
4406cb4a1343Smrg#if OPT_FOCUS_EVENT
4407fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
4408cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
4409cb4a1343Smrg	    break;
4410cb4a1343Smrg#endif
4411fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
44123367019cSmrg	    /* FALLTHRU */
4413fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
44143367019cSmrg	    /* FALLTHRU */
4415fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
44163367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
44173367019cSmrg	    break;
4418fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
44193367019cSmrg	    result = MdBool(screen->alternateScroll);
4420cb4a1343Smrg	    break;
4421fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
4422cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
4423cb4a1343Smrg	    break;
4424fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
4425cb4a1343Smrg	    result = MdBool(screen->scrollkey);
4426cb4a1343Smrg	    break;
4427fa3f02f3Smrg	case srm_EIGHT_BIT_META:
44283367019cSmrg	    result = MdBool(screen->eight_bit_meta);
4429cb4a1343Smrg	    break;
4430cb4a1343Smrg#if OPT_NUM_LOCK
4431fa3f02f3Smrg	case srm_REAL_NUMLOCK:
4432cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
4433cb4a1343Smrg	    break;
4434fa3f02f3Smrg	case srm_META_SENDS_ESC:
4435cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
4436cb4a1343Smrg	    break;
4437cb4a1343Smrg#endif
4438fa3f02f3Smrg	case srm_DELETE_IS_DEL:
4439cb4a1343Smrg	    result = MdBool(screen->delete_is_del);
4440cb4a1343Smrg	    break;
4441cb4a1343Smrg#if OPT_NUM_LOCK
4442fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
4443cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
4444cb4a1343Smrg	    break;
4445cb4a1343Smrg#endif
4446fa3f02f3Smrg	case srm_KEEP_SELECTION:
4447cb4a1343Smrg	    result = MdBool(screen->keepSelection);
4448cb4a1343Smrg	    break;
4449fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
4450cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
4451cb4a1343Smrg	    break;
4452fa3f02f3Smrg	case srm_BELL_IS_URGENT:
4453cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
4454cb4a1343Smrg	    break;
4455fa3f02f3Smrg	case srm_POP_ON_BELL:
4456cb4a1343Smrg	    result = MdBool(screen->poponbell);
4457cb4a1343Smrg	    break;
4458fa3f02f3Smrg	case srm_TITE_INHIBIT:
4459cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
4460cb4a1343Smrg	    break;
4461cb4a1343Smrg#if OPT_TCAP_FKEYS
4462fa3f02f3Smrg	case srm_TCAP_FKEYS:
4463cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
4464cb4a1343Smrg	    break;
4465cb4a1343Smrg#endif
4466cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
4467fa3f02f3Smrg	case srm_SUN_FKEYS:
4468cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
4469cb4a1343Smrg	    break;
4470cb4a1343Smrg#endif
4471cb4a1343Smrg#if OPT_HP_FUNC_KEYS
4472fa3f02f3Smrg	case srm_HP_FKEYS:
4473cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
4474cb4a1343Smrg	    break;
4475cb4a1343Smrg#endif
4476cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
4477fa3f02f3Smrg	case srm_SCO_FKEYS:
4478cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
4479cb4a1343Smrg	    break;
4480cb4a1343Smrg#endif
4481fa3f02f3Smrg	case srm_LEGACY_FKEYS:
4482cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
4483cb4a1343Smrg	    break;
4484cb4a1343Smrg#if OPT_SUNPC_KBD
4485fa3f02f3Smrg	case srm_VT220_FKEYS:
4486cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
4487cb4a1343Smrg	    break;
4488cb4a1343Smrg#endif
4489cb4a1343Smrg#if OPT_READLINE
4490fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
4491cb4a1343Smrg	    result = MdBool(screen->click1_moves);
4492cb4a1343Smrg	    break;
4493fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
4494cb4a1343Smrg	    result = MdBool(screen->paste_moves);
4495cb4a1343Smrg	    break;
4496fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
4497cb4a1343Smrg	    result = MdBool(screen->dclick3_deletes);
4498cb4a1343Smrg	    break;
4499fa3f02f3Smrg	case srm_PASTE_IN_BRACKET:
4500cb4a1343Smrg	    result = MdBool(screen->paste_brackets);
4501cb4a1343Smrg	    break;
4502fa3f02f3Smrg	case srm_PASTE_QUOTE:
4503cb4a1343Smrg	    result = MdBool(screen->paste_quotes);
4504cb4a1343Smrg	    break;
4505fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
4506cb4a1343Smrg	    result = MdBool(screen->paste_literal_nl);
4507cb4a1343Smrg	    break;
4508cb4a1343Smrg#endif /* OPT_READLINE */
4509cb4a1343Smrg	}
4510cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
4511cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
4512cb4a1343Smrg    }
4513cb4a1343Smrg    reply.a_type = ANSI_CSI;
4514cb4a1343Smrg    reply.a_pintro = '?';
4515cb4a1343Smrg    reply.a_nparam = (ParmType) count;
4516cb4a1343Smrg    reply.a_inters = '$';
4517cb4a1343Smrg    reply.a_final = 'y';
4518cb4a1343Smrg    unparseseq(xw, &reply);
4519cb4a1343Smrg}
4520cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
4521cb4a1343Smrg
4522d522f475Smrgchar *
4523d522f475Smrgudk_lookup(int keycode, int *len)
4524d522f475Smrg{
4525d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
4526d522f475Smrg	*len = user_keys[keycode].len;
4527d522f475Smrg	return user_keys[keycode].str;
4528d522f475Smrg    }
4529d522f475Smrg    return 0;
4530d522f475Smrg}
4531d522f475Smrg
45323367019cSmrg#ifdef HAVE_LIBXPM
45333367019cSmrg
45343367019cSmrg#ifndef PIXMAP_ROOTDIR
45353367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
45363367019cSmrg#endif
45373367019cSmrg
45383367019cSmrgtypedef struct {
45393367019cSmrg    const char *name;
45403367019cSmrg    const char *const *data;
45413367019cSmrg} XPM_DATA;
45423367019cSmrg
45433367019cSmrgstatic char *
45443367019cSmrgx_find_icon(char **work, int *state, const char *suffix)
45453367019cSmrg{
45463367019cSmrg    const char *filename = resource.icon_hint;
45473367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
45483367019cSmrg    const char *larger = "_48x48";
45493367019cSmrg    char *result = 0;
45503367019cSmrg    size_t length;
45513367019cSmrg
45523367019cSmrg    if (*state >= 0) {
45533367019cSmrg	if ((*state & 1) == 0)
45543367019cSmrg	    suffix = "";
45553367019cSmrg	if ((*state & 2) == 0)
45563367019cSmrg	    larger = "";
45573367019cSmrg	if ((*state & 4) == 0) {
45583367019cSmrg	    prefix = "";
45593367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
45603367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
45613367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
45623367019cSmrg	    *state = -1;
45633367019cSmrg	} else if (*state >= 8) {
45643367019cSmrg	    *state = -1;
45653367019cSmrg	}
45663367019cSmrg    }
45673367019cSmrg
45683367019cSmrg    if (*state >= 0) {
45693367019cSmrg	if (*work) {
45703367019cSmrg	    free(*work);
45713367019cSmrg	    *work = 0;
45723367019cSmrg	}
45733367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
45743367019cSmrg	    strlen(suffix);
45753367019cSmrg	if ((result = malloc(length)) != 0) {
45763367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
45773367019cSmrg	    *work = result;
45783367019cSmrg	}
45793367019cSmrg	*state += 1;
45803367019cSmrg	TRACE(("x_find_icon %d:%s\n", *state, result));
45813367019cSmrg    }
45823367019cSmrg    return result;
45833367019cSmrg}
45843367019cSmrg
45853367019cSmrg#if OPT_BUILTIN_XPMS
45863367019cSmrgstatic const XPM_DATA *
45873367019cSmrgBuiltInXPM(const XPM_DATA * table, Cardinal length)
45883367019cSmrg{
45893367019cSmrg    const char *find = resource.icon_hint;
45903367019cSmrg    const XPM_DATA *result = 0;
45913367019cSmrg    if (!IsEmpty(find)) {
45923367019cSmrg	Cardinal n;
45933367019cSmrg	for (n = 0; n < length; ++n) {
45943367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
45953367019cSmrg		result = table + n;
45963367019cSmrg		break;
45973367019cSmrg	    }
45983367019cSmrg	}
45993367019cSmrg
46003367019cSmrg	/*
46013367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
46023367019cSmrg	 * which are all _HHxWW format.
46033367019cSmrg	 */
46043367019cSmrg	if (result == 0) {
46053367019cSmrg	    const char *base = table[0].name;
46063367019cSmrg	    const char *last = strchr(base, '_');
46073367019cSmrg	    if (last != 0
46083367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
46093367019cSmrg		result = table + length - 1;
46103367019cSmrg	    }
46113367019cSmrg	}
46123367019cSmrg    }
46133367019cSmrg    return result;
46143367019cSmrg}
46153367019cSmrg#endif /* OPT_BUILTIN_XPMS */
46163367019cSmrg
46173367019cSmrgtypedef enum {
46183367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
46193367019cSmrg    ,eHintNone
46203367019cSmrg    ,eHintSearch
46213367019cSmrg} ICON_HINT;
46223367019cSmrg
46233367019cSmrgstatic ICON_HINT
46243367019cSmrgwhich_icon_hint(void)
46253367019cSmrg{
46263367019cSmrg    ICON_HINT result = eHintDefault;
46273367019cSmrg    if (!IsEmpty(resource.icon_hint)) {
46283367019cSmrg	if (!x_strcasecmp(resource.icon_hint, "none")) {
46293367019cSmrg	    result = eHintNone;
46303367019cSmrg	} else {
46313367019cSmrg	    result = eHintSearch;
46323367019cSmrg	}
46333367019cSmrg    }
46343367019cSmrg    return result;
46353367019cSmrg}
46363367019cSmrg#endif /* HAVE_LIBXPM */
46373367019cSmrg
46383367019cSmrgint
46393367019cSmrggetVisualDepth(XtermWidget xw)
46403367019cSmrg{
46413367019cSmrg    int result = 0;
46423367019cSmrg
4643fa3f02f3Smrg    if (getVisualInfo(xw)) {
4644fa3f02f3Smrg	result = xw->visInfo->depth;
46453367019cSmrg    }
46463367019cSmrg    return result;
46473367019cSmrg}
46483367019cSmrg
46493367019cSmrg/*
46503367019cSmrg * WM_ICON_SIZE should be honored if possible.
46513367019cSmrg */
46523367019cSmrgvoid
46533367019cSmrgxtermLoadIcon(XtermWidget xw)
46543367019cSmrg{
46553367019cSmrg#ifdef HAVE_LIBXPM
46563367019cSmrg    Display *dpy = XtDisplay(xw);
46573367019cSmrg    Pixmap myIcon = 0;
46583367019cSmrg    Pixmap myMask = 0;
46593367019cSmrg    char *workname = 0;
46603367019cSmrg    ICON_HINT hint = which_icon_hint();
4661fa3f02f3Smrg#include <builtin_icons.h>
46623367019cSmrg
46633367019cSmrg    TRACE(("xtermLoadIcon %p:%s\n", (void *) xw, NonNull(resource.icon_hint)));
46643367019cSmrg
46653367019cSmrg    if (hint == eHintSearch) {
46663367019cSmrg	int state = 0;
46673367019cSmrg	while (x_find_icon(&workname, &state, ".xpm") != 0) {
46683367019cSmrg	    Pixmap resIcon = 0;
46693367019cSmrg	    Pixmap shapemask = 0;
46703367019cSmrg	    XpmAttributes attributes;
46713367019cSmrg
46723367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
46733367019cSmrg	    attributes.valuemask = XpmDepth;
46743367019cSmrg
46753367019cSmrg	    if (XpmReadFileToPixmap(dpy,
46763367019cSmrg				    DefaultRootWindow(dpy),
46773367019cSmrg				    workname,
46783367019cSmrg				    &resIcon,
46793367019cSmrg				    &shapemask,
46803367019cSmrg				    &attributes) == XpmSuccess) {
46813367019cSmrg		myIcon = resIcon;
46823367019cSmrg		myMask = shapemask;
46833367019cSmrg		TRACE(("...success\n"));
46843367019cSmrg		break;
46853367019cSmrg	    }
46863367019cSmrg	}
46873367019cSmrg    }
46883367019cSmrg
46893367019cSmrg    /*
46903367019cSmrg     * If no external file was found, look for the name in the built-in table.
46913367019cSmrg     * If that fails, just use the biggest mini-icon.
46923367019cSmrg     */
46933367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
46943367019cSmrg	char **data;
46953367019cSmrg#if OPT_BUILTIN_XPMS
46963367019cSmrg	const XPM_DATA *myData = 0;
46973367019cSmrg	myData = BuiltInXPM(mini_xterm_xpms, XtNumber(mini_xterm_xpms));
46983367019cSmrg	if (myData == 0)
46993367019cSmrg	    myData = BuiltInXPM(filled_xterm_xpms, XtNumber(filled_xterm_xpms));
47003367019cSmrg	if (myData == 0)
47013367019cSmrg	    myData = BuiltInXPM(xterm_color_xpms, XtNumber(xterm_color_xpms));
47023367019cSmrg	if (myData == 0)
47033367019cSmrg	    myData = BuiltInXPM(xterm_xpms, XtNumber(xterm_xpms));
47043367019cSmrg	if (myData == 0)
47053367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
47063367019cSmrg	data = (char **) myData->data,
47073367019cSmrg#else
47083367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
47093367019cSmrg#endif
47103367019cSmrg	if (XpmCreatePixmapFromData(dpy,
47113367019cSmrg				    DefaultRootWindow(dpy),
47123367019cSmrg				    data,
47133367019cSmrg				    &myIcon, &myMask, 0) != 0) {
47143367019cSmrg	    myIcon = 0;
47153367019cSmrg	    myMask = 0;
47163367019cSmrg	}
47173367019cSmrg    }
47183367019cSmrg
47193367019cSmrg    if (myIcon != 0) {
47203367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
47213367019cSmrg	if (!hints)
47223367019cSmrg	    hints = XAllocWMHints();
47233367019cSmrg
47243367019cSmrg	if (hints) {
47253367019cSmrg	    hints->flags |= IconPixmapHint;
47263367019cSmrg	    hints->icon_pixmap = myIcon;
47273367019cSmrg	    if (myMask) {
47283367019cSmrg		hints->flags |= IconMaskHint;
47293367019cSmrg		hints->icon_mask = myMask;
47303367019cSmrg	    }
47313367019cSmrg
47323367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
47333367019cSmrg	    XFree(hints);
47343367019cSmrg	    TRACE(("...loaded icon\n"));
47353367019cSmrg	}
47363367019cSmrg    }
47373367019cSmrg
47383367019cSmrg    if (workname != 0)
47393367019cSmrg	free(workname);
47403367019cSmrg
47413367019cSmrg#else
47423367019cSmrg    (void) xw;
47433367019cSmrg#endif
47443367019cSmrg}
47453367019cSmrg
47463367019cSmrgvoid
4747cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
4748d522f475Smrg{
4749d522f475Smrg#if OPT_WIDE_CHARS
4750d522f475Smrg    static Char *converted;	/* NO_LEAKS */
4751d522f475Smrg#endif
4752d522f475Smrg
4753d522f475Smrg    Arg args[1];
4754cd3331d0Smrg    Boolean changed = True;
4755d522f475Smrg    Widget w = CURRENT_EMU();
4756d522f475Smrg    Widget top = SHELL_OF(w);
4757d522f475Smrg
4758cd3331d0Smrg    char *my_attr;
4759cd3331d0Smrg    char *name;
4760cd3331d0Smrg    size_t limit;
4761cd3331d0Smrg    Char *c1;
4762cd3331d0Smrg    Char *cp;
4763d522f475Smrg
4764b7c89284Ssnj    if (!AllowTitleOps(xw))
4765d522f475Smrg	return;
4766d522f475Smrg
4767cd3331d0Smrg    if (value == 0)
47683367019cSmrg	value = emptyString;
4769cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
4770cd3331d0Smrg	const char *temp;
4771cd3331d0Smrg	char *test;
4772cd3331d0Smrg
4773cd3331d0Smrg	value = x_decode_hex(value, &temp);
47743367019cSmrg	if (*temp != '\0') {
47753367019cSmrg	    free(value);
4776cd3331d0Smrg	    return;
47773367019cSmrg	}
4778cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
4779cd3331d0Smrg	    if (CharOf(*test) < 32) {
4780cd3331d0Smrg		*test = '\0';
4781cd3331d0Smrg		break;
4782cd3331d0Smrg	    }
4783cd3331d0Smrg	}
4784cd3331d0Smrg    }
4785cd3331d0Smrg
4786cd3331d0Smrg    c1 = (Char *) value;
4787cd3331d0Smrg    name = value;
4788cd3331d0Smrg    limit = strlen(name);
4789cd3331d0Smrg    my_attr = x_strdup(attribute);
4790cd3331d0Smrg
4791cd3331d0Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
4792cd3331d0Smrg
4793d522f475Smrg    /*
4794d522f475Smrg     * Ignore titles that are too long to be plausible requests.
4795d522f475Smrg     */
4796cd3331d0Smrg    if (limit > 0 && limit < 1024) {
4797d522f475Smrg
4798cd3331d0Smrg	/*
4799cd3331d0Smrg	 * After all decoding, overwrite nonprintable characters with '?'.
4800cd3331d0Smrg	 */
4801cd3331d0Smrg	for (cp = c1; *cp != 0; ++cp) {
4802cd3331d0Smrg	    Char *c2 = cp;
4803cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
4804cd3331d0Smrg		memset(c2, '?', (size_t) (cp + 1 - c2));
4805cd3331d0Smrg	    }
4806d522f475Smrg	}
4807d522f475Smrg
4808d522f475Smrg#if OPT_WIDE_CHARS
4809cd3331d0Smrg	/*
4810cd3331d0Smrg	 * If we're running in UTF-8 mode, and have not been told that the
4811cd3331d0Smrg	 * title string is in UTF-8, it is likely that non-ASCII text in the
4812cd3331d0Smrg	 * string will be rejected because it is not printable in the current
4813cd3331d0Smrg	 * locale.  So we convert it to UTF-8, allowing the X library to
4814cd3331d0Smrg	 * convert it back.
4815cd3331d0Smrg	 */
4816cd3331d0Smrg	if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) {
4817cd3331d0Smrg	    int n;
4818cd3331d0Smrg
4819cd3331d0Smrg	    for (n = 0; name[n] != '\0'; ++n) {
4820cd3331d0Smrg		if (CharOf(name[n]) > 127) {
4821cd3331d0Smrg		    if (converted != 0)
4822cd3331d0Smrg			free(converted);
4823cd3331d0Smrg		    if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
4824cd3331d0Smrg			Char *temp = converted;
4825cd3331d0Smrg			while (*name != 0) {
4826cd3331d0Smrg			    temp = convertToUTF8(temp, CharOf(*name));
4827cd3331d0Smrg			    ++name;
4828cd3331d0Smrg			}
4829cd3331d0Smrg			*temp = 0;
4830cd3331d0Smrg			name = (char *) converted;
4831cd3331d0Smrg			TRACE(("...converted{%s}\n", name));
4832d522f475Smrg		    }
4833cd3331d0Smrg		    break;
4834d522f475Smrg		}
4835d522f475Smrg	    }
4836d522f475Smrg	}
4837d522f475Smrg#endif
4838d522f475Smrg
4839d522f475Smrg#if OPT_SAME_NAME
4840cd3331d0Smrg	/* If the attribute isn't going to change, then don't bother... */
4841cd3331d0Smrg
4842cd3331d0Smrg	if (resource.sameName) {
4843cd3331d0Smrg	    char *buf = 0;
4844cd3331d0Smrg	    XtSetArg(args[0], my_attr, &buf);
4845cd3331d0Smrg	    XtGetValues(top, args, 1);
4846cd3331d0Smrg	    TRACE(("...comparing{%s}\n", buf));
4847cd3331d0Smrg	    if (buf != 0 && strcmp(name, buf) == 0)
4848cd3331d0Smrg		changed = False;
4849cd3331d0Smrg	}
4850d522f475Smrg#endif /* OPT_SAME_NAME */
4851d522f475Smrg
4852cd3331d0Smrg	if (changed) {
4853cd3331d0Smrg	    TRACE(("...updating %s\n", my_attr));
4854cd3331d0Smrg	    TRACE(("...value is %s\n", name));
4855cd3331d0Smrg	    XtSetArg(args[0], my_attr, name);
4856cd3331d0Smrg	    XtSetValues(top, args, 1);
4857d522f475Smrg
4858d522f475Smrg#if OPT_WIDE_CHARS
4859cd3331d0Smrg	    if (xtermEnvUTF8()) {
4860cd3331d0Smrg		Display *dpy = XtDisplay(xw);
4861cd3331d0Smrg		Atom my_atom;
4862cd3331d0Smrg
4863cd3331d0Smrg		const char *propname = (!strcmp(my_attr, XtNtitle)
4864cd3331d0Smrg					? "_NET_WM_NAME"
4865cd3331d0Smrg					: "_NET_WM_ICON_NAME");
4866cd3331d0Smrg		if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
4867cd3331d0Smrg		    if (IsSetUtf8Title(xw)) {
4868cd3331d0Smrg			TRACE(("...updating %s\n", propname));
4869cd3331d0Smrg			TRACE(("...value is %s\n", value));
4870c219fbebSmrg			XChangeProperty(dpy, VShellWindow(xw), my_atom,
4871cd3331d0Smrg					XA_UTF8_STRING(dpy), 8,
4872cd3331d0Smrg					PropModeReplace,
4873cd3331d0Smrg					(Char *) value,
4874cd3331d0Smrg					(int) strlen(value));
4875cd3331d0Smrg		    } else {
4876cd3331d0Smrg			TRACE(("...deleting %s\n", propname));
4877c219fbebSmrg			XDeleteProperty(dpy, VShellWindow(xw), my_atom);
4878cd3331d0Smrg		    }
4879cd3331d0Smrg		}
4880d522f475Smrg	    }
4881cd3331d0Smrg#endif
4882d522f475Smrg	}
4883d522f475Smrg    }
48843367019cSmrg    if (IsTitleMode(xw, tmSetBase16)) {
48853367019cSmrg	free(value);
48863367019cSmrg    }
48873367019cSmrg    free(my_attr);
48883367019cSmrg
4889cd3331d0Smrg    return;
4890d522f475Smrg}
4891d522f475Smrg
4892d522f475Smrgvoid
4893b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
4894d522f475Smrg{
4895cd3331d0Smrg    if (name == 0) {
48963367019cSmrg	name = emptyString;
48973367019cSmrg    }
48983367019cSmrg    if (!showZIconBeep(xw, name))
4899b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
4900d522f475Smrg}
4901d522f475Smrg
4902d522f475Smrgvoid
4903b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
4904d522f475Smrg{
4905b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
4906d522f475Smrg}
4907d522f475Smrg
4908712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
4909d522f475Smrg
4910d522f475Smrgvoid
4911d522f475SmrgChangeXprop(char *buf)
4912d522f475Smrg{
4913d522f475Smrg    Display *dpy = XtDisplay(toplevel);
4914d522f475Smrg    Window w = XtWindow(toplevel);
4915d522f475Smrg    XTextProperty text_prop;
4916d522f475Smrg    Atom aprop;
4917d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
4918d522f475Smrg
4919d522f475Smrg    if (pchEndPropName)
4920d522f475Smrg	*pchEndPropName = '\0';
4921d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
4922d522f475Smrg    if (pchEndPropName == NULL) {
4923d522f475Smrg	/* no "=value" given, so delete the property */
4924d522f475Smrg	XDeleteProperty(dpy, w, aprop);
4925d522f475Smrg    } else {
4926d522f475Smrg	text_prop.value = pchEndPropName + 1;
4927d522f475Smrg	text_prop.encoding = XA_STRING;
4928d522f475Smrg	text_prop.format = 8;
4929d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
4930d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
4931d522f475Smrg    }
4932d522f475Smrg}
4933d522f475Smrg
4934d522f475Smrg/***====================================================================***/
4935d522f475Smrg
4936d522f475Smrg/*
4937d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
4938d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
4939d522f475Smrg */
4940d522f475Smrgvoid
4941d522f475SmrgReverseOldColors(void)
4942d522f475Smrg{
4943d522f475Smrg    ScrnColors *pOld = pOldColors;
4944d522f475Smrg    Pixel tmpPix;
4945d522f475Smrg    char *tmpName;
4946d522f475Smrg
4947d522f475Smrg    if (pOld) {
4948d522f475Smrg	/* change text cursor, if necesary */
4949d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
4950d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
4951d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
4952d522f475Smrg		XtFree(pOldColors->names[TEXT_CURSOR]);
4953d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
4954d522f475Smrg	    }
4955d522f475Smrg	    if (pOld->names[TEXT_BG]) {
4956d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
4957d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
4958d522f475Smrg		}
4959d522f475Smrg	    }
4960d522f475Smrg	}
4961d522f475Smrg
4962d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
4963d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
4964d522f475Smrg
4965d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
4966d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
4967d522f475Smrg
4968d522f475Smrg#if OPT_TEK4014
4969d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
4970d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
4971d522f475Smrg#endif
4972d522f475Smrg    }
4973d522f475Smrg    return;
4974d522f475Smrg}
4975d522f475Smrg
4976d522f475SmrgBool
4977d522f475SmrgAllocateTermColor(XtermWidget xw,
4978d522f475Smrg		  ScrnColors * pNew,
4979d522f475Smrg		  int ndx,
4980cd3331d0Smrg		  const char *name,
4981cd3331d0Smrg		  Bool always)
4982d522f475Smrg{
4983cd3331d0Smrg    Bool result = False;
4984d522f475Smrg
4985cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
4986cd3331d0Smrg	XColor def;
4987cd3331d0Smrg	char *newName;
4988cd3331d0Smrg
4989712a7ff4Smrg	result = True;
4990712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
4991712a7ff4Smrg	    def.pixel = xw->old_foreground;
4992712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
4993712a7ff4Smrg	    def.pixel = xw->old_background;
49943367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
4995712a7ff4Smrg	    result = False;
4996712a7ff4Smrg	}
4997712a7ff4Smrg
4998712a7ff4Smrg	if (result
4999cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
5000712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
5001cd3331d0Smrg		free(pNew->names[ndx]);
5002712a7ff4Smrg	    }
5003cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
5004cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
5005712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
5006712a7ff4Smrg		   ndx, newName, def.pixel));
5007cd3331d0Smrg	} else {
5008cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
5009712a7ff4Smrg	    result = False;
5010cd3331d0Smrg	}
5011cd3331d0Smrg    }
5012cd3331d0Smrg    return result;
5013d522f475Smrg}
5014d522f475Smrg/***====================================================================***/
5015d522f475Smrg
5016d522f475Smrg/* ARGSUSED */
5017d522f475Smrgvoid
5018cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
5019d522f475Smrg{
50203367019cSmrg    if_DEBUG({
50213367019cSmrg	xtermWarning(s, a);
50223367019cSmrg    });
5023d522f475Smrg}
5024d522f475Smrg
5025d522f475Smrgconst char *
5026d522f475SmrgSysErrorMsg(int code)
5027d522f475Smrg{
5028d522f475Smrg    static char unknown[] = "unknown error";
5029d522f475Smrg    char *s = strerror(code);
5030d522f475Smrg    return s ? s : unknown;
5031d522f475Smrg}
5032d522f475Smrg
5033d522f475Smrgconst char *
5034d522f475SmrgSysReasonMsg(int code)
5035d522f475Smrg{
5036d522f475Smrg    /* *INDENT-OFF* */
5037d522f475Smrg    static const struct {
5038d522f475Smrg	int code;
5039d522f475Smrg	const char *name;
5040d522f475Smrg    } table[] = {
5041d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
5042d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
5043d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
5044d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
5045d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
5046d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
5047d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
5048d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
5049d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
5050d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
5051d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
5052d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
5053d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
5054d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
5055d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
5056d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
5057d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
5058d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
5059d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
5060d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
5061d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
5062d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
5063d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
5064d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
5065d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
5066d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
5067d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
5068d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
5069d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
5070d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
5071d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
5072d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
5073d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
5074d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
5075d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
5076d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
5077d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
5078d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
5079d522f475Smrg    };
5080d522f475Smrg    /* *INDENT-ON* */
5081d522f475Smrg
5082d522f475Smrg    Cardinal n;
5083d522f475Smrg    const char *result = "?";
5084d522f475Smrg
5085d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
5086d522f475Smrg	if (code == table[n].code) {
5087d522f475Smrg	    result = table[n].name;
5088d522f475Smrg	    break;
5089d522f475Smrg	}
5090d522f475Smrg    }
5091d522f475Smrg    return result;
5092d522f475Smrg}
5093d522f475Smrg
5094d522f475Smrgvoid
5095d522f475SmrgSysError(int code)
5096d522f475Smrg{
5097d522f475Smrg    int oerrno = errno;
5098d522f475Smrg
5099c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
5100d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
5101d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
5102d522f475Smrg
5103d522f475Smrg    Cleanup(code);
5104d522f475Smrg}
5105d522f475Smrg
5106d522f475Smrgvoid
51073367019cSmrgNormalExit(void)
5108d522f475Smrg{
5109d522f475Smrg    static Bool cleaning;
5110d522f475Smrg
5111d522f475Smrg    /*
5112d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
5113d522f475Smrg     */
51143367019cSmrg    if (cleaning) {
51153367019cSmrg	hold_screen = 0;
51163367019cSmrg	return;
51173367019cSmrg    }
5118d522f475Smrg
51193367019cSmrg    cleaning = True;
51203367019cSmrg    need_cleanup = False;
5121d522f475Smrg
51223367019cSmrg    if (hold_screen) {
51233367019cSmrg	hold_screen = 2;
51243367019cSmrg	while (hold_screen) {
51253367019cSmrg	    xevents();
51263367019cSmrg	    Sleep(10);
5127d522f475Smrg	}
51283367019cSmrg    }
5129d522f475Smrg#if OPT_SESSION_MGT
51303367019cSmrg    if (resource.sessionMgt) {
51313367019cSmrg	XtVaSetValues(toplevel,
51323367019cSmrg		      XtNjoinSession, False,
51333367019cSmrg		      (void *) 0);
5134d522f475Smrg    }
51353367019cSmrg#endif
51363367019cSmrg    Cleanup(0);
51373367019cSmrg}
51383367019cSmrg
51393367019cSmrg/*
51403367019cSmrg * cleanup by sending SIGHUP to client processes
51413367019cSmrg */
51423367019cSmrgvoid
51433367019cSmrgCleanup(int code)
51443367019cSmrg{
51453367019cSmrg    TScreen *screen = TScreenOf(term);
51463367019cSmrg
51473367019cSmrg    TRACE(("Cleanup %d\n", code));
5148d522f475Smrg
5149d522f475Smrg    if (screen->pid > 1) {
5150d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
5151d522f475Smrg    }
5152d522f475Smrg    Exit(code);
5153d522f475Smrg}
5154d522f475Smrg
5155fa3f02f3Smrg#ifndef S_IXOTH
5156fa3f02f3Smrg#define S_IXOTH 1
5157fa3f02f3Smrg#endif
5158fa3f02f3Smrg
5159fa3f02f3SmrgBoolean
5160fa3f02f3SmrgvalidProgram(const char *pathname)
5161fa3f02f3Smrg{
5162fa3f02f3Smrg    Boolean result = False;
5163fa3f02f3Smrg    struct stat sb;
5164fa3f02f3Smrg
5165fa3f02f3Smrg    if (!IsEmpty(pathname)
5166fa3f02f3Smrg	&& *pathname == '/'
5167fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
5168fa3f02f3Smrg	&& stat(pathname, &sb) == 0
5169fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
5170fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
5171fa3f02f3Smrg	result = True;
5172fa3f02f3Smrg    }
5173fa3f02f3Smrg    return result;
5174fa3f02f3Smrg}
5175fa3f02f3Smrg
5176d522f475Smrg#ifndef VMS
51773367019cSmrg#ifndef PATH_MAX
51783367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
51793367019cSmrg#endif
5180d522f475Smrgchar *
5181d522f475SmrgxtermFindShell(char *leaf, Bool warning)
5182d522f475Smrg{
51833367019cSmrg    char *s0;
5184d522f475Smrg    char *s;
5185d522f475Smrg    char *d;
5186d522f475Smrg    char *tmp;
5187d522f475Smrg    char *result = leaf;
51883367019cSmrg    Bool allocated = False;
5189d522f475Smrg
5190d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
51913367019cSmrg
51923367019cSmrg    if (!strncmp("./", result, (size_t) 2)
51933367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
51943367019cSmrg	size_t need = PATH_MAX;
51953367019cSmrg	size_t used = strlen(result) + 2;
51963367019cSmrg	char *buffer = malloc(used + need);
51973367019cSmrg	if (buffer != 0) {
51983367019cSmrg	    if (getcwd(buffer, need) != 0) {
51993367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
52003367019cSmrg		result = buffer;
52013367019cSmrg		allocated = True;
52023367019cSmrg	    } else {
52033367019cSmrg		free(buffer);
52043367019cSmrg	    }
52053367019cSmrg	}
52063367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
5207d522f475Smrg	/* find it in $PATH */
52083367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
52090d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
5210d522f475Smrg		Bool found = False;
5211d522f475Smrg		while (*s != '\0') {
5212d522f475Smrg		    strcpy(tmp, s);
5213d522f475Smrg		    for (d = tmp;; ++d) {
5214d522f475Smrg			if (*d == ':' || *d == '\0') {
5215d522f475Smrg			    int skip = (*d != '\0');
5216d522f475Smrg			    *d = '/';
5217d522f475Smrg			    strcpy(d + 1, leaf);
5218d522f475Smrg			    if (skip)
5219d522f475Smrg				++d;
5220d522f475Smrg			    s += (d - tmp);
5221fa3f02f3Smrg			    if (validProgram(tmp)) {
5222d522f475Smrg				result = x_strdup(tmp);
5223d522f475Smrg				found = True;
52243367019cSmrg				allocated = True;
5225d522f475Smrg			    }
5226d522f475Smrg			    break;
5227d522f475Smrg			}
5228d522f475Smrg		    }
5229d522f475Smrg		    if (found)
5230d522f475Smrg			break;
5231d522f475Smrg		}
5232d522f475Smrg		free(tmp);
5233d522f475Smrg	    }
52343367019cSmrg	    free(s0);
5235d522f475Smrg	}
5236d522f475Smrg    }
5237d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
5238fa3f02f3Smrg    if (!validProgram(result)) {
5239d522f475Smrg	if (warning)
52403367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
52413367019cSmrg	if (allocated)
52423367019cSmrg	    free(result);
5243d522f475Smrg	result = 0;
5244d522f475Smrg    }
52453367019cSmrg    /* be consistent, so that caller can always free the result */
52463367019cSmrg    if (result != 0 && !allocated)
52473367019cSmrg	result = x_strdup(result);
5248d522f475Smrg    return result;
5249d522f475Smrg}
5250d522f475Smrg#endif /* VMS */
5251d522f475Smrg
52520d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
5253d522f475Smrg
52543367019cSmrg/*
52553367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
52563367019cSmrg * This could happen on some older machines due to the uneven standardization
52573367019cSmrg * process for the two functions.
52583367019cSmrg *
52593367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
52603367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
52613367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
52623367019cSmrg * could copy environ.
52633367019cSmrg */
52643367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
52653367019cSmrg#undef HAVE_PUTENV
52663367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
52673367019cSmrg#undef HAVE_UNSETENV
52683367019cSmrg#endif
52693367019cSmrg
5270d522f475Smrg/*
5271d522f475Smrg * copy the environment before Setenv'ing.
5272d522f475Smrg */
5273d522f475Smrgvoid
5274d522f475SmrgxtermCopyEnv(char **oldenv)
5275d522f475Smrg{
52763367019cSmrg#ifdef HAVE_PUTENV
52773367019cSmrg    (void) oldenv;
52783367019cSmrg#else
5279d522f475Smrg    unsigned size;
5280d522f475Smrg    char **newenv;
5281d522f475Smrg
5282d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
5283d522f475Smrg	;
5284d522f475Smrg    }
5285d522f475Smrg
5286d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
5287d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
5288d522f475Smrg    environ = newenv;
52893367019cSmrg#endif
52903367019cSmrg}
52913367019cSmrg
52923367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
52933367019cSmrgstatic int
52943367019cSmrgfindEnv(const char *var, int *lengthp)
52953367019cSmrg{
52963367019cSmrg    char *test;
52973367019cSmrg    int envindex = 0;
52983367019cSmrg    size_t len = strlen(var);
52993367019cSmrg    int found = -1;
53003367019cSmrg
53013367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
53023367019cSmrg
53033367019cSmrg    while ((test = environ[envindex]) != NULL) {
53043367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
53053367019cSmrg	    found = envindex;
53063367019cSmrg	    break;
53073367019cSmrg	}
53083367019cSmrg	envindex++;
53093367019cSmrg    }
53103367019cSmrg    *lengthp = envindex;
53113367019cSmrg    return found;
5312d522f475Smrg}
53133367019cSmrg#endif
5314d522f475Smrg
5315d522f475Smrg/*
5316d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
5317d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
5318d522f475Smrg * This procedure assumes the memory for the first level of environ
5319d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
5320d522f475Smrg * to have to do a realloc().
5321d522f475Smrg */
5322d522f475Smrgvoid
5323cd3331d0SmrgxtermSetenv(const char *var, const char *value)
5324d522f475Smrg{
5325d522f475Smrg    if (value != 0) {
53263367019cSmrg#ifdef HAVE_PUTENV
53273367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
53283367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
53293367019cSmrg	if (both) {
53303367019cSmrg	    sprintf(both, "%s=%s", var, value);
53313367019cSmrg	    putenv(both);
53323367019cSmrg	}
53333367019cSmrg#else
5334d522f475Smrg	size_t len = strlen(var);
53353367019cSmrg	int envindex;
53363367019cSmrg	int found = findEnv(var, &envindex);
5337d522f475Smrg
5338d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
5339d522f475Smrg
5340d522f475Smrg	if (found < 0) {
5341d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
5342d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
5343d522f475Smrg
5344d522f475Smrg	    if (need > have) {
5345d522f475Smrg		char **newenv;
5346d522f475Smrg		newenv = TypeMallocN(char *, need);
5347d522f475Smrg		if (newenv == 0) {
53483367019cSmrg		    xtermWarning("Cannot increase environment\n");
5349d522f475Smrg		    return;
5350d522f475Smrg		}
5351d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
5352d522f475Smrg		free(environ);
5353d522f475Smrg		environ = newenv;
5354d522f475Smrg	    }
5355d522f475Smrg
5356d522f475Smrg	    found = envindex;
5357d522f475Smrg	    environ[found + 1] = NULL;
5358d522f475Smrg	    environ = environ;
5359d522f475Smrg	}
5360d522f475Smrg
5361d522f475Smrg	environ[found] = CastMallocN(char, 1 + len + strlen(value));
5362d522f475Smrg	if (environ[found] == 0) {
53633367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
5364d522f475Smrg	    return;
5365d522f475Smrg	}
5366d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
53673367019cSmrg#endif
5368d522f475Smrg    }
5369d522f475Smrg}
5370d522f475Smrg
53713367019cSmrgvoid
53723367019cSmrgxtermUnsetenv(const char *var)
53733367019cSmrg{
53743367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
53753367019cSmrg#ifdef HAVE_UNSETENV
53763367019cSmrg    unsetenv(var);
53773367019cSmrg#else
53783367019cSmrg    {
53793367019cSmrg	int ignore;
53803367019cSmrg	int item = findEnv(var, &ignore);
53813367019cSmrg	if (item >= 0) {
53823367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
53833367019cSmrg		++item;
53843367019cSmrg	    }
53853367019cSmrg	}
53863367019cSmrg    }
53873367019cSmrg#endif
53883367019cSmrg}
53893367019cSmrg
5390d522f475Smrg/*ARGSUSED*/
5391d522f475Smrgint
5392fa3f02f3Smrgxerror(Display *d, XErrorEvent * ev)
5393d522f475Smrg{
53943367019cSmrg    xtermWarning("warning, error event received:\n");
5395d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
5396d522f475Smrg    Exit(ERROR_XERROR);
5397d522f475Smrg    return 0;			/* appease the compiler */
5398d522f475Smrg}
5399d522f475Smrg
5400712a7ff4Smrgvoid
5401712a7ff4Smrgice_error(IceConn iceConn)
5402712a7ff4Smrg{
5403712a7ff4Smrg    (void) iceConn;
5404712a7ff4Smrg
54053367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
54063367019cSmrg		 (long) getpid(), errno);
5407712a7ff4Smrg
5408712a7ff4Smrg    Exit(ERROR_ICEERROR);
5409712a7ff4Smrg}
5410712a7ff4Smrg
5411d522f475Smrg/*ARGSUSED*/
5412d522f475Smrgint
5413fa3f02f3Smrgxioerror(Display *dpy)
5414d522f475Smrg{
5415d522f475Smrg    int the_error = errno;
5416d522f475Smrg
54173367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
54183367019cSmrg		 the_error, SysErrorMsg(the_error),
54193367019cSmrg		 DisplayString(dpy));
5420d522f475Smrg
5421d522f475Smrg    Exit(ERROR_XIOERROR);
5422d522f475Smrg    return 0;			/* appease the compiler */
5423d522f475Smrg}
5424d522f475Smrg
5425d522f475Smrgvoid
5426d522f475Smrgxt_error(String message)
5427d522f475Smrg{
54283367019cSmrg    xtermWarning("Xt error: %s\n", message);
5429d522f475Smrg
5430d522f475Smrg    /*
5431d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
5432d522f475Smrg     */
5433d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
54343367019cSmrg	xtermWarning("DISPLAY is not set\n");
5435d522f475Smrg    }
5436d522f475Smrg    exit(1);
5437d522f475Smrg}
5438d522f475Smrg
5439d522f475Smrgint
5440d522f475SmrgXStrCmp(char *s1, char *s2)
5441d522f475Smrg{
5442d522f475Smrg    if (s1 && s2)
5443d522f475Smrg	return (strcmp(s1, s2));
5444d522f475Smrg    if (s1 && *s1)
5445d522f475Smrg	return (1);
5446d522f475Smrg    if (s2 && *s2)
5447d522f475Smrg	return (-1);
5448d522f475Smrg    return (0);
5449d522f475Smrg}
5450d522f475Smrg
5451d522f475Smrg#if OPT_TEK4014
5452d522f475Smrgstatic void
5453fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
5454d522f475Smrg{
5455d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
5456d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
5457d522f475Smrg    XWithdrawWindow(dpy, w, scr);
5458d522f475Smrg    return;
5459d522f475Smrg}
5460d522f475Smrg#endif
5461d522f475Smrg
5462d522f475Smrgvoid
5463d522f475Smrgset_vt_visibility(Bool on)
5464d522f475Smrg{
5465c219fbebSmrg    XtermWidget xw = term;
5466c219fbebSmrg    TScreen *screen = TScreenOf(xw);
5467d522f475Smrg
5468d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
5469d522f475Smrg    if (on) {
5470c219fbebSmrg	if (!screen->Vshow && xw) {
5471c219fbebSmrg	    VTInit(xw);
5472c219fbebSmrg	    XtMapWidget(XtParent(xw));
5473d522f475Smrg#if OPT_TOOLBAR
5474d522f475Smrg	    /* we need both of these during initialization */
5475c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
5476d522f475Smrg	    ShowToolbar(resource.toolBar);
5477d522f475Smrg#endif
5478d522f475Smrg	    screen->Vshow = True;
5479d522f475Smrg	}
5480d522f475Smrg    }
5481d522f475Smrg#if OPT_TEK4014
5482d522f475Smrg    else {
5483c219fbebSmrg	if (screen->Vshow && xw) {
5484c219fbebSmrg	    withdraw_window(XtDisplay(xw),
5485c219fbebSmrg			    VShellWindow(xw),
5486c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
5487d522f475Smrg	    screen->Vshow = False;
5488d522f475Smrg	}
5489d522f475Smrg    }
5490d522f475Smrg    set_vthide_sensitivity();
5491d522f475Smrg    set_tekhide_sensitivity();
5492d522f475Smrg    update_vttekmode();
5493d522f475Smrg    update_tekshow();
5494d522f475Smrg    update_vtshow();
5495d522f475Smrg#endif
5496d522f475Smrg    return;
5497d522f475Smrg}
5498d522f475Smrg
5499d522f475Smrg#if OPT_TEK4014
5500d522f475Smrgvoid
5501d522f475Smrgset_tek_visibility(Bool on)
5502d522f475Smrg{
5503d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
5504d522f475Smrg
5505d522f475Smrg    if (on) {
5506cd3331d0Smrg	if (!TEK4014_SHOWN(term)) {
5507cd3331d0Smrg	    if (tekWidget == 0) {
5508cd3331d0Smrg		TekInit();	/* will exit on failure */
5509cd3331d0Smrg	    }
5510cd3331d0Smrg	    if (tekWidget != 0) {
5511cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
5512cd3331d0Smrg		XtRealizeWidget(tekParent);
5513cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
5514d522f475Smrg#if OPT_TOOLBAR
5515cd3331d0Smrg		/* we need both of these during initialization */
5516cd3331d0Smrg		XtMapWidget(tekParent);
5517cd3331d0Smrg		XtMapWidget(tekWidget);
5518d522f475Smrg#endif
5519cd3331d0Smrg		XtOverrideTranslations(tekParent,
5520cd3331d0Smrg				       XtParseTranslationTable
5521cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
5522cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
5523cd3331d0Smrg				       XtWindow(tekParent),
5524cd3331d0Smrg				       &wm_delete_window, 1);
5525cd3331d0Smrg		TEK4014_SHOWN(term) = True;
5526cd3331d0Smrg	    }
5527d522f475Smrg	}
5528d522f475Smrg    } else {
5529d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
5530d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
5531d522f475Smrg			    TShellWindow,
5532d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
5533d522f475Smrg	    TEK4014_SHOWN(term) = False;
5534d522f475Smrg	}
5535d522f475Smrg    }
5536d522f475Smrg    set_tekhide_sensitivity();
5537d522f475Smrg    set_vthide_sensitivity();
5538d522f475Smrg    update_vtshow();
5539d522f475Smrg    update_tekshow();
5540d522f475Smrg    update_vttekmode();
5541d522f475Smrg    return;
5542d522f475Smrg}
5543d522f475Smrg
5544d522f475Smrgvoid
5545d522f475Smrgend_tek_mode(void)
5546d522f475Smrg{
5547cd3331d0Smrg    XtermWidget xw = term;
5548cd3331d0Smrg
5549cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
5550cd3331d0Smrg	FlushLog(xw);
5551d522f475Smrg	longjmp(Tekend, 1);
5552d522f475Smrg    }
5553d522f475Smrg    return;
5554d522f475Smrg}
5555d522f475Smrg
5556d522f475Smrgvoid
5557d522f475Smrgend_vt_mode(void)
5558d522f475Smrg{
5559cd3331d0Smrg    XtermWidget xw = term;
5560cd3331d0Smrg
5561cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
5562cd3331d0Smrg	FlushLog(xw);
5563cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
5564d522f475Smrg	longjmp(VTend, 1);
5565d522f475Smrg    }
5566d522f475Smrg    return;
5567d522f475Smrg}
5568d522f475Smrg
5569d522f475Smrgvoid
5570d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
5571d522f475Smrg{
5572d522f475Smrg    if (tovt) {
5573d522f475Smrg	if (tekRefreshList)
5574d522f475Smrg	    TekRefresh(tekWidget);
5575d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
5576d522f475Smrg    } else {
5577d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
5578d522f475Smrg    }
5579d522f475Smrg}
5580d522f475Smrg
5581d522f475Smrgvoid
5582d522f475Smrghide_vt_window(void)
5583d522f475Smrg{
5584d522f475Smrg    set_vt_visibility(False);
5585d522f475Smrg    if (!TEK4014_ACTIVE(term))
5586d522f475Smrg	switch_modes(False);	/* switch to tek mode */
5587d522f475Smrg}
5588d522f475Smrg
5589d522f475Smrgvoid
5590d522f475Smrghide_tek_window(void)
5591d522f475Smrg{
5592d522f475Smrg    set_tek_visibility(False);
5593d522f475Smrg    tekRefreshList = (TekLink *) 0;
5594d522f475Smrg    if (TEK4014_ACTIVE(term))
5595d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
5596d522f475Smrg}
5597d522f475Smrg#endif /* OPT_TEK4014 */
5598d522f475Smrg
5599d522f475Smrgstatic const char *
5600d522f475Smrgskip_punct(const char *s)
5601d522f475Smrg{
5602d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
5603d522f475Smrg	++s;
5604d522f475Smrg    }
5605d522f475Smrg    return s;
5606d522f475Smrg}
5607d522f475Smrg
5608d522f475Smrgstatic int
5609d522f475Smrgcmp_options(const void *a, const void *b)
5610d522f475Smrg{
5611d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
5612d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
5613d522f475Smrg    return strcmp(s1, s2);
5614d522f475Smrg}
5615d522f475Smrg
5616d522f475Smrgstatic int
5617d522f475Smrgcmp_resources(const void *a, const void *b)
5618d522f475Smrg{
5619d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
5620d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
5621d522f475Smrg}
5622d522f475Smrg
5623d522f475SmrgXrmOptionDescRec *
5624d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
5625d522f475Smrg{
5626d522f475Smrg    static XrmOptionDescRec *res_array = 0;
5627d522f475Smrg
5628d522f475Smrg#ifdef NO_LEAKS
5629cd3331d0Smrg    if (descs == 0) {
5630cd3331d0Smrg	if (res_array != 0) {
5631cd3331d0Smrg	    free(res_array);
5632cd3331d0Smrg	    res_array = 0;
5633cd3331d0Smrg	}
5634d522f475Smrg    } else
5635d522f475Smrg#endif
5636d522f475Smrg    if (res_array == 0) {
5637d522f475Smrg	Cardinal j;
5638d522f475Smrg
5639d522f475Smrg	/* make a sorted index to 'resources' */
5640d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
5641cd3331d0Smrg	if (res_array != 0) {
5642cd3331d0Smrg	    for (j = 0; j < res_count; j++)
5643cd3331d0Smrg		res_array[j] = descs[j];
5644cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
5645cd3331d0Smrg	}
5646d522f475Smrg    }
5647d522f475Smrg    return res_array;
5648d522f475Smrg}
5649d522f475Smrg
5650d522f475Smrg/*
5651d522f475Smrg * The first time this is called, construct sorted index to the main program's
5652d522f475Smrg * list of options, taking into account the on/off options which will be
5653d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
5654d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
5655d522f475Smrg */
5656d522f475SmrgOptionHelp *
5657d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
5658d522f475Smrg{
5659d522f475Smrg    static OptionHelp *opt_array = 0;
5660d522f475Smrg
5661d522f475Smrg#ifdef NO_LEAKS
5662d522f475Smrg    if (descs == 0 && opt_array != 0) {
5663d522f475Smrg	sortedOptDescs(descs, numDescs);
5664d522f475Smrg	free(opt_array);
5665d522f475Smrg	opt_array = 0;
5666d522f475Smrg	return 0;
5667d522f475Smrg    } else if (options == 0 || descs == 0) {
5668d522f475Smrg	return 0;
5669d522f475Smrg    }
5670d522f475Smrg#endif
5671d522f475Smrg
5672d522f475Smrg    if (opt_array == 0) {
5673cd3331d0Smrg	size_t opt_count, j;
5674d522f475Smrg#if OPT_TRACE
5675d522f475Smrg	Cardinal k;
5676d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
5677d522f475Smrg	int code;
5678cd3331d0Smrg	const char *mesg;
5679d522f475Smrg#else
5680d522f475Smrg	(void) descs;
5681d522f475Smrg	(void) numDescs;
5682d522f475Smrg#endif
5683d522f475Smrg
5684d522f475Smrg	/* count 'options' and make a sorted index to it */
5685d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
5686d522f475Smrg	    ;
5687d522f475Smrg	}
5688d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
5689d522f475Smrg	for (j = 0; j < opt_count; j++)
5690d522f475Smrg	    opt_array[j] = options[j];
5691d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
5692d522f475Smrg
5693d522f475Smrg	/* supply the "turn on/off" strings if needed */
5694d522f475Smrg#if OPT_TRACE
5695d522f475Smrg	for (j = 0; j < opt_count; j++) {
5696712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
5697c219fbebSmrg		char temp[80];
5698cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
5699d522f475Smrg		for (k = 0; k < numDescs; ++k) {
5700cd3331d0Smrg		    const char *value = res_array[k].value;
5701d522f475Smrg		    if (res_array[k].option[0] == '-') {
5702d522f475Smrg			code = -1;
5703d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
5704d522f475Smrg			code = 1;
5705d522f475Smrg		    } else {
5706d522f475Smrg			code = 0;
5707d522f475Smrg		    }
57083367019cSmrg		    sprintf(temp, "%.*s",
57093367019cSmrg			    (int) sizeof(temp) - 2,
57103367019cSmrg			    opt_array[j].desc);
5711c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
5712d522f475Smrg			code = -code;
5713d522f475Smrg		    if (code != 0
5714d522f475Smrg			&& res_array[k].value != 0
5715d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
5716d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
5717d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
5718d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
5719d522f475Smrg			    mesg = "turn on/off";
5720d522f475Smrg			} else {
5721d522f475Smrg			    mesg = "turn off/on";
5722d522f475Smrg			}
5723d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
5724712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
5725d522f475Smrg				char *s = CastMallocN(char,
5726d522f475Smrg						      strlen(mesg)
5727d522f475Smrg						      + 1
5728d522f475Smrg						      + strlen(opt_array[j].desc));
5729d522f475Smrg				if (s != 0) {
5730d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
5731d522f475Smrg				    opt_array[j].desc = s;
5732d522f475Smrg				}
5733d522f475Smrg			    } else {
5734d522f475Smrg				TRACE(("OOPS "));
5735d522f475Smrg			    }
5736d522f475Smrg			}
5737d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
5738d522f475Smrg			       mesg,
5739d522f475Smrg			       res_array[k].option,
5740d522f475Smrg			       res_array[k].value,
5741d522f475Smrg			       opt_array[j].opt,
5742d522f475Smrg			       opt_array[j].desc));
5743d522f475Smrg			break;
5744d522f475Smrg		    }
5745d522f475Smrg		}
5746d522f475Smrg	    }
5747d522f475Smrg	}
5748d522f475Smrg#endif
5749d522f475Smrg    }
5750d522f475Smrg    return opt_array;
5751d522f475Smrg}
5752d522f475Smrg
5753d522f475Smrg/*
5754d522f475Smrg * Report the character-type locale that xterm was started in.
5755d522f475Smrg */
57563367019cSmrgString
5757d522f475SmrgxtermEnvLocale(void)
5758d522f475Smrg{
57593367019cSmrg    static String result;
5760d522f475Smrg
5761d522f475Smrg    if (result == 0) {
5762d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
5763cd3331d0Smrg	    result = x_strdup("C");
5764cd3331d0Smrg	} else {
5765cd3331d0Smrg	    result = x_strdup(result);
5766d522f475Smrg	}
5767d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
5768d522f475Smrg    }
5769d522f475Smrg    return result;
5770d522f475Smrg}
5771d522f475Smrg
5772d522f475Smrgchar *
5773d522f475SmrgxtermEnvEncoding(void)
5774d522f475Smrg{
5775d522f475Smrg    static char *result;
5776d522f475Smrg
5777d522f475Smrg    if (result == 0) {
5778d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5779d522f475Smrg	result = nl_langinfo(CODESET);
5780d522f475Smrg#else
5781d522f475Smrg	char *locale = xtermEnvLocale();
5782d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
5783d522f475Smrg	    result = "ASCII";
5784d522f475Smrg	} else {
5785d522f475Smrg	    result = "ISO-8859-1";
5786d522f475Smrg	}
5787d522f475Smrg#endif
5788d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
5789d522f475Smrg    }
5790d522f475Smrg    return result;
5791d522f475Smrg}
5792d522f475Smrg
5793d522f475Smrg#if OPT_WIDE_CHARS
5794d522f475Smrg/*
5795d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
5796d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
5797d522f475Smrg * various library calls.
5798d522f475Smrg */
5799d522f475SmrgBool
5800d522f475SmrgxtermEnvUTF8(void)
5801d522f475Smrg{
5802d522f475Smrg    static Bool init = False;
5803d522f475Smrg    static Bool result = False;
5804d522f475Smrg
5805d522f475Smrg    if (!init) {
5806d522f475Smrg	init = True;
5807d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
5808d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
5809d522f475Smrg#else
5810fa3f02f3Smrg	{
5811fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
5812fa3f02f3Smrg	    int n;
5813fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
5814fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
5815fa3f02f3Smrg	    }
5816fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
5817fa3f02f3Smrg		result = True;
5818fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
5819fa3f02f3Smrg		result = True;
5820fa3f02f3Smrg	    free(locale);
5821fa3f02f3Smrg	}
5822d522f475Smrg#endif
5823d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
5824d522f475Smrg    }
5825d522f475Smrg    return result;
5826d522f475Smrg}
5827d522f475Smrg#endif /* OPT_WIDE_CHARS */
5828d522f475Smrg
5829b7c89284Ssnj/*
5830b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
5831b7c89284Ssnj */
5832b7c89284SsnjXtermWidget
5833b7c89284SsnjgetXtermWidget(Widget w)
5834b7c89284Ssnj{
5835b7c89284Ssnj    XtermWidget xw;
5836b7c89284Ssnj
5837b7c89284Ssnj    if (w == 0) {
5838b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
5839b7c89284Ssnj	if (!IsXtermWidget(xw)) {
5840b7c89284Ssnj	    xw = 0;
5841b7c89284Ssnj	}
5842b7c89284Ssnj    } else if (IsXtermWidget(w)) {
5843b7c89284Ssnj	xw = (XtermWidget) w;
5844b7c89284Ssnj    } else {
5845b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
5846b7c89284Ssnj    }
5847b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
5848b7c89284Ssnj    return xw;
5849b7c89284Ssnj}
58503367019cSmrg
58513367019cSmrg#if OPT_SESSION_MGT
58523367019cSmrgstatic void
58533367019cSmrgdie_callback(Widget w GCC_UNUSED,
58543367019cSmrg	     XtPointer client_data GCC_UNUSED,
58553367019cSmrg	     XtPointer call_data GCC_UNUSED)
58563367019cSmrg{
58573367019cSmrg    NormalExit();
58583367019cSmrg}
58593367019cSmrg
58603367019cSmrgstatic void
58613367019cSmrgsave_callback(Widget w GCC_UNUSED,
58623367019cSmrg	      XtPointer client_data GCC_UNUSED,
58633367019cSmrg	      XtPointer call_data)
58643367019cSmrg{
58653367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
58663367019cSmrg    /* we have nothing to save */
58673367019cSmrg    token->save_success = True;
58683367019cSmrg}
58693367019cSmrg
58703367019cSmrgstatic void
58713367019cSmrgicewatch(IceConn iceConn,
58723367019cSmrg	 IcePointer clientData GCC_UNUSED,
58733367019cSmrg	 Bool opening,
58743367019cSmrg	 IcePointer * watchData GCC_UNUSED)
58753367019cSmrg{
58763367019cSmrg    if (opening) {
58773367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
58783367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
58793367019cSmrg    } else {
58803367019cSmrg	ice_fd = -1;
58813367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
58823367019cSmrg    }
58833367019cSmrg}
58843367019cSmrg
58853367019cSmrgvoid
58863367019cSmrgxtermOpenSession(void)
58873367019cSmrg{
58883367019cSmrg    if (resource.sessionMgt) {
58893367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
58903367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
58913367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
58923367019cSmrg    }
58933367019cSmrg}
58943367019cSmrg
58953367019cSmrgvoid
58963367019cSmrgxtermCloseSession(void)
58973367019cSmrg{
58983367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
58993367019cSmrg}
59003367019cSmrg#endif /* OPT_SESSION_MGT */
59013367019cSmrg
59023367019cSmrgWidget
59033367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
59043367019cSmrg		     String my_class,
59053367019cSmrg		     XrmOptionDescRec * options,
59063367019cSmrg		     Cardinal num_options,
59073367019cSmrg		     int *argc_in_out,
5908fa3f02f3Smrg		     String *argv_in_out,
5909fa3f02f3Smrg		     String *fallback_resources,
59103367019cSmrg		     WidgetClass widget_class,
59113367019cSmrg		     ArgList args,
59123367019cSmrg		     Cardinal num_args)
59133367019cSmrg{
59143367019cSmrg    Widget result;
59153367019cSmrg
59163367019cSmrg    XtSetErrorHandler(xt_error);
59173367019cSmrg#if OPT_SESSION_MGT
59183367019cSmrg    result = XtOpenApplication(app_context_return,
59193367019cSmrg			       my_class,
59203367019cSmrg			       options,
59213367019cSmrg			       num_options,
59223367019cSmrg			       argc_in_out,
59233367019cSmrg			       argv_in_out,
59243367019cSmrg			       fallback_resources,
59253367019cSmrg			       widget_class,
59263367019cSmrg			       args,
59273367019cSmrg			       num_args);
59283367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
59293367019cSmrg#else
59303367019cSmrg    result = XtAppInitialize(app_context_return,
59313367019cSmrg			     my_class,
59323367019cSmrg			     options,
59333367019cSmrg			     num_options,
59343367019cSmrg			     argc_in_out,
59353367019cSmrg			     argv_in_out,
59363367019cSmrg			     fallback_resources,
59373367019cSmrg			     NULL, 0);
59383367019cSmrg#endif /* OPT_SESSION_MGT */
59393367019cSmrg    XtSetErrorHandler((XtErrorHandler) 0);
59403367019cSmrg
59413367019cSmrg    return result;
59423367019cSmrg}
59433367019cSmrg
59443367019cSmrgstatic int x11_errors;
59453367019cSmrg
59463367019cSmrgstatic int
5947fa3f02f3Smrgcatch_x11_error(Display *display, XErrorEvent * error_event)
59483367019cSmrg{
59493367019cSmrg    (void) display;
59503367019cSmrg    (void) error_event;
59513367019cSmrg    ++x11_errors;
59523367019cSmrg    return 0;
59533367019cSmrg}
59543367019cSmrg
59553367019cSmrgBoolean
5956fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
59573367019cSmrg{
59583367019cSmrg    Boolean result = False;
59593367019cSmrg    Status code;
59603367019cSmrg
59613367019cSmrg    memset(attrs, 0, sizeof(*attrs));
59623367019cSmrg    if (win != None) {
59633367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
59643367019cSmrg	x11_errors = 0;
59653367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
59663367019cSmrg	XSetErrorHandler(save);
59673367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
59683367019cSmrg	if (result) {
59693367019cSmrg	    TRACE_WIN_ATTRS(attrs);
59703367019cSmrg	} else {
59713367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
59723367019cSmrg	}
59733367019cSmrg    }
59743367019cSmrg    return result;
59753367019cSmrg}
59763367019cSmrg
59773367019cSmrgBoolean
5978fa3f02f3SmrgxtermGetWinProp(Display *display,
59793367019cSmrg		Window win,
59803367019cSmrg		Atom property,
59813367019cSmrg		long long_offset,
59823367019cSmrg		long long_length,
59833367019cSmrg		Atom req_type,
59843367019cSmrg		Atom * actual_type_return,
59853367019cSmrg		int *actual_format_return,
59863367019cSmrg		unsigned long *nitems_return,
59873367019cSmrg		unsigned long *bytes_after_return,
59883367019cSmrg		unsigned char **prop_return)
59893367019cSmrg{
59903367019cSmrg    Boolean result = True;
59913367019cSmrg
59923367019cSmrg    if (win != None) {
59933367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
59943367019cSmrg	x11_errors = 0;
59953367019cSmrg	if (XGetWindowProperty(display,
59963367019cSmrg			       win,
59973367019cSmrg			       property,
59983367019cSmrg			       long_offset,
59993367019cSmrg			       long_length,
60003367019cSmrg			       False,
60013367019cSmrg			       req_type,
60023367019cSmrg			       actual_type_return,
60033367019cSmrg			       actual_format_return,
60043367019cSmrg			       nitems_return,
60053367019cSmrg			       bytes_after_return,
60063367019cSmrg			       prop_return) == Success
60073367019cSmrg	    && x11_errors == 0) {
60083367019cSmrg	    result = True;
60093367019cSmrg	}
60103367019cSmrg	XSetErrorHandler(save);
60113367019cSmrg    }
60123367019cSmrg    return result;
60133367019cSmrg}
60143367019cSmrg
60153367019cSmrgvoid
60163367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
60173367019cSmrg{
60183367019cSmrg    Display *dpy = XtDisplay(toplevel);
60193367019cSmrg    XWindowAttributes attrs;
60203367019cSmrg
60213367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
60223367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
60233367019cSmrg	XtermWidget xw = term;
60243367019cSmrg	TScreen *screen = TScreenOf(xw);
60253367019cSmrg
60263367019cSmrg	XtRealizeWidget(toplevel);
60273367019cSmrg
60283367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
60293367019cSmrg	       XtWindow(toplevel),
60303367019cSmrg	       winToEmbedInto));
60313367019cSmrg	XReparentWindow(dpy,
60323367019cSmrg			XtWindow(toplevel),
60333367019cSmrg			winToEmbedInto, 0, 0);
60343367019cSmrg
60353367019cSmrg	screen->embed_high = (Dimension) attrs.height;
60363367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
60373367019cSmrg    }
60383367019cSmrg}
6039