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