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