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