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