main.c revision ad37e533
1/* $XTermId: main.c,v 1.877 2021/03/21 22:09:06 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 = argv[0];
2225
2226#ifdef HAVE_POSIX_SAVED_IDS
2227    save_euid = geteuid();
2228    save_egid = getegid();
2229#endif
2230
2231    save_ruid = getuid();
2232    save_rgid = getgid();
2233
2234#if defined(DISABLE_SETUID) || defined(DISABLE_SETGID)
2235#if defined(DISABLE_SETUID)
2236    disableSetUid();
2237#endif
2238#if defined(DISABLE_SETGID)
2239    disableSetGid();
2240#endif
2241    TRACE_IDS;
2242#endif
2243
2244    /* extra length in case longer tty name like /dev/ttyq255 */
2245    ttydev = TypeMallocN(char, sizeof(TTYDEV) + 80);
2246#ifdef USE_PTY_DEVICE
2247    ptydev = TypeMallocN(char, sizeof(PTYDEV) + 80);
2248    if (!ttydev || !ptydev)
2249#else
2250    if (!ttydev)
2251#endif
2252    {
2253	xtermWarning("unable to allocate memory for ttydev or ptydev\n");
2254	exit(1);
2255    }
2256    strcpy(ttydev, TTYDEV);
2257#ifdef USE_PTY_DEVICE
2258    strcpy(ptydev, PTYDEV);
2259#endif
2260
2261#if defined(USE_UTMP_SETGID)
2262    get_pty(NULL, NULL);
2263    disableSetUid();
2264    disableSetGid();
2265    TRACE_IDS;
2266#define get_pty(pty, from) really_get_pty(pty, from)
2267#endif
2268
2269    /* Do these first, since we may not be able to open the display */
2270    TRACE_OPTS(xtermOptions, optionDescList, XtNumber(optionDescList));
2271    TRACE_ARGV("Before XtOpenApplication", argv);
2272    if (argc > 1) {
2273	XrmOptionDescRec *option_ptr;
2274	char *option_value;
2275	int n;
2276	Bool quit = False;
2277
2278	for (n = 1; n < argc; n++) {
2279	    if ((option_ptr = parseArg(&n, argv, &option_value)) == 0) {
2280		if (argv[n] == 0) {
2281		    break;
2282		} else if (isOption(argv[n])) {
2283		    Syntax(argv[n]);
2284		} else if (explicit_shname != 0) {
2285		    xtermWarning("Explicit shell already was %s\n", explicit_shname);
2286		    Syntax(argv[n]);
2287		}
2288		explicit_shname = xtermFindShell(argv[n], True);
2289		if (explicit_shname == 0)
2290		    exit(0);
2291		TRACE(("...explicit shell %s\n", explicit_shname));
2292	    } else if (!strcmp(option_ptr->option, "-e")) {
2293		command_to_exec = (argv + n + 1);
2294		if (!command_to_exec[0])
2295		    Syntax(argv[n]);
2296		break;
2297	    } else if (!strcmp(option_ptr->option, "-version")) {
2298		Version();
2299		quit = True;
2300	    } else if (!strcmp(option_ptr->option, "-help")) {
2301		Help();
2302		quit = True;
2303	    } else if (!strcmp(option_ptr->option, "-baudrate")) {
2304		NeedParam(option_ptr, option_value);
2305		if ((line_speed = lookup_baudrate(option_value)) == 0) {
2306		    Help();
2307		    quit = True;
2308		}
2309	    } else if (!strcmp(option_ptr->option, "-class")) {
2310		NeedParam(option_ptr, option_value);
2311		free(my_class);
2312		if ((my_class = x_strdup(option_value)) == 0) {
2313		    Help();
2314		    quit = True;
2315		}
2316	    } else if (!strcmp(option_ptr->option, "-into")) {
2317		char *endPtr;
2318		NeedParam(option_ptr, option_value);
2319		winToEmbedInto = (Window) strtol(option_value, &endPtr, 0);
2320		if (!FullS2L(option_value, endPtr)) {
2321		    Help();
2322		    quit = True;
2323		}
2324	    }
2325	}
2326	if (quit)
2327	    exit(0);
2328	/*
2329	 * If there is anything left unparsed, and we're not using "-e",
2330	 * then give up.
2331	 */
2332	if (n < argc && !command_to_exec) {
2333	    Syntax(argv[n]);
2334	}
2335    }
2336
2337    /* This dumped core on HP-UX 9.05 with X11R5 */
2338#if OPT_I18N_SUPPORT
2339    XtSetLanguageProc(NULL, NULL, NULL);
2340#endif
2341
2342    /* enable Xft support in Xaw3DXft */
2343#if defined(HAVE_LIB_XAW3DXFT)
2344    GET_XAW3DXFT_DATA(xaw3dxft_data);
2345    xaw3dxft_data->encoding = -1;
2346#endif
2347
2348#ifdef TERMIO_STRUCT		/* { */
2349    /* Initialization is done here rather than above in order
2350     * to prevent any assumptions about the order of the contents
2351     * of the various terminal structures (which may change from
2352     * implementation to implementation).
2353     */
2354    memset(&d_tio, 0, sizeof(d_tio));
2355    d_tio.c_iflag = ICRNL | IXON;
2356    d_tio.c_oflag = TAB3 | D_TIO_FLAGS;
2357    {
2358	Cardinal nn;
2359
2360	/* fill in default-values */
2361	for (nn = 0; nn < XtNumber(ttyChars); ++nn) {
2362	    if (validTtyChar(d_tio, nn)) {
2363		d_tio.c_cc[ttyChars[nn].sysMode] =
2364		    (cc_t) ttyChars[nn].myDefault;
2365	    }
2366	}
2367    }
2368#if defined(macII) || defined(ATT) || defined(CRAY)	/* { */
2369    d_tio.c_cflag = line_speed | CS8 | CREAD | PARENB | HUPCL;
2370    d_tio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
2371#ifdef ECHOKE
2372    d_tio.c_lflag |= ECHOKE | IEXTEN;
2373#endif
2374#ifdef ECHOCTL
2375    d_tio.c_lflag |= ECHOCTL | IEXTEN;
2376#endif
2377#ifndef USE_TERMIOS		/* { */
2378    d_tio.c_line = 0;
2379#endif /* } */
2380#ifdef HAS_LTCHARS		/* { */
2381    d_ltc.t_suspc = CSUSP;	/* t_suspc */
2382    d_ltc.t_dsuspc = CDSUSP;	/* t_dsuspc */
2383    d_ltc.t_rprntc = CRPRNT;
2384    d_ltc.t_flushc = CFLUSH;
2385    d_ltc.t_werasc = CWERASE;
2386    d_ltc.t_lnextc = CLNEXT;
2387#endif /* } HAS_LTCHARS */
2388#ifdef TIOCLSET			/* { */
2389    d_lmode = 0;
2390#endif /* } TIOCLSET */
2391#else /* }{ else !macII, ATT, CRAY */
2392#ifndef USE_POSIX_TERMIOS
2393#ifdef BAUD_0			/* { */
2394    d_tio.c_cflag = CS8 | CREAD | PARENB | HUPCL;
2395#else /* }{ !BAUD_0 */
2396    d_tio.c_cflag = line_speed | CS8 | CREAD | PARENB | HUPCL;
2397#endif /* } !BAUD_0 */
2398#else /* USE_POSIX_TERMIOS */
2399    d_tio.c_cflag = CS8 | CREAD | PARENB | HUPCL;
2400    cfsetispeed(&d_tio, line_speed);
2401    cfsetospeed(&d_tio, line_speed);
2402#endif
2403    d_tio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
2404#ifdef ECHOKE
2405    d_tio.c_lflag |= ECHOKE | IEXTEN;
2406#endif
2407#ifdef ECHOCTL
2408    d_tio.c_lflag |= ECHOCTL | IEXTEN;
2409#endif
2410#ifndef USE_POSIX_TERMIOS
2411#ifdef NTTYDISC
2412    d_tio.c_line = NTTYDISC;
2413#else
2414    d_tio.c_line = 0;
2415#endif
2416#endif /* USE_POSIX_TERMIOS */
2417#ifdef __sgi
2418    d_tio.c_cflag &= ~(HUPCL | PARENB);
2419    d_tio.c_iflag |= BRKINT | ISTRIP | IGNPAR;
2420#endif
2421#ifdef __MVS__
2422    d_tio.c_cflag &= ~(HUPCL | PARENB);
2423#endif
2424    {
2425	Cardinal nn;
2426	int i;
2427
2428	/* try to inherit tty settings */
2429	for (i = 0; i <= 2; i++) {
2430	    TERMIO_STRUCT deftio;
2431	    if (ttyGetAttr(i, &deftio) == 0) {
2432		for (nn = 0; nn < XtNumber(ttyChars); ++nn) {
2433		    if (validTtyChar(d_tio, nn)) {
2434			d_tio.c_cc[ttyChars[nn].sysMode] =
2435			    deftio.c_cc[ttyChars[nn].sysMode];
2436		    }
2437		}
2438		break;
2439	    }
2440	}
2441    }
2442#if defined(USE_TERMIOS) || defined(USE_POSIX_TERMIOS)	/* { */
2443    d_tio.c_cc[VMIN] = 1;
2444    d_tio.c_cc[VTIME] = 0;
2445#endif /* } */
2446#ifdef HAS_LTCHARS		/* { */
2447    d_ltc.t_suspc = CharOf('\000');	/* t_suspc */
2448    d_ltc.t_dsuspc = CharOf('\000');	/* t_dsuspc */
2449    d_ltc.t_rprntc = CharOf('\377');	/* reserved... */
2450    d_ltc.t_flushc = CharOf('\377');
2451    d_ltc.t_werasc = CharOf('\377');
2452    d_ltc.t_lnextc = CharOf('\377');
2453#endif /* } HAS_LTCHARS */
2454
2455#ifdef TIOCLSET			/* { */
2456    d_lmode = 0;
2457#endif /* } TIOCLSET */
2458#endif /* } macII, ATT, CRAY */
2459#endif /* } TERMIO_STRUCT */
2460
2461    /* Init the Toolkit. */
2462    {
2463#if defined(HAVE_POSIX_SAVED_IDS) && !defined(USE_UTMP_SETGID) && !defined(USE_UTEMPTER)
2464	setEffectiveGroup(save_rgid);
2465	setEffectiveUser(save_ruid);
2466	TRACE_IDS;
2467#endif
2468	toplevel = xtermOpenApplication(&app_con,
2469					my_class,
2470					optionDescList,
2471					XtNumber(optionDescList),
2472					&argc, argv,
2473					fallback_resources,
2474					sessionShellWidgetClass,
2475					NULL, 0);
2476
2477	XtGetApplicationResources(toplevel, (XtPointer) &resource,
2478				  application_resources,
2479				  XtNumber(application_resources), NULL, 0);
2480	TRACE_XRES();
2481#ifdef HAVE_LIB_XCURSOR
2482	if (!strcmp(resource.cursorTheme, "none")) {
2483	    TRACE(("startup with no cursorTheme\n"));
2484	    init_colored_cursor(XtDisplay(toplevel));
2485	} else {
2486	    const char *theme = resource.cursorTheme;
2487	    if (IsEmpty(theme))
2488		theme = "default";
2489	    TRACE(("startup with \"%s\" cursorTheme\n", theme));
2490	    xtermSetenv("XCURSOR_THEME", theme);
2491	}
2492#endif
2493#if USE_DOUBLE_BUFFER
2494	if (resource.buffered_fps <= 0)
2495	    resource.buffered_fps = DEF_BUFFER_RATE;
2496	if (resource.buffered_fps > 100)
2497	    resource.buffered_fps = 100;
2498#endif
2499#if OPT_MAXIMIZE
2500	resource.fullscreen = extendedBoolean(resource.fullscreen_s,
2501					      tblFullscreen,
2502					      esLAST);
2503#endif
2504	VTInitTranslations();
2505#if OPT_PTY_HANDSHAKE
2506	resource.wait_for_map0 = resource.wait_for_map;
2507#endif
2508
2509#if defined(HAVE_POSIX_SAVED_IDS) && !defined(USE_UTMP_SETGID)
2510#if !defined(DISABLE_SETUID) || !defined(DISABLE_SETGID)
2511#if !defined(DISABLE_SETUID)
2512	setEffectiveUser(save_euid);
2513#endif
2514#if !defined(DISABLE_SETGID)
2515	setEffectiveGroup(save_egid);
2516#endif
2517	TRACE_IDS;
2518#endif
2519#endif
2520    }
2521
2522    /*
2523     * ICCCM delete_window.
2524     */
2525    XtAppAddActions(app_con, actionProcs, XtNumber(actionProcs));
2526
2527    /*
2528     * fill in terminal modes
2529     */
2530    if (resource.tty_modes) {
2531	int n = parse_tty_modes(resource.tty_modes);
2532	if (n < 0) {
2533	    xtermWarning("bad tty modes \"%s\"\n", resource.tty_modes);
2534	} else if (n > 0) {
2535	    override_tty_modes = True;
2536	}
2537    }
2538    initZIconBeep();
2539    hold_screen = resource.hold_screen ? 1 : 0;
2540    if (resource.icon_geometry != NULL) {
2541	int scr, junk;
2542	int ix, iy;
2543	Arg args[2];
2544
2545	for (scr = 0;		/* yyuucchh */
2546	     XtScreen(toplevel) != ScreenOfDisplay(XtDisplay(toplevel), scr);
2547	     scr++) ;
2548
2549	args[0].name = XtNiconX;
2550	args[1].name = XtNiconY;
2551	XGeometry(XtDisplay(toplevel), scr, resource.icon_geometry, "",
2552		  0, 0, 0, 0, 0, &ix, &iy, &junk, &junk);
2553	args[0].value = (XtArgVal) ix;
2554	args[1].value = (XtArgVal) iy;
2555	XtSetValues(toplevel, args, 2);
2556    }
2557
2558    XtSetValues(toplevel, ourTopLevelShellArgs,
2559		number_ourTopLevelShellArgs);
2560
2561#if OPT_WIDE_CHARS
2562    /* seems as good a place as any */
2563    init_classtab();
2564#endif
2565
2566    /* Parse the rest of the command line */
2567    TRACE_ARGV("After XtOpenApplication", argv);
2568    for (argc--, argv++; argc > 0; argc--, argv++) {
2569	if (!isOption(*argv)) {
2570#ifdef VMS
2571	    Syntax(*argv);
2572#else
2573	    if (argc > 1)
2574		Syntax(*argv);
2575	    continue;
2576#endif
2577	}
2578
2579	TRACE(("parsing %s\n", argv[0]));
2580	switch (argv[0][1]) {
2581	case 'C':
2582#if defined(TIOCCONS) || defined(SRIOCSREDIR)
2583#ifndef __sgi
2584	    {
2585		struct stat sbuf;
2586
2587		/* Must be owner and have read/write permission.
2588		   xdm cooperates to give the console the right user. */
2589		if (!stat("/dev/console", &sbuf) &&
2590		    (sbuf.st_uid == save_ruid) &&
2591		    !access("/dev/console", R_OK | W_OK)) {
2592		    Console = True;
2593		} else
2594		    Console = False;
2595	    }
2596#else /* __sgi */
2597	    Console = True;
2598#endif /* __sgi */
2599#endif /* TIOCCONS */
2600	    continue;
2601	case 'S':
2602	    if (!ParseSccn(*argv + 2))
2603		Syntax(*argv);
2604	    continue;
2605#ifdef DEBUG
2606	case 'D':
2607	    debug = True;
2608	    continue;
2609#endif /* DEBUG */
2610	case 'b':
2611	    if (strcmp(argv[0], "-baudrate"))
2612		Syntax(*argv);
2613	    argc--;
2614	    argv++;
2615	    continue;
2616	case 'c':
2617	    if (strcmp(argv[0], "-class"))
2618		Syntax(*argv);
2619	    argc--;
2620	    argv++;
2621	    continue;
2622	case 'e':
2623	    if (strcmp(argv[0], "-e"))
2624		Syntax(*argv);
2625	    command_to_exec = (argv + 1);
2626	    break;
2627	case 'i':
2628	    if (strcmp(argv[0], "-into"))
2629		Syntax(*argv);
2630	    argc--;
2631	    argv++;
2632	    continue;
2633
2634	default:
2635	    Syntax(*argv);
2636	}
2637	break;
2638    }
2639
2640    SetupMenus(toplevel, &form_top, &menu_top, &menu_high);
2641
2642    term = (XtermWidget) XtVaCreateManagedWidget("vt100", xtermWidgetClass,
2643						 form_top,
2644#if OPT_TOOLBAR
2645						 XtNmenuBar, menu_top,
2646						 XtNresizable, True,
2647						 XtNfromVert, menu_top,
2648						 XtNleft, XawChainLeft,
2649						 XtNright, XawChainRight,
2650						 XtNtop, XawChainTop,
2651						 XtNbottom, XawChainBottom,
2652						 XtNmenuHeight, menu_high,
2653#endif
2654						 (XtPointer) 0);
2655    decode_keyboard_type(term, &resource);
2656
2657    screen = TScreenOf(term);
2658    screen->inhibit = 0;
2659
2660#ifdef ALLOWLOGGING
2661    if (term->misc.logInhibit)
2662	screen->inhibit |= I_LOG;
2663#endif
2664    if (term->misc.signalInhibit)
2665	screen->inhibit |= I_SIGNAL;
2666#if OPT_TEK4014
2667    if (term->misc.tekInhibit)
2668	screen->inhibit |= I_TEK;
2669#endif
2670
2671    /*
2672     * We might start by showing the tek4014 window.
2673     */
2674#if OPT_TEK4014
2675    if (screen->inhibit & I_TEK)
2676	TEK4014_ACTIVE(term) = False;
2677
2678    if (TEK4014_ACTIVE(term) && !TekInit())
2679	SysError(ERROR_INIT);
2680#endif
2681
2682    /*
2683     * Start the toolbar at this point, after the first window has been setup.
2684     */
2685#if OPT_TOOLBAR
2686    ShowToolbar(resource.toolBar);
2687#endif
2688
2689    xtermOpenSession();
2690
2691    /*
2692     * Set title and icon name if not specified
2693     */
2694    if (command_to_exec) {
2695	Arg args[2];
2696
2697	if (!resource.title) {
2698	    if (command_to_exec) {
2699		resource.title = x_basename(command_to_exec[0]);
2700	    }			/* else not reached */
2701	}
2702
2703	if (!resource.icon_name)
2704	    resource.icon_name = resource.title;
2705	XtSetArg(args[0], XtNtitle, resource.title);
2706	XtSetArg(args[1], XtNiconName, resource.icon_name);
2707
2708	TRACE(("setting:\n\ttitle \"%s\"\n\ticon \"%s\"\n\thint \"%s\"\n\tbased on command \"%s\"\n",
2709	       resource.title,
2710	       resource.icon_name,
2711	       NonNull(resource.icon_hint),
2712	       *command_to_exec));
2713
2714	XtSetValues(toplevel, args, 2);
2715    }
2716#if OPT_LUIT_PROG
2717    if (term->misc.callfilter) {
2718	char **split_filter = x_splitargs(term->misc.localefilter);
2719	unsigned count_split = x_countargv(split_filter);
2720	unsigned count_exec = x_countargv(command_to_exec);
2721	unsigned count_using = (unsigned) (term->misc.use_encoding ? 2 : 0);
2722
2723	command_to_exec_with_luit = TypeCallocN(char *,
2724						  (count_split
2725						   + count_exec
2726						   + count_using
2727						   + 8));
2728	if (command_to_exec_with_luit == NULL)
2729	    SysError(ERROR_LUMALLOC);
2730
2731	x_appendargv(command_to_exec_with_luit, split_filter);
2732	if (count_using) {
2733	    char *encoding_opt[4];
2734	    encoding_opt[0] = x_strdup("-encoding");
2735	    encoding_opt[1] = term->misc.locale_str;
2736	    encoding_opt[2] = 0;
2737	    x_appendargv(command_to_exec_with_luit, encoding_opt);
2738	}
2739	command_length_with_luit = x_countargv(command_to_exec_with_luit);
2740	if (count_exec) {
2741	    static char *fixup_shell[] =
2742	    {(char *) "sh", (char *) "-c", 0};
2743	    char *delimiter[2];
2744	    delimiter[0] = x_strdup("--");
2745	    delimiter[1] = 0;
2746	    x_appendargv(command_to_exec_with_luit, delimiter);
2747	    if (complex_command(command_to_exec)) {
2748		x_appendargv(command_to_exec_with_luit, fixup_shell);
2749	    }
2750	    x_appendargv(command_to_exec_with_luit, command_to_exec);
2751	}
2752	TRACE_ARGV("luit command", command_to_exec_with_luit);
2753	xtermSetenv("XTERM_FILTER", *command_to_exec_with_luit);
2754    }
2755#endif
2756
2757    if_DEBUG({
2758	/* Set up stderr properly.  Opening this log file cannot be
2759	   done securely by a privileged xterm process (although we try),
2760	   so the debug feature is disabled by default. */
2761	char dbglogfile[TIMESTAMP_LEN + 20];
2762	int i = -1;
2763	timestamp_filename(dbglogfile, "xterm.debug.log.");
2764	if (creat_as(save_ruid, save_rgid, False, dbglogfile, 0600) > 0) {
2765	    i = open(dbglogfile, O_WRONLY | O_TRUNC);
2766	}
2767	if (i >= 0) {
2768	    dup2(i, 2);
2769
2770	    /* mark this file as close on exec */
2771	    (void) fcntl(i, F_SETFD, 1);
2772	}
2773    });
2774
2775    spawnXTerm(term, line_speed);
2776
2777#ifndef VMS
2778    /* Child process is out there, let's catch its termination */
2779
2780#ifdef USE_POSIX_SIGNALS
2781    (void) posix_signal(SIGCHLD, reapchild);
2782#else
2783    (void) signal(SIGCHLD, reapchild);
2784#endif
2785    /* Realize procs have now been executed */
2786
2787    if (am_slave >= 0) {	/* Write window id so master end can read and use */
2788	char buf[80];
2789
2790	buf[0] = '\0';
2791	sprintf(buf, "%lx\n", XtWindow(SHELL_OF(CURRENT_EMU())));
2792	IGNORE_RC(write(screen->respond, buf, strlen(buf)));
2793    }
2794#ifdef AIXV3
2795#if (OSMAJORVERSION < 4)
2796    /* In AIXV3, xterms started from /dev/console have CLOCAL set.
2797     * This means we need to clear CLOCAL so that SIGHUP gets sent
2798     * to the slave-pty process when xterm exits.
2799     */
2800
2801    {
2802	TERMIO_STRUCT tio;
2803
2804	if (ttyGetAttr(screen->respond, &tio) == -1)
2805	    SysError(ERROR_TIOCGETP);
2806
2807	tio.c_cflag &= ~(CLOCAL);
2808
2809	if (ttySetAttr(screen->respond, &tio) == -1)
2810	    SysError(ERROR_TIOCSETP);
2811    }
2812#endif
2813#endif
2814#if defined(USE_ANY_SYSV_TERMIO) || defined(__MVS__) || defined(__minix)
2815    if (0 > (mode = fcntl(screen->respond, F_GETFL, 0)))
2816	SysError(ERROR_F_GETFL);
2817#ifdef O_NDELAY
2818    mode |= O_NDELAY;
2819#else
2820    mode |= O_NONBLOCK;
2821#endif /* O_NDELAY */
2822    if (fcntl(screen->respond, F_SETFL, mode))
2823	SysError(ERROR_F_SETFL);
2824#else /* !USE_ANY_SYSV_TERMIO */
2825    mode = 1;
2826    if (ioctl(screen->respond, FIONBIO, (char *) &mode) == -1)
2827	SysError(ERROR_FIONBIO);
2828#endif /* USE_ANY_SYSV_TERMIO, etc */
2829
2830    /* The erase character is used to delete the current completion */
2831#if OPT_DABBREV
2832#ifdef TERMIO_STRUCT
2833    screen->dabbrev_erase_char = d_tio.c_cc[VERASE];
2834#else
2835    screen->dabbrev_erase_char = d_sg.sg_erase;
2836#endif
2837    TRACE(("set dabbrev erase_char %#x\n", screen->dabbrev_erase_char));
2838#endif
2839
2840    FD_ZERO(&pty_mask);
2841    FD_ZERO(&X_mask);
2842    FD_ZERO(&Select_mask);
2843    FD_SET(screen->respond, &pty_mask);
2844    FD_SET(ConnectionNumber(screen->display), &X_mask);
2845    FD_SET(screen->respond, &Select_mask);
2846    FD_SET(ConnectionNumber(screen->display), &Select_mask);
2847    max_plus1 = ((screen->respond < ConnectionNumber(screen->display))
2848		 ? (1 + ConnectionNumber(screen->display))
2849		 : (1 + screen->respond));
2850
2851#endif /* !VMS */
2852    if_DEBUG({
2853	TRACE(("debugging on pid %d\n", (int) getpid()));
2854    });
2855    XSetErrorHandler(xerror);
2856    XSetIOErrorHandler(xioerror);
2857#if OPT_SESSION_MGT
2858    IceSetIOErrorHandler(ice_error);
2859#endif
2860
2861    initPtyData(&VTbuffer);
2862#ifdef ALLOWLOGGING
2863    if (term->misc.log_on) {
2864	StartLog(term);
2865    }
2866#endif
2867
2868    xtermEmbedWindow(winToEmbedInto);
2869#if OPT_COLOR_RES
2870    TRACE(("checking reverseVideo before rv %s fg %s, bg %s\n",
2871	   term->misc.re_verse0 ? "reverse" : "normal",
2872	   NonNull(TScreenOf(term)->Tcolors[TEXT_FG].resource),
2873	   NonNull(TScreenOf(term)->Tcolors[TEXT_BG].resource)));
2874
2875    if (term->misc.re_verse0) {
2876	if (isDefaultForeground(TScreenOf(term)->Tcolors[TEXT_FG].resource)
2877	    && isDefaultBackground(TScreenOf(term)->Tcolors[TEXT_BG].resource)) {
2878	    TScreenOf(term)->Tcolors[TEXT_FG].resource = x_strdup(XtDefaultBackground);
2879	    TScreenOf(term)->Tcolors[TEXT_BG].resource = x_strdup(XtDefaultForeground);
2880	} else {
2881	    ReverseVideo(term);
2882	}
2883	term->misc.re_verse = True;
2884	update_reversevideo();
2885	TRACE(("updated  reverseVideo after  rv %s fg %s, bg %s\n",
2886	       term->misc.re_verse ? "reverse" : "normal",
2887	       NonNull(TScreenOf(term)->Tcolors[TEXT_FG].resource),
2888	       NonNull(TScreenOf(term)->Tcolors[TEXT_BG].resource)));
2889    }
2890#endif /* OPT_COLOR_RES */
2891
2892#if OPT_MAXIMIZE
2893    if (resource.maximized)
2894	RequestMaximize(term, True);
2895#endif
2896    for (;;) {
2897#if OPT_TEK4014
2898	if (TEK4014_ACTIVE(term))
2899	    TekRun();
2900	else
2901#endif
2902	    VTRun(term);
2903    }
2904}
2905
2906#if defined(__osf__) || (defined(__GLIBC__) && !defined(USE_USG_PTYS)) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
2907#define USE_OPENPTY 1
2908static int opened_tty = -1;
2909#endif
2910
2911/*
2912 * This function opens up a pty master and stuffs its value into pty.
2913 *
2914 * If it finds one, it returns a value of 0.  If it does not find one,
2915 * it returns a value of !0.  This routine is designed to be re-entrant,
2916 * so that if a pty master is found and later, we find that the slave
2917 * has problems, we can re-enter this function and get another one.
2918 */
2919static int
2920get_pty(int *pty, char *from GCC_UNUSED)
2921{
2922    int result = 1;
2923
2924#if defined(USE_OPENPTY)
2925    result = openpty(pty, &opened_tty, ttydev, NULL, NULL);
2926    if (opened_tty >= 0) {
2927	close(opened_tty);
2928	opened_tty = -1;
2929    }
2930#elif defined(HAVE_POSIX_OPENPT) && defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT_PTY_ISATTY)
2931    if ((*pty = posix_openpt(O_RDWR)) >= 0) {
2932	char *name = ptsname(*pty);
2933	if (name != 0) {
2934	    strcpy(ttydev, name);
2935	    result = 0;
2936	}
2937    }
2938#ifdef USE_PTY_SEARCH
2939    if (result) {
2940	result = pty_search(pty);
2941    }
2942#endif
2943#elif defined(PUCC_PTYD)
2944    result = ((*pty = openrpty(ttydev, ptydev,
2945			       (resource.utmpInhibit ? OPTY_NOP : OPTY_LOGIN),
2946			       save_ruid, from)) < 0);
2947#elif defined(__QNXNTO__)
2948    result = pty_search(pty);
2949#else
2950#if defined(USE_USG_PTYS) || defined(__CYGWIN__)
2951#if defined(__MVS__)
2952    result = pty_search(pty);
2953#else
2954    result = ((*pty = open("/dev/ptmx", O_RDWR)) < 0);
2955#endif
2956#if defined(SVR4) || defined(__SCO__)
2957    if (!result)
2958	strcpy(ttydev, ptsname(*pty));
2959#endif
2960
2961#elif defined(AIXV3)
2962
2963    if ((*pty = open("/dev/ptc", O_RDWR)) >= 0) {
2964	strcpy(ttydev, ttyname(*pty));
2965	result = 0;
2966    }
2967#elif defined(__convex__)
2968
2969    char *pty_name;
2970    extern char *getpty(void);
2971
2972    while ((pty_name = getpty()) != NULL) {
2973	if ((*pty = open(pty_name, O_RDWR)) >= 0) {
2974	    strcpy(ptydev, pty_name);
2975	    strcpy(ttydev, pty_name);
2976	    *x_basename(ttydev) = 't';
2977	    result = 0;
2978	    break;
2979	}
2980    }
2981
2982#elif defined(sequent)
2983
2984    result = ((*pty = getpseudotty(&ttydev, &ptydev)) < 0);
2985
2986#elif defined(__sgi) && (OSMAJORVERSION >= 4)
2987
2988    char *tty_name;
2989
2990    tty_name = _getpty(pty, O_RDWR, 0622, 0);
2991    if (tty_name != 0) {
2992	strcpy(ttydev, tty_name);
2993	result = 0;
2994    }
2995#elif (defined(__sgi) && (OSMAJORVERSION < 4)) || (defined(umips) && defined (SYSTYPE_SYSV))
2996
2997    struct stat fstat_buf;
2998
2999    *pty = open("/dev/ptc", O_RDWR);
3000    if (*pty >= 0 && (fstat(*pty, &fstat_buf)) >= 0) {
3001	result = 0;
3002	sprintf(ttydev, "/dev/ttyq%d", minor(fstat_buf.st_rdev));
3003    }
3004#elif defined(__hpux)
3005
3006    /*
3007     * Use the clone device if it works, otherwise use pty_search logic.
3008     */
3009    if ((*pty = open("/dev/ptym/clone", O_RDWR)) >= 0) {
3010	char *name = ptsname(*pty);
3011	if (name != 0) {
3012	    strcpy(ttydev, name);
3013	    result = 0;
3014	} else {		/* permissions, or other unexpected problem */
3015	    close(*pty);
3016	    *pty = -1;
3017	    result = pty_search(pty);
3018	}
3019    } else {
3020	result = pty_search(pty);
3021    }
3022
3023#else
3024
3025    result = pty_search(pty);
3026
3027#endif
3028#endif
3029
3030    TRACE(("get_pty(ttydev=%s, ptydev=%s) %s fd=%d\n",
3031	   ttydev != 0 ? ttydev : "?",
3032	   ptydev != 0 ? ptydev : "?",
3033	   result ? "FAIL" : "OK",
3034	   pty != 0 ? *pty : -1));
3035    return result;
3036}
3037
3038static void
3039set_pty_permissions(uid_t uid, unsigned gid, unsigned mode)
3040{
3041#ifdef USE_TTY_GROUP
3042    struct group *ttygrp;
3043
3044    if ((ttygrp = getgrnam(TTY_GROUP_NAME)) != 0) {
3045	gid = (unsigned) ttygrp->gr_gid;
3046	mode &= 0660U;
3047    }
3048    endgrent();
3049#endif /* USE_TTY_GROUP */
3050
3051    TRACE_IDS;
3052    set_owner(ttydev, (unsigned) uid, gid, mode);
3053}
3054
3055#ifdef get_pty			/* USE_UTMP_SETGID */
3056#undef get_pty
3057/*
3058 * Call the real get_pty() before relinquishing root-setuid, caching the
3059 * result.
3060 */
3061static int
3062get_pty(int *pty, char *from)
3063{
3064    static int m_pty = -1;
3065    int result = -1;
3066
3067    if (pty == NULL) {
3068	result = really_get_pty(&m_pty, from);
3069
3070	seteuid(0);
3071	set_pty_permissions(save_ruid, save_rgid, 0600U);
3072	seteuid(save_ruid);
3073	TRACE_IDS;
3074
3075    } else if (m_pty != -1) {
3076	*pty = m_pty;
3077	result = 0;
3078    } else {
3079	result = -1;
3080    }
3081    TRACE(("get_pty(ttydev=%s, ptydev=%s) %s fd=%d (utmp setgid)\n",
3082	   ttydev != 0 ? ttydev : "?",
3083	   ptydev != 0 ? ptydev : "?",
3084	   result ? "FAIL" : "OK",
3085	   pty != 0 ? *pty : -1));
3086#ifdef USE_OPENPTY
3087    if (opened_tty >= 0) {
3088	close(opened_tty);
3089	opened_tty = -1;
3090    }
3091#endif
3092    return result;
3093}
3094#endif
3095
3096/*
3097 * Called from get_pty to iterate over likely pseudo terminals
3098 * we might allocate.  Used on those systems that do not have
3099 * a functional interface for allocating a pty.
3100 * Returns 0 if found a pty, 1 if fails.
3101 */
3102#ifdef USE_PTY_SEARCH
3103static int
3104pty_search(int *pty)
3105{
3106    static int devindex = 0, letter = 0;
3107
3108#if defined(CRAY) || defined(__MVS__)
3109    while (devindex < MAXPTTYS) {
3110	sprintf(ttydev, TTYFORMAT, devindex);
3111	sprintf(ptydev, PTYFORMAT, devindex);
3112	devindex++;
3113
3114	TRACE(("pty_search(ttydev=%s, ptydev=%s)\n", ttydev, ptydev));
3115	if ((*pty = open(ptydev, O_RDWR)) >= 0) {
3116	    return 0;
3117	}
3118    }
3119#else /* CRAY || __MVS__ */
3120    while (PTYCHAR1[letter]) {
3121	ttydev[strlen(ttydev) - 2] =
3122	    ptydev[strlen(ptydev) - 2] = PTYCHAR1[letter];
3123
3124	while (PTYCHAR2[devindex]) {
3125	    ttydev[strlen(ttydev) - 1] =
3126		ptydev[strlen(ptydev) - 1] = PTYCHAR2[devindex];
3127	    devindex++;
3128
3129	    TRACE(("pty_search(ttydev=%s, ptydev=%s)\n", ttydev, ptydev));
3130	    if ((*pty = open(ptydev, O_RDWR)) >= 0) {
3131#ifdef sun
3132		/* Need to check the process group of the pty.
3133		 * If it exists, then the slave pty is in use,
3134		 * and we need to get another one.
3135		 */
3136		int pgrp_rtn;
3137		if (ioctl(*pty, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
3138		    close(*pty);
3139		    continue;
3140		}
3141#endif /* sun */
3142		return 0;
3143	    }
3144	}
3145	devindex = 0;
3146	letter++;
3147    }
3148#endif /* CRAY else */
3149    /*
3150     * We were unable to allocate a pty master!  Return an error
3151     * condition and let our caller terminate cleanly.
3152     */
3153    return 1;
3154}
3155#endif /* USE_PTY_SEARCH */
3156
3157/*
3158 * The only difference in /etc/termcap between 4014 and 4015 is that
3159 * the latter has support for switching character sets.  We support the
3160 * 4015 protocol, but ignore the character switches.  Therefore, we
3161 * choose 4014 over 4015.
3162 *
3163 * Features of the 4014 over the 4012: larger (19") screen, 12-bit
3164 * graphics addressing (compatible with 4012 10-bit addressing),
3165 * special point plot mode, incremental plot mode (not implemented in
3166 * later Tektronix terminals), and 4 character sizes.
3167 * All of these are supported by xterm.
3168 */
3169
3170#if OPT_TEK4014
3171static const char *const tekterm[] =
3172{
3173    "tek4014",
3174    "tek4015",			/* 4014 with APL character set support */
3175    "tek4012",			/* 4010 with lower case */
3176    "tek4013",			/* 4012 with APL character set support */
3177    "tek4010",			/* small screen, upper-case only */
3178    "dumb",
3179    0
3180};
3181#endif
3182
3183/* The VT102 is a VT100 with the Advanced Video Option included standard.
3184 * It also adds Escape sequences for insert/delete character/line.
3185 * The VT220 adds 8-bit character sets, selective erase.
3186 * The VT320 adds a 25th status line, terminal state interrogation.
3187 * The VT420 has up to 48 lines on the screen.
3188 */
3189
3190static const char *const vtterm[] =
3191{
3192#ifdef USE_X11TERM
3193    "x11term",			/* for people who want special term name */
3194#endif
3195    DFT_TERMTYPE,		/* for people who want special term name */
3196    "xterm",			/* the preferred name, should be fastest */
3197    "vt102",
3198    "vt100",
3199    "ansi",
3200    "dumb",
3201    0
3202};
3203
3204/* ARGSUSED */
3205static void
3206hungtty(int i GCC_UNUSED)
3207{
3208    DEBUG_MSG("handle:hungtty\n");
3209    siglongjmp(env, 1);
3210}
3211
3212#if OPT_PTY_HANDSHAKE
3213#define NO_FDS {-1, -1}
3214
3215static int cp_pipe[2] = NO_FDS;	/* this pipe is used for child to parent transfer */
3216static int pc_pipe[2] = NO_FDS;	/* this pipe is used for parent to child transfer */
3217
3218typedef enum {			/* c == child, p == parent                        */
3219    PTY_BAD,			/* c->p: can't open pty slave for some reason     */
3220    PTY_FATALERROR,		/* c->p: we had a fatal error with the pty        */
3221    PTY_GOOD,			/* c->p: we have a good pty, let's go on          */
3222    PTY_NEW,			/* p->c: here is a new pty slave, try this        */
3223    PTY_NOMORE,			/* p->c; no more pty's, terminate                 */
3224    UTMP_ADDED,			/* c->p: utmp entry has been added                */
3225    UTMP_TTYSLOT,		/* c->p: here is my ttyslot                       */
3226    PTY_EXEC			/* p->c: window has been mapped the first time    */
3227} status_t;
3228
3229#define HANDSHAKE_LEN	1024
3230
3231typedef struct {
3232    status_t status;
3233    int error;
3234    int fatal_error;
3235    int tty_slot;
3236    int rows;
3237    int cols;
3238    char buffer[HANDSHAKE_LEN];
3239} handshake_t;
3240
3241/* the buffer is large enough that we can always have a trailing null */
3242#define copy_handshake(dst, src) \
3243	strncpy(dst.buffer, src, (size_t)HANDSHAKE_LEN - 1)[HANDSHAKE_LEN - 1] = '\0'
3244
3245#if OPT_TRACE
3246static void
3247trace_handshake(const char *tag, handshake_t * data)
3248{
3249    const char *status = "?";
3250    switch (data->status) {
3251    case PTY_BAD:
3252	status = "PTY_BAD";
3253	break;
3254    case PTY_FATALERROR:
3255	status = "PTY_FATALERROR";
3256	break;
3257    case PTY_GOOD:
3258	status = "PTY_GOOD";
3259	break;
3260    case PTY_NEW:
3261	status = "PTY_NEW";
3262	break;
3263    case PTY_NOMORE:
3264	status = "PTY_NOMORE";
3265	break;
3266    case UTMP_ADDED:
3267	status = "UTMP_ADDED";
3268	break;
3269    case UTMP_TTYSLOT:
3270	status = "UTMP_TTYSLOT";
3271	break;
3272    case PTY_EXEC:
3273	status = "PTY_EXEC";
3274	break;
3275    }
3276    TRACE(("handshake %s %s errno=%d, error=%d device \"%s\"\n",
3277	   tag,
3278	   status,
3279	   data->error,
3280	   data->fatal_error,
3281	   data->buffer));
3282}
3283#define TRACE_HANDSHAKE(tag, data) trace_handshake(tag, data)
3284#else
3285#define TRACE_HANDSHAKE(tag, data)	/* nothing */
3286#endif
3287
3288/* HsSysError()
3289 *
3290 * This routine does the equivalent of a SysError but it handshakes
3291 * over the errno and error exit to the master process so that it can
3292 * display our error message and exit with our exit code so that the
3293 * user can see it.
3294 */
3295
3296static void
3297HsSysError(int error)
3298{
3299    handshake_t handshake;
3300
3301    memset(&handshake, 0, sizeof(handshake));
3302    handshake.status = PTY_FATALERROR;
3303    handshake.error = errno;
3304    handshake.fatal_error = error;
3305    copy_handshake(handshake, ttydev);
3306
3307    if (resource.ptyHandshake && (cp_pipe[1] >= 0)) {
3308	TRACE(("HsSysError errno=%d, error=%d device \"%s\"\n",
3309	       handshake.error,
3310	       handshake.fatal_error,
3311	       handshake.buffer));
3312	TRACE_HANDSHAKE("writing", &handshake);
3313	IGNORE_RC(write(cp_pipe[1],
3314			(const char *) &handshake,
3315			sizeof(handshake)));
3316    } else {
3317	xtermWarning("fatal pty error errno=%d, error=%d device \"%s\"\n",
3318		     handshake.error,
3319		     handshake.fatal_error,
3320		     handshake.buffer);
3321	fprintf(stderr, "%s\n", SysErrorMsg(handshake.error));
3322	fprintf(stderr, "Reason: %s\n", SysReasonMsg(handshake.fatal_error));
3323    }
3324    exit(error);
3325}
3326
3327void
3328first_map_occurred(void)
3329{
3330    if (resource.wait_for_map) {
3331	if (pc_pipe[1] >= 0) {
3332	    handshake_t handshake;
3333	    TScreen *screen = TScreenOf(term);
3334
3335	    memset(&handshake, 0, sizeof(handshake));
3336	    handshake.status = PTY_EXEC;
3337	    handshake.rows = screen->max_row;
3338	    handshake.cols = screen->max_col;
3339
3340	    TRACE(("first_map_occurred: %dx%d\n", MaxRows(screen), MaxCols(screen)));
3341	    TRACE_HANDSHAKE("writing", &handshake);
3342	    IGNORE_RC(write(pc_pipe[1],
3343			    (const char *) &handshake,
3344			    sizeof(handshake)));
3345	    close(cp_pipe[0]);
3346	    close(pc_pipe[1]);
3347	}
3348	resource.wait_for_map = False;
3349    }
3350}
3351#else
3352/*
3353 * temporary hack to get xterm working on att ptys
3354 */
3355static void
3356HsSysError(int error)
3357{
3358    xtermWarning("fatal pty error %d (errno=%d) on tty %s\n",
3359		 error, errno, ttydev);
3360    exit(error);
3361}
3362#endif /* OPT_PTY_HANDSHAKE else !OPT_PTY_HANDSHAKE */
3363
3364#ifndef VMS
3365static void
3366set_owner(char *device, unsigned uid, unsigned gid, unsigned mode)
3367{
3368    int why;
3369
3370    TRACE_IDS;
3371    TRACE(("set_owner(%s, uid=%d, gid=%d, mode=%#o\n",
3372	   device, (int) uid, (int) gid, (unsigned) mode));
3373
3374    if (chown(device, (uid_t) uid, (gid_t) gid) < 0) {
3375	why = errno;
3376	if (why != ENOENT
3377	    && save_ruid == 0) {
3378	    xtermPerror("Cannot chown %s to %ld,%ld",
3379			device, (long) uid, (long) gid);
3380	}
3381	TRACE(("...chown failed: %s\n", strerror(why)));
3382    } else if (chmod(device, (mode_t) mode) < 0) {
3383	why = errno;
3384	if (why != ENOENT) {
3385	    struct stat sb;
3386	    if (stat(device, &sb) < 0) {
3387		xtermPerror("Cannot chmod %s to %03o",
3388			    device, (unsigned) mode);
3389	    } else if (mode != (sb.st_mode & 0777U)) {
3390		xtermPerror("Cannot chmod %s to %03lo currently %03lo",
3391			    device,
3392			    (unsigned long) mode,
3393			    (unsigned long) (sb.st_mode & 0777U));
3394		TRACE(("...stat uid=%d, gid=%d, mode=%#o\n",
3395		       (int) sb.st_uid, (int) sb.st_gid, (unsigned) sb.st_mode));
3396	    }
3397	}
3398	TRACE(("...chmod failed: %s\n", strerror(why)));
3399    }
3400}
3401
3402/*
3403 * utmp data may not be null-terminated; even if it is, there may be garbage
3404 * after the null.  This fills the unused part of the result with nulls.
3405 */
3406static void
3407copy_filled(char *target, const char *source, size_t len)
3408{
3409    size_t used = 0;
3410    while (used < len) {
3411	if ((target[used] = source[used]) == 0)
3412	    break;
3413	++used;
3414    }
3415    while (used < len) {
3416	target[used++] = '\0';
3417    }
3418}
3419
3420#if defined(HAVE_UTMP) && defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER)
3421/*
3422 * getutid() only looks at ut_type and ut_id.
3423 * But we'll also check ut_line in find_utmp().
3424 */
3425static void
3426init_utmp(int type, struct UTMP_STR *tofind)
3427{
3428    memset(tofind, 0, sizeof(*tofind));
3429    tofind->ut_type = (short) type;
3430    copy_filled(tofind->ut_id, my_utmp_id(ttydev), sizeof(tofind->ut_id));
3431    copy_filled(tofind->ut_line, my_pty_name(ttydev), sizeof(tofind->ut_line));
3432}
3433
3434/*
3435 * We could use getutline() if we didn't support old systems.
3436 */
3437static struct UTMP_STR *
3438find_utmp(struct UTMP_STR *tofind)
3439{
3440    struct UTMP_STR *result;
3441    struct UTMP_STR limited;
3442    struct UTMP_STR working;
3443
3444    for (;;) {
3445	memset(&working, 0, sizeof(working));
3446	working.ut_type = tofind->ut_type;
3447	copy_filled(working.ut_id, tofind->ut_id, sizeof(tofind->ut_id));
3448#if defined(__digital__) && defined(__unix__) && (defined(OSMAJORVERSION) && OSMAJORVERSION < 5)
3449	working.ut_type = 0;
3450#endif
3451	if ((result = call_getutid(&working)) == 0)
3452	    break;
3453	copy_filled(limited.ut_line, result->ut_line, sizeof(result->ut_line));
3454	if (!memcmp(limited.ut_line, tofind->ut_line, sizeof(limited.ut_line)))
3455	    break;
3456	/*
3457	 * Solaris, IRIX64 and HPUX manpages say to fill the static area
3458	 * pointed to by the return-value to zeros if searching for multiple
3459	 * occurrences.  Otherwise it will continue to return the same value.
3460	 */
3461	memset(result, 0, sizeof(*result));
3462    }
3463    return result;
3464}
3465#endif /* HAVE_UTMP... */
3466
3467#define close_fd(fd) close(fd), fd = -1
3468
3469#if defined(TIOCNOTTY) && (!defined(__GLIBC__) || (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
3470#define USE_NO_DEV_TTY 1
3471#else
3472#define USE_NO_DEV_TTY 0
3473#endif
3474
3475static int
3476same_leaf(char *a, char *b)
3477{
3478    char *p = x_basename(a);
3479    char *q = x_basename(b);
3480    return !strcmp(p, q);
3481}
3482
3483/*
3484 * "good enough" (inode wouldn't port to Cygwin)
3485 */
3486static int
3487same_file(const char *a, const char *b)
3488{
3489    struct stat asb;
3490    struct stat bsb;
3491    int result = 0;
3492
3493    if ((stat(a, &asb) == 0)
3494	&& (stat(b, &bsb) == 0)
3495	&& ((asb.st_mode & S_IFMT) == S_IFREG)
3496	&& ((bsb.st_mode & S_IFMT) == S_IFREG)
3497	&& (asb.st_mtime == bsb.st_mtime)
3498	&& (asb.st_size == bsb.st_size)) {
3499	result = 1;
3500    }
3501    return result;
3502}
3503
3504static int
3505findValidShell(const char *haystack, const char *needle)
3506{
3507    int result = -1;
3508    int count = -1;
3509    const char *s, *t;
3510    size_t have;
3511    size_t want = strlen(needle);
3512
3513    TRACE(("findValidShell:\n%s\n", NonNull(haystack)));
3514
3515    for (s = haystack; (s != 0) && (*s != '\0'); s = t) {
3516	++count;
3517	if ((t = strchr(s, '\n')) == 0) {
3518	    t = s + strlen(s);
3519	}
3520	have = (size_t) (t - s);
3521
3522	if ((have >= want) && (*s != '#')) {
3523	    char *p = malloc(have + 1);
3524
3525	    if (p != 0) {
3526		char *q;
3527
3528		memcpy(p, s, have);
3529		p[have] = '\0';
3530		if ((q = x_strtrim(p)) != 0) {
3531		    TRACE(("...test %s\n", q));
3532		    if (!strcmp(q, needle)) {
3533			result = count;
3534		    } else if (same_leaf(q, (char *) needle) &&
3535			       same_file(q, needle)) {
3536			result = count;
3537		    }
3538		    free(q);
3539		}
3540		free(p);
3541	    }
3542	    if (result >= 0)
3543		break;
3544	}
3545	while (*t == '\n') {
3546	    ++t;
3547	}
3548    }
3549    return result;
3550}
3551
3552static int
3553ourValidShell(const char *pathname)
3554{
3555    return findValidShell(x_strtrim(resource.valid_shells), pathname);
3556}
3557
3558#if defined(HAVE_GETUSERSHELL) && defined(HAVE_ENDUSERSHELL)
3559static Boolean
3560validShell(const char *pathname)
3561{
3562    int result = -1;
3563
3564    if (validProgram(pathname)) {
3565	char *q;
3566	int count = -1;
3567
3568	TRACE(("validShell:getusershell\n"));
3569	while ((q = getusershell()) != 0) {
3570	    ++count;
3571	    TRACE(("...test \"%s\"\n", q));
3572	    if (!strcmp(q, pathname)) {
3573		result = count;
3574		break;
3575	    }
3576	}
3577	endusershell();
3578
3579	if (result < 0)
3580	    result = ourValidShell(pathname);
3581    }
3582
3583    TRACE(("validShell %s ->%d\n", NonNull(pathname), result));
3584    return (result >= 0);
3585}
3586#else
3587/*
3588 * Only set $SHELL for paths found in the standard location.
3589 */
3590static Boolean
3591validShell(const char *pathname)
3592{
3593    int result = -1;
3594    const char *ok_shells = "/etc/shells";
3595    char *blob;
3596    struct stat sb;
3597    size_t rc;
3598    FILE *fp;
3599
3600    if (validProgram(pathname)) {
3601
3602	TRACE(("validShell:%s\n", ok_shells));
3603
3604	if (stat(ok_shells, &sb) == 0
3605	    && (sb.st_mode & S_IFMT) == S_IFREG
3606	    && ((size_t) sb.st_size > 0)
3607	    && ((size_t) sb.st_size < (((size_t) ~0) - 2))
3608	    && (blob = calloc((size_t) sb.st_size + 2, sizeof(char))) != 0) {
3609
3610	    if ((fp = fopen(ok_shells, "r")) != 0) {
3611		rc = fread(blob, sizeof(char), (size_t) sb.st_size, fp);
3612		fclose(fp);
3613
3614		if (rc == (size_t) sb.st_size) {
3615		    blob[rc] = '\0';
3616		    result = findValidShell(blob, pathname);
3617		}
3618	    }
3619	    free(blob);
3620	}
3621	if (result < 0)
3622	    result = ourValidShell(pathname);
3623    }
3624    TRACE(("validShell %s ->%d\n", NonNull(pathname), result));
3625    return (result > 0);
3626}
3627#endif
3628
3629static char *
3630resetShell(char *oldPath)
3631{
3632    char *newPath = x_strdup("/bin/sh");
3633    char *envPath = getenv("SHELL");
3634    free(oldPath);
3635    if (!IsEmpty(envPath))
3636	xtermSetenv("SHELL", newPath);
3637    return newPath;
3638}
3639
3640/*
3641 *  Inits pty and tty and forks a login process.
3642 *  Does not close fd Xsocket.
3643 *  If slave, the pty named in passedPty is already open for use
3644 */
3645static int
3646spawnXTerm(XtermWidget xw, unsigned line_speed)
3647{
3648    TScreen *screen = TScreenOf(xw);
3649    Cardinal nn;
3650#if OPT_PTY_HANDSHAKE
3651    Bool got_handshake_size = False;
3652    handshake_t handshake;
3653    int done;
3654#endif
3655#if OPT_INITIAL_ERASE
3656    int initial_erase = VAL_INITIAL_ERASE;
3657    Bool setInitialErase;
3658#endif
3659    int rc = 0;
3660    int ttyfd = -1;
3661    Bool ok_termcap;
3662    char *newtc;
3663
3664#ifdef TERMIO_STRUCT
3665    TERMIO_STRUCT tio;
3666#ifdef __MVS__
3667    TERMIO_STRUCT gio;
3668#endif /* __MVS__ */
3669#ifdef TIOCLSET
3670    unsigned lmode;
3671#endif /* TIOCLSET */
3672#ifdef HAS_LTCHARS
3673    struct ltchars ltc;
3674#endif /* HAS_LTCHARS */
3675#else /* !TERMIO_STRUCT */
3676    int ldisc = 0;
3677    int discipline;
3678    unsigned lmode;
3679    struct tchars tc;
3680    struct ltchars ltc;
3681    struct sgttyb sg;
3682#ifdef sony
3683    int jmode;
3684    struct jtchars jtc;
3685#endif /* sony */
3686#endif /* TERMIO_STRUCT */
3687
3688    char *shell_path = 0;
3689    char *shname, *shname_minus;
3690    int i;
3691#if USE_NO_DEV_TTY
3692    int no_dev_tty = False;
3693#endif
3694    const char *const *envnew;	/* new environment */
3695    char buf[64];
3696    char *TermName = NULL;
3697#ifdef TTYSIZE_STRUCT
3698    TTYSIZE_STRUCT ts;
3699#endif
3700    struct passwd pw;
3701    char *login_name = NULL;
3702#ifndef USE_UTEMPTER
3703#ifdef HAVE_UTMP
3704    struct UTMP_STR utmp;
3705#ifdef USE_SYSV_UTMP
3706    struct UTMP_STR *utret = NULL;
3707#endif
3708#ifdef USE_LASTLOG
3709    struct lastlog lastlog;
3710#endif
3711#ifdef USE_LASTLOGX
3712    struct lastlogx lastlogx;
3713#endif /* USE_LASTLOG */
3714#endif /* HAVE_UTMP */
3715#endif /* !USE_UTEMPTER */
3716
3717#if OPT_TRACE
3718    unsigned long xterm_parent = (unsigned long) getpid();
3719#endif
3720
3721    /* Noisy compilers (suppress some unused-variable warnings) */
3722    (void) rc;
3723#if defined(HAVE_UTMP) && defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER)
3724    (void) utret;
3725#endif
3726
3727    screen->uid = save_ruid;
3728    screen->gid = save_rgid;
3729
3730#ifdef SIGTTOU
3731    /* so that TIOCSWINSZ || TIOCSIZE doesn't block */
3732    signal(SIGTTOU, SIG_IGN);
3733#endif
3734
3735#if OPT_PTY_HANDSHAKE
3736    memset(&handshake, 0, sizeof(handshake));
3737#endif
3738
3739    if (am_slave >= 0) {
3740	screen->respond = am_slave;
3741	set_pty_id(ttydev, passedPty);
3742#ifdef USE_PTY_DEVICE
3743	set_pty_id(ptydev, passedPty);
3744#endif
3745	if (xtermResetIds(screen) < 0)
3746	    exit(1);
3747    } else {
3748	Bool tty_got_hung;
3749
3750	/*
3751	 * Sometimes /dev/tty hangs on open (as in the case of a pty
3752	 * that has gone away).  Simply make up some reasonable
3753	 * defaults.
3754	 */
3755
3756	if (!sigsetjmp(env, 1)) {
3757	    signal(SIGALRM, hungtty);
3758	    alarm(2);		/* alarm(1) might return too soon */
3759	    ttyfd = open("/dev/tty", O_RDWR);
3760	    alarm(0);
3761	    tty_got_hung = False;
3762	} else {
3763	    tty_got_hung = True;
3764	    ttyfd = -1;
3765	    errno = ENXIO;
3766	}
3767	shell_path = 0;
3768	memset(&pw, 0, sizeof(pw));
3769#if OPT_PTY_HANDSHAKE
3770	got_handshake_size = False;
3771#endif /* OPT_PTY_HANDSHAKE */
3772#if OPT_INITIAL_ERASE
3773	initial_erase = VAL_INITIAL_ERASE;
3774#endif
3775	signal(SIGALRM, SIG_DFL);
3776
3777	/*
3778	 * Check results and ignore current control terminal if
3779	 * necessary.  ENXIO is what is normally returned if there is
3780	 * no controlling terminal, but some systems (e.g. SunOS 4.0)
3781	 * seem to return EIO.  Solaris 2.3 is said to return EINVAL.
3782	 * Cygwin returns ENOENT.  FreeBSD can return ENOENT, especially
3783	 * if xterm is run within a jail.
3784	 */
3785#if USE_NO_DEV_TTY
3786	no_dev_tty = False;
3787#endif
3788	if (ttyfd < 0) {
3789	    if (tty_got_hung || errno == ENXIO || errno == EIO ||
3790		errno == ENOENT ||
3791#ifdef ENODEV
3792		errno == ENODEV ||
3793#endif
3794		errno == EINVAL || errno == ENOTTY || errno == EACCES) {
3795#if USE_NO_DEV_TTY
3796		no_dev_tty = True;
3797#endif
3798#ifdef HAS_LTCHARS
3799		ltc = d_ltc;
3800#endif /* HAS_LTCHARS */
3801#ifdef TIOCLSET
3802		lmode = d_lmode;
3803#endif /* TIOCLSET */
3804#ifdef TERMIO_STRUCT
3805		tio = d_tio;
3806#else /* !TERMIO_STRUCT */
3807		sg = d_sg;
3808		tc = d_tc;
3809		discipline = d_disipline;
3810#ifdef sony
3811		jmode = d_jmode;
3812		jtc = d_jtc;
3813#endif /* sony */
3814#endif /* TERMIO_STRUCT */
3815	    } else {
3816		SysError(ERROR_OPDEVTTY);
3817	    }
3818	} else {
3819
3820	    /* Get a copy of the current terminal's state,
3821	     * if we can.  Some systems (e.g., SVR4 and MacII)
3822	     * may not have a controlling terminal at this point
3823	     * if started directly from xdm or xinit,
3824	     * in which case we just use the defaults as above.
3825	     */
3826#ifdef HAS_LTCHARS
3827	    if (ioctl(ttyfd, TIOCGLTC, &ltc) == -1)
3828		ltc = d_ltc;
3829#endif /* HAS_LTCHARS */
3830#ifdef TIOCLSET
3831	    if (ioctl(ttyfd, TIOCLGET, &lmode) == -1)
3832		lmode = d_lmode;
3833#endif /* TIOCLSET */
3834#ifdef TERMIO_STRUCT
3835	    rc = ttyGetAttr(ttyfd, &tio);
3836	    if (rc == -1)
3837		tio = d_tio;
3838#else /* !TERMIO_STRUCT */
3839	    rc = ioctl(ttyfd, TIOCGETP, (char *) &sg);
3840	    if (rc == -1)
3841		sg = d_sg;
3842	    if (ioctl(ttyfd, TIOCGETC, (char *) &tc) == -1)
3843		tc = d_tc;
3844	    if (ioctl(ttyfd, TIOCGETD, (char *) &discipline) == -1)
3845		discipline = d_disipline;
3846#ifdef sony
3847	    if (ioctl(ttyfd, TIOCKGET, (char *) &jmode) == -1)
3848		jmode = d_jmode;
3849	    if (ioctl(ttyfd, TIOCKGETC, (char *) &jtc) == -1)
3850		jtc = d_jtc;
3851#endif /* sony */
3852#endif /* TERMIO_STRUCT */
3853
3854	    /*
3855	     * If ptyInitialErase is set, we want to get the pty's
3856	     * erase value.  Just in case that will fail, first get
3857	     * the value from /dev/tty, so we will have something
3858	     * at least.
3859	     */
3860#if OPT_INITIAL_ERASE
3861	    if (resource.ptyInitialErase) {
3862#ifdef TERMIO_STRUCT
3863		initial_erase = tio.c_cc[VERASE];
3864#else /* !TERMIO_STRUCT */
3865		initial_erase = sg.sg_erase;
3866#endif /* TERMIO_STRUCT */
3867		TRACE(("%s initial_erase:%d (from /dev/tty)\n",
3868		       rc == 0 ? "OK" : "FAIL",
3869		       initial_erase));
3870	    }
3871#endif
3872#ifdef __MVS__
3873	    if (ttyGetAttr(ttyfd, &gio) == 0) {
3874		gio.c_cflag &= ~(HUPCL | PARENB);
3875		ttySetAttr(ttyfd, &gio);
3876	    }
3877#endif /* __MVS__ */
3878
3879	    close_fd(ttyfd);
3880	}
3881
3882	if (get_pty(&screen->respond, XDisplayString(screen->display))) {
3883	    SysError(ERROR_PTYS);
3884	}
3885	TRACE_GET_TTYSIZE(screen->respond, "after get_pty");
3886#if OPT_INITIAL_ERASE
3887	if (resource.ptyInitialErase) {
3888#ifdef TERMIO_STRUCT
3889	    TERMIO_STRUCT my_tio;
3890	    rc = ttyGetAttr(screen->respond, &my_tio);
3891	    if (rc == 0)
3892		initial_erase = my_tio.c_cc[VERASE];
3893#else /* !TERMIO_STRUCT */
3894	    struct sgttyb my_sg;
3895	    rc = ioctl(screen->respond, TIOCGETP, (char *) &my_sg);
3896	    if (rc == 0)
3897		initial_erase = my_sg.sg_erase;
3898#endif /* TERMIO_STRUCT */
3899	    TRACE(("%s initial_erase:%d (from pty)\n",
3900		   (rc == 0) ? "OK" : "FAIL",
3901		   initial_erase));
3902	}
3903#endif /* OPT_INITIAL_ERASE */
3904    }
3905
3906    /* avoid double MapWindow requests */
3907    XtSetMappedWhenManaged(SHELL_OF(CURRENT_EMU()), False);
3908
3909    wm_delete_window = XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW",
3910				   False);
3911
3912    if (!TEK4014_ACTIVE(xw))
3913	VTInit(xw);		/* realize now so know window size for tty driver */
3914#if defined(TIOCCONS) || defined(SRIOCSREDIR)
3915    if (Console) {
3916	/*
3917	 * Inform any running xconsole program
3918	 * that we are going to steal the console.
3919	 */
3920	XmuGetHostname(mit_console_name + MIT_CONSOLE_LEN, 255);
3921	mit_console = XInternAtom(screen->display, mit_console_name, False);
3922	/* the user told us to be the console, so we can use CurrentTime */
3923	XtOwnSelection(SHELL_OF(CURRENT_EMU()),
3924		       mit_console, CurrentTime,
3925		       ConvertConsoleSelection, NULL, NULL);
3926    }
3927#endif
3928#if OPT_TEK4014
3929    if (TEK4014_ACTIVE(xw)) {
3930	envnew = tekterm;
3931    } else
3932#endif
3933    {
3934	envnew = vtterm;
3935    }
3936
3937    /*
3938     * This used to exit if no termcap entry was found for the specified
3939     * terminal name.  That's a little unfriendly, so instead we'll allow
3940     * the program to proceed (but not to set $TERMCAP) if the termcap
3941     * entry is not found.
3942     */
3943    ok_termcap = True;
3944    if (!get_termcap(xw, TermName = resource.term_name)) {
3945	const char *last = NULL;
3946	char *next;
3947
3948	TermName = x_strdup(*envnew);
3949	ok_termcap = False;
3950	while (*envnew != NULL) {
3951	    if (last == NULL || strcmp(last, *envnew)) {
3952		next = x_strdup(*envnew);
3953		if (get_termcap(xw, next)) {
3954		    free(TermName);
3955		    TermName = next;
3956		    ok_termcap = True + 1;
3957		    break;
3958		} else {
3959		    free(next);
3960		}
3961	    }
3962	    last = *envnew;
3963	    envnew++;
3964	}
3965    }
3966    if (ok_termcap) {
3967	resource.term_name = x_strdup(TermName);
3968	resize_termcap(xw);
3969    }
3970
3971    /*
3972     * Check if ptyInitialErase is not set.  If so, we rely on the termcap
3973     * (or terminfo) to tell us what the erase mode should be set to.
3974     */
3975#if OPT_INITIAL_ERASE
3976    TRACE(("resource ptyInitialErase is %sset\n",
3977	   resource.ptyInitialErase ? "" : "not "));
3978    setInitialErase = False;
3979    if (override_tty_modes && ttyModes[XTTYMODE_erase].set) {
3980	initial_erase = ttyModes[XTTYMODE_erase].value;
3981	setInitialErase = True;
3982    } else if (resource.ptyInitialErase) {
3983	/* EMPTY */ ;
3984    } else if (ok_termcap) {
3985	char *s = get_tcap_erase(xw);
3986	TRACE(("...extracting initial_erase value from termcap\n"));
3987	if (s != 0) {
3988	    char *save = s;
3989	    initial_erase = decode_keyvalue(&s, True);
3990	    setInitialErase = True;
3991	    free(save);
3992	}
3993    }
3994    TRACE(("...initial_erase:%d\n", initial_erase));
3995
3996    TRACE(("resource backarrowKeyIsErase is %sset\n",
3997	   resource.backarrow_is_erase ? "" : "not "));
3998    if (resource.backarrow_is_erase) {	/* see input.c */
3999	if (initial_erase == ANSI_DEL) {
4000	    UIntClr(xw->keyboard.flags, MODE_DECBKM);
4001	} else {
4002	    xw->keyboard.flags |= MODE_DECBKM;
4003	    xw->keyboard.reset_DECBKM = 1;
4004	}
4005	TRACE(("...sets DECBKM %s\n",
4006	       (xw->keyboard.flags & MODE_DECBKM) ? "on" : "off"));
4007    } else {
4008	xw->keyboard.reset_DECBKM = 2;
4009    }
4010#endif /* OPT_INITIAL_ERASE */
4011
4012#ifdef TTYSIZE_STRUCT
4013    /* tell tty how big window is */
4014#if OPT_TEK4014
4015    if (TEK4014_ACTIVE(xw)) {
4016	setup_winsize(ts, TDefaultRows, TDefaultCols,
4017		      TFullHeight(TekScreenOf(tekWidget)),
4018		      TFullWidth(TekScreenOf(tekWidget)));
4019    } else
4020#endif
4021    {
4022	setup_winsize(ts, MaxRows(screen), MaxCols(screen),
4023		      FullHeight(screen), FullWidth(screen));
4024    }
4025    TRACE_RC(i, SET_TTYSIZE(screen->respond, ts));
4026    TRACE(("spawn SET_TTYSIZE %dx%d return %d\n",
4027	   TTYSIZE_ROWS(ts),
4028	   TTYSIZE_COLS(ts), i));
4029#endif /* TTYSIZE_STRUCT */
4030
4031#if !defined(USE_OPENPTY)
4032#if defined(USE_USG_PTYS) || defined(HAVE_POSIX_OPENPT)
4033    /*
4034     * utempter checks the ownership of the device; some implementations
4035     * set ownership in grantpt - do this first.
4036     */
4037    grantpt(screen->respond);
4038#endif
4039#if !defined(USE_USG_PTYS) && defined(HAVE_POSIX_OPENPT)
4040    unlockpt(screen->respond);
4041    TRACE_GET_TTYSIZE(screen->respond, "after unlockpt");
4042#endif
4043#endif /* !USE_OPENPTY */
4044
4045    added_utmp_entry = False;
4046#if defined(USE_UTEMPTER)
4047#undef UTMP
4048    if ((xw->misc.login_shell || !command_to_exec) && !resource.utmpInhibit) {
4049	struct UTMP_STR dummy;
4050
4051	/* Note: utempter may trim it anyway */
4052	SetUtmpHost(dummy.ut_host, screen);
4053	TRACE(("...calling addToUtmp(pty=%s, hostname=%s, master_fd=%d)\n",
4054	       ttydev, dummy.ut_host, screen->respond));
4055	UTEMPTER_ADD(ttydev, dummy.ut_host, screen->respond);
4056	added_utmp_entry = True;
4057    }
4058#endif
4059
4060    if (am_slave < 0) {
4061#if OPT_PTY_HANDSHAKE
4062	if (resource.ptyHandshake && (pipe(pc_pipe) || pipe(cp_pipe)))
4063	    SysError(ERROR_FORK);
4064#endif
4065	TRACE(("Forking...\n"));
4066	if ((screen->pid = fork()) == -1)
4067	    SysError(ERROR_FORK);
4068
4069	if (screen->pid == 0) {
4070#ifdef USE_USG_PTYS
4071	    int ptyfd = -1;
4072	    char *pty_name;
4073#endif
4074	    /*
4075	     * now in child process
4076	     */
4077#if defined(_POSIX_SOURCE) || defined(SVR4) || defined(__convex__) || defined(__SCO__) || defined(__QNX__)
4078	    int pgrp = setsid();	/* variable may not be used... */
4079#else
4080	    int pgrp = getpid();
4081#endif
4082	    TRACE_CHILD
4083
4084#ifdef USE_USG_PTYS
4085#ifdef HAVE_SETPGID
4086		setpgid(0, 0);
4087#else
4088		setpgrp();
4089#endif
4090	    unlockpt(screen->respond);
4091	    TRACE_GET_TTYSIZE(screen->respond, "after unlockpt");
4092	    if ((pty_name = ptsname(screen->respond)) == 0) {
4093		SysError(ERROR_PTSNAME);
4094	    } else if ((ptyfd = open(pty_name, O_RDWR)) < 0) {
4095		SysError(ERROR_OPPTSNAME);
4096	    }
4097#ifdef I_PUSH
4098	    else if (PUSH_FAILS(ptyfd, "ptem")) {
4099		SysError(ERROR_PTEM);
4100	    }
4101#if !defined(SVR4) && !(defined(SYSV) && defined(i386))
4102	    else if (!x_getenv("CONSEM")
4103		     && PUSH_FAILS(ptyfd, "consem")) {
4104		SysError(ERROR_CONSEM);
4105	    }
4106#endif /* !SVR4 */
4107	    else if (PUSH_FAILS(ptyfd, "ldterm")) {
4108		SysError(ERROR_LDTERM);
4109	    }
4110#ifdef SVR4			/* from Sony */
4111	    else if (PUSH_FAILS(ptyfd, "ttcompat")) {
4112		SysError(ERROR_TTCOMPAT);
4113	    }
4114#endif /* SVR4 */
4115#endif /* I_PUSH */
4116	    ttyfd = ptyfd;
4117#ifndef __MVS__
4118	    close_fd(screen->respond);
4119#endif /* __MVS__ */
4120
4121#ifdef TTYSIZE_STRUCT
4122	    /* tell tty how big window is */
4123#if OPT_TEK4014
4124	    if (TEK4014_ACTIVE(xw)) {
4125		setup_winsize(ts, TDefaultRows, TDefaultCols,
4126			      TFullHeight(TekScreenOf(tekWidget)),
4127			      TFullWidth(TekScreenOf(tekWidget)));
4128	    } else
4129#endif /* OPT_TEK4014 */
4130	    {
4131		setup_winsize(ts, MaxRows(screen), MaxCols(screen),
4132			      FullHeight(screen), FullWidth(screen));
4133	    }
4134	    trace_winsize(ts, "initial tty size");
4135#endif /* TTYSIZE_STRUCT */
4136
4137#endif /* USE_USG_PTYS */
4138
4139	    (void) pgrp;	/* not all branches use this variable */
4140
4141#if OPT_PTY_HANDSHAKE		/* warning, goes for a long ways */
4142	    if (resource.ptyHandshake) {
4143		char *ptr;
4144
4145		/* close parent's sides of the pipes */
4146		close(cp_pipe[0]);
4147		close(pc_pipe[1]);
4148
4149		/* Make sure that our sides of the pipes are not in the
4150		 * 0, 1, 2 range so that we don't fight with stdin, out
4151		 * or err.
4152		 */
4153		if (cp_pipe[1] <= 2) {
4154		    if ((i = fcntl(cp_pipe[1], F_DUPFD, 3)) >= 0) {
4155			IGNORE_RC(close(cp_pipe[1]));
4156			cp_pipe[1] = i;
4157		    }
4158		}
4159		if (pc_pipe[0] <= 2) {
4160		    if ((i = fcntl(pc_pipe[0], F_DUPFD, 3)) >= 0) {
4161			IGNORE_RC(close(pc_pipe[0]));
4162			pc_pipe[0] = i;
4163		    }
4164		}
4165
4166		/* we don't need the socket, or the pty master anymore */
4167		close(ConnectionNumber(screen->display));
4168#ifndef __MVS__
4169		if (screen->respond >= 0)
4170		    close(screen->respond);
4171#endif /* __MVS__ */
4172
4173		/* Now is the time to set up our process group and
4174		 * open up the pty slave.
4175		 */
4176#ifdef USE_SYSV_PGRP
4177#if defined(CRAY) && (OSMAJORVERSION > 5)
4178		IGNORE_RC(setsid());
4179#else
4180		IGNORE_RC(setpgrp());
4181#endif
4182#endif /* USE_SYSV_PGRP */
4183
4184#if defined(__QNX__) && !defined(__QNXNTO__)
4185		qsetlogin(getlogin(), ttydev);
4186#endif
4187		if (ttyfd >= 0) {
4188#ifdef __MVS__
4189		    if (ttyGetAttr(ttyfd, &gio) == 0) {
4190			gio.c_cflag &= ~(HUPCL | PARENB);
4191			ttySetAttr(ttyfd, &gio);
4192		    }
4193#else /* !__MVS__ */
4194		    close_fd(ttyfd);
4195#endif /* __MVS__ */
4196		}
4197
4198		for (;;) {
4199#if USE_NO_DEV_TTY
4200		    if (!no_dev_tty
4201			&& (ttyfd = open("/dev/tty", O_RDWR)) >= 0) {
4202			ioctl(ttyfd, TIOCNOTTY, (char *) NULL);
4203			close_fd(ttyfd);
4204		    }
4205#endif /* USE_NO_DEV_TTY */
4206#ifdef CSRG_BASED
4207		    IGNORE_RC(revoke(ttydev));
4208#endif
4209		    if ((ttyfd = open(ttydev, O_RDWR)) >= 0) {
4210			TRACE_GET_TTYSIZE(ttyfd, "after open");
4211			TRACE_RC(i, SET_TTYSIZE(ttyfd, ts));
4212			TRACE_GET_TTYSIZE(ttyfd, "after SET_TTYSIZE fixup");
4213#if defined(CRAY) && defined(TCSETCTTY)
4214			/* make /dev/tty work */
4215			ioctl(ttyfd, TCSETCTTY, 0);
4216#endif
4217#if ((defined(__GLIBC__) && defined(__FreeBSD_kernel__)) || defined(__GNU__)) && defined(TIOCSCTTY)
4218			/* make /dev/tty work */
4219			ioctl(ttyfd, TIOCSCTTY, 0);
4220#endif
4221#ifdef USE_SYSV_PGRP
4222			/* We need to make sure that we are actually
4223			 * the process group leader for the pty.  If
4224			 * we are, then we should now be able to open
4225			 * /dev/tty.
4226			 */
4227			if ((i = open("/dev/tty", O_RDWR)) >= 0) {
4228			    /* success! */
4229			    close(i);
4230			    break;
4231			}
4232#else /* USE_SYSV_PGRP */
4233			break;
4234#endif /* USE_SYSV_PGRP */
4235		    }
4236		    perror("open ttydev");
4237#ifdef TIOCSCTTY
4238		    ioctl(ttyfd, TIOCSCTTY, 0);
4239#endif
4240		    /* let our master know that the open failed */
4241		    handshake.status = PTY_BAD;
4242		    handshake.error = errno;
4243		    copy_handshake(handshake, ttydev);
4244		    TRACE_HANDSHAKE("writing", &handshake);
4245		    IGNORE_RC(write(cp_pipe[1],
4246				    (const char *) &handshake,
4247				    sizeof(handshake)));
4248
4249		    /* get reply from parent */
4250		    i = (int) read(pc_pipe[0], (char *) &handshake,
4251				   sizeof(handshake));
4252		    if (i <= 0) {
4253			/* parent terminated */
4254			exit(1);
4255		    }
4256
4257		    if (handshake.status == PTY_NOMORE) {
4258			/* No more ptys, let's shutdown. */
4259			exit(1);
4260		    }
4261
4262		    /* We have a new pty to try */
4263		    if (ttyfd >= 0)
4264			close(ttyfd);
4265		    free(ttydev);
4266		    handshake.buffer[HANDSHAKE_LEN - 1] = '\0';
4267		    ttydev = x_strdup(handshake.buffer);
4268		}
4269
4270		/* use the same tty name that everyone else will use
4271		 * (from ttyname)
4272		 */
4273		if ((ptr = ttyname(ttyfd)) != 0) {
4274		    free(ttydev);
4275		    ttydev = x_strdup(ptr);
4276		}
4277	    }
4278#endif /* OPT_PTY_HANDSHAKE -- from near fork */
4279
4280	    set_pty_permissions(screen->uid,
4281				(unsigned) screen->gid,
4282				(resource.messages
4283				 ? 0622U
4284				 : 0600U));
4285
4286	    /*
4287	     * set up the tty modes
4288	     */
4289	    {
4290#ifdef TERMIO_STRUCT
4291#if defined(umips) || defined(CRAY) || defined(linux)
4292		/* If the control tty had its modes screwed around with,
4293		   eg. by lineedit in the shell, or emacs, etc. then tio
4294		   will have bad values.  Let's just get termio from the
4295		   new tty and tailor it.  */
4296		if (ttyGetAttr(ttyfd, &tio) == -1)
4297		    SysError(ERROR_TIOCGETP);
4298		tio.c_lflag |= ECHOE;
4299#endif /* umips */
4300		/* Now is also the time to change the modes of the
4301		 * child pty.
4302		 */
4303		/* input: nl->nl, don't ignore cr, cr->nl */
4304		UIntClr(tio.c_iflag, (INLCR | IGNCR));
4305		tio.c_iflag |= ICRNL;
4306#if OPT_WIDE_CHARS && defined(IUTF8)
4307#if OPT_LUIT_PROG
4308		if (command_to_exec_with_luit == 0)
4309#endif
4310		    if (screen->utf8_mode)
4311			tio.c_iflag |= IUTF8;
4312#endif
4313		/* output: cr->cr, nl is not return, no delays, ln->cr/nl */
4314#ifndef USE_POSIX_TERMIOS
4315		UIntClr(tio.c_oflag,
4316			(OCRNL
4317			 | ONLRET
4318			 | NLDLY
4319			 | CRDLY
4320			 | TABDLY
4321			 | BSDLY
4322			 | VTDLY
4323			 | FFDLY));
4324#endif /* USE_POSIX_TERMIOS */
4325		tio.c_oflag |= D_TIO_FLAGS;
4326#ifndef USE_POSIX_TERMIOS
4327# if defined(Lynx) && !defined(CBAUD)
4328#  define CBAUD V_CBAUD
4329# endif
4330		UIntClr(tio.c_cflag, CBAUD);
4331#ifdef BAUD_0
4332		/* baud rate is 0 (don't care) */
4333#elif defined(HAVE_TERMIO_C_ISPEED)
4334		tio.c_ispeed = tio.c_ospeed = line_speed;
4335#else /* !BAUD_0 */
4336		tio.c_cflag |= line_speed;
4337#endif /* !BAUD_0 */
4338#else /* USE_POSIX_TERMIOS */
4339		cfsetispeed(&tio, line_speed);
4340		cfsetospeed(&tio, line_speed);
4341#ifdef __MVS__
4342		/* turn off bits that can't be set from the slave side */
4343		tio.c_cflag &= ~(PACKET | PKT3270 | PTU3270 | PKTXTND);
4344#endif /* __MVS__ */
4345		/* Clear CLOCAL so that SIGHUP is sent to us
4346		   when the xterm ends */
4347		tio.c_cflag &= (unsigned) ~CLOCAL;
4348#endif /* USE_POSIX_TERMIOS */
4349		/* enable signals, canonical processing (erase, kill, etc),
4350		 * echo
4351		 */
4352		tio.c_lflag |= ISIG | ICANON | ECHO | ECHOE | ECHOK;
4353#ifdef ECHOKE
4354		tio.c_lflag |= ECHOKE | IEXTEN;
4355#endif
4356#ifdef ECHOCTL
4357		tio.c_lflag |= ECHOCTL | IEXTEN;
4358#endif
4359		for (nn = 0; nn < XtNumber(ttyChars); ++nn) {
4360		    if (validTtyChar(tio, nn)) {
4361			int sysMode = ttyChars[nn].sysMode;
4362#ifdef __MVS__
4363			if (tio.c_cc[sysMode] != 0) {
4364			    switch (sysMode) {
4365			    case VEOL:
4366			    case VEOF:
4367				continue;
4368			    }
4369			}
4370#endif
4371			tio.c_cc[sysMode] = (cc_t) ttyChars[nn].myDefault;
4372		    }
4373		}
4374
4375		if (override_tty_modes) {
4376		    TRACE(("applying termios ttyModes\n"));
4377		    for (nn = 0; nn < XtNumber(ttyChars); ++nn) {
4378			if (validTtyChar(tio, nn)) {
4379			    TMODE(ttyChars[nn].myMode,
4380				  tio.c_cc[ttyChars[nn].sysMode]);
4381			} else if (isTabMode(nn)) {
4382			    unsigned tmp = (unsigned) tio.c_oflag;
4383			    tmp = tmp & (unsigned) ~TABDLY;
4384			    tmp |= (unsigned) ttyModes[ttyChars[nn].myMode].value;
4385			    tio.c_oflag = tmp;
4386			}
4387		    }
4388#ifdef HAS_LTCHARS
4389		    /* both SYSV and BSD have ltchars */
4390		    TMODE(XTTYMODE_susp, ltc.t_suspc);
4391		    TMODE(XTTYMODE_dsusp, ltc.t_dsuspc);
4392		    TMODE(XTTYMODE_rprnt, ltc.t_rprntc);
4393		    TMODE(XTTYMODE_flush, ltc.t_flushc);
4394		    TMODE(XTTYMODE_weras, ltc.t_werasc);
4395		    TMODE(XTTYMODE_lnext, ltc.t_lnextc);
4396#endif
4397		}
4398#ifdef HAS_LTCHARS
4399#ifdef __hpux
4400		/* ioctl chokes when the "reserved" process group controls
4401		 * are not set to _POSIX_VDISABLE */
4402		ltc.t_rprntc = _POSIX_VDISABLE;
4403		ltc.t_rprntc = _POSIX_VDISABLE;
4404		ltc.t_flushc = _POSIX_VDISABLE;
4405		ltc.t_werasc = _POSIX_VDISABLE;
4406		ltc.t_lnextc = _POSIX_VDISABLE;
4407#endif /* __hpux */
4408		if (ioctl(ttyfd, TIOCSLTC, &ltc) == -1)
4409		    HsSysError(ERROR_TIOCSETC);
4410#endif /* HAS_LTCHARS */
4411#ifdef TIOCLSET
4412		if (ioctl(ttyfd, TIOCLSET, (char *) &lmode) == -1)
4413		    HsSysError(ERROR_TIOCLSET);
4414#endif /* TIOCLSET */
4415		if (ttySetAttr(ttyfd, &tio) == -1)
4416		    HsSysError(ERROR_TIOCSETP);
4417
4418		/* ignore errors here - some platforms don't work */
4419		UIntClr(tio.c_cflag, CSIZE);
4420		if (screen->input_eight_bits)
4421		    tio.c_cflag |= CS8;
4422		else
4423		    tio.c_cflag |= CS7;
4424		(void) ttySetAttr(ttyfd, &tio);
4425
4426#else /* !TERMIO_STRUCT */
4427		sg.sg_flags &= ~(ALLDELAY | XTABS | CBREAK | RAW);
4428		sg.sg_flags |= ECHO | CRMOD;
4429		/* make sure speed is set on pty so that editors work right */
4430		sg.sg_ispeed = line_speed;
4431		sg.sg_ospeed = line_speed;
4432		/* reset t_brkc to default value */
4433		tc.t_brkc = -1;
4434#ifdef LPASS8
4435		if (screen->input_eight_bits)
4436		    lmode |= LPASS8;
4437		else
4438		    lmode &= ~(LPASS8);
4439#endif
4440#ifdef sony
4441		jmode &= ~KM_KANJI;
4442#endif /* sony */
4443
4444		ltc = d_ltc;
4445
4446		if (override_tty_modes) {
4447		    TRACE(("applying sgtty ttyModes\n"));
4448		    TMODE(XTTYMODE_intr, tc.t_intrc);
4449		    TMODE(XTTYMODE_quit, tc.t_quitc);
4450		    TMODE(XTTYMODE_erase, sg.sg_erase);
4451		    TMODE(XTTYMODE_kill, sg.sg_kill);
4452		    TMODE(XTTYMODE_eof, tc.t_eofc);
4453		    TMODE(XTTYMODE_start, tc.t_startc);
4454		    TMODE(XTTYMODE_stop, tc.t_stopc);
4455		    TMODE(XTTYMODE_brk, tc.t_brkc);
4456		    /* both SYSV and BSD have ltchars */
4457		    TMODE(XTTYMODE_susp, ltc.t_suspc);
4458		    TMODE(XTTYMODE_dsusp, ltc.t_dsuspc);
4459		    TMODE(XTTYMODE_rprnt, ltc.t_rprntc);
4460		    TMODE(XTTYMODE_flush, ltc.t_flushc);
4461		    TMODE(XTTYMODE_weras, ltc.t_werasc);
4462		    TMODE(XTTYMODE_lnext, ltc.t_lnextc);
4463		    if (ttyModes[XTTYMODE_tabs].set
4464			|| ttyModes[XTTYMODE__tabs].set) {
4465			sg.sg_flags &= ~XTABS;
4466			if (ttyModes[XTTYMODE__tabs].set.set)
4467			    sg.sg_flags |= XTABS;
4468		    }
4469		}
4470
4471		if (ioctl(ttyfd, TIOCSETP, (char *) &sg) == -1)
4472		    HsSysError(ERROR_TIOCSETP);
4473		if (ioctl(ttyfd, TIOCSETC, (char *) &tc) == -1)
4474		    HsSysError(ERROR_TIOCSETC);
4475		if (ioctl(ttyfd, TIOCSETD, (char *) &discipline) == -1)
4476		    HsSysError(ERROR_TIOCSETD);
4477		if (ioctl(ttyfd, TIOCSLTC, (char *) &ltc) == -1)
4478		    HsSysError(ERROR_TIOCSLTC);
4479		if (ioctl(ttyfd, TIOCLSET, (char *) &lmode) == -1)
4480		    HsSysError(ERROR_TIOCLSET);
4481#ifdef sony
4482		if (ioctl(ttyfd, TIOCKSET, (char *) &jmode) == -1)
4483		    HsSysError(ERROR_TIOCKSET);
4484		if (ioctl(ttyfd, TIOCKSETC, (char *) &jtc) == -1)
4485		    HsSysError(ERROR_TIOCKSETC);
4486#endif /* sony */
4487#endif /* TERMIO_STRUCT */
4488#if defined(TIOCCONS) || defined(SRIOCSREDIR)
4489		if (Console) {
4490#ifdef TIOCCONS
4491		    int on = 1;
4492		    if (ioctl(ttyfd, TIOCCONS, (char *) &on) == -1)
4493			xtermPerror("cannot open console");
4494#endif
4495#ifdef SRIOCSREDIR
4496		    int fd = open("/dev/console", O_RDWR);
4497		    if (fd == -1 || ioctl(fd, SRIOCSREDIR, ttyfd) == -1)
4498			xtermPerror("cannot open console");
4499		    IGNORE_RC(close(fd));
4500#endif
4501		}
4502#endif /* TIOCCONS */
4503	    }
4504
4505	    signal(SIGCHLD, SIG_DFL);
4506#ifdef USE_SYSV_SIGHUP
4507	    /* watch out for extra shells (I don't understand either) */
4508	    signal(SIGHUP, SIG_DFL);
4509#else
4510	    signal(SIGHUP, SIG_IGN);
4511#endif
4512	    /* restore various signals to their defaults */
4513	    signal(SIGINT, SIG_DFL);
4514	    signal(SIGQUIT, SIG_DFL);
4515	    signal(SIGTERM, SIG_DFL);
4516
4517	    /*
4518	     * If we're not asked to let the parent process set the terminal's
4519	     * erase mode, or if we had the ttyModes erase resource, then set
4520	     * the terminal's erase mode from our best guess.
4521	     */
4522#if OPT_INITIAL_ERASE
4523	    TRACE(("check if we should set erase to %d:%s\n\tptyInitialErase:%d,\n\toveride_tty_modes:%d,\n\tXTTYMODE_erase:%d\n",
4524		   initial_erase,
4525		   setInitialErase ? "YES" : "NO",
4526		   resource.ptyInitialErase,
4527		   override_tty_modes,
4528		   ttyModes[XTTYMODE_erase].set));
4529	    if (setInitialErase) {
4530#if OPT_TRACE
4531		int old_erase;
4532#endif
4533#ifdef TERMIO_STRUCT
4534		if (ttyGetAttr(ttyfd, &tio) == -1)
4535		    tio = d_tio;
4536#if OPT_TRACE
4537		old_erase = tio.c_cc[VERASE];
4538#endif
4539		tio.c_cc[VERASE] = (cc_t) initial_erase;
4540		TRACE_RC(rc, ttySetAttr(ttyfd, &tio));
4541#else /* !TERMIO_STRUCT */
4542		if (ioctl(ttyfd, TIOCGETP, (char *) &sg) == -1)
4543		    sg = d_sg;
4544#if OPT_TRACE
4545		old_erase = sg.sg_erase;
4546#endif
4547		sg.sg_erase = initial_erase;
4548		rc = ioctl(ttyfd, TIOCSETP, (char *) &sg);
4549#endif /* TERMIO_STRUCT */
4550		TRACE(("%s setting erase to %d (was %d)\n",
4551		       rc ? "FAIL" : "OK", initial_erase, old_erase));
4552	    }
4553#endif
4554
4555	    xtermCopyEnv(environ);
4556
4557	    /*
4558	     * standards.freedesktop.org/startup-notification-spec/
4559	     * notes that this variable is used when a "reliable" mechanism is
4560	     * not available; in practice it must be unset to avoid confusing
4561	     * GTK applications.
4562	     */
4563	    xtermUnsetenv("DESKTOP_STARTUP_ID");
4564	    /*
4565	     * We set this temporarily to work around poor design of Xcursor.
4566	     * Unset it here to avoid confusion.
4567	     */
4568	    xtermUnsetenv("XCURSOR_PATH");
4569
4570	    xtermSetenv("TERM", resource.term_name);
4571	    if (!resource.term_name)
4572		*get_tcap_buffer(xw) = 0;
4573
4574	    sprintf(buf, "%lu",
4575		    ((unsigned long) XtWindow(SHELL_OF(CURRENT_EMU()))));
4576	    xtermSetenv("WINDOWID", buf);
4577
4578	    /* put the display into the environment of the shell */
4579	    xtermSetenv("DISPLAY", XDisplayString(screen->display));
4580
4581	    xtermSetenv("XTERM_VERSION", xtermVersion());
4582	    xtermSetenv("XTERM_LOCALE", xtermEnvLocale());
4583
4584	    /*
4585	     * For debugging only, add environment variables that can be used
4586	     * in scripts to selectively kill xterm's parent or child
4587	     * processes.
4588	     */
4589#if OPT_TRACE
4590	    sprintf(buf, "%lu", (unsigned long) xterm_parent);
4591	    xtermSetenv("XTERM_PARENT", buf);
4592	    sprintf(buf, "%lu", (unsigned long) getpid());
4593	    xtermSetenv("XTERM_CHILD", buf);
4594#endif
4595
4596	    signal(SIGTERM, SIG_DFL);
4597
4598	    /* this is the time to go and set up stdin, out, and err
4599	     */
4600	    {
4601#if defined(CRAY) && (OSMAJORVERSION >= 6)
4602		close_fd(ttyfd);
4603
4604		IGNORE_RC(close(0));
4605
4606		if (open("/dev/tty", O_RDWR)) {
4607		    SysError(ERROR_OPDEVTTY);
4608		}
4609		IGNORE_RC(close(1));
4610		IGNORE_RC(close(2));
4611		dup(0);
4612		dup(0);
4613#else
4614		/* dup the tty */
4615		for (i = 0; i <= 2; i++)
4616		    if (i != ttyfd) {
4617			IGNORE_RC(close(i));
4618			IGNORE_RC(dup(ttyfd));
4619		    }
4620#ifndef ATT
4621		/* and close the tty */
4622		if (ttyfd > 2)
4623		    close_fd(ttyfd);
4624#endif
4625#endif /* CRAY */
4626	    }
4627
4628#if !defined(USE_SYSV_PGRP)
4629#ifdef TIOCSCTTY
4630	    setsid();
4631	    ioctl(0, TIOCSCTTY, 0);
4632#endif
4633	    ioctl(0, TIOCSPGRP, (char *) &pgrp);
4634	    setpgrp(0, 0);
4635	    close(open(ttydev, O_WRONLY));
4636	    setpgrp(0, pgrp);
4637#if defined(__QNX__)
4638	    tcsetpgrp(0, pgrp /*setsid() */ );
4639#endif
4640#endif /* !USE_SYSV_PGRP */
4641
4642#ifdef Lynx
4643	    {
4644		TERMIO_STRUCT t;
4645		if (ttyGetAttr(0, &t) >= 0) {
4646		    /* this gets lost somewhere on our way... */
4647		    t.c_oflag |= OPOST;
4648		    ttySetAttr(0, &t);
4649		}
4650	    }
4651#endif
4652
4653#ifdef HAVE_UTMP
4654	    login_name = NULL;
4655	    if (x_getpwuid(screen->uid, &pw)) {
4656		login_name = x_getlogin(screen->uid, &pw);
4657	    }
4658	    if (login_name != NULL) {
4659		xtermSetenv("LOGNAME", login_name);	/* for POSIX */
4660	    }
4661#ifndef USE_UTEMPTER
4662#ifdef USE_UTMP_SETGID
4663	    setEffectiveGroup(save_egid);
4664	    TRACE_IDS;
4665#endif
4666#ifdef USE_SYSV_UTMP
4667	    /* Set up our utmp entry now.  We need to do it here
4668	     * for the following reasons:
4669	     *   - It needs to have our correct process id (for
4670	     *     login).
4671	     *   - If our parent was to set it after the fork(),
4672	     *     it might make it out before we need it.
4673	     *   - We need to do it before we go and change our
4674	     *     user and group id's.
4675	     */
4676	    (void) call_setutent();
4677	    init_utmp(DEAD_PROCESS, &utmp);
4678
4679	    /* position to entry in utmp file */
4680	    /* Test return value: beware of entries left behind: PSz 9 Mar 00 */
4681	    utret = find_utmp(&utmp);
4682	    if (utret == 0) {
4683		(void) call_setutent();
4684		init_utmp(USER_PROCESS, &utmp);
4685		utret = find_utmp(&utmp);
4686		if (utret == 0) {
4687		    (void) call_setutent();
4688		}
4689	    }
4690#if OPT_TRACE
4691	    if (!utret)
4692		TRACE(("getutid: NULL\n"));
4693	    else
4694		TRACE(("getutid: pid=%d type=%d user=%s line=%.*s id=%.*s\n",
4695		       (int) utret->ut_pid, utret->ut_type, utret->ut_user,
4696		       (int) sizeof(utret->ut_line), utret->ut_line,
4697		       (int) sizeof(utret->ut_id), utret->ut_id));
4698#endif
4699
4700	    /* set up the new entry */
4701	    utmp.ut_type = USER_PROCESS;
4702#ifdef HAVE_UTMP_UT_XSTATUS
4703	    utmp.ut_xstatus = 2;
4704#endif
4705	    copy_filled(utmp.ut_user,
4706			(login_name != NULL) ? login_name : "????",
4707			sizeof(utmp.ut_user));
4708	    /* why are we copying this string again?  (see above) */
4709	    copy_filled(utmp.ut_id, my_utmp_id(ttydev), sizeof(utmp.ut_id));
4710	    copy_filled(utmp.ut_line,
4711			my_pty_name(ttydev), sizeof(utmp.ut_line));
4712
4713#ifdef HAVE_UTMP_UT_HOST
4714	    SetUtmpHost(utmp.ut_host, screen);
4715#endif
4716#ifdef HAVE_UTMP_UT_SYSLEN
4717	    SetUtmpSysLen(utmp);
4718#endif
4719
4720	    copy_filled(utmp.ut_name,
4721			(login_name) ? login_name : "????",
4722			sizeof(utmp.ut_name));
4723
4724	    utmp.ut_pid = getpid();
4725#if defined(HAVE_UTMP_UT_XTIME)
4726#if defined(HAVE_UTMP_UT_SESSION)
4727	    utmp.ut_session = getsid(0);
4728#endif
4729	    utmp.ut_xtime = time((time_t *) 0);
4730	    utmp.ut_tv.tv_usec = 0;
4731#else
4732	    utmp.ut_time = time((time_t *) 0);
4733#endif
4734
4735	    /* write out the entry */
4736	    if (!resource.utmpInhibit) {
4737		errno = 0;
4738		call_pututline(&utmp);
4739		TRACE(("pututline: id %.*s, line %.*s, pid %ld, errno %d %s\n",
4740		       (int) sizeof(utmp.ut_id), utmp.ut_id,
4741		       (int) sizeof(utmp.ut_line), utmp.ut_line,
4742		       (long) utmp.ut_pid,
4743		       errno, (errno != 0) ? strerror(errno) : ""));
4744	    }
4745#ifdef WTMP
4746#if defined(WTMPX_FILE) && (defined(SVR4) || defined(__SCO__))
4747	    if (xw->misc.login_shell)
4748		updwtmpx(WTMPX_FILE, &utmp);
4749#elif defined(linux) && defined(__GLIBC__) && (__GLIBC__ >= 2) && !(defined(__powerpc__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
4750	    if (xw->misc.login_shell)
4751		call_updwtmp(etc_wtmp, &utmp);
4752#else
4753	    if (xw->misc.login_shell &&
4754		(i = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) {
4755		IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp)));
4756		close(i);
4757	    }
4758#endif
4759#endif
4760	    /* close the file */
4761	    (void) call_endutent();
4762
4763#else /* USE_SYSV_UTMP */
4764	    /* We can now get our ttyslot!  We can also set the initial
4765	     * utmp entry.
4766	     */
4767	    tslot = ttyslot();
4768	    added_utmp_entry = False;
4769	    {
4770		if (tslot > 0 && OkPasswd(&pw) && !resource.utmpInhibit &&
4771		    (i = open(etc_utmp, O_WRONLY)) >= 0) {
4772		    memset(&utmp, 0, sizeof(utmp));
4773		    copy_filled(utmp.ut_line,
4774				my_pty_name(ttydev),
4775				sizeof(utmp.ut_line));
4776		    copy_filled(utmp.ut_name, login_name,
4777				sizeof(utmp.ut_name));
4778#ifdef HAVE_UTMP_UT_HOST
4779		    SetUtmpHost(utmp.ut_host, screen);
4780#endif
4781#ifdef HAVE_UTMP_UT_SYSLEN
4782		    SetUtmpSysLen(utmp);
4783#endif
4784
4785		    utmp.ut_time = time((time_t *) 0);
4786		    lseek(i, (long) (tslot * sizeof(utmp)), 0);
4787		    IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp)));
4788		    close(i);
4789		    added_utmp_entry = True;
4790#if defined(WTMP)
4791		    if (xw->misc.login_shell &&
4792			(i = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) {
4793			int status;
4794			status = write(i, (char *) &utmp, sizeof(utmp));
4795			status = close(i);
4796		    }
4797#elif defined(MNX_LASTLOG)
4798		    if (xw->misc.login_shell &&
4799			(i = open(_U_LASTLOG, O_WRONLY)) >= 0) {
4800			lseek(i, (long) (screen->uid *
4801					 sizeof(utmp)), 0);
4802			IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp)));
4803			close(i);
4804		    }
4805#endif /* WTMP or MNX_LASTLOG */
4806		} else
4807		    tslot = -tslot;
4808	    }
4809
4810	    /* Let's pass our ttyslot to our parent so that it can
4811	     * clean up after us.
4812	     */
4813#if OPT_PTY_HANDSHAKE
4814	    if (resource.ptyHandshake) {
4815		handshake.tty_slot = tslot;
4816	    }
4817#endif /* OPT_PTY_HANDSHAKE */
4818#endif /* USE_SYSV_UTMP */
4819
4820#ifdef USE_LASTLOGX
4821	    if (xw->misc.login_shell) {
4822		memset(&lastlogx, 0, sizeof(lastlogx));
4823		copy_filled(lastlogx.ll_line,
4824			    my_pty_name(ttydev),
4825			    sizeof(lastlogx.ll_line));
4826		X_GETTIMEOFDAY(&lastlogx.ll_tv);
4827		SetUtmpHost(lastlogx.ll_host, screen);
4828		updlastlogx(_PATH_LASTLOGX, screen->uid, &lastlogx);
4829	    }
4830#endif
4831
4832#ifdef USE_LASTLOG
4833	    if (xw->misc.login_shell &&
4834		(i = open(etc_lastlog, O_WRONLY)) >= 0) {
4835		size_t size = sizeof(struct lastlog);
4836		off_t offset = (off_t) ((size_t) screen->uid * size);
4837
4838		memset(&lastlog, 0, size);
4839		copy_filled(lastlog.ll_line,
4840			    my_pty_name(ttydev),
4841			    sizeof(lastlog.ll_line));
4842		SetUtmpHost(lastlog.ll_host, screen);
4843		lastlog.ll_time = time((time_t *) 0);
4844		if (lseek(i, offset, 0) != (off_t) (-1)) {
4845		    IGNORE_RC(write(i, (char *) &lastlog, size));
4846		}
4847		close(i);
4848	    }
4849#endif /* USE_LASTLOG */
4850
4851#if defined(USE_UTMP_SETGID)
4852	    disableSetGid();
4853	    TRACE_IDS;
4854#endif
4855
4856#if OPT_PTY_HANDSHAKE
4857	    /* Let our parent know that we set up our utmp entry
4858	     * so that it can clean up after us.
4859	     */
4860	    if (resource.ptyHandshake) {
4861		handshake.status = UTMP_ADDED;
4862		handshake.error = 0;
4863		copy_handshake(handshake, ttydev);
4864		TRACE_HANDSHAKE("writing", &handshake);
4865		IGNORE_RC(write(cp_pipe[1], (char *) &handshake, sizeof(handshake)));
4866	    }
4867#endif /* OPT_PTY_HANDSHAKE */
4868#endif /* USE_UTEMPTER */
4869#endif /* HAVE_UTMP */
4870
4871	    IGNORE_RC(setgid(screen->gid));
4872	    TRACE_IDS;
4873#ifdef HAVE_INITGROUPS
4874	    if (geteuid() == 0 && OkPasswd(&pw)) {
4875		if (initgroups(login_name, pw.pw_gid)) {
4876		    perror("initgroups failed");
4877		    SysError(ERROR_INIGROUPS);
4878		}
4879	    }
4880#endif
4881	    if (setuid(screen->uid)) {
4882		SysError(ERROR_SETUID);
4883	    }
4884	    TRACE_IDS;
4885#if OPT_PTY_HANDSHAKE
4886	    if (resource.ptyHandshake) {
4887		/* mark the pipes as close on exec */
4888		(void) fcntl(cp_pipe[1], F_SETFD, 1);
4889		(void) fcntl(pc_pipe[0], F_SETFD, 1);
4890
4891		/* We are at the point where we are going to
4892		 * exec our shell (or whatever).  Let our parent
4893		 * know we arrived safely.
4894		 */
4895		handshake.status = PTY_GOOD;
4896		handshake.error = 0;
4897		copy_handshake(handshake, ttydev);
4898		TRACE_HANDSHAKE("writing", &handshake);
4899		IGNORE_RC(write(cp_pipe[1],
4900				(const char *) &handshake,
4901				sizeof(handshake)));
4902
4903		if (resource.wait_for_map) {
4904		    i = (int) read(pc_pipe[0], (char *) &handshake,
4905				   sizeof(handshake));
4906		    if (i != sizeof(handshake) ||
4907			handshake.status != PTY_EXEC) {
4908			/* some very bad problem occurred */
4909			exit(ERROR_PTY_EXEC);
4910		    }
4911		    if (handshake.rows > 0 && handshake.cols > 0) {
4912			TRACE(("handshake read ttysize: %dx%d\n",
4913			       handshake.rows, handshake.cols));
4914			set_max_row(screen, handshake.rows);
4915			set_max_col(screen, handshake.cols);
4916#ifdef TTYSIZE_STRUCT
4917			got_handshake_size = True;
4918			setup_winsize(ts, MaxRows(screen), MaxCols(screen),
4919				      FullHeight(screen), FullWidth(screen));
4920			trace_winsize(ts, "got handshake");
4921#endif /* TTYSIZE_STRUCT */
4922		    }
4923		}
4924	    }
4925#endif /* OPT_PTY_HANDSHAKE */
4926
4927#ifdef USE_SYSV_ENVVARS
4928	    {
4929		char numbuf[12];
4930		sprintf(numbuf, "%d", MaxCols(screen));
4931		xtermSetenv("COLUMNS", numbuf);
4932		sprintf(numbuf, "%d", MaxRows(screen));
4933		xtermSetenv("LINES", numbuf);
4934	    }
4935#ifdef HAVE_UTMP
4936	    if (OkPasswd(&pw)) {	/* SVR4 doesn't provide these */
4937		if (!x_getenv("HOME"))
4938		    xtermSetenv("HOME", pw.pw_dir);
4939		if (!x_getenv("SHELL"))
4940		    xtermSetenv("SHELL", pw.pw_shell);
4941	    }
4942#endif /* HAVE_UTMP */
4943#else /* USE_SYSV_ENVVARS */
4944	    if (*(newtc = get_tcap_buffer(xw)) != '\0') {
4945		resize_termcap(xw);
4946		if (xw->misc.titeInhibit && !xw->misc.tiXtraScroll) {
4947		    remove_termcap_entry(newtc, "ti=");
4948		    remove_termcap_entry(newtc, "te=");
4949		}
4950		/*
4951		 * work around broken termcap entries */
4952		if (resource.useInsertMode) {
4953		    remove_termcap_entry(newtc, "ic=");
4954		    /* don't get duplicates */
4955		    remove_termcap_entry(newtc, "im=");
4956		    remove_termcap_entry(newtc, "ei=");
4957		    remove_termcap_entry(newtc, "mi");
4958		    if (*newtc)
4959			strcat(newtc, ":im=\\E[4h:ei=\\E[4l:mi:");
4960		}
4961		if (*newtc) {
4962#if OPT_INITIAL_ERASE
4963		    unsigned len;
4964		    remove_termcap_entry(newtc, TERMCAP_ERASE "=");
4965		    len = (unsigned) strlen(newtc);
4966		    if (len != 0 && newtc[len - 1] == ':')
4967			len--;
4968		    sprintf(newtc + len, ":%s=\\%03o:",
4969			    TERMCAP_ERASE,
4970			    CharOf(initial_erase));
4971#endif
4972		    xtermSetenv("TERMCAP", newtc);
4973		}
4974	    }
4975#endif /* USE_SYSV_ENVVARS */
4976#ifdef OWN_TERMINFO_ENV
4977	    xtermSetenv("TERMINFO", OWN_TERMINFO_DIR);
4978#endif
4979
4980#if OPT_PTY_HANDSHAKE
4981	    /*
4982	     * Need to reset after all the ioctl bashing we did above.
4983	     *
4984	     * If we expect the waitForMap logic to set the handshake-size,
4985	     * use that to prevent races.
4986	     */
4987	    TRACE(("should we reset screensize after pty-handshake?\n"));
4988	    TRACE(("... ptyHandshake      :%d\n", resource.ptyHandshake));
4989	    TRACE(("... ptySttySize       :%d\n", resource.ptySttySize));
4990	    TRACE(("... got_handshake_size:%d\n", got_handshake_size));
4991	    TRACE(("... wait_for_map0     :%d\n", resource.wait_for_map0));
4992	    if (resource.ptyHandshake
4993		&& resource.ptySttySize
4994		&& (got_handshake_size || !resource.wait_for_map0)) {
4995#ifdef TTYSIZE_STRUCT
4996		TRACE_RC(i, SET_TTYSIZE(0, ts));
4997		trace_winsize(ts, "ptyHandshake SET_TTYSIZE");
4998#endif /* TTYSIZE_STRUCT */
4999	    }
5000#endif /* OPT_PTY_HANDSHAKE */
5001	    signal(SIGHUP, SIG_DFL);
5002
5003	    /*
5004	     * If we have an explicit shell to run, make that set $SHELL.
5005	     * Next, allow an existing setting of $SHELL, for absolute paths.
5006	     * Otherwise, if $SHELL is not set, determine it from the user's
5007	     * password information, if possible.
5008	     *
5009	     * Incidentally, our setting of $SHELL tells luit to use that
5010	     * program rather than choosing between $SHELL and "/bin/sh".
5011	     */
5012	    if (validShell(explicit_shname)) {
5013		xtermSetenv("SHELL", explicit_shname);
5014	    } else if (validProgram(shell_path = x_getenv("SHELL"))) {
5015		if (!validShell(shell_path)) {
5016		    xtermUnsetenv("SHELL");
5017		}
5018	    } else if ((!OkPasswd(&pw) && !x_getpwuid(screen->uid, &pw))
5019		       || *(shell_path = x_strdup(pw.pw_shell)) == 0) {
5020		shell_path = resetShell(shell_path);
5021	    } else if (validShell(shell_path)) {
5022		xtermSetenv("SHELL", shell_path);
5023	    } else {
5024		shell_path = resetShell(shell_path);
5025	    }
5026
5027	    /*
5028	     * Set $XTERM_SHELL, which is not necessarily a valid shell, but
5029	     * is executable.
5030	     */
5031	    if (validProgram(explicit_shname)) {
5032		shell_path = explicit_shname;
5033	    } else if (shell_path == 0) {
5034		/* this could happen if the explicit shname lost a race */
5035		shell_path = resetShell(shell_path);
5036	    }
5037	    xtermSetenv("XTERM_SHELL", shell_path);
5038
5039	    shname = x_basename(shell_path);
5040	    TRACE(("shell path '%s' leaf '%s'\n", shell_path, shname));
5041
5042#if OPT_LUIT_PROG
5043	    /*
5044	     * Use two copies of command_to_exec, in case luit is not actually
5045	     * there, or refuses to run.  In that case we will fall-through to
5046	     * to command that the user gave anyway.
5047	     */
5048	    if (command_to_exec_with_luit && command_to_exec) {
5049		char *myShell = xtermFindShell(*command_to_exec_with_luit, False);
5050		xtermSetenv("XTERM_SHELL", myShell);
5051		free(myShell);
5052		TRACE_ARGV("spawning luit command", command_to_exec_with_luit);
5053		execvp(*command_to_exec_with_luit, command_to_exec_with_luit);
5054		xtermPerror("Can't execvp %s", *command_to_exec_with_luit);
5055		xtermWarning("cannot support your locale.\n");
5056	    }
5057#endif
5058	    if (command_to_exec) {
5059		char *myShell = xtermFindShell(*command_to_exec, False);
5060		xtermSetenv("XTERM_SHELL", myShell);
5061		free(myShell);
5062		TRACE_ARGV("spawning command", command_to_exec);
5063		execvp(*command_to_exec, command_to_exec);
5064		if (command_to_exec[1] == 0)
5065		    execlp(shell_path, shname, "-c", command_to_exec[0],
5066			   (void *) 0);
5067		xtermPerror("Can't execvp %s", *command_to_exec);
5068	    }
5069#ifdef USE_SYSV_SIGHUP
5070	    /* fix pts sh hanging around */
5071	    signal(SIGHUP, SIG_DFL);
5072#endif
5073
5074	    if ((shname_minus = malloc(strlen(shname) + 2)) != 0) {
5075		(void) strcpy(shname_minus, "-");
5076		(void) strcat(shname_minus, shname);
5077	    } else {
5078		static char default_minus[] = "-sh";
5079		shname_minus = default_minus;
5080	    }
5081#ifndef TERMIO_STRUCT
5082	    ldisc = (!XStrCmp("csh", shname + strlen(shname) - 3)
5083		     ? NTTYDISC
5084		     : 0);
5085	    ioctl(0, TIOCSETD, (char *) &ldisc);
5086#endif /* !TERMIO_STRUCT */
5087
5088#ifdef USE_LOGIN_DASH_P
5089	    if (xw->misc.login_shell && OkPasswd(&pw) && added_utmp_entry)
5090		execl(bin_login, "login", "-p", "-f", login_name, (void *) 0);
5091#endif
5092
5093#if OPT_LUIT_PROG
5094	    if (command_to_exec_with_luit) {
5095		if (xw->misc.login_shell) {
5096		    char *params[4];
5097		    params[0] = x_strdup("-argv0");
5098		    params[1] = shname_minus;
5099		    params[2] = NULL;
5100		    x_appendargv(command_to_exec_with_luit
5101				 + command_length_with_luit,
5102				 params);
5103		}
5104		TRACE_ARGV("final luit command", command_to_exec_with_luit);
5105		execvp(*command_to_exec_with_luit, command_to_exec_with_luit);
5106		/* Exec failed. */
5107		xtermPerror("Can't execvp %s", *command_to_exec_with_luit);
5108	    }
5109#endif
5110	    execlp(shell_path,
5111		   (xw->misc.login_shell ? shname_minus : shname),
5112		   (void *) 0);
5113
5114	    /* Exec failed. */
5115	    xtermPerror("Could not exec %s", shell_path);
5116	    IGNORE_RC(sleep(5));
5117	    free(shell_path);
5118	    exit(ERROR_EXEC);
5119	}
5120	/* end if in child after fork */
5121#if OPT_PTY_HANDSHAKE
5122	if (resource.ptyHandshake) {
5123	    /* Parent process.  Let's handle handshaked requests to our
5124	     * child process.
5125	     */
5126
5127	    /* close childs's sides of the pipes */
5128	    close(cp_pipe[1]);
5129	    close(pc_pipe[0]);
5130
5131	    for (done = 0; !done;) {
5132		if (read(cp_pipe[0],
5133			 (char *) &handshake,
5134			 sizeof(handshake)) <= 0) {
5135		    /* Our child is done talking to us.  If it terminated
5136		     * due to an error, we will catch the death of child
5137		     * and clean up.
5138		     */
5139		    break;
5140		}
5141
5142		TRACE_HANDSHAKE("read", &handshake);
5143		switch (handshake.status) {
5144		case PTY_GOOD:
5145		    /* Success!  Let's free up resources and
5146		     * continue.
5147		     */
5148		    done = 1;
5149		    break;
5150
5151		case PTY_BAD:
5152		    /* The open of the pty failed!  Let's get
5153		     * another one.
5154		     */
5155		    IGNORE_RC(close(screen->respond));
5156		    if (get_pty(&screen->respond, XDisplayString(screen->display))) {
5157			/* no more ptys! */
5158			xtermPerror("child process can find no available ptys");
5159			handshake.status = PTY_NOMORE;
5160			TRACE_HANDSHAKE("writing", &handshake);
5161			IGNORE_RC(write(pc_pipe[1],
5162					(const char *) &handshake,
5163					sizeof(handshake)));
5164			exit(ERROR_PTYS);
5165		    }
5166		    handshake.status = PTY_NEW;
5167		    copy_handshake(handshake, ttydev);
5168		    TRACE_HANDSHAKE("writing", &handshake);
5169		    IGNORE_RC(write(pc_pipe[1],
5170				    (const char *) &handshake,
5171				    sizeof(handshake)));
5172		    break;
5173
5174		case PTY_FATALERROR:
5175		    errno = handshake.error;
5176		    close(cp_pipe[0]);
5177		    close(pc_pipe[1]);
5178		    SysError(handshake.fatal_error);
5179		    /*NOTREACHED */
5180
5181		case UTMP_ADDED:
5182		    /* The utmp entry was set by our slave.  Remember
5183		     * this so that we can reset it later.
5184		     */
5185		    added_utmp_entry = True;
5186#ifndef	USE_SYSV_UTMP
5187		    tslot = handshake.tty_slot;
5188#endif /* USE_SYSV_UTMP */
5189		    free(ttydev);
5190		    handshake.buffer[HANDSHAKE_LEN - 1] = '\0';
5191		    ttydev = x_strdup(handshake.buffer);
5192		    break;
5193		case PTY_NEW:
5194		case PTY_NOMORE:
5195		case UTMP_TTYSLOT:
5196		case PTY_EXEC:
5197		default:
5198		    xtermWarning("unexpected handshake status %d\n",
5199				 (int) handshake.status);
5200		}
5201	    }
5202	    /* close our sides of the pipes */
5203	    if (!resource.wait_for_map) {
5204		close(cp_pipe[0]);
5205		close(pc_pipe[1]);
5206	    }
5207	}
5208#endif /* OPT_PTY_HANDSHAKE */
5209    }
5210
5211    /* end if no slave */
5212    /*
5213     * still in parent (xterm process)
5214     */
5215#ifdef USE_SYSV_SIGHUP
5216    /* hung sh problem? */
5217    signal(SIGHUP, SIG_DFL);
5218#else
5219    signal(SIGHUP, SIG_IGN);
5220#endif
5221
5222/*
5223 * Unfortunately, System V seems to have trouble divorcing the child process
5224 * from the process group of xterm.  This is a problem because hitting the
5225 * INTR or QUIT characters on the keyboard will cause xterm to go away if we
5226 * don't ignore the signals.  This is annoying.
5227 */
5228
5229#if defined(USE_SYSV_SIGNALS) && !defined(SIGTSTP)
5230    signal(SIGINT, SIG_IGN);
5231
5232#ifndef SYSV
5233    /* hung shell problem */
5234    signal(SIGQUIT, SIG_IGN);
5235#endif
5236    signal(SIGTERM, SIG_IGN);
5237#elif defined(SYSV) || defined(__osf__)
5238    /* if we were spawned by a jobcontrol smart shell (like ksh or csh),
5239     * then our pgrp and pid will be the same.  If we were spawned by
5240     * a jobcontrol dumb shell (like /bin/sh), then we will be in our
5241     * parent's pgrp, and we must ignore keyboard signals, or we will
5242     * tank on everything.
5243     */
5244    if (getpid() == getpgrp()) {
5245	(void) signal(SIGINT, Exit);
5246	(void) signal(SIGQUIT, Exit);
5247	(void) signal(SIGTERM, Exit);
5248    } else {
5249	(void) signal(SIGINT, SIG_IGN);
5250	(void) signal(SIGQUIT, SIG_IGN);
5251	(void) signal(SIGTERM, SIG_IGN);
5252    }
5253    (void) signal(SIGPIPE, Exit);
5254#else /* SYSV */
5255    signal(SIGINT, Exit);
5256    signal(SIGQUIT, Exit);
5257    signal(SIGTERM, Exit);
5258    signal(SIGPIPE, Exit);
5259#endif /* USE_SYSV_SIGNALS and not SIGTSTP */
5260#ifdef NO_LEAKS
5261    if (ok_termcap != True)
5262	free(TermName);
5263#endif
5264
5265    return 0;
5266}				/* end spawnXTerm */
5267
5268void
5269Exit(int n)
5270{
5271    XtermWidget xw = term;
5272    TScreen *screen = TScreenOf(xw);
5273
5274#ifdef USE_UTEMPTER
5275    DEBUG_MSG("handle:Exit USE_UTEMPTER\n");
5276    if (!resource.utmpInhibit && added_utmp_entry) {
5277	TRACE(("...calling removeFromUtmp\n"));
5278	UTEMPTER_DEL();
5279    }
5280#elif defined(HAVE_UTMP)
5281#ifdef USE_SYSV_UTMP
5282    struct UTMP_STR utmp;
5283    struct UTMP_STR *utptr;
5284
5285    DEBUG_MSG("handle:Exit USE_SYSV_UTMP\n");
5286    /* don't do this more than once */
5287    if (xterm_exiting) {
5288	exit(n);
5289    }
5290    xterm_exiting = True;
5291
5292#ifdef PUCC_PTYD
5293    closepty(ttydev, ptydev, (resource.utmpInhibit ? OPTY_NOP : OPTY_LOGIN), screen->respond);
5294#endif /* PUCC_PTYD */
5295
5296    /* cleanup the utmp entry we forged earlier */
5297    if (!resource.utmpInhibit
5298#if OPT_PTY_HANDSHAKE		/* without handshake, no way to know */
5299	&& (resource.ptyHandshake && added_utmp_entry)
5300#endif /* OPT_PTY_HANDSHAKE */
5301	) {
5302#if defined(USE_UTMP_SETGID)
5303	setEffectiveGroup(save_egid);
5304	TRACE_IDS;
5305#endif
5306	init_utmp(USER_PROCESS, &utmp);
5307	(void) call_setutent();
5308
5309	/*
5310	 * We could use getutline() if we didn't support old systems.
5311	 */
5312	while ((utptr = find_utmp(&utmp)) != 0) {
5313	    if (utptr->ut_pid == screen->pid) {
5314		utptr->ut_type = DEAD_PROCESS;
5315#if defined(HAVE_UTMP_UT_XTIME)
5316#if defined(HAVE_UTMP_UT_SESSION)
5317		utptr->ut_session = getsid(0);
5318#endif
5319		utptr->ut_xtime = time((time_t *) 0);
5320		utptr->ut_tv.tv_usec = 0;
5321#else
5322		*utptr->ut_user = 0;
5323		utptr->ut_time = time((time_t *) 0);
5324#endif
5325		(void) call_pututline(utptr);
5326#ifdef WTMP
5327#if defined(WTMPX_FILE) && (defined(SVR4) || defined(__SCO__))
5328		if (xw->misc.login_shell)
5329		    updwtmpx(WTMPX_FILE, utptr);
5330#elif defined(linux) && defined(__GLIBC__) && (__GLIBC__ >= 2) && !(defined(__powerpc__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
5331		copy_filled(utmp.ut_line, utptr->ut_line, sizeof(utmp.ut_line));
5332		if (xw->misc.login_shell)
5333		    call_updwtmp(etc_wtmp, utptr);
5334#else
5335		/* set wtmp entry if wtmp file exists */
5336		if (xw->misc.login_shell) {
5337		    int fd;
5338		    if ((fd = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) {
5339			IGNORE_RC(write(fd, utptr, sizeof(*utptr)));
5340			close(fd);
5341		    }
5342		}
5343#endif
5344#endif
5345		break;
5346	    }
5347	    memset(utptr, 0, sizeof(*utptr));	/* keep searching */
5348	}
5349	(void) call_endutent();
5350#ifdef USE_UTMP_SETGID
5351	disableSetGid();
5352	TRACE_IDS;
5353#endif
5354    }
5355#else /* not USE_SYSV_UTMP */
5356    int wfd;
5357    struct utmp utmp;
5358
5359    DEBUG_MSG("handle:Exit !USE_SYSV_UTMP\n");
5360    if (!resource.utmpInhibit && added_utmp_entry &&
5361	(am_slave < 0 && tslot > 0)) {
5362#if defined(USE_UTMP_SETGID)
5363	setEffectiveGroup(save_egid);
5364	TRACE_IDS;
5365#endif
5366	if ((wfd = open(etc_utmp, O_WRONLY)) >= 0) {
5367	    memset(&utmp, 0, sizeof(utmp));
5368	    lseek(wfd, (long) (tslot * sizeof(utmp)), 0);
5369	    IGNORE_RC(write(wfd, (char *) &utmp, sizeof(utmp)));
5370	    close(wfd);
5371	}
5372#ifdef WTMP
5373	if (xw->misc.login_shell &&
5374	    (wfd = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) {
5375	    copy_filled(utmp.ut_line,
5376			my_pty_name(ttydev),
5377			sizeof(utmp.ut_line));
5378	    utmp.ut_time = time((time_t *) 0);
5379	    IGNORE_RC(write(wfd, (char *) &utmp, sizeof(utmp)));
5380	    close(wfd);
5381	}
5382#endif /* WTMP */
5383#ifdef USE_UTMP_SETGID
5384	disableSetGid();
5385	TRACE_IDS;
5386#endif
5387    }
5388#endif /* USE_SYSV_UTMP */
5389#endif /* HAVE_UTMP */
5390
5391    cleanup_colored_cursor();
5392
5393    /*
5394     * Flush pending data before releasing ownership, so nobody else can write
5395     * in the middle of the data.
5396     */
5397    ttyFlush(screen->respond);
5398
5399#ifdef USE_PTY_SEARCH
5400    if (am_slave < 0) {
5401	TRACE_IDS;
5402	/* restore ownership of tty and pty */
5403	set_owner(ttydev, 0, 0, 0666U);
5404#if (defined(USE_PTY_DEVICE) && !defined(__sgi) && !defined(__hpux))
5405	set_owner(ptydev, 0, 0, 0666U);
5406#endif
5407    }
5408#endif
5409
5410    /*
5411     * Close after releasing ownership to avoid race condition: other programs
5412     * grabbing it, and *then* having us release ownership....
5413     */
5414    close(screen->respond);	/* close explicitly to avoid race with slave side */
5415#ifdef ALLOWLOGGING
5416    if (screen->logging)
5417	CloseLog(xw);
5418#endif
5419
5420    xtermPrintOnXError(xw, n);
5421
5422#ifdef NO_LEAKS
5423    if (n == 0) {
5424	Display *dpy = TScreenOf(xw)->display;
5425
5426	TRACE(("Freeing memory leaks\n"));
5427
5428	if (toplevel) {
5429	    XtDestroyWidget(toplevel);
5430	    TRACE(("destroyed top-level widget\n"));
5431	}
5432	sortedOpts(0, 0, 0);
5433	noleaks_charproc();
5434	noleaks_ptydata();
5435#if OPT_GRAPHICS
5436	noleaks_graphics();
5437#endif
5438#if OPT_WIDE_CHARS
5439	noleaks_CharacterClass();
5440#endif
5441	/* XrmSetDatabase(dpy, 0); increases leaks ;-) */
5442	XtCloseDisplay(dpy);
5443	XtDestroyApplicationContext(app_con);
5444	xtermCloseSession();
5445	TRACE(("closed display\n"));
5446
5447	TRACE_CLOSE();
5448    }
5449#endif
5450
5451    exit(n);
5452}
5453
5454/* ARGSUSED */
5455static void
5456resize_termcap(XtermWidget xw)
5457{
5458    char *newtc = get_tcap_buffer(xw);
5459
5460#ifndef USE_SYSV_ENVVARS
5461    if (!TEK4014_ACTIVE(xw) && *newtc) {
5462	TScreen *screen = TScreenOf(xw);
5463	char *ptr1, *ptr2;
5464	size_t i;
5465	int li_first = 0;
5466	char *temp;
5467	char oldtc[TERMCAP_SIZE];
5468
5469	strcpy(oldtc, newtc);
5470	TRACE(("resize %s\n", oldtc));
5471	if ((ptr1 = x_strindex(oldtc, "co#")) == NULL) {
5472	    strcat(oldtc, "co#80:");
5473	    ptr1 = x_strindex(oldtc, "co#");
5474	}
5475	if ((ptr2 = x_strindex(oldtc, "li#")) == NULL) {
5476	    strcat(oldtc, "li#24:");
5477	    ptr2 = x_strindex(oldtc, "li#");
5478	}
5479	if (ptr1 > ptr2) {
5480	    li_first++;
5481	    temp = ptr1;
5482	    ptr1 = ptr2;
5483	    ptr2 = temp;
5484	}
5485	ptr1 += 3;
5486	ptr2 += 3;
5487	strncpy(newtc, oldtc, i = (size_t) (ptr1 - oldtc));
5488	temp = newtc + i;
5489	sprintf(temp, "%d", (li_first
5490			     ? MaxRows(screen)
5491			     : MaxCols(screen)));
5492	temp += strlen(temp);
5493	if ((ptr1 = strchr(ptr1, ':')) != 0 && (ptr1 < ptr2)) {
5494	    strncpy(temp, ptr1, i = (size_t) (ptr2 - ptr1));
5495	    temp += i;
5496	    sprintf(temp, "%d", (li_first
5497				 ? MaxCols(screen)
5498				 : MaxRows(screen)));
5499	    if ((ptr2 = strchr(ptr2, ':')) != 0) {
5500		strcat(temp, ptr2);
5501	    }
5502	}
5503	TRACE(("   ==> %s\n", newtc));
5504	TRACE(("   new size %dx%d\n", MaxRows(screen), MaxCols(screen)));
5505    }
5506#endif /* USE_SYSV_ENVVARS */
5507}
5508
5509#endif /* ! VMS */
5510
5511/*
5512 * Does a non-blocking wait for a child process.  If the system
5513 * doesn't support non-blocking wait, do nothing.
5514 * Returns the pid of the child, or 0 or -1 if none or error.
5515 */
5516int
5517nonblocking_wait(void)
5518{
5519#ifdef USE_POSIX_WAIT
5520    pid_t pid;
5521
5522    pid = waitpid(-1, NULL, WNOHANG);
5523#elif defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP))
5524    /* cannot do non-blocking wait */
5525    int pid = 0;
5526#else /* defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP)) */
5527#if defined(Lynx)
5528    int status;
5529#else
5530    union wait status;
5531#endif
5532    int pid;
5533
5534    pid = wait3(&status, WNOHANG, (struct rusage *) NULL);
5535#endif /* USE_POSIX_WAIT else */
5536    return pid;
5537}
5538
5539#ifndef VMS
5540
5541/* ARGSUSED */
5542static void
5543reapchild(int n GCC_UNUSED)
5544{
5545    int olderrno = errno;
5546    int pid;
5547
5548    DEBUG_MSG("handle:reapchild\n");
5549
5550    pid = wait(NULL);
5551
5552#ifdef USE_SYSV_SIGNALS
5553    /* cannot re-enable signal before waiting for child
5554     * because then SVR4 loops.  Sigh.  HP-UX 9.01 too.
5555     */
5556    (void) signal(SIGCHLD, reapchild);
5557#endif
5558
5559    do {
5560	if (pid == TScreenOf(term)->pid) {
5561	    DEBUG_MSG("Exiting\n");
5562	    if (hold_screen)
5563		caught_intr = True;
5564	    else
5565		need_cleanup = True;
5566	}
5567    } while ((pid = nonblocking_wait()) > 0);
5568
5569    errno = olderrno;
5570}
5571#endif /* !VMS */
5572
5573static void
5574remove_termcap_entry(char *buf, const char *str)
5575{
5576    char *base = buf;
5577    char *first = base;
5578    int count = 0;
5579    size_t len = strlen(str);
5580
5581    TRACE(("*** remove_termcap_entry('%s', '%s')\n", str, buf));
5582
5583    while (*buf != 0) {
5584	if (!count && !strncmp(buf, str, len)) {
5585	    while (*buf != 0) {
5586		if (*buf == '\\')
5587		    buf++;
5588		else if (*buf == ':')
5589		    break;
5590		if (*buf != 0)
5591		    buf++;
5592	    }
5593	    while ((*first++ = *buf++) != 0) {
5594		;
5595	    }
5596	    TRACE(("...removed_termcap_entry('%s', '%s')\n", str, base));
5597	    return;
5598	} else if (*buf == '\\') {
5599	    buf++;
5600	} else if (*buf == ':') {
5601	    first = buf;
5602	    count = 0;
5603	} else if (!isspace(CharOf(*buf))) {
5604	    count++;
5605	}
5606	if (*buf != 0)
5607	    buf++;
5608    }
5609    TRACE(("...cannot remove\n"));
5610}
5611
5612/*
5613 * parse_tty_modes accepts lines of the following form:
5614 *
5615 *         [SETTING] ...
5616 *
5617 * where setting consists of the words in the ttyModes[] array followed by a
5618 * character or ^char.
5619 */
5620static int
5621parse_tty_modes(char *s)
5622{
5623    int c;
5624    Cardinal j, k;
5625    int count = 0;
5626    Boolean found;
5627
5628    TRACE(("parse_tty_modes\n"));
5629    for (;;) {
5630	size_t len;
5631
5632	while (*s && isspace(CharOf(*s))) {
5633	    s++;
5634	}
5635	if (!*s) {
5636	    return count;
5637	}
5638
5639	for (len = 0; s[len] && !isspace(CharOf(s[len])); ++len) {
5640	    ;
5641	}
5642	found = False;
5643	for (j = 0; j < XtNumber(ttyModes); ++j) {
5644	    if (len == ttyModes[j].len
5645		&& strncmp(s,
5646			   ttyModes[j].name,
5647			   ttyModes[j].len) == 0) {
5648		found = True;
5649		break;
5650	    }
5651	}
5652	if (!found) {
5653	    return -1;
5654	}
5655
5656	s += ttyModes[j].len;
5657	while (*s && isspace(CharOf(*s))) {
5658	    s++;
5659	}
5660
5661	/* check if this needs a parameter */
5662	found = False;
5663	for (k = 0, c = 0; k < XtNumber(ttyChars); ++k) {
5664	    if ((int) j == ttyChars[k].myMode) {
5665		if (ttyChars[k].sysMode < 0) {
5666		    found = True;
5667		    c = ttyChars[k].myDefault;
5668		}
5669		break;
5670	    }
5671	}
5672
5673	if (!found) {
5674	    if (!*s
5675		|| (c = decode_keyvalue(&s, False)) == -1) {
5676		return -1;
5677	    }
5678	}
5679	ttyModes[j].value = c;
5680	ttyModes[j].set = 1;
5681	count++;
5682	TRACE(("...parsed #%d: %s=%#x\n", count, ttyModes[j].name, c));
5683    }
5684}
5685
5686#ifndef VMS			/* don't use pipes on OpenVMS */
5687int
5688GetBytesAvailable(int fd)
5689{
5690#if defined(FIONREAD)
5691    int arg;
5692    ioctl(fd, FIONREAD, (char *) &arg);
5693    return (int) arg;
5694#elif defined(__CYGWIN__)
5695    fd_set set;
5696    struct timeval select_timeout =
5697    {0, 0};
5698
5699    FD_ZERO(&set);
5700    FD_SET(fd, &set);
5701    if (Select(fd + 1, &set, NULL, NULL, &select_timeout) > 0)
5702	return 1;
5703    else
5704	return 0;
5705#elif defined(FIORDCK)
5706    return (ioctl(fd, FIORDCHK, NULL));
5707#else /* !FIORDCK */
5708    struct pollfd pollfds[1];
5709
5710    pollfds[0].fd = fd;
5711    pollfds[0].events = POLLIN;
5712    return poll(pollfds, 1, 0);
5713#endif
5714}
5715#endif /* !VMS */
5716
5717/* Utility function to try to hide system differences from
5718   everybody who used to call killpg() */
5719
5720int
5721kill_process_group(int pid, int sig)
5722{
5723    TRACE(("kill_process_group(pid=%d, sig=%d)\n", pid, sig));
5724#if defined(SVR4) || defined(SYSV) || !defined(X_NOT_POSIX)
5725    return kill(-pid, sig);
5726#else
5727    return killpg(pid, sig);
5728#endif
5729}
5730
5731#if OPT_EBCDIC
5732int
5733A2E(int x)
5734{
5735    char c;
5736    c = x;
5737    __atoe_l(&c, 1);
5738    return c;
5739}
5740
5741int
5742E2A(int x)
5743{
5744    char c;
5745    c = x;
5746    __etoa_l(&c, 1);
5747    return c;
5748}
5749#endif
5750
5751#if defined(__QNX__) && !defined(__QNXNTO__)
5752#include <sys/types.h>
5753#include <sys/proc_msg.h>
5754#include <sys/kernel.h>
5755#include <string.h>
5756#include <errno.h>
5757
5758struct _proc_session ps;
5759struct _proc_session_reply rps;
5760
5761int
5762qsetlogin(char *login, char *ttyname)
5763{
5764    int v = getsid(getpid());
5765
5766    memset(&ps, 0, sizeof(ps));
5767    memset(&rps, 0, sizeof(rps));
5768
5769    ps.type = _PROC_SESSION;
5770    ps.subtype = _PROC_SUB_ACTION1;
5771    ps.sid = v;
5772    strcpy(ps.name, login);
5773
5774    Send(1, &ps, &rps, sizeof(ps), sizeof(rps));
5775
5776    if (rps.status < 0)
5777	return (rps.status);
5778
5779    ps.type = _PROC_SESSION;
5780    ps.subtype = _PROC_SUB_ACTION2;
5781    ps.sid = v;
5782    sprintf(ps.name, "//%d%s", getnid(), ttyname);
5783    Send(1, &ps, &rps, sizeof(ps), sizeof(rps));
5784
5785    return (rps.status);
5786}
5787#endif
5788
5789#ifdef __minix
5790int
5791setpgrp(void)
5792{
5793    return 0;
5794}
5795
5796void
5797_longjmp(jmp_buf _env, int _val)
5798{
5799    longjmp(_env, _val);
5800}
5801#endif
5802