misc.c revision 0d92cbfd
10d92cbfdSchristos/* $XTermId: misc.c,v 1.391 2008/12/30 17:44:50 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4d522f475Smrg *
5d522f475Smrg * Copyright 1999-2007,2008 by Thomas E. Dickey
6d522f475Smrg *
7d522f475Smrg *                        All Rights Reserved
8d522f475Smrg *
9d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
10d522f475Smrg * copy of this software and associated documentation files (the
11d522f475Smrg * "Software"), to deal in the Software without restriction, including
12d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
13d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
14d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
15d522f475Smrg * the following conditions:
16d522f475Smrg *
17d522f475Smrg * The above copyright notice and this permission notice shall be included
18d522f475Smrg * in all copies or substantial portions of the Software.
19d522f475Smrg *
20d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
24d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27d522f475Smrg *
28d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
29d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
30d522f475Smrg * sale, use or other dealings in this Software without prior written
31d522f475Smrg * authorization.
32d522f475Smrg *
33d522f475Smrg *
34d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
35d522f475Smrg *
36d522f475Smrg *                         All Rights Reserved
37d522f475Smrg *
38d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
39d522f475Smrg * documentation for any purpose and without fee is hereby granted,
40d522f475Smrg * provided that the above copyright notice appear in all copies and that
41d522f475Smrg * both that copyright notice and this permission notice appear in
42d522f475Smrg * supporting documentation, and that the name of Digital Equipment
43d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
44d522f475Smrg * distribution of the software without specific, written prior permission.
45d522f475Smrg *
46d522f475Smrg *
47d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
48d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
49d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
50d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
52d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
53d522f475Smrg * SOFTWARE.
54d522f475Smrg */
55d522f475Smrg
56d522f475Smrg#include <version.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;
130d522f475Smrg    unsigned size = 100;
131d522f475Smrg    int n;
132d522f475Smrg
133d522f475Smrg    for (;;) {
134d522f475Smrg	buf = TypeRealloc(char, size, buf);
135d522f475Smrg	memset(buf, 0, size);
136d522f475Smrg
137d522f475Smrg	n = 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    }
186d522f475Smrg}
187d522f475Smrg
188d522f475Smrgstatic void
189d522f475Smrgunselectwindow(TScreen * screen, int flag)
190d522f475Smrg{
191d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
192d522f475Smrg
193d522f475Smrg    if (screen->hide_pointer) {
194d522f475Smrg	screen->hide_pointer = False;
195d522f475Smrg	xtermDisplayCursor(term);
196d522f475Smrg    }
197d522f475Smrg
198d522f475Smrg    if (!screen->always_highlight) {
199d522f475Smrg#if OPT_TEK4014
200d522f475Smrg	if (TEK4014_ACTIVE(term)) {
201d522f475Smrg	    if (!Ttoggled)
202d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
203d522f475Smrg	    screen->select &= ~flag;
204d522f475Smrg	    if (!Ttoggled)
205d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
206d522f475Smrg	} else
207d522f475Smrg#endif
208d522f475Smrg	{
209d522f475Smrg	    if (screen->xic)
210d522f475Smrg		XUnsetICFocus(screen->xic);
211d522f475Smrg
212d522f475Smrg	    screen->select &= ~flag;
213d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
214d522f475Smrg		HideCursor();
215d522f475Smrg	    if (screen->cursor_state)
216d522f475Smrg		ShowCursor();
217d522f475Smrg	}
218d522f475Smrg    }
219d522f475Smrg}
220d522f475Smrg
221d522f475Smrgstatic void
222d522f475SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent * ev)
223d522f475Smrg{
224d522f475Smrg    TScreen *screen = TScreenOf(xw);
225d522f475Smrg
226d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
227d522f475Smrg#ifdef ACTIVEWINDOWINPUTONLY
228d522f475Smrg    if (ev->window == XtWindow(XtParent(CURRENT_EMU())))
229d522f475Smrg#endif
230d522f475Smrg	if (((ev->detail) != NotifyInferior) &&
231d522f475Smrg	    ev->focus &&
232d522f475Smrg	    !(screen->select & FOCUS))
233d522f475Smrg	    selectwindow(screen, INWINDOW);
234d522f475Smrg}
235d522f475Smrg
236d522f475Smrgstatic void
237d522f475SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent * ev)
238d522f475Smrg{
239d522f475Smrg    TScreen *screen = TScreenOf(xw);
240d522f475Smrg
241d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
242d522f475Smrg#ifdef ACTIVEWINDOWINPUTONLY
243d522f475Smrg    if (ev->window == XtWindow(XtParent(CURRENT_EMU())))
244d522f475Smrg#endif
245d522f475Smrg	if (((ev->detail) != NotifyInferior) &&
246d522f475Smrg	    ev->focus &&
247d522f475Smrg	    !(screen->select & FOCUS))
248d522f475Smrg	    unselectwindow(screen, INWINDOW);
249d522f475Smrg}
250d522f475Smrg
251d522f475Smrg#ifndef XUrgencyHint
252d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
253d522f475Smrg#endif
254d522f475Smrg
255d522f475Smrgstatic void
256d522f475SmrgsetXUrgency(TScreen * screen, Bool enable)
257d522f475Smrg{
258d522f475Smrg    if (screen->bellIsUrgent) {
259d522f475Smrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow);
260d522f475Smrg	if (h != 0) {
261d522f475Smrg	    if (enable) {
262d522f475Smrg		h->flags |= XUrgencyHint;
263d522f475Smrg	    } else {
264d522f475Smrg		h->flags &= ~XUrgencyHint;
265d522f475Smrg	    }
266d522f475Smrg	    XSetWMHints(screen->display, VShellWindow, h);
267d522f475Smrg	}
268d522f475Smrg    }
269d522f475Smrg}
270d522f475Smrg
271d522f475Smrgvoid
272d522f475Smrgdo_xevents(void)
273d522f475Smrg{
274d522f475Smrg    TScreen *screen = TScreenOf(term);
275d522f475Smrg
276d522f475Smrg    if (XtAppPending(app_con)
277d522f475Smrg	||
278d522f475Smrg#if defined(VMS) || defined(__VMS)
279d522f475Smrg	screen->display->qlen > 0
280d522f475Smrg#else
281d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
282d522f475Smrg#endif
283d522f475Smrg	)
284d522f475Smrg	xevents();
285d522f475Smrg}
286d522f475Smrg
287d522f475Smrgvoid
288d522f475SmrgxtermDisplayCursor(XtermWidget xw)
289d522f475Smrg{
290d522f475Smrg    TScreen *screen = TScreenOf(xw);
291d522f475Smrg
292d522f475Smrg    if (screen->Vshow) {
293d522f475Smrg	if (screen->hide_pointer) {
294d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
295d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
296d522f475Smrg	} else {
297d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
298d522f475Smrg	    recolor_cursor(screen,
299d522f475Smrg			   screen->pointer_cursor,
300d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
301d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
302d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
303d522f475Smrg	}
304d522f475Smrg    }
305d522f475Smrg}
306d522f475Smrg
307d522f475Smrgvoid
308d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
309d522f475Smrg{
310d522f475Smrg    static int tried = -1;
311d522f475Smrg    TScreen *screen = TScreenOf(xw);
312d522f475Smrg
313d522f475Smrg#if OPT_TEK4014
314d522f475Smrg    if (TEK4014_SHOWN(xw))
315d522f475Smrg	enable = True;
316d522f475Smrg#endif
317d522f475Smrg
318d522f475Smrg    /*
319d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
320d522f475Smrg     * the mouse-mode:
321d522f475Smrg     */
322d522f475Smrg    if (!enable) {
323d522f475Smrg	switch (screen->pointer_mode) {
324d522f475Smrg	case pNever:
325d522f475Smrg	    enable = True;
326d522f475Smrg	    break;
327d522f475Smrg	case pNoMouse:
328d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
329d522f475Smrg		enable = True;
330d522f475Smrg	    break;
331d522f475Smrg	case pAlways:
332d522f475Smrg	    break;
333d522f475Smrg	}
334d522f475Smrg    }
335d522f475Smrg
336d522f475Smrg    if (enable) {
337d522f475Smrg	if (screen->hide_pointer) {
338d522f475Smrg	    screen->hide_pointer = False;
339d522f475Smrg	    xtermDisplayCursor(xw);
340d522f475Smrg	    switch (screen->send_mouse_pos) {
341d522f475Smrg	    case ANY_EVENT_MOUSE:
342d522f475Smrg		break;
343d522f475Smrg	    default:
344d522f475Smrg		MotionOff(screen, xw);
345d522f475Smrg		break;
346d522f475Smrg	    }
347d522f475Smrg	}
348d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
349d522f475Smrg	if (screen->hidden_cursor == 0) {
350d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
351d522f475Smrg	}
352d522f475Smrg	if (screen->hidden_cursor == 0) {
353d522f475Smrg	    tried = 1;
354d522f475Smrg	} else {
355d522f475Smrg	    tried = 0;
356d522f475Smrg	    screen->hide_pointer = True;
357d522f475Smrg	    xtermDisplayCursor(xw);
358d522f475Smrg	    MotionOn(screen, xw);
359d522f475Smrg	}
360d522f475Smrg    }
361d522f475Smrg}
362d522f475Smrg
363d522f475Smrgvoid
364d522f475Smrgxevents(void)
365d522f475Smrg{
366d522f475Smrg    XtermWidget xw = term;
367d522f475Smrg    TScreen *screen = TScreenOf(xw);
368d522f475Smrg    XEvent event;
369d522f475Smrg    XtInputMask input_mask;
370d522f475Smrg
371d522f475Smrg    if (need_cleanup)
372d522f475Smrg	Cleanup(0);
373d522f475Smrg
374d522f475Smrg    if (screen->scroll_amt)
375d522f475Smrg	FlushScroll(xw);
376d522f475Smrg    /*
377d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
378d522f475Smrg     * will process the timeout and return without blockng on the
379d522f475Smrg     * XEvent queue.  Other sources i.e. the pty are handled elsewhere
380d522f475Smrg     * with select().
381d522f475Smrg     */
382d522f475Smrg    while ((input_mask = XtAppPending(app_con)) & XtIMTimer)
383d522f475Smrg	XtAppProcessEvent(app_con, XtIMTimer);
384d522f475Smrg#if OPT_SESSION_MGT
385d522f475Smrg    /*
386d522f475Smrg     * Session management events are alternative input events. Deal with
387d522f475Smrg     * them in the same way.
388d522f475Smrg     */
389d522f475Smrg    while ((input_mask = XtAppPending(app_con)) & XtIMAlternateInput)
390d522f475Smrg	XtAppProcessEvent(app_con, XtIMAlternateInput);
391d522f475Smrg#endif
392d522f475Smrg
393d522f475Smrg    /*
394d522f475Smrg     * If there's no XEvents, don't wait around...
395d522f475Smrg     */
396d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
397d522f475Smrg	return;
398d522f475Smrg    do {
399d522f475Smrg	/*
400d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
401d522f475Smrg	 * We simply ignore all events except for those not passed down to
402d522f475Smrg	 * this function, e.g., those handled in in_put().
403d522f475Smrg	 */
404d522f475Smrg	if (screen->waitingForTrackInfo) {
405d522f475Smrg	    Sleep(10);
406d522f475Smrg	    return;
407d522f475Smrg	}
408d522f475Smrg	XtAppNextEvent(app_con, &event);
409d522f475Smrg	/*
410d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
411d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
412d522f475Smrg	 * looking at the event ourselves we make sure that we can
413d522f475Smrg	 * do the right thing.
414d522f475Smrg	 */
415d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
416d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
417d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
418d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
419d522f475Smrg	} else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE
420d522f475Smrg#if OPT_DEC_LOCATOR
421d522f475Smrg		    || screen->send_mouse_pos == DEC_LOCATOR
422d522f475Smrg#endif /* OPT_DEC_LOCATOR */
423d522f475Smrg		   )
424d522f475Smrg		   && event.xany.type == MotionNotify
425d522f475Smrg		   && event.xcrossing.window == XtWindow(xw)) {
426d522f475Smrg	    SendMousePosition(xw, &event);
427d522f475Smrg	    continue;
428d522f475Smrg	}
429d522f475Smrg
430d522f475Smrg	if (!event.xany.send_event ||
431d522f475Smrg	    screen->allowSendEvents ||
432d522f475Smrg	    ((event.xany.type != KeyPress) &&
433d522f475Smrg	     (event.xany.type != KeyRelease) &&
434d522f475Smrg	     (event.xany.type != ButtonPress) &&
435d522f475Smrg	     (event.xany.type != ButtonRelease))) {
436d522f475Smrg
437d522f475Smrg	    /*
438d522f475Smrg	     * If the event is interesting (and not a keyboard event), turn the
439d522f475Smrg	     * mouse pointer back on.
440d522f475Smrg	     */
441d522f475Smrg	    if (screen->hide_pointer) {
442d522f475Smrg		switch (event.xany.type) {
443d522f475Smrg		case KeyPress:
444d522f475Smrg		case KeyRelease:
445d522f475Smrg		case ButtonPress:
446d522f475Smrg		case ButtonRelease:
447d522f475Smrg		    /* also these... */
448d522f475Smrg		case Expose:
449d522f475Smrg		case NoExpose:
450d522f475Smrg		case PropertyNotify:
451d522f475Smrg		    break;
452d522f475Smrg		default:
453d522f475Smrg		    xtermShowPointer(xw, True);
454d522f475Smrg		    break;
455d522f475Smrg		}
456d522f475Smrg	    }
457d522f475Smrg
458d522f475Smrg	    XtDispatchEvent(&event);
459d522f475Smrg	}
460d522f475Smrg    } while ((input_mask = XtAppPending(app_con)) & XtIMXEvent);
461d522f475Smrg}
462d522f475Smrg
463d522f475Smrgstatic Cursor
464d522f475Smrgmake_hidden_cursor(XtermWidget xw)
465d522f475Smrg{
466d522f475Smrg    TScreen *screen = TScreenOf(xw);
467d522f475Smrg    Cursor c;
468d522f475Smrg    Display *dpy = screen->display;
469d522f475Smrg    XFontStruct *fn;
470d522f475Smrg
471d522f475Smrg    static XColor dummy;
472d522f475Smrg
473d522f475Smrg    /*
474d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
475d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
476d522f475Smrg     * server insists on drawing _somethng_.
477d522f475Smrg     */
478d522f475Smrg    TRACE(("Ask for nil2 font\n"));
479d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
480d522f475Smrg	TRACE(("...Ask for fixed font\n"));
481d522f475Smrg	fn = XLoadQueryFont(dpy, "fixed");
482d522f475Smrg    }
483d522f475Smrg
484d522f475Smrg    if (fn != 0) {
485d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
486d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
487d522f475Smrg	XFreeFont(dpy, fn);
488d522f475Smrg    } else {
489d522f475Smrg	c = 0;
490d522f475Smrg    }
491d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
492d522f475Smrg    return (c);
493d522f475Smrg}
494d522f475Smrg
495d522f475SmrgCursor
496d522f475Smrgmake_colored_cursor(unsigned cursorindex,	/* index into font */
497d522f475Smrg		    unsigned long fg,	/* pixel value */
498d522f475Smrg		    unsigned long bg)	/* pixel value */
499d522f475Smrg{
500d522f475Smrg    TScreen *screen = TScreenOf(term);
501d522f475Smrg    Cursor c;
502d522f475Smrg    Display *dpy = screen->display;
503d522f475Smrg
504d522f475Smrg    c = XCreateFontCursor(dpy, cursorindex);
505d522f475Smrg    if (c != None) {
506d522f475Smrg	recolor_cursor(screen, c, fg, bg);
507d522f475Smrg    }
508d522f475Smrg    return (c);
509d522f475Smrg}
510d522f475Smrg
511d522f475Smrg/* ARGSUSED */
512d522f475Smrgvoid
513d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
514d522f475Smrg		 XEvent * event,
515d522f475Smrg		 String * params GCC_UNUSED,
516d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
517d522f475Smrg{
518d522f475Smrg    TRACE(("Handle 7bit-key\n"));
519d522f475Smrg#ifdef ACTIVEWINDOWINPUTONLY
520d522f475Smrg    if (w == CURRENT_EMU())
521d522f475Smrg#endif
522d522f475Smrg	Input(term, &event->xkey, False);
523d522f475Smrg}
524d522f475Smrg
525d522f475Smrg/* ARGSUSED */
526d522f475Smrgvoid
527d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
528d522f475Smrg			 XEvent * event,
529d522f475Smrg			 String * params GCC_UNUSED,
530d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
531d522f475Smrg{
532d522f475Smrg    TRACE(("Handle 8bit-key\n"));
533d522f475Smrg#ifdef ACTIVEWINDOWINPUTONLY
534d522f475Smrg    if (w == CURRENT_EMU())
535d522f475Smrg#endif
536d522f475Smrg	Input(term, &event->xkey, True);
537d522f475Smrg}
538d522f475Smrg
539d522f475Smrg/* ARGSUSED */
540d522f475Smrgvoid
541d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
542d522f475Smrg		  XEvent * event GCC_UNUSED,
543d522f475Smrg		  String * params,
544d522f475Smrg		  Cardinal *nparams)
545d522f475Smrg{
546d522f475Smrg#ifdef ACTIVEWINDOWINPUTONLY
547d522f475Smrg    if (w != CURRENT_EMU())
548d522f475Smrg	return;
549d522f475Smrg#endif
550d522f475Smrg
551d522f475Smrg    if (*nparams != 1)
552d522f475Smrg	return;
553d522f475Smrg
554d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
5550d92cbfdSchristos	const char *abcdef = "ABCDEF";
5560d92cbfdSchristos	const char *xxxxxx;
557d522f475Smrg	Char c, *p;
5580d92cbfdSchristos	unsigned value = 0;
5590d92cbfdSchristos
5600d92cbfdSchristos	for (p = (Char *) (*params + 2); (c = CharOf(x_toupper(*p))) !=
5610d92cbfdSchristos	     '\0'; p++) {
5620d92cbfdSchristos	    value *= 16;
563d522f475Smrg	    if (c >= '0' && c <= '9')
5640d92cbfdSchristos		value += (unsigned) (c - '0');
5650d92cbfdSchristos	    else if ((xxxxxx = strchr(abcdef, c)) != 0)
5660d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
567d522f475Smrg	    else
568d522f475Smrg		break;
569d522f475Smrg	}
5700d92cbfdSchristos	if (c == '\0') {
5710d92cbfdSchristos	    Char hexval[2];
5720d92cbfdSchristos	    hexval[0] = (Char) value;
5730d92cbfdSchristos	    hexval[1] = 0;
574d522f475Smrg	    StringInput(term, hexval, 1);
5750d92cbfdSchristos	}
576d522f475Smrg    } else {
577d522f475Smrg	StringInput(term, (Char *) * params, strlen(*params));
578d522f475Smrg    }
579d522f475Smrg}
580d522f475Smrg
581d522f475Smrg#if OPT_EXEC_XTERM
582d522f475Smrg
583d522f475Smrg#ifndef PROCFS_ROOT
584d522f475Smrg#define PROCFS_ROOT "/proc"
585d522f475Smrg#endif
586d522f475Smrg
587d522f475Smrg/* ARGSUSED */
588d522f475Smrgvoid
589d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
590d522f475Smrg		    XEvent * event GCC_UNUSED,
591d522f475Smrg		    String * params,
592d522f475Smrg		    Cardinal *nparams)
593d522f475Smrg{
594d522f475Smrg    TScreen *screen = &term->screen;
595d522f475Smrg    char *child_cwd = NULL;
596d522f475Smrg    char *child_exe;
597d522f475Smrg    pid_t pid;
598d522f475Smrg
599d522f475Smrg    /*
600d522f475Smrg     * Try to find the actual program which is running in the child process.
601d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
602d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
603d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
604d522f475Smrg     */
605d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
606d522f475Smrg    if (!child_exe) {
607d522f475Smrg	if (strncmp(ProgramName, "./", 2)
608d522f475Smrg	    && strncmp(ProgramName, "../", 3)) {
609d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
610d522f475Smrg	} else {
611d522f475Smrg	    fprintf(stderr, "Cannot exec-xterm given %s\n", ProgramName);
612d522f475Smrg	}
613d522f475Smrg	if (child_exe == 0)
614d522f475Smrg	    return;
615d522f475Smrg    }
616d522f475Smrg
617d522f475Smrg    /*
618d522f475Smrg     * Determine the current working directory of the child so that we can
619d522f475Smrg     * spawn a new terminal in the same directory.
620d522f475Smrg     *
621d522f475Smrg     * If we cannot get the CWD of the child, just use our own.
622d522f475Smrg     */
623d522f475Smrg    if (screen->pid) {
624d522f475Smrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
625d522f475Smrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid);
626d522f475Smrg	child_cwd = Readlink(child_cwd_link);
627d522f475Smrg    }
628d522f475Smrg
629d522f475Smrg    /* The reaper will take care of cleaning up the child */
630d522f475Smrg    pid = fork();
631d522f475Smrg    if (pid == -1) {
632d522f475Smrg	fprintf(stderr, "Could not fork: %s\n", SysErrorMsg(errno));
633d522f475Smrg    } else if (!pid) {
634d522f475Smrg	/* We are the child */
635d522f475Smrg	if (child_cwd) {
636d522f475Smrg	    chdir(child_cwd);	/* We don't care if this fails */
637d522f475Smrg	}
638d522f475Smrg
639d522f475Smrg	if (setuid(screen->uid) == -1
640d522f475Smrg	    || setgid(screen->gid) == -1) {
641d522f475Smrg	    fprintf(stderr, "Cannot reset uid/gid\n");
642d522f475Smrg	} else {
6430d92cbfdSchristos	    unsigned myargc = *nparams + 1;
644d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
6450d92cbfdSchristos	    unsigned n = 0;
646d522f475Smrg
647d522f475Smrg	    myargv[n++] = child_exe;
648d522f475Smrg
649d522f475Smrg	    while (n < myargc) {
650d522f475Smrg		myargv[n++] = *params++;
651d522f475Smrg	    }
652d522f475Smrg
653d522f475Smrg	    myargv[n] = 0;
654d522f475Smrg	    execv(child_exe, myargv);
655d522f475Smrg
656d522f475Smrg	    /* If we get here, we've failed */
657d522f475Smrg	    fprintf(stderr, "exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
658d522f475Smrg	}
659d522f475Smrg	_exit(0);
660d522f475Smrg    } else {
661d522f475Smrg	/* We are the parent; clean up */
662d522f475Smrg	if (child_cwd)
663d522f475Smrg	    free(child_cwd);
664d522f475Smrg	if (child_exe)
665d522f475Smrg	    free(child_exe);
666d522f475Smrg    }
667d522f475Smrg}
668d522f475Smrg#endif /* OPT_EXEC_XTERM */
669d522f475Smrg
670d522f475Smrg/*
671d522f475Smrg * Rather than sending characters to the host, put them directly into our
672d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
673d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
674d522f475Smrg *
675d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
676d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
677d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
678d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
679d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
680d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
681d522f475Smrg */
682d522f475Smrg/* ARGSUSED */
683d522f475Smrgvoid
684d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
685d522f475Smrg		XEvent * event GCC_UNUSED,
686d522f475Smrg		String * params,
687d522f475Smrg		Cardinal *param_count)
688d522f475Smrg{
689d522f475Smrg    if (*param_count == 1) {
690d522f475Smrg	char *value = params[0];
691d522f475Smrg	int need = strlen(value);
692d522f475Smrg	int used = VTbuffer->next - VTbuffer->buffer;
693d522f475Smrg	int have = VTbuffer->last - VTbuffer->buffer;
694d522f475Smrg
695d522f475Smrg	if (have - used + need < BUF_SIZE) {
696d522f475Smrg
697d522f475Smrg	    fillPtyData(TScreenOf(term), VTbuffer, value, (int) strlen(value));
698d522f475Smrg
699d522f475Smrg	    TRACE(("Interpret %s\n", value));
700d522f475Smrg	    VTbuffer->update++;
701d522f475Smrg	}
702d522f475Smrg    }
703d522f475Smrg}
704d522f475Smrg
705d522f475Smrg/*ARGSUSED*/
706d522f475Smrgvoid
707d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
708d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
709d522f475Smrg		  XEvent * event GCC_UNUSED,
710d522f475Smrg		  Boolean * cont GCC_UNUSED)
711d522f475Smrg{
712d522f475Smrg    /* NOP since we handled it above */
713d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
714d522f475Smrg}
715d522f475Smrg
716d522f475Smrg/*ARGSUSED*/
717d522f475Smrgvoid
718d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
719d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
720d522f475Smrg		  XEvent * event GCC_UNUSED,
721d522f475Smrg		  Boolean * cont GCC_UNUSED)
722d522f475Smrg{
723d522f475Smrg    /* NOP since we handled it above */
724d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
725d522f475Smrg}
726d522f475Smrg
727d522f475Smrg/*ARGSUSED*/
728d522f475Smrgvoid
729d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
730d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
731d522f475Smrg		  XEvent * ev,
732d522f475Smrg		  Boolean * cont GCC_UNUSED)
733d522f475Smrg{
734d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
735d522f475Smrg    XtermWidget xw = term;
736d522f475Smrg    TScreen *screen = TScreenOf(xw);
737d522f475Smrg
738d522f475Smrg    TRACE(("HandleFocusChange type=%s, mode=%d, detail=%d\n",
739d522f475Smrg	   visibleEventType(event->type),
740d522f475Smrg	   event->mode,
741d522f475Smrg	   event->detail));
742d522f475Smrg
743d522f475Smrg    if (screen->quiet_grab
744d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
745d522f475Smrg	;
746d522f475Smrg    } else if (event->type == FocusIn) {
747d522f475Smrg	setXUrgency(screen, False);
748d522f475Smrg
749d522f475Smrg	/*
750d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
751d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
752d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
753d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
754d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
755d522f475Smrg	 * delivered to the obscured window.
756d522f475Smrg	 */
757d522f475Smrg	if (event->detail == NotifyNonlinear
758d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
759d522f475Smrg	    unselectwindow(screen, INWINDOW);
760d522f475Smrg	}
761d522f475Smrg	selectwindow(screen,
762d522f475Smrg		     ((event->detail == NotifyPointer)
763d522f475Smrg		      ? INWINDOW
764d522f475Smrg		      : FOCUS));
765d522f475Smrg	SendFocusButton(xw, event);
766d522f475Smrg    } else {
767d522f475Smrg#if OPT_FOCUS_EVENT
768d522f475Smrg	if (event->type == FocusOut) {
769d522f475Smrg	    SendFocusButton(xw, event);
770d522f475Smrg	}
771d522f475Smrg#endif
772d522f475Smrg	/*
773d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
774d522f475Smrg	 * ignore.
775d522f475Smrg	 */
776d522f475Smrg	if (event->mode != NotifyGrab) {
777d522f475Smrg	    unselectwindow(screen,
778d522f475Smrg			   ((event->detail == NotifyPointer)
779d522f475Smrg			    ? INWINDOW
780d522f475Smrg			    : FOCUS));
781d522f475Smrg	}
782d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
783d522f475Smrg	    Bell(XkbBI_Info, 100);
784d522f475Smrg	    ReverseVideo(xw);
785d522f475Smrg	    screen->grabbedKbd = False;
786d522f475Smrg	    update_securekbd();
787d522f475Smrg	}
788d522f475Smrg    }
789d522f475Smrg}
790d522f475Smrg
791d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
792d522f475Smrg
793d522f475Smrgvoid
794d522f475SmrgBell(Atom which GCC_UNUSED, int percent)
795d522f475Smrg{
796d522f475Smrg    TScreen *screen = TScreenOf(term);
797d522f475Smrg    struct timeval curtime;
798d522f475Smrg    long now_msecs;
799d522f475Smrg
800d522f475Smrg    TRACE(("BELL %ld %d%%\n", (long) which, percent));
801d522f475Smrg    if (!XtIsRealized((Widget) term)) {
802d522f475Smrg	return;
803d522f475Smrg    }
804d522f475Smrg
805d522f475Smrg    setXUrgency(screen, True);
806d522f475Smrg
807d522f475Smrg    /* has enough time gone by that we are allowed to ring
808d522f475Smrg       the bell again? */
809d522f475Smrg    if (screen->bellSuppressTime) {
810d522f475Smrg	if (screen->bellInProgress) {
811d522f475Smrg	    do_xevents();
812d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
813d522f475Smrg		return;
814d522f475Smrg	    }
815d522f475Smrg	}
816d522f475Smrg	X_GETTIMEOFDAY(&curtime);
817d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
818d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
819d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
820d522f475Smrg	    return;
821d522f475Smrg	}
822d522f475Smrg	lastBellTime = now_msecs;
823d522f475Smrg    }
824d522f475Smrg
825d522f475Smrg    if (screen->visualbell) {
826d522f475Smrg	VisualBell();
827d522f475Smrg    } else {
828d522f475Smrg#if defined(HAVE_XKB_BELL_EXT)
829d522f475Smrg	XkbBell(screen->display, VShellWindow, percent, which);
830d522f475Smrg#else
831d522f475Smrg	XBell(screen->display, percent);
832d522f475Smrg#endif
833d522f475Smrg    }
834d522f475Smrg
835d522f475Smrg    if (screen->poponbell)
836d522f475Smrg	XRaiseWindow(screen->display, VShellWindow);
837d522f475Smrg
838d522f475Smrg    if (screen->bellSuppressTime) {
839d522f475Smrg	/* now we change a property and wait for the notify event to come
840d522f475Smrg	   back.  If the server is suspending operations while the bell
841d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
842d522f475Smrg	   know when the previous bell has finished */
843d522f475Smrg	Widget w = CURRENT_EMU();
844d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
845d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
846d522f475Smrg	screen->bellInProgress = True;
847d522f475Smrg    }
848d522f475Smrg}
849d522f475Smrg
850d522f475Smrg#define VB_DELAY screen->visualBellDelay
851d522f475Smrg
852d522f475Smrgstatic void
853d522f475SmrgflashWindow(TScreen * screen, Window window, GC visualGC, unsigned width, unsigned height)
854d522f475Smrg{
855d522f475Smrg    XFillRectangle(screen->display, window, visualGC, 0, 0, width, height);
856d522f475Smrg    XFlush(screen->display);
857d522f475Smrg    Sleep(VB_DELAY);
858d522f475Smrg    XFillRectangle(screen->display, window, visualGC, 0, 0, width, height);
859d522f475Smrg}
860d522f475Smrg
861d522f475Smrgvoid
862d522f475SmrgVisualBell(void)
863d522f475Smrg{
864d522f475Smrg    TScreen *screen = TScreenOf(term);
865d522f475Smrg
866d522f475Smrg    if (VB_DELAY > 0) {
867d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
868d522f475Smrg			  T_COLOR(screen, TEXT_BG));
869d522f475Smrg	XGCValues gcval;
870d522f475Smrg	GC visualGC;
871d522f475Smrg
872d522f475Smrg	gcval.function = GXxor;
873d522f475Smrg	gcval.foreground = xorPixel;
874d522f475Smrg	visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval);
875d522f475Smrg#if OPT_TEK4014
876d522f475Smrg	if (TEK4014_ACTIVE(term)) {
877d522f475Smrg	    TekScreen *tekscr = &(tekWidget->screen);
878d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
879d522f475Smrg			TFullWidth(tekscr),
880d522f475Smrg			TFullHeight(tekscr));
881d522f475Smrg	} else
882d522f475Smrg#endif
883d522f475Smrg	{
884d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
885d522f475Smrg			FullWidth(screen),
886d522f475Smrg			FullHeight(screen));
887d522f475Smrg	}
888d522f475Smrg	XtReleaseGC((Widget) term, visualGC);
889d522f475Smrg    }
890d522f475Smrg}
891d522f475Smrg
892d522f475Smrg/* ARGSUSED */
893d522f475Smrgvoid
894d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
895d522f475Smrg			 XtPointer data GCC_UNUSED,
896d522f475Smrg			 XEvent * ev,
897d522f475Smrg			 Boolean * more GCC_UNUSED)
898d522f475Smrg{
899d522f475Smrg    TScreen *screen = TScreenOf(term);
900d522f475Smrg
901d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
902d522f475Smrg	screen->bellInProgress = False;
903d522f475Smrg    }
904d522f475Smrg}
905d522f475Smrg
906d522f475SmrgWindow
907d522f475SmrgWMFrameWindow(XtermWidget termw)
908d522f475Smrg{
909d522f475Smrg    Window win_root, win_current, *children;
910d522f475Smrg    Window win_parent = 0;
911d522f475Smrg    unsigned int nchildren;
912d522f475Smrg
913d522f475Smrg    win_current = XtWindow(termw);
914d522f475Smrg
915d522f475Smrg    /* find the parent which is child of root */
916d522f475Smrg    do {
917d522f475Smrg	if (win_parent)
918d522f475Smrg	    win_current = win_parent;
919d522f475Smrg	XQueryTree((&termw->screen)->display,
920d522f475Smrg		   win_current,
921d522f475Smrg		   &win_root,
922d522f475Smrg		   &win_parent,
923d522f475Smrg		   &children,
924d522f475Smrg		   &nchildren);
925d522f475Smrg	XFree(children);
926d522f475Smrg    } while (win_root != win_parent);
927d522f475Smrg
928d522f475Smrg    return win_current;
929d522f475Smrg}
930d522f475Smrg
931d522f475Smrg#if OPT_DABBREV
932d522f475Smrg/*
933d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
934d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
935d522f475Smrg * to find expansions of a typed word.  It compares consecutive
936d522f475Smrg * expansions and ignores one of them if they are identical.
937d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
938d522f475Smrg */
939d522f475Smrg
940d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
941d522f475Smrg#define MAXWLEN 1024		/* maximum word length as in tcsh */
942d522f475Smrg
943d522f475Smrgstatic int
944d522f475Smrgdabbrev_prev_char(int *xp, int *yp, TScreen * screen)
945d522f475Smrg{
946d522f475Smrg    Char *linep;
947d522f475Smrg
948d522f475Smrg    while (*yp >= 0) {
949d522f475Smrg	linep = BUF_CHARS(screen->allbuf, *yp);
950d522f475Smrg	if (--*xp >= 0)
951d522f475Smrg	    return linep[*xp];
952d522f475Smrg	if (--*yp < 0)		/* go to previous line */
953d522f475Smrg	    break;
954d522f475Smrg	*xp = MaxCols(screen);
955d522f475Smrg	if (!((long) BUF_FLAGS(screen->allbuf, *yp) & LINEWRAPPED))
956d522f475Smrg	    return ' ';		/* treat lines as separate */
957d522f475Smrg    }
958d522f475Smrg    return -1;
959d522f475Smrg}
960d522f475Smrg
961d522f475Smrgstatic char *
962d522f475Smrgdabbrev_prev_word(int *xp, int *yp, TScreen * screen)
963d522f475Smrg{
964d522f475Smrg    static char ab[MAXWLEN];
965d522f475Smrg    char *abword;
966d522f475Smrg    int c;
967d522f475Smrg
968d522f475Smrg    abword = ab + MAXWLEN - 1;
969d522f475Smrg    *abword = '\0';		/* end of string marker */
970d522f475Smrg
971d522f475Smrg    while ((c = dabbrev_prev_char(xp, yp, screen)) >= 0 &&
972d522f475Smrg	   IS_WORD_CONSTITUENT(c))
973d522f475Smrg	if (abword > ab)	/* store only |MAXWLEN| last chars */
974d522f475Smrg	    *(--abword) = c;
975d522f475Smrg    if (c < 0) {
976d522f475Smrg	if (abword < ab + MAXWLEN - 1)
977d522f475Smrg	    return abword;
978d522f475Smrg	else
979d522f475Smrg	    return 0;
980d522f475Smrg    }
981d522f475Smrg
982d522f475Smrg    while ((c = dabbrev_prev_char(xp, yp, screen)) >= 0 &&
983d522f475Smrg	   !IS_WORD_CONSTITUENT(c)) ;	/* skip preceding spaces */
984d522f475Smrg    (*xp)++;			/* can be | > screen->max_col| */
985d522f475Smrg    return abword;
986d522f475Smrg}
987d522f475Smrg
988d522f475Smrgstatic int
989d522f475Smrgdabbrev_expand(TScreen * screen)
990d522f475Smrg{
991d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
992d522f475Smrg
993d522f475Smrg    static int x, y;
994d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
995d522f475Smrg    static unsigned int expansions;
996d522f475Smrg
997d522f475Smrg    char *expansion;
998d522f475Smrg    Char *copybuffer;
999d522f475Smrg    size_t hint_len;
1000d522f475Smrg    unsigned del_cnt;
1001d522f475Smrg    unsigned buf_cnt;
1002d522f475Smrg
1003d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1004d522f475Smrg	expansions = 0;
1005d522f475Smrg	x = screen->cur_col;
1006d522f475Smrg	y = screen->cur_row + screen->savelines;
1007d522f475Smrg
1008d522f475Smrg	free(dabbrev_hint);	/* free(NULL) is OK */
1009d522f475Smrg	dabbrev_hint = dabbrev_prev_word(&x, &y, screen);
1010d522f475Smrg	if (!dabbrev_hint)
1011d522f475Smrg	    return 0;		/* no preceding word? */
1012d522f475Smrg	free(lastexpansion);
1013d522f475Smrg	if (!(lastexpansion = strdup(dabbrev_hint)))	/* make own copy */
1014d522f475Smrg	    return 0;
1015d522f475Smrg	if (!(dabbrev_hint = strdup(dabbrev_hint))) {
1016d522f475Smrg	    free(lastexpansion);
1017d522f475Smrg	    return 0;
1018d522f475Smrg	}
1019d522f475Smrg	screen->dabbrev_working = 1;	/* we are in the middle of dabbrev process */
1020d522f475Smrg    }
1021d522f475Smrg
1022d522f475Smrg    hint_len = strlen(dabbrev_hint);
1023d522f475Smrg    for (;;) {
1024d522f475Smrg	if (!(expansion = dabbrev_prev_word(&x, &y, screen))) {
1025d522f475Smrg	    if (expansions >= 2) {
1026d522f475Smrg		expansions = 0;
1027d522f475Smrg		x = screen->cur_col;
1028d522f475Smrg		y = screen->cur_row + screen->savelines;
1029d522f475Smrg		continue;
1030d522f475Smrg	    }
1031d522f475Smrg	    break;
1032d522f475Smrg	}
1033d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1034d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1035d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1036d522f475Smrg	    break;
1037d522f475Smrg    }
1038d522f475Smrg    if (!expansion)		/* no expansion found */
1039d522f475Smrg	return 0;
1040d522f475Smrg
1041d522f475Smrg    del_cnt = strlen(lastexpansion) - hint_len;
1042d522f475Smrg    buf_cnt = del_cnt + strlen(expansion) - hint_len;
1043d522f475Smrg    if (!(copybuffer = TypeMallocN(Char, buf_cnt)))
1044d522f475Smrg	return 0;
1045d522f475Smrg    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);	/* delete previous expansion */
1046d522f475Smrg    memmove(copybuffer + del_cnt,
1047d522f475Smrg	    expansion + hint_len,
1048d522f475Smrg	    strlen(expansion) - hint_len);
1049d522f475Smrg    v_write(pty, copybuffer, buf_cnt);
1050d522f475Smrg    screen->dabbrev_working = 1;	/* v_write() just set it to 1 */
1051d522f475Smrg    free(copybuffer);
1052d522f475Smrg
1053d522f475Smrg    free(lastexpansion);
1054d522f475Smrg    lastexpansion = strdup(expansion);
1055d522f475Smrg    if (!lastexpansion)
1056d522f475Smrg	return 0;
1057d522f475Smrg    expansions++;
1058d522f475Smrg    return 1;
1059d522f475Smrg}
1060d522f475Smrg
1061d522f475Smrg/*ARGSUSED*/
1062d522f475Smrgvoid
1063d522f475SmrgHandleDabbrevExpand(Widget gw,
1064d522f475Smrg		    XEvent * event GCC_UNUSED,
1065d522f475Smrg		    String * params GCC_UNUSED,
1066d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1067d522f475Smrg{
1068d522f475Smrg    if (IsXtermWidget(gw)) {
1069d522f475Smrg	XtermWidget w = (XtermWidget) gw;
1070d522f475Smrg	TScreen *screen = &w->screen;
1071d522f475Smrg	if (!dabbrev_expand(screen))
1072d522f475Smrg	    Bell(XkbBI_TerminalBell, 0);
1073d522f475Smrg    }
1074d522f475Smrg}
1075d522f475Smrg#endif /* OPT_DABBREV */
1076d522f475Smrg
1077d522f475Smrg#if OPT_MAXIMIZE
1078d522f475Smrg/*ARGSUSED*/
1079d522f475Smrgvoid
1080d522f475SmrgHandleDeIconify(Widget gw,
1081d522f475Smrg		XEvent * event GCC_UNUSED,
1082d522f475Smrg		String * params GCC_UNUSED,
1083d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1084d522f475Smrg{
1085d522f475Smrg    if (IsXtermWidget(gw)) {
1086d522f475Smrg	TScreen *screen = TScreenOf((XtermWidget) gw);
1087d522f475Smrg	XMapWindow(screen->display, VShellWindow);
1088d522f475Smrg    }
1089d522f475Smrg}
1090d522f475Smrg
1091d522f475Smrg/*ARGSUSED*/
1092d522f475Smrgvoid
1093d522f475SmrgHandleIconify(Widget gw,
1094d522f475Smrg	      XEvent * event GCC_UNUSED,
1095d522f475Smrg	      String * params GCC_UNUSED,
1096d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1097d522f475Smrg{
1098d522f475Smrg    if (IsXtermWidget(gw)) {
1099d522f475Smrg	TScreen *screen = TScreenOf((XtermWidget) gw);
1100d522f475Smrg	XIconifyWindow(screen->display,
1101d522f475Smrg		       VShellWindow,
1102d522f475Smrg		       DefaultScreen(screen->display));
1103d522f475Smrg    }
1104d522f475Smrg}
1105d522f475Smrg
1106d522f475Smrgint
1107d522f475SmrgQueryMaximize(XtermWidget termw, unsigned *width, unsigned *height)
1108d522f475Smrg{
1109d522f475Smrg    TScreen *screen = &termw->screen;
1110d522f475Smrg    XSizeHints hints;
1111d522f475Smrg    long supp = 0;
1112d522f475Smrg    Window root_win;
1113d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1114d522f475Smrg    int root_y = -1;
1115d522f475Smrg    unsigned root_border;
1116d522f475Smrg    unsigned root_depth;
1117d522f475Smrg
1118d522f475Smrg    if (XGetGeometry(screen->display,
1119d522f475Smrg		     RootWindowOfScreen(XtScreen(termw)),
1120d522f475Smrg		     &root_win,
1121d522f475Smrg		     &root_x,
1122d522f475Smrg		     &root_y,
1123d522f475Smrg		     width,
1124d522f475Smrg		     height,
1125d522f475Smrg		     &root_border,
1126d522f475Smrg		     &root_depth)) {
1127d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1128d522f475Smrg	       root_x,
1129d522f475Smrg	       root_y,
1130d522f475Smrg	       *width,
1131d522f475Smrg	       *height,
1132d522f475Smrg	       root_border));
1133d522f475Smrg
1134d522f475Smrg	*width -= (root_border * 2);
1135d522f475Smrg	*height -= (root_border * 2);
1136d522f475Smrg
1137d522f475Smrg	hints.flags = PMaxSize;
1138d522f475Smrg	if (XGetWMNormalHints(screen->display,
1139d522f475Smrg			      VShellWindow,
1140d522f475Smrg			      &hints,
1141d522f475Smrg			      &supp)
1142d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1143d522f475Smrg
1144d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1145d522f475Smrg		   hints.max_width,
1146d522f475Smrg		   hints.max_height));
1147d522f475Smrg
1148d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1149d522f475Smrg		*width = hints.max_width;
1150d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1151d522f475Smrg		*height = hints.max_height;
1152d522f475Smrg	}
1153d522f475Smrg	return 1;
1154d522f475Smrg    }
1155d522f475Smrg    return 0;
1156d522f475Smrg}
1157d522f475Smrg
1158d522f475Smrgvoid
1159d522f475SmrgRequestMaximize(XtermWidget termw, int maximize)
1160d522f475Smrg{
1161d522f475Smrg    TScreen *screen = &termw->screen;
1162d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1163d522f475Smrg    unsigned root_width, root_height;
1164d522f475Smrg
1165d522f475Smrg    if (maximize) {
1166d522f475Smrg
1167d522f475Smrg	if (QueryMaximize(termw, &root_width, &root_height)) {
1168d522f475Smrg
1169d522f475Smrg	    if (XGetWindowAttributes(screen->display,
1170d522f475Smrg				     WMFrameWindow(termw),
1171d522f475Smrg				     &wm_attrs)) {
1172d522f475Smrg
1173d522f475Smrg		if (XGetWindowAttributes(screen->display,
1174d522f475Smrg					 VShellWindow,
1175d522f475Smrg					 &vshell_attrs)) {
1176d522f475Smrg
1177d522f475Smrg		    if (screen->restore_data != True
1178d522f475Smrg			|| screen->restore_width != root_width
1179d522f475Smrg			|| screen->restore_height != root_height) {
1180d522f475Smrg			screen->restore_data = True;
1181d522f475Smrg			screen->restore_x = wm_attrs.x + wm_attrs.border_width;
1182d522f475Smrg			screen->restore_y = wm_attrs.y + wm_attrs.border_width;
1183d522f475Smrg			screen->restore_width = vshell_attrs.width;
1184d522f475Smrg			screen->restore_height = vshell_attrs.height;
1185d522f475Smrg			TRACE(("HandleMaximize: save window position %d,%d size %d,%d\n",
1186d522f475Smrg			       screen->restore_x,
1187d522f475Smrg			       screen->restore_y,
1188d522f475Smrg			       screen->restore_width,
1189d522f475Smrg			       screen->restore_height));
1190d522f475Smrg		    }
1191d522f475Smrg
1192d522f475Smrg		    /* subtract wm decoration dimensions */
1193d522f475Smrg		    root_width -= ((wm_attrs.width - vshell_attrs.width)
1194d522f475Smrg				   + (wm_attrs.border_width * 2));
1195d522f475Smrg		    root_height -= ((wm_attrs.height - vshell_attrs.height)
1196d522f475Smrg				    + (wm_attrs.border_width * 2));
1197d522f475Smrg
1198d522f475Smrg		    XMoveResizeWindow(screen->display, VShellWindow,
1199d522f475Smrg				      0 + wm_attrs.border_width,	/* x */
1200d522f475Smrg				      0 + wm_attrs.border_width,	/* y */
1201d522f475Smrg				      root_width,
1202d522f475Smrg				      root_height);
1203d522f475Smrg		}
1204d522f475Smrg	    }
1205d522f475Smrg	}
1206d522f475Smrg    } else {
1207d522f475Smrg	if (screen->restore_data) {
1208d522f475Smrg	    TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n",
1209d522f475Smrg		   screen->restore_x,
1210d522f475Smrg		   screen->restore_y,
1211d522f475Smrg		   screen->restore_width,
1212d522f475Smrg		   screen->restore_height));
1213d522f475Smrg	    screen->restore_data = False;
1214d522f475Smrg
1215d522f475Smrg	    XMoveResizeWindow(screen->display,
1216d522f475Smrg			      VShellWindow,
1217d522f475Smrg			      screen->restore_x,
1218d522f475Smrg			      screen->restore_y,
1219d522f475Smrg			      screen->restore_width,
1220d522f475Smrg			      screen->restore_height);
1221d522f475Smrg	}
1222d522f475Smrg    }
1223d522f475Smrg}
1224d522f475Smrg
1225d522f475Smrg/*ARGSUSED*/
1226d522f475Smrgvoid
1227d522f475SmrgHandleMaximize(Widget gw,
1228d522f475Smrg	       XEvent * event GCC_UNUSED,
1229d522f475Smrg	       String * params GCC_UNUSED,
1230d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1231d522f475Smrg{
1232d522f475Smrg    if (IsXtermWidget(gw)) {
1233d522f475Smrg	RequestMaximize((XtermWidget) gw, 1);
1234d522f475Smrg    }
1235d522f475Smrg}
1236d522f475Smrg
1237d522f475Smrg/*ARGSUSED*/
1238d522f475Smrgvoid
1239d522f475SmrgHandleRestoreSize(Widget gw,
1240d522f475Smrg		  XEvent * event GCC_UNUSED,
1241d522f475Smrg		  String * params GCC_UNUSED,
1242d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1243d522f475Smrg{
1244d522f475Smrg    if (IsXtermWidget(gw)) {
1245d522f475Smrg	RequestMaximize((XtermWidget) gw, 0);
1246d522f475Smrg    }
1247d522f475Smrg}
1248d522f475Smrg#endif /* OPT_MAXIMIZE */
1249d522f475Smrg
1250d522f475Smrgvoid
1251d522f475SmrgRedraw(void)
1252d522f475Smrg{
1253d522f475Smrg    TScreen *screen = TScreenOf(term);
1254d522f475Smrg    XExposeEvent event;
1255d522f475Smrg
1256d522f475Smrg    TRACE(("Redraw\n"));
1257d522f475Smrg
1258d522f475Smrg    event.type = Expose;
1259d522f475Smrg    event.display = screen->display;
1260d522f475Smrg    event.x = 0;
1261d522f475Smrg    event.y = 0;
1262d522f475Smrg    event.count = 0;
1263d522f475Smrg
1264d522f475Smrg    if (VWindow(screen)) {
1265d522f475Smrg	event.window = VWindow(screen);
1266d522f475Smrg	event.width = term->core.width;
1267d522f475Smrg	event.height = term->core.height;
1268d522f475Smrg	(*term->core.widget_class->core_class.expose) ((Widget) term,
1269d522f475Smrg						       (XEvent *) & event,
1270d522f475Smrg						       NULL);
1271d522f475Smrg	if (ScrollbarWidth(screen)) {
1272d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
1273d522f475Smrg		(screen->scrollWidget, (XEvent *) & event, NULL);
1274d522f475Smrg	}
1275d522f475Smrg    }
1276d522f475Smrg#if OPT_TEK4014
1277d522f475Smrg    if (TEK4014_SHOWN(term)) {
1278d522f475Smrg	TekScreen *tekscr = &(tekWidget->screen);
1279d522f475Smrg	event.window = TWindow(tekscr);
1280d522f475Smrg	event.width = tekWidget->core.width;
1281d522f475Smrg	event.height = tekWidget->core.height;
1282d522f475Smrg	TekExpose((Widget) tekWidget, (XEvent *) & event, NULL);
1283d522f475Smrg    }
1284d522f475Smrg#endif
1285d522f475Smrg}
1286d522f475Smrg
1287d522f475Smrg#ifdef VMS
1288d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1289d522f475Smrg#else
1290d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1291d522f475Smrg#endif
1292d522f475Smrg
1293d522f475Smrgvoid
1294d522f475Smrgtimestamp_filename(char *dst, const char *src)
1295d522f475Smrg{
1296d522f475Smrg    time_t tstamp;
1297d522f475Smrg    struct tm *tstruct;
1298d522f475Smrg
1299d522f475Smrg    tstamp = time((time_t *) 0);
1300d522f475Smrg    tstruct = localtime(&tstamp);
1301d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1302d522f475Smrg	    src,
1303d522f475Smrg	    tstruct->tm_year + 1900,
1304d522f475Smrg	    tstruct->tm_mon + 1,
1305d522f475Smrg	    tstruct->tm_mday,
1306d522f475Smrg	    tstruct->tm_hour,
1307d522f475Smrg	    tstruct->tm_min,
1308d522f475Smrg	    tstruct->tm_sec);
1309d522f475Smrg}
1310d522f475Smrg
1311d522f475Smrgint
1312d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
1313d522f475Smrg{
1314d522f475Smrg    int fd;
1315d522f475Smrg    struct stat sb;
1316d522f475Smrg
1317d522f475Smrg#ifdef VMS
1318d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
1319d522f475Smrg	int the_error = errno;
1320d522f475Smrg	fprintf(stderr, "%s: cannot open %s: %d:%s\n",
1321d522f475Smrg		xterm_name,
1322d522f475Smrg		path,
1323d522f475Smrg		the_error,
1324d522f475Smrg		SysErrorMsg(the_error));
1325d522f475Smrg	return -1;
1326d522f475Smrg    }
1327d522f475Smrg    chown(path, uid, gid);
1328d522f475Smrg#else
1329d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
1330d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
1331d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
1332d522f475Smrg	int the_error = errno;
1333d522f475Smrg	fprintf(stderr, "%s: cannot open %s: %d:%s\n",
1334d522f475Smrg		xterm_name,
1335d522f475Smrg		path,
1336d522f475Smrg		the_error,
1337d522f475Smrg		SysErrorMsg(the_error));
1338d522f475Smrg	return -1;
1339d522f475Smrg    }
1340d522f475Smrg#endif
1341d522f475Smrg
1342d522f475Smrg    /*
1343d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
1344d522f475Smrg     * we do any damage, and that it is not world-writable.
1345d522f475Smrg     */
1346d522f475Smrg    if (fstat(fd, &sb) < 0
1347d522f475Smrg	|| sb.st_uid != uid
1348d522f475Smrg	|| (sb.st_mode & 022) != 0) {
1349d522f475Smrg	fprintf(stderr, "%s: you do not own %s\n", xterm_name, path);
1350d522f475Smrg	close(fd);
1351d522f475Smrg	return -1;
1352d522f475Smrg    }
1353d522f475Smrg    return fd;
1354d522f475Smrg}
1355d522f475Smrg
1356d522f475Smrg#ifndef VMS
1357d522f475Smrg/*
1358d522f475Smrg * Create a file only if we could with the permissions of the real user id.
1359d522f475Smrg * We could emulate this with careful use of access() and following
1360d522f475Smrg * symbolic links, but that is messy and has race conditions.
1361d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
1362d522f475Smrg * being available.
1363d522f475Smrg *
1364d522f475Smrg * Note: When called for user logging, we have ensured that the real and
1365d522f475Smrg * effective user ids are the same, so this remains as a convenience function
1366d522f475Smrg * for the debug logs.
1367d522f475Smrg *
1368d522f475Smrg * Returns
1369d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
1370d522f475Smrg *	-1 on error, e.g., cannot fork
1371d522f475Smrg *	 0 otherwise.
1372d522f475Smrg */
1373d522f475Smrgint
1374d522f475Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, int mode)
1375d522f475Smrg{
1376d522f475Smrg    int fd;
1377d522f475Smrg    pid_t pid;
1378d522f475Smrg    int retval = 0;
1379d522f475Smrg    int childstat = 0;
1380d522f475Smrg#ifndef HAVE_WAITPID
1381d522f475Smrg    int waited;
1382d522f475Smrg    SIGNAL_T(*chldfunc) (int);
1383d522f475Smrg
1384d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
1385d522f475Smrg#endif /* HAVE_WAITPID */
1386d522f475Smrg
1387d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
1388d522f475Smrg	   (int) uid, (int) geteuid(),
1389d522f475Smrg	   (int) gid, (int) getegid(),
1390d522f475Smrg	   append,
1391d522f475Smrg	   pathname,
1392d522f475Smrg	   mode));
1393d522f475Smrg
1394d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
1395d522f475Smrg	fd = open(pathname,
1396d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1397d522f475Smrg		  mode);
1398d522f475Smrg	if (fd >= 0)
1399d522f475Smrg	    close(fd);
1400d522f475Smrg	return (fd >= 0);
1401d522f475Smrg    }
1402d522f475Smrg
1403d522f475Smrg    pid = fork();
1404d522f475Smrg    switch (pid) {
1405d522f475Smrg    case 0:			/* child */
1406d522f475Smrg	if (setgid(gid) == -1
1407d522f475Smrg	    || setuid(uid) == -1) {
1408d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
1409d522f475Smrg	    retval = 1;
1410d522f475Smrg	} else {
1411d522f475Smrg	    fd = open(pathname,
1412d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
1413d522f475Smrg		      mode);
1414d522f475Smrg	    if (fd >= 0) {
1415d522f475Smrg		close(fd);
1416d522f475Smrg		retval = 0;
1417d522f475Smrg	    } else {
1418d522f475Smrg		retval = 1;
1419d522f475Smrg	    }
1420d522f475Smrg	}
1421d522f475Smrg	_exit(retval);
1422d522f475Smrg	/* NOTREACHED */
1423d522f475Smrg    case -1:			/* error */
1424d522f475Smrg	return retval;
1425d522f475Smrg    default:			/* parent */
1426d522f475Smrg#ifdef HAVE_WAITPID
1427d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
1428d522f475Smrg#ifdef EINTR
1429d522f475Smrg	    if (errno == EINTR)
1430d522f475Smrg		continue;
1431d522f475Smrg#endif /* EINTR */
1432d522f475Smrg#ifdef ERESTARTSYS
1433d522f475Smrg	    if (errno == ERESTARTSYS)
1434d522f475Smrg		continue;
1435d522f475Smrg#endif /* ERESTARTSYS */
1436d522f475Smrg	    break;
1437d522f475Smrg	}
1438d522f475Smrg#else /* HAVE_WAITPID */
1439d522f475Smrg	waited = wait(&childstat);
1440d522f475Smrg	signal(SIGCHLD, chldfunc);
1441d522f475Smrg	/*
1442d522f475Smrg	   Since we had the signal handler uninstalled for a while,
1443d522f475Smrg	   we might have missed the termination of our screen child.
1444d522f475Smrg	   If we can check for this possibility without hanging, do so.
1445d522f475Smrg	 */
1446d522f475Smrg	do
1447d522f475Smrg	    if (waited == term->screen.pid)
1448d522f475Smrg		Cleanup(0);
1449d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
1450d522f475Smrg#endif /* HAVE_WAITPID */
1451d522f475Smrg#ifndef WIFEXITED
1452d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
1453d522f475Smrg#endif
1454d522f475Smrg	if (WIFEXITED(childstat))
1455d522f475Smrg	    retval = 1;
1456d522f475Smrg	return retval;
1457d522f475Smrg    }
1458d522f475Smrg}
1459d522f475Smrg#endif /* !VMS */
1460d522f475Smrg
1461d522f475Smrgint
1462d522f475SmrgxtermResetIds(TScreen * screen)
1463d522f475Smrg{
1464d522f475Smrg    int result = 0;
1465d522f475Smrg    if (setgid(screen->gid) == -1) {
1466d522f475Smrg	fprintf(stderr, "%s: unable to reset group-id\n", ProgramName);
1467d522f475Smrg	result = -1;
1468d522f475Smrg    }
1469d522f475Smrg    if (setuid(screen->uid) == -1) {
1470d522f475Smrg	fprintf(stderr, "%s: unable to reset user-id\n", ProgramName);
1471d522f475Smrg	result = -1;
1472d522f475Smrg    }
1473d522f475Smrg    return result;
1474d522f475Smrg}
1475d522f475Smrg
1476d522f475Smrg#ifdef ALLOWLOGGING
1477d522f475Smrg
1478d522f475Smrg/*
1479d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
1480d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
1481d522f475Smrg */
1482d522f475Smrg
1483d522f475Smrg#ifdef ALLOWLOGFILEEXEC
1484d522f475Smrgstatic SIGNAL_T
1485d522f475Smrglogpipe(int sig GCC_UNUSED)
1486d522f475Smrg{
1487d522f475Smrg    TScreen *screen = TScreenOf(term);
1488d522f475Smrg
1489d522f475Smrg#ifdef SYSV
1490d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
1491d522f475Smrg#endif /* SYSV */
1492d522f475Smrg    if (screen->logging)
1493d522f475Smrg	CloseLog(screen);
1494d522f475Smrg}
1495d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1496d522f475Smrg
1497d522f475Smrgvoid
1498d522f475SmrgStartLog(TScreen * screen)
1499d522f475Smrg{
1500d522f475Smrg    static char *log_default;
1501d522f475Smrg#ifdef ALLOWLOGFILEEXEC
1502d522f475Smrg    char *cp;
1503d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
1504d522f475Smrg
1505d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
1506d522f475Smrg	return;
1507d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
1508d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
1509d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
1510d522f475Smrg			 0640);
1511d522f475Smrg    if (screen->logfd < 0)
1512d522f475Smrg	return;			/* open failed */
1513d522f475Smrg#else /*VMS */
1514d522f475Smrg    if (screen->logfile == NULL || *screen->logfile == 0) {
1515d522f475Smrg	if (screen->logfile)
1516d522f475Smrg	    free(screen->logfile);
1517d522f475Smrg	if (log_default == NULL) {
1518d522f475Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
1519d522f475Smrg	    char log_def_name[512];	/* see sprintf below */
1520d522f475Smrg	    char hostname[255 + 1];	/* Internet standard limit (RFC 1035):
1521d522f475Smrg					   ``To simplify implementations, the
1522d522f475Smrg					   total length of a domain name (i.e.,
1523d522f475Smrg					   label octets and label length
1524d522f475Smrg					   octets) is restricted to 255 octets
1525d522f475Smrg					   or less.'' */
1526d522f475Smrg	    char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1];
1527d522f475Smrg	    time_t now;
1528d522f475Smrg	    struct tm *ltm;
1529d522f475Smrg
1530d522f475Smrg	    now = time((time_t *) 0);
1531d522f475Smrg	    ltm = (struct tm *) localtime(&now);
1532d522f475Smrg	    if ((gethostname(hostname, sizeof(hostname)) == 0) &&
1533d522f475Smrg		(strftime(yyyy_mm_dd_hh_mm_ss,
1534d522f475Smrg			  sizeof(yyyy_mm_dd_hh_mm_ss),
1535d522f475Smrg			  "%Y.%m.%d.%H.%M.%S", ltm) > 0)) {
1536d522f475Smrg		(void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d",
1537d522f475Smrg			       hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid());
1538d522f475Smrg	    }
1539d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
1540d522f475Smrg		return;
1541d522f475Smrg#else
1542d522f475Smrg	    const char *log_def_name = "XtermLog.XXXXXX";
1543d522f475Smrg	    if ((log_default = x_strdup(log_def_name)) == NULL)
1544d522f475Smrg		return;
1545d522f475Smrg
1546d522f475Smrg	    mktemp(log_default);
1547d522f475Smrg#endif
1548d522f475Smrg	}
1549d522f475Smrg	if ((screen->logfile = x_strdup(log_default)) == 0)
1550d522f475Smrg	    return;
1551d522f475Smrg    }
1552d522f475Smrg    if (*screen->logfile == '|') {	/* exec command */
1553d522f475Smrg#ifdef ALLOWLOGFILEEXEC
1554d522f475Smrg	/*
1555d522f475Smrg	 * Warning, enabling this "feature" allows arbitrary programs
1556d522f475Smrg	 * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
1557d522f475Smrg	 * done through escape sequences....  You have been warned.
1558d522f475Smrg	 */
1559d522f475Smrg	int pid;
1560d522f475Smrg	int p[2];
1561d522f475Smrg	static char *shell;
1562d522f475Smrg	struct passwd *pw;
1563d522f475Smrg
1564d522f475Smrg	if (pipe(p) < 0 || (pid = fork()) < 0)
1565d522f475Smrg	    return;
1566d522f475Smrg	if (pid == 0) {		/* child */
1567d522f475Smrg	    /*
1568d522f475Smrg	     * Close our output (we won't be talking back to the
1569d522f475Smrg	     * parent), and redirect our child's output to the
1570d522f475Smrg	     * original stderr.
1571d522f475Smrg	     */
1572d522f475Smrg	    close(p[1]);
1573d522f475Smrg	    dup2(p[0], 0);
1574d522f475Smrg	    close(p[0]);
1575d522f475Smrg	    dup2(fileno(stderr), 1);
1576d522f475Smrg	    dup2(fileno(stderr), 2);
1577d522f475Smrg
1578d522f475Smrg	    close(fileno(stderr));
1579d522f475Smrg	    close(ConnectionNumber(screen->display));
1580d522f475Smrg	    close(screen->respond);
1581d522f475Smrg
1582d522f475Smrg	    if ((((cp = x_getenv("SHELL")) == NULL)
1583d522f475Smrg		 && ((pw = getpwuid(screen->uid)) == NULL
1584d522f475Smrg		     || *(cp = pw->pw_shell) == 0))
1585d522f475Smrg		|| (shell = CastMallocN(char, strlen(cp))) == 0) {
1586d522f475Smrg		shell = "/bin/sh";
1587d522f475Smrg	    } else {
1588d522f475Smrg		strcpy(shell, cp);
1589d522f475Smrg	    }
1590d522f475Smrg
1591d522f475Smrg	    signal(SIGHUP, SIG_DFL);
1592d522f475Smrg	    signal(SIGCHLD, SIG_DFL);
1593d522f475Smrg
1594d522f475Smrg	    /* (this is redundant) */
1595d522f475Smrg	    if (xtermResetIds(screen) < 0)
1596d522f475Smrg		exit(ERROR_SETUID);
1597d522f475Smrg
1598d522f475Smrg	    execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
1599d522f475Smrg
1600d522f475Smrg	    fprintf(stderr, "%s: Can't exec `%s'\n", xterm_name,
1601d522f475Smrg		    &screen->logfile[1]);
1602d522f475Smrg	    exit(ERROR_LOGEXEC);
1603d522f475Smrg	}
1604d522f475Smrg	close(p[0]);
1605d522f475Smrg	screen->logfd = p[1];
1606d522f475Smrg	signal(SIGPIPE, logpipe);
1607d522f475Smrg#else
1608d522f475Smrg	Bell(XkbBI_Info, 0);
1609d522f475Smrg	Bell(XkbBI_Info, 0);
1610d522f475Smrg	return;
1611d522f475Smrg#endif
1612d522f475Smrg    } else {
1613d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
1614d522f475Smrg					   screen->gid,
1615d522f475Smrg					   screen->logfile,
1616d522f475Smrg					   (log_default != 0))) < 0)
1617d522f475Smrg	    return;
1618d522f475Smrg    }
1619d522f475Smrg#endif /*VMS */
1620d522f475Smrg    screen->logstart = VTbuffer->next;
1621d522f475Smrg    screen->logging = True;
1622d522f475Smrg    update_logging();
1623d522f475Smrg}
1624d522f475Smrg
1625d522f475Smrgvoid
1626d522f475SmrgCloseLog(TScreen * screen)
1627d522f475Smrg{
1628d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
1629d522f475Smrg	return;
1630d522f475Smrg    FlushLog(screen);
1631d522f475Smrg    close(screen->logfd);
1632d522f475Smrg    screen->logging = False;
1633d522f475Smrg    update_logging();
1634d522f475Smrg}
1635d522f475Smrg
1636d522f475Smrgvoid
1637d522f475SmrgFlushLog(TScreen * screen)
1638d522f475Smrg{
1639d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
1640d522f475Smrg	Char *cp;
1641d522f475Smrg	int i;
1642d522f475Smrg
1643d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
1644d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
1645d522f475Smrg	if (!tt_new_output)
1646d522f475Smrg	    return;
1647d522f475Smrg	tt_new_output = False;
1648d522f475Smrg#endif /* VMS */
1649d522f475Smrg	cp = VTbuffer->next;
1650d522f475Smrg	if (screen->logstart != 0
1651d522f475Smrg	    && (i = cp - screen->logstart) > 0) {
1652d522f475Smrg	    write(screen->logfd, (char *) screen->logstart, (unsigned) i);
1653d522f475Smrg	}
1654d522f475Smrg	screen->logstart = VTbuffer->next;
1655d522f475Smrg    }
1656d522f475Smrg}
1657d522f475Smrg
1658d522f475Smrg#endif /* ALLOWLOGGING */
1659d522f475Smrg
1660d522f475Smrg/***====================================================================***/
1661d522f475Smrg
1662d522f475Smrg#if OPT_ISO_COLORS
1663d522f475Smrgstatic void
1664d522f475SmrgReportAnsiColorRequest(XtermWidget xw, int colornum, int final)
1665d522f475Smrg{
1666d522f475Smrg    XColor color;
1667d522f475Smrg    Colormap cmap = xw->core.colormap;
1668d522f475Smrg    char buffer[80];
1669d522f475Smrg
1670d522f475Smrg    TRACE(("ReportAnsiColorRequest %d\n", colornum));
1671d522f475Smrg    color.pixel = GET_COLOR_RES(xw->screen.Acolors[colornum]);
1672d522f475Smrg    XQueryColor(xw->screen.display, cmap, &color);
1673d522f475Smrg    sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x",
1674d522f475Smrg	    colornum,
1675d522f475Smrg	    color.red,
1676d522f475Smrg	    color.green,
1677d522f475Smrg	    color.blue);
1678d522f475Smrg    unparseputc1(xw, ANSI_OSC);
1679d522f475Smrg    unparseputs(xw, buffer);
1680d522f475Smrg    unparseputc1(xw, final);
1681d522f475Smrg    unparse_end(xw);
1682d522f475Smrg}
1683d522f475Smrg
16840d92cbfdSchristosstatic unsigned
1685d522f475SmrggetColormapSize(Display * display)
1686d522f475Smrg{
16870d92cbfdSchristos    unsigned result;
1688d522f475Smrg    int numFound;
1689d522f475Smrg    XVisualInfo myTemplate, *visInfoPtr;
1690d522f475Smrg
1691d522f475Smrg    myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(display,
1692d522f475Smrg							    XDefaultScreen(display)));
1693d522f475Smrg    visInfoPtr = XGetVisualInfo(display, (long) VisualIDMask,
1694d522f475Smrg				&myTemplate, &numFound);
16950d92cbfdSchristos    result = (numFound >= 1) ? (unsigned) visInfoPtr->colormap_size : 0;
1696d522f475Smrg
1697d522f475Smrg    XFree((char *) visInfoPtr);
1698d522f475Smrg    return result;
1699d522f475Smrg}
1700d522f475Smrg
1701d522f475Smrg/*
1702d522f475Smrg * Find closest color for "def" in "cmap".
1703d522f475Smrg * Set "def" to the resulting color.
1704d522f475Smrg *
1705d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
1706d522f475Smrg * modified with ideas from David Tong's "noflash" library.
1707d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
1708d522f475Smrg *
1709d522f475Smrg * These provide some introduction:
1710d522f475Smrg *	http://en.wikipedia.org/wiki/YIQ
1711d522f475Smrg *		for an introduction to YIQ weights.
1712d522f475Smrg *	http://en.wikipedia.org/wiki/Luminance_(video)
1713d522f475Smrg *		for a discussion of luma.
1714d522f475Smrg *	http://en.wikipedia.org/wiki/YUV
1715d522f475Smrg *
1716d522f475Smrg * Return False if not able to find or allocate a color.
1717d522f475Smrg */
1718d522f475Smrgstatic Boolean
1719d522f475Smrgfind_closest_color(Display * dpy, Colormap cmap, XColor * def)
1720d522f475Smrg{
1721d522f475Smrg    Boolean result = False;
1722d522f475Smrg    XColor *colortable;
1723d522f475Smrg    char *tried;
1724d522f475Smrg    double diff, thisRGB, bestRGB;
1725d522f475Smrg    unsigned attempts;
1726d522f475Smrg    unsigned bestInx;
1727d522f475Smrg    unsigned cmap_size;
1728d522f475Smrg    unsigned i;
1729d522f475Smrg
1730d522f475Smrg    cmap_size = getColormapSize(dpy);
1731d522f475Smrg    if (cmap_size != 0) {
1732d522f475Smrg
1733d522f475Smrg	colortable = TypeMallocN(XColor, cmap_size);
1734d522f475Smrg	if (colortable != 0) {
1735d522f475Smrg
1736d522f475Smrg	    tried = TypeCallocN(char, cmap_size);
1737d522f475Smrg	    if (tried != 0) {
1738d522f475Smrg
1739d522f475Smrg		for (i = 0; i < cmap_size; i++) {
1740d522f475Smrg		    colortable[i].pixel = (unsigned long) i;
1741d522f475Smrg		}
1742d522f475Smrg		XQueryColors(dpy, cmap, colortable, (int) cmap_size);
1743d522f475Smrg
1744d522f475Smrg		/*
1745d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
1746d522f475Smrg		 * approximation to the requested color.
1747d522f475Smrg		 */
1748d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
1749d522f475Smrg		    Boolean first = True;
1750d522f475Smrg
1751d522f475Smrg		    bestRGB = 0.0;
1752d522f475Smrg		    bestInx = 0;
1753d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
1754d522f475Smrg			if (!tried[bestInx]) {
1755d522f475Smrg			    /*
1756d522f475Smrg			     * Look for the best match based on luminance.
1757d522f475Smrg			     * Measure this by the least-squares difference of
1758d522f475Smrg			     * the weighted R/G/B components from the color map
1759d522f475Smrg			     * versus the requested color.  Use the Y (luma)
1760d522f475Smrg			     * component of the YIQ color space model for
1761d522f475Smrg			     * weights that correspond to the luminance.
1762d522f475Smrg			     */
1763d522f475Smrg#define AddColorWeight(weight, color) \
1764d522f475Smrg			    diff = weight * (int) ((def->color) - colortable[i].color); \
1765d522f475Smrg			    thisRGB = diff * diff
1766d522f475Smrg
1767d522f475Smrg			    AddColorWeight(0.30, red);
1768d522f475Smrg			    AddColorWeight(0.61, green);
1769d522f475Smrg			    AddColorWeight(0.11, blue);
1770d522f475Smrg
1771d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
1772d522f475Smrg				first = False;
1773d522f475Smrg				bestInx = i;
1774d522f475Smrg				bestRGB = thisRGB;
1775d522f475Smrg			    }
1776d522f475Smrg			}
1777d522f475Smrg		    }
1778d522f475Smrg		    if (XAllocColor(dpy, cmap, &colortable[bestInx]) != 0) {
1779d522f475Smrg			*def = colortable[bestInx];
1780d522f475Smrg			result = True;
1781d522f475Smrg			break;
1782d522f475Smrg		    }
1783d522f475Smrg		    /*
1784d522f475Smrg		     * It failed - either the color map entry was readonly, or
1785d522f475Smrg		     * another client has allocated the entry.  Mark the entry
1786d522f475Smrg		     * so we will ignore it
1787d522f475Smrg		     */
1788d522f475Smrg		    tried[bestInx] = True;
1789d522f475Smrg		}
1790d522f475Smrg		free(tried);
1791d522f475Smrg	    }
1792d522f475Smrg	    free(colortable);
1793d522f475Smrg	}
1794d522f475Smrg    }
1795d522f475Smrg    return result;
1796d522f475Smrg}
1797d522f475Smrg
1798d522f475Smrg/*
1799d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
1800d522f475Smrg * to 256.
1801d522f475Smrg *
1802d522f475Smrg * Returns
1803d522f475Smrg *	-1 on error
1804d522f475Smrg *	0 on no change
1805d522f475Smrg *	1 if a new color was allocated.
1806d522f475Smrg */
1807d522f475Smrgstatic int
1808d522f475SmrgAllocateAnsiColor(XtermWidget xw,
1809d522f475Smrg		  ColorRes * res,
1810d522f475Smrg		  char *spec)
1811d522f475Smrg{
1812d522f475Smrg    int result;
1813d522f475Smrg    XColor def;
1814d522f475Smrg    TScreen *screen = &xw->screen;
1815d522f475Smrg    Colormap cmap = xw->core.colormap;
1816d522f475Smrg
1817d522f475Smrg    if (XParseColor(screen->display, cmap, spec, &def)
1818d522f475Smrg	&& (XAllocColor(screen->display, cmap, &def)
1819d522f475Smrg	    || find_closest_color(screen->display, cmap, &def))) {
1820d522f475Smrg	if (
1821d522f475Smrg#if OPT_COLOR_RES
1822d522f475Smrg	       res->mode == True &&
1823d522f475Smrg#endif
1824d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
1825d522f475Smrg	    result = 0;
1826d522f475Smrg	} else {
1827d522f475Smrg	    result = 1;
1828d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
1829d522f475Smrg	    TRACE(("AllocateAnsiColor[%d] %s (pixel %#lx)\n",
1830d522f475Smrg		   (res - screen->Acolors), spec, def.pixel));
1831d522f475Smrg#if OPT_COLOR_RES
1832d522f475Smrg	    if (!res->mode)
1833d522f475Smrg		result = 0;
1834d522f475Smrg	    res->mode = True;
1835d522f475Smrg#endif
1836d522f475Smrg	}
1837d522f475Smrg    } else {
1838d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
1839d522f475Smrg	result = -1;
1840d522f475Smrg    }
1841d522f475Smrg    return (result);
1842d522f475Smrg}
1843d522f475Smrg
1844d522f475Smrg#if OPT_COLOR_RES
1845d522f475SmrgPixel
1846d522f475SmrgxtermGetColorRes(ColorRes * res)
1847d522f475Smrg{
1848d522f475Smrg    Pixel result = 0;
1849d522f475Smrg
1850d522f475Smrg    if (res->mode) {
1851d522f475Smrg	result = res->value;
1852d522f475Smrg    } else {
1853d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
1854d522f475Smrg	       res - term->screen.Acolors));
1855d522f475Smrg
1856d522f475Smrg	if (res >= term->screen.Acolors) {
1857d522f475Smrg	    assert(res - term->screen.Acolors < MAXCOLORS);
1858d522f475Smrg
1859d522f475Smrg	    if (AllocateAnsiColor(term, res, res->resource) < 0) {
1860d522f475Smrg		res->value = term->screen.Tcolors[TEXT_FG].value;
1861d522f475Smrg		res->mode = -True;
1862d522f475Smrg		fprintf(stderr,
1863d522f475Smrg			"%s: Cannot allocate color %s\n",
1864d522f475Smrg			xterm_name,
1865d522f475Smrg			NonNull(res->resource));
1866d522f475Smrg	    }
1867d522f475Smrg	    result = res->value;
1868d522f475Smrg	} else {
1869d522f475Smrg	    result = 0;
1870d522f475Smrg	}
1871d522f475Smrg    }
1872d522f475Smrg    return result;
1873d522f475Smrg}
1874d522f475Smrg#endif
1875d522f475Smrg
1876d522f475Smrgstatic Bool
1877d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
1878d522f475Smrg		       char *buf,
1879d522f475Smrg		       int final)
1880d522f475Smrg{
1881d522f475Smrg    char *name;
1882d522f475Smrg    int color;
1883d522f475Smrg    int repaint = False;
1884d522f475Smrg    int code;
1885d522f475Smrg
1886d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
1887d522f475Smrg
1888d522f475Smrg    while (buf && *buf) {
1889d522f475Smrg	name = strchr(buf, ';');
1890d522f475Smrg	if (name == NULL)
1891d522f475Smrg	    break;
1892d522f475Smrg	*name = '\0';
1893d522f475Smrg	name++;
1894d522f475Smrg	color = atoi(buf);
1895d522f475Smrg	if (color < 0 || color >= NUM_ANSI_COLORS)
1896d522f475Smrg	    break;
1897d522f475Smrg	buf = strchr(name, ';');
1898d522f475Smrg	if (buf) {
1899d522f475Smrg	    *buf = '\0';
1900d522f475Smrg	    buf++;
1901d522f475Smrg	}
1902d522f475Smrg	if (!strcmp(name, "?"))
1903d522f475Smrg	    ReportAnsiColorRequest(xw, color, final);
1904d522f475Smrg	else {
1905d522f475Smrg	    TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
1906d522f475Smrg	    code = AllocateAnsiColor(xw, &(xw->screen.Acolors[color]), name);
1907d522f475Smrg	    if (code < 0) {
1908d522f475Smrg		/* stop on any error */
1909d522f475Smrg		break;
1910d522f475Smrg	    } else if (code > 0) {
1911d522f475Smrg		repaint = True;
1912d522f475Smrg	    }
1913d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
1914d522f475Smrg	     * change style (dynamic colors).
1915d522f475Smrg	     */
1916d522f475Smrg	}
1917d522f475Smrg    }
1918d522f475Smrg    if (repaint)
1919d522f475Smrg	xtermRepaint(xw);
1920d522f475Smrg
1921d522f475Smrg    return (repaint);
1922d522f475Smrg}
1923d522f475Smrg#else
1924d522f475Smrg#define find_closest_color(display, cmap, def) 0
1925d522f475Smrg#endif /* OPT_ISO_COLORS */
1926d522f475Smrg
1927d522f475Smrg#if OPT_PASTE64
1928d522f475Smrgstatic void
1929d522f475SmrgManipulateSelectionData(XtermWidget xw, TScreen * screen, char *buf, int final)
1930d522f475Smrg{
1931d522f475Smrg#define PDATA(a,b) { a, #b }
1932d522f475Smrg    static struct {
1933d522f475Smrg	char given;
1934d522f475Smrg	char *result;
1935d522f475Smrg    } table[] = {
1936d522f475Smrg	PDATA('s', SELECT),
1937d522f475Smrg	    PDATA('p', PRIMARY),
1938d522f475Smrg	    PDATA('c', CLIPBOARD),
1939d522f475Smrg	    PDATA('0', CUT_BUFFER0),
1940d522f475Smrg	    PDATA('1', CUT_BUFFER1),
1941d522f475Smrg	    PDATA('2', CUT_BUFFER2),
1942d522f475Smrg	    PDATA('3', CUT_BUFFER3),
1943d522f475Smrg	    PDATA('4', CUT_BUFFER4),
1944d522f475Smrg	    PDATA('5', CUT_BUFFER5),
1945d522f475Smrg	    PDATA('6', CUT_BUFFER6),
1946d522f475Smrg	    PDATA('7', CUT_BUFFER7),
1947d522f475Smrg    };
1948d522f475Smrg
1949d522f475Smrg    char *base = buf;
1950d522f475Smrg    char *used = x_strdup(base);
1951d522f475Smrg    Cardinal j, n = 0;
1952d522f475Smrg    char **select_args = 0;
1953d522f475Smrg
1954d522f475Smrg    TRACE(("Manipulate selection data\n"));
1955d522f475Smrg
1956d522f475Smrg    while (*buf != ';' && *buf != '\0') {
1957d522f475Smrg	++buf;
1958d522f475Smrg    }
1959d522f475Smrg
1960d522f475Smrg    if (*buf == ';') {
1961d522f475Smrg	*buf++ = '\0';
1962d522f475Smrg
1963d522f475Smrg	if (*base == '\0')
1964d522f475Smrg	    base = "s0";
1965d522f475Smrg	if ((select_args = TypeCallocN(String, 1 + strlen(base))) == 0)
1966d522f475Smrg	    return;
1967d522f475Smrg	while (*base != '\0') {
1968d522f475Smrg	    for (j = 0; j < XtNumber(table); ++j) {
1969d522f475Smrg		if (*base == table[j].given) {
1970d522f475Smrg		    used[n] = *base;
1971d522f475Smrg		    select_args[n++] = table[j].result;
1972d522f475Smrg		    TRACE(("atom[%d] %s\n", n, table[j].result));
1973d522f475Smrg		    break;
1974d522f475Smrg		}
1975d522f475Smrg	    }
1976d522f475Smrg	    ++base;
1977d522f475Smrg	}
1978d522f475Smrg	used[n] = 0;
1979d522f475Smrg
1980d522f475Smrg	if (!strcmp(buf, "?")) {
1981d522f475Smrg	    TRACE(("Getting selection\n"));
1982d522f475Smrg	    unparseputc1(xw, ANSI_OSC);
1983d522f475Smrg	    unparseputs(xw, "52");
1984d522f475Smrg	    unparseputc(xw, ';');
1985d522f475Smrg
1986d522f475Smrg	    unparseputs(xw, used);
1987d522f475Smrg	    unparseputc(xw, ';');
1988d522f475Smrg
1989d522f475Smrg	    /* Tell xtermGetSelection data is base64 encoded */
1990d522f475Smrg	    screen->base64_paste = n;
1991d522f475Smrg	    screen->base64_final = final;
1992d522f475Smrg
1993d522f475Smrg	    /* terminator will be written in this call */
1994d522f475Smrg	    xtermGetSelection((Widget) xw, 0, select_args, n, NULL);
1995d522f475Smrg	} else {
1996d522f475Smrg	    TRACE(("Setting selection with %s\n", buf));
1997d522f475Smrg	    ClearSelectionBuffer(screen);
1998d522f475Smrg	    while (*buf != '\0')
1999d522f475Smrg		AppendToSelectionBuffer(screen, CharOf(*buf++));
2000d522f475Smrg	    CompleteSelection(xw, select_args, n);
2001d522f475Smrg	}
2002d522f475Smrg    }
2003d522f475Smrg}
2004d522f475Smrg#endif /* OPT_PASTE64 */
2005d522f475Smrg
2006d522f475Smrg/***====================================================================***/
2007d522f475Smrg
2008d522f475Smrgstatic Bool
2009d522f475SmrgxtermIsPrintable(TScreen * screen, Char ** bufp, Char * last)
2010d522f475Smrg{
2011d522f475Smrg    Bool result = False;
2012d522f475Smrg    Char *cp = *bufp;
2013d522f475Smrg    Char *next = cp;
2014d522f475Smrg
2015d522f475Smrg    (void) screen;
2016d522f475Smrg    (void) last;
2017d522f475Smrg
2018d522f475Smrg#if OPT_WIDE_CHARS
2019d522f475Smrg    if (xtermEnvUTF8() && screen->utf8_title) {
2020d522f475Smrg	PtyData data;
2021d522f475Smrg
2022d522f475Smrg	if (decodeUtf8(fakePtyData(&data, cp, last))) {
2023d522f475Smrg	    if (data.utf_data != UCS_REPL
2024d522f475Smrg		&& (data.utf_data >= 128 ||
2025d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
2026d522f475Smrg		next += (data.utf_size - 1);
2027d522f475Smrg		result = True;
2028d522f475Smrg	    } else {
2029d522f475Smrg		result = False;
2030d522f475Smrg	    }
2031d522f475Smrg	} else {
2032d522f475Smrg	    result = False;
2033d522f475Smrg	}
2034d522f475Smrg    } else
2035d522f475Smrg#endif
2036d522f475Smrg#if OPT_C1_PRINT
2037d522f475Smrg	if (screen->c1_printable
2038d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
2039d522f475Smrg	result = True;
2040d522f475Smrg    } else
2041d522f475Smrg#endif
2042d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
2043d522f475Smrg	result = True;
2044d522f475Smrg    }
2045d522f475Smrg    *bufp = next;
2046d522f475Smrg    return result;
2047d522f475Smrg}
2048d522f475Smrg
2049d522f475Smrg/***====================================================================***/
2050d522f475Smrg
2051d522f475Smrg/*
2052d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
2053d522f475Smrg * array indices.
2054d522f475Smrg */
2055d522f475Smrgtypedef enum {
2056d522f475Smrg    OSC_TEXT_FG = 10
2057d522f475Smrg    ,OSC_TEXT_BG
2058d522f475Smrg    ,OSC_TEXT_CURSOR
2059d522f475Smrg    ,OSC_MOUSE_FG
2060d522f475Smrg    ,OSC_MOUSE_BG
2061d522f475Smrg#if OPT_TEK4014
2062d522f475Smrg    ,OSC_TEK_FG = 15
2063d522f475Smrg    ,OSC_TEK_BG
2064d522f475Smrg#endif
2065d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2066d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
2067d522f475Smrg#endif
2068d522f475Smrg#if OPT_TEK4014
2069d522f475Smrg    ,OSC_TEK_CURSOR = 18
2070d522f475Smrg#endif
2071d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2072d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
2073d522f475Smrg#endif
2074d522f475Smrg    ,OSC_NCOLORS
2075d522f475Smrg} OscTextColors;
2076d522f475Smrg
2077d522f475Smrgstatic ScrnColors *pOldColors = NULL;
2078d522f475Smrg
2079d522f475Smrgstatic Bool
2080d522f475SmrgGetOldColors(XtermWidget xw)
2081d522f475Smrg{
2082d522f475Smrg    int i;
2083d522f475Smrg    if (pOldColors == NULL) {
2084d522f475Smrg	pOldColors = (ScrnColors *) XtMalloc(sizeof(ScrnColors));
2085d522f475Smrg	if (pOldColors == NULL) {
2086d522f475Smrg	    fprintf(stderr, "allocation failure in GetOldColors\n");
2087d522f475Smrg	    return (False);
2088d522f475Smrg	}
2089d522f475Smrg	pOldColors->which = 0;
2090d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
2091d522f475Smrg	    pOldColors->colors[i] = 0;
2092d522f475Smrg	    pOldColors->names[i] = NULL;
2093d522f475Smrg	}
2094d522f475Smrg	GetColors(xw, pOldColors);
2095d522f475Smrg    }
2096d522f475Smrg    return (True);
2097d522f475Smrg}
2098d522f475Smrg
2099d522f475Smrgstatic int
2100d522f475SmrgoppositeColor(int n)
2101d522f475Smrg{
2102d522f475Smrg    switch (n) {
2103d522f475Smrg    case TEXT_FG:
2104d522f475Smrg	n = TEXT_BG;
2105d522f475Smrg	break;
2106d522f475Smrg    case TEXT_BG:
2107d522f475Smrg	n = TEXT_FG;
2108d522f475Smrg	break;
2109d522f475Smrg    case MOUSE_FG:
2110d522f475Smrg	n = MOUSE_BG;
2111d522f475Smrg	break;
2112d522f475Smrg    case MOUSE_BG:
2113d522f475Smrg	n = MOUSE_FG;
2114d522f475Smrg	break;
2115d522f475Smrg#if OPT_TEK4014
2116d522f475Smrg    case TEK_FG:
2117d522f475Smrg	n = TEK_BG;
2118d522f475Smrg	break;
2119d522f475Smrg    case TEK_BG:
2120d522f475Smrg	n = TEK_FG;
2121d522f475Smrg	break;
2122d522f475Smrg#endif
2123d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2124d522f475Smrg    case HIGHLIGHT_FG:
2125d522f475Smrg	n = HIGHLIGHT_BG;
2126d522f475Smrg	break;
2127d522f475Smrg    case HIGHLIGHT_BG:
2128d522f475Smrg	n = HIGHLIGHT_FG;
2129d522f475Smrg	break;
2130d522f475Smrg#endif
2131d522f475Smrg    default:
2132d522f475Smrg	break;
2133d522f475Smrg    }
2134d522f475Smrg    return n;
2135d522f475Smrg}
2136d522f475Smrg
2137d522f475Smrgstatic void
2138d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
2139d522f475Smrg{
2140d522f475Smrg    XColor color;
2141d522f475Smrg    Colormap cmap = xw->core.colormap;
2142d522f475Smrg    char buffer[80];
2143d522f475Smrg
2144d522f475Smrg    /*
2145d522f475Smrg     * ChangeColorsRequest() has "always" chosen the opposite color when
2146d522f475Smrg     * reverse-video is set.  Report this as the original color index, but
2147d522f475Smrg     * reporting the opposite color which would be used.
2148d522f475Smrg     */
2149d522f475Smrg    int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx;
2150d522f475Smrg
2151d522f475Smrg    GetOldColors(xw);
2152d522f475Smrg    color.pixel = pOldColors->colors[ndx];
2153d522f475Smrg    XQueryColor(xw->screen.display, cmap, &color);
2154d522f475Smrg    sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
2155d522f475Smrg	    color.red,
2156d522f475Smrg	    color.green,
2157d522f475Smrg	    color.blue);
2158d522f475Smrg    TRACE(("ReportColors %d: %#lx as %s\n", ndx, pOldColors->colors[ndx], buffer));
2159d522f475Smrg    unparseputc1(xw, ANSI_OSC);
2160d522f475Smrg    unparseputs(xw, buffer);
2161d522f475Smrg    unparseputc1(xw, final);
2162d522f475Smrg    unparse_end(xw);
2163d522f475Smrg}
2164d522f475Smrg
2165d522f475Smrgstatic Bool
2166d522f475SmrgUpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew)
2167d522f475Smrg{
2168d522f475Smrg    int i;
2169d522f475Smrg
2170d522f475Smrg    /* if we were going to free old colors, this would be the place to
2171d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
2172d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
2173d522f475Smrg     * we could save some overhead this way.   The only case in which this
2174d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
2175d522f475Smrg     * which case they can restart xterm
2176d522f475Smrg     */
2177d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
2178d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
2179d522f475Smrg	    if (pOldColors->names[i] != NULL) {
2180d522f475Smrg		XtFree(pOldColors->names[i]);
2181d522f475Smrg		pOldColors->names[i] = NULL;
2182d522f475Smrg	    }
2183d522f475Smrg	    if (pNew->names[i]) {
2184d522f475Smrg		pOldColors->names[i] = pNew->names[i];
2185d522f475Smrg	    }
2186d522f475Smrg	    pOldColors->colors[i] = pNew->colors[i];
2187d522f475Smrg	}
2188d522f475Smrg    }
2189d522f475Smrg    return (True);
2190d522f475Smrg}
2191d522f475Smrg
2192d522f475Smrg/*
2193d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
2194d522f475Smrg * xterm is compiled.
2195d522f475Smrg */
2196d522f475Smrgstatic int
2197d522f475SmrgOscToColorIndex(OscTextColors mode)
2198d522f475Smrg{
2199d522f475Smrg    int result = 0;
2200d522f475Smrg
2201d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
2202d522f475Smrg    switch (mode) {
2203d522f475Smrg	CASE(TEXT_FG);
2204d522f475Smrg	CASE(TEXT_BG);
2205d522f475Smrg	CASE(TEXT_CURSOR);
2206d522f475Smrg	CASE(MOUSE_FG);
2207d522f475Smrg	CASE(MOUSE_BG);
2208d522f475Smrg#if OPT_TEK4014
2209d522f475Smrg	CASE(TEK_FG);
2210d522f475Smrg	CASE(TEK_BG);
2211d522f475Smrg#endif
2212d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2213d522f475Smrg	CASE(HIGHLIGHT_BG);
2214d522f475Smrg	CASE(HIGHLIGHT_FG);
2215d522f475Smrg#endif
2216d522f475Smrg#if OPT_TEK4014
2217d522f475Smrg	CASE(TEK_CURSOR);
2218d522f475Smrg#endif
2219d522f475Smrg    case OSC_NCOLORS:
2220d522f475Smrg	break;
2221d522f475Smrg    }
2222d522f475Smrg    return result;
2223d522f475Smrg}
2224d522f475Smrg
2225d522f475Smrgstatic Bool
2226d522f475SmrgChangeColorsRequest(XtermWidget xw,
2227d522f475Smrg		    int start,
2228d522f475Smrg		    char *names,
2229d522f475Smrg		    int final)
2230d522f475Smrg{
2231d522f475Smrg    Bool result = False;
2232d522f475Smrg    char *thisName;
2233d522f475Smrg    ScrnColors newColors;
2234d522f475Smrg    int i, ndx;
2235d522f475Smrg
2236d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
2237d522f475Smrg
2238d522f475Smrg    if (GetOldColors(xw)) {
2239d522f475Smrg	newColors.which = 0;
2240d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
2241d522f475Smrg	    newColors.names[i] = NULL;
2242d522f475Smrg	}
2243d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
2244d522f475Smrg	    ndx = OscToColorIndex((OscTextColors) i);
2245d522f475Smrg	    if (xw->misc.re_verse)
2246d522f475Smrg		ndx = oppositeColor(ndx);
2247d522f475Smrg
2248d522f475Smrg	    if ((names == NULL) || (names[0] == '\0')) {
2249d522f475Smrg		newColors.names[ndx] = NULL;
2250d522f475Smrg	    } else {
2251d522f475Smrg		if (names[0] == ';')
2252d522f475Smrg		    thisName = NULL;
2253d522f475Smrg		else
2254d522f475Smrg		    thisName = names;
2255d522f475Smrg		names = strchr(names, ';');
2256d522f475Smrg		if (names != NULL) {
2257d522f475Smrg		    *names++ = '\0';
2258d522f475Smrg		}
2259d522f475Smrg		if (thisName != 0 && !strcmp(thisName, "?")) {
2260d522f475Smrg		    ReportColorRequest(xw, ndx, final);
2261d522f475Smrg		} else if (!pOldColors->names[ndx]
2262d522f475Smrg			   || (thisName
2263d522f475Smrg			       && strcmp(thisName, pOldColors->names[ndx]))) {
2264d522f475Smrg		    AllocateTermColor(xw, &newColors, ndx, thisName);
2265d522f475Smrg		}
2266d522f475Smrg	    }
2267d522f475Smrg	}
2268d522f475Smrg
2269d522f475Smrg	if (newColors.which != 0) {
2270d522f475Smrg	    ChangeColors(xw, &newColors);
2271d522f475Smrg	    UpdateOldColors(xw, &newColors);
2272d522f475Smrg	}
2273d522f475Smrg	result = True;
2274d522f475Smrg    }
2275d522f475Smrg    return result;
2276d522f475Smrg}
2277d522f475Smrg
2278d522f475Smrg/***====================================================================***/
2279d522f475Smrg
2280d522f475Smrgvoid
2281d522f475Smrgdo_osc(XtermWidget xw, Char * oscbuf, unsigned len GCC_UNUSED, int final)
2282d522f475Smrg{
2283d522f475Smrg    TScreen *screen = &(xw->screen);
2284d522f475Smrg    int mode;
2285d522f475Smrg    Char *cp;
2286d522f475Smrg    int state = 0;
2287d522f475Smrg    char *buf = 0;
2288d522f475Smrg
2289d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
2290d522f475Smrg
2291d522f475Smrg    /*
2292d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
2293d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
2294d522f475Smrg     * with the same final character as the application sends to make this
2295d522f475Smrg     * work better with shell scripts, which may have trouble reading an
2296d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
2297d522f475Smrg     */
2298d522f475Smrg    mode = 0;
2299d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
2300d522f475Smrg	switch (state) {
2301d522f475Smrg	case 0:
2302d522f475Smrg	    if (isdigit(*cp)) {
2303d522f475Smrg		mode = 10 * mode + (*cp - '0');
2304d522f475Smrg		if (mode > 65535) {
2305d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
2306d522f475Smrg		    return;
2307d522f475Smrg		}
2308d522f475Smrg		break;
2309d522f475Smrg	    }
2310d522f475Smrg	    /* FALLTHRU */
2311d522f475Smrg	case 1:
2312d522f475Smrg	    if (*cp != ';') {
2313d522f475Smrg		TRACE(("do_osc did not find semicolon offset %d\n", cp - oscbuf));
2314d522f475Smrg		return;
2315d522f475Smrg	    }
2316d522f475Smrg	    state = 2;
2317d522f475Smrg	    break;
2318d522f475Smrg	case 2:
2319d522f475Smrg	    buf = (char *) cp;
2320d522f475Smrg	    state = 3;
2321d522f475Smrg	    /* FALLTHRU */
2322d522f475Smrg	default:
2323d522f475Smrg	    if (!xtermIsPrintable(screen, &cp, oscbuf + len)) {
2324d522f475Smrg		switch (mode) {
2325d522f475Smrg		case 0:
2326d522f475Smrg		case 1:
2327d522f475Smrg		case 2:
2328d522f475Smrg		    break;
2329d522f475Smrg		default:
2330d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
2331d522f475Smrg			   CharOf(*cp),
2332d522f475Smrg			   cp - oscbuf));
2333d522f475Smrg		    return;
2334d522f475Smrg		}
2335d522f475Smrg	    }
2336d522f475Smrg	}
2337d522f475Smrg    }
23380d92cbfdSchristos    if (buf == 0) {
23390d92cbfdSchristos	TRACE(("do_osc found no data\n"));
2340d522f475Smrg	return;
23410d92cbfdSchristos    }
2342d522f475Smrg
2343d522f475Smrg    switch (mode) {
2344d522f475Smrg    case 0:			/* new icon name and title */
2345d522f475Smrg	ChangeIconName(buf);
2346d522f475Smrg	ChangeTitle(buf);
2347d522f475Smrg	break;
2348d522f475Smrg
2349d522f475Smrg    case 1:			/* new icon name only */
2350d522f475Smrg	ChangeIconName(buf);
2351d522f475Smrg	break;
2352d522f475Smrg
2353d522f475Smrg    case 2:			/* new title only */
2354d522f475Smrg	ChangeTitle(buf);
2355d522f475Smrg	break;
2356d522f475Smrg
235722d8e007Schristos#ifdef notdef
2358d522f475Smrg    case 3:			/* change X property */
23590d92cbfdSchristos	if (screen->allowWindowOps)
23600d92cbfdSchristos	    ChangeXprop(buf);
2361d522f475Smrg	break;
236222d8e007Schristos#endif
2363d522f475Smrg#if OPT_ISO_COLORS
2364d522f475Smrg    case 4:
2365d522f475Smrg	ChangeAnsiColorRequest(xw, buf, final);
2366d522f475Smrg	break;
2367d522f475Smrg#endif
2368d522f475Smrg    case OSC_TEXT_FG:
2369d522f475Smrg    case OSC_TEXT_BG:
2370d522f475Smrg    case OSC_TEXT_CURSOR:
2371d522f475Smrg    case OSC_MOUSE_FG:
2372d522f475Smrg    case OSC_MOUSE_BG:
2373d522f475Smrg#if OPT_HIGHLIGHT_COLOR
2374d522f475Smrg    case OSC_HIGHLIGHT_BG:
2375d522f475Smrg#endif
2376d522f475Smrg#if OPT_TEK4014
2377d522f475Smrg    case OSC_TEK_FG:
2378d522f475Smrg    case OSC_TEK_BG:
2379d522f475Smrg    case OSC_TEK_CURSOR:
2380d522f475Smrg#endif
2381d522f475Smrg	if (xw->misc.dynamicColors)
2382d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
2383d522f475Smrg	break;
2384d522f475Smrg
2385d522f475Smrg    case 30:
2386d522f475Smrg    case 31:
2387d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
2388d522f475Smrg	break;
2389d522f475Smrg
2390d522f475Smrg#ifdef ALLOWLOGGING
2391d522f475Smrg    case 46:			/* new log file */
2392d522f475Smrg#ifdef ALLOWLOGFILECHANGES
2393d522f475Smrg	/*
2394d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
2395d522f475Smrg	 * arbitrary files accessible to the person running xterm.
2396d522f475Smrg	 */
2397d522f475Smrg	if (buf != 0
2398d522f475Smrg	    && strcmp(buf, "?")
2399d522f475Smrg	    && (cp = CastMallocN(char, strlen(buf)) != NULL)) {
2400d522f475Smrg	    strcpy(cp, buf);
2401d522f475Smrg	    if (screen->logfile)
2402d522f475Smrg		free(screen->logfile);
2403d522f475Smrg	    screen->logfile = cp;
2404d522f475Smrg	    break;
2405d522f475Smrg	}
2406d522f475Smrg#endif
2407d522f475Smrg	Bell(XkbBI_Info, 0);
2408d522f475Smrg	Bell(XkbBI_Info, 0);
2409d522f475Smrg	break;
2410d522f475Smrg#endif /* ALLOWLOGGING */
2411d522f475Smrg
2412d522f475Smrg    case 50:
2413d522f475Smrg#if OPT_SHIFT_FONTS
24140d92cbfdSchristos	if (!screen->allowFontOps && xw->misc.shift_fonts) {
24150d92cbfdSchristos	    ;			/* disabled via resource or control-sequence */
24160d92cbfdSchristos	} else if (buf != 0 && !strcmp(buf, "?")) {
2417d522f475Smrg	    int num = screen->menu_font_number;
2418d522f475Smrg
2419d522f475Smrg	    unparseputc1(xw, ANSI_OSC);
2420d522f475Smrg	    unparseputs(xw, "50");
2421d522f475Smrg
2422d522f475Smrg	    if ((buf = screen->MenuFontName(num)) != 0) {
2423d522f475Smrg		unparseputc(xw, ';');
2424d522f475Smrg		unparseputs(xw, buf);
2425d522f475Smrg	    }
2426d522f475Smrg	    unparseputc1(xw, final);
2427d522f475Smrg	    unparse_end(xw);
2428d522f475Smrg	} else if (buf != 0) {
2429d522f475Smrg	    int num = screen->menu_font_number;
2430d522f475Smrg	    VTFontNames fonts;
2431d522f475Smrg
2432d522f475Smrg	    memset(&fonts, 0, sizeof(fonts));
2433d522f475Smrg
2434d522f475Smrg	    /*
2435d522f475Smrg	     * If the font specification is a "#", followed by an
2436d522f475Smrg	     * optional sign and optional number, lookup the
2437d522f475Smrg	     * corresponding menu font entry.
2438d522f475Smrg	     */
2439d522f475Smrg	    if (*buf == '#') {
2440d522f475Smrg		int rel = 0;
2441d522f475Smrg
2442d522f475Smrg		if (*++buf == '+') {
2443d522f475Smrg		    rel = 1;
2444d522f475Smrg		    buf++;
2445d522f475Smrg		} else if (*buf == '-') {
2446d522f475Smrg		    rel = -1;
2447d522f475Smrg		    buf++;
2448d522f475Smrg		}
2449d522f475Smrg
2450d522f475Smrg		if (isdigit(CharOf(*buf))) {
2451d522f475Smrg		    int val = atoi(buf);
2452d522f475Smrg		    if (rel > 0)
2453d522f475Smrg			rel = val;
2454d522f475Smrg		    else if (rel < 0)
2455d522f475Smrg			rel = -val;
2456d522f475Smrg		    else
2457d522f475Smrg			num = val;
2458d522f475Smrg		} else if (rel == 0) {
2459d522f475Smrg		    num = 0;
2460d522f475Smrg		}
2461d522f475Smrg
2462d522f475Smrg		if (rel != 0) {
2463d522f475Smrg		    num = lookupRelativeFontSize(xw,
2464d522f475Smrg						 screen->menu_font_number, rel);
2465d522f475Smrg
2466d522f475Smrg		}
2467d522f475Smrg		if (num < 0
2468d522f475Smrg		    || num > fontMenu_lastBuiltin
2469d522f475Smrg		    || (buf = screen->MenuFontName(num)) == 0) {
2470d522f475Smrg		    Bell(XkbBI_MinorError, 0);
2471d522f475Smrg		    break;
2472d522f475Smrg		}
2473d522f475Smrg	    } else {
2474d522f475Smrg		num = fontMenu_fontescape;
2475d522f475Smrg	    }
2476d522f475Smrg	    fonts.f_n = buf;
2477d522f475Smrg	    SetVTFont(xw, num, True, &fonts);
2478d522f475Smrg	}
2479d522f475Smrg#endif /* OPT_SHIFT_FONTS */
2480d522f475Smrg	break;
2481d522f475Smrg    case 51:
2482d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
2483d522f475Smrg	break;
2484d522f475Smrg
2485d522f475Smrg#if OPT_PASTE64
2486d522f475Smrg    case 52:
24870d92cbfdSchristos	if (screen->allowWindowOps)
2488d522f475Smrg	    ManipulateSelectionData(xw, screen, buf, final);
2489d522f475Smrg	break;
2490d522f475Smrg#endif
2491d522f475Smrg	/*
2492d522f475Smrg	 * One could write code to send back the display and host names,
2493d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
2494d522f475Smrg	 */
2495d522f475Smrg    }
2496d522f475Smrg    unparse_end(xw);
2497d522f475Smrg}
2498d522f475Smrg
2499d522f475Smrg#ifdef SunXK_F36
2500d522f475Smrg#define MAX_UDK 37
2501d522f475Smrg#else
2502d522f475Smrg#define MAX_UDK 35
2503d522f475Smrg#endif
2504d522f475Smrgstatic struct {
2505d522f475Smrg    char *str;
2506d522f475Smrg    int len;
2507d522f475Smrg} user_keys[MAX_UDK];
2508d522f475Smrg
2509d522f475Smrg/*
2510d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
2511d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
2512d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
2513d522f475Smrg * "real" terminals accept commas in the string definitions).
2514d522f475Smrg */
2515d522f475Smrgstatic int
2516d522f475Smrgudk_value(char **cp)
2517d522f475Smrg{
2518d522f475Smrg    int c;
2519d522f475Smrg
2520d522f475Smrg    for (;;) {
2521d522f475Smrg	if ((c = **cp) != '\0')
2522d522f475Smrg	    *cp = *cp + 1;
2523d522f475Smrg	if (c == ';' || c == '\0')
2524d522f475Smrg	    return -1;
2525d522f475Smrg	if (c >= '0' && c <= '9')
2526d522f475Smrg	    return c - '0';
2527d522f475Smrg	if (c >= 'A' && c <= 'F')
2528d522f475Smrg	    return c - 'A' + 10;
2529d522f475Smrg	if (c >= 'a' && c <= 'f')
2530d522f475Smrg	    return c - 'a' + 10;
2531d522f475Smrg    }
2532d522f475Smrg}
2533d522f475Smrg
2534d522f475Smrgvoid
2535d522f475Smrgreset_decudk(void)
2536d522f475Smrg{
2537d522f475Smrg    int n;
2538d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
2539d522f475Smrg	if (user_keys[n].str != 0) {
2540d522f475Smrg	    free(user_keys[n].str);
2541d522f475Smrg	    user_keys[n].str = 0;
2542d522f475Smrg	    user_keys[n].len = 0;
2543d522f475Smrg	}
2544d522f475Smrg    }
2545d522f475Smrg}
2546d522f475Smrg
2547d522f475Smrg/*
2548d522f475Smrg * Parse the data for DECUDK (user-defined keys).
2549d522f475Smrg */
2550d522f475Smrgstatic void
2551d522f475Smrgparse_decudk(char *cp)
2552d522f475Smrg{
2553d522f475Smrg    while (*cp) {
2554d522f475Smrg	char *base = cp;
2555d522f475Smrg	char *str = CastMallocN(char, strlen(cp) + 1);
2556d522f475Smrg	unsigned key = 0;
2557d522f475Smrg	int lo, hi;
2558d522f475Smrg	int len = 0;
2559d522f475Smrg
2560d522f475Smrg	while (isdigit(CharOf(*cp)))
25610d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
2562d522f475Smrg	if (*cp == '/') {
2563d522f475Smrg	    cp++;
2564d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
2565d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
25660d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
2567d522f475Smrg	    }
2568d522f475Smrg	}
2569d522f475Smrg	if (len > 0 && key < MAX_UDK) {
2570d522f475Smrg	    if (user_keys[key].str != 0)
2571d522f475Smrg		free(user_keys[key].str);
2572d522f475Smrg	    user_keys[key].str = str;
2573d522f475Smrg	    user_keys[key].len = len;
2574d522f475Smrg	} else {
2575d522f475Smrg	    free(str);
2576d522f475Smrg	}
2577d522f475Smrg	if (*cp == ';')
2578d522f475Smrg	    cp++;
2579d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
2580d522f475Smrg	    break;
2581d522f475Smrg    }
2582d522f475Smrg}
2583d522f475Smrg
2584d522f475Smrg#if OPT_TRACE
2585d522f475Smrg#define SOFT_WIDE 10
2586d522f475Smrg#define SOFT_HIGH 20
2587d522f475Smrg
2588d522f475Smrgstatic void
2589d522f475Smrgparse_decdld(ANSI * params, char *string)
2590d522f475Smrg{
2591d522f475Smrg    char DscsName[8];
2592d522f475Smrg    int len;
2593d522f475Smrg    int Pfn = params->a_param[0];
2594d522f475Smrg    int Pcn = params->a_param[1];
2595d522f475Smrg    int Pe = params->a_param[2];
2596d522f475Smrg    int Pcmw = params->a_param[3];
2597d522f475Smrg    int Pw = params->a_param[4];
2598d522f475Smrg    int Pt = params->a_param[5];
2599d522f475Smrg    int Pcmh = params->a_param[6];
2600d522f475Smrg    int Pcss = params->a_param[7];
2601d522f475Smrg
2602d522f475Smrg    int start_char = Pcn + 0x20;
2603d522f475Smrg    int char_wide = ((Pcmw == 0)
2604d522f475Smrg		     ? (Pcss ? 6 : 10)
2605d522f475Smrg		     : (Pcmw > 4
2606d522f475Smrg			? Pcmw
2607d522f475Smrg			: (Pcmw + 3)));
2608d522f475Smrg    int char_high = ((Pcmh == 0)
2609d522f475Smrg		     ? ((Pcmw >= 2 || Pcmw <= 4)
2610d522f475Smrg			? 10
2611d522f475Smrg			: 20)
2612d522f475Smrg		     : Pcmh);
2613d522f475Smrg    Char ch;
2614d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
2615d522f475Smrg    Bool first = True;
2616d522f475Smrg    Bool prior = False;
2617d522f475Smrg    int row = 0, col = 0;
2618d522f475Smrg
2619d522f475Smrg    TRACE(("Parsing DECDLD\n"));
2620d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
2621d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
2622d522f475Smrg    TRACE(("  erase control %d\n", Pe));
2623d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
2624d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
2625d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
2626d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
2627d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
2628d522f475Smrg
2629d522f475Smrg    if (Pfn > 1
2630d522f475Smrg	|| Pcn > 95
2631d522f475Smrg	|| Pe > 2
2632d522f475Smrg	|| Pcmw > 10
2633d522f475Smrg	|| Pcmw == 1
2634d522f475Smrg	|| Pt > 2
2635d522f475Smrg	|| Pcmh > 20
2636d522f475Smrg	|| Pcss > 1
2637d522f475Smrg	|| char_wide > SOFT_WIDE
2638d522f475Smrg	|| char_high > SOFT_HIGH) {
2639d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
2640d522f475Smrg	return;
2641d522f475Smrg    }
2642d522f475Smrg
2643d522f475Smrg    len = 0;
2644d522f475Smrg    while (*string != '\0') {
2645d522f475Smrg	ch = CharOf(*string++);
2646d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
2647d522f475Smrg	    if (len < 2)
2648d522f475Smrg		DscsName[len++] = ch;
2649d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
2650d522f475Smrg	    DscsName[len++] = ch;
2651d522f475Smrg	    break;
2652d522f475Smrg	}
2653d522f475Smrg    }
2654d522f475Smrg    DscsName[len] = 0;
2655d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
2656d522f475Smrg
2657d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
2658d522f475Smrg    while (*string != '\0') {
2659d522f475Smrg	if (first) {
2660d522f475Smrg	    TRACE(("Char %d:\n", start_char));
2661d522f475Smrg	    if (prior) {
2662d522f475Smrg		for (row = 0; row < char_high; ++row) {
2663d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
2664d522f475Smrg		}
2665d522f475Smrg	    }
2666d522f475Smrg	    prior = False;
2667d522f475Smrg	    first = False;
2668d522f475Smrg	    for (row = 0; row < char_high; ++row) {
2669d522f475Smrg		for (col = 0; col < char_wide; ++col) {
2670d522f475Smrg		    bits[row][col] = '.';
2671d522f475Smrg		}
2672d522f475Smrg	    }
2673d522f475Smrg	    row = col = 0;
2674d522f475Smrg	}
2675d522f475Smrg	ch = CharOf(*string++);
2676d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
2677d522f475Smrg	    int n;
2678d522f475Smrg
2679d522f475Smrg	    ch -= 0x3f;
2680d522f475Smrg	    for (n = 0; n < 6; ++n) {
2681d522f475Smrg		bits[row + n][col] = (ch & (1 << n)) ? '*' : '.';
2682d522f475Smrg	    }
2683d522f475Smrg	    col += 1;
2684d522f475Smrg	    prior = True;
2685d522f475Smrg	} else if (ch == '/') {
2686d522f475Smrg	    row += 6;
2687d522f475Smrg	    col = 0;
2688d522f475Smrg	} else if (ch == ';') {
2689d522f475Smrg	    first = True;
2690d522f475Smrg	    ++start_char;
2691d522f475Smrg	}
2692d522f475Smrg    }
2693d522f475Smrg}
2694d522f475Smrg#else
2695d522f475Smrg#define parse_decdld(p,q)	/* nothing */
2696d522f475Smrg#endif
2697d522f475Smrg
2698d522f475Smrg/*
2699d522f475Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
2700d522f475Smrg * interspersing with control characters, but have the string already.
2701d522f475Smrg */
2702d522f475Smrgstatic void
2703d522f475Smrgparse_ansi_params(ANSI * params, char **string)
2704d522f475Smrg{
2705d522f475Smrg    char *cp = *string;
2706d522f475Smrg    short nparam = 0;
2707d522f475Smrg
2708d522f475Smrg    memset(params, 0, sizeof(*params));
2709d522f475Smrg    while (*cp != '\0') {
2710d522f475Smrg	Char ch = CharOf(*cp++);
2711d522f475Smrg
2712d522f475Smrg	if (isdigit(ch)) {
2713d522f475Smrg	    if (nparam < NPARAM) {
2714d522f475Smrg		params->a_param[nparam] *= 10;
2715d522f475Smrg		params->a_param[nparam] += (ch - '0');
2716d522f475Smrg	    }
2717d522f475Smrg	} else if (ch == ';') {
2718d522f475Smrg	    if (++nparam < NPARAM)
2719d522f475Smrg		params->a_nparam = nparam;
2720d522f475Smrg	} else if (ch < 32) {
2721d522f475Smrg	    ;
2722d522f475Smrg	} else {
2723d522f475Smrg	    /* should be 0x30 to 0x7e */
2724d522f475Smrg	    params->a_final = ch;
2725d522f475Smrg	    break;
2726d522f475Smrg	}
2727d522f475Smrg    }
2728d522f475Smrg    *string = cp;
2729d522f475Smrg}
2730d522f475Smrg
2731d522f475Smrgvoid
2732d522f475Smrgdo_dcs(XtermWidget xw, Char * dcsbuf, size_t dcslen)
2733d522f475Smrg{
2734d522f475Smrg    TScreen *screen = &xw->screen;
2735d522f475Smrg    char reply[BUFSIZ];
2736d522f475Smrg    char *cp = (char *) dcsbuf;
2737d522f475Smrg    Bool okay;
2738d522f475Smrg    ANSI params;
2739d522f475Smrg
2740d522f475Smrg    TRACE(("do_dcs(%s:%d)\n", (char *) dcsbuf, dcslen));
2741d522f475Smrg
2742d522f475Smrg    if (dcslen != strlen(cp))
2743d522f475Smrg	/* shouldn't have nulls in the string */
2744d522f475Smrg	return;
2745d522f475Smrg
2746d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
2747d522f475Smrg    case '$':			/* DECRQSS */
2748d522f475Smrg	okay = True;
2749d522f475Smrg
2750d522f475Smrg	cp++;
2751d522f475Smrg	if (*cp++ == 'q') {
2752d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
2753d522f475Smrg		sprintf(reply, "%d%s",
2754d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
2755d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
2756d522f475Smrg			cp);
2757d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
2758d522f475Smrg		sprintf(reply, "%d%s%s",
2759d522f475Smrg			(screen->vtXX_level ?
2760d522f475Smrg			 screen->vtXX_level : 1) + 60,
2761d522f475Smrg			(screen->vtXX_level >= 2)
2762d522f475Smrg			? (screen->control_eight_bits
2763d522f475Smrg			   ? ";0" : ";1")
2764d522f475Smrg			: "",
2765d522f475Smrg			cp);
2766d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
2767d522f475Smrg		sprintf(reply, "%d;%dr",
2768d522f475Smrg			screen->top_marg + 1,
2769d522f475Smrg			screen->bot_marg + 1);
2770d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
2771d522f475Smrg		strcpy(reply, "0");
2772d522f475Smrg		if (xw->flags & BOLD)
2773d522f475Smrg		    strcat(reply, ";1");
2774d522f475Smrg		if (xw->flags & UNDERLINE)
2775d522f475Smrg		    strcat(reply, ";4");
2776d522f475Smrg		if (xw->flags & BLINK)
2777d522f475Smrg		    strcat(reply, ";5");
2778d522f475Smrg		if (xw->flags & INVERSE)
2779d522f475Smrg		    strcat(reply, ";7");
2780d522f475Smrg		if (xw->flags & INVISIBLE)
2781d522f475Smrg		    strcat(reply, ";8");
2782d522f475Smrg		if_OPT_EXT_COLORS(screen, {
2783d522f475Smrg		    if (xw->flags & FG_COLOR) {
2784d522f475Smrg			if (xw->cur_foreground >= 16)
2785d522f475Smrg			    sprintf(reply + strlen(reply),
2786d522f475Smrg				    ";38;5;%d", xw->cur_foreground);
2787d522f475Smrg			else
2788d522f475Smrg			    sprintf(reply + strlen(reply),
2789d522f475Smrg				    ";%d%d",
2790d522f475Smrg				    xw->cur_foreground >= 8 ? 9 : 3,
2791d522f475Smrg				    xw->cur_foreground >= 8 ?
2792d522f475Smrg				    xw->cur_foreground - 8 :
2793d522f475Smrg				    xw->cur_foreground);
2794d522f475Smrg		    }
2795d522f475Smrg		    if (xw->flags & BG_COLOR) {
2796d522f475Smrg			if (xw->cur_background >= 16)
2797d522f475Smrg			    sprintf(reply + strlen(reply),
2798d522f475Smrg				    ";48;5;%d", xw->cur_foreground);
2799d522f475Smrg			else
2800d522f475Smrg			    sprintf(reply + strlen(reply),
2801d522f475Smrg				    ";%d%d",
2802d522f475Smrg				    xw->cur_background >= 8 ? 10 : 4,
2803d522f475Smrg				    xw->cur_background >= 8 ?
2804d522f475Smrg				    xw->cur_background - 8 :
2805d522f475Smrg				    xw->cur_background);
2806d522f475Smrg		    }
2807d522f475Smrg		});
2808d522f475Smrg		if_OPT_ISO_TRADITIONAL_COLORS(screen, {
2809d522f475Smrg		    if (xw->flags & FG_COLOR)
2810d522f475Smrg			sprintf(reply + strlen(reply),
2811d522f475Smrg				";%d%d",
2812d522f475Smrg				xw->cur_foreground >= 8 ? 9 : 3,
2813d522f475Smrg				xw->cur_foreground >= 8 ?
2814d522f475Smrg				xw->cur_foreground - 8 :
2815d522f475Smrg				xw->cur_foreground);
2816d522f475Smrg		    if (xw->flags & BG_COLOR)
2817d522f475Smrg			sprintf(reply + strlen(reply),
2818d522f475Smrg				";%d%d",
2819d522f475Smrg				xw->cur_background >= 8 ? 10 : 4,
2820d522f475Smrg				xw->cur_background >= 8 ?
2821d522f475Smrg				xw->cur_background - 8 :
2822d522f475Smrg				xw->cur_background);
2823d522f475Smrg		});
2824d522f475Smrg		strcat(reply, "m");
2825d522f475Smrg	    } else
2826d522f475Smrg		okay = False;
2827d522f475Smrg
282822d8e007Schristos	    if (okay) {
28290d92cbfdSchristos		unparseputc1(xw, ANSI_DCS);
28300d92cbfdSchristos		unparseputc(xw, okay ? '1' : '0');
28310d92cbfdSchristos		unparseputc(xw, '$');
28320d92cbfdSchristos		unparseputc(xw, 'r');
2833d522f475Smrg		cp = reply;
283422d8e007Schristos		unparseputs(xw, cp);
28350d92cbfdSchristos		unparseputc1(xw, ANSI_ST);
28360d92cbfdSchristos	    } else {
28370d92cbfdSchristos		unparseputc(xw, ANSI_CAN);
283822d8e007Schristos	    }
2839d522f475Smrg	} else {
2840d522f475Smrg	    unparseputc(xw, ANSI_CAN);
2841d522f475Smrg	}
2842d522f475Smrg	break;
2843d522f475Smrg#if OPT_TCAP_QUERY
2844d522f475Smrg    case '+':
2845d522f475Smrg	cp++;
28460d92cbfdSchristos	if ((*cp == 'q') && screen->allowTcapOps) {
2847d522f475Smrg	    Bool fkey;
2848d522f475Smrg	    unsigned state;
2849d522f475Smrg	    int code;
2850d522f475Smrg	    char *tmp;
2851d522f475Smrg	    char *parsed = ++cp;
2852d522f475Smrg
2853d522f475Smrg	    unparseputc1(xw, ANSI_DCS);
2854d522f475Smrg
2855d522f475Smrg	    code = xtermcapKeycode(xw, &parsed, &state, &fkey);
2856d522f475Smrg
2857d522f475Smrg	    unparseputc(xw, code >= 0 ? '1' : '0');
2858d522f475Smrg
2859d522f475Smrg	    unparseputc(xw, '+');
2860d522f475Smrg	    unparseputc(xw, 'r');
2861d522f475Smrg
2862d522f475Smrg	    while (*cp != 0) {
2863d522f475Smrg		if (cp == parsed)
2864d522f475Smrg		    break;	/* no data found, error */
2865d522f475Smrg
2866d522f475Smrg		for (tmp = cp; tmp != parsed; ++tmp)
2867d522f475Smrg		    unparseputc(xw, *tmp);
2868d522f475Smrg
2869d522f475Smrg		if (code >= 0) {
2870d522f475Smrg		    unparseputc(xw, '=');
2871d522f475Smrg		    screen->tc_query_code = code;
2872d522f475Smrg		    screen->tc_query_fkey = fkey;
2873d522f475Smrg#if OPT_ISO_COLORS
2874d522f475Smrg		    /* XK_COLORS is a fake code for the "Co" entry (maximum
2875d522f475Smrg		     * number of colors) */
2876d522f475Smrg		    if (code == XK_COLORS) {
2877d522f475Smrg			unparseputn(xw, NUM_ANSI_COLORS);
2878d522f475Smrg		    } else
2879d522f475Smrg#endif
2880d522f475Smrg#if OPT_TCAP_FKEYS
2881d522f475Smrg			/*
2882d522f475Smrg			 * First ensure that we handle the extended cursor- and
2883d522f475Smrg			 * editing-keypad keys.
2884d522f475Smrg			 */
2885d522f475Smrg			if ((code <= XK_Fn(MAX_FKEY))
2886d522f475Smrg			    || xtermcapString(xw, CodeToXkey(code), 0) == 0)
2887d522f475Smrg#endif
2888d522f475Smrg		    {
2889d522f475Smrg			XKeyEvent event;
2890d522f475Smrg			event.state = state;
2891d522f475Smrg			Input(xw, &event, False);
2892d522f475Smrg		    }
2893d522f475Smrg		    screen->tc_query_code = -1;
2894d522f475Smrg		} else {
2895d522f475Smrg		    break;	/* no match found, error */
2896d522f475Smrg		}
2897d522f475Smrg
2898d522f475Smrg		cp = parsed;
2899d522f475Smrg		if (*parsed == ';') {
2900d522f475Smrg		    unparseputc(xw, *parsed++);
2901d522f475Smrg		    cp = parsed;
2902d522f475Smrg		    code = xtermcapKeycode(xw, &parsed, &state, &fkey);
2903d522f475Smrg		}
2904d522f475Smrg	    }
2905d522f475Smrg	    unparseputc1(xw, ANSI_ST);
2906d522f475Smrg	}
2907d522f475Smrg	break;
2908d522f475Smrg#endif
2909d522f475Smrg    default:
29100d92cbfdSchristos	if (screen->terminal_id >= 200) {	/* VT220 */
29110d92cbfdSchristos	    parse_ansi_params(&params, &cp);
29120d92cbfdSchristos	    switch (params.a_final) {
29130d92cbfdSchristos	    case '|':		/* DECUDK */
29140d92cbfdSchristos		if (params.a_param[0] == 0)
29150d92cbfdSchristos		    reset_decudk();
29160d92cbfdSchristos		parse_decudk(cp);
29170d92cbfdSchristos		break;
29180d92cbfdSchristos	    case '{':		/* DECDLD (no '}' case though) */
29190d92cbfdSchristos		parse_decdld(&params, cp);
29200d92cbfdSchristos		break;
29210d92cbfdSchristos	    }
2922d522f475Smrg	}
2923d522f475Smrg	break;
2924d522f475Smrg    }
2925d522f475Smrg    unparse_end(xw);
2926d522f475Smrg}
2927d522f475Smrg
2928d522f475Smrgchar *
2929d522f475Smrgudk_lookup(int keycode, int *len)
2930d522f475Smrg{
2931d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
2932d522f475Smrg	*len = user_keys[keycode].len;
2933d522f475Smrg	return user_keys[keycode].str;
2934d522f475Smrg    }
2935d522f475Smrg    return 0;
2936d522f475Smrg}
2937d522f475Smrg
2938d522f475Smrgstatic void
2939d522f475SmrgChangeGroup(String attribute, char *value)
2940d522f475Smrg{
2941d522f475Smrg#if OPT_WIDE_CHARS
2942d522f475Smrg    static Char *converted;	/* NO_LEAKS */
2943d522f475Smrg#endif
2944d522f475Smrg    static char empty[1];
2945d522f475Smrg
2946d522f475Smrg    Arg args[1];
2947d522f475Smrg    char *original = (value != 0) ? value : empty;
2948d522f475Smrg    char *name = original;
2949d522f475Smrg    TScreen *screen = TScreenOf(term);
2950d522f475Smrg    Widget w = CURRENT_EMU();
2951d522f475Smrg    Widget top = SHELL_OF(w);
2952d522f475Smrg    unsigned limit = strlen(name);
2953d522f475Smrg    Char *c1 = (Char *) original;
2954d522f475Smrg    Char *cp;
2955d522f475Smrg
2956d522f475Smrg    TRACE(("ChangeGroup(attribute=%s, value=%s)\n", attribute, name));
2957d522f475Smrg
2958d522f475Smrg    if (!screen->allowTitleOps)
2959d522f475Smrg	return;
2960d522f475Smrg
2961d522f475Smrg    /*
2962d522f475Smrg     * Ignore titles that are too long to be plausible requests.
2963d522f475Smrg     */
2964d522f475Smrg    if (limit >= 1024)
2965d522f475Smrg	return;
2966d522f475Smrg
2967d522f475Smrg    for (cp = c1; *cp != 0; ++cp) {
2968d522f475Smrg	Char *c2 = cp;
2969d522f475Smrg	if (!xtermIsPrintable(screen, &cp, c1 + limit)) {
2970d522f475Smrg	    memset(c2, '?', (unsigned) (cp + 1 - c2));
2971d522f475Smrg	}
2972d522f475Smrg    }
2973d522f475Smrg
2974d522f475Smrg#if OPT_WIDE_CHARS
2975d522f475Smrg    /*
2976d522f475Smrg     * Title strings are limited to ISO-8859-1, which is consistent with the
2977d522f475Smrg     * printable data in sos_table.  However, if we're running in UTF-8 mode,
2978d522f475Smrg     * it is likely that non-ASCII text in the string will be rejected because
2979d522f475Smrg     * it is not printable in the current locale.  So we convert it to UTF-8,
2980d522f475Smrg     * allowing the X library to convert it back.
2981d522f475Smrg     */
2982d522f475Smrg    if (xtermEnvUTF8() && !screen->utf8_title) {
2983d522f475Smrg	int n;
2984d522f475Smrg
2985d522f475Smrg	for (n = 0; name[n] != '\0'; ++n) {
2986d522f475Smrg	    if (CharOf(name[n]) > 127) {
2987d522f475Smrg		if (converted != 0)
2988d522f475Smrg		    free(converted);
2989d522f475Smrg		if ((converted = TypeMallocN(Char, 1 + (5 * limit))) != 0) {
2990d522f475Smrg		    Char *temp = converted;
2991d522f475Smrg		    while (*name != 0) {
2992d522f475Smrg			temp = convertToUTF8(temp, CharOf(*name));
2993d522f475Smrg			++name;
2994d522f475Smrg		    }
2995d522f475Smrg		    *temp = 0;
2996d522f475Smrg		    name = (char *) converted;
2997d522f475Smrg		    TRACE(("...converted{%s}\n", name));
2998d522f475Smrg		}
2999d522f475Smrg		break;
3000d522f475Smrg	    }
3001d522f475Smrg	}
3002d522f475Smrg    }
3003d522f475Smrg#endif
3004d522f475Smrg
3005d522f475Smrg#if OPT_SAME_NAME
3006d522f475Smrg    /* If the attribute isn't going to change, then don't bother... */
3007d522f475Smrg
3008d522f475Smrg    if (resource.sameName) {
3009d522f475Smrg	char *buf;
3010d522f475Smrg	XtSetArg(args[0], attribute, &buf);
3011d522f475Smrg	XtGetValues(top, args, 1);
3012d522f475Smrg	TRACE(("...comparing{%s}\n", buf));
3013d522f475Smrg	if (strcmp(name, buf) == 0)
3014d522f475Smrg	    return;
3015d522f475Smrg    }
3016d522f475Smrg#endif /* OPT_SAME_NAME */
3017d522f475Smrg
3018d522f475Smrg    TRACE(("...updating %s\n", attribute));
3019d522f475Smrg    TRACE(("...value is %s\n", name));
3020d522f475Smrg    XtSetArg(args[0], attribute, name);
3021d522f475Smrg    XtSetValues(top, args, 1);
3022d522f475Smrg
3023d522f475Smrg#if OPT_WIDE_CHARS
3024d522f475Smrg    if (xtermEnvUTF8()) {
3025d522f475Smrg	Display *dpy = XtDisplay(term);
3026d522f475Smrg	Atom my_atom;
3027d522f475Smrg
3028d522f475Smrg	const char *propname = (!strcmp(attribute, XtNtitle)
3029d522f475Smrg				? "_NET_WM_NAME"
3030d522f475Smrg				: "_NET_WM_ICON_NAME");
3031d522f475Smrg	if ((my_atom = XInternAtom(dpy, propname, False)) != None) {
3032d522f475Smrg	    if (screen->utf8_title) {	/* FIXME - redundant? */
3033d522f475Smrg		TRACE(("...updating %s\n", propname));
3034d522f475Smrg		TRACE(("...value is %s\n", original));
3035d522f475Smrg		XChangeProperty(dpy, VShellWindow,
3036d522f475Smrg				my_atom, XA_UTF8_STRING(dpy), 8,
3037d522f475Smrg				PropModeReplace,
3038d522f475Smrg				(Char *) original, (int) strlen(original));
3039d522f475Smrg	    } else {
3040d522f475Smrg		TRACE(("...deleting %s\n", propname));
3041d522f475Smrg		XDeleteProperty(dpy, VShellWindow, my_atom);
3042d522f475Smrg	    }
3043d522f475Smrg	}
3044d522f475Smrg    }
3045d522f475Smrg#endif
3046d522f475Smrg}
3047d522f475Smrg
3048d522f475Smrgvoid
3049d522f475SmrgChangeIconName(char *name)
3050d522f475Smrg{
3051d522f475Smrg    if (name == 0)
3052d522f475Smrg	name = "";
3053d522f475Smrg#if OPT_ZICONBEEP		/* If warning should be given then give it */
3054d522f475Smrg    if (resource.zIconBeep && term->screen.zIconBeep_flagged) {
3055d522f475Smrg	char *newname = CastMallocN(char, strlen(name) + 4);
3056d522f475Smrg	if (!newname) {
3057d522f475Smrg	    fprintf(stderr, "malloc failed in ChangeIconName\n");
3058d522f475Smrg	    return;
3059d522f475Smrg	}
3060d522f475Smrg	strcpy(newname, "*** ");
3061d522f475Smrg	strcat(newname, name);
3062d522f475Smrg	ChangeGroup(XtNiconName, newname);
3063d522f475Smrg	free(newname);
3064d522f475Smrg    } else
3065d522f475Smrg#endif /* OPT_ZICONBEEP */
3066d522f475Smrg	ChangeGroup(XtNiconName, name);
3067d522f475Smrg}
3068d522f475Smrg
3069d522f475Smrgvoid
3070d522f475SmrgChangeTitle(char *name)
3071d522f475Smrg{
3072d522f475Smrg    ChangeGroup(XtNtitle, name);
3073d522f475Smrg}
3074d522f475Smrg
3075d522f475Smrg#define Strlen(s) strlen((char *)(s))
3076d522f475Smrg
3077d522f475Smrgvoid
3078d522f475SmrgChangeXprop(char *buf)
3079d522f475Smrg{
3080d522f475Smrg    Display *dpy = XtDisplay(toplevel);
3081d522f475Smrg    Window w = XtWindow(toplevel);
3082d522f475Smrg    XTextProperty text_prop;
3083d522f475Smrg    Atom aprop;
3084d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
3085d522f475Smrg
3086d522f475Smrg    if (pchEndPropName)
3087d522f475Smrg	*pchEndPropName = '\0';
3088d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
3089d522f475Smrg    if (pchEndPropName == NULL) {
3090d522f475Smrg	/* no "=value" given, so delete the property */
3091d522f475Smrg	XDeleteProperty(dpy, w, aprop);
3092d522f475Smrg    } else {
3093d522f475Smrg	text_prop.value = pchEndPropName + 1;
3094d522f475Smrg	text_prop.encoding = XA_STRING;
3095d522f475Smrg	text_prop.format = 8;
3096d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
3097d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
3098d522f475Smrg    }
3099d522f475Smrg}
3100d522f475Smrg
3101d522f475Smrg/***====================================================================***/
3102d522f475Smrg
3103d522f475Smrg/*
3104d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
3105d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
3106d522f475Smrg */
3107d522f475Smrgvoid
3108d522f475SmrgReverseOldColors(void)
3109d522f475Smrg{
3110d522f475Smrg    ScrnColors *pOld = pOldColors;
3111d522f475Smrg    Pixel tmpPix;
3112d522f475Smrg    char *tmpName;
3113d522f475Smrg
3114d522f475Smrg    if (pOld) {
3115d522f475Smrg	/* change text cursor, if necesary */
3116d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
3117d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
3118d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
3119d522f475Smrg		XtFree(pOldColors->names[TEXT_CURSOR]);
3120d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
3121d522f475Smrg	    }
3122d522f475Smrg	    if (pOld->names[TEXT_BG]) {
3123d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
3124d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
3125d522f475Smrg		}
3126d522f475Smrg	    }
3127d522f475Smrg	}
3128d522f475Smrg
3129d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
3130d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
3131d522f475Smrg
3132d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
3133d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
3134d522f475Smrg
3135d522f475Smrg#if OPT_TEK4014
3136d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
3137d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
3138d522f475Smrg#endif
3139d522f475Smrg    }
3140d522f475Smrg    return;
3141d522f475Smrg}
3142d522f475Smrg
3143d522f475SmrgBool
3144d522f475SmrgAllocateTermColor(XtermWidget xw,
3145d522f475Smrg		  ScrnColors * pNew,
3146d522f475Smrg		  int ndx,
3147d522f475Smrg		  const char *name)
3148d522f475Smrg{
3149d522f475Smrg    XColor def;
3150d522f475Smrg    TScreen *screen = &xw->screen;
3151d522f475Smrg    Colormap cmap = xw->core.colormap;
3152d522f475Smrg    char *newName;
3153d522f475Smrg
3154d522f475Smrg    if (XParseColor(screen->display, cmap, name, &def)
3155d522f475Smrg	&& (XAllocColor(screen->display, cmap, &def)
3156d522f475Smrg	    || find_closest_color(screen->display, cmap, &def))
3157d522f475Smrg	&& (newName = x_strdup(name)) != 0) {
3158d522f475Smrg	if (COLOR_DEFINED(pNew, ndx))
3159d522f475Smrg	    free(pNew->names[ndx]);
3160d522f475Smrg	SET_COLOR_VALUE(pNew, ndx, def.pixel);
3161d522f475Smrg	SET_COLOR_NAME(pNew, ndx, newName);
3162d522f475Smrg	TRACE(("AllocateTermColor #%d: %s (pixel %#lx)\n", ndx, newName, def.pixel));
3163d522f475Smrg	return (True);
3164d522f475Smrg    }
3165d522f475Smrg    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
3166d522f475Smrg    return (False);
3167d522f475Smrg}
3168d522f475Smrg/***====================================================================***/
3169d522f475Smrg
3170d522f475Smrg/* ARGSUSED */
3171d522f475Smrgvoid
3172d522f475SmrgPanic(char *s GCC_UNUSED, int a GCC_UNUSED)
3173d522f475Smrg{
3174d522f475Smrg#ifdef DEBUG
3175d522f475Smrg    if (debug) {
3176d522f475Smrg	fprintf(stderr, "%s: PANIC!\t", xterm_name);
3177d522f475Smrg	fprintf(stderr, s, a);
3178d522f475Smrg	fputs("\r\n", stderr);
3179d522f475Smrg	fflush(stderr);
3180d522f475Smrg    }
3181d522f475Smrg#endif /* DEBUG */
3182d522f475Smrg}
3183d522f475Smrg
3184d522f475Smrgconst char *
3185d522f475SmrgSysErrorMsg(int code)
3186d522f475Smrg{
3187d522f475Smrg    static char unknown[] = "unknown error";
3188d522f475Smrg    char *s = strerror(code);
3189d522f475Smrg    return s ? s : unknown;
3190d522f475Smrg}
3191d522f475Smrg
3192d522f475Smrgconst char *
3193d522f475SmrgSysReasonMsg(int code)
3194d522f475Smrg{
3195d522f475Smrg    /* *INDENT-OFF* */
3196d522f475Smrg    static const struct {
3197d522f475Smrg	int code;
3198d522f475Smrg	const char *name;
3199d522f475Smrg    } table[] = {
3200d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
3201d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
3202d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
3203d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
3204d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
3205d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
3206d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
3207d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
3208d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
3209d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
3210d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
3211d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
3212d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
3213d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
3214d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
3215d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
3216d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
3217d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
3218d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
3219d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
3220d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
3221d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
3222d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
3223d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
3224d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
3225d522f475Smrg	{ ERROR_SPREALLOC,	"spawn: realloc of ttydev failed" },
3226d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
3227d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
3228d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
3229d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
3230d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
3231d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
3232d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
3233d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
3234d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
3235d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
3236d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
3237d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
3238d522f475Smrg	{ ERROR_SREALLOC,	"ScreenResize: realloc() failed on alt base" },
3239d522f475Smrg	{ ERROR_RESIZE,		"ScreenResize: malloc() or realloc() failed" },
3240d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
3241d522f475Smrg	{ ERROR_SBRALLOC,	"ScrollBarOn: realloc() failed on base" },
3242d522f475Smrg	{ ERROR_SBRALLOC2,	"ScrollBarOn: realloc() failed on rows" },
3243d522f475Smrg	{ ERROR_MMALLOC,	"my_memmove: malloc/realloc failed" },
3244d522f475Smrg    };
3245d522f475Smrg    /* *INDENT-ON* */
3246d522f475Smrg
3247d522f475Smrg    Cardinal n;
3248d522f475Smrg    const char *result = "?";
3249d522f475Smrg
3250d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
3251d522f475Smrg	if (code == table[n].code) {
3252d522f475Smrg	    result = table[n].name;
3253d522f475Smrg	    break;
3254d522f475Smrg	}
3255d522f475Smrg    }
3256d522f475Smrg    return result;
3257d522f475Smrg}
3258d522f475Smrg
3259d522f475Smrgvoid
3260d522f475SmrgSysError(int code)
3261d522f475Smrg{
3262d522f475Smrg    int oerrno = errno;
3263d522f475Smrg
3264d522f475Smrg    fprintf(stderr, "%s: Error %d, errno %d: ", xterm_name, code, oerrno);
3265d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
3266d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
3267d522f475Smrg
3268d522f475Smrg    Cleanup(code);
3269d522f475Smrg}
3270d522f475Smrg
3271d522f475Smrg/*
3272d522f475Smrg * cleanup by sending SIGHUP to client processes
3273d522f475Smrg */
3274d522f475Smrgvoid
3275d522f475SmrgCleanup(int code)
3276d522f475Smrg{
3277d522f475Smrg    static Bool cleaning;
3278d522f475Smrg    TScreen *screen = TScreenOf(term);
3279d522f475Smrg
3280d522f475Smrg    /*
3281d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
3282d522f475Smrg     */
3283d522f475Smrg    if (code == 0) {
3284d522f475Smrg	if (cleaning) {
3285d522f475Smrg	    hold_screen = 0;
3286d522f475Smrg	    return;
3287d522f475Smrg	}
3288d522f475Smrg
3289d522f475Smrg	cleaning = True;
3290d522f475Smrg	need_cleanup = False;
3291d522f475Smrg
3292d522f475Smrg	TRACE(("Cleanup %d\n", code));
3293d522f475Smrg
3294d522f475Smrg	if (hold_screen) {
3295d522f475Smrg	    hold_screen = 2;
3296d522f475Smrg	    while (hold_screen) {
3297d522f475Smrg		xevents();
3298d522f475Smrg		Sleep(10);
3299d522f475Smrg	    }
3300d522f475Smrg	}
3301d522f475Smrg#if OPT_SESSION_MGT
3302d522f475Smrg	if (resource.sessionMgt) {
3303d522f475Smrg	    XtVaSetValues(toplevel,
3304d522f475Smrg			  XtNjoinSession, False,
3305d522f475Smrg			  (XtPointer *) 0);
3306d522f475Smrg	}
3307d522f475Smrg#endif
3308d522f475Smrg    }
3309d522f475Smrg
3310d522f475Smrg    if (screen->pid > 1) {
3311d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
3312d522f475Smrg    }
3313d522f475Smrg    Exit(code);
3314d522f475Smrg}
3315d522f475Smrg
3316d522f475Smrg#ifndef VMS
3317d522f475Smrgchar *
3318d522f475SmrgxtermFindShell(char *leaf, Bool warning)
3319d522f475Smrg{
3320d522f475Smrg    char *s;
3321d522f475Smrg    char *d;
3322d522f475Smrg    char *tmp;
3323d522f475Smrg    char *result = leaf;
3324d522f475Smrg
3325d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
3326d522f475Smrg    if (*result != '\0' && strchr("+/-", *result) == 0) {
3327d522f475Smrg	/* find it in $PATH */
3328d522f475Smrg	if ((s = x_getenv("PATH")) != 0) {
33290d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
3330d522f475Smrg		Bool found = False;
3331d522f475Smrg		while (*s != '\0') {
3332d522f475Smrg		    strcpy(tmp, s);
3333d522f475Smrg		    for (d = tmp;; ++d) {
3334d522f475Smrg			if (*d == ':' || *d == '\0') {
3335d522f475Smrg			    int skip = (*d != '\0');
3336d522f475Smrg			    *d = '/';
3337d522f475Smrg			    strcpy(d + 1, leaf);
3338d522f475Smrg			    if (skip)
3339d522f475Smrg				++d;
3340d522f475Smrg			    s += (d - tmp);
3341d522f475Smrg			    if (*tmp == '/'
3342d522f475Smrg				&& strstr(tmp, "..") == 0
3343d522f475Smrg				&& access(tmp, X_OK) == 0) {
3344d522f475Smrg				result = x_strdup(tmp);
3345d522f475Smrg				found = True;
3346d522f475Smrg			    }
3347d522f475Smrg			    break;
3348d522f475Smrg			}
3349d522f475Smrg			if (found)
3350d522f475Smrg			    break;
3351d522f475Smrg		    }
3352d522f475Smrg		    if (found)
3353d522f475Smrg			break;
3354d522f475Smrg		}
3355d522f475Smrg		free(tmp);
3356d522f475Smrg	    }
3357d522f475Smrg	}
3358d522f475Smrg    }
3359d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
3360d522f475Smrg    if (*result != '/'
3361d522f475Smrg	|| strstr(result, "..") != 0
3362d522f475Smrg	|| access(result, X_OK) != 0) {
3363d522f475Smrg	if (warning)
3364d522f475Smrg	    fprintf(stderr, "No absolute path found for shell: %s\n", result);
3365d522f475Smrg	result = 0;
3366d522f475Smrg    }
3367d522f475Smrg    return result;
3368d522f475Smrg}
3369d522f475Smrg#endif /* VMS */
3370d522f475Smrg
33710d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
3372d522f475Smrg
3373d522f475Smrg/*
3374d522f475Smrg * copy the environment before Setenv'ing.
3375d522f475Smrg */
3376d522f475Smrgvoid
3377d522f475SmrgxtermCopyEnv(char **oldenv)
3378d522f475Smrg{
3379d522f475Smrg    unsigned size;
3380d522f475Smrg    char **newenv;
3381d522f475Smrg
3382d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
3383d522f475Smrg	;
3384d522f475Smrg    }
3385d522f475Smrg
3386d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
3387d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
3388d522f475Smrg    environ = newenv;
3389d522f475Smrg}
3390d522f475Smrg
3391d522f475Smrg/*
3392d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
3393d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
3394d522f475Smrg * This procedure assumes the memory for the first level of environ
3395d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
3396d522f475Smrg * to have to do a realloc().
3397d522f475Smrg */
3398d522f475Smrgvoid
3399d522f475SmrgxtermSetenv(char *var, char *value)
3400d522f475Smrg{
3401d522f475Smrg    if (value != 0) {
3402d522f475Smrg	char *test;
3403d522f475Smrg	int envindex = 0;
3404d522f475Smrg	size_t len = strlen(var);
3405d522f475Smrg	int found = -1;
3406d522f475Smrg
3407d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
3408d522f475Smrg
3409d522f475Smrg	while ((test = environ[envindex]) != NULL) {
3410d522f475Smrg	    if (strncmp(test, var, len) == 0 && test[len] == '=') {
3411d522f475Smrg		found = envindex;
3412d522f475Smrg		break;
3413d522f475Smrg	    }
3414d522f475Smrg	    envindex++;
3415d522f475Smrg	}
3416d522f475Smrg
3417d522f475Smrg	if (found < 0) {
3418d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
3419d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
3420d522f475Smrg
3421d522f475Smrg	    if (need > have) {
3422d522f475Smrg		char **newenv;
3423d522f475Smrg		newenv = TypeMallocN(char *, need);
3424d522f475Smrg		if (newenv == 0) {
3425d522f475Smrg		    fprintf(stderr, "Cannot increase environment\n");
3426d522f475Smrg		    return;
3427d522f475Smrg		}
3428d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
3429d522f475Smrg		free(environ);
3430d522f475Smrg		environ = newenv;
3431d522f475Smrg	    }
3432d522f475Smrg
3433d522f475Smrg	    found = envindex;
3434d522f475Smrg	    environ[found + 1] = NULL;
3435d522f475Smrg	    environ = environ;
3436d522f475Smrg	}
3437d522f475Smrg
3438d522f475Smrg	environ[found] = CastMallocN(char, 1 + len + strlen(value));
3439d522f475Smrg	if (environ[found] == 0) {
3440d522f475Smrg	    fprintf(stderr, "Cannot allocate environment %s\n", var);
3441d522f475Smrg	    return;
3442d522f475Smrg	}
3443d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
3444d522f475Smrg    }
3445d522f475Smrg}
3446d522f475Smrg
3447d522f475Smrg/*ARGSUSED*/
3448d522f475Smrgint
3449d522f475Smrgxerror(Display * d, XErrorEvent * ev)
3450d522f475Smrg{
3451d522f475Smrg    fprintf(stderr, "%s:  warning, error event received:\n", xterm_name);
3452d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
3453d522f475Smrg    Exit(ERROR_XERROR);
3454d522f475Smrg    return 0;			/* appease the compiler */
3455d522f475Smrg}
3456d522f475Smrg
3457d522f475Smrg/*ARGSUSED*/
3458d522f475Smrgint
3459d522f475Smrgxioerror(Display * dpy)
3460d522f475Smrg{
3461d522f475Smrg    int the_error = errno;
3462d522f475Smrg
3463d522f475Smrg    (void) fprintf(stderr,
3464d522f475Smrg		   "%s:  fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
3465d522f475Smrg		   xterm_name, the_error, SysErrorMsg(the_error),
3466d522f475Smrg		   DisplayString(dpy));
3467d522f475Smrg
3468d522f475Smrg    Exit(ERROR_XIOERROR);
3469d522f475Smrg    return 0;			/* appease the compiler */
3470d522f475Smrg}
3471d522f475Smrg
3472d522f475Smrgvoid
3473d522f475Smrgxt_error(String message)
3474d522f475Smrg{
3475d522f475Smrg    (void) fprintf(stderr, "%s Xt error: %s\n", ProgramName, message);
3476d522f475Smrg
3477d522f475Smrg    /*
3478d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
3479d522f475Smrg     */
3480d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
3481d522f475Smrg	fprintf(stderr, "%s:  DISPLAY is not set\n", ProgramName);
3482d522f475Smrg    }
3483d522f475Smrg    exit(1);
3484d522f475Smrg}
3485d522f475Smrg
3486d522f475Smrgint
3487d522f475SmrgXStrCmp(char *s1, char *s2)
3488d522f475Smrg{
3489d522f475Smrg    if (s1 && s2)
3490d522f475Smrg	return (strcmp(s1, s2));
3491d522f475Smrg    if (s1 && *s1)
3492d522f475Smrg	return (1);
3493d522f475Smrg    if (s2 && *s2)
3494d522f475Smrg	return (-1);
3495d522f475Smrg    return (0);
3496d522f475Smrg}
3497d522f475Smrg
3498d522f475Smrg#if OPT_TEK4014
3499d522f475Smrgstatic void
3500d522f475Smrgwithdraw_window(Display * dpy, Window w, int scr)
3501d522f475Smrg{
3502d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
3503d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
3504d522f475Smrg    XWithdrawWindow(dpy, w, scr);
3505d522f475Smrg    return;
3506d522f475Smrg}
3507d522f475Smrg#endif
3508d522f475Smrg
3509d522f475Smrgvoid
3510d522f475Smrgset_vt_visibility(Bool on)
3511d522f475Smrg{
3512d522f475Smrg    TScreen *screen = TScreenOf(term);
3513d522f475Smrg
3514d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
3515d522f475Smrg    if (on) {
3516d522f475Smrg	if (!screen->Vshow && term) {
3517d522f475Smrg	    VTInit();
3518d522f475Smrg	    XtMapWidget(XtParent(term));
3519d522f475Smrg#if OPT_TOOLBAR
3520d522f475Smrg	    /* we need both of these during initialization */
3521d522f475Smrg	    XtMapWidget(SHELL_OF(term));
3522d522f475Smrg	    ShowToolbar(resource.toolBar);
3523d522f475Smrg#endif
3524d522f475Smrg	    screen->Vshow = True;
3525d522f475Smrg	}
3526d522f475Smrg    }
3527d522f475Smrg#if OPT_TEK4014
3528d522f475Smrg    else {
3529d522f475Smrg	if (screen->Vshow && term) {
3530d522f475Smrg	    withdraw_window(XtDisplay(term),
3531d522f475Smrg			    VShellWindow,
3532d522f475Smrg			    XScreenNumberOfScreen(XtScreen(term)));
3533d522f475Smrg	    screen->Vshow = False;
3534d522f475Smrg	}
3535d522f475Smrg    }
3536d522f475Smrg    set_vthide_sensitivity();
3537d522f475Smrg    set_tekhide_sensitivity();
3538d522f475Smrg    update_vttekmode();
3539d522f475Smrg    update_tekshow();
3540d522f475Smrg    update_vtshow();
3541d522f475Smrg#endif
3542d522f475Smrg    return;
3543d522f475Smrg}
3544d522f475Smrg
3545d522f475Smrg#if OPT_TEK4014
3546d522f475Smrgvoid
3547d522f475Smrgset_tek_visibility(Bool on)
3548d522f475Smrg{
3549d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
3550d522f475Smrg
3551d522f475Smrg    if (on) {
3552d522f475Smrg	if (!TEK4014_SHOWN(term) && (tekWidget || TekInit())) {
3553d522f475Smrg	    Widget tekParent = SHELL_OF(tekWidget);
3554d522f475Smrg	    XtRealizeWidget(tekParent);
3555d522f475Smrg	    XtMapWidget(XtParent(tekWidget));
3556d522f475Smrg#if OPT_TOOLBAR
3557d522f475Smrg	    /* we need both of these during initialization */
3558d522f475Smrg	    XtMapWidget(tekParent);
3559d522f475Smrg	    XtMapWidget(tekWidget);
3560d522f475Smrg#endif
3561d522f475Smrg	    XtOverrideTranslations(tekParent,
3562d522f475Smrg				   XtParseTranslationTable
3563d522f475Smrg				   ("<Message>WM_PROTOCOLS: DeleteWindow()"));
3564d522f475Smrg	    (void) XSetWMProtocols(XtDisplay(tekParent),
3565d522f475Smrg				   XtWindow(tekParent),
3566d522f475Smrg				   &wm_delete_window, 1);
3567d522f475Smrg	    TEK4014_SHOWN(term) = True;
3568d522f475Smrg	}
3569d522f475Smrg    } else {
3570d522f475Smrg	if (TEK4014_SHOWN(term) && tekWidget) {
3571d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
3572d522f475Smrg			    TShellWindow,
3573d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
3574d522f475Smrg	    TEK4014_SHOWN(term) = False;
3575d522f475Smrg	}
3576d522f475Smrg    }
3577d522f475Smrg    set_tekhide_sensitivity();
3578d522f475Smrg    set_vthide_sensitivity();
3579d522f475Smrg    update_vtshow();
3580d522f475Smrg    update_tekshow();
3581d522f475Smrg    update_vttekmode();
3582d522f475Smrg    return;
3583d522f475Smrg}
3584d522f475Smrg
3585d522f475Smrgvoid
3586d522f475Smrgend_tek_mode(void)
3587d522f475Smrg{
3588d522f475Smrg    if (TEK4014_ACTIVE(term)) {
3589d522f475Smrg	FlushLog(&(term->screen));
3590d522f475Smrg	longjmp(Tekend, 1);
3591d522f475Smrg    }
3592d522f475Smrg    return;
3593d522f475Smrg}
3594d522f475Smrg
3595d522f475Smrgvoid
3596d522f475Smrgend_vt_mode(void)
3597d522f475Smrg{
3598d522f475Smrg    if (!TEK4014_ACTIVE(term)) {
3599d522f475Smrg	FlushLog(&(term->screen));
3600d522f475Smrg	TEK4014_ACTIVE(term) = True;
3601d522f475Smrg	longjmp(VTend, 1);
3602d522f475Smrg    }
3603d522f475Smrg    return;
3604d522f475Smrg}
3605d522f475Smrg
3606d522f475Smrgvoid
3607d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
3608d522f475Smrg{
3609d522f475Smrg    if (tovt) {
3610d522f475Smrg	if (tekRefreshList)
3611d522f475Smrg	    TekRefresh(tekWidget);
3612d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
3613d522f475Smrg    } else {
3614d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
3615d522f475Smrg    }
3616d522f475Smrg}
3617d522f475Smrg
3618d522f475Smrgvoid
3619d522f475Smrghide_vt_window(void)
3620d522f475Smrg{
3621d522f475Smrg    set_vt_visibility(False);
3622d522f475Smrg    if (!TEK4014_ACTIVE(term))
3623d522f475Smrg	switch_modes(False);	/* switch to tek mode */
3624d522f475Smrg}
3625d522f475Smrg
3626d522f475Smrgvoid
3627d522f475Smrghide_tek_window(void)
3628d522f475Smrg{
3629d522f475Smrg    set_tek_visibility(False);
3630d522f475Smrg    tekRefreshList = (TekLink *) 0;
3631d522f475Smrg    if (TEK4014_ACTIVE(term))
3632d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
3633d522f475Smrg}
3634d522f475Smrg#endif /* OPT_TEK4014 */
3635d522f475Smrg
3636d522f475Smrgstatic const char *
3637d522f475Smrgskip_punct(const char *s)
3638d522f475Smrg{
3639d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
3640d522f475Smrg	++s;
3641d522f475Smrg    }
3642d522f475Smrg    return s;
3643d522f475Smrg}
3644d522f475Smrg
3645d522f475Smrgstatic int
3646d522f475Smrgcmp_options(const void *a, const void *b)
3647d522f475Smrg{
3648d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
3649d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
3650d522f475Smrg    return strcmp(s1, s2);
3651d522f475Smrg}
3652d522f475Smrg
3653d522f475Smrgstatic int
3654d522f475Smrgcmp_resources(const void *a, const void *b)
3655d522f475Smrg{
3656d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
3657d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
3658d522f475Smrg}
3659d522f475Smrg
3660d522f475SmrgXrmOptionDescRec *
3661d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
3662d522f475Smrg{
3663d522f475Smrg    static XrmOptionDescRec *res_array = 0;
3664d522f475Smrg
3665d522f475Smrg#ifdef NO_LEAKS
3666d522f475Smrg    if (descs == 0 && res_array != 0) {
3667d522f475Smrg	free(res_array);
3668d522f475Smrg	res_array = 0;
3669d522f475Smrg    } else
3670d522f475Smrg#endif
3671d522f475Smrg    if (res_array == 0) {
3672d522f475Smrg	Cardinal j;
3673d522f475Smrg
3674d522f475Smrg	/* make a sorted index to 'resources' */
3675d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
3676d522f475Smrg	for (j = 0; j < res_count; j++)
3677d522f475Smrg	    res_array[j] = descs[j];
3678d522f475Smrg	qsort(res_array, res_count, sizeof(*res_array), cmp_resources);
3679d522f475Smrg    }
3680d522f475Smrg    return res_array;
3681d522f475Smrg}
3682d522f475Smrg
3683d522f475Smrg/*
3684d522f475Smrg * The first time this is called, construct sorted index to the main program's
3685d522f475Smrg * list of options, taking into account the on/off options which will be
3686d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
3687d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
3688d522f475Smrg */
3689d522f475SmrgOptionHelp *
3690d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
3691d522f475Smrg{
3692d522f475Smrg    static OptionHelp *opt_array = 0;
3693d522f475Smrg
3694d522f475Smrg#ifdef NO_LEAKS
3695d522f475Smrg    if (descs == 0 && opt_array != 0) {
3696d522f475Smrg	sortedOptDescs(descs, numDescs);
3697d522f475Smrg	free(opt_array);
3698d522f475Smrg	opt_array = 0;
3699d522f475Smrg	return 0;
3700d522f475Smrg    } else if (options == 0 || descs == 0) {
3701d522f475Smrg	return 0;
3702d522f475Smrg    }
3703d522f475Smrg#endif
3704d522f475Smrg
3705d522f475Smrg    if (opt_array == 0) {
3706d522f475Smrg	Cardinal opt_count, j;
3707d522f475Smrg#if OPT_TRACE
3708d522f475Smrg	Cardinal k;
3709d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
3710d522f475Smrg	int code;
3711d522f475Smrg	char *mesg;
3712d522f475Smrg#else
3713d522f475Smrg	(void) descs;
3714d522f475Smrg	(void) numDescs;
3715d522f475Smrg#endif
3716d522f475Smrg
3717d522f475Smrg	/* count 'options' and make a sorted index to it */
3718d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
3719d522f475Smrg	    ;
3720d522f475Smrg	}
3721d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
3722d522f475Smrg	for (j = 0; j < opt_count; j++)
3723d522f475Smrg	    opt_array[j] = options[j];
3724d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
3725d522f475Smrg
3726d522f475Smrg	/* supply the "turn on/off" strings if needed */
3727d522f475Smrg#if OPT_TRACE
3728d522f475Smrg	for (j = 0; j < opt_count; j++) {
3729d522f475Smrg	    if (!strncmp(opt_array[j].opt, "-/+", 3)) {
3730d522f475Smrg		char *name = opt_array[j].opt + 3;
3731d522f475Smrg		for (k = 0; k < numDescs; ++k) {
3732d522f475Smrg		    char *value = res_array[k].value;
3733d522f475Smrg		    if (res_array[k].option[0] == '-') {
3734d522f475Smrg			code = -1;
3735d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
3736d522f475Smrg			code = 1;
3737d522f475Smrg		    } else {
3738d522f475Smrg			code = 0;
3739d522f475Smrg		    }
3740d522f475Smrg		    if (x_strindex(opt_array[j].desc, "inhibit") != 0)
3741d522f475Smrg			code = -code;
3742d522f475Smrg		    if (code != 0
3743d522f475Smrg			&& res_array[k].value != 0
3744d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
3745d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
3746d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
3747d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
3748d522f475Smrg			    mesg = "turn on/off";
3749d522f475Smrg			} else {
3750d522f475Smrg			    mesg = "turn off/on";
3751d522f475Smrg			}
3752d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
3753d522f475Smrg			    if (strncmp(opt_array[j].desc, "turn ", 5)) {
3754d522f475Smrg				char *s = CastMallocN(char,
3755d522f475Smrg						      strlen(mesg)
3756d522f475Smrg						      + 1
3757d522f475Smrg						      + strlen(opt_array[j].desc));
3758d522f475Smrg				if (s != 0) {
3759d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
3760d522f475Smrg				    opt_array[j].desc = s;
3761d522f475Smrg				}
3762d522f475Smrg			    } else {
3763d522f475Smrg				TRACE(("OOPS "));
3764d522f475Smrg			    }
3765d522f475Smrg			}
3766d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
3767d522f475Smrg			       mesg,
3768d522f475Smrg			       res_array[k].option,
3769d522f475Smrg			       res_array[k].value,
3770d522f475Smrg			       opt_array[j].opt,
3771d522f475Smrg			       opt_array[j].desc));
3772d522f475Smrg			break;
3773d522f475Smrg		    }
3774d522f475Smrg		}
3775d522f475Smrg	    }
3776d522f475Smrg	}
3777d522f475Smrg#endif
3778d522f475Smrg    }
3779d522f475Smrg    return opt_array;
3780d522f475Smrg}
3781d522f475Smrg
3782d522f475Smrg/*
3783d522f475Smrg * Report the character-type locale that xterm was started in.
3784d522f475Smrg */
3785d522f475Smrgchar *
3786d522f475SmrgxtermEnvLocale(void)
3787d522f475Smrg{
3788d522f475Smrg    static char *result;
3789d522f475Smrg
3790d522f475Smrg    if (result == 0) {
3791d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
3792d522f475Smrg	    result = "C";
3793d522f475Smrg	}
3794d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
3795d522f475Smrg    }
3796d522f475Smrg    return result;
3797d522f475Smrg}
3798d522f475Smrg
3799d522f475Smrgchar *
3800d522f475SmrgxtermEnvEncoding(void)
3801d522f475Smrg{
3802d522f475Smrg    static char *result;
3803d522f475Smrg
3804d522f475Smrg    if (result == 0) {
3805d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
3806d522f475Smrg	result = nl_langinfo(CODESET);
3807d522f475Smrg#else
3808d522f475Smrg	char *locale = xtermEnvLocale();
3809d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
3810d522f475Smrg	    result = "ASCII";
3811d522f475Smrg	} else {
3812d522f475Smrg	    result = "ISO-8859-1";
3813d522f475Smrg	}
3814d522f475Smrg#endif
3815d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
3816d522f475Smrg    }
3817d522f475Smrg    return result;
3818d522f475Smrg}
3819d522f475Smrg
3820d522f475Smrg#if OPT_WIDE_CHARS
3821d522f475Smrg/*
3822d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
3823d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
3824d522f475Smrg * various library calls.
3825d522f475Smrg */
3826d522f475SmrgBool
3827d522f475SmrgxtermEnvUTF8(void)
3828d522f475Smrg{
3829d522f475Smrg    static Bool init = False;
3830d522f475Smrg    static Bool result = False;
3831d522f475Smrg
3832d522f475Smrg    if (!init) {
3833d522f475Smrg	init = True;
3834d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
3835d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
3836d522f475Smrg#else
3837d522f475Smrg	result = (strstr(xtermEnvLocale(), "UTF-8") != NULL);
3838d522f475Smrg#endif
3839d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
3840d522f475Smrg    }
3841d522f475Smrg    return result;
3842d522f475Smrg}
3843d522f475Smrg#endif /* OPT_WIDE_CHARS */
3844d522f475Smrg
3845d522f475Smrg/*
3846d522f475Smrg * Returns the version-string used in the "-v' message as well as a few other
3847d522f475Smrg * places.  It is derived (when possible) from the __vendorversion__ symbol
3848d522f475Smrg * that some newer imake configurations define.
3849d522f475Smrg */
3850d522f475Smrgchar *
3851d522f475SmrgxtermVersion(void)
3852d522f475Smrg{
3853d522f475Smrg    static char *result;
3854d522f475Smrg    if (result == 0) {
3855d522f475Smrg	char *vendor = __vendorversion__;
3856d522f475Smrg	char first[BUFSIZ];
3857d522f475Smrg	char second[BUFSIZ];
3858d522f475Smrg
3859d522f475Smrg	result = CastMallocN(char, strlen(vendor) + 9);
3860d522f475Smrg	if (result == 0)
3861d522f475Smrg	    result = vendor;
3862d522f475Smrg	else {
3863d522f475Smrg	    /* some vendors leave trash in this string */
3864d522f475Smrg	    for (;;) {
3865d522f475Smrg		if (!strncmp(vendor, "Version ", 8))
3866d522f475Smrg		    vendor += 8;
3867d522f475Smrg		else if (isspace(CharOf(*vendor)))
3868d522f475Smrg		    ++vendor;
3869d522f475Smrg		else
3870d522f475Smrg		    break;
3871d522f475Smrg	    }
3872d522f475Smrg	    if (strlen(vendor) < BUFSIZ &&
3873d522f475Smrg		sscanf(vendor, "%[0-9.] %[A-Za-z_0-9.]", first, second) == 2)
3874d522f475Smrg		sprintf(result, "%s %s(%d)", second, first, XTERM_PATCH);
3875d522f475Smrg	    else
3876d522f475Smrg		sprintf(result, "%s(%d)", vendor, XTERM_PATCH);
3877d522f475Smrg	}
3878d522f475Smrg    }
3879d522f475Smrg    return result;
3880d522f475Smrg}
3881