home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1965 < prev    next >
Encoding:
Internet Message Format  |  1990-12-28  |  48.1 KB

  1. From: hyc@math.lsa.umich.edu (Howard Chu)
  2. Newsgroups: comp.windows.misc,alt.sources,unix-pc.sources,comp.sys.att
  3. Subject: Re: rmgr Remote Window Manager for MGR
  4. Message-ID: <1990Oct17.010306.4846@math.lsa.umich.edu>
  5. Date: 17 Oct 90 01:03:06 GMT
  6.  
  7. Oops. Left the editor before including the actual sources. Here ya go.
  8.  
  9. #--------------------------------CUT HERE-------------------------------------
  10. #! /bin/sh
  11. #
  12. # This is a shell archive.  Save this into a file, edit it
  13. # and delete all lines above this comment.  Then give this
  14. # file to sh by executing the command "sh file".  The files
  15. # will be extracted into the current directory owned by
  16. # you with default permissions.
  17. #
  18. # The files contained herein are:
  19. #
  20. # -r--r--r--  1 hyc           725 Oct 14 05:39 Makefile
  21. # -r--r--r--  1 hyc          1027 Oct 14 08:43 README
  22. # -r--r--r--  1 hyc          9341 Oct 14 08:29 rmgr.1
  23. # -rw-r--r--  1 hyc         32339 Oct 15 03:39 rmgr.c
  24. # -r--r--r--  1 hyc          1169 Oct 14 08:29 rmgr.h
  25. #
  26. echo 'x - Makefile'
  27. if test -f Makefile; then echo 'shar: not overwriting Makefile'; else
  28. sed 's/^X//' << '________This_Is_The_END________' > Makefile
  29. X# The following options can be set:
  30. X#
  31. X# -DGETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
  32. X#                and the getttyent(3) library functions.
  33. X#
  34. X# -DUSEBCOPY  -- use the bcopy() from the system's C-library.  If this
  35. X#                is set, bcopy must support overlapping source and
  36. X#                destination.  If USEBCOPY is not set, screen uses its
  37. X#                own version of bcopy.
  38. X#
  39. X# You should install as set-uid with owner root, so that it can read/write
  40. X# /etc/utmp, read /dev/kmem, and chown/chmod the allocated pseudo-ttys.
  41. X
  42. XOPTIONS= -DUSEBCOPY -DGETTTYENT
  43. X
  44. XCFLAGS= -O
  45. X
  46. Xrmgr: rmgr.o
  47. X    $(CC) $(CFLAGS) -o rmgr rmgr.o -lmgr
  48. X
  49. Xrmgr.o: rmgr.c rmgr.h
  50. X    $(CC) $(OPTIONS) $(CFLAGS) -c rmgr.c
  51. ________This_Is_The_END________
  52. if test `wc -c < Makefile` -ne      725; then
  53.     echo 'shar: Makefile was damaged during transit (should have been      725 bytes)'
  54. fi
  55. fi        ; : end of overwriting check
  56. echo 'x - README'
  57. if test -f README; then echo 'shar: not overwriting README'; else
  58. sed 's/^X//' << '________This_Is_The_END________' > README
  59. X                            Howard Chu
  60. X                            October 14, 1990
  61. X
  62. XHowdy. This is rmgr, my program for managing multiple remote sessions
  63. Xthru a single communications line (e.g., a dial-up serial port). The
  64. Xheader file rmgr.h tries to #include <mgr/term.h>, so you will need to
  65. Xhave the MGR client library header files installed in a reasonable spot,
  66. Xlike /usr/include/mgr. (Or just add the -Ipath option to the Makefile
  67. Xand fake it...)
  68. X
  69. XThis program is derived from Oliver Laumann's "screen" program, and has
  70. Xnearly the same system requirements as that program. (I.e., BSD sockets
  71. Xand pseudo-ttys are necessary.) Plus, obviously, the console you are
  72. Xsitting in front of must be running an MGR server for you to use this
  73. Xprogram at all.
  74. X
  75. XI am calling this a beta-release, although the program is very stable
  76. Xand I don't expect many problems to crop up. (But read the man page,
  77. Xyou may find that the "features" I note could hamper your use... You're
  78. Xwelcome to tackle the two noted problems and tell me how it went.)
  79. X  -- Howard
  80. X    hyc@math.lsa.umich.edu
  81. ________This_Is_The_END________
  82. if test `wc -c < README` -ne     1027; then
  83.     echo 'shar: README was damaged during transit (should have been     1027 bytes)'
  84. fi
  85. fi        ; : end of overwriting check
  86. echo 'x - rmgr.1'
  87. if test -f rmgr.1; then echo 'shar: not overwriting rmgr.1'; else
  88. sed 's/^X//' << '________This_Is_The_END________' > rmgr.1
  89. X.if n .ds Q \&"
  90. X.if n .ds U \&"
  91. X.if t .ds Q ``
  92. X.if t .ds U ''
  93. X.TH RMGR 1 "14 October 1990"
  94. X.UC 4
  95. X.SH NAME
  96. Xrmgr \- remote graphical window manager
  97. X.SH SYNOPSIS
  98. X.B rmgr
  99. X[
  100. X.B \-n \fIwindowname\fP
  101. X] [
  102. X.BR \fIcmd args\fP ]
  103. X.br
  104. X.B rmgr \-r
  105. X[
  106. X.BR \fIhost.tty\fP ]
  107. X.ta .5i 1.8i
  108. X.SH DESCRIPTION
  109. X.I rmgr
  110. Xis a client for MGR, the Bellcore window manager, that
  111. Xmultiplexes a communications line between several processes (typically
  112. Xinteractive shells).  As the name implies, the program is meant to be run
  113. Xon a host other than where the MGR server resides.
  114. X.I Rmgr
  115. Xcreates a window on the local MGR server for each process spawned on the
  116. Xremote host.  Each process has the full facilities of MGR at its disposal.
  117. X.PP
  118. XWhen
  119. X.I rmgr
  120. Xis called, it creates a single window with a shell; the pathname of the
  121. Xshell is taken from the environment symbol $SHELL; if this is not
  122. Xdefined, \*Q/bin/sh\*U is used.
  123. XNew windows can be created at any time by calling
  124. X.I rmgr
  125. Xfrom within a previously created window.
  126. XThe program to be started in a newly created
  127. Xwindow and optional arguments to the program can be supplied when
  128. X.I rmgr
  129. Xis invoked.
  130. XFor instance,
  131. X.IP
  132. Xrmgr csh
  133. X.PP
  134. Xwill create a window with a C-Shell and switch to that window.
  135. XWhen the process associated with the currently displayed window
  136. Xterminates (e.\|g. ^D has been typed to a shell),
  137. X.I rmgr
  138. Xswitches to the previously displayed window;
  139. Xwhen no more windows are left,
  140. X.I rmgr
  141. Xexits.
  142. X.PP
  143. XWhen \*Q/etc/utmp\*U is writable by
  144. X.IR rmgr ,
  145. Xan appropriate record is written to this file for each window and
  146. Xremoved when the window is terminated.
  147. X.PP
  148. XThe
  149. X.B \-r
  150. Xoption is used to resume a
  151. X.I rmgr
  152. Xsession that has been \fIdetached\fP from the terminal by means
  153. Xof the \*QDetach\*U menu option (see below).
  154. XThis mechanism allows a user to disconnect
  155. X.I rmgr
  156. Xtogether with all currently active windows from the terminal
  157. Xand resume it at a later point in time, e.\|g. at a later
  158. Xlogin session, and even from a different MGR host.
  159. XWhen more than one detached session exists, the
  160. X.B \-r
  161. Xoption displays a list of
  162. X.I host.tty
  163. Xpairs identifying the detached
  164. X.IR sessions .
  165. XIn this case an additional
  166. X.I host.tty
  167. Xargument can be given to resume a specific
  168. X.I rmgr
  169. Xsession.
  170. X.SH "MENU ITEMS"
  171. X.I Rmgr
  172. Xadds two menus to each window it creates, invoked by pressing the
  173. Xright mouse button. The first three menu items are simply place-holders.
  174. XThe first item is a title-bar, reading \*QMGR Windows =>\*U, the second
  175. Xitem is the name of the currently selected window, and the third is a
  176. Xdividing line. No actions are associated with these three items. The rest
  177. Xof this menu shows the available commands:
  178. X
  179. X.IP "\fBShell Window\fP"
  180. XCreate a new window with a shell and switch to that window.
  181. X.IP "\fBOther Window\fP"
  182. XCreate a new window, switch to that window, and prompt for a command
  183. Xto run in the window. A window name may also be specified by typing
  184. X\*Q-n \fIwindowname\fP\*U at the beginning of the line.
  185. X.IP "\fBKill Window\fP"
  186. XClose the current window and kill its associated process.
  187. X.IP "\fBSuspend\fP"
  188. XSuspend
  189. X.IR rmgr .
  190. XThe window context is pushed onto the environment stack.
  191. X.IP "\fBDetach\fP"
  192. XDetach
  193. X.I rmgr
  194. X(put it into the background and disconnect it from its control terminal).
  195. XThis also pushes the window context onto the environment stack, in case
  196. Xthe session will be resumed without first destroying any created windows.
  197. XA detached
  198. X.I rmgr
  199. Xsession may be resumed by invoking
  200. X.I rmgr
  201. Xwith the
  202. X.B \-r
  203. Xoption.
  204. X.IR Rmgr
  205. Xmaintains a circular buffer for the last 2048 bytes written to each window,
  206. Xand will replay the contents of each window when the session is resumed and
  207. Xthe windows are recreated. Note that detached processes continue to run, and
  208. Xmay continue to generate output. So while the buffer will usually preserve all
  209. Xnecessary context, sometimes it may lose information, but it will almost
  210. Xalways yield an accurate representation of what the window should contain.
  211. X.IP "\fBQuit\fP\0\0\0\0\0"
  212. XKill all windows and terminate
  213. X.IR rmgr .
  214. X.PP
  215. XThe \*QMGR Windows =>\*U menu item is linked to a second menu. Moving the mouse
  216. Xto that item and sliding right displays the menu, a list of the names of all
  217. Xcurrently active
  218. X.IR rmgr
  219. Xwindows. Selecting any of these items will cause the associated window to
  220. Xpop forward and become the active window. Of course, windows may always be
  221. Xselected in the usual manner, i.\|e., move the mouse cursor to the window
  222. Xand click the left button.
  223. X
  224. X.SH OPERATION DETAILS
  225. XWhen
  226. X.I rmgr
  227. Xis invoked, it pushes a number of elements of the current window environment
  228. Xonto the stack, including the current font, window size and location, defined
  229. Xmenus, events, and text regions, and mode flags. It then sets up an escape
  230. Xcode to be used for its own event and menu strings. Then the main window is
  231. Xmoved to the top left corner of the screen and resized to 80 columns by 24
  232. Xrows of text. Codes are set for window activation, destruction, and reshaping,
  233. Xand the menus are downloaded to the window in menu slots 1 and 2. Finally, a
  234. Xshell (or other process, if specified) is started in the window.
  235. X
  236. XNew windows are always created near the top left corner of the screen, in
  237. Xthe default font, sized for 80 columns by 24 rows. If the window is resized,
  238. X.I rmgr
  239. Xwill attempt to inform the remote process of the new window size. The window's
  240. Xname defaults to the command name if no other name is explicitly given.
  241. X
  242. XKilling a window closes its control terminal and should cause the associated
  243. Xprocesses to terminate.  If a process in an obscured window (i.\|e., a window
  244. Xthat is not the active window) terminates, the window is not closed. Instead,
  245. Xthe message \*Q[window shut down]\*U is written in the window.  The window
  246. Xmay be removed by activating it, and then using either the
  247. X.I rmgr
  248. X\*QKill Window\*U option, or the system \*Qdestroy\*U option.  This behavior
  249. Xis intended to prevent inadvertent loss of information when a hidden window
  250. Xis exited.  Also, window 0, the main window, is never closed, though it may
  251. Xbe reused any number of times for different commands.
  252. X
  253. X.I Rmgr
  254. Xmay be suspended to temporarily disable the alternate windows and allow direct
  255. Xcommunication with the remote host. This is most useful for users dialing up
  256. Xon a serial line, who may wish to perform some file transfer functions, and
  257. Xthen resume the windowing sessions. As with a detached session, the processes
  258. Xin the various windows are not stopped, and may continue to generate output.
  259. XThe windows created by
  260. X.I rmgr
  261. Xremain on the screen, and should not be destroyed while
  262. X.I rmgr
  263. Xis suspended.  They may safely be selected for viewing, and moved around, but
  264. Xshould not be changed in any way, or else when
  265. X.I rmgr
  266. Xis restarted the program will get confused.  The Detach option should be used
  267. Xif the alternate windows are likely to be modified or nonexistent by the time
  268. Xthe
  269. X.I rmgr
  270. Xsession is resumed.
  271. X
  272. XWhen a detached
  273. X.I rmgr
  274. Xsession is resumed,
  275. X.I rmgr
  276. Xpolls the MGR server to see which alternate windows exist, if any. If the
  277. Xsame windows exist as at the time of detachment, then no special state
  278. Xrestoration is performed. If, instead, alternate windows exist which
  279. X.I rmgr
  280. Xdidn't expect to find, they are destroyed. Also, if expected windows are
  281. Xmissing, they are recreated and the contents of the history buffer are
  282. Xreplayed into the new window. This polling process is not particularly
  283. Xclever; the only checks are that the window IDs and text dimensions match,
  284. Xso it is possible to artifically create an environment that fits what
  285. X.I rmgr
  286. Xexpected to find, thus bypassing the restoration of state, but it would be
  287. Xsilly to try to contrive this situation. Usually resuming a detached session
  288. Xwill recreate all the alternate windows in nearly the exact same state as
  289. Xthey were before, plus any new output that may have appeared. Also, if any
  290. Xprocesses terminated while
  291. X.I rmgr
  292. Xwas detached, their windows will still be created, but will have the
  293. X\*Q[window shut down]\*U message attached. (This is also true when
  294. Xresuming a \fIsuspended\fP session.)
  295. X
  296. XAll new windows are created with the TERM and TERMCAP variables set. Most
  297. XMGR clients that expect to talk directly to the MGR server can also work
  298. Xnormally through
  299. X.I rmgr .
  300. XHowever, any client that directs queries to the MGR server and needs a reply
  301. Xcan only run if its window is the active window.  This is a necessary
  302. Xrestriction, because MGR doesn't tag replies with the window ID of the
  303. Xintended recipient, so
  304. X.I rmgr
  305. Xsimply feeds the data to the active window. Clients that only issue commands
  306. Xand never expect replies should be able to work regardless of whether 
  307. Xtheir window is active or obscured. However, it is still possible that some
  308. Xcommands will get garbled, as
  309. X.I rmgr
  310. Xdoes no interpretation of the data streams sent by the alternate windows,
  311. Xand does not guard against escape sequences being split or interleaved with
  312. Xdata from other windows. Perhaps a future version of
  313. X.I rmgr
  314. Xwill address these issues.
  315. X
  316. X.SH FILES
  317. X.nf
  318. X.ta 2i
  319. X$(HOME)/.rmgr    Directory created by \fIrmgr\fP
  320. X.br
  321. X$(HOME)/.rmgr/\fIhost.tty\fP    Socket created by \fIrmgr\fP
  322. X.br
  323. X/etc/utmp    Login records
  324. X.fi
  325. X.SH "SEE ALSO"
  326. Xtermcap(5), utmp(5)
  327. X.br
  328. XMGR, a window system for Unix, by Stephen A. Uhler, Bell Communications Research
  329. X.SH AUTHOR
  330. XHoward Chu @ University of Michigan, hyc@math.lsa.umich.edu
  331. XDerived from \fIscreen\fP by Oliver Laumann @ Technical University of Berlin
  332. ________This_Is_The_END________
  333. if test `wc -c < rmgr.1` -ne     9341; then
  334.     echo 'shar: rmgr.1 was damaged during transit (should have been     9341 bytes)'
  335. fi
  336. fi        ; : end of overwriting check
  337. echo 'x - rmgr.c'
  338. if test -f rmgr.c; then echo 'shar: not overwriting rmgr.c'; else
  339. sed 's/^X//' << '________This_Is_The_END________' > rmgr.c
  340. X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
  341. X * Not derived from licensed software.
  342. X *
  343. X * Permission is granted to freely use, copy, modify, and redistribute
  344. X * this software, provided that no attempt is made to gain profit from it,
  345. X * the author is not construed to be liable for any results of using the
  346. X * software, alterations are clearly marked as such, and this notice is
  347. X * not modified.
  348. X */
  349. X
  350. X/* This program Copyright 1990 by Howard Chu.
  351. X *
  352. X * This is rmgr, a remote window manager for Bellcore's MGR window
  353. X * manager, by Howard Chu @ University of Michigan (Ann Arbor, MI).
  354. X * Much of this program consists of Oliver Laumann's code; I have not
  355. X * (usually) highlighted my changes in any way. Oliver didn't use any
  356. X * comments at all; for the most part I have followed his lead...  }-)
  357. X * Suffice to say, some of the routines are original, some are heavily
  358. X * hacked upon versions of his code, and some are untouched from his
  359. X * program. Of course, the volume of code here represents only half
  360. X * of the code used in the screen program; the rest, support for ANSI
  361. X * terminal emulation, was discarded.
  362. X */
  363. X
  364. Xstatic char Version[] = "$Header: /usr/src/rmgr/RCS/rmgr.c,v 1.5 90/10/15 03:39:02 hyc Stable $";
  365. X
  366. X#include <stdio.h>
  367. X#include <sgtty.h>
  368. X#include <signal.h>
  369. X#include <errno.h>
  370. X#include <ctype.h>
  371. X#include <utmp.h>
  372. X#include <pwd.h>
  373. X#include <nlist.h>
  374. X#include <fcntl.h>
  375. X#include <sys/types.h>
  376. X#include <sys/time.h>
  377. X#include <sys/file.h>
  378. X#include <sys/wait.h>
  379. X#include <sys/socket.h>
  380. X#include <sys/un.h>
  381. X#include <sys/stat.h>
  382. X#include <sys/dir.h>
  383. X#include "rmgr.h"
  384. X
  385. X#ifdef GETTTYENT
  386. X#   include <ttyent.h>
  387. X#else
  388. X    static struct ttyent {
  389. X    char *ty_name;
  390. X    } *getttyent();
  391. X    static char *tt, *ttnext;
  392. X    static char ttys[] = "/etc/ttys";
  393. X#endif
  394. X
  395. X#define MAXWIN     10
  396. X#define MSGWAIT     5
  397. X
  398. X#define Ctrl(c) ((c)&037)
  399. X
  400. Xextern char **environ;
  401. Xextern errno;
  402. Xextern sys_nerr;
  403. Xextern char *sys_errlist[];
  404. Xextern char *index(), *rindex(), *malloc(), *getenv(), *MakeTermcap();
  405. Xextern char *getlogin(), *ttyname();
  406. Xstatic void AttacherFinit(), Finit(), SigHup(), SigChld();
  407. Xstatic char *Filename(), **SaveArgs(), *GetTtyName();
  408. X
  409. Xstatic char PtyName[32], TtyName[32];
  410. Xstatic char *ShellProg;
  411. Xstatic char *ShellArgs[2];
  412. Xstatic char inbuf[IOSIZE];
  413. Xstatic inlen;
  414. Xstatic ESCseen;
  415. Xstatic GotSignal;
  416. Xstatic char DefaultShell[] = "/bin/sh";
  417. Xstatic char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
  418. Xstatic char PtyProto[] = "/dev/ptyXY";
  419. Xstatic char TtyProto[] = "/dev/ttyXY";
  420. Xstatic int TtyMode = 0622;
  421. Xstatic char SockPath[512];
  422. Xstatic char SockDir[] = ".rmgr";
  423. Xstatic char *SockNamePtr, *SockName;
  424. Xstatic ServerSocket;
  425. Xstatic char *NewEnv[MAXARGS];
  426. Xstatic char Esc = Ctrl('a');
  427. Xstatic char *home;
  428. Xstatic HasWindow;
  429. Xstatic utmp, utmpf;
  430. Xstatic char UtmpName[] = "/etc/utmp";
  431. Xstatic char *LoginName;
  432. Xstatic char HostName[MAXSTR];
  433. Xstatic Detached;
  434. Xstatic AttacherPid;    /* Non-Zero in child if we have an attacher */
  435. Xstatic DevTty;
  436. Xstatic char EventStr[]="%c%cA";
  437. Xstatic char CommNames[MAXLINE];
  438. Xstatic int VoluntaryDetach = 0;
  439. X
  440. Xstruct mode {
  441. X    struct sgttyb m_ttyb;
  442. X    struct tchars m_tchars;
  443. X    struct ltchars m_ltchars;
  444. X    int m_ldisc;
  445. X    int m_lmode;
  446. X} OldMode, NewMode;
  447. X
  448. Xstatic struct win *curr, *other;
  449. Xstatic CurrNum, OtherNum;
  450. Xstatic struct win *wtab[MAXWIN];
  451. X
  452. Xmain (ac, av) char **av; {
  453. X    register n, len;
  454. X    register struct win **pp, *p;
  455. X    char *ap;
  456. X    static InitMenu();
  457. X    int s, r, w, x = 0;
  458. X    int rflag = 0;
  459. X    struct timeval tv;
  460. X    time_t now;
  461. X    char buf[IOSIZE], *myname = (ac == 0) ? "rmgr" : av[0];
  462. X    struct stat st;
  463. X    char *winname="";
  464. X
  465. X    while (ac > 0) {
  466. X    ap = *++av;
  467. X    if (--ac > 0 && *ap == '-') {
  468. X        switch (ap[1]) {
  469. X        case 'r':
  470. X        rflag = 1;
  471. X        if (ap[2]) {
  472. X            SockName = ap+2;
  473. X            if (ac != 1) goto help;
  474. X        } else if (--ac == 1) {
  475. X            SockName = *++av;
  476. X        } else if (ac != 0) goto help;
  477. X        break;
  478. X        case 'n':
  479. X        if (ap[2]) {
  480. X            ap += 2;
  481. X        } else {
  482. X            if (--ac == 0) goto help;
  483. X            ap = *++av;
  484. X        }
  485. X        winname=ap;
  486. X        break;
  487. X        default:
  488. X        help:
  489. X        Msg (0, "Use: %s [-n windowname] [cmd args]\n\
  490. X or: %s -r [host.tty]", myname, myname);
  491. X        }
  492. X    } else break;
  493. X    }
  494. X    CommNames[0]='}';
  495. X    if ((ShellProg = getenv ("SHELL")) == 0)
  496. X    ShellProg = DefaultShell;
  497. X    ShellArgs[0] = ShellProg;
  498. X    if (ac == 0) {
  499. X    ac = 1;
  500. X    av = ShellArgs;
  501. X    }
  502. X    if ((home = getenv ("HOME")) == 0)
  503. X    Msg (0, "$HOME is undefined.");
  504. X    sprintf (SockPath, "%s/%s", home, SockDir);
  505. X    if (stat (SockPath, &st) == -1) {
  506. X    if (errno == ENOENT) {
  507. X        if (mkdir (SockPath, 0700) == -1)
  508. X        Msg (errno, "Cannot make directory %s", SockPath);
  509. X        (void) chown (SockPath, getuid (), getgid ());
  510. X    } else Msg (errno, "Cannot get status of %s", SockPath);
  511. X    } else {
  512. X    if ((st.st_mode & S_IFMT) != S_IFDIR)
  513. X        Msg (0, "%s is not a directory.", SockPath);
  514. X    if ((st.st_mode & 0777) != 0700)
  515. X        Msg (0, "Directory %s must have mode 700.", SockPath);
  516. X    if (st.st_uid != getuid ())
  517. X        Msg (0, "You are not the owner of %s.", SockPath);
  518. X    }
  519. X    (void) gethostname (HostName, MAXSTR);
  520. X    HostName[MAXSTR-1] = '\0';
  521. X    if (ap = index (HostName, '.'))
  522. X    *ap = '\0';
  523. X    strcat (SockPath, "/");
  524. X    SockNamePtr = SockPath + strlen (SockPath);
  525. X    if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
  526. X    Msg (errno, "/dev/tty");
  527. X    if (rflag) {
  528. X    Attach (MSG_ATTACH);
  529. X    Attacher ();
  530. X    /*NOTREACHED*/
  531. X    }
  532. X    if (GetSockName ()) {
  533. X    s = MakeClientSocket (1);
  534. X    SendCreateMsg (s, ac, av, winname);
  535. X    close (s);
  536. X    exit (0);
  537. X    }
  538. X    switch (fork ()) {
  539. X    case -1:
  540. X    Msg (errno, "fork");
  541. X    /*NOTREACHED*/
  542. X    case 0:
  543. X    break;
  544. X    default:
  545. X    Attacher ();
  546. X    /*NOTREACHED*/
  547. X    }
  548. X    AttacherPid = getppid ();
  549. X    ServerSocket = s = MakeServerSocket ();
  550. X    InitTerm ();
  551. X    InitMenu ();
  552. X    MakeNewEnv ();
  553. X    GetTTY (0, &OldMode);
  554. X    InitUtmp ();
  555. X    signal (SIGHUP, SigHup);
  556. X    signal (SIGINT, Finit);
  557. X    signal (SIGQUIT, Finit);
  558. X    signal (SIGTERM, Finit);
  559. X    signal (SIGTTIN, SIG_IGN);
  560. X    signal (SIGTTOU, SIG_IGN);
  561. X    if ((n = MakeWindow (*av, av, winname, (char *)0)) == -1) {
  562. X    SetTTY (0, &OldMode);
  563. X    FinitTerm ();
  564. X    Kill (AttacherPid, SIGHUP);
  565. X    exit (1);
  566. X    }
  567. X    SetCurrWindow (n);
  568. X    HasWindow = 1;
  569. X    SetMode (&OldMode, &NewMode);
  570. X    SetTTY (0, &NewMode);
  571. X    signal (SIGCHLD, SigChld);
  572. X    tv.tv_usec = 0;
  573. X    while (1) {
  574. X    r = 0;
  575. X    w = 0;
  576. X    if (inlen && curr->wpid>=0)
  577. X        w |= 1 << curr->ptyfd;
  578. X    else
  579. X        r |= 1 << 0;
  580. X    for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  581. X        if (!(p = *pp))
  582. X        continue;
  583. X        if (p->wpid >=0)
  584. X            r |= 1 << p->ptyfd;
  585. X    }
  586. X    r |= 1 << s;
  587. X    (void) fflush (stdout);
  588. X    if (GotSignal) {
  589. X        SigHandler ();
  590. X        continue;
  591. X    }
  592. X    if (select (32, &r, &w, &x, (struct timeval *)0) == -1) {
  593. X        if (errno == EINTR)
  594. X        continue;
  595. X        HasWindow = 0;
  596. X        Msg (errno, "select");
  597. X        /*NOTREACHED*/
  598. X    }
  599. X    if (GotSignal) {
  600. X        SigHandler ();
  601. X        continue;
  602. X    }
  603. X    if (r & 1 << s) {
  604. X        ReceiveMsg (s);
  605. X    }
  606. X    if (r & 1 << 0) {
  607. X        if (ESCseen) {
  608. X        inbuf[0] = Esc;
  609. X        inlen = read (0, inbuf+1, IOSIZE-1) + 1;
  610. X        ESCseen = 0;
  611. X        } else {
  612. X        inlen = read (0, inbuf, IOSIZE);
  613. X        }
  614. X        if (inlen > 0)
  615. X        inlen = ProcessInput (inbuf, inlen);
  616. X        if (inlen > 0)
  617. X        continue;
  618. X    }
  619. X    if (GotSignal) {
  620. X        SigHandler ();
  621. X        continue;
  622. X    }
  623. X    if (curr && w & 1 << curr->ptyfd && inlen > 0) {
  624. X        if ((len = write (curr->ptyfd, inbuf, inlen)) > 0) {
  625. X        inlen -= len;
  626. X        bcopy (inbuf+len, inbuf, inlen);
  627. X        }
  628. X    }
  629. X    if (GotSignal) {
  630. X        SigHandler ();
  631. X        continue;
  632. X    }
  633. X    for (n=0; n<MAXWIN; n++) {
  634. X        if (!(p = wtab[n]))
  635. X        continue;
  636. X        if (r & 1 << p->ptyfd) {
  637. X        if ((len = read (p->ptyfd, buf, IOSIZE)) == -1) {
  638. X            if (errno == EWOULDBLOCK)
  639. X            len = 0;
  640. X        }
  641. X        if (len > 0)
  642. X            WriteString (n, buf, len);
  643. X        }
  644. X    }
  645. X    if (GotSignal)
  646. X        SigHandler ();
  647. X    }
  648. X    /*NOTREACHED*/
  649. X}
  650. X
  651. Xstatic InitTerm() {
  652. X    register char *s;
  653. X
  654. X    if ((s = getenv("TERM")) == 0)
  655. X    Msg(0, "No TERM in environment.");
  656. X    if (strcmp(s,"mgr"))
  657. X    Msg(0, "Only runs on mgr terminals.");
  658. X
  659. X    m_setup(M_FLUSH);
  660. X    m_push(P_DEFAULT|P_POSITION);
  661. X    m_dupkey(Esc);
  662. X}
  663. X
  664. Xstatic FinitTerm() {
  665. X    m_nomenu2(); m_nomenu();
  666. X    m_clearmenu(1); m_clearmenu(2);
  667. X    m_clearevent(ACTIVATE); m_clearevent(RESHAPE); m_clearevent(DESTROY);
  668. X    m_popall();
  669. X}
  670. X    
  671. Xstatic WriteString(n, buf, len) int n, len; char *buf; {
  672. X    register int i, j;
  673. X    register struct win *p;
  674. X
  675. X    p=wtab[n];
  676. X
  677. X    if (!Detached) {
  678. X    m_selectwin(n);
  679. X    write(fileno(m_termout), buf, len);
  680. X    } else
  681. X    p->win_ok=0;        /* Screen no longer matches reality... */
  682. X
  683. X    i=SCRBUFSIZE - (p->outptr - p->outbuf);
  684. X    if (i>=len) {
  685. X    bcopy(buf,p->outptr,len);
  686. X    if (i==len) {
  687. X        p->outptr=p->outbuf;
  688. X        p->outful=1;
  689. X    } else
  690. X        p->outptr+=len;
  691. X    } else {
  692. X    j=len-i;
  693. X    bcopy(buf,p->outptr,i);
  694. X    bcopy(&buf[i],p->outbuf,j);
  695. X    p->outptr=p->outbuf+j;
  696. X    p->outful=1;
  697. X    }
  698. X}
  699. X
  700. Xstatic SigHandler () {
  701. X    while (GotSignal) {
  702. X    GotSignal = 0;
  703. X    DoWait ();
  704. X    }
  705. X}
  706. X
  707. Xstatic void SigChld () {
  708. X    GotSignal = 1;
  709. X}
  710. X
  711. Xstatic void SigHup () {
  712. X    Detach (0);
  713. X}
  714. X
  715. Xstatic DoWait () {
  716. X    register pid;
  717. X    register int n;
  718. X    register struct win **pp;
  719. X    union wait wstat;
  720. X
  721. X    while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
  722. X    for (n = 0; n<MAXWIN; n++) {
  723. X        if (wtab[n] && pid == wtab[n]->wpid) {
  724. X        if (WIFSTOPPED (wstat)) {
  725. X            (void) killpg (getpgrp (wtab[n]->wpid), SIGCONT);
  726. X        } else
  727. X            KillWindow(n);
  728. X        }
  729. X    }
  730. X    }
  731. X    for (n=0; n<MAXWIN; n++)
  732. X    if (wtab[n] && wtab[n]->wpid >=0)
  733. X        break;
  734. X    if (n == MAXWIN)
  735. X    Finit();
  736. X}
  737. X
  738. Xstatic KillWindow (n) int n; {
  739. X    if (Detached || n!=CurrNum || !n) {
  740. X    WriteString(n,"[window shut down]",18);
  741. X    wtab[n]->wpid = -1;    /* Shut it down, but don't close */
  742. X    if (!n)
  743. X            FreeWindow (n);    /* Don't ever close window 0, but free it */
  744. X    } else {
  745. X    CurrNum = 0;
  746. X    curr = 0;
  747. X        FreeWindow (n);
  748. X    }
  749. X}
  750. X
  751. Xstatic void Finit () {
  752. X    register int n;
  753. X    register struct win *p, **pp;
  754. X
  755. X    for (n=0; n<MAXWIN; n++) {
  756. X    if (wtab[n])
  757. X        FreeWindow(n);
  758. X    }
  759. X    SetTTY (0, &OldMode);
  760. X    FinitTerm ();
  761. X    printf ("\r[rmgr is terminating]\033c\n");
  762. X    Kill (AttacherPid, SIGHUP);
  763. X    exit (0);
  764. X}
  765. X
  766. Xstatic linemode(onoff) int onoff; {
  767. X    struct sgttyb buff;
  768. X    gtty(0,&buff);
  769. X    if (onoff)
  770. X        buff.sg_flags &= ~CBREAK;
  771. X    else
  772. X        buff.sg_flags |= CBREAK;
  773. X    stty(0,&buff);
  774. X}
  775. X
  776. Xstatic ProcessInput (buf, len) char *buf; {
  777. X    register n, k;
  778. X    register char *s, *p, *q;
  779. X    register struct win **pp;
  780. X
  781. X    for (s = p = buf; len > 0; len--, s++) {
  782. X    if (*s == Esc) {
  783. X        if (len > 1) {
  784. X        len--; s++;
  785. X        if (*s == Esc) {
  786. X            *p++ = Esc;
  787. X        } else {        /* Process event or menu string */
  788. X        if (*s != ' ') {
  789. X            n=(*s++)-'!';
  790. X            len--;
  791. X            if (!len) {        /* Make sure to get 3rd char */
  792. X            while(!read(0,s,1));
  793. X            len++;
  794. X            }
  795. X            p=buf;
  796. X            switch (*s) {    /* Event Strings */
  797. X            case 'A':
  798. X                SetCurrWindow(n);
  799. X                break;
  800. X            case 'D':
  801. X                FreeWindow(n);
  802. X                break;
  803. X            case 'R':
  804. X                SizeWindow(n);
  805. X                break;
  806. X            case 'c':    /* Menu Strings */
  807. X                if ((n=MakeWindow((char *)0, (char *)0,
  808. X                    "", (char *)0)) != -1)
  809. X                    SetCurrWindow(n);
  810. X                break;
  811. X            case 'd':
  812. X                VoluntaryDetach=1;
  813. X                Detach(0);
  814. X                break;
  815. X            case 'f':
  816. X                SwitchWindow(n);
  817. X                break;
  818. X            case 'k':
  819. X                KillWindow(n);
  820. X                break;
  821. X            case 'n':
  822. X                if ((n=MakeWindow(ShellProg, ShellArgs,
  823. X                    "", (char *)0)) != -1)
  824. X                    SetCurrWindow(n);
  825. X                break;
  826. X            case 'q':
  827. X                Finit();
  828. X                break;
  829. X            case 's':
  830. X                VoluntaryDetach=1;
  831. X                Detach(1);
  832. X                break;
  833. X            }
  834. X            }
  835. X        } 
  836. X        } else ESCseen = 1;
  837. X    } else *p++ = *s;
  838. X    }
  839. X    return p - buf;
  840. X}
  841. X
  842. Xstatic SwitchWindow(n) {
  843. X    if (wtab[n]) {
  844. X        SetCurrWindow(n);
  845. X        m_setmode(M_ACTIVATE);
  846. X    }
  847. X}
  848. X
  849. Xstatic SetCurrWindow (n) {
  850. X    CurrNum = n;
  851. X    curr = wtab[n];
  852. X    m_selectwin(n);
  853. X    if (curr->wpid < 0)
  854. X    m_setmode(M_NOINPUT);    /* Dead window, disallow input */
  855. X}
  856. X
  857. Xstatic SizeWindow (n) int n; {
  858. X    struct winsize wbuf;
  859. X    char buf[32]; 
  860. X
  861. X    m_selectwin(n);
  862. X    m_getinfo(G_WINSIZE);
  863. X    linemode(1);
  864. X    read(0,buf,31);
  865. X    linemode(0);
  866. X    buf[31]='\0';
  867. X    sscanf(buf,"%*c %d %d",&(wtab[n]->cols),&(wtab[n]->rows));
  868. X#ifdef    TIOCSWINSZ
  869. X    wbuf.ws_row = wtab[n]->rows;
  870. X    wbuf.ws_col = wtab[n]->cols;
  871. X    ioctl (wtab[n]->ptyfd, TIOCSWINSZ, &wbuf);
  872. X#endif
  873. X}
  874. X
  875. Xstatic FreeWindow (n) register int n; {
  876. X    register i;
  877. X    register struct win *wp;
  878. X
  879. X    wp=wtab[n];
  880. X    if (wp) {
  881. X        RemoveUtmp (wp->slot);
  882. X        (void) chmod (wp->tty, 0666);
  883. X        (void) chown (wp->tty, 0, 0);
  884. X        close (wp->ptyfd);
  885. X        free (wp);
  886. X        wtab[n]=0;
  887. X    }
  888. X    if (n) {
  889. X    m_selectwin(n);
  890. X    m_clearevent(DESTROY);    /* Avoid redundant calling of FreeWindow */
  891. X    m_destroywin(n);
  892. X    }
  893. X    CollectNames();
  894. X    sleep(1);
  895. X    UpdateMenu(n);
  896. X}
  897. X
  898. Xstatic CollectNames() {        /* Collect command names for second menu */
  899. X    register struct win **wp;
  900. X    register int n;
  901. X    char buf[8];
  902. X
  903. X    CommNames[1]='\0';
  904. X    for (wp=wtab; wp<wtab+MAXWIN; wp++)
  905. X    if (*wp) {
  906. X        strcat(CommNames,(*wp)->cmd);
  907. X        strcat(CommNames,"}");
  908. X    }
  909. X
  910. X    buf[0]=Esc;
  911. X    buf[1]='!';
  912. X    buf[2]='f';
  913. X    buf[3]='}';
  914. X    buf[4]='\0';
  915. X    for (n=0;n<MAXWIN;n++)
  916. X    if (wtab[n]) {
  917. X        buf[1]=n+'!';
  918. X        strcat(CommNames,buf);
  919. X    }
  920. X}
  921. X
  922. Xstatic char *MenuNames[]={"MGR Windows =>", "", "-=-=-=-=-=-=-",
  923. X    "Shell Window","Other Window","Kill Window","Suspend","Detach","Quit"};
  924. Xstatic char MenuNull='\0';
  925. Xstatic char MenuActs[24];
  926. Xstatic char MenuLabs[8]="ncksdq";
  927. Xstatic struct menu_entry MainMenu[9];
  928. X
  929. Xstatic InitMenu() {
  930. X    register int i;
  931. X    register char *ptr;
  932. X
  933. X    ptr=MenuActs;
  934. X    for (i=3;i<9;i++) {
  935. X    MainMenu[i].action=ptr;
  936. X    *ptr++=Esc;
  937. X    *ptr++='!';
  938. X    *ptr++=MenuLabs[i-3];
  939. X    *ptr++='\0';
  940. X    }
  941. X    for (i=0;i<3;i++)
  942. X    MainMenu[i].action=(&MenuNull);
  943. X
  944. X    for (i=0;i<9;i++)
  945. X    MainMenu[i].value=MenuNames[i];
  946. X}
  947. X
  948. Xstatic MakeMenu(n) int n; {
  949. X    MenuActs[9]=n+'!';
  950. X    MainMenu[1].value=wtab[n]->cmd;
  951. X    menu_load(1,9,MainMenu);
  952. X    m_loadmenu(2,CommNames);
  953. X    m_linkmenu(1,0,2,MF_SNIP);
  954. X    m_selectmenu2(1);
  955. X}
  956. X
  957. Xstatic UpdateMenu(n) int n; {
  958. X    register int i;
  959. X
  960. X    for (i=0; i<MAXWIN; i++)
  961. X    if (wtab[i] && i!=n) {
  962. X        m_selectwin(i);
  963. X        m_loadmenu(2,CommNames);
  964. X    }
  965. X}
  966. X
  967. Xstatic Parse (buf, args) char *buf, **args; {
  968. X    register char *p = buf, **ap = args;
  969. X    register delim, argc = 0;
  970. X
  971. X    argc = 0;
  972. X    for (;;) {
  973. X    while (*p && (*p == ' ' || *p == '\t')) ++p;
  974. X    if (*p == '\0' || *p == '#')
  975. X        return argc;
  976. X    if (argc > MAXARGS-1)
  977. X        Msg (0, "Too many tokens.");
  978. X    delim = 0;
  979. X    if (*p == '"' || *p == '\'') {
  980. X        delim = *p; *p = '\0'; ++p;
  981. X    }
  982. X    ++argc;
  983. X    *ap = p; ++ap;
  984. X    while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
  985. X        ++p;
  986. X    if (*p == '\0') {
  987. X        if (delim)
  988. X        Msg (0, "Missing quote.");
  989. X        else
  990. X        return argc;
  991. X    }
  992. X    *p++ = '\0';
  993. X    }
  994. X}
  995. X
  996. Xstatic char TermBuf[MAXLINE];
  997. X
  998. Xstatic MakeWindow (prog, args, name, dir)
  999. X    char *prog, *name, **args, *dir; {
  1000. X    register struct win *p;
  1001. X    register char **cp;
  1002. X    register n, f;
  1003. X    int tf;
  1004. X    int mypid;
  1005. X    char ebuf[16];
  1006. X    char ibuf[MY_MAXLINE];
  1007. X    char *av[MAXARGS];
  1008. X    char *ptr,*index();
  1009. X
  1010. X    if ((f = OpenPTY ()) == -1) {
  1011. X    Msg (0, "No more PTYs.");
  1012. X    return -1;
  1013. X    }
  1014. X    (void) fcntl (f, F_SETFL, FNDELAY);
  1015. X    if ((p = (struct win *)malloc (sizeof (struct win))) == 0) {
  1016. X    Msg (0, "Out of memory.");
  1017. X    return -1;
  1018. X    }
  1019. X    p->outful=0;
  1020. X    p->outptr=p->outbuf;
  1021. X
  1022. X    m_ttyset();
  1023. X    if (!wtab[0]) {
  1024. X    n=0;
  1025. X    } else {
  1026. X    m_resetflags(CBREAK);
  1027. X    m_newwin(0,0,50,50);
  1028. X    read(0, TermBuf, MAXLINE);
  1029. X    TermBuf[MAXLINE-1]='\0';
  1030. X        n = atoi(&TermBuf[2]);
  1031. X    }
  1032. X        m_selectwin(n);
  1033. X        m_dupkey(Esc);
  1034. X        m_sizeall(n*5,n*5,80,24);
  1035. X    strcpy(TermBuf,"TERMCAP=");
  1036. X    m_getinfo(G_TERMCAP);
  1037. X    read(0, &TermBuf[6], MAXLINE);
  1038. X    TermBuf[MAXLINE-1]='\0';
  1039. X    m_setflags(CBREAK);
  1040. X    TermBuf[6]='P';
  1041. X    TermBuf[7]='=';
  1042. X    ptr=index(TermBuf,'\n');
  1043. X    *ptr='\0';
  1044. X    m_clear();
  1045. X    sprintf(ebuf,EventStr,Esc,n+'!');
  1046. X    m_setevent(ACTIVATE,ebuf);
  1047. X    ebuf[2]='D';
  1048. X    m_setevent(DESTROY,ebuf);
  1049. X    ebuf[2]='R';
  1050. X    m_setevent(RESHAPE,ebuf);
  1051. X    m_ttyreset();
  1052. X    wtab[n]=p;
  1053. X
  1054. X    p->ptyfd = f;
  1055. X
  1056. X    if (!prog) {
  1057. X    int argc;
  1058. X
  1059. X    SetTTY(0, &OldMode);
  1060. X    printf("Enter command and arguments: ");
  1061. X    fflush(stdout);
  1062. X    ptr=gets(ibuf);
  1063. X    SetTTY(0, &NewMode);
  1064. X    if (!ptr || ((argc = Parse(ibuf, av)) == 0)) {
  1065. X        KillWindow(n);
  1066. X        return;
  1067. X    }
  1068. X    if (strcmp(av[0],"-n") == 0) {
  1069. X        name=av[1];
  1070. X        args=(&av[2]);
  1071. X    } else 
  1072. X        args=av;
  1073. X    prog=args[0];
  1074. X    }
  1075. X
  1076. X    strncpy (p->cmd, *name ? name : Filename (args[0]), MAXSTR-1);
  1077. X    p->cmd[MAXSTR-1] = '\0';
  1078. X
  1079. X    CollectNames();
  1080. X    MakeMenu(n);
  1081. X    UpdateMenu(n);
  1082. X    strncpy (p->tty, TtyName, MAXSTR-1);
  1083. X    (void) chown (TtyName, getuid (), getgid ());
  1084. X    (void) chmod (TtyName, TtyMode);
  1085. X    p->slot = SetUtmp (TtyName);
  1086. X    p->rows=24;
  1087. X    p->cols=80;
  1088. X    switch (p->wpid = fork ()) {
  1089. X    case -1:
  1090. X    Msg (errno, "fork");
  1091. X    free ((char *)p);
  1092. X    return -1;
  1093. X    case 0:
  1094. X    signal (SIGHUP, SIG_DFL);
  1095. X    signal (SIGINT, SIG_DFL);
  1096. X    signal (SIGQUIT, SIG_DFL);
  1097. X    signal (SIGTERM, SIG_DFL);
  1098. X    signal (SIGTTIN, SIG_DFL);
  1099. X    signal (SIGTTOU, SIG_DFL);
  1100. X    setuid (getuid ());
  1101. X    setgid (getgid ());
  1102. X    if (dir && chdir (dir) == -1) {
  1103. X        SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
  1104. X        exit (1);
  1105. X    }
  1106. X    mypid = getpid ();
  1107. X    ioctl (DevTty, TIOCNOTTY, (char *)0);
  1108. X    if ((tf = open (TtyName, O_RDWR)) == -1) {
  1109. X        SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
  1110. X        exit (1);
  1111. X    }
  1112. X    (void) dup2 (tf, 0);
  1113. X    (void) dup2 (tf, 1);
  1114. X    (void) dup2 (tf, 2);
  1115. X    for (f = getdtablesize () - 1; f > 2; f--)
  1116. X        close (f);
  1117. X    ioctl (0, TIOCSPGRP, &mypid);
  1118. X    (void) setpgrp (0, mypid);
  1119. X    SetTTY (0, &OldMode);
  1120. X#ifdef    TIOCSWINSZ
  1121. X    {
  1122. X        struct winsize wbuf;
  1123. X        wbuf.ws_row = p->rows;
  1124. X        wbuf.ws_col=p->cols;
  1125. X        ioctl (0, TIOCSWINSZ, &wbuf);
  1126. X    }
  1127. X#endif
  1128. X    NewEnv[2] = TermBuf;
  1129. X    sprintf (ebuf, "WINDOW=%d", n);
  1130. X    NewEnv[3] = ebuf;
  1131. X    execvpe (prog, args, NewEnv);
  1132. X    SendErrorMsg ("Cannot exec %s: %s", prog, sys_errlist[errno]);
  1133. X    exit (1);
  1134. X    }
  1135. X    return n;
  1136. X}
  1137. X
  1138. Xstatic execvpe (prog, args, env) char *prog, **args, **env; {
  1139. X    register char *path, *p;
  1140. X    char buf[1024];
  1141. X    char *shargs[MAXARGS+1];
  1142. X    register i, eaccess = 0;
  1143. X
  1144. X    if (prog[0] == '/')
  1145. X    path = "";
  1146. X    else if ((path = getenv ("PATH")) == 0)
  1147. X    path = DefaultPath;
  1148. X    do {
  1149. X    p = buf;
  1150. X    while (*path && *path != ':')
  1151. X        *p++ = *path++;
  1152. X    if (p > buf)
  1153. X        *p++ = '/';
  1154. X    strcpy (p, prog);
  1155. X    if (*path)
  1156. X        ++path;
  1157. X    execve (buf, args, env);
  1158. X    switch (errno) {
  1159. X    case ENOEXEC:
  1160. X        shargs[0] = DefaultShell;
  1161. X        shargs[1] = buf;
  1162. X        for (i = 1; shargs[i+1] = args[i]; ++i)
  1163. X        ;
  1164. X        execve (DefaultShell, shargs, env);
  1165. X        return;
  1166. X    case EACCES:
  1167. X        eaccess = 1;
  1168. X        break;
  1169. X    case ENOMEM: case E2BIG: case ETXTBSY:
  1170. X        return;
  1171. X    }
  1172. X    } while (*path);
  1173. X    if (eaccess)
  1174. X    errno = EACCES;
  1175. X}
  1176. X
  1177. Xstatic OpenPTY () {
  1178. X    register char *p, *l, *d;
  1179. X    register i, f, tf;
  1180. X
  1181. X    strcpy (PtyName, PtyProto);
  1182. X    strcpy (TtyName, TtyProto);
  1183. X    for (p = PtyName, i = 0; *p != 'X'; ++p, ++i) ;
  1184. X    for (l = "qpr"; *p = *l; ++l) {
  1185. X    for (d = "0123456789abcdef"; p[1] = *d; ++d) {
  1186. X        if ((f = open (PtyName, O_RDWR)) != -1) {
  1187. X        TtyName[i] = p[0];
  1188. X        TtyName[i+1] = p[1];
  1189. X        if ((tf = open (TtyName, O_RDWR)) != -1) {
  1190. X            close (tf);
  1191. X            return f;
  1192. X        }
  1193. X        close (f);
  1194. X        }
  1195. X    }
  1196. X    }
  1197. X    return -1;
  1198. X}
  1199. X
  1200. Xstatic SetTTY (fd, mp) struct mode *mp; {
  1201. X    ioctl (fd, TIOCSETP, &mp->m_ttyb);
  1202. X    ioctl (fd, TIOCSETC, &mp->m_tchars);
  1203. X    ioctl (fd, TIOCSLTC, &mp->m_ltchars);
  1204. X    ioctl (fd, TIOCLSET, &mp->m_lmode);
  1205. X    ioctl (fd, TIOCSETD, &mp->m_ldisc);
  1206. X}
  1207. X
  1208. Xstatic GetTTY (fd, mp) struct mode *mp; {
  1209. X    ioctl (fd, TIOCGETP, &mp->m_ttyb);
  1210. X    ioctl (fd, TIOCGETC, &mp->m_tchars);
  1211. X    ioctl (fd, TIOCGLTC, &mp->m_ltchars);
  1212. X    ioctl (fd, TIOCLGET, &mp->m_lmode);
  1213. X    ioctl (fd, TIOCGETD, &mp->m_ldisc);
  1214. X}
  1215. X
  1216. Xstatic SetMode (op, np) struct mode *op, *np; {
  1217. X    *np = *op;
  1218. X    np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
  1219. X    np->m_ttyb.sg_flags |= CBREAK;
  1220. X    np->m_tchars.t_intrc = -1;
  1221. X    np->m_tchars.t_quitc = -1;
  1222. X    np->m_ltchars.t_suspc = -1;
  1223. X    np->m_ltchars.t_dsuspc = -1;
  1224. X    np->m_ltchars.t_flushc = -1;
  1225. X    np->m_ltchars.t_lnextc = -1;
  1226. X}
  1227. X
  1228. Xstatic char *GetTtyName () {
  1229. X    register char *p;
  1230. X    register n;
  1231. X
  1232. X    for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++)
  1233. X    ;
  1234. X    if (!p || *p == '\0')
  1235. X    Msg (0, "rmgr must run on a tty.");
  1236. X    return p;
  1237. X}
  1238. X
  1239. Xstatic Attach (how) {
  1240. X    register s, lasts, found = 0;
  1241. X    register DIR *dirp;
  1242. X    register struct direct *dp;
  1243. X    struct msg m;
  1244. X    char last[MAXNAMLEN+1];
  1245. X
  1246. X    if (SockName) {
  1247. X    if ((lasts = MakeClientSocket (0)) == -1)
  1248. X        if (how == MSG_CONT)
  1249. X        Msg (0,
  1250. X            "This session has already been continued from elsewhere.");
  1251. X        else
  1252. X        Msg (0, "There is no session to be resumed from %s.", SockName);
  1253. X    } else {
  1254. X    if ((dirp = opendir (SockPath)) == NULL)
  1255. X        Msg (0, "Cannot open %s", SockPath);
  1256. X    while ((dp = readdir (dirp)) != NULL) {
  1257. X        SockName = dp->d_name;
  1258. X        if (SockName[0] == '.')
  1259. X        continue;
  1260. X        if ((s = MakeClientSocket (0)) != -1) {
  1261. X        if (found == 0) {
  1262. X            strcpy (last, SockName);
  1263. X            lasts = s;
  1264. X        } else {
  1265. X            if (found == 1) {
  1266. X            printf ("There are detached sessions on:\n");
  1267. X            printf ("   %s\n", last);
  1268. X            close (lasts);
  1269. X            }
  1270. X            printf ("   %s\n", SockName);
  1271. X            close (s);
  1272. X        }
  1273. X        found++;
  1274. X        }
  1275. X    }
  1276. X    if (found == 0)
  1277. X        Msg (0, "There is no session to be resumed.");
  1278. X    if (found > 1)
  1279. X        Msg (0, "Type \"rmgr -r host.tty\" to resume one of them.");
  1280. X    closedir (dirp);
  1281. X    strcpy (SockNamePtr, last);
  1282. X    SockName = SockNamePtr;
  1283. X    }
  1284. X    m.type = how;
  1285. X    strcpy (m.m.attach.tty, GetTtyName ());
  1286. X    m.m.attach.apid = getpid ();
  1287. X    if (write (lasts, (char *)&m, sizeof (m)) != sizeof (m))
  1288. X    Msg (errno, "write");
  1289. X}
  1290. X
  1291. Xstatic void AttacherFinit () {
  1292. X    exit (0);
  1293. X}
  1294. X
  1295. Xstatic void ReAttach () {
  1296. X    Attach (MSG_CONT);
  1297. X}
  1298. X
  1299. Xstatic Attacher () {
  1300. X    signal (SIGHUP, AttacherFinit);
  1301. X    signal (SIGCONT, ReAttach);
  1302. X    while (1)
  1303. X    pause ();
  1304. X}
  1305. X
  1306. Xstatic Detach (suspend) {
  1307. X    register struct win **pp;
  1308. X    register int i;
  1309. X
  1310. X    if (Detached)
  1311. X    return;
  1312. X    signal (SIGHUP, SIG_IGN);
  1313. X    if (VoluntaryDetach) {    /* Can't save state if line dropped */
  1314. X    for (i=MAXWIN-1;i>0;i--) {
  1315. X    if (wtab[i]) {
  1316. X    m_selectwin(i);
  1317. X    m_nomenu2();
  1318. X    m_push(P_EVENT);
  1319. X        }
  1320. X    }
  1321. X    m_selectwin(0);
  1322. X    m_nomenu2(); m_nomenu();
  1323. X    m_push(P_ALL);
  1324. X    m_setmode(M_ACTIVATE);
  1325. X    }
  1326. X    SetTTY (0, &OldMode);
  1327. X    if (suspend) {
  1328. X    Kill (AttacherPid, SIGTSTP);
  1329. X    for (pp=wtab; pp < wtab+MAXWIN; ++pp)
  1330. X        if (*pp) (*pp)->win_ok=1;
  1331. X    } else {
  1332. X    for (pp=wtab; pp < wtab+MAXWIN; ++pp)
  1333. X        if (*pp) RemoveUtmp ((*pp)->slot);
  1334. X    printf ("\n[detached]\n");
  1335. X    Kill (AttacherPid, SIGHUP);
  1336. X    AttacherPid = 0;
  1337. X    }
  1338. X    close (0);
  1339. X    close (1);
  1340. X    close (2);
  1341. X    ioctl (DevTty, TIOCNOTTY, (char *)0);
  1342. X    Detached = 1;
  1343. X    do {
  1344. X    ReceiveMsg (ServerSocket); 
  1345. X    } while (Detached);
  1346. X    if (!suspend) {
  1347. X    for (pp = wtab; pp < wtab+MAXWIN; ++pp)
  1348. X        if (*pp) (*pp)->slot = SetUtmp ((*pp)->tty);
  1349. X    }
  1350. X    signal (SIGHUP, SigHup);
  1351. X}
  1352. X
  1353. Xstatic Kill (pid, sig) {
  1354. X    if (pid != 0)
  1355. X    (void) kill (pid, sig);
  1356. X}
  1357. X
  1358. Xstatic GetSockName () {
  1359. X    register client;
  1360. X    static char buf[2*MAXSTR];
  1361. X
  1362. X    if ((SockName = getenv ("STY")) != 0 && *SockName != '\0') {
  1363. X    client = 1;
  1364. X    setuid (getuid ());
  1365. X    setgid (getgid ());
  1366. X    } else {
  1367. X    sprintf (buf, "%s.%s", HostName, Filename (GetTtyName ()));
  1368. X    SockName = buf;
  1369. X    client = 0;
  1370. X    }
  1371. X    return client;
  1372. X}
  1373. X
  1374. Xstatic MakeServerSocket () {
  1375. X    register s;
  1376. X    struct sockaddr_un a;
  1377. X    char *p;
  1378. X
  1379. X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
  1380. X    Msg (errno, "socket");
  1381. X    a.sun_family = AF_UNIX;
  1382. X    strcpy (SockNamePtr, SockName);
  1383. X    strcpy (a.sun_path, SockPath);
  1384. X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1) {
  1385. X    p = Filename (SockPath);
  1386. X    Msg (0, "You already have a session running on %s.\n\
  1387. XIf it has been detached, try \"rmgr -r\".", p);
  1388. X    /*NOTREACHED*/
  1389. X    }
  1390. X    (void) unlink (SockPath);
  1391. X    if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
  1392. X    Msg (errno, "bind");
  1393. X    (void) chown (SockPath, getuid (), getgid ());
  1394. X    if (listen (s, 5) == -1)
  1395. X    Msg (errno, "listen");
  1396. X    return s;
  1397. X}
  1398. X
  1399. Xstatic MakeClientSocket (err) {
  1400. X    register s;
  1401. X    struct sockaddr_un a;
  1402. X
  1403. X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
  1404. X    Msg (errno, "socket");
  1405. X    a.sun_family = AF_UNIX;
  1406. X    strcpy (SockNamePtr, SockName);
  1407. X    strcpy (a.sun_path, SockPath);
  1408. X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1) {
  1409. X    if (err) {
  1410. X        Msg (errno, "connect: %s", SockPath);
  1411. X    } else {
  1412. X        close (s);
  1413. X        return -1;
  1414. X    }
  1415. X    }
  1416. X    return s;
  1417. X}
  1418. X
  1419. Xstatic SendCreateMsg (s, ac, av, name) char **av, *name; {
  1420. X    struct msg m;
  1421. X    register char *p;
  1422. X    register len, n;
  1423. X
  1424. X    m.type = MSG_CREATE;
  1425. X    p = m.m.create.line;
  1426. X    for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n) {
  1427. X    len = strlen (*av) + 1;
  1428. X    if (p + len >= m.m.create.line+MY_MAXLINE)
  1429. X        break;
  1430. X    strcpy (p, *av);
  1431. X    p += len;
  1432. X    }
  1433. X    m.m.create.nargs = n;
  1434. X    if (name)
  1435. X    strcpy(m.m.create.name,name);
  1436. X    else
  1437. X    m.m.create.name[0]='\0';
  1438. X    if (getwd (m.m.create.dir) == 0)
  1439. X    Msg (0, "%s", m.m.create.dir);
  1440. X    if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
  1441. X    Msg (errno, "write");
  1442. X}
  1443. X
  1444. X/*VARARGS1*/
  1445. Xstatic SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
  1446. X    register s;
  1447. X    struct msg m;
  1448. X
  1449. X    s = MakeClientSocket (1);
  1450. X    m.type = MSG_ERROR;
  1451. X    sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
  1452. X    (void) write (s, (char *)&m, sizeof (m));
  1453. X    close (s);
  1454. X    sleep (2);
  1455. X}
  1456. X
  1457. Xstatic char m_rbuf[MAXLINE];
  1458. X
  1459. Xstatic char *m_read() {
  1460. X    register char *ptr; char *index();
  1461. X    register int i;
  1462. X
  1463. X    i=read(0,m_rbuf,MAXLINE-1);
  1464. X    m_rbuf[i]='\0';
  1465. X    ptr=index(m_rbuf,Esc);
  1466. X    if (!ptr)
  1467. X    ptr=m_rbuf;
  1468. X    else
  1469. X    ptr++;
  1470. X    return(ptr);
  1471. X}
  1472. X
  1473. Xstatic ReplayWindow(p) struct win *p; {
  1474. X    register int n;
  1475. X
  1476. X    n=p->outptr-p->outbuf;
  1477. X    if (p->outful)        /* Replay last screen of data */
  1478. X        write(1,p->outptr,SCRBUFSIZE-n);
  1479. X    write(1,p->outbuf,n);
  1480. X}
  1481. X
  1482. Xstatic RestoreWindows(mt) int mt; {
  1483. X    register int i, j=0;
  1484. X    struct window_data wd;
  1485. X    register char *ptr;
  1486. X    register struct win *p;
  1487. X    char buf[8];
  1488. X    int n, nw;
  1489. X    int oldset=1;
  1490. X
  1491. X    if (mt != MSG_CONT) {    /* Restore after a detach, not suspend */
  1492. X    linemode(1);
  1493. X    for (i=MAXWIN-1; i>=0; i--) {
  1494. X    m_selectwin(i);
  1495. X    m_getinfo(G_ID);
  1496. X    ptr=m_read();
  1497. X    sscanf(ptr,"%d %d",&n, &nw);
  1498. X    if (wtab[i]) {
  1499. X        p=wtab[i];
  1500. X        if (n!=i) {        /* That one didn't exist, remake it... */
  1501. X        oldset=0;    /* Looks like the old context is gone. */
  1502. X        m_newwin(j,j,50,50);
  1503. X        ptr=m_read();
  1504. X        n=atoi(ptr);
  1505. X        if (!n) 
  1506. X            Msg(0, "Error restoring window.");
  1507. X        m_selectwin(n);
  1508. X        }
  1509. X        if (!oldset) {
  1510. X        m_size(p->cols,p->rows);
  1511. X            m_dupkey(Esc);
  1512. X            sprintf(buf,EventStr,Esc,n+'!');
  1513. X            m_setevent(ACTIVATE,buf);
  1514. X            buf[2]='D';
  1515. X            m_setevent(DESTROY,buf);
  1516. X            buf[2]='R';
  1517. X            m_setevent(RESHAPE,buf);
  1518. X        MakeMenu(n);
  1519. X        ReplayWindow(p);
  1520. X        } else if (VoluntaryDetach) {
  1521. X        m_pop();
  1522. X        } else ReplayWindow(p);    /* Redraw for hangup */
  1523. X    } else if (i) {        /* Gee, a window we didn't create. Byebye. */
  1524. X        oldset=0;        /* Again, we must have lost the old stuff */
  1525. X        m_destroywin(i);
  1526. X    }
  1527. X    }
  1528. X    linemode(0);
  1529. X    } else {
  1530. X    for (i=0;i<MAXWIN;i++) {
  1531. X        p=wtab[i];
  1532. X        if (p) {
  1533. X        m_selectwin(i);
  1534. X        m_pop();
  1535. X        m_selectmenu2(1);
  1536. X        if (!p->win_ok)
  1537. X            ReplayWindow(p);
  1538. X        }
  1539. X    }
  1540. X    }
  1541. X    VoluntaryDetach=0;
  1542. X}
  1543. X        
  1544. Xstatic ReceiveMsg (s) {
  1545. X    register ns;
  1546. X    struct sockaddr_un a;
  1547. X    int left, len = sizeof (a);
  1548. X    struct msg m;
  1549. X    char *p;
  1550. X
  1551. X    if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) {
  1552. X    Msg (errno, "accept");
  1553. X    return;
  1554. X    }
  1555. X    p = (char *)&m;
  1556. X    left = sizeof (m);
  1557. X    while (left > 0 && (len = read (ns, p, left)) > 0) {
  1558. X    p += len;
  1559. X    left -= len;
  1560. X    }
  1561. X    close (ns);
  1562. X    if (len == -1)
  1563. X    Msg (errno, "read");
  1564. X    if (left > 0)
  1565. X    return;
  1566. X    switch (m.type) {
  1567. X    case MSG_CREATE:
  1568. X    if (!Detached)
  1569. X        ExecCreate (&m);
  1570. X    break;
  1571. X    case MSG_CONT:
  1572. X    if (m.m.attach.apid != AttacherPid || !Detached)
  1573. X        break;    /* Intruder Alert */
  1574. X    /*FALLTHROUGH*/
  1575. X    case MSG_ATTACH:
  1576. X    if (Detached) {
  1577. X        if (kill (m.m.attach.apid, 0) == 0 &&
  1578. X            open (m.m.attach.tty, O_RDWR) == 0) {
  1579. X        (void) dup (0);
  1580. X        (void) dup (0);
  1581. X        AttacherPid = m.m.attach.apid;
  1582. X        Detached = 0;
  1583. X        GetTTY (0, &OldMode);
  1584. X        SetMode (&OldMode, &NewMode);
  1585. X        SetTTY (0, &NewMode);
  1586. X        RestoreWindows(m.type);
  1587. X        SwitchWindow(CurrNum);
  1588. X        }
  1589. X    } else {
  1590. X        Kill (m.m.attach.apid, SIGHUP);
  1591. X        Msg (0, "Not detached.");
  1592. X    }
  1593. X    break;
  1594. X    case MSG_ERROR:
  1595. X    Msg (0, "%s", m.m.message);
  1596. X    break;
  1597. X    default:
  1598. X    Msg (0, "Invalid message (type %d).", m.type);
  1599. X    }
  1600. X}
  1601. X
  1602. Xstatic ExecCreate (mp) struct msg *mp; {
  1603. X    char *args[MAXARGS];
  1604. X    register n;
  1605. X    register char **pp = args, *p = mp->m.create.line;
  1606. X
  1607. X    for (n = mp->m.create.nargs; n > 0; --n) {
  1608. X    *pp++ = p;
  1609. X    p += strlen (p) + 1;
  1610. X    }
  1611. X    *pp = 0;
  1612. X    if ((n = MakeWindow (mp->m.create.line, args, mp->m.create.name,
  1613. X        mp->m.create.dir)) != -1)
  1614. X    SwitchWindow (n);
  1615. X}
  1616. X
  1617. Xstatic char **SaveArgs (argc, argv) register argc; register char **argv; {
  1618. X    register char **ap, **pp;
  1619. X
  1620. X    if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
  1621. X    Msg (0, "Out of memory.");
  1622. X    while (argc--) {
  1623. X    if ((*pp = malloc (strlen (*argv)+1)) == 0)
  1624. X        Msg (0, "Out of memory.");
  1625. X    strcpy (*pp, *argv);
  1626. X    ++pp; ++argv;
  1627. X    }
  1628. X    *pp = 0;
  1629. X    return ap;
  1630. X}
  1631. X
  1632. Xstatic MakeNewEnv () {
  1633. X    register char **op, **np = NewEnv;
  1634. X    static char buf[MAXSTR];
  1635. X
  1636. X    if (strlen (SockName) > MAXSTR-5)
  1637. X    SockName = "?";
  1638. X    sprintf (buf, "STY=%s", SockName);
  1639. X    *np++ = buf;
  1640. X    *np++ = "TERM=mgr";
  1641. X    np += 2;
  1642. X    for (op = environ; *op; ++op) {
  1643. X    if (np == NewEnv + MAXARGS - 1)
  1644. X        break;
  1645. X    if (!IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP")
  1646. X        && !IsSymbol (*op, "STY"))
  1647. X        *np++ = *op;
  1648. X    }
  1649. X    *np = 0;
  1650. X}
  1651. X
  1652. Xstatic IsSymbol (e, s) register char *e, *s; {
  1653. X    register char *p;
  1654. X    register n;
  1655. X
  1656. X    for (p = e; *p && *p != '='; ++p) ;
  1657. X    if (*p) {
  1658. X    *p = '\0';
  1659. X    n = strcmp (e, s);
  1660. X    *p = '=';
  1661. X    return n == 0;
  1662. X    }
  1663. X    return 0;
  1664. X}
  1665. X
  1666. X/*VARARGS2*/
  1667. XMsg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
  1668. X    char buf[1024];
  1669. X    register char *p = buf;
  1670. X
  1671. X    if (Detached)
  1672. X    return;
  1673. X    sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
  1674. X    if (err) {
  1675. X    p += strlen (p);
  1676. X    if (err > 0 && err < sys_nerr)
  1677. X        sprintf (p, ": %s", sys_errlist[err]);
  1678. X    else
  1679. X        sprintf (p, ": Error %d", err);
  1680. X    }
  1681. X    printf ("%s\r\n", buf);
  1682. X    if (!HasWindow) {
  1683. X    Kill (AttacherPid, SIGHUP);
  1684. X    exit (1);
  1685. X    }
  1686. X}
  1687. X
  1688. Xstatic char *Filename (s) char *s; {
  1689. X    register char *p;
  1690. X
  1691. X    p = s + strlen (s) - 1;
  1692. X    while (p >= s && *p != '/') --p;
  1693. X    return ++p;
  1694. X}
  1695. X
  1696. Xstatic IsNum (s, base) register char *s; register base; {
  1697. X    for (base += '0'; *s; ++s)
  1698. X    if (*s < '0' || *s > base)
  1699. X        return 0;
  1700. X    return 1;
  1701. X}
  1702. X
  1703. Xstatic InitUtmp () {
  1704. X    struct passwd *p;
  1705. X
  1706. X    if ((utmpf = open (UtmpName, O_WRONLY)) == -1) {
  1707. X    if (errno != EACCES)
  1708. X        Msg (errno, UtmpName);
  1709. X    return;
  1710. X    }
  1711. X    if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') {
  1712. X    if ((p = getpwuid (getuid ())) == 0)
  1713. X        return;
  1714. X    LoginName = p->pw_name;
  1715. X    }
  1716. X    utmp = 1;
  1717. X}
  1718. X
  1719. Xstatic SetUtmp (name) char *name; {
  1720. X    register char *p;
  1721. X    register struct ttyent *tp;
  1722. X    register slot = 1;
  1723. X    struct utmp u;
  1724. X
  1725. X    if (!utmp)
  1726. X    return 0;
  1727. X    if (p = rindex (name, '/'))
  1728. X    ++p;
  1729. X    else p = name;
  1730. X    setttyent ();
  1731. X    while ((tp = getttyent ()) != NULL && strcmp (p, tp->ty_name) != 0)
  1732. X    ++slot;
  1733. X    if (tp == NULL)
  1734. X    return 0;
  1735. X    strncpy (u.ut_line, p, 8);
  1736. X    strncpy (u.ut_name, LoginName, 8);
  1737. X    u.ut_host[0] = '\0';
  1738. X    time (&u.ut_time);
  1739. X    (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
  1740. X    (void) write (utmpf, (char *)&u, sizeof (u));
  1741. X    return slot;
  1742. X}
  1743. X
  1744. Xstatic RemoveUtmp (slot) {
  1745. X    struct utmp u;
  1746. X
  1747. X    if (slot) {
  1748. X    bzero ((char *)&u, sizeof (u));
  1749. X    (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
  1750. X    (void) write (utmpf, (char *)&u, sizeof (u));
  1751. X    }
  1752. X}
  1753. X
  1754. X#ifndef GETTTYENT
  1755. X
  1756. Xstatic setttyent () {
  1757. X    struct stat s;
  1758. X    register f;
  1759. X    register char *p, *ep;
  1760. X
  1761. X    if (ttnext) {
  1762. X    ttnext = tt;
  1763. X    return;
  1764. X    }
  1765. X    if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
  1766. X    Msg (errno, ttys);
  1767. X    if ((tt = malloc (s.st_size + 1)) == 0)
  1768. X    Msg (0, "Out of memory.");
  1769. X    if (read (f, tt, s.st_size) != s.st_size)
  1770. X    Msg (errno, ttys);
  1771. X    close (f);
  1772. X    for (p = tt, ep = p + s.st_size; p < ep; ++p)
  1773. X    if (*p == '\n') *p = '\0';
  1774. X    *p = '\0';
  1775. X    ttnext = tt;
  1776. X}
  1777. X
  1778. Xstatic struct ttyent *getttyent () {
  1779. X    static struct ttyent t;
  1780. X
  1781. X    if (*ttnext == '\0')
  1782. X    return NULL;
  1783. X    t.ty_name = ttnext + 2;
  1784. X    ttnext += strlen (ttnext) + 1;
  1785. X    return &t;
  1786. X}
  1787. X
  1788. X#endif
  1789. X
  1790. X#ifndef USEBCOPY
  1791. Xbcopy (s1, s2, len) register char *s1, *s2; register len; {
  1792. X    if (s1 < s2 && s2 < s1 + len) {
  1793. X    s1 += len; s2 += len;
  1794. X    while (len-- > 0) {
  1795. X        *--s2 = *--s1;
  1796. X    }
  1797. X    } else {
  1798. X    while (len-- > 0) {
  1799. X        *s2++ = *s1++;
  1800. X    }
  1801. X    }
  1802. X}
  1803. X#endif
  1804. ________This_Is_The_END________
  1805. if test `wc -c < rmgr.c` -ne    32339; then
  1806.     echo 'shar: rmgr.c was damaged during transit (should have been    32339 bytes)'
  1807. fi
  1808. fi        ; : end of overwriting check
  1809. echo 'x - rmgr.h'
  1810. if test -f rmgr.h; then echo 'shar: not overwriting rmgr.h'; else
  1811. sed 's/^X//' << '________This_Is_The_END________' > rmgr.h
  1812. X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
  1813. X * Not derived from licensed software.
  1814. X *
  1815. X * Permission is granted to freely use, copy, modify, and redistribute
  1816. X * this software, provided that no attempt is made to gain profit from it,
  1817. X * the author is not construed to be liable for any results of using the
  1818. X * software, alterations are clearly marked as such, and this notice is
  1819. X * not modified.
  1820. X */
  1821. X
  1822. X#include <mgr/term.h>
  1823. X
  1824. X#define MAXSTR       128
  1825. X#define    MAXARGS      64
  1826. X
  1827. X#define IOSIZE       256
  1828. X#define    SCRBUFSIZE    2048    /* About 1 screenful of characters */
  1829. X
  1830. Xstruct win {
  1831. X    int wpid;
  1832. X    int ptyfd;
  1833. X    int rows;
  1834. X    int cols;
  1835. X    char cmd[MAXSTR];
  1836. X    char tty[MAXSTR];
  1837. X    int slot;
  1838. X    int win_ok;
  1839. X    int outful;
  1840. X    char outbuf[SCRBUFSIZE];
  1841. X    char *outptr;
  1842. X};
  1843. X
  1844. X#define MY_MAXLINE 1024
  1845. X
  1846. X#define MSG_CREATE    0
  1847. X#define MSG_ERROR     1
  1848. X#define MSG_ATTACH    2
  1849. X#define MSG_CONT      3
  1850. X
  1851. Xstruct msg {
  1852. X    int type;
  1853. X    union {
  1854. X    struct {
  1855. X        int nargs;
  1856. X        char line[MY_MAXLINE];
  1857. X        char dir[1024];
  1858. X        char name[MAXSTR];
  1859. X    } create;
  1860. X    struct {
  1861. X        int apid;
  1862. X        char tty[1024];
  1863. X    } attach;
  1864. X    char message[MY_MAXLINE];
  1865. X    } m;
  1866. X};
  1867. ________This_Is_The_END________
  1868. if test `wc -c < rmgr.h` -ne     1169; then
  1869.     echo 'shar: rmgr.h was damaged during transit (should have been     1169 bytes)'
  1870. fi
  1871. fi        ; : end of overwriting check
  1872. exit 0
  1873. --
  1874.   -- Howard Chu @ University of Michigan
  1875.   one million data bits stored on a chip, one million bits per chip
  1876.     if one of those data bits happens to flip,
  1877.         one million data bits stored on the chip...
  1878.