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