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