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