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