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