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