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