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(¶ms, &cp);
4960 switch (params.a_final) {
4961 case 'p': /* ReGIS */
4962 #if OPT_REGIS_GRAPHICS
4963 if (optRegisGraphics(screen)) {
4964 parse_regis(xw, ¶ms, 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, ¶ms, 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(¶ms, 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