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