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