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