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