misc.c revision 0d92cbfd
1/* $XTermId: misc.c,v 1.391 2008/12/30 17:44:50 tom Exp $ */ 2 3/* 4 * 5 * Copyright 1999-2007,2008 by Thomas E. Dickey 6 * 7 * All Rights Reserved 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the 11 * "Software"), to deal in the Software without restriction, including 12 * without limitation the rights to use, copy, modify, merge, publish, 13 * distribute, sublicense, and/or sell copies of the Software, and to 14 * permit persons to whom the Software is furnished to do so, subject to 15 * the following conditions: 16 * 17 * The above copyright notice and this permission notice shall be included 18 * in all copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 24 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 * Except as contained in this notice, the name(s) of the above copyright 29 * holders shall not be used in advertising or otherwise to promote the 30 * sale, use or other dealings in this Software without prior written 31 * authorization. 32 * 33 * 34 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 35 * 36 * All Rights Reserved 37 * 38 * Permission to use, copy, modify, and distribute this software and its 39 * documentation for any purpose and without fee is hereby granted, 40 * provided that the above copyright notice appear in all copies and that 41 * both that copyright notice and this permission notice appear in 42 * supporting documentation, and that the name of Digital Equipment 43 * Corporation not be used in advertising or publicity pertaining to 44 * distribution of the software without specific, written prior permission. 45 * 46 * 47 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 48 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 49 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 50 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 51 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 52 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 53 * SOFTWARE. 54 */ 55 56#include <version.h> 57#include <xterm.h> 58 59#include <sys/stat.h> 60#include <stdio.h> 61#include <signal.h> 62#include <ctype.h> 63#include <pwd.h> 64#include <sys/wait.h> 65 66#include <X11/keysym.h> 67#include <X11/Xatom.h> 68#include <X11/cursorfont.h> 69#include <X11/Xlocale.h> 70 71#include <X11/Xmu/Error.h> 72#include <X11/Xmu/SysUtil.h> 73#include <X11/Xmu/WinUtil.h> 74#include <X11/Xmu/Xmu.h> 75#if HAVE_X11_SUNKEYSYM_H 76#include <X11/Sunkeysym.h> 77#endif 78 79#ifdef HAVE_LANGINFO_CODESET 80#include <langinfo.h> 81#endif 82 83#include <xutf8.h> 84 85#include <data.h> 86#include <error.h> 87#include <menu.h> 88#include <fontutils.h> 89#include <xcharmouse.h> 90#include <xstrings.h> 91#include <xtermcap.h> 92#include <VTparse.h> 93 94#include <assert.h> 95 96#if (XtSpecificationRelease < 6) 97#ifndef X_GETTIMEOFDAY 98#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0) 99#endif 100#endif 101 102#ifdef VMS 103#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT" 104#ifdef ALLOWLOGFILEEXEC 105#undef ALLOWLOGFILEEXEC 106#endif 107#endif /* VMS */ 108 109#if OPT_TEK4014 110#define OUR_EVENT(event,Type) \ 111 (event.type == Type && \ 112 (event.xcrossing.window == XtWindow(XtParent(xw)) || \ 113 (tekWidget && \ 114 event.xcrossing.window == XtWindow(XtParent(tekWidget))))) 115#else 116#define OUR_EVENT(event,Type) \ 117 (event.type == Type && \ 118 (event.xcrossing.window == XtWindow(XtParent(xw)))) 119#endif 120 121static Cursor make_hidden_cursor(XtermWidget); 122 123#if OPT_EXEC_XTERM 124/* Like readlink(2), but returns a malloc()ed buffer, or NULL on 125 error; adapted from libc docs */ 126static char * 127Readlink(const char *filename) 128{ 129 char *buf = NULL; 130 unsigned size = 100; 131 int n; 132 133 for (;;) { 134 buf = TypeRealloc(char, size, buf); 135 memset(buf, 0, size); 136 137 n = readlink(filename, buf, size); 138 if (n < 0) { 139 free(buf); 140 return NULL; 141 } 142 143 if ((unsigned) n < size) { 144 return buf; 145 } 146 147 size *= 2; 148 } 149} 150#endif /* OPT_EXEC_XTERM */ 151 152static void 153Sleep(int msec) 154{ 155 static struct timeval select_timeout; 156 157 select_timeout.tv_sec = 0; 158 select_timeout.tv_usec = msec * 1000; 159 select(0, 0, 0, 0, &select_timeout); 160} 161 162static void 163selectwindow(TScreen * screen, int flag) 164{ 165 TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag)); 166 167#if OPT_TEK4014 168 if (TEK4014_ACTIVE(term)) { 169 if (!Ttoggled) 170 TCursorToggle(tekWidget, TOGGLE); 171 screen->select |= flag; 172 if (!Ttoggled) 173 TCursorToggle(tekWidget, TOGGLE); 174 } else 175#endif 176 { 177 if (screen->xic) 178 XSetICFocus(screen->xic); 179 180 if (screen->cursor_state && CursorMoved(screen)) 181 HideCursor(); 182 screen->select |= flag; 183 if (screen->cursor_state) 184 ShowCursor(); 185 } 186} 187 188static void 189unselectwindow(TScreen * screen, int flag) 190{ 191 TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag)); 192 193 if (screen->hide_pointer) { 194 screen->hide_pointer = False; 195 xtermDisplayCursor(term); 196 } 197 198 if (!screen->always_highlight) { 199#if OPT_TEK4014 200 if (TEK4014_ACTIVE(term)) { 201 if (!Ttoggled) 202 TCursorToggle(tekWidget, TOGGLE); 203 screen->select &= ~flag; 204 if (!Ttoggled) 205 TCursorToggle(tekWidget, TOGGLE); 206 } else 207#endif 208 { 209 if (screen->xic) 210 XUnsetICFocus(screen->xic); 211 212 screen->select &= ~flag; 213 if (screen->cursor_state && CursorMoved(screen)) 214 HideCursor(); 215 if (screen->cursor_state) 216 ShowCursor(); 217 } 218 } 219} 220 221static void 222DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent * ev) 223{ 224 TScreen *screen = TScreenOf(xw); 225 226 TRACE(("DoSpecialEnterNotify(%d)\n", screen->select)); 227#ifdef ACTIVEWINDOWINPUTONLY 228 if (ev->window == XtWindow(XtParent(CURRENT_EMU()))) 229#endif 230 if (((ev->detail) != NotifyInferior) && 231 ev->focus && 232 !(screen->select & FOCUS)) 233 selectwindow(screen, INWINDOW); 234} 235 236static void 237DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent * ev) 238{ 239 TScreen *screen = TScreenOf(xw); 240 241 TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select)); 242#ifdef ACTIVEWINDOWINPUTONLY 243 if (ev->window == XtWindow(XtParent(CURRENT_EMU()))) 244#endif 245 if (((ev->detail) != NotifyInferior) && 246 ev->focus && 247 !(screen->select & FOCUS)) 248 unselectwindow(screen, INWINDOW); 249} 250 251#ifndef XUrgencyHint 252#define XUrgencyHint (1L << 8) /* X11R5 does not define */ 253#endif 254 255static void 256setXUrgency(TScreen * screen, Bool enable) 257{ 258 if (screen->bellIsUrgent) { 259 XWMHints *h = XGetWMHints(screen->display, VShellWindow); 260 if (h != 0) { 261 if (enable) { 262 h->flags |= XUrgencyHint; 263 } else { 264 h->flags &= ~XUrgencyHint; 265 } 266 XSetWMHints(screen->display, VShellWindow, h); 267 } 268 } 269} 270 271void 272do_xevents(void) 273{ 274 TScreen *screen = TScreenOf(term); 275 276 if (XtAppPending(app_con) 277 || 278#if defined(VMS) || defined(__VMS) 279 screen->display->qlen > 0 280#else 281 GetBytesAvailable(ConnectionNumber(screen->display)) > 0 282#endif 283 ) 284 xevents(); 285} 286 287void 288xtermDisplayCursor(XtermWidget xw) 289{ 290 TScreen *screen = TScreenOf(xw); 291 292 if (screen->Vshow) { 293 if (screen->hide_pointer) { 294 TRACE(("Display hidden_cursor\n")); 295 XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor); 296 } else { 297 TRACE(("Display pointer_cursor\n")); 298 recolor_cursor(screen, 299 screen->pointer_cursor, 300 T_COLOR(screen, MOUSE_FG), 301 T_COLOR(screen, MOUSE_BG)); 302 XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor); 303 } 304 } 305} 306 307void 308xtermShowPointer(XtermWidget xw, Bool enable) 309{ 310 static int tried = -1; 311 TScreen *screen = TScreenOf(xw); 312 313#if OPT_TEK4014 314 if (TEK4014_SHOWN(xw)) 315 enable = True; 316#endif 317 318 /* 319 * Whether we actually hide the pointer depends on the pointer-mode and 320 * the mouse-mode: 321 */ 322 if (!enable) { 323 switch (screen->pointer_mode) { 324 case pNever: 325 enable = True; 326 break; 327 case pNoMouse: 328 if (screen->send_mouse_pos != MOUSE_OFF) 329 enable = True; 330 break; 331 case pAlways: 332 break; 333 } 334 } 335 336 if (enable) { 337 if (screen->hide_pointer) { 338 screen->hide_pointer = False; 339 xtermDisplayCursor(xw); 340 switch (screen->send_mouse_pos) { 341 case ANY_EVENT_MOUSE: 342 break; 343 default: 344 MotionOff(screen, xw); 345 break; 346 } 347 } 348 } else if (!(screen->hide_pointer) && (tried <= 0)) { 349 if (screen->hidden_cursor == 0) { 350 screen->hidden_cursor = make_hidden_cursor(xw); 351 } 352 if (screen->hidden_cursor == 0) { 353 tried = 1; 354 } else { 355 tried = 0; 356 screen->hide_pointer = True; 357 xtermDisplayCursor(xw); 358 MotionOn(screen, xw); 359 } 360 } 361} 362 363void 364xevents(void) 365{ 366 XtermWidget xw = term; 367 TScreen *screen = TScreenOf(xw); 368 XEvent event; 369 XtInputMask input_mask; 370 371 if (need_cleanup) 372 Cleanup(0); 373 374 if (screen->scroll_amt) 375 FlushScroll(xw); 376 /* 377 * process timeouts, relying on the fact that XtAppProcessEvent 378 * will process the timeout and return without blockng on the 379 * XEvent queue. Other sources i.e. the pty are handled elsewhere 380 * with select(). 381 */ 382 while ((input_mask = XtAppPending(app_con)) & XtIMTimer) 383 XtAppProcessEvent(app_con, XtIMTimer); 384#if OPT_SESSION_MGT 385 /* 386 * Session management events are alternative input events. Deal with 387 * them in the same way. 388 */ 389 while ((input_mask = XtAppPending(app_con)) & XtIMAlternateInput) 390 XtAppProcessEvent(app_con, XtIMAlternateInput); 391#endif 392 393 /* 394 * If there's no XEvents, don't wait around... 395 */ 396 if ((input_mask & XtIMXEvent) != XtIMXEvent) 397 return; 398 do { 399 /* 400 * This check makes xterm hang when in mouse hilite tracking mode. 401 * We simply ignore all events except for those not passed down to 402 * this function, e.g., those handled in in_put(). 403 */ 404 if (screen->waitingForTrackInfo) { 405 Sleep(10); 406 return; 407 } 408 XtAppNextEvent(app_con, &event); 409 /* 410 * Hack to get around problems with the toolkit throwing away 411 * eventing during the exclusive grab of the menu popup. By 412 * looking at the event ourselves we make sure that we can 413 * do the right thing. 414 */ 415 if (OUR_EVENT(event, EnterNotify)) { 416 DoSpecialEnterNotify(xw, &event.xcrossing); 417 } else if (OUR_EVENT(event, LeaveNotify)) { 418 DoSpecialLeaveNotify(xw, &event.xcrossing); 419 } else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE 420#if OPT_DEC_LOCATOR 421 || screen->send_mouse_pos == DEC_LOCATOR 422#endif /* OPT_DEC_LOCATOR */ 423 ) 424 && event.xany.type == MotionNotify 425 && event.xcrossing.window == XtWindow(xw)) { 426 SendMousePosition(xw, &event); 427 continue; 428 } 429 430 if (!event.xany.send_event || 431 screen->allowSendEvents || 432 ((event.xany.type != KeyPress) && 433 (event.xany.type != KeyRelease) && 434 (event.xany.type != ButtonPress) && 435 (event.xany.type != ButtonRelease))) { 436 437 /* 438 * If the event is interesting (and not a keyboard event), turn the 439 * mouse pointer back on. 440 */ 441 if (screen->hide_pointer) { 442 switch (event.xany.type) { 443 case KeyPress: 444 case KeyRelease: 445 case ButtonPress: 446 case ButtonRelease: 447 /* also these... */ 448 case Expose: 449 case NoExpose: 450 case PropertyNotify: 451 break; 452 default: 453 xtermShowPointer(xw, True); 454 break; 455 } 456 } 457 458 XtDispatchEvent(&event); 459 } 460 } while ((input_mask = XtAppPending(app_con)) & XtIMXEvent); 461} 462 463static Cursor 464make_hidden_cursor(XtermWidget xw) 465{ 466 TScreen *screen = TScreenOf(xw); 467 Cursor c; 468 Display *dpy = screen->display; 469 XFontStruct *fn; 470 471 static XColor dummy; 472 473 /* 474 * Prefer nil2 (which is normally available) to "fixed" (which is supposed 475 * to be "always" available), since it's a smaller glyph in case the 476 * server insists on drawing _somethng_. 477 */ 478 TRACE(("Ask for nil2 font\n")); 479 if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) { 480 TRACE(("...Ask for fixed font\n")); 481 fn = XLoadQueryFont(dpy, "fixed"); 482 } 483 484 if (fn != 0) { 485 /* a space character seems to work as a cursor (dots are not needed) */ 486 c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy); 487 XFreeFont(dpy, fn); 488 } else { 489 c = 0; 490 } 491 TRACE(("XCreateGlyphCursor ->%#lx\n", c)); 492 return (c); 493} 494 495Cursor 496make_colored_cursor(unsigned cursorindex, /* index into font */ 497 unsigned long fg, /* pixel value */ 498 unsigned long bg) /* pixel value */ 499{ 500 TScreen *screen = TScreenOf(term); 501 Cursor c; 502 Display *dpy = screen->display; 503 504 c = XCreateFontCursor(dpy, cursorindex); 505 if (c != None) { 506 recolor_cursor(screen, c, fg, bg); 507 } 508 return (c); 509} 510 511/* ARGSUSED */ 512void 513HandleKeyPressed(Widget w GCC_UNUSED, 514 XEvent * event, 515 String * params GCC_UNUSED, 516 Cardinal *nparams GCC_UNUSED) 517{ 518 TRACE(("Handle 7bit-key\n")); 519#ifdef ACTIVEWINDOWINPUTONLY 520 if (w == CURRENT_EMU()) 521#endif 522 Input(term, &event->xkey, False); 523} 524 525/* ARGSUSED */ 526void 527HandleEightBitKeyPressed(Widget w GCC_UNUSED, 528 XEvent * event, 529 String * params GCC_UNUSED, 530 Cardinal *nparams GCC_UNUSED) 531{ 532 TRACE(("Handle 8bit-key\n")); 533#ifdef ACTIVEWINDOWINPUTONLY 534 if (w == CURRENT_EMU()) 535#endif 536 Input(term, &event->xkey, True); 537} 538 539/* ARGSUSED */ 540void 541HandleStringEvent(Widget w GCC_UNUSED, 542 XEvent * event GCC_UNUSED, 543 String * params, 544 Cardinal *nparams) 545{ 546#ifdef ACTIVEWINDOWINPUTONLY 547 if (w != CURRENT_EMU()) 548 return; 549#endif 550 551 if (*nparams != 1) 552 return; 553 554 if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') { 555 const char *abcdef = "ABCDEF"; 556 const char *xxxxxx; 557 Char c, *p; 558 unsigned value = 0; 559 560 for (p = (Char *) (*params + 2); (c = CharOf(x_toupper(*p))) != 561 '\0'; p++) { 562 value *= 16; 563 if (c >= '0' && c <= '9') 564 value += (unsigned) (c - '0'); 565 else if ((xxxxxx = strchr(abcdef, c)) != 0) 566 value += (unsigned) (xxxxxx - abcdef) + 10; 567 else 568 break; 569 } 570 if (c == '\0') { 571 Char hexval[2]; 572 hexval[0] = (Char) value; 573 hexval[1] = 0; 574 StringInput(term, hexval, 1); 575 } 576 } else { 577 StringInput(term, (Char *) * params, strlen(*params)); 578 } 579} 580 581#if OPT_EXEC_XTERM 582 583#ifndef PROCFS_ROOT 584#define PROCFS_ROOT "/proc" 585#endif 586 587/* ARGSUSED */ 588void 589HandleSpawnTerminal(Widget w GCC_UNUSED, 590 XEvent * event GCC_UNUSED, 591 String * params, 592 Cardinal *nparams) 593{ 594 TScreen *screen = &term->screen; 595 char *child_cwd = NULL; 596 char *child_exe; 597 pid_t pid; 598 599 /* 600 * Try to find the actual program which is running in the child process. 601 * This works for Linux. If we cannot find the program, fall back to the 602 * xterm program (which is usually adequate). Give up if we are given only 603 * a relative path to xterm, since that would not always match $PATH. 604 */ 605 child_exe = Readlink(PROCFS_ROOT "/self/exe"); 606 if (!child_exe) { 607 if (strncmp(ProgramName, "./", 2) 608 && strncmp(ProgramName, "../", 3)) { 609 child_exe = xtermFindShell(ProgramName, True); 610 } else { 611 fprintf(stderr, "Cannot exec-xterm given %s\n", ProgramName); 612 } 613 if (child_exe == 0) 614 return; 615 } 616 617 /* 618 * Determine the current working directory of the child so that we can 619 * spawn a new terminal in the same directory. 620 * 621 * If we cannot get the CWD of the child, just use our own. 622 */ 623 if (screen->pid) { 624 char child_cwd_link[sizeof(PROCFS_ROOT) + 80]; 625 sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid); 626 child_cwd = Readlink(child_cwd_link); 627 } 628 629 /* The reaper will take care of cleaning up the child */ 630 pid = fork(); 631 if (pid == -1) { 632 fprintf(stderr, "Could not fork: %s\n", SysErrorMsg(errno)); 633 } else if (!pid) { 634 /* We are the child */ 635 if (child_cwd) { 636 chdir(child_cwd); /* We don't care if this fails */ 637 } 638 639 if (setuid(screen->uid) == -1 640 || setgid(screen->gid) == -1) { 641 fprintf(stderr, "Cannot reset uid/gid\n"); 642 } else { 643 unsigned myargc = *nparams + 1; 644 char **myargv = TypeMallocN(char *, myargc + 1); 645 unsigned n = 0; 646 647 myargv[n++] = child_exe; 648 649 while (n < myargc) { 650 myargv[n++] = *params++; 651 } 652 653 myargv[n] = 0; 654 execv(child_exe, myargv); 655 656 /* If we get here, we've failed */ 657 fprintf(stderr, "exec of '%s': %s\n", child_exe, SysErrorMsg(errno)); 658 } 659 _exit(0); 660 } else { 661 /* We are the parent; clean up */ 662 if (child_cwd) 663 free(child_cwd); 664 if (child_exe) 665 free(child_exe); 666 } 667} 668#endif /* OPT_EXEC_XTERM */ 669 670/* 671 * Rather than sending characters to the host, put them directly into our 672 * input queue. That lets a user have access to any of the control sequences 673 * for a key binding. This is the equivalent of local function key support. 674 * 675 * NOTE: This code does not support the hexadecimal kludge used in 676 * HandleStringEvent because it prevents us from sending an arbitrary string 677 * (but it appears in a lot of examples - so we are stuck with it). The 678 * standard string converter does recognize "\" for newline ("\n") and for 679 * octal constants (e.g., "\007" for BEL). So we assume the user can make do 680 * without a specialized converter. (Don't try to use \000, though). 681 */ 682/* ARGSUSED */ 683void 684HandleInterpret(Widget w GCC_UNUSED, 685 XEvent * event GCC_UNUSED, 686 String * params, 687 Cardinal *param_count) 688{ 689 if (*param_count == 1) { 690 char *value = params[0]; 691 int need = strlen(value); 692 int used = VTbuffer->next - VTbuffer->buffer; 693 int have = VTbuffer->last - VTbuffer->buffer; 694 695 if (have - used + need < BUF_SIZE) { 696 697 fillPtyData(TScreenOf(term), VTbuffer, value, (int) strlen(value)); 698 699 TRACE(("Interpret %s\n", value)); 700 VTbuffer->update++; 701 } 702 } 703} 704 705/*ARGSUSED*/ 706void 707HandleEnterWindow(Widget w GCC_UNUSED, 708 XtPointer eventdata GCC_UNUSED, 709 XEvent * event GCC_UNUSED, 710 Boolean * cont GCC_UNUSED) 711{ 712 /* NOP since we handled it above */ 713 TRACE(("HandleEnterWindow ignored\n")); 714} 715 716/*ARGSUSED*/ 717void 718HandleLeaveWindow(Widget w GCC_UNUSED, 719 XtPointer eventdata GCC_UNUSED, 720 XEvent * event GCC_UNUSED, 721 Boolean * cont GCC_UNUSED) 722{ 723 /* NOP since we handled it above */ 724 TRACE(("HandleLeaveWindow ignored\n")); 725} 726 727/*ARGSUSED*/ 728void 729HandleFocusChange(Widget w GCC_UNUSED, 730 XtPointer eventdata GCC_UNUSED, 731 XEvent * ev, 732 Boolean * cont GCC_UNUSED) 733{ 734 XFocusChangeEvent *event = (XFocusChangeEvent *) ev; 735 XtermWidget xw = term; 736 TScreen *screen = TScreenOf(xw); 737 738 TRACE(("HandleFocusChange type=%s, mode=%d, detail=%d\n", 739 visibleEventType(event->type), 740 event->mode, 741 event->detail)); 742 743 if (screen->quiet_grab 744 && (event->mode == NotifyGrab || event->mode == NotifyUngrab)) { 745 ; 746 } else if (event->type == FocusIn) { 747 setXUrgency(screen, False); 748 749 /* 750 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in 751 * one of our windows. Use this to reset a case where one xterm is 752 * partly obscuring another, and X gets (us) confused about whether the 753 * pointer was in the window. In particular, this can happen if the 754 * user is resizing the obscuring window, causing some events to not be 755 * delivered to the obscured window. 756 */ 757 if (event->detail == NotifyNonlinear 758 && (screen->select & INWINDOW) != 0) { 759 unselectwindow(screen, INWINDOW); 760 } 761 selectwindow(screen, 762 ((event->detail == NotifyPointer) 763 ? INWINDOW 764 : FOCUS)); 765 SendFocusButton(xw, event); 766 } else { 767#if OPT_FOCUS_EVENT 768 if (event->type == FocusOut) { 769 SendFocusButton(xw, event); 770 } 771#endif 772 /* 773 * XGrabKeyboard() will generate NotifyGrab event that we want to 774 * ignore. 775 */ 776 if (event->mode != NotifyGrab) { 777 unselectwindow(screen, 778 ((event->detail == NotifyPointer) 779 ? INWINDOW 780 : FOCUS)); 781 } 782 if (screen->grabbedKbd && (event->mode == NotifyUngrab)) { 783 Bell(XkbBI_Info, 100); 784 ReverseVideo(xw); 785 screen->grabbedKbd = False; 786 update_securekbd(); 787 } 788 } 789} 790 791static long lastBellTime; /* in milliseconds */ 792 793void 794Bell(Atom which GCC_UNUSED, int percent) 795{ 796 TScreen *screen = TScreenOf(term); 797 struct timeval curtime; 798 long now_msecs; 799 800 TRACE(("BELL %ld %d%%\n", (long) which, percent)); 801 if (!XtIsRealized((Widget) term)) { 802 return; 803 } 804 805 setXUrgency(screen, True); 806 807 /* has enough time gone by that we are allowed to ring 808 the bell again? */ 809 if (screen->bellSuppressTime) { 810 if (screen->bellInProgress) { 811 do_xevents(); 812 if (screen->bellInProgress) { /* even after new events? */ 813 return; 814 } 815 } 816 X_GETTIMEOFDAY(&curtime); 817 now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000; 818 if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 && 819 now_msecs - lastBellTime < screen->bellSuppressTime) { 820 return; 821 } 822 lastBellTime = now_msecs; 823 } 824 825 if (screen->visualbell) { 826 VisualBell(); 827 } else { 828#if defined(HAVE_XKB_BELL_EXT) 829 XkbBell(screen->display, VShellWindow, percent, which); 830#else 831 XBell(screen->display, percent); 832#endif 833 } 834 835 if (screen->poponbell) 836 XRaiseWindow(screen->display, VShellWindow); 837 838 if (screen->bellSuppressTime) { 839 /* now we change a property and wait for the notify event to come 840 back. If the server is suspending operations while the bell 841 is being emitted (problematic for audio bell), this lets us 842 know when the previous bell has finished */ 843 Widget w = CURRENT_EMU(); 844 XChangeProperty(XtDisplay(w), XtWindow(w), 845 XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0); 846 screen->bellInProgress = True; 847 } 848} 849 850#define VB_DELAY screen->visualBellDelay 851 852static void 853flashWindow(TScreen * screen, Window window, GC visualGC, unsigned width, unsigned height) 854{ 855 XFillRectangle(screen->display, window, visualGC, 0, 0, width, height); 856 XFlush(screen->display); 857 Sleep(VB_DELAY); 858 XFillRectangle(screen->display, window, visualGC, 0, 0, width, height); 859} 860 861void 862VisualBell(void) 863{ 864 TScreen *screen = TScreenOf(term); 865 866 if (VB_DELAY > 0) { 867 Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^ 868 T_COLOR(screen, TEXT_BG)); 869 XGCValues gcval; 870 GC visualGC; 871 872 gcval.function = GXxor; 873 gcval.foreground = xorPixel; 874 visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval); 875#if OPT_TEK4014 876 if (TEK4014_ACTIVE(term)) { 877 TekScreen *tekscr = &(tekWidget->screen); 878 flashWindow(screen, TWindow(tekscr), visualGC, 879 TFullWidth(tekscr), 880 TFullHeight(tekscr)); 881 } else 882#endif 883 { 884 flashWindow(screen, VWindow(screen), visualGC, 885 FullWidth(screen), 886 FullHeight(screen)); 887 } 888 XtReleaseGC((Widget) term, visualGC); 889 } 890} 891 892/* ARGSUSED */ 893void 894HandleBellPropertyChange(Widget w GCC_UNUSED, 895 XtPointer data GCC_UNUSED, 896 XEvent * ev, 897 Boolean * more GCC_UNUSED) 898{ 899 TScreen *screen = TScreenOf(term); 900 901 if (ev->xproperty.atom == XA_NOTICE) { 902 screen->bellInProgress = False; 903 } 904} 905 906Window 907WMFrameWindow(XtermWidget termw) 908{ 909 Window win_root, win_current, *children; 910 Window win_parent = 0; 911 unsigned int nchildren; 912 913 win_current = XtWindow(termw); 914 915 /* find the parent which is child of root */ 916 do { 917 if (win_parent) 918 win_current = win_parent; 919 XQueryTree((&termw->screen)->display, 920 win_current, 921 &win_root, 922 &win_parent, 923 &children, 924 &nchildren); 925 XFree(children); 926 } while (win_root != win_parent); 927 928 return win_current; 929} 930 931#if OPT_DABBREV 932/* 933 * The following code implements `dynamic abbreviation' expansion a la 934 * Emacs. It looks in the preceding visible screen and its scrollback 935 * to find expansions of a typed word. It compares consecutive 936 * expansions and ignores one of them if they are identical. 937 * (Tomasz J. Cholewo, t.cholewo@ieee.org) 938 */ 939 940#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0') 941#define MAXWLEN 1024 /* maximum word length as in tcsh */ 942 943static int 944dabbrev_prev_char(int *xp, int *yp, TScreen * screen) 945{ 946 Char *linep; 947 948 while (*yp >= 0) { 949 linep = BUF_CHARS(screen->allbuf, *yp); 950 if (--*xp >= 0) 951 return linep[*xp]; 952 if (--*yp < 0) /* go to previous line */ 953 break; 954 *xp = MaxCols(screen); 955 if (!((long) BUF_FLAGS(screen->allbuf, *yp) & LINEWRAPPED)) 956 return ' '; /* treat lines as separate */ 957 } 958 return -1; 959} 960 961static char * 962dabbrev_prev_word(int *xp, int *yp, TScreen * screen) 963{ 964 static char ab[MAXWLEN]; 965 char *abword; 966 int c; 967 968 abword = ab + MAXWLEN - 1; 969 *abword = '\0'; /* end of string marker */ 970 971 while ((c = dabbrev_prev_char(xp, yp, screen)) >= 0 && 972 IS_WORD_CONSTITUENT(c)) 973 if (abword > ab) /* store only |MAXWLEN| last chars */ 974 *(--abword) = c; 975 if (c < 0) { 976 if (abword < ab + MAXWLEN - 1) 977 return abword; 978 else 979 return 0; 980 } 981 982 while ((c = dabbrev_prev_char(xp, yp, screen)) >= 0 && 983 !IS_WORD_CONSTITUENT(c)) ; /* skip preceding spaces */ 984 (*xp)++; /* can be | > screen->max_col| */ 985 return abword; 986} 987 988static int 989dabbrev_expand(TScreen * screen) 990{ 991 int pty = screen->respond; /* file descriptor of pty */ 992 993 static int x, y; 994 static char *dabbrev_hint = 0, *lastexpansion = 0; 995 static unsigned int expansions; 996 997 char *expansion; 998 Char *copybuffer; 999 size_t hint_len; 1000 unsigned del_cnt; 1001 unsigned buf_cnt; 1002 1003 if (!screen->dabbrev_working) { /* initialize */ 1004 expansions = 0; 1005 x = screen->cur_col; 1006 y = screen->cur_row + screen->savelines; 1007 1008 free(dabbrev_hint); /* free(NULL) is OK */ 1009 dabbrev_hint = dabbrev_prev_word(&x, &y, screen); 1010 if (!dabbrev_hint) 1011 return 0; /* no preceding word? */ 1012 free(lastexpansion); 1013 if (!(lastexpansion = strdup(dabbrev_hint))) /* make own copy */ 1014 return 0; 1015 if (!(dabbrev_hint = strdup(dabbrev_hint))) { 1016 free(lastexpansion); 1017 return 0; 1018 } 1019 screen->dabbrev_working = 1; /* we are in the middle of dabbrev process */ 1020 } 1021 1022 hint_len = strlen(dabbrev_hint); 1023 for (;;) { 1024 if (!(expansion = dabbrev_prev_word(&x, &y, screen))) { 1025 if (expansions >= 2) { 1026 expansions = 0; 1027 x = screen->cur_col; 1028 y = screen->cur_row + screen->savelines; 1029 continue; 1030 } 1031 break; 1032 } 1033 if (!strncmp(dabbrev_hint, expansion, hint_len) && /* empty hint matches everything */ 1034 strlen(expansion) > hint_len && /* trivial expansion disallowed */ 1035 strcmp(expansion, lastexpansion)) /* different from previous */ 1036 break; 1037 } 1038 if (!expansion) /* no expansion found */ 1039 return 0; 1040 1041 del_cnt = strlen(lastexpansion) - hint_len; 1042 buf_cnt = del_cnt + strlen(expansion) - hint_len; 1043 if (!(copybuffer = TypeMallocN(Char, buf_cnt))) 1044 return 0; 1045 memset(copybuffer, screen->dabbrev_erase_char, del_cnt); /* delete previous expansion */ 1046 memmove(copybuffer + del_cnt, 1047 expansion + hint_len, 1048 strlen(expansion) - hint_len); 1049 v_write(pty, copybuffer, buf_cnt); 1050 screen->dabbrev_working = 1; /* v_write() just set it to 1 */ 1051 free(copybuffer); 1052 1053 free(lastexpansion); 1054 lastexpansion = strdup(expansion); 1055 if (!lastexpansion) 1056 return 0; 1057 expansions++; 1058 return 1; 1059} 1060 1061/*ARGSUSED*/ 1062void 1063HandleDabbrevExpand(Widget gw, 1064 XEvent * event GCC_UNUSED, 1065 String * params GCC_UNUSED, 1066 Cardinal *nparams GCC_UNUSED) 1067{ 1068 if (IsXtermWidget(gw)) { 1069 XtermWidget w = (XtermWidget) gw; 1070 TScreen *screen = &w->screen; 1071 if (!dabbrev_expand(screen)) 1072 Bell(XkbBI_TerminalBell, 0); 1073 } 1074} 1075#endif /* OPT_DABBREV */ 1076 1077#if OPT_MAXIMIZE 1078/*ARGSUSED*/ 1079void 1080HandleDeIconify(Widget gw, 1081 XEvent * event GCC_UNUSED, 1082 String * params GCC_UNUSED, 1083 Cardinal *nparams GCC_UNUSED) 1084{ 1085 if (IsXtermWidget(gw)) { 1086 TScreen *screen = TScreenOf((XtermWidget) gw); 1087 XMapWindow(screen->display, VShellWindow); 1088 } 1089} 1090 1091/*ARGSUSED*/ 1092void 1093HandleIconify(Widget gw, 1094 XEvent * event GCC_UNUSED, 1095 String * params GCC_UNUSED, 1096 Cardinal *nparams GCC_UNUSED) 1097{ 1098 if (IsXtermWidget(gw)) { 1099 TScreen *screen = TScreenOf((XtermWidget) gw); 1100 XIconifyWindow(screen->display, 1101 VShellWindow, 1102 DefaultScreen(screen->display)); 1103 } 1104} 1105 1106int 1107QueryMaximize(XtermWidget termw, unsigned *width, unsigned *height) 1108{ 1109 TScreen *screen = &termw->screen; 1110 XSizeHints hints; 1111 long supp = 0; 1112 Window root_win; 1113 int root_x = -1; /* saved co-ordinates */ 1114 int root_y = -1; 1115 unsigned root_border; 1116 unsigned root_depth; 1117 1118 if (XGetGeometry(screen->display, 1119 RootWindowOfScreen(XtScreen(termw)), 1120 &root_win, 1121 &root_x, 1122 &root_y, 1123 width, 1124 height, 1125 &root_border, 1126 &root_depth)) { 1127 TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n", 1128 root_x, 1129 root_y, 1130 *width, 1131 *height, 1132 root_border)); 1133 1134 *width -= (root_border * 2); 1135 *height -= (root_border * 2); 1136 1137 hints.flags = PMaxSize; 1138 if (XGetWMNormalHints(screen->display, 1139 VShellWindow, 1140 &hints, 1141 &supp) 1142 && (hints.flags & PMaxSize) != 0) { 1143 1144 TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n", 1145 hints.max_width, 1146 hints.max_height)); 1147 1148 if ((unsigned) hints.max_width < *width) 1149 *width = hints.max_width; 1150 if ((unsigned) hints.max_height < *height) 1151 *height = hints.max_height; 1152 } 1153 return 1; 1154 } 1155 return 0; 1156} 1157 1158void 1159RequestMaximize(XtermWidget termw, int maximize) 1160{ 1161 TScreen *screen = &termw->screen; 1162 XWindowAttributes wm_attrs, vshell_attrs; 1163 unsigned root_width, root_height; 1164 1165 if (maximize) { 1166 1167 if (QueryMaximize(termw, &root_width, &root_height)) { 1168 1169 if (XGetWindowAttributes(screen->display, 1170 WMFrameWindow(termw), 1171 &wm_attrs)) { 1172 1173 if (XGetWindowAttributes(screen->display, 1174 VShellWindow, 1175 &vshell_attrs)) { 1176 1177 if (screen->restore_data != True 1178 || screen->restore_width != root_width 1179 || screen->restore_height != root_height) { 1180 screen->restore_data = True; 1181 screen->restore_x = wm_attrs.x + wm_attrs.border_width; 1182 screen->restore_y = wm_attrs.y + wm_attrs.border_width; 1183 screen->restore_width = vshell_attrs.width; 1184 screen->restore_height = vshell_attrs.height; 1185 TRACE(("HandleMaximize: save window position %d,%d size %d,%d\n", 1186 screen->restore_x, 1187 screen->restore_y, 1188 screen->restore_width, 1189 screen->restore_height)); 1190 } 1191 1192 /* subtract wm decoration dimensions */ 1193 root_width -= ((wm_attrs.width - vshell_attrs.width) 1194 + (wm_attrs.border_width * 2)); 1195 root_height -= ((wm_attrs.height - vshell_attrs.height) 1196 + (wm_attrs.border_width * 2)); 1197 1198 XMoveResizeWindow(screen->display, VShellWindow, 1199 0 + wm_attrs.border_width, /* x */ 1200 0 + wm_attrs.border_width, /* y */ 1201 root_width, 1202 root_height); 1203 } 1204 } 1205 } 1206 } else { 1207 if (screen->restore_data) { 1208 TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n", 1209 screen->restore_x, 1210 screen->restore_y, 1211 screen->restore_width, 1212 screen->restore_height)); 1213 screen->restore_data = False; 1214 1215 XMoveResizeWindow(screen->display, 1216 VShellWindow, 1217 screen->restore_x, 1218 screen->restore_y, 1219 screen->restore_width, 1220 screen->restore_height); 1221 } 1222 } 1223} 1224 1225/*ARGSUSED*/ 1226void 1227HandleMaximize(Widget gw, 1228 XEvent * event GCC_UNUSED, 1229 String * params GCC_UNUSED, 1230 Cardinal *nparams GCC_UNUSED) 1231{ 1232 if (IsXtermWidget(gw)) { 1233 RequestMaximize((XtermWidget) gw, 1); 1234 } 1235} 1236 1237/*ARGSUSED*/ 1238void 1239HandleRestoreSize(Widget gw, 1240 XEvent * event GCC_UNUSED, 1241 String * params GCC_UNUSED, 1242 Cardinal *nparams GCC_UNUSED) 1243{ 1244 if (IsXtermWidget(gw)) { 1245 RequestMaximize((XtermWidget) gw, 0); 1246 } 1247} 1248#endif /* OPT_MAXIMIZE */ 1249 1250void 1251Redraw(void) 1252{ 1253 TScreen *screen = TScreenOf(term); 1254 XExposeEvent event; 1255 1256 TRACE(("Redraw\n")); 1257 1258 event.type = Expose; 1259 event.display = screen->display; 1260 event.x = 0; 1261 event.y = 0; 1262 event.count = 0; 1263 1264 if (VWindow(screen)) { 1265 event.window = VWindow(screen); 1266 event.width = term->core.width; 1267 event.height = term->core.height; 1268 (*term->core.widget_class->core_class.expose) ((Widget) term, 1269 (XEvent *) & event, 1270 NULL); 1271 if (ScrollbarWidth(screen)) { 1272 (screen->scrollWidget->core.widget_class->core_class.expose) 1273 (screen->scrollWidget, (XEvent *) & event, NULL); 1274 } 1275 } 1276#if OPT_TEK4014 1277 if (TEK4014_SHOWN(term)) { 1278 TekScreen *tekscr = &(tekWidget->screen); 1279 event.window = TWindow(tekscr); 1280 event.width = tekWidget->core.width; 1281 event.height = tekWidget->core.height; 1282 TekExpose((Widget) tekWidget, (XEvent *) & event, NULL); 1283 } 1284#endif 1285} 1286 1287#ifdef VMS 1288#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d" 1289#else 1290#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d" 1291#endif 1292 1293void 1294timestamp_filename(char *dst, const char *src) 1295{ 1296 time_t tstamp; 1297 struct tm *tstruct; 1298 1299 tstamp = time((time_t *) 0); 1300 tstruct = localtime(&tstamp); 1301 sprintf(dst, TIMESTAMP_FMT, 1302 src, 1303 tstruct->tm_year + 1900, 1304 tstruct->tm_mon + 1, 1305 tstruct->tm_mday, 1306 tstruct->tm_hour, 1307 tstruct->tm_min, 1308 tstruct->tm_sec); 1309} 1310 1311int 1312open_userfile(uid_t uid, gid_t gid, char *path, Bool append) 1313{ 1314 int fd; 1315 struct stat sb; 1316 1317#ifdef VMS 1318 if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { 1319 int the_error = errno; 1320 fprintf(stderr, "%s: cannot open %s: %d:%s\n", 1321 xterm_name, 1322 path, 1323 the_error, 1324 SysErrorMsg(the_error)); 1325 return -1; 1326 } 1327 chown(path, uid, gid); 1328#else 1329 if ((access(path, F_OK) != 0 && (errno != ENOENT)) 1330 || (creat_as(uid, gid, append, path, 0644) <= 0) 1331 || ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) { 1332 int the_error = errno; 1333 fprintf(stderr, "%s: cannot open %s: %d:%s\n", 1334 xterm_name, 1335 path, 1336 the_error, 1337 SysErrorMsg(the_error)); 1338 return -1; 1339 } 1340#endif 1341 1342 /* 1343 * Doublecheck that the user really owns the file that we've opened before 1344 * we do any damage, and that it is not world-writable. 1345 */ 1346 if (fstat(fd, &sb) < 0 1347 || sb.st_uid != uid 1348 || (sb.st_mode & 022) != 0) { 1349 fprintf(stderr, "%s: you do not own %s\n", xterm_name, path); 1350 close(fd); 1351 return -1; 1352 } 1353 return fd; 1354} 1355 1356#ifndef VMS 1357/* 1358 * Create a file only if we could with the permissions of the real user id. 1359 * We could emulate this with careful use of access() and following 1360 * symbolic links, but that is messy and has race conditions. 1361 * Forking is messy, too, but we can't count on setreuid() or saved set-uids 1362 * being available. 1363 * 1364 * Note: When called for user logging, we have ensured that the real and 1365 * effective user ids are the same, so this remains as a convenience function 1366 * for the debug logs. 1367 * 1368 * Returns 1369 * 1 if we can proceed to open the file in relative safety, 1370 * -1 on error, e.g., cannot fork 1371 * 0 otherwise. 1372 */ 1373int 1374creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, int mode) 1375{ 1376 int fd; 1377 pid_t pid; 1378 int retval = 0; 1379 int childstat = 0; 1380#ifndef HAVE_WAITPID 1381 int waited; 1382 SIGNAL_T(*chldfunc) (int); 1383 1384 chldfunc = signal(SIGCHLD, SIG_DFL); 1385#endif /* HAVE_WAITPID */ 1386 1387 TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n", 1388 (int) uid, (int) geteuid(), 1389 (int) gid, (int) getegid(), 1390 append, 1391 pathname, 1392 mode)); 1393 1394 if (uid == geteuid() && gid == getegid()) { 1395 fd = open(pathname, 1396 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 1397 mode); 1398 if (fd >= 0) 1399 close(fd); 1400 return (fd >= 0); 1401 } 1402 1403 pid = fork(); 1404 switch (pid) { 1405 case 0: /* child */ 1406 if (setgid(gid) == -1 1407 || setuid(uid) == -1) { 1408 /* we cannot report an error here via stderr, just quit */ 1409 retval = 1; 1410 } else { 1411 fd = open(pathname, 1412 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 1413 mode); 1414 if (fd >= 0) { 1415 close(fd); 1416 retval = 0; 1417 } else { 1418 retval = 1; 1419 } 1420 } 1421 _exit(retval); 1422 /* NOTREACHED */ 1423 case -1: /* error */ 1424 return retval; 1425 default: /* parent */ 1426#ifdef HAVE_WAITPID 1427 while (waitpid(pid, &childstat, 0) < 0) { 1428#ifdef EINTR 1429 if (errno == EINTR) 1430 continue; 1431#endif /* EINTR */ 1432#ifdef ERESTARTSYS 1433 if (errno == ERESTARTSYS) 1434 continue; 1435#endif /* ERESTARTSYS */ 1436 break; 1437 } 1438#else /* HAVE_WAITPID */ 1439 waited = wait(&childstat); 1440 signal(SIGCHLD, chldfunc); 1441 /* 1442 Since we had the signal handler uninstalled for a while, 1443 we might have missed the termination of our screen child. 1444 If we can check for this possibility without hanging, do so. 1445 */ 1446 do 1447 if (waited == term->screen.pid) 1448 Cleanup(0); 1449 while ((waited = nonblocking_wait()) > 0) ; 1450#endif /* HAVE_WAITPID */ 1451#ifndef WIFEXITED 1452#define WIFEXITED(status) ((status & 0xff) != 0) 1453#endif 1454 if (WIFEXITED(childstat)) 1455 retval = 1; 1456 return retval; 1457 } 1458} 1459#endif /* !VMS */ 1460 1461int 1462xtermResetIds(TScreen * screen) 1463{ 1464 int result = 0; 1465 if (setgid(screen->gid) == -1) { 1466 fprintf(stderr, "%s: unable to reset group-id\n", ProgramName); 1467 result = -1; 1468 } 1469 if (setuid(screen->uid) == -1) { 1470 fprintf(stderr, "%s: unable to reset user-id\n", ProgramName); 1471 result = -1; 1472 } 1473 return result; 1474} 1475 1476#ifdef ALLOWLOGGING 1477 1478/* 1479 * Logging is a security hole, since it allows a setuid program to write 1480 * arbitrary data to an arbitrary file. So it is disabled by default. 1481 */ 1482 1483#ifdef ALLOWLOGFILEEXEC 1484static SIGNAL_T 1485logpipe(int sig GCC_UNUSED) 1486{ 1487 TScreen *screen = TScreenOf(term); 1488 1489#ifdef SYSV 1490 (void) signal(SIGPIPE, SIG_IGN); 1491#endif /* SYSV */ 1492 if (screen->logging) 1493 CloseLog(screen); 1494} 1495#endif /* ALLOWLOGFILEEXEC */ 1496 1497void 1498StartLog(TScreen * screen) 1499{ 1500 static char *log_default; 1501#ifdef ALLOWLOGFILEEXEC 1502 char *cp; 1503#endif /* ALLOWLOGFILEEXEC */ 1504 1505 if (screen->logging || (screen->inhibit & I_LOG)) 1506 return; 1507#ifdef VMS /* file name is fixed in VMS variant */ 1508 screen->logfd = open(XTERM_VMS_LOGFILE, 1509 O_CREAT | O_TRUNC | O_APPEND | O_RDWR, 1510 0640); 1511 if (screen->logfd < 0) 1512 return; /* open failed */ 1513#else /*VMS */ 1514 if (screen->logfile == NULL || *screen->logfile == 0) { 1515 if (screen->logfile) 1516 free(screen->logfile); 1517 if (log_default == NULL) { 1518#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME) 1519 char log_def_name[512]; /* see sprintf below */ 1520 char hostname[255 + 1]; /* Internet standard limit (RFC 1035): 1521 ``To simplify implementations, the 1522 total length of a domain name (i.e., 1523 label octets and label length 1524 octets) is restricted to 255 octets 1525 or less.'' */ 1526 char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1]; 1527 time_t now; 1528 struct tm *ltm; 1529 1530 now = time((time_t *) 0); 1531 ltm = (struct tm *) localtime(&now); 1532 if ((gethostname(hostname, sizeof(hostname)) == 0) && 1533 (strftime(yyyy_mm_dd_hh_mm_ss, 1534 sizeof(yyyy_mm_dd_hh_mm_ss), 1535 "%Y.%m.%d.%H.%M.%S", ltm) > 0)) { 1536 (void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d", 1537 hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid()); 1538 } 1539 if ((log_default = x_strdup(log_def_name)) == NULL) 1540 return; 1541#else 1542 const char *log_def_name = "XtermLog.XXXXXX"; 1543 if ((log_default = x_strdup(log_def_name)) == NULL) 1544 return; 1545 1546 mktemp(log_default); 1547#endif 1548 } 1549 if ((screen->logfile = x_strdup(log_default)) == 0) 1550 return; 1551 } 1552 if (*screen->logfile == '|') { /* exec command */ 1553#ifdef ALLOWLOGFILEEXEC 1554 /* 1555 * Warning, enabling this "feature" allows arbitrary programs 1556 * to be run. If ALLOWLOGFILECHANGES is enabled, this can be 1557 * done through escape sequences.... You have been warned. 1558 */ 1559 int pid; 1560 int p[2]; 1561 static char *shell; 1562 struct passwd *pw; 1563 1564 if (pipe(p) < 0 || (pid = fork()) < 0) 1565 return; 1566 if (pid == 0) { /* child */ 1567 /* 1568 * Close our output (we won't be talking back to the 1569 * parent), and redirect our child's output to the 1570 * original stderr. 1571 */ 1572 close(p[1]); 1573 dup2(p[0], 0); 1574 close(p[0]); 1575 dup2(fileno(stderr), 1); 1576 dup2(fileno(stderr), 2); 1577 1578 close(fileno(stderr)); 1579 close(ConnectionNumber(screen->display)); 1580 close(screen->respond); 1581 1582 if ((((cp = x_getenv("SHELL")) == NULL) 1583 && ((pw = getpwuid(screen->uid)) == NULL 1584 || *(cp = pw->pw_shell) == 0)) 1585 || (shell = CastMallocN(char, strlen(cp))) == 0) { 1586 shell = "/bin/sh"; 1587 } else { 1588 strcpy(shell, cp); 1589 } 1590 1591 signal(SIGHUP, SIG_DFL); 1592 signal(SIGCHLD, SIG_DFL); 1593 1594 /* (this is redundant) */ 1595 if (xtermResetIds(screen) < 0) 1596 exit(ERROR_SETUID); 1597 1598 execl(shell, shell, "-c", &screen->logfile[1], (void *) 0); 1599 1600 fprintf(stderr, "%s: Can't exec `%s'\n", xterm_name, 1601 &screen->logfile[1]); 1602 exit(ERROR_LOGEXEC); 1603 } 1604 close(p[0]); 1605 screen->logfd = p[1]; 1606 signal(SIGPIPE, logpipe); 1607#else 1608 Bell(XkbBI_Info, 0); 1609 Bell(XkbBI_Info, 0); 1610 return; 1611#endif 1612 } else { 1613 if ((screen->logfd = open_userfile(screen->uid, 1614 screen->gid, 1615 screen->logfile, 1616 (log_default != 0))) < 0) 1617 return; 1618 } 1619#endif /*VMS */ 1620 screen->logstart = VTbuffer->next; 1621 screen->logging = True; 1622 update_logging(); 1623} 1624 1625void 1626CloseLog(TScreen * screen) 1627{ 1628 if (!screen->logging || (screen->inhibit & I_LOG)) 1629 return; 1630 FlushLog(screen); 1631 close(screen->logfd); 1632 screen->logging = False; 1633 update_logging(); 1634} 1635 1636void 1637FlushLog(TScreen * screen) 1638{ 1639 if (screen->logging && !(screen->inhibit & I_LOG)) { 1640 Char *cp; 1641 int i; 1642 1643#ifdef VMS /* avoid logging output loops which otherwise occur sometimes 1644 when there is no output and cp/screen->logstart are 1 apart */ 1645 if (!tt_new_output) 1646 return; 1647 tt_new_output = False; 1648#endif /* VMS */ 1649 cp = VTbuffer->next; 1650 if (screen->logstart != 0 1651 && (i = cp - screen->logstart) > 0) { 1652 write(screen->logfd, (char *) screen->logstart, (unsigned) i); 1653 } 1654 screen->logstart = VTbuffer->next; 1655 } 1656} 1657 1658#endif /* ALLOWLOGGING */ 1659 1660/***====================================================================***/ 1661 1662#if OPT_ISO_COLORS 1663static void 1664ReportAnsiColorRequest(XtermWidget xw, int colornum, int final) 1665{ 1666 XColor color; 1667 Colormap cmap = xw->core.colormap; 1668 char buffer[80]; 1669 1670 TRACE(("ReportAnsiColorRequest %d\n", colornum)); 1671 color.pixel = GET_COLOR_RES(xw->screen.Acolors[colornum]); 1672 XQueryColor(xw->screen.display, cmap, &color); 1673 sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x", 1674 colornum, 1675 color.red, 1676 color.green, 1677 color.blue); 1678 unparseputc1(xw, ANSI_OSC); 1679 unparseputs(xw, buffer); 1680 unparseputc1(xw, final); 1681 unparse_end(xw); 1682} 1683 1684static unsigned 1685getColormapSize(Display * display) 1686{ 1687 unsigned result; 1688 int numFound; 1689 XVisualInfo myTemplate, *visInfoPtr; 1690 1691 myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(display, 1692 XDefaultScreen(display))); 1693 visInfoPtr = XGetVisualInfo(display, (long) VisualIDMask, 1694 &myTemplate, &numFound); 1695 result = (numFound >= 1) ? (unsigned) visInfoPtr->colormap_size : 0; 1696 1697 XFree((char *) visInfoPtr); 1698 return result; 1699} 1700 1701/* 1702 * Find closest color for "def" in "cmap". 1703 * Set "def" to the resulting color. 1704 * 1705 * Based on Monish Shah's "find_closest_color()" for Vim 6.0, 1706 * modified with ideas from David Tong's "noflash" library. 1707 * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk. 1708 * 1709 * These provide some introduction: 1710 * http://en.wikipedia.org/wiki/YIQ 1711 * for an introduction to YIQ weights. 1712 * http://en.wikipedia.org/wiki/Luminance_(video) 1713 * for a discussion of luma. 1714 * http://en.wikipedia.org/wiki/YUV 1715 * 1716 * Return False if not able to find or allocate a color. 1717 */ 1718static Boolean 1719find_closest_color(Display * dpy, Colormap cmap, XColor * def) 1720{ 1721 Boolean result = False; 1722 XColor *colortable; 1723 char *tried; 1724 double diff, thisRGB, bestRGB; 1725 unsigned attempts; 1726 unsigned bestInx; 1727 unsigned cmap_size; 1728 unsigned i; 1729 1730 cmap_size = getColormapSize(dpy); 1731 if (cmap_size != 0) { 1732 1733 colortable = TypeMallocN(XColor, cmap_size); 1734 if (colortable != 0) { 1735 1736 tried = TypeCallocN(char, cmap_size); 1737 if (tried != 0) { 1738 1739 for (i = 0; i < cmap_size; i++) { 1740 colortable[i].pixel = (unsigned long) i; 1741 } 1742 XQueryColors(dpy, cmap, colortable, (int) cmap_size); 1743 1744 /* 1745 * Try (possibly each entry in the color map) to find the best 1746 * approximation to the requested color. 1747 */ 1748 for (attempts = 0; attempts < cmap_size; attempts++) { 1749 Boolean first = True; 1750 1751 bestRGB = 0.0; 1752 bestInx = 0; 1753 for (i = 0; i < cmap_size; i++) { 1754 if (!tried[bestInx]) { 1755 /* 1756 * Look for the best match based on luminance. 1757 * Measure this by the least-squares difference of 1758 * the weighted R/G/B components from the color map 1759 * versus the requested color. Use the Y (luma) 1760 * component of the YIQ color space model for 1761 * weights that correspond to the luminance. 1762 */ 1763#define AddColorWeight(weight, color) \ 1764 diff = weight * (int) ((def->color) - colortable[i].color); \ 1765 thisRGB = diff * diff 1766 1767 AddColorWeight(0.30, red); 1768 AddColorWeight(0.61, green); 1769 AddColorWeight(0.11, blue); 1770 1771 if (first || (thisRGB < bestRGB)) { 1772 first = False; 1773 bestInx = i; 1774 bestRGB = thisRGB; 1775 } 1776 } 1777 } 1778 if (XAllocColor(dpy, cmap, &colortable[bestInx]) != 0) { 1779 *def = colortable[bestInx]; 1780 result = True; 1781 break; 1782 } 1783 /* 1784 * It failed - either the color map entry was readonly, or 1785 * another client has allocated the entry. Mark the entry 1786 * so we will ignore it 1787 */ 1788 tried[bestInx] = True; 1789 } 1790 free(tried); 1791 } 1792 free(colortable); 1793 } 1794 } 1795 return result; 1796} 1797 1798/* 1799 * Allocate a color for the "ANSI" colors. That actually includes colors up 1800 * to 256. 1801 * 1802 * Returns 1803 * -1 on error 1804 * 0 on no change 1805 * 1 if a new color was allocated. 1806 */ 1807static int 1808AllocateAnsiColor(XtermWidget xw, 1809 ColorRes * res, 1810 char *spec) 1811{ 1812 int result; 1813 XColor def; 1814 TScreen *screen = &xw->screen; 1815 Colormap cmap = xw->core.colormap; 1816 1817 if (XParseColor(screen->display, cmap, spec, &def) 1818 && (XAllocColor(screen->display, cmap, &def) 1819 || find_closest_color(screen->display, cmap, &def))) { 1820 if ( 1821#if OPT_COLOR_RES 1822 res->mode == True && 1823#endif 1824 EQL_COLOR_RES(res, def.pixel)) { 1825 result = 0; 1826 } else { 1827 result = 1; 1828 SET_COLOR_RES(res, def.pixel); 1829 TRACE(("AllocateAnsiColor[%d] %s (pixel %#lx)\n", 1830 (res - screen->Acolors), spec, def.pixel)); 1831#if OPT_COLOR_RES 1832 if (!res->mode) 1833 result = 0; 1834 res->mode = True; 1835#endif 1836 } 1837 } else { 1838 TRACE(("AllocateAnsiColor %s (failed)\n", spec)); 1839 result = -1; 1840 } 1841 return (result); 1842} 1843 1844#if OPT_COLOR_RES 1845Pixel 1846xtermGetColorRes(ColorRes * res) 1847{ 1848 Pixel result = 0; 1849 1850 if (res->mode) { 1851 result = res->value; 1852 } else { 1853 TRACE(("xtermGetColorRes for Acolors[%d]\n", 1854 res - term->screen.Acolors)); 1855 1856 if (res >= term->screen.Acolors) { 1857 assert(res - term->screen.Acolors < MAXCOLORS); 1858 1859 if (AllocateAnsiColor(term, res, res->resource) < 0) { 1860 res->value = term->screen.Tcolors[TEXT_FG].value; 1861 res->mode = -True; 1862 fprintf(stderr, 1863 "%s: Cannot allocate color %s\n", 1864 xterm_name, 1865 NonNull(res->resource)); 1866 } 1867 result = res->value; 1868 } else { 1869 result = 0; 1870 } 1871 } 1872 return result; 1873} 1874#endif 1875 1876static Bool 1877ChangeAnsiColorRequest(XtermWidget xw, 1878 char *buf, 1879 int final) 1880{ 1881 char *name; 1882 int color; 1883 int repaint = False; 1884 int code; 1885 1886 TRACE(("ChangeAnsiColorRequest string='%s'\n", buf)); 1887 1888 while (buf && *buf) { 1889 name = strchr(buf, ';'); 1890 if (name == NULL) 1891 break; 1892 *name = '\0'; 1893 name++; 1894 color = atoi(buf); 1895 if (color < 0 || color >= NUM_ANSI_COLORS) 1896 break; 1897 buf = strchr(name, ';'); 1898 if (buf) { 1899 *buf = '\0'; 1900 buf++; 1901 } 1902 if (!strcmp(name, "?")) 1903 ReportAnsiColorRequest(xw, color, final); 1904 else { 1905 TRACE(("ChangeAnsiColor for Acolors[%d]\n", color)); 1906 code = AllocateAnsiColor(xw, &(xw->screen.Acolors[color]), name); 1907 if (code < 0) { 1908 /* stop on any error */ 1909 break; 1910 } else if (code > 0) { 1911 repaint = True; 1912 } 1913 /* FIXME: free old color somehow? We aren't for the other color 1914 * change style (dynamic colors). 1915 */ 1916 } 1917 } 1918 if (repaint) 1919 xtermRepaint(xw); 1920 1921 return (repaint); 1922} 1923#else 1924#define find_closest_color(display, cmap, def) 0 1925#endif /* OPT_ISO_COLORS */ 1926 1927#if OPT_PASTE64 1928static void 1929ManipulateSelectionData(XtermWidget xw, TScreen * screen, char *buf, int final) 1930{ 1931#define PDATA(a,b) { a, #b } 1932 static struct { 1933 char given; 1934 char *result; 1935 } table[] = { 1936 PDATA('s', SELECT), 1937 PDATA('p', PRIMARY), 1938 PDATA('c', CLIPBOARD), 1939 PDATA('0', CUT_BUFFER0), 1940 PDATA('1', CUT_BUFFER1), 1941 PDATA('2', CUT_BUFFER2), 1942 PDATA('3', CUT_BUFFER3), 1943 PDATA('4', CUT_BUFFER4), 1944 PDATA('5', CUT_BUFFER5), 1945 PDATA('6', CUT_BUFFER6), 1946 PDATA('7', CUT_BUFFER7), 1947 }; 1948 1949 char *base = buf; 1950 char *used = x_strdup(base); 1951 Cardinal j, n = 0; 1952 char **select_args = 0; 1953 1954 TRACE(("Manipulate selection data\n")); 1955 1956 while (*buf != ';' && *buf != '\0') { 1957 ++buf; 1958 } 1959 1960 if (*buf == ';') { 1961 *buf++ = '\0'; 1962 1963 if (*base == '\0') 1964 base = "s0"; 1965 if ((select_args = TypeCallocN(String, 1 + strlen(base))) == 0) 1966 return; 1967 while (*base != '\0') { 1968 for (j = 0; j < XtNumber(table); ++j) { 1969 if (*base == table[j].given) { 1970 used[n] = *base; 1971 select_args[n++] = table[j].result; 1972 TRACE(("atom[%d] %s\n", n, table[j].result)); 1973 break; 1974 } 1975 } 1976 ++base; 1977 } 1978 used[n] = 0; 1979 1980 if (!strcmp(buf, "?")) { 1981 TRACE(("Getting selection\n")); 1982 unparseputc1(xw, ANSI_OSC); 1983 unparseputs(xw, "52"); 1984 unparseputc(xw, ';'); 1985 1986 unparseputs(xw, used); 1987 unparseputc(xw, ';'); 1988 1989 /* Tell xtermGetSelection data is base64 encoded */ 1990 screen->base64_paste = n; 1991 screen->base64_final = final; 1992 1993 /* terminator will be written in this call */ 1994 xtermGetSelection((Widget) xw, 0, select_args, n, NULL); 1995 } else { 1996 TRACE(("Setting selection with %s\n", buf)); 1997 ClearSelectionBuffer(screen); 1998 while (*buf != '\0') 1999 AppendToSelectionBuffer(screen, CharOf(*buf++)); 2000 CompleteSelection(xw, select_args, n); 2001 } 2002 } 2003} 2004#endif /* OPT_PASTE64 */ 2005 2006/***====================================================================***/ 2007 2008static Bool 2009xtermIsPrintable(TScreen * screen, Char ** bufp, Char * last) 2010{ 2011 Bool result = False; 2012 Char *cp = *bufp; 2013 Char *next = cp; 2014 2015 (void) screen; 2016 (void) last; 2017 2018#if OPT_WIDE_CHARS 2019 if (xtermEnvUTF8() && screen->utf8_title) { 2020 PtyData data; 2021 2022 if (decodeUtf8(fakePtyData(&data, cp, last))) { 2023 if (data.utf_data != UCS_REPL 2024 && (data.utf_data >= 128 || 2025 ansi_table[data.utf_data] == CASE_PRINT)) { 2026 next += (data.utf_size - 1); 2027 result = True; 2028 } else { 2029 result = False; 2030 } 2031 } else { 2032 result = False; 2033 } 2034 } else 2035#endif 2036#if OPT_C1_PRINT 2037 if (screen->c1_printable 2038 && (*cp >= 128 && *cp < 160)) { 2039 result = True; 2040 } else 2041#endif 2042 if (ansi_table[*cp] == CASE_PRINT) { 2043 result = True; 2044 } 2045 *bufp = next; 2046 return result; 2047} 2048 2049/***====================================================================***/ 2050 2051/* 2052 * Enum corresponding to the actual OSC codes rather than the internal 2053 * array indices. 2054 */ 2055typedef enum { 2056 OSC_TEXT_FG = 10 2057 ,OSC_TEXT_BG 2058 ,OSC_TEXT_CURSOR 2059 ,OSC_MOUSE_FG 2060 ,OSC_MOUSE_BG 2061#if OPT_TEK4014 2062 ,OSC_TEK_FG = 15 2063 ,OSC_TEK_BG 2064#endif 2065#if OPT_HIGHLIGHT_COLOR 2066 ,OSC_HIGHLIGHT_BG = 17 2067#endif 2068#if OPT_TEK4014 2069 ,OSC_TEK_CURSOR = 18 2070#endif 2071#if OPT_HIGHLIGHT_COLOR 2072 ,OSC_HIGHLIGHT_FG = 19 2073#endif 2074 ,OSC_NCOLORS 2075} OscTextColors; 2076 2077static ScrnColors *pOldColors = NULL; 2078 2079static Bool 2080GetOldColors(XtermWidget xw) 2081{ 2082 int i; 2083 if (pOldColors == NULL) { 2084 pOldColors = (ScrnColors *) XtMalloc(sizeof(ScrnColors)); 2085 if (pOldColors == NULL) { 2086 fprintf(stderr, "allocation failure in GetOldColors\n"); 2087 return (False); 2088 } 2089 pOldColors->which = 0; 2090 for (i = 0; i < NCOLORS; i++) { 2091 pOldColors->colors[i] = 0; 2092 pOldColors->names[i] = NULL; 2093 } 2094 GetColors(xw, pOldColors); 2095 } 2096 return (True); 2097} 2098 2099static int 2100oppositeColor(int n) 2101{ 2102 switch (n) { 2103 case TEXT_FG: 2104 n = TEXT_BG; 2105 break; 2106 case TEXT_BG: 2107 n = TEXT_FG; 2108 break; 2109 case MOUSE_FG: 2110 n = MOUSE_BG; 2111 break; 2112 case MOUSE_BG: 2113 n = MOUSE_FG; 2114 break; 2115#if OPT_TEK4014 2116 case TEK_FG: 2117 n = TEK_BG; 2118 break; 2119 case TEK_BG: 2120 n = TEK_FG; 2121 break; 2122#endif 2123#if OPT_HIGHLIGHT_COLOR 2124 case HIGHLIGHT_FG: 2125 n = HIGHLIGHT_BG; 2126 break; 2127 case HIGHLIGHT_BG: 2128 n = HIGHLIGHT_FG; 2129 break; 2130#endif 2131 default: 2132 break; 2133 } 2134 return n; 2135} 2136 2137static void 2138ReportColorRequest(XtermWidget xw, int ndx, int final) 2139{ 2140 XColor color; 2141 Colormap cmap = xw->core.colormap; 2142 char buffer[80]; 2143 2144 /* 2145 * ChangeColorsRequest() has "always" chosen the opposite color when 2146 * reverse-video is set. Report this as the original color index, but 2147 * reporting the opposite color which would be used. 2148 */ 2149 int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx; 2150 2151 GetOldColors(xw); 2152 color.pixel = pOldColors->colors[ndx]; 2153 XQueryColor(xw->screen.display, cmap, &color); 2154 sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10, 2155 color.red, 2156 color.green, 2157 color.blue); 2158 TRACE(("ReportColors %d: %#lx as %s\n", ndx, pOldColors->colors[ndx], buffer)); 2159 unparseputc1(xw, ANSI_OSC); 2160 unparseputs(xw, buffer); 2161 unparseputc1(xw, final); 2162 unparse_end(xw); 2163} 2164 2165static Bool 2166UpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew) 2167{ 2168 int i; 2169 2170 /* if we were going to free old colors, this would be the place to 2171 * do it. I've decided not to (for now), because it seems likely 2172 * that we'd have a small set of colors we use over and over, and that 2173 * we could save some overhead this way. The only case in which this 2174 * (clearly) fails is if someone is trying a boatload of colors, in 2175 * which case they can restart xterm 2176 */ 2177 for (i = 0; i < NCOLORS; i++) { 2178 if (COLOR_DEFINED(pNew, i)) { 2179 if (pOldColors->names[i] != NULL) { 2180 XtFree(pOldColors->names[i]); 2181 pOldColors->names[i] = NULL; 2182 } 2183 if (pNew->names[i]) { 2184 pOldColors->names[i] = pNew->names[i]; 2185 } 2186 pOldColors->colors[i] = pNew->colors[i]; 2187 } 2188 } 2189 return (True); 2190} 2191 2192/* 2193 * OSC codes are constant, but the indices for the color arrays depend on how 2194 * xterm is compiled. 2195 */ 2196static int 2197OscToColorIndex(OscTextColors mode) 2198{ 2199 int result = 0; 2200 2201#define CASE(name) case OSC_##name: result = name; break 2202 switch (mode) { 2203 CASE(TEXT_FG); 2204 CASE(TEXT_BG); 2205 CASE(TEXT_CURSOR); 2206 CASE(MOUSE_FG); 2207 CASE(MOUSE_BG); 2208#if OPT_TEK4014 2209 CASE(TEK_FG); 2210 CASE(TEK_BG); 2211#endif 2212#if OPT_HIGHLIGHT_COLOR 2213 CASE(HIGHLIGHT_BG); 2214 CASE(HIGHLIGHT_FG); 2215#endif 2216#if OPT_TEK4014 2217 CASE(TEK_CURSOR); 2218#endif 2219 case OSC_NCOLORS: 2220 break; 2221 } 2222 return result; 2223} 2224 2225static Bool 2226ChangeColorsRequest(XtermWidget xw, 2227 int start, 2228 char *names, 2229 int final) 2230{ 2231 Bool result = False; 2232 char *thisName; 2233 ScrnColors newColors; 2234 int i, ndx; 2235 2236 TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names)); 2237 2238 if (GetOldColors(xw)) { 2239 newColors.which = 0; 2240 for (i = 0; i < NCOLORS; i++) { 2241 newColors.names[i] = NULL; 2242 } 2243 for (i = start; i < OSC_NCOLORS; i++) { 2244 ndx = OscToColorIndex((OscTextColors) i); 2245 if (xw->misc.re_verse) 2246 ndx = oppositeColor(ndx); 2247 2248 if ((names == NULL) || (names[0] == '\0')) { 2249 newColors.names[ndx] = NULL; 2250 } else { 2251 if (names[0] == ';') 2252 thisName = NULL; 2253 else 2254 thisName = names; 2255 names = strchr(names, ';'); 2256 if (names != NULL) { 2257 *names++ = '\0'; 2258 } 2259 if (thisName != 0 && !strcmp(thisName, "?")) { 2260 ReportColorRequest(xw, ndx, final); 2261 } else if (!pOldColors->names[ndx] 2262 || (thisName 2263 && strcmp(thisName, pOldColors->names[ndx]))) { 2264 AllocateTermColor(xw, &newColors, ndx, thisName); 2265 } 2266 } 2267 } 2268 2269 if (newColors.which != 0) { 2270 ChangeColors(xw, &newColors); 2271 UpdateOldColors(xw, &newColors); 2272 } 2273 result = True; 2274 } 2275 return result; 2276} 2277 2278/***====================================================================***/ 2279 2280void 2281do_osc(XtermWidget xw, Char * oscbuf, unsigned len GCC_UNUSED, int final) 2282{ 2283 TScreen *screen = &(xw->screen); 2284 int mode; 2285 Char *cp; 2286 int state = 0; 2287 char *buf = 0; 2288 2289 TRACE(("do_osc %s\n", oscbuf)); 2290 2291 /* 2292 * Lines should be of the form <OSC> number ; string <ST>, however 2293 * older xterms can accept <BEL> as a final character. We will respond 2294 * with the same final character as the application sends to make this 2295 * work better with shell scripts, which may have trouble reading an 2296 * <ESC><backslash>, which is the 7-bit equivalent to <ST>. 2297 */ 2298 mode = 0; 2299 for (cp = oscbuf; *cp != '\0'; cp++) { 2300 switch (state) { 2301 case 0: 2302 if (isdigit(*cp)) { 2303 mode = 10 * mode + (*cp - '0'); 2304 if (mode > 65535) { 2305 TRACE(("do_osc found unknown mode %d\n", mode)); 2306 return; 2307 } 2308 break; 2309 } 2310 /* FALLTHRU */ 2311 case 1: 2312 if (*cp != ';') { 2313 TRACE(("do_osc did not find semicolon offset %d\n", cp - oscbuf)); 2314 return; 2315 } 2316 state = 2; 2317 break; 2318 case 2: 2319 buf = (char *) cp; 2320 state = 3; 2321 /* FALLTHRU */ 2322 default: 2323 if (!xtermIsPrintable(screen, &cp, oscbuf + len)) { 2324 switch (mode) { 2325 case 0: 2326 case 1: 2327 case 2: 2328 break; 2329 default: 2330 TRACE(("do_osc found nonprinting char %02X offset %d\n", 2331 CharOf(*cp), 2332 cp - oscbuf)); 2333 return; 2334 } 2335 } 2336 } 2337 } 2338 if (buf == 0) { 2339 TRACE(("do_osc found no data\n")); 2340 return; 2341 } 2342 2343 switch (mode) { 2344 case 0: /* new icon name and title */ 2345 ChangeIconName(buf); 2346 ChangeTitle(buf); 2347 break; 2348 2349 case 1: /* new icon name only */ 2350 ChangeIconName(buf); 2351 break; 2352 2353 case 2: /* new title only */ 2354 ChangeTitle(buf); 2355 break; 2356 2357#ifdef notdef 2358 case 3: /* change X property */ 2359 if (screen->allowWindowOps) 2360 ChangeXprop(buf); 2361 break; 2362#endif 2363#if OPT_ISO_COLORS 2364 case 4: 2365 ChangeAnsiColorRequest(xw, buf, final); 2366 break; 2367#endif 2368 case OSC_TEXT_FG: 2369 case OSC_TEXT_BG: 2370 case OSC_TEXT_CURSOR: 2371 case OSC_MOUSE_FG: 2372 case OSC_MOUSE_BG: 2373#if OPT_HIGHLIGHT_COLOR 2374 case OSC_HIGHLIGHT_BG: 2375#endif 2376#if OPT_TEK4014 2377 case OSC_TEK_FG: 2378 case OSC_TEK_BG: 2379 case OSC_TEK_CURSOR: 2380#endif 2381 if (xw->misc.dynamicColors) 2382 ChangeColorsRequest(xw, mode, buf, final); 2383 break; 2384 2385 case 30: 2386 case 31: 2387 /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ 2388 break; 2389 2390#ifdef ALLOWLOGGING 2391 case 46: /* new log file */ 2392#ifdef ALLOWLOGFILECHANGES 2393 /* 2394 * Warning, enabling this feature allows people to overwrite 2395 * arbitrary files accessible to the person running xterm. 2396 */ 2397 if (buf != 0 2398 && strcmp(buf, "?") 2399 && (cp = CastMallocN(char, strlen(buf)) != NULL)) { 2400 strcpy(cp, buf); 2401 if (screen->logfile) 2402 free(screen->logfile); 2403 screen->logfile = cp; 2404 break; 2405 } 2406#endif 2407 Bell(XkbBI_Info, 0); 2408 Bell(XkbBI_Info, 0); 2409 break; 2410#endif /* ALLOWLOGGING */ 2411 2412 case 50: 2413#if OPT_SHIFT_FONTS 2414 if (!screen->allowFontOps && xw->misc.shift_fonts) { 2415 ; /* disabled via resource or control-sequence */ 2416 } else if (buf != 0 && !strcmp(buf, "?")) { 2417 int num = screen->menu_font_number; 2418 2419 unparseputc1(xw, ANSI_OSC); 2420 unparseputs(xw, "50"); 2421 2422 if ((buf = screen->MenuFontName(num)) != 0) { 2423 unparseputc(xw, ';'); 2424 unparseputs(xw, buf); 2425 } 2426 unparseputc1(xw, final); 2427 unparse_end(xw); 2428 } else if (buf != 0) { 2429 int num = screen->menu_font_number; 2430 VTFontNames fonts; 2431 2432 memset(&fonts, 0, sizeof(fonts)); 2433 2434 /* 2435 * If the font specification is a "#", followed by an 2436 * optional sign and optional number, lookup the 2437 * corresponding menu font entry. 2438 */ 2439 if (*buf == '#') { 2440 int rel = 0; 2441 2442 if (*++buf == '+') { 2443 rel = 1; 2444 buf++; 2445 } else if (*buf == '-') { 2446 rel = -1; 2447 buf++; 2448 } 2449 2450 if (isdigit(CharOf(*buf))) { 2451 int val = atoi(buf); 2452 if (rel > 0) 2453 rel = val; 2454 else if (rel < 0) 2455 rel = -val; 2456 else 2457 num = val; 2458 } else if (rel == 0) { 2459 num = 0; 2460 } 2461 2462 if (rel != 0) { 2463 num = lookupRelativeFontSize(xw, 2464 screen->menu_font_number, rel); 2465 2466 } 2467 if (num < 0 2468 || num > fontMenu_lastBuiltin 2469 || (buf = screen->MenuFontName(num)) == 0) { 2470 Bell(XkbBI_MinorError, 0); 2471 break; 2472 } 2473 } else { 2474 num = fontMenu_fontescape; 2475 } 2476 fonts.f_n = buf; 2477 SetVTFont(xw, num, True, &fonts); 2478 } 2479#endif /* OPT_SHIFT_FONTS */ 2480 break; 2481 case 51: 2482 /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ 2483 break; 2484 2485#if OPT_PASTE64 2486 case 52: 2487 if (screen->allowWindowOps) 2488 ManipulateSelectionData(xw, screen, buf, final); 2489 break; 2490#endif 2491 /* 2492 * One could write code to send back the display and host names, 2493 * but that could potentially open a fairly nasty security hole. 2494 */ 2495 } 2496 unparse_end(xw); 2497} 2498 2499#ifdef SunXK_F36 2500#define MAX_UDK 37 2501#else 2502#define MAX_UDK 35 2503#endif 2504static struct { 2505 char *str; 2506 int len; 2507} user_keys[MAX_UDK]; 2508 2509/* 2510 * Parse one nibble of a hex byte from the OSC string. We have removed the 2511 * string-terminator (replacing it with a null), so the only other delimiter 2512 * that is expected is semicolon. Ignore other characters (Ray Neuman says 2513 * "real" terminals accept commas in the string definitions). 2514 */ 2515static int 2516udk_value(char **cp) 2517{ 2518 int c; 2519 2520 for (;;) { 2521 if ((c = **cp) != '\0') 2522 *cp = *cp + 1; 2523 if (c == ';' || c == '\0') 2524 return -1; 2525 if (c >= '0' && c <= '9') 2526 return c - '0'; 2527 if (c >= 'A' && c <= 'F') 2528 return c - 'A' + 10; 2529 if (c >= 'a' && c <= 'f') 2530 return c - 'a' + 10; 2531 } 2532} 2533 2534void 2535reset_decudk(void) 2536{ 2537 int n; 2538 for (n = 0; n < MAX_UDK; n++) { 2539 if (user_keys[n].str != 0) { 2540 free(user_keys[n].str); 2541 user_keys[n].str = 0; 2542 user_keys[n].len = 0; 2543 } 2544 } 2545} 2546 2547/* 2548 * Parse the data for DECUDK (user-defined keys). 2549 */ 2550static void 2551parse_decudk(char *cp) 2552{ 2553 while (*cp) { 2554 char *base = cp; 2555 char *str = CastMallocN(char, strlen(cp) + 1); 2556 unsigned key = 0; 2557 int lo, hi; 2558 int len = 0; 2559 2560 while (isdigit(CharOf(*cp))) 2561 key = (key * 10) + (unsigned) (*cp++ - '0'); 2562 if (*cp == '/') { 2563 cp++; 2564 while ((hi = udk_value(&cp)) >= 0 2565 && (lo = udk_value(&cp)) >= 0) { 2566 str[len++] = (char) ((hi << 4) | lo); 2567 } 2568 } 2569 if (len > 0 && key < MAX_UDK) { 2570 if (user_keys[key].str != 0) 2571 free(user_keys[key].str); 2572 user_keys[key].str = str; 2573 user_keys[key].len = len; 2574 } else { 2575 free(str); 2576 } 2577 if (*cp == ';') 2578 cp++; 2579 if (cp == base) /* badly-formed sequence - bail out */ 2580 break; 2581 } 2582} 2583 2584#if OPT_TRACE 2585#define SOFT_WIDE 10 2586#define SOFT_HIGH 20 2587 2588static void 2589parse_decdld(ANSI * params, char *string) 2590{ 2591 char DscsName[8]; 2592 int len; 2593 int Pfn = params->a_param[0]; 2594 int Pcn = params->a_param[1]; 2595 int Pe = params->a_param[2]; 2596 int Pcmw = params->a_param[3]; 2597 int Pw = params->a_param[4]; 2598 int Pt = params->a_param[5]; 2599 int Pcmh = params->a_param[6]; 2600 int Pcss = params->a_param[7]; 2601 2602 int start_char = Pcn + 0x20; 2603 int char_wide = ((Pcmw == 0) 2604 ? (Pcss ? 6 : 10) 2605 : (Pcmw > 4 2606 ? Pcmw 2607 : (Pcmw + 3))); 2608 int char_high = ((Pcmh == 0) 2609 ? ((Pcmw >= 2 || Pcmw <= 4) 2610 ? 10 2611 : 20) 2612 : Pcmh); 2613 Char ch; 2614 Char bits[SOFT_HIGH][SOFT_WIDE]; 2615 Bool first = True; 2616 Bool prior = False; 2617 int row = 0, col = 0; 2618 2619 TRACE(("Parsing DECDLD\n")); 2620 TRACE((" font number %d\n", Pfn)); 2621 TRACE((" starting char %d\n", Pcn)); 2622 TRACE((" erase control %d\n", Pe)); 2623 TRACE((" char-width %d\n", Pcmw)); 2624 TRACE((" font-width %d\n", Pw)); 2625 TRACE((" text/full %d\n", Pt)); 2626 TRACE((" char-height %d\n", Pcmh)); 2627 TRACE((" charset-size %d\n", Pcss)); 2628 2629 if (Pfn > 1 2630 || Pcn > 95 2631 || Pe > 2 2632 || Pcmw > 10 2633 || Pcmw == 1 2634 || Pt > 2 2635 || Pcmh > 20 2636 || Pcss > 1 2637 || char_wide > SOFT_WIDE 2638 || char_high > SOFT_HIGH) { 2639 TRACE(("DECDLD illegal parameter\n")); 2640 return; 2641 } 2642 2643 len = 0; 2644 while (*string != '\0') { 2645 ch = CharOf(*string++); 2646 if (ch >= ANSI_SPA && ch <= 0x2f) { 2647 if (len < 2) 2648 DscsName[len++] = ch; 2649 } else if (ch >= 0x30 && ch <= 0x7e) { 2650 DscsName[len++] = ch; 2651 break; 2652 } 2653 } 2654 DscsName[len] = 0; 2655 TRACE((" Dscs name '%s'\n", DscsName)); 2656 2657 TRACE((" character matrix %dx%d\n", char_high, char_wide)); 2658 while (*string != '\0') { 2659 if (first) { 2660 TRACE(("Char %d:\n", start_char)); 2661 if (prior) { 2662 for (row = 0; row < char_high; ++row) { 2663 TRACE(("%.*s\n", char_wide, bits[row])); 2664 } 2665 } 2666 prior = False; 2667 first = False; 2668 for (row = 0; row < char_high; ++row) { 2669 for (col = 0; col < char_wide; ++col) { 2670 bits[row][col] = '.'; 2671 } 2672 } 2673 row = col = 0; 2674 } 2675 ch = CharOf(*string++); 2676 if (ch >= 0x3f && ch <= 0x7e) { 2677 int n; 2678 2679 ch -= 0x3f; 2680 for (n = 0; n < 6; ++n) { 2681 bits[row + n][col] = (ch & (1 << n)) ? '*' : '.'; 2682 } 2683 col += 1; 2684 prior = True; 2685 } else if (ch == '/') { 2686 row += 6; 2687 col = 0; 2688 } else if (ch == ';') { 2689 first = True; 2690 ++start_char; 2691 } 2692 } 2693} 2694#else 2695#define parse_decdld(p,q) /* nothing */ 2696#endif 2697 2698/* 2699 * Parse numeric parameters. Normally we use a state machine to simplify 2700 * interspersing with control characters, but have the string already. 2701 */ 2702static void 2703parse_ansi_params(ANSI * params, char **string) 2704{ 2705 char *cp = *string; 2706 short nparam = 0; 2707 2708 memset(params, 0, sizeof(*params)); 2709 while (*cp != '\0') { 2710 Char ch = CharOf(*cp++); 2711 2712 if (isdigit(ch)) { 2713 if (nparam < NPARAM) { 2714 params->a_param[nparam] *= 10; 2715 params->a_param[nparam] += (ch - '0'); 2716 } 2717 } else if (ch == ';') { 2718 if (++nparam < NPARAM) 2719 params->a_nparam = nparam; 2720 } else if (ch < 32) { 2721 ; 2722 } else { 2723 /* should be 0x30 to 0x7e */ 2724 params->a_final = ch; 2725 break; 2726 } 2727 } 2728 *string = cp; 2729} 2730 2731void 2732do_dcs(XtermWidget xw, Char * dcsbuf, size_t dcslen) 2733{ 2734 TScreen *screen = &xw->screen; 2735 char reply[BUFSIZ]; 2736 char *cp = (char *) dcsbuf; 2737 Bool okay; 2738 ANSI params; 2739 2740 TRACE(("do_dcs(%s:%d)\n", (char *) dcsbuf, dcslen)); 2741 2742 if (dcslen != strlen(cp)) 2743 /* shouldn't have nulls in the string */ 2744 return; 2745 2746 switch (*cp) { /* intermediate character, or parameter */ 2747 case '$': /* DECRQSS */ 2748 okay = True; 2749 2750 cp++; 2751 if (*cp++ == 'q') { 2752 if (!strcmp(cp, "\"q")) { /* DECSCA */ 2753 sprintf(reply, "%d%s", 2754 (screen->protected_mode == DEC_PROTECT) 2755 && (xw->flags & PROTECTED) ? 1 : 0, 2756 cp); 2757 } else if (!strcmp(cp, "\"p")) { /* DECSCL */ 2758 sprintf(reply, "%d%s%s", 2759 (screen->vtXX_level ? 2760 screen->vtXX_level : 1) + 60, 2761 (screen->vtXX_level >= 2) 2762 ? (screen->control_eight_bits 2763 ? ";0" : ";1") 2764 : "", 2765 cp); 2766 } else if (!strcmp(cp, "r")) { /* DECSTBM */ 2767 sprintf(reply, "%d;%dr", 2768 screen->top_marg + 1, 2769 screen->bot_marg + 1); 2770 } else if (!strcmp(cp, "m")) { /* SGR */ 2771 strcpy(reply, "0"); 2772 if (xw->flags & BOLD) 2773 strcat(reply, ";1"); 2774 if (xw->flags & UNDERLINE) 2775 strcat(reply, ";4"); 2776 if (xw->flags & BLINK) 2777 strcat(reply, ";5"); 2778 if (xw->flags & INVERSE) 2779 strcat(reply, ";7"); 2780 if (xw->flags & INVISIBLE) 2781 strcat(reply, ";8"); 2782 if_OPT_EXT_COLORS(screen, { 2783 if (xw->flags & FG_COLOR) { 2784 if (xw->cur_foreground >= 16) 2785 sprintf(reply + strlen(reply), 2786 ";38;5;%d", xw->cur_foreground); 2787 else 2788 sprintf(reply + strlen(reply), 2789 ";%d%d", 2790 xw->cur_foreground >= 8 ? 9 : 3, 2791 xw->cur_foreground >= 8 ? 2792 xw->cur_foreground - 8 : 2793 xw->cur_foreground); 2794 } 2795 if (xw->flags & BG_COLOR) { 2796 if (xw->cur_background >= 16) 2797 sprintf(reply + strlen(reply), 2798 ";48;5;%d", xw->cur_foreground); 2799 else 2800 sprintf(reply + strlen(reply), 2801 ";%d%d", 2802 xw->cur_background >= 8 ? 10 : 4, 2803 xw->cur_background >= 8 ? 2804 xw->cur_background - 8 : 2805 xw->cur_background); 2806 } 2807 }); 2808 if_OPT_ISO_TRADITIONAL_COLORS(screen, { 2809 if (xw->flags & FG_COLOR) 2810 sprintf(reply + strlen(reply), 2811 ";%d%d", 2812 xw->cur_foreground >= 8 ? 9 : 3, 2813 xw->cur_foreground >= 8 ? 2814 xw->cur_foreground - 8 : 2815 xw->cur_foreground); 2816 if (xw->flags & BG_COLOR) 2817 sprintf(reply + strlen(reply), 2818 ";%d%d", 2819 xw->cur_background >= 8 ? 10 : 4, 2820 xw->cur_background >= 8 ? 2821 xw->cur_background - 8 : 2822 xw->cur_background); 2823 }); 2824 strcat(reply, "m"); 2825 } else 2826 okay = False; 2827 2828 if (okay) { 2829 unparseputc1(xw, ANSI_DCS); 2830 unparseputc(xw, okay ? '1' : '0'); 2831 unparseputc(xw, '$'); 2832 unparseputc(xw, 'r'); 2833 cp = reply; 2834 unparseputs(xw, cp); 2835 unparseputc1(xw, ANSI_ST); 2836 } else { 2837 unparseputc(xw, ANSI_CAN); 2838 } 2839 } else { 2840 unparseputc(xw, ANSI_CAN); 2841 } 2842 break; 2843#if OPT_TCAP_QUERY 2844 case '+': 2845 cp++; 2846 if ((*cp == 'q') && screen->allowTcapOps) { 2847 Bool fkey; 2848 unsigned state; 2849 int code; 2850 char *tmp; 2851 char *parsed = ++cp; 2852 2853 unparseputc1(xw, ANSI_DCS); 2854 2855 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 2856 2857 unparseputc(xw, code >= 0 ? '1' : '0'); 2858 2859 unparseputc(xw, '+'); 2860 unparseputc(xw, 'r'); 2861 2862 while (*cp != 0) { 2863 if (cp == parsed) 2864 break; /* no data found, error */ 2865 2866 for (tmp = cp; tmp != parsed; ++tmp) 2867 unparseputc(xw, *tmp); 2868 2869 if (code >= 0) { 2870 unparseputc(xw, '='); 2871 screen->tc_query_code = code; 2872 screen->tc_query_fkey = fkey; 2873#if OPT_ISO_COLORS 2874 /* XK_COLORS is a fake code for the "Co" entry (maximum 2875 * number of colors) */ 2876 if (code == XK_COLORS) { 2877 unparseputn(xw, NUM_ANSI_COLORS); 2878 } else 2879#endif 2880#if OPT_TCAP_FKEYS 2881 /* 2882 * First ensure that we handle the extended cursor- and 2883 * editing-keypad keys. 2884 */ 2885 if ((code <= XK_Fn(MAX_FKEY)) 2886 || xtermcapString(xw, CodeToXkey(code), 0) == 0) 2887#endif 2888 { 2889 XKeyEvent event; 2890 event.state = state; 2891 Input(xw, &event, False); 2892 } 2893 screen->tc_query_code = -1; 2894 } else { 2895 break; /* no match found, error */ 2896 } 2897 2898 cp = parsed; 2899 if (*parsed == ';') { 2900 unparseputc(xw, *parsed++); 2901 cp = parsed; 2902 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 2903 } 2904 } 2905 unparseputc1(xw, ANSI_ST); 2906 } 2907 break; 2908#endif 2909 default: 2910 if (screen->terminal_id >= 200) { /* VT220 */ 2911 parse_ansi_params(¶ms, &cp); 2912 switch (params.a_final) { 2913 case '|': /* DECUDK */ 2914 if (params.a_param[0] == 0) 2915 reset_decudk(); 2916 parse_decudk(cp); 2917 break; 2918 case '{': /* DECDLD (no '}' case though) */ 2919 parse_decdld(¶ms, cp); 2920 break; 2921 } 2922 } 2923 break; 2924 } 2925 unparse_end(xw); 2926} 2927 2928char * 2929udk_lookup(int keycode, int *len) 2930{ 2931 if (keycode >= 0 && keycode < MAX_UDK) { 2932 *len = user_keys[keycode].len; 2933 return user_keys[keycode].str; 2934 } 2935 return 0; 2936} 2937 2938static void 2939ChangeGroup(String attribute, char *value) 2940{ 2941#if OPT_WIDE_CHARS 2942 static Char *converted; /* NO_LEAKS */ 2943#endif 2944 static char empty[1]; 2945 2946 Arg args[1]; 2947 char *original = (value != 0) ? value : empty; 2948 char *name = original; 2949 TScreen *screen = TScreenOf(term); 2950 Widget w = CURRENT_EMU(); 2951 Widget top = SHELL_OF(w); 2952 unsigned limit = strlen(name); 2953 Char *c1 = (Char *) original; 2954 Char *cp; 2955 2956 TRACE(("ChangeGroup(attribute=%s, value=%s)\n", attribute, name)); 2957 2958 if (!screen->allowTitleOps) 2959 return; 2960 2961 /* 2962 * Ignore titles that are too long to be plausible requests. 2963 */ 2964 if (limit >= 1024) 2965 return; 2966 2967 for (cp = c1; *cp != 0; ++cp) { 2968 Char *c2 = cp; 2969 if (!xtermIsPrintable(screen, &cp, c1 + limit)) { 2970 memset(c2, '?', (unsigned) (cp + 1 - c2)); 2971 } 2972 } 2973 2974#if OPT_WIDE_CHARS 2975 /* 2976 * Title strings are limited to ISO-8859-1, which is consistent with the 2977 * printable data in sos_table. However, if we're running in UTF-8 mode, 2978 * it is likely that non-ASCII text in the string will be rejected because 2979 * it is not printable in the current locale. So we convert it to UTF-8, 2980 * allowing the X library to convert it back. 2981 */ 2982 if (xtermEnvUTF8() && !screen->utf8_title) { 2983 int n; 2984 2985 for (n = 0; name[n] != '\0'; ++n) { 2986 if (CharOf(name[n]) > 127) { 2987 if (converted != 0) 2988 free(converted); 2989 if ((converted = TypeMallocN(Char, 1 + (5 * limit))) != 0) { 2990 Char *temp = converted; 2991 while (*name != 0) { 2992 temp = convertToUTF8(temp, CharOf(*name)); 2993 ++name; 2994 } 2995 *temp = 0; 2996 name = (char *) converted; 2997 TRACE(("...converted{%s}\n", name)); 2998 } 2999 break; 3000 } 3001 } 3002 } 3003#endif 3004 3005#if OPT_SAME_NAME 3006 /* If the attribute isn't going to change, then don't bother... */ 3007 3008 if (resource.sameName) { 3009 char *buf; 3010 XtSetArg(args[0], attribute, &buf); 3011 XtGetValues(top, args, 1); 3012 TRACE(("...comparing{%s}\n", buf)); 3013 if (strcmp(name, buf) == 0) 3014 return; 3015 } 3016#endif /* OPT_SAME_NAME */ 3017 3018 TRACE(("...updating %s\n", attribute)); 3019 TRACE(("...value is %s\n", name)); 3020 XtSetArg(args[0], attribute, name); 3021 XtSetValues(top, args, 1); 3022 3023#if OPT_WIDE_CHARS 3024 if (xtermEnvUTF8()) { 3025 Display *dpy = XtDisplay(term); 3026 Atom my_atom; 3027 3028 const char *propname = (!strcmp(attribute, XtNtitle) 3029 ? "_NET_WM_NAME" 3030 : "_NET_WM_ICON_NAME"); 3031 if ((my_atom = XInternAtom(dpy, propname, False)) != None) { 3032 if (screen->utf8_title) { /* FIXME - redundant? */ 3033 TRACE(("...updating %s\n", propname)); 3034 TRACE(("...value is %s\n", original)); 3035 XChangeProperty(dpy, VShellWindow, 3036 my_atom, XA_UTF8_STRING(dpy), 8, 3037 PropModeReplace, 3038 (Char *) original, (int) strlen(original)); 3039 } else { 3040 TRACE(("...deleting %s\n", propname)); 3041 XDeleteProperty(dpy, VShellWindow, my_atom); 3042 } 3043 } 3044 } 3045#endif 3046} 3047 3048void 3049ChangeIconName(char *name) 3050{ 3051 if (name == 0) 3052 name = ""; 3053#if OPT_ZICONBEEP /* If warning should be given then give it */ 3054 if (resource.zIconBeep && term->screen.zIconBeep_flagged) { 3055 char *newname = CastMallocN(char, strlen(name) + 4); 3056 if (!newname) { 3057 fprintf(stderr, "malloc failed in ChangeIconName\n"); 3058 return; 3059 } 3060 strcpy(newname, "*** "); 3061 strcat(newname, name); 3062 ChangeGroup(XtNiconName, newname); 3063 free(newname); 3064 } else 3065#endif /* OPT_ZICONBEEP */ 3066 ChangeGroup(XtNiconName, name); 3067} 3068 3069void 3070ChangeTitle(char *name) 3071{ 3072 ChangeGroup(XtNtitle, name); 3073} 3074 3075#define Strlen(s) strlen((char *)(s)) 3076 3077void 3078ChangeXprop(char *buf) 3079{ 3080 Display *dpy = XtDisplay(toplevel); 3081 Window w = XtWindow(toplevel); 3082 XTextProperty text_prop; 3083 Atom aprop; 3084 Char *pchEndPropName = (Char *) strchr(buf, '='); 3085 3086 if (pchEndPropName) 3087 *pchEndPropName = '\0'; 3088 aprop = XInternAtom(dpy, buf, False); 3089 if (pchEndPropName == NULL) { 3090 /* no "=value" given, so delete the property */ 3091 XDeleteProperty(dpy, w, aprop); 3092 } else { 3093 text_prop.value = pchEndPropName + 1; 3094 text_prop.encoding = XA_STRING; 3095 text_prop.format = 8; 3096 text_prop.nitems = Strlen(text_prop.value); 3097 XSetTextProperty(dpy, w, &text_prop, aprop); 3098 } 3099} 3100 3101/***====================================================================***/ 3102 3103/* 3104 * This is part of ReverseVideo(). It reverses the data stored for the old 3105 * "dynamic" colors that might have been retrieved using OSC 10-18. 3106 */ 3107void 3108ReverseOldColors(void) 3109{ 3110 ScrnColors *pOld = pOldColors; 3111 Pixel tmpPix; 3112 char *tmpName; 3113 3114 if (pOld) { 3115 /* change text cursor, if necesary */ 3116 if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { 3117 pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; 3118 if (pOld->names[TEXT_CURSOR]) { 3119 XtFree(pOldColors->names[TEXT_CURSOR]); 3120 pOld->names[TEXT_CURSOR] = NULL; 3121 } 3122 if (pOld->names[TEXT_BG]) { 3123 if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { 3124 pOld->names[TEXT_CURSOR] = tmpName; 3125 } 3126 } 3127 } 3128 3129 EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); 3130 EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); 3131 3132 EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); 3133 EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); 3134 3135#if OPT_TEK4014 3136 EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); 3137 EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); 3138#endif 3139 } 3140 return; 3141} 3142 3143Bool 3144AllocateTermColor(XtermWidget xw, 3145 ScrnColors * pNew, 3146 int ndx, 3147 const char *name) 3148{ 3149 XColor def; 3150 TScreen *screen = &xw->screen; 3151 Colormap cmap = xw->core.colormap; 3152 char *newName; 3153 3154 if (XParseColor(screen->display, cmap, name, &def) 3155 && (XAllocColor(screen->display, cmap, &def) 3156 || find_closest_color(screen->display, cmap, &def)) 3157 && (newName = x_strdup(name)) != 0) { 3158 if (COLOR_DEFINED(pNew, ndx)) 3159 free(pNew->names[ndx]); 3160 SET_COLOR_VALUE(pNew, ndx, def.pixel); 3161 SET_COLOR_NAME(pNew, ndx, newName); 3162 TRACE(("AllocateTermColor #%d: %s (pixel %#lx)\n", ndx, newName, def.pixel)); 3163 return (True); 3164 } 3165 TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); 3166 return (False); 3167} 3168/***====================================================================***/ 3169 3170/* ARGSUSED */ 3171void 3172Panic(char *s GCC_UNUSED, int a GCC_UNUSED) 3173{ 3174#ifdef DEBUG 3175 if (debug) { 3176 fprintf(stderr, "%s: PANIC!\t", xterm_name); 3177 fprintf(stderr, s, a); 3178 fputs("\r\n", stderr); 3179 fflush(stderr); 3180 } 3181#endif /* DEBUG */ 3182} 3183 3184const char * 3185SysErrorMsg(int code) 3186{ 3187 static char unknown[] = "unknown error"; 3188 char *s = strerror(code); 3189 return s ? s : unknown; 3190} 3191 3192const char * 3193SysReasonMsg(int code) 3194{ 3195 /* *INDENT-OFF* */ 3196 static const struct { 3197 int code; 3198 const char *name; 3199 } table[] = { 3200 { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" }, 3201 { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" }, 3202 { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", }, 3203 { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", }, 3204 { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", }, 3205 { ERROR_PTSNAME, "spawn: ptsname() failed", }, 3206 { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", }, 3207 { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" }, 3208 { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" }, 3209 { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" }, 3210 { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" }, 3211 { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" }, 3212 { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" }, 3213 { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" }, 3214 { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" }, 3215 { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" }, 3216 { ERROR_INIGROUPS, "spawn: initgroups() failed" }, 3217 { ERROR_FORK, "spawn: fork() failed" }, 3218 { ERROR_EXEC, "spawn: exec() failed" }, 3219 { ERROR_PTYS, "get_pty: not enough ptys" }, 3220 { ERROR_PTY_EXEC, "waiting for initial map" }, 3221 { ERROR_SETUID, "spawn: setuid() failed" }, 3222 { ERROR_INIT, "spawn: can't initialize window" }, 3223 { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" }, 3224 { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" }, 3225 { ERROR_SPREALLOC, "spawn: realloc of ttydev failed" }, 3226 { ERROR_LUMALLOC, "luit: command-line malloc failed" }, 3227 { ERROR_SELECT, "in_put: select() failed" }, 3228 { ERROR_VINIT, "VTInit: can't initialize window" }, 3229 { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" }, 3230 { ERROR_TSELECT, "Tinput: select() failed" }, 3231 { ERROR_TINIT, "TekInit: can't initialize window" }, 3232 { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" }, 3233 { ERROR_LOGEXEC, "StartLog: exec() failed" }, 3234 { ERROR_XERROR, "xerror: XError event" }, 3235 { ERROR_XIOERROR, "xioerror: X I/O error" }, 3236 { ERROR_SCALLOC, "Alloc: calloc() failed on base" }, 3237 { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" }, 3238 { ERROR_SREALLOC, "ScreenResize: realloc() failed on alt base" }, 3239 { ERROR_RESIZE, "ScreenResize: malloc() or realloc() failed" }, 3240 { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" }, 3241 { ERROR_SBRALLOC, "ScrollBarOn: realloc() failed on base" }, 3242 { ERROR_SBRALLOC2, "ScrollBarOn: realloc() failed on rows" }, 3243 { ERROR_MMALLOC, "my_memmove: malloc/realloc failed" }, 3244 }; 3245 /* *INDENT-ON* */ 3246 3247 Cardinal n; 3248 const char *result = "?"; 3249 3250 for (n = 0; n < XtNumber(table); ++n) { 3251 if (code == table[n].code) { 3252 result = table[n].name; 3253 break; 3254 } 3255 } 3256 return result; 3257} 3258 3259void 3260SysError(int code) 3261{ 3262 int oerrno = errno; 3263 3264 fprintf(stderr, "%s: Error %d, errno %d: ", xterm_name, code, oerrno); 3265 fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); 3266 fprintf(stderr, "Reason: %s\n", SysReasonMsg(code)); 3267 3268 Cleanup(code); 3269} 3270 3271/* 3272 * cleanup by sending SIGHUP to client processes 3273 */ 3274void 3275Cleanup(int code) 3276{ 3277 static Bool cleaning; 3278 TScreen *screen = TScreenOf(term); 3279 3280 /* 3281 * Process "-hold" and session cleanup only for a normal exit. 3282 */ 3283 if (code == 0) { 3284 if (cleaning) { 3285 hold_screen = 0; 3286 return; 3287 } 3288 3289 cleaning = True; 3290 need_cleanup = False; 3291 3292 TRACE(("Cleanup %d\n", code)); 3293 3294 if (hold_screen) { 3295 hold_screen = 2; 3296 while (hold_screen) { 3297 xevents(); 3298 Sleep(10); 3299 } 3300 } 3301#if OPT_SESSION_MGT 3302 if (resource.sessionMgt) { 3303 XtVaSetValues(toplevel, 3304 XtNjoinSession, False, 3305 (XtPointer *) 0); 3306 } 3307#endif 3308 } 3309 3310 if (screen->pid > 1) { 3311 (void) kill_process_group(screen->pid, SIGHUP); 3312 } 3313 Exit(code); 3314} 3315 3316#ifndef VMS 3317char * 3318xtermFindShell(char *leaf, Bool warning) 3319{ 3320 char *s; 3321 char *d; 3322 char *tmp; 3323 char *result = leaf; 3324 3325 TRACE(("xtermFindShell(%s)\n", leaf)); 3326 if (*result != '\0' && strchr("+/-", *result) == 0) { 3327 /* find it in $PATH */ 3328 if ((s = x_getenv("PATH")) != 0) { 3329 if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) { 3330 Bool found = False; 3331 while (*s != '\0') { 3332 strcpy(tmp, s); 3333 for (d = tmp;; ++d) { 3334 if (*d == ':' || *d == '\0') { 3335 int skip = (*d != '\0'); 3336 *d = '/'; 3337 strcpy(d + 1, leaf); 3338 if (skip) 3339 ++d; 3340 s += (d - tmp); 3341 if (*tmp == '/' 3342 && strstr(tmp, "..") == 0 3343 && access(tmp, X_OK) == 0) { 3344 result = x_strdup(tmp); 3345 found = True; 3346 } 3347 break; 3348 } 3349 if (found) 3350 break; 3351 } 3352 if (found) 3353 break; 3354 } 3355 free(tmp); 3356 } 3357 } 3358 } 3359 TRACE(("...xtermFindShell(%s)\n", result)); 3360 if (*result != '/' 3361 || strstr(result, "..") != 0 3362 || access(result, X_OK) != 0) { 3363 if (warning) 3364 fprintf(stderr, "No absolute path found for shell: %s\n", result); 3365 result = 0; 3366 } 3367 return result; 3368} 3369#endif /* VMS */ 3370 3371#define ENV_HUNK(n) (unsigned) ((((n) + 1) | 31) + 1) 3372 3373/* 3374 * copy the environment before Setenv'ing. 3375 */ 3376void 3377xtermCopyEnv(char **oldenv) 3378{ 3379 unsigned size; 3380 char **newenv; 3381 3382 for (size = 0; oldenv[size] != NULL; size++) { 3383 ; 3384 } 3385 3386 newenv = TypeCallocN(char *, ENV_HUNK(size)); 3387 memmove(newenv, oldenv, size * sizeof(char *)); 3388 environ = newenv; 3389} 3390 3391/* 3392 * sets the value of var to be arg in the Unix 4.2 BSD environment env. 3393 * Var should end with '=' (bindings are of the form "var=value"). 3394 * This procedure assumes the memory for the first level of environ 3395 * was allocated using calloc, with enough extra room at the end so not 3396 * to have to do a realloc(). 3397 */ 3398void 3399xtermSetenv(char *var, char *value) 3400{ 3401 if (value != 0) { 3402 char *test; 3403 int envindex = 0; 3404 size_t len = strlen(var); 3405 int found = -1; 3406 3407 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 3408 3409 while ((test = environ[envindex]) != NULL) { 3410 if (strncmp(test, var, len) == 0 && test[len] == '=') { 3411 found = envindex; 3412 break; 3413 } 3414 envindex++; 3415 } 3416 3417 if (found < 0) { 3418 unsigned need = ENV_HUNK(envindex + 1); 3419 unsigned have = ENV_HUNK(envindex); 3420 3421 if (need > have) { 3422 char **newenv; 3423 newenv = TypeMallocN(char *, need); 3424 if (newenv == 0) { 3425 fprintf(stderr, "Cannot increase environment\n"); 3426 return; 3427 } 3428 memmove(newenv, environ, have * sizeof(*newenv)); 3429 free(environ); 3430 environ = newenv; 3431 } 3432 3433 found = envindex; 3434 environ[found + 1] = NULL; 3435 environ = environ; 3436 } 3437 3438 environ[found] = CastMallocN(char, 1 + len + strlen(value)); 3439 if (environ[found] == 0) { 3440 fprintf(stderr, "Cannot allocate environment %s\n", var); 3441 return; 3442 } 3443 sprintf(environ[found], "%s=%s", var, value); 3444 } 3445} 3446 3447/*ARGSUSED*/ 3448int 3449xerror(Display * d, XErrorEvent * ev) 3450{ 3451 fprintf(stderr, "%s: warning, error event received:\n", xterm_name); 3452 (void) XmuPrintDefaultErrorMessage(d, ev, stderr); 3453 Exit(ERROR_XERROR); 3454 return 0; /* appease the compiler */ 3455} 3456 3457/*ARGSUSED*/ 3458int 3459xioerror(Display * dpy) 3460{ 3461 int the_error = errno; 3462 3463 (void) fprintf(stderr, 3464 "%s: fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", 3465 xterm_name, the_error, SysErrorMsg(the_error), 3466 DisplayString(dpy)); 3467 3468 Exit(ERROR_XIOERROR); 3469 return 0; /* appease the compiler */ 3470} 3471 3472void 3473xt_error(String message) 3474{ 3475 (void) fprintf(stderr, "%s Xt error: %s\n", ProgramName, message); 3476 3477 /* 3478 * Check for the obvious - Xt does a poor job of reporting this. 3479 */ 3480 if (x_getenv("DISPLAY") == 0) { 3481 fprintf(stderr, "%s: DISPLAY is not set\n", ProgramName); 3482 } 3483 exit(1); 3484} 3485 3486int 3487XStrCmp(char *s1, char *s2) 3488{ 3489 if (s1 && s2) 3490 return (strcmp(s1, s2)); 3491 if (s1 && *s1) 3492 return (1); 3493 if (s2 && *s2) 3494 return (-1); 3495 return (0); 3496} 3497 3498#if OPT_TEK4014 3499static void 3500withdraw_window(Display * dpy, Window w, int scr) 3501{ 3502 TRACE(("withdraw_window %#lx\n", (long) w)); 3503 (void) XmuUpdateMapHints(dpy, w, NULL); 3504 XWithdrawWindow(dpy, w, scr); 3505 return; 3506} 3507#endif 3508 3509void 3510set_vt_visibility(Bool on) 3511{ 3512 TScreen *screen = TScreenOf(term); 3513 3514 TRACE(("set_vt_visibility(%d)\n", on)); 3515 if (on) { 3516 if (!screen->Vshow && term) { 3517 VTInit(); 3518 XtMapWidget(XtParent(term)); 3519#if OPT_TOOLBAR 3520 /* we need both of these during initialization */ 3521 XtMapWidget(SHELL_OF(term)); 3522 ShowToolbar(resource.toolBar); 3523#endif 3524 screen->Vshow = True; 3525 } 3526 } 3527#if OPT_TEK4014 3528 else { 3529 if (screen->Vshow && term) { 3530 withdraw_window(XtDisplay(term), 3531 VShellWindow, 3532 XScreenNumberOfScreen(XtScreen(term))); 3533 screen->Vshow = False; 3534 } 3535 } 3536 set_vthide_sensitivity(); 3537 set_tekhide_sensitivity(); 3538 update_vttekmode(); 3539 update_tekshow(); 3540 update_vtshow(); 3541#endif 3542 return; 3543} 3544 3545#if OPT_TEK4014 3546void 3547set_tek_visibility(Bool on) 3548{ 3549 TRACE(("set_tek_visibility(%d)\n", on)); 3550 3551 if (on) { 3552 if (!TEK4014_SHOWN(term) && (tekWidget || TekInit())) { 3553 Widget tekParent = SHELL_OF(tekWidget); 3554 XtRealizeWidget(tekParent); 3555 XtMapWidget(XtParent(tekWidget)); 3556#if OPT_TOOLBAR 3557 /* we need both of these during initialization */ 3558 XtMapWidget(tekParent); 3559 XtMapWidget(tekWidget); 3560#endif 3561 XtOverrideTranslations(tekParent, 3562 XtParseTranslationTable 3563 ("<Message>WM_PROTOCOLS: DeleteWindow()")); 3564 (void) XSetWMProtocols(XtDisplay(tekParent), 3565 XtWindow(tekParent), 3566 &wm_delete_window, 1); 3567 TEK4014_SHOWN(term) = True; 3568 } 3569 } else { 3570 if (TEK4014_SHOWN(term) && tekWidget) { 3571 withdraw_window(XtDisplay(tekWidget), 3572 TShellWindow, 3573 XScreenNumberOfScreen(XtScreen(tekWidget))); 3574 TEK4014_SHOWN(term) = False; 3575 } 3576 } 3577 set_tekhide_sensitivity(); 3578 set_vthide_sensitivity(); 3579 update_vtshow(); 3580 update_tekshow(); 3581 update_vttekmode(); 3582 return; 3583} 3584 3585void 3586end_tek_mode(void) 3587{ 3588 if (TEK4014_ACTIVE(term)) { 3589 FlushLog(&(term->screen)); 3590 longjmp(Tekend, 1); 3591 } 3592 return; 3593} 3594 3595void 3596end_vt_mode(void) 3597{ 3598 if (!TEK4014_ACTIVE(term)) { 3599 FlushLog(&(term->screen)); 3600 TEK4014_ACTIVE(term) = True; 3601 longjmp(VTend, 1); 3602 } 3603 return; 3604} 3605 3606void 3607switch_modes(Bool tovt) /* if true, then become vt mode */ 3608{ 3609 if (tovt) { 3610 if (tekRefreshList) 3611 TekRefresh(tekWidget); 3612 end_tek_mode(); /* WARNING: this does a longjmp... */ 3613 } else { 3614 end_vt_mode(); /* WARNING: this does a longjmp... */ 3615 } 3616} 3617 3618void 3619hide_vt_window(void) 3620{ 3621 set_vt_visibility(False); 3622 if (!TEK4014_ACTIVE(term)) 3623 switch_modes(False); /* switch to tek mode */ 3624} 3625 3626void 3627hide_tek_window(void) 3628{ 3629 set_tek_visibility(False); 3630 tekRefreshList = (TekLink *) 0; 3631 if (TEK4014_ACTIVE(term)) 3632 switch_modes(True); /* does longjmp to vt mode */ 3633} 3634#endif /* OPT_TEK4014 */ 3635 3636static const char * 3637skip_punct(const char *s) 3638{ 3639 while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { 3640 ++s; 3641 } 3642 return s; 3643} 3644 3645static int 3646cmp_options(const void *a, const void *b) 3647{ 3648 const char *s1 = skip_punct(((const OptionHelp *) a)->opt); 3649 const char *s2 = skip_punct(((const OptionHelp *) b)->opt); 3650 return strcmp(s1, s2); 3651} 3652 3653static int 3654cmp_resources(const void *a, const void *b) 3655{ 3656 return strcmp(((const XrmOptionDescRec *) a)->option, 3657 ((const XrmOptionDescRec *) b)->option); 3658} 3659 3660XrmOptionDescRec * 3661sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) 3662{ 3663 static XrmOptionDescRec *res_array = 0; 3664 3665#ifdef NO_LEAKS 3666 if (descs == 0 && res_array != 0) { 3667 free(res_array); 3668 res_array = 0; 3669 } else 3670#endif 3671 if (res_array == 0) { 3672 Cardinal j; 3673 3674 /* make a sorted index to 'resources' */ 3675 res_array = TypeCallocN(XrmOptionDescRec, res_count); 3676 for (j = 0; j < res_count; j++) 3677 res_array[j] = descs[j]; 3678 qsort(res_array, res_count, sizeof(*res_array), cmp_resources); 3679 } 3680 return res_array; 3681} 3682 3683/* 3684 * The first time this is called, construct sorted index to the main program's 3685 * list of options, taking into account the on/off options which will be 3686 * compressed into one token. It's a lot simpler to do it this way than 3687 * maintain the list in sorted form with lots of ifdef's. 3688 */ 3689OptionHelp * 3690sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) 3691{ 3692 static OptionHelp *opt_array = 0; 3693 3694#ifdef NO_LEAKS 3695 if (descs == 0 && opt_array != 0) { 3696 sortedOptDescs(descs, numDescs); 3697 free(opt_array); 3698 opt_array = 0; 3699 return 0; 3700 } else if (options == 0 || descs == 0) { 3701 return 0; 3702 } 3703#endif 3704 3705 if (opt_array == 0) { 3706 Cardinal opt_count, j; 3707#if OPT_TRACE 3708 Cardinal k; 3709 XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); 3710 int code; 3711 char *mesg; 3712#else 3713 (void) descs; 3714 (void) numDescs; 3715#endif 3716 3717 /* count 'options' and make a sorted index to it */ 3718 for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { 3719 ; 3720 } 3721 opt_array = TypeCallocN(OptionHelp, opt_count + 1); 3722 for (j = 0; j < opt_count; j++) 3723 opt_array[j] = options[j]; 3724 qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); 3725 3726 /* supply the "turn on/off" strings if needed */ 3727#if OPT_TRACE 3728 for (j = 0; j < opt_count; j++) { 3729 if (!strncmp(opt_array[j].opt, "-/+", 3)) { 3730 char *name = opt_array[j].opt + 3; 3731 for (k = 0; k < numDescs; ++k) { 3732 char *value = res_array[k].value; 3733 if (res_array[k].option[0] == '-') { 3734 code = -1; 3735 } else if (res_array[k].option[0] == '+') { 3736 code = 1; 3737 } else { 3738 code = 0; 3739 } 3740 if (x_strindex(opt_array[j].desc, "inhibit") != 0) 3741 code = -code; 3742 if (code != 0 3743 && res_array[k].value != 0 3744 && !strcmp(name, res_array[k].option + 1)) { 3745 if (((code < 0) && !strcmp(value, "on")) 3746 || ((code > 0) && !strcmp(value, "off")) 3747 || ((code > 0) && !strcmp(value, "0"))) { 3748 mesg = "turn on/off"; 3749 } else { 3750 mesg = "turn off/on"; 3751 } 3752 if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) { 3753 if (strncmp(opt_array[j].desc, "turn ", 5)) { 3754 char *s = CastMallocN(char, 3755 strlen(mesg) 3756 + 1 3757 + strlen(opt_array[j].desc)); 3758 if (s != 0) { 3759 sprintf(s, "%s %s", mesg, opt_array[j].desc); 3760 opt_array[j].desc = s; 3761 } 3762 } else { 3763 TRACE(("OOPS ")); 3764 } 3765 } 3766 TRACE(("%s: %s %s: %s (%s)\n", 3767 mesg, 3768 res_array[k].option, 3769 res_array[k].value, 3770 opt_array[j].opt, 3771 opt_array[j].desc)); 3772 break; 3773 } 3774 } 3775 } 3776 } 3777#endif 3778 } 3779 return opt_array; 3780} 3781 3782/* 3783 * Report the character-type locale that xterm was started in. 3784 */ 3785char * 3786xtermEnvLocale(void) 3787{ 3788 static char *result; 3789 3790 if (result == 0) { 3791 if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) { 3792 result = "C"; 3793 } 3794 TRACE(("xtermEnvLocale ->%s\n", result)); 3795 } 3796 return result; 3797} 3798 3799char * 3800xtermEnvEncoding(void) 3801{ 3802 static char *result; 3803 3804 if (result == 0) { 3805#ifdef HAVE_LANGINFO_CODESET 3806 result = nl_langinfo(CODESET); 3807#else 3808 char *locale = xtermEnvLocale(); 3809 if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { 3810 result = "ASCII"; 3811 } else { 3812 result = "ISO-8859-1"; 3813 } 3814#endif 3815 TRACE(("xtermEnvEncoding ->%s\n", result)); 3816 } 3817 return result; 3818} 3819 3820#if OPT_WIDE_CHARS 3821/* 3822 * Tell whether xterm was started in a locale that uses UTF-8 encoding for 3823 * characters. That environment is inherited by subprocesses and used in 3824 * various library calls. 3825 */ 3826Bool 3827xtermEnvUTF8(void) 3828{ 3829 static Bool init = False; 3830 static Bool result = False; 3831 3832 if (!init) { 3833 init = True; 3834#ifdef HAVE_LANGINFO_CODESET 3835 result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); 3836#else 3837 result = (strstr(xtermEnvLocale(), "UTF-8") != NULL); 3838#endif 3839 TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); 3840 } 3841 return result; 3842} 3843#endif /* OPT_WIDE_CHARS */ 3844 3845/* 3846 * Returns the version-string used in the "-v' message as well as a few other 3847 * places. It is derived (when possible) from the __vendorversion__ symbol 3848 * that some newer imake configurations define. 3849 */ 3850char * 3851xtermVersion(void) 3852{ 3853 static char *result; 3854 if (result == 0) { 3855 char *vendor = __vendorversion__; 3856 char first[BUFSIZ]; 3857 char second[BUFSIZ]; 3858 3859 result = CastMallocN(char, strlen(vendor) + 9); 3860 if (result == 0) 3861 result = vendor; 3862 else { 3863 /* some vendors leave trash in this string */ 3864 for (;;) { 3865 if (!strncmp(vendor, "Version ", 8)) 3866 vendor += 8; 3867 else if (isspace(CharOf(*vendor))) 3868 ++vendor; 3869 else 3870 break; 3871 } 3872 if (strlen(vendor) < BUFSIZ && 3873 sscanf(vendor, "%[0-9.] %[A-Za-z_0-9.]", first, second) == 2) 3874 sprintf(result, "%s %s(%d)", second, first, XTERM_PATCH); 3875 else 3876 sprintf(result, "%s(%d)", vendor, XTERM_PATCH); 3877 } 3878 } 3879 return result; 3880} 3881