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