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