main.c revision 04b94745
1/* $XTermId: main.c,v 1.921 2024/05/22 20:11:43 tom Exp $ */ 2 3/* 4 * Copyright 2002-2023,2024 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 * Copyright 1987, 1988 X Consortium 33 * 34 * Permission to use, copy, modify, distribute, and sell this software and its 35 * documentation for any purpose is hereby granted without fee, provided that 36 * the above copyright notice appear in all copies and that both that 37 * copyright notice and this permission notice appear in supporting 38 * documentation. 39 * 40 * The above copyright notice and this permission notice shall be included in 41 * all copies or substantial portions of the Software. 42 * 43 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 47 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 * 50 * Except as contained in this notice, the name of the X Consortium shall not be 51 * used in advertising or otherwise to promote the sale, use or other dealings 52 * in this Software without prior written authorization from the X Consortium. 53 * 54 * Copyright 1987, 1988 by Digital Equipment Corporation, Maynard. 55 * 56 * All Rights Reserved 57 * 58 * Permission to use, copy, modify, and distribute this software and its 59 * documentation for any purpose and without fee is hereby granted, 60 * provided that the above copyright notice appear in all copies and that 61 * both that copyright notice and this permission notice appear in 62 * supporting documentation, and that the name of Digital not be used in 63 * advertising or publicity pertaining to distribution of the software 64 * without specific, written prior permission. 65 * 66 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 67 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 68 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 69 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 70 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 71 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 72 * SOFTWARE. 73 */ 74 75/* 76 * W A R N I N G 77 * 78 * If you think you know what all of this code is doing, you are 79 * probably very mistaken. There be serious and nasty dragons here. 80 * 81 * This client is *not* to be taken as an example of how to write X 82 * Toolkit applications. It is in need of a substantial rewrite, 83 * ideally to create a generic tty widget with several different parsing 84 * widgets so that you can plug 'em together any way you want. Don't 85 * hold your breath, though.... 86 */ 87 88/* main.c */ 89 90#define RES_OFFSET(field) XtOffsetOf(XTERM_RESOURCE, field) 91 92#include <xterm.h> 93#include <version.h> 94#include <graphics.h> 95 96/* xterm uses these X Toolkit resource names, which are exported in array */ 97#undef XtNborderWidth 98#undef XtNiconName 99#undef XtNgeometry 100#undef XtNreverseVideo 101#undef XtNtitle 102 103#define XtNborderWidth "borderWidth" 104#define XtNgeometry "geometry" 105#define XtNiconName "iconName" 106#define XtNreverseVideo "reverseVideo" 107#define XtNtitle "title" 108 109#if OPT_TOOLBAR 110 111#if defined(HAVE_LIB_XAW) 112#include <X11/Xaw/Form.h> 113#elif defined(HAVE_LIB_XAW3D) 114#include <X11/Xaw3d/Form.h> 115#elif defined(HAVE_LIB_XAW3DXFT) 116#include <X11/Xaw3dxft/Form.h> 117#include <X11/Xaw3dxft/Xaw3dXft.h> 118#elif defined(HAVE_LIB_NEXTAW) 119#include <X11/neXtaw/Form.h> 120#elif defined(HAVE_LIB_XAWPLUS) 121#include <X11/XawPlus/Form.h> 122#endif 123 124#else 125 126#if defined(HAVE_LIB_XAW3DXFT) 127#include <X11/Xaw3dxft/Xaw3dXft.h> 128#endif 129 130#endif /* OPT_TOOLBAR */ 131 132#include <pwd.h> 133#include <ctype.h> 134 135#include <data.h> 136#include <error.h> 137#include <menu.h> 138#include <main.h> 139#include <xstrings.h> 140#include <xtermcap.h> 141#include <xterm_io.h> 142 143#if OPT_WIDE_CHARS 144#include <charclass.h> 145#endif 146 147#ifdef __osf__ 148#define USE_SYSV_SIGNALS 149#define WTMP 150#include <pty.h> /* openpty() */ 151#endif 152 153#ifdef __sgi 154#include <grp.h> /* initgroups() */ 155#endif 156 157static GCC_NORETURN void hungtty(int); 158static GCC_NORETURN void Syntax(char *); 159static GCC_NORETURN void HsSysError(int); 160 161#if defined(__SCO__) || defined(SVR4) || defined(_POSIX_VERSION) || ( defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 1) ) 162#define USE_POSIX_SIGNALS 163#endif 164 165#if defined(SYSV) && !defined(SVR4) && !defined(ISC22) && !defined(ISC30) 166/* older SYSV systems cannot ignore SIGHUP. 167 Shell hangs, or you get extra shells, or something like that */ 168#define USE_SYSV_SIGHUP 169#endif 170 171#if defined(sony) && defined(bsd43) && !defined(KANJI) 172#define KANJI 173#endif 174 175#ifdef __linux__ 176#define USE_SYSV_PGRP 177#define USE_SYSV_SIGNALS 178#define WTMP 179#ifdef HAVE_PTY_H 180#include <pty.h> 181#endif 182#endif 183 184#ifdef __MVS__ 185#define USE_SYSV_PGRP 186#define USE_SYSV_SIGNALS 187#endif 188 189#ifdef __CYGWIN__ 190#define WTMP 191#endif 192 193#ifdef __SCO__ 194#ifndef _SVID3 195#define _SVID3 196#endif 197#endif 198 199#if defined(__GLIBC__) && !defined(__linux__) 200#define USE_SYSV_PGRP 201#define WTMP 202#endif 203 204#if defined(USE_TTY_GROUP) || defined(USE_UTMP_SETGID) || defined(HAVE_INITGROUPS) 205#include <grp.h> 206#endif 207 208#ifndef TTY_GROUP_NAME 209#define TTY_GROUP_NAME "tty" 210#endif 211 212#include <sys/stat.h> 213 214#ifdef Lynx 215#ifndef BSDLY 216#define BSDLY 0 217#endif 218#ifndef VTDLY 219#define VTDLY 0 220#endif 221#ifndef FFDLY 222#define FFDLY 0 223#endif 224#endif 225 226#ifdef SYSV /* { */ 227 228#ifdef USE_USG_PTYS /* AT&T SYSV has no ptyio.h */ 229#include <sys/stropts.h> /* for I_PUSH */ 230#include <poll.h> /* for POLLIN */ 231#endif /* USE_USG_PTYS */ 232 233#define USE_SYSV_SIGNALS 234#define USE_SYSV_PGRP 235 236#if !defined(TIOCSWINSZ) || defined(__SCO__) || defined(__UNIXWARE__) 237#define USE_SYSV_ENVVARS /* COLUMNS/LINES vs. TERMCAP */ 238#endif 239 240/* 241 * now get system-specific includes 242 */ 243#ifdef macII 244#include <sys/ttychars.h> 245#undef USE_SYSV_ENVVARS 246#undef FIOCLEX 247#undef FIONCLEX 248#define setpgrp2 setpgrp 249#include <sgtty.h> 250#include <sys/resource.h> 251#endif 252 253#ifdef __hpux 254#include <sys/ptyio.h> 255#endif /* __hpux */ 256 257#ifdef __osf__ 258#undef USE_SYSV_PGRP 259#define setpgrp setpgid 260#endif 261 262#ifdef __sgi 263#include <sys/sysmacros.h> 264#endif /* __sgi */ 265 266#ifdef sun 267#include <sys/strredir.h> 268#endif 269 270#else /* } !SYSV { */ /* BSD systems */ 271 272#ifdef __QNX__ 273 274#ifndef __QNXNTO__ 275#define ttyslot() 1 276#else 277#define USE_SYSV_PGRP 278extern __inline__ 279int 280ttyslot(void) 281{ 282 return 1; /* yuk */ 283} 284#endif 285 286#else 287 288#if defined(__INTERIX) || defined(__APPLE__) 289#define setpgrp setpgid 290#endif 291 292#ifndef __linux__ 293#ifndef VMS 294#ifndef USE_POSIX_TERMIOS 295#ifndef USE_ANY_SYSV_TERMIO 296#include <sgtty.h> 297#endif 298#endif /* USE_POSIX_TERMIOS */ 299#ifdef Lynx 300#include <resource.h> 301#else 302#include <sys/resource.h> 303#endif 304#endif /* !VMS */ 305#endif /* !__linux__ */ 306 307#endif /* __QNX__ */ 308 309#endif /* } !SYSV */ 310 311/* Xpoll.h and <sys/param.h> on glibc 2.1 systems have colliding NBBY's */ 312#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))) 313#ifndef NOFILE 314#define NOFILE OPEN_MAX 315#endif 316#elif !(defined(VMS) || defined(WIN32) || defined(Lynx) || defined(__GNU__) || defined(__MVS__)) 317#include <sys/param.h> /* for NOFILE */ 318#endif 319 320#if defined(BSD) && (BSD >= 199103) 321#define WTMP 322#endif 323 324#include <stdio.h> 325#include <math.h> 326 327#ifdef __hpux 328#include <sys/utsname.h> 329#endif /* __hpux */ 330 331#if defined(apollo) && (OSMAJORVERSION == 10) && (OSMINORVERSION < 4) 332#define ttyslot() 1 333#endif /* apollo */ 334 335#if defined(UTMPX_FOR_UTMP) 336#define UTMP_STR utmpx 337#else 338#define UTMP_STR utmp 339#endif 340 341#if defined(USE_UTEMPTER) 342#include <utempter.h> 343#if 1 344#define UTEMPTER_ADD(pty,hostname,master_fd) utempter_add_record(master_fd, hostname) 345#define UTEMPTER_DEL() utempter_remove_added_record () 346#else 347#define UTEMPTER_ADD(pty,hostname,master_fd) addToUtmp(pty, hostname, master_fd) 348#define UTEMPTER_DEL() removeFromUtmp() 349#endif 350#endif 351 352#if defined(I_FIND) && defined(I_PUSH) 353#define PUSH_FAILS(fd,name) ioctl(fd, I_FIND, name) == 0 \ 354 && ioctl(fd, I_PUSH, name) < 0 355#else 356#define PUSH_FAILS(fd,name) ioctl(fd, I_PUSH, name) < 0 357#endif 358 359#if defined(UTMPX_FOR_UTMP) 360 361#include <utmpx.h> 362 363#define call_endutent endutxent 364#define call_getutid getutxid 365#define call_pututline pututxline 366#define call_setutent setutxent 367#define call_updwtmp updwtmpx 368 369#elif defined(HAVE_UTMP) 370 371#include <utmp.h> 372 373#if defined(_CRAY) && (OSMAJORVERSION < 8) 374extern struct utmp *getutid __((struct utmp * _Id)); 375#endif 376 377#define call_endutent endutent 378#define call_getutid getutid 379#define call_pututline pututline 380#define call_setutent setutent 381#define call_updwtmp updwtmp 382 383#endif 384 385#if defined(USE_LASTLOG) && defined(HAVE_LASTLOG_H) 386#include <lastlog.h> /* caution: glibc includes utmp.h here */ 387#endif 388 389#ifndef USE_LASTLOGX 390#if defined(_NETBSD_SOURCE) && defined(_PATH_LASTLOGX) 391#define USE_LASTLOGX 1 392#endif 393#endif 394 395#ifdef PUCC_PTYD 396#include <local/openpty.h> 397#endif /* PUCC_PTYD */ 398 399#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 400#include <util.h> /* openpty() */ 401#endif 402 403#if defined(__FreeBSD__) || defined(__DragonFly__) 404#include <libutil.h> /* openpty() */ 405#endif 406 407#if !defined(UTMP_FILENAME) 408#if defined(UTMP_FILE) 409#define UTMP_FILENAME UTMP_FILE 410#elif defined(_PATH_UTMP) 411#define UTMP_FILENAME _PATH_UTMP 412#else 413#define UTMP_FILENAME "/etc/utmp" 414#endif 415#endif 416 417#ifndef LASTLOG_FILENAME 418#ifdef _PATH_LASTLOG 419#define LASTLOG_FILENAME _PATH_LASTLOG 420#else 421#define LASTLOG_FILENAME "/usr/adm/lastlog" /* only on BSD systems */ 422#endif 423#endif 424 425#if !defined(WTMP_FILENAME) 426#if defined(WTMP_FILE) 427#define WTMP_FILENAME WTMP_FILE 428#elif defined(_PATH_WTMP) 429#define WTMP_FILENAME _PATH_WTMP 430#elif defined(SYSV) 431#define WTMP_FILENAME "/etc/wtmp" 432#else 433#define WTMP_FILENAME "/usr/adm/wtmp" 434#endif 435#endif 436 437#include <signal.h> 438 439#if defined(__SCO__) || (defined(ISC) && !defined(_POSIX_VERSION)) 440#undef SIGTSTP /* defined, but not the BSD way */ 441#endif 442 443#ifdef SIGTSTP 444#include <sys/wait.h> 445#endif 446 447#if defined(__SCO__) || defined(__UNIXWARE__) 448#undef ECHOKE 449#undef ECHOCTL 450#endif 451 452#if defined(HAVE_SYS_TTYDEFAULTS_H) && !defined(CEOF) 453#include <sys/ttydefaults.h> 454#endif 455 456#ifdef X_NOT_POSIX 457extern long lseek(); 458#if defined(USG) || defined(SVR4) 459extern unsigned sleep(); 460#else 461extern void sleep(); 462#endif 463extern char *ttyname(); 464#endif 465 466#if defined(SYSV) && defined(DECL_PTSNAME) 467extern char *ptsname(int); 468#endif 469 470#ifndef VMS 471static void reapchild(int /* n */ ); 472static int spawnXTerm(XtermWidget /* xw */ 473 ,unsigned /* line_speed */ ); 474static void remove_termcap_entry(char *, const char *); 475#ifdef USE_PTY_SEARCH 476static int pty_search(int * /* pty */ ); 477#endif 478#endif /* ! VMS */ 479 480static int get_pty(int *pty, char *from); 481static void resize_termcap(XtermWidget xw); 482static void set_owner(char *device, unsigned uid, unsigned gid, unsigned mode); 483 484static Bool added_utmp_entry = False; 485 486#ifdef HAVE_POSIX_SAVED_IDS 487static uid_t save_euid; 488static gid_t save_egid; 489#endif 490 491static uid_t save_ruid; 492static gid_t save_rgid; 493 494#if defined(USE_UTMP_SETGID) 495static int really_get_pty(int *pty, char *from); 496#endif 497 498#if defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 499static Bool xterm_exiting = False; 500#endif 501 502static char *explicit_shname = NULL; 503 504/* 505** Ordinarily it should be okay to omit the assignment in the following 506** statement. Apparently the c89 compiler on AIX 4.1.3 has a bug, or does 507** it? Without the assignment though the compiler will init command_to_exec 508** to 0xffffffff instead of NULL; and subsequent usage, e.g. in spawnXTerm() to 509** SEGV. 510*/ 511static char **command_to_exec = NULL; 512 513#if OPT_LUIT_PROG 514static char **command_to_exec_with_luit = NULL; 515static unsigned command_length_with_luit = 0; 516#endif 517 518/* choose a nice default value for speed - if we make it too low, users who 519 * mistakenly use $TERM set to vt100 will get padding delays. Setting it to a 520 * higher value is not useful since legacy applications (termcap) that care 521 * about padding generally store the code in a short, which does not have 522 * enough bits for the extended values. 523 */ 524#ifdef B38400 /* everyone should define this */ 525#define VAL_LINE_SPEED B38400 526#else /* ...but xterm's used this for a long time */ 527#define VAL_LINE_SPEED B9600 528#endif 529 530/* 531 * Allow use of system default characters if defined and reasonable. 532 * These are based on the BSD ttydefaults.h 533 */ 534#ifndef CBRK 535#define CBRK 0xff /* was 0 */ 536#endif 537#ifndef CDISCARD 538#define CDISCARD CONTROL('O') 539#endif 540#ifndef CDSUSP 541#define CDSUSP CONTROL('Y') 542#endif 543#ifndef CEOF 544#define CEOF CONTROL('D') 545#endif 546#ifndef CEOL 547#define CEOL 0xff /* was 0 */ 548#endif 549#ifndef CERASE 550#define CERASE 0177 551#endif 552#ifndef CERASE2 553#define CERASE2 CONTROL('H') 554#endif 555#ifndef CFLUSH 556#define CFLUSH CONTROL('O') 557#endif 558#ifndef CINTR 559#define CINTR CONTROL('C') 560#endif 561#ifndef CKILL 562#define CKILL CONTROL('U') /* was '@' */ 563#endif 564#ifndef CLNEXT 565#define CLNEXT CONTROL('V') 566#endif 567#ifndef CNUL 568#define CNUL 0 569#endif 570#ifndef CQUIT 571#define CQUIT CONTROL('\\') 572#endif 573#ifndef CRPRNT 574#define CRPRNT CONTROL('R') 575#endif 576#ifndef CREPRINT 577#define CREPRINT CRPRNT 578#endif 579#ifndef CSTART 580#define CSTART CONTROL('Q') 581#endif 582#ifndef CSTATUS 583#define CSTATUS CONTROL('T') 584#endif 585#ifndef CSTOP 586#define CSTOP CONTROL('S') 587#endif 588#ifndef CSUSP 589#define CSUSP CONTROL('Z') 590#endif 591#ifndef CSWTCH 592#define CSWTCH 0 593#endif 594#ifndef CWERASE 595#define CWERASE CONTROL('W') 596#endif 597 598#ifndef VMS 599#ifdef TERMIO_STRUCT 600/* The following structures are initialized in main() in order 601** to eliminate any assumptions about the internal order of their 602** contents. 603*/ 604static TERMIO_STRUCT d_tio; 605 606#ifndef ONLCR 607#define ONLCR 0 608#endif 609 610#ifndef OPOST 611#define OPOST 0 612#endif 613 614#define D_TIO_FLAGS (OPOST | ONLCR) 615 616#ifdef HAS_LTCHARS 617static struct ltchars d_ltc; 618#endif /* HAS_LTCHARS */ 619 620#ifdef TIOCLSET 621static unsigned int d_lmode; 622#endif /* TIOCLSET */ 623 624#else /* !TERMIO_STRUCT */ 625 626#define D_SG_FLAGS (EVENP | ODDP | ECHO | CRMOD) 627 628static struct sgttyb d_sg = 629{ 630 0, 0, 0177, CKILL, (D_SG_FLAGS | XTABS) 631}; 632static struct tchars d_tc = 633{ 634 CINTR, CQUIT, CSTART, 635 CSTOP, CEOF, CBRK 636}; 637static struct ltchars d_ltc = 638{ 639 CSUSP, CDSUSP, CRPRNT, 640 CFLUSH, CWERASE, CLNEXT 641}; 642static int d_disipline = NTTYDISC; 643static long int d_lmode = LCRTBS | LCRTERA | LCRTKIL | LCTLECH; 644#ifdef sony 645static long int d_jmode = KM_SYSSJIS | KM_ASCII; 646static struct jtchars d_jtc = 647{ 648 'J', 'B' 649}; 650#endif /* sony */ 651#endif /* TERMIO_STRUCT */ 652#endif /* ! VMS */ 653 654/* 655 * SYSV has the termio.c_cc[V] and ltchars; BSD has tchars and ltchars; 656 * SVR4 has only termio.c_cc, but it includes everything from ltchars. 657 * POSIX termios has termios.c_cc, which is similar to SVR4. 658 */ 659#define TTYMODE(name) { name, sizeof(name)-1, 0, 0 } 660static Boolean override_tty_modes = False; 661/* *INDENT-OFF* */ 662static struct { 663 const char *name; 664 size_t len; 665 int set; 666 int value; 667} ttyModes[] = { 668 TTYMODE("intr"), /* tchars.t_intrc ; VINTR */ 669#define XTTYMODE_intr 0 670 TTYMODE("quit"), /* tchars.t_quitc ; VQUIT */ 671#define XTTYMODE_quit 1 672 TTYMODE("erase"), /* sgttyb.sg_erase ; VERASE */ 673#define XTTYMODE_erase 2 674 TTYMODE("kill"), /* sgttyb.sg_kill ; VKILL */ 675#define XTTYMODE_kill 3 676 TTYMODE("eof"), /* tchars.t_eofc ; VEOF */ 677#define XTTYMODE_eof 4 678 TTYMODE("eol"), /* VEOL */ 679#define XTTYMODE_eol 5 680 TTYMODE("swtch"), /* VSWTCH */ 681#define XTTYMODE_swtch 6 682 TTYMODE("start"), /* tchars.t_startc ; VSTART */ 683#define XTTYMODE_start 7 684 TTYMODE("stop"), /* tchars.t_stopc ; VSTOP */ 685#define XTTYMODE_stop 8 686 TTYMODE("brk"), /* tchars.t_brkc */ 687#define XTTYMODE_brk 9 688 TTYMODE("susp"), /* ltchars.t_suspc ; VSUSP */ 689#define XTTYMODE_susp 10 690 TTYMODE("dsusp"), /* ltchars.t_dsuspc ; VDSUSP */ 691#define XTTYMODE_dsusp 11 692 TTYMODE("rprnt"), /* ltchars.t_rprntc ; VREPRINT */ 693#define XTTYMODE_rprnt 12 694 TTYMODE("flush"), /* ltchars.t_flushc ; VDISCARD */ 695#define XTTYMODE_flush 13 696 TTYMODE("weras"), /* ltchars.t_werasc ; VWERASE */ 697#define XTTYMODE_weras 14 698 TTYMODE("lnext"), /* ltchars.t_lnextc ; VLNEXT */ 699#define XTTYMODE_lnext 15 700 TTYMODE("status"), /* VSTATUS */ 701#define XTTYMODE_status 16 702 TTYMODE("erase2"), /* VERASE2 */ 703#define XTTYMODE_erase2 17 704 TTYMODE("eol2"), /* VEOL2 */ 705#define XTTYMODE_eol2 18 706 TTYMODE("tabs"), /* TAB0 */ 707#define XTTYMODE_tabs 19 708 TTYMODE("-tabs"), /* TAB3 */ 709#define XTTYMODE__tabs 20 710}; 711 712#ifndef TAB0 713#define TAB0 0 714#endif 715 716#ifndef TAB3 717#if defined(OXTABS) 718#define TAB3 OXTABS 719#elif defined(XTABS) 720#define TAB3 XTABS 721#endif 722#endif 723 724#ifndef TABDLY 725#define TABDLY (TAB0|TAB3) 726#endif 727 728#define isTtyMode(p,q) (ttyChars[p].myMode == q && ttyModes[q].set) 729 730#define isTabMode(n) \ 731 (isTtyMode(n, XTTYMODE_tabs) || \ 732 isTtyMode(n, XTTYMODE__tabs)) 733 734#define TMODE(ind,var) \ 735 if (ttyModes[ind].set) \ 736 var = (cc_t) ttyModes[ind].value 737 738#define validTtyChar(data, n) \ 739 (ttyChars[n].sysMode >= 0 && \ 740 ttyChars[n].sysMode < (int) XtNumber(data.c_cc)) 741 742static const struct { 743 int sysMode; 744 int myMode; 745 int myDefault; 746} ttyChars[] = { 747#ifdef VINTR 748 { VINTR, XTTYMODE_intr, CINTR }, 749#endif 750#ifdef VQUIT 751 { VQUIT, XTTYMODE_quit, CQUIT }, 752#endif 753#ifdef VERASE 754 { VERASE, XTTYMODE_erase, CERASE }, 755#endif 756#ifdef VKILL 757 { VKILL, XTTYMODE_kill, CKILL }, 758#endif 759#ifdef VEOF 760 { VEOF, XTTYMODE_eof, CEOF }, 761#endif 762#ifdef VEOL 763 { VEOL, XTTYMODE_eol, CEOL }, 764#endif 765#ifdef VSWTCH 766 { VSWTCH, XTTYMODE_swtch, CNUL }, 767#endif 768#ifdef VSTART 769 { VSTART, XTTYMODE_start, CSTART }, 770#endif 771#ifdef VSTOP 772 { VSTOP, XTTYMODE_stop, CSTOP }, 773#endif 774#ifdef VSUSP 775 { VSUSP, XTTYMODE_susp, CSUSP }, 776#endif 777#ifdef VDSUSP 778 { VDSUSP, XTTYMODE_dsusp, CDSUSP }, 779#endif 780#ifdef VREPRINT 781 { VREPRINT, XTTYMODE_rprnt, CREPRINT }, 782#endif 783#ifdef VDISCARD 784 { VDISCARD, XTTYMODE_flush, CDISCARD }, 785#endif 786#ifdef VWERASE 787 { VWERASE, XTTYMODE_weras, CWERASE }, 788#endif 789#ifdef VLNEXT 790 { VLNEXT, XTTYMODE_lnext, CLNEXT }, 791#endif 792#ifdef VSTATUS 793 { VSTATUS, XTTYMODE_status, CSTATUS }, 794#endif 795#ifdef VERASE2 796 { VERASE2, XTTYMODE_erase2, CERASE2 }, 797#endif 798#ifdef VEOL2 799 { VEOL2, XTTYMODE_eol2, CNUL }, 800#endif 801 { -1, XTTYMODE_tabs, TAB0 }, 802 { -1, XTTYMODE__tabs, TAB3 }, 803}; 804/* *INDENT-ON* */ 805 806static int parse_tty_modes(char *s); 807 808#ifndef USE_UTEMPTER 809#ifdef USE_SYSV_UTMP 810#if (defined(AIXV3) && (OSMAJORVERSION < 4)) && !(defined(getutid)) 811extern struct utmp *getutid(); 812#endif /* AIXV3 */ 813 814#else /* not USE_SYSV_UTMP */ 815static char etc_utmp[] = UTMP_FILENAME; 816#endif /* USE_SYSV_UTMP */ 817 818#if defined(USE_LASTLOG) && defined(USE_STRUCT_LASTLOG) 819static char etc_lastlog[] = LASTLOG_FILENAME; 820#else 821#undef USE_LASTLOG 822#endif 823 824#ifdef WTMP 825static char etc_wtmp[] = WTMP_FILENAME; 826#endif 827#endif /* !USE_UTEMPTER */ 828 829/* 830 * Some people with 4.3bsd /bin/login seem to like to use login -p -f user 831 * to implement xterm -ls. They can turn on USE_LOGIN_DASH_P and turn off 832 * WTMP and USE_LASTLOG. 833 */ 834#ifdef USE_LOGIN_DASH_P 835#ifndef LOGIN_FILENAME 836#define LOGIN_FILENAME "/bin/login" 837#endif 838static char bin_login[] = LOGIN_FILENAME; 839#endif 840 841static char noPassedPty[2]; 842static char *passedPty = noPassedPty; /* name if pty if slave */ 843 844#if defined(TIOCCONS) || defined(SRIOCSREDIR) 845static int Console; 846#include <X11/Xmu/SysUtil.h> /* XmuGetHostname */ 847#define MIT_CONSOLE_LEN 12 848#define MIT_CONSOLE "MIT_CONSOLE_" 849static char mit_console_name[255 + MIT_CONSOLE_LEN + 1] = MIT_CONSOLE; 850static Atom mit_console; 851#endif /* TIOCCONS */ 852 853#ifndef USE_SYSV_UTMP 854static int tslot; 855#endif /* USE_SYSV_UTMP */ 856static sigjmp_buf env; 857 858#define SetUtmpHost(dst, screen) \ 859 { \ 860 char host[sizeof(dst) + 1]; \ 861 strncpy(host, DisplayString(screen->display), sizeof(host) - 1); \ 862 host[sizeof(dst)] = '\0'; \ 863 TRACE(("DisplayString(%s)\n", host)); \ 864 if (!resource.utmpDisplayId) { \ 865 char *endptr = strrchr(host, ':'); \ 866 if (endptr) { \ 867 TRACE(("trimming display-id '%s'\n", host)); \ 868 *endptr = '\0'; \ 869 } \ 870 } \ 871 copy_filled(dst, host, sizeof(dst)); \ 872 } 873 874#ifdef HAVE_UTMP_UT_SYSLEN 875# define SetUtmpSysLen(utmp) \ 876 { \ 877 utmp.ut_host[sizeof(utmp.ut_host)-1] = '\0'; \ 878 utmp.ut_syslen = (short) ((int) strlen(utmp.ut_host) + 1); \ 879 } 880#endif 881 882/* used by VT (charproc.c) */ 883 884static XtResource application_resources[] = 885{ 886 Sres(XtNiconGeometry, XtCIconGeometry, icon_geometry, NULL), 887 Sres(XtNtitle, XtCTitle, title, NULL), 888 Sres(XtNiconHint, XtCIconHint, icon_hint, NULL), 889 Sres(XtNiconName, XtCIconName, icon_name, NULL), 890 Sres(XtNtermName, XtCTermName, term_name, NULL), 891 Sres(XtNttyModes, XtCTtyModes, tty_modes, NULL), 892 Sres(XtNvalidShells, XtCValidShells, valid_shells, NULL), 893 Bres(XtNhold, XtCHold, hold_screen, False), 894 Bres(XtNutmpInhibit, XtCUtmpInhibit, utmpInhibit, False), 895 Bres(XtNutmpDisplayId, XtCUtmpDisplayId, utmpDisplayId, True), 896 Bres(XtNmessages, XtCMessages, messages, True), 897 Ires(XtNminBufSize, XtCMinBufSize, minBufSize, 4096), 898 Ires(XtNmaxBufSize, XtCMaxBufSize, maxBufSize, 32768), 899 Sres(XtNmenuLocale, XtCMenuLocale, menuLocale, DEF_MENU_LOCALE), 900 Sres(XtNomitTranslation, XtCOmitTranslation, omitTranslation, NULL), 901 Sres(XtNkeyboardType, XtCKeyboardType, keyboardType, "unknown"), 902#ifdef HAVE_LIB_XCURSOR 903 Sres(XtNcursorTheme, XtCCursorTheme, cursorTheme, "none"), 904#endif 905#if OPT_PRINT_ON_EXIT 906 Ires(XtNprintModeImmediate, XtCPrintModeImmediate, printModeNow, 0), 907 Ires(XtNprintOptsImmediate, XtCPrintOptsImmediate, printOptsNow, 9), 908 Sres(XtNprintFileImmediate, XtCPrintFileImmediate, printFileNow, NULL), 909 Ires(XtNprintModeOnXError, XtCPrintModeOnXError, printModeOnXError, 0), 910 Ires(XtNprintOptsOnXError, XtCPrintOptsOnXError, printOptsOnXError, 9), 911 Sres(XtNprintFileOnXError, XtCPrintFileOnXError, printFileOnXError, NULL), 912#endif 913#if OPT_SUNPC_KBD 914 Bres(XtNsunKeyboard, XtCSunKeyboard, sunKeyboard, False), 915#endif 916#if OPT_HP_FUNC_KEYS 917 Bres(XtNhpFunctionKeys, XtCHpFunctionKeys, hpFunctionKeys, False), 918#endif 919#if OPT_SCO_FUNC_KEYS 920 Bres(XtNscoFunctionKeys, XtCScoFunctionKeys, scoFunctionKeys, False), 921#endif 922#if OPT_SUN_FUNC_KEYS 923 Bres(XtNsunFunctionKeys, XtCSunFunctionKeys, sunFunctionKeys, False), 924#endif 925#if OPT_TCAP_FKEYS 926 Bres(XtNtcapFunctionKeys, XtCTcapFunctionKeys, termcapKeys, False), 927#endif 928#if OPT_INITIAL_ERASE 929 Bres(XtNptyInitialErase, XtCPtyInitialErase, ptyInitialErase, DEF_INITIAL_ERASE), 930 Bres(XtNbackarrowKeyIsErase, XtCBackarrowKeyIsErase, backarrow_is_erase, DEF_BACKARO_ERASE), 931#endif 932 Bres(XtNuseInsertMode, XtCUseInsertMode, useInsertMode, False), 933#if OPT_ZICONBEEP 934 Ires(XtNzIconBeep, XtCZIconBeep, zIconBeep, 0), 935 Sres(XtNzIconTitleFormat, XtCZIconTitleFormat, zIconFormat, "*** %s"), 936#endif 937#if OPT_PTY_HANDSHAKE 938 Bres(XtNwaitForMap, XtCWaitForMap, wait_for_map, False), 939 Bres(XtNptyHandshake, XtCPtyHandshake, ptyHandshake, True), 940 Bres(XtNptySttySize, XtCPtySttySize, ptySttySize, DEF_PTY_STTY_SIZE), 941#endif 942#if OPT_REPORT_CCLASS 943 Bres(XtNreportCClass, XtCReportCClass, reportCClass, False), 944#endif 945#if OPT_REPORT_COLORS 946 Bres(XtNreportColors, XtCReportColors, reportColors, False), 947#endif 948#if OPT_REPORT_FONTS 949 Bres(XtNreportFonts, XtCReportFonts, reportFonts, False), 950#endif 951#if OPT_REPORT_ICONS 952 Bres(XtNreportIcons, XtCReportIcons, reportIcons, False), 953#endif 954#if OPT_XRES_QUERY 955 Bres(XtNreportXRes, XtCReportXRes, reportXRes, False), 956#endif 957#if OPT_SAME_NAME 958 Bres(XtNsameName, XtCSameName, sameName, True), 959#endif 960#if OPT_SESSION_MGT 961 Bres(XtNsessionMgt, XtCSessionMgt, sessionMgt, True), 962#endif 963#if OPT_TOOLBAR 964 Bres(XtNtoolBar, XtCToolBar, toolBar, True), 965#endif 966#if OPT_MAXIMIZE 967 Bres(XtNmaximized, XtCMaximized, maximized, False), 968 Sres(XtNfullscreen, XtCFullscreen, fullscreen_s, "off"), 969#endif 970#if USE_DOUBLE_BUFFER 971 Bres(XtNbuffered, XtCBuffered, buffered, DEF_DOUBLE_BUFFER), 972 Ires(XtNbufferedFPS, XtCBufferedFPS, buffered_fps, 40), 973#endif 974}; 975 976static String fallback_resources[] = 977{ 978#if OPT_TOOLBAR 979 "*toolBar: false", 980#endif 981 "*SimpleMenu*menuLabel.vertSpace: 100", 982 "*SimpleMenu*HorizontalMargins: 16", 983 "*SimpleMenu*Sme.height: 16", 984 "*SimpleMenu*Cursor: left_ptr", 985 "*mainMenu.Label: Main Options (no app-defaults)", 986 "*vtMenu.Label: VT Options (no app-defaults)", 987 "*fontMenu.Label: VT Fonts (no app-defaults)", 988#if OPT_TEK4014 989 "*tekMenu.Label: Tek Options (no app-defaults)", 990#endif 991 NULL 992}; 993 994/* Command line options table. Only resources are entered here...there is a 995 pass over the remaining options after XrmParseCommand is let loose. */ 996/* *INDENT-OFF* */ 997#define DATA(option,pattern,type,value) { (char *) option, (char *) pattern, type, (XPointer) value } 998#define OPTS(option,pattern_type,value) { (char *) option, (char *) pattern_type, (XPointer) value } 999#define UP_ARG(name) "."name, XrmoptionSepArg 1000#define MY_ARG(name) "*"name, XrmoptionSepArg 1001#define VT_ARG(name) "*vt100."name, XrmoptionSepArg 1002#define NO_ARG(name) "*"name, XrmoptionNoArg 1003static XrmOptionDescRec optionDescList[] = { 1004OPTS("-geometry", VT_ARG(XtNgeometry), NULL), 1005OPTS("-132", NO_ARG(XtNc132), "on"), 1006OPTS("+132", NO_ARG(XtNc132), "off"), 1007OPTS("-ah", NO_ARG(XtNalwaysHighlight), "on"), 1008OPTS("+ah", NO_ARG(XtNalwaysHighlight), "off"), 1009OPTS("-aw", NO_ARG(XtNautoWrap), "on"), 1010OPTS("+aw", NO_ARG(XtNautoWrap), "off"), 1011#ifndef NO_ACTIVE_ICON 1012OPTS("-ai", NO_ARG(XtNactiveIcon), "off"), 1013OPTS("+ai", NO_ARG(XtNactiveIcon), "on"), 1014#endif /* NO_ACTIVE_ICON */ 1015OPTS("-b", MY_ARG(XtNinternalBorder), NULL), 1016OPTS("-barc", NO_ARG(XtNcursorBar), "on"), 1017OPTS("+barc", NO_ARG(XtNcursorBar), "off"), 1018OPTS("-bc", NO_ARG(XtNcursorBlink), "on"), 1019OPTS("+bc", NO_ARG(XtNcursorBlink), "off"), 1020OPTS("-bcf", MY_ARG(XtNcursorOffTime), NULL), 1021OPTS("-bcn", MY_ARG(XtNcursorOnTime), NULL), 1022OPTS("-bdc", NO_ARG(XtNcolorBDMode), "off"), 1023OPTS("+bdc", NO_ARG(XtNcolorBDMode), "on"), 1024OPTS("-cb", NO_ARG(XtNcutToBeginningOfLine), "off"), 1025OPTS("+cb", NO_ARG(XtNcutToBeginningOfLine), "on"), 1026OPTS("-cc", MY_ARG(XtNcharClass), NULL), 1027OPTS("-cm", NO_ARG(XtNcolorMode), "off"), 1028OPTS("+cm", NO_ARG(XtNcolorMode), "on"), 1029OPTS("-cn", NO_ARG(XtNcutNewline), "off"), 1030OPTS("+cn", NO_ARG(XtNcutNewline), "on"), 1031OPTS("-cr", MY_ARG(XtNcursorColor), NULL), 1032OPTS("-cu", NO_ARG(XtNcurses), "on"), 1033OPTS("+cu", NO_ARG(XtNcurses), "off"), 1034OPTS("-dc", NO_ARG(XtNdynamicColors), "off"), 1035OPTS("+dc", NO_ARG(XtNdynamicColors), "on"), 1036OPTS("-fb", MY_ARG(XtNboldFont), NULL), 1037OPTS("-fbb", NO_ARG(XtNfreeBoldBox), "off"), 1038OPTS("+fbb", NO_ARG(XtNfreeBoldBox), "on"), 1039OPTS("-fbx", NO_ARG(XtNforceBoxChars), "off"), 1040OPTS("+fbx", NO_ARG(XtNforceBoxChars), "on"), 1041OPTS("-fc", MY_ARG(XtNinitialFont), NULL), 1042#ifndef NO_ACTIVE_ICON 1043OPTS("-fi", MY_ARG(XtNiconFont), NULL), 1044#endif /* NO_ACTIVE_ICON */ 1045#if OPT_RENDERFONT 1046OPTS("-fa", MY_ARG(XtNfaceName), NULL), 1047OPTS("-fd", MY_ARG(XtNfaceNameDoublesize), NULL), 1048OPTS("-fs", MY_ARG(XtNfaceSize), NULL), 1049#endif 1050#if OPT_WIDE_ATTRS && OPT_ISO_COLORS 1051OPTS("-itc", NO_ARG(XtNcolorITMode), "off"), 1052OPTS("+itc", NO_ARG(XtNcolorITMode), "on"), 1053#endif 1054#if OPT_WIDE_CHARS 1055OPTS("-fw", MY_ARG(XtNwideFont), NULL), 1056OPTS("-fwb", MY_ARG(XtNwideBoldFont), NULL), 1057#endif 1058#if OPT_INPUT_METHOD 1059OPTS("-fx", MY_ARG(XtNximFont), NULL), 1060#endif 1061#if OPT_HIGHLIGHT_COLOR 1062OPTS("-hc", MY_ARG(XtNhighlightColor), NULL), 1063OPTS("-hm", NO_ARG(XtNhighlightColorMode), "on"), 1064OPTS("+hm", NO_ARG(XtNhighlightColorMode), "off"), 1065OPTS("-selfg", MY_ARG(XtNhighlightTextColor), NULL), 1066OPTS("-selbg", MY_ARG(XtNhighlightColor), NULL), 1067#endif 1068#if OPT_HP_FUNC_KEYS 1069OPTS("-hf", NO_ARG(XtNhpFunctionKeys), "on"), 1070OPTS("+hf", NO_ARG(XtNhpFunctionKeys), "off"), 1071#endif 1072OPTS("-hold", NO_ARG(XtNhold), "on"), 1073OPTS("+hold", NO_ARG(XtNhold), "off"), 1074#if OPT_INITIAL_ERASE 1075OPTS("-ie", NO_ARG(XtNptyInitialErase), "on"), 1076OPTS("+ie", NO_ARG(XtNptyInitialErase), "off"), 1077#endif 1078OPTS("-j", NO_ARG(XtNjumpScroll), "on"), 1079OPTS("+j", NO_ARG(XtNjumpScroll), "off"), 1080OPTS("-jf", NO_ARG(XtNfastScroll), "on"), 1081OPTS("+jf", NO_ARG(XtNfastScroll), "off"), 1082#if OPT_C1_PRINT 1083OPTS("-k8", NO_ARG(XtNallowC1Printable), "on"), 1084OPTS("+k8", NO_ARG(XtNallowC1Printable), "off"), 1085#endif 1086OPTS("-kt", MY_ARG(XtNkeyboardType), NULL), 1087/* parse logging options anyway for compatibility */ 1088OPTS("-l", NO_ARG(XtNlogging), "on"), 1089OPTS("+l", NO_ARG(XtNlogging), "off"), 1090OPTS("-lf", MY_ARG(XtNlogFile), NULL), 1091OPTS("-ls", NO_ARG(XtNloginShell), "on"), 1092OPTS("+ls", NO_ARG(XtNloginShell), "off"), 1093OPTS("-mb", NO_ARG(XtNmarginBell), "on"), 1094OPTS("+mb", NO_ARG(XtNmarginBell), "off"), 1095OPTS("-mc", MY_ARG(XtNmultiClickTime), NULL), 1096OPTS("-mesg", NO_ARG(XtNmessages), "off"), 1097OPTS("+mesg", NO_ARG(XtNmessages), "on"), 1098OPTS("-ms", MY_ARG(XtNpointerColor), NULL), 1099OPTS("-nb", MY_ARG(XtNnMarginBell), NULL), 1100OPTS("-nul", NO_ARG(XtNunderLine), "off"), 1101OPTS("+nul", NO_ARG(XtNunderLine), "on"), 1102OPTS("-pc", NO_ARG(XtNboldColors), "on"), 1103OPTS("+pc", NO_ARG(XtNboldColors), "off"), 1104OPTS("-pf", MY_ARG(XtNpointerFont), NULL), 1105OPTS("-rw", NO_ARG(XtNreverseWrap), "on"), 1106OPTS("+rw", NO_ARG(XtNreverseWrap), "off"), 1107OPTS("-s", NO_ARG(XtNmultiScroll), "on"), 1108OPTS("+s", NO_ARG(XtNmultiScroll), "off"), 1109OPTS("-sb", NO_ARG(XtNscrollBar), "on"), 1110OPTS("+sb", NO_ARG(XtNscrollBar), "off"), 1111#if OPT_REPORT_CCLASS 1112OPTS("-report-charclass", NO_ARG(XtNreportCClass), "on"), 1113#endif 1114#if OPT_REPORT_COLORS 1115OPTS("-report-colors", NO_ARG(XtNreportColors), "on"), 1116#endif 1117#if OPT_REPORT_ICONS 1118OPTS("-report-icons", NO_ARG(XtNreportIcons), "on"), 1119#endif 1120#if OPT_REPORT_FONTS 1121OPTS("-report-fonts", NO_ARG(XtNreportFonts), "on"), 1122#endif 1123#if OPT_XRES_QUERY 1124OPTS("-report-xres", NO_ARG(XtNreportXRes), "on"), 1125#endif 1126#ifdef SCROLLBAR_RIGHT 1127OPTS("-leftbar", NO_ARG(XtNrightScrollBar), "off"), 1128OPTS("-rightbar", NO_ARG(XtNrightScrollBar), "on"), 1129#endif 1130OPTS("-rvc", NO_ARG(XtNcolorRVMode), "off"), 1131OPTS("+rvc", NO_ARG(XtNcolorRVMode), "on"), 1132OPTS("-sf", NO_ARG(XtNsunFunctionKeys), "on"), 1133OPTS("+sf", NO_ARG(XtNsunFunctionKeys), "off"), 1134OPTS("-sh", MY_ARG(XtNscaleHeight), NULL), 1135OPTS("-si", NO_ARG(XtNscrollTtyOutput), "off"), 1136OPTS("+si", NO_ARG(XtNscrollTtyOutput), "on"), 1137OPTS("-sk", NO_ARG(XtNscrollKey), "on"), 1138OPTS("+sk", NO_ARG(XtNscrollKey), "off"), 1139OPTS("-sl", MY_ARG(XtNsaveLines), NULL), 1140#if OPT_SUNPC_KBD 1141OPTS("-sp", NO_ARG(XtNsunKeyboard), "on"), 1142OPTS("+sp", NO_ARG(XtNsunKeyboard), "off"), 1143#endif 1144#if OPT_TEK4014 1145OPTS("-t", NO_ARG(XtNtekStartup), "on"), 1146OPTS("+t", NO_ARG(XtNtekStartup), "off"), 1147#endif 1148OPTS("-ti", MY_ARG(XtNdecTerminalID), NULL), 1149OPTS("-tm", MY_ARG(XtNttyModes), NULL), 1150OPTS("-tn", MY_ARG(XtNtermName), NULL), 1151#if OPT_WIDE_CHARS 1152OPTS("-u8", NO_ARG(XtNutf8), "2"), 1153OPTS("+u8", NO_ARG(XtNutf8), "0"), 1154#endif 1155#if OPT_LUIT_PROG 1156OPTS("-lc", NO_ARG(XtNlocale), "on"), 1157OPTS("+lc", NO_ARG(XtNlocale), "off"), 1158OPTS("-lcc", MY_ARG(XtNlocaleFilter), NULL), 1159OPTS("-en", MY_ARG(XtNlocale), NULL), 1160#endif 1161OPTS("-uc", NO_ARG(XtNcursorUnderLine), "on"), 1162OPTS("+uc", NO_ARG(XtNcursorUnderLine), "off"), 1163OPTS("-ulc", NO_ARG(XtNcolorULMode), "off"), 1164OPTS("+ulc", NO_ARG(XtNcolorULMode), "on"), 1165OPTS("-ulit", NO_ARG(XtNitalicULMode), "off"), 1166OPTS("+ulit", NO_ARG(XtNitalicULMode), "on"), 1167OPTS("-ut", NO_ARG(XtNutmpInhibit), "on"), 1168OPTS("+ut", NO_ARG(XtNutmpInhibit), "off"), 1169OPTS("-im", NO_ARG(XtNuseInsertMode), "on"), 1170OPTS("+im", NO_ARG(XtNuseInsertMode), "off"), 1171OPTS("-vb", NO_ARG(XtNvisualBell), "on"), 1172OPTS("+vb", NO_ARG(XtNvisualBell), "off"), 1173OPTS("-pob", NO_ARG(XtNpopOnBell), "on"), 1174OPTS("+pob", NO_ARG(XtNpopOnBell), "off"), 1175#if OPT_WIDE_CHARS 1176OPTS("-wc", NO_ARG(XtNwideChars), "on"), 1177OPTS("+wc", NO_ARG(XtNwideChars), "off"), 1178OPTS("-mk_width", NO_ARG(XtNmkWidth), "on"), 1179OPTS("+mk_width", NO_ARG(XtNmkWidth), "off"), 1180OPTS("-cjk_width", NO_ARG(XtNcjkWidth), "on"), 1181OPTS("+cjk_width", NO_ARG(XtNcjkWidth), "off"), 1182#endif 1183OPTS("-wf", NO_ARG(XtNwaitForMap), "on"), 1184OPTS("+wf", NO_ARG(XtNwaitForMap), "off"), 1185#if OPT_ZICONBEEP 1186OPTS("-ziconbeep", MY_ARG(XtNzIconBeep), NULL), 1187#endif 1188#if OPT_SAME_NAME 1189OPTS("-samename", NO_ARG(XtNsameName), "on"), 1190OPTS("+samename", NO_ARG(XtNsameName), "off"), 1191#endif 1192#if OPT_SESSION_MGT 1193OPTS("-sm", NO_ARG(XtNsessionMgt), "on"), 1194OPTS("+sm", NO_ARG(XtNsessionMgt), "off"), 1195#endif 1196#if OPT_TOOLBAR 1197OPTS("-tb", NO_ARG(XtNtoolBar), "on"), 1198OPTS("+tb", NO_ARG(XtNtoolBar), "off"), 1199#endif 1200#if OPT_MAXIMIZE 1201OPTS("-maximized", NO_ARG(XtNmaximized), "on"), 1202OPTS("+maximized", NO_ARG(XtNmaximized), "off"), 1203OPTS("-fullscreen", NO_ARG(XtNfullscreen), "on"), 1204OPTS("+fullscreen", NO_ARG(XtNfullscreen), "off"), 1205#endif 1206/* options that we process ourselves */ 1207DATA("-help", NULL, XrmoptionSkipNArgs, NULL), 1208DATA("-version", NULL, XrmoptionSkipNArgs, NULL), 1209DATA("-baudrate", NULL, XrmoptionSkipArg, NULL), 1210DATA("-class", NULL, XrmoptionSkipArg, NULL), 1211DATA("-e", NULL, XrmoptionSkipLine, NULL), 1212DATA("-into", NULL, XrmoptionSkipArg, NULL), 1213/* bogus old compatibility stuff for which there are 1214 standard XtOpenApplication options now */ 1215DATA("%", "*tekGeometry", XrmoptionStickyArg, NULL), 1216DATA("#", ".iconGeometry", XrmoptionStickyArg, NULL), 1217OPTS("-T", UP_ARG(XtNtitle), NULL), 1218OPTS("-n", MY_ARG(XtNiconName), NULL), 1219OPTS("-r", NO_ARG(XtNreverseVideo), "on"), 1220OPTS("+r", NO_ARG(XtNreverseVideo), "off"), 1221OPTS("-rv", NO_ARG(XtNreverseVideo), "on"), 1222OPTS("+rv", NO_ARG(XtNreverseVideo), "off"), 1223OPTS("-w", UP_ARG(XtNborderWidth), NULL), 1224#undef DATA 1225}; 1226 1227static OptionHelp xtermOptions[] = { 1228{ "-version", "print the version number" }, 1229{ "-help", "print out this message" }, 1230{ "-display displayname", "X server to contact" }, 1231{ "-geometry geom", "size (in characters) and position" }, 1232{ "-/+rv", "turn on/off reverse video" }, 1233{ "-bg color", "background color" }, 1234{ "-fg color", "foreground color" }, 1235{ "-bd color", "border color" }, 1236{ "-bw number", "border width in pixels" }, 1237{ "-fn fontname", "normal text font" }, 1238{ "-fb fontname", "bold text font" }, 1239{ "-fc fontmenu", "start with named fontmenu choice" }, 1240{ "-/+fbb", "turn on/off normal/bold font comparison inhibit"}, 1241{ "-/+fbx", "turn off/on linedrawing characters"}, 1242#if OPT_RENDERFONT 1243{ "-fa pattern", "FreeType font-selection pattern" }, 1244{ "-fd pattern", "FreeType Doublesize font-selection pattern" }, 1245{ "-fs size", "FreeType font-size" }, 1246#endif 1247#if OPT_WIDE_CHARS 1248{ "-fw fontname", "doublewidth text font" }, 1249{ "-fwb fontname", "doublewidth bold text font" }, 1250#endif 1251#if OPT_INPUT_METHOD 1252{ "-fx fontname", "XIM fontset" }, 1253#endif 1254{ "-iconic", "start iconic" }, 1255{ "-name string", "client instance, icon, and title strings" }, 1256{ "-baudrate rate", "set line-speed (default 38400)" }, 1257{ "-class string", "class string (XTerm)" }, 1258{ "-title string", "title string" }, 1259{ "-xrm resourcestring", "additional resource specifications" }, 1260{ "-/+132", "turn on/off 80/132 column switching" }, 1261{ "-/+ah", "turn on/off always highlight" }, 1262#ifndef NO_ACTIVE_ICON 1263{ "-/+ai", "turn off/on active icon" }, 1264{ "-fi fontname", "icon font for active icon" }, 1265#endif /* NO_ACTIVE_ICON */ 1266{ "-b number", "internal border in pixels" }, 1267{ "-/+bc", "turn on/off text cursor blinking" }, 1268{ "-bcf milliseconds", "time text cursor is off when blinking"}, 1269{ "-bcn milliseconds", "time text cursor is on when blinking"}, 1270{ "-/+bdc", "turn off/on display of bold as color"}, 1271{ "-/+cb", "turn on/off cut-to-beginning-of-line inhibit" }, 1272{ "-cc classrange", "specify additional character classes" }, 1273{ "-/+cm", "turn off/on ANSI color mode" }, 1274{ "-/+cn", "turn on/off cut newline inhibit" }, 1275{ "-pf fontname", "cursor font for text area pointer" }, 1276{ "-cr color", "text cursor color" }, 1277{ "-/+cu", "turn on/off curses emulation" }, 1278{ "-/+dc", "turn off/on dynamic color selection" }, 1279#if OPT_HIGHLIGHT_COLOR 1280{ "-/+hm", "turn on/off selection-color override" }, 1281{ "-selbg color", "selection background color" }, 1282{ "-selfg color", "selection foreground color" }, 1283/* -hc is deprecated, not shown in help message */ 1284#endif 1285#if OPT_HP_FUNC_KEYS 1286{ "-/+hf", "turn on/off HP Function Key escape codes" }, 1287#endif 1288{ "-/+hold", "turn on/off logic that retains window after exit" }, 1289#if OPT_INITIAL_ERASE 1290{ "-/+ie", "turn on/off initialization of 'erase' from pty" }, 1291#endif 1292{ "-/+im", "use insert mode for TERMCAP" }, 1293{ "-/+j", "turn on/off jump scroll" }, 1294#if OPT_C1_PRINT 1295{ "-/+k8", "turn on/off C1-printable classification"}, 1296#endif 1297{ "-kt keyboardtype", "set keyboard type:" KEYBOARD_TYPES }, 1298#ifdef ALLOWLOGGING 1299{ "-/+l", "turn on/off logging" }, 1300{ "-lf filename", "logging filename (use '-' for standard out)" }, 1301#else 1302{ "-/+l", "turn on/off logging (not supported)" }, 1303{ "-lf filename", "logging filename (not supported)" }, 1304#endif 1305{ "-/+ls", "turn on/off login shell" }, 1306{ "-/+mb", "turn on/off margin bell" }, 1307{ "-mc milliseconds", "multiclick time in milliseconds" }, 1308{ "-/+mesg", "forbid/allow messages" }, 1309{ "-ms color", "pointer color" }, 1310{ "-nb number", "margin bell in characters from right end" }, 1311{ "-/+nul", "turn off/on display of underlining" }, 1312{ "-/+aw", "turn on/off auto wraparound" }, 1313{ "-/+pc", "turn on/off PC-style bold colors" }, 1314{ "-/+rw", "turn on/off reverse wraparound" }, 1315{ "-/+s", "turn on/off multiscroll" }, 1316{ "-/+sb", "turn on/off scrollbar" }, 1317#if OPT_REPORT_CCLASS 1318{"-report-charclass", "report \"charClass\" after initialization"}, 1319#endif 1320#if OPT_REPORT_COLORS 1321{ "-report-colors", "report colors as they are allocated" }, 1322#endif 1323#if OPT_REPORT_FONTS 1324{ "-report-fonts", "report fonts as loaded to stdout" }, 1325#endif 1326#if OPT_REPORT_ICONS 1327{ "-report-icons", "report title/icon updates" }, 1328#endif 1329#if OPT_XRES_QUERY 1330{ "-report-xres", "report X resources for VT100 widget" }, 1331#endif 1332#ifdef SCROLLBAR_RIGHT 1333{ "-rightbar", "force scrollbar right (default left)" }, 1334{ "-leftbar", "force scrollbar left" }, 1335#endif 1336{ "-/+rvc", "turn off/on display of reverse as color" }, 1337{ "-/+sf", "turn on/off Sun Function Key escape codes" }, 1338{ "-sh number", "scale line-height values by the given number" }, 1339{ "-/+si", "turn on/off scroll-on-tty-output inhibit" }, 1340{ "-/+sk", "turn on/off scroll-on-keypress" }, 1341{ "-sl number", "number of scrolled lines to save" }, 1342#if OPT_SUNPC_KBD 1343{ "-/+sp", "turn on/off Sun/PC Function/Keypad mapping" }, 1344#endif 1345#if OPT_TEK4014 1346{ "-/+t", "turn on/off Tek emulation window" }, 1347#endif 1348#if OPT_TOOLBAR 1349{ "-/+tb", "turn on/off toolbar" }, 1350#endif 1351{ "-ti termid", "terminal identifier" }, 1352{ "-tm string", "terminal mode keywords and characters" }, 1353{ "-tn name", "TERM environment variable name" }, 1354#if OPT_WIDE_CHARS 1355{ "-/+u8", "turn on/off UTF-8 mode (implies wide-characters)" }, 1356#endif 1357#if OPT_LUIT_PROG 1358{ "-/+lc", "turn on/off locale mode using luit" }, 1359{ "-lcc path", "filename of locale converter (" DEFLOCALEFILTER ")" }, 1360/* -en is deprecated, not shown in help message */ 1361#endif 1362{ "-/+uc", "turn on/off underline cursor" }, 1363{ "-/+ulc", "turn off/on display of underline as color" }, 1364{ "-/+ulit", "turn off/on display of underline as italics" }, 1365#ifdef HAVE_UTMP 1366{ "-/+ut", "turn on/off utmp support" }, 1367#else 1368{ "-/+ut", "turn on/off utmp support (not available)" }, 1369#endif 1370{ "-/+vb", "turn on/off visual bell" }, 1371{ "-/+pob", "turn on/off pop on bell" }, 1372#if OPT_WIDE_ATTRS && OPT_ISO_COLORS 1373{ "-/+itc", "turn off/on display of italic as color"}, 1374#endif 1375#if OPT_WIDE_CHARS 1376{ "-/+wc", "turn on/off wide-character mode" }, 1377{ "-/+mk_width", "turn on/off simple width convention" }, 1378{ "-/+cjk_width", "turn on/off legacy CJK width convention" }, 1379#endif 1380{ "-/+wf", "turn on/off wait for map before command exec" }, 1381{ "-e command args ...", "command to execute" }, 1382#if OPT_TEK4014 1383{ "%geom", "Tek window geometry" }, 1384#endif 1385{ "#geom", "icon window geometry" }, 1386{ "-T string", "title name for window" }, 1387{ "-n string", "icon name for window" }, 1388#if defined(TIOCCONS) || defined(SRIOCSREDIR) 1389{ "-C", "intercept console messages" }, 1390#else 1391{ "-C", "intercept console messages (not supported)" }, 1392#endif 1393{ "-Sccn", "slave mode on \"ttycc\", file descriptor \"n\"" }, 1394{ "-into windowId", "use the window id given to -into as the parent window rather than the default root window" }, 1395#if OPT_ZICONBEEP 1396{ "-ziconbeep percent", "beep and flag icon of window having hidden output" }, 1397#endif 1398#if OPT_SAME_NAME 1399{ "-/+samename", "turn on/off the no-flicker option for title and icon name" }, 1400#endif 1401#if OPT_SESSION_MGT 1402{ "-/+sm", "turn on/off the session-management support" }, 1403#endif 1404#if OPT_MAXIMIZE 1405{"-/+maximized", "turn on/off maximize on startup" }, 1406{"-/+fullscreen", "turn on/off fullscreen on startup" }, 1407#endif 1408{ NULL, NULL }}; 1409/* *INDENT-ON* */ 1410 1411static const char *const help_message[] = 1412{ 1413 "Fonts should be fixed width and, if both normal and bold are specified, should", 1414 "have the same size. If only a normal font is specified, it will be used for", 1415 "both normal and bold text (by doing overstriking). The -e option, if given,", 1416 "must appear at the end of the command line, otherwise the user's default shell", 1417 "will be started. Options that start with a plus sign (+) restore the default.", 1418 NULL 1419}; 1420 1421int 1422xtermDisabledChar(void) 1423{ 1424 int value = -1; 1425#if defined(_POSIX_VDISABLE) && defined(HAVE_UNISTD_H) 1426 value = _POSIX_VDISABLE; 1427#endif 1428#if defined(_PC_VDISABLE) 1429 if (value == -1) { 1430 value = (int) fpathconf(0, _PC_VDISABLE); 1431 if (value == -1) { 1432 if (errno == 0) 1433 value = 0377; 1434 } 1435 } 1436#elif defined(VDISABLE) 1437 if (value == -1) 1438 value = VDISABLE; 1439#endif 1440 return value; 1441} 1442 1443/* 1444 * Retrieve (possibly allocating) an atom from the server. Cache the result. 1445 */ 1446Atom 1447CachedInternAtom(Display *display, const char *name) 1448{ 1449 /* 1450 * Aside from a couple of rarely used atoms, the others are all known. 1451 */ 1452 static const char *const known_atoms[] = 1453 { 1454 "FONT", 1455 "WM_CLASS", 1456 "WM_DELETE_WINDOW", 1457 "_NET_ACTIVE_WINDOW", 1458 "_NET_FRAME_EXTENTS", 1459 "_NET_FRAME_EXTENTS", 1460 "_NET_SUPPORTED", 1461 "_NET_SUPPORTING_WM_CHECK", 1462 "_NET_WM_ALLOWED_ACTIONS", 1463 "_NET_WM_ICON_NAME", 1464 "_NET_WM_NAME", 1465 "_NET_WM_PID", 1466 "_NET_WM_STATE", 1467 "_NET_WM_STATE_ADD", 1468 "_NET_WM_STATE_FULLSCREEN", 1469 "_NET_WM_STATE_HIDDEN", 1470 "_NET_WM_STATE_MAXIMIZED_HORZ", 1471 "_NET_WM_STATE_MAXIMIZED_VERT", 1472 "_NET_WM_STATE_REMOVE", 1473 "_WIN_SUPPORTING_WM_CHECK", 1474#if defined(HAVE_XKB_BELL_EXT) 1475 XkbBN_Info, 1476 XkbBN_MarginBell, 1477 XkbBN_MinorError, 1478 XkbBN_TerminalBell, 1479#endif 1480#if defined(HAVE_XKBQUERYEXTENSION) 1481 "Num Lock", 1482 "Caps Lock", 1483 "Scroll Lock", 1484#endif 1485 }; 1486 1487#define NumKnownAtoms XtNumber(known_atoms) 1488 1489 /* 1490 * The "+1" entry of the array is used for the occasional atom which is not 1491 * predefined. 1492 */ 1493 static struct { 1494 Atom atom; 1495 const char *name; 1496 } AtomCache[NumKnownAtoms + 1]; 1497 1498 Boolean found = False; 1499 Atom result = None; 1500 Cardinal i; 1501 1502 TRACE(("intern_atom \"%s\"\n", name)); 1503 1504 /* 1505 * This should never happen as it implies xterm is aware of multiple 1506 * displays. 1507 */ 1508 if (display != XtDisplay(toplevel)) { 1509 SysError(ERROR_GET_ATOM); 1510 } 1511 1512 if (AtomCache[0].name == NULL) { 1513 /* pre-load a number of atoms in one request to reduce latency */ 1514 Atom atom_return[NumKnownAtoms]; 1515 int code; 1516 1517 TRACE(("initialising atom list\n")); 1518 code = XInternAtoms(display, 1519 (char **) known_atoms, 1520 (int) NumKnownAtoms, 1521 False, 1522 atom_return); 1523 /* 1524 * result should be Success, but actually returns BadRequest. 1525 * manpage says XInternAtoms can generate BadAlloc and BadValue errors. 1526 */ 1527 if (code > BadRequest) 1528 SysError(ERROR_GET_ATOM); 1529 1530 for (i = 0; i < NumKnownAtoms; ++i) { 1531 AtomCache[i].name = known_atoms[i]; 1532 AtomCache[i].atom = atom_return[i]; 1533 } 1534 } 1535 1536 /* Linear search is probably OK here, due to the small number of atoms */ 1537 for (i = 0; i < NumKnownAtoms; i++) { 1538 if (strcmp(name, AtomCache[i].name) == 0) { 1539 found = True; 1540 result = AtomCache[i].atom; 1541 break; 1542 } 1543 } 1544 1545 if (!found) { 1546 if (AtomCache[NumKnownAtoms].name == NULL 1547 || strcmp(AtomCache[NumKnownAtoms].name, name)) { 1548 char *actual = x_strdup(name); 1549 free((void *) AtomCache[NumKnownAtoms].name); 1550 result = XInternAtom(display, actual, False); 1551 AtomCache[NumKnownAtoms].atom = result; 1552 AtomCache[NumKnownAtoms].name = actual; 1553 TRACE(("...allocated new atom\n")); 1554 } else { 1555 result = AtomCache[NumKnownAtoms].atom; 1556 TRACE(("...reused cached atom\n")); 1557 } 1558 } 1559 TRACE(("...intern_atom -> %ld\n", result)); 1560 return result; 1561} 1562 1563/* 1564 * Decode a key-definition. This combines the termcap and ttyModes, for 1565 * comparison. Note that octal escapes in ttyModes are done by the normal 1566 * resource translation. Also, ttyModes allows '^-' as a synonym for disabled. 1567 */ 1568static int 1569decode_keyvalue(char **ptr, int termcap) 1570{ 1571 char *string = *ptr; 1572 int value = -1; 1573 1574 TRACE(("decode_keyvalue '%s'\n", string)); 1575 if (*string == '^') { 1576 switch (*++string) { 1577 case '?': 1578 value = A2E(ANSI_DEL); 1579 break; 1580 case '-': 1581 if (!termcap) { 1582 value = xtermDisabledChar(); 1583 break; 1584 } 1585 /* FALLTHRU */ 1586 default: 1587 value = CONTROL(*string); 1588 break; 1589 } 1590 ++string; 1591 } else if (termcap && (*string == '\\')) { 1592 char *s = (string + 1); 1593 char *d; 1594 int temp = (int) strtol(s, &d, 8); 1595 if (PartS2L(s, d) && temp > 0) { 1596 value = temp; 1597 string = d; 1598 } 1599 } else { 1600 value = CharOf(*string); 1601 ++string; 1602 } 1603 *ptr = string; 1604 TRACE(("...decode_keyvalue %#x\n", value)); 1605 return value; 1606} 1607 1608static int 1609matchArg(XrmOptionDescRec * table, const char *param) 1610{ 1611 int result = -1; 1612 int n; 1613 int ch; 1614 1615 for (n = 0; (ch = table->option[n]) != '\0'; ++n) { 1616 if (param[n] == ch) { 1617 result = n; 1618 } else { 1619 if (param[n] != '\0') 1620 result = -1; 1621 break; 1622 } 1623 } 1624 1625 return result; 1626} 1627 1628/* return the number of argv[] entries which constitute arguments of option */ 1629static int 1630countArg(XrmOptionDescRec * item) 1631{ 1632 int result = 0; 1633 1634 switch (item->argKind) { 1635 case XrmoptionNoArg: 1636 /* FALLTHRU */ 1637 case XrmoptionIsArg: 1638 /* FALLTHRU */ 1639 case XrmoptionStickyArg: 1640 break; 1641 case XrmoptionSepArg: 1642 /* FALLTHRU */ 1643 case XrmoptionResArg: 1644 /* FALLTHRU */ 1645 case XrmoptionSkipArg: 1646 result = 1; 1647 break; 1648 case XrmoptionSkipLine: 1649 break; 1650 case XrmoptionSkipNArgs: 1651 result = (int) (long) (item->value); 1652 break; 1653 } 1654 return result; 1655} 1656 1657#define isOption(string) (Boolean)((string)[0] == '-' || (string)[0] == '+') 1658 1659/* 1660 * Parse the argument list, more/less as XtInitialize, etc., would do, so we 1661 * can find our own "-help" and "-version" options reliably. Improve on just 1662 * doing that, by detecting ambiguous options (things that happen to match the 1663 * abbreviated option we are examining), and making it smart enough to handle 1664 * "-d" as an abbreviation for "-display". Doing this requires checking the 1665 * standard table (something that the X libraries should do). 1666 */ 1667static XrmOptionDescRec * 1668parseArg(int *num, char **argv, char **valuep) 1669{ 1670 /* table adapted from XtInitialize, used here to improve abbreviations */ 1671 /* *INDENT-OFF* */ 1672#define DATA(option,kind) { (char *) option, NULL, kind, (XPointer) 0 } 1673 static XrmOptionDescRec opTable[] = { 1674 DATA("+synchronous", XrmoptionNoArg), 1675 DATA("-background", XrmoptionSepArg), 1676 DATA("-bd", XrmoptionSepArg), 1677 DATA("-bg", XrmoptionSepArg), 1678 DATA("-bordercolor", XrmoptionSepArg), 1679 DATA("-borderwidth", XrmoptionSepArg), 1680 DATA("-bw", XrmoptionSepArg), 1681 DATA("-display", XrmoptionSepArg), 1682 DATA("-fg", XrmoptionSepArg), 1683 DATA("-fn", XrmoptionSepArg), 1684 DATA("-font", XrmoptionSepArg), 1685 DATA("-foreground", XrmoptionSepArg), 1686 DATA("-iconic", XrmoptionNoArg), 1687 DATA("-name", XrmoptionSepArg), 1688 DATA("-reverse", XrmoptionNoArg), 1689 DATA("-selectionTimeout", XrmoptionSepArg), 1690 DATA("-synchronous", XrmoptionNoArg), 1691 DATA("-title", XrmoptionSepArg), 1692 DATA("-xnllanguage", XrmoptionSepArg), 1693 DATA("-xrm", XrmoptionResArg), 1694 DATA("-xtsessionID", XrmoptionSepArg), 1695 /* These xterm options are processed after XtOpenApplication */ 1696#if defined(TIOCCONS) || defined(SRIOCSREDIR) 1697 DATA("-C", XrmoptionNoArg), 1698#endif /* TIOCCONS */ 1699 DATA("-S", XrmoptionStickyArg), 1700 DATA("-D", XrmoptionNoArg), 1701 }; 1702#undef DATA 1703 /* *INDENT-ON* */ 1704 XrmOptionDescRec *result = 0; 1705 Cardinal inlist; 1706 Cardinal limit = XtNumber(optionDescList) + XtNumber(opTable); 1707 int atbest = -1; 1708 int best = -1; 1709 int test; 1710 Boolean exact = False; 1711 int ambiguous1 = -1; 1712 int ambiguous2 = -1; 1713 char *option; 1714 char *value; 1715 1716#define ITEM(n) ((Cardinal)(n) < XtNumber(optionDescList) \ 1717 ? &optionDescList[n] \ 1718 : &opTable[(Cardinal)(n) - XtNumber(optionDescList)]) 1719 1720 if ((option = argv[*num]) != 0) { 1721 Boolean need_value; 1722 Boolean have_value = False; 1723 1724 TRACE(("parseArg %s\n", option)); 1725 if ((value = argv[(*num) + 1]) != 0) { 1726 have_value = (Boolean) !isOption(value); 1727 } 1728 for (inlist = 0; inlist < limit; ++inlist) { 1729 XrmOptionDescRec *check = ITEM(inlist); 1730 1731 test = matchArg(check, option); 1732 if (test < 0) 1733 continue; 1734 1735 /* check for exact match */ 1736 if ((test + 1) == (int) strlen(check->option)) { 1737 if (check->argKind == XrmoptionStickyArg) { 1738 if (strlen(option) > strlen(check->option)) { 1739 exact = True; 1740 atbest = (int) inlist; 1741 break; 1742 } 1743 } else if ((test + 1) == (int) strlen(option)) { 1744 exact = True; 1745 atbest = (int) inlist; 1746 break; 1747 } 1748 } 1749 1750 need_value = (Boolean) (test > 0 && countArg(check) > 0); 1751 1752 if (need_value && value != 0) { 1753 ; 1754 } else if (need_value ^ have_value) { 1755 TRACE(("...skipping, need %d vs have %d\n", need_value, have_value)); 1756 continue; 1757 } 1758 1759 /* special-case for our own options - always allow abbreviation */ 1760 if (test > 0 1761 && ITEM(inlist)->argKind >= XrmoptionSkipArg) { 1762 atbest = (int) inlist; 1763 if (ITEM(inlist)->argKind == XrmoptionSkipNArgs) { 1764 /* in particular, silence a warning about ambiguity */ 1765 exact = 1; 1766 } 1767 break; 1768 } 1769 if (test > best) { 1770 best = test; 1771 atbest = (int) inlist; 1772 } else if (test == best) { 1773 if (atbest >= 0) { 1774 if (atbest > 0) { 1775 ambiguous1 = (int) inlist; 1776 ambiguous2 = (int) atbest; 1777 } 1778 atbest = -1; 1779 } 1780 } 1781 } 1782 } 1783 1784 *valuep = 0; 1785 if (atbest >= 0) { 1786 result = ITEM(atbest); 1787 if (!exact) { 1788 if (ambiguous1 >= 0 && ambiguous2 >= 0) { 1789 xtermWarning("ambiguous option \"%s\" vs \"%s\"\n", 1790 ITEM(ambiguous1)->option, 1791 ITEM(ambiguous2)->option); 1792 } else if (strlen(option) > strlen(result->option)) { 1793 result = 0; 1794 } 1795 } 1796 if (result != 0) { 1797 TRACE(("...result %s\n", result->option)); 1798 /* expand abbreviations */ 1799 if (result->argKind != XrmoptionStickyArg) { 1800 if (strcmp(argv[*num], result->option)) { 1801 argv[*num] = x_strdup(result->option); 1802 } 1803 } 1804 1805 /* adjust (*num) to skip option value */ 1806 (*num) += countArg(result); 1807 TRACE(("...next %s\n", NonNull(argv[*num]))); 1808 if (result->argKind == XrmoptionSkipArg) { 1809 *valuep = argv[*num]; 1810 TRACE(("...parameter %s\n", NonNull(*valuep))); 1811 } 1812 } 1813 } 1814#undef ITEM 1815 return result; 1816} 1817 1818static void 1819Syntax(char *badOption) 1820{ 1821 OptionHelp *opt; 1822 OptionHelp *list = sortedOpts(xtermOptions, optionDescList, XtNumber(optionDescList)); 1823 int col; 1824 1825 TRACE(("Syntax error at %s\n", badOption)); 1826 xtermWarning("bad command line option \"%s\"\r\n\n", badOption); 1827 1828 fprintf(stderr, "usage: %s", ProgramName); 1829 col = 8 + (int) strlen(ProgramName); 1830 for (opt = list; opt->opt; opt++) { 1831 int len = 3 + (int) strlen(opt->opt); /* space [ string ] */ 1832 if (col + len > 79) { 1833 fprintf(stderr, "\r\n "); /* 3 spaces */ 1834 col = 3; 1835 } 1836 fprintf(stderr, " [%s]", opt->opt); 1837 col += len; 1838 } 1839 1840 fprintf(stderr, "\r\n\nType %s -help for a full description.\r\n\n", 1841 ProgramName); 1842 exit(ERROR_MISC); 1843} 1844 1845static void 1846Version(void) 1847{ 1848 printf("%s\n", xtermVersion()); 1849 fflush(stdout); 1850} 1851 1852static void 1853Help(void) 1854{ 1855 OptionHelp *opt; 1856 OptionHelp *list = sortedOpts(xtermOptions, optionDescList, XtNumber(optionDescList)); 1857 const char *const *cpp; 1858 1859 printf("%s usage:\n %s [-options ...] [-e command args]\n\n", 1860 xtermVersion(), ProgramName); 1861 printf("where options include:\n"); 1862 for (opt = list; opt->opt; opt++) { 1863 printf(" %-28s %s\n", opt->opt, opt->desc); 1864 } 1865 1866 putchar('\n'); 1867 for (cpp = help_message; *cpp; cpp++) 1868 puts(*cpp); 1869 putchar('\n'); 1870 fflush(stdout); 1871} 1872 1873static void 1874NeedParam(XrmOptionDescRec * option_ptr, const char *option_val) 1875{ 1876 if (IsEmpty(option_val)) { 1877 xtermWarning("option %s requires a value\n", option_ptr->option); 1878 exit(ERROR_MISC); 1879 } 1880} 1881 1882#if defined(TIOCCONS) || defined(SRIOCSREDIR) 1883/* ARGSUSED */ 1884static Boolean 1885ConvertConsoleSelection(Widget w GCC_UNUSED, 1886 Atom *selection GCC_UNUSED, 1887 Atom *target GCC_UNUSED, 1888 Atom *type GCC_UNUSED, 1889 XtPointer *value GCC_UNUSED, 1890 unsigned long *length GCC_UNUSED, 1891 int *format GCC_UNUSED) 1892{ 1893 /* we don't save console output, so can't offer it */ 1894 return False; 1895} 1896#endif /* TIOCCONS */ 1897 1898/* 1899 * DeleteWindow(): Action proc to implement ICCCM delete_window. 1900 */ 1901/* ARGSUSED */ 1902static void 1903DeleteWindow(Widget w, 1904 XEvent *event GCC_UNUSED, 1905 String *params GCC_UNUSED, 1906 Cardinal *num_params GCC_UNUSED) 1907{ 1908#if OPT_TEK4014 1909 if (w == toplevel) { 1910 if (TEK4014_SHOWN(term)) 1911 hide_vt_window(); 1912 else 1913 do_hangup(w, (XtPointer) 0, (XtPointer) 0); 1914 } else if (TScreenOf(term)->Vshow) 1915 hide_tek_window(); 1916 else 1917#endif 1918 do_hangup(w, (XtPointer) 0, (XtPointer) 0); 1919} 1920 1921/* ARGSUSED */ 1922static void 1923KeyboardMapping(Widget w GCC_UNUSED, 1924 XEvent *event, 1925 String *params GCC_UNUSED, 1926 Cardinal *num_params GCC_UNUSED) 1927{ 1928 switch (event->type) { 1929 case MappingNotify: 1930 XRefreshKeyboardMapping(&event->xmapping); 1931 break; 1932 } 1933} 1934 1935static XtActionsRec actionProcs[] = 1936{ 1937 {"DeleteWindow", DeleteWindow}, 1938 {"KeyboardMapping", KeyboardMapping}, 1939}; 1940 1941/* 1942 * Some platforms use names such as /dev/tty01, others /dev/pts/1. Parse off 1943 * the "tty01" or "pts/1" portion, and return that for use as an identifier for 1944 * utmp. 1945 */ 1946static char * 1947my_pty_name(char *device) 1948{ 1949 size_t len = strlen(device); 1950 Bool name = False; 1951 1952 while (len != 0) { 1953 int ch = device[len - 1]; 1954 if (isdigit(ch)) { 1955 len--; 1956 } else if (ch == '/') { 1957 if (name) 1958 break; 1959 len--; 1960 } else if (isalpha(CharOf(ch))) { 1961 name = True; 1962 len--; 1963 } else { 1964 break; 1965 } 1966 } 1967 TRACE(("my_pty_name(%s) -> '%s'\n", device, device + len)); 1968 return device + len; 1969} 1970 1971/* 1972 * If the name contains a '/', it is a "pts/1" case. Otherwise, return the 1973 * last few characters for a utmp identifier. 1974 */ 1975static char * 1976my_pty_id(char *device) 1977{ 1978 char *name = my_pty_name(device); 1979 char *leaf = x_basename(name); 1980 1981 if (name == leaf) { /* no '/' in the name */ 1982 int len = (int) strlen(leaf); 1983 if (PTYCHARLEN < len) 1984 leaf = leaf + (len - PTYCHARLEN); 1985 } 1986 TRACE(("my_pty_id (%s) -> '%s'\n", device, leaf)); 1987 return leaf; 1988} 1989 1990/* 1991 * Set the tty/pty identifier 1992 */ 1993static void 1994set_pty_id(char *device, char *id) 1995{ 1996 char *name = my_pty_name(device); 1997 char *leaf = x_basename(name); 1998 1999 if (name == leaf) { 2000 strcpy(my_pty_id(device), id); 2001 } else { 2002 strcpy(leaf, id); 2003 } 2004 TRACE(("set_pty_id(%s) -> '%s'\n", id, device)); 2005} 2006 2007/* 2008 * The original -S option accepts two characters to identify the pty, and a 2009 * file-descriptor (assumed to be nonzero). That is not general enough, so we 2010 * check first if the option contains a '/' to delimit the two fields, and if 2011 * not, fall-thru to the original logic. 2012 */ 2013static Bool 2014ParseSccn(char *option) 2015{ 2016 char *leaf = x_basename(option); 2017 Bool code = False; 2018 2019 passedPty = x_strdup(option); 2020 if (leaf != option) { 2021 if (leaf - option > 0 2022 && isdigit(CharOf(*leaf)) 2023 && sscanf(leaf, "%d", &am_slave) == 1) { 2024 size_t len = (size_t) (leaf - option - 1); 2025 /* 2026 * If we have a slash, we only care about the part after the slash, 2027 * which is a file-descriptor. The part before the slash can be 2028 * the /dev/pts/XXX value, but since we do not need to reopen it, 2029 * it is useful mainly for display in a "ps -ef". 2030 */ 2031 passedPty[len] = 0; 2032 code = True; 2033 } 2034 } else { 2035 code = (sscanf(option, "%c%c%d", 2036 passedPty, passedPty + 1, &am_slave) == 3); 2037 passedPty[2] = '\0'; 2038 } 2039 TRACE(("ParseSccn(%s) = '%s' %d (%s)\n", option, 2040 passedPty, am_slave, code ? "OK" : "ERR")); 2041 return code; 2042} 2043 2044#if defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 2045/* 2046 * From "man utmp": 2047 * xterm and other terminal emulators directly create a USER_PROCESS record 2048 * and generate the ut_id by using the last two letters of /dev/ttyp%c or by 2049 * using p%d for /dev/pts/%d. If they find a DEAD_PROCESS for this id, they 2050 * recycle it, otherwise they create a new entry. If they can, they will mark 2051 * it as DEAD_PROCESS on exiting and it is advised that they null ut_line, 2052 * ut_time, ut_user and ut_host as well. 2053 * 2054 * Generally ut_id allows no more than 3 characters (plus null), even if the 2055 * pty implementation allows more than 3 digits. 2056 */ 2057static char * 2058my_utmp_id(char *device) 2059{ 2060 typedef struct UTMP_STR UTMP_STRUCT; 2061#define UTIDSIZE (sizeof(((UTMP_STRUCT *)NULL)->ut_id)) 2062 static char result[UTIDSIZE + 1]; 2063 2064#if defined(__SCO__) || defined(__UNIXWARE__) 2065 /* 2066 * Legend does not support old-style pty's, has no related compatibility 2067 * issues, and can use the available space in ut_id differently from the 2068 * default convention. 2069 * 2070 * This scheme is intended to avoid conflicts both with other users of 2071 * utmpx as well as between multiple xterms. First, Legend uses all of the 2072 * characters of ut_id, and adds no terminating NUL is required (the 2073 * default scheme may add a trailing NUL). Second, all xterm entries will 2074 * start with the letter 'x' followed by three digits, which will be the 2075 * last three digits of the device name, regardless of the format of the 2076 * device name, with leading 0's added where necessary. For instance, an 2077 * xterm on /dev/pts/3 will have a ut_id of x003; an xterm on /dev/pts123 2078 * will have a ut_id of x123. Under the other convention, /dev/pts/3 would 2079 * have a ut_id of p3 and /dev/pts123 would have a ut_id of p123. 2080 */ 2081 int len, n; 2082 2083 len = strlen(device); 2084 n = UTIDSIZE; 2085 result[n] = '\0'; 2086 while ((n > 0) && (len > 0) && isdigit(device[len - 1])) 2087 result[--n] = device[--len]; 2088 while (n > 0) 2089 result[--n] = '0'; 2090 result[0] = 'x'; 2091#else 2092 char *name = my_pty_name(device); 2093 char *leaf = x_basename(name); 2094 size_t len = strlen(leaf); 2095 2096 if ((UTIDSIZE - 1) < len) 2097 leaf = leaf + (len - (UTIDSIZE - 1)); 2098 sprintf(result, "p%s", leaf); 2099#endif 2100 2101 TRACE(("my_utmp_id (%s) -> '%s'\n", device, result)); 2102 return result; 2103} 2104#endif /* USE_SYSV_UTMP */ 2105 2106#ifdef USE_POSIX_SIGNALS 2107 2108typedef void (*sigfunc) (int); 2109 2110/* make sure we sure we ignore SIGCHLD for the cases parent 2111 has just been stopped and not actually killed */ 2112 2113static sigfunc 2114posix_signal(int signo, sigfunc func) 2115{ 2116 struct sigaction act, oact; 2117 2118 act.sa_handler = func; 2119 sigemptyset(&act.sa_mask); 2120#ifdef SA_RESTART 2121 act.sa_flags = SA_NOCLDSTOP | SA_RESTART; 2122#else 2123 act.sa_flags = SA_NOCLDSTOP; 2124#endif 2125 if (sigaction(signo, &act, &oact) < 0) 2126 return (SIG_ERR); 2127 return (oact.sa_handler); 2128} 2129 2130#endif /* USE_POSIX_SIGNALS */ 2131 2132#if defined(DISABLE_SETUID) || defined(USE_UTMP_SETGID) 2133static void 2134disableSetUid(void) 2135{ 2136 TRACE(("process %d disableSetUid\n", (int) getpid())); 2137 if (setuid(save_ruid) == -1) { 2138 xtermWarning("unable to reset uid\n"); 2139 exit(ERROR_MISC); 2140 } 2141 TRACE_IDS; 2142} 2143#else 2144#define disableSetUid() /* nothing */ 2145#endif /* DISABLE_SETUID */ 2146 2147#if defined(DISABLE_SETGID) || defined(USE_UTMP_SETGID) 2148static void 2149disableSetGid(void) 2150{ 2151 TRACE(("process %d disableSetGid\n", (int) getpid())); 2152 if (setegid(save_rgid) == -1) { 2153 xtermWarning("unable to reset effective gid\n"); 2154 exit(ERROR_MISC); 2155 } 2156 TRACE_IDS; 2157} 2158#else 2159#define disableSetGid() /* nothing */ 2160#endif /* DISABLE_SETGID */ 2161 2162#if defined(HAVE_POSIX_SAVED_IDS) 2163#if (!defined(USE_UTEMPTER) || !defined(DISABLE_SETGID)) 2164static void 2165setEffectiveGroup(gid_t group) 2166{ 2167 TRACE(("process %d setEffectiveGroup(%d)\n", (int) getpid(), (int) group)); 2168 if (setegid(group) == -1) { 2169#ifdef __MVS__ 2170 if (!(errno == EMVSERR)) /* could happen if _BPX_SHAREAS=REUSE */ 2171#endif 2172 { 2173 xtermPerror("setegid(%d)", (int) group); 2174 } 2175 } 2176 TRACE_IDS; 2177} 2178#endif 2179 2180#if !defined(USE_UTMP_SETGID) && (!defined(USE_UTEMPTER) || !defined(DISABLE_SETUID)) 2181static void 2182setEffectiveUser(uid_t user) 2183{ 2184 TRACE(("process %d setEffectiveUser(%d)\n", (int) getpid(), (int) user)); 2185 if (seteuid(user) == -1) { 2186#ifdef __MVS__ 2187 if (!(errno == EMVSERR)) 2188#endif 2189 { 2190 xtermPerror("seteuid(%d)", (int) user); 2191 } 2192 } 2193 TRACE_IDS; 2194} 2195#endif 2196#endif /* HAVE_POSIX_SAVED_IDS */ 2197 2198#if OPT_LUIT_PROG 2199static Boolean 2200complex_command(char **args) 2201{ 2202 Boolean result = False; 2203 if (x_countargv(args) == 1) { 2204 char *check = xtermFindShell(args[0], False); 2205 if (check == 0) { 2206 result = True; 2207 } else { 2208 free(check); 2209 } 2210 } 2211 return result; 2212} 2213#endif 2214 2215static unsigned 2216lookup_baudrate(const char *value) 2217{ 2218 struct speed { 2219 unsigned given_speed; /* values for 'ospeed' */ 2220 unsigned actual_speed; /* the actual speed */ 2221 }; 2222 2223#define DATA(number) { B##number, number } 2224 2225 static struct speed const speeds[] = 2226 { 2227 DATA(0), 2228 DATA(50), 2229 DATA(75), 2230 DATA(110), 2231 DATA(134), 2232 DATA(150), 2233 DATA(200), 2234 DATA(300), 2235 DATA(600), 2236 DATA(1200), 2237 DATA(1800), 2238 DATA(2400), 2239 DATA(4800), 2240 DATA(9600), 2241#ifdef B19200 2242 DATA(19200), 2243#elif defined(EXTA) 2244 {EXTA, 19200}, 2245#endif 2246#ifdef B28800 2247 DATA(28800), 2248#endif 2249#ifdef B38400 2250 DATA(38400), 2251#elif defined(EXTB) 2252 {EXTB, 38400}, 2253#endif 2254#ifdef B57600 2255 DATA(57600), 2256#endif 2257#ifdef B76800 2258 DATA(76800), 2259#endif 2260#ifdef B115200 2261 DATA(115200), 2262#endif 2263#ifdef B153600 2264 DATA(153600), 2265#endif 2266#ifdef B230400 2267 DATA(230400), 2268#endif 2269#ifdef B307200 2270 DATA(307200), 2271#endif 2272#ifdef B460800 2273 DATA(460800), 2274#endif 2275#ifdef B500000 2276 DATA(500000), 2277#endif 2278#ifdef B576000 2279 DATA(576000), 2280#endif 2281#ifdef B921600 2282 DATA(921600), 2283#endif 2284#ifdef B1000000 2285 DATA(1000000), 2286#endif 2287#ifdef B1152000 2288 DATA(1152000), 2289#endif 2290#ifdef B1500000 2291 DATA(1500000), 2292#endif 2293#ifdef B2000000 2294 DATA(2000000), 2295#endif 2296#ifdef B2500000 2297 DATA(2500000), 2298#endif 2299#ifdef B3000000 2300 DATA(3000000), 2301#endif 2302#ifdef B3500000 2303 DATA(3500000), 2304#endif 2305#ifdef B4000000 2306 DATA(4000000), 2307#endif 2308 }; 2309#undef DATA 2310 unsigned result = 0; 2311 long check; 2312 char *next; 2313 if (x_toupper(*value) == 'B') 2314 value++; 2315 if (isdigit(CharOf(*value))) { 2316 check = strtol(value, &next, 10); 2317 if (FullS2L(value, next) && (check > 0)) { 2318 Cardinal n; 2319 for (n = 0; n < XtNumber(speeds); ++n) { 2320 if (speeds[n].actual_speed == (unsigned) check) { 2321 result = speeds[n].given_speed; 2322 break; 2323 } 2324 } 2325 } 2326 } 2327 if (result == 0) { 2328 fprintf(stderr, "unsupported value for baudrate: %s\n", value); 2329 } 2330 return result; 2331} 2332 2333int 2334get_tty_erase(int fd, int default_erase, const char *tag) 2335{ 2336 int result = default_erase; 2337 int rc; 2338 2339#ifdef TERMIO_STRUCT 2340 TERMIO_STRUCT my_tio; 2341 rc = ttyGetAttr(fd, &my_tio); 2342 if (rc == 0) 2343 result = my_tio.c_cc[VERASE]; 2344#else /* !TERMIO_STRUCT */ 2345 struct sgttyb my_sg; 2346 rc = ioctl(fd, TIOCGETP, (char *) &my_sg); 2347 if (rc == 0) 2348 result = my_sg.sg_erase; 2349#endif /* TERMIO_STRUCT */ 2350 TRACE(("%s erase:%d (from %s)\n", (rc == 0) ? "OK" : "FAIL", result, tag)); 2351 (void) tag; 2352 return result; 2353} 2354 2355int 2356get_tty_lnext(int fd, int default_lnext, const char *tag) 2357{ 2358 int result = default_lnext; 2359 int rc; 2360 2361#ifdef TERMIO_STRUCT 2362 TERMIO_STRUCT my_tio; 2363 rc = ttyGetAttr(fd, &my_tio); 2364 if (rc == 0) 2365 result = my_tio.c_cc[VLNEXT]; 2366#elif defined(HAS_LTCHARS) 2367 struct ltchars my_ltc; 2368 rc = ioctl(fd, TIOCGLTC, (char *) &my_ltc); 2369 if (rc == 0) 2370 result = my_ltc.t_lnextc; 2371#else 2372 result = XTERM_LNEXT; 2373#endif /* TERMIO_STRUCT */ 2374 TRACE(("%s lnext:%d (from %s)\n", (rc == 0) ? "OK" : "FAIL", result, tag)); 2375 (void) tag; 2376 return result; 2377} 2378 2379int 2380main(int argc, char *argv[]ENVP_ARG) 2381{ 2382#if OPT_MAXIMIZE 2383#define DATA(name) { #name, es##name } 2384 static const FlagList tblFullscreen[] = 2385 { 2386 DATA(Always), 2387 DATA(Never) 2388 }; 2389#undef DATA 2390#endif 2391 2392 Widget form_top, menu_top; 2393 Dimension menu_high; 2394 TScreen *screen; 2395 int mode; 2396 char *my_class = x_strdup(DEFCLASS); 2397 unsigned line_speed = VAL_LINE_SPEED; 2398 Window winToEmbedInto = None; 2399#if defined(HAVE_LIB_XAW3DXFT) 2400 Xaw3dXftData *xaw3dxft_data; 2401#endif 2402 2403 ProgramName = x_strdup(x_basename(argv[0])); 2404 ProgramPath = xtermFindShell(argv[0], True); 2405 if (ProgramPath != NULL) 2406 argv[0] = ProgramPath; 2407 2408#ifdef HAVE_POSIX_SAVED_IDS 2409 save_euid = geteuid(); 2410 save_egid = getegid(); 2411#endif 2412 2413 save_ruid = getuid(); 2414 save_rgid = getgid(); 2415 2416#if defined(DISABLE_SETUID) || defined(DISABLE_SETGID) 2417#if defined(DISABLE_SETUID) 2418 disableSetUid(); 2419#endif 2420#if defined(DISABLE_SETGID) 2421 disableSetGid(); 2422#endif 2423 TRACE_IDS; 2424#endif 2425 2426 /* extra length in case longer tty name like /dev/ttyq255 */ 2427 ttydev = TypeMallocN(char, sizeof(TTYDEV) + 80); 2428#ifdef USE_PTY_DEVICE 2429 ptydev = TypeMallocN(char, sizeof(PTYDEV) + 80); 2430 if (!ttydev || !ptydev) 2431#else 2432 if (!ttydev) 2433#endif 2434 { 2435 xtermWarning("unable to allocate memory for ttydev or ptydev\n"); 2436 exit(ERROR_MISC); 2437 } 2438 strcpy(ttydev, TTYDEV); 2439#ifdef USE_PTY_DEVICE 2440 strcpy(ptydev, PTYDEV); 2441#endif 2442 2443#if defined(USE_UTMP_SETGID) 2444 get_pty(NULL, NULL); 2445 disableSetUid(); 2446 disableSetGid(); 2447 TRACE_IDS; 2448#define get_pty(pty, from) really_get_pty(pty, from) 2449#endif 2450 2451 /* Do these first, since we may not be able to open the display */ 2452 TRACE_OPTS(xtermOptions, optionDescList, XtNumber(optionDescList)); 2453 TRACE_ARGV("Before XtOpenApplication", argv); 2454 restart_params = 0; 2455 if (argc > 1) { 2456 XrmOptionDescRec *option_ptr; 2457 char *option_value; 2458 int n; 2459 Bool quit = False; 2460 2461 for (n = 1; n < argc; n++) { 2462 if ((option_ptr = parseArg(&n, argv, &option_value)) == 0) { 2463 if (argv[n] == 0) { 2464 break; 2465 } else if (isOption(argv[n])) { 2466 Syntax(argv[n]); 2467 } else if (explicit_shname != 0) { 2468 xtermWarning("Explicit shell already was %s\n", explicit_shname); 2469 Syntax(argv[n]); 2470 } 2471 explicit_shname = xtermFindShell(argv[n], True); 2472 if (explicit_shname == 0) 2473 exit(0); 2474 TRACE(("...explicit shell %s\n", explicit_shname)); 2475 restart_params = (argc - n); 2476 } else if (!strcmp(option_ptr->option, "-e")) { 2477 command_to_exec = (argv + n + 1); 2478 if (!command_to_exec[0]) 2479 Syntax(argv[n]); 2480 restart_params = (argc - n); 2481 break; 2482 } else if (!strcmp(option_ptr->option, "-version")) { 2483 Version(); 2484 quit = True; 2485 } else if (!strcmp(option_ptr->option, "-help")) { 2486 Help(); 2487 quit = True; 2488 } else if (!strcmp(option_ptr->option, "-baudrate")) { 2489 NeedParam(option_ptr, option_value); 2490 if ((line_speed = lookup_baudrate(option_value)) == 0) { 2491 Help(); 2492 quit = True; 2493 } 2494 } else if (!strcmp(option_ptr->option, "-class")) { 2495 NeedParam(option_ptr, option_value); 2496 free(my_class); 2497 if ((my_class = x_strdup(option_value)) == 0) { 2498 Help(); 2499 quit = True; 2500 } 2501 } else if (!strcmp(option_ptr->option, "-into")) { 2502 char *endPtr; 2503 NeedParam(option_ptr, option_value); 2504 winToEmbedInto = (Window) strtol(option_value, &endPtr, 0); 2505 if (!FullS2L(option_value, endPtr)) { 2506 Help(); 2507 quit = True; 2508 } 2509 } 2510 } 2511 if (quit) 2512 exit(0); 2513 /* 2514 * If there is anything left unparsed, and we're not using "-e", 2515 * then give up. 2516 */ 2517 if (n < argc && !command_to_exec) { 2518 Syntax(argv[n]); 2519 } 2520 } 2521 2522 /* This dumped core on HP-UX 9.05 with X11R5 */ 2523#if OPT_I18N_SUPPORT 2524 XtSetLanguageProc(NULL, NULL, NULL); 2525#endif 2526 2527 /* enable Xft support in Xaw3DXft */ 2528#if defined(HAVE_LIB_XAW3DXFT) 2529 GET_XAW3DXFT_DATA(xaw3dxft_data); 2530 xaw3dxft_data->encoding = -1; 2531#endif 2532 2533#ifdef TERMIO_STRUCT /* { */ 2534 /* Initialization is done here rather than above in order 2535 * to prevent any assumptions about the order of the contents 2536 * of the various terminal structures (which may change from 2537 * implementation to implementation). 2538 */ 2539 memset(&d_tio, 0, sizeof(d_tio)); 2540 d_tio.c_iflag = ICRNL | IXON; 2541 d_tio.c_oflag = TAB3 | D_TIO_FLAGS; 2542 { 2543 Cardinal nn; 2544 2545 /* fill in default-values */ 2546 for (nn = 0; nn < XtNumber(ttyChars); ++nn) { 2547 if (validTtyChar(d_tio, nn)) { 2548 d_tio.c_cc[ttyChars[nn].sysMode] = 2549 (cc_t) ttyChars[nn].myDefault; 2550 } 2551 } 2552 } 2553#if defined(macII) || defined(ATT) || defined(CRAY) /* { */ 2554 d_tio.c_cflag = line_speed | CS8 | CREAD | PARENB | HUPCL; 2555 d_tio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK; 2556#ifdef ECHOKE 2557 d_tio.c_lflag |= ECHOKE | IEXTEN; 2558#endif 2559#ifdef ECHOCTL 2560 d_tio.c_lflag |= ECHOCTL | IEXTEN; 2561#endif 2562#ifndef USE_TERMIOS /* { */ 2563 d_tio.c_line = 0; 2564#endif /* } */ 2565#ifdef HAS_LTCHARS /* { */ 2566 d_ltc.t_suspc = CSUSP; /* t_suspc */ 2567 d_ltc.t_dsuspc = CDSUSP; /* t_dsuspc */ 2568 d_ltc.t_rprntc = CRPRNT; 2569 d_ltc.t_flushc = CFLUSH; 2570 d_ltc.t_werasc = CWERASE; 2571 d_ltc.t_lnextc = CLNEXT; 2572#endif /* } HAS_LTCHARS */ 2573#ifdef TIOCLSET /* { */ 2574 d_lmode = 0; 2575#endif /* } TIOCLSET */ 2576#else /* }{ else !macII, ATT, CRAY */ 2577#ifndef USE_POSIX_TERMIOS 2578#ifdef BAUD_0 /* { */ 2579 d_tio.c_cflag = CS8 | CREAD | PARENB | HUPCL; 2580#else /* }{ !BAUD_0 */ 2581 d_tio.c_cflag = line_speed | CS8 | CREAD | PARENB | HUPCL; 2582#endif /* } !BAUD_0 */ 2583#else /* USE_POSIX_TERMIOS */ 2584 d_tio.c_cflag = CS8 | CREAD | PARENB | HUPCL; 2585 cfsetispeed(&d_tio, line_speed); 2586 cfsetospeed(&d_tio, line_speed); 2587#endif 2588 d_tio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK; 2589#ifdef ECHOKE 2590 d_tio.c_lflag |= ECHOKE | IEXTEN; 2591#endif 2592#ifdef ECHOCTL 2593 d_tio.c_lflag |= ECHOCTL | IEXTEN; 2594#endif 2595#ifndef USE_POSIX_TERMIOS 2596#ifdef NTTYDISC 2597 d_tio.c_line = NTTYDISC; 2598#else 2599 d_tio.c_line = 0; 2600#endif 2601#endif /* USE_POSIX_TERMIOS */ 2602#ifdef __sgi 2603 d_tio.c_cflag &= ~(HUPCL | PARENB); 2604 d_tio.c_iflag |= BRKINT | ISTRIP | IGNPAR; 2605#endif 2606#ifdef __MVS__ 2607 d_tio.c_cflag &= ~(HUPCL | PARENB); 2608#endif 2609 { 2610 Cardinal nn; 2611 int i; 2612 2613 /* try to inherit tty settings */ 2614 for (i = 0; i <= 2; i++) { 2615 TERMIO_STRUCT deftio; 2616 if (ttyGetAttr(i, &deftio) == 0) { 2617 for (nn = 0; nn < XtNumber(ttyChars); ++nn) { 2618 if (validTtyChar(d_tio, nn)) { 2619 d_tio.c_cc[ttyChars[nn].sysMode] = 2620 deftio.c_cc[ttyChars[nn].sysMode]; 2621 } 2622 } 2623 break; 2624 } 2625 } 2626 } 2627#if defined(USE_TERMIOS) || defined(USE_POSIX_TERMIOS) /* { */ 2628 d_tio.c_cc[VMIN] = 1; 2629 d_tio.c_cc[VTIME] = 0; 2630#endif /* } */ 2631#ifdef HAS_LTCHARS /* { */ 2632 d_ltc.t_suspc = CharOf('\000'); /* t_suspc */ 2633 d_ltc.t_dsuspc = CharOf('\000'); /* t_dsuspc */ 2634 d_ltc.t_rprntc = CharOf('\377'); /* reserved... */ 2635 d_ltc.t_flushc = CharOf('\377'); 2636 d_ltc.t_werasc = CharOf('\377'); 2637 d_ltc.t_lnextc = CharOf('\377'); 2638#endif /* } HAS_LTCHARS */ 2639 2640#ifdef TIOCLSET /* { */ 2641 d_lmode = 0; 2642#endif /* } TIOCLSET */ 2643#endif /* } macII, ATT, CRAY */ 2644#endif /* } TERMIO_STRUCT */ 2645 2646 /* Init the Toolkit. */ 2647 { 2648#if defined(HAVE_POSIX_SAVED_IDS) && !defined(USE_UTMP_SETGID) && !defined(USE_UTEMPTER) 2649 setEffectiveGroup(save_rgid); 2650 setEffectiveUser(save_ruid); 2651 TRACE_IDS; 2652#endif 2653 toplevel = xtermOpenApplication(&app_con, 2654 my_class, 2655 optionDescList, 2656 XtNumber(optionDescList), 2657 &argc, argv, 2658 fallback_resources, 2659 sessionShellWidgetClass, 2660 NULL, 0); 2661 TRACE(("created toplevel widget %p, window %#lx\n", 2662 (void *) toplevel, XtWindow(toplevel))); 2663 2664 XtGetApplicationResources(toplevel, (XtPointer) &resource, 2665 application_resources, 2666 XtNumber(application_resources), NULL, 0); 2667 TRACE_XRES(); 2668#ifdef HAVE_LIB_XCURSOR 2669 if (!strcmp(resource.cursorTheme, "none")) { 2670 TRACE(("startup with no cursorTheme\n")); 2671 init_colored_cursor(XtDisplay(toplevel)); 2672 } else { 2673 const char *theme = resource.cursorTheme; 2674 if (IsEmpty(theme)) 2675 theme = "default"; 2676 TRACE(("startup with \"%s\" cursorTheme\n", theme)); 2677 xtermSetenv("XCURSOR_THEME", theme); 2678 } 2679#endif 2680#if USE_DOUBLE_BUFFER 2681 if (resource.buffered_fps <= 0) 2682 resource.buffered_fps = DEF_BUFFER_RATE; 2683 if (resource.buffered_fps > 100) 2684 resource.buffered_fps = 100; 2685#endif 2686#if OPT_MAXIMIZE 2687 resource.fullscreen = extendedBoolean(resource.fullscreen_s, 2688 tblFullscreen, 2689 esLAST); 2690#endif 2691 VTInitTranslations(); 2692#if OPT_PTY_HANDSHAKE 2693 resource.wait_for_map0 = resource.wait_for_map; 2694#endif 2695 2696#if defined(HAVE_POSIX_SAVED_IDS) && !defined(USE_UTMP_SETGID) 2697#if !defined(DISABLE_SETUID) || !defined(DISABLE_SETGID) 2698#if !defined(DISABLE_SETUID) 2699 setEffectiveUser(save_euid); 2700#endif 2701#if !defined(DISABLE_SETGID) 2702 setEffectiveGroup(save_egid); 2703#endif 2704 TRACE_IDS; 2705#endif 2706#endif 2707 } 2708 2709 /* 2710 * ICCCM delete_window. 2711 */ 2712 XtAppAddActions(app_con, actionProcs, XtNumber(actionProcs)); 2713 2714 /* 2715 * fill in terminal modes 2716 */ 2717 if (resource.tty_modes) { 2718 int n = parse_tty_modes(resource.tty_modes); 2719 if (n < 0) { 2720 xtermWarning("bad tty modes \"%s\"\n", resource.tty_modes); 2721 } else if (n > 0) { 2722 override_tty_modes = True; 2723 } 2724 } 2725 initZIconBeep(); 2726 hold_screen = resource.hold_screen ? 1 : 0; 2727 if (resource.icon_geometry != NULL) { 2728 int scr, junk; 2729 int ix, iy; 2730 Arg args[2]; 2731 2732 for (scr = 0; /* yyuucchh */ 2733 XtScreen(toplevel) != ScreenOfDisplay(XtDisplay(toplevel), scr); 2734 scr++) ; 2735 2736 args[0].name = XtNiconX; 2737 args[1].name = XtNiconY; 2738 XGeometry(XtDisplay(toplevel), scr, resource.icon_geometry, "", 2739 0, 0, 0, 0, 0, &ix, &iy, &junk, &junk); 2740 args[0].value = (XtArgVal) ix; 2741 args[1].value = (XtArgVal) iy; 2742 XtSetValues(toplevel, args, 2); 2743 } 2744 2745 XtSetValues(toplevel, ourTopLevelShellArgs, 2746 number_ourTopLevelShellArgs); 2747 2748#if OPT_WIDE_CHARS 2749 /* seems as good a place as any */ 2750 init_classtab(); 2751#endif 2752 2753 /* Parse the rest of the command line */ 2754 TRACE_ARGV("After XtOpenApplication", argv); 2755 for (argc--, argv++; argc > 0; argc--, argv++) { 2756 if (!isOption(*argv)) { 2757#ifdef VMS 2758 Syntax(*argv); 2759#else 2760 if (argc > 1) 2761 Syntax(*argv); 2762 continue; 2763#endif 2764 } 2765 2766 TRACE(("parsing %s\n", argv[0])); 2767 switch (argv[0][1]) { 2768 case 'C': 2769#if defined(TIOCCONS) || defined(SRIOCSREDIR) 2770#ifndef __sgi 2771 { 2772 struct stat sbuf; 2773 2774 /* Must be owner and have read/write permission. 2775 xdm cooperates to give the console the right user. */ 2776 if (!stat("/dev/console", &sbuf) && 2777 (sbuf.st_uid == save_ruid) && 2778 !access("/dev/console", R_OK | W_OK)) { 2779 Console = True; 2780 } else 2781 Console = False; 2782 } 2783#else /* __sgi */ 2784 Console = True; 2785#endif /* __sgi */ 2786#endif /* TIOCCONS */ 2787 continue; 2788 case 'S': 2789 if (!ParseSccn(*argv + 2)) 2790 Syntax(*argv); 2791 continue; 2792#ifdef DEBUG 2793 case 'D': 2794 debug = True; 2795 continue; 2796#endif /* DEBUG */ 2797 case 'b': 2798 if (strcmp(argv[0], "-baudrate")) 2799 Syntax(*argv); 2800 argc--; 2801 argv++; 2802 continue; 2803 case 'c': 2804 if (strcmp(argv[0], "-class")) 2805 Syntax(*argv); 2806 argc--; 2807 argv++; 2808 continue; 2809 case 'e': 2810 if (strcmp(argv[0], "-e")) 2811 Syntax(*argv); 2812 command_to_exec = (argv + 1); 2813 break; 2814 case 'i': 2815 if (strcmp(argv[0], "-into")) 2816 Syntax(*argv); 2817 argc--; 2818 argv++; 2819 continue; 2820 2821 default: 2822 Syntax(*argv); 2823 } 2824 break; 2825 } 2826 2827 SetupMenus(toplevel, &form_top, &menu_top, &menu_high); 2828 2829 term = (XtermWidget) XtVaCreateManagedWidget("vt100", xtermWidgetClass, 2830 form_top, 2831#if OPT_TOOLBAR 2832 XtNmenuBar, menu_top, 2833 XtNresizable, True, 2834 XtNfromVert, menu_top, 2835 XtNleft, XawChainLeft, 2836 XtNright, XawChainRight, 2837 XtNtop, XawChainTop, 2838 XtNbottom, XawChainBottom, 2839 XtNmenuHeight, menu_high, 2840#endif 2841 (XtPointer) 0); 2842 TRACE(("created vt100 widget %p, window %#lx\n", 2843 (void *) term, XtWindow(term))); 2844 decode_keyboard_type(term, &resource); 2845 2846 screen = TScreenOf(term); 2847 screen->inhibit = 0; 2848 2849#ifdef ALLOWLOGGING 2850 if (term->misc.logInhibit) 2851 screen->inhibit |= I_LOG; 2852#endif 2853 if (term->misc.signalInhibit) 2854 screen->inhibit |= I_SIGNAL; 2855#if OPT_TEK4014 2856 if (term->misc.tekInhibit) 2857 screen->inhibit |= I_TEK; 2858#endif 2859 2860 /* 2861 * We might start by showing the tek4014 window. 2862 */ 2863#if OPT_TEK4014 2864 if (screen->inhibit & I_TEK) 2865 TEK4014_ACTIVE(term) = False; 2866 2867 if (TEK4014_ACTIVE(term) && !TekInit()) 2868 SysError(ERROR_INIT); 2869#endif 2870 2871 /* 2872 * Start the toolbar at this point, after the first window has been setup. 2873 */ 2874#if OPT_TOOLBAR 2875 ShowToolbar(resource.toolBar); 2876#endif 2877 2878 xtermOpenSession(); 2879 2880 /* 2881 * Set title and icon name if not specified 2882 */ 2883 if (command_to_exec) { 2884 Arg args[2]; 2885 2886 if (!resource.title) 2887 resource.title = x_basename(command_to_exec[0]); 2888 if (!resource.icon_name) 2889 resource.icon_name = resource.title; 2890 2891 XtSetArg(args[0], XtNtitle, resource.title); 2892 XtSetArg(args[1], XtNiconName, resource.icon_name); 2893 2894 TRACE(("setting:\n\ttitle \"%s\"\n\ticon \"%s\"\n\thint \"%s\"\n\tbased on command \"%s\"\n", 2895 resource.title, 2896 resource.icon_name, 2897 NonNull(resource.icon_hint), 2898 *command_to_exec)); 2899 2900 XtSetValues(toplevel, args, 2); 2901 } else if (IsEmpty(resource.title) && strcmp(my_class, DEFCLASS)) { 2902 Arg args[2]; 2903 int n; 2904 2905 resource.title = x_strdup(my_class); 2906 for (n = 0; resource.title[n]; ++n) { 2907 if (isalpha(CharOf(resource.title[n]))) 2908 resource.title[n] = (char) tolower(resource.title[n]); 2909 } 2910 TRACE(("setting:\n\ttitle \"%s\"\n", resource.title)); 2911 XtSetArg(args[0], XtNtitle, resource.title); 2912 XtSetValues(toplevel, args, 1); 2913 } 2914#if OPT_LUIT_PROG 2915 if (term->misc.callfilter) { 2916 char **split_filter = x_splitargs(term->misc.localefilter); 2917 unsigned count_split = x_countargv(split_filter); 2918 unsigned count_exec = x_countargv(command_to_exec); 2919 unsigned count_using = (unsigned) (term->misc.use_encoding ? 2 : 0); 2920 2921 command_to_exec_with_luit = TypeCallocN(char *, 2922 (count_split 2923 + count_exec 2924 + count_using 2925 + 8)); 2926 if (command_to_exec_with_luit == NULL) 2927 SysError(ERROR_LUMALLOC); 2928 2929 x_appendargv(command_to_exec_with_luit, split_filter); 2930 if (count_using) { 2931 char *encoding_opt[4]; 2932 encoding_opt[0] = x_strdup("-encoding"); 2933 encoding_opt[1] = term->misc.locale_str; 2934 encoding_opt[2] = 0; 2935 x_appendargv(command_to_exec_with_luit, encoding_opt); 2936 } 2937 command_length_with_luit = x_countargv(command_to_exec_with_luit); 2938 if (count_exec) { 2939 static char *fixup_shell[] = 2940 {(char *) "sh", (char *) "-c", 0}; 2941 char *delimiter[2]; 2942 delimiter[0] = x_strdup("--"); 2943 delimiter[1] = 0; 2944 x_appendargv(command_to_exec_with_luit, delimiter); 2945 if (complex_command(command_to_exec)) { 2946 x_appendargv(command_to_exec_with_luit, fixup_shell); 2947 } 2948 x_appendargv(command_to_exec_with_luit, command_to_exec); 2949 } 2950 TRACE_ARGV("luit command", command_to_exec_with_luit); 2951 xtermSetenv("XTERM_FILTER", *command_to_exec_with_luit); 2952 } 2953#endif 2954 2955 if_DEBUG({ 2956 /* Set up stderr properly. Opening this log file cannot be 2957 done securely by a privileged xterm process (although we try), 2958 so the debug feature is disabled by default. */ 2959 char dbglogfile[TIMESTAMP_LEN + 20]; 2960 int i = -1; 2961 timestamp_filename(dbglogfile, "xterm.debug.log."); 2962 if (creat_as(save_ruid, save_rgid, False, dbglogfile, 0600) > 0) { 2963 i = open(dbglogfile, O_WRONLY | O_TRUNC); 2964 } 2965 if (i >= 0) { 2966 dup2(i, 2); 2967 2968 /* mark this file as close on exec */ 2969 (void) fcntl(i, F_SETFD, 1); 2970 } 2971 }); 2972 2973 spawnXTerm(term, line_speed); 2974 2975#ifndef VMS 2976 /* Child process is out there, let's catch its termination */ 2977 2978#ifdef USE_POSIX_SIGNALS 2979 (void) posix_signal(SIGCHLD, reapchild); 2980#else 2981 (void) signal(SIGCHLD, reapchild); 2982#endif 2983 /* Realize procs have now been executed */ 2984 2985 if (am_slave >= 0) { /* Write window id so master end can read and use */ 2986 char buf[80]; 2987 2988 buf[0] = '\0'; 2989 sprintf(buf, "%lx\n", XtWindow(SHELL_OF(CURRENT_EMU()))); 2990 IGNORE_RC(write(screen->respond, buf, strlen(buf))); 2991 } 2992#ifdef AIXV3 2993#if (OSMAJORVERSION < 4) 2994 /* In AIXV3, xterms started from /dev/console have CLOCAL set. 2995 * This means we need to clear CLOCAL so that SIGHUP gets sent 2996 * to the slave-pty process when xterm exits. 2997 */ 2998 2999 { 3000 TERMIO_STRUCT tio; 3001 3002 if (ttyGetAttr(screen->respond, &tio) == -1) 3003 SysError(ERROR_TIOCGETP); 3004 3005 tio.c_cflag &= ~(CLOCAL); 3006 3007 if (ttySetAttr(screen->respond, &tio) == -1) 3008 SysError(ERROR_TIOCSETP); 3009 } 3010#endif 3011#endif 3012#if defined(USE_ANY_SYSV_TERMIO) || defined(__MVS__) || defined(__minix) 3013 if (0 > (mode = fcntl(screen->respond, F_GETFL, 0))) 3014 SysError(ERROR_F_GETFL); 3015#ifdef O_NDELAY 3016 mode |= O_NDELAY; 3017#else 3018 mode |= O_NONBLOCK; 3019#endif /* O_NDELAY */ 3020 if (fcntl(screen->respond, F_SETFL, mode)) 3021 SysError(ERROR_F_SETFL); 3022#else /* !USE_ANY_SYSV_TERMIO */ 3023 mode = 1; 3024 if (ioctl(screen->respond, FIONBIO, (char *) &mode) == -1) 3025 SysError(ERROR_FIONBIO); 3026#endif /* USE_ANY_SYSV_TERMIO, etc */ 3027 3028 /* The erase character is used to delete the current completion */ 3029#if OPT_DABBREV 3030#ifdef TERMIO_STRUCT 3031 screen->dabbrev_erase_char = d_tio.c_cc[VERASE]; 3032#else 3033 screen->dabbrev_erase_char = d_sg.sg_erase; 3034#endif 3035 TRACE(("set dabbrev erase_char %#x\n", screen->dabbrev_erase_char)); 3036#endif 3037 3038 FD_ZERO(&pty_mask); 3039 FD_ZERO(&X_mask); 3040 FD_ZERO(&Select_mask); 3041 FD_SET(screen->respond, &pty_mask); 3042 FD_SET(ConnectionNumber(screen->display), &X_mask); 3043 FD_SET(screen->respond, &Select_mask); 3044 FD_SET(ConnectionNumber(screen->display), &Select_mask); 3045 max_plus1 = ((screen->respond < ConnectionNumber(screen->display)) 3046 ? (1 + ConnectionNumber(screen->display)) 3047 : (1 + screen->respond)); 3048 3049#endif /* !VMS */ 3050 if_DEBUG({ 3051 TRACE(("debugging on pid %d\n", (int) getpid())); 3052 }); 3053 XSetErrorHandler(xerror); 3054 XSetIOErrorHandler(xioerror); 3055#if OPT_SESSION_MGT 3056 IceSetIOErrorHandler(ice_error); 3057#endif 3058 3059 initPtyData(&VTbuffer); 3060#ifdef ALLOWLOGGING 3061 if (term->misc.log_on) { 3062 StartLog(term); 3063 } 3064#endif 3065 3066 xtermEmbedWindow(winToEmbedInto); 3067 3068 TRACE(("checking reverseVideo before rv %s fg %s, bg %s\n", 3069 term->misc.re_verse0 ? "reverse" : "normal", 3070 NonNull(TScreenOf(term)->Tcolors[TEXT_FG].resource), 3071 NonNull(TScreenOf(term)->Tcolors[TEXT_BG].resource))); 3072 3073 if (term->misc.re_verse0) { 3074 if (isDefaultForeground(TScreenOf(term)->Tcolors[TEXT_FG].resource) 3075 && isDefaultBackground(TScreenOf(term)->Tcolors[TEXT_BG].resource)) { 3076 TScreenOf(term)->Tcolors[TEXT_FG].resource = x_strdup(XtDefaultBackground); 3077 TScreenOf(term)->Tcolors[TEXT_BG].resource = x_strdup(XtDefaultForeground); 3078 } else { 3079 ReverseVideo(term); 3080 } 3081 term->misc.re_verse = True; 3082 update_reversevideo(); 3083 TRACE(("updated reverseVideo after rv %s fg %s, bg %s\n", 3084 term->misc.re_verse ? "reverse" : "normal", 3085 NonNull(TScreenOf(term)->Tcolors[TEXT_FG].resource), 3086 NonNull(TScreenOf(term)->Tcolors[TEXT_BG].resource))); 3087 } 3088#if OPT_MAXIMIZE 3089 if (resource.maximized) 3090 RequestMaximize(term, True); 3091#endif 3092 for (;;) { 3093#if OPT_TEK4014 3094 if (TEK4014_ACTIVE(term)) 3095 TekRun(); 3096 else 3097#endif 3098 VTRun(term); 3099 } 3100} 3101 3102#if defined(__osf__) || (defined(__linux__) && !defined(USE_USG_PTYS)) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) 3103#define USE_OPENPTY 1 3104static int opened_tty = -1; 3105#endif 3106 3107/* 3108 * This function opens up a pty master and stuffs its value into pty. 3109 * 3110 * If it finds one, it returns a value of 0. If it does not find one, 3111 * it returns a value of !0. This routine is designed to be re-entrant, 3112 * so that if a pty master is found and later, we find that the slave 3113 * has problems, we can re-enter this function and get another one. 3114 */ 3115static int 3116get_pty(int *pty, char *from GCC_UNUSED) 3117{ 3118 int result = 1; 3119 3120#if defined(USE_OPENPTY) 3121 result = openpty(pty, &opened_tty, ttydev, NULL, NULL); 3122 if (opened_tty >= 0) { 3123 close(opened_tty); 3124 opened_tty = -1; 3125 } 3126#elif defined(HAVE_POSIX_OPENPT) && defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT_PTY_ISATTY) 3127 if ((*pty = posix_openpt(O_RDWR)) >= 0) { 3128 char *name = ptsname(*pty); 3129 if (name != 0) { 3130 strcpy(ttydev, name); 3131 result = 0; 3132 } 3133 } 3134#ifdef USE_PTY_SEARCH 3135 if (result) { 3136 result = pty_search(pty); 3137 } 3138#endif 3139#elif defined(PUCC_PTYD) 3140 result = ((*pty = openrpty(ttydev, ptydev, 3141 (resource.utmpInhibit ? OPTY_NOP : OPTY_LOGIN), 3142 save_ruid, from)) < 0); 3143#elif defined(__QNXNTO__) 3144 result = pty_search(pty); 3145#else 3146#if defined(USE_USG_PTYS) || defined(__CYGWIN__) 3147#if defined(__MVS__) 3148 result = pty_search(pty); 3149#else 3150 result = ((*pty = open("/dev/ptmx", O_RDWR)) < 0); 3151#endif 3152#if defined(SVR4) || defined(__SCO__) 3153 if (!result) 3154 strcpy(ttydev, ptsname(*pty)); 3155#endif 3156 3157#elif defined(AIXV3) 3158 3159 if ((*pty = open("/dev/ptc", O_RDWR)) >= 0) { 3160 strcpy(ttydev, ttyname(*pty)); 3161 result = 0; 3162 } 3163#elif defined(__convex__) 3164 3165 char *pty_name; 3166 extern char *getpty(void); 3167 3168 while ((pty_name = getpty()) != NULL) { 3169 if ((*pty = open(pty_name, O_RDWR)) >= 0) { 3170 strcpy(ptydev, pty_name); 3171 strcpy(ttydev, pty_name); 3172 *x_basename(ttydev) = 't'; 3173 result = 0; 3174 break; 3175 } 3176 } 3177 3178#elif defined(sequent) 3179 3180 result = ((*pty = getpseudotty(&ttydev, &ptydev)) < 0); 3181 3182#elif defined(__sgi) && (OSMAJORVERSION >= 4) 3183 3184 char *tty_name; 3185 3186 tty_name = _getpty(pty, O_RDWR, 0622, 0); 3187 if (tty_name != 0) { 3188 strcpy(ttydev, tty_name); 3189 result = 0; 3190 } 3191#elif (defined(__sgi) && (OSMAJORVERSION < 4)) || (defined(umips) && defined (SYSTYPE_SYSV)) 3192 3193 struct stat fstat_buf; 3194 3195 *pty = open("/dev/ptc", O_RDWR); 3196 if (*pty >= 0 && (fstat(*pty, &fstat_buf)) >= 0) { 3197 result = 0; 3198 sprintf(ttydev, "/dev/ttyq%d", minor(fstat_buf.st_rdev)); 3199 } 3200#elif defined(__hpux) 3201 3202 /* 3203 * Use the clone device if it works, otherwise use pty_search logic. 3204 */ 3205 if ((*pty = open("/dev/ptym/clone", O_RDWR)) >= 0) { 3206 char *name = ptsname(*pty); 3207 if (name != 0) { 3208 strcpy(ttydev, name); 3209 result = 0; 3210 } else { /* permissions, or other unexpected problem */ 3211 close(*pty); 3212 *pty = -1; 3213 result = pty_search(pty); 3214 } 3215 } else { 3216 result = pty_search(pty); 3217 } 3218 3219#else 3220 3221 result = pty_search(pty); 3222 3223#endif 3224#endif 3225 3226 TRACE(("get_pty(ttydev=%s, ptydev=%s) %s fd=%d\n", 3227 ttydev != 0 ? ttydev : "?", 3228 ptydev != 0 ? ptydev : "?", 3229 result ? "FAIL" : "OK", 3230 pty != 0 ? *pty : -1)); 3231 return result; 3232} 3233 3234static void 3235set_pty_permissions(uid_t uid, unsigned gid, unsigned mode) 3236{ 3237#ifdef USE_TTY_GROUP 3238 struct group *ttygrp; 3239 3240 if ((ttygrp = getgrnam(TTY_GROUP_NAME)) != 0) { 3241 gid = (unsigned) ttygrp->gr_gid; 3242 mode &= 0660U; 3243 } 3244 endgrent(); 3245#endif /* USE_TTY_GROUP */ 3246 3247 TRACE_IDS; 3248 set_owner(ttydev, (unsigned) uid, gid, mode); 3249} 3250 3251#ifdef get_pty /* USE_UTMP_SETGID */ 3252#undef get_pty 3253/* 3254 * Call the real get_pty() before relinquishing root-setuid, caching the 3255 * result. 3256 */ 3257static int 3258get_pty(int *pty, char *from) 3259{ 3260 static int m_pty = -1; 3261 int result = -1; 3262 3263 if (pty == NULL) { 3264 result = really_get_pty(&m_pty, from); 3265 3266 seteuid(0); 3267 set_pty_permissions(save_ruid, save_rgid, 0600U); 3268 seteuid(save_ruid); 3269 TRACE_IDS; 3270 3271 } else if (m_pty != -1) { 3272 *pty = m_pty; 3273 result = 0; 3274 } else { 3275 result = -1; 3276 } 3277 TRACE(("get_pty(ttydev=%s, ptydev=%s) %s fd=%d (utmp setgid)\n", 3278 ttydev != 0 ? ttydev : "?", 3279 ptydev != 0 ? ptydev : "?", 3280 result ? "FAIL" : "OK", 3281 pty != 0 ? *pty : -1)); 3282#ifdef USE_OPENPTY 3283 if (opened_tty >= 0) { 3284 close(opened_tty); 3285 opened_tty = -1; 3286 } 3287#endif 3288 return result; 3289} 3290#endif 3291 3292/* 3293 * Called from get_pty to iterate over likely pseudo terminals 3294 * we might allocate. Used on those systems that do not have 3295 * a functional interface for allocating a pty. 3296 * Returns 0 if found a pty, 1 if fails. 3297 */ 3298#ifdef USE_PTY_SEARCH 3299static int 3300pty_search(int *pty) 3301{ 3302 static int devindex = 0, letter = 0; 3303 3304#if defined(CRAY) || defined(__MVS__) 3305 while (devindex < MAXPTTYS) { 3306 sprintf(ttydev, TTYFORMAT, devindex); 3307 sprintf(ptydev, PTYFORMAT, devindex); 3308 devindex++; 3309 3310 TRACE(("pty_search(ttydev=%s, ptydev=%s)\n", ttydev, ptydev)); 3311 if ((*pty = open(ptydev, O_RDWR)) >= 0) { 3312 return 0; 3313 } 3314 } 3315#else /* CRAY || __MVS__ */ 3316 while (PTYCHAR1[letter]) { 3317 ttydev[strlen(ttydev) - 2] = 3318 ptydev[strlen(ptydev) - 2] = PTYCHAR1[letter]; 3319 3320 while (PTYCHAR2[devindex]) { 3321 ttydev[strlen(ttydev) - 1] = 3322 ptydev[strlen(ptydev) - 1] = PTYCHAR2[devindex]; 3323 devindex++; 3324 3325 TRACE(("pty_search(ttydev=%s, ptydev=%s)\n", ttydev, ptydev)); 3326 if ((*pty = open(ptydev, O_RDWR)) >= 0) { 3327#ifdef sun 3328 /* Need to check the process group of the pty. 3329 * If it exists, then the slave pty is in use, 3330 * and we need to get another one. 3331 */ 3332 int pgrp_rtn; 3333 if (ioctl(*pty, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) { 3334 close(*pty); 3335 continue; 3336 } 3337#endif /* sun */ 3338 return 0; 3339 } 3340 } 3341 devindex = 0; 3342 letter++; 3343 } 3344#endif /* CRAY else */ 3345 /* 3346 * We were unable to allocate a pty master! Return an error 3347 * condition and let our caller terminate cleanly. 3348 */ 3349 return 1; 3350} 3351#endif /* USE_PTY_SEARCH */ 3352 3353/* 3354 * The only difference in /etc/termcap between 4014 and 4015 is that 3355 * the latter has support for switching character sets. We support the 3356 * 4015 protocol, but ignore the character switches. Therefore, we 3357 * choose 4014 over 4015. 3358 * 3359 * Features of the 4014 over the 4012: larger (19") screen, 12-bit 3360 * graphics addressing (compatible with 4012 10-bit addressing), 3361 * special point plot mode, incremental plot mode (not implemented in 3362 * later Tektronix terminals), and 4 character sizes. 3363 * All of these are supported by xterm. 3364 */ 3365 3366#if OPT_TEK4014 3367static const char *const tekterm[] = 3368{ 3369 "tek4014", 3370 "tek4015", /* 4014 with APL character set support */ 3371 "tek4012", /* 4010 with lower case */ 3372 "tek4013", /* 4012 with APL character set support */ 3373 "tek4010", /* small screen, upper-case only */ 3374 "dumb", 3375 0 3376}; 3377#endif 3378 3379/* The VT102 is a VT100 with the Advanced Video Option included standard. 3380 * It also adds Escape sequences for insert/delete character/line. 3381 * The VT220 adds 8-bit character sets, selective erase. 3382 * The VT320 adds a 25th status line, terminal state interrogation. 3383 * The VT420 has up to 48 lines on the screen. 3384 */ 3385 3386static const char *const vtterm[] = 3387{ 3388#ifdef USE_X11TERM 3389 "x11term", /* for people who want special term name */ 3390#endif 3391 DFT_TERMTYPE, /* for people who want special term name */ 3392 "xterm", /* the preferred name, should be fastest */ 3393 "vt102", 3394 "vt100", 3395 "ansi", 3396 "dumb", 3397 0 3398}; 3399 3400/* ARGSUSED */ 3401static void 3402hungtty(int i GCC_UNUSED) 3403{ 3404 DEBUG_MSG("handle:hungtty\n"); 3405 siglongjmp(env, 1); 3406} 3407 3408#if OPT_PTY_HANDSHAKE 3409#define NO_FDS {-1, -1} 3410 3411static int cp_pipe[2] = NO_FDS; /* this pipe is used for child to parent transfer */ 3412static int pc_pipe[2] = NO_FDS; /* this pipe is used for parent to child transfer */ 3413 3414typedef enum { /* c == child, p == parent */ 3415 PTY_BAD, /* c->p: can't open pty slave for some reason */ 3416 PTY_FATALERROR, /* c->p: we had a fatal error with the pty */ 3417 PTY_GOOD, /* c->p: we have a good pty, let's go on */ 3418 PTY_NEW, /* p->c: here is a new pty slave, try this */ 3419 PTY_NOMORE, /* p->c; no more pty's, terminate */ 3420 UTMP_ADDED, /* c->p: utmp entry has been added */ 3421 UTMP_TTYSLOT, /* c->p: here is my ttyslot */ 3422 PTY_EXEC /* p->c: window has been mapped the first time */ 3423} status_t; 3424 3425#define HANDSHAKE_LEN 1024 3426 3427typedef struct { 3428 status_t status; 3429 int error; 3430 int fatal_error; 3431 int tty_slot; 3432 int rows; 3433 int cols; 3434 char buffer[HANDSHAKE_LEN]; 3435} handshake_t; 3436 3437/* the buffer is large enough that we can always have a trailing null */ 3438#define copy_handshake(dst, src) \ 3439 strncpy(dst.buffer, src, (size_t)HANDSHAKE_LEN - 1)[HANDSHAKE_LEN - 1] = '\0' 3440 3441#if OPT_TRACE 3442static void 3443trace_handshake(const char *tag, handshake_t * data) 3444{ 3445 const char *status = "?"; 3446 switch (data->status) { 3447 case PTY_BAD: 3448 status = "PTY_BAD"; 3449 break; 3450 case PTY_FATALERROR: 3451 status = "PTY_FATALERROR"; 3452 break; 3453 case PTY_GOOD: 3454 status = "PTY_GOOD"; 3455 break; 3456 case PTY_NEW: 3457 status = "PTY_NEW"; 3458 break; 3459 case PTY_NOMORE: 3460 status = "PTY_NOMORE"; 3461 break; 3462 case UTMP_ADDED: 3463 status = "UTMP_ADDED"; 3464 break; 3465 case UTMP_TTYSLOT: 3466 status = "UTMP_TTYSLOT"; 3467 break; 3468 case PTY_EXEC: 3469 status = "PTY_EXEC"; 3470 break; 3471 } 3472 TRACE(("handshake %s %s errno=%d, error=%d device \"%s\"\n", 3473 tag, 3474 status, 3475 data->error, 3476 data->fatal_error, 3477 data->buffer)); 3478} 3479#define TRACE_HANDSHAKE(tag, data) trace_handshake(tag, data) 3480#else 3481#define TRACE_HANDSHAKE(tag, data) /* nothing */ 3482#endif 3483 3484/* HsSysError() 3485 * 3486 * This routine does the equivalent of a SysError but it handshakes 3487 * over the errno and error exit to the master process so that it can 3488 * display our error message and exit with our exit code so that the 3489 * user can see it. 3490 */ 3491 3492static void 3493HsSysError(int error) 3494{ 3495 handshake_t handshake; 3496 3497 memset(&handshake, 0, sizeof(handshake)); 3498 handshake.status = PTY_FATALERROR; 3499 handshake.error = errno; 3500 handshake.fatal_error = error; 3501 copy_handshake(handshake, ttydev); 3502 3503 if (resource.ptyHandshake && (cp_pipe[1] >= 0)) { 3504 TRACE(("HsSysError errno=%d, error=%d device \"%s\"\n", 3505 handshake.error, 3506 handshake.fatal_error, 3507 handshake.buffer)); 3508 TRACE_HANDSHAKE("writing", &handshake); 3509 IGNORE_RC(write(cp_pipe[1], 3510 (const char *) &handshake, 3511 sizeof(handshake))); 3512 } else { 3513 xtermWarning("fatal pty error errno=%d, error=%d device \"%s\"\n", 3514 handshake.error, 3515 handshake.fatal_error, 3516 handshake.buffer); 3517 fprintf(stderr, "%s\n", SysErrorMsg(handshake.error)); 3518 fprintf(stderr, "Reason: %s\n", SysReasonMsg(handshake.fatal_error)); 3519 } 3520 exit(error); 3521} 3522 3523void 3524first_map_occurred(void) 3525{ 3526 if (resource.wait_for_map) { 3527 if (pc_pipe[1] >= 0) { 3528 handshake_t handshake; 3529 TScreen *screen = TScreenOf(term); 3530 3531 memset(&handshake, 0, sizeof(handshake)); 3532 handshake.status = PTY_EXEC; 3533 handshake.rows = screen->max_row; 3534 handshake.cols = screen->max_col; 3535 3536 TRACE(("first_map_occurred: %dx%d\n", MaxRows(screen), MaxCols(screen))); 3537 TRACE_HANDSHAKE("writing", &handshake); 3538 IGNORE_RC(write(pc_pipe[1], 3539 (const char *) &handshake, 3540 sizeof(handshake))); 3541 close(cp_pipe[0]); 3542 close(pc_pipe[1]); 3543 } 3544 resource.wait_for_map = False; 3545 } 3546} 3547#else 3548/* 3549 * temporary hack to get xterm working on att ptys 3550 */ 3551static void 3552HsSysError(int error) 3553{ 3554 xtermWarning("fatal pty error %d (errno=%d) on tty %s\n", 3555 error, errno, ttydev); 3556 exit(error); 3557} 3558#endif /* OPT_PTY_HANDSHAKE else !OPT_PTY_HANDSHAKE */ 3559 3560#ifndef VMS 3561static void 3562set_owner(char *device, unsigned uid, unsigned gid, unsigned mode) 3563{ 3564 int why; 3565 3566 TRACE_IDS; 3567 TRACE(("set_owner(%s, uid=%d, gid=%d, mode=%#o\n", 3568 device, (int) uid, (int) gid, (unsigned) mode)); 3569 3570 if (chown(device, (uid_t) uid, (gid_t) gid) < 0) { 3571 why = errno; 3572 if (why != ENOENT 3573 && save_ruid == 0) { 3574 xtermPerror("Cannot chown %s to %ld,%ld", 3575 device, (long) uid, (long) gid); 3576 } 3577 TRACE(("...chown failed: %s\n", strerror(why))); 3578 } else if (chmod(device, (mode_t) mode) < 0) { 3579 why = errno; 3580 if (why != ENOENT) { 3581 struct stat sb; 3582 if (stat(device, &sb) < 0) { 3583 xtermPerror("Cannot chmod %s to %03o", 3584 device, (unsigned) mode); 3585 } else if (mode != (sb.st_mode & 0777U)) { 3586 xtermPerror("Cannot chmod %s to %03lo currently %03lo", 3587 device, 3588 (unsigned long) mode, 3589 (unsigned long) (sb.st_mode & 0777U)); 3590 TRACE(("...stat uid=%d, gid=%d, mode=%#o\n", 3591 (int) sb.st_uid, (int) sb.st_gid, (unsigned) sb.st_mode)); 3592 } 3593 } 3594 TRACE(("...chmod failed: %s\n", strerror(why))); 3595 } 3596} 3597 3598/* 3599 * utmp data may not be null-terminated; even if it is, there may be garbage 3600 * after the null. This fills the unused part of the result with nulls. 3601 */ 3602static void 3603copy_filled(char *target, const char *source, size_t len) 3604{ 3605 size_t used = 0; 3606 while (used < len) { 3607 if ((target[used] = source[used]) == 0) 3608 break; 3609 ++used; 3610 } 3611 while (used < len) { 3612 target[used++] = '\0'; 3613 } 3614} 3615 3616#if defined(HAVE_UTMP) && defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 3617/* 3618 * getutid() only looks at ut_type and ut_id. 3619 * But we'll also check ut_line in find_utmp(). 3620 */ 3621static void 3622init_utmp(int type, struct UTMP_STR *tofind) 3623{ 3624 memset(tofind, 0, sizeof(*tofind)); 3625 tofind->ut_type = (short) type; 3626 copy_filled(tofind->ut_id, my_utmp_id(ttydev), sizeof(tofind->ut_id)); 3627 copy_filled(tofind->ut_line, my_pty_name(ttydev), sizeof(tofind->ut_line)); 3628} 3629 3630/* 3631 * We could use getutline() if we didn't support old systems. 3632 */ 3633static struct UTMP_STR * 3634find_utmp(struct UTMP_STR *tofind) 3635{ 3636 struct UTMP_STR *result; 3637 struct UTMP_STR limited; 3638 struct UTMP_STR working; 3639 3640 for (;;) { 3641 memset(&working, 0, sizeof(working)); 3642 working.ut_type = tofind->ut_type; 3643 copy_filled(working.ut_id, tofind->ut_id, sizeof(tofind->ut_id)); 3644#if defined(__digital__) && defined(__unix__) && (defined(OSMAJORVERSION) && OSMAJORVERSION < 5) 3645 working.ut_type = 0; 3646#endif 3647 if ((result = call_getutid(&working)) == 0) 3648 break; 3649 copy_filled(limited.ut_line, result->ut_line, sizeof(result->ut_line)); 3650 if (!memcmp(limited.ut_line, tofind->ut_line, sizeof(limited.ut_line))) 3651 break; 3652 /* 3653 * Solaris, IRIX64 and HPUX manpages say to fill the static area 3654 * pointed to by the return-value to zeros if searching for multiple 3655 * occurrences. Otherwise it will continue to return the same value. 3656 */ 3657 memset(result, 0, sizeof(*result)); 3658 } 3659 return result; 3660} 3661#endif /* HAVE_UTMP... */ 3662 3663#define close_fd(fd) close(fd), fd = -1 3664 3665#if defined(TIOCNOTTY) && (!defined(__GLIBC__) || (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) 3666#define USE_NO_DEV_TTY 1 3667#else 3668#define USE_NO_DEV_TTY 0 3669#endif 3670 3671static int 3672same_leaf(char *a, char *b) 3673{ 3674 char *p = x_basename(a); 3675 char *q = x_basename(b); 3676 return !strcmp(p, q); 3677} 3678 3679/* 3680 * "good enough" (inode wouldn't port to Cygwin) 3681 */ 3682static int 3683same_file(const char *a, const char *b) 3684{ 3685 struct stat asb; 3686 struct stat bsb; 3687 int result = 0; 3688 3689 if ((stat(a, &asb) == 0) 3690 && (stat(b, &bsb) == 0) 3691 && ((asb.st_mode & S_IFMT) == S_IFREG) 3692 && ((bsb.st_mode & S_IFMT) == S_IFREG) 3693 && (asb.st_mtime == bsb.st_mtime) 3694 && (asb.st_size == bsb.st_size)) { 3695 result = 1; 3696 } 3697 return result; 3698} 3699 3700static int 3701findValidShell(const char *haystack, const char *needle) 3702{ 3703 int result = -1; 3704 int count = -1; 3705 const char *s, *t; 3706 size_t have; 3707 size_t want = strlen(needle); 3708 3709 TRACE(("findValidShell:\n%s\n", NonNull(haystack))); 3710 3711 for (s = haystack; (s != 0) && (*s != '\0'); s = t) { 3712 ++count; 3713 if ((t = strchr(s, '\n')) == 0) { 3714 t = s + strlen(s); 3715 } 3716 have = (size_t) (t - s); 3717 3718 if ((have >= want) && (*s != '#')) { 3719 char *p = (char *) malloc(have + 1); 3720 3721 if (p != 0) { 3722 char *q; 3723 3724 memcpy(p, s, have); 3725 p[have] = '\0'; 3726 if ((q = x_strtrim(p)) != 0) { 3727 TRACE(("...test %s\n", q)); 3728 if (!strcmp(q, needle)) { 3729 result = count; 3730 } else if (same_leaf(q, (char *) needle) && 3731 same_file(q, needle)) { 3732 result = count; 3733 } 3734 free(q); 3735 } 3736 free(p); 3737 } 3738 if (result >= 0) 3739 break; 3740 } 3741 while (*t == '\n') { 3742 ++t; 3743 } 3744 } 3745 return result; 3746} 3747 3748static int 3749ourValidShell(const char *pathname) 3750{ 3751 char *trimmed = x_strtrim(resource.valid_shells); 3752 int result = findValidShell(trimmed, pathname); 3753 free(trimmed); 3754 return result; 3755} 3756 3757#if defined(HAVE_GETUSERSHELL) && defined(HAVE_ENDUSERSHELL) 3758static Boolean 3759validShell(const char *pathname) 3760{ 3761 int result = -1; 3762 3763 if (validProgram(pathname)) { 3764 char *q; 3765 int count = -1; 3766 3767 TRACE(("validShell:getusershell\n")); 3768 while ((q = getusershell()) != 0) { 3769 ++count; 3770 TRACE(("...test \"%s\"\n", q)); 3771 if (!strcmp(q, pathname)) { 3772 result = count; 3773 break; 3774 } 3775 } 3776 endusershell(); 3777 3778 if (result < 0) 3779 result = ourValidShell(pathname); 3780 } 3781 3782 TRACE(("validShell %s ->%d\n", NonNull(pathname), result)); 3783 return (result >= 0); 3784} 3785#else 3786/* 3787 * Only set $SHELL for paths found in the standard location. 3788 */ 3789static Boolean 3790validShell(const char *pathname) 3791{ 3792 int result = -1; 3793 const char *ok_shells = "/etc/shells"; 3794 char *blob; 3795 struct stat sb; 3796 size_t rc; 3797 FILE *fp; 3798 3799 if (validProgram(pathname)) { 3800 3801 TRACE(("validShell:%s\n", ok_shells)); 3802 3803 if (stat(ok_shells, &sb) == 0 3804 && (sb.st_mode & S_IFMT) == S_IFREG 3805 && ((size_t) sb.st_size > 0) 3806 && ((size_t) sb.st_size < (((size_t) ~0) - 2)) 3807 && (blob = calloc((size_t) sb.st_size + 2, sizeof(char))) != 0) { 3808 3809 if ((fp = fopen(ok_shells, "r")) != 0) { 3810 rc = fread(blob, sizeof(char), (size_t) sb.st_size, fp); 3811 fclose(fp); 3812 3813 if (rc == (size_t) sb.st_size) { 3814 blob[rc] = '\0'; 3815 result = findValidShell(blob, pathname); 3816 } 3817 } 3818 free(blob); 3819 } 3820 if (result < 0) 3821 result = ourValidShell(pathname); 3822 } 3823 TRACE(("validShell %s ->%d\n", NonNull(pathname), result)); 3824 return (result > 0); 3825} 3826#endif 3827 3828static char * 3829resetShell(char *oldPath) 3830{ 3831 char *newPath = x_strdup("/bin/sh"); 3832 char *envPath = getenv("SHELL"); 3833 free(oldPath); 3834 if (!IsEmpty(envPath)) 3835 xtermSetenv("SHELL", newPath); 3836 return newPath; 3837} 3838 3839/* 3840 * Trim unwanted environment variables: 3841 * 3842 * DESKTOP_STARTUP_ID 3843 * standards.freedesktop.org/startup-notification-spec/ 3844 * notes that this variable is used when a "reliable" mechanism is 3845 * not available; in practice it must be unset to avoid confusing 3846 * GTK applications. 3847 * 3848 * XCURSOR_PATH 3849 * We set this temporarily to work around poor design of Xcursor. Unset it 3850 * here to avoid confusion. 3851 * 3852 * Other... 3853 * These are set by other terminal emulators or non-standard libraries, and are 3854 * a nuisance if one starts xterm from a shell inside one of those. 3855 */ 3856static void 3857xtermTrimEnv(void) 3858{ 3859#define KEEP(wild,name) { 0, wild, #name } 3860#define TRIM(wild,name) { 1, wild, #name } 3861 /* *INDENT-OFF* */ 3862 static const struct { 3863 int trim; 3864 int wild; 3865 const char *name; 3866 } table[] = { 3867 TRIM(0, COLUMNS), 3868 TRIM(0, DEFAULT_COLORS), 3869 TRIM(0, DESKTOP_STARTUP_ID), 3870 TRIM(0, LINES), 3871 TRIM(0, SHLVL), /* ksh, bash */ 3872 TRIM(0, STY), /* screen */ 3873 TRIM(0, TERMCAP), 3874 TRIM(0, TMUX), 3875 TRIM(0, TMUX_PANE), 3876 TRIM(0, WCWIDTH_CJK_LEGACY), 3877 TRIM(0, WINDOW), /* screen */ 3878 TRIM(0, XCURSOR_PATH), 3879 KEEP(0, MC_XDG_OPEN), 3880 TRIM(1, COLORFGBG), 3881 TRIM(1, COLORTERM), 3882 TRIM(1, GIO_LAUNCHED_), 3883 TRIM(1, ITERM2_), 3884 TRIM(1, MC_), 3885 TRIM(1, MINTTY_), 3886 TRIM(1, PUTTY), 3887 TRIM(1, RXVT_), 3888 TRIM(1, TERM_), 3889 TRIM(1, URXVT_), 3890 TRIM(1, VTE_), 3891 TRIM(1, XTERM_), 3892 }; 3893#undef TRIM 3894 /* *INDENT-ON* */ 3895 Cardinal j, k; 3896 3897 for (j = 0; environ[j] != NULL; ++j) { 3898 char *equals = strchr(environ[j], '='); 3899 size_t dstlen = strlen(environ[j]); 3900 3901 if (equals != NULL) 3902 dstlen = (size_t) (equals - environ[j]); 3903 3904 for (k = 0; k < XtNumber(table); ++k) { 3905 size_t srclen = strlen(table[k].name); 3906 if (table[k].wild) { 3907 if (dstlen >= srclen && 3908 !strncmp(environ[j], table[k].name, srclen)) { 3909 char *my_var; 3910 if (table[k].trim && 3911 (my_var = x_strdup(environ[j])) != NULL) { 3912 my_var[dstlen] = '\0'; 3913 xtermUnsetenv(my_var); 3914 free(my_var); 3915 /* When removing an entry, check the same slot again. */ 3916 j--; 3917 } 3918 break; 3919 } 3920 } else if (dstlen == srclen && 3921 !strncmp(environ[j], table[k].name, srclen)) { 3922 if (table[k].trim) { 3923 xtermUnsetenv(table[k].name); 3924 /* When removing an entry, check the same slot again. */ 3925 j--; 3926 } 3927 break; 3928 } 3929 } 3930 } 3931} 3932 3933/* 3934 * Inits pty and tty and forks a login process. 3935 * Does not close fd Xsocket. 3936 * If slave, the pty named in passedPty is already open for use 3937 */ 3938static int 3939spawnXTerm(XtermWidget xw, unsigned line_speed) 3940{ 3941 TScreen *screen = TScreenOf(xw); 3942 Cardinal nn; 3943#if OPT_PTY_HANDSHAKE 3944 Bool got_handshake_size = False; 3945 handshake_t handshake; 3946 int done; 3947#endif 3948#if OPT_INITIAL_ERASE 3949 int initial_erase = XTERM_ERASE; 3950 Bool setInitialErase; 3951#endif 3952 int rc = 0; 3953 int ttyfd = -1; 3954 Bool ok_termcap; 3955 char *newtc; 3956 3957#ifdef TERMIO_STRUCT 3958 TERMIO_STRUCT tio; 3959#ifdef __MVS__ 3960 TERMIO_STRUCT gio; 3961#endif /* __MVS__ */ 3962#ifdef TIOCLSET 3963 unsigned lmode; 3964#endif /* TIOCLSET */ 3965#ifdef HAS_LTCHARS 3966 struct ltchars ltc; 3967#endif /* HAS_LTCHARS */ 3968#else /* !TERMIO_STRUCT */ 3969 int ldisc = 0; 3970 int discipline; 3971 unsigned lmode; 3972 struct tchars tc; 3973 struct ltchars ltc; 3974 struct sgttyb sg; 3975#ifdef sony 3976 int jmode; 3977 struct jtchars jtc; 3978#endif /* sony */ 3979#endif /* TERMIO_STRUCT */ 3980 3981 char *shell_path = 0; 3982 char *shname, *shname_minus; 3983 int i; 3984#if USE_NO_DEV_TTY 3985 int no_dev_tty = False; 3986#endif 3987 const char *const *envnew; /* new environment */ 3988 char buf[64]; 3989 char *TermName = NULL; 3990#ifdef TTYSIZE_STRUCT 3991 TTYSIZE_STRUCT ts; 3992#endif 3993 struct passwd pw; 3994 char *login_name = NULL; 3995#ifndef USE_UTEMPTER 3996#ifdef HAVE_UTMP 3997 struct UTMP_STR utmp; 3998#ifdef USE_SYSV_UTMP 3999 struct UTMP_STR *utret = NULL; 4000#endif 4001#ifdef USE_LASTLOG 4002 struct lastlog lastlog; 4003#endif 4004#ifdef USE_LASTLOGX 4005 struct lastlogx lastlogx; 4006#endif /* USE_LASTLOG */ 4007#endif /* HAVE_UTMP */ 4008#endif /* !USE_UTEMPTER */ 4009 4010#if OPT_TRACE 4011 unsigned long xterm_parent = (unsigned long) getpid(); 4012#endif 4013 4014 /* Noisy compilers (suppress some unused-variable warnings) */ 4015 (void) rc; 4016#if defined(HAVE_UTMP) && defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 4017 (void) utret; 4018#endif 4019 4020 screen->uid = save_ruid; 4021 screen->gid = save_rgid; 4022 4023#ifdef SIGTTOU 4024 /* so that TIOCSWINSZ || TIOCSIZE doesn't block */ 4025 signal(SIGTTOU, SIG_IGN); 4026#endif 4027 4028#if OPT_PTY_HANDSHAKE 4029 memset(&handshake, 0, sizeof(handshake)); 4030#endif 4031 4032 if (am_slave >= 0) { 4033 screen->respond = am_slave; 4034 set_pty_id(ttydev, passedPty); 4035#ifdef USE_PTY_DEVICE 4036 set_pty_id(ptydev, passedPty); 4037#endif 4038 if (xtermResetIds(screen) < 0) 4039 exit(ERROR_MISC); 4040 } else { 4041 Bool tty_got_hung; 4042 4043 /* 4044 * Sometimes /dev/tty hangs on open (as in the case of a pty 4045 * that has gone away). Simply make up some reasonable 4046 * defaults. 4047 */ 4048 4049 if (!sigsetjmp(env, 1)) { 4050 signal(SIGALRM, hungtty); 4051 alarm(2); /* alarm(1) might return too soon */ 4052 ttyfd = open("/dev/tty", O_RDWR); 4053 alarm(0); 4054 tty_got_hung = False; 4055 } else { 4056 tty_got_hung = True; 4057 ttyfd = -1; 4058 errno = ENXIO; 4059 } 4060 shell_path = 0; 4061 memset(&pw, 0, sizeof(pw)); 4062#if OPT_PTY_HANDSHAKE 4063 got_handshake_size = False; 4064#endif /* OPT_PTY_HANDSHAKE */ 4065#if OPT_INITIAL_ERASE 4066 initial_erase = XTERM_ERASE; 4067#endif 4068 signal(SIGALRM, SIG_DFL); 4069 4070 /* 4071 * Check results and ignore current control terminal if 4072 * necessary. ENXIO is what is normally returned if there is 4073 * no controlling terminal, but some systems (e.g. SunOS 4.0) 4074 * seem to return EIO. Solaris 2.3 is said to return EINVAL. 4075 * Cygwin returns ENOENT. FreeBSD can return ENOENT, especially 4076 * if xterm is run within a jail. 4077 */ 4078#if USE_NO_DEV_TTY 4079 no_dev_tty = False; 4080#endif 4081 if (ttyfd < 0) { 4082 if (tty_got_hung || errno == ENXIO || errno == EIO || 4083 errno == ENOENT || 4084#ifdef ENODEV 4085 errno == ENODEV || 4086#endif 4087 errno == EINVAL || errno == ENOTTY || errno == EACCES) { 4088#if USE_NO_DEV_TTY 4089 no_dev_tty = True; 4090#endif 4091#ifdef HAS_LTCHARS 4092 ltc = d_ltc; 4093#endif /* HAS_LTCHARS */ 4094#ifdef TIOCLSET 4095 lmode = d_lmode; 4096#endif /* TIOCLSET */ 4097#ifdef TERMIO_STRUCT 4098 tio = d_tio; 4099#else /* !TERMIO_STRUCT */ 4100 sg = d_sg; 4101 tc = d_tc; 4102 discipline = d_disipline; 4103#ifdef sony 4104 jmode = d_jmode; 4105 jtc = d_jtc; 4106#endif /* sony */ 4107#endif /* TERMIO_STRUCT */ 4108 } else { 4109 SysError(ERROR_OPDEVTTY); 4110 } 4111 } else { 4112 4113 /* Get a copy of the current terminal's state, 4114 * if we can. Some systems (e.g., SVR4 and MacII) 4115 * may not have a controlling terminal at this point 4116 * if started directly from xdm or xinit, 4117 * in which case we just use the defaults as above. 4118 */ 4119#ifdef HAS_LTCHARS 4120 if (ioctl(ttyfd, TIOCGLTC, <c) == -1) 4121 ltc = d_ltc; 4122#endif /* HAS_LTCHARS */ 4123#ifdef TIOCLSET 4124 if (ioctl(ttyfd, TIOCLGET, &lmode) == -1) 4125 lmode = d_lmode; 4126#endif /* TIOCLSET */ 4127#ifdef TERMIO_STRUCT 4128 rc = ttyGetAttr(ttyfd, &tio); 4129 if (rc == -1) 4130 tio = d_tio; 4131#else /* !TERMIO_STRUCT */ 4132 rc = ioctl(ttyfd, TIOCGETP, (char *) &sg); 4133 if (rc == -1) 4134 sg = d_sg; 4135 if (ioctl(ttyfd, TIOCGETC, (char *) &tc) == -1) 4136 tc = d_tc; 4137 if (ioctl(ttyfd, TIOCGETD, (char *) &discipline) == -1) 4138 discipline = d_disipline; 4139#ifdef sony 4140 if (ioctl(ttyfd, TIOCKGET, (char *) &jmode) == -1) 4141 jmode = d_jmode; 4142 if (ioctl(ttyfd, TIOCKGETC, (char *) &jtc) == -1) 4143 jtc = d_jtc; 4144#endif /* sony */ 4145#endif /* TERMIO_STRUCT */ 4146 4147 /* 4148 * If ptyInitialErase is set, we want to get the pty's 4149 * erase value. Just in case that will fail, first get 4150 * the value from /dev/tty, so we will have something 4151 * at least. 4152 */ 4153#if OPT_INITIAL_ERASE 4154 if (resource.ptyInitialErase) { 4155#ifdef TERMIO_STRUCT 4156 initial_erase = tio.c_cc[VERASE]; 4157#else /* !TERMIO_STRUCT */ 4158 initial_erase = sg.sg_erase; 4159#endif /* TERMIO_STRUCT */ 4160 TRACE(("%s initial_erase:%d (from /dev/tty)\n", 4161 rc == 0 ? "OK" : "FAIL", 4162 initial_erase)); 4163 } 4164#endif 4165#ifdef __MVS__ 4166 if (ttyGetAttr(ttyfd, &gio) == 0) { 4167 gio.c_cflag &= ~(HUPCL | PARENB); 4168 ttySetAttr(ttyfd, &gio); 4169 } 4170#endif /* __MVS__ */ 4171 4172 close_fd(ttyfd); 4173 } 4174 4175 if (get_pty(&screen->respond, XDisplayString(screen->display))) { 4176 SysError(ERROR_PTYS); 4177 } 4178 TRACE_GET_TTYSIZE(screen->respond, "after get_pty"); 4179#if OPT_INITIAL_ERASE 4180 if (resource.ptyInitialErase) { 4181 initial_erase = get_tty_erase(screen->respond, 4182 initial_erase, 4183 "pty"); 4184 } 4185#endif /* OPT_INITIAL_ERASE */ 4186 } 4187 4188 /* avoid double MapWindow requests */ 4189 XtSetMappedWhenManaged(SHELL_OF(CURRENT_EMU()), False); 4190 4191 wm_delete_window = CachedInternAtom(XtDisplay(toplevel), 4192 "WM_DELETE_WINDOW"); 4193 4194 if (!TEK4014_ACTIVE(xw)) 4195 VTInit(xw); /* realize now so know window size for tty driver */ 4196#if defined(TIOCCONS) || defined(SRIOCSREDIR) 4197 if (Console) { 4198 /* 4199 * Inform any running xconsole program 4200 * that we are going to steal the console. 4201 */ 4202 XmuGetHostname(mit_console_name + MIT_CONSOLE_LEN, 255); 4203 TRACE(("getting for console name property \"%s\"\n", mit_console_name)); 4204 mit_console = CachedInternAtom(screen->display, mit_console_name); 4205 /* the user told us to be the console, so we can use CurrentTime */ 4206 XtOwnSelection(SHELL_OF(CURRENT_EMU()), 4207 mit_console, CurrentTime, 4208 ConvertConsoleSelection, NULL, NULL); 4209 } 4210#endif 4211#if OPT_TEK4014 4212 if (TEK4014_ACTIVE(xw)) { 4213 envnew = tekterm; 4214 } else 4215#endif 4216 { 4217 envnew = vtterm; 4218 } 4219 4220 /* 4221 * This used to exit if no termcap entry was found for the specified 4222 * terminal name. That's a little unfriendly, so instead we'll allow 4223 * the program to proceed (but not to set $TERMCAP) if the termcap 4224 * entry is not found. 4225 */ 4226 ok_termcap = True; 4227 if (!get_termcap(xw, TermName = resource.term_name)) { 4228 const char *last = NULL; 4229 char *next; 4230 4231 TermName = x_strdup(*envnew); 4232 ok_termcap = False; 4233 while (*envnew != NULL) { 4234 if (last == NULL || strcmp(last, *envnew)) { 4235 next = x_strdup(*envnew); 4236 if (get_termcap(xw, next)) { 4237 free(TermName); 4238 TermName = next; 4239 ok_termcap = True + 1; 4240 break; 4241 } else { 4242 free(next); 4243 } 4244 } 4245 last = *envnew; 4246 envnew++; 4247 } 4248 } 4249 if (ok_termcap) { 4250 resource.term_name = x_strdup(TermName); 4251 resize_termcap(xw); 4252 } 4253 4254 /* 4255 * Check if ptyInitialErase is not set. If so, we rely on the termcap 4256 * (or terminfo) to tell us what the erase mode should be set to. 4257 */ 4258#if OPT_INITIAL_ERASE 4259 TRACE(("resource ptyInitialErase is %sset\n", 4260 resource.ptyInitialErase ? "" : "not ")); 4261 setInitialErase = False; 4262 if (override_tty_modes && ttyModes[XTTYMODE_erase].set) { 4263 initial_erase = ttyModes[XTTYMODE_erase].value; 4264 setInitialErase = True; 4265 } else if (resource.ptyInitialErase) { 4266 /* EMPTY */ ; 4267 } else if (ok_termcap) { 4268 char *s = get_tcap_erase(xw); 4269 TRACE(("...extracting initial_erase value from termcap\n")); 4270 if (s != 0) { 4271 char *save = s; 4272 initial_erase = decode_keyvalue(&s, True); 4273 setInitialErase = True; 4274 free(save); 4275 } 4276 } 4277 TRACE(("...initial_erase:%d\n", initial_erase)); 4278 4279 TRACE(("resource backarrowKeyIsErase is %sset\n", 4280 resource.backarrow_is_erase ? "" : "not ")); 4281 if (resource.backarrow_is_erase) { /* see input.c */ 4282 if (initial_erase == ANSI_DEL) { 4283 UIntClr(xw->keyboard.flags, MODE_DECBKM); 4284 } else { 4285 xw->keyboard.flags |= MODE_DECBKM; 4286 xw->keyboard.reset_DECBKM = 1; 4287 } 4288 TRACE(("...sets DECBKM %s\n", 4289 (xw->keyboard.flags & MODE_DECBKM) ? "on" : "off")); 4290 } else { 4291 xw->keyboard.reset_DECBKM = 2; 4292 } 4293#endif /* OPT_INITIAL_ERASE */ 4294 4295#ifdef TTYSIZE_STRUCT 4296 /* tell tty how big window is */ 4297#if OPT_TEK4014 4298 if (TEK4014_ACTIVE(xw)) { 4299 setup_winsize(ts, TDefaultRows, TDefaultCols, 4300 TFullHeight(TekScreenOf(tekWidget)), 4301 TFullWidth(TekScreenOf(tekWidget))); 4302 } else 4303#endif 4304 { 4305 setup_winsize(ts, MaxRows(screen), MaxCols(screen), 4306 FullHeight(screen), FullWidth(screen)); 4307 } 4308 TRACE_RC(i, SET_TTYSIZE(screen->respond, ts)); 4309 TRACE(("spawn SET_TTYSIZE %dx%d return %d\n", 4310 TTYSIZE_ROWS(ts), 4311 TTYSIZE_COLS(ts), i)); 4312#endif /* TTYSIZE_STRUCT */ 4313 4314#if !defined(USE_OPENPTY) 4315#if defined(USE_USG_PTYS) || defined(HAVE_POSIX_OPENPT) 4316 /* 4317 * utempter checks the ownership of the device; some implementations 4318 * set ownership in grantpt - do this first. 4319 */ 4320 grantpt(screen->respond); 4321#endif 4322#if !defined(USE_USG_PTYS) && defined(HAVE_POSIX_OPENPT) 4323 unlockpt(screen->respond); 4324 TRACE_GET_TTYSIZE(screen->respond, "after unlockpt"); 4325#endif 4326#endif /* !USE_OPENPTY */ 4327 4328 added_utmp_entry = False; 4329#if defined(USE_UTEMPTER) 4330#undef UTMP 4331 if ((xw->misc.login_shell || !command_to_exec) && !resource.utmpInhibit) { 4332 struct UTMP_STR dummy; 4333 4334 /* Note: utempter may trim it anyway */ 4335 SetUtmpHost(dummy.ut_host, screen); 4336 TRACE(("...calling addToUtmp(pty=%s, hostname=%s, master_fd=%d)\n", 4337 ttydev, dummy.ut_host, screen->respond)); 4338 UTEMPTER_ADD(ttydev, dummy.ut_host, screen->respond); 4339 added_utmp_entry = True; 4340 } 4341#endif 4342 4343 if (am_slave < 0) { 4344#if OPT_PTY_HANDSHAKE 4345 if (resource.ptyHandshake && (pipe(pc_pipe) || pipe(cp_pipe))) 4346 SysError(ERROR_FORK); 4347#endif 4348 TRACE(("Forking...\n")); 4349 if ((screen->pid = fork()) == -1) 4350 SysError(ERROR_FORK); 4351 4352 if (screen->pid == 0) { 4353#ifdef USE_USG_PTYS 4354 int ptyfd = -1; 4355 char *pty_name; 4356#endif 4357 /* 4358 * now in child process 4359 */ 4360#ifdef HAVE_SETSID 4361 int pgrp = setsid(); /* variable may not be used... */ 4362#else 4363 int pgrp = getpid(); 4364#endif 4365 TRACE_CHILD; 4366 4367#ifdef USE_USG_PTYS 4368#ifdef HAVE_SETPGID 4369 setpgid(0, 0); 4370#else 4371 setpgrp(); 4372#endif 4373 unlockpt(screen->respond); 4374 TRACE_GET_TTYSIZE(screen->respond, "after unlockpt"); 4375 if ((pty_name = ptsname(screen->respond)) == 0) { 4376 SysError(ERROR_PTSNAME); 4377 } else if ((ptyfd = open(pty_name, O_RDWR)) < 0) { 4378 SysError(ERROR_OPPTSNAME); 4379 } 4380#ifdef I_PUSH 4381 else if (PUSH_FAILS(ptyfd, "ptem")) { 4382 SysError(ERROR_PTEM); 4383 } 4384#if !defined(SVR4) && !(defined(SYSV) && defined(i386)) 4385 else if (!x_getenv("CONSEM") 4386 && PUSH_FAILS(ptyfd, "consem")) { 4387 SysError(ERROR_CONSEM); 4388 } 4389#endif /* !SVR4 */ 4390 else if (PUSH_FAILS(ptyfd, "ldterm")) { 4391 SysError(ERROR_LDTERM); 4392 } 4393#ifdef SVR4 /* from Sony */ 4394 else if (PUSH_FAILS(ptyfd, "ttcompat")) { 4395 SysError(ERROR_TTCOMPAT); 4396 } 4397#endif /* SVR4 */ 4398#endif /* I_PUSH */ 4399 ttyfd = ptyfd; 4400#ifndef __MVS__ 4401 close_fd(screen->respond); 4402#endif /* __MVS__ */ 4403 4404#ifdef TTYSIZE_STRUCT 4405 /* tell tty how big window is */ 4406#if OPT_TEK4014 4407 if (TEK4014_ACTIVE(xw)) { 4408 setup_winsize(ts, TDefaultRows, TDefaultCols, 4409 TFullHeight(TekScreenOf(tekWidget)), 4410 TFullWidth(TekScreenOf(tekWidget))); 4411 } else 4412#endif /* OPT_TEK4014 */ 4413 { 4414 setup_winsize(ts, MaxRows(screen), MaxCols(screen), 4415 FullHeight(screen), FullWidth(screen)); 4416 } 4417 trace_winsize(ts, "initial tty size"); 4418#endif /* TTYSIZE_STRUCT */ 4419 4420#endif /* USE_USG_PTYS */ 4421 4422 (void) pgrp; /* not all branches use this variable */ 4423 4424#if OPT_PTY_HANDSHAKE /* warning, goes for a long ways */ 4425 if (resource.ptyHandshake) { 4426 char *ptr; 4427 4428 /* close parent's sides of the pipes */ 4429 close(cp_pipe[0]); 4430 close(pc_pipe[1]); 4431 4432 /* Make sure that our sides of the pipes are not in the 4433 * 0, 1, 2 range so that we don't fight with stdin, out 4434 * or err. 4435 */ 4436 if (cp_pipe[1] <= 2) { 4437 if ((i = fcntl(cp_pipe[1], F_DUPFD, 3)) >= 0) { 4438 IGNORE_RC(close(cp_pipe[1])); 4439 cp_pipe[1] = i; 4440 } 4441 } 4442 if (pc_pipe[0] <= 2) { 4443 if ((i = fcntl(pc_pipe[0], F_DUPFD, 3)) >= 0) { 4444 IGNORE_RC(close(pc_pipe[0])); 4445 pc_pipe[0] = i; 4446 } 4447 } 4448 4449 /* we don't need the socket, or the pty master anymore */ 4450 close(ConnectionNumber(screen->display)); 4451#ifndef __MVS__ 4452 if (screen->respond >= 0) 4453 close(screen->respond); 4454#endif /* __MVS__ */ 4455 4456 /* Now is the time to set up our process group and 4457 * open up the pty slave. 4458 */ 4459#ifdef USE_SYSV_PGRP 4460#if defined(CRAY) && (OSMAJORVERSION > 5) 4461 IGNORE_RC(setsid()); 4462#else 4463 IGNORE_RC(setpgrp()); 4464#endif 4465#endif /* USE_SYSV_PGRP */ 4466 4467#if defined(__QNX__) && !defined(__QNXNTO__) 4468 qsetlogin(getlogin(), ttydev); 4469#endif 4470 if (ttyfd >= 0) { 4471#ifdef __MVS__ 4472 if (ttyGetAttr(ttyfd, &gio) == 0) { 4473 gio.c_cflag &= ~(HUPCL | PARENB); 4474 ttySetAttr(ttyfd, &gio); 4475 } 4476#else /* !__MVS__ */ 4477 close_fd(ttyfd); 4478#endif /* __MVS__ */ 4479 } 4480 4481 for (;;) { 4482#if USE_NO_DEV_TTY 4483 if (!no_dev_tty 4484 && (ttyfd = open("/dev/tty", O_RDWR)) >= 0) { 4485 ioctl(ttyfd, TIOCNOTTY, (char *) NULL); 4486 close_fd(ttyfd); 4487 } 4488#endif /* USE_NO_DEV_TTY */ 4489#ifdef CSRG_BASED 4490 IGNORE_RC(revoke(ttydev)); 4491#endif 4492 if ((ttyfd = open(ttydev, O_RDWR)) >= 0) { 4493 TRACE_GET_TTYSIZE(ttyfd, "after open"); 4494 TRACE_RC(i, SET_TTYSIZE(ttyfd, ts)); 4495 TRACE_GET_TTYSIZE(ttyfd, "after SET_TTYSIZE fixup"); 4496#if defined(CRAY) && defined(TCSETCTTY) 4497 /* make /dev/tty work */ 4498 ioctl(ttyfd, TCSETCTTY, 0); 4499#endif 4500#if ((defined(__GLIBC__) && defined(__FreeBSD_kernel__)) || defined(__GNU__)) && defined(TIOCSCTTY) 4501 /* make /dev/tty work */ 4502 ioctl(ttyfd, TIOCSCTTY, 0); 4503#endif 4504#ifdef USE_SYSV_PGRP 4505 /* We need to make sure that we are actually 4506 * the process group leader for the pty. If 4507 * we are, then we should now be able to open 4508 * /dev/tty. 4509 */ 4510 if ((i = open("/dev/tty", O_RDWR)) >= 0) { 4511 /* success! */ 4512 close(i); 4513 break; 4514 } 4515#else /* USE_SYSV_PGRP */ 4516 break; 4517#endif /* USE_SYSV_PGRP */ 4518 } 4519 perror("open ttydev"); 4520#ifdef TIOCSCTTY 4521 ioctl(ttyfd, TIOCSCTTY, 0); 4522#endif 4523 /* let our master know that the open failed */ 4524 handshake.status = PTY_BAD; 4525 handshake.error = errno; 4526 copy_handshake(handshake, ttydev); 4527 TRACE_HANDSHAKE("writing", &handshake); 4528 IGNORE_RC(write(cp_pipe[1], 4529 (const char *) &handshake, 4530 sizeof(handshake))); 4531 4532 /* get reply from parent */ 4533 i = (int) read(pc_pipe[0], (char *) &handshake, 4534 sizeof(handshake)); 4535 if (i <= 0) { 4536 /* parent terminated */ 4537 exit(ERROR_MISC); 4538 } 4539 4540 if (handshake.status == PTY_NOMORE) { 4541 /* No more ptys, let's shutdown. */ 4542 exit(ERROR_MISC); 4543 } 4544 4545 /* We have a new pty to try */ 4546 if (ttyfd >= 0) 4547 close(ttyfd); 4548 free(ttydev); 4549 handshake.buffer[HANDSHAKE_LEN - 1] = '\0'; 4550 ttydev = x_strdup(handshake.buffer); 4551 } 4552 4553 /* use the same tty name that everyone else will use 4554 * (from ttyname) 4555 */ 4556 if ((ptr = ttyname(ttyfd)) != 0) { 4557 free(ttydev); 4558 ttydev = x_strdup(ptr); 4559 } 4560 } 4561#endif /* OPT_PTY_HANDSHAKE -- from near fork */ 4562 4563 set_pty_permissions(screen->uid, 4564 (unsigned) screen->gid, 4565 (resource.messages 4566 ? 0622U 4567 : 0600U)); 4568 4569 /* 4570 * set up the tty modes 4571 */ 4572 { 4573#ifdef TERMIO_STRUCT 4574#if defined(umips) || defined(CRAY) || defined(__linux__) 4575 /* If the control tty had its modes screwed around with, 4576 eg. by lineedit in the shell, or emacs, etc. then tio 4577 will have bad values. Let's just get termio from the 4578 new tty and tailor it. */ 4579 if (ttyGetAttr(ttyfd, &tio) == -1) 4580 SysError(ERROR_TIOCGETP); 4581 tio.c_lflag |= ECHOE; 4582#endif /* umips */ 4583 /* Now is also the time to change the modes of the 4584 * child pty. 4585 */ 4586 /* input: nl->nl, don't ignore cr, cr->nl */ 4587 UIntClr(tio.c_iflag, (INLCR | IGNCR)); 4588 tio.c_iflag |= ICRNL; 4589#if OPT_WIDE_CHARS && defined(IUTF8) 4590#if OPT_LUIT_PROG 4591 if (command_to_exec_with_luit == 0) 4592#endif 4593 if (screen->utf8_mode) 4594 tio.c_iflag |= IUTF8; 4595#endif 4596 /* output: cr->cr, nl is not return, no delays, ln->cr/nl */ 4597#ifndef USE_POSIX_TERMIOS 4598 UIntClr(tio.c_oflag, 4599 (OCRNL 4600 | ONLRET 4601 | NLDLY 4602 | CRDLY 4603 | TABDLY 4604 | BSDLY 4605 | VTDLY 4606 | FFDLY)); 4607#endif /* USE_POSIX_TERMIOS */ 4608 tio.c_oflag |= D_TIO_FLAGS; 4609#ifndef USE_POSIX_TERMIOS 4610# if defined(Lynx) && !defined(CBAUD) 4611# define CBAUD V_CBAUD 4612# endif 4613 UIntClr(tio.c_cflag, CBAUD); 4614#ifdef BAUD_0 4615 /* baud rate is 0 (don't care) */ 4616#elif defined(HAVE_TERMIO_C_ISPEED) 4617 tio.c_ispeed = tio.c_ospeed = line_speed; 4618#else /* !BAUD_0 */ 4619 tio.c_cflag |= line_speed; 4620#endif /* !BAUD_0 */ 4621#else /* USE_POSIX_TERMIOS */ 4622 cfsetispeed(&tio, line_speed); 4623 cfsetospeed(&tio, line_speed); 4624#ifdef __MVS__ 4625 /* turn off bits that can't be set from the slave side */ 4626 tio.c_cflag &= ~(PACKET | PKT3270 | PTU3270 | PKTXTND); 4627#endif /* __MVS__ */ 4628 /* Clear CLOCAL so that SIGHUP is sent to us 4629 when the xterm ends */ 4630 tio.c_cflag &= (unsigned) ~CLOCAL; 4631#endif /* USE_POSIX_TERMIOS */ 4632 /* enable signals, canonical processing (erase, kill, etc), 4633 * echo 4634 */ 4635 tio.c_lflag |= ISIG | ICANON | ECHO | ECHOE | ECHOK; 4636#ifdef ECHOKE 4637 tio.c_lflag |= ECHOKE | IEXTEN; 4638#endif 4639#ifdef ECHOCTL 4640 tio.c_lflag |= ECHOCTL | IEXTEN; 4641#endif 4642 for (nn = 0; nn < XtNumber(ttyChars); ++nn) { 4643 if (validTtyChar(tio, nn)) { 4644 int sysMode = ttyChars[nn].sysMode; 4645#ifdef __MVS__ 4646 if (tio.c_cc[sysMode] != 0) { 4647 switch (sysMode) { 4648 case VEOL: 4649 case VEOF: 4650 continue; 4651 } 4652 } 4653#endif 4654 tio.c_cc[sysMode] = (cc_t) ttyChars[nn].myDefault; 4655 } 4656 } 4657 4658 if (override_tty_modes) { 4659 TRACE(("applying termios ttyModes\n")); 4660 for (nn = 0; nn < XtNumber(ttyChars); ++nn) { 4661 if (validTtyChar(tio, nn)) { 4662 TMODE(ttyChars[nn].myMode, 4663 tio.c_cc[ttyChars[nn].sysMode]); 4664 } else if (isTabMode(nn)) { 4665 unsigned tmp = (unsigned) tio.c_oflag; 4666 tmp = tmp & (unsigned) ~TABDLY; 4667 tmp |= (unsigned) ttyModes[ttyChars[nn].myMode].value; 4668 tio.c_oflag = tmp; 4669 } 4670 } 4671#ifdef HAS_LTCHARS 4672 /* both SYSV and BSD have ltchars */ 4673 TMODE(XTTYMODE_susp, ltc.t_suspc); 4674 TMODE(XTTYMODE_dsusp, ltc.t_dsuspc); 4675 TMODE(XTTYMODE_rprnt, ltc.t_rprntc); 4676 TMODE(XTTYMODE_flush, ltc.t_flushc); 4677 TMODE(XTTYMODE_weras, ltc.t_werasc); 4678 TMODE(XTTYMODE_lnext, ltc.t_lnextc); 4679#endif 4680 } 4681#ifdef HAS_LTCHARS 4682#ifdef __hpux 4683 /* ioctl chokes when the "reserved" process group controls 4684 * are not set to _POSIX_VDISABLE */ 4685 ltc.t_rprntc = _POSIX_VDISABLE; 4686 ltc.t_rprntc = _POSIX_VDISABLE; 4687 ltc.t_flushc = _POSIX_VDISABLE; 4688 ltc.t_werasc = _POSIX_VDISABLE; 4689 ltc.t_lnextc = _POSIX_VDISABLE; 4690#endif /* __hpux */ 4691 if (ioctl(ttyfd, TIOCSLTC, <c) == -1) 4692 HsSysError(ERROR_TIOCSETC); 4693#endif /* HAS_LTCHARS */ 4694#ifdef TIOCLSET 4695 if (ioctl(ttyfd, TIOCLSET, (char *) &lmode) == -1) 4696 HsSysError(ERROR_TIOCLSET); 4697#endif /* TIOCLSET */ 4698 if (ttySetAttr(ttyfd, &tio) == -1) 4699 HsSysError(ERROR_TIOCSETP); 4700 4701 /* ignore errors here - some platforms don't work */ 4702 UIntClr(tio.c_cflag, CSIZE); 4703 if (screen->input_eight_bits) 4704 tio.c_cflag |= CS8; 4705 else 4706 tio.c_cflag |= CS7; 4707 (void) ttySetAttr(ttyfd, &tio); 4708 4709#else /* !TERMIO_STRUCT */ 4710 sg.sg_flags &= ~(ALLDELAY | XTABS | CBREAK | RAW); 4711 sg.sg_flags |= ECHO | CRMOD; 4712 /* make sure speed is set on pty so that editors work right */ 4713 sg.sg_ispeed = line_speed; 4714 sg.sg_ospeed = line_speed; 4715 /* reset t_brkc to default value */ 4716 tc.t_brkc = -1; 4717#ifdef LPASS8 4718 if (screen->input_eight_bits) 4719 lmode |= LPASS8; 4720 else 4721 lmode &= ~(LPASS8); 4722#endif 4723#ifdef sony 4724 jmode &= ~KM_KANJI; 4725#endif /* sony */ 4726 4727 ltc = d_ltc; 4728 4729 if (override_tty_modes) { 4730 TRACE(("applying sgtty ttyModes\n")); 4731 TMODE(XTTYMODE_intr, tc.t_intrc); 4732 TMODE(XTTYMODE_quit, tc.t_quitc); 4733 TMODE(XTTYMODE_erase, sg.sg_erase); 4734 TMODE(XTTYMODE_kill, sg.sg_kill); 4735 TMODE(XTTYMODE_eof, tc.t_eofc); 4736 TMODE(XTTYMODE_start, tc.t_startc); 4737 TMODE(XTTYMODE_stop, tc.t_stopc); 4738 TMODE(XTTYMODE_brk, tc.t_brkc); 4739 /* both SYSV and BSD have ltchars */ 4740 TMODE(XTTYMODE_susp, ltc.t_suspc); 4741 TMODE(XTTYMODE_dsusp, ltc.t_dsuspc); 4742 TMODE(XTTYMODE_rprnt, ltc.t_rprntc); 4743 TMODE(XTTYMODE_flush, ltc.t_flushc); 4744 TMODE(XTTYMODE_weras, ltc.t_werasc); 4745 TMODE(XTTYMODE_lnext, ltc.t_lnextc); 4746 if (ttyModes[XTTYMODE_tabs].set 4747 || ttyModes[XTTYMODE__tabs].set) { 4748 sg.sg_flags &= ~XTABS; 4749 if (ttyModes[XTTYMODE__tabs].set.set) 4750 sg.sg_flags |= XTABS; 4751 } 4752 } 4753 4754 if (ioctl(ttyfd, TIOCSETP, (char *) &sg) == -1) 4755 HsSysError(ERROR_TIOCSETP); 4756 if (ioctl(ttyfd, TIOCSETC, (char *) &tc) == -1) 4757 HsSysError(ERROR_TIOCSETC); 4758 if (ioctl(ttyfd, TIOCSETD, (char *) &discipline) == -1) 4759 HsSysError(ERROR_TIOCSETD); 4760 if (ioctl(ttyfd, TIOCSLTC, (char *) <c) == -1) 4761 HsSysError(ERROR_TIOCSLTC); 4762 if (ioctl(ttyfd, TIOCLSET, (char *) &lmode) == -1) 4763 HsSysError(ERROR_TIOCLSET); 4764#ifdef sony 4765 if (ioctl(ttyfd, TIOCKSET, (char *) &jmode) == -1) 4766 HsSysError(ERROR_TIOCKSET); 4767 if (ioctl(ttyfd, TIOCKSETC, (char *) &jtc) == -1) 4768 HsSysError(ERROR_TIOCKSETC); 4769#endif /* sony */ 4770#endif /* TERMIO_STRUCT */ 4771#if defined(TIOCCONS) || defined(SRIOCSREDIR) 4772 if (Console) { 4773#ifdef TIOCCONS 4774 int on = 1; 4775 if (ioctl(ttyfd, TIOCCONS, (char *) &on) == -1) 4776 xtermPerror("cannot open console"); 4777#endif 4778#ifdef SRIOCSREDIR 4779 int fd = open("/dev/console", O_RDWR); 4780 if (fd == -1 || ioctl(fd, SRIOCSREDIR, ttyfd) == -1) 4781 xtermPerror("cannot open console"); 4782 IGNORE_RC(close(fd)); 4783#endif 4784 } 4785#endif /* TIOCCONS */ 4786 } 4787 4788 signal(SIGCHLD, SIG_DFL); 4789#ifdef USE_SYSV_SIGHUP 4790 /* watch out for extra shells (I don't understand either) */ 4791 signal(SIGHUP, SIG_DFL); 4792#else 4793 signal(SIGHUP, SIG_IGN); 4794#endif 4795 /* restore various signals to their defaults */ 4796 signal(SIGINT, SIG_DFL); 4797 signal(SIGQUIT, SIG_DFL); 4798 signal(SIGTERM, SIG_DFL); 4799 4800 /* 4801 * If we're not asked to let the parent process set the terminal's 4802 * erase mode, or if we had the ttyModes erase resource, then set 4803 * the terminal's erase mode from our best guess. 4804 */ 4805#if OPT_INITIAL_ERASE 4806 TRACE(("check if we should set erase to %d:%s\n\tptyInitialErase:%d,\n\toveride_tty_modes:%d,\n\tXTTYMODE_erase:%d\n", 4807 initial_erase, 4808 setInitialErase ? "YES" : "NO", 4809 resource.ptyInitialErase, 4810 override_tty_modes, 4811 ttyModes[XTTYMODE_erase].set)); 4812 if (setInitialErase) { 4813#if OPT_TRACE 4814 int old_erase; 4815#endif 4816#ifdef TERMIO_STRUCT 4817 if (ttyGetAttr(ttyfd, &tio) == -1) 4818 tio = d_tio; 4819#if OPT_TRACE 4820 old_erase = tio.c_cc[VERASE]; 4821#endif 4822 tio.c_cc[VERASE] = (cc_t) initial_erase; 4823 TRACE_RC(rc, ttySetAttr(ttyfd, &tio)); 4824#else /* !TERMIO_STRUCT */ 4825 if (ioctl(ttyfd, TIOCGETP, (char *) &sg) == -1) 4826 sg = d_sg; 4827#if OPT_TRACE 4828 old_erase = sg.sg_erase; 4829#endif 4830 sg.sg_erase = initial_erase; 4831 rc = ioctl(ttyfd, TIOCSETP, (char *) &sg); 4832#endif /* TERMIO_STRUCT */ 4833 TRACE(("%s setting erase to %d (was %d)\n", 4834 rc ? "FAIL" : "OK", initial_erase, old_erase)); 4835 } 4836#endif 4837 4838 xtermCopyEnv(environ); 4839 xtermTrimEnv(); 4840 4841 xtermSetenv("TERM", resource.term_name); 4842 if (!resource.term_name) 4843 *get_tcap_buffer(xw) = 0; 4844 4845 sprintf(buf, "%lu", 4846 ((unsigned long) XtWindow(SHELL_OF(CURRENT_EMU())))); 4847 xtermSetenv("WINDOWID", buf); 4848 4849 /* put the display into the environment of the shell */ 4850 xtermSetenv("DISPLAY", XDisplayString(screen->display)); 4851 4852 xtermSetenv("XTERM_VERSION", xtermVersion()); 4853 xtermSetenv("XTERM_LOCALE", xtermEnvLocale()); 4854 4855 /* 4856 * For debugging only, add environment variables that can be used 4857 * in scripts to selectively kill xterm's parent or child 4858 * processes. 4859 */ 4860#if OPT_TRACE 4861 sprintf(buf, "%lu", (unsigned long) xterm_parent); 4862 xtermSetenv("XTERM_PARENT", buf); 4863 sprintf(buf, "%lu", (unsigned long) getpid()); 4864 xtermSetenv("XTERM_CHILD", buf); 4865#endif 4866 4867 signal(SIGTERM, SIG_DFL); 4868 4869 /* this is the time to go and set up stdin, out, and err 4870 */ 4871 { 4872#if defined(CRAY) && (OSMAJORVERSION >= 6) 4873 close_fd(ttyfd); 4874 4875 IGNORE_RC(close(0)); 4876 4877 if (open("/dev/tty", O_RDWR)) { 4878 SysError(ERROR_OPDEVTTY); 4879 } 4880 IGNORE_RC(close(1)); 4881 IGNORE_RC(close(2)); 4882 dup(0); 4883 dup(0); 4884#else 4885 /* dup the tty */ 4886 for (i = 0; i <= 2; i++) 4887 if (i != ttyfd) { 4888 IGNORE_RC(close(i)); 4889 IGNORE_RC(dup(ttyfd)); 4890 } 4891#ifndef ATT 4892 /* and close the tty */ 4893 if (ttyfd > 2) 4894 close_fd(ttyfd); 4895#endif 4896#endif /* CRAY */ 4897 } 4898 4899#if !defined(USE_SYSV_PGRP) 4900#ifdef TIOCSCTTY 4901 setsid(); 4902 ioctl(0, TIOCSCTTY, 0); 4903#endif 4904 ioctl(0, TIOCSPGRP, (char *) &pgrp); 4905 setpgrp(0, 0); 4906 close(open(ttydev, O_WRONLY)); 4907 setpgrp(0, pgrp); 4908#if defined(__QNX__) 4909 tcsetpgrp(0, pgrp /*setsid() */ ); 4910#endif 4911#endif /* !USE_SYSV_PGRP */ 4912 4913#ifdef Lynx 4914 { 4915 TERMIO_STRUCT t; 4916 if (ttyGetAttr(0, &t) >= 0) { 4917 /* this gets lost somewhere on our way... */ 4918 t.c_oflag |= OPOST; 4919 ttySetAttr(0, &t); 4920 } 4921 } 4922#endif 4923 4924#ifdef HAVE_UTMP 4925 login_name = NULL; 4926 if (x_getpwuid(screen->uid, &pw)) { 4927 login_name = x_getlogin(screen->uid, &pw); 4928 } 4929 if (login_name != NULL) { 4930 xtermSetenv("LOGNAME", login_name); /* for POSIX */ 4931 } 4932#ifndef USE_UTEMPTER 4933#ifdef USE_UTMP_SETGID 4934 setEffectiveGroup(save_egid); 4935 TRACE_IDS; 4936#endif 4937#ifdef USE_SYSV_UTMP 4938 /* Set up our utmp entry now. We need to do it here 4939 * for the following reasons: 4940 * - It needs to have our correct process id (for 4941 * login). 4942 * - If our parent was to set it after the fork(), 4943 * it might make it out before we need it. 4944 * - We need to do it before we go and change our 4945 * user and group id's. 4946 */ 4947 (void) call_setutent(); 4948 init_utmp(DEAD_PROCESS, &utmp); 4949 4950 /* position to entry in utmp file */ 4951 /* Test return value: beware of entries left behind: PSz 9 Mar 00 */ 4952 utret = find_utmp(&utmp); 4953 if (utret == 0) { 4954 (void) call_setutent(); 4955 init_utmp(USER_PROCESS, &utmp); 4956 utret = find_utmp(&utmp); 4957 if (utret == 0) { 4958 (void) call_setutent(); 4959 } 4960 } 4961#if OPT_TRACE 4962 if (!utret) 4963 TRACE(("getutid: NULL\n")); 4964 else 4965 TRACE(("getutid: pid=%d type=%d user=%s line=%.*s id=%.*s\n", 4966 (int) utret->ut_pid, utret->ut_type, utret->ut_user, 4967 (int) sizeof(utret->ut_line), utret->ut_line, 4968 (int) sizeof(utret->ut_id), utret->ut_id)); 4969#endif 4970 4971 /* set up the new entry */ 4972 utmp.ut_type = USER_PROCESS; 4973#ifdef HAVE_UTMP_UT_XSTATUS 4974 utmp.ut_xstatus = 2; 4975#endif 4976 copy_filled(utmp.ut_user, 4977 (login_name != NULL) ? login_name : "????", 4978 sizeof(utmp.ut_user)); 4979 /* why are we copying this string again? (see above) */ 4980 copy_filled(utmp.ut_id, my_utmp_id(ttydev), sizeof(utmp.ut_id)); 4981 copy_filled(utmp.ut_line, 4982 my_pty_name(ttydev), sizeof(utmp.ut_line)); 4983 4984#ifdef HAVE_UTMP_UT_HOST 4985 SetUtmpHost(utmp.ut_host, screen); 4986#endif 4987#ifdef HAVE_UTMP_UT_SYSLEN 4988 SetUtmpSysLen(utmp); 4989#endif 4990 4991 copy_filled(utmp.ut_name, 4992 (login_name) ? login_name : "????", 4993 sizeof(utmp.ut_name)); 4994 4995 utmp.ut_pid = getpid(); 4996#if defined(HAVE_UTMP_UT_XTIME) 4997#if defined(HAVE_UTMP_UT_SESSION) 4998 utmp.ut_session = getsid(0); 4999#endif 5000 utmp.ut_xtime = time((time_t *) 0); 5001 utmp.ut_tv.tv_usec = 0; 5002#else 5003 utmp.ut_time = time((time_t *) 0); 5004#endif 5005 5006 /* write out the entry */ 5007 if (!resource.utmpInhibit) { 5008 errno = 0; 5009 call_pututline(&utmp); 5010 TRACE(("pututline: id %.*s, line %.*s, pid %ld, errno %d %s\n", 5011 (int) sizeof(utmp.ut_id), utmp.ut_id, 5012 (int) sizeof(utmp.ut_line), utmp.ut_line, 5013 (long) utmp.ut_pid, 5014 errno, (errno != 0) ? strerror(errno) : "")); 5015 } 5016#ifdef WTMP 5017#if defined(WTMPX_FILE) && (defined(SVR4) || defined(__SCO__)) 5018 if (xw->misc.login_shell) 5019 updwtmpx(WTMPX_FILE, &utmp); 5020#elif defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ >= 2) && !(defined(__powerpc__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)) 5021 if (xw->misc.login_shell) 5022 call_updwtmp(etc_wtmp, &utmp); 5023#else 5024 if (xw->misc.login_shell && 5025 (i = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 5026 IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp))); 5027 close(i); 5028 } 5029#endif 5030#endif 5031 /* close the file */ 5032 (void) call_endutent(); 5033 5034#else /* USE_SYSV_UTMP */ 5035 /* We can now get our ttyslot! We can also set the initial 5036 * utmp entry. 5037 */ 5038 tslot = ttyslot(); 5039 added_utmp_entry = False; 5040 { 5041 if (tslot > 0 && OkPasswd(&pw) && !resource.utmpInhibit && 5042 (i = open(etc_utmp, O_WRONLY)) >= 0) { 5043 memset(&utmp, 0, sizeof(utmp)); 5044 copy_filled(utmp.ut_line, 5045 my_pty_name(ttydev), 5046 sizeof(utmp.ut_line)); 5047 copy_filled(utmp.ut_name, login_name, 5048 sizeof(utmp.ut_name)); 5049#ifdef HAVE_UTMP_UT_HOST 5050 SetUtmpHost(utmp.ut_host, screen); 5051#endif 5052#ifdef HAVE_UTMP_UT_SYSLEN 5053 SetUtmpSysLen(utmp); 5054#endif 5055 5056 utmp.ut_time = time((time_t *) 0); 5057 lseek(i, (long) (tslot * sizeof(utmp)), 0); 5058 IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp))); 5059 close(i); 5060 added_utmp_entry = True; 5061#if defined(WTMP) 5062 if (xw->misc.login_shell && 5063 (i = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 5064 int status; 5065 status = write(i, (char *) &utmp, sizeof(utmp)); 5066 status = close(i); 5067 } 5068#elif defined(MNX_LASTLOG) 5069 if (xw->misc.login_shell && 5070 (i = open(_U_LASTLOG, O_WRONLY)) >= 0) { 5071 lseek(i, (long) (screen->uid * 5072 sizeof(utmp)), 0); 5073 IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp))); 5074 close(i); 5075 } 5076#endif /* WTMP or MNX_LASTLOG */ 5077 } else 5078 tslot = -tslot; 5079 } 5080 5081 /* Let's pass our ttyslot to our parent so that it can 5082 * clean up after us. 5083 */ 5084#if OPT_PTY_HANDSHAKE 5085 if (resource.ptyHandshake) { 5086 handshake.tty_slot = tslot; 5087 } 5088#endif /* OPT_PTY_HANDSHAKE */ 5089#endif /* USE_SYSV_UTMP */ 5090 5091#ifdef USE_LASTLOGX 5092 if (xw->misc.login_shell) { 5093 memset(&lastlogx, 0, sizeof(lastlogx)); 5094 copy_filled(lastlogx.ll_line, 5095 my_pty_name(ttydev), 5096 sizeof(lastlogx.ll_line)); 5097 X_GETTIMEOFDAY(&lastlogx.ll_tv); 5098 SetUtmpHost(lastlogx.ll_host, screen); 5099 updlastlogx(_PATH_LASTLOGX, screen->uid, &lastlogx); 5100 } 5101#endif 5102 5103#ifdef USE_LASTLOG 5104 if (xw->misc.login_shell && 5105 (i = open(etc_lastlog, O_WRONLY)) >= 0) { 5106 size_t size = sizeof(struct lastlog); 5107 off_t offset = (off_t) ((size_t) screen->uid * size); 5108 5109 memset(&lastlog, 0, size); 5110 copy_filled(lastlog.ll_line, 5111 my_pty_name(ttydev), 5112 sizeof(lastlog.ll_line)); 5113 SetUtmpHost(lastlog.ll_host, screen); 5114 lastlog.ll_time = time((time_t *) 0); 5115 if (lseek(i, offset, 0) != (off_t) (-1)) { 5116 IGNORE_RC(write(i, (char *) &lastlog, size)); 5117 } 5118 close(i); 5119 } 5120#endif /* USE_LASTLOG */ 5121 5122#if defined(USE_UTMP_SETGID) 5123 disableSetGid(); 5124 TRACE_IDS; 5125#endif 5126 5127#if OPT_PTY_HANDSHAKE 5128 /* Let our parent know that we set up our utmp entry 5129 * so that it can clean up after us. 5130 */ 5131 if (resource.ptyHandshake) { 5132 handshake.status = UTMP_ADDED; 5133 handshake.error = 0; 5134 copy_handshake(handshake, ttydev); 5135 TRACE_HANDSHAKE("writing", &handshake); 5136 IGNORE_RC(write(cp_pipe[1], (char *) &handshake, sizeof(handshake))); 5137 } 5138#endif /* OPT_PTY_HANDSHAKE */ 5139#endif /* USE_UTEMPTER */ 5140#endif /* HAVE_UTMP */ 5141 5142 IGNORE_RC(setgid(screen->gid)); 5143 TRACE_IDS; 5144#ifdef HAVE_INITGROUPS 5145 if (geteuid() == 0 && OkPasswd(&pw)) { 5146 if (initgroups(login_name, pw.pw_gid)) { 5147 perror("initgroups failed"); 5148 SysError(ERROR_INIGROUPS); 5149 } 5150 } 5151#endif 5152 if (setuid(screen->uid)) { 5153 SysError(ERROR_SETUID); 5154 } 5155 TRACE_IDS; 5156#if OPT_PTY_HANDSHAKE 5157 if (resource.ptyHandshake) { 5158 /* mark the pipes as close on exec */ 5159 (void) fcntl(cp_pipe[1], F_SETFD, 1); 5160 (void) fcntl(pc_pipe[0], F_SETFD, 1); 5161 5162 /* We are at the point where we are going to 5163 * exec our shell (or whatever). Let our parent 5164 * know we arrived safely. 5165 */ 5166 handshake.status = PTY_GOOD; 5167 handshake.error = 0; 5168 copy_handshake(handshake, ttydev); 5169 TRACE_HANDSHAKE("writing", &handshake); 5170 IGNORE_RC(write(cp_pipe[1], 5171 (const char *) &handshake, 5172 sizeof(handshake))); 5173 5174 if (resource.wait_for_map) { 5175 i = (int) read(pc_pipe[0], (char *) &handshake, 5176 sizeof(handshake)); 5177 if (i != sizeof(handshake) || 5178 handshake.status != PTY_EXEC) { 5179 /* some very bad problem occurred */ 5180 exit(ERROR_PTY_EXEC); 5181 } 5182 if (handshake.rows > 0 && handshake.cols > 0) { 5183 TRACE(("handshake read ttysize: %dx%d\n", 5184 handshake.rows, handshake.cols)); 5185 set_max_row(screen, handshake.rows); 5186 set_max_col(screen, handshake.cols); 5187#ifdef TTYSIZE_STRUCT 5188 got_handshake_size = True; 5189 setup_winsize(ts, MaxRows(screen), MaxCols(screen), 5190 FullHeight(screen), FullWidth(screen)); 5191 trace_winsize(ts, "got handshake"); 5192#endif /* TTYSIZE_STRUCT */ 5193 } 5194 } 5195 } 5196#endif /* OPT_PTY_HANDSHAKE */ 5197 5198#ifdef USE_SYSV_ENVVARS 5199 { 5200 char numbuf[12]; 5201 sprintf(numbuf, "%d", MaxCols(screen)); 5202 xtermSetenv("COLUMNS", numbuf); 5203 sprintf(numbuf, "%d", MaxRows(screen)); 5204 xtermSetenv("LINES", numbuf); 5205 } 5206#ifdef HAVE_UTMP 5207 if (OkPasswd(&pw)) { /* SVR4 doesn't provide these */ 5208 if (!x_getenv("HOME")) 5209 xtermSetenv("HOME", pw.pw_dir); 5210 if (!x_getenv("SHELL")) 5211 xtermSetenv("SHELL", pw.pw_shell); 5212 } 5213#endif /* HAVE_UTMP */ 5214#else /* USE_SYSV_ENVVARS */ 5215 if (*(newtc = get_tcap_buffer(xw)) != '\0') { 5216 resize_termcap(xw); 5217 if (xw->misc.titeInhibit && !xw->misc.tiXtraScroll) { 5218 remove_termcap_entry(newtc, "ti="); 5219 remove_termcap_entry(newtc, "te="); 5220 } 5221 /* 5222 * work around broken termcap entries */ 5223 if (resource.useInsertMode) { 5224 remove_termcap_entry(newtc, "ic="); 5225 /* don't get duplicates */ 5226 remove_termcap_entry(newtc, "im="); 5227 remove_termcap_entry(newtc, "ei="); 5228 remove_termcap_entry(newtc, "mi"); 5229 if (*newtc) 5230 strcat(newtc, ":im=\\E[4h:ei=\\E[4l:mi:"); 5231 } 5232 if (*newtc) { 5233#if OPT_INITIAL_ERASE 5234#define TERMCAP_ERASE "kb" 5235 unsigned len; 5236 remove_termcap_entry(newtc, TERMCAP_ERASE "="); 5237 len = (unsigned) strlen(newtc); 5238 if (len != 0 && newtc[len - 1] == ':') 5239 len--; 5240 sprintf(newtc + len, ":%s=\\%03o:", 5241 TERMCAP_ERASE, 5242 CharOf(initial_erase)); 5243#endif 5244 xtermSetenv("TERMCAP", newtc); 5245 } 5246 } 5247#endif /* USE_SYSV_ENVVARS */ 5248#ifdef OWN_TERMINFO_ENV 5249 xtermSetenv("TERMINFO", OWN_TERMINFO_DIR); 5250#endif 5251 5252#if OPT_PTY_HANDSHAKE 5253 /* 5254 * Need to reset after all the ioctl bashing we did above. 5255 * 5256 * If we expect the waitForMap logic to set the handshake-size, 5257 * use that to prevent races. 5258 */ 5259 TRACE(("should we reset screensize after pty-handshake?\n")); 5260 TRACE(("... ptyHandshake :%d\n", resource.ptyHandshake)); 5261 TRACE(("... ptySttySize :%d\n", resource.ptySttySize)); 5262 TRACE(("... got_handshake_size:%d\n", got_handshake_size)); 5263 TRACE(("... wait_for_map0 :%d\n", resource.wait_for_map0)); 5264 if (resource.ptyHandshake 5265 && resource.ptySttySize 5266 && (got_handshake_size || !resource.wait_for_map0)) { 5267#ifdef TTYSIZE_STRUCT 5268 TRACE_RC(i, SET_TTYSIZE(0, ts)); 5269 trace_winsize(ts, "ptyHandshake SET_TTYSIZE"); 5270#endif /* TTYSIZE_STRUCT */ 5271 } 5272#endif /* OPT_PTY_HANDSHAKE */ 5273 signal(SIGHUP, SIG_DFL); 5274 5275 /* 5276 * If we have an explicit shell to run, make that set $SHELL. 5277 * Next, allow an existing setting of $SHELL, for absolute paths. 5278 * Otherwise, if $SHELL is not set, determine it from the user's 5279 * password information, if possible. 5280 * 5281 * Incidentally, our setting of $SHELL tells luit to use that 5282 * program rather than choosing between $SHELL and "/bin/sh". 5283 */ 5284 if (validShell(explicit_shname)) { 5285 xtermSetenv("SHELL", explicit_shname); 5286 } else if (validProgram(shell_path = x_getenv("SHELL"))) { 5287 if (!validShell(shell_path)) { 5288 xtermUnsetenv("SHELL"); 5289 } 5290 } else if ((!OkPasswd(&pw) && !x_getpwuid(screen->uid, &pw)) 5291 || *(shell_path = x_strdup(pw.pw_shell)) == 0) { 5292 shell_path = resetShell(shell_path); 5293 } else if (validShell(shell_path)) { 5294 xtermSetenv("SHELL", shell_path); 5295 } else { 5296 shell_path = resetShell(shell_path); 5297 } 5298 5299 /* 5300 * Set $XTERM_SHELL, which is not necessarily a valid shell, but 5301 * is executable. 5302 */ 5303 if (validProgram(explicit_shname)) { 5304 shell_path = explicit_shname; 5305 } else if (shell_path == 0) { 5306 /* this could happen if the explicit shname lost a race */ 5307 shell_path = resetShell(shell_path); 5308 } 5309 xtermSetenv("XTERM_SHELL", shell_path); 5310 5311 shname = x_basename(shell_path); 5312 TRACE(("shell path '%s' leaf '%s'\n", shell_path, shname)); 5313 5314#if OPT_LUIT_PROG 5315 /* 5316 * Use two copies of command_to_exec, in case luit is not actually 5317 * there, or refuses to run. In that case we will fall-through to 5318 * to command that the user gave anyway. 5319 */ 5320 if (command_to_exec_with_luit && command_to_exec) { 5321 char *myShell = xtermFindShell(*command_to_exec_with_luit, False); 5322 xtermSetenv("XTERM_SHELL", myShell); 5323 free(myShell); 5324 TRACE_ARGV("spawning luit command", command_to_exec_with_luit); 5325 execvp(*command_to_exec_with_luit, command_to_exec_with_luit); 5326 xtermPerror("Can't execvp %s", *command_to_exec_with_luit); 5327 xtermWarning("cannot support your locale.\n"); 5328 } 5329#endif 5330 if (command_to_exec) { 5331 char *myShell = xtermFindShell(*command_to_exec, False); 5332 xtermSetenv("XTERM_SHELL", myShell); 5333 free(myShell); 5334 TRACE_ARGV("spawning command", command_to_exec); 5335 execvp(*command_to_exec, command_to_exec); 5336 if (command_to_exec[1] == 0) 5337 execlp(shell_path, shname, "-c", command_to_exec[0], 5338 (void *) 0); 5339 xtermPerror("Can't execvp %s", *command_to_exec); 5340 } 5341#ifdef USE_SYSV_SIGHUP 5342 /* fix pts sh hanging around */ 5343 signal(SIGHUP, SIG_DFL); 5344#endif 5345 5346 if ((shname_minus = (char *) malloc(strlen(shname) + 2)) != 0) { 5347 (void) strcpy(shname_minus, "-"); 5348 (void) strcat(shname_minus, shname); 5349 } else { 5350 static char default_minus[] = "-sh"; 5351 shname_minus = default_minus; 5352 } 5353#ifndef TERMIO_STRUCT 5354 ldisc = (!XStrCmp("csh", shname + strlen(shname) - 3) 5355 ? NTTYDISC 5356 : 0); 5357 ioctl(0, TIOCSETD, (char *) &ldisc); 5358#endif /* !TERMIO_STRUCT */ 5359 5360#ifdef USE_LOGIN_DASH_P 5361 if (xw->misc.login_shell && OkPasswd(&pw) && added_utmp_entry) 5362 execl(bin_login, "login", "-p", "-f", login_name, (void *) 0); 5363#endif 5364 5365#if OPT_LUIT_PROG 5366 if (command_to_exec_with_luit) { 5367 if (xw->misc.login_shell) { 5368 char *params[4]; 5369 params[0] = x_strdup("-argv0"); 5370 params[1] = shname_minus; 5371 params[2] = NULL; 5372 x_appendargv(command_to_exec_with_luit 5373 + command_length_with_luit, 5374 params); 5375 } 5376 TRACE_ARGV("final luit command", command_to_exec_with_luit); 5377 execvp(*command_to_exec_with_luit, command_to_exec_with_luit); 5378 /* Exec failed. */ 5379 xtermPerror("Can't execvp %s", *command_to_exec_with_luit); 5380 } 5381#endif 5382 execlp(shell_path, 5383 (xw->misc.login_shell ? shname_minus : shname), 5384 (void *) 0); 5385 5386 /* Exec failed. */ 5387 xtermPerror("Could not exec %s", shell_path); 5388 IGNORE_RC(sleep(5)); 5389 free(shell_path); 5390 exit(ERROR_EXEC); 5391 } 5392 /* end if in child after fork */ 5393#if OPT_PTY_HANDSHAKE 5394 if (resource.ptyHandshake) { 5395 /* Parent process. Let's handle handshaked requests to our 5396 * child process. 5397 */ 5398 5399 /* close childs's sides of the pipes */ 5400 close(cp_pipe[1]); 5401 close(pc_pipe[0]); 5402 5403 for (done = 0; !done;) { 5404 if (read(cp_pipe[0], 5405 (char *) &handshake, 5406 sizeof(handshake)) <= 0) { 5407 /* Our child is done talking to us. If it terminated 5408 * due to an error, we will catch the death of child 5409 * and clean up. 5410 */ 5411 break; 5412 } 5413 5414 TRACE_HANDSHAKE("read", &handshake); 5415 switch (handshake.status) { 5416 case PTY_GOOD: 5417 /* Success! Let's free up resources and 5418 * continue. 5419 */ 5420 done = 1; 5421 break; 5422 5423 case PTY_BAD: 5424 /* The open of the pty failed! Let's get 5425 * another one. 5426 */ 5427 IGNORE_RC(close(screen->respond)); 5428 if (get_pty(&screen->respond, XDisplayString(screen->display))) { 5429 /* no more ptys! */ 5430 xtermPerror("child process can find no available ptys"); 5431 handshake.status = PTY_NOMORE; 5432 TRACE_HANDSHAKE("writing", &handshake); 5433 IGNORE_RC(write(pc_pipe[1], 5434 (const char *) &handshake, 5435 sizeof(handshake))); 5436 exit(ERROR_PTYS); 5437 } 5438 handshake.status = PTY_NEW; 5439 copy_handshake(handshake, ttydev); 5440 TRACE_HANDSHAKE("writing", &handshake); 5441 IGNORE_RC(write(pc_pipe[1], 5442 (const char *) &handshake, 5443 sizeof(handshake))); 5444 break; 5445 5446 case PTY_FATALERROR: 5447 errno = handshake.error; 5448 close(cp_pipe[0]); 5449 close(pc_pipe[1]); 5450 SysError(handshake.fatal_error); 5451 /*NOTREACHED */ 5452 5453 case UTMP_ADDED: 5454 /* The utmp entry was set by our slave. Remember 5455 * this so that we can reset it later. 5456 */ 5457 added_utmp_entry = True; 5458#ifndef USE_SYSV_UTMP 5459 tslot = handshake.tty_slot; 5460#endif /* USE_SYSV_UTMP */ 5461 free(ttydev); 5462 handshake.buffer[HANDSHAKE_LEN - 1] = '\0'; 5463 ttydev = x_strdup(handshake.buffer); 5464 break; 5465 case PTY_NEW: 5466 case PTY_NOMORE: 5467 case UTMP_TTYSLOT: 5468 case PTY_EXEC: 5469 default: 5470 xtermWarning("unexpected handshake status %d\n", 5471 (int) handshake.status); 5472 } 5473 } 5474 /* close our sides of the pipes */ 5475 if (!resource.wait_for_map) { 5476 close(cp_pipe[0]); 5477 close(pc_pipe[1]); 5478 } 5479 } 5480#endif /* OPT_PTY_HANDSHAKE */ 5481 } 5482 5483 /* end if no slave */ 5484 /* 5485 * still in parent (xterm process) 5486 */ 5487#ifdef USE_SYSV_SIGHUP 5488 /* hung sh problem? */ 5489 signal(SIGHUP, SIG_DFL); 5490#else 5491 signal(SIGHUP, SIG_IGN); 5492#endif 5493 5494/* 5495 * Unfortunately, System V seems to have trouble divorcing the child process 5496 * from the process group of xterm. This is a problem because hitting the 5497 * INTR or QUIT characters on the keyboard will cause xterm to go away if we 5498 * don't ignore the signals. This is annoying. 5499 */ 5500 5501#if defined(USE_SYSV_SIGNALS) && !defined(SIGTSTP) 5502 signal(SIGINT, SIG_IGN); 5503 5504#ifndef SYSV 5505 /* hung shell problem */ 5506 signal(SIGQUIT, SIG_IGN); 5507#endif 5508 signal(SIGTERM, SIG_IGN); 5509#elif defined(SYSV) || defined(__osf__) 5510 /* if we were spawned by a jobcontrol smart shell (like ksh or csh), 5511 * then our pgrp and pid will be the same. If we were spawned by 5512 * a jobcontrol dumb shell (like /bin/sh), then we will be in our 5513 * parent's pgrp, and we must ignore keyboard signals, or we will 5514 * tank on everything. 5515 */ 5516 if (getpid() == getpgrp()) { 5517 (void) signal(SIGINT, Exit); 5518 (void) signal(SIGQUIT, Exit); 5519 (void) signal(SIGTERM, Exit); 5520 } else { 5521 (void) signal(SIGINT, SIG_IGN); 5522 (void) signal(SIGQUIT, SIG_IGN); 5523 (void) signal(SIGTERM, SIG_IGN); 5524 } 5525 (void) signal(SIGPIPE, Exit); 5526#else /* SYSV */ 5527 signal(SIGINT, Exit); 5528 signal(SIGQUIT, Exit); 5529 signal(SIGTERM, Exit); 5530 signal(SIGPIPE, Exit); 5531#endif /* USE_SYSV_SIGNALS and not SIGTSTP */ 5532#ifdef NO_LEAKS 5533 if (ok_termcap != True) 5534 free(TermName); 5535#endif 5536 5537 return 0; 5538} /* end spawnXTerm */ 5539 5540void 5541Exit(int n) 5542{ 5543 XtermWidget xw = term; 5544 TScreen *screen = TScreenOf(xw); 5545 5546#ifdef USE_UTEMPTER 5547 DEBUG_MSG("handle:Exit USE_UTEMPTER\n"); 5548 if (!resource.utmpInhibit && added_utmp_entry) { 5549 TRACE(("...calling removeFromUtmp\n")); 5550 UTEMPTER_DEL(); 5551 } 5552#elif defined(HAVE_UTMP) 5553#ifdef USE_SYSV_UTMP 5554 struct UTMP_STR utmp; 5555 struct UTMP_STR *utptr; 5556 5557 DEBUG_MSG("handle:Exit USE_SYSV_UTMP\n"); 5558 /* don't do this more than once */ 5559 if (xterm_exiting) { 5560 exit(n); 5561 } 5562 xterm_exiting = True; 5563 5564#ifdef PUCC_PTYD 5565 closepty(ttydev, ptydev, (resource.utmpInhibit ? OPTY_NOP : OPTY_LOGIN), screen->respond); 5566#endif /* PUCC_PTYD */ 5567 5568 /* cleanup the utmp entry we forged earlier */ 5569 if (!resource.utmpInhibit 5570#if OPT_PTY_HANDSHAKE /* without handshake, no way to know */ 5571 && (resource.ptyHandshake && added_utmp_entry) 5572#endif /* OPT_PTY_HANDSHAKE */ 5573 ) { 5574#if defined(USE_UTMP_SETGID) 5575 setEffectiveGroup(save_egid); 5576 TRACE_IDS; 5577#endif 5578 init_utmp(USER_PROCESS, &utmp); 5579 (void) call_setutent(); 5580 5581 /* 5582 * We could use getutline() if we didn't support old systems. 5583 */ 5584 while ((utptr = find_utmp(&utmp)) != 0) { 5585 if (utptr->ut_pid == screen->pid) { 5586 utptr->ut_type = DEAD_PROCESS; 5587#if defined(HAVE_UTMP_UT_XTIME) 5588#if defined(HAVE_UTMP_UT_SESSION) 5589 utptr->ut_session = getsid(0); 5590#endif 5591 utptr->ut_xtime = time((time_t *) 0); 5592 utptr->ut_tv.tv_usec = 0; 5593#else 5594 *utptr->ut_user = 0; 5595 utptr->ut_time = time((time_t *) 0); 5596#endif 5597 (void) call_pututline(utptr); 5598#ifdef WTMP 5599#if defined(WTMPX_FILE) && (defined(SVR4) || defined(__SCO__)) 5600 if (xw->misc.login_shell) 5601 updwtmpx(WTMPX_FILE, utptr); 5602#elif defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ >= 2) && !(defined(__powerpc__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)) 5603 copy_filled(utmp.ut_line, utptr->ut_line, sizeof(utmp.ut_line)); 5604 if (xw->misc.login_shell) 5605 call_updwtmp(etc_wtmp, utptr); 5606#else 5607 /* set wtmp entry if wtmp file exists */ 5608 if (xw->misc.login_shell) { 5609 int fd; 5610 if ((fd = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 5611 IGNORE_RC(write(fd, utptr, sizeof(*utptr))); 5612 close(fd); 5613 } 5614 } 5615#endif 5616#endif 5617 break; 5618 } 5619 memset(utptr, 0, sizeof(*utptr)); /* keep searching */ 5620 } 5621 (void) call_endutent(); 5622#ifdef USE_UTMP_SETGID 5623 disableSetGid(); 5624 TRACE_IDS; 5625#endif 5626 } 5627#else /* not USE_SYSV_UTMP */ 5628 int wfd; 5629 struct utmp utmp; 5630 5631 DEBUG_MSG("handle:Exit !USE_SYSV_UTMP\n"); 5632 if (!resource.utmpInhibit && added_utmp_entry && 5633 (am_slave < 0 && tslot > 0)) { 5634#if defined(USE_UTMP_SETGID) 5635 setEffectiveGroup(save_egid); 5636 TRACE_IDS; 5637#endif 5638 if ((wfd = open(etc_utmp, O_WRONLY)) >= 0) { 5639 memset(&utmp, 0, sizeof(utmp)); 5640 lseek(wfd, (long) (tslot * sizeof(utmp)), 0); 5641 IGNORE_RC(write(wfd, (char *) &utmp, sizeof(utmp))); 5642 close(wfd); 5643 } 5644#ifdef WTMP 5645 if (xw->misc.login_shell && 5646 (wfd = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 5647 copy_filled(utmp.ut_line, 5648 my_pty_name(ttydev), 5649 sizeof(utmp.ut_line)); 5650 utmp.ut_time = time((time_t *) 0); 5651 IGNORE_RC(write(wfd, (char *) &utmp, sizeof(utmp))); 5652 close(wfd); 5653 } 5654#endif /* WTMP */ 5655#ifdef USE_UTMP_SETGID 5656 disableSetGid(); 5657 TRACE_IDS; 5658#endif 5659 } 5660#endif /* USE_SYSV_UTMP */ 5661#endif /* HAVE_UTMP */ 5662 5663 cleanup_colored_cursor(); 5664 5665 /* 5666 * Flush pending data before releasing ownership, so nobody else can write 5667 * in the middle of the data. 5668 */ 5669 ttyFlush(screen->respond); 5670 5671#ifdef USE_PTY_SEARCH 5672 if (am_slave < 0) { 5673 TRACE_IDS; 5674 /* restore ownership of tty and pty */ 5675 set_owner(ttydev, 0, 0, 0666U); 5676#if (defined(USE_PTY_DEVICE) && !defined(__sgi) && !defined(__hpux)) 5677 set_owner(ptydev, 0, 0, 0666U); 5678#endif 5679 } 5680#endif 5681 5682 /* 5683 * Close after releasing ownership to avoid race condition: other programs 5684 * grabbing it, and *then* having us release ownership.... 5685 */ 5686 close(screen->respond); /* close explicitly to avoid race with slave side */ 5687#ifdef ALLOWLOGGING 5688 if (screen->logging) 5689 CloseLog(xw); 5690#endif 5691 5692 xtermPrintOnXError(xw, n); 5693 5694#ifdef NO_LEAKS 5695 if (n == 0) { 5696 Display *dpy = TScreenOf(xw)->display; 5697 5698 TRACE(("Freeing memory leaks\n")); 5699 5700 if (toplevel) { 5701 XtDestroyWidget(toplevel); 5702 TRACE(("destroyed top-level widget\n")); 5703 } 5704 sortedOpts(0, 0, 0); 5705 noleaks_charproc(); 5706 noleaks_ptydata(); 5707#if OPT_GRAPHICS 5708 noleaks_graphics(dpy); 5709#endif 5710#if OPT_WIDE_CHARS 5711 noleaks_CharacterClass(); 5712#endif 5713 /* XrmSetDatabase(dpy, 0); increases leaks ;-) */ 5714 XtCloseDisplay(dpy); 5715 XtDestroyApplicationContext(app_con); 5716 xtermCloseSession(); 5717 TRACE(("closed display\n")); 5718 5719 TRACE_CLOSE(); 5720 } 5721#endif 5722 5723 exit(n); 5724} 5725 5726/* ARGSUSED */ 5727static void 5728resize_termcap(XtermWidget xw) 5729{ 5730 char *newtc = get_tcap_buffer(xw); 5731 5732#ifndef USE_SYSV_ENVVARS 5733 if (!TEK4014_ACTIVE(xw) && newtc != NULL && *newtc) { 5734 TScreen *screen = TScreenOf(xw); 5735 char *ptr1, *ptr2; 5736 size_t i; 5737 int li_first = 0; 5738 char *temp; 5739 char oldtc[TERMCAP_SIZE]; 5740 5741 strcpy(oldtc, newtc); 5742 TRACE(("resize %s\n", oldtc)); 5743 if ((ptr1 = x_strindex(oldtc, "co#")) == NULL) { 5744 strcat(oldtc, "co#80:"); 5745 ptr1 = x_strindex(oldtc, "co#"); 5746 } 5747 if ((ptr2 = x_strindex(oldtc, "li#")) == NULL) { 5748 strcat(oldtc, "li#24:"); 5749 ptr2 = x_strindex(oldtc, "li#"); 5750 } 5751 if (ptr1 > ptr2) { 5752 li_first++; 5753 temp = ptr1; 5754 ptr1 = ptr2; 5755 ptr2 = temp; 5756 } 5757 ptr1 += 3; 5758 ptr2 += 3; 5759 strncpy(newtc, oldtc, i = (size_t) (ptr1 - oldtc)); 5760 if (i >= TERMCAP_SIZE - 10) { 5761 TRACE(("...insufficient space: %lu\n", (unsigned long) i)); 5762 return; 5763 } 5764 temp = newtc + i; 5765 sprintf(temp, "%d", (li_first 5766 ? MaxRows(screen) 5767 : MaxCols(screen))); 5768 temp += strlen(temp); 5769 if ((ptr1 = strchr(ptr1, ':')) != 0 && (ptr1 < ptr2)) { 5770 strncpy(temp, ptr1, i = (size_t) (ptr2 - ptr1)); 5771 temp += i; 5772 sprintf(temp, "%d", (li_first 5773 ? MaxCols(screen) 5774 : MaxRows(screen))); 5775 if ((ptr2 = strchr(ptr2, ':')) != 0) { 5776 strcat(temp, ptr2); 5777 } 5778 } 5779 TRACE((" ==> %s\n", newtc)); 5780 TRACE((" new size %dx%d\n", MaxRows(screen), MaxCols(screen))); 5781 } 5782#endif /* USE_SYSV_ENVVARS */ 5783} 5784 5785#endif /* ! VMS */ 5786 5787/* 5788 * Does a non-blocking wait for a child process. If the system 5789 * doesn't support non-blocking wait, do nothing. 5790 * Returns the pid of the child, or 0 or -1 if none or error. 5791 */ 5792int 5793nonblocking_wait(void) 5794{ 5795#ifdef USE_POSIX_WAIT 5796 pid_t pid; 5797 5798 pid = waitpid(-1, NULL, WNOHANG); 5799#elif defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP)) 5800 /* cannot do non-blocking wait */ 5801 int pid = 0; 5802#else /* defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP)) */ 5803#if defined(Lynx) 5804 int status; 5805#else 5806 union wait status; 5807#endif 5808 int pid; 5809 5810 pid = wait3(&status, WNOHANG, (struct rusage *) NULL); 5811#endif /* USE_POSIX_WAIT else */ 5812 return pid; 5813} 5814 5815#ifndef VMS 5816 5817/* ARGSUSED */ 5818static void 5819reapchild(int n GCC_UNUSED) 5820{ 5821 int olderrno = errno; 5822 int pid; 5823 5824 DEBUG_MSG("handle:reapchild\n"); 5825 5826 pid = wait(NULL); 5827 5828#ifdef USE_SYSV_SIGNALS 5829 /* cannot re-enable signal before waiting for child 5830 * because then SVR4 loops. Sigh. HP-UX 9.01 too. 5831 */ 5832 (void) signal(SIGCHLD, reapchild); 5833#endif 5834 5835 do { 5836 if (pid == TScreenOf(term)->pid) { 5837 DEBUG_MSG("Exiting\n"); 5838 if (hold_screen) 5839 caught_intr = True; 5840 else 5841 need_cleanup = True; 5842 } 5843 } while ((pid = nonblocking_wait()) > 0); 5844 5845 errno = olderrno; 5846} 5847#endif /* !VMS */ 5848 5849static void 5850remove_termcap_entry(char *buf, const char *str) 5851{ 5852 char *base = buf; 5853 char *first = base; 5854 int count = 0; 5855 size_t len = strlen(str); 5856 5857 TRACE(("*** remove_termcap_entry('%s', '%s')\n", str, buf)); 5858 5859 while (*buf != 0) { 5860 if (!count && !strncmp(buf, str, len)) { 5861 while (*buf != 0) { 5862 if (*buf == '\\') 5863 buf++; 5864 else if (*buf == ':') 5865 break; 5866 if (*buf != 0) 5867 buf++; 5868 } 5869 while ((*first++ = *buf++) != 0) { 5870 ; 5871 } 5872 TRACE(("...removed_termcap_entry('%s', '%s')\n", str, base)); 5873 return; 5874 } else if (*buf == '\\') { 5875 buf++; 5876 } else if (*buf == ':') { 5877 first = buf; 5878 count = 0; 5879 } else if (!isspace(CharOf(*buf))) { 5880 count++; 5881 } 5882 if (*buf != 0) 5883 buf++; 5884 } 5885 TRACE(("...cannot remove\n")); 5886} 5887 5888/* 5889 * parse_tty_modes accepts lines of the following form: 5890 * 5891 * [SETTING] ... 5892 * 5893 * where setting consists of the words in the ttyModes[] array followed by a 5894 * character or ^char. 5895 */ 5896static int 5897parse_tty_modes(char *s) 5898{ 5899 int c; 5900 Cardinal j, k; 5901 int count = 0; 5902 Boolean found; 5903 5904 TRACE(("parse_tty_modes\n")); 5905 for (;;) { 5906 size_t len; 5907 5908 while (*s && isspace(CharOf(*s))) { 5909 s++; 5910 } 5911 if (!*s) { 5912 return count; 5913 } 5914 5915 for (len = 0; s[len] && !isspace(CharOf(s[len])); ++len) { 5916 ; 5917 } 5918 found = False; 5919 for (j = 0; j < XtNumber(ttyModes); ++j) { 5920 if (len == ttyModes[j].len 5921 && strncmp(s, 5922 ttyModes[j].name, 5923 ttyModes[j].len) == 0) { 5924 found = True; 5925 break; 5926 } 5927 } 5928 if (!found) { 5929 return -1; 5930 } 5931 5932 s += ttyModes[j].len; 5933 while (*s && isspace(CharOf(*s))) { 5934 s++; 5935 } 5936 5937 /* check if this needs a parameter */ 5938 found = False; 5939 for (k = 0, c = 0; k < XtNumber(ttyChars); ++k) { 5940 if ((int) j == ttyChars[k].myMode) { 5941 if (ttyChars[k].sysMode < 0) { 5942 found = True; 5943 c = ttyChars[k].myDefault; 5944 } 5945 break; 5946 } 5947 } 5948 5949 if (!found) { 5950 if (!*s 5951 || (c = decode_keyvalue(&s, False)) == -1) { 5952 return -1; 5953 } 5954 } 5955 ttyModes[j].value = c; 5956 ttyModes[j].set = 1; 5957 count++; 5958 TRACE(("...parsed #%d: %s=%#x\n", count, ttyModes[j].name, c)); 5959 } 5960} 5961 5962#ifndef GetBytesAvailable 5963int 5964GetBytesAvailable(Display *dpy) 5965{ 5966 int fd = ConnectionNumber(dpy); 5967 int result; 5968#if defined(FIONREAD) 5969 ioctl(fd, FIONREAD, (char *) &result); 5970#elif defined(__CYGWIN__) 5971 fd_set set; 5972 struct timeval select_timeout = 5973 {0, 0}; 5974 5975 FD_ZERO(&set); 5976 FD_SET(fd, &set); 5977 result = (Select(fd + 1, &set, NULL, NULL, &select_timeout) > 0) ? 1 : 0; 5978#else 5979 struct pollfd pollfds[1]; 5980 5981 pollfds[0].fd = fd; 5982 pollfds[0].events = POLLIN; 5983 result = poll(pollfds, 1, 0); 5984#endif 5985 return result; 5986} 5987#endif /* !VMS */ 5988 5989/* Utility function to try to hide system differences from 5990 everybody who used to call killpg() */ 5991 5992int 5993kill_process_group(int pid, int sig) 5994{ 5995 TRACE(("kill_process_group(pid=%d, sig=%d)\n", pid, sig)); 5996#if defined(SVR4) || defined(SYSV) || !defined(X_NOT_POSIX) 5997 return kill(-pid, sig); 5998#else 5999 return killpg(pid, sig); 6000#endif 6001} 6002 6003#if OPT_EBCDIC 6004int 6005A2E(int x) 6006{ 6007 char c; 6008 c = x; 6009 __atoe_l(&c, 1); 6010 return c; 6011} 6012 6013int 6014E2A(int x) 6015{ 6016 char c; 6017 c = x; 6018 __etoa_l(&c, 1); 6019 return c; 6020} 6021#endif 6022 6023#if defined(__QNX__) && !defined(__QNXNTO__) 6024#include <sys/types.h> 6025#include <sys/proc_msg.h> 6026#include <sys/kernel.h> 6027#include <string.h> 6028#include <errno.h> 6029 6030struct _proc_session ps; 6031struct _proc_session_reply rps; 6032 6033int 6034qsetlogin(char *login, char *ttyname) 6035{ 6036 int v = getsid(getpid()); 6037 6038 memset(&ps, 0, sizeof(ps)); 6039 memset(&rps, 0, sizeof(rps)); 6040 6041 ps.type = _PROC_SESSION; 6042 ps.subtype = _PROC_SUB_ACTION1; 6043 ps.sid = v; 6044 strcpy(ps.name, login); 6045 6046 Send(1, &ps, &rps, sizeof(ps), sizeof(rps)); 6047 6048 if (rps.status < 0) 6049 return (rps.status); 6050 6051 ps.type = _PROC_SESSION; 6052 ps.subtype = _PROC_SUB_ACTION2; 6053 ps.sid = v; 6054 sprintf(ps.name, "//%d%s", getnid(), ttyname); 6055 Send(1, &ps, &rps, sizeof(ps), sizeof(rps)); 6056 6057 return (rps.status); 6058} 6059#endif 6060 6061#ifdef __minix 6062int 6063setpgrp(void) 6064{ 6065 return 0; 6066} 6067 6068void 6069_longjmp(jmp_buf _env, int _val) 6070{ 6071 longjmp(_env, _val); 6072} 6073#endif 6074