misc.c revision 22d8e007
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#ifdef notdef 2351 case 3: /* change X property */ 2352 ChangeXprop(buf); 2353 break; 2354#endif 2355#if OPT_ISO_COLORS 2356 case 4: 2357 ChangeAnsiColorRequest(xw, buf, final); 2358 break; 2359#endif 2360 case OSC_TEXT_FG: 2361 case OSC_TEXT_BG: 2362 case OSC_TEXT_CURSOR: 2363 case OSC_MOUSE_FG: 2364 case OSC_MOUSE_BG: 2365#if OPT_HIGHLIGHT_COLOR 2366 case OSC_HIGHLIGHT_BG: 2367#endif 2368#if OPT_TEK4014 2369 case OSC_TEK_FG: 2370 case OSC_TEK_BG: 2371 case OSC_TEK_CURSOR: 2372#endif 2373 if (xw->misc.dynamicColors) 2374 ChangeColorsRequest(xw, mode, buf, final); 2375 break; 2376 2377 case 30: 2378 case 31: 2379 /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ 2380 break; 2381 2382#ifdef ALLOWLOGGING 2383 case 46: /* new log file */ 2384#ifdef ALLOWLOGFILECHANGES 2385 /* 2386 * Warning, enabling this feature allows people to overwrite 2387 * arbitrary files accessible to the person running xterm. 2388 */ 2389 if (buf != 0 2390 && strcmp(buf, "?") 2391 && (cp = CastMallocN(char, strlen(buf)) != NULL)) { 2392 strcpy(cp, buf); 2393 if (screen->logfile) 2394 free(screen->logfile); 2395 screen->logfile = cp; 2396 break; 2397 } 2398#endif 2399 Bell(XkbBI_Info, 0); 2400 Bell(XkbBI_Info, 0); 2401 break; 2402#endif /* ALLOWLOGGING */ 2403 2404 case 50: 2405#if OPT_SHIFT_FONTS 2406 if (buf != 0 && !strcmp(buf, "?")) { 2407 int num = screen->menu_font_number; 2408 2409 unparseputc1(xw, ANSI_OSC); 2410 unparseputs(xw, "50"); 2411 2412 if ((buf = screen->MenuFontName(num)) != 0) { 2413 unparseputc(xw, ';'); 2414 unparseputs(xw, buf); 2415 } 2416 unparseputc1(xw, final); 2417 unparse_end(xw); 2418 } else if (buf != 0) { 2419 int num = screen->menu_font_number; 2420 VTFontNames fonts; 2421 2422 memset(&fonts, 0, sizeof(fonts)); 2423 2424 /* 2425 * If the font specification is a "#", followed by an 2426 * optional sign and optional number, lookup the 2427 * corresponding menu font entry. 2428 */ 2429 if (*buf == '#') { 2430 int rel = 0; 2431 2432 if (*++buf == '+') { 2433 rel = 1; 2434 buf++; 2435 } else if (*buf == '-') { 2436 rel = -1; 2437 buf++; 2438 } 2439 2440 if (isdigit(CharOf(*buf))) { 2441 int val = atoi(buf); 2442 if (rel > 0) 2443 rel = val; 2444 else if (rel < 0) 2445 rel = -val; 2446 else 2447 num = val; 2448 } else if (rel == 0) { 2449 num = 0; 2450 } 2451 2452 if (rel != 0) { 2453 num = lookupRelativeFontSize(xw, 2454 screen->menu_font_number, rel); 2455 2456 } 2457 if (num < 0 2458 || num > fontMenu_lastBuiltin 2459 || (buf = screen->MenuFontName(num)) == 0) { 2460 Bell(XkbBI_MinorError, 0); 2461 break; 2462 } 2463 } else { 2464 num = fontMenu_fontescape; 2465 } 2466 fonts.f_n = buf; 2467 SetVTFont(xw, num, True, &fonts); 2468 } 2469#endif /* OPT_SHIFT_FONTS */ 2470 break; 2471 case 51: 2472 /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ 2473 break; 2474 2475#if OPT_PASTE64 2476 case 52: 2477 if (screen->allowWindowOps && (buf != 0)) 2478 ManipulateSelectionData(xw, screen, buf, final); 2479 break; 2480#endif 2481 /* 2482 * One could write code to send back the display and host names, 2483 * but that could potentially open a fairly nasty security hole. 2484 */ 2485 } 2486 unparse_end(xw); 2487} 2488 2489#ifdef SunXK_F36 2490#define MAX_UDK 37 2491#else 2492#define MAX_UDK 35 2493#endif 2494static struct { 2495 char *str; 2496 int len; 2497} user_keys[MAX_UDK]; 2498 2499/* 2500 * Parse one nibble of a hex byte from the OSC string. We have removed the 2501 * string-terminator (replacing it with a null), so the only other delimiter 2502 * that is expected is semicolon. Ignore other characters (Ray Neuman says 2503 * "real" terminals accept commas in the string definitions). 2504 */ 2505static int 2506udk_value(char **cp) 2507{ 2508 int c; 2509 2510 for (;;) { 2511 if ((c = **cp) != '\0') 2512 *cp = *cp + 1; 2513 if (c == ';' || c == '\0') 2514 return -1; 2515 if (c >= '0' && c <= '9') 2516 return c - '0'; 2517 if (c >= 'A' && c <= 'F') 2518 return c - 'A' + 10; 2519 if (c >= 'a' && c <= 'f') 2520 return c - 'a' + 10; 2521 } 2522} 2523 2524void 2525reset_decudk(void) 2526{ 2527 int n; 2528 for (n = 0; n < MAX_UDK; n++) { 2529 if (user_keys[n].str != 0) { 2530 free(user_keys[n].str); 2531 user_keys[n].str = 0; 2532 user_keys[n].len = 0; 2533 } 2534 } 2535} 2536 2537/* 2538 * Parse the data for DECUDK (user-defined keys). 2539 */ 2540static void 2541parse_decudk(char *cp) 2542{ 2543 while (*cp) { 2544 char *base = cp; 2545 char *str = CastMallocN(char, strlen(cp) + 1); 2546 unsigned key = 0; 2547 int lo, hi; 2548 int len = 0; 2549 2550 while (isdigit(CharOf(*cp))) 2551 key = (key * 10) + (*cp++ - '0'); 2552 if (*cp == '/') { 2553 cp++; 2554 while ((hi = udk_value(&cp)) >= 0 2555 && (lo = udk_value(&cp)) >= 0) { 2556 str[len++] = (hi << 4) | lo; 2557 } 2558 } 2559 if (len > 0 && key < MAX_UDK) { 2560 if (user_keys[key].str != 0) 2561 free(user_keys[key].str); 2562 user_keys[key].str = str; 2563 user_keys[key].len = len; 2564 } else { 2565 free(str); 2566 } 2567 if (*cp == ';') 2568 cp++; 2569 if (cp == base) /* badly-formed sequence - bail out */ 2570 break; 2571 } 2572} 2573 2574#if OPT_TRACE 2575#define SOFT_WIDE 10 2576#define SOFT_HIGH 20 2577 2578static void 2579parse_decdld(ANSI * params, char *string) 2580{ 2581 char DscsName[8]; 2582 int len; 2583 int Pfn = params->a_param[0]; 2584 int Pcn = params->a_param[1]; 2585 int Pe = params->a_param[2]; 2586 int Pcmw = params->a_param[3]; 2587 int Pw = params->a_param[4]; 2588 int Pt = params->a_param[5]; 2589 int Pcmh = params->a_param[6]; 2590 int Pcss = params->a_param[7]; 2591 2592 int start_char = Pcn + 0x20; 2593 int char_wide = ((Pcmw == 0) 2594 ? (Pcss ? 6 : 10) 2595 : (Pcmw > 4 2596 ? Pcmw 2597 : (Pcmw + 3))); 2598 int char_high = ((Pcmh == 0) 2599 ? ((Pcmw >= 2 || Pcmw <= 4) 2600 ? 10 2601 : 20) 2602 : Pcmh); 2603 Char ch; 2604 Char bits[SOFT_HIGH][SOFT_WIDE]; 2605 Bool first = True; 2606 Bool prior = False; 2607 int row = 0, col = 0; 2608 2609 TRACE(("Parsing DECDLD\n")); 2610 TRACE((" font number %d\n", Pfn)); 2611 TRACE((" starting char %d\n", Pcn)); 2612 TRACE((" erase control %d\n", Pe)); 2613 TRACE((" char-width %d\n", Pcmw)); 2614 TRACE((" font-width %d\n", Pw)); 2615 TRACE((" text/full %d\n", Pt)); 2616 TRACE((" char-height %d\n", Pcmh)); 2617 TRACE((" charset-size %d\n", Pcss)); 2618 2619 if (Pfn > 1 2620 || Pcn > 95 2621 || Pe > 2 2622 || Pcmw > 10 2623 || Pcmw == 1 2624 || Pt > 2 2625 || Pcmh > 20 2626 || Pcss > 1 2627 || char_wide > SOFT_WIDE 2628 || char_high > SOFT_HIGH) { 2629 TRACE(("DECDLD illegal parameter\n")); 2630 return; 2631 } 2632 2633 len = 0; 2634 while (*string != '\0') { 2635 ch = CharOf(*string++); 2636 if (ch >= ANSI_SPA && ch <= 0x2f) { 2637 if (len < 2) 2638 DscsName[len++] = ch; 2639 } else if (ch >= 0x30 && ch <= 0x7e) { 2640 DscsName[len++] = ch; 2641 break; 2642 } 2643 } 2644 DscsName[len] = 0; 2645 TRACE((" Dscs name '%s'\n", DscsName)); 2646 2647 TRACE((" character matrix %dx%d\n", char_high, char_wide)); 2648 while (*string != '\0') { 2649 if (first) { 2650 TRACE(("Char %d:\n", start_char)); 2651 if (prior) { 2652 for (row = 0; row < char_high; ++row) { 2653 TRACE(("%.*s\n", char_wide, bits[row])); 2654 } 2655 } 2656 prior = False; 2657 first = False; 2658 for (row = 0; row < char_high; ++row) { 2659 for (col = 0; col < char_wide; ++col) { 2660 bits[row][col] = '.'; 2661 } 2662 } 2663 row = col = 0; 2664 } 2665 ch = CharOf(*string++); 2666 if (ch >= 0x3f && ch <= 0x7e) { 2667 int n; 2668 2669 ch -= 0x3f; 2670 for (n = 0; n < 6; ++n) { 2671 bits[row + n][col] = (ch & (1 << n)) ? '*' : '.'; 2672 } 2673 col += 1; 2674 prior = True; 2675 } else if (ch == '/') { 2676 row += 6; 2677 col = 0; 2678 } else if (ch == ';') { 2679 first = True; 2680 ++start_char; 2681 } 2682 } 2683} 2684#else 2685#define parse_decdld(p,q) /* nothing */ 2686#endif 2687 2688/* 2689 * Parse numeric parameters. Normally we use a state machine to simplify 2690 * interspersing with control characters, but have the string already. 2691 */ 2692static void 2693parse_ansi_params(ANSI * params, char **string) 2694{ 2695 char *cp = *string; 2696 short nparam = 0; 2697 2698 memset(params, 0, sizeof(*params)); 2699 while (*cp != '\0') { 2700 Char ch = CharOf(*cp++); 2701 2702 if (isdigit(ch)) { 2703 if (nparam < NPARAM) { 2704 params->a_param[nparam] *= 10; 2705 params->a_param[nparam] += (ch - '0'); 2706 } 2707 } else if (ch == ';') { 2708 if (++nparam < NPARAM) 2709 params->a_nparam = nparam; 2710 } else if (ch < 32) { 2711 ; 2712 } else { 2713 /* should be 0x30 to 0x7e */ 2714 params->a_final = ch; 2715 break; 2716 } 2717 } 2718 *string = cp; 2719} 2720 2721void 2722do_dcs(XtermWidget xw, Char * dcsbuf, size_t dcslen) 2723{ 2724 TScreen *screen = &xw->screen; 2725 char reply[BUFSIZ]; 2726 char *cp = (char *) dcsbuf; 2727 Bool okay; 2728 ANSI params; 2729 2730 TRACE(("do_dcs(%s:%d)\n", (char *) dcsbuf, dcslen)); 2731 2732 if (dcslen != strlen(cp)) 2733 /* shouldn't have nulls in the string */ 2734 return; 2735 2736 switch (*cp) { /* intermediate character, or parameter */ 2737 case '$': /* DECRQSS */ 2738 okay = True; 2739 2740 cp++; 2741 if (*cp++ == 'q') { 2742 if (!strcmp(cp, "\"q")) { /* DECSCA */ 2743 sprintf(reply, "%d%s", 2744 (screen->protected_mode == DEC_PROTECT) 2745 && (xw->flags & PROTECTED) ? 1 : 0, 2746 cp); 2747 } else if (!strcmp(cp, "\"p")) { /* DECSCL */ 2748 sprintf(reply, "%d%s%s", 2749 (screen->vtXX_level ? 2750 screen->vtXX_level : 1) + 60, 2751 (screen->vtXX_level >= 2) 2752 ? (screen->control_eight_bits 2753 ? ";0" : ";1") 2754 : "", 2755 cp); 2756 } else if (!strcmp(cp, "r")) { /* DECSTBM */ 2757 sprintf(reply, "%d;%dr", 2758 screen->top_marg + 1, 2759 screen->bot_marg + 1); 2760 } else if (!strcmp(cp, "m")) { /* SGR */ 2761 strcpy(reply, "0"); 2762 if (xw->flags & BOLD) 2763 strcat(reply, ";1"); 2764 if (xw->flags & UNDERLINE) 2765 strcat(reply, ";4"); 2766 if (xw->flags & BLINK) 2767 strcat(reply, ";5"); 2768 if (xw->flags & INVERSE) 2769 strcat(reply, ";7"); 2770 if (xw->flags & INVISIBLE) 2771 strcat(reply, ";8"); 2772 if_OPT_EXT_COLORS(screen, { 2773 if (xw->flags & FG_COLOR) { 2774 if (xw->cur_foreground >= 16) 2775 sprintf(reply + strlen(reply), 2776 ";38;5;%d", xw->cur_foreground); 2777 else 2778 sprintf(reply + strlen(reply), 2779 ";%d%d", 2780 xw->cur_foreground >= 8 ? 9 : 3, 2781 xw->cur_foreground >= 8 ? 2782 xw->cur_foreground - 8 : 2783 xw->cur_foreground); 2784 } 2785 if (xw->flags & BG_COLOR) { 2786 if (xw->cur_background >= 16) 2787 sprintf(reply + strlen(reply), 2788 ";48;5;%d", xw->cur_foreground); 2789 else 2790 sprintf(reply + strlen(reply), 2791 ";%d%d", 2792 xw->cur_background >= 8 ? 10 : 4, 2793 xw->cur_background >= 8 ? 2794 xw->cur_background - 8 : 2795 xw->cur_background); 2796 } 2797 }); 2798 if_OPT_ISO_TRADITIONAL_COLORS(screen, { 2799 if (xw->flags & FG_COLOR) 2800 sprintf(reply + strlen(reply), 2801 ";%d%d", 2802 xw->cur_foreground >= 8 ? 9 : 3, 2803 xw->cur_foreground >= 8 ? 2804 xw->cur_foreground - 8 : 2805 xw->cur_foreground); 2806 if (xw->flags & BG_COLOR) 2807 sprintf(reply + strlen(reply), 2808 ";%d%d", 2809 xw->cur_background >= 8 ? 10 : 4, 2810 xw->cur_background >= 8 ? 2811 xw->cur_background - 8 : 2812 xw->cur_background); 2813 }); 2814 strcat(reply, "m"); 2815 } else 2816 okay = False; 2817 2818 unparseputc1(xw, ANSI_DCS); 2819 unparseputc(xw, okay ? '1' : '0'); 2820 unparseputc(xw, '$'); 2821 unparseputc(xw, 'r'); 2822 if (okay) { 2823 cp = reply; 2824 unparseputs(xw, cp); 2825 } 2826 unparseputc1(xw, ANSI_ST); 2827 } else { 2828 unparseputc(xw, ANSI_CAN); 2829 } 2830 break; 2831#if OPT_TCAP_QUERY 2832 case '+': 2833 cp++; 2834 if (*cp == 'q') { 2835 Bool fkey; 2836 unsigned state; 2837 int code; 2838 char *tmp; 2839 char *parsed = ++cp; 2840 2841 unparseputc1(xw, ANSI_DCS); 2842 2843 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 2844 2845 unparseputc(xw, code >= 0 ? '1' : '0'); 2846 2847 unparseputc(xw, '+'); 2848 unparseputc(xw, 'r'); 2849 2850 while (*cp != 0) { 2851 if (cp == parsed) 2852 break; /* no data found, error */ 2853 2854 for (tmp = cp; tmp != parsed; ++tmp) 2855 unparseputc(xw, *tmp); 2856 2857 if (code >= 0) { 2858 unparseputc(xw, '='); 2859 screen->tc_query_code = code; 2860 screen->tc_query_fkey = fkey; 2861#if OPT_ISO_COLORS 2862 /* XK_COLORS is a fake code for the "Co" entry (maximum 2863 * number of colors) */ 2864 if (code == XK_COLORS) { 2865 unparseputn(xw, NUM_ANSI_COLORS); 2866 } else 2867#endif 2868#if OPT_TCAP_FKEYS 2869 /* 2870 * First ensure that we handle the extended cursor- and 2871 * editing-keypad keys. 2872 */ 2873 if ((code <= XK_Fn(MAX_FKEY)) 2874 || xtermcapString(xw, CodeToXkey(code), 0) == 0) 2875#endif 2876 { 2877 XKeyEvent event; 2878 event.state = state; 2879 Input(xw, &event, False); 2880 } 2881 screen->tc_query_code = -1; 2882 } else { 2883 break; /* no match found, error */ 2884 } 2885 2886 cp = parsed; 2887 if (*parsed == ';') { 2888 unparseputc(xw, *parsed++); 2889 cp = parsed; 2890 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 2891 } 2892 } 2893 unparseputc1(xw, ANSI_ST); 2894 } 2895 break; 2896#endif 2897 default: 2898 parse_ansi_params(¶ms, &cp); 2899 switch (params.a_final) { 2900 case '|': /* DECUDK */ 2901 if (params.a_param[0] == 0) 2902 reset_decudk(); 2903#ifdef notdef 2904 parse_decudk(cp); 2905#endif 2906 break; 2907 case '{': /* DECDLD (no '}' case though) */ 2908 parse_decdld(¶ms, cp); 2909 break; 2910 } 2911 break; 2912 } 2913 unparse_end(xw); 2914} 2915 2916char * 2917udk_lookup(int keycode, int *len) 2918{ 2919 if (keycode >= 0 && keycode < MAX_UDK) { 2920 *len = user_keys[keycode].len; 2921 return user_keys[keycode].str; 2922 } 2923 return 0; 2924} 2925 2926static void 2927ChangeGroup(String attribute, char *value) 2928{ 2929#if OPT_WIDE_CHARS 2930 static Char *converted; /* NO_LEAKS */ 2931#endif 2932 static char empty[1]; 2933 2934 Arg args[1]; 2935 char *original = (value != 0) ? value : empty; 2936 char *name = original; 2937 TScreen *screen = TScreenOf(term); 2938 Widget w = CURRENT_EMU(); 2939 Widget top = SHELL_OF(w); 2940 unsigned limit = strlen(name); 2941 Char *c1 = (Char *) original; 2942 Char *cp; 2943 2944 TRACE(("ChangeGroup(attribute=%s, value=%s)\n", attribute, name)); 2945 2946 if (!screen->allowTitleOps) 2947 return; 2948 2949 /* 2950 * Ignore titles that are too long to be plausible requests. 2951 */ 2952 if (limit >= 1024) 2953 return; 2954 2955 for (cp = c1; *cp != 0; ++cp) { 2956 Char *c2 = cp; 2957 if (!xtermIsPrintable(screen, &cp, c1 + limit)) { 2958 memset(c2, '?', (unsigned) (cp + 1 - c2)); 2959 } 2960 } 2961 2962#if OPT_WIDE_CHARS 2963 /* 2964 * Title strings are limited to ISO-8859-1, which is consistent with the 2965 * printable data in sos_table. However, if we're running in UTF-8 mode, 2966 * it is likely that non-ASCII text in the string will be rejected because 2967 * it is not printable in the current locale. So we convert it to UTF-8, 2968 * allowing the X library to convert it back. 2969 */ 2970 if (xtermEnvUTF8() && !screen->utf8_title) { 2971 int n; 2972 2973 for (n = 0; name[n] != '\0'; ++n) { 2974 if (CharOf(name[n]) > 127) { 2975 if (converted != 0) 2976 free(converted); 2977 if ((converted = TypeMallocN(Char, 1 + (5 * limit))) != 0) { 2978 Char *temp = converted; 2979 while (*name != 0) { 2980 temp = convertToUTF8(temp, CharOf(*name)); 2981 ++name; 2982 } 2983 *temp = 0; 2984 name = (char *) converted; 2985 TRACE(("...converted{%s}\n", name)); 2986 } 2987 break; 2988 } 2989 } 2990 } 2991#endif 2992 2993#if OPT_SAME_NAME 2994 /* If the attribute isn't going to change, then don't bother... */ 2995 2996 if (resource.sameName) { 2997 char *buf; 2998 XtSetArg(args[0], attribute, &buf); 2999 XtGetValues(top, args, 1); 3000 TRACE(("...comparing{%s}\n", buf)); 3001 if (strcmp(name, buf) == 0) 3002 return; 3003 } 3004#endif /* OPT_SAME_NAME */ 3005 3006 TRACE(("...updating %s\n", attribute)); 3007 TRACE(("...value is %s\n", name)); 3008 XtSetArg(args[0], attribute, name); 3009 XtSetValues(top, args, 1); 3010 3011#if OPT_WIDE_CHARS 3012 if (xtermEnvUTF8()) { 3013 Display *dpy = XtDisplay(term); 3014 Atom my_atom; 3015 3016 const char *propname = (!strcmp(attribute, XtNtitle) 3017 ? "_NET_WM_NAME" 3018 : "_NET_WM_ICON_NAME"); 3019 if ((my_atom = XInternAtom(dpy, propname, False)) != None) { 3020 if (screen->utf8_title) { /* FIXME - redundant? */ 3021 TRACE(("...updating %s\n", propname)); 3022 TRACE(("...value is %s\n", original)); 3023 XChangeProperty(dpy, VShellWindow, 3024 my_atom, XA_UTF8_STRING(dpy), 8, 3025 PropModeReplace, 3026 (Char *) original, (int) strlen(original)); 3027 } else { 3028 TRACE(("...deleting %s\n", propname)); 3029 XDeleteProperty(dpy, VShellWindow, my_atom); 3030 } 3031 } 3032 } 3033#endif 3034} 3035 3036void 3037ChangeIconName(char *name) 3038{ 3039 if (name == 0) 3040 name = ""; 3041#if OPT_ZICONBEEP /* If warning should be given then give it */ 3042 if (resource.zIconBeep && term->screen.zIconBeep_flagged) { 3043 char *newname = CastMallocN(char, strlen(name) + 4); 3044 if (!newname) { 3045 fprintf(stderr, "malloc failed in ChangeIconName\n"); 3046 return; 3047 } 3048 strcpy(newname, "*** "); 3049 strcat(newname, name); 3050 ChangeGroup(XtNiconName, newname); 3051 free(newname); 3052 } else 3053#endif /* OPT_ZICONBEEP */ 3054 ChangeGroup(XtNiconName, name); 3055} 3056 3057void 3058ChangeTitle(char *name) 3059{ 3060 ChangeGroup(XtNtitle, name); 3061} 3062 3063#define Strlen(s) strlen((char *)(s)) 3064 3065void 3066ChangeXprop(char *buf) 3067{ 3068 Display *dpy = XtDisplay(toplevel); 3069 Window w = XtWindow(toplevel); 3070 XTextProperty text_prop; 3071 Atom aprop; 3072 Char *pchEndPropName = (Char *) strchr(buf, '='); 3073 3074 if (pchEndPropName) 3075 *pchEndPropName = '\0'; 3076 aprop = XInternAtom(dpy, buf, False); 3077 if (pchEndPropName == NULL) { 3078 /* no "=value" given, so delete the property */ 3079 XDeleteProperty(dpy, w, aprop); 3080 } else { 3081 text_prop.value = pchEndPropName + 1; 3082 text_prop.encoding = XA_STRING; 3083 text_prop.format = 8; 3084 text_prop.nitems = Strlen(text_prop.value); 3085 XSetTextProperty(dpy, w, &text_prop, aprop); 3086 } 3087} 3088 3089/***====================================================================***/ 3090 3091/* 3092 * This is part of ReverseVideo(). It reverses the data stored for the old 3093 * "dynamic" colors that might have been retrieved using OSC 10-18. 3094 */ 3095void 3096ReverseOldColors(void) 3097{ 3098 ScrnColors *pOld = pOldColors; 3099 Pixel tmpPix; 3100 char *tmpName; 3101 3102 if (pOld) { 3103 /* change text cursor, if necesary */ 3104 if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { 3105 pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; 3106 if (pOld->names[TEXT_CURSOR]) { 3107 XtFree(pOldColors->names[TEXT_CURSOR]); 3108 pOld->names[TEXT_CURSOR] = NULL; 3109 } 3110 if (pOld->names[TEXT_BG]) { 3111 if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { 3112 pOld->names[TEXT_CURSOR] = tmpName; 3113 } 3114 } 3115 } 3116 3117 EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); 3118 EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); 3119 3120 EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); 3121 EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); 3122 3123#if OPT_TEK4014 3124 EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); 3125 EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); 3126#endif 3127 } 3128 return; 3129} 3130 3131Bool 3132AllocateTermColor(XtermWidget xw, 3133 ScrnColors * pNew, 3134 int ndx, 3135 const char *name) 3136{ 3137 XColor def; 3138 TScreen *screen = &xw->screen; 3139 Colormap cmap = xw->core.colormap; 3140 char *newName; 3141 3142 if (XParseColor(screen->display, cmap, name, &def) 3143 && (XAllocColor(screen->display, cmap, &def) 3144 || find_closest_color(screen->display, cmap, &def)) 3145 && (newName = x_strdup(name)) != 0) { 3146 if (COLOR_DEFINED(pNew, ndx)) 3147 free(pNew->names[ndx]); 3148 SET_COLOR_VALUE(pNew, ndx, def.pixel); 3149 SET_COLOR_NAME(pNew, ndx, newName); 3150 TRACE(("AllocateTermColor #%d: %s (pixel %#lx)\n", ndx, newName, def.pixel)); 3151 return (True); 3152 } 3153 TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); 3154 return (False); 3155} 3156/***====================================================================***/ 3157 3158/* ARGSUSED */ 3159void 3160Panic(char *s GCC_UNUSED, int a GCC_UNUSED) 3161{ 3162#ifdef DEBUG 3163 if (debug) { 3164 fprintf(stderr, "%s: PANIC!\t", xterm_name); 3165 fprintf(stderr, s, a); 3166 fputs("\r\n", stderr); 3167 fflush(stderr); 3168 } 3169#endif /* DEBUG */ 3170} 3171 3172const char * 3173SysErrorMsg(int code) 3174{ 3175 static char unknown[] = "unknown error"; 3176 char *s = strerror(code); 3177 return s ? s : unknown; 3178} 3179 3180const char * 3181SysReasonMsg(int code) 3182{ 3183 /* *INDENT-OFF* */ 3184 static const struct { 3185 int code; 3186 const char *name; 3187 } table[] = { 3188 { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" }, 3189 { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" }, 3190 { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", }, 3191 { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", }, 3192 { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", }, 3193 { ERROR_PTSNAME, "spawn: ptsname() failed", }, 3194 { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", }, 3195 { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" }, 3196 { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" }, 3197 { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" }, 3198 { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" }, 3199 { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" }, 3200 { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" }, 3201 { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" }, 3202 { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" }, 3203 { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" }, 3204 { ERROR_INIGROUPS, "spawn: initgroups() failed" }, 3205 { ERROR_FORK, "spawn: fork() failed" }, 3206 { ERROR_EXEC, "spawn: exec() failed" }, 3207 { ERROR_PTYS, "get_pty: not enough ptys" }, 3208 { ERROR_PTY_EXEC, "waiting for initial map" }, 3209 { ERROR_SETUID, "spawn: setuid() failed" }, 3210 { ERROR_INIT, "spawn: can't initialize window" }, 3211 { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" }, 3212 { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" }, 3213 { ERROR_SPREALLOC, "spawn: realloc of ttydev failed" }, 3214 { ERROR_LUMALLOC, "luit: command-line malloc failed" }, 3215 { ERROR_SELECT, "in_put: select() failed" }, 3216 { ERROR_VINIT, "VTInit: can't initialize window" }, 3217 { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" }, 3218 { ERROR_TSELECT, "Tinput: select() failed" }, 3219 { ERROR_TINIT, "TekInit: can't initialize window" }, 3220 { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" }, 3221 { ERROR_LOGEXEC, "StartLog: exec() failed" }, 3222 { ERROR_XERROR, "xerror: XError event" }, 3223 { ERROR_XIOERROR, "xioerror: X I/O error" }, 3224 { ERROR_SCALLOC, "Alloc: calloc() failed on base" }, 3225 { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" }, 3226 { ERROR_SREALLOC, "ScreenResize: realloc() failed on alt base" }, 3227 { ERROR_RESIZE, "ScreenResize: malloc() or realloc() failed" }, 3228 { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" }, 3229 { ERROR_SBRALLOC, "ScrollBarOn: realloc() failed on base" }, 3230 { ERROR_SBRALLOC2, "ScrollBarOn: realloc() failed on rows" }, 3231 { ERROR_MMALLOC, "my_memmove: malloc/realloc failed" }, 3232 }; 3233 /* *INDENT-ON* */ 3234 3235 Cardinal n; 3236 const char *result = "?"; 3237 3238 for (n = 0; n < XtNumber(table); ++n) { 3239 if (code == table[n].code) { 3240 result = table[n].name; 3241 break; 3242 } 3243 } 3244 return result; 3245} 3246 3247void 3248SysError(int code) 3249{ 3250 int oerrno = errno; 3251 3252 fprintf(stderr, "%s: Error %d, errno %d: ", xterm_name, code, oerrno); 3253 fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); 3254 fprintf(stderr, "Reason: %s\n", SysReasonMsg(code)); 3255 3256 Cleanup(code); 3257} 3258 3259/* 3260 * cleanup by sending SIGHUP to client processes 3261 */ 3262void 3263Cleanup(int code) 3264{ 3265 static Bool cleaning; 3266 TScreen *screen = TScreenOf(term); 3267 3268 /* 3269 * Process "-hold" and session cleanup only for a normal exit. 3270 */ 3271 if (code == 0) { 3272 if (cleaning) { 3273 hold_screen = 0; 3274 return; 3275 } 3276 3277 cleaning = True; 3278 need_cleanup = False; 3279 3280 TRACE(("Cleanup %d\n", code)); 3281 3282 if (hold_screen) { 3283 hold_screen = 2; 3284 while (hold_screen) { 3285 xevents(); 3286 Sleep(10); 3287 } 3288 } 3289#if OPT_SESSION_MGT 3290 if (resource.sessionMgt) { 3291 XtVaSetValues(toplevel, 3292 XtNjoinSession, False, 3293 (XtPointer *) 0); 3294 } 3295#endif 3296 } 3297 3298 if (screen->pid > 1) { 3299 (void) kill_process_group(screen->pid, SIGHUP); 3300 } 3301 Exit(code); 3302} 3303 3304#ifndef VMS 3305char * 3306xtermFindShell(char *leaf, Bool warning) 3307{ 3308 char *s; 3309 char *d; 3310 char *tmp; 3311 char *result = leaf; 3312 3313 TRACE(("xtermFindShell(%s)\n", leaf)); 3314 if (*result != '\0' && strchr("+/-", *result) == 0) { 3315 /* find it in $PATH */ 3316 if ((s = x_getenv("PATH")) != 0) { 3317 if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 1)) != 0) { 3318 Bool found = False; 3319 while (*s != '\0') { 3320 strcpy(tmp, s); 3321 for (d = tmp;; ++d) { 3322 if (*d == ':' || *d == '\0') { 3323 int skip = (*d != '\0'); 3324 *d = '/'; 3325 strcpy(d + 1, leaf); 3326 if (skip) 3327 ++d; 3328 s += (d - tmp); 3329 if (*tmp == '/' 3330 && strstr(tmp, "..") == 0 3331 && access(tmp, X_OK) == 0) { 3332 result = x_strdup(tmp); 3333 found = True; 3334 } 3335 break; 3336 } 3337 if (found) 3338 break; 3339 } 3340 if (found) 3341 break; 3342 } 3343 free(tmp); 3344 } 3345 } 3346 } 3347 TRACE(("...xtermFindShell(%s)\n", result)); 3348 if (*result != '/' 3349 || strstr(result, "..") != 0 3350 || access(result, X_OK) != 0) { 3351 if (warning) 3352 fprintf(stderr, "No absolute path found for shell: %s\n", result); 3353 result = 0; 3354 } 3355 return result; 3356} 3357#endif /* VMS */ 3358 3359#define ENV_HUNK(n) ((((n) + 1) | 31) + 1) 3360 3361/* 3362 * copy the environment before Setenv'ing. 3363 */ 3364void 3365xtermCopyEnv(char **oldenv) 3366{ 3367 unsigned size; 3368 char **newenv; 3369 3370 for (size = 0; oldenv[size] != NULL; size++) { 3371 ; 3372 } 3373 3374 newenv = TypeCallocN(char *, ENV_HUNK(size)); 3375 memmove(newenv, oldenv, size * sizeof(char *)); 3376 environ = newenv; 3377} 3378 3379/* 3380 * sets the value of var to be arg in the Unix 4.2 BSD environment env. 3381 * Var should end with '=' (bindings are of the form "var=value"). 3382 * This procedure assumes the memory for the first level of environ 3383 * was allocated using calloc, with enough extra room at the end so not 3384 * to have to do a realloc(). 3385 */ 3386void 3387xtermSetenv(char *var, char *value) 3388{ 3389 if (value != 0) { 3390 char *test; 3391 int envindex = 0; 3392 size_t len = strlen(var); 3393 int found = -1; 3394 3395 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 3396 3397 while ((test = environ[envindex]) != NULL) { 3398 if (strncmp(test, var, len) == 0 && test[len] == '=') { 3399 found = envindex; 3400 break; 3401 } 3402 envindex++; 3403 } 3404 3405 if (found < 0) { 3406 unsigned need = ENV_HUNK(envindex + 1); 3407 unsigned have = ENV_HUNK(envindex); 3408 3409 if (need > have) { 3410 char **newenv; 3411 newenv = TypeMallocN(char *, need); 3412 if (newenv == 0) { 3413 fprintf(stderr, "Cannot increase environment\n"); 3414 return; 3415 } 3416 memmove(newenv, environ, have * sizeof(*newenv)); 3417 free(environ); 3418 environ = newenv; 3419 } 3420 3421 found = envindex; 3422 environ[found + 1] = NULL; 3423 environ = environ; 3424 } 3425 3426 environ[found] = CastMallocN(char, 1 + len + strlen(value)); 3427 if (environ[found] == 0) { 3428 fprintf(stderr, "Cannot allocate environment %s\n", var); 3429 return; 3430 } 3431 sprintf(environ[found], "%s=%s", var, value); 3432 } 3433} 3434 3435/*ARGSUSED*/ 3436int 3437xerror(Display * d, XErrorEvent * ev) 3438{ 3439 fprintf(stderr, "%s: warning, error event received:\n", xterm_name); 3440 (void) XmuPrintDefaultErrorMessage(d, ev, stderr); 3441 Exit(ERROR_XERROR); 3442 return 0; /* appease the compiler */ 3443} 3444 3445/*ARGSUSED*/ 3446int 3447xioerror(Display * dpy) 3448{ 3449 int the_error = errno; 3450 3451 (void) fprintf(stderr, 3452 "%s: fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", 3453 xterm_name, the_error, SysErrorMsg(the_error), 3454 DisplayString(dpy)); 3455 3456 Exit(ERROR_XIOERROR); 3457 return 0; /* appease the compiler */ 3458} 3459 3460void 3461xt_error(String message) 3462{ 3463 (void) fprintf(stderr, "%s Xt error: %s\n", ProgramName, message); 3464 3465 /* 3466 * Check for the obvious - Xt does a poor job of reporting this. 3467 */ 3468 if (x_getenv("DISPLAY") == 0) { 3469 fprintf(stderr, "%s: DISPLAY is not set\n", ProgramName); 3470 } 3471 exit(1); 3472} 3473 3474int 3475XStrCmp(char *s1, char *s2) 3476{ 3477 if (s1 && s2) 3478 return (strcmp(s1, s2)); 3479 if (s1 && *s1) 3480 return (1); 3481 if (s2 && *s2) 3482 return (-1); 3483 return (0); 3484} 3485 3486#if OPT_TEK4014 3487static void 3488withdraw_window(Display * dpy, Window w, int scr) 3489{ 3490 TRACE(("withdraw_window %#lx\n", (long) w)); 3491 (void) XmuUpdateMapHints(dpy, w, NULL); 3492 XWithdrawWindow(dpy, w, scr); 3493 return; 3494} 3495#endif 3496 3497void 3498set_vt_visibility(Bool on) 3499{ 3500 TScreen *screen = TScreenOf(term); 3501 3502 TRACE(("set_vt_visibility(%d)\n", on)); 3503 if (on) { 3504 if (!screen->Vshow && term) { 3505 VTInit(); 3506 XtMapWidget(XtParent(term)); 3507#if OPT_TOOLBAR 3508 /* we need both of these during initialization */ 3509 XtMapWidget(SHELL_OF(term)); 3510 ShowToolbar(resource.toolBar); 3511#endif 3512 screen->Vshow = True; 3513 } 3514 } 3515#if OPT_TEK4014 3516 else { 3517 if (screen->Vshow && term) { 3518 withdraw_window(XtDisplay(term), 3519 VShellWindow, 3520 XScreenNumberOfScreen(XtScreen(term))); 3521 screen->Vshow = False; 3522 } 3523 } 3524 set_vthide_sensitivity(); 3525 set_tekhide_sensitivity(); 3526 update_vttekmode(); 3527 update_tekshow(); 3528 update_vtshow(); 3529#endif 3530 return; 3531} 3532 3533#if OPT_TEK4014 3534void 3535set_tek_visibility(Bool on) 3536{ 3537 TRACE(("set_tek_visibility(%d)\n", on)); 3538 3539 if (on) { 3540 if (!TEK4014_SHOWN(term) && (tekWidget || TekInit())) { 3541 Widget tekParent = SHELL_OF(tekWidget); 3542 XtRealizeWidget(tekParent); 3543 XtMapWidget(XtParent(tekWidget)); 3544#if OPT_TOOLBAR 3545 /* we need both of these during initialization */ 3546 XtMapWidget(tekParent); 3547 XtMapWidget(tekWidget); 3548#endif 3549 XtOverrideTranslations(tekParent, 3550 XtParseTranslationTable 3551 ("<Message>WM_PROTOCOLS: DeleteWindow()")); 3552 (void) XSetWMProtocols(XtDisplay(tekParent), 3553 XtWindow(tekParent), 3554 &wm_delete_window, 1); 3555 TEK4014_SHOWN(term) = True; 3556 } 3557 } else { 3558 if (TEK4014_SHOWN(term) && tekWidget) { 3559 withdraw_window(XtDisplay(tekWidget), 3560 TShellWindow, 3561 XScreenNumberOfScreen(XtScreen(tekWidget))); 3562 TEK4014_SHOWN(term) = False; 3563 } 3564 } 3565 set_tekhide_sensitivity(); 3566 set_vthide_sensitivity(); 3567 update_vtshow(); 3568 update_tekshow(); 3569 update_vttekmode(); 3570 return; 3571} 3572 3573void 3574end_tek_mode(void) 3575{ 3576 if (TEK4014_ACTIVE(term)) { 3577 FlushLog(&(term->screen)); 3578 longjmp(Tekend, 1); 3579 } 3580 return; 3581} 3582 3583void 3584end_vt_mode(void) 3585{ 3586 if (!TEK4014_ACTIVE(term)) { 3587 FlushLog(&(term->screen)); 3588 TEK4014_ACTIVE(term) = True; 3589 longjmp(VTend, 1); 3590 } 3591 return; 3592} 3593 3594void 3595switch_modes(Bool tovt) /* if true, then become vt mode */ 3596{ 3597 if (tovt) { 3598 if (tekRefreshList) 3599 TekRefresh(tekWidget); 3600 end_tek_mode(); /* WARNING: this does a longjmp... */ 3601 } else { 3602 end_vt_mode(); /* WARNING: this does a longjmp... */ 3603 } 3604} 3605 3606void 3607hide_vt_window(void) 3608{ 3609 set_vt_visibility(False); 3610 if (!TEK4014_ACTIVE(term)) 3611 switch_modes(False); /* switch to tek mode */ 3612} 3613 3614void 3615hide_tek_window(void) 3616{ 3617 set_tek_visibility(False); 3618 tekRefreshList = (TekLink *) 0; 3619 if (TEK4014_ACTIVE(term)) 3620 switch_modes(True); /* does longjmp to vt mode */ 3621} 3622#endif /* OPT_TEK4014 */ 3623 3624static const char * 3625skip_punct(const char *s) 3626{ 3627 while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { 3628 ++s; 3629 } 3630 return s; 3631} 3632 3633static int 3634cmp_options(const void *a, const void *b) 3635{ 3636 const char *s1 = skip_punct(((const OptionHelp *) a)->opt); 3637 const char *s2 = skip_punct(((const OptionHelp *) b)->opt); 3638 return strcmp(s1, s2); 3639} 3640 3641static int 3642cmp_resources(const void *a, const void *b) 3643{ 3644 return strcmp(((const XrmOptionDescRec *) a)->option, 3645 ((const XrmOptionDescRec *) b)->option); 3646} 3647 3648XrmOptionDescRec * 3649sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) 3650{ 3651 static XrmOptionDescRec *res_array = 0; 3652 3653#ifdef NO_LEAKS 3654 if (descs == 0 && res_array != 0) { 3655 free(res_array); 3656 res_array = 0; 3657 } else 3658#endif 3659 if (res_array == 0) { 3660 Cardinal j; 3661 3662 /* make a sorted index to 'resources' */ 3663 res_array = TypeCallocN(XrmOptionDescRec, res_count); 3664 for (j = 0; j < res_count; j++) 3665 res_array[j] = descs[j]; 3666 qsort(res_array, res_count, sizeof(*res_array), cmp_resources); 3667 } 3668 return res_array; 3669} 3670 3671/* 3672 * The first time this is called, construct sorted index to the main program's 3673 * list of options, taking into account the on/off options which will be 3674 * compressed into one token. It's a lot simpler to do it this way than 3675 * maintain the list in sorted form with lots of ifdef's. 3676 */ 3677OptionHelp * 3678sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) 3679{ 3680 static OptionHelp *opt_array = 0; 3681 3682#ifdef NO_LEAKS 3683 if (descs == 0 && opt_array != 0) { 3684 sortedOptDescs(descs, numDescs); 3685 free(opt_array); 3686 opt_array = 0; 3687 return 0; 3688 } else if (options == 0 || descs == 0) { 3689 return 0; 3690 } 3691#endif 3692 3693 if (opt_array == 0) { 3694 Cardinal opt_count, j; 3695#if OPT_TRACE 3696 Cardinal k; 3697 XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); 3698 int code; 3699 char *mesg; 3700#else 3701 (void) descs; 3702 (void) numDescs; 3703#endif 3704 3705 /* count 'options' and make a sorted index to it */ 3706 for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { 3707 ; 3708 } 3709 opt_array = TypeCallocN(OptionHelp, opt_count + 1); 3710 for (j = 0; j < opt_count; j++) 3711 opt_array[j] = options[j]; 3712 qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); 3713 3714 /* supply the "turn on/off" strings if needed */ 3715#if OPT_TRACE 3716 for (j = 0; j < opt_count; j++) { 3717 if (!strncmp(opt_array[j].opt, "-/+", 3)) { 3718 char *name = opt_array[j].opt + 3; 3719 for (k = 0; k < numDescs; ++k) { 3720 char *value = res_array[k].value; 3721 if (res_array[k].option[0] == '-') { 3722 code = -1; 3723 } else if (res_array[k].option[0] == '+') { 3724 code = 1; 3725 } else { 3726 code = 0; 3727 } 3728 if (x_strindex(opt_array[j].desc, "inhibit") != 0) 3729 code = -code; 3730 if (code != 0 3731 && res_array[k].value != 0 3732 && !strcmp(name, res_array[k].option + 1)) { 3733 if (((code < 0) && !strcmp(value, "on")) 3734 || ((code > 0) && !strcmp(value, "off")) 3735 || ((code > 0) && !strcmp(value, "0"))) { 3736 mesg = "turn on/off"; 3737 } else { 3738 mesg = "turn off/on"; 3739 } 3740 if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) { 3741 if (strncmp(opt_array[j].desc, "turn ", 5)) { 3742 char *s = CastMallocN(char, 3743 strlen(mesg) 3744 + 1 3745 + strlen(opt_array[j].desc)); 3746 if (s != 0) { 3747 sprintf(s, "%s %s", mesg, opt_array[j].desc); 3748 opt_array[j].desc = s; 3749 } 3750 } else { 3751 TRACE(("OOPS ")); 3752 } 3753 } 3754 TRACE(("%s: %s %s: %s (%s)\n", 3755 mesg, 3756 res_array[k].option, 3757 res_array[k].value, 3758 opt_array[j].opt, 3759 opt_array[j].desc)); 3760 break; 3761 } 3762 } 3763 } 3764 } 3765#endif 3766 } 3767 return opt_array; 3768} 3769 3770/* 3771 * Report the character-type locale that xterm was started in. 3772 */ 3773char * 3774xtermEnvLocale(void) 3775{ 3776 static char *result; 3777 3778 if (result == 0) { 3779 if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) { 3780 result = "C"; 3781 } 3782 TRACE(("xtermEnvLocale ->%s\n", result)); 3783 } 3784 return result; 3785} 3786 3787char * 3788xtermEnvEncoding(void) 3789{ 3790 static char *result; 3791 3792 if (result == 0) { 3793#ifdef HAVE_LANGINFO_CODESET 3794 result = nl_langinfo(CODESET); 3795#else 3796 char *locale = xtermEnvLocale(); 3797 if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { 3798 result = "ASCII"; 3799 } else { 3800 result = "ISO-8859-1"; 3801 } 3802#endif 3803 TRACE(("xtermEnvEncoding ->%s\n", result)); 3804 } 3805 return result; 3806} 3807 3808#if OPT_WIDE_CHARS 3809/* 3810 * Tell whether xterm was started in a locale that uses UTF-8 encoding for 3811 * characters. That environment is inherited by subprocesses and used in 3812 * various library calls. 3813 */ 3814Bool 3815xtermEnvUTF8(void) 3816{ 3817 static Bool init = False; 3818 static Bool result = False; 3819 3820 if (!init) { 3821 init = True; 3822#ifdef HAVE_LANGINFO_CODESET 3823 result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); 3824#else 3825 result = (strstr(xtermEnvLocale(), "UTF-8") != NULL); 3826#endif 3827 TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); 3828 } 3829 return result; 3830} 3831#endif /* OPT_WIDE_CHARS */ 3832 3833/* 3834 * Returns the version-string used in the "-v' message as well as a few other 3835 * places. It is derived (when possible) from the __vendorversion__ symbol 3836 * that some newer imake configurations define. 3837 */ 3838char * 3839xtermVersion(void) 3840{ 3841 static char *result; 3842 if (result == 0) { 3843 char *vendor = __vendorversion__; 3844 char first[BUFSIZ]; 3845 char second[BUFSIZ]; 3846 3847 result = CastMallocN(char, strlen(vendor) + 9); 3848 if (result == 0) 3849 result = vendor; 3850 else { 3851 /* some vendors leave trash in this string */ 3852 for (;;) { 3853 if (!strncmp(vendor, "Version ", 8)) 3854 vendor += 8; 3855 else if (isspace(CharOf(*vendor))) 3856 ++vendor; 3857 else 3858 break; 3859 } 3860 if (strlen(vendor) < BUFSIZ && 3861 sscanf(vendor, "%[0-9.] %[A-Za-z_0-9.]", first, second) == 2) 3862 sprintf(result, "%s %s(%d)", second, first, XTERM_PATCH); 3863 else 3864 sprintf(result, "%s(%d)", vendor, XTERM_PATCH); 3865 } 3866 } 3867 return result; 3868} 3869