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