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