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