misc.c revision 712a7ff4
1712a7ff4Smrg/* $XTermId: misc.c,v 1.530 2011/07/11 00:16:41 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4c219fbebSmrg * Copyright 1999-2010,2011 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>
61d522f475Smrg#include <signal.h>
62d522f475Smrg#include <ctype.h>
63d522f475Smrg#include <pwd.h>
64d522f475Smrg#include <sys/wait.h>
65d522f475Smrg
66d522f475Smrg#include <X11/keysym.h>
67d522f475Smrg#include <X11/Xatom.h>
68d522f475Smrg#include <X11/cursorfont.h>
69d522f475Smrg#include <X11/Xlocale.h>
70d522f475Smrg
71d522f475Smrg#include <X11/Xmu/Error.h>
72d522f475Smrg#include <X11/Xmu/SysUtil.h>
73d522f475Smrg#include <X11/Xmu/WinUtil.h>
74d522f475Smrg#include <X11/Xmu/Xmu.h>
75d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
76d522f475Smrg#include <X11/Sunkeysym.h>
77d522f475Smrg#endif
78d522f475Smrg
79d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
80d522f475Smrg#include <langinfo.h>
81d522f475Smrg#endif
82d522f475Smrg
83d522f475Smrg#include <xutf8.h>
84d522f475Smrg
85d522f475Smrg#include <data.h>
86d522f475Smrg#include <error.h>
87d522f475Smrg#include <menu.h>
88d522f475Smrg#include <fontutils.h>
89d522f475Smrg#include <xcharmouse.h>
90d522f475Smrg#include <xstrings.h>
91d522f475Smrg#include <xtermcap.h>
92d522f475Smrg#include <VTparse.h>
93d522f475Smrg
94d522f475Smrg#include <assert.h>
95d522f475Smrg
96d522f475Smrg#if (XtSpecificationRelease < 6)
97d522f475Smrg#ifndef X_GETTIMEOFDAY
98d522f475Smrg#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0)
99d522f475Smrg#endif
100d522f475Smrg#endif
101d522f475Smrg
102d522f475Smrg#ifdef VMS
103d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
104d522f475Smrg#ifdef ALLOWLOGFILEEXEC
105d522f475Smrg#undef ALLOWLOGFILEEXEC
106d522f475Smrg#endif
107d522f475Smrg#endif /* VMS */
108d522f475Smrg
109d522f475Smrg#if OPT_TEK4014
110d522f475Smrg#define OUR_EVENT(event,Type) \
111d522f475Smrg		(event.type == Type && \
112d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
113d522f475Smrg		    (tekWidget && \
114d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
115d522f475Smrg#else
116d522f475Smrg#define OUR_EVENT(event,Type) \
117d522f475Smrg		(event.type == Type && \
118d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
119d522f475Smrg#endif
120d522f475Smrg
121d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
122d522f475Smrg
123d522f475Smrg#if OPT_EXEC_XTERM
124d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
125d522f475Smrg   error; adapted from libc docs */
126d522f475Smrgstatic char *
127d522f475SmrgReadlink(const char *filename)
128d522f475Smrg{
129d522f475Smrg    char *buf = NULL;
130cd3331d0Smrg    size_t size = 100;
131d522f475Smrg    int n;
132d522f475Smrg
133d522f475Smrg    for (;;) {
134d522f475Smrg	buf = TypeRealloc(char, size, buf);
135d522f475Smrg	memset(buf, 0, size);
136d522f475Smrg
137cd3331d0Smrg	n = (int) readlink(filename, buf, size);
138d522f475Smrg	if (n < 0) {
139d522f475Smrg	    free(buf);
140d522f475Smrg	    return NULL;
141d522f475Smrg	}
142d522f475Smrg
143d522f475Smrg	if ((unsigned) n < size) {
144d522f475Smrg	    return buf;
145d522f475Smrg	}
146d522f475Smrg
147d522f475Smrg	size *= 2;
148d522f475Smrg    }
149d522f475Smrg}
150d522f475Smrg#endif /* OPT_EXEC_XTERM */
151d522f475Smrg
152d522f475Smrgstatic void
153d522f475SmrgSleep(int msec)
154d522f475Smrg{
155d522f475Smrg    static struct timeval select_timeout;
156d522f475Smrg
157d522f475Smrg    select_timeout.tv_sec = 0;
158d522f475Smrg    select_timeout.tv_usec = msec * 1000;
159d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
160d522f475Smrg}
161d522f475Smrg
162d522f475Smrgstatic void
163d522f475Smrgselectwindow(TScreen * screen, int flag)
164d522f475Smrg{
165d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
166d522f475Smrg
167d522f475Smrg#if OPT_TEK4014
168d522f475Smrg    if (TEK4014_ACTIVE(term)) {
169d522f475Smrg	if (!Ttoggled)
170d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
171d522f475Smrg	screen->select |= flag;
172d522f475Smrg	if (!Ttoggled)
173d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
174d522f475Smrg    } else
175d522f475Smrg#endif
176d522f475Smrg    {
177d522f475Smrg	if (screen->xic)
178d522f475Smrg	    XSetICFocus(screen->xic);
179d522f475Smrg
180d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
181d522f475Smrg	    HideCursor();
182d522f475Smrg	screen->select |= flag;
183d522f475Smrg	if (screen->cursor_state)
184d522f475Smrg	    ShowCursor();
185d522f475Smrg    }
186cd3331d0Smrg    GetScrollLock(screen);
187d522f475Smrg}
188d522f475Smrg
189d522f475Smrgstatic void
190d522f475Smrgunselectwindow(TScreen * screen, int flag)
191d522f475Smrg{
192d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
193d522f475Smrg
194d522f475Smrg    if (screen->hide_pointer) {
195d522f475Smrg	screen->hide_pointer = False;
196d522f475Smrg	xtermDisplayCursor(term);
197d522f475Smrg    }
198d522f475Smrg
199d522f475Smrg    if (!screen->always_highlight) {
200d522f475Smrg#if OPT_TEK4014
201d522f475Smrg	if (TEK4014_ACTIVE(term)) {
202d522f475Smrg	    if (!Ttoggled)
203d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
204d522f475Smrg	    screen->select &= ~flag;
205d522f475Smrg	    if (!Ttoggled)
206d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
207d522f475Smrg	} else
208d522f475Smrg#endif
209d522f475Smrg	{
210d522f475Smrg	    if (screen->xic)
211d522f475Smrg		XUnsetICFocus(screen->xic);
212d522f475Smrg
213d522f475Smrg	    screen->select &= ~flag;
214d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
215d522f475Smrg		HideCursor();
216d522f475Smrg	    if (screen->cursor_state)
217d522f475Smrg		ShowCursor();
218d522f475Smrg	}
219d522f475Smrg    }
220d522f475Smrg}
221d522f475Smrg
222d522f475Smrgstatic void
223d522f475SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent * ev)
224d522f475Smrg{
225d522f475Smrg    TScreen *screen = TScreenOf(xw);
226d522f475Smrg
227d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
228cd3331d0Smrg    TRACE_FOCUS(xw, ev);
229cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
230cd3331d0Smrg	ev->focus &&
231cd3331d0Smrg	!(screen->select & FOCUS))
232cd3331d0Smrg	selectwindow(screen, INWINDOW);
233d522f475Smrg}
234d522f475Smrg
235d522f475Smrgstatic void
236d522f475SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent * ev)
237d522f475Smrg{
238d522f475Smrg    TScreen *screen = TScreenOf(xw);
239d522f475Smrg
240d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
241cd3331d0Smrg    TRACE_FOCUS(xw, ev);
242cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
243cd3331d0Smrg	ev->focus &&
244cd3331d0Smrg	!(screen->select & FOCUS))
245cd3331d0Smrg	unselectwindow(screen, INWINDOW);
246d522f475Smrg}
247d522f475Smrg
248d522f475Smrg#ifndef XUrgencyHint
249d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
250d522f475Smrg#endif
251d522f475Smrg
252d522f475Smrgstatic void
253c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
254d522f475Smrg{
255c219fbebSmrg    TScreen *screen = TScreenOf(xw);
256c219fbebSmrg
257d522f475Smrg    if (screen->bellIsUrgent) {
258c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
259d522f475Smrg	if (h != 0) {
260c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
261d522f475Smrg		h->flags |= XUrgencyHint;
262d522f475Smrg	    } else {
263d522f475Smrg		h->flags &= ~XUrgencyHint;
264d522f475Smrg	    }
265c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
266d522f475Smrg	}
267d522f475Smrg    }
268d522f475Smrg}
269d522f475Smrg
270d522f475Smrgvoid
271d522f475Smrgdo_xevents(void)
272d522f475Smrg{
273d522f475Smrg    TScreen *screen = TScreenOf(term);
274d522f475Smrg
275d522f475Smrg    if (XtAppPending(app_con)
276d522f475Smrg	||
277d522f475Smrg#if defined(VMS) || defined(__VMS)
278d522f475Smrg	screen->display->qlen > 0
279d522f475Smrg#else
280d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
281d522f475Smrg#endif
282d522f475Smrg	)
283d522f475Smrg	xevents();
284d522f475Smrg}
285d522f475Smrg
286d522f475Smrgvoid
287d522f475SmrgxtermDisplayCursor(XtermWidget xw)
288d522f475Smrg{
289d522f475Smrg    TScreen *screen = TScreenOf(xw);
290d522f475Smrg
291d522f475Smrg    if (screen->Vshow) {
292d522f475Smrg	if (screen->hide_pointer) {
293d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
294d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
295d522f475Smrg	} else {
296d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
297d522f475Smrg	    recolor_cursor(screen,
298d522f475Smrg			   screen->pointer_cursor,
299d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
300d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
301d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
302d522f475Smrg	}
303d522f475Smrg    }
304d522f475Smrg}
305d522f475Smrg
306d522f475Smrgvoid
307d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
308d522f475Smrg{
309d522f475Smrg    static int tried = -1;
310d522f475Smrg    TScreen *screen = TScreenOf(xw);
311d522f475Smrg
312d522f475Smrg#if OPT_TEK4014
313d522f475Smrg    if (TEK4014_SHOWN(xw))
314d522f475Smrg	enable = True;
315d522f475Smrg#endif
316d522f475Smrg
317d522f475Smrg    /*
318d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
319d522f475Smrg     * the mouse-mode:
320d522f475Smrg     */
321d522f475Smrg    if (!enable) {
322d522f475Smrg	switch (screen->pointer_mode) {
323d522f475Smrg	case pNever:
324d522f475Smrg	    enable = True;
325d522f475Smrg	    break;
326d522f475Smrg	case pNoMouse:
327d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
328d522f475Smrg		enable = True;
329d522f475Smrg	    break;
330d522f475Smrg	case pAlways:
331d522f475Smrg	    break;
332d522f475Smrg	}
333d522f475Smrg    }
334d522f475Smrg
335d522f475Smrg    if (enable) {
336d522f475Smrg	if (screen->hide_pointer) {
337d522f475Smrg	    screen->hide_pointer = False;
338d522f475Smrg	    xtermDisplayCursor(xw);
339d522f475Smrg	    switch (screen->send_mouse_pos) {
340d522f475Smrg	    case ANY_EVENT_MOUSE:
341d522f475Smrg		break;
342d522f475Smrg	    default:
343d522f475Smrg		MotionOff(screen, xw);
344d522f475Smrg		break;
345d522f475Smrg	    }
346d522f475Smrg	}
347d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
348d522f475Smrg	if (screen->hidden_cursor == 0) {
349d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
350d522f475Smrg	}
351d522f475Smrg	if (screen->hidden_cursor == 0) {
352d522f475Smrg	    tried = 1;
353d522f475Smrg	} else {
354d522f475Smrg	    tried = 0;
355d522f475Smrg	    screen->hide_pointer = True;
356d522f475Smrg	    xtermDisplayCursor(xw);
357d522f475Smrg	    MotionOn(screen, xw);
358d522f475Smrg	}
359d522f475Smrg    }
360d522f475Smrg}
361d522f475Smrg
362d522f475Smrgvoid
363d522f475Smrgxevents(void)
364d522f475Smrg{
365d522f475Smrg    XtermWidget xw = term;
366d522f475Smrg    TScreen *screen = TScreenOf(xw);
367d522f475Smrg    XEvent event;
368d522f475Smrg    XtInputMask input_mask;
369d522f475Smrg
370d522f475Smrg    if (need_cleanup)
371d522f475Smrg	Cleanup(0);
372d522f475Smrg
373d522f475Smrg    if (screen->scroll_amt)
374d522f475Smrg	FlushScroll(xw);
375d522f475Smrg    /*
376d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
377d522f475Smrg     * will process the timeout and return without blockng on the
378cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
379d522f475Smrg     * with select().
380d522f475Smrg     */
381cd3331d0Smrg    while ((input_mask = XtAppPending(app_con)) != 0) {
382cd3331d0Smrg	if (input_mask & XtIMTimer)
383cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
384d522f475Smrg#if OPT_SESSION_MGT
385cd3331d0Smrg	/*
386cd3331d0Smrg	 * Session management events are alternative input events. Deal with
387cd3331d0Smrg	 * them in the same way.
388cd3331d0Smrg	 */
389cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
390cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
391d522f475Smrg#endif
392cd3331d0Smrg	else
393cd3331d0Smrg	    break;
394cd3331d0Smrg    }
395d522f475Smrg
396d522f475Smrg    /*
397d522f475Smrg     * If there's no XEvents, don't wait around...
398d522f475Smrg     */
399d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
400d522f475Smrg	return;
401d522f475Smrg    do {
402d522f475Smrg	/*
403d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
404d522f475Smrg	 * We simply ignore all events except for those not passed down to
405d522f475Smrg	 * this function, e.g., those handled in in_put().
406d522f475Smrg	 */
407d522f475Smrg	if (screen->waitingForTrackInfo) {
408d522f475Smrg	    Sleep(10);
409d522f475Smrg	    return;
410d522f475Smrg	}
411d522f475Smrg	XtAppNextEvent(app_con, &event);
412d522f475Smrg	/*
413d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
414d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
415d522f475Smrg	 * looking at the event ourselves we make sure that we can
416d522f475Smrg	 * do the right thing.
417d522f475Smrg	 */
418d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
419d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
420d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
421d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
422d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
423d522f475Smrg#if OPT_DEC_LOCATOR
424d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
425d522f475Smrg#endif /* OPT_DEC_LOCATOR */
426d522f475Smrg		   )
427d522f475Smrg		   && event.xany.type == MotionNotify
428d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
429d522f475Smrg	    SendMousePosition(xw, &event);
430cb4a1343Smrg	    xtermShowPointer(xw, True);
431d522f475Smrg	    continue;
432d522f475Smrg	}
433d522f475Smrg
434cb4a1343Smrg	/*
435cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
436cb4a1343Smrg	 * mouse pointer back on.
437cb4a1343Smrg	 */
438cb4a1343Smrg	if (screen->hide_pointer) {
439cb4a1343Smrg	    switch (event.xany.type) {
440cb4a1343Smrg	    case KeyPress:
441cb4a1343Smrg	    case KeyRelease:
442cb4a1343Smrg	    case ButtonPress:
443cb4a1343Smrg	    case ButtonRelease:
444cb4a1343Smrg		/* also these... */
445cb4a1343Smrg	    case Expose:
446cb4a1343Smrg	    case NoExpose:
447cb4a1343Smrg	    case PropertyNotify:
448cb4a1343Smrg	    case ClientMessage:
449cb4a1343Smrg		break;
450cb4a1343Smrg	    default:
451cb4a1343Smrg		xtermShowPointer(xw, True);
452cb4a1343Smrg		break;
453cb4a1343Smrg	    }
454cb4a1343Smrg	}
455cb4a1343Smrg
456d522f475Smrg	if (!event.xany.send_event ||
457d522f475Smrg	    screen->allowSendEvents ||
458d522f475Smrg	    ((event.xany.type != KeyPress) &&
459d522f475Smrg	     (event.xany.type != KeyRelease) &&
460d522f475Smrg	     (event.xany.type != ButtonPress) &&
461d522f475Smrg	     (event.xany.type != ButtonRelease))) {
462d522f475Smrg
463d522f475Smrg	    XtDispatchEvent(&event);
464d522f475Smrg	}
465cd3331d0Smrg    } while (XtAppPending(app_con) & XtIMXEvent);
466d522f475Smrg}
467d522f475Smrg
468d522f475Smrgstatic Cursor
469d522f475Smrgmake_hidden_cursor(XtermWidget xw)
470d522f475Smrg{
471d522f475Smrg    TScreen *screen = TScreenOf(xw);
472d522f475Smrg    Cursor c;
473d522f475Smrg    Display *dpy = screen->display;
474d522f475Smrg    XFontStruct *fn;
475d522f475Smrg
476d522f475Smrg    static XColor dummy;
477d522f475Smrg
478d522f475Smrg    /*
479d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
480d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
481b7c89284Ssnj     * server insists on drawing _something_.
482d522f475Smrg     */
483d522f475Smrg    TRACE(("Ask for nil2 font\n"));
484d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
485d522f475Smrg	TRACE(("...Ask for fixed font\n"));
486b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
487d522f475Smrg    }
488d522f475Smrg
489d522f475Smrg    if (fn != 0) {
490d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
491d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
492d522f475Smrg	XFreeFont(dpy, fn);
493d522f475Smrg    } else {
494d522f475Smrg	c = 0;
495d522f475Smrg    }
496d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
497d522f475Smrg    return (c);
498d522f475Smrg}
499d522f475Smrg
500d522f475SmrgCursor
501d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
502d522f475Smrg		    unsigned long fg,	/* pixel value */
503d522f475Smrg		    unsigned long bg)	/* pixel value */
504d522f475Smrg{
505d522f475Smrg    TScreen *screen = TScreenOf(term);
506d522f475Smrg    Cursor c;
507d522f475Smrg    Display *dpy = screen->display;
508d522f475Smrg
509d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
510d522f475Smrg    if (c != None) {
511d522f475Smrg	recolor_cursor(screen, c, fg, bg);
512d522f475Smrg    }
513d522f475Smrg    return (c);
514d522f475Smrg}
515d522f475Smrg
516d522f475Smrg/* ARGSUSED */
517d522f475Smrgvoid
518d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
519d522f475Smrg		 XEvent * event,
520d522f475Smrg		 String * params GCC_UNUSED,
521d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
522d522f475Smrg{
523cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
524cd3331d0Smrg    Input(term, &event->xkey, False);
525d522f475Smrg}
526d522f475Smrg
527d522f475Smrg/* ARGSUSED */
528d522f475Smrgvoid
529d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
530d522f475Smrg			 XEvent * event,
531d522f475Smrg			 String * params GCC_UNUSED,
532d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
533d522f475Smrg{
534cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
535cd3331d0Smrg    Input(term, &event->xkey, True);
536d522f475Smrg}
537d522f475Smrg
538d522f475Smrg/* ARGSUSED */
539d522f475Smrgvoid
540d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
541d522f475Smrg		  XEvent * event GCC_UNUSED,
542d522f475Smrg		  String * params,
543d522f475Smrg		  Cardinal *nparams)
544d522f475Smrg{
545d522f475Smrg
546d522f475Smrg    if (*nparams != 1)
547d522f475Smrg	return;
548d522f475Smrg
549d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
5500d92cbfdSchristos	const char *abcdef = "ABCDEF";
5510d92cbfdSchristos	const char *xxxxxx;
552cd3331d0Smrg	Char c;
553cd3331d0Smrg	UString p;
5540d92cbfdSchristos	unsigned value = 0;
5550d92cbfdSchristos
556cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
5570d92cbfdSchristos	     '\0'; p++) {
5580d92cbfdSchristos	    value *= 16;
559d522f475Smrg	    if (c >= '0' && c <= '9')
5600d92cbfdSchristos		value += (unsigned) (c - '0');
5610d92cbfdSchristos	    else if ((xxxxxx = strchr(abcdef, c)) != 0)
5620d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
563d522f475Smrg	    else
564d522f475Smrg		break;
565d522f475Smrg	}
5660d92cbfdSchristos	if (c == '\0') {
5670d92cbfdSchristos	    Char hexval[2];
5680d92cbfdSchristos	    hexval[0] = (Char) value;
5690d92cbfdSchristos	    hexval[1] = 0;
570b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
5710d92cbfdSchristos	}
572d522f475Smrg    } else {
573cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
574d522f475Smrg    }
575d522f475Smrg}
576d522f475Smrg
577d522f475Smrg#if OPT_EXEC_XTERM
578d522f475Smrg
579d522f475Smrg#ifndef PROCFS_ROOT
580d522f475Smrg#define PROCFS_ROOT "/proc"
581d522f475Smrg#endif
582d522f475Smrg
583d522f475Smrg/* ARGSUSED */
584d522f475Smrgvoid
585d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
586d522f475Smrg		    XEvent * event GCC_UNUSED,
587d522f475Smrg		    String * params,
588d522f475Smrg		    Cardinal *nparams)
589d522f475Smrg{
590cd3331d0Smrg    TScreen *screen = TScreenOf(term);
591d522f475Smrg    char *child_cwd = NULL;
592d522f475Smrg    char *child_exe;
593d522f475Smrg    pid_t pid;
594d522f475Smrg
595d522f475Smrg    /*
596d522f475Smrg     * Try to find the actual program which is running in the child process.
597d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
598d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
599d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
600d522f475Smrg     */
601d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
602d522f475Smrg    if (!child_exe) {
603cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
604cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
605d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
606d522f475Smrg	} else {
607d522f475Smrg	    fprintf(stderr, "Cannot exec-xterm given %s\n", ProgramName);
608d522f475Smrg	}
609d522f475Smrg	if (child_exe == 0)
610d522f475Smrg	    return;
611d522f475Smrg    }
612d522f475Smrg
613d522f475Smrg    /*
614d522f475Smrg     * Determine the current working directory of the child so that we can
615d522f475Smrg     * spawn a new terminal in the same directory.
616d522f475Smrg     *
617d522f475Smrg     * If we cannot get the CWD of the child, just use our own.
618d522f475Smrg     */
619d522f475Smrg    if (screen->pid) {
620d522f475Smrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
621d522f475Smrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid);
622d522f475Smrg	child_cwd = Readlink(child_cwd_link);
623d522f475Smrg    }
624d522f475Smrg
625d522f475Smrg    /* The reaper will take care of cleaning up the child */
626d522f475Smrg    pid = fork();
627d522f475Smrg    if (pid == -1) {
628d522f475Smrg	fprintf(stderr, "Could not fork: %s\n", SysErrorMsg(errno));
629d522f475Smrg    } else if (!pid) {
630d522f475Smrg	/* We are the child */
631d522f475Smrg	if (child_cwd) {
632cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
633d522f475Smrg	}
634d522f475Smrg
635d522f475Smrg	if (setuid(screen->uid) == -1
636d522f475Smrg	    || setgid(screen->gid) == -1) {
637d522f475Smrg	    fprintf(stderr, "Cannot reset uid/gid\n");
638d522f475Smrg	} else {
6390d92cbfdSchristos	    unsigned myargc = *nparams + 1;
640d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
6410d92cbfdSchristos	    unsigned n = 0;
642d522f475Smrg
643d522f475Smrg	    myargv[n++] = child_exe;
644d522f475Smrg
645d522f475Smrg	    while (n < myargc) {
646d522f475Smrg		myargv[n++] = *params++;
647d522f475Smrg	    }
648d522f475Smrg
649d522f475Smrg	    myargv[n] = 0;
650d522f475Smrg	    execv(child_exe, myargv);
651d522f475Smrg
652d522f475Smrg	    /* If we get here, we've failed */
653d522f475Smrg	    fprintf(stderr, "exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
654d522f475Smrg	}
655d522f475Smrg	_exit(0);
656d522f475Smrg    } else {
657d522f475Smrg	/* We are the parent; clean up */
658d522f475Smrg	if (child_cwd)
659d522f475Smrg	    free(child_cwd);
660d522f475Smrg	if (child_exe)
661d522f475Smrg	    free(child_exe);
662d522f475Smrg    }
663d522f475Smrg}
664d522f475Smrg#endif /* OPT_EXEC_XTERM */
665d522f475Smrg
666d522f475Smrg/*
667d522f475Smrg * Rather than sending characters to the host, put them directly into our
668d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
669d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
670d522f475Smrg *
671d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
672d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
673d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
674d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
675d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
676d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
677d522f475Smrg */
678d522f475Smrg/* ARGSUSED */
679d522f475Smrgvoid
680d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
681d522f475Smrg		XEvent * event GCC_UNUSED,
682d522f475Smrg		String * params,
683d522f475Smrg		Cardinal *param_count)
684d522f475Smrg{
685d522f475Smrg    if (*param_count == 1) {
686cd3331d0Smrg	const char *value = params[0];
687b7c89284Ssnj	int need = (int) strlen(value);
688cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
689cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
690d522f475Smrg
691d522f475Smrg	if (have - used + need < BUF_SIZE) {
692d522f475Smrg
693cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
694d522f475Smrg
695d522f475Smrg	    TRACE(("Interpret %s\n", value));
696d522f475Smrg	    VTbuffer->update++;
697d522f475Smrg	}
698d522f475Smrg    }
699d522f475Smrg}
700d522f475Smrg
701d522f475Smrg/*ARGSUSED*/
702d522f475Smrgvoid
703d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
704d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
705d522f475Smrg		  XEvent * event GCC_UNUSED,
706d522f475Smrg		  Boolean * cont GCC_UNUSED)
707d522f475Smrg{
708d522f475Smrg    /* NOP since we handled it above */
709d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
710cd3331d0Smrg    TRACE_FOCUS(w, event);
711d522f475Smrg}
712d522f475Smrg
713d522f475Smrg/*ARGSUSED*/
714d522f475Smrgvoid
715d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
716d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
717d522f475Smrg		  XEvent * event GCC_UNUSED,
718d522f475Smrg		  Boolean * cont GCC_UNUSED)
719d522f475Smrg{
720d522f475Smrg    /* NOP since we handled it above */
721d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
722cd3331d0Smrg    TRACE_FOCUS(w, event);
723d522f475Smrg}
724d522f475Smrg
725d522f475Smrg/*ARGSUSED*/
726d522f475Smrgvoid
727d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
728d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
729d522f475Smrg		  XEvent * ev,
730d522f475Smrg		  Boolean * cont GCC_UNUSED)
731d522f475Smrg{
732d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
733d522f475Smrg    XtermWidget xw = term;
734d522f475Smrg    TScreen *screen = TScreenOf(xw);
735d522f475Smrg
736d522f475Smrg    TRACE(("HandleFocusChange type=%s, mode=%d, detail=%d\n",
737d522f475Smrg	   visibleEventType(event->type),
738d522f475Smrg	   event->mode,
739d522f475Smrg	   event->detail));
740cd3331d0Smrg    TRACE_FOCUS(xw, event);
741d522f475Smrg
742d522f475Smrg    if (screen->quiet_grab
743d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
744c219fbebSmrg	/* EMPTY */ ;
745d522f475Smrg    } else if (event->type == FocusIn) {
746c219fbebSmrg	setXUrgency(xw, False);
747d522f475Smrg
748d522f475Smrg	/*
749d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
750d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
751d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
752d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
753d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
754d522f475Smrg	 * delivered to the obscured window.
755d522f475Smrg	 */
756d522f475Smrg	if (event->detail == NotifyNonlinear
757d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
758d522f475Smrg	    unselectwindow(screen, INWINDOW);
759d522f475Smrg	}
760d522f475Smrg	selectwindow(screen,
761d522f475Smrg		     ((event->detail == NotifyPointer)
762d522f475Smrg		      ? INWINDOW
763d522f475Smrg		      : FOCUS));
764d522f475Smrg	SendFocusButton(xw, event);
765d522f475Smrg    } else {
766d522f475Smrg#if OPT_FOCUS_EVENT
767d522f475Smrg	if (event->type == FocusOut) {
768d522f475Smrg	    SendFocusButton(xw, event);
769d522f475Smrg	}
770d522f475Smrg#endif
771d522f475Smrg	/*
772d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
773d522f475Smrg	 * ignore.
774d522f475Smrg	 */
775d522f475Smrg	if (event->mode != NotifyGrab) {
776d522f475Smrg	    unselectwindow(screen,
777d522f475Smrg			   ((event->detail == NotifyPointer)
778d522f475Smrg			    ? INWINDOW
779d522f475Smrg			    : FOCUS));
780d522f475Smrg	}
781d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
782cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
783d522f475Smrg	    ReverseVideo(xw);
784d522f475Smrg	    screen->grabbedKbd = False;
785d522f475Smrg	    update_securekbd();
786d522f475Smrg	}
787d522f475Smrg    }
788d522f475Smrg}
789d522f475Smrg
790d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
791d522f475Smrg
792b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
793b7c89284Ssnjstatic Atom
794b7c89284SsnjAtomBell(XtermWidget xw, int which)
795b7c89284Ssnj{
796b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
797b7c89284Ssnj    static struct {
798b7c89284Ssnj	int value;
799b7c89284Ssnj	const char *name;
800b7c89284Ssnj    } table[] = {
801b7c89284Ssnj	DATA(Info),
802b7c89284Ssnj	    DATA(MarginBell),
803b7c89284Ssnj	    DATA(MinorError),
804b7c89284Ssnj	    DATA(TerminalBell)
805b7c89284Ssnj    };
806b7c89284Ssnj    Cardinal n;
807b7c89284Ssnj    Atom result = None;
808b7c89284Ssnj
809b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
810b7c89284Ssnj	if (table[n].value == which) {
811cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
812b7c89284Ssnj	    break;
813b7c89284Ssnj	}
814b7c89284Ssnj    }
815b7c89284Ssnj    return result;
816b7c89284Ssnj}
817b7c89284Ssnj#endif
818b7c89284Ssnj
819d522f475Smrgvoid
820b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
821d522f475Smrg{
822b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
823b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
824b7c89284Ssnj    Atom tony = AtomBell(xw, which);
825cd3331d0Smrg#endif
826cd3331d0Smrg
827cd3331d0Smrg    switch (which) {
828cd3331d0Smrg    case XkbBI_Info:
829cd3331d0Smrg    case XkbBI_MinorError:
830cd3331d0Smrg    case XkbBI_MajorError:
831cd3331d0Smrg    case XkbBI_TerminalBell:
832cd3331d0Smrg	switch (screen->warningVolume) {
833cd3331d0Smrg	case bvOff:
834cd3331d0Smrg	    percent = -100;
835cd3331d0Smrg	    break;
836cd3331d0Smrg	case bvLow:
837cd3331d0Smrg	    break;
838cd3331d0Smrg	case bvHigh:
839cd3331d0Smrg	    percent = 100;
840cd3331d0Smrg	    break;
841cd3331d0Smrg	}
842cd3331d0Smrg	break;
843cd3331d0Smrg    case XkbBI_MarginBell:
844cd3331d0Smrg	switch (screen->marginVolume) {
845cd3331d0Smrg	case bvOff:
846cd3331d0Smrg	    percent = -100;
847cd3331d0Smrg	    break;
848cd3331d0Smrg	case bvLow:
849cd3331d0Smrg	    break;
850cd3331d0Smrg	case bvHigh:
851cd3331d0Smrg	    percent = 100;
852cd3331d0Smrg	    break;
853cd3331d0Smrg	}
854cd3331d0Smrg	break;
855cd3331d0Smrg    default:
856cd3331d0Smrg	break;
857cd3331d0Smrg    }
858cd3331d0Smrg
859cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
860b7c89284Ssnj    if (tony != None) {
861c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
862b7c89284Ssnj    } else
863b7c89284Ssnj#endif
864b7c89284Ssnj	XBell(screen->display, percent);
865b7c89284Ssnj}
866b7c89284Ssnj
867b7c89284Ssnjvoid
868cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
869b7c89284Ssnj{
870b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
871d522f475Smrg    struct timeval curtime;
872d522f475Smrg    long now_msecs;
873d522f475Smrg
874b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
875b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
876d522f475Smrg	return;
877d522f475Smrg    }
878d522f475Smrg
879c219fbebSmrg    setXUrgency(xw, True);
880d522f475Smrg
881d522f475Smrg    /* has enough time gone by that we are allowed to ring
882d522f475Smrg       the bell again? */
883d522f475Smrg    if (screen->bellSuppressTime) {
884d522f475Smrg	if (screen->bellInProgress) {
885d522f475Smrg	    do_xevents();
886d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
887d522f475Smrg		return;
888d522f475Smrg	    }
889d522f475Smrg	}
890d522f475Smrg	X_GETTIMEOFDAY(&curtime);
891d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
892d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
893d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
894d522f475Smrg	    return;
895d522f475Smrg	}
896d522f475Smrg	lastBellTime = now_msecs;
897d522f475Smrg    }
898d522f475Smrg
899d522f475Smrg    if (screen->visualbell) {
900d522f475Smrg	VisualBell();
901d522f475Smrg    } else {
902b7c89284Ssnj	xtermBell(xw, which, percent);
903d522f475Smrg    }
904d522f475Smrg
905d522f475Smrg    if (screen->poponbell)
906c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
907d522f475Smrg
908d522f475Smrg    if (screen->bellSuppressTime) {
909d522f475Smrg	/* now we change a property and wait for the notify event to come
910d522f475Smrg	   back.  If the server is suspending operations while the bell
911d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
912d522f475Smrg	   know when the previous bell has finished */
913d522f475Smrg	Widget w = CURRENT_EMU();
914d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
915d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
916d522f475Smrg	screen->bellInProgress = True;
917d522f475Smrg    }
918d522f475Smrg}
919d522f475Smrg
920d522f475Smrg#define VB_DELAY screen->visualBellDelay
921d522f475Smrg
922d522f475Smrgstatic void
923d522f475SmrgflashWindow(TScreen * screen, Window window, GC visualGC, unsigned width, unsigned height)
924d522f475Smrg{
925d522f475Smrg    XFillRectangle(screen->display, window, visualGC, 0, 0, width, height);
926d522f475Smrg    XFlush(screen->display);
927d522f475Smrg    Sleep(VB_DELAY);
928d522f475Smrg    XFillRectangle(screen->display, window, visualGC, 0, 0, width, height);
929d522f475Smrg}
930d522f475Smrg
931d522f475Smrgvoid
932d522f475SmrgVisualBell(void)
933d522f475Smrg{
934d522f475Smrg    TScreen *screen = TScreenOf(term);
935d522f475Smrg
936d522f475Smrg    if (VB_DELAY > 0) {
937d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
938d522f475Smrg			  T_COLOR(screen, TEXT_BG));
939d522f475Smrg	XGCValues gcval;
940d522f475Smrg	GC visualGC;
941d522f475Smrg
942d522f475Smrg	gcval.function = GXxor;
943d522f475Smrg	gcval.foreground = xorPixel;
944d522f475Smrg	visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval);
945d522f475Smrg#if OPT_TEK4014
946d522f475Smrg	if (TEK4014_ACTIVE(term)) {
947cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
948d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
949d522f475Smrg			TFullWidth(tekscr),
950d522f475Smrg			TFullHeight(tekscr));
951d522f475Smrg	} else
952d522f475Smrg#endif
953d522f475Smrg	{
954d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
955d522f475Smrg			FullWidth(screen),
956d522f475Smrg			FullHeight(screen));
957d522f475Smrg	}
958d522f475Smrg	XtReleaseGC((Widget) term, visualGC);
959d522f475Smrg    }
960d522f475Smrg}
961d522f475Smrg
962d522f475Smrg/* ARGSUSED */
963d522f475Smrgvoid
964d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
965d522f475Smrg			 XtPointer data GCC_UNUSED,
966d522f475Smrg			 XEvent * ev,
967d522f475Smrg			 Boolean * more GCC_UNUSED)
968d522f475Smrg{
969d522f475Smrg    TScreen *screen = TScreenOf(term);
970d522f475Smrg
971d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
972d522f475Smrg	screen->bellInProgress = False;
973d522f475Smrg    }
974d522f475Smrg}
975d522f475Smrg
976d522f475SmrgWindow
977c219fbebSmrgWMFrameWindow(XtermWidget xw)
978d522f475Smrg{
979d522f475Smrg    Window win_root, win_current, *children;
980d522f475Smrg    Window win_parent = 0;
981d522f475Smrg    unsigned int nchildren;
982d522f475Smrg
983c219fbebSmrg    win_current = XtWindow(xw);
984d522f475Smrg
985d522f475Smrg    /* find the parent which is child of root */
986d522f475Smrg    do {
987d522f475Smrg	if (win_parent)
988d522f475Smrg	    win_current = win_parent;
989c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
990d522f475Smrg		   win_current,
991d522f475Smrg		   &win_root,
992d522f475Smrg		   &win_parent,
993d522f475Smrg		   &children,
994d522f475Smrg		   &nchildren);
995d522f475Smrg	XFree(children);
996d522f475Smrg    } while (win_root != win_parent);
997d522f475Smrg
998d522f475Smrg    return win_current;
999d522f475Smrg}
1000d522f475Smrg
1001d522f475Smrg#if OPT_DABBREV
1002d522f475Smrg/*
1003d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1004d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1005d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1006d522f475Smrg * expansions and ignores one of them if they are identical.
1007d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1008d522f475Smrg */
1009d522f475Smrg
1010d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1011d522f475Smrg#define MAXWLEN 1024		/* maximum word length as in tcsh */
1012d522f475Smrg
1013d522f475Smrgstatic int
1014b7c89284Ssnjdabbrev_prev_char(TScreen * screen, CELL * cell, LineData ** ld)
1015d522f475Smrg{
1016b7c89284Ssnj    int result = -1;
1017b7c89284Ssnj    int firstLine = -(screen->savedlines);
1018d522f475Smrg
1019b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1020b7c89284Ssnj    while (cell->row >= firstLine) {
1021b7c89284Ssnj	if (--(cell->col) >= 0) {
1022b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1023b7c89284Ssnj	    break;
1024b7c89284Ssnj	}
1025b7c89284Ssnj	if (--(cell->row) < firstLine)
1026b7c89284Ssnj	    break;		/* ...there is no previous line */
1027b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1028b7c89284Ssnj	cell->col = MaxCols(screen);
1029b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1030b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1031d522f475Smrg	    break;
1032b7c89284Ssnj	}
1033d522f475Smrg    }
1034b7c89284Ssnj    return result;
1035d522f475Smrg}
1036d522f475Smrg
1037d522f475Smrgstatic char *
1038b7c89284Ssnjdabbrev_prev_word(TScreen * screen, CELL * cell, LineData ** ld)
1039d522f475Smrg{
1040d522f475Smrg    static char ab[MAXWLEN];
1041b7c89284Ssnj
1042d522f475Smrg    char *abword;
1043d522f475Smrg    int c;
1044b7c89284Ssnj    char *ab_end = (ab + MAXWLEN - 1);
1045b7c89284Ssnj    char *result = 0;
1046d522f475Smrg
1047b7c89284Ssnj    abword = ab_end;
1048d522f475Smrg    *abword = '\0';		/* end of string marker */
1049d522f475Smrg
1050b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1051b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
1052d522f475Smrg	if (abword > ab)	/* store only |MAXWLEN| last chars */
1053b7c89284Ssnj	    *(--abword) = (char) c;
1054d522f475Smrg    }
1055d522f475Smrg
1056b7c89284Ssnj    if (c >= 0) {
1057b7c89284Ssnj	result = abword;
1058b7c89284Ssnj    } else if (abword != ab_end) {
1059b7c89284Ssnj	result = abword;
1060b7c89284Ssnj    }
1061b7c89284Ssnj
1062b7c89284Ssnj    if (result != 0) {
1063b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1064b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1065b7c89284Ssnj	    ;			/* skip preceding spaces */
1066b7c89284Ssnj	}
1067b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1068b7c89284Ssnj    }
1069b7c89284Ssnj    return result;
1070d522f475Smrg}
1071d522f475Smrg
1072d522f475Smrgstatic int
1073d522f475Smrgdabbrev_expand(TScreen * screen)
1074d522f475Smrg{
1075d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1076d522f475Smrg
1077b7c89284Ssnj    static CELL cell;
1078d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1079d522f475Smrg    static unsigned int expansions;
1080d522f475Smrg
1081d522f475Smrg    char *expansion;
1082d522f475Smrg    Char *copybuffer;
1083d522f475Smrg    size_t hint_len;
1084cd3331d0Smrg    size_t del_cnt;
1085cd3331d0Smrg    size_t buf_cnt;
1086b7c89284Ssnj    int result = 0;
1087b7c89284Ssnj    LineData *ld;
1088d522f475Smrg
1089d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1090d522f475Smrg	expansions = 0;
1091b7c89284Ssnj	cell.col = screen->cur_col;
1092b7c89284Ssnj	cell.row = screen->cur_row;
1093b7c89284Ssnj
1094b7c89284Ssnj	if (dabbrev_hint != 0)
1095b7c89284Ssnj	    free(dabbrev_hint);
1096b7c89284Ssnj
1097b7c89284Ssnj	if ((dabbrev_hint = dabbrev_prev_word(screen, &cell, &ld)) != 0) {
1098b7c89284Ssnj
1099b7c89284Ssnj	    if (lastexpansion != 0)
1100b7c89284Ssnj		free(lastexpansion);
1101b7c89284Ssnj
1102b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1103b7c89284Ssnj
1104b7c89284Ssnj		/* make own copy */
1105b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1106b7c89284Ssnj		    screen->dabbrev_working = True;
1107b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1108b7c89284Ssnj		}
1109cd3331d0Smrg	    } else {
1110cd3331d0Smrg		return result;
1111b7c89284Ssnj	    }
1112cd3331d0Smrg	} else {
1113cd3331d0Smrg	    return result;
1114d522f475Smrg	}
1115b7c89284Ssnj	if (!screen->dabbrev_working) {
1116b7c89284Ssnj	    if (lastexpansion != 0) {
1117b7c89284Ssnj		free(lastexpansion);
1118b7c89284Ssnj		lastexpansion = 0;
1119b7c89284Ssnj	    }
1120b7c89284Ssnj	    return result;
1121b7c89284Ssnj	}
1122d522f475Smrg    }
1123d522f475Smrg
1124cd3331d0Smrg    if (dabbrev_hint == 0)
1125cd3331d0Smrg	return result;
1126cd3331d0Smrg
1127d522f475Smrg    hint_len = strlen(dabbrev_hint);
1128d522f475Smrg    for (;;) {
1129b7c89284Ssnj	if ((expansion = dabbrev_prev_word(screen, &cell, &ld)) == 0) {
1130d522f475Smrg	    if (expansions >= 2) {
1131d522f475Smrg		expansions = 0;
1132b7c89284Ssnj		cell.col = screen->cur_col;
1133b7c89284Ssnj		cell.row = screen->cur_row;
1134d522f475Smrg		continue;
1135d522f475Smrg	    }
1136d522f475Smrg	    break;
1137d522f475Smrg	}
1138d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1139d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1140d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1141d522f475Smrg	    break;
1142d522f475Smrg    }
1143d522f475Smrg
1144b7c89284Ssnj    if (expansion != 0) {
1145b7c89284Ssnj	del_cnt = strlen(lastexpansion) - hint_len;
1146b7c89284Ssnj	buf_cnt = del_cnt + strlen(expansion) - hint_len;
1147b7c89284Ssnj
1148b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1149b7c89284Ssnj	    /* delete previous expansion */
1150b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1151b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1152b7c89284Ssnj		    expansion + hint_len,
1153b7c89284Ssnj		    strlen(expansion) - hint_len);
1154cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1155b7c89284Ssnj	    /* v_write() just reset our flag */
1156b7c89284Ssnj	    screen->dabbrev_working = True;
1157b7c89284Ssnj	    free(copybuffer);
1158b7c89284Ssnj
1159b7c89284Ssnj	    free(lastexpansion);
1160b7c89284Ssnj
1161b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1162b7c89284Ssnj		result = 1;
1163b7c89284Ssnj		expansions++;
1164b7c89284Ssnj	    }
1165b7c89284Ssnj	}
1166b7c89284Ssnj    }
1167b7c89284Ssnj
1168b7c89284Ssnj    return result;
1169d522f475Smrg}
1170d522f475Smrg
1171d522f475Smrg/*ARGSUSED*/
1172d522f475Smrgvoid
1173b7c89284SsnjHandleDabbrevExpand(Widget w,
1174d522f475Smrg		    XEvent * event GCC_UNUSED,
1175d522f475Smrg		    String * params GCC_UNUSED,
1176d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1177d522f475Smrg{
1178b7c89284Ssnj    XtermWidget xw;
1179b7c89284Ssnj
1180cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1181b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1182cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
1183d522f475Smrg	if (!dabbrev_expand(screen))
1184cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1185d522f475Smrg    }
1186d522f475Smrg}
1187d522f475Smrg#endif /* OPT_DABBREV */
1188d522f475Smrg
1189d522f475Smrg#if OPT_MAXIMIZE
1190d522f475Smrg/*ARGSUSED*/
1191d522f475Smrgvoid
1192b7c89284SsnjHandleDeIconify(Widget w,
1193d522f475Smrg		XEvent * event GCC_UNUSED,
1194d522f475Smrg		String * params GCC_UNUSED,
1195d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1196d522f475Smrg{
1197b7c89284Ssnj    XtermWidget xw;
1198b7c89284Ssnj
1199b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1200b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1201c219fbebSmrg	XMapWindow(screen->display, VShellWindow(xw));
1202d522f475Smrg    }
1203d522f475Smrg}
1204d522f475Smrg
1205d522f475Smrg/*ARGSUSED*/
1206d522f475Smrgvoid
1207b7c89284SsnjHandleIconify(Widget w,
1208d522f475Smrg	      XEvent * event GCC_UNUSED,
1209d522f475Smrg	      String * params GCC_UNUSED,
1210d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1211d522f475Smrg{
1212b7c89284Ssnj    XtermWidget xw;
1213b7c89284Ssnj
1214b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1215b7c89284Ssnj	TScreen *screen = TScreenOf(xw);
1216d522f475Smrg	XIconifyWindow(screen->display,
1217c219fbebSmrg		       VShellWindow(xw),
1218d522f475Smrg		       DefaultScreen(screen->display));
1219d522f475Smrg    }
1220d522f475Smrg}
1221d522f475Smrg
1222d522f475Smrgint
1223c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1224d522f475Smrg{
1225c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1226d522f475Smrg    XSizeHints hints;
1227d522f475Smrg    long supp = 0;
1228d522f475Smrg    Window root_win;
1229d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1230d522f475Smrg    int root_y = -1;
1231d522f475Smrg    unsigned root_border;
1232d522f475Smrg    unsigned root_depth;
1233d522f475Smrg
1234d522f475Smrg    if (XGetGeometry(screen->display,
1235c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1236d522f475Smrg		     &root_win,
1237d522f475Smrg		     &root_x,
1238d522f475Smrg		     &root_y,
1239d522f475Smrg		     width,
1240d522f475Smrg		     height,
1241d522f475Smrg		     &root_border,
1242d522f475Smrg		     &root_depth)) {
1243d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1244d522f475Smrg	       root_x,
1245d522f475Smrg	       root_y,
1246d522f475Smrg	       *width,
1247d522f475Smrg	       *height,
1248d522f475Smrg	       root_border));
1249d522f475Smrg
1250d522f475Smrg	*width -= (root_border * 2);
1251d522f475Smrg	*height -= (root_border * 2);
1252d522f475Smrg
1253d522f475Smrg	hints.flags = PMaxSize;
1254d522f475Smrg	if (XGetWMNormalHints(screen->display,
1255c219fbebSmrg			      VShellWindow(xw),
1256d522f475Smrg			      &hints,
1257d522f475Smrg			      &supp)
1258d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1259d522f475Smrg
1260d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1261d522f475Smrg		   hints.max_width,
1262d522f475Smrg		   hints.max_height));
1263d522f475Smrg
1264d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1265b7c89284Ssnj		*width = (unsigned) hints.max_width;
1266d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1267b7c89284Ssnj		*height = (unsigned) hints.max_height;
1268d522f475Smrg	}
1269d522f475Smrg	return 1;
1270d522f475Smrg    }
1271d522f475Smrg    return 0;
1272d522f475Smrg}
1273d522f475Smrg
1274d522f475Smrgvoid
1275c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1276d522f475Smrg{
1277c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1278d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1279d522f475Smrg    unsigned root_width, root_height;
1280d522f475Smrg
1281cd3331d0Smrg    TRACE(("RequestMaximize %s\n", maximize ? "maximize" : "restore"));
1282cd3331d0Smrg
1283d522f475Smrg    if (maximize) {
1284d522f475Smrg
1285c219fbebSmrg	if (QueryMaximize(xw, &root_width, &root_height)) {
1286d522f475Smrg
1287d522f475Smrg	    if (XGetWindowAttributes(screen->display,
1288c219fbebSmrg				     WMFrameWindow(xw),
1289d522f475Smrg				     &wm_attrs)) {
1290d522f475Smrg
1291d522f475Smrg		if (XGetWindowAttributes(screen->display,
1292c219fbebSmrg					 VShellWindow(xw),
1293d522f475Smrg					 &vshell_attrs)) {
1294d522f475Smrg
1295d522f475Smrg		    if (screen->restore_data != True
1296d522f475Smrg			|| screen->restore_width != root_width
1297d522f475Smrg			|| screen->restore_height != root_height) {
1298d522f475Smrg			screen->restore_data = True;
1299d522f475Smrg			screen->restore_x = wm_attrs.x + wm_attrs.border_width;
1300d522f475Smrg			screen->restore_y = wm_attrs.y + wm_attrs.border_width;
1301b7c89284Ssnj			screen->restore_width = (unsigned) vshell_attrs.width;
1302b7c89284Ssnj			screen->restore_height = (unsigned) vshell_attrs.height;
1303d522f475Smrg			TRACE(("HandleMaximize: save window position %d,%d size %d,%d\n",
1304d522f475Smrg			       screen->restore_x,
1305d522f475Smrg			       screen->restore_y,
1306d522f475Smrg			       screen->restore_width,
1307d522f475Smrg			       screen->restore_height));
1308d522f475Smrg		    }
1309d522f475Smrg
1310d522f475Smrg		    /* subtract wm decoration dimensions */
1311b7c89284Ssnj		    root_width -=
1312b7c89284Ssnj			(unsigned) ((wm_attrs.width - vshell_attrs.width)
1313b7c89284Ssnj				    + (wm_attrs.border_width * 2));
1314b7c89284Ssnj		    root_height -=
1315b7c89284Ssnj			(unsigned) ((wm_attrs.height - vshell_attrs.height)
1316d522f475Smrg				    + (wm_attrs.border_width * 2));
1317d522f475Smrg
1318c219fbebSmrg		    XMoveResizeWindow(screen->display, VShellWindow(xw),
1319d522f475Smrg				      0 + wm_attrs.border_width,	/* x */
1320d522f475Smrg				      0 + wm_attrs.border_width,	/* y */
1321d522f475Smrg				      root_width,
1322d522f475Smrg				      root_height);
1323d522f475Smrg		}
1324d522f475Smrg	    }
1325d522f475Smrg	}
1326d522f475Smrg    } else {
1327d522f475Smrg	if (screen->restore_data) {
1328d522f475Smrg	    TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
1329d522f475Smrg		   screen->restore_x,
1330d522f475Smrg		   screen->restore_y,
1331d522f475Smrg		   screen->restore_width,
1332d522f475Smrg		   screen->restore_height));
1333d522f475Smrg	    screen->restore_data = False;
1334d522f475Smrg
1335d522f475Smrg	    XMoveResizeWindow(screen->display,
1336c219fbebSmrg			      VShellWindow(xw),
1337d522f475Smrg			      screen->restore_x,
1338d522f475Smrg			      screen->restore_y,
1339d522f475Smrg			      screen->restore_width,
1340d522f475Smrg			      screen->restore_height);
1341d522f475Smrg	}
1342d522f475Smrg    }
1343d522f475Smrg}
1344d522f475Smrg
1345d522f475Smrg/*ARGSUSED*/
1346d522f475Smrgvoid
1347b7c89284SsnjHandleMaximize(Widget w,
1348d522f475Smrg	       XEvent * event GCC_UNUSED,
1349d522f475Smrg	       String * params GCC_UNUSED,
1350d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1351d522f475Smrg{
1352b7c89284Ssnj    XtermWidget xw;
1353b7c89284Ssnj
1354b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1355b7c89284Ssnj	RequestMaximize(xw, 1);
1356d522f475Smrg    }
1357d522f475Smrg}
1358d522f475Smrg
1359d522f475Smrg/*ARGSUSED*/
1360d522f475Smrgvoid
1361b7c89284SsnjHandleRestoreSize(Widget w,
1362d522f475Smrg		  XEvent * event GCC_UNUSED,
1363d522f475Smrg		  String * params GCC_UNUSED,
1364d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1365d522f475Smrg{
1366b7c89284Ssnj    XtermWidget xw;
1367b7c89284Ssnj
1368b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1369b7c89284Ssnj	RequestMaximize(xw, 0);
1370d522f475Smrg    }
1371d522f475Smrg}
1372d522f475Smrg#endif /* OPT_MAXIMIZE */
1373d522f475Smrg
1374d522f475Smrgvoid
1375d522f475SmrgRedraw(void)
1376d522f475Smrg{
1377d522f475Smrg    TScreen *screen = TScreenOf(term);
1378d522f475Smrg    XExposeEvent event;
1379d522f475Smrg
1380d522f475Smrg    TRACE(("Redraw\n"));
1381d522f475Smrg
1382d522f475Smrg    event.type = Expose;
1383d522f475Smrg    event.display = screen->display;
1384d522f475Smrg    event.x = 0;
1385d522f475Smrg    event.y = 0;
1386d522f475Smrg    event.count = 0;
1387d522f475Smrg
1388d522f475Smrg    if (VWindow(screen)) {
1389d522f475Smrg	event.window = VWindow(screen);
1390d522f475Smrg	event.width = term->core.width;
1391d522f475Smrg	event.height = term->core.height;
1392d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
1393d522f475Smrg						       (XEvent *) & event,
1394d522f475Smrg						       NULL);
1395d522f475Smrg	if (ScrollbarWidth(screen)) {
1396d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
1397d522f475Smrg		(screen->scrollWidget, (XEvent *) & event, NULL);
1398d522f475Smrg	}
1399d522f475Smrg    }
1400d522f475Smrg#if OPT_TEK4014
1401d522f475Smrg    if (TEK4014_SHOWN(term)) {
1402cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1403d522f475Smrg	event.window = TWindow(tekscr);
1404d522f475Smrg	event.width = tekWidget->core.width;
1405d522f475Smrg	event.height = tekWidget->core.height;
1406d522f475Smrg	TekExpose((Widget) tekWidget, (XEvent *) & event, NULL);
1407d522f475Smrg    }
1408d522f475Smrg#endif
1409d522f475Smrg}
1410d522f475Smrg
1411d522f475Smrg#ifdef VMS
1412d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1413d522f475Smrg#else
1414d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1415d522f475Smrg#endif
1416d522f475Smrg
1417d522f475Smrgvoid
1418d522f475Smrgtimestamp_filename(char *dst, const char *src)
1419d522f475Smrg{
1420d522f475Smrg    time_t tstamp;
1421d522f475Smrg    struct tm *tstruct;
1422d522f475Smrg
1423d522f475Smrg    tstamp = time((time_t *) 0);
1424d522f475Smrg    tstruct = localtime(&tstamp);
1425d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1426d522f475Smrg	    src,
1427d522f475Smrg	    tstruct->tm_year + 1900,
1428d522f475Smrg	    tstruct->tm_mon + 1,
1429d522f475Smrg	    tstruct->tm_mday,
1430d522f475Smrg	    tstruct->tm_hour,
1431d522f475Smrg	    tstruct->tm_min,
1432d522f475Smrg	    tstruct->tm_sec);
1433d522f475Smrg}
1434d522f475Smrg
1435d522f475Smrgint
1436d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1437d522f475Smrg{
1438d522f475Smrg    int fd;
1439d522f475Smrg    struct stat sb;
1440d522f475Smrg
1441d522f475Smrg#ifdef VMS
1442d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1443d522f475Smrg	int the_error = errno;
1444d522f475Smrg	fprintf(stderr, "%s: cannot open %s: %d:%s\n",
1445c219fbebSmrg		ProgramName,
1446d522f475Smrg		path,
1447d522f475Smrg		the_error,
1448d522f475Smrg		SysErrorMsg(the_error));
1449d522f475Smrg	return -1;
1450d522f475Smrg    }
1451d522f475Smrg    chown(path, uid, gid);
1452d522f475Smrg#else
1453d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1454d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1455d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1456d522f475Smrg	int the_error = errno;
1457d522f475Smrg	fprintf(stderr, "%s: cannot open %s: %d:%s\n",
1458c219fbebSmrg		ProgramName,
1459d522f475Smrg		path,
1460d522f475Smrg		the_error,
1461d522f475Smrg		SysErrorMsg(the_error));
1462d522f475Smrg	return -1;
1463d522f475Smrg    }
1464d522f475Smrg#endif
1465d522f475Smrg
1466d522f475Smrg    /*
1467d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1468d522f475Smrg     * we do any damage, and that it is not world-writable.
1469d522f475Smrg     */
1470d522f475Smrg    if (fstat(fd, &sb) < 0
1471d522f475Smrg	|| sb.st_uid != uid
1472d522f475Smrg	|| (sb.st_mode & 022) != 0) {
1473c219fbebSmrg	fprintf(stderr, "%s: you do not own %s\n", ProgramName, path);
1474d522f475Smrg	close(fd);
1475d522f475Smrg	return -1;
1476d522f475Smrg    }
1477d522f475Smrg    return fd;
1478d522f475Smrg}
1479d522f475Smrg
1480d522f475Smrg#ifndef VMS
1481d522f475Smrg/*
1482d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1483d522f475Smrg * We could emulate this with careful use of access() and following
1484d522f475Smrg * symbolic links, but that is messy and has race conditions.
1485d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1486d522f475Smrg * being available.
1487d522f475Smrg *
1488d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1489d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1490d522f475Smrg * for the debug logs.
1491d522f475Smrg *
1492d522f475Smrg * Returns
1493d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1494d522f475Smrg *	-1 on error, e.g., cannot fork
1495d522f475Smrg *	 0 otherwise.
1496d522f475Smrg */
1497d522f475Smrgint
1498712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
1499d522f475Smrg{
1500d522f475Smrg    int fd;
1501d522f475Smrg    pid_t pid;
1502d522f475Smrg    int retval = 0;
1503d522f475Smrg    int childstat = 0;
1504d522f475Smrg#ifndef HAVE_WAITPID
1505d522f475Smrg    int waited;
1506d522f475Smrg    SIGNAL_T(*chldfunc) (int);
1507d522f475Smrg
1508d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1509d522f475Smrg#endif /* HAVE_WAITPID */
1510d522f475Smrg
1511d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1512d522f475Smrg	   (int) uid, (int) geteuid(),
1513d522f475Smrg	   (int) gid, (int) getegid(),
1514d522f475Smrg	   append,
1515d522f475Smrg	   pathname,
1516d522f475Smrg	   mode));
1517d522f475Smrg
1518d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1519d522f475Smrg	fd = open(pathname,
1520d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1521d522f475Smrg		  mode);
1522d522f475Smrg	if (fd >= 0)
1523d522f475Smrg	    close(fd);
1524d522f475Smrg	return (fd >= 0);
1525d522f475Smrg    }
1526d522f475Smrg
1527d522f475Smrg    pid = fork();
1528d522f475Smrg    switch (pid) {
1529d522f475Smrg    case 0:			/* child */
1530d522f475Smrg	if (setgid(gid) == -1
1531d522f475Smrg	    || setuid(uid) == -1) {
1532d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1533d522f475Smrg	    retval = 1;
1534d522f475Smrg	} else {
1535d522f475Smrg	    fd = open(pathname,
1536d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1537d522f475Smrg		      mode);
1538d522f475Smrg	    if (fd >= 0) {
1539d522f475Smrg		close(fd);
1540d522f475Smrg		retval = 0;
1541d522f475Smrg	    } else {
1542d522f475Smrg		retval = 1;
1543d522f475Smrg	    }
1544d522f475Smrg	}
1545d522f475Smrg	_exit(retval);
1546d522f475Smrg	/* NOTREACHED */
1547d522f475Smrg    case -1:			/* error */
1548d522f475Smrg	return retval;
1549d522f475Smrg    default:			/* parent */
1550d522f475Smrg#ifdef HAVE_WAITPID
1551d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1552d522f475Smrg#ifdef EINTR
1553d522f475Smrg	    if (errno == EINTR)
1554d522f475Smrg		continue;
1555d522f475Smrg#endif /* EINTR */
1556d522f475Smrg#ifdef ERESTARTSYS
1557d522f475Smrg	    if (errno == ERESTARTSYS)
1558d522f475Smrg		continue;
1559d522f475Smrg#endif /* ERESTARTSYS */
1560d522f475Smrg	    break;
1561d522f475Smrg	}
1562d522f475Smrg#else /* HAVE_WAITPID */
1563d522f475Smrg	waited = wait(&childstat);
1564d522f475Smrg	signal(SIGCHLD, chldfunc);
1565d522f475Smrg	/*
1566d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1567d522f475Smrg	   we might have missed the termination of our screen child.
1568d522f475Smrg	   If we can check for this possibility without hanging, do so.
1569d522f475Smrg	 */
1570d522f475Smrg	do
1571cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
1572d522f475Smrg		Cleanup(0);
1573d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1574d522f475Smrg#endif /* HAVE_WAITPID */
1575d522f475Smrg#ifndef WIFEXITED
1576d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1577d522f475Smrg#endif
1578d522f475Smrg	if (WIFEXITED(childstat))
1579d522f475Smrg	    retval = 1;
1580d522f475Smrg	return retval;
1581d522f475Smrg    }
1582d522f475Smrg}
1583d522f475Smrg#endif /* !VMS */
1584d522f475Smrg
1585d522f475Smrgint
1586d522f475SmrgxtermResetIds(TScreen * screen)
1587d522f475Smrg{
1588d522f475Smrg    int result = 0;
1589d522f475Smrg    if (setgid(screen->gid) == -1) {
1590d522f475Smrg	fprintf(stderr, "%s: unable to reset group-id\n", ProgramName);
1591d522f475Smrg	result = -1;
1592d522f475Smrg    }
1593d522f475Smrg    if (setuid(screen->uid) == -1) {
1594d522f475Smrg	fprintf(stderr, "%s: unable to reset user-id\n", ProgramName);
1595d522f475Smrg	result = -1;
1596d522f475Smrg    }
1597d522f475Smrg    return result;
1598d522f475Smrg}
1599d522f475Smrg
1600d522f475Smrg#ifdef ALLOWLOGGING
1601d522f475Smrg
1602d522f475Smrg/*
1603d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1604d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1605d522f475Smrg */
1606d522f475Smrg
1607d522f475Smrg#ifdef ALLOWLOGFILEEXEC
1608d522f475Smrgstatic SIGNAL_T
1609d522f475Smrglogpipe(int sig GCC_UNUSED)
1610d522f475Smrg{
1611cd3331d0Smrg    XtermWidget xw = term;
1612cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1613d522f475Smrg
1614d522f475Smrg#ifdef SYSV
1615d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
1616d522f475Smrg#endif /* SYSV */
1617d522f475Smrg    if (screen->logging)
1618cd3331d0Smrg	CloseLog(xw);
1619d522f475Smrg}
1620d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1621d522f475Smrg
1622d522f475Smrgvoid
1623cd3331d0SmrgStartLog(XtermWidget xw)
1624d522f475Smrg{
1625d522f475Smrg    static char *log_default;
1626d522f475Smrg#ifdef ALLOWLOGFILEEXEC
1627d522f475Smrg    char *cp;
1628d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1629cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1630d522f475Smrg
1631d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
1632d522f475Smrg	return;
1633d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
1634d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
1635d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
1636d522f475Smrg			 0640);
1637d522f475Smrg    if (screen->logfd < 0)
1638d522f475Smrg	return;			/* open failed */
1639d522f475Smrg#else /*VMS */
1640d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
1641d522f475Smrg	if (screen->logfile)
1642d522f475Smrg	    free(screen->logfile);
1643d522f475Smrg	if (log_default == NULL) {
1644d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
1645d522f475Smrg	    char log_def_name[512];	/* see sprintf below */
1646d522f475Smrg	    char hostname[255 + 1];	/* Internet standard limit (RFC 1035):
1647d522f475Smrg					   ``To simplify implementations, the
1648d522f475Smrg					   total length of a domain name (i.e.,
1649d522f475Smrg					   label octets and label length
1650d522f475Smrg					   octets) is restricted to 255 octets
1651d522f475Smrg					   or less.'' */
1652d522f475Smrg	    char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1];
1653d522f475Smrg	    time_t now;
1654d522f475Smrg	    struct tm *ltm;
1655d522f475Smrg
1656d522f475Smrg	    now = time((time_t *) 0);
1657d522f475Smrg	    ltm = (struct tm *) localtime(&now);
1658d522f475Smrg	    if ((gethostname(hostname, sizeof(hostname)) == 0) &&
1659d522f475Smrg		(strftime(yyyy_mm_dd_hh_mm_ss,
1660d522f475Smrg			  sizeof(yyyy_mm_dd_hh_mm_ss),
1661d522f475Smrg			  "%Y.%m.%d.%H.%M.%S", ltm) > 0)) {
1662d522f475Smrg		(void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d",
1663d522f475Smrg			       hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid());
1664d522f475Smrg	    }
1665d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
1666d522f475Smrg		return;
1667d522f475Smrg#else
1668d522f475Smrg	    const char *log_def_name = "XtermLog.XXXXXX";
1669d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
1670d522f475Smrg		return;
1671d522f475Smrg
1672d522f475Smrg	    mktemp(log_default);
1673d522f475Smrg#endif
1674d522f475Smrg	}
1675d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
1676d522f475Smrg	    return;
1677d522f475Smrg    }
1678d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
1679d522f475Smrg#ifdef ALLOWLOGFILEEXEC
1680d522f475Smrg	/*
1681d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
1682d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
1683d522f475Smrg	 * done through escape sequences....  You have been warned.
1684d522f475Smrg	 */
1685d522f475Smrg	int pid;
1686d522f475Smrg	int p[2];
1687d522f475Smrg	static char *shell;
1688d522f475Smrg	struct passwd *pw;
1689d522f475Smrg
1690d522f475Smrg	if (pipe(p) < 0 || (pid = fork()) < 0)
1691d522f475Smrg	    return;
1692d522f475Smrg	if (pid == 0) {		/* child */
1693d522f475Smrg	    /*
1694d522f475Smrg	     * Close our output (we won't be talking back to the
1695d522f475Smrg	     * parent), and redirect our child's output to the
1696d522f475Smrg	     * original stderr.
1697d522f475Smrg	     */
1698d522f475Smrg	    close(p[1]);
1699d522f475Smrg	    dup2(p[0], 0);
1700d522f475Smrg	    close(p[0]);
1701d522f475Smrg	    dup2(fileno(stderr), 1);
1702d522f475Smrg	    dup2(fileno(stderr), 2);
1703d522f475Smrg
1704d522f475Smrg	    close(fileno(stderr));
1705d522f475Smrg	    close(ConnectionNumber(screen->display));
1706d522f475Smrg	    close(screen->respond);
1707d522f475Smrg
1708d522f475Smrg	    if ((((cp = x_getenv("SHELL")) == NULL)
1709d522f475Smrg		 && ((pw = getpwuid(screen->uid)) == NULL
1710d522f475Smrg		     || *(cp = pw->pw_shell) == 0))
1711d522f475Smrg		|| (shell = CastMallocN(char, strlen(cp))) == 0) {
1712cd3331d0Smrg		static char dummy[] = "/bin/sh";
1713cd3331d0Smrg		shell = dummy;
1714d522f475Smrg	    } else {
1715d522f475Smrg		strcpy(shell, cp);
1716d522f475Smrg	    }
1717d522f475Smrg
1718d522f475Smrg	    signal(SIGHUP, SIG_DFL);
1719d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
1720d522f475Smrg
1721d522f475Smrg	    /* (this is redundant) */
1722d522f475Smrg	    if (xtermResetIds(screen) < 0)
1723d522f475Smrg		exit(ERROR_SETUID);
1724d522f475Smrg
1725d522f475Smrg	    execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
1726d522f475Smrg
1727c219fbebSmrg	    fprintf(stderr, "%s: Can't exec `%s'\n",
1728c219fbebSmrg		    ProgramName,
1729d522f475Smrg		    &screen->logfile[1]);
1730d522f475Smrg	    exit(ERROR_LOGEXEC);
1731d522f475Smrg	}
1732d522f475Smrg	close(p[0]);
1733d522f475Smrg	screen->logfd = p[1];
1734d522f475Smrg	signal(SIGPIPE, logpipe);
1735d522f475Smrg#else
1736cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
1737cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
1738d522f475Smrg	return;
1739d522f475Smrg#endif
1740d522f475Smrg    } else {
1741d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
1742d522f475Smrg					   screen->gid,
1743d522f475Smrg					   screen->logfile,
1744d522f475Smrg					   (log_default != 0))) < 0)
1745d522f475Smrg	    return;
1746d522f475Smrg    }
1747d522f475Smrg#endif /*VMS */
1748d522f475Smrg    screen->logstart = VTbuffer->next;
1749d522f475Smrg    screen->logging = True;
1750d522f475Smrg    update_logging();
1751d522f475Smrg}
1752d522f475Smrg
1753d522f475Smrgvoid
1754cd3331d0SmrgCloseLog(XtermWidget xw)
1755d522f475Smrg{
1756cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1757cd3331d0Smrg
1758d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
1759d522f475Smrg	return;
1760cd3331d0Smrg    FlushLog(xw);
1761d522f475Smrg    close(screen->logfd);
1762d522f475Smrg    screen->logging = False;
1763d522f475Smrg    update_logging();
1764d522f475Smrg}
1765d522f475Smrg
1766d522f475Smrgvoid
1767cd3331d0SmrgFlushLog(XtermWidget xw)
1768d522f475Smrg{
1769cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1770cd3331d0Smrg
1771d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
1772d522f475Smrg	Char *cp;
1773d522f475Smrg	int i;
1774d522f475Smrg
1775d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
1776d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
1777d522f475Smrg	if (!tt_new_output)
1778d522f475Smrg	    return;
1779d522f475Smrg	tt_new_output = False;
1780d522f475Smrg#endif /* VMS */
1781d522f475Smrg	cp = VTbuffer->next;
1782d522f475Smrg	if (screen->logstart != 0
1783cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
1784cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
1785d522f475Smrg	}
1786d522f475Smrg	screen->logstart = VTbuffer->next;
1787d522f475Smrg    }
1788d522f475Smrg}
1789d522f475Smrg
1790d522f475Smrg#endif /* ALLOWLOGGING */
1791d522f475Smrg
1792d522f475Smrg/***====================================================================***/
1793d522f475Smrg
1794d522f475Smrg#if OPT_ISO_COLORS
1795d522f475Smrgstatic void
1796d522f475SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
1797d522f475Smrg{
1798cd3331d0Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
1799cd3331d0Smrg	XColor color;
1800cd3331d0Smrg	Colormap cmap = xw->core.colormap;
1801cd3331d0Smrg	char buffer[80];
1802cd3331d0Smrg
1803cd3331d0Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
1804cd3331d0Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
1805cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
1806cd3331d0Smrg	sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
1807cd3331d0Smrg		colornum,
1808cd3331d0Smrg		color.red,
1809cd3331d0Smrg		color.green,
1810cd3331d0Smrg		color.blue);
1811cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
1812cd3331d0Smrg	unparseputs(xw, buffer);
1813cd3331d0Smrg	unparseputc1(xw, final);
1814cd3331d0Smrg	unparse_end(xw);
1815cd3331d0Smrg    }
1816d522f475Smrg}
1817d522f475Smrg
18180d92cbfdSchristosstatic unsigned
1819d522f475SmrggetColormapSize(Display * display)
1820d522f475Smrg{
18210d92cbfdSchristos    unsigned result;
1822d522f475Smrg    int numFound;
1823d522f475Smrg    XVisualInfo myTemplate, *visInfoPtr;
1824d522f475Smrg
1825d522f475Smrg    myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(display,
1826d522f475Smrg							    XDefaultScreen(display)));
1827d522f475Smrg    visInfoPtr = XGetVisualInfo(display, (long) VisualIDMask,
1828d522f475Smrg				&myTemplate, &numFound);
18290d92cbfdSchristos    result = (numFound >= 1) ? (unsigned) visInfoPtr->colormap_size : 0;
1830d522f475Smrg
1831d522f475Smrg    XFree((char *) visInfoPtr);
1832d522f475Smrg    return result;
1833d522f475Smrg}
1834d522f475Smrg
1835d522f475Smrg/*
1836d522f475Smrg * Find closest color for "def" in "cmap".
1837d522f475Smrg * Set "def" to the resulting color.
1838d522f475Smrg *
1839d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
1840d522f475Smrg * modified with ideas from David Tong's "noflash" library.
1841d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
1842d522f475Smrg *
1843d522f475Smrg * These provide some introduction:
1844d522f475Smrg *	http://en.wikipedia.org/wiki/YIQ
1845d522f475Smrg *		for an introduction to YIQ weights.
1846d522f475Smrg *	http://en.wikipedia.org/wiki/Luminance_(video)
1847d522f475Smrg *		for a discussion of luma.
1848d522f475Smrg *	http://en.wikipedia.org/wiki/YUV
1849d522f475Smrg *
1850d522f475Smrg * Return False if not able to find or allocate a color.
1851d522f475Smrg */
1852d522f475Smrgstatic Boolean
1853d522f475Smrgfind_closest_color(Display * dpy, Colormap cmap, XColor * def)
1854d522f475Smrg{
1855d522f475Smrg    Boolean result = False;
1856d522f475Smrg    XColor *colortable;
1857d522f475Smrg    char *tried;
1858d522f475Smrg    double diff, thisRGB, bestRGB;
1859d522f475Smrg    unsigned attempts;
1860d522f475Smrg    unsigned bestInx;
1861d522f475Smrg    unsigned cmap_size;
1862d522f475Smrg    unsigned i;
1863d522f475Smrg
1864d522f475Smrg    cmap_size = getColormapSize(dpy);
1865d522f475Smrg    if (cmap_size != 0) {
1866d522f475Smrg
1867b7c89284Ssnj	colortable = TypeMallocN(XColor, (size_t) cmap_size);
1868d522f475Smrg	if (colortable != 0) {
1869d522f475Smrg
1870b7c89284Ssnj	    tried = TypeCallocN(char, (size_t) cmap_size);
1871d522f475Smrg	    if (tried != 0) {
1872d522f475Smrg
1873d522f475Smrg		for (i = 0; i < cmap_size; i++) {
1874d522f475Smrg		    colortable[i].pixel = (unsigned long) i;
1875d522f475Smrg		}
1876d522f475Smrg		XQueryColors(dpy, cmap, colortable, (int) cmap_size);
1877d522f475Smrg
1878d522f475Smrg		/*
1879d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
1880d522f475Smrg		 * approximation to the requested color.
1881d522f475Smrg		 */
1882d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
1883d522f475Smrg		    Boolean first = True;
1884d522f475Smrg
1885d522f475Smrg		    bestRGB = 0.0;
1886d522f475Smrg		    bestInx = 0;
1887d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
1888d522f475Smrg			if (!tried[bestInx]) {
1889d522f475Smrg			    /*
1890d522f475Smrg			     * Look for the best match based on luminance.
1891d522f475Smrg			     * Measure this by the least-squares difference of
1892d522f475Smrg			     * the weighted R/G/B components from the color map
1893d522f475Smrg			     * versus the requested color.  Use the Y (luma)
1894d522f475Smrg			     * component of the YIQ color space model for
1895d522f475Smrg			     * weights that correspond to the luminance.
1896d522f475Smrg			     */
1897d522f475Smrg#define AddColorWeight(weight, color) \
1898d522f475Smrg			    diff = weight * (int) ((def->color) - colortable[i].color); \
1899d522f475Smrg			    thisRGB = diff * diff
1900d522f475Smrg
1901d522f475Smrg			    AddColorWeight(0.30, red);
1902d522f475Smrg			    AddColorWeight(0.61, green);
1903d522f475Smrg			    AddColorWeight(0.11, blue);
1904d522f475Smrg
1905d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
1906d522f475Smrg				first = False;
1907d522f475Smrg				bestInx = i;
1908d522f475Smrg				bestRGB = thisRGB;
1909d522f475Smrg			    }
1910d522f475Smrg			}
1911d522f475Smrg		    }
1912d522f475Smrg		    if (XAllocColor(dpy, cmap, &colortable[bestInx]) != 0) {
1913d522f475Smrg			*def = colortable[bestInx];
1914d522f475Smrg			result = True;
1915d522f475Smrg			break;
1916d522f475Smrg		    }
1917d522f475Smrg		    /*
1918d522f475Smrg		     * It failed - either the color map entry was readonly, or
1919d522f475Smrg		     * another client has allocated the entry.  Mark the entry
1920d522f475Smrg		     * so we will ignore it
1921d522f475Smrg		     */
1922d522f475Smrg		    tried[bestInx] = True;
1923d522f475Smrg		}
1924d522f475Smrg		free(tried);
1925d522f475Smrg	    }
1926d522f475Smrg	    free(colortable);
1927d522f475Smrg	}
1928d522f475Smrg    }
1929d522f475Smrg    return result;
1930d522f475Smrg}
1931d522f475Smrg
1932d522f475Smrg/*
1933d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
1934d522f475Smrg * to 256.
1935d522f475Smrg *
1936d522f475Smrg * Returns
1937d522f475Smrg *	-1 on error
1938d522f475Smrg *	0 on no change
1939d522f475Smrg *	1 if a new color was allocated.
1940d522f475Smrg */
1941d522f475Smrgstatic int
1942d522f475SmrgAllocateAnsiColor(XtermWidget xw,
1943d522f475Smrg		  ColorRes * res,
1944cd3331d0Smrg		  const char *spec)
1945d522f475Smrg{
1946d522f475Smrg    int result;
1947d522f475Smrg    XColor def;
1948cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
1949d522f475Smrg    Colormap cmap = xw->core.colormap;
1950d522f475Smrg
1951d522f475Smrg    if (XParseColor(screen->display, cmap, spec, &def)
1952d522f475Smrg	&& (XAllocColor(screen->display, cmap, &def)
1953d522f475Smrg	    || find_closest_color(screen->display, cmap, &def))) {
1954d522f475Smrg	if (
1955d522f475Smrg#if OPT_COLOR_RES
1956d522f475Smrg	       res->mode == True &&
1957d522f475Smrg#endif
1958d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
1959d522f475Smrg	    result = 0;
1960d522f475Smrg	} else {
1961d522f475Smrg	    result = 1;
1962d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
1963712a7ff4Smrg	    TRACE(("AllocateAnsiColor[%d] %s (pixel 0x%06lx)\n",
1964cd3331d0Smrg		   (int) (res - screen->Acolors), spec, def.pixel));
1965d522f475Smrg#if OPT_COLOR_RES
1966d522f475Smrg	    if (!res->mode)
1967d522f475Smrg		result = 0;
1968d522f475Smrg	    res->mode = True;
1969d522f475Smrg#endif
1970d522f475Smrg	}
1971d522f475Smrg    } else {
1972d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
1973d522f475Smrg	result = -1;
1974d522f475Smrg    }
1975d522f475Smrg    return (result);
1976d522f475Smrg}
1977d522f475Smrg
1978d522f475Smrg#if OPT_COLOR_RES
1979d522f475SmrgPixel
1980cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
1981d522f475Smrg{
1982d522f475Smrg    Pixel result = 0;
1983d522f475Smrg
1984d522f475Smrg    if (res->mode) {
1985d522f475Smrg	result = res->value;
1986d522f475Smrg    } else {
1987d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
1988cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
1989d522f475Smrg
1990cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
1991cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
1992d522f475Smrg
1993cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
1994cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
1995d522f475Smrg		res->mode = -True;
1996d522f475Smrg		fprintf(stderr,
1997c952d7faSmrg			"%s: Cannot allocate color \"%s\"\n",
1998c219fbebSmrg			ProgramName,
1999d522f475Smrg			NonNull(res->resource));
2000d522f475Smrg	    }
2001d522f475Smrg	    result = res->value;
2002d522f475Smrg	} else {
2003d522f475Smrg	    result = 0;
2004d522f475Smrg	}
2005d522f475Smrg    }
2006d522f475Smrg    return result;
2007d522f475Smrg}
2008d522f475Smrg#endif
2009d522f475Smrg
2010cd3331d0Smrgstatic int
2011cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2012cd3331d0Smrg{
2013cd3331d0Smrg    int code;
2014cd3331d0Smrg
2015cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2016cd3331d0Smrg	code = -1;
2017cd3331d0Smrg    } else {
2018cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2019cd3331d0Smrg
2020cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2021cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2022cd3331d0Smrg    }
2023cd3331d0Smrg    return code;
2024cd3331d0Smrg}
2025cd3331d0Smrg
2026cd3331d0Smrg/*
2027cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2028cd3331d0Smrg * values from the given buffer.
2029cd3331d0Smrg *
2030cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2031cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2032cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2033cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2034cd3331d0Smrg * 'first' set to the beginning of those indices.
2035cd3331d0Smrg *
2036cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2037cd3331d0Smrg */
2038d522f475Smrgstatic Bool
2039d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2040d522f475Smrg		       char *buf,
2041cd3331d0Smrg		       int first,
2042d522f475Smrg		       int final)
2043d522f475Smrg{
2044d522f475Smrg    char *name;
2045d522f475Smrg    int color;
2046d522f475Smrg    int repaint = False;
2047d522f475Smrg    int code;
2048cd3331d0Smrg    int last = (MAXCOLORS - first);
2049d522f475Smrg
2050d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2051d522f475Smrg
2052d522f475Smrg    while (buf && *buf) {
2053d522f475Smrg	name = strchr(buf, ';');
2054d522f475Smrg	if (name == NULL)
2055d522f475Smrg	    break;
2056d522f475Smrg	*name = '\0';
2057d522f475Smrg	name++;
2058d522f475Smrg	color = atoi(buf);
2059cd3331d0Smrg	if (color < 0 || color >= last)
2060cd3331d0Smrg	    break;		/* quit on any error */
2061d522f475Smrg	buf = strchr(name, ';');
2062d522f475Smrg	if (buf) {
2063d522f475Smrg	    *buf = '\0';
2064d522f475Smrg	    buf++;
2065d522f475Smrg	}
2066cd3331d0Smrg	if (!strcmp(name, "?")) {
2067cd3331d0Smrg	    ReportAnsiColorRequest(xw, color + first, final);
2068cd3331d0Smrg	} else {
2069cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
2070d522f475Smrg	    if (code < 0) {
2071d522f475Smrg		/* stop on any error */
2072d522f475Smrg		break;
2073d522f475Smrg	    } else if (code > 0) {
2074d522f475Smrg		repaint = True;
2075d522f475Smrg	    }
2076d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
2077d522f475Smrg	     * change style (dynamic colors).
2078d522f475Smrg	     */
2079d522f475Smrg	}
2080d522f475Smrg    }
2081d522f475Smrg
2082d522f475Smrg    return (repaint);
2083d522f475Smrg}
2084cd3331d0Smrg
2085cd3331d0Smrgstatic Bool
2086cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
2087cd3331d0Smrg{
2088cd3331d0Smrg    Bool repaint = False;
2089cd3331d0Smrg    int last = MAXCOLORS - start;
2090cd3331d0Smrg
2091cd3331d0Smrg    if (color >= 0 && color < last) {
2092cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
2093cd3331d0Smrg
2094cd3331d0Smrg	if (res->mode) {
2095cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
2096cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
2097cd3331d0Smrg		repaint = True;
2098cd3331d0Smrg	    }
2099cd3331d0Smrg	}
2100cd3331d0Smrg    }
2101cd3331d0Smrg    return repaint;
2102cd3331d0Smrg}
2103cd3331d0Smrg
2104cd3331d0Smrgint
2105cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
2106cd3331d0Smrg{
2107cd3331d0Smrg    int repaint = 0;
2108cd3331d0Smrg    int color;
2109cd3331d0Smrg
2110cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
2111cd3331d0Smrg    if (*buf != '\0') {
2112cd3331d0Smrg	/* reset specific colors */
2113cd3331d0Smrg	while (!IsEmpty(buf)) {
2114cd3331d0Smrg	    char *next;
2115cd3331d0Smrg
2116cd3331d0Smrg	    color = (int) strtol(buf, &next, 10);
2117cd3331d0Smrg	    if ((next == buf) || (color < 0))
2118cd3331d0Smrg		break;		/* no number at all */
2119cd3331d0Smrg	    if (next != 0) {
2120cd3331d0Smrg		if (strchr(";", *next) == 0)
2121cd3331d0Smrg		    break;	/* unexpected delimiter */
2122cd3331d0Smrg		++next;
2123cd3331d0Smrg	    }
2124cd3331d0Smrg
2125cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2126cd3331d0Smrg		++repaint;
2127cd3331d0Smrg	    }
2128cd3331d0Smrg	    buf = next;
2129cd3331d0Smrg	}
2130cd3331d0Smrg    } else {
2131cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
2132cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
2133cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
2134cd3331d0Smrg		++repaint;
2135cd3331d0Smrg	    }
2136cd3331d0Smrg	}
2137cd3331d0Smrg    }
2138cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
2139cd3331d0Smrg    return repaint;
2140cd3331d0Smrg}
2141d522f475Smrg#else
2142d522f475Smrg#define find_closest_color(display, cmap, def) 0
2143d522f475Smrg#endif /* OPT_ISO_COLORS */
2144d522f475Smrg
2145d522f475Smrg#if OPT_PASTE64
2146d522f475Smrgstatic void
2147d522f475SmrgManipulateSelectionData(XtermWidget xw, TScreen * screen, char *buf, int final)
2148d522f475Smrg{
2149d522f475Smrg#define PDATA(a,b) { a, #b }
2150d522f475Smrg    static struct {
2151d522f475Smrg	char given;
2152cd3331d0Smrg	String result;
2153d522f475Smrg    } table[] = {
2154d522f475Smrg	PDATA('s', SELECT),
2155d522f475Smrg	    PDATA('p', PRIMARY),
2156d522f475Smrg	    PDATA('c', CLIPBOARD),
2157d522f475Smrg	    PDATA('0', CUT_BUFFER0),
2158d522f475Smrg	    PDATA('1', CUT_BUFFER1),
2159d522f475Smrg	    PDATA('2', CUT_BUFFER2),
2160d522f475Smrg	    PDATA('3', CUT_BUFFER3),
2161d522f475Smrg	    PDATA('4', CUT_BUFFER4),
2162d522f475Smrg	    PDATA('5', CUT_BUFFER5),
2163d522f475Smrg	    PDATA('6', CUT_BUFFER6),
2164d522f475Smrg	    PDATA('7', CUT_BUFFER7),
2165d522f475Smrg    };
2166d522f475Smrg
2167cd3331d0Smrg    const char *base = buf;
2168d522f475Smrg    char *used = x_strdup(base);
2169d522f475Smrg    Cardinal j, n = 0;
2170cd3331d0Smrg    String *select_args = 0;
2171d522f475Smrg
2172d522f475Smrg    TRACE(("Manipulate selection data\n"));
2173d522f475Smrg
2174d522f475Smrg    while (*buf != ';' && *buf != '\0') {
2175d522f475Smrg	++buf;
2176d522f475Smrg    }
2177d522f475Smrg
2178d522f475Smrg    if (*buf == ';') {
2179d522f475Smrg	*buf++ = '\0';
2180d522f475Smrg
2181d522f475Smrg	if (*base == '\0')
2182d522f475Smrg	    base = "s0";
2183d522f475Smrg	if ((select_args = TypeCallocN(String, 1 + strlen(base))) == 0)
2184d522f475Smrg	    return;
2185d522f475Smrg	while (*base != '\0') {
2186d522f475Smrg	    for (j = 0; j < XtNumber(table); ++j) {
2187d522f475Smrg		if (*base == table[j].given) {
2188d522f475Smrg		    used[n] = *base;
2189d522f475Smrg		    select_args[n++] = table[j].result;
2190d522f475Smrg		    TRACE(("atom[%d] %s\n", n, table[j].result));
2191d522f475Smrg		    break;
2192d522f475Smrg		}
2193d522f475Smrg	    }
2194d522f475Smrg	    ++base;
2195d522f475Smrg	}
2196d522f475Smrg	used[n] = 0;
2197d522f475Smrg
2198d522f475Smrg	if (!strcmp(buf, "?")) {
2199cd3331d0Smrg	    if (AllowWindowOps(xw, ewGetSelection)) {
2200cd3331d0Smrg		TRACE(("Getting selection\n"));
2201cd3331d0Smrg		unparseputc1(xw, ANSI_OSC);
2202cd3331d0Smrg		unparseputs(xw, "52");
2203cd3331d0Smrg		unparseputc(xw, ';');
2204d522f475Smrg
2205cd3331d0Smrg		unparseputs(xw, used);
2206cd3331d0Smrg		unparseputc(xw, ';');
2207d522f475Smrg
2208cd3331d0Smrg		/* Tell xtermGetSelection data is base64 encoded */
2209cd3331d0Smrg		screen->base64_paste = n;
2210cd3331d0Smrg		screen->base64_final = final;
2211d522f475Smrg
2212cd3331d0Smrg		/* terminator will be written in this call */
2213cd3331d0Smrg		xtermGetSelection((Widget) xw, (Time) 0, select_args, n, NULL);
2214cd3331d0Smrg	    }
2215d522f475Smrg	} else {
2216cd3331d0Smrg	    if (AllowWindowOps(xw, ewSetSelection)) {
2217cd3331d0Smrg		TRACE(("Setting selection with %s\n", buf));
2218cd3331d0Smrg		ClearSelectionBuffer(screen);
2219cd3331d0Smrg		while (*buf != '\0')
2220cd3331d0Smrg		    AppendToSelectionBuffer(screen, CharOf(*buf++));
2221cd3331d0Smrg		CompleteSelection(xw, select_args, n);
2222cd3331d0Smrg	    }
2223d522f475Smrg	}
2224d522f475Smrg    }
2225d522f475Smrg}
2226d522f475Smrg#endif /* OPT_PASTE64 */
2227d522f475Smrg
2228d522f475Smrg/***====================================================================***/
2229d522f475Smrg
2230cd3331d0Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title))
2231cd3331d0Smrg
2232d522f475Smrgstatic Bool
2233cd3331d0SmrgxtermIsPrintable(XtermWidget xw, Char ** bufp, Char * last)
2234d522f475Smrg{
2235cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2236d522f475Smrg    Bool result = False;
2237d522f475Smrg    Char *cp = *bufp;
2238d522f475Smrg    Char *next = cp;
2239d522f475Smrg
2240d522f475Smrg    (void) screen;
2241d522f475Smrg    (void) last;
2242d522f475Smrg
2243d522f475Smrg#if OPT_WIDE_CHARS
2244cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
2245d522f475Smrg	PtyData data;
2246d522f475Smrg
2247d522f475Smrg	if (decodeUtf8(fakePtyData(&data, cp, last))) {
2248d522f475Smrg	    if (data.utf_data != UCS_REPL
2249d522f475Smrg		&& (data.utf_data >= 128 ||
2250d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
2251d522f475Smrg		next += (data.utf_size - 1);
2252d522f475Smrg		result = True;
2253d522f475Smrg	    } else {
2254d522f475Smrg		result = False;
2255d522f475Smrg	    }
2256d522f475Smrg	} else {
2257d522f475Smrg	    result = False;
2258d522f475Smrg	}
2259d522f475Smrg    } else
2260d522f475Smrg#endif
2261d522f475Smrg#if OPT_C1_PRINT
2262d522f475Smrg	if (screen->c1_printable
2263d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
2264d522f475Smrg	result = True;
2265d522f475Smrg    } else
2266d522f475Smrg#endif
2267d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
2268d522f475Smrg	result = True;
2269d522f475Smrg    }
2270d522f475Smrg    *bufp = next;
2271d522f475Smrg    return result;
2272d522f475Smrg}
2273d522f475Smrg
2274d522f475Smrg/***====================================================================***/
2275d522f475Smrg
2276d522f475Smrg/*
2277d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
2278cd3331d0Smrg * array indices.  Compare with TermColors.
2279d522f475Smrg */
2280d522f475Smrgtypedef enum {
2281d522f475Smrg    OSC_TEXT_FG = 10
2282d522f475Smrg    ,OSC_TEXT_BG
2283d522f475Smrg    ,OSC_TEXT_CURSOR
2284d522f475Smrg    ,OSC_MOUSE_FG
2285d522f475Smrg    ,OSC_MOUSE_BG
2286d522f475Smrg#if OPT_TEK4014
2287d522f475Smrg    ,OSC_TEK_FG = 15
2288d522f475Smrg    ,OSC_TEK_BG
2289d522f475Smrg#endif
2290d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2291d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
2292d522f475Smrg#endif
2293d522f475Smrg#if OPT_TEK4014
2294d522f475Smrg    ,OSC_TEK_CURSOR = 18
2295d522f475Smrg#endif
2296d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2297d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
2298d522f475Smrg#endif
2299d522f475Smrg    ,OSC_NCOLORS
2300d522f475Smrg} OscTextColors;
2301d522f475Smrg
2302cd3331d0Smrg/*
2303cd3331d0Smrg * Map codes to OSC controls that can reset colors.
2304cd3331d0Smrg */
2305cd3331d0Smrg#define OSC_RESET 100
2306cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
2307cd3331d0Smrg
2308d522f475Smrgstatic ScrnColors *pOldColors = NULL;
2309d522f475Smrg
2310d522f475Smrgstatic Bool
2311d522f475SmrgGetOldColors(XtermWidget xw)
2312d522f475Smrg{
2313d522f475Smrg    int i;
2314d522f475Smrg    if (pOldColors == NULL) {
2315c219fbebSmrg	pOldColors = TypeXtMalloc(ScrnColors);
2316d522f475Smrg	if (pOldColors == NULL) {
2317d522f475Smrg	    fprintf(stderr, "allocation failure in GetOldColors\n");
2318d522f475Smrg	    return (False);
2319d522f475Smrg	}
2320d522f475Smrg	pOldColors->which = 0;
2321d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
2322d522f475Smrg	    pOldColors->colors[i] = 0;
2323d522f475Smrg	    pOldColors->names[i] = NULL;
2324d522f475Smrg	}
2325d522f475Smrg	GetColors(xw, pOldColors);
2326d522f475Smrg    }
2327d522f475Smrg    return (True);
2328d522f475Smrg}
2329d522f475Smrg
2330d522f475Smrgstatic int
2331d522f475SmrgoppositeColor(int n)
2332d522f475Smrg{
2333d522f475Smrg    switch (n) {
2334d522f475Smrg    case TEXT_FG:
2335d522f475Smrg	n = TEXT_BG;
2336d522f475Smrg	break;
2337d522f475Smrg    case TEXT_BG:
2338d522f475Smrg	n = TEXT_FG;
2339d522f475Smrg	break;
2340d522f475Smrg    case MOUSE_FG:
2341d522f475Smrg	n = MOUSE_BG;
2342d522f475Smrg	break;
2343d522f475Smrg    case MOUSE_BG:
2344d522f475Smrg	n = MOUSE_FG;
2345d522f475Smrg	break;
2346d522f475Smrg#if OPT_TEK4014
2347d522f475Smrg    case TEK_FG:
2348d522f475Smrg	n = TEK_BG;
2349d522f475Smrg	break;
2350d522f475Smrg    case TEK_BG:
2351d522f475Smrg	n = TEK_FG;
2352d522f475Smrg	break;
2353d522f475Smrg#endif
2354d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2355d522f475Smrg    case HIGHLIGHT_FG:
2356d522f475Smrg	n = HIGHLIGHT_BG;
2357d522f475Smrg	break;
2358d522f475Smrg    case HIGHLIGHT_BG:
2359d522f475Smrg	n = HIGHLIGHT_FG;
2360d522f475Smrg	break;
2361d522f475Smrg#endif
2362d522f475Smrg    default:
2363d522f475Smrg	break;
2364d522f475Smrg    }
2365d522f475Smrg    return n;
2366d522f475Smrg}
2367d522f475Smrg
2368d522f475Smrgstatic void
2369d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
2370d522f475Smrg{
2371cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
2372cd3331d0Smrg	XColor color;
2373cd3331d0Smrg	Colormap cmap = xw->core.colormap;
2374cd3331d0Smrg	char buffer[80];
2375d522f475Smrg
2376cd3331d0Smrg	/*
2377cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
2378cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
2379cd3331d0Smrg	 * reporting the opposite color which would be used.
2380cd3331d0Smrg	 */
2381cd3331d0Smrg	int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
2382cd3331d0Smrg
2383cd3331d0Smrg	GetOldColors(xw);
2384cd3331d0Smrg	color.pixel = pOldColors->colors[ndx];
2385cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
2386cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
2387cd3331d0Smrg		color.red,
2388cd3331d0Smrg		color.green,
2389cd3331d0Smrg		color.blue);
2390712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
2391cd3331d0Smrg	       ndx, pOldColors->colors[ndx], buffer));
2392cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
2393cd3331d0Smrg	unparseputs(xw, buffer);
2394cd3331d0Smrg	unparseputc1(xw, final);
2395cd3331d0Smrg	unparse_end(xw);
2396cd3331d0Smrg    }
2397d522f475Smrg}
2398d522f475Smrg
2399d522f475Smrgstatic Bool
2400d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
2401d522f475Smrg{
2402d522f475Smrg    int i;
2403d522f475Smrg
2404d522f475Smrg    /* if we were going to free old colors, this would be the place to
2405d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
2406d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
2407d522f475Smrg     * we could save some overhead this way.   The only case in which this
2408d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
2409d522f475Smrg     * which case they can restart xterm
2410d522f475Smrg     */
2411d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
2412d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
2413d522f475Smrg	    if (pOldColors->names[i] != NULL) {
2414d522f475Smrg		XtFree(pOldColors->names[i]);
2415d522f475Smrg		pOldColors->names[i] = NULL;
2416d522f475Smrg	    }
2417d522f475Smrg	    if (pNew->names[i]) {
2418d522f475Smrg		pOldColors->names[i] = pNew->names[i];
2419d522f475Smrg	    }
2420d522f475Smrg	    pOldColors->colors[i] = pNew->colors[i];
2421d522f475Smrg	}
2422d522f475Smrg    }
2423d522f475Smrg    return (True);
2424d522f475Smrg}
2425d522f475Smrg
2426d522f475Smrg/*
2427d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
2428d522f475Smrg * xterm is compiled.
2429d522f475Smrg */
2430d522f475Smrgstatic int
2431d522f475SmrgOscToColorIndex(OscTextColors mode)
2432d522f475Smrg{
2433d522f475Smrg    int result = 0;
2434d522f475Smrg
2435d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
2436d522f475Smrg    switch (mode) {
2437d522f475Smrg	CASE(TEXT_FG);
2438d522f475Smrg	CASE(TEXT_BG);
2439d522f475Smrg	CASE(TEXT_CURSOR);
2440d522f475Smrg	CASE(MOUSE_FG);
2441d522f475Smrg	CASE(MOUSE_BG);
2442d522f475Smrg#if OPT_TEK4014
2443d522f475Smrg	CASE(TEK_FG);
2444d522f475Smrg	CASE(TEK_BG);
2445d522f475Smrg#endif
2446d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2447d522f475Smrg	CASE(HIGHLIGHT_BG);
2448d522f475Smrg	CASE(HIGHLIGHT_FG);
2449d522f475Smrg#endif
2450d522f475Smrg#if OPT_TEK4014
2451d522f475Smrg	CASE(TEK_CURSOR);
2452d522f475Smrg#endif
2453d522f475Smrg    case OSC_NCOLORS:
2454d522f475Smrg	break;
2455d522f475Smrg    }
2456d522f475Smrg    return result;
2457d522f475Smrg}
2458d522f475Smrg
2459d522f475Smrgstatic Bool
2460d522f475SmrgChangeColorsRequest(XtermWidget xw,
2461d522f475Smrg		    int start,
2462d522f475Smrg		    char *names,
2463d522f475Smrg		    int final)
2464d522f475Smrg{
2465d522f475Smrg    Bool result = False;
2466d522f475Smrg    char *thisName;
2467d522f475Smrg    ScrnColors newColors;
2468d522f475Smrg    int i, ndx;
2469d522f475Smrg
2470d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
2471d522f475Smrg
2472d522f475Smrg    if (GetOldColors(xw)) {
2473d522f475Smrg	newColors.which = 0;
2474d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
2475d522f475Smrg	    newColors.names[i] = NULL;
2476d522f475Smrg	}
2477d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
2478d522f475Smrg	    ndx = OscToColorIndex((OscTextColors) i);
2479d522f475Smrg	    if (xw->misc.re_verse)
2480d522f475Smrg		ndx = oppositeColor(ndx);
2481d522f475Smrg
2482cd3331d0Smrg	    if (IsEmpty(names)) {
2483d522f475Smrg		newColors.names[ndx] = NULL;
2484d522f475Smrg	    } else {
2485d522f475Smrg		if (names[0] == ';')
2486d522f475Smrg		    thisName = NULL;
2487d522f475Smrg		else
2488d522f475Smrg		    thisName = names;
2489d522f475Smrg		names = strchr(names, ';');
2490d522f475Smrg		if (names != NULL) {
2491d522f475Smrg		    *names++ = '\0';
2492d522f475Smrg		}
2493d522f475Smrg		if (thisName != 0 && !strcmp(thisName, "?")) {
2494d522f475Smrg		    ReportColorRequest(xw, ndx, final);
2495d522f475Smrg		} else if (!pOldColors->names[ndx]
2496d522f475Smrg			   || (thisName
2497d522f475Smrg			       && strcmp(thisName, pOldColors->names[ndx]))) {
2498cd3331d0Smrg		    AllocateTermColor(xw, &newColors, ndx, thisName, False);
2499d522f475Smrg		}
2500d522f475Smrg	    }
2501d522f475Smrg	}
2502d522f475Smrg
2503d522f475Smrg	if (newColors.which != 0) {
2504d522f475Smrg	    ChangeColors(xw, &newColors);
2505d522f475Smrg	    UpdateOldColors(xw, &newColors);
2506d522f475Smrg	}
2507d522f475Smrg	result = True;
2508d522f475Smrg    }
2509d522f475Smrg    return result;
2510d522f475Smrg}
2511d522f475Smrg
2512cd3331d0Smrgstatic Bool
2513cd3331d0SmrgResetColorsRequest(XtermWidget xw,
2514cd3331d0Smrg		   int code)
2515cd3331d0Smrg{
2516cd3331d0Smrg    Bool result = False;
2517cd3331d0Smrg    const char *thisName;
2518cd3331d0Smrg    ScrnColors newColors;
2519cd3331d0Smrg    int ndx;
2520cd3331d0Smrg
2521cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
2522cd3331d0Smrg
2523cd3331d0Smrg#if OPT_COLOR_RES
2524cd3331d0Smrg    if (GetOldColors(xw)) {
2525cd3331d0Smrg	ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
2526cd3331d0Smrg	if (xw->misc.re_verse)
2527cd3331d0Smrg	    ndx = oppositeColor(ndx);
2528cd3331d0Smrg
2529cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
2530cd3331d0Smrg
2531cd3331d0Smrg	newColors.which = 0;
2532cd3331d0Smrg	newColors.names[ndx] = NULL;
2533cd3331d0Smrg
2534cd3331d0Smrg	if (thisName != 0
2535cd3331d0Smrg	    && pOldColors->names[ndx] != 0
2536cd3331d0Smrg	    && strcmp(thisName, pOldColors->names[ndx])) {
2537cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
2538cd3331d0Smrg
2539cd3331d0Smrg	    if (newColors.which != 0) {
2540cd3331d0Smrg		ChangeColors(xw, &newColors);
2541cd3331d0Smrg		UpdateOldColors(xw, &newColors);
2542cd3331d0Smrg	    }
2543cd3331d0Smrg	}
2544cd3331d0Smrg	result = True;
2545cd3331d0Smrg    }
2546cd3331d0Smrg#endif
2547cd3331d0Smrg    return result;
2548cd3331d0Smrg}
2549cd3331d0Smrg
2550cd3331d0Smrg#if OPT_SHIFT_FONTS
2551cd3331d0Smrg/*
2552cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
2553cd3331d0Smrg *
2554cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
2555cd3331d0Smrg * the corresponding menu font entry.
2556cd3331d0Smrg */
2557cd3331d0Smrgstatic int
2558cb4a1343SmrgParseShiftedFont(XtermWidget xw, String source, String * target)
2559cd3331d0Smrg{
2560cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2561cd3331d0Smrg    int num = screen->menu_font_number;
2562cd3331d0Smrg    int rel = 0;
2563cd3331d0Smrg
2564cd3331d0Smrg    if (*++source == '+') {
2565cd3331d0Smrg	rel = 1;
2566cd3331d0Smrg	source++;
2567cd3331d0Smrg    } else if (*source == '-') {
2568cd3331d0Smrg	rel = -1;
2569cd3331d0Smrg	source++;
2570cd3331d0Smrg    }
2571cd3331d0Smrg
2572cd3331d0Smrg    if (isdigit(CharOf(*source))) {
2573cd3331d0Smrg	int val = atoi(source);
2574cd3331d0Smrg	if (rel > 0)
2575cd3331d0Smrg	    rel = val;
2576cd3331d0Smrg	else if (rel < 0)
2577cd3331d0Smrg	    rel = -val;
2578cd3331d0Smrg	else
2579cd3331d0Smrg	    num = val;
2580cd3331d0Smrg    }
2581cd3331d0Smrg
2582cd3331d0Smrg    if (rel != 0) {
2583cd3331d0Smrg	num = lookupRelativeFontSize(xw,
2584cd3331d0Smrg				     screen->menu_font_number, rel);
2585cd3331d0Smrg
2586cd3331d0Smrg    }
2587cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
2588cd3331d0Smrg    *target = source;
2589cd3331d0Smrg    return num;
2590cd3331d0Smrg}
2591cd3331d0Smrg
2592cd3331d0Smrgstatic void
2593cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
2594cd3331d0Smrg{
2595cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
2596cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
2597cd3331d0Smrg	Bool success = True;
2598cd3331d0Smrg	int num;
2599cb4a1343Smrg	String base = buf + 1;
2600cd3331d0Smrg	const char *name = 0;
2601cd3331d0Smrg	char temp[10];
2602cd3331d0Smrg
2603cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
2604cd3331d0Smrg	if (num < 0
2605cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
2606cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
2607cd3331d0Smrg	    success = False;
2608cd3331d0Smrg	} else {
2609cd3331d0Smrg#if OPT_RENDERFONT
2610cd3331d0Smrg	    if (UsingRenderFont(xw)) {
2611cd3331d0Smrg		name = getFaceName(xw, False);
2612cd3331d0Smrg	    } else
2613cd3331d0Smrg#endif
2614cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
2615cd3331d0Smrg		success = False;
2616cd3331d0Smrg	    }
2617cd3331d0Smrg	}
2618cd3331d0Smrg
2619cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
2620cd3331d0Smrg	unparseputs(xw, "50");
2621cd3331d0Smrg
2622cd3331d0Smrg	if (success) {
2623cd3331d0Smrg	    unparseputc(xw, ';');
2624cd3331d0Smrg	    if (buf >= base) {
2625cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
2626cd3331d0Smrg		if (*buf != '\0') {
2627cd3331d0Smrg		    unparseputc(xw, '#');
2628cd3331d0Smrg		    sprintf(temp, "%d", num);
2629cd3331d0Smrg		    unparseputs(xw, temp);
2630cd3331d0Smrg		    if (*name != '\0')
2631cd3331d0Smrg			unparseputc(xw, ' ');
2632cd3331d0Smrg		}
2633cd3331d0Smrg	    }
2634cd3331d0Smrg	    unparseputs(xw, name);
2635cd3331d0Smrg	}
2636cd3331d0Smrg
2637cd3331d0Smrg	unparseputc1(xw, final);
2638cd3331d0Smrg	unparse_end(xw);
2639cd3331d0Smrg    }
2640cd3331d0Smrg}
2641cd3331d0Smrg
2642cd3331d0Smrgstatic void
2643cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
2644cd3331d0Smrg{
2645cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
2646cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
2647cd3331d0Smrg	Bool success = True;
2648cd3331d0Smrg	int num;
2649cd3331d0Smrg	VTFontNames fonts;
2650cd3331d0Smrg	char *name;
2651cd3331d0Smrg
2652cd3331d0Smrg	/*
2653cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
2654cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
2655cd3331d0Smrg	 *
2656cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
2657cd3331d0Smrg	 * to load the font entry.
2658cd3331d0Smrg	 */
2659cd3331d0Smrg	if (*buf == '#') {
2660cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
2661cd3331d0Smrg
2662cd3331d0Smrg	    if (num < 0
2663cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
2664cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
2665cd3331d0Smrg		success = False;
2666cd3331d0Smrg	    } else {
2667cd3331d0Smrg		/*
2668cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
2669cd3331d0Smrg		 * for a font specification within the control.
2670cd3331d0Smrg		 */
2671cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
2672cd3331d0Smrg		    ++buf;
2673cd3331d0Smrg		}
2674cd3331d0Smrg		while (isspace(CharOf(*buf))) {
2675cd3331d0Smrg		    ++buf;
2676cd3331d0Smrg		}
2677cd3331d0Smrg#if OPT_RENDERFONT
2678cd3331d0Smrg		if (UsingRenderFont(xw)) {
2679c219fbebSmrg		    /* EMPTY */
2680c219fbebSmrg		    /* there is only one font entry to load */
2681c219fbebSmrg		    ;
2682cd3331d0Smrg		} else
2683cd3331d0Smrg#endif
2684cd3331d0Smrg		{
2685cd3331d0Smrg		    /*
2686cd3331d0Smrg		     * Normally there is no font specified in the control.
2687cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
2688cd3331d0Smrg		     */
2689cd3331d0Smrg		    if (*buf == '\0') {
2690cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
2691cd3331d0Smrg			    success = False;
2692cd3331d0Smrg			}
2693cd3331d0Smrg		    }
2694cd3331d0Smrg		}
2695cd3331d0Smrg	    }
2696cd3331d0Smrg	} else {
2697cd3331d0Smrg	    num = screen->menu_font_number;
2698cd3331d0Smrg	}
2699cd3331d0Smrg	name = x_strtrim(buf);
2700cd3331d0Smrg	if (success && !IsEmpty(name)) {
2701cd3331d0Smrg#if OPT_RENDERFONT
2702cd3331d0Smrg	    if (UsingRenderFont(xw)) {
2703cd3331d0Smrg		setFaceName(xw, name);
2704cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
2705cd3331d0Smrg	    } else
2706cd3331d0Smrg#endif
2707cd3331d0Smrg	    {
2708cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
2709cd3331d0Smrg		fonts.f_n = name;
2710cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
2711cd3331d0Smrg	    }
2712cd3331d0Smrg	} else {
2713cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
2714cd3331d0Smrg	}
2715cd3331d0Smrg	free(name);
2716cd3331d0Smrg    }
2717cd3331d0Smrg}
2718cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
2719cd3331d0Smrg
2720d522f475Smrg/***====================================================================***/
2721d522f475Smrg
2722d522f475Smrgvoid
2723cd3331d0Smrgdo_osc(XtermWidget xw, Char * oscbuf, size_t len, int final)
2724d522f475Smrg{
2725cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2726d522f475Smrg    int mode;
2727d522f475Smrg    Char *cp;
2728d522f475Smrg    int state = 0;
2729d522f475Smrg    char *buf = 0;
2730cd3331d0Smrg    char temp[2];
2731cd3331d0Smrg#if OPT_ISO_COLORS
2732cd3331d0Smrg    int ansi_colors = 0;
2733cd3331d0Smrg#endif
2734cd3331d0Smrg    Bool need_data = True;
2735d522f475Smrg
2736d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
2737d522f475Smrg
2738712a7ff4Smrg    (void) screen;
2739712a7ff4Smrg
2740d522f475Smrg    /*
2741d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
2742d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
2743d522f475Smrg     * with the same final character as the application sends to make this
2744d522f475Smrg     * work better with shell scripts, which may have trouble reading an
2745d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
2746d522f475Smrg     */
2747d522f475Smrg    mode = 0;
2748d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
2749d522f475Smrg	switch (state) {
2750d522f475Smrg	case 0:
2751d522f475Smrg	    if (isdigit(*cp)) {
2752d522f475Smrg		mode = 10 * mode + (*cp - '0');
2753d522f475Smrg		if (mode > 65535) {
2754d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
2755d522f475Smrg		    return;
2756d522f475Smrg		}
2757d522f475Smrg		break;
2758d522f475Smrg	    }
2759d522f475Smrg	    /* FALLTHRU */
2760d522f475Smrg	case 1:
2761d522f475Smrg	    if (*cp != ';') {
2762cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
2763cd3331d0Smrg		       (int) (cp - oscbuf)));
2764d522f475Smrg		return;
2765d522f475Smrg	    }
2766d522f475Smrg	    state = 2;
2767d522f475Smrg	    break;
2768d522f475Smrg	case 2:
2769d522f475Smrg	    buf = (char *) cp;
2770d522f475Smrg	    state = 3;
2771d522f475Smrg	    /* FALLTHRU */
2772d522f475Smrg	default:
2773cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
2774d522f475Smrg		switch (mode) {
2775d522f475Smrg		case 0:
2776d522f475Smrg		case 1:
2777d522f475Smrg		case 2:
2778d522f475Smrg		    break;
2779d522f475Smrg		default:
2780d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
2781d522f475Smrg			   CharOf(*cp),
2782cd3331d0Smrg			   (int) (cp - oscbuf)));
2783d522f475Smrg		    return;
2784d522f475Smrg		}
2785d522f475Smrg	    }
2786d522f475Smrg	}
2787d522f475Smrg    }
2788cd3331d0Smrg
2789cd3331d0Smrg    /*
2790cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
2791cd3331d0Smrg     * a special case.
2792cd3331d0Smrg     */
2793cd3331d0Smrg    switch (mode) {
2794cd3331d0Smrg#if OPT_ISO_COLORS
2795cd3331d0Smrg    case OSC_Reset(4):
2796cd3331d0Smrg    case OSC_Reset(5):
2797cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
2798cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
2799cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
2800cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
2801cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
2802cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
2803cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
2804cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
2805cd3331d0Smrg#endif
2806cd3331d0Smrg#if OPT_TEK4014
2807cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
2808cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
2809cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
2810cd3331d0Smrg#endif
2811cd3331d0Smrg	need_data = False;
2812cd3331d0Smrg	break;
2813cd3331d0Smrg#endif
2814cd3331d0Smrg    default:
2815cd3331d0Smrg	break;
2816cd3331d0Smrg    }
2817cd3331d0Smrg
2818cd3331d0Smrg    /*
2819cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
2820cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
2821cd3331d0Smrg     */
2822cd3331d0Smrg    if (IsEmpty(buf)) {
2823cd3331d0Smrg	if (need_data) {
2824cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
2825cd3331d0Smrg	    return;
2826cd3331d0Smrg	}
2827cd3331d0Smrg	temp[0] = '\0';
2828cd3331d0Smrg	buf = temp;
2829cd3331d0Smrg    } else if (!need_data) {
2830cd3331d0Smrg	TRACE(("do_osc found found unwanted data\n"));
2831d522f475Smrg	return;
28320d92cbfdSchristos    }
2833d522f475Smrg
2834d522f475Smrg    switch (mode) {
2835d522f475Smrg    case 0:			/* new icon name and title */
2836b7c89284Ssnj	ChangeIconName(xw, buf);
2837b7c89284Ssnj	ChangeTitle(xw, buf);
2838d522f475Smrg	break;
2839d522f475Smrg
2840d522f475Smrg    case 1:			/* new icon name only */
2841b7c89284Ssnj	ChangeIconName(xw, buf);
2842d522f475Smrg	break;
2843d522f475Smrg
2844d522f475Smrg    case 2:			/* new title only */
2845b7c89284Ssnj	ChangeTitle(xw, buf);
2846d522f475Smrg	break;
2847d522f475Smrg
284822d8e007Schristos#ifdef notdef
2849d522f475Smrg    case 3:			/* change X property */
2850cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
28510d92cbfdSchristos	    ChangeXprop(buf);
2852d522f475Smrg	break;
285322d8e007Schristos#endif
2854d522f475Smrg#if OPT_ISO_COLORS
2855cd3331d0Smrg    case 5:
2856cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
2857cd3331d0Smrg	/* FALLTHRU */
2858d522f475Smrg    case 4:
2859cd3331d0Smrg	if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final))
2860cd3331d0Smrg	    xtermRepaint(xw);
2861cd3331d0Smrg	break;
2862cd3331d0Smrg    case OSC_Reset(5):
2863cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
2864cd3331d0Smrg	/* FALLTHRU */
2865cd3331d0Smrg    case OSC_Reset(4):
2866cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
2867cd3331d0Smrg	    xtermRepaint(xw);
2868d522f475Smrg	break;
2869d522f475Smrg#endif
2870d522f475Smrg    case OSC_TEXT_FG:
2871d522f475Smrg    case OSC_TEXT_BG:
2872d522f475Smrg    case OSC_TEXT_CURSOR:
2873d522f475Smrg    case OSC_MOUSE_FG:
2874d522f475Smrg    case OSC_MOUSE_BG:
2875d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2876d522f475Smrg    case OSC_HIGHLIGHT_BG:
2877cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
2878d522f475Smrg#endif
2879d522f475Smrg#if OPT_TEK4014
2880d522f475Smrg    case OSC_TEK_FG:
2881d522f475Smrg    case OSC_TEK_BG:
2882d522f475Smrg    case OSC_TEK_CURSOR:
2883d522f475Smrg#endif
2884cd3331d0Smrg	if (xw->misc.dynamicColors) {
2885d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
2886cd3331d0Smrg	}
2887cd3331d0Smrg	break;
2888cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
2889cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
2890cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
2891cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
2892cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
2893cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
2894cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
2895cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
2896cd3331d0Smrg#endif
2897cd3331d0Smrg#if OPT_TEK4014
2898cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
2899cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
2900cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
2901cd3331d0Smrg#endif
2902cd3331d0Smrg	if (xw->misc.dynamicColors) {
2903cd3331d0Smrg	    ResetColorsRequest(xw, mode);
2904cd3331d0Smrg	}
2905d522f475Smrg	break;
2906d522f475Smrg
2907d522f475Smrg    case 30:
2908d522f475Smrg    case 31:
2909d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
2910d522f475Smrg	break;
2911d522f475Smrg
2912d522f475Smrg#ifdef ALLOWLOGGING
2913d522f475Smrg    case 46:			/* new log file */
2914d522f475Smrg#ifdef ALLOWLOGFILECHANGES
2915d522f475Smrg	/*
2916d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
2917d522f475Smrg	 * arbitrary files accessible to the person running xterm.
2918d522f475Smrg	 */
2919cd3331d0Smrg	if (strcmp(buf, "?")
2920d522f475Smrg	    && (cp = CastMallocN(char, strlen(buf)) != NULL)) {
2921d522f475Smrg	    strcpy(cp, buf);
2922d522f475Smrg	    if (screen->logfile)
2923d522f475Smrg		free(screen->logfile);
2924d522f475Smrg	    screen->logfile = cp;
2925d522f475Smrg	    break;
2926d522f475Smrg	}
2927d522f475Smrg#endif
2928cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2929cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2930d522f475Smrg	break;
2931d522f475Smrg#endif /* ALLOWLOGGING */
2932d522f475Smrg
2933d522f475Smrg    case 50:
2934d522f475Smrg#if OPT_SHIFT_FONTS
2935cd3331d0Smrg	if (*buf == '?') {
2936cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
2937cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
2938cd3331d0Smrg	    ChangeFontRequest(xw, buf);
2939d522f475Smrg	}
2940d522f475Smrg#endif /* OPT_SHIFT_FONTS */
2941d522f475Smrg	break;
2942d522f475Smrg    case 51:
2943d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
2944d522f475Smrg	break;
2945d522f475Smrg
2946d522f475Smrg#if OPT_PASTE64
2947d522f475Smrg    case 52:
2948cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
2949d522f475Smrg	break;
2950d522f475Smrg#endif
2951d522f475Smrg	/*
2952d522f475Smrg	 * One could write code to send back the display and host names,
2953d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
2954d522f475Smrg	 */
2955cd3331d0Smrg    default:
2956cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
2957cd3331d0Smrg	break;
2958d522f475Smrg    }
2959d522f475Smrg    unparse_end(xw);
2960d522f475Smrg}
2961d522f475Smrg
2962d522f475Smrg#ifdef SunXK_F36
2963d522f475Smrg#define MAX_UDK 37
2964d522f475Smrg#else
2965d522f475Smrg#define MAX_UDK 35
2966d522f475Smrg#endif
2967d522f475Smrgstatic struct {
2968d522f475Smrg    char *str;
2969d522f475Smrg    int len;
2970d522f475Smrg} user_keys[MAX_UDK];
2971d522f475Smrg
2972d522f475Smrg/*
2973d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
2974d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
2975d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
2976d522f475Smrg * "real" terminals accept commas in the string definitions).
2977d522f475Smrg */
2978d522f475Smrgstatic int
2979cd3331d0Smrgudk_value(const char **cp)
2980d522f475Smrg{
2981cd3331d0Smrg    int result = -1;
2982d522f475Smrg    int c;
2983d522f475Smrg
2984d522f475Smrg    for (;;) {
2985d522f475Smrg	if ((c = **cp) != '\0')
2986d522f475Smrg	    *cp = *cp + 1;
2987d522f475Smrg	if (c == ';' || c == '\0')
2988cd3331d0Smrg	    break;
2989cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
2990cd3331d0Smrg	    break;
2991d522f475Smrg    }
2992cd3331d0Smrg
2993cd3331d0Smrg    return result;
2994d522f475Smrg}
2995d522f475Smrg
2996d522f475Smrgvoid
2997d522f475Smrgreset_decudk(void)
2998d522f475Smrg{
2999d522f475Smrg    int n;
3000d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
3001d522f475Smrg	if (user_keys[n].str != 0) {
3002d522f475Smrg	    free(user_keys[n].str);
3003d522f475Smrg	    user_keys[n].str = 0;
3004d522f475Smrg	    user_keys[n].len = 0;
3005d522f475Smrg	}
3006d522f475Smrg    }
3007d522f475Smrg}
3008d522f475Smrg
3009d522f475Smrg/*
3010d522f475Smrg * Parse the data for DECUDK (user-defined keys).
3011d522f475Smrg */
3012d522f475Smrgstatic void
3013cd3331d0Smrgparse_decudk(const char *cp)
3014d522f475Smrg{
3015d522f475Smrg    while (*cp) {
3016cd3331d0Smrg	const char *base = cp;
3017d522f475Smrg	char *str = CastMallocN(char, strlen(cp) + 1);
3018d522f475Smrg	unsigned key = 0;
3019d522f475Smrg	int lo, hi;
3020d522f475Smrg	int len = 0;
3021d522f475Smrg
3022d522f475Smrg	while (isdigit(CharOf(*cp)))
30230d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
3024d522f475Smrg	if (*cp == '/') {
3025d522f475Smrg	    cp++;
3026d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
3027d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
30280d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
3029d522f475Smrg	    }
3030d522f475Smrg	}
3031d522f475Smrg	if (len > 0 && key < MAX_UDK) {
3032d522f475Smrg	    if (user_keys[key].str != 0)
3033d522f475Smrg		free(user_keys[key].str);
3034d522f475Smrg	    user_keys[key].str = str;
3035d522f475Smrg	    user_keys[key].len = len;
3036d522f475Smrg	} else {
3037d522f475Smrg	    free(str);
3038d522f475Smrg	}
3039d522f475Smrg	if (*cp == ';')
3040d522f475Smrg	    cp++;
3041d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
3042d522f475Smrg	    break;
3043d522f475Smrg    }
3044d522f475Smrg}
3045d522f475Smrg
3046d522f475Smrg#if OPT_TRACE
3047d522f475Smrg#define SOFT_WIDE 10
3048d522f475Smrg#define SOFT_HIGH 20
3049d522f475Smrg
3050d522f475Smrgstatic void
3051cd3331d0Smrgparse_decdld(ANSI * params, const char *string)
3052d522f475Smrg{
3053d522f475Smrg    char DscsName[8];
3054d522f475Smrg    int len;
3055d522f475Smrg    int Pfn = params->a_param[0];
3056d522f475Smrg    int Pcn = params->a_param[1];
3057d522f475Smrg    int Pe = params->a_param[2];
3058d522f475Smrg    int Pcmw = params->a_param[3];
3059d522f475Smrg    int Pw = params->a_param[4];
3060d522f475Smrg    int Pt = params->a_param[5];
3061d522f475Smrg    int Pcmh = params->a_param[6];
3062d522f475Smrg    int Pcss = params->a_param[7];
3063d522f475Smrg
3064d522f475Smrg    int start_char = Pcn + 0x20;
3065d522f475Smrg    int char_wide = ((Pcmw == 0)
3066d522f475Smrg		     ? (Pcss ? 6 : 10)
3067d522f475Smrg		     : (Pcmw > 4
3068d522f475Smrg			? Pcmw
3069d522f475Smrg			: (Pcmw + 3)));
3070d522f475Smrg    int char_high = ((Pcmh == 0)
3071d522f475Smrg		     ? ((Pcmw >= 2 || Pcmw <= 4)
3072d522f475Smrg			? 10
3073d522f475Smrg			: 20)
3074d522f475Smrg		     : Pcmh);
3075d522f475Smrg    Char ch;
3076d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
3077d522f475Smrg    Bool first = True;
3078d522f475Smrg    Bool prior = False;
3079d522f475Smrg    int row = 0, col = 0;
3080d522f475Smrg
3081d522f475Smrg    TRACE(("Parsing DECDLD\n"));
3082d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
3083d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
3084d522f475Smrg    TRACE(("  erase control %d\n", Pe));
3085d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
3086d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
3087d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
3088d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
3089d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
3090d522f475Smrg
3091d522f475Smrg    if (Pfn > 1
3092d522f475Smrg	|| Pcn > 95
3093d522f475Smrg	|| Pe > 2
3094d522f475Smrg	|| Pcmw > 10
3095d522f475Smrg	|| Pcmw == 1
3096d522f475Smrg	|| Pt > 2
3097d522f475Smrg	|| Pcmh > 20
3098d522f475Smrg	|| Pcss > 1
3099d522f475Smrg	|| char_wide > SOFT_WIDE
3100d522f475Smrg	|| char_high > SOFT_HIGH) {
3101d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
3102d522f475Smrg	return;
3103d522f475Smrg    }
3104d522f475Smrg
3105d522f475Smrg    len = 0;
3106d522f475Smrg    while (*string != '\0') {
3107d522f475Smrg	ch = CharOf(*string++);
3108d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
3109d522f475Smrg	    if (len < 2)
3110b7c89284Ssnj		DscsName[len++] = (char) ch;
3111d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
3112b7c89284Ssnj	    DscsName[len++] = (char) ch;
3113d522f475Smrg	    break;
3114d522f475Smrg	}
3115d522f475Smrg    }
3116d522f475Smrg    DscsName[len] = 0;
3117d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
3118d522f475Smrg
3119d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
3120d522f475Smrg    while (*string != '\0') {
3121d522f475Smrg	if (first) {
3122d522f475Smrg	    TRACE(("Char %d:\n", start_char));
3123d522f475Smrg	    if (prior) {
3124d522f475Smrg		for (row = 0; row < char_high; ++row) {
3125d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
3126d522f475Smrg		}
3127d522f475Smrg	    }
3128d522f475Smrg	    prior = False;
3129d522f475Smrg	    first = False;
3130d522f475Smrg	    for (row = 0; row < char_high; ++row) {
3131d522f475Smrg		for (col = 0; col < char_wide; ++col) {
3132d522f475Smrg		    bits[row][col] = '.';
3133d522f475Smrg		}
3134d522f475Smrg	    }
3135d522f475Smrg	    row = col = 0;
3136d522f475Smrg	}
3137d522f475Smrg	ch = CharOf(*string++);
3138d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
3139d522f475Smrg	    int n;
3140d522f475Smrg
3141b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
3142d522f475Smrg	    for (n = 0; n < 6; ++n) {
3143b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
3144d522f475Smrg	    }
3145d522f475Smrg	    col += 1;
3146d522f475Smrg	    prior = True;
3147d522f475Smrg	} else if (ch == '/') {
3148d522f475Smrg	    row += 6;
3149d522f475Smrg	    col = 0;
3150d522f475Smrg	} else if (ch == ';') {
3151d522f475Smrg	    first = True;
3152d522f475Smrg	    ++start_char;
3153d522f475Smrg	}
3154d522f475Smrg    }
3155d522f475Smrg}
3156d522f475Smrg#else
3157d522f475Smrg#define parse_decdld(p,q)	/* nothing */
3158d522f475Smrg#endif
3159d522f475Smrg
3160d522f475Smrg/*
3161d522f475Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
3162d522f475Smrg * interspersing with control characters, but have the string already.
3163d522f475Smrg */
3164d522f475Smrgstatic void
3165cd3331d0Smrgparse_ansi_params(ANSI * params, const char **string)
3166d522f475Smrg{
3167cd3331d0Smrg    const char *cp = *string;
3168b7c89284Ssnj    ParmType nparam = 0;
3169d522f475Smrg
3170d522f475Smrg    memset(params, 0, sizeof(*params));
3171d522f475Smrg    while (*cp != '\0') {
3172d522f475Smrg	Char ch = CharOf(*cp++);
3173d522f475Smrg
3174d522f475Smrg	if (isdigit(ch)) {
3175d522f475Smrg	    if (nparam < NPARAM) {
3176b7c89284Ssnj		params->a_param[nparam] =
3177b7c89284Ssnj		    (ParmType) ((params->a_param[nparam] * 10)
3178b7c89284Ssnj				+ (ch - '0'));
3179d522f475Smrg	    }
3180d522f475Smrg	} else if (ch == ';') {
3181d522f475Smrg	    if (++nparam < NPARAM)
3182d522f475Smrg		params->a_nparam = nparam;
3183d522f475Smrg	} else if (ch < 32) {
3184c219fbebSmrg	    /* EMPTY */ ;
3185d522f475Smrg	} else {
3186d522f475Smrg	    /* should be 0x30 to 0x7e */
3187d522f475Smrg	    params->a_final = ch;
3188d522f475Smrg	    break;
3189d522f475Smrg	}
3190d522f475Smrg    }
3191d522f475Smrg    *string = cp;
3192d522f475Smrg}
3193d522f475Smrg
3194d522f475Smrgvoid
3195d522f475Smrgdo_dcs(XtermWidget xw, Char * dcsbuf, size_t dcslen)
3196d522f475Smrg{
3197cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3198d522f475Smrg    char reply[BUFSIZ];
3199cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
3200d522f475Smrg    Bool okay;
3201d522f475Smrg    ANSI params;
3202d522f475Smrg
3203cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
3204d522f475Smrg
3205d522f475Smrg    if (dcslen != strlen(cp))
3206d522f475Smrg	/* shouldn't have nulls in the string */
3207d522f475Smrg	return;
3208d522f475Smrg
3209d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
3210d522f475Smrg    case '$':			/* DECRQSS */
3211d522f475Smrg	okay = True;
3212d522f475Smrg
3213d522f475Smrg	cp++;
3214d522f475Smrg	if (*cp++ == 'q') {
3215d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
3216d522f475Smrg		sprintf(reply, "%d%s",
3217d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
3218d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
3219d522f475Smrg			cp);
3220d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
3221d522f475Smrg		sprintf(reply, "%d%s%s",
3222d522f475Smrg			(screen->vtXX_level ?
3223d522f475Smrg			 screen->vtXX_level : 1) + 60,
3224d522f475Smrg			(screen->vtXX_level >= 2)
3225d522f475Smrg			? (screen->control_eight_bits
3226d522f475Smrg			   ? ";0" : ";1")
3227d522f475Smrg			: "",
3228d522f475Smrg			cp);
3229d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
3230d522f475Smrg		sprintf(reply, "%d;%dr",
3231d522f475Smrg			screen->top_marg + 1,
3232d522f475Smrg			screen->bot_marg + 1);
3233d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
3234d522f475Smrg		strcpy(reply, "0");
3235d522f475Smrg		if (xw->flags & BOLD)
3236d522f475Smrg		    strcat(reply, ";1");
3237d522f475Smrg		if (xw->flags & UNDERLINE)
3238d522f475Smrg		    strcat(reply, ";4");
3239d522f475Smrg		if (xw->flags & BLINK)
3240d522f475Smrg		    strcat(reply, ";5");
3241d522f475Smrg		if (xw->flags & INVERSE)
3242d522f475Smrg		    strcat(reply, ";7");
3243d522f475Smrg		if (xw->flags & INVISIBLE)
3244d522f475Smrg		    strcat(reply, ";8");
3245b7c89284Ssnj#if OPT_256_COLORS || OPT_88_COLORS
3246b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
3247d522f475Smrg		    if (xw->flags & FG_COLOR) {
3248d522f475Smrg			if (xw->cur_foreground >= 16)
3249d522f475Smrg			    sprintf(reply + strlen(reply),
3250d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
3251d522f475Smrg			else
3252d522f475Smrg			    sprintf(reply + strlen(reply),
3253d522f475Smrg				    ";%d%d",
3254d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
3255d522f475Smrg				    xw->cur_foreground >= 8 ?
3256d522f475Smrg				    xw->cur_foreground - 8 :
3257d522f475Smrg				    xw->cur_foreground);
3258d522f475Smrg		    }
3259d522f475Smrg		    if (xw->flags & BG_COLOR) {
3260d522f475Smrg			if (xw->cur_background >= 16)
3261d522f475Smrg			    sprintf(reply + strlen(reply),
3262d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
3263d522f475Smrg			else
3264d522f475Smrg			    sprintf(reply + strlen(reply),
3265d522f475Smrg				    ";%d%d",
3266d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
3267d522f475Smrg				    xw->cur_background >= 8 ?
3268d522f475Smrg				    xw->cur_background - 8 :
3269d522f475Smrg				    xw->cur_background);
3270d522f475Smrg		    }
3271d522f475Smrg		});
3272b7c89284Ssnj#elif OPT_ISO_COLORS
3273b7c89284Ssnj		if_OPT_ISO_COLORS(screen, {
3274d522f475Smrg		    if (xw->flags & FG_COLOR)
3275d522f475Smrg			sprintf(reply + strlen(reply),
3276d522f475Smrg				";%d%d",
3277d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
3278d522f475Smrg				xw->cur_foreground >= 8 ?
3279d522f475Smrg				xw->cur_foreground - 8 :
3280d522f475Smrg				xw->cur_foreground);
3281d522f475Smrg		    if (xw->flags & BG_COLOR)
3282d522f475Smrg			sprintf(reply + strlen(reply),
3283d522f475Smrg				";%d%d",
3284d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
3285d522f475Smrg				xw->cur_background >= 8 ?
3286d522f475Smrg				xw->cur_background - 8 :
3287d522f475Smrg				xw->cur_background);
3288d522f475Smrg		});
3289b7c89284Ssnj#endif
3290d522f475Smrg		strcat(reply, "m");
3291712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
3292712a7ff4Smrg		int code = 0;
3293712a7ff4Smrg		if (screen->cursor_underline)
3294712a7ff4Smrg		    code |= 2;
3295712a7ff4Smrg		if (screen->cursor_blink)
3296712a7ff4Smrg		    code |= 1;
3297712a7ff4Smrg		sprintf(reply, "%d%s", code + 1, cp);
3298d522f475Smrg	    } else
3299d522f475Smrg		okay = False;
3300d522f475Smrg
330122d8e007Schristos	    if (okay) {
33020d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
33030d92cbfdSchristos		unparseputc(xw, okay ? '1' : '0');
33040d92cbfdSchristos		unparseputc(xw, '$');
33050d92cbfdSchristos		unparseputc(xw, 'r');
3306d522f475Smrg		cp = reply;
330722d8e007Schristos		unparseputs(xw, cp);
33080d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
33090d92cbfdSchristos	    } else {
33100d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
331122d8e007Schristos	    }
3312d522f475Smrg	} else {
3313d522f475Smrg	    unparseputc(xw, ANSI_CAN);
3314d522f475Smrg	}
3315d522f475Smrg	break;
3316d522f475Smrg#if OPT_TCAP_QUERY
3317d522f475Smrg    case '+':
3318d522f475Smrg	cp++;
3319cd3331d0Smrg	switch (*cp) {
3320cd3331d0Smrg	case 'p':
3321cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
3322cd3331d0Smrg		set_termcap(xw, cp + 1);
3323cd3331d0Smrg	    }
3324cd3331d0Smrg	    break;
3325cd3331d0Smrg	case 'q':
3326cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
3327cd3331d0Smrg		Bool fkey;
3328cd3331d0Smrg		unsigned state;
3329cd3331d0Smrg		int code;
3330cd3331d0Smrg		const char *tmp;
3331cd3331d0Smrg		const char *parsed = ++cp;
3332d522f475Smrg
3333cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
3334d522f475Smrg
3335cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
3336b7c89284Ssnj
3337cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
3338d522f475Smrg
3339cd3331d0Smrg		unparseputc(xw, '+');
3340cd3331d0Smrg		unparseputc(xw, 'r');
3341d522f475Smrg
3342cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
3343cd3331d0Smrg		    if (cp == parsed)
3344cd3331d0Smrg			break;	/* no data found, error */
3345d522f475Smrg
3346cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
3347cd3331d0Smrg			unparseputc(xw, *tmp);
3348d522f475Smrg
3349cd3331d0Smrg		    if (code >= 0) {
3350cd3331d0Smrg			unparseputc(xw, '=');
3351cd3331d0Smrg			screen->tc_query_code = code;
3352cd3331d0Smrg			screen->tc_query_fkey = fkey;
3353d522f475Smrg#if OPT_ISO_COLORS
3354cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
3355cd3331d0Smrg			 * number of colors) */
3356cd3331d0Smrg			if (code == XK_COLORS) {
3357cd3331d0Smrg			    unparseputn(xw, NUM_ANSI_COLORS);
3358cd3331d0Smrg			} else
3359cd3331d0Smrg#endif
3360cd3331d0Smrg			if (code == XK_TCAPNAME) {
3361c219fbebSmrg			    unparseputs(xw, resource.term_name);
3362cd3331d0Smrg			} else {
3363cd3331d0Smrg			    XKeyEvent event;
3364cd3331d0Smrg			    event.state = state;
3365cd3331d0Smrg			    Input(xw, &event, False);
3366cd3331d0Smrg			}
3367cd3331d0Smrg			screen->tc_query_code = -1;
3368cd3331d0Smrg		    } else {
3369cd3331d0Smrg			break;	/* no match found, error */
3370d522f475Smrg		    }
3371d522f475Smrg
3372d522f475Smrg		    cp = parsed;
3373cd3331d0Smrg		    if (*parsed == ';') {
3374cd3331d0Smrg			unparseputc(xw, *parsed++);
3375cd3331d0Smrg			cp = parsed;
3376cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
3377cd3331d0Smrg		    }
3378d522f475Smrg		}
3379cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
3380d522f475Smrg	    }
3381cd3331d0Smrg	    break;
3382d522f475Smrg	}
3383d522f475Smrg	break;
3384d522f475Smrg#endif
3385d522f475Smrg    default:
33860d92cbfdSchristos	if (screen->terminal_id >= 200) {	/* VT220 */
33870d92cbfdSchristos	    parse_ansi_params(&params, &cp);
33880d92cbfdSchristos	    switch (params.a_final) {
33890d92cbfdSchristos	    case '|':		/* DECUDK */
33900d92cbfdSchristos		if (params.a_param[0] == 0)
33910d92cbfdSchristos		    reset_decudk();
33920d92cbfdSchristos		parse_decudk(cp);
33930d92cbfdSchristos		break;
33940d92cbfdSchristos	    case '{':		/* DECDLD (no '}' case though) */
33950d92cbfdSchristos		parse_decdld(&params, cp);
33960d92cbfdSchristos		break;
33970d92cbfdSchristos	    }
3398d522f475Smrg	}
3399d522f475Smrg	break;
3400d522f475Smrg    }
3401d522f475Smrg    unparse_end(xw);
3402d522f475Smrg}
3403d522f475Smrg
3404cb4a1343Smrg#if OPT_DEC_RECTOPS
3405cb4a1343Smrgenum {
3406cb4a1343Smrg    mdUnknown = 0,
3407cb4a1343Smrg    mdMaybeSet = 1,
3408cb4a1343Smrg    mdMaybeReset = 2,
3409cb4a1343Smrg    mdAlwaysSet = 3,
3410cb4a1343Smrg    mdAlwaysReset = 4
3411cb4a1343Smrg};
3412cb4a1343Smrg
3413cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
3414cb4a1343Smrg#define MdFlag(mode,flag) MdBool(xw->keyboard.flags & MODE_KAM)
3415cb4a1343Smrg
3416cb4a1343Smrg/*
3417cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
3418cb4a1343Smrg * 0 - not recognized
3419cb4a1343Smrg * 1 - set
3420cb4a1343Smrg * 2 - reset
3421cb4a1343Smrg * 3 - permanently set
3422cb4a1343Smrg * 4 - permanently reset
3423cb4a1343Smrg * Only one mode can be reported at a time.
3424cb4a1343Smrg */
3425cb4a1343Smrgvoid
3426cb4a1343Smrgdo_rpm(XtermWidget xw, int nparams, int *params)
3427cb4a1343Smrg{
3428cb4a1343Smrg    ANSI reply;
3429cb4a1343Smrg    int result = 0;
3430cb4a1343Smrg    int count = 0;
3431cb4a1343Smrg
3432cb4a1343Smrg    TRACE(("do_rpm %d:%d\n", nparams, params[0]));
3433cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
3434cb4a1343Smrg    if (nparams >= 1) {
3435cb4a1343Smrg	switch (params[0]) {
3436cb4a1343Smrg	case 1:		/* GATM */
3437cb4a1343Smrg	    result = mdAlwaysReset;
3438cb4a1343Smrg	    break;
3439cb4a1343Smrg	case 2:
3440cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
3441cb4a1343Smrg	    break;
3442cb4a1343Smrg	case 3:		/* CRM */
3443cb4a1343Smrg	    result = mdMaybeReset;
3444cb4a1343Smrg	    break;
3445cb4a1343Smrg	case 4:
3446cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
3447cb4a1343Smrg	    break;
3448cb4a1343Smrg	case 5:		/* SRTM */
3449cb4a1343Smrg	case 7:		/* VEM */
3450cb4a1343Smrg	case 10:		/* HEM */
3451cb4a1343Smrg	case 11:		/* PUM */
3452cb4a1343Smrg	    result = mdAlwaysReset;
3453cb4a1343Smrg	    break;
3454cb4a1343Smrg	case 12:
3455cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
3456cb4a1343Smrg	    break;
3457cb4a1343Smrg	case 13:		/* FEAM */
3458cb4a1343Smrg	case 14:		/* FETM */
3459cb4a1343Smrg	case 15:		/* MATM */
3460cb4a1343Smrg	case 16:		/* TTM */
3461cb4a1343Smrg	case 17:		/* SATM */
3462cb4a1343Smrg	case 18:		/* TSM */
3463cb4a1343Smrg	case 19:		/* EBM */
3464cb4a1343Smrg	    result = mdAlwaysReset;
3465cb4a1343Smrg	    break;
3466cb4a1343Smrg	case 20:
3467cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
3468cb4a1343Smrg	    break;
3469cb4a1343Smrg	}
3470cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
3471cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
3472cb4a1343Smrg    }
3473cb4a1343Smrg    reply.a_type = ANSI_CSI;
3474cb4a1343Smrg    reply.a_nparam = (ParmType) count;
3475cb4a1343Smrg    reply.a_inters = '$';
3476cb4a1343Smrg    reply.a_final = 'y';
3477cb4a1343Smrg    unparseseq(xw, &reply);
3478cb4a1343Smrg}
3479cb4a1343Smrg
3480cb4a1343Smrgvoid
3481cb4a1343Smrgdo_decrpm(XtermWidget xw, int nparams, int *params)
3482cb4a1343Smrg{
3483cb4a1343Smrg    ANSI reply;
3484cb4a1343Smrg    int result = 0;
3485cb4a1343Smrg    int count = 0;
3486cb4a1343Smrg
3487cb4a1343Smrg    TRACE(("do_decrpm %d:%d\n", nparams, params[0]));
3488cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
3489cb4a1343Smrg    if (nparams >= 1) {
3490cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
3491cb4a1343Smrg
3492cb4a1343Smrg	switch (params[0]) {
3493cb4a1343Smrg	case 1:		/* DECCKM                       */
3494cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
3495cb4a1343Smrg	    break;
3496cb4a1343Smrg	case 2:		/* DECANM - ANSI/VT52 mode      */
3497cb4a1343Smrg#if OPT_VT52_MODE
3498cb4a1343Smrg	    result = MdBool(screen->terminal_id >= 100);
3499cb4a1343Smrg#else
3500cb4a1343Smrg	    result = mdMaybeSet;
3501cb4a1343Smrg#endif
3502cb4a1343Smrg	    break;
3503cb4a1343Smrg	case 3:		/* DECCOLM                      */
3504cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
3505cb4a1343Smrg	    break;
3506cb4a1343Smrg	case 4:		/* DECSCLM (slow scroll)        */
3507cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
3508cb4a1343Smrg	    break;
3509cb4a1343Smrg	case 5:		/* DECSCNM                      */
3510cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
3511cb4a1343Smrg	    break;
3512cb4a1343Smrg	case 6:		/* DECOM                        */
3513cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
3514cb4a1343Smrg	    break;
3515cb4a1343Smrg	case 7:		/* DECAWM                       */
3516cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
3517cb4a1343Smrg	    break;
3518cb4a1343Smrg	case 8:		/* DECARM                       */
3519cb4a1343Smrg	    result = mdAlwaysReset;
3520cb4a1343Smrg	    break;
3521cb4a1343Smrg	case SET_X10_MOUSE:	/* X10 mouse                    */
3522cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
3523cb4a1343Smrg	    break;
3524cb4a1343Smrg#if OPT_TOOLBAR
3525cb4a1343Smrg	case 10:		/* rxvt */
3526cb4a1343Smrg	    result = MdBool(resource.toolBar);
3527cb4a1343Smrg	    break;
3528cb4a1343Smrg#endif
3529cb4a1343Smrg#if OPT_BLINK_CURS
3530cb4a1343Smrg	case 12:		/* att610: Start/stop blinking cursor */
3531cb4a1343Smrg	    result = MdBool(screen->cursor_blink_res);
3532cb4a1343Smrg	    break;
3533cb4a1343Smrg#endif
3534cb4a1343Smrg	case 18:		/* DECPFF: print form feed */
3535712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
3536cb4a1343Smrg	    break;
3537cb4a1343Smrg	case 19:		/* DECPEX: print extent */
3538712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
3539cb4a1343Smrg	    break;
3540cb4a1343Smrg	case 25:		/* DECTCEM: Show/hide cursor (VT200) */
3541cb4a1343Smrg	    result = MdBool(screen->cursor_set);
3542cb4a1343Smrg	    break;
3543cb4a1343Smrg	case 30:		/* rxvt */
3544cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
3545cb4a1343Smrg	    break;
3546cb4a1343Smrg#if OPT_SHIFT_FONTS
3547cb4a1343Smrg	case 35:		/* rxvt */
3548cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
3549cb4a1343Smrg	    break;
3550cb4a1343Smrg#endif
3551cb4a1343Smrg#if OPT_TEK4014
3552cb4a1343Smrg	case 38:		/* DECTEK                       */
3553cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
3554cb4a1343Smrg	    break;
3555cb4a1343Smrg#endif
3556cb4a1343Smrg	case 40:		/* 132 column mode              */
3557cb4a1343Smrg	    result = MdBool(screen->c132);
3558cb4a1343Smrg	    break;
3559cb4a1343Smrg	case 41:		/* curses hack                  */
3560cb4a1343Smrg	    result = MdBool(screen->curses);
3561cb4a1343Smrg	    break;
3562cb4a1343Smrg	case 42:		/* DECNRCM national charset (VT220) */
3563cb4a1343Smrg	    result = MdFlag(xw->flags, NATIONAL);
3564cb4a1343Smrg	    break;
3565cb4a1343Smrg	case 44:		/* margin bell                  */
3566cb4a1343Smrg	    result = MdBool(screen->marginbell);
3567cb4a1343Smrg	    break;
3568cb4a1343Smrg	case 45:		/* reverse wraparound   */
3569cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSEWRAP);
3570cb4a1343Smrg	    break;
3571cb4a1343Smrg#ifdef ALLOWLOGGING
3572cb4a1343Smrg	case 46:		/* logging              */
3573cb4a1343Smrg#ifdef ALLOWLOGFILEONOFF
3574cb4a1343Smrg	    result = MdBool(screen->logging);
3575cb4a1343Smrg#endif /* ALLOWLOGFILEONOFF */
3576cb4a1343Smrg	    break;
3577cb4a1343Smrg#endif
3578cb4a1343Smrg	case 1049:		/* alternate buffer & cursor */
3579cb4a1343Smrg	    /* FALLTHRU */
3580cb4a1343Smrg	case 1047:
3581cb4a1343Smrg	    /* FALLTHRU */
3582cb4a1343Smrg	case 47:		/* alternate buffer */
3583cb4a1343Smrg	    result = MdBool(screen->whichBuf);
3584cb4a1343Smrg	    break;
3585cb4a1343Smrg	case 66:		/* DECNKM */
3586cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
3587cb4a1343Smrg	    break;
3588cb4a1343Smrg	case 67:		/* DECBKM */
3589cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
3590cb4a1343Smrg	    break;
3591cb4a1343Smrg	case SET_VT200_MOUSE:	/* xterm bogus sequence         */
3592cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
3593cb4a1343Smrg	    break;
3594cb4a1343Smrg	case SET_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
3595cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
3596cb4a1343Smrg	    break;
3597cb4a1343Smrg	case SET_BTN_EVENT_MOUSE:
3598cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
3599cb4a1343Smrg	    break;
3600cb4a1343Smrg	case SET_ANY_EVENT_MOUSE:
3601cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
3602cb4a1343Smrg	    break;
3603cb4a1343Smrg#if OPT_FOCUS_EVENT
3604cb4a1343Smrg	case SET_FOCUS_EVENT_MOUSE:
3605cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
3606cb4a1343Smrg	    break;
3607cb4a1343Smrg#endif
3608cb4a1343Smrg	case SET_EXT_MODE_MOUSE:
3609cb4a1343Smrg	    result = MdBool(screen->ext_mode_mouse);
3610cb4a1343Smrg	    break;
3611cb4a1343Smrg	case 1010:		/* rxvt */
3612cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
3613cb4a1343Smrg	    break;
3614cb4a1343Smrg	case 1011:		/* rxvt */
3615cb4a1343Smrg	    result = MdBool(screen->scrollkey);
3616cb4a1343Smrg	    break;
3617cb4a1343Smrg	case 1034:
3618cb4a1343Smrg	    result = MdBool(screen->input_eight_bits);
3619cb4a1343Smrg	    break;
3620cb4a1343Smrg#if OPT_NUM_LOCK
3621cb4a1343Smrg	case 1035:
3622cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
3623cb4a1343Smrg	    break;
3624cb4a1343Smrg	case 1036:
3625cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
3626cb4a1343Smrg	    break;
3627cb4a1343Smrg#endif
3628cb4a1343Smrg	case 1037:
3629cb4a1343Smrg	    result = MdBool(screen->delete_is_del);
3630cb4a1343Smrg	    break;
3631cb4a1343Smrg#if OPT_NUM_LOCK
3632cb4a1343Smrg	case 1039:
3633cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
3634cb4a1343Smrg	    break;
3635cb4a1343Smrg#endif
3636cb4a1343Smrg	case 1040:
3637cb4a1343Smrg	    result = MdBool(screen->keepSelection);
3638cb4a1343Smrg	    break;
3639cb4a1343Smrg	case 1041:
3640cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
3641cb4a1343Smrg	    break;
3642cb4a1343Smrg	case 1042:
3643cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
3644cb4a1343Smrg	    break;
3645cb4a1343Smrg	case 1043:
3646cb4a1343Smrg	    result = MdBool(screen->poponbell);
3647cb4a1343Smrg	    break;
3648cb4a1343Smrg	case 1048:
3649cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
3650cb4a1343Smrg	    break;
3651cb4a1343Smrg#if OPT_TCAP_FKEYS
3652cb4a1343Smrg	case 1050:
3653cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
3654cb4a1343Smrg	    break;
3655cb4a1343Smrg#endif
3656cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
3657cb4a1343Smrg	case 1051:
3658cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
3659cb4a1343Smrg	    break;
3660cb4a1343Smrg#endif
3661cb4a1343Smrg#if OPT_HP_FUNC_KEYS
3662cb4a1343Smrg	case 1052:
3663cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
3664cb4a1343Smrg	    break;
3665cb4a1343Smrg#endif
3666cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
3667cb4a1343Smrg	case 1053:
3668cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
3669cb4a1343Smrg	    break;
3670cb4a1343Smrg#endif
3671cb4a1343Smrg	case 1060:
3672cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
3673cb4a1343Smrg	    break;
3674cb4a1343Smrg#if OPT_SUNPC_KBD
3675cb4a1343Smrg	case 1061:
3676cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
3677cb4a1343Smrg	    break;
3678cb4a1343Smrg#endif
3679cb4a1343Smrg#if OPT_READLINE
3680cb4a1343Smrg	case SET_BUTTON1_MOVE_POINT:
3681cb4a1343Smrg	    result = MdBool(screen->click1_moves);
3682cb4a1343Smrg	    break;
3683cb4a1343Smrg	case SET_BUTTON2_MOVE_POINT:
3684cb4a1343Smrg	    result = MdBool(screen->paste_moves);
3685cb4a1343Smrg	    break;
3686cb4a1343Smrg	case SET_DBUTTON3_DELETE:
3687cb4a1343Smrg	    result = MdBool(screen->dclick3_deletes);
3688cb4a1343Smrg	    break;
3689cb4a1343Smrg	case SET_PASTE_IN_BRACKET:
3690cb4a1343Smrg	    result = MdBool(screen->paste_brackets);
3691cb4a1343Smrg	    break;
3692cb4a1343Smrg	case SET_PASTE_QUOTE:
3693cb4a1343Smrg	    result = MdBool(screen->paste_quotes);
3694cb4a1343Smrg	    break;
3695cb4a1343Smrg	case SET_PASTE_LITERAL_NL:
3696cb4a1343Smrg	    result = MdBool(screen->paste_literal_nl);
3697cb4a1343Smrg	    break;
3698cb4a1343Smrg#endif /* OPT_READLINE */
3699cb4a1343Smrg	}
3700cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
3701cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
3702cb4a1343Smrg    }
3703cb4a1343Smrg    reply.a_type = ANSI_CSI;
3704cb4a1343Smrg    reply.a_pintro = '?';
3705cb4a1343Smrg    reply.a_nparam = (ParmType) count;
3706cb4a1343Smrg    reply.a_inters = '$';
3707cb4a1343Smrg    reply.a_final = 'y';
3708cb4a1343Smrg    unparseseq(xw, &reply);
3709cb4a1343Smrg}
3710cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
3711cb4a1343Smrg
3712d522f475Smrgchar *
3713d522f475Smrgudk_lookup(int keycode, int *len)
3714d522f475Smrg{
3715d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
3716d522f475Smrg	*len = user_keys[keycode].len;
3717d522f475Smrg	return user_keys[keycode].str;
3718d522f475Smrg    }
3719d522f475Smrg    return 0;
3720d522f475Smrg}
3721d522f475Smrg
3722d522f475Smrgstatic void
3723cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
3724d522f475Smrg{
3725d522f475Smrg#if OPT_WIDE_CHARS
3726d522f475Smrg    static Char *converted;	/* NO_LEAKS */
3727d522f475Smrg#endif
3728d522f475Smrg    static char empty[1];
3729d522f475Smrg
3730d522f475Smrg    Arg args[1];
3731cd3331d0Smrg    Boolean changed = True;
3732d522f475Smrg    Widget w = CURRENT_EMU();
3733d522f475Smrg    Widget top = SHELL_OF(w);
3734d522f475Smrg
3735cd3331d0Smrg    char *my_attr;
3736cd3331d0Smrg    char *name;
3737cd3331d0Smrg    size_t limit;
3738cd3331d0Smrg    Char *c1;
3739cd3331d0Smrg    Char *cp;
3740d522f475Smrg
3741b7c89284Ssnj    if (!AllowTitleOps(xw))
3742d522f475Smrg	return;
3743d522f475Smrg
3744cd3331d0Smrg    if (value == 0)
3745cd3331d0Smrg	value = empty;
3746cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
3747cd3331d0Smrg	const char *temp;
3748cd3331d0Smrg	char *test;
3749cd3331d0Smrg
3750cd3331d0Smrg	value = x_decode_hex(value, &temp);
3751cd3331d0Smrg	if (*temp != '\0')
3752cd3331d0Smrg	    return;
3753cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
3754cd3331d0Smrg	    if (CharOf(*test) < 32) {
3755cd3331d0Smrg		*test = '\0';
3756cd3331d0Smrg		break;
3757cd3331d0Smrg	    }
3758cd3331d0Smrg	}
3759cd3331d0Smrg    }
3760cd3331d0Smrg
3761cd3331d0Smrg    c1 = (Char *) value;
3762cd3331d0Smrg    name = value;
3763cd3331d0Smrg    limit = strlen(name);
3764cd3331d0Smrg    my_attr = x_strdup(attribute);
3765cd3331d0Smrg
3766cd3331d0Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name));
3767cd3331d0Smrg
3768d522f475Smrg    /*
3769d522f475Smrg     * Ignore titles that are too long to be plausible requests.
3770d522f475Smrg     */
3771cd3331d0Smrg    if (limit > 0 && limit < 1024) {
3772d522f475Smrg
3773cd3331d0Smrg	/*
3774cd3331d0Smrg	 * After all decoding, overwrite nonprintable characters with '?'.
3775cd3331d0Smrg	 */
3776cd3331d0Smrg	for (cp = c1; *cp != 0; ++cp) {
3777cd3331d0Smrg	    Char *c2 = cp;
3778cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, c1 + limit)) {
3779cd3331d0Smrg		memset(c2, '?', (size_t) (cp + 1 - c2));
3780cd3331d0Smrg	    }
3781d522f475Smrg	}
3782d522f475Smrg
3783d522f475Smrg#if OPT_WIDE_CHARS
3784cd3331d0Smrg	/*
3785cd3331d0Smrg	 * If we're running in UTF-8 mode, and have not been told that the
3786cd3331d0Smrg	 * title string is in UTF-8, it is likely that non-ASCII text in the
3787cd3331d0Smrg	 * string will be rejected because it is not printable in the current
3788cd3331d0Smrg	 * locale.  So we convert it to UTF-8, allowing the X library to
3789cd3331d0Smrg	 * convert it back.
3790cd3331d0Smrg	 */
3791cd3331d0Smrg	if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) {
3792cd3331d0Smrg	    int n;
3793cd3331d0Smrg
3794cd3331d0Smrg	    for (n = 0; name[n] != '\0'; ++n) {
3795cd3331d0Smrg		if (CharOf(name[n]) > 127) {
3796cd3331d0Smrg		    if (converted != 0)
3797cd3331d0Smrg			free(converted);
3798cd3331d0Smrg		    if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
3799cd3331d0Smrg			Char *temp = converted;
3800cd3331d0Smrg			while (*name != 0) {
3801cd3331d0Smrg			    temp = convertToUTF8(temp, CharOf(*name));
3802cd3331d0Smrg			    ++name;
3803cd3331d0Smrg			}
3804cd3331d0Smrg			*temp = 0;
3805cd3331d0Smrg			name = (char *) converted;
3806cd3331d0Smrg			TRACE(("...converted{%s}\n", name));
3807d522f475Smrg		    }
3808cd3331d0Smrg		    break;
3809d522f475Smrg		}
3810d522f475Smrg	    }
3811d522f475Smrg	}
3812d522f475Smrg#endif
3813d522f475Smrg
3814d522f475Smrg#if OPT_SAME_NAME
3815cd3331d0Smrg	/* If the attribute isn't going to change, then don't bother... */
3816cd3331d0Smrg
3817cd3331d0Smrg	if (resource.sameName) {
3818cd3331d0Smrg	    char *buf = 0;
3819cd3331d0Smrg	    XtSetArg(args[0], my_attr, &buf);
3820cd3331d0Smrg	    XtGetValues(top, args, 1);
3821cd3331d0Smrg	    TRACE(("...comparing{%s}\n", buf));
3822cd3331d0Smrg	    if (buf != 0 && strcmp(name, buf) == 0)
3823cd3331d0Smrg		changed = False;
3824cd3331d0Smrg	}
3825d522f475Smrg#endif /* OPT_SAME_NAME */
3826d522f475Smrg
3827cd3331d0Smrg	if (changed) {
3828cd3331d0Smrg	    TRACE(("...updating %s\n", my_attr));
3829cd3331d0Smrg	    TRACE(("...value is %s\n", name));
3830cd3331d0Smrg	    XtSetArg(args[0], my_attr, name);
3831cd3331d0Smrg	    XtSetValues(top, args, 1);
3832d522f475Smrg
3833d522f475Smrg#if OPT_WIDE_CHARS
3834cd3331d0Smrg	    if (xtermEnvUTF8()) {
3835cd3331d0Smrg		Display *dpy = XtDisplay(xw);
3836cd3331d0Smrg		Atom my_atom;
3837cd3331d0Smrg
3838cd3331d0Smrg		const char *propname = (!strcmp(my_attr, XtNtitle)
3839cd3331d0Smrg					? "_NET_WM_NAME"
3840cd3331d0Smrg					: "_NET_WM_ICON_NAME");
3841cd3331d0Smrg		if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
3842cd3331d0Smrg		    if (IsSetUtf8Title(xw)) {
3843cd3331d0Smrg			TRACE(("...updating %s\n", propname));
3844cd3331d0Smrg			TRACE(("...value is %s\n", value));
3845c219fbebSmrg			XChangeProperty(dpy, VShellWindow(xw), my_atom,
3846cd3331d0Smrg					XA_UTF8_STRING(dpy), 8,
3847cd3331d0Smrg					PropModeReplace,
3848cd3331d0Smrg					(Char *) value,
3849cd3331d0Smrg					(int) strlen(value));
3850cd3331d0Smrg		    } else {
3851cd3331d0Smrg			TRACE(("...deleting %s\n", propname));
3852c219fbebSmrg			XDeleteProperty(dpy, VShellWindow(xw), my_atom);
3853cd3331d0Smrg		    }
3854cd3331d0Smrg		}
3855d522f475Smrg	    }
3856cd3331d0Smrg#endif
3857d522f475Smrg	}
3858cd3331d0Smrg
3859cd3331d0Smrg	free(my_attr);
3860cd3331d0Smrg
3861cd3331d0Smrg	if (IsTitleMode(xw, tmSetBase16))
3862cd3331d0Smrg	    free(value);
3863cd3331d0Smrg
3864d522f475Smrg    }
3865cd3331d0Smrg    return;
3866d522f475Smrg}
3867d522f475Smrg
3868d522f475Smrgvoid
3869b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
3870d522f475Smrg{
3871cd3331d0Smrg    if (name == 0) {
3872cd3331d0Smrg	static char dummy[] = "";
3873cd3331d0Smrg	name = dummy;
3874cd3331d0Smrg    }
3875d522f475Smrg#if OPT_ZICONBEEP		/* If warning should be given then give it */
3876cd3331d0Smrg    if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) {
3877d522f475Smrg	char *newname = CastMallocN(char, strlen(name) + 4);
3878d522f475Smrg	if (!newname) {
3879d522f475Smrg	    fprintf(stderr, "malloc failed in ChangeIconName\n");
3880d522f475Smrg	    return;
3881d522f475Smrg	}
3882d522f475Smrg	strcpy(newname, "*** ");
3883d522f475Smrg	strcat(newname, name);
3884b7c89284Ssnj	ChangeGroup(xw, XtNiconName, newname);
3885d522f475Smrg	free(newname);
3886d522f475Smrg    } else
3887d522f475Smrg#endif /* OPT_ZICONBEEP */
3888b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
3889d522f475Smrg}
3890d522f475Smrg
3891d522f475Smrgvoid
3892b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
3893d522f475Smrg{
3894b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
3895d522f475Smrg}
3896d522f475Smrg
3897712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
3898d522f475Smrg
3899d522f475Smrgvoid
3900d522f475SmrgChangeXprop(char *buf)
3901d522f475Smrg{
3902d522f475Smrg    Display *dpy = XtDisplay(toplevel);
3903d522f475Smrg    Window w = XtWindow(toplevel);
3904d522f475Smrg    XTextProperty text_prop;
3905d522f475Smrg    Atom aprop;
3906d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
3907d522f475Smrg
3908d522f475Smrg    if (pchEndPropName)
3909d522f475Smrg	*pchEndPropName = '\0';
3910d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
3911d522f475Smrg    if (pchEndPropName == NULL) {
3912d522f475Smrg	/* no "=value" given, so delete the property */
3913d522f475Smrg	XDeleteProperty(dpy, w, aprop);
3914d522f475Smrg    } else {
3915d522f475Smrg	text_prop.value = pchEndPropName + 1;
3916d522f475Smrg	text_prop.encoding = XA_STRING;
3917d522f475Smrg	text_prop.format = 8;
3918d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
3919d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
3920d522f475Smrg    }
3921d522f475Smrg}
3922d522f475Smrg
3923d522f475Smrg/***====================================================================***/
3924d522f475Smrg
3925d522f475Smrg/*
3926d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
3927d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
3928d522f475Smrg */
3929d522f475Smrgvoid
3930d522f475SmrgReverseOldColors(void)
3931d522f475Smrg{
3932d522f475Smrg    ScrnColors *pOld = pOldColors;
3933d522f475Smrg    Pixel tmpPix;
3934d522f475Smrg    char *tmpName;
3935d522f475Smrg
3936d522f475Smrg    if (pOld) {
3937d522f475Smrg	/* change text cursor, if necesary */
3938d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
3939d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
3940d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
3941d522f475Smrg		XtFree(pOldColors->names[TEXT_CURSOR]);
3942d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
3943d522f475Smrg	    }
3944d522f475Smrg	    if (pOld->names[TEXT_BG]) {
3945d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
3946d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
3947d522f475Smrg		}
3948d522f475Smrg	    }
3949d522f475Smrg	}
3950d522f475Smrg
3951d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
3952d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
3953d522f475Smrg
3954d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
3955d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
3956d522f475Smrg
3957d522f475Smrg#if OPT_TEK4014
3958d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
3959d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
3960d522f475Smrg#endif
3961d522f475Smrg    }
3962d522f475Smrg    return;
3963d522f475Smrg}
3964d522f475Smrg
3965d522f475SmrgBool
3966d522f475SmrgAllocateTermColor(XtermWidget xw,
3967d522f475Smrg		  ScrnColors * pNew,
3968d522f475Smrg		  int ndx,
3969cd3331d0Smrg		  const char *name,
3970cd3331d0Smrg		  Bool always)
3971d522f475Smrg{
3972cd3331d0Smrg    Bool result = False;
3973d522f475Smrg
3974cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
3975cd3331d0Smrg	XColor def;
3976cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3977cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3978cd3331d0Smrg	char *newName;
3979cd3331d0Smrg
3980712a7ff4Smrg	result = True;
3981712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
3982712a7ff4Smrg	    def.pixel = xw->old_foreground;
3983712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
3984712a7ff4Smrg	    def.pixel = xw->old_background;
3985712a7ff4Smrg	} else if (XParseColor(screen->display, cmap, name, &def)
3986712a7ff4Smrg		   && (XAllocColor(screen->display, cmap, &def)
3987712a7ff4Smrg		       || find_closest_color(screen->display, cmap, &def))) {
3988712a7ff4Smrg	    ;			/*empty */
3989712a7ff4Smrg	} else {
3990712a7ff4Smrg	    result = False;
3991712a7ff4Smrg	}
3992712a7ff4Smrg
3993712a7ff4Smrg	if (result
3994cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
3995712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
3996cd3331d0Smrg		free(pNew->names[ndx]);
3997712a7ff4Smrg	    }
3998cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
3999cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
4000712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
4001712a7ff4Smrg		   ndx, newName, def.pixel));
4002cd3331d0Smrg	} else {
4003cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
4004712a7ff4Smrg	    result = False;
4005cd3331d0Smrg	}
4006cd3331d0Smrg    }
4007cd3331d0Smrg    return result;
4008d522f475Smrg}
4009d522f475Smrg/***====================================================================***/
4010d522f475Smrg
4011d522f475Smrg/* ARGSUSED */
4012d522f475Smrgvoid
4013cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
4014d522f475Smrg{
4015d522f475Smrg#ifdef DEBUG
4016d522f475Smrg    if (debug) {
4017c219fbebSmrg	fprintf(stderr, "%s: PANIC!\t", ProgramName);
4018d522f475Smrg	fprintf(stderr, s, a);
4019d522f475Smrg	fputs("\r\n", stderr);
4020d522f475Smrg	fflush(stderr);
4021d522f475Smrg    }
4022d522f475Smrg#endif /* DEBUG */
4023d522f475Smrg}
4024d522f475Smrg
4025d522f475Smrgconst char *
4026d522f475SmrgSysErrorMsg(int code)
4027d522f475Smrg{
4028d522f475Smrg    static char unknown[] = "unknown error";
4029d522f475Smrg    char *s = strerror(code);
4030d522f475Smrg    return s ? s : unknown;
4031d522f475Smrg}
4032d522f475Smrg
4033d522f475Smrgconst char *
4034d522f475SmrgSysReasonMsg(int code)
4035d522f475Smrg{
4036d522f475Smrg    /* *INDENT-OFF* */
4037d522f475Smrg    static const struct {
4038d522f475Smrg	int code;
4039d522f475Smrg	const char *name;
4040d522f475Smrg    } table[] = {
4041d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
4042d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
4043d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
4044d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
4045d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
4046d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
4047d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
4048d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
4049d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
4050d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
4051d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
4052d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
4053d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
4054d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
4055d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
4056d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
4057d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
4058d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
4059d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
4060d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
4061d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
4062d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
4063d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
4064d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
4065d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
4066d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
4067d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
4068d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
4069d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
4070d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
4071d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
4072d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
4073d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
4074d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
4075d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
4076d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
4077d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
4078d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
4079d522f475Smrg	{ ERROR_MMALLOC,	"my_memmove: malloc/realloc failed" },
4080d522f475Smrg    };
4081d522f475Smrg    /* *INDENT-ON* */
4082d522f475Smrg
4083d522f475Smrg    Cardinal n;
4084d522f475Smrg    const char *result = "?";
4085d522f475Smrg
4086d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
4087d522f475Smrg	if (code == table[n].code) {
4088d522f475Smrg	    result = table[n].name;
4089d522f475Smrg	    break;
4090d522f475Smrg	}
4091d522f475Smrg    }
4092d522f475Smrg    return result;
4093d522f475Smrg}
4094d522f475Smrg
4095d522f475Smrgvoid
4096d522f475SmrgSysError(int code)
4097d522f475Smrg{
4098d522f475Smrg    int oerrno = errno;
4099d522f475Smrg
4100c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
4101d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
4102d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
4103d522f475Smrg
4104d522f475Smrg    Cleanup(code);
4105d522f475Smrg}
4106d522f475Smrg
4107d522f475Smrg/*
4108d522f475Smrg * cleanup by sending SIGHUP to client processes
4109d522f475Smrg */
4110d522f475Smrgvoid
4111d522f475SmrgCleanup(int code)
4112d522f475Smrg{
4113d522f475Smrg    static Bool cleaning;
4114d522f475Smrg    TScreen *screen = TScreenOf(term);
4115d522f475Smrg
4116d522f475Smrg    /*
4117d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
4118d522f475Smrg     */
4119d522f475Smrg    if (code == 0) {
4120d522f475Smrg	if (cleaning) {
4121d522f475Smrg	    hold_screen = 0;
4122d522f475Smrg	    return;
4123d522f475Smrg	}
4124d522f475Smrg
4125d522f475Smrg	cleaning = True;
4126d522f475Smrg	need_cleanup = False;
4127d522f475Smrg
4128d522f475Smrg	TRACE(("Cleanup %d\n", code));
4129d522f475Smrg
4130d522f475Smrg	if (hold_screen) {
4131d522f475Smrg	    hold_screen = 2;
4132d522f475Smrg	    while (hold_screen) {
4133d522f475Smrg		xevents();
4134d522f475Smrg		Sleep(10);
4135d522f475Smrg	    }
4136d522f475Smrg	}
4137d522f475Smrg#if OPT_SESSION_MGT
4138d522f475Smrg	if (resource.sessionMgt) {
4139d522f475Smrg	    XtVaSetValues(toplevel,
4140d522f475Smrg			  XtNjoinSession, False,
4141cd3331d0Smrg			  NULL);
4142d522f475Smrg	}
4143d522f475Smrg#endif
4144d522f475Smrg    }
4145d522f475Smrg
4146d522f475Smrg    if (screen->pid > 1) {
4147d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
4148d522f475Smrg    }
4149d522f475Smrg    Exit(code);
4150d522f475Smrg}
4151d522f475Smrg
4152d522f475Smrg#ifndef VMS
4153d522f475Smrgchar *
4154d522f475SmrgxtermFindShell(char *leaf, Bool warning)
4155d522f475Smrg{
4156d522f475Smrg    char *s;
4157d522f475Smrg    char *d;
4158d522f475Smrg    char *tmp;
4159d522f475Smrg    char *result = leaf;
4160d522f475Smrg
4161d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
4162d522f475Smrg    if (*result != '\0' && strchr("+/-", *result) == 0) {
4163d522f475Smrg	/* find it in $PATH */
4164d522f475Smrg	if ((s = x_getenv("PATH")) != 0) {
41650d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
4166d522f475Smrg		Bool found = False;
4167d522f475Smrg		while (*s != '\0') {
4168d522f475Smrg		    strcpy(tmp, s);
4169d522f475Smrg		    for (d = tmp;; ++d) {
4170d522f475Smrg			if (*d == ':' || *d == '\0') {
4171d522f475Smrg			    int skip = (*d != '\0');
4172d522f475Smrg			    *d = '/';
4173d522f475Smrg			    strcpy(d + 1, leaf);
4174d522f475Smrg			    if (skip)
4175d522f475Smrg				++d;
4176d522f475Smrg			    s += (d - tmp);
4177d522f475Smrg			    if (*tmp == '/'
4178d522f475Smrg				&& strstr(tmp, "..") == 0
4179d522f475Smrg				&& access(tmp, X_OK) == 0) {
4180d522f475Smrg				result = x_strdup(tmp);
4181d522f475Smrg				found = True;
4182d522f475Smrg			    }
4183d522f475Smrg			    break;
4184d522f475Smrg			}
4185d522f475Smrg			if (found)
4186d522f475Smrg			    break;
4187d522f475Smrg		    }
4188d522f475Smrg		    if (found)
4189d522f475Smrg			break;
4190d522f475Smrg		}
4191d522f475Smrg		free(tmp);
4192d522f475Smrg	    }
4193d522f475Smrg	}
4194d522f475Smrg    }
4195d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
4196d522f475Smrg    if (*result != '/'
4197d522f475Smrg	|| strstr(result, "..") != 0
4198d522f475Smrg	|| access(result, X_OK) != 0) {
4199d522f475Smrg	if (warning)
4200d522f475Smrg	    fprintf(stderr, "No absolute path found for shell: %s\n", result);
4201d522f475Smrg	result = 0;
4202d522f475Smrg    }
4203d522f475Smrg    return result;
4204d522f475Smrg}
4205d522f475Smrg#endif /* VMS */
4206d522f475Smrg
42070d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
4208d522f475Smrg
4209d522f475Smrg/*
4210d522f475Smrg * copy the environment before Setenv'ing.
4211d522f475Smrg */
4212d522f475Smrgvoid
4213d522f475SmrgxtermCopyEnv(char **oldenv)
4214d522f475Smrg{
4215d522f475Smrg    unsigned size;
4216d522f475Smrg    char **newenv;
4217d522f475Smrg
4218d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
4219d522f475Smrg	;
4220d522f475Smrg    }
4221d522f475Smrg
4222d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
4223d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
4224d522f475Smrg    environ = newenv;
4225d522f475Smrg}
4226d522f475Smrg
4227d522f475Smrg/*
4228d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
4229d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
4230d522f475Smrg * This procedure assumes the memory for the first level of environ
4231d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
4232d522f475Smrg * to have to do a realloc().
4233d522f475Smrg */
4234d522f475Smrgvoid
4235cd3331d0SmrgxtermSetenv(const char *var, const char *value)
4236d522f475Smrg{
4237d522f475Smrg    if (value != 0) {
4238d522f475Smrg	char *test;
4239d522f475Smrg	int envindex = 0;
4240d522f475Smrg	size_t len = strlen(var);
4241d522f475Smrg	int found = -1;
4242d522f475Smrg
4243d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
4244d522f475Smrg
4245d522f475Smrg	while ((test = environ[envindex]) != NULL) {
4246d522f475Smrg	    if (strncmp(test, var, len) == 0 && test[len] == '=') {
4247d522f475Smrg		found = envindex;
4248d522f475Smrg		break;
4249d522f475Smrg	    }
4250d522f475Smrg	    envindex++;
4251d522f475Smrg	}
4252d522f475Smrg
4253d522f475Smrg	if (found < 0) {
4254d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
4255d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
4256d522f475Smrg
4257d522f475Smrg	    if (need > have) {
4258d522f475Smrg		char **newenv;
4259d522f475Smrg		newenv = TypeMallocN(char *, need);
4260d522f475Smrg		if (newenv == 0) {
4261d522f475Smrg		    fprintf(stderr, "Cannot increase environment\n");
4262d522f475Smrg		    return;
4263d522f475Smrg		}
4264d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
4265d522f475Smrg		free(environ);
4266d522f475Smrg		environ = newenv;
4267d522f475Smrg	    }
4268d522f475Smrg
4269d522f475Smrg	    found = envindex;
4270d522f475Smrg	    environ[found + 1] = NULL;
4271d522f475Smrg	    environ = environ;
4272d522f475Smrg	}
4273d522f475Smrg
4274d522f475Smrg	environ[found] = CastMallocN(char, 1 + len + strlen(value));
4275d522f475Smrg	if (environ[found] == 0) {
4276d522f475Smrg	    fprintf(stderr, "Cannot allocate environment %s\n", var);
4277d522f475Smrg	    return;
4278d522f475Smrg	}
4279d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
4280d522f475Smrg    }
4281d522f475Smrg}
4282d522f475Smrg
4283d522f475Smrg/*ARGSUSED*/
4284d522f475Smrgint
4285d522f475Smrgxerror(Display * d, XErrorEvent * ev)
4286d522f475Smrg{
4287c219fbebSmrg    fprintf(stderr, "%s:  warning, error event received:\n", ProgramName);
4288d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
4289d522f475Smrg    Exit(ERROR_XERROR);
4290d522f475Smrg    return 0;			/* appease the compiler */
4291d522f475Smrg}
4292d522f475Smrg
4293712a7ff4Smrgvoid
4294712a7ff4Smrgice_error(IceConn iceConn)
4295712a7ff4Smrg{
4296712a7ff4Smrg    (void) iceConn;
4297712a7ff4Smrg
4298712a7ff4Smrg    fprintf(stderr,
4299712a7ff4Smrg	    "%s:  ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
4300712a7ff4Smrg	    ProgramName, (long) getpid(), errno);
4301712a7ff4Smrg
4302712a7ff4Smrg    Exit(ERROR_ICEERROR);
4303712a7ff4Smrg}
4304712a7ff4Smrg
4305d522f475Smrg/*ARGSUSED*/
4306d522f475Smrgint
4307d522f475Smrgxioerror(Display * dpy)
4308d522f475Smrg{
4309d522f475Smrg    int the_error = errno;
4310d522f475Smrg
4311d522f475Smrg    (void) fprintf(stderr,
4312d522f475Smrg		   "%s:  fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
4313c219fbebSmrg		   ProgramName, the_error, SysErrorMsg(the_error),
4314d522f475Smrg		   DisplayString(dpy));
4315d522f475Smrg
4316d522f475Smrg    Exit(ERROR_XIOERROR);
4317d522f475Smrg    return 0;			/* appease the compiler */
4318d522f475Smrg}
4319d522f475Smrg
4320d522f475Smrgvoid
4321d522f475Smrgxt_error(String message)
4322d522f475Smrg{
4323d522f475Smrg    (void) fprintf(stderr, "%s Xt error: %s\n", ProgramName, message);
4324d522f475Smrg
4325d522f475Smrg    /*
4326d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
4327d522f475Smrg     */
4328d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
4329d522f475Smrg	fprintf(stderr, "%s:  DISPLAY is not set\n", ProgramName);
4330d522f475Smrg    }
4331d522f475Smrg    exit(1);
4332d522f475Smrg}
4333d522f475Smrg
4334d522f475Smrgint
4335d522f475SmrgXStrCmp(char *s1, char *s2)
4336d522f475Smrg{
4337d522f475Smrg    if (s1 && s2)
4338d522f475Smrg	return (strcmp(s1, s2));
4339d522f475Smrg    if (s1 && *s1)
4340d522f475Smrg	return (1);
4341d522f475Smrg    if (s2 && *s2)
4342d522f475Smrg	return (-1);
4343d522f475Smrg    return (0);
4344d522f475Smrg}
4345d522f475Smrg
4346d522f475Smrg#if OPT_TEK4014
4347d522f475Smrgstatic void
4348d522f475Smrgwithdraw_window(Display * dpy, Window w, int scr)
4349d522f475Smrg{
4350d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
4351d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
4352d522f475Smrg    XWithdrawWindow(dpy, w, scr);
4353d522f475Smrg    return;
4354d522f475Smrg}
4355d522f475Smrg#endif
4356d522f475Smrg
4357d522f475Smrgvoid
4358d522f475Smrgset_vt_visibility(Bool on)
4359d522f475Smrg{
4360c219fbebSmrg    XtermWidget xw = term;
4361c219fbebSmrg    TScreen *screen = TScreenOf(xw);
4362d522f475Smrg
4363d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
4364d522f475Smrg    if (on) {
4365c219fbebSmrg	if (!screen->Vshow && xw) {
4366c219fbebSmrg	    VTInit(xw);
4367c219fbebSmrg	    XtMapWidget(XtParent(xw));
4368d522f475Smrg#if OPT_TOOLBAR
4369d522f475Smrg	    /* we need both of these during initialization */
4370c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
4371d522f475Smrg	    ShowToolbar(resource.toolBar);
4372d522f475Smrg#endif
4373d522f475Smrg	    screen->Vshow = True;
4374d522f475Smrg	}
4375d522f475Smrg    }
4376d522f475Smrg#if OPT_TEK4014
4377d522f475Smrg    else {
4378c219fbebSmrg	if (screen->Vshow && xw) {
4379c219fbebSmrg	    withdraw_window(XtDisplay(xw),
4380c219fbebSmrg			    VShellWindow(xw),
4381c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
4382d522f475Smrg	    screen->Vshow = False;
4383d522f475Smrg	}
4384d522f475Smrg    }
4385d522f475Smrg    set_vthide_sensitivity();
4386d522f475Smrg    set_tekhide_sensitivity();
4387d522f475Smrg    update_vttekmode();
4388d522f475Smrg    update_tekshow();
4389d522f475Smrg    update_vtshow();
4390d522f475Smrg#endif
4391d522f475Smrg    return;
4392d522f475Smrg}
4393d522f475Smrg
4394d522f475Smrg#if OPT_TEK4014
4395d522f475Smrgvoid
4396d522f475Smrgset_tek_visibility(Bool on)
4397d522f475Smrg{
4398d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
4399d522f475Smrg
4400d522f475Smrg    if (on) {
4401cd3331d0Smrg	if (!TEK4014_SHOWN(term)) {
4402cd3331d0Smrg	    if (tekWidget == 0) {
4403cd3331d0Smrg		TekInit();	/* will exit on failure */
4404cd3331d0Smrg	    }
4405cd3331d0Smrg	    if (tekWidget != 0) {
4406cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
4407cd3331d0Smrg		XtRealizeWidget(tekParent);
4408cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
4409d522f475Smrg#if OPT_TOOLBAR
4410cd3331d0Smrg		/* we need both of these during initialization */
4411cd3331d0Smrg		XtMapWidget(tekParent);
4412cd3331d0Smrg		XtMapWidget(tekWidget);
4413d522f475Smrg#endif
4414cd3331d0Smrg		XtOverrideTranslations(tekParent,
4415cd3331d0Smrg				       XtParseTranslationTable
4416cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
4417cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
4418cd3331d0Smrg				       XtWindow(tekParent),
4419cd3331d0Smrg				       &wm_delete_window, 1);
4420cd3331d0Smrg		TEK4014_SHOWN(term) = True;
4421cd3331d0Smrg	    }
4422d522f475Smrg	}
4423d522f475Smrg    } else {
4424d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
4425d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
4426d522f475Smrg			    TShellWindow,
4427d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
4428d522f475Smrg	    TEK4014_SHOWN(term) = False;
4429d522f475Smrg	}
4430d522f475Smrg    }
4431d522f475Smrg    set_tekhide_sensitivity();
4432d522f475Smrg    set_vthide_sensitivity();
4433d522f475Smrg    update_vtshow();
4434d522f475Smrg    update_tekshow();
4435d522f475Smrg    update_vttekmode();
4436d522f475Smrg    return;
4437d522f475Smrg}
4438d522f475Smrg
4439d522f475Smrgvoid
4440d522f475Smrgend_tek_mode(void)
4441d522f475Smrg{
4442cd3331d0Smrg    XtermWidget xw = term;
4443cd3331d0Smrg
4444cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
4445cd3331d0Smrg	FlushLog(xw);
4446d522f475Smrg	longjmp(Tekend, 1);
4447d522f475Smrg    }
4448d522f475Smrg    return;
4449d522f475Smrg}
4450d522f475Smrg
4451d522f475Smrgvoid
4452d522f475Smrgend_vt_mode(void)
4453d522f475Smrg{
4454cd3331d0Smrg    XtermWidget xw = term;
4455cd3331d0Smrg
4456cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
4457cd3331d0Smrg	FlushLog(xw);
4458cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
4459d522f475Smrg	longjmp(VTend, 1);
4460d522f475Smrg    }
4461d522f475Smrg    return;
4462d522f475Smrg}
4463d522f475Smrg
4464d522f475Smrgvoid
4465d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
4466d522f475Smrg{
4467d522f475Smrg    if (tovt) {
4468d522f475Smrg	if (tekRefreshList)
4469d522f475Smrg	    TekRefresh(tekWidget);
4470d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
4471d522f475Smrg    } else {
4472d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
4473d522f475Smrg    }
4474d522f475Smrg}
4475d522f475Smrg
4476d522f475Smrgvoid
4477d522f475Smrghide_vt_window(void)
4478d522f475Smrg{
4479d522f475Smrg    set_vt_visibility(False);
4480d522f475Smrg    if (!TEK4014_ACTIVE(term))
4481d522f475Smrg	switch_modes(False);	/* switch to tek mode */
4482d522f475Smrg}
4483d522f475Smrg
4484d522f475Smrgvoid
4485d522f475Smrghide_tek_window(void)
4486d522f475Smrg{
4487d522f475Smrg    set_tek_visibility(False);
4488d522f475Smrg    tekRefreshList = (TekLink *) 0;
4489d522f475Smrg    if (TEK4014_ACTIVE(term))
4490d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
4491d522f475Smrg}
4492d522f475Smrg#endif /* OPT_TEK4014 */
4493d522f475Smrg
4494d522f475Smrgstatic const char *
4495d522f475Smrgskip_punct(const char *s)
4496d522f475Smrg{
4497d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
4498d522f475Smrg	++s;
4499d522f475Smrg    }
4500d522f475Smrg    return s;
4501d522f475Smrg}
4502d522f475Smrg
4503d522f475Smrgstatic int
4504d522f475Smrgcmp_options(const void *a, const void *b)
4505d522f475Smrg{
4506d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
4507d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
4508d522f475Smrg    return strcmp(s1, s2);
4509d522f475Smrg}
4510d522f475Smrg
4511d522f475Smrgstatic int
4512d522f475Smrgcmp_resources(const void *a, const void *b)
4513d522f475Smrg{
4514d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
4515d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
4516d522f475Smrg}
4517d522f475Smrg
4518d522f475SmrgXrmOptionDescRec *
4519d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
4520d522f475Smrg{
4521d522f475Smrg    static XrmOptionDescRec *res_array = 0;
4522d522f475Smrg
4523d522f475Smrg#ifdef NO_LEAKS
4524cd3331d0Smrg    if (descs == 0) {
4525cd3331d0Smrg	if (res_array != 0) {
4526cd3331d0Smrg	    free(res_array);
4527cd3331d0Smrg	    res_array = 0;
4528cd3331d0Smrg	}
4529d522f475Smrg    } else
4530d522f475Smrg#endif
4531d522f475Smrg    if (res_array == 0) {
4532d522f475Smrg	Cardinal j;
4533d522f475Smrg
4534d522f475Smrg	/* make a sorted index to 'resources' */
4535d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
4536cd3331d0Smrg	if (res_array != 0) {
4537cd3331d0Smrg	    for (j = 0; j < res_count; j++)
4538cd3331d0Smrg		res_array[j] = descs[j];
4539cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
4540cd3331d0Smrg	}
4541d522f475Smrg    }
4542d522f475Smrg    return res_array;
4543d522f475Smrg}
4544d522f475Smrg
4545d522f475Smrg/*
4546d522f475Smrg * The first time this is called, construct sorted index to the main program's
4547d522f475Smrg * list of options, taking into account the on/off options which will be
4548d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
4549d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
4550d522f475Smrg */
4551d522f475SmrgOptionHelp *
4552d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
4553d522f475Smrg{
4554d522f475Smrg    static OptionHelp *opt_array = 0;
4555d522f475Smrg
4556d522f475Smrg#ifdef NO_LEAKS
4557d522f475Smrg    if (descs == 0 && opt_array != 0) {
4558d522f475Smrg	sortedOptDescs(descs, numDescs);
4559d522f475Smrg	free(opt_array);
4560d522f475Smrg	opt_array = 0;
4561d522f475Smrg	return 0;
4562d522f475Smrg    } else if (options == 0 || descs == 0) {
4563d522f475Smrg	return 0;
4564d522f475Smrg    }
4565d522f475Smrg#endif
4566d522f475Smrg
4567d522f475Smrg    if (opt_array == 0) {
4568cd3331d0Smrg	size_t opt_count, j;
4569d522f475Smrg#if OPT_TRACE
4570d522f475Smrg	Cardinal k;
4571d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
4572d522f475Smrg	int code;
4573cd3331d0Smrg	const char *mesg;
4574d522f475Smrg#else
4575d522f475Smrg	(void) descs;
4576d522f475Smrg	(void) numDescs;
4577d522f475Smrg#endif
4578d522f475Smrg
4579d522f475Smrg	/* count 'options' and make a sorted index to it */
4580d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
4581d522f475Smrg	    ;
4582d522f475Smrg	}
4583d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
4584d522f475Smrg	for (j = 0; j < opt_count; j++)
4585d522f475Smrg	    opt_array[j] = options[j];
4586d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
4587d522f475Smrg
4588d522f475Smrg	/* supply the "turn on/off" strings if needed */
4589d522f475Smrg#if OPT_TRACE
4590d522f475Smrg	for (j = 0; j < opt_count; j++) {
4591712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
4592c219fbebSmrg		char temp[80];
4593cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
4594d522f475Smrg		for (k = 0; k < numDescs; ++k) {
4595cd3331d0Smrg		    const char *value = res_array[k].value;
4596d522f475Smrg		    if (res_array[k].option[0] == '-') {
4597d522f475Smrg			code = -1;
4598d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
4599d522f475Smrg			code = 1;
4600d522f475Smrg		    } else {
4601d522f475Smrg			code = 0;
4602d522f475Smrg		    }
4603c219fbebSmrg		    strcpy(temp, opt_array[j].desc);
4604c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
4605d522f475Smrg			code = -code;
4606d522f475Smrg		    if (code != 0
4607d522f475Smrg			&& res_array[k].value != 0
4608d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
4609d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
4610d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
4611d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
4612d522f475Smrg			    mesg = "turn on/off";
4613d522f475Smrg			} else {
4614d522f475Smrg			    mesg = "turn off/on";
4615d522f475Smrg			}
4616d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
4617712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
4618d522f475Smrg				char *s = CastMallocN(char,
4619d522f475Smrg						      strlen(mesg)
4620d522f475Smrg						      + 1
4621d522f475Smrg						      + strlen(opt_array[j].desc));
4622d522f475Smrg				if (s != 0) {
4623d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
4624d522f475Smrg				    opt_array[j].desc = s;
4625d522f475Smrg				}
4626d522f475Smrg			    } else {
4627d522f475Smrg				TRACE(("OOPS "));
4628d522f475Smrg			    }
4629d522f475Smrg			}
4630d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
4631d522f475Smrg			       mesg,
4632d522f475Smrg			       res_array[k].option,
4633d522f475Smrg			       res_array[k].value,
4634d522f475Smrg			       opt_array[j].opt,
4635d522f475Smrg			       opt_array[j].desc));
4636d522f475Smrg			break;
4637d522f475Smrg		    }
4638d522f475Smrg		}
4639d522f475Smrg	    }
4640d522f475Smrg	}
4641d522f475Smrg#endif
4642d522f475Smrg    }
4643d522f475Smrg    return opt_array;
4644d522f475Smrg}
4645d522f475Smrg
4646d522f475Smrg/*
4647d522f475Smrg * Report the character-type locale that xterm was started in.
4648d522f475Smrg */
4649d522f475Smrgchar *
4650d522f475SmrgxtermEnvLocale(void)
4651d522f475Smrg{
4652d522f475Smrg    static char *result;
4653d522f475Smrg
4654d522f475Smrg    if (result == 0) {
4655d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
4656cd3331d0Smrg	    result = x_strdup("C");
4657cd3331d0Smrg	} else {
4658cd3331d0Smrg	    result = x_strdup(result);
4659d522f475Smrg	}
4660d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
4661d522f475Smrg    }
4662d522f475Smrg    return result;
4663d522f475Smrg}
4664d522f475Smrg
4665d522f475Smrgchar *
4666d522f475SmrgxtermEnvEncoding(void)
4667d522f475Smrg{
4668d522f475Smrg    static char *result;
4669d522f475Smrg
4670d522f475Smrg    if (result == 0) {
4671d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
4672d522f475Smrg	result = nl_langinfo(CODESET);
4673d522f475Smrg#else
4674d522f475Smrg	char *locale = xtermEnvLocale();
4675d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
4676d522f475Smrg	    result = "ASCII";
4677d522f475Smrg	} else {
4678d522f475Smrg	    result = "ISO-8859-1";
4679d522f475Smrg	}
4680d522f475Smrg#endif
4681d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
4682d522f475Smrg    }
4683d522f475Smrg    return result;
4684d522f475Smrg}
4685d522f475Smrg
4686d522f475Smrg#if OPT_WIDE_CHARS
4687d522f475Smrg/*
4688d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
4689d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
4690d522f475Smrg * various library calls.
4691d522f475Smrg */
4692d522f475SmrgBool
4693d522f475SmrgxtermEnvUTF8(void)
4694d522f475Smrg{
4695d522f475Smrg    static Bool init = False;
4696d522f475Smrg    static Bool result = False;
4697d522f475Smrg
4698d522f475Smrg    if (!init) {
4699d522f475Smrg	init = True;
4700d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
4701d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
4702d522f475Smrg#else
4703d522f475Smrg	result = (strstr(xtermEnvLocale(), "UTF-8") != NULL);
4704d522f475Smrg#endif
4705d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
4706d522f475Smrg    }
4707d522f475Smrg    return result;
4708d522f475Smrg}
4709d522f475Smrg#endif /* OPT_WIDE_CHARS */
4710d522f475Smrg
4711d522f475Smrg/*
4712d522f475Smrg * Returns the version-string used in the "-v' message as well as a few other
4713d522f475Smrg * places.  It is derived (when possible) from the __vendorversion__ symbol
4714d522f475Smrg * that some newer imake configurations define.
4715d522f475Smrg */
4716d522f475Smrgchar *
4717d522f475SmrgxtermVersion(void)
4718d522f475Smrg{
4719cd3331d0Smrg    static char vendor_version[] = __vendorversion__;
4720d522f475Smrg    static char *result;
4721cd3331d0Smrg
4722d522f475Smrg    if (result == 0) {
4723cd3331d0Smrg	char *vendor = vendor_version;
4724d522f475Smrg	char first[BUFSIZ];
4725d522f475Smrg	char second[BUFSIZ];
4726d522f475Smrg
4727d522f475Smrg	result = CastMallocN(char, strlen(vendor) + 9);
4728d522f475Smrg	if (result == 0)
4729d522f475Smrg	    result = vendor;
4730d522f475Smrg	else {
4731d522f475Smrg	    /* some vendors leave trash in this string */
4732d522f475Smrg	    for (;;) {
4733cd3331d0Smrg		if (!strncmp(vendor, "Version ", (size_t) 8))
4734d522f475Smrg		    vendor += 8;
4735d522f475Smrg		else if (isspace(CharOf(*vendor)))
4736d522f475Smrg		    ++vendor;
4737d522f475Smrg		else
4738d522f475Smrg		    break;
4739d522f475Smrg	    }
4740d522f475Smrg	    if (strlen(vendor) < BUFSIZ &&
4741d522f475Smrg		sscanf(vendor, "%[0-9.] %[A-Za-z_0-9.]", first, second) == 2)
4742d522f475Smrg		sprintf(result, "%s %s(%d)", second, first, XTERM_PATCH);
4743d522f475Smrg	    else
4744d522f475Smrg		sprintf(result, "%s(%d)", vendor, XTERM_PATCH);
4745d522f475Smrg	}
4746d522f475Smrg    }
4747d522f475Smrg    return result;
4748d522f475Smrg}
4749b7c89284Ssnj
4750b7c89284Ssnj/*
4751b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
4752b7c89284Ssnj */
4753b7c89284SsnjXtermWidget
4754b7c89284SsnjgetXtermWidget(Widget w)
4755b7c89284Ssnj{
4756b7c89284Ssnj    XtermWidget xw;
4757b7c89284Ssnj
4758b7c89284Ssnj    if (w == 0) {
4759b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
4760b7c89284Ssnj	if (!IsXtermWidget(xw)) {
4761b7c89284Ssnj	    xw = 0;
4762b7c89284Ssnj	}
4763b7c89284Ssnj    } else if (IsXtermWidget(w)) {
4764b7c89284Ssnj	xw = (XtermWidget) w;
4765b7c89284Ssnj    } else {
4766b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
4767b7c89284Ssnj    }
4768b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
4769b7c89284Ssnj    return xw;
4770b7c89284Ssnj}
4771