1 /* $XTermId: misc.c,v 1.1007 2021/11/12 09:28:19 tom Exp $ */
2 
3 /*
4  * Copyright 1999-2020,2021 by Thomas E. Dickey
5  *
6  *                         All Rights Reserved
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sublicense, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject to
14  * the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Except as contained in this notice, the name(s) of the above copyright
28  * holders shall not be used in advertising or otherwise to promote the
29  * sale, use or other dealings in this Software without prior written
30  * authorization.
31  *
32  *
33  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34  *
35  *                         All Rights Reserved
36  *
37  * Permission to use, copy, modify, and distribute this software and its
38  * documentation for any purpose and without fee is hereby granted,
39  * provided that the above copyright notice appear in all copies and that
40  * both that copyright notice and this permission notice appear in
41  * supporting documentation, and that the name of Digital Equipment
42  * Corporation not be used in advertising or publicity pertaining to
43  * distribution of the software without specific, written prior permission.
44  *
45  *
46  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52  * SOFTWARE.
53  */
54 
55 #include <version.h>
56 #include <main.h>
57 #include <xterm.h>
58 #include <xterm_io.h>
59 
60 #include <sys/stat.h>
61 #include <stdio.h>
62 #include <stdarg.h>
63 #include <signal.h>
64 #include <ctype.h>
65 #include <pwd.h>
66 #include <sys/wait.h>
67 
68 #include <X11/keysym.h>
69 #include <X11/Xatom.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_LIBXPM
80 #include <X11/xpm.h>
81 #endif
82 
83 #ifdef HAVE_LANGINFO_CODESET
84 #include <langinfo.h>
85 #endif
86 
87 #include <xutf8.h>
88 
89 #include <data.h>
90 #include <error.h>
91 #include <menu.h>
92 #include <fontutils.h>
93 #include <xstrings.h>
94 #include <xtermcap.h>
95 #include <VTparse.h>
96 #include <graphics.h>
97 #include <graphics_regis.h>
98 #include <graphics_sixel.h>
99 
100 #include <assert.h>
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 USE_DOUBLE_BUFFER
110 #include <X11/extensions/Xdbe.h>
111 #endif
112 
113 #if OPT_WIDE_CHARS
114 #include <wctype.h>
115 #endif
116 
117 #if OPT_TEK4014
118 #define OUR_EVENT(event,Type) \
119 		(event.type == Type && \
120 		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
121 		    (tekWidget && \
122 		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
123 #else
124 #define OUR_EVENT(event,Type) \
125 		(event.type == Type && \
126 		   (event.xcrossing.window == XtWindow(XtParent(xw))))
127 #endif
128 
129 #define VB_DELAY    screen->visualBellDelay
130 #define EVENT_DELAY TScreenOf(term)->nextEventDelay
131 
132 static Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
133 static Cursor make_hidden_cursor(XtermWidget);
134 
135 static char emptyString[] = "";
136 
137 #if OPT_EXEC_XTERM
138 /* Like readlink(2), but returns a malloc()ed buffer, or NULL on
139    error; adapted from libc docs */
140 static char *
Readlink(const char * filename)141 Readlink(const char *filename)
142 {
143     char *buf = NULL;
144     size_t size = 100;
145 
146     for (;;) {
147 	int n;
148 	char *tmp = TypeRealloc(char, size, buf);
149 	if (tmp == NULL) {
150 	    free(buf);
151 	    return NULL;
152 	}
153 	buf = tmp;
154 	memset(buf, 0, size);
155 
156 	n = (int) readlink(filename, buf, size);
157 	if (n < 0) {
158 	    free(buf);
159 	    return NULL;
160 	}
161 
162 	if ((unsigned) n < size) {
163 	    return buf;
164 	}
165 
166 	size *= 2;
167     }
168 }
169 #endif /* OPT_EXEC_XTERM */
170 
171 static void
Sleep(int msec)172 Sleep(int msec)
173 {
174     static struct timeval select_timeout;
175 
176     select_timeout.tv_sec = 0;
177     select_timeout.tv_usec = msec * 1000;
178     select(0, 0, 0, 0, &select_timeout);
179 }
180 
181 static void
selectwindow(XtermWidget xw,int flag)182 selectwindow(XtermWidget xw, int flag)
183 {
184     TScreen *screen = TScreenOf(xw);
185 
186     TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
187 
188 #if OPT_TEK4014
189     if (TEK4014_ACTIVE(xw)) {
190 	if (!Ttoggled)
191 	    TCursorToggle(tekWidget, TOGGLE);
192 	screen->select |= flag;
193 	if (!Ttoggled)
194 	    TCursorToggle(tekWidget, TOGGLE);
195     } else
196 #endif
197     {
198 #if OPT_INPUT_METHOD
199 	TInput *input = lookupTInput(xw, (Widget) xw);
200 	if (input && input->xic)
201 	    XSetICFocus(input->xic);
202 #endif
203 
204 	if (screen->cursor_state && CursorMoved(screen))
205 	    HideCursor(xw);
206 	screen->select |= flag;
207 	if (screen->cursor_state)
208 	    ShowCursor(xw);
209     }
210     GetScrollLock(screen);
211 }
212 
213 static void
unselectwindow(XtermWidget xw,int flag)214 unselectwindow(XtermWidget xw, int flag)
215 {
216     TScreen *screen = TScreenOf(xw);
217 
218     TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
219 
220     if (screen->hide_pointer && screen->pointer_mode < pFocused) {
221 	screen->hide_pointer = False;
222 	xtermDisplayPointer(xw);
223     }
224 
225     screen->select &= ~flag;
226 
227     if (!screen->always_highlight) {
228 #if OPT_TEK4014
229 	if (TEK4014_ACTIVE(xw)) {
230 	    if (!Ttoggled)
231 		TCursorToggle(tekWidget, TOGGLE);
232 	    if (!Ttoggled)
233 		TCursorToggle(tekWidget, TOGGLE);
234 	} else
235 #endif
236 	{
237 #if OPT_INPUT_METHOD
238 	    TInput *input = lookupTInput(xw, (Widget) xw);
239 	    if (input && input->xic)
240 		XUnsetICFocus(input->xic);
241 #endif
242 
243 	    if (screen->cursor_state && CursorMoved(screen))
244 		HideCursor(xw);
245 	    if (screen->cursor_state)
246 		ShowCursor(xw);
247 	}
248     }
249 }
250 
251 static void
DoSpecialEnterNotify(XtermWidget xw,XEnterWindowEvent * ev)252 DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
253 {
254     TScreen *screen = TScreenOf(xw);
255 
256     TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
257     TRACE_FOCUS(xw, ev);
258     if (((ev->detail) != NotifyInferior) &&
259 	ev->focus &&
260 	!(screen->select & FOCUS))
261 	selectwindow(xw, INWINDOW);
262 }
263 
264 static void
DoSpecialLeaveNotify(XtermWidget xw,XEnterWindowEvent * ev)265 DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
266 {
267     TScreen *screen = TScreenOf(xw);
268 
269     TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
270     TRACE_FOCUS(xw, ev);
271     if (((ev->detail) != NotifyInferior) &&
272 	ev->focus &&
273 	!(screen->select & FOCUS))
274 	unselectwindow(xw, INWINDOW);
275 }
276 
277 #ifndef XUrgencyHint
278 #define XUrgencyHint (1L << 8)	/* X11R5 does not define */
279 #endif
280 
281 static void
setXUrgency(XtermWidget xw,Bool enable)282 setXUrgency(XtermWidget xw, Bool enable)
283 {
284     TScreen *screen = TScreenOf(xw);
285 
286     if (screen->bellIsUrgent) {
287 	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
288 	if (h != 0) {
289 	    if (enable && !(screen->select & FOCUS)) {
290 		h->flags |= XUrgencyHint;
291 	    } else {
292 		h->flags &= ~XUrgencyHint;
293 	    }
294 	    XSetWMHints(screen->display, VShellWindow(xw), h);
295 	}
296     }
297 }
298 
299 void
do_xevents(XtermWidget xw)300 do_xevents(XtermWidget xw)
301 {
302     TScreen *screen = TScreenOf(xw);
303 
304     if (xtermAppPending()
305 	||
306 #if defined(VMS) || defined(__VMS)
307 	screen->display->qlen > 0
308 #else
309 	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
310 #endif
311 	)
312 	xevents(xw);
313 }
314 
315 void
xtermDisplayPointer(XtermWidget xw)316 xtermDisplayPointer(XtermWidget xw)
317 {
318     TScreen *screen = TScreenOf(xw);
319 
320     if (screen->Vshow) {
321 	if (screen->hide_pointer) {
322 	    TRACE(("Display text pointer (hidden)\n"));
323 	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
324 	} else {
325 	    TRACE(("Display text pointer (visible)\n"));
326 	    recolor_cursor(screen,
327 			   screen->pointer_cursor,
328 			   T_COLOR(screen, MOUSE_FG),
329 			   T_COLOR(screen, MOUSE_BG));
330 	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
331 	}
332     }
333 }
334 
335 void
xtermShowPointer(XtermWidget xw,Bool enable)336 xtermShowPointer(XtermWidget xw, Bool enable)
337 {
338     static int tried = -1;
339     TScreen *screen = TScreenOf(xw);
340 
341 #if OPT_TEK4014
342     if (TEK4014_SHOWN(xw))
343 	enable = True;
344 #endif
345 
346     /*
347      * Whether we actually hide the pointer depends on the pointer-mode and
348      * the mouse-mode:
349      */
350     if (!enable) {
351 	switch (screen->pointer_mode) {
352 	case pNever:
353 	    enable = True;
354 	    break;
355 	case pNoMouse:
356 	    if (screen->send_mouse_pos != MOUSE_OFF)
357 		enable = True;
358 	    break;
359 	case pAlways:
360 	case pFocused:
361 	    break;
362 	}
363     }
364 
365     if (enable) {
366 	if (screen->hide_pointer) {
367 	    screen->hide_pointer = False;
368 	    xtermDisplayPointer(xw);
369 	    switch (screen->send_mouse_pos) {
370 	    case ANY_EVENT_MOUSE:
371 		break;
372 	    default:
373 		MotionOff(screen, xw);
374 		break;
375 	    }
376 	}
377     } else if (!(screen->hide_pointer) && (tried <= 0)) {
378 	if (screen->hidden_cursor == 0) {
379 	    screen->hidden_cursor = make_hidden_cursor(xw);
380 	}
381 	if (screen->hidden_cursor == 0) {
382 	    tried = 1;
383 	} else {
384 	    tried = 0;
385 	    screen->hide_pointer = True;
386 	    xtermDisplayPointer(xw);
387 	    MotionOn(screen, xw);
388 	}
389     }
390 }
391 
392 /* true if p contains q */
393 #define ExposeContains(p,q) \
394 	    ((p)->y <= (q)->y \
395 	  && (p)->x <= (q)->x \
396 	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
397 	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
398 
399 static XtInputMask
mergeExposeEvents(XEvent * target)400 mergeExposeEvents(XEvent *target)
401 {
402     XEvent next_event;
403     XExposeEvent *p;
404 
405     XtAppNextEvent(app_con, target);
406     p = (XExposeEvent *) target;
407 
408     while (XtAppPending(app_con)
409 	   && XtAppPeekEvent(app_con, &next_event)
410 	   && next_event.type == Expose) {
411 	Boolean merge_this = False;
412 	XExposeEvent *q = (XExposeEvent *) (&next_event);
413 
414 	XtAppNextEvent(app_con, &next_event);
415 	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
416 
417 	/*
418 	 * If either window is contained within the other, merge the events.
419 	 * The traces show that there are also cases where a full repaint of
420 	 * a window is broken into 3 or more rectangles, which do not arrive
421 	 * in the same instant.  We could merge those if xterm were modified
422 	 * to skim several events ahead.
423 	 */
424 	if (p->window == q->window) {
425 	    if (ExposeContains(p, q)) {
426 		TRACE(("pending Expose...merged forward\n"));
427 		merge_this = True;
428 		next_event = *target;
429 	    } else if (ExposeContains(q, p)) {
430 		TRACE(("pending Expose...merged backward\n"));
431 		merge_this = True;
432 	    }
433 	}
434 	if (!merge_this) {
435 	    XtDispatchEvent(target);
436 	}
437 	*target = next_event;
438     }
439     XtDispatchEvent(target);
440     return XtAppPending(app_con);
441 }
442 
443 /*
444  * On entry, we have peeked at the event queue and see a configure-notify
445  * event.  Remove that from the queue so we can look further.
446  *
447  * Then, as long as there is a configure-notify event in the queue, remove
448  * that.  If the adjacent events are for different windows, process the older
449  * event and update the event used for comparing windows.  If they are for the
450  * same window, only the newer event is of interest.
451  *
452  * Finally, process the (remaining) configure-notify event.
453  */
454 static XtInputMask
mergeConfigureEvents(XEvent * target)455 mergeConfigureEvents(XEvent *target)
456 {
457     XEvent next_event;
458     XConfigureEvent *p;
459 
460     XtAppNextEvent(app_con, target);
461     p = (XConfigureEvent *) target;
462 
463     if (XtAppPending(app_con)
464 	&& XtAppPeekEvent(app_con, &next_event)
465 	&& next_event.type == ConfigureNotify) {
466 	Boolean merge_this = False;
467 	XConfigureEvent *q = (XConfigureEvent *) (&next_event);
468 
469 	XtAppNextEvent(app_con, &next_event);
470 	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
471 
472 	if (p->window == q->window) {
473 	    TRACE(("pending Configure...merged\n"));
474 	    merge_this = True;
475 	}
476 	if (!merge_this) {
477 	    TRACE(("pending Configure...skipped\n"));
478 	    XtDispatchEvent(target);
479 	}
480 	*target = next_event;
481     }
482     XtDispatchEvent(target);
483     return XtAppPending(app_con);
484 }
485 
486 #define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
487 #define SameButtonEvent(a,b) ( \
488 	SAME(a,b,type) && \
489 	SAME(a,b,serial) && \
490 	SAME(a,b,send_event) && \
491 	SAME(a,b,display) && \
492 	SAME(a,b,window) && \
493 	SAME(a,b,root) && \
494 	SAME(a,b,subwindow) && \
495 	SAME(a,b,time) && \
496 	SAME(a,b,x) && \
497 	SAME(a,b,y) && \
498 	SAME(a,b,x_root) && \
499 	SAME(a,b,y_root) && \
500 	SAME(a,b,state) && \
501 	SAME(a,b,button) && \
502 	SAME(a,b,same_screen))
503 
504 /*
505  * Work around a bug in the X mouse code, which delivers duplicate events.
506  */
507 static XtInputMask
mergeButtonEvents(XEvent * target)508 mergeButtonEvents(XEvent *target)
509 {
510     XEvent next_event;
511     XButtonEvent *p;
512 
513     XtAppNextEvent(app_con, target);
514     p = (XButtonEvent *) target;
515 
516     if (XtAppPending(app_con)
517 	&& XtAppPeekEvent(app_con, &next_event)
518 	&& SameButtonEvent(target, &next_event)) {
519 	Boolean merge_this = False;
520 	XButtonEvent *q = (XButtonEvent *) (&next_event);
521 
522 	XtAppNextEvent(app_con, &next_event);
523 	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
524 
525 	if (p->window == q->window) {
526 	    TRACE(("pending ButtonEvent...merged\n"));
527 	    merge_this = True;
528 	}
529 	if (!merge_this) {
530 	    TRACE(("pending ButtonEvent...skipped\n"));
531 	    XtDispatchEvent(target);
532 	}
533 	*target = next_event;
534     }
535     XtDispatchEvent(target);
536     return XtAppPending(app_con);
537 }
538 
539 /*
540  * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
541  * adjacent events because there could be other event-loop processing.  Absent
542  * that limitation, it might be possible to scan ahead to find when the screen
543  * would be completely updated, skipping unnecessary re-repainting before that
544  * point.
545  *
546  * Note: all cases should allow doing XtAppNextEvent if result is true.
547  */
548 XtInputMask
xtermAppPending(void)549 xtermAppPending(void)
550 {
551     XtInputMask result = XtAppPending(app_con);
552     XEvent this_event;
553     Boolean found = False;
554 
555     while (result && XtAppPeekEvent(app_con, &this_event)) {
556 	found = True;
557 	TRACE_EVENT("pending", &this_event, (String *) 0, 0);
558 	if (this_event.type == Expose) {
559 	    result = mergeExposeEvents(&this_event);
560 	} else if (this_event.type == ConfigureNotify) {
561 	    result = mergeConfigureEvents(&this_event);
562 	} else if (this_event.type == ButtonPress ||
563 		   this_event.type == ButtonRelease) {
564 	    result = mergeButtonEvents(&this_event);
565 	} else {
566 	    break;
567 	}
568     }
569 
570     /*
571      * With NetBSD, closing a shell results in closing the X input event
572      * stream, which interferes with the "-hold" option.  Wait a short time in
573      * this case, to avoid max'ing the CPU.
574      */
575     if (hold_screen && caught_intr && !found) {
576 	Sleep(EVENT_DELAY);
577     }
578     return result;
579 }
580 
581 void
xevents(XtermWidget xw)582 xevents(XtermWidget xw)
583 {
584     TScreen *screen = TScreenOf(xw);
585     XEvent event;
586     XtInputMask input_mask;
587 
588     if (need_cleanup)
589 	NormalExit();
590 
591     if (screen->scroll_amt)
592 	FlushScroll(xw);
593     /*
594      * process timeouts, relying on the fact that XtAppProcessEvent
595      * will process the timeout and return without blockng on the
596      * XEvent queue.  Other sources i.e., the pty are handled elsewhere
597      * with select().
598      */
599     while ((input_mask = xtermAppPending()) != 0) {
600 	if (input_mask & XtIMTimer)
601 	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
602 #if OPT_SESSION_MGT
603 	/*
604 	 * Session management events are alternative input events. Deal with
605 	 * them in the same way.
606 	 */
607 	else if (input_mask & XtIMAlternateInput)
608 	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
609 #endif
610 	else
611 	    break;
612     }
613 
614     /*
615      * If there are no XEvents, don't wait around...
616      */
617     if ((input_mask & XtIMXEvent) != XtIMXEvent)
618 	return;
619     do {
620 	/*
621 	 * This check makes xterm hang when in mouse hilite tracking mode.
622 	 * We simply ignore all events except for those not passed down to
623 	 * this function, e.g., those handled in in_put().
624 	 */
625 	if (screen->waitingForTrackInfo) {
626 	    Sleep(EVENT_DELAY);
627 	    return;
628 	}
629 	XtAppNextEvent(app_con, &event);
630 	/*
631 	 * Hack to get around problems with the toolkit throwing away
632 	 * eventing during the exclusive grab of the menu popup.  By
633 	 * looking at the event ourselves we make sure that we can
634 	 * do the right thing.
635 	 */
636 	if (OUR_EVENT(event, EnterNotify)) {
637 	    DoSpecialEnterNotify(xw, &event.xcrossing);
638 	} else if (OUR_EVENT(event, LeaveNotify)) {
639 	    DoSpecialLeaveNotify(xw, &event.xcrossing);
640 	} else if (event.xany.type == MotionNotify
641 		   && event.xcrossing.window == XtWindow(xw)) {
642 	    switch (screen->send_mouse_pos) {
643 	    case ANY_EVENT_MOUSE:
644 #if OPT_DEC_LOCATOR
645 	    case DEC_LOCATOR:
646 #endif /* OPT_DEC_LOCATOR */
647 		SendMousePosition(xw, &event);
648 		xtermShowPointer(xw, True);
649 		continue;
650 	    case BTN_EVENT_MOUSE:
651 		SendMousePosition(xw, &event);
652 		xtermShowPointer(xw, True);
653 	    }
654 	}
655 
656 	/*
657 	 * If the event is interesting (and not a keyboard event), turn the
658 	 * mouse pointer back on.
659 	 */
660 	if (screen->hide_pointer) {
661 	    if (screen->pointer_mode >= pFocused) {
662 		switch (event.xany.type) {
663 		case MotionNotify:
664 		    xtermShowPointer(xw, True);
665 		    break;
666 		}
667 	    } else {
668 		switch (event.xany.type) {
669 		case KeyPress:
670 		case KeyRelease:
671 		case ButtonPress:
672 		case ButtonRelease:
673 		    /* also these... */
674 		case Expose:
675 		case GraphicsExpose:
676 		case NoExpose:
677 		case PropertyNotify:
678 		case ClientMessage:
679 		    break;
680 		default:
681 		    xtermShowPointer(xw, True);
682 		    break;
683 		}
684 	    }
685 	}
686 
687 	if (!event.xany.send_event ||
688 	    screen->allowSendEvents ||
689 	    ((event.xany.type != KeyPress) &&
690 	     (event.xany.type != KeyRelease) &&
691 	     (event.xany.type != ButtonPress) &&
692 	     (event.xany.type != ButtonRelease))) {
693 
694 	    if (event.xany.type == MappingNotify) {
695 		XRefreshKeyboardMapping(&(event.xmapping));
696 		VTInitModifiers(xw);
697 	    }
698 	    XtDispatchEvent(&event);
699 	}
700     } while (xtermAppPending() & XtIMXEvent);
701 }
702 
703 static Cursor
make_hidden_cursor(XtermWidget xw)704 make_hidden_cursor(XtermWidget xw)
705 {
706     TScreen *screen = TScreenOf(xw);
707     Cursor c;
708     Display *dpy = screen->display;
709     XFontStruct *fn;
710 
711     static XColor dummy;
712 
713     /*
714      * Prefer nil2 (which is normally available) to "fixed" (which is supposed
715      * to be "always" available), since it's a smaller glyph in case the
716      * server insists on drawing _something_.
717      */
718     TRACE(("Ask for nil2 font\n"));
719     if ((fn = xtermLoadQueryFont(xw, "nil2")) == 0) {
720 	TRACE(("...Ask for fixed font\n"));
721 	fn = xtermLoadQueryFont(xw, DEFFONT);
722     }
723 
724     if (fn != None) {
725 	/* a space character seems to work as a cursor (dots are not needed) */
726 	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
727 	XFreeFont(dpy, fn);
728     } else {
729 	c = None;
730     }
731     TRACE(("XCreateGlyphCursor ->%#lx\n", c));
732     return c;
733 }
734 
735 /*
736  * Xlib uses Xcursor to customize cursor coloring, which interferes with
737  * xterm's pointerColor resource.  Work around this by providing our own
738  * default theme.  Testing seems to show that we only have to provide this
739  * until the window is initialized.
740  */
741 #ifdef HAVE_LIB_XCURSOR
742 void
init_colored_cursor(Display * dpy)743 init_colored_cursor(Display *dpy)
744 {
745     static const char theme[] = "index.theme";
746     static const char pattern[] = "xtermXXXXXX";
747     char *env = getenv("XCURSOR_THEME");
748 
749     xterm_cursor_theme = 0;
750     /*
751      * The environment variable overrides a (possible) resource Xcursor.theme
752      */
753     if (IsEmpty(env)) {
754 	env = XGetDefault(dpy, "Xcursor", "theme");
755 	TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
756     } else {
757 	TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
758     }
759 
760     /*
761      * If neither found, provide our own default theme.
762      */
763     if (IsEmpty(env)) {
764 	const char *tmp_dir;
765 	char *filename;
766 	size_t needed;
767 
768 	TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
769 
770 	if ((tmp_dir = getenv("TMPDIR")) == 0) {
771 	    tmp_dir = P_tmpdir;
772 	}
773 	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
774 	if ((filename = malloc(needed)) != 0) {
775 	    sprintf(filename, "%s/%s", tmp_dir, pattern);
776 
777 #ifdef HAVE_MKDTEMP
778 	    xterm_cursor_theme = mkdtemp(filename);
779 #else
780 	    if (mktemp(filename) != 0
781 		&& mkdir(filename, 0700) == 0) {
782 		xterm_cursor_theme = filename;
783 	    }
784 #endif
785 	    if (xterm_cursor_theme != filename)
786 		free(filename);
787 	    /*
788 	     * Actually, Xcursor does what _we_ want just by steering its
789 	     * search path away from home.  We are setting up the complete
790 	     * theme just in case the library ever acquires a maintainer.
791 	     */
792 	    if (xterm_cursor_theme != 0) {
793 		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
794 		FILE *fp;
795 
796 		strcat(leaf, "/");
797 		strcat(leaf, theme);
798 
799 		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
800 		    fprintf(fp, "[Icon Theme]\n");
801 		    fclose(fp);
802 		    *leaf = '\0';
803 		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
804 		    *leaf = '/';
805 
806 		    TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
807 			   xterm_cursor_theme));
808 		    atexit(cleanup_colored_cursor);
809 		} else {
810 		    FreeAndNull(xterm_cursor_theme);
811 		}
812 	    }
813 	}
814     }
815 }
816 #endif /* HAVE_LIB_XCURSOR */
817 
818 /*
819  * Once done, discard the file and directory holding it.
820  */
821 void
cleanup_colored_cursor(void)822 cleanup_colored_cursor(void)
823 {
824 #ifdef HAVE_LIB_XCURSOR
825     if (xterm_cursor_theme != 0) {
826 	char *my_path = getenv("XCURSOR_PATH");
827 	struct stat sb;
828 	if (!IsEmpty(my_path)
829 	    && stat(my_path, &sb) == 0
830 	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
831 	    unlink(xterm_cursor_theme);
832 	    rmdir(my_path);
833 	}
834 	FreeAndNull(xterm_cursor_theme);
835     }
836 #endif /* HAVE_LIB_XCURSOR */
837 }
838 
839 Cursor
make_colored_cursor(unsigned c_index,unsigned long fg,unsigned long bg)840 make_colored_cursor(unsigned c_index,		/* index into font */
841 		    unsigned long fg,	/* pixel value */
842 		    unsigned long bg)	/* pixel value */
843 {
844     TScreen *screen = TScreenOf(term);
845     Cursor c = None;
846     Display *dpy = screen->display;
847 
848     TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
849     if (!IsEmpty(screen->cursor_font_name)) {
850 	static XTermFonts myFont;
851 
852 	/* adapted from XCreateFontCursor(), which hardcodes the font name */
853 	TRACE(("loading cursor from alternate cursor font\n"));
854 	myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
855 	if (myFont.fs != NULL) {
856 	    if (!xtermMissingChar(c_index, &myFont)
857 		&& !xtermMissingChar(c_index + 1, &myFont)) {
858 #define DATA(c) { 0UL, c, c, c, 0, 0 }
859 		static XColor foreground = DATA(0);
860 		static XColor background = DATA(65535);
861 #undef DATA
862 
863 		/*
864 		 * Cursor fonts follow each shape glyph with a mask glyph; so
865 		 * that character position 0 contains a shape, 1 the mask for
866 		 * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
867 		 * contains defined names for each shape.
868 		 */
869 		c = XCreateGlyphCursor(dpy,
870 				       myFont.fs->fid,	/* source_font */
871 				       myFont.fs->fid,	/* mask_font */
872 				       c_index + 0,	/* source_char */
873 				       c_index + 1,	/* mask_char */
874 				       &foreground,
875 				       &background);
876 	    }
877 	    XFreeFont(dpy, myFont.fs);
878 	}
879 	if (c == None) {
880 	    xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
881 			 c_index, screen->cursor_font_name);
882 	}
883     }
884     if (c == None)
885 	c = XCreateFontCursor(dpy, c_index);
886 
887     if (c != None) {
888 	recolor_cursor(screen, c, fg, bg);
889     }
890     return c;
891 }
892 
893 /* adapted from <X11/cursorfont.h> */
894 static int
LookupCursorShape(const char * name)895 LookupCursorShape(const char *name)
896 {
897 #define DATA(name) { XC_##name, #name }
898     static struct {
899 	int code;
900 	const char name[25];
901     } table[] = {
902 	DATA(X_cursor),
903 	    DATA(arrow),
904 	    DATA(based_arrow_down),
905 	    DATA(based_arrow_up),
906 	    DATA(boat),
907 	    DATA(bogosity),
908 	    DATA(bottom_left_corner),
909 	    DATA(bottom_right_corner),
910 	    DATA(bottom_side),
911 	    DATA(bottom_tee),
912 	    DATA(box_spiral),
913 	    DATA(center_ptr),
914 	    DATA(circle),
915 	    DATA(clock),
916 	    DATA(coffee_mug),
917 	    DATA(cross),
918 	    DATA(cross_reverse),
919 	    DATA(crosshair),
920 	    DATA(diamond_cross),
921 	    DATA(dot),
922 	    DATA(dotbox),
923 	    DATA(double_arrow),
924 	    DATA(draft_large),
925 	    DATA(draft_small),
926 	    DATA(draped_box),
927 	    DATA(exchange),
928 	    DATA(fleur),
929 	    DATA(gobbler),
930 	    DATA(gumby),
931 	    DATA(hand1),
932 	    DATA(hand2),
933 	    DATA(heart),
934 	    DATA(icon),
935 	    DATA(iron_cross),
936 	    DATA(left_ptr),
937 	    DATA(left_side),
938 	    DATA(left_tee),
939 	    DATA(leftbutton),
940 	    DATA(ll_angle),
941 	    DATA(lr_angle),
942 	    DATA(man),
943 	    DATA(middlebutton),
944 	    DATA(mouse),
945 	    DATA(pencil),
946 	    DATA(pirate),
947 	    DATA(plus),
948 	    DATA(question_arrow),
949 	    DATA(right_ptr),
950 	    DATA(right_side),
951 	    DATA(right_tee),
952 	    DATA(rightbutton),
953 	    DATA(rtl_logo),
954 	    DATA(sailboat),
955 	    DATA(sb_down_arrow),
956 	    DATA(sb_h_double_arrow),
957 	    DATA(sb_left_arrow),
958 	    DATA(sb_right_arrow),
959 	    DATA(sb_up_arrow),
960 	    DATA(sb_v_double_arrow),
961 	    DATA(shuttle),
962 	    DATA(sizing),
963 	    DATA(spider),
964 	    DATA(spraycan),
965 	    DATA(star),
966 	    DATA(target),
967 	    DATA(tcross),
968 	    DATA(top_left_arrow),
969 	    DATA(top_left_corner),
970 	    DATA(top_right_corner),
971 	    DATA(top_side),
972 	    DATA(top_tee),
973 	    DATA(trek),
974 	    DATA(ul_angle),
975 	    DATA(umbrella),
976 	    DATA(ur_angle),
977 	    DATA(watch),
978 	    DATA(xterm),
979     };
980 #undef DATA
981     Cardinal j;
982     int result = -1;
983     if (!IsEmpty(name)) {
984 	for (j = 0; j < XtNumber(table); ++j) {
985 	    if (!strcmp(name, table[j].name)) {
986 		result = table[j].code;
987 		break;
988 	    }
989 	}
990     }
991     return result;
992 }
993 
994 void
xtermSetupPointer(XtermWidget xw,const char * theShape)995 xtermSetupPointer(XtermWidget xw, const char *theShape)
996 {
997     TScreen *screen = TScreenOf(xw);
998     unsigned shape = XC_xterm;
999     int other = LookupCursorShape(theShape);
1000     unsigned which;
1001 
1002     if (other >= 0 && other < XC_num_glyphs)
1003 	shape = (unsigned) other;
1004 
1005     TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
1006 	   NonNull(theShape)));
1007 
1008     which = (unsigned) (shape / 2);
1009     if (xw->work.pointer_cursors[which] == None) {
1010 	TRACE(("creating text pointer cursor from shape %d\n", shape));
1011 	xw->work.pointer_cursors[which] =
1012 	    make_colored_cursor(shape,
1013 				T_COLOR(screen, MOUSE_FG),
1014 				T_COLOR(screen, MOUSE_BG));
1015     } else {
1016 	TRACE(("updating text pointer cursor for shape %d\n", shape));
1017 	recolor_cursor(screen,
1018 		       screen->pointer_cursor,
1019 		       T_COLOR(screen, MOUSE_FG),
1020 		       T_COLOR(screen, MOUSE_BG));
1021     }
1022     if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
1023 	screen->pointer_cursor = xw->work.pointer_cursors[which];
1024 	TRACE(("defining text pointer cursor with shape %d\n", shape));
1025 	XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
1026 	if (XtIsRealized((Widget) xw)) {
1027 	    /* briefly override pointerMode after changing the pointer */
1028 	    if (screen->pointer_mode != pNever)
1029 		screen->hide_pointer = True;
1030 	    xtermShowPointer(xw, True);
1031 	}
1032     }
1033 }
1034 
1035 /* ARGSUSED */
1036 void
HandleKeyPressed(Widget w GCC_UNUSED,XEvent * event,String * params GCC_UNUSED,Cardinal * nparams GCC_UNUSED)1037 HandleKeyPressed(Widget w GCC_UNUSED,
1038 		 XEvent *event,
1039 		 String *params GCC_UNUSED,
1040 		 Cardinal *nparams GCC_UNUSED)
1041 {
1042     TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
1043     Input(term, &event->xkey, False);
1044 }
1045 
1046 /* ARGSUSED */
1047 void
HandleEightBitKeyPressed(Widget w GCC_UNUSED,XEvent * event,String * params GCC_UNUSED,Cardinal * nparams GCC_UNUSED)1048 HandleEightBitKeyPressed(Widget w GCC_UNUSED,
1049 			 XEvent *event,
1050 			 String *params GCC_UNUSED,
1051 			 Cardinal *nparams GCC_UNUSED)
1052 {
1053     TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
1054     Input(term, &event->xkey, True);
1055 }
1056 
1057 /* ARGSUSED */
1058 void
HandleStringEvent(Widget w GCC_UNUSED,XEvent * event GCC_UNUSED,String * params,Cardinal * nparams)1059 HandleStringEvent(Widget w GCC_UNUSED,
1060 		  XEvent *event GCC_UNUSED,
1061 		  String *params,
1062 		  Cardinal *nparams)
1063 {
1064 
1065     if (*nparams != 1)
1066 	return;
1067 
1068     if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
1069 	const char *abcdef = "ABCDEF";
1070 	const char *xxxxxx;
1071 	Char c;
1072 	UString p;
1073 	unsigned value = 0;
1074 
1075 	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
1076 	     '\0'; p++) {
1077 	    value *= 16;
1078 	    if (c >= '0' && c <= '9')
1079 		value += (unsigned) (c - '0');
1080 	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
1081 		value += (unsigned) (xxxxxx - abcdef) + 10;
1082 	    else
1083 		break;
1084 	}
1085 	if (c == '\0') {
1086 	    Char hexval[2];
1087 	    hexval[0] = (Char) value;
1088 	    hexval[1] = 0;
1089 	    StringInput(term, hexval, (size_t) 1);
1090 	}
1091     } else {
1092 	StringInput(term, (const Char *) *params, strlen(*params));
1093     }
1094 }
1095 
1096 #if OPT_EXEC_XTERM
1097 
1098 #ifndef PROCFS_ROOT
1099 #define PROCFS_ROOT "/proc"
1100 #endif
1101 
1102 /*
1103  * Determine the current working directory of the child so that we can
1104  * spawn a new terminal in the same directory.
1105  *
1106  * If we cannot get the CWD of the child, just use our own.
1107  */
1108 char *
ProcGetCWD(pid_t pid)1109 ProcGetCWD(pid_t pid)
1110 {
1111     char *child_cwd = NULL;
1112 
1113     if (pid) {
1114 	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
1115 	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
1116 	child_cwd = Readlink(child_cwd_link);
1117     }
1118     return child_cwd;
1119 }
1120 
1121 /* ARGSUSED */
1122 void
HandleSpawnTerminal(Widget w GCC_UNUSED,XEvent * event GCC_UNUSED,String * params,Cardinal * nparams)1123 HandleSpawnTerminal(Widget w GCC_UNUSED,
1124 		    XEvent *event GCC_UNUSED,
1125 		    String *params,
1126 		    Cardinal *nparams)
1127 {
1128     TScreen *screen = TScreenOf(term);
1129     char *child_cwd = NULL;
1130     char *child_exe;
1131     pid_t pid;
1132 
1133     /*
1134      * Try to find the actual program which is running in the child process.
1135      * This works for Linux.  If we cannot find the program, fall back to the
1136      * xterm program (which is usually adequate).  Give up if we are given only
1137      * a relative path to xterm, since that would not always match $PATH.
1138      */
1139     child_exe = Readlink(PROCFS_ROOT "/self/exe");
1140     if (!child_exe) {
1141 	if (strncmp(ProgramName, "./", (size_t) 2)
1142 	    && strncmp(ProgramName, "../", (size_t) 3)) {
1143 	    child_exe = xtermFindShell(ProgramName, True);
1144 	} else {
1145 	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
1146 	}
1147 	if (child_exe == 0)
1148 	    return;
1149     }
1150 
1151     child_cwd = ProcGetCWD(screen->pid);
1152 
1153     /* The reaper will take care of cleaning up the child */
1154     pid = fork();
1155     if (pid == -1) {
1156 	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
1157     } else if (!pid) {
1158 	/* We are the child */
1159 	if (child_cwd) {
1160 	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
1161 	}
1162 
1163 	if (setuid(screen->uid) == -1
1164 	    || setgid(screen->gid) == -1) {
1165 	    xtermWarning("Cannot reset uid/gid\n");
1166 	} else {
1167 	    unsigned myargc = *nparams + 1;
1168 	    char **myargv = TypeMallocN(char *, myargc + 1);
1169 
1170 	    if (myargv != 0) {
1171 		unsigned n = 0;
1172 
1173 		myargv[n++] = child_exe;
1174 
1175 		while (n < myargc) {
1176 		    myargv[n++] = (char *) *params++;
1177 		}
1178 
1179 		myargv[n] = 0;
1180 		execv(child_exe, myargv);
1181 	    }
1182 
1183 	    /* If we get here, we've failed */
1184 	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
1185 	}
1186 	_exit(0);
1187     }
1188 
1189     /* We are the parent; clean up */
1190     free(child_cwd);
1191     free(child_exe);
1192 }
1193 #endif /* OPT_EXEC_XTERM */
1194 
1195 /*
1196  * Rather than sending characters to the host, put them directly into our
1197  * input queue.  That lets a user have access to any of the control sequences
1198  * for a key binding.  This is the equivalent of local function key support.
1199  *
1200  * NOTE:  This code does not support the hexadecimal kludge used in
1201  * HandleStringEvent because it prevents us from sending an arbitrary string
1202  * (but it appears in a lot of examples - so we are stuck with it).  The
1203  * standard string converter does recognize "\" for newline ("\n") and for
1204  * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
1205  * without a specialized converter.  (Don't try to use \000, though).
1206  */
1207 /* ARGSUSED */
1208 void
HandleInterpret(Widget w GCC_UNUSED,XEvent * event GCC_UNUSED,String * params,Cardinal * param_count)1209 HandleInterpret(Widget w GCC_UNUSED,
1210 		XEvent *event GCC_UNUSED,
1211 		String *params,
1212 		Cardinal *param_count)
1213 {
1214     if (*param_count == 1) {
1215 	const char *value = params[0];
1216 	int need = (int) strlen(value);
1217 	int used = (int) (VTbuffer->next - VTbuffer->buffer);
1218 	int have = (int) (VTbuffer->last - VTbuffer->buffer);
1219 
1220 	if (have - used + need < BUF_SIZE) {
1221 
1222 	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
1223 
1224 	    TRACE(("Interpret %s\n", value));
1225 	    VTbuffer->update++;
1226 	}
1227     }
1228 }
1229 
1230 /*ARGSUSED*/
1231 void
HandleEnterWindow(Widget w GCC_UNUSED,XtPointer eventdata GCC_UNUSED,XEvent * event GCC_UNUSED,Boolean * cont GCC_UNUSED)1232 HandleEnterWindow(Widget w GCC_UNUSED,
1233 		  XtPointer eventdata GCC_UNUSED,
1234 		  XEvent *event GCC_UNUSED,
1235 		  Boolean *cont GCC_UNUSED)
1236 {
1237     /* NOP since we handled it above */
1238     TRACE(("HandleEnterWindow ignored\n"));
1239     TRACE_FOCUS(w, event);
1240 }
1241 
1242 /*ARGSUSED*/
1243 void
HandleLeaveWindow(Widget w GCC_UNUSED,XtPointer eventdata GCC_UNUSED,XEvent * event GCC_UNUSED,Boolean * cont GCC_UNUSED)1244 HandleLeaveWindow(Widget w GCC_UNUSED,
1245 		  XtPointer eventdata GCC_UNUSED,
1246 		  XEvent *event GCC_UNUSED,
1247 		  Boolean *cont GCC_UNUSED)
1248 {
1249     /* NOP since we handled it above */
1250     TRACE(("HandleLeaveWindow ignored\n"));
1251     TRACE_FOCUS(w, event);
1252 }
1253 
1254 /*ARGSUSED*/
1255 void
HandleFocusChange(Widget w GCC_UNUSED,XtPointer eventdata GCC_UNUSED,XEvent * ev,Boolean * cont GCC_UNUSED)1256 HandleFocusChange(Widget w GCC_UNUSED,
1257 		  XtPointer eventdata GCC_UNUSED,
1258 		  XEvent *ev,
1259 		  Boolean *cont GCC_UNUSED)
1260 {
1261     XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1262     XtermWidget xw = term;
1263     TScreen *screen = TScreenOf(xw);
1264 
1265     TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1266 	   visibleEventType(event->type),
1267 	   visibleNotifyMode(event->mode),
1268 	   visibleNotifyDetail(event->detail)));
1269     TRACE_FOCUS(xw, event);
1270 
1271     if (screen->quiet_grab
1272 	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1273 	/* EMPTY */ ;
1274     } else if (event->type == FocusIn) {
1275 	if (event->detail != NotifyPointer) {
1276 	    setXUrgency(xw, False);
1277 	}
1278 
1279 	/*
1280 	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1281 	 * one of our windows.  Use this to reset a case where one xterm is
1282 	 * partly obscuring another, and X gets (us) confused about whether the
1283 	 * pointer was in the window.  In particular, this can happen if the
1284 	 * user is resizing the obscuring window, causing some events to not be
1285 	 * delivered to the obscured window.
1286 	 */
1287 	if (event->detail == NotifyNonlinear
1288 	    && (screen->select & INWINDOW) != 0) {
1289 	    unselectwindow(xw, INWINDOW);
1290 	}
1291 	selectwindow(xw,
1292 		     ((event->detail == NotifyPointer)
1293 		      ? INWINDOW
1294 		      : FOCUS));
1295 	SendFocusButton(xw, event);
1296     } else {
1297 #if OPT_FOCUS_EVENT
1298 	if (event->type == FocusOut) {
1299 	    SendFocusButton(xw, event);
1300 	}
1301 #endif
1302 	/*
1303 	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1304 	 * ignore.
1305 	 */
1306 	if (event->mode != NotifyGrab) {
1307 	    unselectwindow(xw,
1308 			   ((event->detail == NotifyPointer)
1309 			    ? INWINDOW
1310 			    : FOCUS));
1311 	}
1312 	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1313 	    Bell(xw, XkbBI_Info, 100);
1314 	    ReverseVideo(xw);
1315 	    screen->grabbedKbd = False;
1316 	    update_securekbd();
1317 	}
1318     }
1319 }
1320 
1321 static long lastBellTime;	/* in milliseconds */
1322 
1323 #if defined(HAVE_XKB_BELL_EXT)
1324 static Atom
AtomBell(XtermWidget xw,int which)1325 AtomBell(XtermWidget xw, int which)
1326 {
1327 #define DATA(name) { XkbBI_##name, XkbBN_##name }
1328     static struct {
1329 	int value;
1330 	const char *name;
1331     } table[] = {
1332 	DATA(Info),
1333 	    DATA(MarginBell),
1334 	    DATA(MinorError),
1335 	    DATA(TerminalBell)
1336     };
1337 #undef DATA
1338     Cardinal n;
1339     Atom result = None;
1340 
1341     for (n = 0; n < XtNumber(table); ++n) {
1342 	if (table[n].value == which) {
1343 	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1344 	    break;
1345 	}
1346     }
1347     return result;
1348 }
1349 #endif
1350 
1351 void
xtermBell(XtermWidget xw,int which,int percent)1352 xtermBell(XtermWidget xw, int which, int percent)
1353 {
1354     TScreen *screen = TScreenOf(xw);
1355 #if defined(HAVE_XKB_BELL_EXT)
1356     Atom tony = AtomBell(xw, which);
1357 #endif
1358 
1359     switch (which) {
1360     case XkbBI_Info:
1361     case XkbBI_MinorError:
1362     case XkbBI_MajorError:
1363     case XkbBI_TerminalBell:
1364 	switch (screen->warningVolume) {
1365 	case bvOff:
1366 	    percent = -100;
1367 	    break;
1368 	case bvLow:
1369 	    break;
1370 	case bvHigh:
1371 	    percent = 100;
1372 	    break;
1373 	}
1374 	break;
1375     case XkbBI_MarginBell:
1376 	switch (screen->marginVolume) {
1377 	case bvOff:
1378 	    percent = -100;
1379 	    break;
1380 	case bvLow:
1381 	    break;
1382 	case bvHigh:
1383 	    percent = 100;
1384 	    break;
1385 	}
1386 	break;
1387     default:
1388 	break;
1389     }
1390 
1391 #if defined(HAVE_XKB_BELL_EXT)
1392     if (tony != None) {
1393 	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1394     } else
1395 #endif
1396 	XBell(screen->display, percent);
1397 }
1398 
1399 void
Bell(XtermWidget xw,int which,int percent)1400 Bell(XtermWidget xw, int which, int percent)
1401 {
1402     TScreen *screen = TScreenOf(xw);
1403     struct timeval curtime;
1404 
1405     TRACE(("BELL %d %d%%\n", which, percent));
1406     if (!XtIsRealized((Widget) xw)) {
1407 	return;
1408     }
1409 
1410     setXUrgency(xw, True);
1411 
1412     /* has enough time gone by that we are allowed to ring
1413        the bell again? */
1414     if (screen->bellSuppressTime) {
1415 	long now_msecs;
1416 
1417 	if (screen->bellInProgress) {
1418 	    do_xevents(xw);
1419 	    if (screen->bellInProgress) {	/* even after new events? */
1420 		return;
1421 	    }
1422 	}
1423 	X_GETTIMEOFDAY(&curtime);
1424 	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1425 	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1426 	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1427 	    return;
1428 	}
1429 	lastBellTime = now_msecs;
1430     }
1431 
1432     if (screen->visualbell) {
1433 	VisualBell();
1434     } else {
1435 	xtermBell(xw, which, percent);
1436     }
1437 
1438     if (screen->poponbell)
1439 	XRaiseWindow(screen->display, VShellWindow(xw));
1440 
1441     if (screen->bellSuppressTime) {
1442 	/* now we change a property and wait for the notify event to come
1443 	   back.  If the server is suspending operations while the bell
1444 	   is being emitted (problematic for audio bell), this lets us
1445 	   know when the previous bell has finished */
1446 	Widget w = CURRENT_EMU();
1447 	XChangeProperty(XtDisplay(w), XtWindow(w),
1448 			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1449 	screen->bellInProgress = True;
1450     }
1451 }
1452 
1453 static void
flashWindow(TScreen * screen,Window window,GC visualGC,unsigned width,unsigned height)1454 flashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1455 {
1456     int y = 0;
1457     int x = 0;
1458 
1459     if (screen->flash_line) {
1460 	y = CursorY(screen, screen->cur_row);
1461 	height = (unsigned) FontHeight(screen);
1462     }
1463     XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1464     XFlush(screen->display);
1465     Sleep(VB_DELAY);
1466     XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1467 }
1468 
1469 void
VisualBell(void)1470 VisualBell(void)
1471 {
1472     XtermWidget xw = term;
1473     TScreen *screen = TScreenOf(xw);
1474 
1475     if (VB_DELAY > 0) {
1476 	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1477 			  T_COLOR(screen, TEXT_BG));
1478 	XGCValues gcval;
1479 	GC visualGC;
1480 
1481 	gcval.function = GXxor;
1482 	gcval.foreground = xorPixel;
1483 	visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
1484 #if OPT_TEK4014
1485 	if (TEK4014_ACTIVE(xw)) {
1486 	    TekScreen *tekscr = TekScreenOf(tekWidget);
1487 	    flashWindow(screen, TWindow(tekscr), visualGC,
1488 			TFullWidth(tekscr),
1489 			TFullHeight(tekscr));
1490 	} else
1491 #endif
1492 	{
1493 	    flashWindow(screen, VWindow(screen), visualGC,
1494 			FullWidth(screen),
1495 			FullHeight(screen));
1496 	}
1497 	XtReleaseGC((Widget) xw, visualGC);
1498     }
1499 }
1500 
1501 /* ARGSUSED */
1502 void
HandleBellPropertyChange(Widget w GCC_UNUSED,XtPointer data GCC_UNUSED,XEvent * ev,Boolean * more GCC_UNUSED)1503 HandleBellPropertyChange(Widget w GCC_UNUSED,
1504 			 XtPointer data GCC_UNUSED,
1505 			 XEvent *ev,
1506 			 Boolean *more GCC_UNUSED)
1507 {
1508     TScreen *screen = TScreenOf(term);
1509 
1510     if (ev->xproperty.atom == XA_NOTICE) {
1511 	screen->bellInProgress = False;
1512     }
1513 }
1514 
1515 void
xtermWarning(const char * fmt,...)1516 xtermWarning(const char *fmt, ...)
1517 {
1518     int save_err = errno;
1519     va_list ap;
1520 
1521     fflush(stdout);
1522 
1523 #if OPT_TRACE
1524     va_start(ap, fmt);
1525     Trace("xtermWarning: ");
1526     TraceVA(fmt, ap);
1527     va_end(ap);
1528 #endif
1529 
1530     fprintf(stderr, "%s: ", ProgramName);
1531     va_start(ap, fmt);
1532     vfprintf(stderr, fmt, ap);
1533     (void) fflush(stderr);
1534 
1535     va_end(ap);
1536     errno = save_err;
1537 }
1538 
1539 void
xtermPerror(const char * fmt,...)1540 xtermPerror(const char *fmt, ...)
1541 {
1542     int save_err = errno;
1543     const char *msg = strerror(errno);
1544     va_list ap;
1545 
1546     fflush(stdout);
1547 
1548 #if OPT_TRACE
1549     va_start(ap, fmt);
1550     Trace("xtermPerror: ");
1551     TraceVA(fmt, ap);
1552     va_end(ap);
1553 #endif
1554 
1555     fprintf(stderr, "%s: ", ProgramName);
1556     va_start(ap, fmt);
1557     vfprintf(stderr, fmt, ap);
1558     fprintf(stderr, ": %s\n", msg);
1559     (void) fflush(stderr);
1560 
1561     va_end(ap);
1562     errno = save_err;
1563 }
1564 
1565 Window
WMFrameWindow(XtermWidget xw)1566 WMFrameWindow(XtermWidget xw)
1567 {
1568     Window win_root, win_current, *children;
1569     Window win_parent = 0;
1570     unsigned int nchildren;
1571 
1572     win_current = XtWindow(xw);
1573 
1574     /* find the parent which is child of root */
1575     do {
1576 	if (win_parent)
1577 	    win_current = win_parent;
1578 	XQueryTree(TScreenOf(xw)->display,
1579 		   win_current,
1580 		   &win_root,
1581 		   &win_parent,
1582 		   &children,
1583 		   &nchildren);
1584 	XFree(children);
1585     } while (win_root != win_parent);
1586 
1587     return win_current;
1588 }
1589 
1590 #if OPT_DABBREV
1591 /*
1592  * The following code implements `dynamic abbreviation' expansion a la
1593  * Emacs.  It looks in the preceding visible screen and its scrollback
1594  * to find expansions of a typed word.  It compares consecutive
1595  * expansions and ignores one of them if they are identical.
1596  * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1597  */
1598 
1599 #define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1600 
1601 static int
dabbrev_prev_char(TScreen * screen,CELL * cell,LineData ** ld)1602 dabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1603 {
1604     int result = -1;
1605     int firstLine = -(screen->savedlines);
1606 
1607     *ld = getLineData(screen, cell->row);
1608     while (cell->row >= firstLine) {
1609 	if (--(cell->col) >= 0) {
1610 	    result = (int) (*ld)->charData[cell->col];
1611 	    break;
1612 	}
1613 	if (--(cell->row) < firstLine)
1614 	    break;		/* ...there is no previous line */
1615 	*ld = getLineData(screen, cell->row);
1616 	cell->col = MaxCols(screen);
1617 	if (!LineTstWrapped(*ld)) {
1618 	    result = ' ';	/* treat lines as separate */
1619 	    break;
1620 	}
1621     }
1622     return result;
1623 }
1624 
1625 static char *
dabbrev_prev_word(XtermWidget xw,CELL * cell,LineData ** ld)1626 dabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1627 {
1628     TScreen *screen = TScreenOf(xw);
1629     char *abword;
1630     int c;
1631     char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1632     char *result = 0;
1633 
1634     abword = ab_end;
1635     *abword = '\0';		/* end of string marker */
1636 
1637     while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1638 	   IS_WORD_CONSTITUENT(c)) {
1639 	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1640 	    *(--abword) = (char) c;
1641     }
1642 
1643     if (c >= 0) {
1644 	result = abword;
1645     } else if (abword != ab_end) {
1646 	result = abword;
1647     }
1648 
1649     if (result != 0) {
1650 	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1651 	       !IS_WORD_CONSTITUENT(c)) {
1652 	    ;			/* skip preceding spaces */
1653 	}
1654 	(cell->col)++;		/* can be | > screen->max_col| */
1655     }
1656     return result;
1657 }
1658 
1659 static int
dabbrev_expand(XtermWidget xw)1660 dabbrev_expand(XtermWidget xw)
1661 {
1662     TScreen *screen = TScreenOf(xw);
1663     int pty = screen->respond;	/* file descriptor of pty */
1664 
1665     static CELL cell;
1666     static char *dabbrev_hint = 0, *lastexpansion = 0;
1667     static unsigned int expansions;
1668 
1669     char *expansion;
1670     size_t hint_len;
1671     int result = 0;
1672     LineData *ld;
1673 
1674     if (!screen->dabbrev_working) {	/* initialize */
1675 	expansions = 0;
1676 	cell.col = screen->cur_col;
1677 	cell.row = screen->cur_row;
1678 
1679 	free(dabbrev_hint);
1680 
1681 	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1682 
1683 	    free(lastexpansion);
1684 
1685 	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1686 
1687 		/* make own copy */
1688 		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1689 		    screen->dabbrev_working = True;
1690 		    /* we are in the middle of dabbrev process */
1691 		}
1692 	    } else {
1693 		return result;
1694 	    }
1695 	} else {
1696 	    return result;
1697 	}
1698 	if (!screen->dabbrev_working) {
1699 	    free(lastexpansion);
1700 	    lastexpansion = 0;
1701 	    return result;
1702 	}
1703     }
1704 
1705     if (dabbrev_hint == 0)
1706 	return result;
1707 
1708     hint_len = strlen(dabbrev_hint);
1709     for (;;) {
1710 	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1711 	    if (expansions >= 2) {
1712 		expansions = 0;
1713 		cell.col = screen->cur_col;
1714 		cell.row = screen->cur_row;
1715 		continue;
1716 	    }
1717 	    break;
1718 	}
1719 	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1720 	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1721 	    strcmp(expansion, lastexpansion))	/* different from previous */
1722 	    break;
1723     }
1724 
1725     if (expansion != 0) {
1726 	Char *copybuffer;
1727 	size_t del_cnt = strlen(lastexpansion) - hint_len;
1728 	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1729 
1730 	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1731 	    /* delete previous expansion */
1732 	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1733 	    memmove(copybuffer + del_cnt,
1734 		    expansion + hint_len,
1735 		    strlen(expansion) - hint_len);
1736 	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1737 	    /* v_write() just reset our flag */
1738 	    screen->dabbrev_working = True;
1739 	    free(copybuffer);
1740 
1741 	    free(lastexpansion);
1742 
1743 	    if ((lastexpansion = strdup(expansion)) != 0) {
1744 		result = 1;
1745 		expansions++;
1746 	    }
1747 	}
1748     }
1749 
1750     return result;
1751 }
1752 
1753 /*ARGSUSED*/
1754 void
HandleDabbrevExpand(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * nparams GCC_UNUSED)1755 HandleDabbrevExpand(Widget w,
1756 		    XEvent *event GCC_UNUSED,
1757 		    String *params GCC_UNUSED,
1758 		    Cardinal *nparams GCC_UNUSED)
1759 {
1760     XtermWidget xw;
1761 
1762     TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1763     if ((xw = getXtermWidget(w)) != 0) {
1764 	if (!dabbrev_expand(xw))
1765 	    Bell(xw, XkbBI_TerminalBell, 0);
1766     }
1767 }
1768 #endif /* OPT_DABBREV */
1769 
1770 void
xtermDeiconify(XtermWidget xw)1771 xtermDeiconify(XtermWidget xw)
1772 {
1773     TScreen *screen = TScreenOf(xw);
1774     Display *dpy = screen->display;
1775     Window target = VShellWindow(xw);
1776     XEvent e;
1777     Atom atom_state = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1778 
1779     if (xtermIsIconified(xw)) {
1780 	TRACE(("...de-iconify window %#lx\n", target));
1781 	XMapWindow(dpy, target);
1782 
1783 	memset(&e, 0, sizeof(e));
1784 	e.xclient.type = ClientMessage;
1785 	e.xclient.message_type = atom_state;
1786 	e.xclient.display = dpy;
1787 	e.xclient.window = target;
1788 	e.xclient.format = 32;
1789 	e.xclient.data.l[0] = 1;
1790 	e.xclient.data.l[1] = CurrentTime;
1791 
1792 	XSendEvent(dpy, DefaultRootWindow(dpy), False,
1793 		   SubstructureRedirectMask | SubstructureNotifyMask, &e);
1794 	xevents(xw);
1795     }
1796 }
1797 
1798 void
xtermIconify(XtermWidget xw)1799 xtermIconify(XtermWidget xw)
1800 {
1801     TScreen *screen = TScreenOf(xw);
1802     Window target = VShellWindow(xw);
1803 
1804     if (!xtermIsIconified(xw)) {
1805 	TRACE(("...iconify window %#lx\n", target));
1806 	XIconifyWindow(screen->display,
1807 		       target,
1808 		       DefaultScreen(screen->display));
1809 	xevents(xw);
1810     }
1811 }
1812 
1813 Boolean
xtermIsIconified(XtermWidget xw)1814 xtermIsIconified(XtermWidget xw)
1815 {
1816     XWindowAttributes win_attrs;
1817     TScreen *screen = TScreenOf(xw);
1818     Window target = VShellWindow(xw);
1819     Display *dpy = screen->display;
1820     Boolean result = False;
1821 
1822     if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
1823 	Atom actual_return_type;
1824 	int actual_format_return = 0;
1825 	unsigned long nitems_return = 0;
1826 	unsigned long bytes_after_return = 0;
1827 	unsigned char *prop_return = 0;
1828 	long long_length = 1024;
1829 	Atom requested_type = XA_ATOM;
1830 	Atom is_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
1831 	Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
1832 
1833 	/* this works with non-EWMH */
1834 	result = (win_attrs.map_state != IsViewable) ? True : False;
1835 
1836 	/* this is a convention used by some EWMH applications */
1837 	if (xtermGetWinProp(dpy,
1838 			    target,
1839 			    wm_state,
1840 			    0L,
1841 			    long_length,
1842 			    requested_type,
1843 			    &actual_return_type,
1844 			    &actual_format_return,
1845 			    &nitems_return,
1846 			    &bytes_after_return,
1847 			    &prop_return)
1848 	    && prop_return != 0
1849 	    && actual_return_type == requested_type
1850 	    && actual_format_return == 32) {
1851 	    unsigned long n;
1852 	    for (n = 0; n < nitems_return; ++n) {
1853 		unsigned long check = (((unsigned long *)
1854 					(void *) prop_return)[n]);
1855 		if (check == is_hidden) {
1856 		    result = True;
1857 		    break;
1858 		}
1859 	    }
1860 	}
1861     }
1862     TRACE(("...window %#lx is%s iconified\n",
1863 	   target,
1864 	   result ? "" : " not"));
1865     return result;
1866 }
1867 
1868 #if OPT_MAXIMIZE
1869 /*ARGSUSED*/
1870 void
HandleDeIconify(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * nparams GCC_UNUSED)1871 HandleDeIconify(Widget w,
1872 		XEvent *event GCC_UNUSED,
1873 		String *params GCC_UNUSED,
1874 		Cardinal *nparams GCC_UNUSED)
1875 {
1876     XtermWidget xw;
1877 
1878     if ((xw = getXtermWidget(w)) != 0) {
1879 	xtermDeiconify(xw);
1880     }
1881 }
1882 
1883 /*ARGSUSED*/
1884 void
HandleIconify(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * nparams GCC_UNUSED)1885 HandleIconify(Widget w,
1886 	      XEvent *event GCC_UNUSED,
1887 	      String *params GCC_UNUSED,
1888 	      Cardinal *nparams GCC_UNUSED)
1889 {
1890     XtermWidget xw;
1891 
1892     if ((xw = getXtermWidget(w)) != 0) {
1893 	xtermIconify(xw);
1894     }
1895 }
1896 
1897 int
QueryMaximize(XtermWidget xw,unsigned * width,unsigned * height)1898 QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1899 {
1900     TScreen *screen = TScreenOf(xw);
1901     XSizeHints hints;
1902     long supp = 0;
1903     Window root_win;
1904     int root_x = -1;		/* saved co-ordinates */
1905     int root_y = -1;
1906     unsigned root_border;
1907     unsigned root_depth;
1908     int code;
1909 
1910     if (XGetGeometry(screen->display,
1911 		     RootWindowOfScreen(XtScreen(xw)),
1912 		     &root_win,
1913 		     &root_x,
1914 		     &root_y,
1915 		     width,
1916 		     height,
1917 		     &root_border,
1918 		     &root_depth)) {
1919 	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1920 	       root_x,
1921 	       root_y,
1922 	       *width,
1923 	       *height,
1924 	       root_border));
1925 
1926 	*width -= (root_border * 2);
1927 	*height -= (root_border * 2);
1928 
1929 	hints.flags = PMaxSize;
1930 	if (XGetWMNormalHints(screen->display,
1931 			      VShellWindow(xw),
1932 			      &hints,
1933 			      &supp)
1934 	    && (hints.flags & PMaxSize) != 0) {
1935 
1936 	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1937 		   hints.max_width,
1938 		   hints.max_height));
1939 
1940 	    if ((unsigned) hints.max_width < *width)
1941 		*width = (unsigned) hints.max_width;
1942 	    if ((unsigned) hints.max_height < *height)
1943 		*height = (unsigned) hints.max_height;
1944 	}
1945 	code = 1;
1946     } else {
1947 	*width = 0;
1948 	*height = 0;
1949 	code = 0;
1950     }
1951     return code;
1952 }
1953 
1954 void
RequestMaximize(XtermWidget xw,int maximize)1955 RequestMaximize(XtermWidget xw, int maximize)
1956 {
1957     TScreen *screen = TScreenOf(xw);
1958     XWindowAttributes wm_attrs, vshell_attrs;
1959     unsigned root_width = 0, root_height = 0;
1960     Boolean success = False;
1961 
1962     TRACE(("RequestMaximize %d:%s\n",
1963 	   maximize,
1964 	   (maximize
1965 	    ? "maximize"
1966 	    : "restore")));
1967 
1968     /*
1969      * Before any maximize, ensure that we can capture the current screensize
1970      * as well as the estimated root-window size.
1971      */
1972     if (maximize
1973 	&& QueryMaximize(xw, &root_width, &root_height)
1974 	&& xtermGetWinAttrs(screen->display,
1975 			    WMFrameWindow(xw),
1976 			    &wm_attrs)
1977 	&& xtermGetWinAttrs(screen->display,
1978 			    VShellWindow(xw),
1979 			    &vshell_attrs)) {
1980 
1981 	if (screen->restore_data != True
1982 	    || screen->restore_width != root_width
1983 	    || screen->restore_height != root_height) {
1984 	    screen->restore_data = True;
1985 	    screen->restore_x = wm_attrs.x;
1986 	    screen->restore_y = wm_attrs.y;
1987 	    screen->restore_width = (unsigned) vshell_attrs.width;
1988 	    screen->restore_height = (unsigned) vshell_attrs.height;
1989 	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1990 		   screen->restore_x,
1991 		   screen->restore_y,
1992 		   screen->restore_width,
1993 		   screen->restore_height));
1994 	}
1995 
1996 	/* subtract wm decoration dimensions */
1997 	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
1998 	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
1999 	success = True;
2000     } else if (screen->restore_data) {
2001 	success = True;
2002 	maximize = 0;
2003     }
2004 
2005     if (success) {
2006 	switch (maximize) {
2007 	case 3:
2008 	    FullScreen(xw, 3);	/* depends on EWMH */
2009 	    break;
2010 	case 2:
2011 	    FullScreen(xw, 2);	/* depends on EWMH */
2012 	    break;
2013 	case 1:
2014 	    FullScreen(xw, 0);	/* overrides any EWMH hint */
2015 	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
2016 		   0,
2017 		   0,
2018 		   root_width,
2019 		   root_height));
2020 	    XMoveResizeWindow(screen->display, VShellWindow(xw),
2021 			      0,	/* x */
2022 			      0,	/* y */
2023 			      root_width,
2024 			      root_height);
2025 	    break;
2026 
2027 	default:
2028 	    FullScreen(xw, 0);	/* reset any EWMH hint */
2029 	    if (screen->restore_data) {
2030 		screen->restore_data = False;
2031 
2032 		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
2033 		       screen->restore_x,
2034 		       screen->restore_y,
2035 		       screen->restore_width,
2036 		       screen->restore_height));
2037 
2038 		XMoveResizeWindow(screen->display,
2039 				  VShellWindow(xw),
2040 				  screen->restore_x,
2041 				  screen->restore_y,
2042 				  screen->restore_width,
2043 				  screen->restore_height);
2044 	    }
2045 	    break;
2046 	}
2047     }
2048 }
2049 
2050 /*ARGSUSED*/
2051 void
HandleMaximize(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * nparams GCC_UNUSED)2052 HandleMaximize(Widget w,
2053 	       XEvent *event GCC_UNUSED,
2054 	       String *params GCC_UNUSED,
2055 	       Cardinal *nparams GCC_UNUSED)
2056 {
2057     XtermWidget xw;
2058 
2059     if ((xw = getXtermWidget(w)) != 0) {
2060 	RequestMaximize(xw, 1);
2061     }
2062 }
2063 
2064 /*ARGSUSED*/
2065 void
HandleRestoreSize(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * nparams GCC_UNUSED)2066 HandleRestoreSize(Widget w,
2067 		  XEvent *event GCC_UNUSED,
2068 		  String *params GCC_UNUSED,
2069 		  Cardinal *nparams GCC_UNUSED)
2070 {
2071     XtermWidget xw;
2072 
2073     if ((xw = getXtermWidget(w)) != 0) {
2074 	RequestMaximize(xw, 0);
2075     }
2076 }
2077 #endif /* OPT_MAXIMIZE */
2078 
2079 void
Redraw(void)2080 Redraw(void)
2081 {
2082     XtermWidget xw = term;
2083     TScreen *screen = TScreenOf(xw);
2084     XExposeEvent event;
2085 
2086     TRACE(("Redraw\n"));
2087 
2088     event.type = Expose;
2089     event.display = screen->display;
2090     event.x = 0;
2091     event.y = 0;
2092     event.count = 0;
2093 
2094     if (VWindow(screen)) {
2095 	event.window = VWindow(screen);
2096 	event.width = xw->core.width;
2097 	event.height = xw->core.height;
2098 	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
2099 						     (XEvent *) &event,
2100 						     NULL);
2101 	if (ScrollbarWidth(screen)) {
2102 	    (screen->scrollWidget->core.widget_class->core_class.expose)
2103 		(screen->scrollWidget, (XEvent *) &event, NULL);
2104 	}
2105     }
2106 #if OPT_TEK4014
2107     if (TEK4014_SHOWN(xw)) {
2108 	TekScreen *tekscr = TekScreenOf(tekWidget);
2109 	event.window = TWindow(tekscr);
2110 	event.width = tekWidget->core.width;
2111 	event.height = tekWidget->core.height;
2112 	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
2113     }
2114 #endif
2115 }
2116 
2117 #ifdef VMS
2118 #define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
2119 #else
2120 #define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
2121 #endif
2122 
2123 void
timestamp_filename(char * dst,const char * src)2124 timestamp_filename(char *dst, const char *src)
2125 {
2126     time_t tstamp;
2127     struct tm *tstruct;
2128 
2129     tstamp = time((time_t *) 0);
2130     tstruct = localtime(&tstamp);
2131     sprintf(dst, TIMESTAMP_FMT,
2132 	    src,
2133 	    (int) tstruct->tm_year + 1900,
2134 	    tstruct->tm_mon + 1,
2135 	    tstruct->tm_mday,
2136 	    tstruct->tm_hour,
2137 	    tstruct->tm_min,
2138 	    tstruct->tm_sec);
2139 }
2140 
2141 FILE *
create_printfile(XtermWidget xw,const char * suffix)2142 create_printfile(XtermWidget xw, const char *suffix)
2143 {
2144     TScreen *screen = TScreenOf(xw);
2145     char fname[1024];
2146     int fd;
2147     FILE *fp;
2148 
2149 #ifdef VMS
2150     sprintf(fname, "sys$scratch:xterm%s", suffix);
2151 #elif defined(HAVE_STRFTIME)
2152     {
2153 	char format[1024];
2154 	time_t now;
2155 	struct tm *ltm;
2156 
2157 	now = time((time_t *) 0);
2158 	ltm = localtime(&now);
2159 
2160 	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2161 	if (strftime(fname, sizeof fname, format, ltm) == 0) {
2162 	    sprintf(fname, "xterm%s", suffix);
2163 	}
2164     }
2165 #else
2166     sprintf(fname, "xterm%s", suffix);
2167 #endif
2168     fd = open_userfile(screen->uid, screen->gid, fname, False);
2169     fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2170     return fp;
2171 }
2172 
2173 int
open_userfile(uid_t uid,gid_t gid,char * path,Bool append)2174 open_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2175 {
2176     int fd;
2177     struct stat sb;
2178 
2179 #ifdef VMS
2180     if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
2181 	int the_error = errno;
2182 	xtermWarning("cannot open %s: %d:%s\n",
2183 		     path,
2184 		     the_error,
2185 		     SysErrorMsg(the_error));
2186 	return -1;
2187     }
2188     chown(path, uid, gid);
2189 #else
2190     if ((access(path, F_OK) != 0 && (errno != ENOENT))
2191 	|| (creat_as(uid, gid, append, path, 0644) <= 0)
2192 	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2193 	int the_error = errno;
2194 	xtermWarning("cannot open %s: %d:%s\n",
2195 		     path,
2196 		     the_error,
2197 		     SysErrorMsg(the_error));
2198 	return -1;
2199     }
2200 #endif
2201 
2202     /*
2203      * Doublecheck that the user really owns the file that we've opened before
2204      * we do any damage, and that it is not world-writable.
2205      */
2206     if (fstat(fd, &sb) < 0
2207 	|| sb.st_uid != uid
2208 	|| (sb.st_mode & 022) != 0) {
2209 	xtermWarning("you do not own %s\n", path);
2210 	close(fd);
2211 	return -1;
2212     }
2213     return fd;
2214 }
2215 
2216 #ifndef VMS
2217 /*
2218  * Create a file only if we could with the permissions of the real user id.
2219  * We could emulate this with careful use of access() and following
2220  * symbolic links, but that is messy and has race conditions.
2221  * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2222  * being available.
2223  *
2224  * Note: When called for user logging, we have ensured that the real and
2225  * effective user ids are the same, so this remains as a convenience function
2226  * for the debug logs.
2227  *
2228  * Returns
2229  *	 1 if we can proceed to open the file in relative safety,
2230  *	-1 on error, e.g., cannot fork
2231  *	 0 otherwise.
2232  */
2233 int
creat_as(uid_t uid,gid_t gid,Bool append,char * pathname,unsigned mode)2234 creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2235 {
2236     int fd;
2237     pid_t pid;
2238     int retval = 0;
2239     int childstat = 0;
2240 #ifndef HAVE_WAITPID
2241     int waited;
2242     void (*chldfunc) (int);
2243 
2244     chldfunc = signal(SIGCHLD, SIG_DFL);
2245 #endif /* HAVE_WAITPID */
2246 
2247     TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2248 	   (int) uid, (int) geteuid(),
2249 	   (int) gid, (int) getegid(),
2250 	   append,
2251 	   pathname,
2252 	   mode));
2253 
2254     if (uid == geteuid() && gid == getegid()) {
2255 	fd = open(pathname,
2256 		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2257 		  mode);
2258 	if (fd >= 0)
2259 	    close(fd);
2260 	return (fd >= 0);
2261     }
2262 
2263     pid = fork();
2264     switch (pid) {
2265     case 0:			/* child */
2266 	if (setgid(gid) == -1
2267 	    || setuid(uid) == -1) {
2268 	    /* we cannot report an error here via stderr, just quit */
2269 	    retval = 1;
2270 	} else {
2271 	    fd = open(pathname,
2272 		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2273 		      mode);
2274 	    if (fd >= 0) {
2275 		close(fd);
2276 		retval = 0;
2277 	    } else {
2278 		retval = 1;
2279 	    }
2280 	}
2281 	_exit(retval);
2282 	/* NOTREACHED */
2283     case -1:			/* error */
2284 	return retval;
2285     default:			/* parent */
2286 #ifdef HAVE_WAITPID
2287 	while (waitpid(pid, &childstat, 0) < 0) {
2288 #ifdef EINTR
2289 	    if (errno == EINTR)
2290 		continue;
2291 #endif /* EINTR */
2292 #ifdef ERESTARTSYS
2293 	    if (errno == ERESTARTSYS)
2294 		continue;
2295 #endif /* ERESTARTSYS */
2296 	    break;
2297 	}
2298 #else /* HAVE_WAITPID */
2299 	waited = wait(&childstat);
2300 	signal(SIGCHLD, chldfunc);
2301 	/*
2302 	   Since we had the signal handler uninstalled for a while,
2303 	   we might have missed the termination of our screen child.
2304 	   If we can check for this possibility without hanging, do so.
2305 	 */
2306 	do
2307 	    if (waited == TScreenOf(term)->pid)
2308 		NormalExit();
2309 	while ((waited = nonblocking_wait()) > 0) ;
2310 #endif /* HAVE_WAITPID */
2311 #ifndef WIFEXITED
2312 #define WIFEXITED(status) ((status & 0xff) != 0)
2313 #endif
2314 	if (WIFEXITED(childstat))
2315 	    retval = 1;
2316 	return retval;
2317     }
2318 }
2319 #endif /* !VMS */
2320 
2321 int
xtermResetIds(TScreen * screen)2322 xtermResetIds(TScreen *screen)
2323 {
2324     int result = 0;
2325     if (setgid(screen->gid) == -1) {
2326 	xtermWarning("unable to reset group-id\n");
2327 	result = -1;
2328     }
2329     if (setuid(screen->uid) == -1) {
2330 	xtermWarning("unable to reset user-id\n");
2331 	result = -1;
2332     }
2333     return result;
2334 }
2335 
2336 #ifdef ALLOWLOGGING
2337 
2338 /*
2339  * Logging is a security hole, since it allows a setuid program to write
2340  * arbitrary data to an arbitrary file.  So it is disabled by default.
2341  */
2342 
2343 #ifdef ALLOWLOGFILEEXEC
2344 static void
handle_SIGPIPE(int sig GCC_UNUSED)2345 handle_SIGPIPE(int sig GCC_UNUSED)
2346 {
2347     XtermWidget xw = term;
2348     TScreen *screen = TScreenOf(xw);
2349 
2350     DEBUG_MSG("handle:logpipe\n");
2351 #ifdef SYSV
2352     (void) signal(SIGPIPE, SIG_IGN);
2353 #endif /* SYSV */
2354     if (screen->logging)
2355 	CloseLog(xw);
2356 }
2357 
2358 /*
2359  * Open a command to pipe log data to it.
2360  * Warning, enabling this "feature" allows arbitrary programs
2361  * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2362  * done through escape sequences....  You have been warned.
2363  */
2364 static void
StartLogExec(TScreen * screen)2365 StartLogExec(TScreen *screen)
2366 {
2367     int pid;
2368     int p[2];
2369     static char *shell;
2370     struct passwd pw;
2371 
2372     if ((shell = x_getenv("SHELL")) == NULL) {
2373 
2374 	if (x_getpwuid(screen->uid, &pw)) {
2375 	    char *name = x_getlogin(screen->uid, &pw);
2376 	    if (*(pw.pw_shell)) {
2377 		shell = pw.pw_shell;
2378 	    }
2379 	    free(name);
2380 	}
2381     }
2382 
2383     if (shell == 0) {
2384 	static char dummy[] = "/bin/sh";
2385 	shell = dummy;
2386     }
2387 
2388     if (access(shell, X_OK) != 0) {
2389 	xtermPerror("Can't execute `%s'\n", shell);
2390 	return;
2391     }
2392 
2393     if (pipe(p) < 0) {
2394 	xtermPerror("Can't make a pipe connection\n");
2395 	return;
2396     } else if ((pid = fork()) < 0) {
2397 	xtermPerror("Can't fork...\n");
2398 	return;
2399     }
2400     if (pid == 0) {		/* child */
2401 	/*
2402 	 * Close our output (we won't be talking back to the
2403 	 * parent), and redirect our child's output to the
2404 	 * original stderr.
2405 	 */
2406 	close(p[1]);
2407 	dup2(p[0], 0);
2408 	close(p[0]);
2409 	dup2(fileno(stderr), 1);
2410 	dup2(fileno(stderr), 2);
2411 
2412 	close(fileno(stderr));
2413 	close(ConnectionNumber(screen->display));
2414 	close(screen->respond);
2415 
2416 	signal(SIGHUP, SIG_DFL);
2417 	signal(SIGCHLD, SIG_DFL);
2418 
2419 	/* (this is redundant) */
2420 	if (xtermResetIds(screen) < 0)
2421 	    exit(ERROR_SETUID);
2422 
2423 	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2424 	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2425 	exit(ERROR_LOGEXEC);
2426     }
2427     close(p[0]);
2428     screen->logfd = p[1];
2429     signal(SIGPIPE, handle_SIGPIPE);
2430 }
2431 #endif /* ALLOWLOGFILEEXEC */
2432 
2433 /*
2434  * Generate a path for a logfile if no default path is given.
2435  */
2436 static char *
GenerateLogPath(void)2437 GenerateLogPath(void)
2438 {
2439     static char *log_default = NULL;
2440 
2441     /* once opened we just reuse the same log name */
2442     if (log_default)
2443 	return (log_default);
2444 
2445 #if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2446     {
2447 #define LEN_HOSTNAME 255
2448 	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
2449 	 * the total length of a domain name (i.e., label octets and label
2450 	 * length octets) is restricted to 255 octets or less.''
2451 	 */
2452 #define LEN_GETPID 9
2453 	/*
2454 	 * This is arbitrary...
2455 	 */
2456 	const char form[] = "Xterm.log.%s%s.%lu";
2457 	char where[LEN_HOSTNAME + 1];
2458 	char when[LEN_TIMESTAMP];
2459 	time_t now = time((time_t *) 0);
2460 	struct tm *ltm = (struct tm *) localtime(&now);
2461 
2462 	if ((gethostname(where, sizeof(where)) == 0) &&
2463 	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2464 	    ((log_default = (char *) malloc((sizeof(form)
2465 					     + strlen(where)
2466 					     + strlen(when)
2467 					     + LEN_GETPID))) != NULL)) {
2468 	    (void) sprintf(log_default,
2469 			   form,
2470 			   where, when,
2471 			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
2472 	}
2473     }
2474 #else
2475     static const char log_def_name[] = "XtermLog.XXXXXX";
2476     if ((log_default = x_strdup(log_def_name)) != NULL) {
2477 	mktemp(log_default);
2478     }
2479 #endif
2480 
2481     return (log_default);
2482 }
2483 
2484 void
StartLog(XtermWidget xw)2485 StartLog(XtermWidget xw)
2486 {
2487     TScreen *screen = TScreenOf(xw);
2488 
2489     if (screen->logging || (screen->inhibit & I_LOG))
2490 	return;
2491 #ifdef VMS			/* file name is fixed in VMS variant */
2492     screen->logfd = open(XTERM_VMS_LOGFILE,
2493 			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2494 			 0640);
2495     if (screen->logfd < 0)
2496 	return;			/* open failed */
2497 #else /*VMS */
2498 
2499     /* if we weren't supplied with a logfile path, generate one */
2500     if (IsEmpty(screen->logfile))
2501 	screen->logfile = GenerateLogPath();
2502 
2503     /* give up if we were unable to allocate the filename */
2504     if (!screen->logfile)
2505 	return;
2506 
2507     if (*screen->logfile == '|') {	/* exec command */
2508 #ifdef ALLOWLOGFILEEXEC
2509 	StartLogExec(screen);
2510 #else
2511 	Bell(xw, XkbBI_Info, 0);
2512 	Bell(xw, XkbBI_Info, 0);
2513 	return;
2514 #endif
2515     } else if (strcmp(screen->logfile, "-") == 0) {
2516 	screen->logfd = STDOUT_FILENO;
2517     } else {
2518 	if ((screen->logfd = open_userfile(screen->uid,
2519 					   screen->gid,
2520 					   screen->logfile,
2521 					   True)) < 0)
2522 	    return;
2523     }
2524 #endif /*VMS */
2525     screen->logstart = VTbuffer->next;
2526     screen->logging = True;
2527     update_logging();
2528 }
2529 
2530 void
CloseLog(XtermWidget xw)2531 CloseLog(XtermWidget xw)
2532 {
2533     TScreen *screen = TScreenOf(xw);
2534 
2535     if (!screen->logging || (screen->inhibit & I_LOG))
2536 	return;
2537     FlushLog(xw);
2538     close(screen->logfd);
2539     screen->logging = False;
2540     update_logging();
2541 }
2542 
2543 void
FlushLog(XtermWidget xw)2544 FlushLog(XtermWidget xw)
2545 {
2546     TScreen *screen = TScreenOf(xw);
2547 
2548     if (screen->logging && !(screen->inhibit & I_LOG)) {
2549 	Char *cp;
2550 	int i;
2551 
2552 #ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2553 				   when there is no output and cp/screen->logstart are 1 apart */
2554 	if (!tt_new_output)
2555 	    return;
2556 	tt_new_output = False;
2557 #endif /* VMS */
2558 	cp = VTbuffer->next;
2559 	if (screen->logstart != 0
2560 	    && (i = (int) (cp - screen->logstart)) > 0) {
2561 	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2562 	}
2563 	screen->logstart = VTbuffer->next;
2564     }
2565 }
2566 
2567 #endif /* ALLOWLOGGING */
2568 
2569 /***====================================================================***/
2570 
2571 static unsigned
maskToShift(unsigned long mask)2572 maskToShift(unsigned long mask)
2573 {
2574     unsigned result = 0;
2575     if (mask != 0) {
2576 	while ((mask & 1) == 0) {
2577 	    mask >>= 1;
2578 	    ++result;
2579 	}
2580     }
2581     return result;
2582 }
2583 
2584 static unsigned
maskToWidth(unsigned long mask)2585 maskToWidth(unsigned long mask)
2586 {
2587     unsigned result = 0;
2588     while (mask != 0) {
2589 	if ((mask & 1) != 0)
2590 	    ++result;
2591 	mask >>= 1;
2592     }
2593     return result;
2594 }
2595 
2596 XVisualInfo *
getVisualInfo(XtermWidget xw)2597 getVisualInfo(XtermWidget xw)
2598 {
2599 #define MYFMT "getVisualInfo \
2600 depth %d, \
2601 type %d (%s), \
2602 size %d \
2603 rgb masks (%04lx/%04lx/%04lx)\n"
2604 #define MYARG \
2605        vi->depth,\
2606        vi->class,\
2607        ((vi->class & 1) ? "dynamic" : "static"),\
2608        vi->colormap_size,\
2609        vi->red_mask,\
2610        vi->green_mask,\
2611        vi->blue_mask
2612 
2613     TScreen *screen = TScreenOf(xw);
2614     Display *dpy = screen->display;
2615     XVisualInfo myTemplate;
2616 
2617     if (xw->visInfo == 0 && xw->numVisuals == 0) {
2618 	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2619 								XDefaultScreen(dpy)));
2620 	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2621 				     &myTemplate, &xw->numVisuals);
2622 
2623 	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2624 	    XVisualInfo *vi = xw->visInfo;
2625 	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2626 	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2627 	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2628 	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2629 	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2630 	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2631 
2632 	    xw->has_rgb = ((vi->red_mask != 0) &&
2633 			   (vi->green_mask != 0) &&
2634 			   (vi->blue_mask != 0) &&
2635 			   ((vi->red_mask & vi->green_mask) == 0) &&
2636 			   ((vi->green_mask & vi->blue_mask) == 0) &&
2637 			   ((vi->blue_mask & vi->red_mask) == 0) &&
2638 			   (vi->class == TrueColor
2639 			    || vi->class == DirectColor));
2640 
2641 	    if (resource.reportColors) {
2642 		printf(MYFMT, MYARG);
2643 	    }
2644 	    TRACE((MYFMT, MYARG));
2645 	    TRACE(("...shifts %u/%u/%u\n",
2646 		   xw->rgb_shifts[0],
2647 		   xw->rgb_shifts[1],
2648 		   xw->rgb_shifts[2]));
2649 	    TRACE(("...widths %u/%u/%u\n",
2650 		   xw->rgb_widths[0],
2651 		   xw->rgb_widths[1],
2652 		   xw->rgb_widths[2]));
2653 	}
2654     }
2655     return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
2656 #undef MYFMT
2657 #undef MYARG
2658 }
2659 
2660 #if OPT_ISO_COLORS
2661 static Bool
ReportAnsiColorRequest(XtermWidget xw,int opcode,int colornum,int final)2662 ReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
2663 {
2664     Bool result = False;
2665 
2666     if (AllowColorOps(xw, ecGetAnsiColor)) {
2667 	XColor color;
2668 	char buffer[80];
2669 
2670 	TRACE(("ReportAnsiColorRequest %d\n", colornum));
2671 	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2672 	(void) QueryOneColor(xw, &color);
2673 	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2674 		opcode,
2675 		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
2676 		color.red,
2677 		color.green,
2678 		color.blue);
2679 	unparseputc1(xw, ANSI_OSC);
2680 	unparseputs(xw, buffer);
2681 	unparseputc1(xw, final);
2682 	result = True;
2683     }
2684     return result;
2685 }
2686 
2687 static void
getColormapInfo(XtermWidget xw,unsigned * typep,unsigned * sizep)2688 getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2689 {
2690     if (getVisualInfo(xw)) {
2691 	*typep = (unsigned) xw->visInfo->class;
2692 	*sizep = (unsigned) xw->visInfo->colormap_size;
2693     } else {
2694 	*typep = 0;
2695 	*sizep = 0;
2696     }
2697 }
2698 
2699 #define MAX_COLORTABLE 4096
2700 
2701 /*
2702  * Make only one call to XQueryColors(), since it can be slow.
2703  */
2704 static Boolean
loadColorTable(XtermWidget xw,unsigned length)2705 loadColorTable(XtermWidget xw, unsigned length)
2706 {
2707     Colormap cmap = xw->core.colormap;
2708     TScreen *screen = TScreenOf(xw);
2709     Boolean result = (screen->cmap_data != 0);
2710 
2711     if (!result
2712 	&& length != 0
2713 	&& length < MAX_COLORTABLE) {
2714 	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2715 
2716 	if (screen->cmap_data != 0) {
2717 	    unsigned i;
2718 	    unsigned shift;
2719 
2720 	    if (getVisualInfo(xw))
2721 		shift = xw->rgb_shifts[2];
2722 	    else
2723 		shift = 0;
2724 
2725 	    screen->cmap_size = length;
2726 
2727 	    for (i = 0; i < screen->cmap_size; i++) {
2728 		screen->cmap_data[i].pixel = (unsigned long) i << shift;
2729 	    }
2730 	    result = (Boolean) (XQueryColors(screen->display,
2731 					     cmap,
2732 					     screen->cmap_data,
2733 					     (int) screen->cmap_size) != 0);
2734 	}
2735     }
2736     return result;
2737 }
2738 
2739 /***====================================================================***/
2740 
2741 /*
2742  * Call this function with def->{red,green,blue} initialized, to obtain a pixel
2743  * value.
2744  */
2745 Boolean
AllocOneColor(XtermWidget xw,XColor * def)2746 AllocOneColor(XtermWidget xw, XColor *def)
2747 {
2748     TScreen *screen = TScreenOf(xw);
2749     XVisualInfo *visInfo;
2750     Boolean result = True;
2751 
2752 #define MaskIt(name,nn) \
2753 	((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
2754 	             << xw->rgb_shifts[nn]) \
2755 	 & xw->visInfo->name ##_mask)
2756 
2757     if ((visInfo = getVisualInfo(xw)) != NULL && xw->has_rgb) {
2758 	def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
2759     } else {
2760 	Display *dpy = screen->display;
2761 	if (!XAllocColor(dpy, xw->core.colormap, def)) {
2762 	    /*
2763 	     * Decide between foreground and background by a grayscale
2764 	     * approximation.
2765 	     */
2766 	    int bright = def->red * 3 + def->green * 10 + def->blue;
2767 	    int levels = 14 * 0x8000;
2768 	    def->pixel = ((bright >= levels)
2769 			  ? xw->dft_background
2770 			  : xw->dft_foreground);
2771 	    result = False;
2772 	}
2773     }
2774     return result;
2775 }
2776 
2777 /***====================================================================***/
2778 
2779 /*
2780  * Call this function with def->pixel set to the color that we want to convert
2781  * to separate red/green/blue.
2782  */
2783 Boolean
QueryOneColor(XtermWidget xw,XColor * def)2784 QueryOneColor(XtermWidget xw, XColor *def)
2785 {
2786     XVisualInfo *visInfo;
2787     Boolean result = True;
2788 
2789 #define UnMaskIt(name,nn) \
2790 	((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
2791 #define UnMaskIt2(name,nn) \
2792 	(unsigned short)((((UnMaskIt(name,nn) << 8) \
2793 			   |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
2794 
2795     if ((visInfo = getVisualInfo(xw)) != NULL && xw->has_rgb) {
2796 	/* *INDENT-EQLS* */
2797 	def->red   = UnMaskIt2(red, 0);
2798 	def->green = UnMaskIt2(green, 1);
2799 	def->blue  = UnMaskIt2(blue, 2);
2800     } else if (!XQueryColor(TScreenOf(xw)->display, xw->core.colormap, def)) {
2801 	result     = False;
2802     }
2803     return result;
2804 }
2805 
2806 /***====================================================================***/
2807 
2808 /*
2809  * Find closest color for "def" in "cmap".
2810  * Set "def" to the resulting color.
2811  *
2812  * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2813  * modified with ideas from David Tong's "noflash" library.
2814  * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2815  *
2816  * Return False if not able to find or allocate a color.
2817  */
2818 static Boolean
allocateClosestRGB(XtermWidget xw,XColor * def)2819 allocateClosestRGB(XtermWidget xw, XColor *def)
2820 {
2821     TScreen *screen = TScreenOf(xw);
2822     Boolean result = False;
2823     unsigned cmap_type;
2824     unsigned cmap_size;
2825 
2826     getColormapInfo(xw, &cmap_type, &cmap_size);
2827 
2828     if ((cmap_type & 1) != 0) {
2829 
2830 	if (loadColorTable(xw, cmap_size)) {
2831 	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2832 
2833 	    if (tried != 0) {
2834 		unsigned attempts;
2835 
2836 		/*
2837 		 * Try (possibly each entry in the color map) to find the best
2838 		 * approximation to the requested color.
2839 		 */
2840 		for (attempts = 0; attempts < cmap_size; attempts++) {
2841 		    Boolean first = True;
2842 		    double bestRGB = 0.0;
2843 		    unsigned bestInx = 0;
2844 		    unsigned i;
2845 
2846 		    for (i = 0; i < cmap_size; i++) {
2847 			if (!tried[bestInx]) {
2848 			    double diff, thisRGB = 0.0;
2849 
2850 			    /*
2851 			     * Look for the best match based on luminance.
2852 			     * Measure this by the least-squares difference of
2853 			     * the weighted R/G/B components from the color map
2854 			     * versus the requested color.  Use the Y (luma)
2855 			     * component of the YIQ color space model for
2856 			     * weights that correspond to the luminance.
2857 			     */
2858 #define AddColorWeight(weight, color) \
2859 			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2860 			    thisRGB += diff * diff
2861 
2862 			    AddColorWeight(0.30, red);
2863 			    AddColorWeight(0.61, green);
2864 			    AddColorWeight(0.11, blue);
2865 
2866 			    if (first || (thisRGB < bestRGB)) {
2867 				first = False;
2868 				bestInx = i;
2869 				bestRGB = thisRGB;
2870 			    }
2871 			}
2872 		    }
2873 		    if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
2874 			*def = screen->cmap_data[bestInx];
2875 			TRACE(("...closest %x/%x/%x\n", def->red,
2876 			       def->green, def->blue));
2877 			result = True;
2878 			break;
2879 		    }
2880 		    /*
2881 		     * It failed - either the color map entry was readonly, or
2882 		     * another client has allocated the entry.  Mark the entry
2883 		     * so we will ignore it
2884 		     */
2885 		    tried[bestInx] = True;
2886 		}
2887 		free(tried);
2888 	    }
2889 	}
2890     }
2891     return result;
2892 }
2893 
2894 #ifndef ULONG_MAX
2895 #define ULONG_MAX (unsigned long)(~(0L))
2896 #endif
2897 
2898 /*
2899  * Allocate a color for the "ANSI" colors.  That actually includes colors up
2900  * to 256.
2901  *
2902  * Returns
2903  *	-1 on error
2904  *	0 on no change
2905  *	1 if a new color was allocated.
2906  */
2907 static int
AllocateAnsiColor(XtermWidget xw,ColorRes * res,const char * spec)2908 AllocateAnsiColor(XtermWidget xw,
2909 		  ColorRes * res,
2910 		  const char *spec)
2911 {
2912     int result;
2913     XColor def;
2914 
2915     if (xtermAllocColor(xw, &def, spec)) {
2916 	if (res->mode == True &&
2917 	    EQL_COLOR_RES(res, def.pixel)) {
2918 	    result = 0;
2919 	} else {
2920 	    result = 1;
2921 	    SET_COLOR_RES(res, def.pixel);
2922 	    res->red = def.red;
2923 	    res->green = def.green;
2924 	    res->blue = def.blue;
2925 	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
2926 		   (int) (res - TScreenOf(xw)->Acolors), spec,
2927 		   def.red,
2928 		   def.green,
2929 		   def.blue,
2930 		   def.pixel));
2931 	    if (!res->mode)
2932 		result = 0;
2933 	    res->mode = True;
2934 	}
2935     } else {
2936 	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2937 	result = -1;
2938     }
2939     return (result);
2940 }
2941 
2942 Pixel
xtermGetColorRes(XtermWidget xw,ColorRes * res)2943 xtermGetColorRes(XtermWidget xw, ColorRes * res)
2944 {
2945     Pixel result = 0;
2946 
2947     if (res->mode) {
2948 	result = res->value;
2949     } else {
2950 	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2951 	       (int) (res - TScreenOf(xw)->Acolors)));
2952 
2953 	if (res >= TScreenOf(xw)->Acolors) {
2954 	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2955 
2956 	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2957 		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2958 		res->mode = -True;
2959 		xtermWarning("Cannot allocate color \"%s\"\n",
2960 			     NonNull(res->resource));
2961 	    }
2962 	    result = res->value;
2963 	} else {
2964 	    result = 0;
2965 	}
2966     }
2967     return result;
2968 }
2969 
2970 static int
ChangeOneAnsiColor(XtermWidget xw,int color,const char * name)2971 ChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2972 {
2973     int code;
2974 
2975     if (color < 0 || color >= MAXCOLORS) {
2976 	code = -1;
2977     } else {
2978 	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2979 
2980 	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2981 	code = AllocateAnsiColor(xw, res, name);
2982     }
2983     return code;
2984 }
2985 
2986 /*
2987  * Set or query entries in the Acolors[] array by parsing pairs of color/name
2988  * values from the given buffer.
2989  *
2990  * The color can be any legal index into Acolors[], which consists of the
2991  * 16/88/256 "ANSI" colors, followed by special color values for the various
2992  * colorXX resources.  The indices for the special color values are not
2993  * simple to work with, so an alternative is to use the calls which pass in
2994  * 'first' set to the beginning of those indices.
2995  *
2996  * If the name is "?", report to the host the current value for the color.
2997  */
2998 static Bool
ChangeAnsiColorRequest(XtermWidget xw,int opcode,char * buf,int first,int final)2999 ChangeAnsiColorRequest(XtermWidget xw,
3000 		       int opcode,
3001 		       char *buf,
3002 		       int first,
3003 		       int final)
3004 {
3005     int repaint = False;
3006     int code;
3007     int last = (MAXCOLORS - first);
3008     int queried = 0;
3009 
3010     TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
3011 
3012     while (buf && *buf) {
3013 	int color;
3014 	char *name = strchr(buf, ';');
3015 
3016 	if (name == NULL)
3017 	    break;
3018 	*name = '\0';
3019 	name++;
3020 	color = atoi(buf);
3021 	if (color < 0 || color >= last)
3022 	    break;		/* quit on any error */
3023 	buf = strchr(name, ';');
3024 	if (buf) {
3025 	    *buf = '\0';
3026 	    buf++;
3027 	}
3028 	if (!strcmp(name, "?")) {
3029 	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3030 		++queried;
3031 	} else {
3032 	    code = ChangeOneAnsiColor(xw, color + first, name);
3033 	    if (code < 0) {
3034 		/* stop on any error */
3035 		break;
3036 	    } else if (code > 0) {
3037 		repaint = True;
3038 	    }
3039 	    /* FIXME:  free old color somehow?  We aren't for the other color
3040 	     * change style (dynamic colors).
3041 	     */
3042 	}
3043     }
3044     if (queried)
3045 	unparse_end(xw);
3046 
3047     return (repaint);
3048 }
3049 
3050 static Bool
ResetOneAnsiColor(XtermWidget xw,int color,int start)3051 ResetOneAnsiColor(XtermWidget xw, int color, int start)
3052 {
3053     Bool repaint = False;
3054     int last = MAXCOLORS - start;
3055 
3056     if (color >= 0 && color < last) {
3057 	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3058 
3059 	if (res->mode) {
3060 	    /* a color has been allocated for this slot - test further... */
3061 	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3062 		repaint = True;
3063 	    }
3064 	}
3065     }
3066     return repaint;
3067 }
3068 
3069 int
ResetAnsiColorRequest(XtermWidget xw,char * buf,int start)3070 ResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3071 {
3072     int repaint = 0;
3073     int color;
3074 
3075     TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3076     if (*buf != '\0') {
3077 	/* reset specific colors */
3078 	while (!IsEmpty(buf)) {
3079 	    char *next;
3080 
3081 	    color = (int) (strtol) (buf, &next, 10);
3082 	    if (!PartS2L(buf, next) || (color < 0))
3083 		break;		/* no number at all */
3084 	    if (next != 0) {
3085 		if (strchr(";", *next) == 0)
3086 		    break;	/* unexpected delimiter */
3087 		++next;
3088 	    }
3089 
3090 	    if (ResetOneAnsiColor(xw, color, start)) {
3091 		++repaint;
3092 	    }
3093 	    buf = next;
3094 	}
3095     } else {
3096 	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3097 	for (color = 0; color < MAXCOLORS; ++color) {
3098 	    if (ResetOneAnsiColor(xw, color, start)) {
3099 		++repaint;
3100 	    }
3101 	}
3102     }
3103     TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3104     return repaint;
3105 }
3106 #else
3107 #define allocateClosestRGB(xw, def) 0
3108 #endif /* OPT_ISO_COLORS */
3109 
3110 Boolean
allocateBestRGB(XtermWidget xw,XColor * def)3111 allocateBestRGB(XtermWidget xw, XColor *def)
3112 {
3113     (void) xw;
3114     (void) def;
3115     return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
3116 }
3117 
3118 static Boolean
xtermAllocColor(XtermWidget xw,XColor * def,const char * spec)3119 xtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
3120 {
3121     Boolean result = False;
3122     TScreen *screen = TScreenOf(xw);
3123     Colormap cmap = xw->core.colormap;
3124     size_t have = strlen(spec);
3125 
3126     if (have == 0 || have > MAX_U_STRING) {
3127 	if (resource.reportColors) {
3128 	    printf("color  (ignored, length %lu)\n", (unsigned long) have);
3129 	}
3130     } else if (XParseColor(screen->display, cmap, spec, def)) {
3131 	XColor save_def = *def;
3132 	if (resource.reportColors) {
3133 	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3134 		   def->red, def->green, def->blue,
3135 		   spec);
3136 	}
3137 	if (allocateBestRGB(xw, def)) {
3138 	    if (resource.reportColors) {
3139 		if (def->red != save_def.red ||
3140 		    def->green != save_def.green ||
3141 		    def->blue != save_def.blue) {
3142 		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3143 			   def->red, def->green, def->blue,
3144 			   spec);
3145 		}
3146 	    }
3147 	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3148 		   def->red, def->green, def->blue));
3149 	    result = True;
3150 	}
3151     }
3152     return result;
3153 }
3154 
3155 /*
3156  * This provides an approximation (the closest color from xterm's palette)
3157  * rather than the "exact" color (whatever the display could provide, actually)
3158  * because of the context in which it is used.
3159  */
3160 #define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
3161 int
xtermClosestColor(XtermWidget xw,int find_red,int find_green,int find_blue)3162 xtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
3163 {
3164     int result = -1;
3165 #if OPT_ISO_COLORS
3166     int n;
3167     int best_index = -1;
3168     unsigned long best_value = 0;
3169     unsigned long this_value;
3170     long diff_red, diff_green, diff_blue;
3171 
3172     TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
3173 
3174     for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
3175 	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
3176 
3177 	/* ensure that we have a value for each of the colors */
3178 	if (!res->mode) {
3179 	    (void) AllocateAnsiColor(xw, res, res->resource);
3180 	}
3181 
3182 	/* find the closest match */
3183 	if (res->mode == True) {
3184 	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
3185 		    res->value, res->red, res->green, res->blue));
3186 	    diff_red = ColorDiff(find_red, res->red);
3187 	    diff_green = ColorDiff(find_green, res->green);
3188 	    diff_blue = ColorDiff(find_blue, res->blue);
3189 	    this_value = (unsigned long) ((diff_red * diff_red)
3190 					  + (diff_green * diff_green)
3191 					  + (diff_blue * diff_blue));
3192 	    if (best_index < 0 || this_value < best_value) {
3193 		best_index = n;
3194 		best_value = this_value;
3195 	    }
3196 	}
3197     }
3198     TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
3199     result = best_index;
3200 
3201 #else
3202     (void) xw;
3203     (void) find_red;
3204     (void) find_green;
3205     (void) find_blue;
3206 #endif
3207     return result;
3208 }
3209 
3210 #if OPT_DIRECT_COLOR
3211 int
getDirectColor(XtermWidget xw,int red,int green,int blue)3212 getDirectColor(XtermWidget xw, int red, int green, int blue)
3213 {
3214     Pixel result = 0;
3215 
3216 #define getRGB(name,shift) \
3217     do { \
3218 	Pixel value = (Pixel) name & 0xff; \
3219 	if (xw->rgb_widths[shift] < 8) { \
3220 	    value >>= (int) (8 - xw->rgb_widths[shift]); \
3221 	} \
3222 	value <<= xw->rgb_shifts[shift]; \
3223 	value &= xw->visInfo->name ##_mask; \
3224 	result |= value; \
3225     } while (0)
3226 
3227     getRGB(red, 0);
3228     getRGB(green, 1);
3229     getRGB(blue, 2);
3230 
3231 #undef getRGB
3232 
3233     return (int) result;
3234 }
3235 
3236 static void
formatDirectColor(char * target,XtermWidget xw,unsigned value)3237 formatDirectColor(char *target, XtermWidget xw, unsigned value)
3238 {
3239     Pixel result[3];
3240 
3241 #define getRGB(name, shift) \
3242     do { \
3243 	result[shift] = value & xw->visInfo->name ## _mask; \
3244 	result[shift] >>= xw->rgb_shifts[shift]; \
3245 	if (xw->rgb_widths[shift] < 8) \
3246 	    result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
3247     } while(0)
3248 
3249     getRGB(red, 0);
3250     getRGB(green, 1);
3251     getRGB(blue, 2);
3252 
3253 #undef getRGB
3254 
3255     sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
3256 }
3257 #endif /* OPT_DIRECT_COLOR */
3258 
3259 #define fg2SGR(n) \
3260 		(n) >= 8 ? 9 : 3, \
3261 		(n) >= 8 ? (n) - 8 : (n)
3262 #define bg2SGR(n) \
3263 		(n) >= 8 ? 10 : 4, \
3264 		(n) >= 8 ? (n) - 8 : (n)
3265 
3266 #define EndOf(s) (s) + strlen(s)
3267 
3268 char *
xtermFormatSGR(XtermWidget xw,char * target,unsigned attr,int fg,int bg)3269 xtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3270 {
3271     TScreen *screen = TScreenOf(xw);
3272     char *msg = target;
3273 
3274     strcpy(target, "0");
3275     if (attr & BOLD)
3276 	strcat(msg, ";1");
3277     if (attr & UNDERLINE)
3278 	strcat(msg, ";4");
3279     if (attr & BLINK)
3280 	strcat(msg, ";5");
3281     if (attr & INVERSE)
3282 	strcat(msg, ";7");
3283     if (attr & INVISIBLE)
3284 	strcat(msg, ";8");
3285 #if OPT_WIDE_ATTRS
3286     if (attr & ATR_FAINT)
3287 	strcat(msg, ";2");
3288     if (attr & ATR_ITALIC)
3289 	strcat(msg, ";3");
3290     if (attr & ATR_STRIKEOUT)
3291 	strcat(msg, ";9");
3292     if (attr & ATR_DBL_UNDER)
3293 	strcat(msg, ";21");
3294 #endif
3295 #if OPT_256_COLORS || OPT_88_COLORS
3296     if_OPT_ISO_COLORS(screen, {
3297 	if (attr & FG_COLOR) {
3298 	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3299 		strcat(msg, ";38:2::");
3300 		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3301 	    }) if (fg >= 16) {
3302 		sprintf(EndOf(msg), ";38:5:%d", fg);
3303 	    } else {
3304 		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3305 	    }
3306 	}
3307 	if (attr & BG_COLOR) {
3308 	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3309 		strcat(msg, ";48:2::");
3310 		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3311 	    }) if (bg >= 16) {
3312 		sprintf(EndOf(msg), ";48:5:%d", bg);
3313 	    } else {
3314 		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3315 	    }
3316 	}
3317     });
3318 #elif OPT_ISO_COLORS
3319     if_OPT_ISO_COLORS(screen, {
3320 	if (attr & FG_COLOR) {
3321 	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3322 	}
3323 	if (attr & BG_COLOR) {
3324 	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3325 	}
3326     });
3327 #else
3328     (void) screen;
3329     (void) fg;
3330     (void) bg;
3331 #endif
3332     return target;
3333 }
3334 
3335 #if OPT_PASTE64
3336 static void
ManipulateSelectionData(XtermWidget xw,TScreen * screen,char * buf,int final)3337 ManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3338 {
3339 #define PDATA(a,b) { a, #b }
3340     static struct {
3341 	char given;
3342 	String result;
3343     } table[] = {
3344 	PDATA('s', SELECT),
3345 	    PDATA('p', PRIMARY),
3346 	    PDATA('q', SECONDARY),
3347 	    PDATA('c', CLIPBOARD),
3348 	    PDATA('0', CUT_BUFFER0),
3349 	    PDATA('1', CUT_BUFFER1),
3350 	    PDATA('2', CUT_BUFFER2),
3351 	    PDATA('3', CUT_BUFFER3),
3352 	    PDATA('4', CUT_BUFFER4),
3353 	    PDATA('5', CUT_BUFFER5),
3354 	    PDATA('6', CUT_BUFFER6),
3355 	    PDATA('7', CUT_BUFFER7),
3356     };
3357 
3358     const char *base = buf;
3359     Cardinal j, n = 0;
3360 
3361     TRACE(("Manipulate selection data\n"));
3362 
3363     while (*buf != ';' && *buf != '\0') {
3364 	++buf;
3365     }
3366 
3367     if (*buf == ';') {
3368 	char *used;
3369 
3370 	*buf++ = '\0';
3371 
3372 	if (*base == '\0')
3373 	    base = "s0";
3374 
3375 	if ((used = x_strdup(base)) != 0) {
3376 	    String *select_args;
3377 
3378 	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
3379 		while (*base != '\0') {
3380 		    for (j = 0; j < XtNumber(table); ++j) {
3381 			if (*base == table[j].given) {
3382 			    used[n] = *base;
3383 			    select_args[n++] = table[j].result;
3384 			    TRACE(("atom[%d] %s\n", n, table[j].result));
3385 			    break;
3386 			}
3387 		    }
3388 		    ++base;
3389 		}
3390 		used[n] = 0;
3391 
3392 		if (!strcmp(buf, "?")) {
3393 		    if (AllowWindowOps(xw, ewGetSelection)) {
3394 			TRACE(("Getting selection\n"));
3395 			unparseputc1(xw, ANSI_OSC);
3396 			unparseputs(xw, "52");
3397 			unparseputc(xw, ';');
3398 
3399 			unparseputs(xw, used);
3400 			unparseputc(xw, ';');
3401 
3402 			/* Tell xtermGetSelection data is base64 encoded */
3403 			screen->base64_paste = n;
3404 			screen->base64_final = final;
3405 
3406 			screen->selection_time =
3407 			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3408 
3409 			/* terminator will be written in this call */
3410 			xtermGetSelection((Widget) xw,
3411 					  screen->selection_time,
3412 					  select_args, n,
3413 					  NULL);
3414 			/*
3415 			 * select_args is used via SelectionReceived, cannot
3416 			 * free it here.
3417 			 */
3418 		    } else {
3419 			free(select_args);
3420 		    }
3421 		} else {
3422 		    if (AllowWindowOps(xw, ewSetSelection)) {
3423 			char *old = buf;
3424 
3425 			TRACE(("Setting selection(%s) with %s\n", used, buf));
3426 			screen->selection_time =
3427 			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3428 
3429 			for (j = 0; j < n; ++j) {
3430 			    buf = old;
3431 			    ClearSelectionBuffer(screen, select_args[j]);
3432 			    while (*buf != '\0') {
3433 				AppendToSelectionBuffer(screen,
3434 							CharOf(*buf++),
3435 							select_args[j]);
3436 			    }
3437 			}
3438 			CompleteSelection(xw, select_args, n);
3439 		    }
3440 		    free(select_args);
3441 		}
3442 	    }
3443 	    free(used);
3444 	}
3445     }
3446 }
3447 #endif /* OPT_PASTE64 */
3448 
3449 /***====================================================================***/
3450 
3451 #define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3452 			 || (xw->screen.utf8_title) \
3453 			 || (xw->screen.c1_printable))
3454 
3455 static Bool
xtermIsPrintable(XtermWidget xw,Char ** bufp,Char * last)3456 xtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3457 {
3458     TScreen *screen = TScreenOf(xw);
3459     Bool result = False;
3460     Char *cp = *bufp;
3461     Char *next = cp;
3462 
3463     (void) screen;
3464     (void) last;
3465 
3466 #if OPT_WIDE_CHARS
3467     if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3468 	PtyData data;
3469 
3470 	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3471 	    if (data.utf_data != UCS_REPL
3472 		&& (data.utf_data >= 128 ||
3473 		    ansi_table[data.utf_data] == CASE_PRINT)) {
3474 		next += (data.utf_size - 1);
3475 		result = True;
3476 	    } else {
3477 		result = False;
3478 	    }
3479 	} else {
3480 	    result = False;
3481 	}
3482     } else
3483 #endif
3484 #if OPT_C1_PRINT
3485 	if (screen->c1_printable
3486 	    && (*cp >= 128 && *cp < 160)) {
3487 	result = True;
3488     } else
3489 #endif
3490     if (ansi_table[*cp] == CASE_PRINT) {
3491 	result = True;
3492     }
3493     *bufp = next;
3494     return result;
3495 }
3496 
3497 /***====================================================================***/
3498 
3499 /*
3500  * Enum corresponding to the actual OSC codes rather than the internal
3501  * array indices.  Compare with TermColors.
3502  */
3503 typedef enum {
3504     OSC_TEXT_FG = 10
3505     ,OSC_TEXT_BG
3506     ,OSC_TEXT_CURSOR
3507     ,OSC_MOUSE_FG
3508     ,OSC_MOUSE_BG
3509 #if OPT_TEK4014
3510     ,OSC_TEK_FG = 15
3511     ,OSC_TEK_BG
3512 #endif
3513 #if OPT_HIGHLIGHT_COLOR
3514     ,OSC_HIGHLIGHT_BG = 17
3515 #endif
3516 #if OPT_TEK4014
3517     ,OSC_TEK_CURSOR = 18
3518 #endif
3519 #if OPT_HIGHLIGHT_COLOR
3520     ,OSC_HIGHLIGHT_FG = 19
3521 #endif
3522     ,OSC_NCOLORS
3523 } OscTextColors;
3524 
3525 /*
3526  * Map codes to OSC controls that can reset colors.
3527  */
3528 #define OSC_RESET 100
3529 #define OSC_Reset(code) (code) + OSC_RESET
3530 
3531 static Bool
GetOldColors(XtermWidget xw)3532 GetOldColors(XtermWidget xw)
3533 {
3534     if (xw->work.oldColors == NULL) {
3535 	int i;
3536 
3537 	xw->work.oldColors = TypeXtMalloc(ScrnColors);
3538 	if (xw->work.oldColors == NULL) {
3539 	    xtermWarning("allocation failure in GetOldColors\n");
3540 	    return (False);
3541 	}
3542 	xw->work.oldColors->which = 0;
3543 	for (i = 0; i < NCOLORS; i++) {
3544 	    xw->work.oldColors->colors[i] = 0;
3545 	    xw->work.oldColors->names[i] = NULL;
3546 	}
3547 	GetColors(xw, xw->work.oldColors);
3548     }
3549     return (True);
3550 }
3551 
3552 static int
oppositeColor(XtermWidget xw,int n)3553 oppositeColor(XtermWidget xw, int n)
3554 {
3555     Boolean reversed = (xw->misc.re_verse);
3556 
3557     switch (n) {
3558     case TEXT_FG:
3559 	n = reversed ? TEXT_FG : TEXT_BG;
3560 	break;
3561     case TEXT_BG:
3562 	n = reversed ? TEXT_BG : TEXT_FG;
3563 	break;
3564     case MOUSE_FG:
3565 	n = MOUSE_BG;
3566 	break;
3567     case MOUSE_BG:
3568 	n = MOUSE_FG;
3569 	break;
3570 #if OPT_TEK4014
3571     case TEK_FG:
3572 	n = reversed ? TEK_FG : TEK_BG;
3573 	break;
3574     case TEK_BG:
3575 	n = reversed ? TEK_BG : TEK_FG;
3576 	break;
3577 #endif
3578 #if OPT_HIGHLIGHT_COLOR
3579     case HIGHLIGHT_FG:
3580 	n = HIGHLIGHT_BG;
3581 	break;
3582     case HIGHLIGHT_BG:
3583 	n = HIGHLIGHT_FG;
3584 	break;
3585 #endif
3586     default:
3587 	break;
3588     }
3589     return n;
3590 }
3591 
3592 static Bool
ReportColorRequest(XtermWidget xw,int ndx,int final)3593 ReportColorRequest(XtermWidget xw, int ndx, int final)
3594 {
3595     Bool result = False;
3596 
3597     if (AllowColorOps(xw, ecGetColor)) {
3598 	XColor color;
3599 	char buffer[80];
3600 
3601 	/*
3602 	 * ChangeColorsRequest() has "always" chosen the opposite color when
3603 	 * reverse-video is set.  Report this as the original color index, but
3604 	 * reporting the opposite color which would be used.
3605 	 */
3606 	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3607 
3608 	GetOldColors(xw);
3609 	color.pixel = xw->work.oldColors->colors[ndx];
3610 	(void) QueryOneColor(xw, &color);
3611 	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3612 		color.red,
3613 		color.green,
3614 		color.blue);
3615 	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
3616 	       ndx, xw->work.oldColors->colors[ndx], buffer));
3617 	unparseputc1(xw, ANSI_OSC);
3618 	unparseputs(xw, buffer);
3619 	unparseputc1(xw, final);
3620 	result = True;
3621     }
3622     return result;
3623 }
3624 
3625 static Bool
UpdateOldColors(XtermWidget xw,ScrnColors * pNew)3626 UpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3627 {
3628     int i;
3629 
3630     /* if we were going to free old colors, this would be the place to
3631      * do it.   I've decided not to (for now), because it seems likely
3632      * that we'd have a small set of colors we use over and over, and that
3633      * we could save some overhead this way.   The only case in which this
3634      * (clearly) fails is if someone is trying a boatload of colors, in
3635      * which case they can restart xterm
3636      */
3637     for (i = 0; i < NCOLORS; i++) {
3638 	if (COLOR_DEFINED(pNew, i)) {
3639 	    if (xw->work.oldColors->names[i] != NULL) {
3640 		XtFree(xw->work.oldColors->names[i]);
3641 		xw->work.oldColors->names[i] = NULL;
3642 	    }
3643 	    if (pNew->names[i]) {
3644 		xw->work.oldColors->names[i] = pNew->names[i];
3645 	    }
3646 	    xw->work.oldColors->colors[i] = pNew->colors[i];
3647 	}
3648     }
3649     return (True);
3650 }
3651 
3652 /*
3653  * OSC codes are constant, but the indices for the color arrays depend on how
3654  * xterm is compiled.
3655  */
3656 static int
OscToColorIndex(OscTextColors mode)3657 OscToColorIndex(OscTextColors mode)
3658 {
3659     int result = 0;
3660 
3661 #define CASE(name) case OSC_##name: result = name; break
3662     switch (mode) {
3663 	CASE(TEXT_FG);
3664 	CASE(TEXT_BG);
3665 	CASE(TEXT_CURSOR);
3666 	CASE(MOUSE_FG);
3667 	CASE(MOUSE_BG);
3668 #if OPT_TEK4014
3669 	CASE(TEK_FG);
3670 	CASE(TEK_BG);
3671 #endif
3672 #if OPT_HIGHLIGHT_COLOR
3673 	CASE(HIGHLIGHT_BG);
3674 	CASE(HIGHLIGHT_FG);
3675 #endif
3676 #if OPT_TEK4014
3677 	CASE(TEK_CURSOR);
3678 #endif
3679     case OSC_NCOLORS:
3680 	break;
3681     }
3682     return result;
3683 }
3684 
3685 static Bool
ChangeColorsRequest(XtermWidget xw,int start,char * names,int final)3686 ChangeColorsRequest(XtermWidget xw,
3687 		    int start,
3688 		    char *names,
3689 		    int final)
3690 {
3691     Bool result = False;
3692     ScrnColors newColors;
3693 
3694     TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3695 
3696     if (GetOldColors(xw)) {
3697 	int i;
3698 	int queried = 0;
3699 
3700 	newColors.which = 0;
3701 	for (i = 0; i < NCOLORS; i++) {
3702 	    newColors.names[i] = NULL;
3703 	}
3704 	for (i = start; i < OSC_NCOLORS; i++) {
3705 	    int ndx = OscToColorIndex((OscTextColors) i);
3706 	    if (xw->misc.re_verse)
3707 		ndx = oppositeColor(xw, ndx);
3708 
3709 	    if (IsEmpty(names)) {
3710 		newColors.names[ndx] = NULL;
3711 	    } else {
3712 		char *thisName = ((names[0] == ';') ? NULL : names);
3713 
3714 		names = strchr(names, ';');
3715 		if (names != NULL) {
3716 		    *names++ = '\0';
3717 		}
3718 		if (thisName != 0) {
3719 		    if (!strcmp(thisName, "?")) {
3720 			if (ReportColorRequest(xw, ndx, final))
3721 			    ++queried;
3722 		    } else if (!xw->work.oldColors->names[ndx]
3723 			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3724 			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3725 		    }
3726 		}
3727 	    }
3728 	}
3729 
3730 	if (newColors.which != 0) {
3731 	    ChangeColors(xw, &newColors);
3732 	    UpdateOldColors(xw, &newColors);
3733 	} else if (queried) {
3734 	    unparse_end(xw);
3735 	}
3736 	result = True;
3737     }
3738     return result;
3739 }
3740 
3741 static Bool
ResetColorsRequest(XtermWidget xw,int code)3742 ResetColorsRequest(XtermWidget xw,
3743 		   int code)
3744 {
3745     Bool result = False;
3746 
3747     (void) xw;
3748     (void) code;
3749 
3750     TRACE(("ResetColorsRequest code=%d\n", code));
3751     if (GetOldColors(xw)) {
3752 	ScrnColors newColors;
3753 	const char *thisName;
3754 	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3755 
3756 	if (xw->misc.re_verse)
3757 	    ndx = oppositeColor(xw, ndx);
3758 
3759 	thisName = xw->screen.Tcolors[ndx].resource;
3760 
3761 	newColors.which = 0;
3762 	newColors.names[ndx] = NULL;
3763 
3764 	if (thisName != 0
3765 	    && xw->work.oldColors->names[ndx] != 0
3766 	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3767 	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3768 
3769 	    if (newColors.which != 0) {
3770 		ChangeColors(xw, &newColors);
3771 		UpdateOldColors(xw, &newColors);
3772 	    }
3773 	}
3774 	result = True;
3775     }
3776     return result;
3777 }
3778 
3779 #if OPT_SHIFT_FONTS
3780 /*
3781  * Initially, 'source' points to '#' or '?'.
3782  *
3783  * Look for an optional sign and optional number.  If those are found, lookup
3784  * the corresponding menu font entry.
3785  */
3786 static int
ParseShiftedFont(XtermWidget xw,String source,String * target)3787 ParseShiftedFont(XtermWidget xw, String source, String *target)
3788 {
3789     TScreen *screen = TScreenOf(xw);
3790     int num = screen->menu_font_number;
3791     int rel = 0;
3792 
3793     if (*++source == '+') {
3794 	rel = 1;
3795 	source++;
3796     } else if (*source == '-') {
3797 	rel = -1;
3798 	source++;
3799     }
3800 
3801     if (isdigit(CharOf(*source))) {
3802 	int val = atoi(source);
3803 	if (rel > 0)
3804 	    rel = val;
3805 	else if (rel < 0)
3806 	    rel = -val;
3807 	else
3808 	    num = val;
3809     }
3810 
3811     if (rel != 0) {
3812 	num = lookupRelativeFontSize(xw,
3813 				     screen->menu_font_number, rel);
3814 
3815     }
3816     TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3817     *target = source;
3818     return num;
3819 }
3820 
3821 static void
QueryFontRequest(XtermWidget xw,String buf,int final)3822 QueryFontRequest(XtermWidget xw, String buf, int final)
3823 {
3824     if (AllowFontOps(xw, efGetFont)) {
3825 	TScreen *screen = TScreenOf(xw);
3826 	Bool success = True;
3827 	int num;
3828 	String base = buf + 1;
3829 	const char *name = 0;
3830 
3831 	num = ParseShiftedFont(xw, buf, &buf);
3832 	if (num < 0
3833 	    || num > fontMenu_lastBuiltin) {
3834 	    Bell(xw, XkbBI_MinorError, 0);
3835 	    success = False;
3836 	} else {
3837 #if OPT_RENDERFONT
3838 	    if (UsingRenderFont(xw)) {
3839 		name = getFaceName(xw, False);
3840 	    } else
3841 #endif
3842 	    if ((name = screen->MenuFontName(num)) == 0) {
3843 		success = False;
3844 	    }
3845 	}
3846 
3847 	unparseputc1(xw, ANSI_OSC);
3848 	unparseputs(xw, "50");
3849 
3850 	if (success) {
3851 	    unparseputc(xw, ';');
3852 	    if (buf >= base) {
3853 		/* identify the font-entry, unless it is the current one */
3854 		if (*buf != '\0') {
3855 		    char temp[10];
3856 
3857 		    unparseputc(xw, '#');
3858 		    sprintf(temp, "%d", num);
3859 		    unparseputs(xw, temp);
3860 		    if (*name != '\0')
3861 			unparseputc(xw, ' ');
3862 		}
3863 	    }
3864 	    unparseputs(xw, name);
3865 	}
3866 
3867 	unparseputc1(xw, final);
3868 	unparse_end(xw);
3869     }
3870 }
3871 
3872 static void
ChangeFontRequest(XtermWidget xw,String buf)3873 ChangeFontRequest(XtermWidget xw, String buf)
3874 {
3875     if (AllowFontOps(xw, efSetFont)) {
3876 	TScreen *screen = TScreenOf(xw);
3877 	Bool success = True;
3878 	int num;
3879 	VTFontNames fonts;
3880 	char *name;
3881 
3882 	/*
3883 	 * If the font specification is a "#", followed by an optional sign and
3884 	 * optional number, lookup the corresponding menu font entry.
3885 	 *
3886 	 * Further, if the "#", etc., is followed by a font name, use that
3887 	 * to load the font entry.
3888 	 */
3889 	if (*buf == '#') {
3890 	    num = ParseShiftedFont(xw, buf, &buf);
3891 
3892 	    if (num < 0
3893 		|| num > fontMenu_lastBuiltin) {
3894 		Bell(xw, XkbBI_MinorError, 0);
3895 		success = False;
3896 	    } else {
3897 		/*
3898 		 * Skip past the optional number, and any whitespace to look
3899 		 * for a font specification within the control.
3900 		 */
3901 		while (isdigit(CharOf(*buf))) {
3902 		    ++buf;
3903 		}
3904 		while (isspace(CharOf(*buf))) {
3905 		    ++buf;
3906 		}
3907 #if OPT_RENDERFONT
3908 		if (UsingRenderFont(xw)) {
3909 		    /* EMPTY */
3910 		    /* there is only one font entry to load */
3911 		    ;
3912 		} else
3913 #endif
3914 		{
3915 		    /*
3916 		     * Normally there is no font specified in the control.
3917 		     * But if there is, simply overwrite the font entry.
3918 		     */
3919 		    if (*buf == '\0') {
3920 			if ((buf = screen->MenuFontName(num)) == 0) {
3921 			    success = False;
3922 			}
3923 		    }
3924 		}
3925 	    }
3926 	} else {
3927 	    num = screen->menu_font_number;
3928 	}
3929 	name = x_strtrim(buf);
3930 	if (screen->EscapeFontName()) {
3931 	    FREE_STRING(screen->EscapeFontName());
3932 	    screen->EscapeFontName() = 0;
3933 	}
3934 	if (success && !IsEmpty(name)) {
3935 #if OPT_RENDERFONT
3936 	    if (UsingRenderFont(xw)) {
3937 		setFaceName(xw, name);
3938 		xtermUpdateFontInfo(xw, True);
3939 	    } else
3940 #endif
3941 	    {
3942 		memset(&fonts, 0, sizeof(fonts));
3943 		fonts.f_n = name;
3944 		SetVTFont(xw, num, True, &fonts);
3945 		if (num == screen->menu_font_number &&
3946 		    num != fontMenu_fontescape) {
3947 		    screen->EscapeFontName() = x_strdup(name);
3948 		}
3949 	    }
3950 	} else {
3951 	    Bell(xw, XkbBI_MinorError, 0);
3952 	}
3953 	update_font_escape();
3954 	free(name);
3955     }
3956 }
3957 #endif /* OPT_SHIFT_FONTS */
3958 
3959 /***====================================================================***/
3960 
3961 void
do_osc(XtermWidget xw,Char * oscbuf,size_t len,int final)3962 do_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3963 {
3964     TScreen *screen = TScreenOf(xw);
3965     int mode;
3966     Char *cp;
3967     int state = 0;
3968     char *buf = 0;
3969     char temp[2];
3970 #if OPT_ISO_COLORS
3971     int ansi_colors = 0;
3972 #endif
3973     Bool need_data = True;
3974     Bool optional_data = False;
3975 
3976     TRACE(("do_osc %s\n", oscbuf));
3977 
3978     (void) screen;
3979 
3980     /*
3981      * Lines should be of the form <OSC> number ; string <ST>, however
3982      * older xterms can accept <BEL> as a final character.  We will respond
3983      * with the same final character as the application sends to make this
3984      * work better with shell scripts, which may have trouble reading an
3985      * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3986      */
3987     mode = 0;
3988     for (cp = oscbuf; *cp != '\0'; cp++) {
3989 	switch (state) {
3990 	case 0:
3991 	    if (isdigit(*cp)) {
3992 		mode = 10 * mode + (*cp - '0');
3993 		if (mode > 65535) {
3994 		    TRACE(("do_osc found unknown mode %d\n", mode));
3995 		    return;
3996 		}
3997 		break;
3998 	    } else {
3999 		switch (*cp) {
4000 		case 'I':
4001 		    xtermLoadIcon(xw, (char *) ++cp);
4002 		    return;
4003 		case 'l':
4004 		    ChangeTitle(xw, (char *) ++cp);
4005 		    return;
4006 		case 'L':
4007 		    ChangeIconName(xw, (char *) ++cp);
4008 		    return;
4009 		}
4010 	    }
4011 	    /* FALLTHRU */
4012 	case 1:
4013 	    if (*cp != ';') {
4014 		TRACE(("do_osc did not find semicolon offset %d\n",
4015 		       (int) (cp - oscbuf)));
4016 		return;
4017 	    }
4018 	    state = 2;
4019 	    break;
4020 	case 2:
4021 	    buf = (char *) cp;
4022 	    state = 3;
4023 	    /* FALLTHRU */
4024 	default:
4025 	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4026 		switch (mode) {
4027 		case 0:
4028 		case 1:
4029 		case 2:
4030 		    break;
4031 		default:
4032 		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
4033 			   CharOf(*cp),
4034 			   (int) (cp - oscbuf)));
4035 		    return;
4036 		}
4037 	    }
4038 	}
4039     }
4040 
4041     /*
4042      * Check if the palette changed and there are no more immediate changes
4043      * that could be deferred to the next repaint.
4044      */
4045     if (xw->work.palette_changed) {
4046 	switch (mode) {
4047 	case 03:		/* change X property */
4048 	case 30:		/* Konsole (unused) */
4049 	case 31:		/* Konsole (unused) */
4050 	case 50:		/* font operations */
4051 	case 51:		/* Emacs (unused) */
4052 #if OPT_PASTE64
4053 	case 52:		/* selection data */
4054 #endif
4055 	    TRACE(("forced repaint after palette changed\n"));
4056 	    xw->work.palette_changed = False;
4057 	    xtermRepaint(xw);
4058 	    break;
4059 	default:
4060 	    xtermNeedSwap(xw, 1);
4061 	    break;
4062 	}
4063     }
4064 
4065     /*
4066      * Most OSC controls other than resets require data.  Handle the others as
4067      * a special case.
4068      */
4069     switch (mode) {
4070     case 50:
4071 #if OPT_ISO_COLORS
4072     case OSC_Reset(4):
4073     case OSC_Reset(5):
4074 	need_data = False;
4075 	optional_data = True;
4076 	break;
4077     case OSC_Reset(OSC_TEXT_FG):
4078     case OSC_Reset(OSC_TEXT_BG):
4079     case OSC_Reset(OSC_TEXT_CURSOR):
4080     case OSC_Reset(OSC_MOUSE_FG):
4081     case OSC_Reset(OSC_MOUSE_BG):
4082 #if OPT_HIGHLIGHT_COLOR
4083     case OSC_Reset(OSC_HIGHLIGHT_BG):
4084     case OSC_Reset(OSC_HIGHLIGHT_FG):
4085 #endif
4086 #if OPT_TEK4014
4087     case OSC_Reset(OSC_TEK_FG):
4088     case OSC_Reset(OSC_TEK_BG):
4089     case OSC_Reset(OSC_TEK_CURSOR):
4090 #endif
4091 	need_data = False;
4092 	break;
4093 #endif
4094     default:
4095 	break;
4096     }
4097 
4098     /*
4099      * Check if we have data when we want, and not when we do not want it.
4100      * Either way, that is a malformed control sequence, and will be ignored.
4101      */
4102     if (IsEmpty(buf)) {
4103 	if (need_data) {
4104 	    TRACE(("do_osc found no data\n"));
4105 	    return;
4106 	}
4107 	temp[0] = '\0';
4108 	buf = temp;
4109     } else if (!need_data && !optional_data) {
4110 	TRACE(("do_osc found unwanted data\n"));
4111 	return;
4112     }
4113 
4114     switch (mode) {
4115     case 0:			/* new icon name and title */
4116 	ChangeIconName(xw, buf);
4117 	ChangeTitle(xw, buf);
4118 	break;
4119 
4120     case 1:			/* new icon name only */
4121 	ChangeIconName(xw, buf);
4122 	break;
4123 
4124     case 2:			/* new title only */
4125 	ChangeTitle(xw, buf);
4126 	break;
4127 
4128     case 3:			/* change X property */
4129 	if (AllowWindowOps(xw, ewSetXprop))
4130 	    ChangeXprop(buf);
4131 	break;
4132 #if OPT_ISO_COLORS
4133     case 5:
4134 	ansi_colors = NUM_ANSI_COLORS;
4135 	/* FALLTHRU */
4136     case 4:
4137 	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4138 	    xw->work.palette_changed = True;
4139 	break;
4140     case 6:
4141 	/* FALLTHRU */
4142     case OSC_Reset(6):
4143 	TRACE(("parse colorXXMode:%s\n", buf));
4144 	while (*buf != '\0') {
4145 	    long which = 0;
4146 	    long value = 0;
4147 	    char *next;
4148 	    if (*buf == ';') {
4149 		++buf;
4150 	    } else {
4151 		which = strtol(buf, &next, 10);
4152 		if (!PartS2L(buf, next) || (which < 0))
4153 		    break;
4154 		buf = next;
4155 		if (*buf == ';')
4156 		    ++buf;
4157 	    }
4158 	    if (*buf == ';') {
4159 		++buf;
4160 	    } else {
4161 		value = strtol(buf, &next, 10);
4162 		if (!PartS2L(buf, next) || (value < 0))
4163 		    break;
4164 		buf = next;
4165 		if (*buf == ';')
4166 		    ++buf;
4167 	    }
4168 	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
4169 	    switch (which) {
4170 	    case 0:
4171 		screen->colorBDMode = (value != 0);
4172 		break;
4173 	    case 1:
4174 		screen->colorULMode = (value != 0);
4175 		break;
4176 	    case 2:
4177 		screen->colorBLMode = (value != 0);
4178 		break;
4179 	    case 3:
4180 		screen->colorRVMode = (value != 0);
4181 		break;
4182 #if OPT_WIDE_ATTRS
4183 	    case 4:
4184 		screen->colorITMode = (value != 0);
4185 		break;
4186 #endif
4187 	    default:
4188 		TRACE(("...unknown colorXXMode\n"));
4189 		break;
4190 	    }
4191 	}
4192 	break;
4193     case OSC_Reset(5):
4194 	ansi_colors = NUM_ANSI_COLORS;
4195 	/* FALLTHRU */
4196     case OSC_Reset(4):
4197 	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4198 	    xw->work.palette_changed = True;
4199 	break;
4200 #endif
4201     case OSC_TEXT_FG:
4202     case OSC_TEXT_BG:
4203     case OSC_TEXT_CURSOR:
4204     case OSC_MOUSE_FG:
4205     case OSC_MOUSE_BG:
4206 #if OPT_HIGHLIGHT_COLOR
4207     case OSC_HIGHLIGHT_BG:
4208     case OSC_HIGHLIGHT_FG:
4209 #endif
4210 #if OPT_TEK4014
4211     case OSC_TEK_FG:
4212     case OSC_TEK_BG:
4213     case OSC_TEK_CURSOR:
4214 #endif
4215 	if (xw->misc.dynamicColors) {
4216 	    ChangeColorsRequest(xw, mode, buf, final);
4217 	}
4218 	break;
4219     case OSC_Reset(OSC_TEXT_FG):
4220     case OSC_Reset(OSC_TEXT_BG):
4221     case OSC_Reset(OSC_TEXT_CURSOR):
4222     case OSC_Reset(OSC_MOUSE_FG):
4223     case OSC_Reset(OSC_MOUSE_BG):
4224 #if OPT_HIGHLIGHT_COLOR
4225     case OSC_Reset(OSC_HIGHLIGHT_BG):
4226     case OSC_Reset(OSC_HIGHLIGHT_FG):
4227 #endif
4228 #if OPT_TEK4014
4229     case OSC_Reset(OSC_TEK_FG):
4230     case OSC_Reset(OSC_TEK_BG):
4231     case OSC_Reset(OSC_TEK_CURSOR):
4232 #endif
4233 	if (xw->misc.dynamicColors) {
4234 	    ResetColorsRequest(xw, mode);
4235 	}
4236 	break;
4237 
4238     case 22:
4239 	xtermSetupPointer(xw, buf);
4240 	break;
4241 
4242     case 30:
4243     case 31:
4244 	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
4245 	break;
4246 
4247 #ifdef ALLOWLOGGING
4248     case 46:			/* new log file */
4249 #ifdef ALLOWLOGFILECHANGES
4250 	/*
4251 	 * Warning, enabling this feature allows people to overwrite
4252 	 * arbitrary files accessible to the person running xterm.
4253 	 */
4254 	if (strcmp(buf, "?")) {
4255 	    char *bp;
4256 	    if ((bp = x_strdup(buf)) != NULL) {
4257 		free(screen->logfile);
4258 		screen->logfile = bp;
4259 		break;
4260 	    }
4261 	}
4262 #endif
4263 	Bell(xw, XkbBI_Info, 0);
4264 	Bell(xw, XkbBI_Info, 0);
4265 	break;
4266 #endif /* ALLOWLOGGING */
4267 
4268     case 50:
4269 #if OPT_SHIFT_FONTS
4270 	if (*buf == '?') {
4271 	    QueryFontRequest(xw, buf, final);
4272 	} else if (xw->misc.shift_fonts) {
4273 	    ChangeFontRequest(xw, buf);
4274 	}
4275 #endif /* OPT_SHIFT_FONTS */
4276 	break;
4277     case 51:
4278 	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
4279 	break;
4280 
4281 #if OPT_PASTE64
4282     case 52:
4283 	ManipulateSelectionData(xw, screen, buf, final);
4284 	break;
4285 #endif
4286 	/*
4287 	 * One could write code to send back the display and host names,
4288 	 * but that could potentially open a fairly nasty security hole.
4289 	 */
4290     default:
4291 	TRACE(("do_osc - unrecognized code\n"));
4292 	break;
4293     }
4294     unparse_end(xw);
4295 }
4296 
4297 /*
4298  * Parse one nibble of a hex byte from the OSC string.  We have removed the
4299  * string-terminator (replacing it with a null), so the only other delimiter
4300  * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4301  * "real" terminals accept commas in the string definitions).
4302  */
4303 static int
udk_value(const char ** cp)4304 udk_value(const char **cp)
4305 {
4306     int result = -1;
4307 
4308     for (;;) {
4309 	int c;
4310 
4311 	if ((c = **cp) != '\0')
4312 	    *cp = *cp + 1;
4313 	if (c == ';' || c == '\0')
4314 	    break;
4315 	if ((result = x_hex2int(c)) >= 0)
4316 	    break;
4317     }
4318 
4319     return result;
4320 }
4321 
4322 void
reset_decudk(XtermWidget xw)4323 reset_decudk(XtermWidget xw)
4324 {
4325     int n;
4326     for (n = 0; n < MAX_UDK; n++) {
4327 	FreeAndNull(xw->work.user_keys[n].str);
4328 	xw->work.user_keys[n].len = 0;
4329     }
4330 }
4331 
4332 /*
4333  * Parse the data for DECUDK (user-defined keys).
4334  */
4335 static void
parse_decudk(XtermWidget xw,const char * cp)4336 parse_decudk(XtermWidget xw, const char *cp)
4337 {
4338     while (*cp) {
4339 	const char *base = cp;
4340 	char *str = malloc(strlen(cp) + 3);
4341 	unsigned key = 0;
4342 	int len = 0;
4343 
4344 	if (str == NULL)
4345 	    break;
4346 
4347 	while (isdigit(CharOf(*cp)))
4348 	    key = (key * 10) + (unsigned) (*cp++ - '0');
4349 
4350 	if (*cp == '/') {
4351 	    int lo, hi;
4352 
4353 	    cp++;
4354 	    while ((hi = udk_value(&cp)) >= 0
4355 		   && (lo = udk_value(&cp)) >= 0) {
4356 		str[len++] = (char) ((hi << 4) | lo);
4357 	    }
4358 	}
4359 	if (len > 0 && key < MAX_UDK) {
4360 	    str[len] = '\0';
4361 	    free(xw->work.user_keys[key].str);
4362 	    xw->work.user_keys[key].str = str;
4363 	    xw->work.user_keys[key].len = len;
4364 	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4365 	} else {
4366 	    free(str);
4367 	}
4368 	if (*cp == ';')
4369 	    cp++;
4370 	if (cp == base)		/* badly-formed sequence - bail out */
4371 	    break;
4372     }
4373 }
4374 
4375 /*
4376  * Parse numeric parameters.  Normally we use a state machine to simplify
4377  * interspersing with control characters, but have the string already.
4378  */
4379 static void
parse_ansi_params(ANSI * params,const char ** string)4380 parse_ansi_params(ANSI *params, const char **string)
4381 {
4382     const char *cp = *string;
4383     ParmType nparam = 0;
4384     int last_empty = 1;
4385 
4386     memset(params, 0, sizeof(*params));
4387     while (*cp != '\0') {
4388 	Char ch = CharOf(*cp++);
4389 
4390 	if (isdigit(ch)) {
4391 	    last_empty = 0;
4392 	    if (nparam < NPARAM) {
4393 		params->a_param[nparam] =
4394 		    (ParmType) ((params->a_param[nparam] * 10)
4395 				+ (ch - '0'));
4396 	    }
4397 	} else if (ch == ';') {
4398 	    last_empty = 1;
4399 	    nparam++;
4400 	} else if (ch < 32) {
4401 	    /* EMPTY */ ;
4402 	} else {
4403 	    /* should be 0x30 to 0x7e */
4404 	    params->a_final = ch;
4405 	    break;
4406 	}
4407     }
4408 
4409     *string = cp;
4410     if (!last_empty)
4411 	nparam++;
4412     if (nparam > NPARAM)
4413 	params->a_nparam = NPARAM;
4414     else
4415 	params->a_nparam = nparam;
4416 }
4417 
4418 #if OPT_TRACE
4419 #define SOFT_WIDE 10
4420 #define SOFT_HIGH 20
4421 
4422 static void
parse_decdld(ANSI * params,const char * string)4423 parse_decdld(ANSI *params, const char *string)
4424 {
4425     char DscsName[8];
4426     int len;
4427     int Pfn = params->a_param[0];
4428     int Pcn = params->a_param[1];
4429     int Pe = params->a_param[2];
4430     int Pcmw = params->a_param[3];
4431     int Pw = params->a_param[4];
4432     int Pt = params->a_param[5];
4433     int Pcmh = params->a_param[6];
4434     int Pcss = params->a_param[7];
4435 
4436     int start_char = Pcn + 0x20;
4437     int char_wide = ((Pcmw == 0)
4438 		     ? (Pcss ? 6 : 10)
4439 		     : (Pcmw > 4
4440 			? Pcmw
4441 			: (Pcmw + 3)));
4442     int char_high = ((Pcmh == 0)
4443 		     ? ((Pcmw >= 2 && Pcmw <= 4)
4444 			? 10
4445 			: 20)
4446 		     : Pcmh);
4447     Char ch;
4448     Char bits[SOFT_HIGH][SOFT_WIDE];
4449     Bool first = True;
4450     Bool prior = False;
4451     int row = 0, col = 0;
4452 
4453     TRACE(("Parsing DECDLD\n"));
4454     TRACE(("  font number   %d\n", Pfn));
4455     TRACE(("  starting char %d\n", Pcn));
4456     TRACE(("  erase control %d\n", Pe));
4457     TRACE(("  char-width    %d\n", Pcmw));
4458     TRACE(("  font-width    %d\n", Pw));
4459     TRACE(("  text/full     %d\n", Pt));
4460     TRACE(("  char-height   %d\n", Pcmh));
4461     TRACE(("  charset-size  %d\n", Pcss));
4462 
4463     if (Pfn > 1
4464 	|| Pcn > 95
4465 	|| Pe > 2
4466 	|| Pcmw > 10
4467 	|| Pcmw == 1
4468 	|| Pt > 2
4469 	|| Pcmh > 20
4470 	|| Pcss > 1
4471 	|| char_wide > SOFT_WIDE
4472 	|| char_high > SOFT_HIGH) {
4473 	TRACE(("DECDLD illegal parameter\n"));
4474 	return;
4475     }
4476 
4477     len = 0;
4478     while (*string != '\0') {
4479 	ch = CharOf(*string++);
4480 	if (ch >= ANSI_SPA && ch <= 0x2f) {
4481 	    if (len < 2)
4482 		DscsName[len++] = (char) ch;
4483 	} else if (ch >= 0x30 && ch <= 0x7e) {
4484 	    DscsName[len++] = (char) ch;
4485 	    break;
4486 	}
4487     }
4488     DscsName[len] = 0;
4489     TRACE(("  Dscs name     '%s'\n", DscsName));
4490 
4491     TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4492     while (*string != '\0') {
4493 	if (first) {
4494 	    TRACE(("Char %d:\n", start_char));
4495 	    if (prior) {
4496 		for (row = 0; row < char_high; ++row) {
4497 		    TRACE(("%.*s\n", char_wide, bits[row]));
4498 		}
4499 	    }
4500 	    prior = False;
4501 	    first = False;
4502 	    for (row = 0; row < char_high; ++row) {
4503 		for (col = 0; col < char_wide; ++col) {
4504 		    bits[row][col] = '.';
4505 		}
4506 	    }
4507 	    row = col = 0;
4508 	}
4509 	ch = CharOf(*string++);
4510 	if (ch >= 0x3f && ch <= 0x7e) {
4511 	    int n;
4512 
4513 	    ch = CharOf(ch - 0x3f);
4514 	    for (n = 0; n < 6; ++n) {
4515 		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4516 	    }
4517 	    col += 1;
4518 	    prior = True;
4519 	} else if (ch == '/') {
4520 	    row += 6;
4521 	    col = 0;
4522 	} else if (ch == ';') {
4523 	    first = True;
4524 	    ++start_char;
4525 	}
4526     }
4527 }
4528 #else
4529 #define parse_decdld(p,q)	/* nothing */
4530 #endif
4531 
4532 #if OPT_DEC_RECTOPS
4533 static const char *
skip_params(const char * cp)4534 skip_params(const char *cp)
4535 {
4536     while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4537 	++cp;
4538     return cp;
4539 }
4540 
4541 static int
parse_int_param(const char ** cp)4542 parse_int_param(const char **cp)
4543 {
4544     int result = 0;
4545     const char *s = *cp;
4546     while (*s != '\0') {
4547 	if (*s == ';') {
4548 	    ++s;
4549 	    break;
4550 	} else if (*s >= '0' && *s <= '9') {
4551 	    result = (result * 10) + (*s++ - '0');
4552 	} else {
4553 	    s += strlen(s);
4554 	}
4555     }
4556     TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
4557     *cp = s;
4558     return result;
4559 }
4560 
4561 static int
parse_chr_param(const char ** cp)4562 parse_chr_param(const char **cp)
4563 {
4564     int result = 0;
4565     const char *s = *cp;
4566     if (*s != '\0') {
4567 	if ((result = CharOf(*s++)) != 0) {
4568 	    if (*s == ';') {
4569 		++s;
4570 	    } else if (*s != '\0') {
4571 		result = 0;
4572 	    }
4573 	}
4574     }
4575     TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
4576     *cp = s;
4577     return result;
4578 }
4579 
4580 static void
restore_DECCIR(XtermWidget xw,const char * cp)4581 restore_DECCIR(XtermWidget xw, const char *cp)
4582 {
4583     TScreen *screen = TScreenOf(xw);
4584     int value;
4585 
4586     /* row */
4587     if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4588 	return;
4589     screen->cur_row = (value - 1);
4590 
4591     /* column */
4592     if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4593 	return;
4594     screen->cur_col = (value - 1);
4595 
4596     /* page */
4597     if (parse_int_param(&cp) != 1)
4598 	return;
4599 
4600     /* rendition */
4601     if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4602 	return;
4603     UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4604     xw->flags |= (value & 8) ? INVERSE : 0;
4605     xw->flags |= (value & 4) ? BLINK : 0;
4606     xw->flags |= (value & 2) ? UNDERLINE : 0;
4607     xw->flags |= (value & 1) ? BOLD : 0;
4608 
4609     /* attributes */
4610     if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4611 	return;
4612     screen->protected_mode &= ~DEC_PROTECT;
4613     screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4614 
4615     /* flags */
4616     if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4617 	return;
4618     screen->do_wrap = (value & 8) ? True : False;
4619     screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4620     UIntClr(xw->flags, ORIGIN);
4621     xw->flags |= (value & 1) ? ORIGIN : 0;
4622 
4623     if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4624 	return;
4625     screen->curgl = (Char) value;
4626 
4627     if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4628 	return;
4629     screen->curgr = (Char) value;
4630 
4631     /* character-set size */
4632     if (parse_chr_param(&cp) != 0x4f)	/* works for xterm */
4633 	return;
4634 
4635     /* SCS designators */
4636     for (value = 0; value < NUM_GSETS; ++value) {
4637 	if (*cp == '%') {
4638 	    xtermDecodeSCS(xw, value, 0, '%', *++cp);
4639 	} else if (*cp != '\0') {
4640 	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4641 	} else {
4642 	    return;
4643 	}
4644 	cp++;
4645     }
4646 
4647     TRACE(("...done DECCIR\n"));
4648 }
4649 
4650 static void
restore_DECTABSR(XtermWidget xw,const char * cp)4651 restore_DECTABSR(XtermWidget xw, const char *cp)
4652 {
4653     int stop = 0;
4654     Bool fail = False;
4655 
4656     TabZonk(xw->tabs);
4657     while (*cp != '\0' && !fail) {
4658 	if ((*cp) >= '0' && (*cp) <= '9') {
4659 	    stop = (stop * 10) + ((*cp) - '0');
4660 	} else if (*cp == '/') {
4661 	    --stop;
4662 	    if (OkTAB(stop)) {
4663 		TabSet(xw->tabs, stop);
4664 		stop = 0;
4665 	    } else {
4666 		fail = True;
4667 	    }
4668 	} else {
4669 	    fail = True;
4670 	}
4671 	++cp;
4672     }
4673     --stop;
4674     if (OkTAB(stop))
4675 	TabSet(xw->tabs, stop);
4676 
4677     TRACE(("...done DECTABSR\n"));
4678 }
4679 #endif
4680 
4681 void
do_dcs(XtermWidget xw,Char * dcsbuf,size_t dcslen)4682 do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4683 {
4684     TScreen *screen = TScreenOf(xw);
4685     char reply[BUFSIZ];
4686     const char *cp = (const char *) dcsbuf;
4687     Bool okay;
4688     ANSI params;
4689 #if OPT_DEC_RECTOPS
4690     char psarg = '0';
4691 #endif
4692 
4693     TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4694 
4695     if (dcslen != strlen(cp))
4696 	/* shouldn't have nulls in the string */
4697 	return;
4698 
4699     switch (*cp) {		/* intermediate character, or parameter */
4700     case '$':			/* DECRQSS */
4701 	okay = True;
4702 
4703 	cp++;
4704 	if (*cp == 'q') {
4705 	    *reply = '\0';
4706 	    cp++;
4707 	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4708 		TRACE(("DECRQSS -> DECSCA\n"));
4709 		sprintf(reply, "%d%s",
4710 			(screen->protected_mode == DEC_PROTECT)
4711 			&& (xw->flags & PROTECTED) ? 1 : 0,
4712 			cp);
4713 	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
4714 		if (screen->vtXX_level < 2) {
4715 		    /* actually none of DECRQSS is valid for vt100's */
4716 		    break;
4717 		}
4718 		TRACE(("DECRQSS -> DECSCL\n"));
4719 		sprintf(reply, "%d%s%s",
4720 			(screen->vtXX_level ?
4721 			 screen->vtXX_level : 1) + 60,
4722 			(screen->vtXX_level >= 2)
4723 			? (screen->control_eight_bits
4724 			   ? ";0" : ";1")
4725 			: "",
4726 			cp);
4727 	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4728 		TRACE(("DECRQSS -> DECSTBM\n"));
4729 		sprintf(reply, "%d;%dr",
4730 			screen->top_marg + 1,
4731 			screen->bot_marg + 1);
4732 	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
4733 		if (screen->vtXX_level >= 4) {	/* VT420 */
4734 		    TRACE(("DECRQSS -> DECSLRM\n"));
4735 		    sprintf(reply, "%d;%ds",
4736 			    screen->lft_marg + 1,
4737 			    screen->rgt_marg + 1);
4738 		} else {
4739 		    okay = False;
4740 		}
4741 	    } else if (!strcmp(cp, "m")) {	/* SGR */
4742 		TRACE(("DECRQSS -> SGR\n"));
4743 		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4744 		strcat(reply, "m");
4745 	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
4746 		int code = STEADY_BLOCK;
4747 		if (isCursorUnderline(screen))
4748 		    code = STEADY_UNDERLINE;
4749 		else if (isCursorBar(screen))
4750 		    code = STEADY_BAR;
4751 #if OPT_BLINK_CURS
4752 		if (screen->cursor_blink_esc != 0)
4753 		    code -= 1;
4754 #endif
4755 		TRACE(("reply DECSCUSR\n"));
4756 		sprintf(reply, "%d%s", code, cp);
4757 	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4758 		sprintf(reply, "%d%s",
4759 			((screen->max_row > 24) ? screen->max_row : 24),
4760 			cp);
4761 		TRACE(("reply DECSLPP\n"));
4762 	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4763 		TRACE(("reply DECSCPP\n"));
4764 		sprintf(reply, "%d%s",
4765 			((xw->flags & IN132COLUMNS) ? 132 : 80),
4766 			cp);
4767 	    } else if (!strcmp(cp, "*|")) {	/* DECSNLS */
4768 		TRACE(("reply DECSNLS\n"));
4769 		sprintf(reply, "%d%s",
4770 			screen->max_row + 1,
4771 			cp);
4772 	    } else {
4773 		okay = False;
4774 	    }
4775 
4776 	    unparseputc1(xw, ANSI_DCS);
4777 	    unparseputc(xw, okay ? '1' : '0');
4778 	    unparseputc(xw, '$');
4779 	    unparseputc(xw, 'r');
4780 	    cp = reply;
4781 	    unparseputs(xw, cp);
4782 	    unparseputc1(xw, ANSI_ST);
4783 	} else {
4784 	    unparseputc(xw, ANSI_CAN);
4785 	}
4786 	break;
4787     case '+':
4788 	cp++;
4789 	switch (*cp) {
4790 #if OPT_TCAP_QUERY
4791 	case 'p':
4792 	    if (AllowTcapOps(xw, etSetTcap)) {
4793 		set_termcap(xw, cp + 1);
4794 	    }
4795 	    break;
4796 	case 'q':
4797 	    if (AllowTcapOps(xw, etGetTcap)) {
4798 		Bool fkey;
4799 		unsigned state;
4800 		int code;
4801 		const char *tmp;
4802 		const char *parsed = ++cp;
4803 
4804 		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4805 
4806 		unparseputc1(xw, ANSI_DCS);
4807 
4808 		unparseputc(xw, code >= 0 ? '1' : '0');
4809 
4810 		unparseputc(xw, '+');
4811 		unparseputc(xw, 'r');
4812 
4813 		while (*cp != 0 && (code >= -1)) {
4814 		    if (cp == parsed)
4815 			break;	/* no data found, error */
4816 
4817 		    for (tmp = cp; tmp != parsed; ++tmp)
4818 			unparseputc(xw, *tmp);
4819 
4820 		    if (code >= 0) {
4821 			unparseputc(xw, '=');
4822 			screen->tc_query_code = code;
4823 			screen->tc_query_fkey = fkey;
4824 #if OPT_ISO_COLORS
4825 			/* XK_COLORS is a fake code for the "Co" entry (maximum
4826 			 * number of colors) */
4827 			if (code == XK_COLORS) {
4828 			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
4829 			} else
4830 #if OPT_DIRECT_COLOR
4831 			if (code == XK_RGB) {
4832 			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
4833 				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
4834 				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
4835 				    unparseputn(xw, xw->rgb_widths[0]);
4836 				} else {
4837 				    char temp[1024];
4838 				    sprintf(temp, "%d/%d/%d",
4839 					    xw->rgb_widths[0],
4840 					    xw->rgb_widths[1],
4841 					    xw->rgb_widths[2]);
4842 				    unparseputs(xw, temp);
4843 				}
4844 			    } else {
4845 				unparseputs(xw, "-1");
4846 			    }
4847 			} else
4848 #endif
4849 #endif
4850 			if (code == XK_TCAPNAME) {
4851 			    unparseputs(xw, resource.term_name);
4852 			} else {
4853 			    XKeyEvent event;
4854 			    memset(&event, 0, sizeof(event));
4855 			    event.state = state;
4856 			    Input(xw, &event, False);
4857 			}
4858 			screen->tc_query_code = -1;
4859 		    } else {
4860 			break;	/* no match found, error */
4861 		    }
4862 
4863 		    cp = parsed;
4864 		    if (*parsed == ';') {
4865 			unparseputc(xw, *parsed++);
4866 			cp = parsed;
4867 			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4868 		    }
4869 		}
4870 		unparseputc1(xw, ANSI_ST);
4871 	    }
4872 	    break;
4873 #endif
4874 #if OPT_XRES_QUERY
4875 	case 'Q':
4876 	    ++cp;
4877 	    if (AllowXResOps(xw)) {
4878 		Boolean first = True;
4879 		while (*cp != '\0') {
4880 		    const char *parsed = 0;
4881 		    const char *tmp;
4882 		    char *name = x_decode_hex(cp, &parsed);
4883 		    char *value;
4884 		    char *result;
4885 		    if (cp == parsed || name == NULL) {
4886 			free(name);
4887 			break;	/* no data found, error */
4888 		    }
4889 		    TRACE(("query-feature '%s'\n", name));
4890 		    if ((value = vt100ResourceToString(xw, name)) != 0) {
4891 			okay = True;	/* valid */
4892 		    } else {
4893 			okay = False;	/* invalid */
4894 		    }
4895 		    if (first) {
4896 			unparseputc1(xw, ANSI_DCS);
4897 			unparseputc(xw, okay ? '1' : '0');
4898 			unparseputc(xw, '+');
4899 			unparseputc(xw, 'R');
4900 			first = False;
4901 		    }
4902 
4903 		    for (tmp = cp; tmp != parsed; ++tmp)
4904 			unparseputc(xw, *tmp);
4905 
4906 		    if (value != 0) {
4907 			unparseputc1(xw, '=');
4908 			result = x_encode_hex(value);
4909 			unparseputs(xw, result);
4910 		    } else {
4911 			result = NULL;
4912 		    }
4913 
4914 		    free(name);
4915 		    free(value);
4916 		    free(result);
4917 
4918 		    cp = parsed;
4919 		    if (*parsed == ';') {
4920 			unparseputc(xw, *parsed++);
4921 			cp = parsed;
4922 		    }
4923 		}
4924 		if (!first)
4925 		    unparseputc1(xw, ANSI_ST);
4926 	    }
4927 	    break;
4928 #endif
4929 	}
4930 	break;
4931 #if OPT_DEC_RECTOPS
4932     case '1':
4933 	/* FALLTHRU */
4934     case '2':
4935 	if (*skip_params(cp) == '$') {
4936 	    psarg = *cp++;
4937 	    if ((*cp++ == '$')
4938 		&& (*cp++ == 't')
4939 		&& (screen->vtXX_level >= 3)) {
4940 		switch (psarg) {
4941 		case '1':
4942 		    TRACE(("DECRSPS (DECCIR)\n"));
4943 		    restore_DECCIR(xw, cp);
4944 		    break;
4945 		case '2':
4946 		    TRACE(("DECRSPS (DECTABSR)\n"));
4947 		    restore_DECTABSR(xw, cp);
4948 		    break;
4949 		}
4950 	    }
4951 	    break;
4952 	}
4953 #endif
4954 	/* FALLTHRU */
4955     default:
4956 	if (optRegisGraphics(screen) ||
4957 	    optSixelGraphics(screen) ||
4958 	    screen->vtXX_level >= 2) {	/* VT220 */
4959 	    parse_ansi_params(&params, &cp);
4960 	    switch (params.a_final) {
4961 	    case 'p':		/* ReGIS */
4962 #if OPT_REGIS_GRAPHICS
4963 		if (optRegisGraphics(screen)) {
4964 		    parse_regis(xw, &params, cp);
4965 		}
4966 #else
4967 		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
4968 #endif
4969 		break;
4970 	    case 'q':		/* sixel */
4971 #if OPT_SIXEL_GRAPHICS
4972 		if (optSixelGraphics(screen)) {
4973 		    (void) parse_sixel(xw, &params, cp);
4974 		}
4975 #else
4976 		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
4977 #endif
4978 		break;
4979 	    case '|':		/* DECUDK */
4980 		if (screen->vtXX_level >= 2) {	/* VT220 */
4981 		    if (params.a_param[0] == 0)
4982 			reset_decudk(xw);
4983 		    parse_decudk(xw, cp);
4984 		}
4985 		break;
4986 	    case L_CURL:	/* DECDLD */
4987 		if (screen->vtXX_level >= 2) {	/* VT220 */
4988 		    parse_decdld(&params, cp);
4989 		}
4990 		break;
4991 	    }
4992 	}
4993 	break;
4994     }
4995     unparse_end(xw);
4996 }
4997 
4998 #if OPT_DEC_RECTOPS
4999 enum {
5000     mdUnknown = 0,
5001     mdMaybeSet = 1,
5002     mdMaybeReset = 2,
5003     mdAlwaysSet = 3,
5004     mdAlwaysReset = 4
5005 };
5006 
5007 #define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
5008 #define MdFlag(mode,flag) MdBool((mode) & (flag))
5009 
5010 /*
5011  * Reply is the same format as the query, with pair of mode/value:
5012  * 0 - not recognized
5013  * 1 - set
5014  * 2 - reset
5015  * 3 - permanently set
5016  * 4 - permanently reset
5017  * Only one mode can be reported at a time.
5018  */
5019 void
do_ansi_rqm(XtermWidget xw,int nparams,int * params)5020 do_ansi_rqm(XtermWidget xw, int nparams, int *params)
5021 {
5022     ANSI reply;
5023     int count = 0;
5024 
5025     TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5026     memset(&reply, 0, sizeof(reply));
5027 
5028     if (nparams >= 1) {
5029 	int result = mdUnknown;
5030 
5031 	/* DECRQM can only ask about one mode at a time */
5032 	switch (params[0]) {
5033 	case 1:		/* GATM */
5034 	    result = mdAlwaysReset;
5035 	    break;
5036 	case 2:
5037 	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
5038 	    break;
5039 	case 3:		/* CRM */
5040 	    result = mdMaybeReset;
5041 	    break;
5042 	case 4:
5043 	    result = MdFlag(xw->flags, INSERT);
5044 	    break;
5045 	case 5:		/* SRTM */
5046 	case 7:		/* VEM */
5047 	case 10:		/* HEM */
5048 	case 11:		/* PUM */
5049 	    result = mdAlwaysReset;
5050 	    break;
5051 	case 12:
5052 	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
5053 	    break;
5054 	case 13:		/* FEAM */
5055 	case 14:		/* FETM */
5056 	case 15:		/* MATM */
5057 	case 16:		/* TTM */
5058 	case 17:		/* SATM */
5059 	case 18:		/* TSM */
5060 	case 19:		/* EBM */
5061 	    result = mdAlwaysReset;
5062 	    break;
5063 	case 20:
5064 	    result = MdFlag(xw->flags, LINEFEED);
5065 	    break;
5066 	}
5067 	reply.a_param[count++] = (ParmType) params[0];
5068 	reply.a_param[count++] = (ParmType) result;
5069     }
5070     reply.a_type = ANSI_CSI;
5071     reply.a_nparam = (ParmType) count;
5072     reply.a_inters = '$';
5073     reply.a_final = 'y';
5074     unparseseq(xw, &reply);
5075 }
5076 
5077 void
do_dec_rqm(XtermWidget xw,int nparams,int * params)5078 do_dec_rqm(XtermWidget xw, int nparams, int *params)
5079 {
5080     ANSI reply;
5081     int count = 0;
5082 
5083     TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5084     memset(&reply, 0, sizeof(reply));
5085 
5086     if (nparams >= 1) {
5087 	TScreen *screen = TScreenOf(xw);
5088 	int result = mdUnknown;
5089 
5090 	/* DECRQM can only ask about one mode at a time */
5091 	switch ((DECSET_codes) params[0]) {
5092 	case srm_DECCKM:
5093 	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5094 	    break;
5095 	case srm_DECANM:	/* ANSI/VT52 mode      */
5096 #if OPT_VT52_MODE
5097 	    result = MdBool(screen->vtXX_level >= 1);
5098 #else
5099 	    result = mdMaybeSet;
5100 #endif
5101 	    break;
5102 	case srm_DECCOLM:
5103 	    result = MdFlag(xw->flags, IN132COLUMNS);
5104 	    break;
5105 	case srm_DECSCLM:	/* (slow scroll)        */
5106 	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5107 	    break;
5108 	case srm_DECSCNM:
5109 	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5110 	    break;
5111 	case srm_DECOM:
5112 	    result = MdFlag(xw->flags, ORIGIN);
5113 	    break;
5114 	case srm_DECAWM:
5115 	    result = MdFlag(xw->flags, WRAPAROUND);
5116 	    break;
5117 	case srm_DECARM:
5118 	    result = mdAlwaysReset;
5119 	    break;
5120 	case srm_X10_MOUSE:	/* X10 mouse                    */
5121 	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5122 	    break;
5123 #if OPT_TOOLBAR
5124 	case srm_RXVT_TOOLBAR:
5125 	    result = MdBool(resource.toolBar);
5126 	    break;
5127 #endif
5128 #if OPT_BLINK_CURS
5129 	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5130 	    result = MdBool(screen->cursor_blink_esc);
5131 	    break;
5132 	case srm_CURSOR_BLINK_OPS:
5133 	    switch (screen->cursor_blink) {
5134 	    case cbTrue:
5135 		result = mdMaybeSet;
5136 		break;
5137 	    case cbFalse:
5138 		result = mdMaybeReset;
5139 		break;
5140 	    case cbAlways:
5141 		result = mdAlwaysSet;
5142 		break;
5143 	    case cbLAST:
5144 		/* FALLTHRU */
5145 	    case cbNever:
5146 		result = mdAlwaysReset;
5147 		break;
5148 	    }
5149 	    break;
5150 	case srm_XOR_CURSOR_BLINKS:
5151 	    result = (screen->cursor_blink_xor
5152 		      ? mdAlwaysSet
5153 		      : mdAlwaysReset);
5154 	    break;
5155 #endif
5156 	case srm_DECPFF:	/* print form feed */
5157 	    result = MdBool(PrinterOf(screen).printer_formfeed);
5158 	    break;
5159 	case srm_DECPEX:	/* print extent */
5160 	    result = MdBool(PrinterOf(screen).printer_extent);
5161 	    break;
5162 	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5163 	    result = MdBool(screen->cursor_set);
5164 	    break;
5165 	case srm_RXVT_SCROLLBAR:
5166 	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5167 	    break;
5168 #if OPT_SHIFT_FONTS
5169 	case srm_RXVT_FONTSIZE:
5170 	    result = MdBool(xw->misc.shift_fonts);
5171 	    break;
5172 #endif
5173 #if OPT_TEK4014
5174 	case srm_DECTEK:
5175 	    result = MdBool(TEK4014_ACTIVE(xw));
5176 	    break;
5177 #endif
5178 	case srm_132COLS:
5179 	    result = MdBool(screen->c132);
5180 	    break;
5181 	case srm_CURSES_HACK:
5182 	    result = MdBool(screen->curses);
5183 	    break;
5184 	case srm_DECNRCM:	/* national charset (VT220) */
5185 	    if (screen->vtXX_level >= 2) {
5186 		result = MdFlag(xw->flags, NATIONAL);
5187 	    } else {
5188 		result = 0;
5189 	    }
5190 	    break;
5191 	case srm_MARGIN_BELL:	/* margin bell                  */
5192 	    result = MdBool(screen->marginbell);
5193 	    break;
5194 #if OPT_PRINT_GRAPHICS
5195 	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5196 	    result = MdBool(screen->graphics_expanded_print_mode);
5197 	    break;
5198 #endif
5199 	case srm_REVERSEWRAP:	/* reverse wraparound   */
5200 	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5201 		result = MdFlag(xw->flags, REVERSEWRAP);
5202 	    break;
5203 #if defined(ALLOWLOGGING)
5204 	case srm_ALLOWLOGGING:	/* logging              */
5205 	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5206 #if defined(ALLOWLOGFILEONOFF)
5207 		result = MdBool(screen->logging);
5208 #else
5209 		result = ((MdBool(screen->logging) == mdMaybeSet)
5210 			  ? mdAlwaysSet
5211 			  : mdAlwaysReset);
5212 #endif
5213 	    break;
5214 #elif OPT_PRINT_GRAPHICS
5215 	case srm_DECGPBM:	/* Graphics Print Background Mode */
5216 	    result = MdBool(screen->graphics_print_background_mode);
5217 	    break;
5218 #endif
5219 	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5220 	    /* FALLTHRU */
5221 	case srm_OPT_ALTBUF:
5222 	    result = MdBool(screen->whichBuf);
5223 	    break;
5224 	case srm_ALTBUF:
5225 	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5226 		result = MdBool(screen->whichBuf);
5227 	    break;
5228 	case srm_DECNKM:
5229 	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5230 	    break;
5231 	case srm_DECBKM:
5232 	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5233 	    break;
5234 	case srm_DECLRMM:
5235 	    if (screen->vtXX_level >= 4) {	/* VT420 */
5236 		result = MdFlag(xw->flags, LEFT_RIGHT);
5237 	    } else {
5238 		result = 0;
5239 	    }
5240 	    break;
5241 #if OPT_SIXEL_GRAPHICS
5242 	case srm_DECSDM:
5243 	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5244 	    break;
5245 #endif
5246 	case srm_DECNCSM:
5247 	    if (screen->vtXX_level >= 5) {	/* VT510 */
5248 		result = MdFlag(xw->flags, NOCLEAR_COLM);
5249 	    } else {
5250 		result = 0;
5251 	    }
5252 	    break;
5253 	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5254 	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5255 	    break;
5256 	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5257 	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5258 	    break;
5259 	case srm_BTN_EVENT_MOUSE:
5260 	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5261 	    break;
5262 	case srm_ANY_EVENT_MOUSE:
5263 	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5264 	    break;
5265 #if OPT_FOCUS_EVENT
5266 	case srm_FOCUS_EVENT_MOUSE:
5267 	    result = MdBool(screen->send_focus_pos);
5268 	    break;
5269 #endif
5270 	case srm_EXT_MODE_MOUSE:
5271 	    /* FALLTHRU */
5272 	case srm_SGR_EXT_MODE_MOUSE:
5273 	    /* FALLTHRU */
5274 	case srm_URXVT_EXT_MODE_MOUSE:
5275 	    /* FALLTHRU */
5276 	case srm_PIXEL_POSITION_MOUSE:
5277 	    result = MdBool(screen->extend_coords == params[0]);
5278 	    break;
5279 	case srm_ALTERNATE_SCROLL:
5280 	    result = MdBool(screen->alternateScroll);
5281 	    break;
5282 	case srm_RXVT_SCROLL_TTY_OUTPUT:
5283 	    result = MdBool(screen->scrollttyoutput);
5284 	    break;
5285 	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5286 	    result = MdBool(screen->scrollkey);
5287 	    break;
5288 	case srm_EIGHT_BIT_META:
5289 	    result = MdBool(screen->eight_bit_meta);
5290 	    break;
5291 #if OPT_NUM_LOCK
5292 	case srm_REAL_NUMLOCK:
5293 	    result = MdBool(xw->misc.real_NumLock);
5294 	    break;
5295 	case srm_META_SENDS_ESC:
5296 	    result = MdBool(screen->meta_sends_esc);
5297 	    break;
5298 #endif
5299 	case srm_DELETE_IS_DEL:
5300 	    result = MdBool(xtermDeleteIsDEL(xw));
5301 	    break;
5302 #if OPT_NUM_LOCK
5303 	case srm_ALT_SENDS_ESC:
5304 	    result = MdBool(screen->alt_sends_esc);
5305 	    break;
5306 #endif
5307 	case srm_KEEP_SELECTION:
5308 	    result = MdBool(screen->keepSelection);
5309 	    break;
5310 	case srm_SELECT_TO_CLIPBOARD:
5311 	    result = MdBool(screen->selectToClipboard);
5312 	    break;
5313 	case srm_BELL_IS_URGENT:
5314 	    result = MdBool(screen->bellIsUrgent);
5315 	    break;
5316 	case srm_POP_ON_BELL:
5317 	    result = MdBool(screen->poponbell);
5318 	    break;
5319 	case srm_KEEP_CLIPBOARD:
5320 	    result = MdBool(screen->keepClipboard);
5321 	    break;
5322 	case srm_ALLOW_ALTBUF:
5323 	    result = MdBool(xw->misc.titeInhibit);
5324 	    break;
5325 	case srm_SAVE_CURSOR:
5326 	    result = MdBool(screen->sc[screen->whichBuf].saved);
5327 	    break;
5328 #if OPT_TCAP_FKEYS
5329 	case srm_TCAP_FKEYS:
5330 	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5331 	    break;
5332 #endif
5333 #if OPT_SUN_FUNC_KEYS
5334 	case srm_SUN_FKEYS:
5335 	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5336 	    break;
5337 #endif
5338 #if OPT_HP_FUNC_KEYS
5339 	case srm_HP_FKEYS:
5340 	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5341 	    break;
5342 #endif
5343 #if OPT_SCO_FUNC_KEYS
5344 	case srm_SCO_FKEYS:
5345 	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5346 	    break;
5347 #endif
5348 	case srm_LEGACY_FKEYS:
5349 	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5350 	    break;
5351 #if OPT_SUNPC_KBD
5352 	case srm_VT220_FKEYS:
5353 	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5354 	    break;
5355 #endif
5356 #if OPT_PASTE64 || OPT_READLINE
5357 	case srm_PASTE_IN_BRACKET:
5358 	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5359 	    break;
5360 #endif
5361 #if OPT_READLINE
5362 	case srm_BUTTON1_MOVE_POINT:
5363 	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5364 	    break;
5365 	case srm_BUTTON2_MOVE_POINT:
5366 	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5367 	    break;
5368 	case srm_DBUTTON3_DELETE:
5369 	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5370 	    break;
5371 	case srm_PASTE_QUOTE:
5372 	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5373 	    break;
5374 	case srm_PASTE_LITERAL_NL:
5375 	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5376 	    break;
5377 #endif /* OPT_READLINE */
5378 #if OPT_GRAPHICS
5379 	case srm_PRIVATE_COLOR_REGISTERS:
5380 	    result = MdBool(screen->privatecolorregisters);
5381 	    break;
5382 #endif
5383 #if OPT_SIXEL_GRAPHICS
5384 	case srm_SIXEL_SCROLLS_RIGHT:
5385 	    result = MdBool(screen->sixel_scrolls_right);
5386 	    break;
5387 #endif
5388 	default:
5389 	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
5390 		   params[0]));
5391 	}
5392 	reply.a_param[count++] = (ParmType) params[0];
5393 	reply.a_param[count++] = (ParmType) result;
5394 	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5395     }
5396     reply.a_type = ANSI_CSI;
5397     reply.a_pintro = '?';
5398     reply.a_nparam = (ParmType) count;
5399     reply.a_inters = '$';
5400     reply.a_final = 'y';
5401     unparseseq(xw, &reply);
5402 }
5403 #endif /* OPT_DEC_RECTOPS */
5404 
5405 char *
udk_lookup(XtermWidget xw,int keycode,int * len)5406 udk_lookup(XtermWidget xw, int keycode, int *len)
5407 {
5408     char *result = NULL;
5409     if (keycode >= 0 && keycode < MAX_UDK) {
5410 	*len = xw->work.user_keys[keycode].len;
5411 	result = xw->work.user_keys[keycode].str;
5412 	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5413     } else {
5414 	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5415     }
5416     return result;
5417 }
5418 
5419 #if OPT_REPORT_ICONS
5420 void
report_icons(const char * fmt,...)5421 report_icons(const char *fmt, ...)
5422 {
5423     if (resource.reportIcons) {
5424 	va_list ap;
5425 	va_start(ap, fmt);
5426 	vfprintf(stdout, fmt, ap);
5427 	va_end(ap);
5428 #if OPT_TRACE
5429 	va_start(ap, fmt);
5430 	TraceVA(fmt, ap);
5431 	va_end(ap);
5432 #endif
5433     }
5434 }
5435 #endif
5436 
5437 #ifdef HAVE_LIBXPM
5438 
5439 #ifndef PIXMAP_ROOTDIR
5440 #define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
5441 #endif
5442 
5443 typedef struct {
5444     const char *name;
5445     const char *const *data;
5446 } XPM_DATA;
5447 
5448 static char *
x_find_icon(char ** work,int * state,const char * filename,const char * suffix)5449 x_find_icon(char **work, int *state, const char *filename, const char *suffix)
5450 {
5451     const char *prefix = PIXMAP_ROOTDIR;
5452     const char *larger = "_48x48";
5453     char *result = 0;
5454 
5455     if (*state >= 0) {
5456 	if ((*state & 1) == 0)
5457 	    suffix = "";
5458 	if ((*state & 2) == 0)
5459 	    larger = "";
5460 	if ((*state & 4) == 0) {
5461 	    prefix = "";
5462 	} else if (!strncmp(filename, "/", (size_t) 1) ||
5463 		   !strncmp(filename, "./", (size_t) 2) ||
5464 		   !strncmp(filename, "../", (size_t) 3)) {
5465 	    *state = -1;
5466 	} else if (*state >= 8) {
5467 	    *state = -1;
5468 	}
5469     }
5470 
5471     if (*state >= 0) {
5472 	size_t length;
5473 
5474 	FreeAndNull(*work);
5475 	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
5476 	    strlen(suffix);
5477 	if ((result = malloc(length)) != 0) {
5478 	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
5479 	    *work = result;
5480 	}
5481 	*state += 1;
5482     }
5483     TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
5484     return result;
5485 }
5486 
5487 #if OPT_BUILTIN_XPMS
5488 
5489 static const XPM_DATA *
built_in_xpm(const XPM_DATA * table,Cardinal length,const char * find)5490 built_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
5491 {
5492     const XPM_DATA *result = 0;
5493     if (!IsEmpty(find)) {
5494 	Cardinal n;
5495 	for (n = 0; n < length; ++n) {
5496 	    if (!x_strcasecmp(find, table[n].name)) {
5497 		result = table + n;
5498 		ReportIcons(("use builtin-icon %s\n", table[n].name));
5499 		break;
5500 	    }
5501 	}
5502 
5503 	/*
5504 	 * As a fallback, check if the icon name matches without the lengths,
5505 	 * which are all _HHxWW format.
5506 	 */
5507 	if (result == 0) {
5508 	    const char *base = table[0].name;
5509 	    const char *last = strchr(base, '_');
5510 	    if (last != 0
5511 		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
5512 		result = table + length - 1;
5513 		ReportIcons(("use builtin-icon %s\n", table[0].name));
5514 	    }
5515 	}
5516     }
5517     return result;
5518 }
5519 #define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
5520 #endif /* OPT_BUILTIN_XPMS */
5521 
5522 typedef enum {
5523     eHintDefault = 0		/* use the largest builtin-icon */
5524     ,eHintNone
5525     ,eHintSearch
5526 } ICON_HINT;
5527 #endif /* HAVE_LIBXPM */
5528 
5529 int
getVisualDepth(XtermWidget xw)5530 getVisualDepth(XtermWidget xw)
5531 {
5532     int result = 0;
5533 
5534     if (getVisualInfo(xw)) {
5535 	result = xw->visInfo->depth;
5536     }
5537     return result;
5538 }
5539 
5540 /*
5541  * WM_ICON_SIZE should be honored if possible.
5542  */
5543 void
xtermLoadIcon(XtermWidget xw,const char * icon_hint)5544 xtermLoadIcon(XtermWidget xw, const char *icon_hint)
5545 {
5546 #ifdef HAVE_LIBXPM
5547     Display *dpy = XtDisplay(xw);
5548     Pixmap myIcon = 0;
5549     Pixmap myMask = 0;
5550     char *workname = 0;
5551     ICON_HINT hint = eHintDefault;
5552 #include <builtin_icons.h>
5553 
5554     ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5555     if (!IsEmpty(icon_hint)) {
5556 	if (!x_strcasecmp(icon_hint, "none")) {
5557 	    hint = eHintNone;
5558 	} else {
5559 	    hint = eHintSearch;
5560 	}
5561     }
5562 
5563     if (hint == eHintSearch) {
5564 	int state = 0;
5565 	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) {
5566 	    Pixmap resIcon = 0;
5567 	    Pixmap shapemask = 0;
5568 	    XpmAttributes attributes;
5569 	    struct stat sb;
5570 
5571 	    attributes.depth = (unsigned) getVisualDepth(xw);
5572 	    attributes.valuemask = XpmDepth;
5573 
5574 	    if (IsEmpty(workname)
5575 		|| lstat(workname, &sb) != 0
5576 		|| !S_ISREG(sb.st_mode)) {
5577 		TRACE(("...failure (no such file)\n"));
5578 	    } else {
5579 		int rc = XpmReadFileToPixmap(dpy,
5580 					     DefaultRootWindow(dpy),
5581 					     workname,
5582 					     &resIcon,
5583 					     &shapemask,
5584 					     &attributes);
5585 		if (rc == XpmSuccess) {
5586 		    myIcon = resIcon;
5587 		    myMask = shapemask;
5588 		    TRACE(("...success\n"));
5589 		    ReportIcons(("found/loaded icon-file %s\n", workname));
5590 		    break;
5591 		} else {
5592 		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5593 		}
5594 	    }
5595 	}
5596     }
5597 
5598     /*
5599      * If no external file was found, look for the name in the built-in table.
5600      * If that fails, just use the biggest mini-icon.
5601      */
5602     if (myIcon == 0 && hint != eHintNone) {
5603 	char **data;
5604 #if OPT_BUILTIN_XPMS
5605 	const XPM_DATA *myData = 0;
5606 	myData = BuiltInXPM(mini_xterm_xpms);
5607 	if (myData == 0)
5608 	    myData = BuiltInXPM(filled_xterm_xpms);
5609 	if (myData == 0)
5610 	    myData = BuiltInXPM(xterm_color_xpms);
5611 	if (myData == 0)
5612 	    myData = BuiltInXPM(xterm_xpms);
5613 	if (myData == 0)
5614 	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
5615 	data = (char **) myData->data;
5616 #else
5617 	data = (char **) &mini_xterm_48x48_xpm;
5618 #endif
5619 	if (XpmCreatePixmapFromData(dpy,
5620 				    DefaultRootWindow(dpy),
5621 				    data,
5622 				    &myIcon, &myMask, 0) == 0) {
5623 	    ReportIcons(("loaded built-in pixmap icon\n"));
5624 	} else {
5625 	    myIcon = 0;
5626 	    myMask = 0;
5627 	}
5628     }
5629 
5630     if (myIcon != 0) {
5631 	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
5632 	if (!hints)
5633 	    hints = XAllocWMHints();
5634 
5635 	if (hints) {
5636 	    hints->flags |= IconPixmapHint;
5637 	    hints->icon_pixmap = myIcon;
5638 	    if (myMask) {
5639 		hints->flags |= IconMaskHint;
5640 		hints->icon_mask = myMask;
5641 	    }
5642 
5643 	    XSetWMHints(dpy, VShellWindow(xw), hints);
5644 	    XFree(hints);
5645 	    ReportIcons(("updated window-manager hints\n"));
5646 	}
5647     }
5648 
5649     free(workname);
5650 
5651 #else
5652     (void) xw;
5653     (void) icon_hint;
5654 #endif
5655 }
5656 
5657 void
ChangeGroup(XtermWidget xw,const char * attribute,char * value)5658 ChangeGroup(XtermWidget xw, const char *attribute, char *value)
5659 {
5660     Arg args[1];
5661     Boolean changed = True;
5662     Widget w = CURRENT_EMU();
5663     Widget top = SHELL_OF(w);
5664 
5665     char *my_attr = NULL;
5666     char *old_value = value;
5667 #if OPT_WIDE_CHARS
5668     Boolean titleIsUTF8;
5669 #endif
5670 
5671     if (!AllowTitleOps(xw))
5672 	return;
5673 
5674     /*
5675      * Ignore empty or too-long requests.
5676      */
5677     if (value == 0 || strlen(value) > 1000)
5678 	return;
5679 
5680     if (IsTitleMode(xw, tmSetBase16)) {
5681 	const char *temp;
5682 	char *test;
5683 
5684 	/* this allocates a new string, if no error is detected */
5685 	value = x_decode_hex(value, &temp);
5686 	if (value == 0 || *temp != '\0') {
5687 	    free(value);
5688 	    return;
5689 	}
5690 	for (test = value; *test != '\0'; ++test) {
5691 	    if (CharOf(*test) < 32) {
5692 		*test = '\0';
5693 		break;
5694 	    }
5695 	}
5696     }
5697 #if OPT_WIDE_CHARS
5698     /*
5699      * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
5700      * the WM_NAME property, rather than doing this directly.  That relies on
5701      * the application to tell it if the format should be something other than
5702      * STRING, i.e., by setting the XtNtitleEncoding resource.
5703      *
5704      * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
5705      * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
5706      * added UTF8_STRING (the documentation for that was discarded by an Xorg
5707      * developer, although the source-code provides this feature).
5708      *
5709      * Since X11R5, if the X11 library fails to store a text property as
5710      * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
5711      * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
5712      * case, limit the resulting characters to the printable ISO-8859-1 set.
5713      */
5714     titleIsUTF8 = isValidUTF8((Char *) value);
5715     if (IsSetUtf8Title(xw) && titleIsUTF8) {
5716 	char *testc = malloc(strlen(value) + 1);
5717 	Char *nextc = (Char *) value;
5718 	Boolean ok8bit = True;
5719 
5720 	if (testc != NULL) {
5721 	    /*
5722 	     * Check if the data fits in STRING.  Along the way, replace
5723 	     * control characters.
5724 	     */
5725 	    Char *lastc = (Char *) testc;
5726 	    while (*nextc != '\0') {
5727 		unsigned ch;
5728 		nextc = convertFromUTF8(nextc, &ch);
5729 		if (ch > 255) {
5730 		    ok8bit = False;
5731 		} else if (!IsLatin1(ch)) {
5732 		    ch = OnlyLatin1(ch);
5733 		}
5734 		*lastc++ = (Char) ch;
5735 	    }
5736 	    *lastc = '\0';
5737 	    if (ok8bit) {
5738 		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
5739 		if (value != old_value)
5740 		    free(value);
5741 		value = testc;
5742 		titleIsUTF8 = False;
5743 	    } else {
5744 		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
5745 		       "\t%s\n", value));
5746 		free(testc);
5747 		nextc = (Char *) value;
5748 		while (*nextc != '\0') {
5749 		    unsigned ch;
5750 		    Char *skip = convertFromUTF8(nextc, &ch);
5751 		    if (iswcntrl((wint_t) ch)) {
5752 			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
5753 		    }
5754 		    nextc = skip;
5755 		}
5756 	    }
5757 	}
5758     } else
5759 #endif
5760     {
5761 	Char *c1 = (Char *) value;
5762 
5763 	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
5764 	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
5765 	    *c1 = (Char) OnlyLatin1(*c1);
5766 	}
5767     }
5768 
5769     my_attr = x_strdup(attribute);
5770 
5771     ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
5772 
5773 #if OPT_WIDE_CHARS
5774     /*
5775      * If we're running in UTF-8 mode, and have not been told that the
5776      * title string is in UTF-8, it is likely that non-ASCII text in the
5777      * string will be rejected because it is not printable in the current
5778      * locale.  So we convert it to UTF-8, allowing the X library to
5779      * convert it back.
5780      */
5781     TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
5782     if (xtermEnvUTF8() && !titleIsUTF8) {
5783 	size_t limit = strlen(value);
5784 	Char *c1 = (Char *) value;
5785 	int n;
5786 
5787 	for (n = 0; c1[n] != '\0'; ++n) {
5788 	    if (c1[n] > 127) {
5789 		Char *converted;
5790 		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
5791 		    Char *temp = converted;
5792 		    while (*c1 != 0) {
5793 			temp = convertToUTF8(temp, *c1++);
5794 		    }
5795 		    *temp = 0;
5796 		    if (value != old_value)
5797 			free(value);
5798 		    value = (char *) converted;
5799 		    ReportIcons(("...converted{%s}\n", value));
5800 		}
5801 		break;
5802 	    }
5803 	}
5804     }
5805 #endif
5806 
5807 #if OPT_SAME_NAME
5808     /* If the attribute isn't going to change, then don't bother... */
5809     if (resource.sameName) {
5810 	char *buf = 0;
5811 	XtSetArg(args[0], my_attr, &buf);
5812 	XtGetValues(top, args, 1);
5813 	TRACE(("...comparing{%s}\n", NonNull(buf)));
5814 	if (buf != 0 && strcmp(value, buf) == 0)
5815 	    changed = False;
5816     }
5817 #endif /* OPT_SAME_NAME */
5818 
5819     if (changed) {
5820 	ReportIcons(("...updating %s\n", my_attr));
5821 	ReportIcons(("...value is %s\n", value));
5822 	XtSetArg(args[0], my_attr, value);
5823 	XtSetValues(top, args, 1);
5824     }
5825 #if OPT_WIDE_CHARS
5826     if (xtermEnvUTF8()) {
5827 	Display *dpy = XtDisplay(xw);
5828 	const char *propname = (!strcmp(my_attr, XtNtitle)
5829 				? "_NET_WM_NAME"
5830 				: "_NET_WM_ICON_NAME");
5831 	Atom my_atom = XInternAtom(dpy, propname, False);
5832 
5833 	if (my_atom != None) {
5834 	    changed = True;
5835 
5836 	    if (IsSetUtf8Title(xw)) {
5837 #if OPT_SAME_NAME
5838 		if (resource.sameName) {
5839 		    Atom actual_type;
5840 		    Atom requested_type = XA_UTF8_STRING(dpy);
5841 		    int actual_format = 0;
5842 		    long long_length = 1024;
5843 		    unsigned long nitems = 0;
5844 		    unsigned long bytes_after = 0;
5845 		    unsigned char *prop = 0;
5846 
5847 		    if (xtermGetWinProp(dpy,
5848 					VShellWindow(xw),
5849 					my_atom,
5850 					0L,
5851 					long_length,
5852 					requested_type,
5853 					&actual_type,
5854 					&actual_format,
5855 					&nitems,
5856 					&bytes_after,
5857 					&prop)
5858 			&& actual_type == requested_type
5859 			&& actual_format == 8
5860 			&& prop != 0
5861 			&& nitems == strlen(value)
5862 			&& memcmp(value, prop, nitems) == 0) {
5863 			changed = False;
5864 		    }
5865 		}
5866 #endif /* OPT_SAME_NAME */
5867 		if (changed) {
5868 		    ReportIcons(("...updating %s\n", propname));
5869 		    ReportIcons(("...value is %s\n", value));
5870 		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
5871 				    XA_UTF8_STRING(dpy), 8,
5872 				    PropModeReplace,
5873 				    (Char *) value,
5874 				    (int) strlen(value));
5875 		}
5876 	    } else {
5877 		ReportIcons(("...deleting %s\n", propname));
5878 		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5879 	    }
5880 	}
5881     }
5882 #endif
5883     if (value != old_value) {
5884 	free(value);
5885     }
5886     free(my_attr);
5887 
5888     return;
5889 }
5890 
5891 void
ChangeIconName(XtermWidget xw,char * name)5892 ChangeIconName(XtermWidget xw, char *name)
5893 {
5894     if (name == 0) {
5895 	name = emptyString;
5896     }
5897     if (!showZIconBeep(xw, name))
5898 	ChangeGroup(xw, XtNiconName, name);
5899 }
5900 
5901 void
ChangeTitle(XtermWidget xw,char * name)5902 ChangeTitle(XtermWidget xw, char *name)
5903 {
5904     ChangeGroup(xw, XtNtitle, name);
5905 }
5906 
5907 #define Strlen(s) strlen((const char *)(s))
5908 
5909 void
ChangeXprop(char * buf)5910 ChangeXprop(char *buf)
5911 {
5912     Display *dpy = XtDisplay(toplevel);
5913     Window w = XtWindow(toplevel);
5914     XTextProperty text_prop;
5915     Atom aprop;
5916     Char *pchEndPropName = (Char *) strchr(buf, '=');
5917 
5918     if (pchEndPropName)
5919 	*pchEndPropName = '\0';
5920     aprop = XInternAtom(dpy, buf, False);
5921     if (pchEndPropName == NULL) {
5922 	/* no "=value" given, so delete the property */
5923 	XDeleteProperty(dpy, w, aprop);
5924     } else {
5925 	text_prop.value = pchEndPropName + 1;
5926 	text_prop.encoding = XA_STRING;
5927 	text_prop.format = 8;
5928 	text_prop.nitems = Strlen(text_prop.value);
5929 	XSetTextProperty(dpy, w, &text_prop, aprop);
5930     }
5931 }
5932 
5933 /***====================================================================***/
5934 
5935 /*
5936  * This is part of ReverseVideo().  It reverses the data stored for the old
5937  * "dynamic" colors that might have been retrieved using OSC 10-18.
5938  */
5939 void
ReverseOldColors(XtermWidget xw)5940 ReverseOldColors(XtermWidget xw)
5941 {
5942     ScrnColors *pOld = xw->work.oldColors;
5943     Pixel tmpPix;
5944     char *tmpName;
5945 
5946     if (pOld) {
5947 	/* change text cursor, if necessary */
5948 	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
5949 	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
5950 	    if (pOld->names[TEXT_CURSOR]) {
5951 		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
5952 		pOld->names[TEXT_CURSOR] = NULL;
5953 	    }
5954 	    if (pOld->names[TEXT_BG]) {
5955 		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
5956 		    pOld->names[TEXT_CURSOR] = tmpName;
5957 		}
5958 	    }
5959 	}
5960 
5961 	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
5962 	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
5963 
5964 	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
5965 	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
5966 
5967 #if OPT_TEK4014
5968 	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
5969 	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
5970 #endif
5971 	FreeMarkGCs(xw);
5972     }
5973     return;
5974 }
5975 
5976 Bool
AllocateTermColor(XtermWidget xw,ScrnColors * pNew,int ndx,const char * name,Bool always)5977 AllocateTermColor(XtermWidget xw,
5978 		  ScrnColors * pNew,
5979 		  int ndx,
5980 		  const char *name,
5981 		  Bool always)
5982 {
5983     Bool result = False;
5984 
5985     if (always || AllowColorOps(xw, ecSetColor)) {
5986 	XColor def;
5987 	char *newName;
5988 
5989 	result = True;
5990 	if (!x_strcasecmp(name, XtDefaultForeground)) {
5991 	    def.pixel = xw->old_foreground;
5992 	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
5993 	    def.pixel = xw->old_background;
5994 	} else if (!xtermAllocColor(xw, &def, name)) {
5995 	    result = False;
5996 	}
5997 
5998 	if (result
5999 	    && (newName = x_strdup(name)) != 0) {
6000 	    if (COLOR_DEFINED(pNew, ndx)) {
6001 		free(pNew->names[ndx]);
6002 	    }
6003 	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
6004 	    SET_COLOR_NAME(pNew, ndx, newName);
6005 	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
6006 		   ndx, newName, def.pixel));
6007 	} else {
6008 	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
6009 	    result = False;
6010 	}
6011     }
6012     return result;
6013 }
6014 /***====================================================================***/
6015 
6016 /* ARGSUSED */
6017 void
Panic(const char * s GCC_UNUSED,int a GCC_UNUSED)6018 Panic(const char *s GCC_UNUSED, int a GCC_UNUSED)
6019 {
6020     if_DEBUG({
6021 	xtermWarning(s, a);
6022     });
6023 }
6024 
6025 const char *
SysErrorMsg(int code)6026 SysErrorMsg(int code)
6027 {
6028     static const char unknown[] = "unknown error";
6029     const char *s = strerror(code);
6030     return s ? s : unknown;
6031 }
6032 
6033 const char *
SysReasonMsg(int code)6034 SysReasonMsg(int code)
6035 {
6036     /* *INDENT-OFF* */
6037     static const struct {
6038 	int code;
6039 	const char *name;
6040     } table[] = {
6041 	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
6042 	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
6043 	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
6044 	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
6045 	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
6046 	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
6047 	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
6048 	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
6049 	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
6050 	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
6051 	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
6052 	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
6053 	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
6054 	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
6055 	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
6056 	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
6057 	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
6058 	{ ERROR_FORK,		"spawn: fork() failed" },
6059 	{ ERROR_EXEC,		"spawn: exec() failed" },
6060 	{ ERROR_PTYS,		"get_pty: not enough ptys" },
6061 	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6062 	{ ERROR_SETUID,		"spawn: setuid() failed" },
6063 	{ ERROR_INIT,		"spawn: can't initialize window" },
6064 	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6065 	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6066 	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6067 	{ ERROR_SELECT,		"in_put: select() failed" },
6068 	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6069 	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6070 	{ ERROR_TSELECT,	"Tinput: select() failed" },
6071 	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6072 	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6073 	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6074 	{ ERROR_XERROR,		"xerror: XError event" },
6075 	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6076 	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6077 	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6078 	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6079     };
6080     /* *INDENT-ON* */
6081 
6082     Cardinal n;
6083     const char *result = "?";
6084 
6085     for (n = 0; n < XtNumber(table); ++n) {
6086 	if (code == table[n].code) {
6087 	    result = table[n].name;
6088 	    break;
6089 	}
6090     }
6091     return result;
6092 }
6093 
6094 void
SysError(int code)6095 SysError(int code)
6096 {
6097     int oerrno = errno;
6098 
6099     fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6100     fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6101     fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6102 
6103     Cleanup(code);
6104 }
6105 
6106 void
NormalExit(void)6107 NormalExit(void)
6108 {
6109     static Bool cleaning;
6110 
6111     /*
6112      * Process "-hold" and session cleanup only for a normal exit.
6113      */
6114     if (cleaning) {
6115 	hold_screen = 0;
6116 	return;
6117     }
6118 
6119     cleaning = True;
6120     need_cleanup = False;
6121 
6122     if (hold_screen) {
6123 	hold_screen = 2;
6124 	while (hold_screen) {
6125 	    xtermFlushDbe(term);
6126 	    xevents(term);
6127 	    Sleep(EVENT_DELAY);
6128 	}
6129     }
6130 #if OPT_SESSION_MGT
6131     if (resource.sessionMgt) {
6132 	XtVaSetValues(toplevel,
6133 		      XtNjoinSession, False,
6134 		      (void *) 0);
6135     }
6136 #endif
6137     Cleanup(0);
6138 }
6139 
6140 #if USE_DOUBLE_BUFFER
6141 void
xtermFlushDbe(XtermWidget xw)6142 xtermFlushDbe(XtermWidget xw)
6143 {
6144     TScreen *screen = TScreenOf(xw);
6145     if (resource.buffered && screen->needSwap) {
6146 	XdbeSwapInfo swap;
6147 	swap.swap_window = VWindow(screen);
6148 	swap.swap_action = XdbeCopied;
6149 	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6150 	XFlush(XtDisplay(xw));
6151 	screen->needSwap = 0;
6152 	ScrollBarDrawThumb(xw, 2);
6153 	X_GETTIMEOFDAY(&screen->buffered_at);
6154     }
6155 }
6156 
6157 void
xtermTimedDbe(XtermWidget xw)6158 xtermTimedDbe(XtermWidget xw)
6159 {
6160     if (resource.buffered) {
6161 	TScreen *screen = TScreenOf(xw);
6162 	struct timeval now;
6163 	long elapsed;
6164 	long limit = DbeMsecs(xw);
6165 
6166 	X_GETTIMEOFDAY(&now);
6167 	if (screen->buffered_at.tv_sec) {
6168 	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6169 		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6170 	} else {
6171 	    elapsed = limit;
6172 	}
6173 	if (elapsed >= limit) {
6174 	    xtermNeedSwap(xw, 1);
6175 	    xtermFlushDbe(xw);
6176 	}
6177     }
6178 }
6179 #endif
6180 
6181 /*
6182  * cleanup by sending SIGHUP to client processes
6183  */
6184 void
Cleanup(int code)6185 Cleanup(int code)
6186 {
6187     TScreen *screen = TScreenOf(term);
6188 
6189     TRACE(("Cleanup %d\n", code));
6190 
6191     if (screen->pid > 1) {
6192 	(void) kill_process_group(screen->pid, SIGHUP);
6193     }
6194     Exit(code);
6195 }
6196 
6197 #ifndef S_IXOTH
6198 #define S_IXOTH 1
6199 #endif
6200 
6201 Boolean
validProgram(const char * pathname)6202 validProgram(const char *pathname)
6203 {
6204     Boolean result = False;
6205     struct stat sb;
6206 
6207     if (!IsEmpty(pathname)
6208 	&& *pathname == '/'
6209 	&& strstr(pathname, "/..") == 0
6210 	&& stat(pathname, &sb) == 0
6211 	&& (sb.st_mode & S_IFMT) == S_IFREG
6212 	&& (sb.st_mode & S_IXOTH) != 0) {
6213 	result = True;
6214     }
6215     return result;
6216 }
6217 
6218 #ifndef VMS
6219 #ifndef PATH_MAX
6220 #define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
6221 #endif
6222 char *
xtermFindShell(char * leaf,Bool warning)6223 xtermFindShell(char *leaf, Bool warning)
6224 {
6225     char *s0;
6226     char *s;
6227     char *d;
6228     char *tmp;
6229     char *result = leaf;
6230     Bool allocated = False;
6231 
6232     TRACE(("xtermFindShell(%s)\n", leaf));
6233 
6234     if (!strncmp("./", result, (size_t) 2)
6235 	|| !strncmp("../", result, (size_t) 3)) {
6236 	size_t need = PATH_MAX;
6237 	size_t used = strlen(result) + 2;
6238 	char *buffer = malloc(used + need);
6239 	if (buffer != 0) {
6240 	    if (getcwd(buffer, need) != 0) {
6241 		sprintf(buffer + strlen(buffer), "/%s", result);
6242 		result = buffer;
6243 		allocated = True;
6244 	    } else {
6245 		free(buffer);
6246 	    }
6247 	}
6248     } else if (*result != '\0' && strchr("+/-", *result) == 0) {
6249 	/* find it in $PATH */
6250 	if ((s = s0 = x_getenv("PATH")) != 0) {
6251 	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
6252 		Bool found = False;
6253 		while (*s != '\0') {
6254 		    strcpy(tmp, s);
6255 		    for (d = tmp;; ++d) {
6256 			if (*d == ':' || *d == '\0') {
6257 			    int skip = (*d != '\0');
6258 			    *d = '/';
6259 			    strcpy(d + 1, leaf);
6260 			    if (skip)
6261 				++d;
6262 			    s += (d - tmp);
6263 			    if (validProgram(tmp)) {
6264 				result = x_strdup(tmp);
6265 				found = True;
6266 				allocated = True;
6267 			    }
6268 			    break;
6269 			}
6270 		    }
6271 		    if (found)
6272 			break;
6273 		}
6274 		free(tmp);
6275 	    }
6276 	    free(s0);
6277 	}
6278     }
6279     TRACE(("...xtermFindShell(%s)\n", result));
6280     if (!validProgram(result)) {
6281 	if (warning)
6282 	    xtermWarning("No absolute path found for shell: %s\n", result);
6283 	if (allocated)
6284 	    free(result);
6285 	result = 0;
6286     }
6287     /* be consistent, so that caller can always free the result */
6288     if (result != 0 && !allocated)
6289 	result = x_strdup(result);
6290     return result;
6291 }
6292 #endif /* VMS */
6293 
6294 #define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6295 
6296 /*
6297  * If we do not have unsetenv(), make consistent updates for environ[].
6298  * This could happen on some older machines due to the uneven standardization
6299  * process for the two functions.
6300  *
6301  * That is, putenv() makes a copy of environ, and some implementations do not
6302  * update the environ pointer, so the fallback when unsetenv() is missing would
6303  * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
6304  * could copy environ.
6305  */
6306 #if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
6307 #undef HAVE_PUTENV
6308 #elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
6309 #undef HAVE_UNSETENV
6310 #endif
6311 
6312 /*
6313  * copy the environment before Setenv'ing.
6314  */
6315 void
xtermCopyEnv(char ** oldenv)6316 xtermCopyEnv(char **oldenv)
6317 {
6318 #ifdef HAVE_PUTENV
6319     (void) oldenv;
6320 #else
6321     unsigned size;
6322     char **newenv;
6323 
6324     for (size = 0; oldenv[size] != NULL; size++) {
6325 	;
6326     }
6327 
6328     newenv = TypeCallocN(char *, ENV_HUNK(size));
6329     memmove(newenv, oldenv, size * sizeof(char *));
6330     environ = newenv;
6331 #endif
6332 }
6333 
6334 #if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
6335 static int
findEnv(const char * var,int * lengthp)6336 findEnv(const char *var, int *lengthp)
6337 {
6338     char *test;
6339     int envindex = 0;
6340     size_t len = strlen(var);
6341     int found = -1;
6342 
6343     TRACE(("findEnv(%s=..)\n", var));
6344 
6345     while ((test = environ[envindex]) != NULL) {
6346 	if (strncmp(test, var, len) == 0 && test[len] == '=') {
6347 	    found = envindex;
6348 	    break;
6349 	}
6350 	envindex++;
6351     }
6352     *lengthp = envindex;
6353     return found;
6354 }
6355 #endif
6356 
6357 /*
6358  * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6359  * Var should end with '=' (bindings are of the form "var=value").
6360  * This procedure assumes the memory for the first level of environ
6361  * was allocated using calloc, with enough extra room at the end so not
6362  * to have to do a realloc().
6363  */
6364 void
xtermSetenv(const char * var,const char * value)6365 xtermSetenv(const char *var, const char *value)
6366 {
6367     if (value != 0) {
6368 #ifdef HAVE_PUTENV
6369 	char *both = malloc(2 + strlen(var) + strlen(value));
6370 	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6371 	if (both) {
6372 	    sprintf(both, "%s=%s", var, value);
6373 	    putenv(both);
6374 	}
6375 #else
6376 	size_t len = strlen(var);
6377 	int envindex;
6378 	int found = findEnv(var, &envindex);
6379 
6380 	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6381 
6382 	if (found < 0) {
6383 	    unsigned need = ENV_HUNK(envindex + 1);
6384 	    unsigned have = ENV_HUNK(envindex);
6385 
6386 	    if (need > have) {
6387 		char **newenv;
6388 		newenv = TypeMallocN(char *, need);
6389 		if (newenv == 0) {
6390 		    xtermWarning("Cannot increase environment\n");
6391 		    return;
6392 		}
6393 		memmove(newenv, environ, have * sizeof(*newenv));
6394 		free(environ);
6395 		environ = newenv;
6396 	    }
6397 
6398 	    found = envindex;
6399 	    environ[found + 1] = NULL;
6400 	    environ = environ;
6401 	}
6402 
6403 	environ[found] = malloc(2 + len + strlen(value));
6404 	if (environ[found] == 0) {
6405 	    xtermWarning("Cannot allocate environment %s\n", var);
6406 	    return;
6407 	}
6408 	sprintf(environ[found], "%s=%s", var, value);
6409 #endif
6410     }
6411 }
6412 
6413 void
xtermUnsetenv(const char * var)6414 xtermUnsetenv(const char *var)
6415 {
6416     TRACE(("xtermUnsetenv(%s)\n", var));
6417 #ifdef HAVE_UNSETENV
6418     unsetenv(var);
6419 #else
6420     {
6421 	int ignore;
6422 	int item = findEnv(var, &ignore);
6423 	if (item >= 0) {
6424 	    while ((environ[item] = environ[item + 1]) != 0) {
6425 		++item;
6426 	    }
6427 	}
6428     }
6429 #endif
6430 }
6431 
6432 /*ARGSUSED*/
6433 int
xerror(Display * d,XErrorEvent * ev)6434 xerror(Display *d, XErrorEvent *ev)
6435 {
6436     xtermWarning("warning, error event received:\n");
6437     TRACE_X_ERR(d, ev);
6438     (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6439     Exit(ERROR_XERROR);
6440     return 0;			/* appease the compiler */
6441 }
6442 
6443 void
ice_error(IceConn iceConn)6444 ice_error(IceConn iceConn)
6445 {
6446     (void) iceConn;
6447 
6448     xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
6449 		 (long) getpid(), errno);
6450 
6451     Exit(ERROR_ICEERROR);
6452 }
6453 
6454 /*ARGSUSED*/
6455 int
xioerror(Display * dpy)6456 xioerror(Display *dpy)
6457 {
6458     int the_error = errno;
6459 
6460     xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
6461 		 the_error, SysErrorMsg(the_error),
6462 		 DisplayString(dpy));
6463 
6464     Exit(ERROR_XIOERROR);
6465     return 0;			/* appease the compiler */
6466 }
6467 
6468 void
xt_error(String message)6469 xt_error(String message)
6470 {
6471     xtermWarning("Xt error: %s\n", message);
6472 
6473     /*
6474      * Check for the obvious - Xt does a poor job of reporting this.
6475      */
6476     if (x_getenv("DISPLAY") == 0) {
6477 	xtermWarning("DISPLAY is not set\n");
6478     }
6479     exit(1);
6480 }
6481 
6482 int
XStrCmp(char * s1,char * s2)6483 XStrCmp(char *s1, char *s2)
6484 {
6485     if (s1 && s2)
6486 	return (strcmp(s1, s2));
6487     if (s1 && *s1)
6488 	return (1);
6489     if (s2 && *s2)
6490 	return (-1);
6491     return (0);
6492 }
6493 
6494 #if OPT_TEK4014
6495 static void
withdraw_window(Display * dpy,Window w,int scr)6496 withdraw_window(Display *dpy, Window w, int scr)
6497 {
6498     TRACE(("withdraw_window %#lx\n", (long) w));
6499     (void) XmuUpdateMapHints(dpy, w, NULL);
6500     XWithdrawWindow(dpy, w, scr);
6501     return;
6502 }
6503 #endif
6504 
6505 void
set_vt_visibility(Bool on)6506 set_vt_visibility(Bool on)
6507 {
6508     XtermWidget xw = term;
6509     TScreen *screen = TScreenOf(xw);
6510 
6511     TRACE(("set_vt_visibility(%d)\n", on));
6512     if (on) {
6513 	if (!screen->Vshow && xw) {
6514 	    VTInit(xw);
6515 	    XtMapWidget(XtParent(xw));
6516 #if OPT_TOOLBAR
6517 	    /* we need both of these during initialization */
6518 	    XtMapWidget(SHELL_OF(xw));
6519 	    ShowToolbar(resource.toolBar);
6520 #endif
6521 	    screen->Vshow = True;
6522 	}
6523     }
6524 #if OPT_TEK4014
6525     else {
6526 	if (screen->Vshow && xw) {
6527 	    withdraw_window(XtDisplay(xw),
6528 			    VShellWindow(xw),
6529 			    XScreenNumberOfScreen(XtScreen(xw)));
6530 	    screen->Vshow = False;
6531 	}
6532     }
6533     set_vthide_sensitivity();
6534     set_tekhide_sensitivity();
6535     update_vttekmode();
6536     update_tekshow();
6537     update_vtshow();
6538 #endif
6539     return;
6540 }
6541 
6542 #if OPT_TEK4014
6543 void
set_tek_visibility(Bool on)6544 set_tek_visibility(Bool on)
6545 {
6546     XtermWidget xw = term;
6547 
6548     TRACE(("set_tek_visibility(%d)\n", on));
6549 
6550     if (on) {
6551 	if (!TEK4014_SHOWN(xw)) {
6552 	    if (tekWidget == 0) {
6553 		TekInit();	/* will exit on failure */
6554 	    }
6555 	    if (tekWidget != 0) {
6556 		Widget tekParent = SHELL_OF(tekWidget);
6557 		XtRealizeWidget(tekParent);
6558 		XtMapWidget(XtParent(tekWidget));
6559 #if OPT_TOOLBAR
6560 		/* we need both of these during initialization */
6561 		XtMapWidget(tekParent);
6562 		XtMapWidget(tekWidget);
6563 #endif
6564 		XtOverrideTranslations(tekParent,
6565 				       XtParseTranslationTable
6566 				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6567 		(void) XSetWMProtocols(XtDisplay(tekParent),
6568 				       XtWindow(tekParent),
6569 				       &wm_delete_window, 1);
6570 		TEK4014_SHOWN(xw) = True;
6571 	    }
6572 	}
6573     } else {
6574 	if (TEK4014_SHOWN(xw) && tekWidget) {
6575 	    withdraw_window(XtDisplay(tekWidget),
6576 			    TShellWindow,
6577 			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6578 	    TEK4014_SHOWN(xw) = False;
6579 	}
6580     }
6581     set_tekhide_sensitivity();
6582     set_vthide_sensitivity();
6583     update_vtshow();
6584     update_tekshow();
6585     update_vttekmode();
6586     return;
6587 }
6588 
6589 void
end_tek_mode(void)6590 end_tek_mode(void)
6591 {
6592     XtermWidget xw = term;
6593 
6594     if (TEK4014_ACTIVE(xw)) {
6595 	FlushLog(xw);
6596 	TEK4014_ACTIVE(xw) = False;
6597 	xtermSetWinSize(xw);
6598 	longjmp(Tekend, 1);
6599     }
6600     return;
6601 }
6602 
6603 void
end_vt_mode(void)6604 end_vt_mode(void)
6605 {
6606     XtermWidget xw = term;
6607 
6608     if (!TEK4014_ACTIVE(xw)) {
6609 	FlushLog(xw);
6610 	set_tek_visibility(True);
6611 	TEK4014_ACTIVE(xw) = True;
6612 	TekSetWinSize(tekWidget);
6613 	longjmp(VTend, 1);
6614     }
6615     return;
6616 }
6617 
6618 void
switch_modes(Bool tovt)6619 switch_modes(Bool tovt)		/* if true, then become vt mode */
6620 {
6621     if (tovt) {
6622 	if (tekRefreshList)
6623 	    TekRefresh(tekWidget);
6624 	end_tek_mode();		/* WARNING: this does a longjmp... */
6625     } else {
6626 	end_vt_mode();		/* WARNING: this does a longjmp... */
6627     }
6628 }
6629 
6630 void
hide_vt_window(void)6631 hide_vt_window(void)
6632 {
6633     set_vt_visibility(False);
6634     if (!TEK4014_ACTIVE(term))
6635 	switch_modes(False);	/* switch to tek mode */
6636 }
6637 
6638 void
hide_tek_window(void)6639 hide_tek_window(void)
6640 {
6641     set_tek_visibility(False);
6642     tekRefreshList = (TekLink *) 0;
6643     if (TEK4014_ACTIVE(term))
6644 	switch_modes(True);	/* does longjmp to vt mode */
6645 }
6646 #endif /* OPT_TEK4014 */
6647 
6648 static const char *
skip_punct(const char * s)6649 skip_punct(const char *s)
6650 {
6651     while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
6652 	++s;
6653     }
6654     return s;
6655 }
6656 
6657 static int
cmp_options(const void * a,const void * b)6658 cmp_options(const void *a, const void *b)
6659 {
6660     const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
6661     const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
6662     return strcmp(s1, s2);
6663 }
6664 
6665 static int
cmp_resources(const void * a,const void * b)6666 cmp_resources(const void *a, const void *b)
6667 {
6668     return strcmp(((const XrmOptionDescRec *) a)->option,
6669 		  ((const XrmOptionDescRec *) b)->option);
6670 }
6671 
6672 XrmOptionDescRec *
sortedOptDescs(XrmOptionDescRec * descs,Cardinal res_count)6673 sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
6674 {
6675     static XrmOptionDescRec *res_array = 0;
6676 
6677 #ifdef NO_LEAKS
6678     if (descs == 0) {
6679 	FreeAndNull(res_array);
6680     } else
6681 #endif
6682     if (res_array == 0) {
6683 	Cardinal j;
6684 
6685 	/* make a sorted index to 'resources' */
6686 	res_array = TypeCallocN(XrmOptionDescRec, res_count);
6687 	if (res_array != 0) {
6688 	    for (j = 0; j < res_count; j++)
6689 		res_array[j] = descs[j];
6690 	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
6691 	}
6692     }
6693     return res_array;
6694 }
6695 
6696 /*
6697  * The first time this is called, construct sorted index to the main program's
6698  * list of options, taking into account the on/off options which will be
6699  * compressed into one token.  It's a lot simpler to do it this way than
6700  * maintain the list in sorted form with lots of ifdef's.
6701  */
6702 OptionHelp *
sortedOpts(OptionHelp * options,XrmOptionDescRec * descs,Cardinal numDescs)6703 sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
6704 {
6705     static OptionHelp *opt_array = 0;
6706 
6707 #ifdef NO_LEAKS
6708     if (descs == 0 && opt_array != 0) {
6709 	sortedOptDescs(descs, numDescs);
6710 	FreeAndNull(opt_array);
6711 	return 0;
6712     } else if (options == 0 || descs == 0) {
6713 	return 0;
6714     }
6715 #endif
6716 
6717     if (opt_array == 0) {
6718 	size_t opt_count, j;
6719 #if OPT_TRACE
6720 	Cardinal k;
6721 	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
6722 	int code;
6723 	const char *mesg;
6724 #else
6725 	(void) descs;
6726 	(void) numDescs;
6727 #endif
6728 
6729 	/* count 'options' and make a sorted index to it */
6730 	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
6731 	    ;
6732 	}
6733 	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
6734 	for (j = 0; j < opt_count; j++)
6735 	    opt_array[j] = options[j];
6736 	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
6737 
6738 	/* supply the "turn on/off" strings if needed */
6739 #if OPT_TRACE
6740 	for (j = 0; j < opt_count; j++) {
6741 	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
6742 		char temp[80];
6743 		const char *name = opt_array[j].opt + 3;
6744 		for (k = 0; k < numDescs; ++k) {
6745 		    const char *value = res_array[k].value;
6746 		    if (res_array[k].option[0] == '-') {
6747 			code = -1;
6748 		    } else if (res_array[k].option[0] == '+') {
6749 			code = 1;
6750 		    } else {
6751 			code = 0;
6752 		    }
6753 		    sprintf(temp, "%.*s",
6754 			    (int) sizeof(temp) - 2,
6755 			    opt_array[j].desc);
6756 		    if (x_strindex(temp, "inhibit") != 0)
6757 			code = -code;
6758 		    if (code != 0
6759 			&& res_array[k].value != 0
6760 			&& !strcmp(name, res_array[k].option + 1)) {
6761 			if (((code < 0) && !strcmp(value, "on"))
6762 			    || ((code > 0) && !strcmp(value, "off"))
6763 			    || ((code > 0) && !strcmp(value, "0"))) {
6764 			    mesg = "turn on/off";
6765 			} else {
6766 			    mesg = "turn off/on";
6767 			}
6768 			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
6769 			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
6770 				char *s = malloc(strlen(mesg)
6771 						 + strlen(opt_array[j].desc)
6772 						 + 2);
6773 				if (s != 0) {
6774 				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
6775 				    opt_array[j].desc = s;
6776 				}
6777 			    } else {
6778 				TRACE(("OOPS "));
6779 			    }
6780 			}
6781 			TRACE(("%s: %s %s: %s (%s)\n",
6782 			       mesg,
6783 			       res_array[k].option,
6784 			       res_array[k].value,
6785 			       opt_array[j].opt,
6786 			       opt_array[j].desc));
6787 			break;
6788 		    }
6789 		}
6790 	    }
6791 	}
6792 #endif
6793     }
6794     return opt_array;
6795 }
6796 
6797 /*
6798  * Report the character-type locale that xterm was started in.
6799  */
6800 String
xtermEnvLocale(void)6801 xtermEnvLocale(void)
6802 {
6803     static String result;
6804 
6805     if (result == 0) {
6806 	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
6807 	    result = x_strdup("C");
6808 	} else {
6809 	    result = x_strdup(result);
6810 	}
6811 	TRACE(("xtermEnvLocale ->%s\n", result));
6812     }
6813     return result;
6814 }
6815 
6816 char *
xtermEnvEncoding(void)6817 xtermEnvEncoding(void)
6818 {
6819     static char *result;
6820 
6821     if (result == 0) {
6822 #ifdef HAVE_LANGINFO_CODESET
6823 	result = nl_langinfo(CODESET);
6824 #else
6825 	const char *locale = xtermEnvLocale();
6826 	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
6827 	    result = x_strdup("ASCII");
6828 	} else {
6829 	    result = x_strdup("ISO-8859-1");
6830 	}
6831 #endif
6832 	TRACE(("xtermEnvEncoding ->%s\n", result));
6833     }
6834     return result;
6835 }
6836 
6837 #if OPT_WIDE_CHARS
6838 /*
6839  * Tell whether xterm was started in a locale that uses UTF-8 encoding for
6840  * characters.  That environment is inherited by subprocesses and used in
6841  * various library calls.
6842  */
6843 Bool
xtermEnvUTF8(void)6844 xtermEnvUTF8(void)
6845 {
6846     static Bool init = False;
6847     static Bool result = False;
6848 
6849     if (!init) {
6850 	init = True;
6851 #ifdef HAVE_LANGINFO_CODESET
6852 	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
6853 #else
6854 	{
6855 	    char *locale = x_strdup(xtermEnvLocale());
6856 	    int n;
6857 	    for (n = 0; locale[n] != 0; ++n) {
6858 		locale[n] = x_toupper(locale[n]);
6859 	    }
6860 	    if (strstr(locale, "UTF-8") != 0)
6861 		result = True;
6862 	    else if (strstr(locale, "UTF8") != 0)
6863 		result = True;
6864 	    free(locale);
6865 	}
6866 #endif
6867 	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
6868     }
6869     return result;
6870 }
6871 #endif /* OPT_WIDE_CHARS */
6872 
6873 /*
6874  * Check if the current widget, or any parent, is the VT100 "xterm" widget.
6875  */
6876 XtermWidget
getXtermWidget(Widget w)6877 getXtermWidget(Widget w)
6878 {
6879     XtermWidget xw;
6880 
6881     if (w == 0) {
6882 	xw = (XtermWidget) CURRENT_EMU();
6883 	if (!IsXtermWidget(xw)) {
6884 	    xw = 0;
6885 	}
6886     } else if (IsXtermWidget(w)) {
6887 	xw = (XtermWidget) w;
6888     } else {
6889 	xw = getXtermWidget(XtParent(w));
6890     }
6891     TRACE2(("getXtermWidget %p -> %p\n", w, xw));
6892     return xw;
6893 }
6894 
6895 #if OPT_SESSION_MGT
6896 
6897 #if OPT_TRACE
6898 static void
trace_1_SM(const char * tag,String name)6899 trace_1_SM(const char *tag, String name)
6900 {
6901     Arg args[1];
6902     char *buf = 0;
6903 
6904     XtSetArg(args[0], name, &buf);
6905     XtGetValues(toplevel, args, 1);
6906 
6907     if (strstr(name, "Path") || strstr(name, "Directory")) {
6908 	TRACE(("%s %s: %s\n", tag, name, NonNull(buf)));
6909     } else if (strstr(name, "Command")) {
6910 	if (buf != NULL) {
6911 	    char **vec = (char **) (void *) buf;
6912 	    int n;
6913 	    TRACE(("%s %s:\n", tag, name));
6914 	    for (n = 0; vec[n] != NULL; ++n) {
6915 		TRACE((" arg[%d] = %s\n", n, vec[n]));
6916 	    }
6917 	} else {
6918 	    TRACE(("%s %s: %p\n", tag, name, buf));
6919 	}
6920     } else {
6921 	TRACE(("%s %s: %p\n", tag, name, buf));
6922     }
6923 }
6924 
6925 static void
trace_SM_props(void)6926 trace_SM_props(void)
6927 {
6928     /* *INDENT-OFF* */
6929     static struct { String app, cls; } table[] = {
6930 	{ XtNcurrentDirectory,	XtCCurrentDirectory },
6931 	{ XtNdieCallback,	XtNdiscardCommand },
6932 	{ XtCDiscardCommand,	XtNenvironment },
6933 	{ XtCEnvironment,	XtNinteractCallback },
6934 	{ XtNjoinSession,	XtCJoinSession },
6935 	{ XtNprogramPath,	XtCProgramPath },
6936 	{ XtNresignCommand,	XtCResignCommand },
6937 	{ XtNrestartCommand,	XtCRestartCommand },
6938 	{ XtNrestartStyle,	XtCRestartStyle },
6939 	{ XtNsaveCallback,	XtNsaveCompleteCallback },
6940 	{ XtNsessionID,		XtCSessionID },
6941 	{ XtNshutdownCommand,	XtCShutdownCommand },
6942     };
6943     /* *INDENT-ON* */
6944     Cardinal n;
6945     TRACE(("Session properties:\n"));
6946     for (n = 0; n < XtNumber(table); ++n) {
6947 	trace_1_SM("app", table[n].app);
6948 	trace_1_SM("cls", table[n].cls);
6949     }
6950 }
6951 #define TRACE_SM_PROPS()	trace_SM_props()
6952 #else
6953 #define TRACE_SM_PROPS()	/* nothing */
6954 #endif
6955 
6956 static void
die_callback(Widget w GCC_UNUSED,XtPointer client_data GCC_UNUSED,XtPointer call_data GCC_UNUSED)6957 die_callback(Widget w GCC_UNUSED,
6958 	     XtPointer client_data GCC_UNUSED,
6959 	     XtPointer call_data GCC_UNUSED)
6960 {
6961     TRACE(("die_callback client=%p, call=%p\n",
6962 	   (void *) client_data,
6963 	   (void *) call_data));
6964     TRACE_SM_PROPS();
6965     NormalExit();
6966 }
6967 
6968 static void
save_callback(Widget w GCC_UNUSED,XtPointer client_data GCC_UNUSED,XtPointer call_data)6969 save_callback(Widget w GCC_UNUSED,
6970 	      XtPointer client_data GCC_UNUSED,
6971 	      XtPointer call_data)
6972 {
6973     XtCheckpointToken token = (XtCheckpointToken) call_data;
6974     TRACE(("save_callback:\n"));
6975     TRACE(("... save_type            <-%d\n", token->save_type));
6976     TRACE(("... interact_style       <-%d\n", token->interact_style));
6977     TRACE(("... shutdown             <-%s\n", BtoS(token->shutdown)));
6978     TRACE(("... fast                 <-%s\n", BtoS(token->fast)));
6979     TRACE(("... cancel_shutdown      <-%s\n", BtoS(token->cancel_shutdown)));
6980     TRACE(("... phase                <-%d\n", token->phase));
6981     TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type));
6982     TRACE(("... request_cancel       ->%s\n", BtoS(token->request_cancel)));
6983     TRACE(("... request_next_phase   ->%s\n", BtoS(token->request_next_phase)));
6984     TRACE(("... save_success         ->%s\n", BtoS(token->save_success)));
6985     xtermUpdateRestartCommand(term);
6986     /* we have nothing more to save */
6987     token->save_success = True;
6988 }
6989 
6990 static void
icewatch(IceConn iceConn,IcePointer clientData GCC_UNUSED,Bool opening,IcePointer * watchData GCC_UNUSED)6991 icewatch(IceConn iceConn,
6992 	 IcePointer clientData GCC_UNUSED,
6993 	 Bool opening,
6994 	 IcePointer * watchData GCC_UNUSED)
6995 {
6996     if (opening) {
6997 	ice_fd = IceConnectionNumber(iceConn);
6998 	TRACE(("got IceConnectionNumber %d\n", ice_fd));
6999     } else {
7000 	ice_fd = -1;
7001 	TRACE(("reset IceConnectionNumber\n"));
7002     }
7003 }
7004 
7005 void
xtermOpenSession(void)7006 xtermOpenSession(void)
7007 {
7008     if (resource.sessionMgt) {
7009 	TRACE(("Enabling session-management callbacks\n"));
7010 	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
7011 	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
7012 
7013 	TRACE_SM_PROPS();
7014     }
7015 }
7016 
7017 void
xtermCloseSession(void)7018 xtermCloseSession(void)
7019 {
7020     IceRemoveConnectionWatch(icewatch, NULL);
7021 }
7022 
7023 typedef enum {
7024     B_ARG = 0,
7025     I_ARG,
7026     D_ARG,
7027     S_ARG
7028 } ParamType;
7029 
7030 #define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) }
7031 #define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) }
7032 #define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) }
7033 #define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) }
7034 
7035 typedef struct {
7036     const char name[30];
7037     ParamType type;
7038     Cardinal offset;
7039 } FontParams;
7040 
7041 /* *INDENT-OFF* */
7042 static const FontParams fontParams[] = {
7043     Iarg(XtNinitialFont,     screen.menu_font_number),	/* "-fc" */
7044     Barg(XtNallowBoldFonts,  screen.allowBoldFonts),	/* menu */
7045 #if OPT_BOX_CHARS
7046     Barg(XtNforceBoxChars,   screen.force_box_chars),	/* "-fbx" */
7047     Barg(XtNforcePackedFont, screen.force_packed),	/* menu */
7048 #endif
7049 #if OPT_DEC_CHRSET
7050     Barg(XtNfontDoublesize,  screen.font_doublesize),	/* menu */
7051 #endif
7052 #if OPT_WIDE_CHARS
7053     Barg(XtNutf8Fonts,       screen.utf8_fonts),	/* menu */
7054 #endif
7055 #if OPT_RENDERFONT
7056     Darg(XtNfaceSize,        misc.face_size[0]),	/* "-fs" */
7057     Sarg(XtNfaceName,        misc.default_xft.f_n),	/* "-fa" */
7058     Sarg(XtNrenderFont,      misc.render_font_s),	/* (resource) */
7059 #endif
7060 };
7061 /* *INDENT-ON* */
7062 
7063 #define RESTART_PARAMS (int)(XtNumber(fontParams) * 2)
7064 #define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset)
7065 
7066 /*
7067  * If no widget is given, no value is used.
7068  */
7069 static char *
formatFontParam(char * result,XtermWidget xw,const FontParams * parameter)7070 formatFontParam(char *result, XtermWidget xw, const FontParams * parameter)
7071 {
7072     sprintf(result, "%s*%s:", ProgramName, parameter->name);
7073     if (xw != None) {
7074 	char *next = result + strlen(result);
7075 	switch (parameter->type) {
7076 	case B_ARG:
7077 	    sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset)
7078 		    ? "true"
7079 		    : "false");
7080 	    break;
7081 	case I_ARG:
7082 	    sprintf(next, "%d", TypedPtr(int));
7083 	    break;
7084 	case D_ARG:
7085 	    sprintf(next, "%.1f", TypedPtr(float));
7086 	    break;
7087 	case S_ARG:
7088 	    strcpy(next, TypedPtr(char *));
7089 #if OPT_RENDERFONT
7090 	    if (!strcmp(parameter->name, XtNfaceName)) {
7091 		if (IsEmpty(next)
7092 		    && xw->work.render_font) {
7093 		    strcpy(next, DEFFACENAME_AUTO);
7094 		}
7095 	    } else if (!strcmp(parameter->name, XtNrenderFont)) {
7096 		if (xw->work.render_font == erDefault
7097 		    && IsEmpty(xw->misc.default_xft.f_n)) {
7098 		    strcpy(next, "DefaultOff");
7099 		}
7100 	    }
7101 #endif
7102 	    break;
7103 	}
7104     }
7105     return result;
7106 }
7107 
7108 #if OPT_TRACE
7109 static void
dumpFontParams(XtermWidget xw)7110 dumpFontParams(XtermWidget xw)
7111 {
7112     char buffer[1024];
7113     Cardinal n;
7114 
7115     TRACE(("FontParams:\n"));
7116     for (n = 0; n < XtNumber(fontParams); ++n) {
7117 	TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n)));
7118     }
7119 }
7120 #else
7121 #define dumpFontParams(xw)	/* nothing */
7122 #endif
7123 
7124 static Boolean
findFontParams(int argc,char ** argv)7125 findFontParams(int argc, char **argv)
7126 {
7127     Boolean result = False;
7128 
7129     if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) {
7130 	int n;
7131 
7132 	for (n = 0; n < RESTART_PARAMS; ++n) {
7133 	    int my_index = argc - restart_params - n - 1;
7134 	    int my_param = (RESTART_PARAMS - n - 1) / 2;
7135 	    char *actual = argv[my_index];
7136 	    char expect[1024];
7137 	    Boolean value = (Boolean) ((n % 2) == 0);
7138 
7139 	    result = False;
7140 	    TRACE(("...index: %d\n", my_index));
7141 	    TRACE(("...param: %d\n", my_param));
7142 	    TRACE(("...actual %s\n", actual));
7143 	    if (IsEmpty(actual))
7144 		break;
7145 
7146 	    if (value) {
7147 		formatFontParam(expect, None, fontParams + my_param);
7148 	    } else {
7149 		strcpy(expect, "-xrm");
7150 	    }
7151 
7152 	    TRACE(("...expect %s\n", expect));
7153 
7154 	    if (value) {
7155 		if (strlen(expect) >= strlen(actual))
7156 		    break;
7157 		if (strncmp(expect, actual, strlen(expect)))
7158 		    break;
7159 	    } else {
7160 		if (strcmp(actual, expect))
7161 		    break;
7162 	    }
7163 	    TRACE(("fixme/ok:%d\n", n));
7164 	    result = True;
7165 	}
7166 	TRACE(("findFontParams: %s (tested %d of %d parameters)\n",
7167 	       BtoS(result), n + 1, RESTART_PARAMS));
7168     }
7169     return result;
7170 }
7171 
7172 static int
insertFontParams(XtermWidget xw,int * targetp,Bool first)7173 insertFontParams(XtermWidget xw, int *targetp, Bool first)
7174 {
7175     int changed = 0;
7176     int n;
7177     int target = *targetp;
7178     char buffer[1024];
7179     const char *option = "-xrm";
7180 
7181     for (n = 0; n < (int) XtNumber(fontParams); ++n) {
7182 	formatFontParam(buffer, xw, fontParams + n);
7183 	TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer));
7184 	if (restart_command[target] == NULL)
7185 	    restart_command[target] = x_strdup(option);
7186 	++target;
7187 	if (first) {
7188 	    restart_command[target] = x_strdup(buffer);
7189 	    ++changed;
7190 	} else if (restart_command[target] == NULL
7191 		   || strcmp(restart_command[target], buffer)) {
7192 	    free(restart_command[target]);
7193 	    restart_command[target] = x_strdup(buffer);
7194 	    ++changed;
7195 	}
7196 	++target;
7197     }
7198     *targetp = target;
7199     return changed;
7200 }
7201 
7202 void
xtermUpdateRestartCommand(XtermWidget xw)7203 xtermUpdateRestartCommand(XtermWidget xw)
7204 {
7205     if (resource.sessionMgt) {
7206 	Arg args[1];
7207 	char **argv = 0;
7208 
7209 	XtSetArg(args[0], XtNrestartCommand, &argv);
7210 	XtGetValues(toplevel, args, 1);
7211 	if (argv != NULL) {
7212 	    static int my_params = 0;
7213 
7214 	    int changes = 0;
7215 	    Boolean first = False;
7216 	    int argc;
7217 	    int want;
7218 	    int source, target;
7219 
7220 	    TRACE(("xtermUpdateRestartCommand\n"));
7221 	    dumpFontParams(xw);
7222 	    for (argc = 0; argv[argc] != NULL; ++argc) {
7223 		TRACE((" arg[%d] = %s\n", argc, argv[argc]));
7224 		;
7225 	    }
7226 	    want = argc - (restart_params + RESTART_PARAMS);
7227 
7228 	    TRACE((" argc:           %d\n", argc));
7229 	    TRACE((" restart_params: %d\n", restart_params));
7230 	    TRACE((" want to insert: %d\n", want));
7231 
7232 	    /*
7233 	     * If we already have the font-choice option, do not add it again.
7234 	     */
7235 	    if (findFontParams(argc, argv)) {
7236 		my_params = (want);
7237 	    } else {
7238 		first = True;
7239 		my_params = (argc - restart_params);
7240 	    }
7241 	    TRACE((" my_params:      %d\n", my_params));
7242 
7243 	    if (my_params > argc) {
7244 		TRACE((" re-allocate restartCommand\n"));
7245 		FreeAndNull(restart_command);
7246 	    }
7247 
7248 	    if (restart_command == NULL) {
7249 		int need = argc + RESTART_PARAMS + 1;
7250 
7251 		restart_command = TypeCallocN(char *, need);
7252 
7253 		TRACE(("..inserting font-parameters\n"));
7254 		for (source = target = 0; source < argc; ++source) {
7255 		    if (source == my_params) {
7256 			changes += insertFontParams(xw, &target, first);
7257 			if (!first) {
7258 			    source += (RESTART_PARAMS - 1);
7259 			    continue;
7260 			}
7261 		    }
7262 		    if (argv[source] == NULL)
7263 			break;
7264 		    restart_command[target++] = x_strdup(argv[source]);
7265 		}
7266 		restart_command[target] = NULL;
7267 	    } else {
7268 		TRACE(("..replacing font-parameters\n"));
7269 		target = my_params;
7270 		changes += insertFontParams(xw, &target, first);
7271 	    }
7272 	    if (changes) {
7273 		TRACE(("..%d parameters changed\n", changes));
7274 		XtSetArg(args[0], XtNrestartCommand, restart_command);
7275 		XtSetValues(toplevel, args, 1);
7276 	    } else {
7277 		TRACE(("..NO parameters changed\n"));
7278 	    }
7279 	}
7280 	TRACE_SM_PROPS();
7281     }
7282 }
7283 #endif /* OPT_SESSION_MGT */
7284 
7285 Widget
xtermOpenApplication(XtAppContext * app_context_return,String my_class,XrmOptionDescRec * options,Cardinal num_options,int * argc_in_out,char ** argv_in_out,String * fallback_resources,WidgetClass widget_class,ArgList args,Cardinal num_args)7286 xtermOpenApplication(XtAppContext * app_context_return,
7287 		     String my_class,
7288 		     XrmOptionDescRec * options,
7289 		     Cardinal num_options,
7290 		     int *argc_in_out,
7291 		     char **argv_in_out,
7292 		     String *fallback_resources,
7293 		     WidgetClass widget_class,
7294 		     ArgList args,
7295 		     Cardinal num_args)
7296 {
7297     Widget result;
7298 
7299     XtSetErrorHandler(xt_error);
7300 #if OPT_SESSION_MGT
7301     result = XtOpenApplication(app_context_return,
7302 			       my_class,
7303 			       options,
7304 			       num_options,
7305 			       argc_in_out,
7306 			       argv_in_out,
7307 			       fallback_resources,
7308 			       widget_class,
7309 			       args,
7310 			       num_args);
7311     IceAddConnectionWatch(icewatch, NULL);
7312 #else
7313     (void) widget_class;
7314     (void) args;
7315     (void) num_args;
7316     result = XtAppInitialize(app_context_return,
7317 			     my_class,
7318 			     options,
7319 			     num_options,
7320 			     argc_in_out,
7321 			     argv_in_out,
7322 			     fallback_resources,
7323 			     NULL, 0);
7324 #endif /* OPT_SESSION_MGT */
7325     XtSetErrorHandler(NULL);
7326 
7327     return result;
7328 }
7329 
7330 /*
7331  * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
7332  * our own error-handler.
7333  */
7334 /* ARGSUSED */
7335 int
ignore_x11_error(Display * dpy GCC_UNUSED,XErrorEvent * event GCC_UNUSED)7336 ignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
7337 {
7338     return 1;
7339 }
7340 
7341 static int x11_errors;
7342 
7343 static int
catch_x11_error(Display * display,XErrorEvent * error_event)7344 catch_x11_error(Display *display, XErrorEvent *error_event)
7345 {
7346     (void) display;
7347     (void) error_event;
7348     ++x11_errors;
7349     return 0;
7350 }
7351 
7352 Boolean
xtermGetWinAttrs(Display * dpy,Window win,XWindowAttributes * attrs)7353 xtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
7354 {
7355     Boolean result = False;
7356     Status code;
7357 
7358     memset(attrs, 0, sizeof(*attrs));
7359     if (win != None) {
7360 	XErrorHandler save = XSetErrorHandler(catch_x11_error);
7361 	x11_errors = 0;
7362 	code = XGetWindowAttributes(dpy, win, attrs);
7363 	XSetErrorHandler(save);
7364 	result = (Boolean) ((code != 0) && !x11_errors);
7365 	if (result) {
7366 	    TRACE_WIN_ATTRS(attrs);
7367 	} else {
7368 	    xtermWarning("invalid window-id %ld\n", (long) win);
7369 	}
7370     }
7371     return result;
7372 }
7373 
7374 Boolean
xtermGetWinProp(Display * display,Window win,Atom property,long long_offset,long long_length,Atom req_type,Atom * actual_type_return,int * actual_format_return,unsigned long * nitems_return,unsigned long * bytes_after_return,unsigned char ** prop_return)7375 xtermGetWinProp(Display *display,
7376 		Window win,
7377 		Atom property,
7378 		long long_offset,
7379 		long long_length,
7380 		Atom req_type,
7381 		Atom *actual_type_return,
7382 		int *actual_format_return,
7383 		unsigned long *nitems_return,
7384 		unsigned long *bytes_after_return,
7385 		unsigned char **prop_return)
7386 {
7387     Boolean result = False;
7388 
7389     if (win != None) {
7390 	XErrorHandler save = XSetErrorHandler(catch_x11_error);
7391 	x11_errors = 0;
7392 	if (XGetWindowProperty(display,
7393 			       win,
7394 			       property,
7395 			       long_offset,
7396 			       long_length,
7397 			       False,
7398 			       req_type,
7399 			       actual_type_return,
7400 			       actual_format_return,
7401 			       nitems_return,
7402 			       bytes_after_return,
7403 			       prop_return) == Success
7404 	    && x11_errors == 0) {
7405 	    result = True;
7406 	}
7407 	XSetErrorHandler(save);
7408     }
7409     return result;
7410 }
7411 
7412 void
xtermEmbedWindow(Window winToEmbedInto)7413 xtermEmbedWindow(Window winToEmbedInto)
7414 {
7415     Display *dpy = XtDisplay(toplevel);
7416     XWindowAttributes attrs;
7417 
7418     TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
7419     if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
7420 	XtermWidget xw = term;
7421 	TScreen *screen = TScreenOf(xw);
7422 
7423 	XtRealizeWidget(toplevel);
7424 
7425 	TRACE(("...reparenting toplevel %#lx into %#lx\n",
7426 	       XtWindow(toplevel),
7427 	       winToEmbedInto));
7428 	XReparentWindow(dpy,
7429 			XtWindow(toplevel),
7430 			winToEmbedInto, 0, 0);
7431 
7432 	screen->embed_high = (Dimension) attrs.height;
7433 	screen->embed_wide = (Dimension) attrs.width;
7434     }
7435 }
7436 
7437 void
free_string(String value)7438 free_string(String value)
7439 {
7440     free((void *) value);
7441 }
7442 
7443 /* Set tty's idea of window size, using the given file descriptor 'fd'. */
7444 int
update_winsize(int fd,int rows,int cols,int height,int width)7445 update_winsize(int fd, int rows, int cols, int height, int width)
7446 {
7447     int code = -1;
7448 #ifdef TTYSIZE_STRUCT
7449     static int last_rows = -1;
7450     static int last_cols = -1;
7451     static int last_high = -1;
7452     static int last_wide = -1;
7453 
7454     TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n",
7455 	   last_rows, last_cols, last_high, last_wide,
7456 	   rows, cols, height, width));
7457 
7458     if (rows != last_rows
7459 	|| cols != last_cols
7460 	|| last_high != height
7461 	|| last_wide != width) {
7462 	TTYSIZE_STRUCT ts;
7463 
7464 	last_rows = rows;
7465 	last_cols = cols;
7466 	last_high = height;
7467 	last_wide = width;
7468 	setup_winsize(ts, rows, cols, height, width);
7469 	TRACE_RC(code, SET_TTYSIZE(fd, ts));
7470 	trace_winsize(ts, "from SET_TTYSIZE");
7471     }
7472 #endif
7473 
7474     (void) rows;
7475     (void) cols;
7476     (void) height;
7477     (void) width;
7478 
7479     return code;
7480 }
7481 
7482 /*
7483  * Update stty settings to match the values returned by dtterm window
7484  * manipulation 18 and 19.
7485  */
7486 void
xtermSetWinSize(XtermWidget xw)7487 xtermSetWinSize(XtermWidget xw)
7488 {
7489 #if OPT_TEK4014
7490     if (!TEK4014_ACTIVE(xw))
7491 #endif
7492 	if (XtIsRealized((Widget) xw)) {
7493 	    TScreen *screen = TScreenOf(xw);
7494 
7495 	    TRACE(("xtermSetWinSize\n"));
7496 	    update_winsize(screen->respond,
7497 			   MaxRows(screen),
7498 			   MaxCols(screen),
7499 			   Height(screen),
7500 			   Width(screen));
7501 	}
7502 }
7503 
7504 #if OPT_XTERM_SGR
7505 
7506 #if OPT_TRACE
7507 static char *
traceIFlags(IFlags flags)7508 traceIFlags(IFlags flags)
7509 {
7510     static char result[1000];
7511     result[0] = '\0';
7512 #define DATA(name) if (flags & name) { strcat(result, " " #name); }
7513     DATA(INVERSE);
7514     DATA(UNDERLINE);
7515     DATA(BOLD);
7516     DATA(BLINK);
7517     DATA(INVISIBLE);
7518     DATA(BG_COLOR);
7519     DATA(FG_COLOR);
7520 
7521 #if OPT_WIDE_ATTRS
7522     DATA(ATR_FAINT);
7523     DATA(ATR_ITALIC);
7524     DATA(ATR_STRIKEOUT);
7525     DATA(ATR_DBL_UNDER);
7526     DATA(ATR_DIRECT_FG);
7527     DATA(ATR_DIRECT_BG);
7528 #endif
7529 #undef DATA
7530     return result;
7531 }
7532 
7533 static char *
traceIStack(unsigned flags)7534 traceIStack(unsigned flags)
7535 {
7536     static char result[1000];
7537     result[0] = '\0';
7538 #define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7539     DATA(INVERSE);
7540     DATA(UNDERLINE);
7541     DATA(BOLD);
7542     DATA(BLINK);
7543     DATA(INVISIBLE);
7544 #if OPT_ISO_COLORS
7545     DATA(BG_COLOR);
7546     DATA(FG_COLOR);
7547 #endif
7548 
7549 #if OPT_WIDE_ATTRS
7550     DATA(ATR_FAINT);
7551     DATA(ATR_ITALIC);
7552     DATA(ATR_STRIKEOUT);
7553     DATA(ATR_DBL_UNDER);
7554     /* direct-colors are a special case of ISO-colors (see above) */
7555 #endif
7556 #undef DATA
7557     return result;
7558 }
7559 #endif
7560 
7561 void
xtermPushSGR(XtermWidget xw,int value)7562 xtermPushSGR(XtermWidget xw, int value)
7563 {
7564     SavedSGR *s = &(xw->saved_sgr);
7565 
7566     TRACE(("xtermPushSGR %d mask %#x %s\n",
7567 	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
7568 
7569     if (s->used < MAX_SAVED_SGR) {
7570 	s->stack[s->used].mask = (IFlags) value;
7571 #define PUSH_FLAG(name) \
7572 	    s->stack[s->used].name = xw->name;\
7573 	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
7574 #define PUSH_DATA(name) \
7575 	    s->stack[s->used].name = xw->name;\
7576 	    TRACE(("...may pop %s %d\n", #name, xw->name))
7577 	PUSH_FLAG(flags);
7578 #if OPT_ISO_COLORS
7579 	PUSH_DATA(sgr_foreground);
7580 	PUSH_DATA(sgr_background);
7581 	PUSH_DATA(sgr_38_xcolors);
7582 #endif
7583     }
7584     s->used++;
7585 }
7586 
7587 #define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
7588 
7589 void
xtermReportSGR(XtermWidget xw,XTermRect * value)7590 xtermReportSGR(XtermWidget xw, XTermRect *value)
7591 {
7592     TScreen *screen = TScreenOf(xw);
7593     char reply[BUFSIZ];
7594     CellData working;
7595     int row, col;
7596     Boolean first = True;
7597 
7598     TRACE(("xtermReportSGR %d,%d - %d,%d\n",
7599 	   value->top, value->left,
7600 	   value->bottom, value->right));
7601 
7602     memset(&working, 0, sizeof(working));
7603     for (row = value->top - 1; row < value->bottom; ++row) {
7604 	LineData *ld = getLineData(screen, row);
7605 	if (ld == 0)
7606 	    continue;
7607 	for (col = value->left - 1; col < value->right; ++col) {
7608 	    if (first) {
7609 		first = False;
7610 		saveCellData(screen, &working, 0, ld, NULL, col);
7611 	    }
7612 	    working.attribs &= ld->attribs[col];
7613 #if OPT_ISO_COLORS
7614 	    if (working.attribs & FG_COLOR
7615 		&& GetCellColorFG(working.color)
7616 		!= GetCellColorFG(ld->color[col])) {
7617 		IAttrClr(working.attribs, FG_COLOR);
7618 	    }
7619 	    if (working.attribs & BG_COLOR
7620 		&& GetCellColorBG(working.color)
7621 		!= GetCellColorBG(ld->color[col])) {
7622 		IAttrClr(working.attribs, BG_COLOR);
7623 	    }
7624 #endif
7625 	}
7626     }
7627     xtermFormatSGR(xw, reply,
7628 		   working.attribs,
7629 		   GetCellColorFG(working.color),
7630 		   GetCellColorBG(working.color));
7631     unparseputc1(xw, ANSI_CSI);
7632     unparseputs(xw, reply);
7633     unparseputc(xw, 'm');
7634     unparse_end(xw);
7635 }
7636 
7637 void
xtermPopSGR(XtermWidget xw)7638 xtermPopSGR(XtermWidget xw)
7639 {
7640     SavedSGR *s = &(xw->saved_sgr);
7641 
7642     TRACE(("xtermPopSGR %d\n", s->used));
7643 
7644     if (s->used > 0) {
7645 	if (s->used-- <= MAX_SAVED_SGR) {
7646 	    IFlags mask = s->stack[s->used].mask;
7647 	    Boolean changed = False;
7648 
7649 	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
7650 	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
7651 	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
7652 #define POP_FLAG(name) \
7653 	    if (xBIT(ps##name - 1) & mask) { \
7654 	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7655 		    changed = True; \
7656 		    UIntClr(xw->flags, name); \
7657 		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7658 		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7659 		} \
7660 	    }
7661 #define POP_FLAG2(name,part) \
7662 	    if (xBIT(ps##name - 1) & mask) { \
7663 	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
7664 		    changed = True; \
7665 		    UIntClr(xw->flags, part); \
7666 		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
7667 		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
7668 		} \
7669 	    }
7670 #define POP_DATA(name,value) \
7671 	    if (xBIT(ps##name - 1) & mask) { \
7672 	        Bool always = False; \
7673 	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7674 		    always = changed = True; \
7675 		    UIntClr(xw->flags, name); \
7676 		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7677 		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7678 		} \
7679 		if (always || (xw->value != s->stack[s->used].value)) { \
7680 		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
7681 		    xw->value = s->stack[s->used].value; \
7682 		    changed = True; \
7683 		} \
7684 	    }
7685 	    POP_FLAG(BOLD);
7686 	    POP_FLAG(UNDERLINE);
7687 	    POP_FLAG(BLINK);
7688 	    POP_FLAG(INVERSE);
7689 	    POP_FLAG(INVISIBLE);
7690 #if OPT_WIDE_ATTRS
7691 	    if (xBIT(psATR_ITALIC - 1) & mask) {
7692 		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
7693 	    }
7694 	    POP_FLAG(ATR_ITALIC);
7695 	    POP_FLAG(ATR_FAINT);
7696 	    POP_FLAG(ATR_STRIKEOUT);
7697 	    POP_FLAG(ATR_DBL_UNDER);
7698 #endif
7699 #if OPT_ISO_COLORS
7700 	    POP_DATA(FG_COLOR, sgr_foreground);
7701 	    POP_DATA(BG_COLOR, sgr_background);
7702 	    POP_DATA(BG_COLOR, sgr_38_xcolors);
7703 #if OPT_DIRECT_COLOR
7704 	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
7705 	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
7706 #endif
7707 	    if (changed) {
7708 		setExtendedColors(xw);
7709 	    }
7710 #else
7711 	    (void) changed;
7712 #endif
7713 	}
7714 #if OPT_ISO_COLORS
7715 	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
7716 	       traceIFlags(xw->flags),
7717 	       xw->sgr_foreground,
7718 	       xw->sgr_background,
7719 	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
7720 #else
7721 	TRACE(("xtermP -> flags%s\n",
7722 	       traceIFlags(xw->flags)));
7723 #endif
7724     }
7725 }
7726 
7727 #if OPT_ISO_COLORS
7728 static ColorSlot *
allocColorSlot(XtermWidget xw,int slot)7729 allocColorSlot(XtermWidget xw, int slot)
7730 {
7731     SavedColors *s = &(xw->saved_colors);
7732     ColorSlot *result = NULL;
7733 
7734     if (slot >= 0 && slot < MAX_SAVED_SGR) {
7735 	if (s->palettes[slot] == NULL) {
7736 	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
7737 						     sizeof(ColorSlot)
7738 						     + (sizeof(ColorRes)
7739 							* MAXCOLORS));
7740 	}
7741 	result = s->palettes[slot];
7742     }
7743     return result;
7744 }
7745 
7746 static void
popOldColors(XtermWidget xw,ScrnColors * source)7747 popOldColors(XtermWidget xw, ScrnColors * source)
7748 {
7749     Boolean changed = False;
7750     ScrnColors *target = xw->work.oldColors;
7751 
7752     if (source->which != target->which) {
7753 	changed = True;
7754     } else {
7755 	int n;
7756 	for (n = 0; n < NCOLORS; ++n) {
7757 	    if (COLOR_DEFINED(source, n)) {
7758 		if (COLOR_DEFINED(target, n)) {
7759 		    if (source->colors[n] != target->colors[n]) {
7760 			changed = True;
7761 			break;
7762 		    }
7763 		} else {
7764 		    changed = True;
7765 		    break;
7766 		}
7767 	    } else if (COLOR_DEFINED(target, n)) {
7768 		changed = True;
7769 		break;
7770 	    }
7771 	}
7772     }
7773     if (changed) {
7774 	ChangeColors(xw, source);
7775 	UpdateOldColors(xw, source);
7776     }
7777 }
7778 #endif /* OPT_ISO_COLORS */
7779 
7780 #define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
7781 #define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
7782 
7783 /*
7784  * By default, a "push" increments the stack after copying to the current
7785  * slot.  But a specific target allows one to copy into a specific slot.
7786  */
7787 void
xtermPushColors(XtermWidget xw,int value)7788 xtermPushColors(XtermWidget xw, int value)
7789 {
7790 #if OPT_ISO_COLORS
7791     SavedColors *s = &(xw->saved_colors);
7792     int pushed = s->used;
7793     int actual = (value <= 0) ? pushed : (value - 1);
7794 
7795     TRACE(("xtermPushColors %d:%d\n", actual, pushed));
7796     if (actual < MAX_SAVED_SGR && actual >= 0) {
7797 	TScreen *screen = TScreenOf(xw);
7798 	ColorSlot *palette;
7799 
7800 	if ((palette = allocColorSlot(xw, actual)) != NULL) {
7801 	    GetColors(xw, &(palette->base));
7802 	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
7803 	    if (value < 0) {
7804 		s->used++;
7805 		if (s->last < s->used)
7806 		    s->last = s->used;
7807 	    } else {
7808 		s->used = value;
7809 	    }
7810 	}
7811     }
7812 #else
7813     (void) xw;
7814     (void) value;
7815 #endif
7816 }
7817 
7818 void
xtermPopColors(XtermWidget xw,int value)7819 xtermPopColors(XtermWidget xw, int value)
7820 {
7821 #if OPT_ISO_COLORS
7822     SavedColors *s = &(xw->saved_colors);
7823     int popped = (s->used - 1);
7824     int actual = (value <= 0) ? popped : (value - 1);
7825 
7826     TRACE(("xtermPopColors %d:%d\n", actual, popped));
7827     if (actual < MAX_SAVED_SGR && actual >= 0) {
7828 	TScreen *screen = TScreenOf(xw);
7829 	ColorSlot *palette;
7830 
7831 	if ((palette = s->palettes[actual]) != NULL) {
7832 	    Boolean changed = DiffColorSlot(screen->Acolors,
7833 					    palette->ansi,
7834 					    MAXCOLORS);
7835 
7836 	    GetOldColors(xw);
7837 	    popOldColors(xw, &(palette->base));
7838 	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
7839 	    s->used = actual;
7840 	    if (changed)
7841 		xtermRepaint(xw);
7842 	}
7843     }
7844 #else
7845     (void) xw;
7846     (void) value;
7847 #endif
7848 }
7849 
7850 void
xtermReportColors(XtermWidget xw)7851 xtermReportColors(XtermWidget xw)
7852 {
7853     ANSI reply;
7854     SavedColors *s = &(xw->saved_colors);
7855 
7856     memset(&reply, 0, sizeof(reply));
7857     reply.a_type = ANSI_CSI;
7858     reply.a_pintro = '?';
7859     reply.a_param[reply.a_nparam++] = (ParmType) s->used;
7860     reply.a_param[reply.a_nparam++] = (ParmType) s->last;
7861     reply.a_inters = '#';
7862     reply.a_final = 'Q';
7863     unparseseq(xw, &reply);
7864 }
7865 #endif /* OPT_XTERM_SGR */
7866