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