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