root/foundation-apps/mxterm-maxx/misc.c

Revision 8, 90.5 KB (checked in by emasson, 3 years ago)

initial import for the community edition

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