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