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