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