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