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