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