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