"Fossies" - the Fresh Open Source Software Archive 
Member "xterm-379/misc.c" (7 Jan 2023, 190536 Bytes) of package /linux/misc/xterm-379.tgz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "misc.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
377_vs_379.
1 /* $XTermId: misc.c,v 1.1044 2023/01/07 01:11:16 tom Exp $ */
2
3 /*
4 * Copyright 1999-2022,2023 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 HAVE_MKSTEMP
103 #define MakeTemp(f) mkstemp(f)
104 #else
105 #define MakeTemp(f) mktemp(f)
106 #endif
107
108 #ifdef VMS
109 #define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
110 #ifdef ALLOWLOGFILEEXEC
111 #undef ALLOWLOGFILEEXEC
112 #endif
113 #endif /* VMS */
114
115 #if USE_DOUBLE_BUFFER
116 #include <X11/extensions/Xdbe.h>
117 #endif
118
119 #if OPT_WIDE_CHARS
120 #include <wctype.h>
121 #endif
122
123 #if OPT_TEK4014
124 #define OUR_EVENT(event,Type) \
125 (event.type == Type && \
126 (event.xcrossing.window == XtWindow(XtParent(xw)) || \
127 (tekWidget && \
128 event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
129 #else
130 #define OUR_EVENT(event,Type) \
131 (event.type == Type && \
132 (event.xcrossing.window == XtWindow(XtParent(xw))))
133 #endif
134
135 #define VB_DELAY screen->visualBellDelay
136 #define EVENT_DELAY TScreenOf(term)->nextEventDelay
137
138 static Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
139 static Cursor make_hidden_cursor(XtermWidget);
140
141 static char emptyString[] = "";
142
143 #if OPT_EXEC_XTERM
144 /* Like readlink(2), but returns a malloc()ed buffer, or NULL on
145 error; adapted from libc docs */
146 static char *
147 Readlink(const char *filename)
148 {
149 char *buf = NULL;
150 size_t size = 100;
151
152 for (;;) {
153 int n;
154 char *tmp = TypeRealloc(char, size, buf);
155 if (tmp == NULL) {
156 free(buf);
157 return NULL;
158 }
159 buf = tmp;
160 memset(buf, 0, size);
161
162 n = (int) readlink(filename, buf, size);
163 if (n < 0) {
164 free(buf);
165 return NULL;
166 }
167
168 if ((unsigned) n < size) {
169 return buf;
170 }
171
172 size *= 2;
173 }
174 }
175 #endif /* OPT_EXEC_XTERM */
176
177 static void
178 Sleep(int msec)
179 {
180 static struct timeval select_timeout;
181
182 select_timeout.tv_sec = 0;
183 select_timeout.tv_usec = msec * 1000;
184 select(0, 0, 0, 0, &select_timeout);
185 }
186
187 static void
188 selectwindow(XtermWidget xw, int flag)
189 {
190 TScreen *screen = TScreenOf(xw);
191
192 TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
193
194 #if OPT_TEK4014
195 if (TEK4014_ACTIVE(xw)) {
196 if (!Ttoggled)
197 TCursorToggle(tekWidget, TOGGLE);
198 screen->select |= flag;
199 if (!Ttoggled)
200 TCursorToggle(tekWidget, TOGGLE);
201 } else
202 #endif
203 {
204 #if OPT_INPUT_METHOD
205 TInput *input = lookupTInput(xw, (Widget) xw);
206 if (input && input->xic)
207 XSetICFocus(input->xic);
208 #endif
209
210 if (screen->cursor_state && CursorMoved(screen))
211 HideCursor(xw);
212 screen->select |= flag;
213 if (screen->cursor_state)
214 ShowCursor(xw);
215 }
216 GetScrollLock(screen);
217 }
218
219 static void
220 unselectwindow(XtermWidget xw, int flag)
221 {
222 TScreen *screen = TScreenOf(xw);
223
224 TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
225
226 if (screen->hide_pointer && screen->pointer_mode < pFocused) {
227 screen->hide_pointer = False;
228 xtermDisplayPointer(xw);
229 }
230
231 screen->select &= ~flag;
232
233 if (!screen->always_highlight) {
234 #if OPT_TEK4014
235 if (TEK4014_ACTIVE(xw)) {
236 if (!Ttoggled)
237 TCursorToggle(tekWidget, TOGGLE);
238 if (!Ttoggled)
239 TCursorToggle(tekWidget, TOGGLE);
240 } else
241 #endif
242 {
243 #if OPT_INPUT_METHOD
244 TInput *input = lookupTInput(xw, (Widget) xw);
245 if (input && input->xic)
246 XUnsetICFocus(input->xic);
247 #endif
248
249 if (screen->cursor_state && CursorMoved(screen))
250 HideCursor(xw);
251 if (screen->cursor_state)
252 ShowCursor(xw);
253 }
254 }
255 }
256
257 static void
258 DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
259 {
260 TScreen *screen = TScreenOf(xw);
261
262 TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
263 TRACE_FOCUS(xw, ev);
264 if (((ev->detail) != NotifyInferior) &&
265 ev->focus &&
266 !(screen->select & FOCUS))
267 selectwindow(xw, INWINDOW);
268 }
269
270 static void
271 DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
272 {
273 TScreen *screen = TScreenOf(xw);
274
275 TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
276 TRACE_FOCUS(xw, ev);
277 if (((ev->detail) != NotifyInferior) &&
278 ev->focus &&
279 !(screen->select & FOCUS))
280 unselectwindow(xw, INWINDOW);
281 }
282
283 #ifndef XUrgencyHint
284 #define XUrgencyHint (1L << 8) /* X11R5 does not define */
285 #endif
286
287 static void
288 setXUrgency(XtermWidget xw, Bool enable)
289 {
290 TScreen *screen = TScreenOf(xw);
291
292 if (screen->bellIsUrgent) {
293 XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
294 if (h != 0) {
295 if (enable && !(screen->select & FOCUS)) {
296 h->flags |= XUrgencyHint;
297 } else {
298 h->flags &= ~XUrgencyHint;
299 }
300 XSetWMHints(screen->display, VShellWindow(xw), h);
301 }
302 }
303 }
304
305 void
306 do_xevents(XtermWidget xw)
307 {
308 TScreen *screen = TScreenOf(xw);
309
310 if (xtermAppPending()
311 ||
312 #if defined(VMS) || defined(__VMS)
313 screen->display->qlen > 0
314 #else
315 GetBytesAvailable(ConnectionNumber(screen->display)) > 0
316 #endif
317 )
318 xevents(xw);
319 }
320
321 void
322 xtermDisplayPointer(XtermWidget xw)
323 {
324 TScreen *screen = TScreenOf(xw);
325
326 if (screen->Vshow) {
327 if (screen->hide_pointer) {
328 TRACE(("Display text pointer (hidden)\n"));
329 XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
330 } else {
331 TRACE(("Display text pointer (visible)\n"));
332 recolor_cursor(screen,
333 screen->pointer_cursor,
334 T_COLOR(screen, MOUSE_FG),
335 T_COLOR(screen, MOUSE_BG));
336 XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
337 }
338 }
339 }
340
341 void
342 xtermShowPointer(XtermWidget xw, Bool enable)
343 {
344 static int tried = -1;
345 TScreen *screen = TScreenOf(xw);
346
347 #if OPT_TEK4014
348 if (TEK4014_SHOWN(xw))
349 enable = True;
350 #endif
351
352 /*
353 * Whether we actually hide the pointer depends on the pointer-mode and
354 * the mouse-mode:
355 */
356 if (!enable) {
357 switch (screen->pointer_mode) {
358 case pNever:
359 enable = True;
360 break;
361 case pNoMouse:
362 if (screen->send_mouse_pos != MOUSE_OFF)
363 enable = True;
364 break;
365 case pAlways:
366 case pFocused:
367 break;
368 }
369 }
370
371 if (enable) {
372 if (screen->hide_pointer) {
373 screen->hide_pointer = False;
374 xtermDisplayPointer(xw);
375 switch (screen->send_mouse_pos) {
376 case ANY_EVENT_MOUSE:
377 break;
378 default:
379 MotionOff(screen, xw);
380 break;
381 }
382 }
383 } else if (!(screen->hide_pointer) && (tried <= 0)) {
384 if (screen->hidden_cursor == 0) {
385 screen->hidden_cursor = make_hidden_cursor(xw);
386 }
387 if (screen->hidden_cursor == 0) {
388 tried = 1;
389 } else {
390 tried = 0;
391 screen->hide_pointer = True;
392 xtermDisplayPointer(xw);
393 MotionOn(screen, xw);
394 }
395 }
396 }
397
398 /* true if p contains q */
399 #define ExposeContains(p,q) \
400 ((p)->y <= (q)->y \
401 && (p)->x <= (q)->x \
402 && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
403 && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
404
405 static XtInputMask
406 mergeExposeEvents(XEvent *target)
407 {
408 XEvent next_event;
409 XExposeEvent *p;
410
411 XtAppNextEvent(app_con, target);
412 p = (XExposeEvent *) target;
413
414 while (XtAppPending(app_con)
415 && XtAppPeekEvent(app_con, &next_event)
416 && next_event.type == Expose) {
417 Boolean merge_this = False;
418 XExposeEvent *q = (XExposeEvent *) (&next_event);
419
420 XtAppNextEvent(app_con, &next_event);
421 TRACE_EVENT("pending", &next_event, (String *) 0, 0);
422
423 /*
424 * If either window is contained within the other, merge the events.
425 * The traces show that there are also cases where a full repaint of
426 * a window is broken into 3 or more rectangles, which do not arrive
427 * in the same instant. We could merge those if xterm were modified
428 * to skim several events ahead.
429 */
430 if (p->window == q->window) {
431 if (ExposeContains(p, q)) {
432 TRACE(("pending Expose...merged forward\n"));
433 merge_this = True;
434 next_event = *target;
435 } else if (ExposeContains(q, p)) {
436 TRACE(("pending Expose...merged backward\n"));
437 merge_this = True;
438 }
439 }
440 if (!merge_this) {
441 XtDispatchEvent(target);
442 }
443 *target = next_event;
444 }
445 XtDispatchEvent(target);
446 return XtAppPending(app_con);
447 }
448
449 /*
450 * On entry, we have peeked at the event queue and see a configure-notify
451 * event. Remove that from the queue so we can look further.
452 *
453 * Then, as long as there is a configure-notify event in the queue, remove
454 * that. If the adjacent events are for different windows, process the older
455 * event and update the event used for comparing windows. If they are for the
456 * same window, only the newer event is of interest.
457 *
458 * Finally, process the (remaining) configure-notify event.
459 */
460 static XtInputMask
461 mergeConfigureEvents(XEvent *target)
462 {
463 XEvent next_event;
464 XConfigureEvent *p;
465
466 XtAppNextEvent(app_con, target);
467 p = (XConfigureEvent *) target;
468
469 if (XtAppPending(app_con)
470 && XtAppPeekEvent(app_con, &next_event)
471 && next_event.type == ConfigureNotify) {
472 Boolean merge_this = False;
473 XConfigureEvent *q = (XConfigureEvent *) (&next_event);
474
475 XtAppNextEvent(app_con, &next_event);
476 TRACE_EVENT("pending", &next_event, (String *) 0, 0);
477
478 if (p->window == q->window) {
479 TRACE(("pending Configure...merged\n"));
480 merge_this = True;
481 }
482 if (!merge_this) {
483 TRACE(("pending Configure...skipped\n"));
484 XtDispatchEvent(target);
485 }
486 *target = next_event;
487 }
488 XtDispatchEvent(target);
489 return XtAppPending(app_con);
490 }
491
492 #define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
493 #define SameButtonEvent(a,b) ( \
494 SAME(a,b,type) && \
495 SAME(a,b,serial) && \
496 SAME(a,b,send_event) && \
497 SAME(a,b,display) && \
498 SAME(a,b,window) && \
499 SAME(a,b,root) && \
500 SAME(a,b,subwindow) && \
501 SAME(a,b,time) && \
502 SAME(a,b,x) && \
503 SAME(a,b,y) && \
504 SAME(a,b,x_root) && \
505 SAME(a,b,y_root) && \
506 SAME(a,b,state) && \
507 SAME(a,b,button) && \
508 SAME(a,b,same_screen))
509
510 /*
511 * Work around a bug in the X mouse code, which delivers duplicate events.
512 */
513 static XtInputMask
514 mergeButtonEvents(XEvent *target)
515 {
516 XEvent next_event;
517 XButtonEvent *p;
518
519 XtAppNextEvent(app_con, target);
520 p = (XButtonEvent *) target;
521
522 if (XtAppPending(app_con)
523 && XtAppPeekEvent(app_con, &next_event)
524 && SameButtonEvent(target, &next_event)) {
525 Boolean merge_this = False;
526 XButtonEvent *q = (XButtonEvent *) (&next_event);
527
528 XtAppNextEvent(app_con, &next_event);
529 TRACE_EVENT("pending", &next_event, (String *) 0, 0);
530
531 if (p->window == q->window) {
532 TRACE(("pending ButtonEvent...merged\n"));
533 merge_this = True;
534 }
535 if (!merge_this) {
536 TRACE(("pending ButtonEvent...skipped\n"));
537 XtDispatchEvent(target);
538 }
539 *target = next_event;
540 }
541 XtDispatchEvent(target);
542 return XtAppPending(app_con);
543 }
544
545 /*
546 * Filter redundant Expose- and ConfigureNotify-events. This is limited to
547 * adjacent events because there could be other event-loop processing. Absent
548 * that limitation, it might be possible to scan ahead to find when the screen
549 * would be completely updated, skipping unnecessary re-repainting before that
550 * point.
551 *
552 * Note: all cases should allow doing XtAppNextEvent if result is true.
553 */
554 XtInputMask
555 xtermAppPending(void)
556 {
557 XtInputMask result = XtAppPending(app_con);
558 XEvent this_event;
559 Boolean found = False;
560
561 while (result && XtAppPeekEvent(app_con, &this_event)) {
562 found = True;
563 TRACE_EVENT("pending", &this_event, (String *) 0, 0);
564 if (this_event.type == Expose) {
565 result = mergeExposeEvents(&this_event);
566 } else if (this_event.type == ConfigureNotify) {
567 result = mergeConfigureEvents(&this_event);
568 } else if (this_event.type == ButtonPress ||
569 this_event.type == ButtonRelease) {
570 result = mergeButtonEvents(&this_event);
571 } else {
572 break;
573 }
574 }
575
576 /*
577 * With NetBSD, closing a shell results in closing the X input event
578 * stream, which interferes with the "-hold" option. Wait a short time in
579 * this case, to avoid max'ing the CPU.
580 */
581 if (hold_screen && caught_intr && !found) {
582 Sleep(EVENT_DELAY);
583 }
584 return result;
585 }
586
587 void
588 xevents(XtermWidget xw)
589 {
590 TScreen *screen = TScreenOf(xw);
591 XEvent event;
592 XtInputMask input_mask;
593
594 if (need_cleanup)
595 NormalExit();
596
597 if (screen->scroll_amt)
598 FlushScroll(xw);
599 /*
600 * process timeouts, relying on the fact that XtAppProcessEvent
601 * will process the timeout and return without blockng on the
602 * XEvent queue. Other sources i.e., the pty are handled elsewhere
603 * with select().
604 */
605 while ((input_mask = xtermAppPending()) != 0) {
606 if (input_mask & XtIMTimer)
607 XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
608 #if OPT_SESSION_MGT
609 /*
610 * Session management events are alternative input events. Deal with
611 * them in the same way.
612 */
613 else if (input_mask & XtIMAlternateInput)
614 XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
615 #endif
616 else
617 break;
618 }
619
620 /*
621 * If there are no XEvents, don't wait around...
622 */
623 if ((input_mask & XtIMXEvent) != XtIMXEvent)
624 return;
625 do {
626 /*
627 * This check makes xterm hang when in mouse hilite tracking mode.
628 * We simply ignore all events except for those not passed down to
629 * this function, e.g., those handled in in_put().
630 */
631 if (screen->waitingForTrackInfo) {
632 Sleep(EVENT_DELAY);
633 return;
634 }
635 XtAppNextEvent(app_con, &event);
636 /*
637 * Hack to get around problems with the toolkit throwing away
638 * eventing during the exclusive grab of the menu popup. By
639 * looking at the event ourselves we make sure that we can
640 * do the right thing.
641 */
642 if (OUR_EVENT(event, EnterNotify)) {
643 DoSpecialEnterNotify(xw, &event.xcrossing);
644 } else if (OUR_EVENT(event, LeaveNotify)) {
645 DoSpecialLeaveNotify(xw, &event.xcrossing);
646 } else if (event.xany.type == MotionNotify
647 && event.xcrossing.window == XtWindow(xw)) {
648 switch (screen->send_mouse_pos) {
649 case ANY_EVENT_MOUSE:
650 #if OPT_DEC_LOCATOR
651 case DEC_LOCATOR:
652 #endif /* OPT_DEC_LOCATOR */
653 SendMousePosition(xw, &event);
654 xtermShowPointer(xw, True);
655 continue;
656 case BTN_EVENT_MOUSE:
657 SendMousePosition(xw, &event);
658 xtermShowPointer(xw, True);
659 }
660 }
661
662 /*
663 * If the event is interesting (and not a keyboard event), turn the
664 * mouse pointer back on.
665 */
666 if (screen->hide_pointer) {
667 if (screen->pointer_mode >= pFocused) {
668 switch (event.xany.type) {
669 case MotionNotify:
670 xtermShowPointer(xw, True);
671 break;
672 }
673 } else {
674 switch (event.xany.type) {
675 case KeyPress:
676 case KeyRelease:
677 case ButtonPress:
678 case ButtonRelease:
679 /* also these... */
680 case Expose:
681 case GraphicsExpose:
682 case NoExpose:
683 case PropertyNotify:
684 case ClientMessage:
685 break;
686 default:
687 xtermShowPointer(xw, True);
688 break;
689 }
690 }
691 }
692
693 if (!event.xany.send_event ||
694 screen->allowSendEvents ||
695 ((event.xany.type != KeyPress) &&
696 (event.xany.type != KeyRelease) &&
697 (event.xany.type != ButtonPress) &&
698 (event.xany.type != ButtonRelease))) {
699
700 if (event.xany.type == MappingNotify) {
701 XRefreshKeyboardMapping(&(event.xmapping));
702 VTInitModifiers(xw);
703 }
704 XtDispatchEvent(&event);
705 }
706 } while (xtermAppPending() & XtIMXEvent);
707 }
708
709 static Cursor
710 make_hidden_cursor(XtermWidget xw)
711 {
712 TScreen *screen = TScreenOf(xw);
713 Cursor c;
714 Display *dpy = screen->display;
715 XFontStruct *fn;
716
717 static XColor dummy;
718
719 /*
720 * Prefer nil2 (which is normally available) to "fixed" (which is supposed
721 * to be "always" available), since it's a smaller glyph in case the
722 * server insists on drawing _something_.
723 */
724 TRACE(("Ask for nil2 font\n"));
725 if ((fn = xtermLoadQueryFont(xw, "nil2")) == 0) {
726 TRACE(("...Ask for fixed font\n"));
727 fn = xtermLoadQueryFont(xw, DEFFONT);
728 }
729
730 if (fn != None) {
731 /* a space character seems to work as a cursor (dots are not needed) */
732 c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
733 XFreeFont(dpy, fn);
734 } else {
735 c = None;
736 }
737 TRACE(("XCreateGlyphCursor ->%#lx\n", c));
738 return c;
739 }
740
741 /*
742 * Xlib uses Xcursor to customize cursor coloring, which interferes with
743 * xterm's pointerColor resource. Work around this by providing our own
744 * default theme. Testing seems to show that we only have to provide this
745 * until the window is initialized.
746 */
747 #ifdef HAVE_LIB_XCURSOR
748 void
749 init_colored_cursor(Display *dpy)
750 {
751 static const char theme[] = "index.theme";
752 static const char pattern[] = "xtermXXXXXXXX";
753 char *env = getenv("XCURSOR_THEME");
754
755 xterm_cursor_theme = 0;
756 /*
757 * The environment variable overrides a (possible) resource Xcursor.theme
758 */
759 if (IsEmpty(env)) {
760 env = XGetDefault(dpy, "Xcursor", "theme");
761 TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
762 } else {
763 TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
764 }
765
766 /*
767 * If neither found, provide our own default theme.
768 */
769 if (IsEmpty(env)) {
770 const char *tmp_dir;
771 char *filename;
772 size_t needed;
773
774 TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
775
776 if ((tmp_dir = getenv("TMPDIR")) == 0) {
777 tmp_dir = P_tmpdir;
778 }
779 needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
780 if ((filename = malloc(needed)) != 0) {
781 sprintf(filename, "%s/%s", tmp_dir, pattern);
782
783 #ifdef HAVE_MKDTEMP
784 xterm_cursor_theme = mkdtemp(filename);
785 #else
786 if (MakeTemp(filename) != 0
787 && mkdir(filename, 0700) == 0) {
788 xterm_cursor_theme = filename;
789 }
790 #endif
791 if (xterm_cursor_theme != filename)
792 free(filename);
793 /*
794 * Actually, Xcursor does what _we_ want just by steering its
795 * search path away from home. We are setting up the complete
796 * theme just in case the library ever acquires a maintainer.
797 */
798 if (xterm_cursor_theme != 0) {
799 char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
800 FILE *fp;
801
802 strcat(leaf, "/");
803 strcat(leaf, theme);
804
805 if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
806 fprintf(fp, "[Icon Theme]\n");
807 fclose(fp);
808 *leaf = '\0';
809 xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
810 *leaf = '/';
811
812 TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
813 xterm_cursor_theme));
814 atexit(cleanup_colored_cursor);
815 } else {
816 FreeAndNull(xterm_cursor_theme);
817 }
818 }
819 }
820 }
821 }
822 #endif /* HAVE_LIB_XCURSOR */
823
824 /*
825 * Once done, discard the file and directory holding it.
826 */
827 void
828 cleanup_colored_cursor(void)
829 {
830 #ifdef HAVE_LIB_XCURSOR
831 if (xterm_cursor_theme != 0) {
832 char *my_path = getenv("XCURSOR_PATH");
833 struct stat sb;
834 if (!IsEmpty(my_path)
835 && stat(my_path, &sb) == 0
836 && (sb.st_mode & S_IFMT) == S_IFDIR) {
837 unlink(xterm_cursor_theme);
838 rmdir(my_path);
839 }
840 FreeAndNull(xterm_cursor_theme);
841 }
842 #endif /* HAVE_LIB_XCURSOR */
843 }
844
845 Cursor
846 make_colored_cursor(unsigned c_index, /* index into font */
847 unsigned long fg, /* pixel value */
848 unsigned long bg) /* pixel value */
849 {
850 TScreen *screen = TScreenOf(term);
851 Cursor c = None;
852 Display *dpy = screen->display;
853
854 TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
855 if (!IsEmpty(screen->cursor_font_name)) {
856 static XTermFonts myFont;
857
858 /* adapted from XCreateFontCursor(), which hardcodes the font name */
859 TRACE(("loading cursor from alternate cursor font\n"));
860 myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
861 if (myFont.fs != NULL) {
862 if (!xtermMissingChar(c_index, &myFont)
863 && !xtermMissingChar(c_index + 1, &myFont)) {
864 #define DATA(c) { 0UL, c, c, c, 0, 0 }
865 static XColor foreground = DATA(0);
866 static XColor background = DATA(65535);
867 #undef DATA
868
869 /*
870 * Cursor fonts follow each shape glyph with a mask glyph; so
871 * that character position 0 contains a shape, 1 the mask for
872 * 0, 2 a shape, 3 a mask for 2, etc. <X11/cursorfont.h>
873 * contains defined names for each shape.
874 */
875 c = XCreateGlyphCursor(dpy,
876 myFont.fs->fid, /* source_font */
877 myFont.fs->fid, /* mask_font */
878 c_index + 0, /* source_char */
879 c_index + 1, /* mask_char */
880 &foreground,
881 &background);
882 }
883 XFreeFont(dpy, myFont.fs);
884 }
885 if (c == None) {
886 xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
887 c_index, screen->cursor_font_name);
888 }
889 }
890 if (c == None)
891 c = XCreateFontCursor(dpy, c_index);
892
893 if (c != None) {
894 recolor_cursor(screen, c, fg, bg);
895 }
896 return c;
897 }
898
899 /* adapted from <X11/cursorfont.h> */
900 static int
901 LookupCursorShape(const char *name)
902 {
903 #define DATA(name) { XC_##name, #name }
904 static struct {
905 int code;
906 const char name[25];
907 } table[] = {
908 DATA(X_cursor),
909 DATA(arrow),
910 DATA(based_arrow_down),
911 DATA(based_arrow_up),
912 DATA(boat),
913 DATA(bogosity),
914 DATA(bottom_left_corner),
915 DATA(bottom_right_corner),
916 DATA(bottom_side),
917 DATA(bottom_tee),
918 DATA(box_spiral),
919 DATA(center_ptr),
920 DATA(circle),
921 DATA(clock),
922 DATA(coffee_mug),
923 DATA(cross),
924 DATA(cross_reverse),
925 DATA(crosshair),
926 DATA(diamond_cross),
927 DATA(dot),
928 DATA(dotbox),
929 DATA(double_arrow),
930 DATA(draft_large),
931 DATA(draft_small),
932 DATA(draped_box),
933 DATA(exchange),
934 DATA(fleur),
935 DATA(gobbler),
936 DATA(gumby),
937 DATA(hand1),
938 DATA(hand2),
939 DATA(heart),
940 DATA(icon),
941 DATA(iron_cross),
942 DATA(left_ptr),
943 DATA(left_side),
944 DATA(left_tee),
945 DATA(leftbutton),
946 DATA(ll_angle),
947 DATA(lr_angle),
948 DATA(man),
949 DATA(middlebutton),
950 DATA(mouse),
951 DATA(pencil),
952 DATA(pirate),
953 DATA(plus),
954 DATA(question_arrow),
955 DATA(right_ptr),
956 DATA(right_side),
957 DATA(right_tee),
958 DATA(rightbutton),
959 DATA(rtl_logo),
960 DATA(sailboat),
961 DATA(sb_down_arrow),
962 DATA(sb_h_double_arrow),
963 DATA(sb_left_arrow),
964 DATA(sb_right_arrow),
965 DATA(sb_up_arrow),
966 DATA(sb_v_double_arrow),
967 DATA(shuttle),
968 DATA(sizing),
969 DATA(spider),
970 DATA(spraycan),
971 DATA(star),
972 DATA(target),
973 DATA(tcross),
974 DATA(top_left_arrow),
975 DATA(top_left_corner),
976 DATA(top_right_corner),
977 DATA(top_side),
978 DATA(top_tee),
979 DATA(trek),
980 DATA(ul_angle),
981 DATA(umbrella),
982 DATA(ur_angle),
983 DATA(watch),
984 DATA(xterm),
985 };
986 #undef DATA
987 Cardinal j;
988 int result = -1;
989 if (!IsEmpty(name)) {
990 for (j = 0; j < XtNumber(table); ++j) {
991 if (!strcmp(name, table[j].name)) {
992 result = table[j].code;
993 break;
994 }
995 }
996 }
997 return result;
998 }
999
1000 void
1001 xtermSetupPointer(XtermWidget xw, const char *theShape)
1002 {
1003 TScreen *screen = TScreenOf(xw);
1004 unsigned shape = XC_xterm;
1005 int other = LookupCursorShape(theShape);
1006 unsigned which;
1007
1008 if (other >= 0 && other < XC_num_glyphs)
1009 shape = (unsigned) other;
1010
1011 TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
1012 NonNull(theShape)));
1013
1014 which = (unsigned) (shape / 2);
1015 if (xw->work.pointer_cursors[which] == None) {
1016 TRACE(("creating text pointer cursor from shape %d\n", shape));
1017 xw->work.pointer_cursors[which] =
1018 make_colored_cursor(shape,
1019 T_COLOR(screen, MOUSE_FG),
1020 T_COLOR(screen, MOUSE_BG));
1021 } else {
1022 TRACE(("updating text pointer cursor for shape %d\n", shape));
1023 recolor_cursor(screen,
1024 screen->pointer_cursor,
1025 T_COLOR(screen, MOUSE_FG),
1026 T_COLOR(screen, MOUSE_BG));
1027 }
1028 if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
1029 screen->pointer_cursor = xw->work.pointer_cursors[which];
1030 TRACE(("defining text pointer cursor with shape %d\n", shape));
1031 XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
1032 if (XtIsRealized((Widget) xw)) {
1033 /* briefly override pointerMode after changing the pointer */
1034 if (screen->pointer_mode != pNever)
1035 screen->hide_pointer = True;
1036 xtermShowPointer(xw, True);
1037 }
1038 }
1039 }
1040
1041 /* ARGSUSED */
1042 void
1043 HandleKeyPressed(Widget w GCC_UNUSED,
1044 XEvent *event,
1045 String *params GCC_UNUSED,
1046 Cardinal *nparams GCC_UNUSED)
1047 {
1048 TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
1049 Input(term, &event->xkey, False);
1050 }
1051
1052 /* ARGSUSED */
1053 void
1054 HandleEightBitKeyPressed(Widget w GCC_UNUSED,
1055 XEvent *event,
1056 String *params GCC_UNUSED,
1057 Cardinal *nparams GCC_UNUSED)
1058 {
1059 TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
1060 Input(term, &event->xkey, True);
1061 }
1062
1063 /* ARGSUSED */
1064 void
1065 HandleStringEvent(Widget w GCC_UNUSED,
1066 XEvent *event GCC_UNUSED,
1067 String *params,
1068 Cardinal *nparams)
1069 {
1070
1071 if (*nparams != 1)
1072 return;
1073
1074 if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
1075 const char *abcdef = "ABCDEF";
1076 const char *xxxxxx;
1077 Char c;
1078 UString p;
1079 unsigned value = 0;
1080
1081 for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
1082 '\0'; p++) {
1083 value *= 16;
1084 if (c >= '0' && c <= '9')
1085 value += (unsigned) (c - '0');
1086 else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
1087 value += (unsigned) (xxxxxx - abcdef) + 10;
1088 else
1089 break;
1090 }
1091 if (c == '\0') {
1092 Char hexval[2];
1093 hexval[0] = (Char) value;
1094 hexval[1] = 0;
1095 StringInput(term, hexval, (size_t) 1);
1096 }
1097 } else {
1098 StringInput(term, (const Char *) *params, strlen(*params));
1099 }
1100 }
1101
1102 #if OPT_EXEC_XTERM
1103
1104 #ifndef PROCFS_ROOT
1105 #define PROCFS_ROOT "/proc"
1106 #endif
1107
1108 /*
1109 * Determine the current working directory of the child so that we can
1110 * spawn a new terminal in the same directory.
1111 *
1112 * If we cannot get the CWD of the child, just use our own.
1113 */
1114 char *
1115 ProcGetCWD(pid_t pid)
1116 {
1117 char *child_cwd = NULL;
1118
1119 if (pid) {
1120 char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
1121 sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
1122 child_cwd = Readlink(child_cwd_link);
1123 }
1124 return child_cwd;
1125 }
1126
1127 /* ARGSUSED */
1128 void
1129 HandleSpawnTerminal(Widget w GCC_UNUSED,
1130 XEvent *event GCC_UNUSED,
1131 String *params,
1132 Cardinal *nparams)
1133 {
1134 TScreen *screen = TScreenOf(term);
1135 char *child_cwd = NULL;
1136 char *child_exe;
1137 pid_t pid;
1138
1139 /*
1140 * Try to find the actual program which is running in the child process.
1141 * This works for Linux. If we cannot find the program, fall back to the
1142 * xterm program (which is usually adequate). Give up if we are given only
1143 * a relative path to xterm, since that would not always match $PATH.
1144 */
1145 child_exe = Readlink(PROCFS_ROOT "/self/exe");
1146 if (!child_exe) {
1147 if (strncmp(ProgramName, "./", (size_t) 2)
1148 && strncmp(ProgramName, "../", (size_t) 3)) {
1149 child_exe = xtermFindShell(ProgramName, True);
1150 } else {
1151 xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
1152 }
1153 if (child_exe == 0)
1154 return;
1155 }
1156
1157 child_cwd = ProcGetCWD(screen->pid);
1158
1159 /* The reaper will take care of cleaning up the child */
1160 pid = fork();
1161 if (pid == -1) {
1162 xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
1163 } else if (!pid) {
1164 /* We are the child */
1165 if (child_cwd) {
1166 IGNORE_RC(chdir(child_cwd)); /* We don't care if this fails */
1167 }
1168
1169 if (setuid(screen->uid) == -1
1170 || setgid(screen->gid) == -1) {
1171 xtermWarning("Cannot reset uid/gid\n");
1172 } else {
1173 unsigned myargc = *nparams + 1;
1174 char **myargv = TypeMallocN(char *, myargc + 1);
1175
1176 if (myargv != 0) {
1177 unsigned n = 0;
1178
1179 myargv[n++] = child_exe;
1180
1181 while (n < myargc) {
1182 myargv[n++] = (char *) *params++;
1183 }
1184
1185 myargv[n] = 0;
1186 execv(child_exe, myargv);
1187 }
1188
1189 /* If we get here, we've failed */
1190 xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
1191 }
1192 _exit(0);
1193 }
1194
1195 /* We are the parent; clean up */
1196 free(child_cwd);
1197 free(child_exe);
1198 }
1199 #endif /* OPT_EXEC_XTERM */
1200
1201 /*
1202 * Rather than sending characters to the host, put them directly into our
1203 * input queue. That lets a user have access to any of the control sequences
1204 * for a key binding. This is the equivalent of local function key support.
1205 *
1206 * NOTE: This code does not support the hexadecimal kludge used in
1207 * HandleStringEvent because it prevents us from sending an arbitrary string
1208 * (but it appears in a lot of examples - so we are stuck with it). The
1209 * standard string converter does recognize "\" for newline ("\n") and for
1210 * octal constants (e.g., "\007" for BEL). So we assume the user can make do
1211 * without a specialized converter. (Don't try to use \000, though).
1212 */
1213 /* ARGSUSED */
1214 void
1215 HandleInterpret(Widget w GCC_UNUSED,
1216 XEvent *event GCC_UNUSED,
1217 String *params,
1218 Cardinal *param_count)
1219 {
1220 if (*param_count == 1) {
1221 const char *value = params[0];
1222 size_t need = strlen(value);
1223 size_t used = (size_t) (VTbuffer->next - VTbuffer->buffer);
1224 size_t have = (size_t) (VTbuffer->last - VTbuffer->buffer);
1225
1226 if ((have - used) + need < (size_t) BUF_SIZE) {
1227
1228 fillPtyData(term, VTbuffer, value, strlen(value));
1229
1230 TRACE(("Interpret %s\n", value));
1231 VTbuffer->update++;
1232 }
1233 }
1234 }
1235
1236 /*ARGSUSED*/
1237 void
1238 HandleEnterWindow(Widget w GCC_UNUSED,
1239 XtPointer eventdata GCC_UNUSED,
1240 XEvent *event GCC_UNUSED,
1241 Boolean *cont GCC_UNUSED)
1242 {
1243 /* NOP since we handled it above */
1244 TRACE(("HandleEnterWindow ignored\n"));
1245 TRACE_FOCUS(w, event);
1246 }
1247
1248 /*ARGSUSED*/
1249 void
1250 HandleLeaveWindow(Widget w GCC_UNUSED,
1251 XtPointer eventdata GCC_UNUSED,
1252 XEvent *event GCC_UNUSED,
1253 Boolean *cont GCC_UNUSED)
1254 {
1255 /* NOP since we handled it above */
1256 TRACE(("HandleLeaveWindow ignored\n"));
1257 TRACE_FOCUS(w, event);
1258 }
1259
1260 /*ARGSUSED*/
1261 void
1262 HandleFocusChange(Widget w GCC_UNUSED,
1263 XtPointer eventdata GCC_UNUSED,
1264 XEvent *ev,
1265 Boolean *cont GCC_UNUSED)
1266 {
1267 XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1268 XtermWidget xw = term;
1269 TScreen *screen = TScreenOf(xw);
1270
1271 TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1272 visibleEventType(event->type),
1273 visibleNotifyMode(event->mode),
1274 visibleNotifyDetail(event->detail)));
1275 TRACE_FOCUS(xw, event);
1276
1277 if (screen->quiet_grab
1278 && (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1279 /* EMPTY */ ;
1280 } else if (event->type == FocusIn) {
1281 if (event->detail != NotifyPointer) {
1282 setXUrgency(xw, False);
1283 }
1284
1285 /*
1286 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1287 * one of our windows. Use this to reset a case where one xterm is
1288 * partly obscuring another, and X gets (us) confused about whether the
1289 * pointer was in the window. In particular, this can happen if the
1290 * user is resizing the obscuring window, causing some events to not be
1291 * delivered to the obscured window.
1292 */
1293 if (event->detail == NotifyNonlinear
1294 && (screen->select & INWINDOW) != 0) {
1295 unselectwindow(xw, INWINDOW);
1296 }
1297 selectwindow(xw,
1298 ((event->detail == NotifyPointer)
1299 ? INWINDOW
1300 : FOCUS));
1301 SendFocusButton(xw, event);
1302 } else {
1303 #if OPT_FOCUS_EVENT
1304 if (event->type == FocusOut) {
1305 SendFocusButton(xw, event);
1306 }
1307 #endif
1308 /*
1309 * XGrabKeyboard() will generate NotifyGrab event that we want to
1310 * ignore.
1311 */
1312 if (event->mode != NotifyGrab) {
1313 unselectwindow(xw,
1314 ((event->detail == NotifyPointer)
1315 ? INWINDOW
1316 : FOCUS));
1317 }
1318 if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1319 Bell(xw, XkbBI_Info, 100);
1320 ReverseVideo(xw);
1321 screen->grabbedKbd = False;
1322 update_securekbd();
1323 }
1324 }
1325 }
1326
1327 static long lastBellTime; /* in milliseconds */
1328
1329 #if defined(HAVE_XKB_BELL_EXT)
1330 static Atom
1331 AtomBell(XtermWidget xw, int which)
1332 {
1333 #define DATA(name) { XkbBI_##name, XkbBN_##name }
1334 static struct {
1335 int value;
1336 const char *name;
1337 } table[] = {
1338 DATA(Info),
1339 DATA(MarginBell),
1340 DATA(MinorError),
1341 DATA(TerminalBell)
1342 };
1343 #undef DATA
1344 Cardinal n;
1345 Atom result = None;
1346
1347 for (n = 0; n < XtNumber(table); ++n) {
1348 if (table[n].value == which) {
1349 result = XInternAtom(XtDisplay(xw), table[n].name, False);
1350 break;
1351 }
1352 }
1353 return result;
1354 }
1355 #endif
1356
1357 void
1358 xtermBell(XtermWidget xw, int which, int percent)
1359 {
1360 TScreen *screen = TScreenOf(xw);
1361 #if defined(HAVE_XKB_BELL_EXT)
1362 Atom tony = AtomBell(xw, which);
1363 #endif
1364
1365 switch (which) {
1366 case XkbBI_Info:
1367 case XkbBI_MinorError:
1368 case XkbBI_MajorError:
1369 case XkbBI_TerminalBell:
1370 switch (screen->warningVolume) {
1371 case bvOff:
1372 percent = -100;
1373 break;
1374 case bvLow:
1375 break;
1376 case bvHigh:
1377 percent = 100;
1378 break;
1379 }
1380 break;
1381 case XkbBI_MarginBell:
1382 switch (screen->marginVolume) {
1383 case bvOff:
1384 percent = -100;
1385 break;
1386 case bvLow:
1387 break;
1388 case bvHigh:
1389 percent = 100;
1390 break;
1391 }
1392 break;
1393 default:
1394 break;
1395 }
1396
1397 #if defined(HAVE_XKB_BELL_EXT)
1398 if (tony != None) {
1399 XkbBell(screen->display, VShellWindow(xw), percent, tony);
1400 } else
1401 #endif
1402 XBell(screen->display, percent);
1403 }
1404
1405 void
1406 Bell(XtermWidget xw, int which, int percent)
1407 {
1408 TScreen *screen = TScreenOf(xw);
1409 struct timeval curtime;
1410
1411 TRACE(("BELL %d %d%%\n", which, percent));
1412 if (!XtIsRealized((Widget) xw)) {
1413 return;
1414 }
1415
1416 setXUrgency(xw, True);
1417
1418 /* has enough time gone by that we are allowed to ring
1419 the bell again? */
1420 if (screen->bellSuppressTime) {
1421 long now_msecs;
1422
1423 if (screen->bellInProgress) {
1424 do_xevents(xw);
1425 if (screen->bellInProgress) { /* even after new events? */
1426 return;
1427 }
1428 }
1429 X_GETTIMEOFDAY(&curtime);
1430 now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1431 if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1432 now_msecs - lastBellTime < screen->bellSuppressTime) {
1433 return;
1434 }
1435 lastBellTime = now_msecs;
1436 }
1437
1438 if (screen->visualbell) {
1439 VisualBell();
1440 } else {
1441 xtermBell(xw, which, percent);
1442 }
1443
1444 if (screen->poponbell)
1445 XRaiseWindow(screen->display, VShellWindow(xw));
1446
1447 if (screen->bellSuppressTime) {
1448 /* now we change a property and wait for the notify event to come
1449 back. If the server is suspending operations while the bell
1450 is being emitted (problematic for audio bell), this lets us
1451 know when the previous bell has finished */
1452 Widget w = CURRENT_EMU();
1453 XChangeProperty(XtDisplay(w), XtWindow(w),
1454 XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1455 screen->bellInProgress = True;
1456 }
1457 }
1458
1459 static void
1460 flashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1461 {
1462 int y = 0;
1463 int x = 0;
1464
1465 if (screen->flash_line) {
1466 y = CursorY(screen, screen->cur_row);
1467 height = (unsigned) FontHeight(screen);
1468 }
1469 XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1470 XFlush(screen->display);
1471 Sleep(VB_DELAY);
1472 XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1473 }
1474
1475 void
1476 VisualBell(void)
1477 {
1478 XtermWidget xw = term;
1479 TScreen *screen = TScreenOf(xw);
1480
1481 if (VB_DELAY > 0) {
1482 Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1483 T_COLOR(screen, TEXT_BG));
1484 XGCValues gcval;
1485 GC visualGC;
1486
1487 gcval.function = GXxor;
1488 gcval.foreground = xorPixel;
1489 visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
1490 #if OPT_TEK4014
1491 if (TEK4014_ACTIVE(xw)) {
1492 TekScreen *tekscr = TekScreenOf(tekWidget);
1493 flashWindow(screen, TWindow(tekscr), visualGC,
1494 TFullWidth(tekscr),
1495 TFullHeight(tekscr));
1496 } else
1497 #endif
1498 {
1499 flashWindow(screen, VWindow(screen), visualGC,
1500 FullWidth(screen),
1501 FullHeight(screen));
1502 }
1503 XtReleaseGC((Widget) xw, visualGC);
1504 }
1505 }
1506
1507 /* ARGSUSED */
1508 void
1509 HandleBellPropertyChange(Widget w GCC_UNUSED,
1510 XtPointer data GCC_UNUSED,
1511 XEvent *ev,
1512 Boolean *more GCC_UNUSED)
1513 {
1514 TScreen *screen = TScreenOf(term);
1515
1516 if (ev->xproperty.atom == XA_NOTICE) {
1517 screen->bellInProgress = False;
1518 }
1519 }
1520
1521 void
1522 xtermWarning(const char *fmt, ...)
1523 {
1524 int save_err = errno;
1525 va_list ap;
1526
1527 fflush(stdout);
1528
1529 #if OPT_TRACE
1530 va_start(ap, fmt);
1531 Trace("xtermWarning: ");
1532 TraceVA(fmt, ap);
1533 va_end(ap);
1534 #endif
1535
1536 fprintf(stderr, "%s: ", ProgramName);
1537 va_start(ap, fmt);
1538 vfprintf(stderr, fmt, ap);
1539 (void) fflush(stderr);
1540
1541 va_end(ap);
1542 errno = save_err;
1543 }
1544
1545 void
1546 xtermPerror(const char *fmt, ...)
1547 {
1548 int save_err = errno;
1549 const char *msg = strerror(errno);
1550 va_list ap;
1551
1552 fflush(stdout);
1553
1554 #if OPT_TRACE
1555 va_start(ap, fmt);
1556 Trace("xtermPerror: ");
1557 TraceVA(fmt, ap);
1558 va_end(ap);
1559 #endif
1560
1561 fprintf(stderr, "%s: ", ProgramName);
1562 va_start(ap, fmt);
1563 vfprintf(stderr, fmt, ap);
1564 fprintf(stderr, ": %s\n", msg);
1565 (void) fflush(stderr);
1566
1567 va_end(ap);
1568 errno = save_err;
1569 }
1570
1571 Window
1572 WMFrameWindow(XtermWidget xw)
1573 {
1574 Window win_root, win_current, *children;
1575 Window win_parent = 0;
1576 unsigned int nchildren;
1577
1578 win_current = XtWindow(xw);
1579
1580 /* find the parent which is child of root */
1581 do {
1582 if (win_parent)
1583 win_current = win_parent;
1584 XQueryTree(TScreenOf(xw)->display,
1585 win_current,
1586 &win_root,
1587 &win_parent,
1588 &children,
1589 &nchildren);
1590 XFree(children);
1591 } while (win_root != win_parent);
1592
1593 return win_current;
1594 }
1595
1596 #if OPT_DABBREV
1597 /*
1598 * The following code implements `dynamic abbreviation' expansion a la
1599 * Emacs. It looks in the preceding visible screen and its scrollback
1600 * to find expansions of a typed word. It compares consecutive
1601 * expansions and ignores one of them if they are identical.
1602 * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1603 */
1604
1605 #define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1606
1607 static int
1608 dabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1609 {
1610 int result = -1;
1611 int firstLine = -(screen->savedlines);
1612
1613 *ld = getLineData(screen, cell->row);
1614 while (cell->row >= firstLine) {
1615 if (--(cell->col) >= 0) {
1616 result = (int) (*ld)->charData[cell->col];
1617 break;
1618 }
1619 if (--(cell->row) < firstLine)
1620 break; /* ...there is no previous line */
1621 *ld = getLineData(screen, cell->row);
1622 cell->col = MaxCols(screen);
1623 if (!LineTstWrapped(*ld)) {
1624 result = ' '; /* treat lines as separate */
1625 break;
1626 }
1627 }
1628 return result;
1629 }
1630
1631 static char *
1632 dabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1633 {
1634 TScreen *screen = TScreenOf(xw);
1635 char *abword;
1636 int c;
1637 char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1638 char *result = 0;
1639
1640 abword = ab_end;
1641 *abword = '\0'; /* end of string marker */
1642
1643 while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1644 IS_WORD_CONSTITUENT(c)) {
1645 if (abword > xw->work.dabbrev_data) /* store only the last chars */
1646 *(--abword) = (char) c;
1647 }
1648
1649 if (c >= 0) {
1650 result = abword;
1651 } else if (abword != ab_end) {
1652 result = abword;
1653 }
1654
1655 if (result != 0) {
1656 while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1657 !IS_WORD_CONSTITUENT(c)) {
1658 ; /* skip preceding spaces */
1659 }
1660 (cell->col)++; /* can be | > screen->max_col| */
1661 }
1662 return result;
1663 }
1664
1665 static int
1666 dabbrev_expand(XtermWidget xw)
1667 {
1668 TScreen *screen = TScreenOf(xw);
1669 int pty = screen->respond; /* file descriptor of pty */
1670
1671 static CELL cell;
1672 static char *dabbrev_hint = 0, *lastexpansion = 0;
1673 static unsigned int expansions;
1674
1675 char *expansion;
1676 size_t hint_len;
1677 int result = 0;
1678 LineData *ld;
1679
1680 if (!screen->dabbrev_working) { /* initialize */
1681 expansions = 0;
1682 cell.col = screen->cur_col;
1683 cell.row = screen->cur_row;
1684
1685 free(dabbrev_hint);
1686
1687 if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1688
1689 free(lastexpansion);
1690
1691 if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1692
1693 /* make own copy */
1694 if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1695 screen->dabbrev_working = True;
1696 /* we are in the middle of dabbrev process */
1697 }
1698 } else {
1699 return result;
1700 }
1701 } else {
1702 return result;
1703 }
1704 if (!screen->dabbrev_working) {
1705 free(lastexpansion);
1706 lastexpansion = 0;
1707 return result;
1708 }
1709 }
1710
1711 if (dabbrev_hint == 0)
1712 return result;
1713
1714 hint_len = strlen(dabbrev_hint);
1715 for (;;) {
1716 if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1717 if (expansions >= 2) {
1718 expansions = 0;
1719 cell.col = screen->cur_col;
1720 cell.row = screen->cur_row;
1721 continue;
1722 }
1723 break;
1724 }
1725 if (!strncmp(dabbrev_hint, expansion, hint_len) && /* empty hint matches everything */
1726 strlen(expansion) > hint_len && /* trivial expansion disallowed */
1727 strcmp(expansion, lastexpansion)) /* different from previous */
1728 break;
1729 }
1730
1731 if (expansion != 0) {
1732 Char *copybuffer;
1733 size_t del_cnt = strlen(lastexpansion) - hint_len;
1734 size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1735
1736 if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1737 /* delete previous expansion */
1738 memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1739 memmove(copybuffer + del_cnt,
1740 expansion + hint_len,
1741 strlen(expansion) - hint_len);
1742 v_write(pty, copybuffer, buf_cnt);
1743 /* v_write() just reset our flag */
1744 screen->dabbrev_working = True;
1745 free(copybuffer);
1746
1747 free(lastexpansion);
1748
1749 if ((lastexpansion = strdup(expansion)) != 0) {
1750 result = 1;
1751 expansions++;
1752 }
1753 }
1754 }
1755
1756 return result;
1757 }
1758
1759 /*ARGSUSED*/
1760 void
1761 HandleDabbrevExpand(Widget w,
1762 XEvent *event GCC_UNUSED,
1763 String *params GCC_UNUSED,
1764 Cardinal *nparams GCC_UNUSED)
1765 {
1766 XtermWidget xw;
1767
1768 TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1769 if ((xw = getXtermWidget(w)) != 0) {
1770 if (!dabbrev_expand(xw))
1771 Bell(xw, XkbBI_TerminalBell, 0);
1772 }
1773 }
1774 #endif /* OPT_DABBREV */
1775
1776 void
1777 xtermDeiconify(XtermWidget xw)
1778 {
1779 TScreen *screen = TScreenOf(xw);
1780 Display *dpy = screen->display;
1781 Window target = VShellWindow(xw);
1782 XEvent e;
1783 Atom atom_state = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1784
1785 if (xtermIsIconified(xw)) {
1786 TRACE(("...de-iconify window %#lx\n", target));
1787 XMapWindow(dpy, target);
1788
1789 memset(&e, 0, sizeof(e));
1790 e.xclient.type = ClientMessage;
1791 e.xclient.message_type = atom_state;
1792 e.xclient.display = dpy;
1793 e.xclient.window = target;
1794 e.xclient.format = 32;
1795 e.xclient.data.l[0] = 1;
1796 e.xclient.data.l[1] = CurrentTime;
1797
1798 XSendEvent(dpy, DefaultRootWindow(dpy), False,
1799 SubstructureRedirectMask | SubstructureNotifyMask, &e);
1800 xevents(xw);
1801 }
1802 }
1803
1804 void
1805 xtermIconify(XtermWidget xw)
1806 {
1807 TScreen *screen = TScreenOf(xw);
1808 Window target = VShellWindow(xw);
1809
1810 if (!xtermIsIconified(xw)) {
1811 TRACE(("...iconify window %#lx\n", target));
1812 XIconifyWindow(screen->display,
1813 target,
1814 DefaultScreen(screen->display));
1815 xevents(xw);
1816 }
1817 }
1818
1819 Boolean
1820 xtermIsIconified(XtermWidget xw)
1821 {
1822 XWindowAttributes win_attrs;
1823 TScreen *screen = TScreenOf(xw);
1824 Window target = VShellWindow(xw);
1825 Display *dpy = screen->display;
1826 Boolean result = False;
1827
1828 if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
1829 Atom actual_return_type;
1830 int actual_format_return = 0;
1831 unsigned long nitems_return = 0;
1832 unsigned long bytes_after_return = 0;
1833 unsigned char *prop_return = 0;
1834 long long_length = 1024;
1835 Atom requested_type = XA_ATOM;
1836 Atom is_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
1837 Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
1838
1839 /* this works with non-EWMH */
1840 result = (win_attrs.map_state != IsViewable) ? True : False;
1841
1842 /* this is a convention used by some EWMH applications */
1843 if (xtermGetWinProp(dpy,
1844 target,
1845 wm_state,
1846 0L,
1847 long_length,
1848 requested_type,
1849 &actual_return_type,
1850 &actual_format_return,
1851 &nitems_return,
1852 &bytes_after_return,
1853 &prop_return)) {
1854 if (prop_return != 0
1855 && actual_return_type == requested_type
1856 && actual_format_return == 32) {
1857 unsigned long n;
1858 for (n = 0; n < nitems_return; ++n) {
1859 unsigned long check = (((unsigned long *)
1860 (void *) prop_return)[n]);
1861 if (check == is_hidden) {
1862 result = True;
1863 break;
1864 }
1865 }
1866 XFree(prop_return);
1867 }
1868 }
1869 }
1870 TRACE(("...window %#lx is%s iconified\n",
1871 target,
1872 result ? "" : " not"));
1873 return result;
1874 }
1875
1876 #if OPT_MAXIMIZE
1877 /*ARGSUSED*/
1878 void
1879 HandleDeIconify(Widget w,
1880 XEvent *event GCC_UNUSED,
1881 String *params GCC_UNUSED,
1882 Cardinal *nparams GCC_UNUSED)
1883 {
1884 XtermWidget xw;
1885
1886 if ((xw = getXtermWidget(w)) != 0) {
1887 xtermDeiconify(xw);
1888 }
1889 }
1890
1891 /*ARGSUSED*/
1892 void
1893 HandleIconify(Widget w,
1894 XEvent *event GCC_UNUSED,
1895 String *params GCC_UNUSED,
1896 Cardinal *nparams GCC_UNUSED)
1897 {
1898 XtermWidget xw;
1899
1900 if ((xw = getXtermWidget(w)) != 0) {
1901 xtermIconify(xw);
1902 }
1903 }
1904
1905 int
1906 QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1907 {
1908 TScreen *screen = TScreenOf(xw);
1909 XSizeHints hints;
1910 long supp = 0;
1911 Window root_win;
1912 int root_x = -1; /* saved co-ordinates */
1913 int root_y = -1;
1914 unsigned root_border;
1915 unsigned root_depth;
1916 int code;
1917
1918 if (XGetGeometry(screen->display,
1919 RootWindowOfScreen(XtScreen(xw)),
1920 &root_win,
1921 &root_x,
1922 &root_y,
1923 width,
1924 height,
1925 &root_border,
1926 &root_depth)) {
1927 TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1928 root_x,
1929 root_y,
1930 *width,
1931 *height,
1932 root_border));
1933
1934 *width -= (root_border * 2);
1935 *height -= (root_border * 2);
1936
1937 hints.flags = PMaxSize;
1938 if (XGetWMNormalHints(screen->display,
1939 VShellWindow(xw),
1940 &hints,
1941 &supp)
1942 && (hints.flags & PMaxSize) != 0) {
1943
1944 TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1945 hints.max_width,
1946 hints.max_height));
1947
1948 if ((unsigned) hints.max_width < *width)
1949 *width = (unsigned) hints.max_width;
1950 if ((unsigned) hints.max_height < *height)
1951 *height = (unsigned) hints.max_height;
1952 }
1953 code = 1;
1954 } else {
1955 *width = 0;
1956 *height = 0;
1957 code = 0;
1958 }
1959 return code;
1960 }
1961
1962 void
1963 RequestMaximize(XtermWidget xw, int maximize)
1964 {
1965 TScreen *screen = TScreenOf(xw);
1966 XWindowAttributes wm_attrs, vshell_attrs;
1967 unsigned root_width = 0, root_height = 0;
1968 Boolean success = False;
1969
1970 TRACE(("RequestMaximize %d:%s\n",
1971 maximize,
1972 (maximize
1973 ? "maximize"
1974 : "restore")));
1975
1976 /*
1977 * Before any maximize, ensure that we can capture the current screensize
1978 * as well as the estimated root-window size.
1979 */
1980 if (maximize
1981 && QueryMaximize(xw, &root_width, &root_height)
1982 && xtermGetWinAttrs(screen->display,
1983 WMFrameWindow(xw),
1984 &wm_attrs)
1985 && xtermGetWinAttrs(screen->display,
1986 VShellWindow(xw),
1987 &vshell_attrs)) {
1988
1989 if (screen->restore_data != True
1990 || screen->restore_width != root_width
1991 || screen->restore_height != root_height) {
1992 screen->restore_data = True;
1993 screen->restore_x = wm_attrs.x;
1994 screen->restore_y = wm_attrs.y;
1995 screen->restore_width = (unsigned) vshell_attrs.width;
1996 screen->restore_height = (unsigned) vshell_attrs.height;
1997 TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1998 screen->restore_x,
1999 screen->restore_y,
2000 screen->restore_width,
2001 screen->restore_height));
2002 }
2003
2004 /* subtract wm decoration dimensions */
2005 root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
2006 root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
2007 success = True;
2008 } else if (screen->restore_data) {
2009 success = True;
2010 maximize = 0;
2011 }
2012
2013 if (success) {
2014 switch (maximize) {
2015 case 3:
2016 FullScreen(xw, 3); /* depends on EWMH */
2017 break;
2018 case 2:
2019 FullScreen(xw, 2); /* depends on EWMH */
2020 break;
2021 case 1:
2022 FullScreen(xw, 0); /* overrides any EWMH hint */
2023 TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
2024 0,
2025 0,
2026 root_width,
2027 root_height));
2028 XMoveResizeWindow(screen->display, VShellWindow(xw),
2029 0, /* x */
2030 0, /* y */
2031 root_width,
2032 root_height);
2033 break;
2034
2035 default:
2036 FullScreen(xw, 0); /* reset any EWMH hint */
2037 if (screen->restore_data) {
2038 screen->restore_data = False;
2039
2040 TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
2041 screen->restore_x,
2042 screen->restore_y,
2043 screen->restore_width,
2044 screen->restore_height));
2045
2046 XMoveResizeWindow(screen->display,
2047 VShellWindow(xw),
2048 screen->restore_x,
2049 screen->restore_y,
2050 screen->restore_width,
2051 screen->restore_height);
2052 }
2053 break;
2054 }
2055 }
2056 }
2057
2058 /*ARGSUSED*/
2059 void
2060 HandleMaximize(Widget w,
2061 XEvent *event GCC_UNUSED,
2062 String *params GCC_UNUSED,
2063 Cardinal *nparams GCC_UNUSED)
2064 {
2065 XtermWidget xw;
2066
2067 if ((xw = getXtermWidget(w)) != 0) {
2068 RequestMaximize(xw, 1);
2069 }
2070 }
2071
2072 /*ARGSUSED*/
2073 void
2074 HandleRestoreSize(Widget w,
2075 XEvent *event GCC_UNUSED,
2076 String *params GCC_UNUSED,
2077 Cardinal *nparams GCC_UNUSED)
2078 {
2079 XtermWidget xw;
2080
2081 if ((xw = getXtermWidget(w)) != 0) {
2082 RequestMaximize(xw, 0);
2083 }
2084 }
2085 #endif /* OPT_MAXIMIZE */
2086
2087 void
2088 Redraw(void)
2089 {
2090 XtermWidget xw = term;
2091 TScreen *screen = TScreenOf(xw);
2092 XExposeEvent event;
2093
2094 TRACE(("Redraw\n"));
2095
2096 event.type = Expose;
2097 event.display = screen->display;
2098 event.x = 0;
2099 event.y = 0;
2100 event.count = 0;
2101
2102 if (VWindow(screen)) {
2103 event.window = VWindow(screen);
2104 event.width = xw->core.width;
2105 event.height = xw->core.height;
2106 (*xw->core.widget_class->core_class.expose) ((Widget) xw,
2107 (XEvent *) &event,
2108 NULL);
2109 if (ScrollbarWidth(screen)) {
2110 (screen->scrollWidget->core.widget_class->core_class.expose)
2111 (screen->scrollWidget, (XEvent *) &event, NULL);
2112 }
2113 }
2114 #if OPT_TEK4014
2115 if (TEK4014_SHOWN(xw)) {
2116 TekScreen *tekscr = TekScreenOf(tekWidget);
2117 event.window = TWindow(tekscr);
2118 event.width = tekWidget->core.width;
2119 event.height = tekWidget->core.height;
2120 TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
2121 }
2122 #endif
2123 }
2124
2125 #ifdef VMS
2126 #define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
2127 #else
2128 #define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
2129 #endif
2130
2131 void
2132 timestamp_filename(char *dst, const char *src)
2133 {
2134 time_t tstamp;
2135 struct tm *tstruct;
2136
2137 tstamp = time((time_t *) 0);
2138 tstruct = localtime(&tstamp);
2139 sprintf(dst, TIMESTAMP_FMT,
2140 src,
2141 (int) tstruct->tm_year + 1900,
2142 tstruct->tm_mon + 1,
2143 tstruct->tm_mday,
2144 tstruct->tm_hour,
2145 tstruct->tm_min,
2146 tstruct->tm_sec);
2147 }
2148
2149 #if OPT_SCREEN_DUMPS
2150 FILE *
2151 create_printfile(XtermWidget xw, const char *suffix)
2152 {
2153 TScreen *screen = TScreenOf(xw);
2154 char fname[1024];
2155 int fd;
2156 FILE *fp;
2157
2158 #ifdef VMS
2159 sprintf(fname, "sys$scratch:xterm%s", suffix);
2160 #elif defined(HAVE_STRFTIME)
2161 {
2162 char format[1024];
2163 time_t now;
2164 struct tm *ltm;
2165
2166 now = time((time_t *) 0);
2167 ltm = localtime(&now);
2168
2169 sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2170 if (strftime(fname, sizeof fname, format, ltm) == 0) {
2171 sprintf(fname, "xterm%s", suffix);
2172 }
2173 }
2174 #else
2175 sprintf(fname, "xterm%s", suffix);
2176 #endif
2177 fd = open_userfile(screen->uid, screen->gid, fname, False);
2178 fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2179 return fp;
2180 }
2181 #endif /* OPT_SCREEN_DUMPS */
2182
2183 #if OPT_SCREEN_DUMPS || defined(ALLOWLOGGING)
2184 int
2185 open_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2186 {
2187 int fd;
2188 struct stat sb;
2189
2190 #ifdef VMS
2191 if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
2192 int the_error = errno;
2193 xtermWarning("cannot open %s: %d:%s\n",
2194 path,
2195 the_error,
2196 SysErrorMsg(the_error));
2197 return -1;
2198 }
2199 chown(path, uid, gid);
2200 #else
2201 if ((access(path, F_OK) != 0 && (errno != ENOENT))
2202 || (creat_as(uid, gid, append, path, 0644) <= 0)
2203 || ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2204 int the_error = errno;
2205 xtermWarning("cannot open %s: %d:%s\n",
2206 path,
2207 the_error,
2208 SysErrorMsg(the_error));
2209 return -1;
2210 }
2211 #endif
2212
2213 /*
2214 * Doublecheck that the user really owns the file that we've opened before
2215 * we do any damage, and that it is not world-writable.
2216 */
2217 if (fstat(fd, &sb) < 0
2218 || sb.st_uid != uid
2219 || (sb.st_mode & 022) != 0) {
2220 xtermWarning("you do not own %s\n", path);
2221 close(fd);
2222 return -1;
2223 }
2224 return fd;
2225 }
2226
2227 #ifndef VMS
2228 /*
2229 * Create a file only if we could with the permissions of the real user id.
2230 * We could emulate this with careful use of access() and following
2231 * symbolic links, but that is messy and has race conditions.
2232 * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2233 * being available.
2234 *
2235 * Note: When called for user logging, we have ensured that the real and
2236 * effective user ids are the same, so this remains as a convenience function
2237 * for the debug logs.
2238 *
2239 * Returns
2240 * 1 if we can proceed to open the file in relative safety,
2241 * -1 on error, e.g., cannot fork
2242 * 0 otherwise.
2243 */
2244 int
2245 creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2246 {
2247 int fd;
2248 pid_t pid;
2249 int retval = 0;
2250 int childstat = 0;
2251 #ifndef HAVE_WAITPID
2252 int waited;
2253 void (*chldfunc) (int);
2254
2255 chldfunc = signal(SIGCHLD, SIG_DFL);
2256 #endif /* HAVE_WAITPID */
2257
2258 TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2259 (int) uid, (int) geteuid(),
2260 (int) gid, (int) getegid(),
2261 append,
2262 pathname,
2263 mode));
2264
2265 if (uid == geteuid() && gid == getegid()) {
2266 fd = open(pathname,
2267 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2268 mode);
2269 if (fd >= 0)
2270 close(fd);
2271 return (fd >= 0);
2272 }
2273
2274 pid = fork();
2275 switch (pid) {
2276 case 0: /* child */
2277 if (setgid(gid) == -1
2278 || setuid(uid) == -1) {
2279 /* we cannot report an error here via stderr, just quit */
2280 retval = 1;
2281 } else {
2282 fd = open(pathname,
2283 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2284 mode);
2285 if (fd >= 0) {
2286 close(fd);
2287 retval = 0;
2288 } else {
2289 retval = 1;
2290 }
2291 }
2292 _exit(retval);
2293 /* NOTREACHED */
2294 case -1: /* error */
2295 return retval;
2296 default: /* parent */
2297 #ifdef HAVE_WAITPID
2298 while (waitpid(pid, &childstat, 0) < 0) {
2299 #ifdef EINTR
2300 if (errno == EINTR)
2301 continue;
2302 #endif /* EINTR */
2303 #ifdef ERESTARTSYS
2304 if (errno == ERESTARTSYS)
2305 continue;
2306 #endif /* ERESTARTSYS */
2307 break;
2308 }
2309 #else /* HAVE_WAITPID */
2310 waited = wait(&childstat);
2311 signal(SIGCHLD, chldfunc);
2312 /*
2313 Since we had the signal handler uninstalled for a while,
2314 we might have missed the termination of our screen child.
2315 If we can check for this possibility without hanging, do so.
2316 */
2317 do
2318 if (waited == TScreenOf(term)->pid)
2319 NormalExit();
2320 while ((waited = nonblocking_wait()) > 0) ;
2321 #endif /* HAVE_WAITPID */
2322 #ifndef WIFEXITED
2323 #define WIFEXITED(status) ((status & 0xff) != 0)
2324 #endif
2325 if (WIFEXITED(childstat))
2326 retval = 1;
2327 return retval;
2328 }
2329 }
2330 #endif /* !VMS */
2331 #endif /* OPT_SCREEN_DUMPS || defined(ALLOWLOGGING) */
2332
2333 int
2334 xtermResetIds(TScreen *screen)
2335 {
2336 int result = 0;
2337 if (setgid(screen->gid) == -1) {
2338 xtermWarning("unable to reset group-id\n");
2339 result = -1;
2340 }
2341 if (setuid(screen->uid) == -1) {
2342 xtermWarning("unable to reset user-id\n");
2343 result = -1;
2344 }
2345 return result;
2346 }
2347
2348 #ifdef ALLOWLOGGING
2349
2350 /*
2351 * Logging is a security hole, since it allows a setuid program to write
2352 * arbitrary data to an arbitrary file. So it is disabled by default.
2353 */
2354
2355 #ifdef ALLOWLOGFILEEXEC
2356 static void
2357 handle_SIGPIPE(int sig GCC_UNUSED)
2358 {
2359 XtermWidget xw = term;
2360 TScreen *screen = TScreenOf(xw);
2361
2362 DEBUG_MSG("handle:logpipe\n");
2363 #ifdef SYSV
2364 (void) signal(SIGPIPE, SIG_IGN);
2365 #endif /* SYSV */
2366 if (screen->logging)
2367 CloseLog(xw);
2368 }
2369
2370 /*
2371 * Open a command to pipe log data to it.
2372 * Warning, enabling this "feature" allows arbitrary programs
2373 * to be run. If ALLOWLOGFILECHANGES is enabled, this can be
2374 * done through escape sequences.... You have been warned.
2375 */
2376 static void
2377 StartLogExec(TScreen *screen)
2378 {
2379 int pid;
2380 int p[2];
2381 static char *shell;
2382 struct passwd pw;
2383
2384 if ((shell = x_getenv("SHELL")) == NULL) {
2385
2386 if (x_getpwuid(screen->uid, &pw)) {
2387 char *name = x_getlogin(screen->uid, &pw);
2388 if (*(pw.pw_shell)) {
2389 shell = pw.pw_shell;
2390 }
2391 free(name);
2392 }
2393 }
2394
2395 if (shell == 0) {
2396 static char dummy[] = "/bin/sh";
2397 shell = dummy;
2398 }
2399
2400 if (access(shell, X_OK) != 0) {
2401 xtermPerror("Can't execute `%s'\n", shell);
2402 return;
2403 }
2404
2405 if (pipe(p) < 0) {
2406 xtermPerror("Can't make a pipe connection\n");
2407 return;
2408 } else if ((pid = fork()) < 0) {
2409 xtermPerror("Can't fork...\n");
2410 return;
2411 }
2412 if (pid == 0) { /* child */
2413 /*
2414 * Close our output (we won't be talking back to the
2415 * parent), and redirect our child's output to the
2416 * original stderr.
2417 */
2418 close(p[1]);
2419 dup2(p[0], 0);
2420 close(p[0]);
2421 dup2(fileno(stderr), 1);
2422 dup2(fileno(stderr), 2);
2423
2424 close(fileno(stderr));
2425 close(ConnectionNumber(screen->display));
2426 close(screen->respond);
2427
2428 signal(SIGHUP, SIG_DFL);
2429 signal(SIGCHLD, SIG_DFL);
2430
2431 /* (this is redundant) */
2432 if (xtermResetIds(screen) < 0)
2433 exit(ERROR_SETUID);
2434
2435 execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2436 xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2437 exit(ERROR_LOGEXEC);
2438 }
2439 close(p[0]);
2440 screen->logfd = p[1];
2441 signal(SIGPIPE, handle_SIGPIPE);
2442 }
2443 #endif /* ALLOWLOGFILEEXEC */
2444
2445 /*
2446 * Generate a path for a logfile if no default path is given.
2447 */
2448 static char *
2449 GenerateLogPath(void)
2450 {
2451 static char *log_default = NULL;
2452
2453 /* once opened we just reuse the same log name */
2454 if (log_default)
2455 return (log_default);
2456
2457 #if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2458 {
2459 #define LEN_HOSTNAME 255
2460 /* Internet standard limit (RFC 1035): ``To simplify implementations,
2461 * the total length of a domain name (i.e., label octets and label
2462 * length octets) is restricted to 255 octets or less.''
2463 */
2464 #define LEN_GETPID 9
2465 /*
2466 * This is arbitrary...
2467 */
2468 const char form[] = "Xterm.log.%s%s.%lu";
2469 char where[LEN_HOSTNAME + 1];
2470 char when[LEN_TIMESTAMP];
2471 time_t now = time((time_t *) 0);
2472 struct tm *ltm = (struct tm *) localtime(&now);
2473
2474 if ((gethostname(where, sizeof(where)) == 0) &&
2475 (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2476 ((log_default = (char *) malloc((sizeof(form)
2477 + strlen(where)
2478 + strlen(when)
2479 + LEN_GETPID))) != NULL)) {
2480 (void) sprintf(log_default,
2481 form,
2482 where, when,
2483 ((unsigned long) getpid()) % ((unsigned long) 1e10));
2484 }
2485 }
2486 #else
2487 static const char log_def_name[] = "XtermLog.XXXXXX";
2488 if ((log_default = x_strdup(log_def_name)) != NULL) {
2489 MakeTemp(log_default);
2490 }
2491 #endif
2492
2493 return (log_default);
2494 }
2495
2496 void
2497 StartLog(XtermWidget xw)
2498 {
2499 TScreen *screen = TScreenOf(xw);
2500
2501 if (screen->logging || (screen->inhibit & I_LOG))
2502 return;
2503 #ifdef VMS /* file name is fixed in VMS variant */
2504 screen->logfd = open(XTERM_VMS_LOGFILE,
2505 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2506 0640);
2507 if (screen->logfd < 0)
2508 return; /* open failed */
2509 #else /*VMS */
2510
2511 /* if we weren't supplied with a logfile path, generate one */
2512 if (IsEmpty(screen->logfile))
2513 screen->logfile = GenerateLogPath();
2514
2515 /* give up if we were unable to allocate the filename */
2516 if (!screen->logfile)
2517 return;
2518
2519 if (*screen->logfile == '|') { /* exec command */
2520 #ifdef ALLOWLOGFILEEXEC
2521 StartLogExec(screen);
2522 #else
2523 Bell(xw, XkbBI_Info, 0);
2524 Bell(xw, XkbBI_Info, 0);
2525 return;
2526 #endif
2527 } else if (strcmp(screen->logfile, "-") == 0) {
2528 screen->logfd = STDOUT_FILENO;
2529 } else {
2530 if ((screen->logfd = open_userfile(screen->uid,
2531 screen->gid,
2532 screen->logfile,
2533 True)) < 0)
2534 return;
2535 }
2536 #endif /*VMS */
2537 screen->logstart = VTbuffer->next;
2538 screen->logging = True;
2539 update_logging();
2540 }
2541
2542 void
2543 CloseLog(XtermWidget xw)
2544 {
2545 TScreen *screen = TScreenOf(xw);
2546
2547 if (!screen->logging || (screen->inhibit & I_LOG))
2548 return;
2549 FlushLog(xw);
2550 close(screen->logfd);
2551 screen->logging = False;
2552 update_logging();
2553 }
2554
2555 void
2556 FlushLog(XtermWidget xw)
2557 {
2558 TScreen *screen = TScreenOf(xw);
2559
2560 if (screen->logging && !(screen->inhibit & I_LOG)) {
2561 Char *cp;
2562 size_t i;
2563
2564 #ifdef VMS /* avoid logging output loops which otherwise occur sometimes
2565 when there is no output and cp/screen->logstart are 1 apart */
2566 if (!tt_new_output)
2567 return;
2568 tt_new_output = False;
2569 #endif /* VMS */
2570 cp = VTbuffer->next;
2571 if (screen->logstart != 0
2572 && (i = (size_t) (cp - screen->logstart)) > 0) {
2573 IGNORE_RC(write(screen->logfd, screen->logstart, i));
2574 }
2575 screen->logstart = VTbuffer->next;
2576 }
2577 }
2578
2579 #endif /* ALLOWLOGGING */
2580
2581 /***====================================================================***/
2582
2583 static unsigned
2584 maskToShift(unsigned long mask)
2585 {
2586 unsigned result = 0;
2587 if (mask != 0) {
2588 while ((mask & 1) == 0) {
2589 mask >>= 1;
2590 ++result;
2591 }
2592 }
2593 return result;
2594 }
2595
2596 static unsigned
2597 maskToWidth(unsigned long mask)
2598 {
2599 unsigned result = 0;
2600 while (mask != 0) {
2601 if ((mask & 1) != 0)
2602 ++result;
2603 mask >>= 1;
2604 }
2605 return result;
2606 }
2607
2608 XVisualInfo *
2609 getVisualInfo(XtermWidget xw)
2610 {
2611 #define MYFMT "getVisualInfo \
2612 depth %d, \
2613 type %d (%s), \
2614 size %d \
2615 rgb masks (%04lx/%04lx/%04lx)\n"
2616 #define MYARG \
2617 vi->depth,\
2618 vi->class,\
2619 ((vi->class & 1) ? "dynamic" : "static"),\
2620 vi->colormap_size,\
2621 vi->red_mask,\
2622 vi->green_mask,\
2623 vi->blue_mask
2624
2625 TScreen *screen = TScreenOf(xw);
2626 Display *dpy = screen->display;
2627 XVisualInfo myTemplate;
2628
2629 if (xw->visInfo == 0 && xw->numVisuals == 0) {
2630 myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2631 XDefaultScreen(dpy)));
2632 xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2633 &myTemplate, &xw->numVisuals);
2634
2635 if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2636 XVisualInfo *vi = xw->visInfo;
2637 xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2638 xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2639 xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2640 xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2641 xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2642 xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2643
2644 xw->has_rgb = ((vi->red_mask != 0) &&
2645 (vi->green_mask != 0) &&
2646 (vi->blue_mask != 0) &&
2647 ((vi->red_mask & vi->green_mask) == 0) &&
2648 ((vi->green_mask & vi->blue_mask) == 0) &&
2649 ((vi->blue_mask & vi->red_mask) == 0) &&
2650 xw->rgb_widths[0] <= (unsigned) vi->bits_per_rgb &&
2651 xw->rgb_widths[1] <= (unsigned) vi->bits_per_rgb &&
2652 xw->rgb_widths[2] <= (unsigned) vi->bits_per_rgb &&
2653 (vi->class == TrueColor
2654 || vi->class == DirectColor));
2655
2656 if (resource.reportColors) {
2657 printf(MYFMT, MYARG);
2658 }
2659 TRACE((MYFMT, MYARG));
2660 TRACE(("...shifts %u/%u/%u\n",
2661 xw->rgb_shifts[0],
2662 xw->rgb_shifts[1],
2663 xw->rgb_shifts[2]));
2664 TRACE(("...widths %u/%u/%u\n",
2665 xw->rgb_widths[0],
2666 xw->rgb_widths[1],
2667 xw->rgb_widths[2]));
2668 }
2669 }
2670 return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
2671 #undef MYFMT
2672 #undef MYARG
2673 }
2674
2675 #if OPT_ISO_COLORS
2676 static Bool
2677 ReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
2678 {
2679 Bool result = False;
2680
2681 if (AllowColorOps(xw, ecGetAnsiColor)) {
2682 XColor color;
2683 char buffer[80];
2684
2685 TRACE(("ReportAnsiColorRequest %d\n", colornum));
2686 color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2687 (void) QueryOneColor(xw, &color);
2688 sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2689 opcode,
2690 (opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
2691 color.red,
2692 color.green,
2693 color.blue);
2694 unparseputc1(xw, ANSI_OSC);
2695 unparseputs(xw, buffer);
2696 unparseputc1(xw, final);
2697 result = True;
2698 }
2699 return result;
2700 }
2701
2702 static void
2703 getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2704 {
2705 if (getVisualInfo(xw)) {
2706 *typep = (unsigned) xw->visInfo->class;
2707 *sizep = (unsigned) xw->visInfo->colormap_size;
2708 } else {
2709 *typep = 0;
2710 *sizep = 0;
2711 }
2712 }
2713
2714 #define MAX_COLORTABLE 4096
2715
2716 /*
2717 * Make only one call to XQueryColors(), since it can be slow.
2718 */
2719 static Boolean
2720 loadColorTable(XtermWidget xw, unsigned length)
2721 {
2722 Colormap cmap = xw->core.colormap;
2723 TScreen *screen = TScreenOf(xw);
2724 Boolean result = (screen->cmap_data != 0);
2725
2726 if (!result
2727 && length != 0
2728 && length < MAX_COLORTABLE) {
2729 screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2730
2731 if (screen->cmap_data != 0) {
2732 unsigned i;
2733 unsigned shift;
2734
2735 if (getVisualInfo(xw))
2736 shift = xw->rgb_shifts[2];
2737 else
2738 shift = 0;
2739
2740 screen->cmap_size = length;
2741
2742 for (i = 0; i < screen->cmap_size; i++) {
2743 screen->cmap_data[i].pixel = (unsigned long) i << shift;
2744 }
2745 result = (Boolean) (XQueryColors(screen->display,
2746 cmap,
2747 screen->cmap_data,
2748 (int) screen->cmap_size) != 0);
2749 }
2750 }
2751 return result;
2752 }
2753
2754 /***====================================================================***/
2755
2756 /*
2757 * Call this function with def->{red,green,blue} initialized, to obtain a pixel
2758 * value.
2759 */
2760 Boolean
2761 AllocOneColor(XtermWidget xw, XColor *def)
2762 {
2763 TScreen *screen = TScreenOf(xw);
2764 Boolean result = True;
2765
2766 #define MaskIt(name,nn) \
2767 ((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
2768 << xw->rgb_shifts[nn]) \
2769 & xw->visInfo->name ##_mask)
2770
2771 #define VisualIsRGB(xw) (getVisualInfo(xw) != NULL && xw->has_rgb && xw->visInfo->bits_per_rgb <= 8)
2772
2773 if (VisualIsRGB(xw)) {
2774 def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
2775 } else {
2776 Display *dpy = screen->display;
2777 if (!XAllocColor(dpy, xw->core.colormap, def)) {
2778 /*
2779 * Decide between foreground and background by a grayscale
2780 * approximation.
2781 */
2782 int bright = def->red * 3 + def->green * 10 + def->blue;
2783 int levels = 14 * 0x8000;
2784 def->pixel = ((bright >= levels)
2785 ? xw->dft_background
2786 : xw->dft_foreground);
2787 TRACE(("XAllocColor failed, for %04x/%04x/%04x: choose %08lx (%d vs %d)\n",
2788 def->red, def->green, def->blue,
2789 def->pixel, bright, levels));
2790 result = False;
2791 }
2792 }
2793 return result;
2794 }
2795
2796 /***====================================================================***/
2797
2798 /*
2799 * Call this function with def->pixel set to the color that we want to convert
2800 * to separate red/green/blue.
2801 */
2802 Boolean
2803 QueryOneColor(XtermWidget xw, XColor *def)
2804 {
2805 Boolean result = True;
2806
2807 #define UnMaskIt(name,nn) \
2808 ((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
2809 #define UnMaskIt2(name,nn) \
2810 (unsigned short)((((UnMaskIt(name,nn) << 8) \
2811 |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
2812
2813 if (VisualIsRGB(xw)) {
2814 /* *INDENT-EQLS* */
2815 def->red = UnMaskIt2(red, 0);
2816 def->green = UnMaskIt2(green, 1);
2817 def->blue = UnMaskIt2(blue, 2);
2818 } else {
2819 Display *dpy = TScreenOf(xw)->display;
2820 if (!XQueryColor(dpy, xw->core.colormap, def)) {
2821 TRACE(("XQueryColor failed, given %08lx\n", def->pixel));
2822 result = False;
2823 }
2824 }
2825 return result;
2826 }
2827
2828 /***====================================================================***/
2829
2830 /*
2831 * Find closest color for "def" in "cmap".
2832 * Set "def" to the resulting color.
2833 *
2834 * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2835 * modified with ideas from David Tong's "noflash" library.
2836 * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2837 *
2838 * Return False if not able to find or allocate a color.
2839 */
2840 static Boolean
2841 allocateClosestRGB(XtermWidget xw, XColor *def)
2842 {
2843 TScreen *screen = TScreenOf(xw);
2844 Boolean result = False;
2845 unsigned cmap_type;
2846 unsigned cmap_size;
2847
2848 getColormapInfo(xw, &cmap_type, &cmap_size);
2849
2850 if ((cmap_type & 1) != 0) {
2851
2852 if (loadColorTable(xw, cmap_size)) {
2853 char *tried = TypeCallocN(char, (size_t) cmap_size);
2854
2855 if (tried != 0) {
2856 unsigned attempts;
2857
2858 /*
2859 * Try (possibly each entry in the color map) to find the best
2860 * approximation to the requested color.
2861 */
2862 for (attempts = 0; attempts < cmap_size; attempts++) {
2863 Boolean first = True;
2864 double bestRGB = 0.0;
2865 unsigned bestInx = 0;
2866 unsigned i;
2867
2868 for (i = 0; i < cmap_size; i++) {
2869 if (!tried[bestInx]) {
2870 double diff, thisRGB = 0.0;
2871
2872 /*
2873 * Look for the best match based on luminance.
2874 * Measure this by the least-squares difference of
2875 * the weighted R/G/B components from the color map
2876 * versus the requested color. Use the Y (luma)
2877 * component of the YIQ color space model for
2878 * weights that correspond to the luminance.
2879 */
2880 #define AddColorWeight(weight, color) \
2881 diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2882 thisRGB += diff * diff
2883
2884 AddColorWeight(0.30, red);
2885 AddColorWeight(0.61, green);
2886 AddColorWeight(0.11, blue);
2887
2888 if (first || (thisRGB < bestRGB)) {
2889 first = False;
2890 bestInx = i;
2891 bestRGB = thisRGB;
2892 }
2893 }
2894 }
2895 if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
2896 *def = screen->cmap_data[bestInx];
2897 TRACE(("...closest %x/%x/%x\n", def->red,
2898 def->green, def->blue));
2899 result = True;
2900 break;
2901 }
2902 /*
2903 * It failed - either the color map entry was readonly, or
2904 * another client has allocated the entry. Mark the entry
2905 * so we will ignore it
2906 */
2907 tried[bestInx] = True;
2908 }
2909 free(tried);
2910 }
2911 }
2912 }
2913 return result;
2914 }
2915
2916 #ifndef ULONG_MAX
2917 #define ULONG_MAX (unsigned long)(~(0L))
2918 #endif
2919
2920 /*
2921 * Allocate a color for the "ANSI" colors. That actually includes colors up
2922 * to 256.
2923 *
2924 * Returns
2925 * -1 on error
2926 * 0 on no change
2927 * 1 if a new color was allocated.
2928 */
2929 static int
2930 AllocateAnsiColor(XtermWidget xw,
2931 ColorRes * res,
2932 const char *spec)
2933 {
2934 int result;
2935 XColor def;
2936
2937 if (xtermAllocColor(xw, &def, spec)) {
2938 if (res->mode == True &&
2939 EQL_COLOR_RES(res, def.pixel)) {
2940 result = 0;
2941 } else {
2942 result = 1;
2943 SET_COLOR_RES(res, def.pixel);
2944 res->red = def.red;
2945 res->green = def.green;
2946 res->blue = def.blue;
2947 TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
2948 (int) (res - TScreenOf(xw)->Acolors), spec,
2949 def.red,
2950 def.green,
2951 def.blue,
2952 def.pixel));
2953 if (!res->mode)
2954 result = 0;
2955 res->mode = True;
2956 }
2957 } else {
2958 TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2959 result = -1;
2960 }
2961 return (result);
2962 }
2963
2964 Pixel
2965 xtermGetColorRes(XtermWidget xw, ColorRes * res)
2966 {
2967 Pixel result = 0;
2968
2969 if (res->mode) {
2970 result = res->value;
2971 } else {
2972 TRACE(("xtermGetColorRes for Acolors[%d]\n",
2973 (int) (res - TScreenOf(xw)->Acolors)));
2974
2975 if (res >= TScreenOf(xw)->Acolors) {
2976 assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2977
2978 if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2979 res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2980 res->mode = -True;
2981 xtermWarning("Cannot allocate color \"%s\"\n",
2982 NonNull(res->resource));
2983 }
2984 result = res->value;
2985 } else {
2986 result = 0;
2987 }
2988 }
2989 return result;
2990 }
2991
2992 static int
2993 ChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2994 {
2995 int code;
2996
2997 if (color < 0 || color >= MAXCOLORS) {
2998 code = -1;
2999 } else {
3000 ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
3001
3002 TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
3003 code = AllocateAnsiColor(xw, res, name);
3004 }
3005 return code;
3006 }
3007
3008 /*
3009 * Set or query entries in the Acolors[] array by parsing pairs of color/name
3010 * values from the given buffer.
3011 *
3012 * The color can be any legal index into Acolors[], which consists of the
3013 * 16/88/256 "ANSI" colors, followed by special color values for the various
3014 * colorXX resources. The indices for the special color values are not
3015 * simple to work with, so an alternative is to use the calls which pass in
3016 * 'first' set to the beginning of those indices.
3017 *
3018 * If the name is "?", report to the host the current value for the color.
3019 */
3020 static Bool
3021 ChangeAnsiColorRequest(XtermWidget xw,
3022 int opcode,
3023 char *buf,
3024 int first,
3025 int final)
3026 {
3027 int repaint = False;
3028 int code;
3029 int last = (MAXCOLORS - first);
3030 int queried = 0;
3031
3032 TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
3033
3034 while (buf && *buf) {
3035 int color;
3036 char *name = strchr(buf, ';');
3037
3038 if (name == NULL)
3039 break;
3040 *name = '\0';
3041 name++;
3042 color = atoi(buf);
3043 if (color < 0 || color >= last)
3044 break; /* quit on any error */
3045 buf = strchr(name, ';');
3046 if (buf) {
3047 *buf = '\0';
3048 buf++;
3049 }
3050 if (!strcmp(name, "?")) {
3051 if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3052 ++queried;
3053 } else {
3054 code = ChangeOneAnsiColor(xw, color + first, name);
3055 if (code < 0) {
3056 /* stop on any error */
3057 break;
3058 } else if (code > 0) {
3059 repaint = True;
3060 }
3061 /* FIXME: free old color somehow? We aren't for the other color
3062 * change style (dynamic colors).
3063 */
3064 }
3065 }
3066 if (queried)
3067 unparse_end(xw);
3068
3069 return (repaint);
3070 }
3071
3072 static Bool
3073 ResetOneAnsiColor(XtermWidget xw, int color, int start)
3074 {
3075 Bool repaint = False;
3076 int last = MAXCOLORS - start;
3077
3078 if (color >= 0 && color < last) {
3079 ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3080
3081 if (res->mode) {
3082 /* a color has been allocated for this slot - test further... */
3083 if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3084 repaint = True;
3085 }
3086 }
3087 }
3088 return repaint;
3089 }
3090
3091 int
3092 ResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3093 {
3094 int repaint = 0;
3095 int color;
3096
3097 TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3098 if (*buf != '\0') {
3099 /* reset specific colors */
3100 while (!IsEmpty(buf)) {
3101 char *next;
3102
3103 color = (int) (strtol) (buf, &next, 10);
3104 if (!PartS2L(buf, next) || (color < 0))
3105 break; /* no number at all */
3106 if (next != 0) {
3107 if (strchr(";", *next) == 0)
3108 break; /* unexpected delimiter */
3109 ++next;
3110 }
3111
3112 if (ResetOneAnsiColor(xw, color, start)) {
3113 ++repaint;
3114 }
3115 buf = next;
3116 }
3117 } else {
3118 TRACE(("...resetting all %d colors\n", MAXCOLORS));
3119 for (color = 0; color < MAXCOLORS; ++color) {
3120 if (ResetOneAnsiColor(xw, color, start)) {
3121 ++repaint;
3122 }
3123 }
3124 }
3125 TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3126 return repaint;
3127 }
3128 #else
3129 #define allocateClosestRGB(xw, def) 0
3130 #endif /* OPT_ISO_COLORS */
3131
3132 Boolean
3133 allocateBestRGB(XtermWidget xw, XColor *def)
3134 {
3135 (void) xw;
3136 (void) def;
3137 return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
3138 }
3139
3140 static Boolean
3141 xtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
3142 {
3143 Boolean result = False;
3144 TScreen *screen = TScreenOf(xw);
3145 Colormap cmap = xw->core.colormap;
3146 size_t have = strlen(spec);
3147
3148 if (have == 0 || have > MAX_U_STRING) {
3149 if (resource.reportColors) {
3150 printf("color (ignored, length %lu)\n", (unsigned long) have);
3151 }
3152 } else if (XParseColor(screen->display, cmap, spec, def)) {
3153 XColor save_def = *def;
3154 if (resource.reportColors) {
3155 printf("color %04x/%04x/%04x = \"%s\"\n",
3156 def->red, def->green, def->blue,
3157 spec);
3158 }
3159 if (allocateBestRGB(xw, def)) {
3160 if (resource.reportColors) {
3161 if (def->red != save_def.red ||
3162 def->green != save_def.green ||
3163 def->blue != save_def.blue) {
3164 printf("color %04x/%04x/%04x ~ \"%s\"\n",
3165 def->red, def->green, def->blue,
3166 spec);
3167 }
3168 }
3169 TRACE(("xtermAllocColor -> %x/%x/%x\n",
3170 def->red, def->green, def->blue));
3171 result = True;
3172 }
3173 }
3174 return result;
3175 }
3176
3177 /*
3178 * This provides an approximation (the closest color from xterm's palette)
3179 * rather than the "exact" color (whatever the display could provide, actually)
3180 * because of the context in which it is used.
3181 */
3182 #define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
3183 int
3184 xtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
3185 {
3186 int result = -1;
3187 #if OPT_ISO_COLORS
3188 int n;
3189 int best_index = -1;
3190 unsigned long best_value = 0;
3191 unsigned long this_value;
3192 long diff_red, diff_green, diff_blue;
3193
3194 TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
3195
3196 for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
3197 ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
3198
3199 /* ensure that we have a value for each of the colors */
3200 if (!res->mode) {
3201 (void) AllocateAnsiColor(xw, res, res->resource);
3202 }
3203
3204 /* find the closest match */
3205 if (res->mode == True) {
3206 TRACE2(("...lookup %lx -> %x/%x/%x\n",
3207 res->value, res->red, res->green, res->blue));
3208 diff_red = ColorDiff(find_red, res->red);
3209 diff_green = ColorDiff(find_green, res->green);
3210 diff_blue = ColorDiff(find_blue, res->blue);
3211 this_value = (unsigned long) ((diff_red * diff_red)
3212 + (diff_green * diff_green)
3213 + (diff_blue * diff_blue));
3214 if (best_index < 0 || this_value < best_value) {
3215 best_index = n;
3216 best_value = this_value;
3217 }
3218 }
3219 }
3220 TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
3221 result = best_index;
3222
3223 #else
3224 (void) xw;
3225 (void) find_red;
3226 (void) find_green;
3227 (void) find_blue;
3228 #endif
3229 return result;
3230 }
3231
3232 #if OPT_DIRECT_COLOR
3233 int
3234 getDirectColor(XtermWidget xw, int red, int green, int blue)
3235 {
3236 Pixel result = 0;
3237
3238 #define getRGB(name,shift) \
3239 do { \
3240 Pixel value = (Pixel) name & 0xff; \
3241 if (xw->rgb_widths[shift] < 8) { \
3242 value >>= (int) (8 - xw->rgb_widths[shift]); \
3243 } \
3244 value <<= xw->rgb_shifts[shift]; \
3245 value &= xw->visInfo->name ##_mask; \
3246 result |= value; \
3247 } while (0)
3248
3249 getRGB(red, 0);
3250 getRGB(green, 1);
3251 getRGB(blue, 2);
3252
3253 #undef getRGB
3254
3255 return (int) result;
3256 }
3257
3258 static void
3259 formatDirectColor(char *target, XtermWidget xw, unsigned value)
3260 {
3261 Pixel result[3];
3262
3263 #define getRGB(name, shift) \
3264 do { \
3265 result[shift] = value & xw->visInfo->name ## _mask; \
3266 result[shift] >>= xw->rgb_shifts[shift]; \
3267 if (xw->rgb_widths[shift] < 8) \
3268 result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
3269 } while(0)
3270
3271 getRGB(red, 0);
3272 getRGB(green, 1);
3273 getRGB(blue, 2);
3274
3275 #undef getRGB
3276
3277 sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
3278 }
3279 #endif /* OPT_DIRECT_COLOR */
3280
3281 #define fg2SGR(n) \
3282 (n) >= 8 ? 9 : 3, \
3283 (n) >= 8 ? (n) - 8 : (n)
3284 #define bg2SGR(n) \
3285 (n) >= 8 ? 10 : 4, \
3286 (n) >= 8 ? (n) - 8 : (n)
3287
3288 #define EndOf(s) (s) + strlen(s)
3289
3290 char *
3291 xtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3292 {
3293 TScreen *screen = TScreenOf(xw);
3294 char *msg = target;
3295
3296 strcpy(target, "0");
3297 if (attr & BOLD)
3298 strcat(msg, ";1");
3299 if (attr & UNDERLINE)
3300 strcat(msg, ";4");
3301 if (attr & BLINK)
3302 strcat(msg, ";5");
3303 if (attr & INVERSE)
3304 strcat(msg, ";7");
3305 if (attr & INVISIBLE)
3306 strcat(msg, ";8");
3307 #if OPT_WIDE_ATTRS
3308 if (attr & ATR_FAINT)
3309 strcat(msg, ";2");
3310 if (attr & ATR_ITALIC)
3311 strcat(msg, ";3");
3312 if (attr & ATR_STRIKEOUT)
3313 strcat(msg, ";9");
3314 if (attr & ATR_DBL_UNDER)
3315 strcat(msg, ";21");
3316 #endif
3317 #if OPT_256_COLORS || OPT_88_COLORS
3318 if_OPT_ISO_COLORS(screen, {
3319 if (attr & FG_COLOR) {
3320 if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3321 strcat(msg, ";38:2::");
3322 formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3323 }) if (fg >= 16) {
3324 sprintf(EndOf(msg), ";38:5:%d", fg);
3325 } else {
3326 sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3327 }
3328 }
3329 if (attr & BG_COLOR) {
3330 if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3331 strcat(msg, ";48:2::");
3332 formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3333 }) if (bg >= 16) {
3334 sprintf(EndOf(msg), ";48:5:%d", bg);
3335 } else {
3336 sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3337 }
3338 }
3339 });
3340 #elif OPT_ISO_COLORS
3341 if_OPT_ISO_COLORS(screen, {
3342 if (attr & FG_COLOR) {
3343 sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3344 }
3345 if (attr & BG_COLOR) {
3346 sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3347 }
3348 });
3349 #else
3350 (void) screen;
3351 (void) fg;
3352 (void) bg;
3353 #endif
3354 return target;
3355 }
3356
3357 #if OPT_PASTE64
3358 static void
3359 ManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3360 {
3361 #define PDATA(a,b) { a, #b }
3362 static struct {
3363 char given;
3364 String result;
3365 } table[] = {
3366 PDATA('s', SELECT),
3367 PDATA('p', PRIMARY),
3368 PDATA('q', SECONDARY),
3369 PDATA('c', CLIPBOARD),
3370 PDATA('0', CUT_BUFFER0),
3371 PDATA('1', CUT_BUFFER1),
3372 PDATA('2', CUT_BUFFER2),
3373 PDATA('3', CUT_BUFFER3),
3374 PDATA('4', CUT_BUFFER4),
3375 PDATA('5', CUT_BUFFER5),
3376 PDATA('6', CUT_BUFFER6),
3377 PDATA('7', CUT_BUFFER7),
3378 };
3379 char target_used[XtNumber(table)];
3380 char select_code[XtNumber(table) + 1];
3381 String select_args[XtNumber(table) + 1];
3382
3383 const char *base = buf;
3384 Cardinal j;
3385 Cardinal num_targets = 0;
3386
3387 TRACE(("Manipulate selection data\n"));
3388
3389 memset(target_used, 0, sizeof(target_used));
3390 while (*buf != ';' && *buf != '\0') {
3391 ++buf;
3392 }
3393
3394 if (*buf == ';') {
3395
3396 *buf++ = '\0';
3397 if (*base == '\0')
3398 base = "s0";
3399
3400 while (*base != '\0') {
3401 for (j = 0; j < XtNumber(table); ++j) {
3402 if (*base == table[j].given) {
3403 if (!target_used[j]) {
3404 target_used[j] = 1;
3405 select_code[num_targets] = *base;
3406 select_args[num_targets++] = table[j].result;
3407 TRACE(("atom[%d] %s\n", num_targets, table[j].result));
3408 }
3409 break;
3410 }
3411 }
3412 ++base;
3413 }
3414 select_code[num_targets] = '\0';
3415
3416 if (!strcmp(buf, "?")) {
3417 if (AllowWindowOps(xw, ewGetSelection)) {
3418 TRACE(("Getting selection\n"));
3419 unparseputc1(xw, ANSI_OSC);
3420 unparseputs(xw, "52");
3421 unparseputc(xw, ';');
3422
3423 unparseputs(xw, select_code);
3424 unparseputc(xw, ';');
3425
3426 /* Tell xtermGetSelection data is base64 encoded */
3427 screen->base64_paste = num_targets;
3428 screen->base64_final = final;
3429
3430 screen->selection_time =
3431 XtLastTimestampProcessed(TScreenOf(xw)->display);
3432
3433 /* terminator will be written in this call */
3434 xtermGetSelection((Widget) xw,
3435 screen->selection_time,
3436 select_args, num_targets,
3437 NULL);
3438 }
3439 } else {
3440 if (AllowWindowOps(xw, ewSetSelection)) {
3441 char *old = buf;
3442
3443 TRACE(("Setting selection(%s) with %s\n", select_code, buf));
3444 screen->selection_time =
3445 XtLastTimestampProcessed(TScreenOf(xw)->display);
3446
3447 for (j = 0; j < num_targets; ++j) {
3448 buf = old;
3449 ClearSelectionBuffer(screen, select_args[j]);
3450 while (*buf != '\0') {
3451 AppendToSelectionBuffer(screen,
3452 CharOf(*buf++),
3453 select_args[j]);
3454 }
3455 }
3456 CompleteSelection(xw, select_args, num_targets);
3457 }
3458 }
3459 }
3460 }
3461 #endif /* OPT_PASTE64 */
3462
3463 /***====================================================================***/
3464
3465 #define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3466 || (xw->screen.utf8_title) \
3467 || (xw->screen.c1_printable))
3468
3469 static Bool
3470 xtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3471 {
3472 TScreen *screen = TScreenOf(xw);
3473 Bool result = False;
3474 Char *cp = *bufp;
3475 Char *next = cp;
3476
3477 (void) screen;
3478 (void) last;
3479
3480 #if OPT_WIDE_CHARS
3481 if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3482 PtyData data;
3483
3484 if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3485 if (data.utf_data != UCS_REPL
3486 && (data.utf_data >= 128 ||
3487 ansi_table[data.utf_data] == CASE_PRINT)) {
3488 next += (data.utf_size - 1);
3489 result = True;
3490 } else {
3491 result = False;
3492 }
3493 } else {
3494 result = False;
3495 }
3496 } else
3497 #endif
3498 #if OPT_C1_PRINT
3499 if (screen->c1_printable
3500 && (*cp >= 128 && *cp < 160)) {
3501 result = True;
3502 } else
3503 #endif
3504 if (ansi_table[*cp] == CASE_PRINT) {
3505 result = True;
3506 }
3507 *bufp = next;
3508 return result;
3509 }
3510
3511 /***====================================================================***/
3512
3513 /*
3514 * Enum corresponding to the actual OSC codes rather than the internal
3515 * array indices. Compare with TermColors.
3516 */
3517 typedef enum {
3518 OSC_TEXT_FG = 10
3519 ,OSC_TEXT_BG
3520 ,OSC_TEXT_CURSOR
3521 ,OSC_MOUSE_FG
3522 ,OSC_MOUSE_BG
3523 #if OPT_TEK4014
3524 ,OSC_TEK_FG = 15
3525 ,OSC_TEK_BG
3526 #endif
3527 #if OPT_HIGHLIGHT_COLOR
3528 ,OSC_HIGHLIGHT_BG = 17
3529 #endif
3530 #if OPT_TEK4014
3531 ,OSC_TEK_CURSOR = 18
3532 #endif
3533 #if OPT_HIGHLIGHT_COLOR
3534 ,OSC_HIGHLIGHT_FG = 19
3535 #endif
3536 ,OSC_NCOLORS
3537 } OscTextColors;
3538
3539 /*
3540 * Map codes to OSC controls that can reset colors.
3541 */
3542 #define OSC_RESET 100
3543 #define OSC_Reset(code) (code) + OSC_RESET
3544
3545 /*
3546 * Other (non-color) OSC controls
3547 */
3548 typedef enum {
3549 OSC_IconBoth = 0
3550 ,OSC_IconOnly = 1
3551 ,OSC_TitleOnly = 2
3552 ,OSC_X_Property = 3
3553 ,OSC_SetAnsiColor = 4
3554 ,OSC_GetAnsiColors = 5
3555 ,OSC_ColorMode = 6
3556 ,OSC_SetupPointer = 22
3557 ,OSC_Unused_30 = 30 /* Konsole (unused) */
3558 ,OSC_Unused_31 = 31 /* Konsole (unused) */
3559 ,OSC_NewLogFile = 46
3560 ,OSC_FontOps = 50
3561 ,OSC_Unused_51 /* Emacs (unused) */
3562 ,OSC_SelectionData = 52
3563 ,OSC_AllowedOps = 60
3564 ,OSC_DisallowedOps = 61
3565 } OscMiscOps;
3566
3567 static Bool
3568 GetOldColors(XtermWidget xw)
3569 {
3570 if (xw->work.oldColors == NULL) {
3571 int i;
3572
3573 xw->work.oldColors = TypeXtMalloc(ScrnColors);
3574 if (xw->work.oldColors == NULL) {
3575 xtermWarning("allocation failure in GetOldColors\n");
3576 return (False);
3577 }
3578 xw->work.oldColors->which = 0;
3579 for (i = 0; i < NCOLORS; i++) {
3580 xw->work.oldColors->colors[i] = 0;
3581 xw->work.oldColors->names[i] = NULL;
3582 }
3583 GetColors(xw, xw->work.oldColors);
3584 }
3585 return (True);
3586 }
3587
3588 static int
3589 oppositeColor(XtermWidget xw, int n)
3590 {
3591 Boolean reversed = (xw->misc.re_verse);
3592
3593 switch (n) {
3594 case TEXT_FG:
3595 n = reversed ? TEXT_FG : TEXT_BG;
3596 break;
3597 case TEXT_BG:
3598 n = reversed ? TEXT_BG : TEXT_FG;
3599 break;
3600 case MOUSE_FG:
3601 n = MOUSE_BG;
3602 break;
3603 case MOUSE_BG:
3604 n = MOUSE_FG;
3605 break;
3606 #if OPT_TEK4014
3607 case TEK_FG:
3608 n = reversed ? TEK_FG : TEK_BG;
3609 break;
3610 case TEK_BG:
3611 n = reversed ? TEK_BG : TEK_FG;
3612 break;
3613 #endif
3614 #if OPT_HIGHLIGHT_COLOR
3615 case HIGHLIGHT_FG:
3616 n = HIGHLIGHT_BG;
3617 break;
3618 case HIGHLIGHT_BG:
3619 n = HIGHLIGHT_FG;
3620 break;
3621 #endif
3622 default:
3623 break;
3624 }
3625 return n;
3626 }
3627
3628 static Bool
3629 ReportColorRequest(XtermWidget xw, int ndx, int final)
3630 {
3631 Bool result = False;
3632
3633 if (AllowColorOps(xw, ecGetColor)) {
3634 XColor color;
3635 char buffer[80];
3636
3637 /*
3638 * ChangeColorsRequest() has "always" chosen the opposite color when
3639 * reverse-video is set. Report this as the original color index, but
3640 * reporting the opposite color which would be used.
3641 */
3642 int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3643
3644 GetOldColors(xw);
3645 color.pixel = xw->work.oldColors->colors[ndx];
3646 (void) QueryOneColor(xw, &color);
3647 sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3648 color.red,
3649 color.green,
3650 color.blue);
3651 TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
3652 ndx, xw->work.oldColors->colors[ndx], buffer));
3653 unparseputc1(xw, ANSI_OSC);
3654 unparseputs(xw, buffer);
3655 unparseputc1(xw, final);
3656 result = True;
3657 }
3658 return result;
3659 }
3660
3661 static Bool
3662 UpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3663 {
3664 int i;
3665
3666 /* if we were going to free old colors, this would be the place to
3667 * do it. I've decided not to (for now), because it seems likely
3668 * that we'd have a small set of colors we use over and over, and that
3669 * we could save some overhead this way. The only case in which this
3670 * (clearly) fails is if someone is trying a boatload of colors, in
3671 * which case they can restart xterm
3672 */
3673 for (i = 0; i < NCOLORS; i++) {
3674 if (COLOR_DEFINED(pNew, i)) {
3675 if (xw->work.oldColors->names[i] != NULL) {
3676 XtFree(xw->work.oldColors->names[i]);
3677 xw->work.oldColors->names[i] = NULL;
3678 }
3679 if (pNew->names[i]) {
3680 xw->work.oldColors->names[i] = pNew->names[i];
3681 }
3682 xw->work.oldColors->colors[i] = pNew->colors[i];
3683 }
3684 }
3685 return (True);
3686 }
3687
3688 /*
3689 * OSC codes are constant, but the indices for the color arrays depend on how
3690 * xterm is compiled.
3691 */
3692 static int
3693 OscToColorIndex(OscTextColors mode)
3694 {
3695 int result = 0;
3696
3697 #define CASE(name) case OSC_##name: result = name; break
3698 switch (mode) {
3699 CASE(TEXT_FG);
3700 CASE(TEXT_BG);
3701 CASE(TEXT_CURSOR);
3702 CASE(MOUSE_FG);
3703 CASE(MOUSE_BG);
3704 #if OPT_TEK4014
3705 CASE(TEK_FG);
3706 CASE(TEK_BG);
3707 #endif
3708 #if OPT_HIGHLIGHT_COLOR
3709 CASE(HIGHLIGHT_BG);
3710 CASE(HIGHLIGHT_FG);
3711 #endif
3712 #if OPT_TEK4014
3713 CASE(TEK_CURSOR);
3714 #endif
3715 case OSC_NCOLORS:
3716 break;
3717 }
3718 #undef CASE
3719 return result;
3720 }
3721
3722 static Bool
3723 ChangeColorsRequest(XtermWidget xw,
3724 int start,
3725 char *names,
3726 int final)
3727 {
3728 Bool result = False;
3729 ScrnColors newColors;
3730
3731 TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3732
3733 if (GetOldColors(xw)) {
3734 int i;
3735 int queried = 0;
3736
3737 newColors.which = 0;
3738 for (i = 0; i < NCOLORS; i++) {
3739 newColors.names[i] = NULL;
3740 }
3741 for (i = start; i < OSC_NCOLORS; i++) {
3742 int ndx = OscToColorIndex((OscTextColors) i);
3743 if (xw->misc.re_verse)
3744 ndx = oppositeColor(xw, ndx);
3745
3746 if (IsEmpty(names)) {
3747 newColors.names[ndx] = NULL;
3748 } else {
3749 char *thisName = ((names[0] == ';') ? NULL : names);
3750
3751 names = strchr(names, ';');
3752 if (names != NULL) {
3753 *names++ = '\0';
3754 }
3755 if (thisName != 0) {
3756 if (!strcmp(thisName, "?")) {
3757 if (ReportColorRequest(xw, ndx, final))
3758 ++queried;
3759 } else if (!xw->work.oldColors->names[ndx]
3760 || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3761 AllocateTermColor(xw, &newColors, ndx, thisName, False);
3762 }
3763 }
3764 }
3765 }
3766
3767 if (newColors.which != 0) {
3768 ChangeColors(xw, &newColors);
3769 UpdateOldColors(xw, &newColors);
3770 } else if (queried) {
3771 unparse_end(xw);
3772 }
3773 result = True;
3774 }
3775 return result;
3776 }
3777
3778 static Bool
3779 ResetColorsRequest(XtermWidget xw,
3780 int code)
3781 {
3782 Bool result = False;
3783
3784 (void) xw;
3785 (void) code;
3786
3787 TRACE(("ResetColorsRequest code=%d\n", code));
3788 if (GetOldColors(xw)) {
3789 ScrnColors newColors;
3790 const char *thisName;
3791 int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3792
3793 if (xw->misc.re_verse)
3794 ndx = oppositeColor(xw, ndx);
3795
3796 thisName = xw->screen.Tcolors[ndx].resource;
3797
3798 newColors.which = 0;
3799 newColors.names[ndx] = NULL;
3800
3801 if (thisName != 0
3802 && xw->work.oldColors->names[ndx] != 0
3803 && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3804 AllocateTermColor(xw, &newColors, ndx, thisName, False);
3805
3806 if (newColors.which != 0) {
3807 ChangeColors(xw, &newColors);
3808 UpdateOldColors(xw, &newColors);
3809 }
3810 }
3811 result = True;
3812 }
3813 return result;
3814 }
3815
3816 #if OPT_SHIFT_FONTS
3817 /*
3818 * Initially, 'source' points to '#' or '?'.
3819 *
3820 * Look for an optional sign and optional number. If those are found, lookup
3821 * the corresponding menu font entry.
3822 */
3823 static int
3824 ParseShiftedFont(XtermWidget xw, String source, String *target)
3825 {
3826 TScreen *screen = TScreenOf(xw);
3827 int num = screen->menu_font_number;
3828 int rel = 0;
3829
3830 if (*++source == '+') {
3831 rel = 1;
3832 source++;
3833 } else if (*source == '-') {
3834 rel = -1;
3835 source++;
3836 }
3837
3838 if (isdigit(CharOf(*source))) {
3839 int val = atoi(source);
3840 if (rel > 0)
3841 rel = val;
3842 else if (rel < 0)
3843 rel = -val;
3844 else
3845 num = val;
3846 }
3847
3848 if (rel != 0) {
3849 num = lookupRelativeFontSize(xw,
3850 screen->menu_font_number, rel);
3851
3852 }
3853 TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3854 *target = source;
3855 return num;
3856 }
3857
3858 static void
3859 QueryFontRequest(XtermWidget xw, String buf, int final)
3860 {
3861 if (AllowFontOps(xw, efGetFont)) {
3862 TScreen *screen = TScreenOf(xw);
3863 Bool success = True;
3864 int num;
3865 String base = buf + 1;
3866 const char *name = 0;
3867
3868 num = ParseShiftedFont(xw, buf, &buf);
3869 if (num < 0
3870 || num > fontMenu_lastBuiltin) {
3871 Bell(xw, XkbBI_MinorError, 0);
3872 success = False;
3873 } else {
3874 #if OPT_RENDERFONT
3875 if (UsingRenderFont(xw)) {
3876 name = getFaceName(xw, False);
3877 } else
3878 #endif
3879 if ((name = screen->MenuFontName(num)) == 0) {
3880 success = False;
3881 }
3882 }
3883
3884 unparseputc1(xw, ANSI_OSC);
3885 unparseputs(xw, "50");
3886
3887 if (success) {
3888 unparseputc(xw, ';');
3889 if (buf >= base) {
3890 /* identify the font-entry, unless it is the current one */
3891 if (*buf != '\0') {
3892 char temp[10];
3893
3894 unparseputc(xw, '#');
3895 sprintf(temp, "%d", num);
3896 unparseputs(xw, temp);
3897 if (*name != '\0')
3898 unparseputc(xw, ' ');
3899 }
3900 }
3901 unparseputs(xw, name);
3902 }
3903
3904 unparseputc1(xw, final);
3905 unparse_end(xw);
3906 }
3907 }
3908
3909 static void
3910 ChangeFontRequest(XtermWidget xw, String buf)
3911 {
3912 if (AllowFontOps(xw, efSetFont)) {
3913 TScreen *screen = TScreenOf(xw);
3914 Bool success = True;
3915 int num;
3916 VTFontNames fonts;
3917 char *name;
3918
3919 /*
3920 * If the font specification is a "#", followed by an optional sign and
3921 * optional number, lookup the corresponding menu font entry.
3922 *
3923 * Further, if the "#", etc., is followed by a font name, use that
3924 * to load the font entry.
3925 */
3926 if (*buf == '#') {
3927 num = ParseShiftedFont(xw, buf, &buf);
3928
3929 if (num < 0
3930 || num > fontMenu_lastBuiltin) {
3931 Bell(xw, XkbBI_MinorError, 0);
3932 success = False;
3933 } else {
3934 /*
3935 * Skip past the optional number, and any whitespace to look
3936 * for a font specification within the control.
3937 */
3938 while (isdigit(CharOf(*buf))) {
3939 ++buf;
3940 }
3941 while (isspace(CharOf(*buf))) {
3942 ++buf;
3943 }
3944 #if OPT_RENDERFONT
3945 if (UsingRenderFont(xw)) {
3946 /* EMPTY */
3947 /* there is only one font entry to load */
3948 ;
3949 } else
3950 #endif
3951 {
3952 /*
3953 * Normally there is no font specified in the control.
3954 * But if there is, simply overwrite the font entry.
3955 */
3956 if (*buf == '\0') {
3957 if ((buf = screen->MenuFontName(num)) == 0) {
3958 success = False;
3959 }
3960 }
3961 }
3962 }
3963 } else {
3964 num = screen->menu_font_number;
3965 }
3966 name = x_strtrim(buf);
3967 if (screen->EscapeFontName()) {
3968 FREE_STRING(screen->EscapeFontName());
3969 screen->EscapeFontName() = 0;
3970 }
3971 if (success && !IsEmpty(name)) {
3972 #if OPT_RENDERFONT
3973 if (UsingRenderFont(xw)) {
3974 setFaceName(xw, name);
3975 xtermUpdateFontInfo(xw, True);
3976 } else
3977 #endif
3978 {
3979 memset(&fonts, 0, sizeof(fonts));
3980 fonts.f_n = name;
3981 if (SetVTFont(xw, num, True, &fonts)
3982 && num == screen->menu_font_number
3983 && num != fontMenu_fontescape) {
3984 screen->EscapeFontName() = x_strdup(name);
3985 }
3986 }
3987 } else {
3988 Bell(xw, XkbBI_MinorError, 0);
3989 }
3990 update_font_escape();
3991 free(name);
3992 }
3993 }
3994 #endif /* OPT_SHIFT_FONTS */
3995
3996 /***====================================================================***/
3997
3998 static void
3999 report_allowed_ops(XtermWidget xw, int final)
4000 {
4001 TScreen *screen = TScreenOf(xw);
4002 char delimiter = ';';
4003
4004 unparseputc1(xw, ANSI_OSC);
4005 unparseputn(xw, OSC_AllowedOps);
4006
4007 #define CASE(name) \
4008 if (screen->name) { \
4009 unparseputc(xw, delimiter); \
4010 unparseputs(xw, #name); \
4011 delimiter = ','; \
4012 }
4013 CASE(allowColorOps);
4014 CASE(allowFontOps);
4015 CASE(allowMouseOps);
4016 CASE(allowPasteControls);
4017 CASE(allowTcapOps);
4018 CASE(allowTitleOps);
4019 CASE(allowWindowOps);
4020 #undef CASE
4021
4022 unparseputc1(xw, final);
4023 }
4024
4025 static void
4026 report_disallowed_ops(XtermWidget xw, char *value, int final)
4027 {
4028 unparseputc1(xw, ANSI_OSC);
4029 unparseputn(xw, OSC_DisallowedOps);
4030 unparse_disallowed_ops(xw, value);
4031 unparseputc1(xw, final);
4032 }
4033
4034 /***====================================================================***/
4035
4036 void
4037 do_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
4038 {
4039 TScreen *screen = TScreenOf(xw);
4040 int mode;
4041 Char *cp;
4042 int state = 0;
4043 char *buf = 0;
4044 char temp[20];
4045 #if OPT_ISO_COLORS
4046 int ansi_colors = 0;
4047 #endif
4048 Bool need_data = True;
4049 Bool optional_data = False;
4050
4051 TRACE(("do_osc %s\n", oscbuf));
4052
4053 (void) screen;
4054
4055 /*
4056 * Lines should be of the form <OSC> number ; string <ST>, however
4057 * older xterms can accept <BEL> as a final character. We will respond
4058 * with the same final character as the application sends to make this
4059 * work better with shell scripts, which may have trouble reading an
4060 * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
4061 */
4062 mode = 0;
4063 for (cp = oscbuf; *cp != '\0'; cp++) {
4064 switch (state) {
4065 case 0:
4066 if (isdigit(*cp)) {
4067 mode = 10 * mode + (*cp - '0');
4068 if (mode > 65535) {
4069 TRACE(("do_osc found unknown mode %d\n", mode));
4070 return;
4071 }
4072 break;
4073 } else {
4074 switch (*cp) {
4075 case 'I':
4076 xtermLoadIcon(xw, (char *) ++cp);
4077 return;
4078 case 'l':
4079 ChangeTitle(xw, (char *) ++cp);
4080 return;
4081 case 'L':
4082 ChangeIconName(xw, (char *) ++cp);
4083 return;
4084 }
4085 }
4086 /* FALLTHRU */
4087 case 1:
4088 if (*cp != ';') {
4089 TRACE(("do_osc did not find semicolon offset %lu\n",
4090 (unsigned long) (cp - oscbuf)));
4091 return;
4092 }
4093 state = 2;
4094 break;
4095 case 2:
4096 buf = (char *) cp;
4097 state = 3;
4098 /* FALLTHRU */
4099 default:
4100 if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4101 switch (mode) {
4102 case 0:
4103 case 1:
4104 case 2:
4105 break;
4106 default:
4107 TRACE(("do_osc found nonprinting char %02X offset %lu\n",
4108 CharOf(*cp),
4109 (unsigned long) (cp - oscbuf)));
4110 return;
4111 }
4112 }
4113 }
4114 }
4115
4116 /*
4117 * Check if the palette changed and there are no more immediate changes
4118 * that could be deferred to the next repaint.
4119 */
4120 if (xw->work.palette_changed) {
4121 switch (mode) {
4122 case OSC_AllowedOps:
4123 case OSC_DisallowedOps:
4124 case OSC_FontOps:
4125 case OSC_NewLogFile:
4126 case OSC_SelectionData:
4127 case OSC_Unused_30:
4128 case OSC_Unused_31:
4129 case OSC_Unused_51:
4130 case OSC_X_Property:
4131 TRACE(("forced repaint after palette changed\n"));
4132 xw->work.palette_changed = False;
4133 xtermRepaint(xw);
4134 break;
4135 default:
4136 xtermNeedSwap(xw, 1);
4137 break;
4138 }
4139 }
4140
4141 /*
4142 * Most OSC controls other than resets require data. Handle the others as
4143 * a special case.
4144 */
4145 switch (mode) {
4146 case OSC_FontOps:
4147 #if OPT_ISO_COLORS
4148 case OSC_Reset(OSC_SetAnsiColor):
4149 case OSC_Reset(OSC_GetAnsiColors):
4150 need_data = False;
4151 optional_data = True;
4152 break;
4153 case OSC_Reset(OSC_TEXT_FG):
4154 case OSC_Reset(OSC_TEXT_BG):
4155 case OSC_Reset(OSC_TEXT_CURSOR):
4156 case OSC_Reset(OSC_MOUSE_FG):
4157 case OSC_Reset(OSC_MOUSE_BG):
4158 #if OPT_HIGHLIGHT_COLOR
4159 case OSC_Reset(OSC_HIGHLIGHT_BG):
4160 case OSC_Reset(OSC_HIGHLIGHT_FG):
4161 #endif
4162 #if OPT_TEK4014
4163 case OSC_Reset(OSC_TEK_FG):
4164 case OSC_Reset(OSC_TEK_BG):
4165 case OSC_Reset(OSC_TEK_CURSOR):
4166 #endif
4167 case OSC_AllowedOps:
4168 need_data = False;
4169 break;
4170 #endif
4171 default:
4172 break;
4173 }
4174
4175 /*
4176 * Check if we have data when we want, and not when we do not want it.
4177 * Either way, that is a malformed control sequence, and will be ignored.
4178 */
4179 if (IsEmpty(buf)) {
4180 if (need_data) {
4181 switch (mode) {
4182 case 0:
4183 case 1:
4184 case 2:
4185 buf = strcpy(temp, "xterm");
4186 break;
4187 default:
4188 TRACE(("do_osc found no data\n"));
4189 return;
4190 }
4191 } else {
4192 temp[0] = '\0';
4193 buf = temp;
4194 }
4195 } else if (!need_data && !optional_data) {
4196 TRACE(("do_osc found unwanted data\n"));
4197 return;
4198 }
4199
4200 switch (mode) {
4201 case OSC_IconBoth: /* new icon name and title */
4202 ChangeIconName(xw, buf);
4203 ChangeTitle(xw, buf);
4204 break;
4205
4206 case OSC_IconOnly: /* new icon name only */
4207 ChangeIconName(xw, buf);
4208 break;
4209
4210 case OSC_TitleOnly: /* new title only */
4211 ChangeTitle(xw, buf);
4212 break;
4213
4214 case OSC_X_Property: /* change X property */
4215 if (AllowWindowOps(xw, ewSetXprop))
4216 ChangeXprop(buf);
4217 break;
4218 #if OPT_ISO_COLORS
4219 case OSC_GetAnsiColors:
4220 ansi_colors = NUM_ANSI_COLORS;
4221 /* FALLTHRU */
4222 case OSC_SetAnsiColor:
4223 if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4224 xw->work.palette_changed = True;
4225 break;
4226 case OSC_ColorMode:
4227 /* FALLTHRU */
4228 case OSC_Reset(OSC_ColorMode):
4229 TRACE(("parse colorXXMode:%s\n", buf));
4230 while (*buf != '\0') {
4231 long which = 0;
4232 long value = 0;
4233 char *next;
4234 if (*buf == ';') {
4235 ++buf;
4236 } else {
4237 which = strtol(buf, &next, 10);
4238 if (!PartS2L(buf, next) || (which < 0))
4239 break;
4240 buf = next;
4241 if (*buf == ';')
4242 ++buf;
4243 }
4244 if (*buf == ';') {
4245 ++buf;
4246 } else {
4247 value = strtol(buf, &next, 10);
4248 if (!PartS2L(buf, next) || (value < 0))
4249 break;
4250 buf = next;
4251 if (*buf == ';')
4252 ++buf;
4253 }
4254 TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
4255 switch (which) {
4256 case 0:
4257 screen->colorBDMode = (value != 0);
4258 break;
4259 case 1:
4260 screen->colorULMode = (value != 0);
4261 break;
4262 case 2:
4263 screen->colorBLMode = (value != 0);
4264 break;
4265 case 3:
4266 screen->colorRVMode = (value != 0);
4267 break;
4268 #if OPT_WIDE_ATTRS
4269 case 4:
4270 screen->colorITMode = (value != 0);
4271 break;
4272 #endif
4273 default:
4274 TRACE(("...unknown colorXXMode\n"));
4275 break;
4276 }
4277 }
4278 break;
4279 case OSC_Reset(OSC_GetAnsiColors):
4280 ansi_colors = NUM_ANSI_COLORS;
4281 /* FALLTHRU */
4282 case OSC_Reset(OSC_SetAnsiColor):
4283 if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4284 xw->work.palette_changed = True;
4285 break;
4286 #endif
4287 case OSC_TEXT_FG:
4288 case OSC_TEXT_BG:
4289 case OSC_TEXT_CURSOR:
4290 case OSC_MOUSE_FG:
4291 case OSC_MOUSE_BG:
4292 #if OPT_HIGHLIGHT_COLOR
4293 case OSC_HIGHLIGHT_BG:
4294 case OSC_HIGHLIGHT_FG:
4295 #endif
4296 #if OPT_TEK4014
4297 case OSC_TEK_FG:
4298 case OSC_TEK_BG:
4299 case OSC_TEK_CURSOR:
4300 #endif
4301 if (xw->misc.dynamicColors) {
4302 ChangeColorsRequest(xw, mode, buf, final);
4303 }
4304 break;
4305 case OSC_Reset(OSC_TEXT_FG):
4306 case OSC_Reset(OSC_TEXT_BG):
4307 case OSC_Reset(OSC_TEXT_CURSOR):
4308 case OSC_Reset(OSC_MOUSE_FG):
4309 case OSC_Reset(OSC_MOUSE_BG):
4310 #if OPT_HIGHLIGHT_COLOR
4311 case OSC_Reset(OSC_HIGHLIGHT_BG):
4312 case OSC_Reset(OSC_HIGHLIGHT_FG):
4313 #endif
4314 #if OPT_TEK4014
4315 case OSC_Reset(OSC_TEK_FG):
4316 case OSC_Reset(OSC_TEK_BG):
4317 case OSC_Reset(OSC_TEK_CURSOR):
4318 #endif
4319 if (xw->misc.dynamicColors) {
4320 ResetColorsRequest(xw, mode);
4321 }
4322 break;
4323
4324 case OSC_SetupPointer:
4325 xtermSetupPointer(xw, buf);
4326 break;
4327
4328 #ifdef ALLOWLOGGING
4329 case OSC_NewLogFile:
4330 #ifdef ALLOWLOGFILECHANGES
4331 /*
4332 * Warning, enabling this feature allows people to overwrite
4333 * arbitrary files accessible to the person running xterm.
4334 */
4335 if (strcmp(buf, "?")) {
4336 char *bp;
4337 if ((bp = x_strdup(buf)) != NULL) {
4338 free(screen->logfile);
4339 screen->logfile = bp;
4340 break;
4341 }
4342 }
4343 #endif
4344 Bell(xw, XkbBI_Info, 0);
4345 Bell(xw, XkbBI_Info, 0);
4346 break;
4347 #endif /* ALLOWLOGGING */
4348
4349 case OSC_FontOps:
4350 #if OPT_SHIFT_FONTS
4351 if (*buf == '?') {
4352 QueryFontRequest(xw, buf, final);
4353 } else if (xw->misc.shift_fonts) {
4354 ChangeFontRequest(xw, buf);
4355 }
4356 #endif /* OPT_SHIFT_FONTS */
4357 break;
4358
4359 #if OPT_PASTE64
4360 case OSC_SelectionData:
4361 ManipulateSelectionData(xw, screen, buf, final);
4362 break;
4363 #endif
4364
4365 case OSC_AllowedOps: /* XTQALLOWED */
4366 report_allowed_ops(xw, final);
4367 break;
4368
4369 case OSC_DisallowedOps: /* XTQDISALLOWED */
4370 report_disallowed_ops(xw, buf, final);
4371 break;
4372
4373 case OSC_Unused_30:
4374 case OSC_Unused_31:
4375 case OSC_Unused_51:
4376 default:
4377 TRACE(("do_osc - unrecognized code\n"));
4378 break;
4379 }
4380 unparse_end(xw);
4381 }
4382
4383 /*
4384 * Parse one nibble of a hex byte from the OSC string. We have removed the
4385 * string-terminator (replacing it with a null), so the only other delimiter
4386 * that is expected is semicolon. Ignore other characters (Ray Neuman says
4387 * "real" terminals accept commas in the string definitions).
4388 */
4389 static int
4390 udk_value(const char **cp)
4391 {
4392 int result = -1;
4393
4394 for (;;) {
4395 int c;
4396
4397 if ((c = **cp) != '\0')
4398 *cp = *cp + 1;
4399 if (c == ';' || c == '\0')
4400 break;
4401 if ((result = x_hex2int(c)) >= 0)
4402 break;
4403 }
4404
4405 return result;
4406 }
4407
4408 void
4409 reset_decudk(XtermWidget xw)
4410 {
4411 int n;
4412 for (n = 0; n < MAX_UDK; n++) {
4413 FreeAndNull(xw->work.user_keys[n].str);
4414 xw->work.user_keys[n].len = 0;
4415 }
4416 }
4417
4418 /*
4419 * Parse the data for DECUDK (user-defined keys).
4420 */
4421 static void
4422 parse_decudk(XtermWidget xw, const char *cp)
4423 {
4424 while (*cp) {
4425 const char *base = cp;
4426 char *str = malloc(strlen(cp) + 3);
4427 unsigned key = 0;
4428 int len = 0;
4429
4430 if (str == NULL)
4431 break;
4432
4433 while (isdigit(CharOf(*cp)))
4434 key = (key * 10) + (unsigned) (*cp++ - '0');
4435
4436 if (*cp == '/') {
4437 int lo, hi;
4438
4439 cp++;
4440 while ((hi = udk_value(&cp)) >= 0
4441 && (lo = udk_value(&cp)) >= 0) {
4442 str[len++] = (char) ((hi << 4) | lo);
4443 }
4444 }
4445 if (len > 0 && key < MAX_UDK) {
4446 str[len] = '\0';
4447 free(xw->work.user_keys[key].str);
4448 xw->work.user_keys[key].str = str;
4449 xw->work.user_keys[key].len = len;
4450 TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4451 } else {
4452 free(str);
4453 }
4454 if (*cp == ';')
4455 cp++;
4456 if (cp == base) /* badly-formed sequence - bail out */
4457 break;
4458 }
4459 }
4460
4461 /*
4462 * Parse numeric parameters. Normally we use a state machine to simplify
4463 * interspersing with control characters, but have the string already.
4464 */
4465 static void
4466 parse_ansi_params(ANSI *params, const char **string)
4467 {
4468 const char *cp = *string;
4469 ParmType nparam = 0;
4470 int last_empty = 1;
4471
4472 memset(params, 0, sizeof(*params));
4473 while (*cp != '\0') {
4474 Char ch = CharOf(*cp++);
4475
4476 if (isdigit(ch)) {
4477 last_empty = 0;
4478 if (nparam < NPARAM) {
4479 params->a_param[nparam] =
4480 (ParmType) ((params->a_param[nparam] * 10)
4481 + (ch - '0'));
4482 }
4483 } else if (ch == ';') {
4484 last_empty = 1;
4485 nparam++;
4486 } else if (ch < 32) {
4487 /* EMPTY */ ;
4488 } else {
4489 /* should be 0x30 to 0x7e */
4490 params->a_final = ch;
4491 break;
4492 }
4493 }
4494
4495 *string = cp;
4496 if (!last_empty)
4497 nparam++;
4498 if (nparam > NPARAM)
4499 params->a_nparam = NPARAM;
4500 else
4501 params->a_nparam = nparam;
4502 }
4503
4504 #if OPT_TRACE
4505 #define SOFT_WIDE 10
4506 #define SOFT_HIGH 20
4507
4508 static void
4509 parse_decdld(ANSI *params, const char *string)
4510 {
4511 char DscsName[8];
4512 int len;
4513 int Pfn = params->a_param[0];
4514 int Pcn = params->a_param[1];
4515 int Pe = params->a_param[2];
4516 int Pcmw = params->a_param[3];
4517 int Pw = params->a_param[4];
4518 int Pt = params->a_param[5];
4519 int Pcmh = params->a_param[6];
4520 int Pcss = params->a_param[7];
4521
4522 int start_char = Pcn + 0x20;
4523 int char_wide = ((Pcmw == 0)
4524 ? (Pcss ? 6 : 10)
4525 : (Pcmw > 4
4526 ? Pcmw
4527 : (Pcmw + 3)));
4528 int char_high = ((Pcmh == 0)
4529 ? ((Pcmw >= 2 && Pcmw <= 4)
4530 ? 10
4531 : 20)
4532 : Pcmh);
4533 Char ch;
4534 Char bits[SOFT_HIGH][SOFT_WIDE];
4535 Bool first = True;
4536 Bool prior = False;
4537 int row = 0, col = 0;
4538
4539 TRACE(("Parsing DECDLD\n"));
4540 TRACE((" font number %d\n", Pfn));
4541 TRACE((" starting char %d\n", Pcn));
4542 TRACE((" erase control %d\n", Pe));
4543 TRACE((" char-width %d\n", Pcmw));
4544 TRACE((" font-width %d\n", Pw));
4545 TRACE((" text/full %d\n", Pt));
4546 TRACE((" char-height %d\n", Pcmh));
4547 TRACE((" charset-size %d\n", Pcss));
4548
4549 if (Pfn > 1
4550 || Pcn > 95
4551 || Pe > 2
4552 || Pcmw > 10
4553 || Pcmw == 1
4554 || Pt > 2
4555 || Pcmh > 20
4556 || Pcss > 1
4557 || char_wide > SOFT_WIDE
4558 || char_high > SOFT_HIGH) {
4559 TRACE(("DECDLD illegal parameter\n"));
4560 return;
4561 }
4562
4563 len = 0;
4564 while (*string != '\0') {
4565 ch = CharOf(*string++);
4566 if (ch >= ANSI_SPA && ch <= 0x2f) {
4567 if (len < 2)
4568 DscsName[len++] = (char) ch;
4569 } else if (ch >= 0x30 && ch <= 0x7e) {
4570 DscsName[len++] = (char) ch;
4571 break;
4572 }
4573 }
4574 DscsName[len] = 0;
4575 TRACE((" Dscs name '%s'\n", DscsName));
4576
4577 TRACE((" character matrix %dx%d\n", char_high, char_wide));
4578 while (*string != '\0') {
4579 if (first) {
4580 TRACE(("Char %d:\n", start_char));
4581 if (prior) {
4582 for (row = 0; row < char_high; ++row) {
4583 TRACE(("%.*s\n", char_wide, bits[row]));
4584 }
4585 }
4586 prior = False;
4587 first = False;
4588 for (row = 0; row < char_high; ++row) {
4589 for (col = 0; col < char_wide; ++col) {
4590 bits[row][col] = '.';
4591 }
4592 }
4593 row = col = 0;
4594 }
4595 ch = CharOf(*string++);
4596 if (ch >= 0x3f && ch <= 0x7e) {
4597 int n;
4598
4599 ch = CharOf(ch - 0x3f);
4600 for (n = 0; n < 6; ++n) {
4601 bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4602 }
4603 col += 1;
4604 prior = True;
4605 } else if (ch == '/') {
4606 row += 6;
4607 col = 0;
4608 } else if (ch == ';') {
4609 first = True;
4610 ++start_char;
4611 }
4612 }
4613 }
4614 #else
4615 #define parse_decdld(p,q) /* nothing */
4616 #endif
4617
4618 #if OPT_DEC_RECTOPS
4619 static const char *
4620 skip_params(const char *cp)
4621 {
4622 while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4623 ++cp;
4624 return cp;
4625 }
4626
4627 static int
4628 parse_int_param(const char **cp)
4629 {
4630 int result = 0;
4631 const char *s = *cp;
4632 while (*s != '\0') {
4633 if (*s == ';') {
4634 ++s;
4635 break;
4636 } else if (*s >= '0' && *s <= '9') {
4637 result = (result * 10) + (*s++ - '0');
4638 } else {
4639 s += strlen(s);
4640 }
4641 }
4642 TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
4643 *cp = s;
4644 return result;
4645 }
4646
4647 static int
4648 parse_chr_param(const char **cp)
4649 {
4650 int result = 0;
4651 const char *s = *cp;
4652 if (*s != '\0') {
4653 if ((result = CharOf(*s++)) != 0) {
4654 if (*s == ';') {
4655 ++s;
4656 } else if (*s != '\0') {
4657 result = 0;
4658 }
4659 }
4660 }
4661 TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
4662 *cp = s;
4663 return result;
4664 }
4665
4666 static void
4667 restore_DECCIR(XtermWidget xw, const char *cp)
4668 {
4669 TScreen *screen = TScreenOf(xw);
4670 int value;
4671
4672 /* row */
4673 if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4674 return;
4675 screen->cur_row = (value - 1);
4676
4677 /* column */
4678 if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4679 return;
4680 screen->cur_col = (value - 1);
4681
4682 /* page */
4683 if (parse_int_param(&cp) != 1)
4684 return;
4685
4686 /* rendition */
4687 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4688 return;
4689 UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4690 xw->flags |= (value & 8) ? INVERSE : 0;
4691 xw->flags |= (value & 4) ? BLINK : 0;
4692 xw->flags |= (value & 2) ? UNDERLINE : 0;
4693 xw->flags |= (value & 1) ? BOLD : 0;
4694
4695 /* attributes */
4696 if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4697 return;
4698 screen->protected_mode &= ~DEC_PROTECT;
4699 screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4700
4701 /* flags */
4702 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4703 return;
4704 screen->do_wrap = (value & 8) ? True : False;
4705 screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4706 UIntClr(xw->flags, ORIGIN);
4707 xw->flags |= (value & 1) ? ORIGIN : 0;
4708
4709 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4710 return;
4711 screen->curgl = (Char) value;
4712
4713 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4714 return;
4715 screen->curgr = (Char) value;
4716
4717 /* character-set size */
4718 if (parse_chr_param(&cp) != 0x4f) /* works for xterm */
4719 return;
4720
4721 /* SCS designators */
4722 for (value = 0; value < NUM_GSETS; ++value) {
4723 if (*cp == '%') {
4724 xtermDecodeSCS(xw, value, 0, '%', *++cp);
4725 } else if (*cp != '\0') {
4726 xtermDecodeSCS(xw, value, 0, '\0', *cp);
4727 } else {
4728 return;
4729 }
4730 cp++;
4731 }
4732
4733 TRACE(("...done DECCIR\n"));
4734 }
4735
4736 static void
4737 restore_DECTABSR(XtermWidget xw, const char *cp)
4738 {
4739 int stop = 0;
4740 Bool fail = False;
4741
4742 TabZonk(xw->tabs);
4743 while (*cp != '\0' && !fail) {
4744 if ((*cp) >= '0' && (*cp) <= '9') {
4745 stop = (stop * 10) + ((*cp) - '0');
4746 } else if (*cp == '/') {
4747 --stop;
4748 if (OkTAB(stop)) {
4749 TabSet(xw->tabs, stop);
4750 stop = 0;
4751 } else {
4752 fail = True;
4753 }
4754 } else {
4755 fail = True;
4756 }
4757 ++cp;
4758 }
4759 --stop;
4760 if (OkTAB(stop))
4761 TabSet(xw->tabs, stop);
4762
4763 TRACE(("...done DECTABSR\n"));
4764 }
4765 #endif
4766
4767 void
4768 do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4769 {
4770 TScreen *screen = TScreenOf(xw);
4771 char reply[BUFSIZ];
4772 const char *cp = (const char *) dcsbuf;
4773 Bool okay;
4774 ANSI params;
4775 #if OPT_DEC_RECTOPS
4776 char psarg = '0';
4777 #endif
4778
4779 TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4780
4781 if (dcslen != strlen(cp))
4782 /* shouldn't have nulls in the string */
4783 return;
4784
4785 switch (*cp) { /* intermediate character, or parameter */
4786 case '$': /* DECRQSS */
4787 okay = True;
4788
4789 cp++;
4790 if (*cp == 'q') {
4791 *reply = '\0';
4792 cp++;
4793 if (!strcmp(cp, "\"q")) { /* DECSCA */
4794 TRACE(("DECRQSS -> DECSCA\n"));
4795 sprintf(reply, "%d%s",
4796 (screen->protected_mode == DEC_PROTECT)
4797 && (xw->flags & PROTECTED) ? 1 : 0,
4798 cp);
4799 } else if (!strcmp(cp, "\"p")) { /* DECSCL */
4800 if (screen->vtXX_level < 2) {
4801 /* actually none of DECRQSS is valid for vt100's */
4802 break;
4803 }
4804 TRACE(("DECRQSS -> DECSCL\n"));
4805 sprintf(reply, "%d%s%s",
4806 (screen->vtXX_level ?
4807 screen->vtXX_level : 1) + 60,
4808 (screen->vtXX_level >= 2)
4809 ? (screen->control_eight_bits
4810 ? ";0" : ";1")
4811 : "",
4812 cp);
4813 } else if (!strcmp(cp, "r")) { /* DECSTBM */
4814 TRACE(("DECRQSS -> DECSTBM\n"));
4815 sprintf(reply, "%d;%dr",
4816 screen->top_marg + 1,
4817 screen->bot_marg + 1);
4818 } else if (!strcmp(cp, "s")) { /* DECSLRM */
4819 if (screen->vtXX_level >= 4) { /* VT420 */
4820 TRACE(("DECRQSS -> DECSLRM\n"));
4821 sprintf(reply, "%d;%ds",
4822 screen->lft_marg + 1,
4823 screen->rgt_marg + 1);
4824 } else {
4825 okay = False;
4826 }
4827 } else if (!strcmp(cp, "m")) { /* SGR */
4828 TRACE(("DECRQSS -> SGR\n"));
4829 xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4830 strcat(reply, "m");
4831 } else if (!strcmp(cp, " q")) { /* DECSCUSR */
4832 int code = STEADY_BLOCK;
4833 if (isCursorUnderline(screen))
4834 code = STEADY_UNDERLINE;
4835 else if (isCursorBar(screen))
4836 code = STEADY_BAR;
4837 #if OPT_BLINK_CURS
4838 if (screen->cursor_blink_esc != 0)
4839 code -= 1;
4840 #endif
4841 TRACE(("reply DECSCUSR\n"));
4842 sprintf(reply, "%d%s", code, cp);
4843 } else if (!strcmp(cp, "t")) { /* DECSLPP */
4844 sprintf(reply, "%d%s",
4845 ((screen->max_row > 24) ? screen->max_row : 24),
4846 cp);
4847 TRACE(("reply DECSLPP\n"));
4848 } else if (!strcmp(cp, "$|")) { /* DECSCPP */
4849 TRACE(("reply DECSCPP\n"));
4850 sprintf(reply, "%d%s",
4851 ((xw->flags & IN132COLUMNS) ? 132 : 80),
4852 cp);
4853 } else
4854 #if OPT_STATUS_LINE
4855 if (!strcmp(cp, "$}")) { /* DECSASD */
4856 TRACE(("reply DECSASD\n"));
4857 sprintf(reply, "%d%s",
4858 screen->status_active,
4859 cp);
4860 } else if (!strcmp(cp, "$~")) { /* DECSSDT */
4861 TRACE(("reply DECSASD\n"));
4862 sprintf(reply, "%d%s",
4863 screen->status_type,
4864 cp);
4865 } else
4866 #endif
4867 if (!strcmp(cp, "*|")) { /* DECSNLS */
4868 TRACE(("reply DECSNLS\n"));
4869 sprintf(reply, "%d%s",
4870 screen->max_row + 1,
4871 cp);
4872 } else {
4873 okay = False;
4874 }
4875
4876 unparseputc1(xw, ANSI_DCS);
4877 unparseputc(xw, okay ? '1' : '0');
4878 unparseputc(xw, '$');
4879 unparseputc(xw, 'r');
4880 cp = reply;
4881 unparseputs(xw, cp);
4882 unparseputc1(xw, ANSI_ST);
4883 } else {
4884 unparseputc(xw, ANSI_CAN);
4885 }
4886 break;
4887 case '+':
4888 cp++;
4889 switch (*cp) {
4890 #if OPT_TCAP_QUERY
4891 case 'p': /* XTSETTCAP */
4892 if (AllowTcapOps(xw, etSetTcap)) {
4893 set_termcap(xw, cp + 1);
4894 }
4895 break;
4896 case 'q': /* XTGETTCAP */
4897 if (AllowTcapOps(xw, etGetTcap)) {
4898 Bool fkey;
4899 unsigned state;
4900 int code;
4901 const char *tmp;
4902 const char *parsed = ++cp;
4903
4904 code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4905
4906 unparseputc1(xw, ANSI_DCS);
4907
4908 unparseputc(xw, code >= 0 ? '1' : '0');
4909
4910 unparseputc(xw, '+');
4911 unparseputc(xw, 'r');
4912
4913 while (*cp != 0 && (code >= -1)) {
4914 if (cp == parsed)
4915 break; /* no data found, error */
4916
4917 for (tmp = cp; tmp != parsed; ++tmp)
4918 unparseputc(xw, *tmp);
4919
4920 if (code >= 0) {
4921 unparseputc(xw, '=');
4922 screen->tc_query_code = code;
4923 screen->tc_query_fkey = fkey;
4924 #if OPT_ISO_COLORS
4925 /* XK_COLORS is a fake code for the "Co" entry (maximum
4926 * number of colors) */
4927 if (code == XK_COLORS) {
4928 unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
4929 } else
4930 #if OPT_DIRECT_COLOR
4931 if (code == XK_RGB) {
4932 if (TScreenOf(xw)->direct_color && xw->has_rgb) {
4933 if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
4934 xw->rgb_widths[1] == xw->rgb_widths[2]) {
4935 unparseputn(xw, xw->rgb_widths[0]);
4936 } else {
4937 char temp[1024];
4938 sprintf(temp, "%d/%d/%d",
4939 xw->rgb_widths[0],
4940 xw->rgb_widths[1],
4941 xw->rgb_widths[2]);
4942 unparseputs(xw, temp);
4943 }
4944 } else {
4945 unparseputs(xw, "-1");
4946 }
4947 } else
4948 #endif
4949 #endif
4950 if (code == XK_TCAPNAME) {
4951 unparseputs(xw, resource.term_name);
4952 } else {
4953 XKeyEvent event;
4954 memset(&event, 0, sizeof(event));
4955 event.state = state;
4956 Input(xw, &event, False);
4957 }
4958 screen->tc_query_code = -1;
4959 } else {
4960 break; /* no match found, error */
4961 }
4962
4963 cp = parsed;
4964 if (*parsed == ';') {
4965 unparseputc(xw, *parsed++);
4966 cp = parsed;
4967 code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4968 }
4969 }
4970 unparseputc1(xw, ANSI_ST);
4971 }
4972 break;
4973 #endif
4974 #if OPT_XRES_QUERY
4975 case 'Q': /* XTGETXRES */
4976 ++cp;
4977 if (AllowXResOps(xw)) {
4978 Boolean first = True;
4979 okay = True;
4980 while (*cp != '\0' && okay) {
4981 const char *parsed = 0;
4982 const char *tmp;
4983 char *name = x_decode_hex(cp, &parsed);
4984 char *value;
4985 char *result;
4986 if (cp == parsed || name == NULL) {
4987 free(name);
4988 break; /* no data found, error */
4989 }
4990 if ((cp - parsed) > 1024) {
4991 break; /* ignore improbable resource */
4992 }
4993 TRACE(("query-feature '%s'\n", name));
4994 if ((value = vt100ResourceToString(xw, name)) != 0) {
4995 okay = True; /* valid */
4996 } else {
4997 okay = False; /* invalid */
4998 }
4999 if (first) {
5000 unparseputc1(xw, ANSI_DCS);
5001 unparseputc(xw, okay ? '1' : '0');
5002 unparseputc(xw, '+');
5003 unparseputc(xw, 'R');
5004 first = False;
5005 }
5006
5007 for (tmp = cp; tmp != parsed; ++tmp)
5008 unparseputc(xw, *tmp);
5009
5010 if (value != 0) {
5011 unparseputc1(xw, '=');
5012 result = x_encode_hex(value);
5013 unparseputs(xw, result);
5014 } else {
5015 result = NULL;
5016 }
5017
5018 free(name);
5019 free(value);
5020 free(result);
5021
5022 cp = parsed;
5023 if (*parsed == ';') {
5024 unparseputc(xw, *parsed++);
5025 cp = parsed;
5026 }
5027 }
5028 if (!first)
5029 unparseputc1(xw, ANSI_ST);
5030 }
5031 break;
5032 #endif
5033 }
5034 break;
5035 #if OPT_DEC_RECTOPS
5036 case '1':
5037 /* FALLTHRU */
5038 case '2':
5039 if (*skip_params(cp) == '$') {
5040 psarg = *cp++;
5041 if ((*cp++ == '$')
5042 && (*cp++ == 't')
5043 && (screen->vtXX_level >= 3)) {
5044 switch (psarg) {
5045 case '1':
5046 TRACE(("DECRSPS (DECCIR)\n"));
5047 restore_DECCIR(xw, cp);
5048 break;
5049 case '2':
5050 TRACE(("DECRSPS (DECTABSR)\n"));
5051 restore_DECTABSR(xw, cp);
5052 break;
5053 }
5054 }
5055 break;
5056 }
5057 #endif
5058 /* FALLTHRU */
5059 default:
5060 if (optRegisGraphics(screen) ||
5061 optSixelGraphics(screen) ||
5062 screen->vtXX_level >= 2) { /* VT220 */
5063 parse_ansi_params(¶ms, &cp);
5064 switch (params.a_final) {
5065 case 'p': /* ReGIS */
5066 #if OPT_REGIS_GRAPHICS
5067 if (optRegisGraphics(screen)) {
5068 parse_regis(xw, ¶ms, cp);
5069 }
5070 #else
5071 TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
5072 #endif
5073 break;
5074 case 'q': /* sixel */
5075 #if OPT_SIXEL_GRAPHICS
5076 if (optSixelGraphics(screen)) {
5077 (void) parse_sixel(xw, ¶ms, cp);
5078 }
5079 #else
5080 TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
5081 #endif
5082 break;
5083 case '|': /* DECUDK */
5084 if (screen->vtXX_level >= 2) { /* VT220 */
5085 if (params.a_param[0] == 0)
5086 reset_decudk(xw);
5087 parse_decudk(xw, cp);
5088 }
5089 break;
5090 case L_CURL: /* DECDLD */
5091 if (screen->vtXX_level >= 2) { /* VT220 */
5092 parse_decdld(¶ms, cp);
5093 }
5094 break;
5095 }
5096 }
5097 break;
5098 }
5099 unparse_end(xw);
5100 }
5101
5102 #if OPT_DEC_RECTOPS
5103 enum {
5104 mdUnknown = 0,
5105 mdMaybeSet = 1,
5106 mdMaybeReset = 2,
5107 mdAlwaysSet = 3,
5108 mdAlwaysReset = 4
5109 };
5110
5111 #define MdBool(bool) ((bool) ? mdMaybeSet : mdMaybeReset)
5112 #define MdFlag(mode,flag) MdBool((mode) & (flag))
5113
5114 /*
5115 * Reply is the same format as the query, with pair of mode/value:
5116 * 0 - not recognized
5117 * 1 - set
5118 * 2 - reset
5119 * 3 - permanently set
5120 * 4 - permanently reset
5121 * Only one mode can be reported at a time.
5122 */
5123 void
5124 do_ansi_rqm(XtermWidget xw, int nparams, int *params)
5125 {
5126 ANSI reply;
5127 int count = 0;
5128
5129 TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5130 memset(&reply, 0, sizeof(reply));
5131
5132 if (nparams >= 1) {
5133 int result = mdUnknown;
5134
5135 /* DECRQM can only ask about one mode at a time */
5136 switch (params[0]) {
5137 case 1: /* GATM */
5138 result = mdAlwaysReset;
5139 break;
5140 case 2:
5141 result = MdFlag(xw->keyboard.flags, MODE_KAM);
5142 break;
5143 case 3: /* CRM */
5144 result = mdMaybeReset;
5145 break;
5146 case 4:
5147 result = MdFlag(xw->flags, INSERT);
5148 break;
5149 case 5: /* SRTM */
5150 case 7: /* VEM */
5151 case 10: /* HEM */
5152 case 11: /* PUM */
5153 result = mdAlwaysReset;
5154 break;
5155 case 12:
5156 result = MdFlag(xw->keyboard.flags, MODE_SRM);
5157 break;
5158 case 13: /* FEAM */
5159 case 14: /* FETM */
5160 case 15: /* MATM */
5161 case 16: /* TTM */
5162 case 17: /* SATM */
5163 case 18: /* TSM */
5164 case 19: /* EBM */
5165 result = mdAlwaysReset;
5166 break;
5167 case 20:
5168 result = MdFlag(xw->flags, LINEFEED);
5169 break;
5170 }
5171 reply.a_param[count++] = (ParmType) params[0];
5172 reply.a_param[count++] = (ParmType) result;
5173 }
5174 reply.a_type = ANSI_CSI;
5175 reply.a_nparam = (ParmType) count;
5176 reply.a_inters = '$';
5177 reply.a_final = 'y';
5178 unparseseq(xw, &reply);
5179 }
5180
5181 void
5182 do_dec_rqm(XtermWidget xw, int nparams, int *params)
5183 {
5184 ANSI reply;
5185 int count = 0;
5186
5187 TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5188 memset(&reply, 0, sizeof(reply));
5189
5190 if (nparams >= 1) {
5191 TScreen *screen = TScreenOf(xw);
5192 int result = mdUnknown;
5193
5194 /* DECRQM can only ask about one mode at a time */
5195 switch ((DECSET_codes) params[0]) {
5196 case srm_DECCKM:
5197 result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5198 break;
5199 case srm_DECANM: /* ANSI/VT52 mode */
5200 #if OPT_VT52_MODE
5201 result = MdBool(screen->vtXX_level >= 1);
5202 #else
5203 result = mdMaybeSet;
5204 #endif
5205 break;
5206 case srm_DECCOLM:
5207 result = MdFlag(xw->flags, IN132COLUMNS);
5208 break;
5209 case srm_DECSCLM: /* (slow scroll) */
5210 result = MdFlag(xw->flags, SMOOTHSCROLL);
5211 break;
5212 case srm_DECSCNM:
5213 result = MdFlag(xw->flags, REVERSE_VIDEO);
5214 break;
5215 case srm_DECOM:
5216 result = MdFlag(xw->flags, ORIGIN);
5217 break;
5218 case srm_DECAWM:
5219 result = MdFlag(xw->flags, WRAPAROUND);
5220 break;
5221 case srm_DECARM:
5222 result = mdAlwaysReset;
5223 break;
5224 case srm_X10_MOUSE: /* X10 mouse */
5225 result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5226 break;
5227 #if OPT_TOOLBAR
5228 case srm_RXVT_TOOLBAR:
5229 result = MdBool(resource.toolBar);
5230 break;
5231 #endif
5232 #if OPT_BLINK_CURS
5233 case srm_ATT610_BLINK: /* AT&T 610: Start/stop blinking cursor */
5234 result = MdBool(screen->cursor_blink_esc);
5235 break;
5236 case srm_CURSOR_BLINK_OPS:
5237 switch (screen->cursor_blink) {
5238 case cbTrue:
5239 result = mdMaybeSet;
5240 break;
5241 case cbFalse:
5242 result = mdMaybeReset;
5243 break;
5244 case cbAlways:
5245 result = mdAlwaysSet;
5246 break;
5247 case cbLAST:
5248 /* FALLTHRU */
5249 case cbNever:
5250 result = mdAlwaysReset;
5251 break;
5252 }
5253 break;
5254 case srm_XOR_CURSOR_BLINKS:
5255 result = (screen->cursor_blink_xor
5256 ? mdAlwaysSet
5257 : mdAlwaysReset);
5258 break;
5259 #endif
5260 case srm_DECPFF: /* print form feed */
5261 result = MdBool(PrinterOf(screen).printer_formfeed);
5262 break;
5263 case srm_DECPEX: /* print extent */
5264 result = MdBool(PrinterOf(screen).printer_extent);
5265 break;
5266 case srm_DECTCEM: /* Show/hide cursor (VT200) */
5267 result = MdBool(screen->cursor_set);
5268 break;
5269 case srm_RXVT_SCROLLBAR:
5270 result = MdBool(screen->fullVwin.sb_info.width != OFF);
5271 break;
5272 #if OPT_SHIFT_FONTS
5273 case srm_RXVT_FONTSIZE:
5274 result = MdBool(xw->misc.shift_fonts);
5275 break;
5276 #endif
5277 #if OPT_TEK4014
5278 case srm_DECTEK:
5279 result = MdBool(TEK4014_ACTIVE(xw));
5280 break;
5281 #endif
5282 case srm_132COLS:
5283 result = MdBool(screen->c132);
5284 break;
5285 case srm_CURSES_HACK:
5286 result = MdBool(screen->curses);
5287 break;
5288 case srm_DECNRCM: /* national charset (VT220) */
5289 if (screen->vtXX_level >= 2) {
5290 result = MdFlag(xw->flags, NATIONAL);
5291 } else {
5292 result = 0;
5293 }
5294 break;
5295 case srm_MARGIN_BELL: /* margin bell */
5296 result = MdBool(screen->marginbell);
5297 break;
5298 #if OPT_PRINT_GRAPHICS
5299 case srm_DECGEPM: /* Graphics Expanded Print Mode */
5300 result = MdBool(screen->graphics_expanded_print_mode);
5301 break;
5302 #endif
5303 case srm_REVERSEWRAP: /* reverse wraparound */
5304 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5305 result = MdFlag(xw->flags, REVERSEWRAP);
5306 break;
5307 #if defined(ALLOWLOGGING)
5308 case srm_ALLOWLOGGING: /* logging */
5309 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5310 #if defined(ALLOWLOGFILEONOFF)
5311 result = MdBool(screen->logging);
5312 #else
5313 result = ((MdBool(screen->logging) == mdMaybeSet)
5314 ? mdAlwaysSet
5315 : mdAlwaysReset);
5316 #endif
5317 break;
5318 #elif OPT_PRINT_GRAPHICS
5319 case srm_DECGPBM: /* Graphics Print Background Mode */
5320 result = MdBool(screen->graphics_print_background_mode);
5321 break;
5322 #endif
5323 case srm_OPT_ALTBUF_CURSOR: /* alternate buffer & cursor */
5324 /* FALLTHRU */
5325 case srm_OPT_ALTBUF:
5326 result = MdBool(screen->whichBuf);
5327 break;
5328 case srm_ALTBUF:
5329 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5330 result = MdBool(screen->whichBuf);
5331 break;
5332 case srm_DECNKM:
5333 result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5334 break;
5335 case srm_DECBKM:
5336 result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5337 break;
5338 case srm_DECLRMM:
5339 if (screen->vtXX_level >= 4) { /* VT420 */
5340 result = MdFlag(xw->flags, LEFT_RIGHT);
5341 } else {
5342 result = 0;
5343 }
5344 break;
5345 #if OPT_SIXEL_GRAPHICS
5346 case srm_DECSDM:
5347 result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5348 break;
5349 #endif
5350 case srm_DECNCSM:
5351 if (screen->vtXX_level >= 5) { /* VT510 */
5352 result = MdFlag(xw->flags, NOCLEAR_COLM);
5353 } else {
5354 result = 0;
5355 }
5356 break;
5357 case srm_VT200_MOUSE: /* xterm bogus sequence */
5358 result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5359 break;
5360 case srm_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */
5361 result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5362 break;
5363 case srm_BTN_EVENT_MOUSE:
5364 result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5365 break;
5366 case srm_ANY_EVENT_MOUSE:
5367 result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5368 break;
5369 #if OPT_FOCUS_EVENT
5370 case srm_FOCUS_EVENT_MOUSE:
5371 result = MdBool(screen->send_focus_pos);
5372 break;
5373 #endif
5374 case srm_EXT_MODE_MOUSE:
5375 /* FALLTHRU */
5376 case srm_SGR_EXT_MODE_MOUSE:
5377 /* FALLTHRU */
5378 case srm_URXVT_EXT_MODE_MOUSE:
5379 /* FALLTHRU */
5380 case srm_PIXEL_POSITION_MOUSE:
5381 result = MdBool(screen->extend_coords == params[0]);
5382 break;
5383 case srm_ALTERNATE_SCROLL:
5384 result = MdBool(screen->alternateScroll);
5385 break;
5386 case srm_RXVT_SCROLL_TTY_OUTPUT:
5387 result = MdBool(screen->scrollttyoutput);
5388 break;
5389 case srm_RXVT_SCROLL_TTY_KEYPRESS:
5390 result = MdBool(screen->scrollkey);
5391 break;
5392 case srm_EIGHT_BIT_META:
5393 result = MdBool(screen->eight_bit_meta);
5394 break;
5395 #if OPT_NUM_LOCK
5396 case srm_REAL_NUMLOCK:
5397 result = MdBool(xw->misc.real_NumLock);
5398 break;
5399 case srm_META_SENDS_ESC:
5400 result = MdBool(screen->meta_sends_esc);
5401 break;
5402 #endif
5403 case srm_DELETE_IS_DEL:
5404 result = MdBool(xtermDeleteIsDEL(xw));
5405 break;
5406 #if OPT_NUM_LOCK
5407 case srm_ALT_SENDS_ESC:
5408 result = MdBool(screen->alt_sends_esc);
5409 break;
5410 #endif
5411 case srm_KEEP_SELECTION:
5412 result = MdBool(screen->keepSelection);
5413 break;
5414 case srm_SELECT_TO_CLIPBOARD:
5415 result = MdBool(screen->selectToClipboard);
5416 break;
5417 case srm_BELL_IS_URGENT:
5418 result = MdBool(screen->bellIsUrgent);
5419 break;
5420 case srm_POP_ON_BELL:
5421 result = MdBool(screen->poponbell);
5422 break;
5423 case srm_KEEP_CLIPBOARD:
5424 result = MdBool(screen->keepClipboard);
5425 break;
5426 case srm_ALLOW_ALTBUF:
5427 result = MdBool(xw->misc.titeInhibit);
5428 break;
5429 case srm_SAVE_CURSOR:
5430 result = MdBool(screen->sc[screen->whichBuf].saved);
5431 break;
5432 #if OPT_TCAP_FKEYS
5433 case srm_TCAP_FKEYS:
5434 result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5435 break;
5436 #endif
5437 #if OPT_SUN_FUNC_KEYS
5438 case srm_SUN_FKEYS:
5439 result = MdBool(xw->keyboard.type == keyboardIsSun);
5440 break;
5441 #endif
5442 #if OPT_HP_FUNC_KEYS
5443 case srm_HP_FKEYS:
5444 result = MdBool(xw->keyboard.type == keyboardIsHP);
5445 break;
5446 #endif
5447 #if OPT_SCO_FUNC_KEYS
5448 case srm_SCO_FKEYS:
5449 result = MdBool(xw->keyboard.type == keyboardIsSCO);
5450 break;
5451 #endif
5452 case srm_LEGACY_FKEYS:
5453 result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5454 break;
5455 #if OPT_SUNPC_KBD
5456 case srm_VT220_FKEYS:
5457 result = MdBool(xw->keyboard.type == keyboardIsVT220);
5458 break;
5459 #endif
5460 #if OPT_PASTE64 || OPT_READLINE
5461 case srm_PASTE_IN_BRACKET:
5462 result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5463 break;
5464 #endif
5465 #if OPT_READLINE
5466 case srm_BUTTON1_MOVE_POINT:
5467 result = MdBool(SCREEN_FLAG(screen, click1_moves));
5468 break;
5469 case srm_BUTTON2_MOVE_POINT:
5470 result = MdBool(SCREEN_FLAG(screen, paste_moves));
5471 break;
5472 case srm_DBUTTON3_DELETE:
5473 result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5474 break;
5475 case srm_PASTE_QUOTE:
5476 result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5477 break;
5478 case srm_PASTE_LITERAL_NL:
5479 result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));