misc.c revision c219fbeb
1/* $XTermId: misc.c,v 1.520 2011/02/13 21:03:21 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, int 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 %#lx)\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(("ReportColors %d: %#lx 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 /* 2739 * Lines should be of the form <OSC> number ; string <ST>, however 2740 * older xterms can accept <BEL> as a final character. We will respond 2741 * with the same final character as the application sends to make this 2742 * work better with shell scripts, which may have trouble reading an 2743 * <ESC><backslash>, which is the 7-bit equivalent to <ST>. 2744 */ 2745 mode = 0; 2746 for (cp = oscbuf; *cp != '\0'; cp++) { 2747 switch (state) { 2748 case 0: 2749 if (isdigit(*cp)) { 2750 mode = 10 * mode + (*cp - '0'); 2751 if (mode > 65535) { 2752 TRACE(("do_osc found unknown mode %d\n", mode)); 2753 return; 2754 } 2755 break; 2756 } 2757 /* FALLTHRU */ 2758 case 1: 2759 if (*cp != ';') { 2760 TRACE(("do_osc did not find semicolon offset %d\n", 2761 (int) (cp - oscbuf))); 2762 return; 2763 } 2764 state = 2; 2765 break; 2766 case 2: 2767 buf = (char *) cp; 2768 state = 3; 2769 /* FALLTHRU */ 2770 default: 2771 if (!xtermIsPrintable(xw, &cp, oscbuf + len)) { 2772 switch (mode) { 2773 case 0: 2774 case 1: 2775 case 2: 2776 break; 2777 default: 2778 TRACE(("do_osc found nonprinting char %02X offset %d\n", 2779 CharOf(*cp), 2780 (int) (cp - oscbuf))); 2781 return; 2782 } 2783 } 2784 } 2785 } 2786 2787 /* 2788 * Most OSC controls other than resets require data. Handle the others as 2789 * a special case. 2790 */ 2791 switch (mode) { 2792#if OPT_ISO_COLORS 2793 case OSC_Reset(4): 2794 case OSC_Reset(5): 2795 case OSC_Reset(OSC_TEXT_FG): 2796 case OSC_Reset(OSC_TEXT_BG): 2797 case OSC_Reset(OSC_TEXT_CURSOR): 2798 case OSC_Reset(OSC_MOUSE_FG): 2799 case OSC_Reset(OSC_MOUSE_BG): 2800#if OPT_HIGHLIGHT_COLOR 2801 case OSC_Reset(OSC_HIGHLIGHT_BG): 2802 case OSC_Reset(OSC_HIGHLIGHT_FG): 2803#endif 2804#if OPT_TEK4014 2805 case OSC_Reset(OSC_TEK_FG): 2806 case OSC_Reset(OSC_TEK_BG): 2807 case OSC_Reset(OSC_TEK_CURSOR): 2808#endif 2809 need_data = False; 2810 break; 2811#endif 2812 default: 2813 break; 2814 } 2815 2816 /* 2817 * Check if we have data when we want, and not when we do not want it. 2818 * Either way, that is a malformed control sequence, and will be ignored. 2819 */ 2820 if (IsEmpty(buf)) { 2821 if (need_data) { 2822 TRACE(("do_osc found no data\n")); 2823 return; 2824 } 2825 temp[0] = '\0'; 2826 buf = temp; 2827 } else if (!need_data) { 2828 TRACE(("do_osc found found unwanted data\n")); 2829 return; 2830 } 2831 2832 switch (mode) { 2833 case 0: /* new icon name and title */ 2834 ChangeIconName(xw, buf); 2835 ChangeTitle(xw, buf); 2836 break; 2837 2838 case 1: /* new icon name only */ 2839 ChangeIconName(xw, buf); 2840 break; 2841 2842 case 2: /* new title only */ 2843 ChangeTitle(xw, buf); 2844 break; 2845 2846#ifdef notdef 2847 case 3: /* change X property */ 2848 if (AllowWindowOps(xw, ewSetXprop)) 2849 ChangeXprop(buf); 2850 break; 2851#endif 2852#if OPT_ISO_COLORS 2853 case 5: 2854 ansi_colors = NUM_ANSI_COLORS; 2855 /* FALLTHRU */ 2856 case 4: 2857 if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final)) 2858 xtermRepaint(xw); 2859 break; 2860 case OSC_Reset(5): 2861 ansi_colors = NUM_ANSI_COLORS; 2862 /* FALLTHRU */ 2863 case OSC_Reset(4): 2864 if (ResetAnsiColorRequest(xw, buf, ansi_colors)) 2865 xtermRepaint(xw); 2866 break; 2867#endif 2868 case OSC_TEXT_FG: 2869 case OSC_TEXT_BG: 2870 case OSC_TEXT_CURSOR: 2871 case OSC_MOUSE_FG: 2872 case OSC_MOUSE_BG: 2873#if OPT_HIGHLIGHT_COLOR 2874 case OSC_HIGHLIGHT_BG: 2875 case OSC_HIGHLIGHT_FG: 2876#endif 2877#if OPT_TEK4014 2878 case OSC_TEK_FG: 2879 case OSC_TEK_BG: 2880 case OSC_TEK_CURSOR: 2881#endif 2882 if (xw->misc.dynamicColors) { 2883 ChangeColorsRequest(xw, mode, buf, final); 2884 } 2885 break; 2886 case OSC_Reset(OSC_TEXT_FG): 2887 case OSC_Reset(OSC_TEXT_BG): 2888 case OSC_Reset(OSC_TEXT_CURSOR): 2889 case OSC_Reset(OSC_MOUSE_FG): 2890 case OSC_Reset(OSC_MOUSE_BG): 2891#if OPT_HIGHLIGHT_COLOR 2892 case OSC_Reset(OSC_HIGHLIGHT_BG): 2893 case OSC_Reset(OSC_HIGHLIGHT_FG): 2894#endif 2895#if OPT_TEK4014 2896 case OSC_Reset(OSC_TEK_FG): 2897 case OSC_Reset(OSC_TEK_BG): 2898 case OSC_Reset(OSC_TEK_CURSOR): 2899#endif 2900 if (xw->misc.dynamicColors) { 2901 ResetColorsRequest(xw, mode); 2902 } 2903 break; 2904 2905 case 30: 2906 case 31: 2907 /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ 2908 break; 2909 2910#ifdef ALLOWLOGGING 2911 case 46: /* new log file */ 2912#ifdef ALLOWLOGFILECHANGES 2913 /* 2914 * Warning, enabling this feature allows people to overwrite 2915 * arbitrary files accessible to the person running xterm. 2916 */ 2917 if (strcmp(buf, "?") 2918 && (cp = CastMallocN(char, strlen(buf)) != NULL)) { 2919 strcpy(cp, buf); 2920 if (screen->logfile) 2921 free(screen->logfile); 2922 screen->logfile = cp; 2923 break; 2924 } 2925#endif 2926 Bell(xw, XkbBI_Info, 0); 2927 Bell(xw, XkbBI_Info, 0); 2928 break; 2929#endif /* ALLOWLOGGING */ 2930 2931 case 50: 2932#if OPT_SHIFT_FONTS 2933 if (*buf == '?') { 2934 QueryFontRequest(xw, buf, final); 2935 } else if (xw->misc.shift_fonts) { 2936 ChangeFontRequest(xw, buf); 2937 } 2938#endif /* OPT_SHIFT_FONTS */ 2939 break; 2940 case 51: 2941 /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ 2942 break; 2943 2944#if OPT_PASTE64 2945 case 52: 2946 ManipulateSelectionData(xw, screen, buf, final); 2947 break; 2948#endif 2949 /* 2950 * One could write code to send back the display and host names, 2951 * but that could potentially open a fairly nasty security hole. 2952 */ 2953 default: 2954 TRACE(("do_osc - unrecognized code\n")); 2955 break; 2956 } 2957 unparse_end(xw); 2958} 2959 2960#ifdef SunXK_F36 2961#define MAX_UDK 37 2962#else 2963#define MAX_UDK 35 2964#endif 2965static struct { 2966 char *str; 2967 int len; 2968} user_keys[MAX_UDK]; 2969 2970/* 2971 * Parse one nibble of a hex byte from the OSC string. We have removed the 2972 * string-terminator (replacing it with a null), so the only other delimiter 2973 * that is expected is semicolon. Ignore other characters (Ray Neuman says 2974 * "real" terminals accept commas in the string definitions). 2975 */ 2976static int 2977udk_value(const char **cp) 2978{ 2979 int result = -1; 2980 int c; 2981 2982 for (;;) { 2983 if ((c = **cp) != '\0') 2984 *cp = *cp + 1; 2985 if (c == ';' || c == '\0') 2986 break; 2987 if ((result = x_hex2int(c)) >= 0) 2988 break; 2989 } 2990 2991 return result; 2992} 2993 2994void 2995reset_decudk(void) 2996{ 2997 int n; 2998 for (n = 0; n < MAX_UDK; n++) { 2999 if (user_keys[n].str != 0) { 3000 free(user_keys[n].str); 3001 user_keys[n].str = 0; 3002 user_keys[n].len = 0; 3003 } 3004 } 3005} 3006 3007/* 3008 * Parse the data for DECUDK (user-defined keys). 3009 */ 3010static void 3011parse_decudk(const char *cp) 3012{ 3013 while (*cp) { 3014 const char *base = cp; 3015 char *str = CastMallocN(char, strlen(cp) + 1); 3016 unsigned key = 0; 3017 int lo, hi; 3018 int len = 0; 3019 3020 while (isdigit(CharOf(*cp))) 3021 key = (key * 10) + (unsigned) (*cp++ - '0'); 3022 if (*cp == '/') { 3023 cp++; 3024 while ((hi = udk_value(&cp)) >= 0 3025 && (lo = udk_value(&cp)) >= 0) { 3026 str[len++] = (char) ((hi << 4) | lo); 3027 } 3028 } 3029 if (len > 0 && key < MAX_UDK) { 3030 if (user_keys[key].str != 0) 3031 free(user_keys[key].str); 3032 user_keys[key].str = str; 3033 user_keys[key].len = len; 3034 } else { 3035 free(str); 3036 } 3037 if (*cp == ';') 3038 cp++; 3039 if (cp == base) /* badly-formed sequence - bail out */ 3040 break; 3041 } 3042} 3043 3044#if OPT_TRACE 3045#define SOFT_WIDE 10 3046#define SOFT_HIGH 20 3047 3048static void 3049parse_decdld(ANSI * params, const char *string) 3050{ 3051 char DscsName[8]; 3052 int len; 3053 int Pfn = params->a_param[0]; 3054 int Pcn = params->a_param[1]; 3055 int Pe = params->a_param[2]; 3056 int Pcmw = params->a_param[3]; 3057 int Pw = params->a_param[4]; 3058 int Pt = params->a_param[5]; 3059 int Pcmh = params->a_param[6]; 3060 int Pcss = params->a_param[7]; 3061 3062 int start_char = Pcn + 0x20; 3063 int char_wide = ((Pcmw == 0) 3064 ? (Pcss ? 6 : 10) 3065 : (Pcmw > 4 3066 ? Pcmw 3067 : (Pcmw + 3))); 3068 int char_high = ((Pcmh == 0) 3069 ? ((Pcmw >= 2 || Pcmw <= 4) 3070 ? 10 3071 : 20) 3072 : Pcmh); 3073 Char ch; 3074 Char bits[SOFT_HIGH][SOFT_WIDE]; 3075 Bool first = True; 3076 Bool prior = False; 3077 int row = 0, col = 0; 3078 3079 TRACE(("Parsing DECDLD\n")); 3080 TRACE((" font number %d\n", Pfn)); 3081 TRACE((" starting char %d\n", Pcn)); 3082 TRACE((" erase control %d\n", Pe)); 3083 TRACE((" char-width %d\n", Pcmw)); 3084 TRACE((" font-width %d\n", Pw)); 3085 TRACE((" text/full %d\n", Pt)); 3086 TRACE((" char-height %d\n", Pcmh)); 3087 TRACE((" charset-size %d\n", Pcss)); 3088 3089 if (Pfn > 1 3090 || Pcn > 95 3091 || Pe > 2 3092 || Pcmw > 10 3093 || Pcmw == 1 3094 || Pt > 2 3095 || Pcmh > 20 3096 || Pcss > 1 3097 || char_wide > SOFT_WIDE 3098 || char_high > SOFT_HIGH) { 3099 TRACE(("DECDLD illegal parameter\n")); 3100 return; 3101 } 3102 3103 len = 0; 3104 while (*string != '\0') { 3105 ch = CharOf(*string++); 3106 if (ch >= ANSI_SPA && ch <= 0x2f) { 3107 if (len < 2) 3108 DscsName[len++] = (char) ch; 3109 } else if (ch >= 0x30 && ch <= 0x7e) { 3110 DscsName[len++] = (char) ch; 3111 break; 3112 } 3113 } 3114 DscsName[len] = 0; 3115 TRACE((" Dscs name '%s'\n", DscsName)); 3116 3117 TRACE((" character matrix %dx%d\n", char_high, char_wide)); 3118 while (*string != '\0') { 3119 if (first) { 3120 TRACE(("Char %d:\n", start_char)); 3121 if (prior) { 3122 for (row = 0; row < char_high; ++row) { 3123 TRACE(("%.*s\n", char_wide, bits[row])); 3124 } 3125 } 3126 prior = False; 3127 first = False; 3128 for (row = 0; row < char_high; ++row) { 3129 for (col = 0; col < char_wide; ++col) { 3130 bits[row][col] = '.'; 3131 } 3132 } 3133 row = col = 0; 3134 } 3135 ch = CharOf(*string++); 3136 if (ch >= 0x3f && ch <= 0x7e) { 3137 int n; 3138 3139 ch = CharOf(ch - 0x3f); 3140 for (n = 0; n < 6; ++n) { 3141 bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.'); 3142 } 3143 col += 1; 3144 prior = True; 3145 } else if (ch == '/') { 3146 row += 6; 3147 col = 0; 3148 } else if (ch == ';') { 3149 first = True; 3150 ++start_char; 3151 } 3152 } 3153} 3154#else 3155#define parse_decdld(p,q) /* nothing */ 3156#endif 3157 3158/* 3159 * Parse numeric parameters. Normally we use a state machine to simplify 3160 * interspersing with control characters, but have the string already. 3161 */ 3162static void 3163parse_ansi_params(ANSI * params, const char **string) 3164{ 3165 const char *cp = *string; 3166 ParmType nparam = 0; 3167 3168 memset(params, 0, sizeof(*params)); 3169 while (*cp != '\0') { 3170 Char ch = CharOf(*cp++); 3171 3172 if (isdigit(ch)) { 3173 if (nparam < NPARAM) { 3174 params->a_param[nparam] = 3175 (ParmType) ((params->a_param[nparam] * 10) 3176 + (ch - '0')); 3177 } 3178 } else if (ch == ';') { 3179 if (++nparam < NPARAM) 3180 params->a_nparam = nparam; 3181 } else if (ch < 32) { 3182 /* EMPTY */ ; 3183 } else { 3184 /* should be 0x30 to 0x7e */ 3185 params->a_final = ch; 3186 break; 3187 } 3188 } 3189 *string = cp; 3190} 3191 3192void 3193do_dcs(XtermWidget xw, Char * dcsbuf, size_t dcslen) 3194{ 3195 TScreen *screen = TScreenOf(xw); 3196 char reply[BUFSIZ]; 3197 const char *cp = (const char *) dcsbuf; 3198 Bool okay; 3199 ANSI params; 3200 3201 TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen)); 3202 3203 if (dcslen != strlen(cp)) 3204 /* shouldn't have nulls in the string */ 3205 return; 3206 3207 switch (*cp) { /* intermediate character, or parameter */ 3208 case '$': /* DECRQSS */ 3209 okay = True; 3210 3211 cp++; 3212 if (*cp++ == 'q') { 3213 if (!strcmp(cp, "\"q")) { /* DECSCA */ 3214 sprintf(reply, "%d%s", 3215 (screen->protected_mode == DEC_PROTECT) 3216 && (xw->flags & PROTECTED) ? 1 : 0, 3217 cp); 3218 } else if (!strcmp(cp, "\"p")) { /* DECSCL */ 3219 sprintf(reply, "%d%s%s", 3220 (screen->vtXX_level ? 3221 screen->vtXX_level : 1) + 60, 3222 (screen->vtXX_level >= 2) 3223 ? (screen->control_eight_bits 3224 ? ";0" : ";1") 3225 : "", 3226 cp); 3227 } else if (!strcmp(cp, "r")) { /* DECSTBM */ 3228 sprintf(reply, "%d;%dr", 3229 screen->top_marg + 1, 3230 screen->bot_marg + 1); 3231 } else if (!strcmp(cp, "m")) { /* SGR */ 3232 strcpy(reply, "0"); 3233 if (xw->flags & BOLD) 3234 strcat(reply, ";1"); 3235 if (xw->flags & UNDERLINE) 3236 strcat(reply, ";4"); 3237 if (xw->flags & BLINK) 3238 strcat(reply, ";5"); 3239 if (xw->flags & INVERSE) 3240 strcat(reply, ";7"); 3241 if (xw->flags & INVISIBLE) 3242 strcat(reply, ";8"); 3243#if OPT_256_COLORS || OPT_88_COLORS 3244 if_OPT_ISO_COLORS(screen, { 3245 if (xw->flags & FG_COLOR) { 3246 if (xw->cur_foreground >= 16) 3247 sprintf(reply + strlen(reply), 3248 ";38;5;%d", xw->cur_foreground); 3249 else 3250 sprintf(reply + strlen(reply), 3251 ";%d%d", 3252 xw->cur_foreground >= 8 ? 9 : 3, 3253 xw->cur_foreground >= 8 ? 3254 xw->cur_foreground - 8 : 3255 xw->cur_foreground); 3256 } 3257 if (xw->flags & BG_COLOR) { 3258 if (xw->cur_background >= 16) 3259 sprintf(reply + strlen(reply), 3260 ";48;5;%d", xw->cur_foreground); 3261 else 3262 sprintf(reply + strlen(reply), 3263 ";%d%d", 3264 xw->cur_background >= 8 ? 10 : 4, 3265 xw->cur_background >= 8 ? 3266 xw->cur_background - 8 : 3267 xw->cur_background); 3268 } 3269 }); 3270#elif OPT_ISO_COLORS 3271 if_OPT_ISO_COLORS(screen, { 3272 if (xw->flags & FG_COLOR) 3273 sprintf(reply + strlen(reply), 3274 ";%d%d", 3275 xw->cur_foreground >= 8 ? 9 : 3, 3276 xw->cur_foreground >= 8 ? 3277 xw->cur_foreground - 8 : 3278 xw->cur_foreground); 3279 if (xw->flags & BG_COLOR) 3280 sprintf(reply + strlen(reply), 3281 ";%d%d", 3282 xw->cur_background >= 8 ? 10 : 4, 3283 xw->cur_background >= 8 ? 3284 xw->cur_background - 8 : 3285 xw->cur_background); 3286 }); 3287#endif 3288 strcat(reply, "m"); 3289 } else 3290 okay = False; 3291 3292 if (okay) { 3293 unparseputc1(xw, ANSI_DCS); 3294 unparseputc(xw, okay ? '1' : '0'); 3295 unparseputc(xw, '$'); 3296 unparseputc(xw, 'r'); 3297 cp = reply; 3298 unparseputs(xw, cp); 3299 unparseputc1(xw, ANSI_ST); 3300 } else { 3301 unparseputc(xw, ANSI_CAN); 3302 } 3303 } else { 3304 unparseputc(xw, ANSI_CAN); 3305 } 3306 break; 3307#if OPT_TCAP_QUERY 3308 case '+': 3309 cp++; 3310 switch (*cp) { 3311 case 'p': 3312 if (AllowTcapOps(xw, etSetTcap)) { 3313 set_termcap(xw, cp + 1); 3314 } 3315 break; 3316 case 'q': 3317 if (AllowTcapOps(xw, etGetTcap)) { 3318 Bool fkey; 3319 unsigned state; 3320 int code; 3321 const char *tmp; 3322 const char *parsed = ++cp; 3323 3324 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 3325 3326 unparseputc1(xw, ANSI_DCS); 3327 3328 unparseputc(xw, code >= 0 ? '1' : '0'); 3329 3330 unparseputc(xw, '+'); 3331 unparseputc(xw, 'r'); 3332 3333 while (*cp != 0 && (code >= -1)) { 3334 if (cp == parsed) 3335 break; /* no data found, error */ 3336 3337 for (tmp = cp; tmp != parsed; ++tmp) 3338 unparseputc(xw, *tmp); 3339 3340 if (code >= 0) { 3341 unparseputc(xw, '='); 3342 screen->tc_query_code = code; 3343 screen->tc_query_fkey = fkey; 3344#if OPT_ISO_COLORS 3345 /* XK_COLORS is a fake code for the "Co" entry (maximum 3346 * number of colors) */ 3347 if (code == XK_COLORS) { 3348 unparseputn(xw, NUM_ANSI_COLORS); 3349 } else 3350#endif 3351 if (code == XK_TCAPNAME) { 3352 unparseputs(xw, resource.term_name); 3353 } else { 3354 XKeyEvent event; 3355 event.state = state; 3356 Input(xw, &event, False); 3357 } 3358 screen->tc_query_code = -1; 3359 } else { 3360 break; /* no match found, error */ 3361 } 3362 3363 cp = parsed; 3364 if (*parsed == ';') { 3365 unparseputc(xw, *parsed++); 3366 cp = parsed; 3367 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 3368 } 3369 } 3370 unparseputc1(xw, ANSI_ST); 3371 } 3372 break; 3373 } 3374 break; 3375#endif 3376 default: 3377 if (screen->terminal_id >= 200) { /* VT220 */ 3378 parse_ansi_params(¶ms, &cp); 3379 switch (params.a_final) { 3380 case '|': /* DECUDK */ 3381 if (params.a_param[0] == 0) 3382 reset_decudk(); 3383 parse_decudk(cp); 3384 break; 3385 case '{': /* DECDLD (no '}' case though) */ 3386 parse_decdld(¶ms, cp); 3387 break; 3388 } 3389 } 3390 break; 3391 } 3392 unparse_end(xw); 3393} 3394 3395#if OPT_DEC_RECTOPS 3396enum { 3397 mdUnknown = 0, 3398 mdMaybeSet = 1, 3399 mdMaybeReset = 2, 3400 mdAlwaysSet = 3, 3401 mdAlwaysReset = 4 3402}; 3403 3404#define MdBool(bool) ((bool) ? mdMaybeSet : mdMaybeReset) 3405#define MdFlag(mode,flag) MdBool(xw->keyboard.flags & MODE_KAM) 3406 3407/* 3408 * Reply is the same format as the query, with pair of mode/value: 3409 * 0 - not recognized 3410 * 1 - set 3411 * 2 - reset 3412 * 3 - permanently set 3413 * 4 - permanently reset 3414 * Only one mode can be reported at a time. 3415 */ 3416void 3417do_rpm(XtermWidget xw, int nparams, int *params) 3418{ 3419 ANSI reply; 3420 int result = 0; 3421 int count = 0; 3422 3423 TRACE(("do_rpm %d:%d\n", nparams, params[0])); 3424 memset(&reply, 0, sizeof(reply)); 3425 if (nparams >= 1) { 3426 switch (params[0]) { 3427 case 1: /* GATM */ 3428 result = mdAlwaysReset; 3429 break; 3430 case 2: 3431 result = MdFlag(xw->keyboard.flags, MODE_KAM); 3432 break; 3433 case 3: /* CRM */ 3434 result = mdMaybeReset; 3435 break; 3436 case 4: 3437 result = MdFlag(xw->flags, INSERT); 3438 break; 3439 case 5: /* SRTM */ 3440 case 7: /* VEM */ 3441 case 10: /* HEM */ 3442 case 11: /* PUM */ 3443 result = mdAlwaysReset; 3444 break; 3445 case 12: 3446 result = MdFlag(xw->keyboard.flags, MODE_SRM); 3447 break; 3448 case 13: /* FEAM */ 3449 case 14: /* FETM */ 3450 case 15: /* MATM */ 3451 case 16: /* TTM */ 3452 case 17: /* SATM */ 3453 case 18: /* TSM */ 3454 case 19: /* EBM */ 3455 result = mdAlwaysReset; 3456 break; 3457 case 20: 3458 result = MdFlag(xw->flags, LINEFEED); 3459 break; 3460 } 3461 reply.a_param[count++] = (ParmType) params[0]; 3462 reply.a_param[count++] = (ParmType) result; 3463 } 3464 reply.a_type = ANSI_CSI; 3465 reply.a_nparam = (ParmType) count; 3466 reply.a_inters = '$'; 3467 reply.a_final = 'y'; 3468 unparseseq(xw, &reply); 3469} 3470 3471void 3472do_decrpm(XtermWidget xw, int nparams, int *params) 3473{ 3474 ANSI reply; 3475 int result = 0; 3476 int count = 0; 3477 3478 TRACE(("do_decrpm %d:%d\n", nparams, params[0])); 3479 memset(&reply, 0, sizeof(reply)); 3480 if (nparams >= 1) { 3481 TScreen *screen = TScreenOf(xw); 3482 3483 switch (params[0]) { 3484 case 1: /* DECCKM */ 3485 result = MdFlag(xw->keyboard.flags, MODE_DECCKM); 3486 break; 3487 case 2: /* DECANM - ANSI/VT52 mode */ 3488#if OPT_VT52_MODE 3489 result = MdBool(screen->terminal_id >= 100); 3490#else 3491 result = mdMaybeSet; 3492#endif 3493 break; 3494 case 3: /* DECCOLM */ 3495 result = MdFlag(xw->flags, IN132COLUMNS); 3496 break; 3497 case 4: /* DECSCLM (slow scroll) */ 3498 result = MdFlag(xw->flags, SMOOTHSCROLL); 3499 break; 3500 case 5: /* DECSCNM */ 3501 result = MdFlag(xw->flags, REVERSE_VIDEO); 3502 break; 3503 case 6: /* DECOM */ 3504 result = MdFlag(xw->flags, ORIGIN); 3505 break; 3506 case 7: /* DECAWM */ 3507 result = MdFlag(xw->flags, WRAPAROUND); 3508 break; 3509 case 8: /* DECARM */ 3510 result = mdAlwaysReset; 3511 break; 3512 case SET_X10_MOUSE: /* X10 mouse */ 3513 result = MdBool(screen->send_mouse_pos == X10_MOUSE); 3514 break; 3515#if OPT_TOOLBAR 3516 case 10: /* rxvt */ 3517 result = MdBool(resource.toolBar); 3518 break; 3519#endif 3520#if OPT_BLINK_CURS 3521 case 12: /* att610: Start/stop blinking cursor */ 3522 result = MdBool(screen->cursor_blink_res); 3523 break; 3524#endif 3525 case 18: /* DECPFF: print form feed */ 3526 result = MdBool(screen->printer_formfeed); 3527 break; 3528 case 19: /* DECPEX: print extent */ 3529 result = MdBool(screen->printer_extent); 3530 break; 3531 case 25: /* DECTCEM: Show/hide cursor (VT200) */ 3532 result = MdBool(screen->cursor_set); 3533 break; 3534 case 30: /* rxvt */ 3535 result = MdBool(screen->fullVwin.sb_info.width != OFF); 3536 break; 3537#if OPT_SHIFT_FONTS 3538 case 35: /* rxvt */ 3539 result = MdBool(xw->misc.shift_fonts); 3540 break; 3541#endif 3542#if OPT_TEK4014 3543 case 38: /* DECTEK */ 3544 result = MdBool(TEK4014_ACTIVE(xw)); 3545 break; 3546#endif 3547 case 40: /* 132 column mode */ 3548 result = MdBool(screen->c132); 3549 break; 3550 case 41: /* curses hack */ 3551 result = MdBool(screen->curses); 3552 break; 3553 case 42: /* DECNRCM national charset (VT220) */ 3554 result = MdFlag(xw->flags, NATIONAL); 3555 break; 3556 case 44: /* margin bell */ 3557 result = MdBool(screen->marginbell); 3558 break; 3559 case 45: /* reverse wraparound */ 3560 result = MdFlag(xw->flags, REVERSEWRAP); 3561 break; 3562#ifdef ALLOWLOGGING 3563 case 46: /* logging */ 3564#ifdef ALLOWLOGFILEONOFF 3565 result = MdBool(screen->logging); 3566#endif /* ALLOWLOGFILEONOFF */ 3567 break; 3568#endif 3569 case 1049: /* alternate buffer & cursor */ 3570 /* FALLTHRU */ 3571 case 1047: 3572 /* FALLTHRU */ 3573 case 47: /* alternate buffer */ 3574 result = MdBool(screen->whichBuf); 3575 break; 3576 case 66: /* DECNKM */ 3577 result = MdFlag(xw->keyboard.flags, MODE_DECKPAM); 3578 break; 3579 case 67: /* DECBKM */ 3580 result = MdFlag(xw->keyboard.flags, MODE_DECBKM); 3581 break; 3582 case SET_VT200_MOUSE: /* xterm bogus sequence */ 3583 result = MdBool(screen->send_mouse_pos == VT200_MOUSE); 3584 break; 3585 case SET_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */ 3586 result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE); 3587 break; 3588 case SET_BTN_EVENT_MOUSE: 3589 result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE); 3590 break; 3591 case SET_ANY_EVENT_MOUSE: 3592 result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE); 3593 break; 3594#if OPT_FOCUS_EVENT 3595 case SET_FOCUS_EVENT_MOUSE: 3596 result = MdBool(screen->send_focus_pos); 3597 break; 3598#endif 3599 case SET_EXT_MODE_MOUSE: 3600 result = MdBool(screen->ext_mode_mouse); 3601 break; 3602 case 1010: /* rxvt */ 3603 result = MdBool(screen->scrollttyoutput); 3604 break; 3605 case 1011: /* rxvt */ 3606 result = MdBool(screen->scrollkey); 3607 break; 3608 case 1034: 3609 result = MdBool(screen->input_eight_bits); 3610 break; 3611#if OPT_NUM_LOCK 3612 case 1035: 3613 result = MdBool(xw->misc.real_NumLock); 3614 break; 3615 case 1036: 3616 result = MdBool(screen->meta_sends_esc); 3617 break; 3618#endif 3619 case 1037: 3620 result = MdBool(screen->delete_is_del); 3621 break; 3622#if OPT_NUM_LOCK 3623 case 1039: 3624 result = MdBool(screen->alt_sends_esc); 3625 break; 3626#endif 3627 case 1040: 3628 result = MdBool(screen->keepSelection); 3629 break; 3630 case 1041: 3631 result = MdBool(screen->selectToClipboard); 3632 break; 3633 case 1042: 3634 result = MdBool(screen->bellIsUrgent); 3635 break; 3636 case 1043: 3637 result = MdBool(screen->poponbell); 3638 break; 3639 case 1048: 3640 result = MdBool(screen->sc[screen->whichBuf].saved); 3641 break; 3642#if OPT_TCAP_FKEYS 3643 case 1050: 3644 result = MdBool(xw->keyboard.type == keyboardIsTermcap); 3645 break; 3646#endif 3647#if OPT_SUN_FUNC_KEYS 3648 case 1051: 3649 result = MdBool(xw->keyboard.type == keyboardIsSun); 3650 break; 3651#endif 3652#if OPT_HP_FUNC_KEYS 3653 case 1052: 3654 result = MdBool(xw->keyboard.type == keyboardIsHP); 3655 break; 3656#endif 3657#if OPT_SCO_FUNC_KEYS 3658 case 1053: 3659 result = MdBool(xw->keyboard.type == keyboardIsSCO); 3660 break; 3661#endif 3662 case 1060: 3663 result = MdBool(xw->keyboard.type == keyboardIsLegacy); 3664 break; 3665#if OPT_SUNPC_KBD 3666 case 1061: 3667 result = MdBool(xw->keyboard.type == keyboardIsVT220); 3668 break; 3669#endif 3670#if OPT_READLINE 3671 case SET_BUTTON1_MOVE_POINT: 3672 result = MdBool(screen->click1_moves); 3673 break; 3674 case SET_BUTTON2_MOVE_POINT: 3675 result = MdBool(screen->paste_moves); 3676 break; 3677 case SET_DBUTTON3_DELETE: 3678 result = MdBool(screen->dclick3_deletes); 3679 break; 3680 case SET_PASTE_IN_BRACKET: 3681 result = MdBool(screen->paste_brackets); 3682 break; 3683 case SET_PASTE_QUOTE: 3684 result = MdBool(screen->paste_quotes); 3685 break; 3686 case SET_PASTE_LITERAL_NL: 3687 result = MdBool(screen->paste_literal_nl); 3688 break; 3689#endif /* OPT_READLINE */ 3690 } 3691 reply.a_param[count++] = (ParmType) params[0]; 3692 reply.a_param[count++] = (ParmType) result; 3693 } 3694 reply.a_type = ANSI_CSI; 3695 reply.a_pintro = '?'; 3696 reply.a_nparam = (ParmType) count; 3697 reply.a_inters = '$'; 3698 reply.a_final = 'y'; 3699 unparseseq(xw, &reply); 3700} 3701#endif /* OPT_DEC_RECTOPS */ 3702 3703char * 3704udk_lookup(int keycode, int *len) 3705{ 3706 if (keycode >= 0 && keycode < MAX_UDK) { 3707 *len = user_keys[keycode].len; 3708 return user_keys[keycode].str; 3709 } 3710 return 0; 3711} 3712 3713static void 3714ChangeGroup(XtermWidget xw, const char *attribute, char *value) 3715{ 3716#if OPT_WIDE_CHARS 3717 static Char *converted; /* NO_LEAKS */ 3718#endif 3719 static char empty[1]; 3720 3721 Arg args[1]; 3722 Boolean changed = True; 3723 Widget w = CURRENT_EMU(); 3724 Widget top = SHELL_OF(w); 3725 3726 char *my_attr; 3727 char *name; 3728 size_t limit; 3729 Char *c1; 3730 Char *cp; 3731 3732 if (!AllowTitleOps(xw)) 3733 return; 3734 3735 if (value == 0) 3736 value = empty; 3737 if (IsTitleMode(xw, tmSetBase16)) { 3738 const char *temp; 3739 char *test; 3740 3741 value = x_decode_hex(value, &temp); 3742 if (*temp != '\0') 3743 return; 3744 for (test = value; *test != '\0'; ++test) { 3745 if (CharOf(*test) < 32) { 3746 *test = '\0'; 3747 break; 3748 } 3749 } 3750 } 3751 3752 c1 = (Char *) value; 3753 name = value; 3754 limit = strlen(name); 3755 my_attr = x_strdup(attribute); 3756 3757 TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name)); 3758 3759 /* 3760 * Ignore titles that are too long to be plausible requests. 3761 */ 3762 if (limit > 0 && limit < 1024) { 3763 3764 /* 3765 * After all decoding, overwrite nonprintable characters with '?'. 3766 */ 3767 for (cp = c1; *cp != 0; ++cp) { 3768 Char *c2 = cp; 3769 if (!xtermIsPrintable(xw, &cp, c1 + limit)) { 3770 memset(c2, '?', (size_t) (cp + 1 - c2)); 3771 } 3772 } 3773 3774#if OPT_WIDE_CHARS 3775 /* 3776 * If we're running in UTF-8 mode, and have not been told that the 3777 * title string is in UTF-8, it is likely that non-ASCII text in the 3778 * string will be rejected because it is not printable in the current 3779 * locale. So we convert it to UTF-8, allowing the X library to 3780 * convert it back. 3781 */ 3782 if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) { 3783 int n; 3784 3785 for (n = 0; name[n] != '\0'; ++n) { 3786 if (CharOf(name[n]) > 127) { 3787 if (converted != 0) 3788 free(converted); 3789 if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) { 3790 Char *temp = converted; 3791 while (*name != 0) { 3792 temp = convertToUTF8(temp, CharOf(*name)); 3793 ++name; 3794 } 3795 *temp = 0; 3796 name = (char *) converted; 3797 TRACE(("...converted{%s}\n", name)); 3798 } 3799 break; 3800 } 3801 } 3802 } 3803#endif 3804 3805#if OPT_SAME_NAME 3806 /* If the attribute isn't going to change, then don't bother... */ 3807 3808 if (resource.sameName) { 3809 char *buf = 0; 3810 XtSetArg(args[0], my_attr, &buf); 3811 XtGetValues(top, args, 1); 3812 TRACE(("...comparing{%s}\n", buf)); 3813 if (buf != 0 && strcmp(name, buf) == 0) 3814 changed = False; 3815 } 3816#endif /* OPT_SAME_NAME */ 3817 3818 if (changed) { 3819 TRACE(("...updating %s\n", my_attr)); 3820 TRACE(("...value is %s\n", name)); 3821 XtSetArg(args[0], my_attr, name); 3822 XtSetValues(top, args, 1); 3823 3824#if OPT_WIDE_CHARS 3825 if (xtermEnvUTF8()) { 3826 Display *dpy = XtDisplay(xw); 3827 Atom my_atom; 3828 3829 const char *propname = (!strcmp(my_attr, XtNtitle) 3830 ? "_NET_WM_NAME" 3831 : "_NET_WM_ICON_NAME"); 3832 if ((my_atom = XInternAtom(dpy, propname, False)) != None) { 3833 if (IsSetUtf8Title(xw)) { 3834 TRACE(("...updating %s\n", propname)); 3835 TRACE(("...value is %s\n", value)); 3836 XChangeProperty(dpy, VShellWindow(xw), my_atom, 3837 XA_UTF8_STRING(dpy), 8, 3838 PropModeReplace, 3839 (Char *) value, 3840 (int) strlen(value)); 3841 } else { 3842 TRACE(("...deleting %s\n", propname)); 3843 XDeleteProperty(dpy, VShellWindow(xw), my_atom); 3844 } 3845 } 3846 } 3847#endif 3848 } 3849 3850 free(my_attr); 3851 3852 if (IsTitleMode(xw, tmSetBase16)) 3853 free(value); 3854 3855 } 3856 return; 3857} 3858 3859void 3860ChangeIconName(XtermWidget xw, char *name) 3861{ 3862 if (name == 0) { 3863 static char dummy[] = ""; 3864 name = dummy; 3865 } 3866#if OPT_ZICONBEEP /* If warning should be given then give it */ 3867 if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) { 3868 char *newname = CastMallocN(char, strlen(name) + 4); 3869 if (!newname) { 3870 fprintf(stderr, "malloc failed in ChangeIconName\n"); 3871 return; 3872 } 3873 strcpy(newname, "*** "); 3874 strcat(newname, name); 3875 ChangeGroup(xw, XtNiconName, newname); 3876 free(newname); 3877 } else 3878#endif /* OPT_ZICONBEEP */ 3879 ChangeGroup(xw, XtNiconName, name); 3880} 3881 3882void 3883ChangeTitle(XtermWidget xw, char *name) 3884{ 3885 ChangeGroup(xw, XtNtitle, name); 3886} 3887 3888#define Strlen(s) strlen((char *)(s)) 3889 3890void 3891ChangeXprop(char *buf) 3892{ 3893 Display *dpy = XtDisplay(toplevel); 3894 Window w = XtWindow(toplevel); 3895 XTextProperty text_prop; 3896 Atom aprop; 3897 Char *pchEndPropName = (Char *) strchr(buf, '='); 3898 3899 if (pchEndPropName) 3900 *pchEndPropName = '\0'; 3901 aprop = XInternAtom(dpy, buf, False); 3902 if (pchEndPropName == NULL) { 3903 /* no "=value" given, so delete the property */ 3904 XDeleteProperty(dpy, w, aprop); 3905 } else { 3906 text_prop.value = pchEndPropName + 1; 3907 text_prop.encoding = XA_STRING; 3908 text_prop.format = 8; 3909 text_prop.nitems = Strlen(text_prop.value); 3910 XSetTextProperty(dpy, w, &text_prop, aprop); 3911 } 3912} 3913 3914/***====================================================================***/ 3915 3916/* 3917 * This is part of ReverseVideo(). It reverses the data stored for the old 3918 * "dynamic" colors that might have been retrieved using OSC 10-18. 3919 */ 3920void 3921ReverseOldColors(void) 3922{ 3923 ScrnColors *pOld = pOldColors; 3924 Pixel tmpPix; 3925 char *tmpName; 3926 3927 if (pOld) { 3928 /* change text cursor, if necesary */ 3929 if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { 3930 pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; 3931 if (pOld->names[TEXT_CURSOR]) { 3932 XtFree(pOldColors->names[TEXT_CURSOR]); 3933 pOld->names[TEXT_CURSOR] = NULL; 3934 } 3935 if (pOld->names[TEXT_BG]) { 3936 if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { 3937 pOld->names[TEXT_CURSOR] = tmpName; 3938 } 3939 } 3940 } 3941 3942 EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); 3943 EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); 3944 3945 EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); 3946 EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); 3947 3948#if OPT_TEK4014 3949 EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); 3950 EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); 3951#endif 3952 } 3953 return; 3954} 3955 3956Bool 3957AllocateTermColor(XtermWidget xw, 3958 ScrnColors * pNew, 3959 int ndx, 3960 const char *name, 3961 Bool always) 3962{ 3963 Bool result = False; 3964 3965 if (always || AllowColorOps(xw, ecSetColor)) { 3966 XColor def; 3967 TScreen *screen = TScreenOf(xw); 3968 Colormap cmap = xw->core.colormap; 3969 char *newName; 3970 3971 if (XParseColor(screen->display, cmap, name, &def) 3972 && (XAllocColor(screen->display, cmap, &def) 3973 || find_closest_color(screen->display, cmap, &def)) 3974 && (newName = x_strdup(name)) != 0) { 3975 if (COLOR_DEFINED(pNew, ndx)) 3976 free(pNew->names[ndx]); 3977 SET_COLOR_VALUE(pNew, ndx, def.pixel); 3978 SET_COLOR_NAME(pNew, ndx, newName); 3979 TRACE(("AllocateTermColor #%d: %s (pixel %#lx)\n", ndx, newName, def.pixel)); 3980 result = True; 3981 } else { 3982 TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); 3983 } 3984 } 3985 return result; 3986} 3987/***====================================================================***/ 3988 3989/* ARGSUSED */ 3990void 3991Panic(const char *s GCC_UNUSED, int a GCC_UNUSED) 3992{ 3993#ifdef DEBUG 3994 if (debug) { 3995 fprintf(stderr, "%s: PANIC!\t", ProgramName); 3996 fprintf(stderr, s, a); 3997 fputs("\r\n", stderr); 3998 fflush(stderr); 3999 } 4000#endif /* DEBUG */ 4001} 4002 4003const char * 4004SysErrorMsg(int code) 4005{ 4006 static char unknown[] = "unknown error"; 4007 char *s = strerror(code); 4008 return s ? s : unknown; 4009} 4010 4011const char * 4012SysReasonMsg(int code) 4013{ 4014 /* *INDENT-OFF* */ 4015 static const struct { 4016 int code; 4017 const char *name; 4018 } table[] = { 4019 { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" }, 4020 { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" }, 4021 { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", }, 4022 { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", }, 4023 { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", }, 4024 { ERROR_PTSNAME, "spawn: ptsname() failed", }, 4025 { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", }, 4026 { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" }, 4027 { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" }, 4028 { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" }, 4029 { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" }, 4030 { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" }, 4031 { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" }, 4032 { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" }, 4033 { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" }, 4034 { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" }, 4035 { ERROR_INIGROUPS, "spawn: initgroups() failed" }, 4036 { ERROR_FORK, "spawn: fork() failed" }, 4037 { ERROR_EXEC, "spawn: exec() failed" }, 4038 { ERROR_PTYS, "get_pty: not enough ptys" }, 4039 { ERROR_PTY_EXEC, "waiting for initial map" }, 4040 { ERROR_SETUID, "spawn: setuid() failed" }, 4041 { ERROR_INIT, "spawn: can't initialize window" }, 4042 { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" }, 4043 { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" }, 4044 { ERROR_LUMALLOC, "luit: command-line malloc failed" }, 4045 { ERROR_SELECT, "in_put: select() failed" }, 4046 { ERROR_VINIT, "VTInit: can't initialize window" }, 4047 { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" }, 4048 { ERROR_TSELECT, "Tinput: select() failed" }, 4049 { ERROR_TINIT, "TekInit: can't initialize window" }, 4050 { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" }, 4051 { ERROR_LOGEXEC, "StartLog: exec() failed" }, 4052 { ERROR_XERROR, "xerror: XError event" }, 4053 { ERROR_XIOERROR, "xioerror: X I/O error" }, 4054 { ERROR_SCALLOC, "Alloc: calloc() failed on base" }, 4055 { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" }, 4056 { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" }, 4057 { ERROR_MMALLOC, "my_memmove: malloc/realloc failed" }, 4058 }; 4059 /* *INDENT-ON* */ 4060 4061 Cardinal n; 4062 const char *result = "?"; 4063 4064 for (n = 0; n < XtNumber(table); ++n) { 4065 if (code == table[n].code) { 4066 result = table[n].name; 4067 break; 4068 } 4069 } 4070 return result; 4071} 4072 4073void 4074SysError(int code) 4075{ 4076 int oerrno = errno; 4077 4078 fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno); 4079 fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); 4080 fprintf(stderr, "Reason: %s\n", SysReasonMsg(code)); 4081 4082 Cleanup(code); 4083} 4084 4085/* 4086 * cleanup by sending SIGHUP to client processes 4087 */ 4088void 4089Cleanup(int code) 4090{ 4091 static Bool cleaning; 4092 TScreen *screen = TScreenOf(term); 4093 4094 /* 4095 * Process "-hold" and session cleanup only for a normal exit. 4096 */ 4097 if (code == 0) { 4098 if (cleaning) { 4099 hold_screen = 0; 4100 return; 4101 } 4102 4103 cleaning = True; 4104 need_cleanup = False; 4105 4106 TRACE(("Cleanup %d\n", code)); 4107 4108 if (hold_screen) { 4109 hold_screen = 2; 4110 while (hold_screen) { 4111 xevents(); 4112 Sleep(10); 4113 } 4114 } 4115#if OPT_SESSION_MGT 4116 if (resource.sessionMgt) { 4117 XtVaSetValues(toplevel, 4118 XtNjoinSession, False, 4119 NULL); 4120 } 4121#endif 4122 } 4123 4124 if (screen->pid > 1) { 4125 (void) kill_process_group(screen->pid, SIGHUP); 4126 } 4127 Exit(code); 4128} 4129 4130#ifndef VMS 4131char * 4132xtermFindShell(char *leaf, Bool warning) 4133{ 4134 char *s; 4135 char *d; 4136 char *tmp; 4137 char *result = leaf; 4138 4139 TRACE(("xtermFindShell(%s)\n", leaf)); 4140 if (*result != '\0' && strchr("+/-", *result) == 0) { 4141 /* find it in $PATH */ 4142 if ((s = x_getenv("PATH")) != 0) { 4143 if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) { 4144 Bool found = False; 4145 while (*s != '\0') { 4146 strcpy(tmp, s); 4147 for (d = tmp;; ++d) { 4148 if (*d == ':' || *d == '\0') { 4149 int skip = (*d != '\0'); 4150 *d = '/'; 4151 strcpy(d + 1, leaf); 4152 if (skip) 4153 ++d; 4154 s += (d - tmp); 4155 if (*tmp == '/' 4156 && strstr(tmp, "..") == 0 4157 && access(tmp, X_OK) == 0) { 4158 result = x_strdup(tmp); 4159 found = True; 4160 } 4161 break; 4162 } 4163 if (found) 4164 break; 4165 } 4166 if (found) 4167 break; 4168 } 4169 free(tmp); 4170 } 4171 } 4172 } 4173 TRACE(("...xtermFindShell(%s)\n", result)); 4174 if (*result != '/' 4175 || strstr(result, "..") != 0 4176 || access(result, X_OK) != 0) { 4177 if (warning) 4178 fprintf(stderr, "No absolute path found for shell: %s\n", result); 4179 result = 0; 4180 } 4181 return result; 4182} 4183#endif /* VMS */ 4184 4185#define ENV_HUNK(n) (unsigned) ((((n) + 1) | 31) + 1) 4186 4187/* 4188 * copy the environment before Setenv'ing. 4189 */ 4190void 4191xtermCopyEnv(char **oldenv) 4192{ 4193 unsigned size; 4194 char **newenv; 4195 4196 for (size = 0; oldenv[size] != NULL; size++) { 4197 ; 4198 } 4199 4200 newenv = TypeCallocN(char *, ENV_HUNK(size)); 4201 memmove(newenv, oldenv, size * sizeof(char *)); 4202 environ = newenv; 4203} 4204 4205/* 4206 * sets the value of var to be arg in the Unix 4.2 BSD environment env. 4207 * Var should end with '=' (bindings are of the form "var=value"). 4208 * This procedure assumes the memory for the first level of environ 4209 * was allocated using calloc, with enough extra room at the end so not 4210 * to have to do a realloc(). 4211 */ 4212void 4213xtermSetenv(const char *var, const char *value) 4214{ 4215 if (value != 0) { 4216 char *test; 4217 int envindex = 0; 4218 size_t len = strlen(var); 4219 int found = -1; 4220 4221 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 4222 4223 while ((test = environ[envindex]) != NULL) { 4224 if (strncmp(test, var, len) == 0 && test[len] == '=') { 4225 found = envindex; 4226 break; 4227 } 4228 envindex++; 4229 } 4230 4231 if (found < 0) { 4232 unsigned need = ENV_HUNK(envindex + 1); 4233 unsigned have = ENV_HUNK(envindex); 4234 4235 if (need > have) { 4236 char **newenv; 4237 newenv = TypeMallocN(char *, need); 4238 if (newenv == 0) { 4239 fprintf(stderr, "Cannot increase environment\n"); 4240 return; 4241 } 4242 memmove(newenv, environ, have * sizeof(*newenv)); 4243 free(environ); 4244 environ = newenv; 4245 } 4246 4247 found = envindex; 4248 environ[found + 1] = NULL; 4249 environ = environ; 4250 } 4251 4252 environ[found] = CastMallocN(char, 1 + len + strlen(value)); 4253 if (environ[found] == 0) { 4254 fprintf(stderr, "Cannot allocate environment %s\n", var); 4255 return; 4256 } 4257 sprintf(environ[found], "%s=%s", var, value); 4258 } 4259} 4260 4261/*ARGSUSED*/ 4262int 4263xerror(Display * d, XErrorEvent * ev) 4264{ 4265 fprintf(stderr, "%s: warning, error event received:\n", ProgramName); 4266 (void) XmuPrintDefaultErrorMessage(d, ev, stderr); 4267 Exit(ERROR_XERROR); 4268 return 0; /* appease the compiler */ 4269} 4270 4271/*ARGSUSED*/ 4272int 4273xioerror(Display * dpy) 4274{ 4275 int the_error = errno; 4276 4277 (void) fprintf(stderr, 4278 "%s: fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", 4279 ProgramName, the_error, SysErrorMsg(the_error), 4280 DisplayString(dpy)); 4281 4282 Exit(ERROR_XIOERROR); 4283 return 0; /* appease the compiler */ 4284} 4285 4286void 4287xt_error(String message) 4288{ 4289 (void) fprintf(stderr, "%s Xt error: %s\n", ProgramName, message); 4290 4291 /* 4292 * Check for the obvious - Xt does a poor job of reporting this. 4293 */ 4294 if (x_getenv("DISPLAY") == 0) { 4295 fprintf(stderr, "%s: DISPLAY is not set\n", ProgramName); 4296 } 4297 exit(1); 4298} 4299 4300int 4301XStrCmp(char *s1, char *s2) 4302{ 4303 if (s1 && s2) 4304 return (strcmp(s1, s2)); 4305 if (s1 && *s1) 4306 return (1); 4307 if (s2 && *s2) 4308 return (-1); 4309 return (0); 4310} 4311 4312#if OPT_TEK4014 4313static void 4314withdraw_window(Display * dpy, Window w, int scr) 4315{ 4316 TRACE(("withdraw_window %#lx\n", (long) w)); 4317 (void) XmuUpdateMapHints(dpy, w, NULL); 4318 XWithdrawWindow(dpy, w, scr); 4319 return; 4320} 4321#endif 4322 4323void 4324set_vt_visibility(Bool on) 4325{ 4326 XtermWidget xw = term; 4327 TScreen *screen = TScreenOf(xw); 4328 4329 TRACE(("set_vt_visibility(%d)\n", on)); 4330 if (on) { 4331 if (!screen->Vshow && xw) { 4332 VTInit(xw); 4333 XtMapWidget(XtParent(xw)); 4334#if OPT_TOOLBAR 4335 /* we need both of these during initialization */ 4336 XtMapWidget(SHELL_OF(xw)); 4337 ShowToolbar(resource.toolBar); 4338#endif 4339 screen->Vshow = True; 4340 } 4341 } 4342#if OPT_TEK4014 4343 else { 4344 if (screen->Vshow && xw) { 4345 withdraw_window(XtDisplay(xw), 4346 VShellWindow(xw), 4347 XScreenNumberOfScreen(XtScreen(xw))); 4348 screen->Vshow = False; 4349 } 4350 } 4351 set_vthide_sensitivity(); 4352 set_tekhide_sensitivity(); 4353 update_vttekmode(); 4354 update_tekshow(); 4355 update_vtshow(); 4356#endif 4357 return; 4358} 4359 4360#if OPT_TEK4014 4361void 4362set_tek_visibility(Bool on) 4363{ 4364 TRACE(("set_tek_visibility(%d)\n", on)); 4365 4366 if (on) { 4367 if (!TEK4014_SHOWN(term)) { 4368 if (tekWidget == 0) { 4369 TekInit(); /* will exit on failure */ 4370 } 4371 if (tekWidget != 0) { 4372 Widget tekParent = SHELL_OF(tekWidget); 4373 XtRealizeWidget(tekParent); 4374 XtMapWidget(XtParent(tekWidget)); 4375#if OPT_TOOLBAR 4376 /* we need both of these during initialization */ 4377 XtMapWidget(tekParent); 4378 XtMapWidget(tekWidget); 4379#endif 4380 XtOverrideTranslations(tekParent, 4381 XtParseTranslationTable 4382 ("<Message>WM_PROTOCOLS: DeleteWindow()")); 4383 (void) XSetWMProtocols(XtDisplay(tekParent), 4384 XtWindow(tekParent), 4385 &wm_delete_window, 1); 4386 TEK4014_SHOWN(term) = True; 4387 } 4388 } 4389 } else { 4390 if (TEK4014_SHOWN(term) && tekWidget) { 4391 withdraw_window(XtDisplay(tekWidget), 4392 TShellWindow, 4393 XScreenNumberOfScreen(XtScreen(tekWidget))); 4394 TEK4014_SHOWN(term) = False; 4395 } 4396 } 4397 set_tekhide_sensitivity(); 4398 set_vthide_sensitivity(); 4399 update_vtshow(); 4400 update_tekshow(); 4401 update_vttekmode(); 4402 return; 4403} 4404 4405void 4406end_tek_mode(void) 4407{ 4408 XtermWidget xw = term; 4409 4410 if (TEK4014_ACTIVE(xw)) { 4411 FlushLog(xw); 4412 longjmp(Tekend, 1); 4413 } 4414 return; 4415} 4416 4417void 4418end_vt_mode(void) 4419{ 4420 XtermWidget xw = term; 4421 4422 if (!TEK4014_ACTIVE(xw)) { 4423 FlushLog(xw); 4424 TEK4014_ACTIVE(xw) = True; 4425 longjmp(VTend, 1); 4426 } 4427 return; 4428} 4429 4430void 4431switch_modes(Bool tovt) /* if true, then become vt mode */ 4432{ 4433 if (tovt) { 4434 if (tekRefreshList) 4435 TekRefresh(tekWidget); 4436 end_tek_mode(); /* WARNING: this does a longjmp... */ 4437 } else { 4438 end_vt_mode(); /* WARNING: this does a longjmp... */ 4439 } 4440} 4441 4442void 4443hide_vt_window(void) 4444{ 4445 set_vt_visibility(False); 4446 if (!TEK4014_ACTIVE(term)) 4447 switch_modes(False); /* switch to tek mode */ 4448} 4449 4450void 4451hide_tek_window(void) 4452{ 4453 set_tek_visibility(False); 4454 tekRefreshList = (TekLink *) 0; 4455 if (TEK4014_ACTIVE(term)) 4456 switch_modes(True); /* does longjmp to vt mode */ 4457} 4458#endif /* OPT_TEK4014 */ 4459 4460static const char * 4461skip_punct(const char *s) 4462{ 4463 while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { 4464 ++s; 4465 } 4466 return s; 4467} 4468 4469static int 4470cmp_options(const void *a, const void *b) 4471{ 4472 const char *s1 = skip_punct(((const OptionHelp *) a)->opt); 4473 const char *s2 = skip_punct(((const OptionHelp *) b)->opt); 4474 return strcmp(s1, s2); 4475} 4476 4477static int 4478cmp_resources(const void *a, const void *b) 4479{ 4480 return strcmp(((const XrmOptionDescRec *) a)->option, 4481 ((const XrmOptionDescRec *) b)->option); 4482} 4483 4484XrmOptionDescRec * 4485sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) 4486{ 4487 static XrmOptionDescRec *res_array = 0; 4488 4489#ifdef NO_LEAKS 4490 if (descs == 0) { 4491 if (res_array != 0) { 4492 free(res_array); 4493 res_array = 0; 4494 } 4495 } else 4496#endif 4497 if (res_array == 0) { 4498 Cardinal j; 4499 4500 /* make a sorted index to 'resources' */ 4501 res_array = TypeCallocN(XrmOptionDescRec, res_count); 4502 if (res_array != 0) { 4503 for (j = 0; j < res_count; j++) 4504 res_array[j] = descs[j]; 4505 qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources); 4506 } 4507 } 4508 return res_array; 4509} 4510 4511/* 4512 * The first time this is called, construct sorted index to the main program's 4513 * list of options, taking into account the on/off options which will be 4514 * compressed into one token. It's a lot simpler to do it this way than 4515 * maintain the list in sorted form with lots of ifdef's. 4516 */ 4517OptionHelp * 4518sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) 4519{ 4520 static OptionHelp *opt_array = 0; 4521 4522#ifdef NO_LEAKS 4523 if (descs == 0 && opt_array != 0) { 4524 sortedOptDescs(descs, numDescs); 4525 free(opt_array); 4526 opt_array = 0; 4527 return 0; 4528 } else if (options == 0 || descs == 0) { 4529 return 0; 4530 } 4531#endif 4532 4533 if (opt_array == 0) { 4534 size_t opt_count, j; 4535#if OPT_TRACE 4536 Cardinal k; 4537 XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); 4538 int code; 4539 const char *mesg; 4540#else 4541 (void) descs; 4542 (void) numDescs; 4543#endif 4544 4545 /* count 'options' and make a sorted index to it */ 4546 for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { 4547 ; 4548 } 4549 opt_array = TypeCallocN(OptionHelp, opt_count + 1); 4550 for (j = 0; j < opt_count; j++) 4551 opt_array[j] = options[j]; 4552 qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); 4553 4554 /* supply the "turn on/off" strings if needed */ 4555#if OPT_TRACE 4556 for (j = 0; j < opt_count; j++) { 4557 if (!strncmp(opt_array[j].opt, "-/+", 3)) { 4558 char temp[80]; 4559 const char *name = opt_array[j].opt + 3; 4560 for (k = 0; k < numDescs; ++k) { 4561 const char *value = res_array[k].value; 4562 if (res_array[k].option[0] == '-') { 4563 code = -1; 4564 } else if (res_array[k].option[0] == '+') { 4565 code = 1; 4566 } else { 4567 code = 0; 4568 } 4569 strcpy(temp, opt_array[j].desc); 4570 if (x_strindex(temp, "inhibit") != 0) 4571 code = -code; 4572 if (code != 0 4573 && res_array[k].value != 0 4574 && !strcmp(name, res_array[k].option + 1)) { 4575 if (((code < 0) && !strcmp(value, "on")) 4576 || ((code > 0) && !strcmp(value, "off")) 4577 || ((code > 0) && !strcmp(value, "0"))) { 4578 mesg = "turn on/off"; 4579 } else { 4580 mesg = "turn off/on"; 4581 } 4582 if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) { 4583 if (strncmp(opt_array[j].desc, "turn ", 5)) { 4584 char *s = CastMallocN(char, 4585 strlen(mesg) 4586 + 1 4587 + strlen(opt_array[j].desc)); 4588 if (s != 0) { 4589 sprintf(s, "%s %s", mesg, opt_array[j].desc); 4590 opt_array[j].desc = s; 4591 } 4592 } else { 4593 TRACE(("OOPS ")); 4594 } 4595 } 4596 TRACE(("%s: %s %s: %s (%s)\n", 4597 mesg, 4598 res_array[k].option, 4599 res_array[k].value, 4600 opt_array[j].opt, 4601 opt_array[j].desc)); 4602 break; 4603 } 4604 } 4605 } 4606 } 4607#endif 4608 } 4609 return opt_array; 4610} 4611 4612/* 4613 * Report the character-type locale that xterm was started in. 4614 */ 4615char * 4616xtermEnvLocale(void) 4617{ 4618 static char *result; 4619 4620 if (result == 0) { 4621 if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) { 4622 result = x_strdup("C"); 4623 } else { 4624 result = x_strdup(result); 4625 } 4626 TRACE(("xtermEnvLocale ->%s\n", result)); 4627 } 4628 return result; 4629} 4630 4631char * 4632xtermEnvEncoding(void) 4633{ 4634 static char *result; 4635 4636 if (result == 0) { 4637#ifdef HAVE_LANGINFO_CODESET 4638 result = nl_langinfo(CODESET); 4639#else 4640 char *locale = xtermEnvLocale(); 4641 if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { 4642 result = "ASCII"; 4643 } else { 4644 result = "ISO-8859-1"; 4645 } 4646#endif 4647 TRACE(("xtermEnvEncoding ->%s\n", result)); 4648 } 4649 return result; 4650} 4651 4652#if OPT_WIDE_CHARS 4653/* 4654 * Tell whether xterm was started in a locale that uses UTF-8 encoding for 4655 * characters. That environment is inherited by subprocesses and used in 4656 * various library calls. 4657 */ 4658Bool 4659xtermEnvUTF8(void) 4660{ 4661 static Bool init = False; 4662 static Bool result = False; 4663 4664 if (!init) { 4665 init = True; 4666#ifdef HAVE_LANGINFO_CODESET 4667 result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); 4668#else 4669 result = (strstr(xtermEnvLocale(), "UTF-8") != NULL); 4670#endif 4671 TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); 4672 } 4673 return result; 4674} 4675#endif /* OPT_WIDE_CHARS */ 4676 4677/* 4678 * Returns the version-string used in the "-v' message as well as a few other 4679 * places. It is derived (when possible) from the __vendorversion__ symbol 4680 * that some newer imake configurations define. 4681 */ 4682char * 4683xtermVersion(void) 4684{ 4685 static char vendor_version[] = __vendorversion__; 4686 static char *result; 4687 4688 if (result == 0) { 4689 char *vendor = vendor_version; 4690 char first[BUFSIZ]; 4691 char second[BUFSIZ]; 4692 4693 result = CastMallocN(char, strlen(vendor) + 9); 4694 if (result == 0) 4695 result = vendor; 4696 else { 4697 /* some vendors leave trash in this string */ 4698 for (;;) { 4699 if (!strncmp(vendor, "Version ", (size_t) 8)) 4700 vendor += 8; 4701 else if (isspace(CharOf(*vendor))) 4702 ++vendor; 4703 else 4704 break; 4705 } 4706 if (strlen(vendor) < BUFSIZ && 4707 sscanf(vendor, "%[0-9.] %[A-Za-z_0-9.]", first, second) == 2) 4708 sprintf(result, "%s %s(%d)", second, first, XTERM_PATCH); 4709 else 4710 sprintf(result, "%s(%d)", vendor, XTERM_PATCH); 4711 } 4712 } 4713 return result; 4714} 4715 4716/* 4717 * Check if the current widget, or any parent, is the VT100 "xterm" widget. 4718 */ 4719XtermWidget 4720getXtermWidget(Widget w) 4721{ 4722 XtermWidget xw; 4723 4724 if (w == 0) { 4725 xw = (XtermWidget) CURRENT_EMU(); 4726 if (!IsXtermWidget(xw)) { 4727 xw = 0; 4728 } 4729 } else if (IsXtermWidget(w)) { 4730 xw = (XtermWidget) w; 4731 } else { 4732 xw = getXtermWidget(XtParent(w)); 4733 } 4734 TRACE2(("getXtermWidget %p -> %p\n", w, xw)); 4735 return xw; 4736} 4737