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