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