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