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