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