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