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