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