james@umcp-cs.UUCP (10/26/83)
: Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting phone.1l' sed 's/^X//' <<'//go.sysin dd *' >phone.1l X.TH PHONE 1 local X.SH NAME phone \- talk with other users X.SH SYNOPSIS X.B phone [ -a ] [ -p | -c ] [ -s X.I number ] [ user [user ...] ] X.SH DESCRIPTION X.I Phone is used to set up a conversation between two users on CRT or printing terminals. The connection is started by one user typing ``phone X.IR username ''. If the other user is logged in and has messages enabled, he will be notified that someone is phoning him. He can then answer by typing ``phone''. Both screens will be cleared and divided in half horizontally. Everything each user types will appear in the top half of his screen, and everything the other user types will appear in the bottom half. The conversation continues until either user types control-D. A control-L (formfeed) will refresh the screen. X.B Phone will inform you of when the connection is really opened -- the screen is cleared immediately, but the window label reflects the actual state of the connection. X.PP X.B Phone is capable of talking with up to eight users, as in a conference call. By default, when you answer someone who's talking to more than one person, X.B phone also calls everyone that someone is calling. They can choose to not answer, if they like; or you can disconnect them if you don't want to talk to them. You can use the ``colon format'' (see below) to answer a particular caller if you so wish. X.PP The options to phone are as follows: X.TP X.B -c Do not use windows; instead, assume that the terminal is a CRT. This is useful at low baud rates. X.TP X.B -p Do not use windows; instead, assume that the terminal is a printing terminal. The difference between this and X.B -c is that under X.BR -p , X.B phone prints backspaces as number signs (#). X.TP X.BR -s " number" This option only affects window mode. X.I Number is used as the number of lines by which windows are to scroll (called the X.IR "scroll step" ). If X.I number is zero, the windows won't scroll at all, but rather the cursor will wrap from the bottom line of a window to the top line, and clear each line as it gets to it. This is faster on some terminals, but is somewhat confusing. X.PP While you are talking to others, you can ask to call more users, or hang up on some of the users you are talking to. To get to command mode, type ESCAPE. This will pop up a window and prompt you for a command. The commands are as follows: X.TP X.B call Call other users. This command takes a list of users, and calls each named user. X.TP X.B answer Answers calls. With no arguments, answers everyone. With a list of users, answers each specified call. (You can also answer by calling the user who called.) X.TP X.B quit Exits X.BR phone (equivalent to typing control-D). X.TP X.B hangup Hangs up on some of the users you're connected to. This command takes a list of users, and hangs up each one. X.TP X.B scrollstep Changes the scroll step (as if you had given the X.B -s option on the command line). This command takes a numeric argument which is the new scroll step. X.TP X.B ? Gives a short help listing. X.SH "THE ``COLON FORMAT''" If one user is logged onto several terminals, phone will normally attempt to call each terminal's user. This can be prevented by giving the user's name as "user:tty" (where tty is the terminal you really want to call). All user names are treated this way; you can answer "bob" on "tty6" by typing ``ESC answer bob:tty6 RETURN''. X.PP This program uses the CMU IPC facility and the Maryland Window Library, and is not likely to work on machines without both. X.SH FILES X/tmp/ph* user lists X.SH SEE ALSO mesg(1), write(1), who(1), mail(1) X.SH BUGS X.I \-home option not implemented. //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 phone.1l /bin/echo -n ' '; /bin/ls -ld phone.1l fi /bin/echo 'Extracting Makefile' sed 's/^X//' <<'//go.sysin dd *' >Makefile PH= answer.o chat.o cmd.o display.o ipcio.o keys.o phone.o\ ngets.o phio.o u.o PHSRC= answer.c chat.c cmd.c display.c ipcio.c keys.c phone.c\ ngets.c phio.c u.c HDRS= phone.h ipc.h CFLAGS= -O -R DESTDIR=/usr/local phone: $(PH) cc $(CFLAGS) -o phone $(PH) -lipc -lwinlib -ljobs -ltermlib install: rm -f $(DESTDIR)/phone cp phone $(DESTDIR)/phone strip $(DESTDIR)/phone uucp -c /usr/local/phone cvl!~uucp clean: rm -f *.o phone script: cd /usr/src/local/cmd/phone; makescript /tmp/phonescript Makefile $(HDRS) $(PHSRC) answer.o: phone.h ipc.h chat.o: phone.h ipc.h cmd.o: phone.h ipc.h display.o: phone.h ipc.h ipcio.o: ipc.h keys.o: phone.h ipc.h phone.o: phone.h ipc.h ngets.o: phone.h ipc.h phio.o: phone.h ipc.h u.o: phone.h ipc.h //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 Makefile /bin/echo -n ' '; /bin/ls -ld Makefile fi /bin/echo 'Extracting ipc.h' sed 's/^X//' <<'//go.sysin dd *' >ipc.h #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #define IPCD 2048 /* Max data in an IPC buffer */ #define NPORTS 20 /* Max number of ports */ struct ipcio { int i_cnt; /* Data count, strategically placed */ int i_mode:1, /* 0 => incoming, 1 => outgoing */ i_blocked:1, /* True => port is blocked */ i_deleted:1, /* True => port was deleted */ i_closed:1; /* True => port is closed */ char i_name[50]; /* Port name */ struct Msg i_msg; /* Msg structure for I/O */ char *i_data; /* Data */ }; extern struct ipcio _ports[NPORTS]; #define IPCIO struct ipcio IPCIO *iopen(); #define FixSysPort(p) ((p)->i_msg.LocalPort = SysLocalPort) #define SetSysPort(x) (SysLocalPort = (x)) #define ISOPEN(p) ((p)->i_closed == 0 && (p)->i_msg.RemotePort!=NULLPORT) extern localport SysLocalPort; //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 ipc.h /bin/echo -n ' '; /bin/ls -ld ipc.h fi /bin/echo 'Extracting phone.h' sed 's/^X//' <<'//go.sysin dd *' >phone.h #include "ipc.h" #include <local/window.h> #define CTRL(c) ((c)&0x1f) #define MAXCONN 8 /* Max number of users to connect to */ X/* * con_c means "I have called him, and am waiting for him to call back, * at which point I will answer"; con_t means "talking". */ enum constates { con_c, con_t }; X/* * Disp_Unix means have not initialized display. Disp_Windows means * windows are being used. Disp_CRT & Disp_Printer indicate that the * terminal is not using windows, and is or is not able to overwrite * characters (CRT==use \b \b, Printer==use #). This information is not * local to display.c since GetChar() needs to know if it should call * Wrefresh(). */ enum Disp_Modes { Disp_Unix = 0, Disp_Windows, Disp_CRT, Disp_Printer }; struct connection { IPCIO *con_ipc; char con_user[9]; char con_tty[9]; char con_id[18]; enum constates con_state; union { /* For the display routines */ Win *d_win; struct { char *l_base; int l_cnt; } d_line; } con_d; #define d_base d_line.l_base #define d_cnt d_line.l_cnt } conlist[MAXCONN]; int nconn; /* Number of active connections */ int ScrollStep; /* Scroll step for windows */ int lineno; /* Line number for puts_more */ char LogFile[30]; /* Name of phone log file */ char *Caller; /* Name of caller if From == 0 */ struct connection *From; /* For GetChar(0), user char is from */ X/* Shorthand */ #define Myself (&conlist[0]) #define Me Myself->con_user #define MyTty Myself->con_tty #define MyID Myself->con_id X/* Non int functions */ char *tyme (), *concat (), *NextU (), *malloc (), *strsave (), *alloc (); struct connection *ph_iscon (); //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 phone.h /bin/echo -n ' '; /bin/ls -ld phone.h fi /bin/echo 'Extracting answer.c' sed 's/^X//' <<'//go.sysin dd *' >answer.c #ifndef lint static char rcsid[] = "@(#)answer.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" #include <sys/dir.h> #include <sys/stat.h> AnswerAllCalls () { struct direct tmp; struct stat sbuf; register FILE *f, *dirf; char nbuf[DIRSIZ+10]; char lbuf[100]; dirf = fopen ("/tmp", "r"); if (dirf == NULL) { error (0, "Can't open /tmp !?\r\n"); return; } while (fread ((char *)&tmp, sizeof tmp, 1, dirf) == 1) { if (tmp.d_ino == 0 || strncmp (tmp.d_name, "ph", 2)) continue; sprintf (nbuf, "/tmp/%.*s", DIRSIZ, tmp.d_name); if ((f = fopen (nbuf, "r")) == NULL) continue; if (fgets (lbuf, sizeof lbuf, f))/* First line is caller */ while (fgets (lbuf, sizeof lbuf, f)) { lbuf[strlen(lbuf) - 1] = 0; if (strcmp (MyID, lbuf) == 0) { callthem (f); break; } } fclose (f); } fclose (dirf); } static callthem (f) register FILE *f; { char lbuf[100]; rewind (f); while (fgets (lbuf, sizeof lbuf, f)) { lbuf[strlen (lbuf) - 1] = 0; if (strcmp (lbuf, MyID)) PlaceCall (lbuf, 0, 1); } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 answer.c /bin/echo -n ' '; /bin/ls -ld answer.c fi /bin/echo 'Extracting chat.c' sed 's/^X//' <<'//go.sysin dd *' >chat.c #ifndef lint static char rcsid[] = "@(#)chat.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" static EndChat; X/* quit command */ c_quit () { EndChat++; } X/* Chat() -- take Myself's characters and do the right stuff */ Chat () { register c; ioption (REPLY); /* Set REPLY mode */ while (!EndChat && nconn > 1 && (c = GetChar ()) >= 0) { switch (c) { case '\r': DChar ('\n', Myself); chatter ('\n', 1); break; default: if (c < 32 || c == 127) break; case '\t': case '\b': case CTRL('X'): case '\n': DChar (c, Myself); chatter (c, 1); break; case 033: DoCommand (); break; case CTRL('L'): ScreenGarbaged++; break; case CTRL('Z'): Suspend (); break; case CTRL('D'): EndChat++; break; } } } ChatChar (c) register c; { static char stop[] = " [^Z] "; if (From == 0) { if (c == CTRL('D')) return;/* Extra eof - ignore */ if (c != CTRL('C')) error (0, "message from '%s'? (%c)\r\n", Caller, c); else error (0, "%s is calling (type ESC ? for help)\r\n", Caller); return; } switch (c) { case '\r': DChar ('\n', From); return; default: if (c < 32 || c == 127) return; case '\t': case '\b': case CTRL('X'): case '\n': DChar (c, From); return; case CTRL('Z'): DStr (stop, From); return; case CTRL('Y'): c = strlen (stop); while (--c >= 0) DChar ('\b', From); return; case CTRL('D'): ph_disconn (From); return; case CTRL('C'): /* he is calling me; I had called him; I answer */ From -> con_state = con_t; ph_write (From, "\1"); DConn (From); return; case CTRL('A'): /* I had called him; he is answering */ From -> con_state = con_t; DConn (From); return; } } X/* Send character c to everyone (but not waiting-fors if not all) */ chatter (c, all) char c; int all; { register struct connection *ch; static char cbuf[2]; cbuf[0] = c; for (ch = conlist + 1; ch < &conlist[MAXCONN]; ch++) if (ch -> con_user[0] && (all || (ch -> con_state == con_t))) ph_write (ch, cbuf); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 chat.c /bin/echo -n ' '; /bin/ls -ld chat.c fi /bin/echo 'Extracting cmd.c' sed 's/^X//' <<'//go.sysin dd *' >cmd.c #ifndef lint static char rcsid[] = "@(#)cmd.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" X/* c_quit is in chat.c */ extern c_help(), c_call(), c_ss(), c_who(), c_quit(); struct cmd { char *cmd_name; /* Cmd name */ int (*cmd_func)(); /* Function */ int cmd_arg2; /* Arg 2 to function */ }; static struct cmd cmdlist[] = { "?", c_help, 0, "help", c_help, 0, "call", c_call, 0, "hangup", c_call, 1, "answer", c_call, 2, "scrollstep", c_ss, 0, "who", c_who, 0, "quit", c_quit, 0, 0, 0, 0 }; DoCommand () { register char *cp; register struct cmd *p, *c; char *s; int len; s = (char *) DGetCmd (); for (cp = s; *cp && *cp != ' '; cp++) ; if (*cp) { *cp++ = 0; /* terminate command */ while (*cp == ' ') cp++; } len = strlen (s); if (len == 0) /* no command */ return; c = 0; for (p = cmdlist; p -> cmd_name; p++) { if (strncmp (s, p -> cmd_name, len) == 0) if (c) { error (0, "%s: ambiguous\r\n", s); return; } else c = p; } if (c == 0) { error (0, "%s: command not recognized\r\n", s); return; } (*c -> cmd_func) (cp, c -> cmd_arg2); } c_help () { error (0, "\ Use ^D (Control-D) to quit.\r\n\ Commands are:\r\n\ call user [user ...]\r\n\ answer [user [user ...]]\r\n\ hangup user [user ...]\r\n\ scrollstep number\r\n\ who\r\n\ quit\r\n\ where user is either ``name'', or ``name:tty''.\r\n\ Only the first few characters of a command need\r\n\ be typed. If you're using windows: to make\r\n\ this window go away, type ESC RETURN (i.e. an\r\n\ empty command).\r\n"); } c_who () { error (0, "Not implemented yet\r\n"); } c_ss (s) char *s; { if (!*s) error (0, "Usage: scrollstep number\r\n"); else { ScrollStep = atoi (s); DFixScroll (); } } X/* call, answer, and hangup */ c_call (users, what) char *users; { register char *cp = users; if (*cp == 0 && what == 2) { AnswerAllCalls (); return; } if (!*cp) { error (0, "Usage: %s user(s)\r\n", what ? "hangup" : "call"); return; } while (*cp) { users = cp; while (*cp && *cp != ' ') cp++; if (*cp) *cp++ = 0; PlaceCall (users, what, 0); while (*cp == ' ') cp++; } } X/* Place a call to (or hang up on) a particular user. (2 == answer) */ PlaceCall (u, what, okerr) register char *u; int what, okerr; { register char *t; t = u; while (*t && *t != ':') t++; if (*t) { *t++ = 0; if (what != 1 && !IsOn (u, t)) { if (!okerr) { if (what == 2) error (0, "%s isn't calling\r\n"); else error (0, "%s isn't on %s\r\n", u, t); } return; } if (what == 1) Hangup (u, t, okerr); else Call (u, t, okerr); } else { RewindU (); t = NextU (u); if (t == 0) if (!okerr) error (0, "%s isn't %s\r\n", u, what == 2 ? "calling" : "logged on"); while (t) { if (what == 1) Hangup (u, t, okerr); else Call (u, t, okerr); t = NextU (u); } } } X/* Call user u on tty t */ Call (u, t, okerr) register char *u, *t; int okerr; { if (strcmp(u, Myself->con_user)==0 && strcmp(t, Myself->con_tty)==0) { if (!okerr) error (0, "You can't call yourself.\r\n"); return; } if (ph_iscon (u, t)) { if (!okerr) error (0, "%s:%s already connected!\r\n", u, t); return; } if (nconn >= MAXCONN) { error (0, "I can't handle any more outgoing calls\r\n"); return; } if (ph_conn (u, t)) error (0, "%s:%s is not receiving calls\r\n", u, t); /* Else connection established. In any case, nothing more to do. */ } X/* Hang up line to user u, tty t */ X/* "okerr" arg is unused as yet */ Hangup (u, t, okerr) register char *u, *t; int okerr; { struct connection *c; if (!(c = ph_iscon (u, t))) { error (0, "Not calling %s:%s!\r\n", u, t); return; } ph_disconn (c); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 cmd.c /bin/echo -n ' '; /bin/ls -ld cmd.c fi /bin/echo 'Extracting display.c' sed 's/^X//' <<'//go.sysin dd *' >display.c #ifndef lint static char rcsid[] = "@(#)display.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" #include <sgtty.h> #include <signal.h> extern struct sgttyb WOld; X/* Anything having to do with displaying things */ enum Disp_Modes DisplayMode; static NoLocalEcho; /* Set for n mode; prevents Myself echo */ static CCol; /* The current column on the crt/printer */ static Rows, Cols; /* The actual screen size [for windows] */ static NextRow, NextCol; /* Used during DAdjust - next Win posn */ static struct connection *CConn;/* The connection using CCol */ static char StringBuf[1024]; /* The string buffer */ static Win *CommandWindow; /* The error/command window */ X/* Given fmt & args & such, stuff into StringBuf the printf-ed output */ static sprintrmt (arg) register char **arg; { struct _iobuf _strbuf; _strbuf._flag = _IOSTRG; _strbuf._ptr = StringBuf; _strbuf._cnt = sizeof StringBuf - 1; _doprnt (*arg, arg+1, &_strbuf); _strbuf._cnt++; putc (0, &_strbuf); } X/* This is almost more an "announce" than an "error" function. Oh well. */ error (fatal, m) { sprintrmt (&m); switch (DisplayMode) { case Disp_Unix: fputs (StringBuf, stdout); fflush (stdout); break; case Disp_Windows: Wfront (CommandWindow); Wunhide (CommandWindow); if (fatal) { Wputs (StringBuf, CommandWindow); Wrefresh (0); } else puts_more (StringBuf, CommandWindow); break; case Disp_CRT: case Disp_Printer: if (CCol) { putchar ('\r'); putchar ('\n'); } fputs (StringBuf, stdout); fflush (stdout); CCol = 0; break; } if (!fatal) return; if (DisplayMode != Disp_Unix) { ph_disall (); CleanUpDisplay (1); } Exit (1); } X/* for ngets */ static WEcho (c) register c; { if (c == '\n') { Wputs ("\r\n", CommandWindow); return; } Wputc (c, CommandWindow); if (c == '\b') Wputs (" \b", CommandWindow); } X/* for ngets */ static DEcho (c) { DChar (c, Myself); } X/* Get a command string */ char * DGetCmd () { static char cmd[] = "Command: ", buf[200]; if (DisplayMode == Disp_Windows) { Win *oldcur = CurWin; Woncursor (CommandWindow, 0); Wfront (CommandWindow); Wunhide (CommandWindow); Wputs (cmd, CommandWindow); CurWin = CommandWindow; ngets (buf, sizeof buf, WEcho, 0); Whide (CommandWindow); Woncursor (CommandWindow, 1); CurWin = oldcur; } else { DStr (cmd, Myself); ngets (buf, sizeof buf, DEcho, DisplayMode == Disp_Printer); } return buf; } X/* Display string s on connection c */ DStr (s, c) register char *s; register struct connection *c; { while (*s) DChar (*s++, c); } X/* Display char ch on connection c */ DChar (ch, c) register ch; register struct connection *c; { switch (DisplayMode) { case Disp_Unix: /* Flake-o */ error (1, "No display mode at DChar\n"); case Disp_Windows: if (ch == CTRL('x')) { Wputc ('\r', c -> con_d.d_win); Wclearline (c -> con_d.d_win, 0); } else if (ch == '\b') Wputs ("\b \b", c -> con_d.d_win); else if (ch == '\n') { Wputs ("\r\n", c -> con_d.d_win); if (c == Myself) Whide (CommandWindow); } else Wputc (ch, c -> con_d.d_win); return; case Disp_CRT: case Disp_Printer: if (c == Myself && NoLocalEcho) return; /* Myself overrides everyone else */ if (CCol && CConn != c && c == Myself) { putchar ('\r'); putchar ('\n'); printf ("%s: ", MyID); CConn = c; CCol = 1;/* ok, so its not the column number */ } if (ch == '\r') ch = '\n'; /* If we are in the midst of something, then compare the connection with the one last printed. If same, then continue, otherwise store characters up to a newline. Newline forces pending characters to be pushed out. */ if (CCol) { /* Same as last connection? */ if (c == CConn) { if (ch == CTRL('x')) { fputs (" XXX", stdout); fflush (stdout); ch = '\n'; } put: if (ch == '\n') putchar ('\r'); if (ch == '\b') { /* Backspace and decrement column (but dont get to zero) */ if (DisplayMode == Disp_CRT) { putchar (ch); putchar (' '); putchar (ch); } else putchar ('#'); fflush (stdout); /* if (--CCol <= 0) CCol = 1; */ break; } putchar (ch); fflush (stdout); /* Compute new column for tab/newline/other */ /* else if (ch == '\t') CCol = (CCol + 8) & ~7; else */ if (ch == '\n') CCol = 0; /* else CCol++; */ } /* Different connection */ else { if (ch == CTRL('x')) c -> con_d.d_cnt = 0; else if (ch == '\b') { if (c -> con_d.d_cnt) c -> con_d.d_cnt--; } else { c->con_d.d_base[c->con_d.d_cnt++] = ch; if (ch == '\n') goto pushout; } } break; /* go to if (CCol == 0) below */ } /* Wasnt in the midst of something, just start this connection going */ if (ch == CTRL('x') || ch == '\b') break; printf ("%s: ", c -> con_id); CConn = c; CCol = 1; goto put; } /* If now not in the midst of things, flush pending line(s). Try to wind up in midst of last pushed out line. */ if (CCol == 0) { pushout: for (c = conlist; c < &conlist[MAXCONN]; c++) { if (c -> con_user[0] == 0) continue; if (c -> con_d.d_cnt) { c -> con_d.d_base[c -> con_d.d_cnt] = 0; if (CCol) { putchar ('\r'); putchar ('\n'); } printf ("%s: %s", c->con_id, c->con_d.d_base); if (c->con_d.d_base[c->con_d.d_cnt-1] != '\n') CCol = 1;/* a bit off... */ else { putchar ('\r'); CCol = 0; } c -> con_d.d_cnt = 0; fflush (stdout); CConn = c; } } } } X/* Go out of funny mode so can exit */ CleanUpDisplay (ref) { if (DisplayMode == Disp_Windows) { if (ref) Wrefresh (0); /* Force display of errors */ Wcleanup (); } else { if (DisplayMode != Disp_Unix) stty (0, &WOld); putchar ('\n'); } } X/* Suspend for a while */ Suspend () { extern DisallowSuspends; /* set for OTTYDISC users! */ if (DisallowSuspends) return; chatter (CTRL('Z'), 0); /* sigh */ if (DisplayMode == Disp_Windows) Wsuspend (); else { stty (0, &WOld); kill (0, SIGTSTP); Raw (); } chatter (CTRL('Y'), 0); } X/* Allocate a display thingy */ DAlloc (c) register struct connection *c; { if (DisplayMode == Disp_Windows) { register Win *w, *oldcur; oldcur = CurWin; w = Wopen (0, 0, 0, Cols, Rows, 0, 0); if (w == 0) error (1, "Can't open window !?\r\n"); c -> con_d.d_win = w; Wsetpopup (w, ScrollStep); if (c == Myself) Woncursor (w, 0); else CurWin = oldcur; DAdjust (); /* Adjust all windows */ return; } c -> con_d.d_base = alloc (IPCD); c -> con_d.d_cnt = 0; } X/* A connection has been established, notify */ DConn (c) register struct connection *c; { if (DisplayMode == Disp_Windows) { sprintf (StringBuf, "Talking to %s", c -> con_id); Wlabel (c -> con_d.d_win, StringBuf, 0, 1); Ding (); /* awaken the user */ } else error (0, "[open %s]\7\r\n", c -> con_id); } X/* Free the display thingy */ DFree (c) register struct connection *c; { if (DisplayMode == Disp_Windows) { Wclose (c -> con_d.d_win); DAdjust (); return; } error (0, "[EOF %s]\r\n", c -> con_id); free (c -> con_d.d_base); } X/* Initialize display */ StartDisplay (mode) int mode; /* 0 = whatever 1 = Disp_CRT 2 = Disp_Printer 3 = n */ { struct sgttyb s; if (mode) { DisplayMode = mode == 1 ? Disp_CRT : Disp_Printer; if (mode == 3) NoLocalEcho++; Raw (); return; } gtty (0, &s); s.sg_flags |= RAW; if (Winit (&s, 0)) { ioctl (0, TIOCLGET, &mode); DisplayMode = (mode & LCRTBS) ? Disp_CRT : Disp_Printer; Raw (); return; } Wscreensize (&Rows, &Cols); CommandWindow = Wopen (0, 0, 0, Min (Cols, 50), Min (Rows, 10), 0, 0); if (CommandWindow == 0) { printf ("error opening cmd win\r\n"); Wexit (1); } Whide (CommandWindow); Wframe (CommandWindow); Wlabel (CommandWindow, "Commands & Such", 0, 1); DisplayMode = Disp_Windows; } X/* Start raw mode */ static Raw () { struct sgttyb s; gtty (0, &WOld); s = WOld; s.sg_flags |= RAW; s.sg_flags &= ~ECHO; stty (0, &s); } X/* Fix the scroll step for all existing windows */ DFixScroll () { register struct connection *c; if (DisplayMode != Disp_Windows) return; for (c = conlist; c < &conlist[MAXCONN]; c++) if (c -> con_user[0]) Wsetpopup (c -> con_d.d_win, ScrollStep); } X/* Adjust the sizes and positions of all the windows on the display */ static DAdjust () { register width, height, n; register struct connection *c; if (nconn < 2) return; width = Cols / ((nconn + 3) / 4); height = Rows / ((nconn - 1) % 4 + 1); NextRow = 0; NextCol = 0; for (c = conlist; c < &conlist[MAXCONN]; c++) { if (c -> con_user[0]) { sprintf (StringBuf, c == Myself ? "Outgoing line" : c -> con_state == con_t ? "Talking to %s" : "Waiting for %s", c -> con_id); SetSize (c -> con_d.d_win, width, height); } } } X/* Set the size and position of the specified window, relscrolling as necessary to retain the last visible context. */ static SetSize (w, cols, rows) register Win *w; int cols, rows; { int orows; /* Old number of rows */ orows = w -> w_cursor.row + 3; Wmove (w, 0, 0); /* Move it home so Wsize will succeed */ Wsize (w, cols, rows); Wmove (w, NextCol, NextRow); if (rows < orows) Wrelscroll (w, orows - rows, 0, 1); Wframe (w); Wlabel (w, StringBuf, 0, 1); NextCol += cols; if (NextCol >= Cols) { NextCol = 0; NextRow += rows; if (NextRow >= Rows) NextRow = 0; } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 display.c /bin/echo -n ' '; /bin/ls -ld display.c fi /bin/echo 'Extracting ipcio.c' sed 's/^X//' <<'//go.sysin dd *' >ipcio.c #ifndef lint static char rcsid[] = "@(#)ipcio.c U of Maryland ACT 31-Mar-1983"; #endif #include "ipc.h" char *malloc (); localport SysLocalPort; IPCIO _ports[NPORTS]; static Option; X/* Called to set REPLY or WAIT mode, as appropriate */ ioption (opt) int opt; { Option = opt; } X/* Called to invalidate all ports. The keyboard server (started with a fork() in keys.c) uses this to tell these routines that all those IPCIO things he inherited are no good. */ iinvalid () { register IPCIO *i; for (i = _ports; i < &_ports[NPORTS]; i++) i -> i_name[0] = 0; } X/* Open a port and return a pointer to it */ IPCIO * iopen (name, mode) register char *name, *mode; { register IPCIO *p; for (p = _ports; p < &_ports[NPORTS]; p++) if (p -> i_name[0] == 0) break; if (p >= &_ports[NPORTS])/* None free */ return 0; strcpy (p -> i_name, name); p -> i_mode = *mode == 'w' ? 1 : 0; p -> i_blocked = 0; p -> i_closed = 0; p -> i_cnt = 0; if (p -> i_data == 0) { p -> i_data = malloc (IPCD); if (p -> i_data == 0) return 0; } p -> i_msg.MsgType = NORMALMSG; p -> i_msg.LPType = TYPEPT; p -> i_msg.LocalPort = SysLocalPort; p -> i_msg.RPType = TYPEPT; p -> i_msg.ID = 1; p -> i_msg.Type = TYPEBYTE; p -> i_msg.DataPtr = (caddr_t) p -> i_data; /* If reading, create a port for others to write to */ if (*mode == 'r') { p -> i_msg.RemotePort = ipcallocateport (0); if (p -> i_msg.RemotePort == NULLPORT) { p -> i_name[0] = 0; return 0; } if (!ipcassertname (name, p -> i_msg.RemotePort)) { ipcdeallocateport (p -> i_msg.RemotePort); p -> i_name[0] = 0; return 0; } return p; } p -> i_msg.RemotePort = ipclocate (p -> i_name); return p; } X/* Close a port. force says if data is pending, close anyway. */ iclose (p, force) register IPCIO *p; int force; { if (p -> i_msg.RemotePort == NULLPORT) { ifree (p); return; } isend (p, 0); /* Flush output if possible */ p -> i_closed = 1; /* Mark as closed */ if (p -> i_cnt == 0 || force) ifree (p); /* MACRO (see ipc.h) */ } X/* This routine really closes the IPC port. This won't happen unless someone forces it to, or if iclose is called and there's no pending data, or if the pending data gets flushed. */ ifree (p) register IPCIO *p; { ipcdeallocateport (p -> i_msg.RemotePort); p -> i_msg.RemotePort = NULLPORT; p -> i_name[0] = 0; } X/* Write data from buf (n bytes) to ipc port p. n<0 means write -n bytes but do not flush. */ iwrite (p, buf, n) register IPCIO *p; char *buf; register n; { int noflush = n < 0; if (n < 0) n = -n; if (n == 0) return 0; if (p -> i_mode == 0) /* Its a read port, not a write port */ return -1; if (p -> i_closed) /* Its closed, dont write */ return -1; if (p -> i_cnt + n > IPCD)/* See if its near-full */ n = IPCD - p -> i_cnt; if (n <= 0) return -1; bcopy (buf, &p -> i_data[p -> i_cnt], n); p -> i_cnt += n; if (p -> i_msg.RemotePort == NULLPORT) itryopen (p); if (!noflush && !p -> i_blocked && p -> i_msg.RemotePort != NULLPORT) return isend (p, 0, Option) ? 0 : n; return 0; } X/* Actually send the pending data on port p. Won't try if p->i_blocked is true and t == 0. */ isend (p, t) register IPCIO *p; int t; { if (p -> i_mode == 0) /* Its an incoming port (!) */ return -1; if (p -> i_cnt == 0 || p -> i_msg.RemotePort == NULLPORT) return -1; /* Cant send, no data */ if (p -> i_blocked && t == 0) return -1; /* Cant send, blocked */ p -> i_msg.NumElts = p -> i_cnt; t = ipcsend (&p -> i_msg, t, Option); if (t == 0) p -> i_blocked = 1; else if (t == -1) /* Didnt get sent */ return -1; p -> i_cnt = 0; if (p -> i_closed) /* Done with this port */ ifree (p); return 0; } X/* Try to open port p if it isn't already. */ itryopen (p) register IPCIO *p; { if (p -> i_msg.RemotePort == NULLPORT) {/* Try to open it */ p -> i_msg.RemotePort = ipclocate (p -> i_name); if (p -> i_msg.RemotePort == NULLPORT) return -1;/* Didnt get it */ } return 0; } X/* Read a message from port(s) */ iread (ports, buf, n) struct SetOfPorts *ports; char *buf; register n; { struct Msg msgin; register localport p; register IPCIO *i; rec: msgin.Type = TYPEBYTE; msgin.NumElts = n; msgin.DataPtr = (caddr_t) buf; if (ipcreceive (ports, &msgin, 0) != true) return -1; if (msgin.LocalPort == DATAPORT) { p = * (localport *) buf; if (p == DATAPORT) goto rec; /* ignore */ for (i = _ports; i < &_ports[NPORTS]; i++) if (i -> i_msg.RemotePort == p) break; if (i >= &_ports[NPORTS]) { error (0, "?? p %d (msgid = %d)\r\n", p, msgin.ID); goto rec; } if (msgin.ID == PORTDELETED) i -> i_deleted = 1; else if (msgin.ID == MSGACCEPTED) { i -> i_blocked = 0; isend (i, 0); } goto rec; } return msgin.NumElts; } X/* This is called after "the" system local port is obtained */ X/* SetSysPort (p): CHANGED TO MACRO */ X/* Called to fix the localport value for the system port */ X/* FixSysPort (p): CHANGED TO MACRO */ //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 ipcio.c /bin/echo -n ' '; /bin/ls -ld ipcio.c fi /bin/echo 'Extracting keys.c' sed 's/^X//' <<'//go.sysin dd *' >keys.c #ifndef lint static char rcsid[] = "@(#)keys.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" #include <signal.h> static ChildPID; StartKeyboardServer () { int pid = fork (); if (pid == -1) error (1, "Can't fork!?\r\n"); if (pid) { ChildPID = pid; return; } if (KInit ()) { kill (getppid (), SIGINT); exit (1); } Keys (); printf("done with Keys() (shouldn't happen)\r\n"); kill (getppid (), SIGINT); exit (1); } Exit (e) { if (ChildPID) kill (ChildPID, SIGKILL); exit (e); } static KInit () { sigset (SIGTSTP, SIG_DFL); sigset (SIGINT, SIG_DFL); ioption (WAIT); iinvalid (); return ph_findmyself (); } static Keys () { static char cbuf[100]; register n; for (;;) { n = read (0, cbuf, sizeof cbuf - 1); if (n <= 0) { ph_write (Myself, "\4"); sleep (20); /* Give parent time to kill me */ return; } cbuf[n] = 0; ph_write (Myself, cbuf); } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 keys.c /bin/echo -n ' '; /bin/ls -ld keys.c fi /bin/echo 'Extracting ngets.c' sed 's/^X//' <<'//go.sysin dd *' >ngets.c #ifndef lint static char rcsid[] = "@(#)ngets.c U of Maryland ACT 31-Mar-1983"; #endif #include <sgtty.h> #include "phone.h" #define BK '\\' #define BKSL 01 #define LNXT 02 extern struct sgttyb WOld; X/* * ngets (s, n, f) * * 27 March 1983 ACT * * Get at most n characters into buffer s from stdin, echoing via (*f)() */ char *ngets (s, n, f, sendctlx) char *s; /* Space get into */ register n; /* Max bytes */ register (*f)(); /* Echo function */ int sendctlx; /* True iff ^X should just "echo" ^X */ { register c; register char *cp = s; int md = 0; for (;;) { c = GetChar (); if (c < 0) { *cp = 0; return s; } /* Check for literal-next */ if (md & LNXT) { md &= ~LNXT; goto literal; } /* Check for specials. ^L: redraw screen */ if (c == CTRL('L')) { ScreenGarbaged++;/* does nothing in printer mode */ continue; } /* ^M, ^J: enter the line */ if (c == '\r' || c == '\n') { *cp = 0; (*f) ('\n'); return s; } /* ^V: Literal next */ if (c == CTRL('V')) { if (n < 2) continue; md |= LNXT; md &= ~BKSL; continue; } /* ^W: Word erase */ if (c == CTRL('W')) { if (cp == s) continue; if (*--cp == ' ' || *cp == '\t') { while (cp >= s && *cp == ' ') { back (*cp, f); n++; cp--; } } while (cp >= s && *cp != ' ') { back (*cp, f); n++; cp--; } cp++; continue; } /* ^R: Retype */ if (c == CTRL('R')) { (*f) ('^'); (*f) ('R'); (*f) ('\n'); c = cp - s; cp = s; while (--c >= 0) echo (*cp++, f); continue; } /* ^Z: Suspend */ if (c == CTRL('Z')) { Suspend (); continue; } /* Check for backspace or line kill, but only if didn't just type a backslash (backslash lnexts them) */ if ((md & BKSL) == 0) { if (c == WOld.sg_erase) { if (cp > s) { back (*--cp, f); n++; } continue; } else if (c == WOld.sg_kill) { n += cp - s; if (sendctlx) (*f) (CTRL ('X')); else while (cp > s) back (*--cp, f); cp = s; continue; } } else if (c == WOld.sg_erase || c == WOld.sg_kill) { md &= ~BKSL; (*f) ('\b'); --cp; goto put; /* lnext it */ } if (n < 2) continue; if (c == BK) md |= BKSL; else md &= ~BKSL; literal: n--; put: echo (c, f); *cp++ = c; } } X/* Echo a character, converting controls to ^ escapes */ static echo (c, f) register c; register (*f)(); { if (c >= ' ' && c < 0177) (*f) (c); else { (*f) ('^'); (*f) (c == 0177 ? '?' : c + '@'); } } X/* Called to un-type one character */ static back (c, f) register c; register (*f)(); { if (c < ' ' || c == 0177) f ('\b'); f ('\b'); } X/* * puts_more (s, w) * * 31 March 1983 ACT * * Put string 's' into window 'w' with 'more' mode. */ static struct q { char *q_s; /* queued string */ struct q *q_n; /* next in queue */ } *putsh, *putst; puts_more (s, w) register char *s; register Win *w; { register winlines = w -> IYE; register char *cp; int dofree = 0; char c; Win *oldcur; static nest; if (nest) { if (putsh) putst = putst -> q_n = (struct q *) alloc (sizeof (struct q)); else putsh = putst = (struct q *) alloc (sizeof (struct q)); putst -> q_s = strsave (s); putst -> q_n = 0; return; } nest++; oldcur = CurWin; CurWin = w; Woncursor (w, 0); top: cp = s; while (*cp) { Wputc (*cp, w); if (*cp++ == '\n') lineno++; if (lineno == winlines - 1 && (*cp || putsh)) { Wsetmode (w, WINVERSE); Wputs ("--More--[space to continue, DEL to abort]\r", w); Wsetmode (w, 0); c = GetChar (); Wclearline (w, 0); if (c == 0177 || c == 'q') break; lineno = 0; } } if (dofree) free (s); if (putsh) { s = putsh -> q_s; dofree++; free (putsh); putsh = putsh -> q_n; goto top; } CurWin = oldcur; Woncursor (w, 1); nest = 0; } X/* malloc, but complains if fails */ char *alloc (n) unsigned n; { char *s = malloc (n); if (s) return s; else error (1, "malloc failed (shouldn't happen)\r\n"); } X/* save a string */ char *strsave (s) register char *s; { strcpy (alloc (strlen (s) + 1), s); return s; } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 ngets.c /bin/echo -n ' '; /bin/ls -ld ngets.c fi /bin/echo 'Extracting phio.c' sed 's/^X//' <<'//go.sysin dd *' >phio.c #ifndef lint static char rcsid[] = "@(#)phio.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" X/* * NEWLOG is not in use. I don't think I need it. */ static DisableLog; /* If true ph_log() will do nothing */ ph_makemyself () { register struct connection *c = conlist; char *user, *tty; if ((user = (char *) getlogin ()) == 0) error (1, "Who are you?\r\n"); if ((tty = (char *) ttyname (0)) == 0) error (1, "Where are you?\r\n"); if (strncmp (tty, "/dev/", 5) == 0) tty += 5; else if (strncmp (tty, "/tmp/dev_", 9) == 0) tty += 9; else { register char *c = tty; while (*c) if (*c++ == '/') c[-1] = '.'; } strcpy (c -> con_user, user); strcpy (c -> con_tty, tty); strcpy (c -> con_id, concat (user, ":", tty, 0)); sprintf (LogFile, "/tmp/ph%s", c -> con_id); c -> con_ipc = iopen (concat (user, ":", tty, 0), "r"); if (c -> con_ipc == 0) error (1, "Failed to open IPC port\r\n"); SetSysPort (c -> con_ipc -> i_msg.RemotePort);/* ycch */ FixSysPort (c -> con_ipc);/* ycch */ c -> con_state = con_t; nconn++; DAlloc (c); } X/* Find Myself port */ ph_findmyself () { register struct connection *c; for (c = &conlist[1]; c < &conlist[MAXCONN]; c++) c -> con_user[0] = 0; Myself -> con_ipc = iopen (MyID, "w"); return ISOPEN (Myself -> con_ipc) ? 0 : -1; } X/* Connect to user "user" on tty "tty" */ ph_conn (user, tty) char *user, *tty; { register struct connection *c; for (c = conlist; c < &conlist[MAXCONN]; c++) if (c -> con_user[0] == 0) break; if (c >= &conlist[MAXCONN]) error (1, "No slot for connection!\r\n"); strcpy (c -> con_user, user); strcpy (c -> con_tty, tty); strcpy (c -> con_id, concat (user, ":", tty, 0)); c -> con_ipc = iopen (c -> con_id, "w"); c -> con_state = con_c; if (ISOPEN (c -> con_ipc)) { c -> con_state = con_t;/* Force ph_write to send */ ph_write (c, "\3");/* We know this wont block */ c -> con_state = con_c; } else if (tell (user, tty, "\r\n%s:%s is calling you. Type \"phone\" to answer.\r\n\7", Me, MyTty)) { c -> con_user[0] = 0; return -1; } nconn++; ph_log (); /* Update the log file */ DAlloc (c); /* Allocate a display */ return 0; } X/* Write string s to connection c */ ph_write (c, s) register struct connection *c; register char *s; { register IPCIO *i = c -> con_ipc; static beenhere, mlen; static char m[100]; if (!beenhere) { beenhere++; /* Use ~ to separate user:tty from text */ sprintf (m, "%s~", MyID); mlen = -strlen (m); } if (i -> i_cnt == 0) iwrite (i, m, mlen); /* Do NOT attempt to flush when writing to a waiting-for conn.! */ if (c -> con_state == con_t) iwrite (i, s, strlen (s)); else iwrite (i, s, -strlen (s)); } X/* Disconnect connection c */ ph_disconn (c) register struct connection *c; { if (c -> con_state == con_c /* && !ISOPEN (c -> con_ipc) */) { /* Have to send him a message */ tell (c -> con_user, c -> con_tty, "%s:%s hung up at %s\r\n", Me, MyTty, tyme ()); iclose (c -> con_ipc, 1); } else { ph_write (c, "\4");/* Write a ^D to him */ isend (c -> con_ipc, 40); iclose (c -> con_ipc, 0); } c -> con_user[0] = 0; nconn--; if (!DisableLog) ph_log (); /* Update the log file */ DFree (c); /* Free the display for conn c */ } X/* NOTE: SECRET +1s ARE TO SKIP Myself AS A CONNECTION */ X/* Disconnect all connections */ ph_disall () { register struct connection *c; unlink (LogFile); DisableLog++; for (c = &conlist[1]; c < &conlist[MAXCONN]; c++) if (c -> con_user[0]) ph_disconn (c); } X/* Returns conn. if user u on tty t is connected (or being waited for) */ struct connection * ph_iscon (u, t) register char *u, *t; { register struct connection *c; for (c = &conlist[1]; c < &conlist[MAXCONN]; c++) if (strcmp (c -> con_user, u) == 0 && strcmp (c -> con_tty, t) == 0) return c; return 0; } X/* (Re)write the log file of who's connected */ ph_log () { register struct connection *c; register FILE *fp; unlink (LogFile); if (DisableLog) return; fp = fopen (LogFile, "w"); if (fp == 0) error (1, "Can't open phone log file '%s'\r\n", LogFile); chmod (LogFile, 0644); for (c = conlist; c < &conlist[MAXCONN]; c++) { if (c -> con_user[0]) #ifdef NEWLOG fprintf (fp, "%s %c\n", c -> con_id, c -> con_state == con_t ? 't' : 'c'); #else not NEWLOG fprintf (fp, "%s\n", c -> con_id); #endif NEWLOG } fclose (fp); } X/* * Get a character from keyboard port. Call ChatChar for characters * from other ports. In any case, connection is left in From (0 == * new caller) and name in Caller. As a special hack, when ChatChar * is called, before reading more, a 0 will be returned if there are * < 2 connections. This is so that Chat() can exit when there's no * one left to talk to. */ GetChar () { register char *p; register struct connection *c; int others = 0; static char buf[IPCD + 1], *cp; static n; extern enum Disp_Modes DisplayMode; top: while (n) { n--; *cp &= 0177; if (From != Myself) { ChatChar (*cp++); others++; } else { lineno = 0; return *cp++; } } get: do { if (others && nconn < 2) return 0; if (DisplayMode == Disp_Windows) { WRCurRow = CurWin -> OYO + CurWin -> IYO + CurWin -> w_cursor.row; WRCurCol = CurWin -> OXO + CurWin -> IXO + CurWin -> w_cursor.col; Wrefresh (0); } n = iread ((struct SetOfPorts *) -1, buf, sizeof buf - 1); } while (n == 0); if (n < 0) { error (0, "ipcreceive returned false (shouldn't happen)\r\n"); goto get; } buf[n] = 0; /* Find ~ that marks text, and figure out who it came from */ for (p = buf; *p && *p != '~'; p++) ; n -= p - buf + 1; if (*p) { *p++ = 0; cp = p; From = 0; Caller = buf; for (c = conlist; c < &conlist[MAXCONN]; c++) { if (c->con_user[0] && strcmp (c->con_id, buf) == 0) { From = c; break; } } goto top; } /* This shouldnt happen */ error (0, "bad format data '%s' (shouldn't happen)\r\n", buf); goto get; } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 phio.c /bin/echo -n ' '; /bin/ls -ld phio.c fi /bin/echo 'Extracting phone.c' sed 's/^X//' <<'//go.sysin dd *' >phone.c #ifndef lint static char rcsid[] = "@(#)phone.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" #include <sys/ioctl.h> X/* * Phone * version 2.2 * * Chris Torek @ umaryland * From phone by Jim Rees @ uw-beaver * * Usage: phone [-a] [-c|-p|-n] [-s#] [user ...] * where * -a => answer all incoming calls (default if no user specified) * -c => CRT mode: don't use windows, but do assume that backspace works * -p => printer mode: don't use windows, # for \b, " XXX" for line kill * -n => no local echo, crt mode * -s# => set scroll step to <number> -- like $scroll # command * user => call user (user is of form "loginname[:ttyname]") */ int DisallowSuspends; main (argc, argv) register argc; register char **argv; { register i; int nusers = 0, answerall = 0, crtmode = 0, printmode = 0, nmode = 0; ScrollStep = 1; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') switch (argv[i][1]) { case 'a': answerall++; break; case 'c': if (printmode || nmode) error (0, "Use only one of (-p, -c, -n)\r\n"); else crtmode++; break; case 'p': if (crtmode || nmode) error (0, "Use only one of (-p, -c, -n)\r\n"); else printmode++; break; case 'n': if (crtmode || printmode) error (0, "Use only one of (-p, -c, -n)\r\n"); else nmode++; break; case 's': ScrollStep = atoi (argv[i] + 2); if (ScrollStep < 0) ScrollStep = 0; break; default: error (0, "Unrecognized option -%c\r\n", argv[i][1]); break; } else nusers++; } if (nusers == 0) answerall++; if (ioctl (0, TIOCGETD, &DisallowSuspends) == 0 && DisallowSuspends == OTTYDISC) DisallowSuspends = 1; else DisallowSuspends = 0; StartDisplay (crtmode ? 1 : printmode ? 2 : nmode ? 3 : 0); ph_makemyself (); if (answerall) AnswerAllCalls (); if (nusers) { for (i = 1; i < argc; i++) if (argv[i][0] != '-') PlaceCall (argv[i], 0, 0); } if (nconn == 1) { if (nusers == 0) error (0, "No one is calling you.\r\n"); CleanUpDisplay (1); exit (1); } StartKeyboardServer (); Chat (); ph_disall (); /* Disconnect everyone */ CleanUpDisplay (0); Exit (0); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 phone.c /bin/echo -n ' '; /bin/ls -ld phone.c fi /bin/echo 'Extracting u.c' sed 's/^X//' <<'//go.sysin dd *' >u.c #ifndef lint static char rcsid[] = "@(#)u.c U of Maryland ACT 31-Mar-1983"; #endif #include "phone.h" #include <utmp.h> #include <time.h> struct utmp u; #define NMAX (sizeof u.ut_name) #define LMAX (sizeof u.ut_line) X/* tell gives the specified person a message. */ tell (user, tty, fmt, args) register char *user, *tty; char *fmt; int args; { register FILE *f; if (!IsOn (user, tty)) return -1; /* Can't tell, not on spec'd tty */ f = fopen (concat ("/dev/", tty, 0), "w"); if (f == NULL) return 1; /* Cant tell, user is not accepting */ _doprnt (fmt, &args, f);/* Ick */ fclose (f); return 0; } X/* Return the time as a string: nn:nn {am,pm} */ char * tyme () { static char tymbuf[12]; long now; register struct tm *lt; struct tm *localtime(); (void) time (&now); lt = localtime (&now); sprintf (tymbuf, "%d:%02d %s", lt -> tm_hour == 0 ? 12 : lt -> tm_hour > 12 ? lt -> tm_hour - 12 : lt -> tm_hour, lt -> tm_min, lt -> tm_hour >= 12 ? "pm" : "am"); return tymbuf; } X/* Returns the concatenation of its arglist */ char * concat (s1) char *s1; { static char buf[1024]; register char **sp = &s1, *s, *d = buf; while (s = *sp++) { while (*d++ = *s++) ; d--; } return buf; } static FILE *ufp; RewindU () { if (!ufp) { ufp = fopen ("/etc/utmp", "r"); if (!ufp) error (1, "Can't open /etc/utmp\r\n"); return; } rewind (ufp); } char *NextU (who) char *who; { static char tt[LMAX+1]; while (fread (&u, sizeof u, 1, ufp) == 1) { if (u.ut_line[0] && u.ut_name[0] && (who == 0 || strncmp (who, u.ut_name, NMAX) == 0)) { strncpy (tt, u.ut_line, LMAX); return tt; } } return 0; } IsOn (who, t) char *who, *t; { long pos; pos = ufp ? ftell (ufp) : 0L; RewindU (); while (fread (&u, sizeof u, 1, ufp) == 1) { if (u.ut_line[0] && u.ut_name[0] && strncmp (who, u.ut_name, NMAX) == 0 && strncmp (t, u.ut_line, LMAX) == 0) { fseek (ufp, pos, 0); return 1; } } fseek (ufp, pos, 0); return 0; } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 u.c /bin/echo -n ' '; /bin/ls -ld u.c fi
jdi@psuvax.UUCP (10/27/83)
Ok, now that we have the program, just what is 'libipc.a'??? I mean I realize it must be inter-process control, and it sounds interesting, but I sure don't have it. Can it be sent via sources? -- John Irwin Pennsylvania State University {akgua, allegra, ihnp4, burdvax}!psuvax!jdi (814) 238-7556