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