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