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