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