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