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