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