[unix-pc.sources] windowing programs

andy@cit-vax.Caltech.Edu (Andy Fyfe) (12/08/88)

I have set up my own windowing environment.  I use the window manager
posted by Scott Mueller though in a much reduced form to handle switching
between windows.  And I wrote a program "window" to create new windows,
as well as a program "dial" to allow me to dial out to other computers
without using the phone manager (which was always dying anyway).

The programs are quite minimal, but I find them useful.  One day when I
get time I'll revise them as needed.  In the mean time others might find
them useful, and perhaps suggest enhancements.

I have a 3b1 (I think -- it has a 40Mb drive and a 'higher' case, but it
does say 7300 on the bottom) running 3.5, just in case they've changed
something from earlier releases, or to later ones.

Andy Fyfe
            andy@csvax.caltech.edu
            wjafyfe@caltech.bitnet
            andy@cit-vax.UUCP	(...!ames!elroy!cit-vax!andy)

Of all the things I've lost I miss my mind the most.
-------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  ReadMe Makefile dial.c newmgr.c startmgr.c window.c
# Wrapped by andy@marmot on Wed Dec  7 23:45:43 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ReadMe' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ReadMe'\"
else
echo shar: Extracting \"'ReadMe'\" \(4054 characters\)
sed "s/^X//" >'ReadMe' <<'END_OF_FILE'
XReadMe for 7 Dec 1988 version of startmgr, newmgr, window and dial
X
Xstartmgr & newmgr
X-----------------
X
XThese are modified versions of the ones by Scott Mueller.  So I'm simply
Xediting the ReadMe he provided.
X
XThese two programs implement a replacement for smgr, wmgr and ph.  They don't
Xdo the cron stuff; use cron for that.  They don't do mail, and I don't care,
Xbecause I don't use /bin/mail anyway.  Likewise, they know nothing about
Xcalendars or memos.  Additionally, the wmgr function doesn't bring up a menu;
Xit just switches you between windows.
X
XWhat startmgr does is start newmgr.  It starts it in a window across the
Xtop of the screen, in the places where the Phone Manager, the Status Manager
Xand the Window Manager live (I don't use the PM either...).  It then returns,
Xso all you need to do is startmgr.  It probably can be turned into a fairly
Xgeneral process starter, but I don't have the time, what with all of the
Xstuff that I've got piled up to look at.
X
XNewmgr is just a forever loop that displays some status information to
Xstdout and peeks at the Suspend, Resume and Shift-Print.  (It may get
Xothers but these are the only ones it acts on.)  Suspend and Resume 
Xswitch the active window (suspend increases the window number and resume
Xdecreases it.)  Shift-Print spawns a login window (see window below).
X
XCompiling:  just type make.  Use make install to move the executables to the
Xdefault locations.  Modify the Makefile and the #define BARPROG for your site.
X
XHow to use:  startmgr goes in /etc/daemons and newmgr goes into your
Xlocal executable directory (change a #define in startmgr if you don't
Xuse /usr/local).  You've then either got to change /etc/rc (so it doesn't
Xstart smgr,wmgr and ph) or put a script in /etc/daemons to kill them off.
XIf you don't know about /etc/rc and /etc/daemons, maybe you shouldn't
Xbe doing this... :-)
X
XOptionally, you *can* run startmgr by hand; I did for testing.
X
XKnown bugs:  There are probably some.  There is the odd time that it
Xdisplays itself as the current window, but nothing too serious seems to
Xbe wrong with it.
X
XLast note:  this stuff is copyrighted with free redistribution permitted.
XI don't really think that this is the sort of thing that needs or warrants
Xscads of support, so I'm not promising to do so; however, I would like to
Xsee any changes that anyone makes.
X
XI'd like to add some system manager stuff to this -- in particular I'd
Xlike it to monitor /dev/error.  When I get some time I'll probably rewrite
Xthe thing from scratch.
X
Xwindow
X------
X
XWindow creates a new window and runs a given program in it.  The standard
Xusage is
X	window path-name argv-0 ...
XPath-name is the name of the program to execute.  It need not be a full
Xpath as window will use $PATH to execute it.  Argv[0] must be provided,
Xand any arguments that follow become argv[1], etc.  Window makes utmp
Xentries so that the window will show up in things like "who".  And because
Xit does, it can run /bin/login.  This is done by simply typing
X	window
Xwhich will create a login window.  In this case it also makes wtmp enties.
XIn order to make utmp/wtmp entries and run /bin/login, window needs to be
Xsuid root.  If you don't care about those features, then it doesn't.
X
XWindow has no options for window size, font etc.  The created window
Xis 24 by 80.  The window name is the path name provided, and is displayed
Xby the window manager.
X
XWindow exits immediately so it need never be run in the background
Xexplicitly.  It leaves a copy of itself running, waiting for the new
Xwindow to die so that it can update the utmp/wtmp files as needed.
X
Xdial
X----
X
XThis program takes a telephone number as an argument, calls it and then
Xruns the async emulated provided with the UnixPC.  Using that emulator
Xwas my only use of the phone manager and with this I had no trouble dropping
Xit.  Now I just use the new window manager (with cron).  It needs to be
Xsuid or sgid in order to make a lock file in /usr/spool/uucp.  These days
XI use pcomm and don't even bother much with this (unless I really want
Xa vt100).
END_OF_FILE
if test 4054 -ne `wc -c <'ReadMe'`; then
    echo shar: \"'ReadMe'\" unpacked with wrong size!
fi
# end of 'ReadMe'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(831 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XLOCALDIR = /usr/local/bin
XCC = gcc
XLD = gcc
XCFLAGS = -O
XLDFLAGS = -s -shlib
X#CC = cc
X#LD = ld
X#CFLAGS = -O
X#LDFLAGS = -s /lib/shlib.ifile /lib/crt0s.o
X
Xall: newmgr startmgr window dial
X
Xstartmgr: startmgr.o
X	$(LD) $(LDFLAGS) startmgr.o -o startmgr
X
Xstartmgr.o: startmgr.c
X	$(CC) $(CFLAGS) -c -DBARPROG=\"$(LOCALDIR)/newmgr\" startmgr.c
X
Xnewmgr: newmgr.o
X	$(LD) $(LDFLAGS) newmgr.o -o newmgr
X
Xnewmgr.o: newmgr.c
X	$(CC) $(CFLAGS) -c -DWINDOW_PROGRAM=\"$(LOCALDIR)/window\" newmgr.c
X
Xwindow: window.o
X	$(LD) $(LDFLAGS) window.o -o window
X
Xdial: dial.o
X	$(LD) $(LDFLAGS) dial.o -o dial
X
Xinstall: newmgr startmgr
X	mv newmgr window dial $(LOCALDIR)
X	mv startmgr /etc/daemons
X	chown root $(LOCALDIR)/window
X	chmod 4755 $(LOCALDIR)/window
X	chgrp bin  $(LOCALDIR)/dial
X	chmod 2755 $(LOCALDIR)/dial
X
Xclean:
X	rm -f *.o startmgr newmgr window
END_OF_FILE
if test 831 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'dial.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dial.c'\"
else
echo shar: Extracting \"'dial.c'\" \(3099 characters\)
sed "s/^X//" >'dial.c' <<'END_OF_FILE'
X/*
X *     dial phone-number
X *
X * Dial the given phone number and then call the async emulator
X *
X * This software is Copyright (c) 1988 by Andy Fyfe.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * (Copyright notice courtesy of News 2.11 :-)
X */
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <dial.h>
X
X#define PROFILE 	"/usr/lib/ua/1200bps:Am"
X#define PHONE_LINE	"ph1"
X#define PHONE_PATH	"/dev/ph1"
X#define ASYNC_PROGRAM	"/usr/bin/async_main"
X#define ASYNC_NAME	"async_main"
X
Xstatic char *errs[] = { "zero", "interrupt", "dialer hung",
X			"no answer", "illegal baud-rate", "acu problem",
X			"line problem", "can't open LDEVS",
X			"device not available", "device unknown",
X			"no device available at baud-rate",
X			"no device known at baud-rate" };
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    CALL call;
X    struct termio term;
X    int fd, pid, status;
X    char fd_str[10], pid_str[10], win_dev[20];
X
X    if (argc != 2) {
X	fprintf(stderr, "%s: Usage: %s <telno>\n", argv[0], argv[0]);
X	exit(1);
X    }
X    fd = open(PHONE_PATH, O_RDWR | O_NDELAY);
X    if (fd < 0) {
X	fprintf(stderr, "%s: Can't open %s: ", argv[0], PHONE_PATH);
X	perror("");
X	exit(1);
X    }
X    ioctl(fd, TCGETA, &term);
X    close(fd);
X    term.c_iflag = IXOFF | IXON | ISTRIP | IGNPAR;
X    term.c_oflag = 0;
X    term.c_cflag = HUPCL | CREAD | CS8 | B1200;
X    term.c_lflag = 0;
X    call.attr = &term;
X    call.baud = 1200;
X    call.speed = 1200;
X    call.line = PHONE_LINE;
X    call.telno = argv[1];
X    call.modem = 0;
X
X    fd = -1;
X    fd = dial(call);
X    if (fd < 0) {
X	fprintf(stderr, "%s: dial failed: %s\n", argv[0], errs[-fd]);
X	exit(2);
X    }
X
X    pid = fork();
X    if (pid < 0) {
X	fprintf(stderr, "%s: can't fork: ", argv[0]);
X	perror("");
X	exit(3);
X    }
X    if (pid == 0) {	/* child */
X	setpgrp();
X	sprintf(fd_str, "%d", fd);
X	sprintf(pid_str, "%d", getpid());
X	close(0);
X	if (open("/dev/window", 2) != 0) {
X	    fprintf(stderr, "%s: Can't open /dev/window: ", argv[0]);
X	    perror("");
X	    exit(1);
X	}
X	close(1);
X	dup(0);
X	close(2);
X	dup(0);
X	setuid(getuid());
X	setgid(getgid());
X	execl(ASYNC_PROGRAM, ASYNC_NAME, "r", PROFILE, "f",
X	    PHONE_PATH, "i", fd_str, "p", pid_str, (char *)0);
X	fprintf(stderr, "%s: execl failed: ", argv[0]);
X	exit(1);
X    }
X    else {		/* parent */
X	while (wait(&status) != pid)
X	    ;
X	undial(fd);
X	if (status & 0xff00)
X	    fprintf(stderr, "%s: %s: exit status %d\n",
X		argv[0], ASYNC_NAME, (status >> 8) & 0xff);
X	else if (status & 0xff)
X	    fprintf(stderr, "%s: %s: signal %d%s\n",
X		argv[0], ASYNC_NAME, status & 0x7f,
X		(status & 0x80) ? " (core dumped)" : "");
X    }
X    exit(0);
X}
END_OF_FILE
if test 3099 -ne `wc -c <'dial.c'`; then
    echo shar: \"'dial.c'\" unpacked with wrong size!
fi
# end of 'dial.c'
fi
if test -f 'newmgr.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newmgr.c'\"
else
echo shar: Extracting \"'newmgr.c'\" \(11889 characters\)
sed "s/^X//" >'newmgr.c' <<'END_OF_FILE'
X/*	newmgr:  Loop forever, printing a status display and checking the
X	Suspend, Resume and shifted Function Keys.  Meant to be used on the
X	top line of a Unix-PC (7300/3B1) screen that has had the Phone Manager,
X	Status Manager and Window Manager disabled.  What it shows:
X
X	- Phone line states
X		o Idle - completely free line
X		o Ansr - computer answering line (getty job)
X		o Mach - incoming machine
X		o User - incoming user
X		o Call - any outbound
X	- Current date and time
X	- Uptime
X	- Active window (device and name)
X
X	When Suspend is hit, it makes the next window current; Resume
X	makes the last window current.
X
X	This is started by startmgr (which should be included in any
X	distribution) so that the fork/exec/open code doesn't have to be
X	carried around with this program.
X
X	The process table stuff is heavily inspired by the 'fuser.c' program
X	posted to the Usenet by Michael 'Ford' Ditto.
X
X	This software is Copyright (c) 1987 by Scott Hazen Mueller.
X 
X	Permission is hereby granted to copy, reproduce, redistribute or
X	otherwise use this software as long as: there is no monetary
X	profit gained specifically from the use or reproduction or this
X	software, it is not sold, rented, traded or otherwise marketed, and
X	this copyright notice is included prominently in any copy
X	made.
X
X	The author make no claims as to the fitness or correctness of
X	this software for any use whatsoever, and it is provided as is. 
X	Any use of this software is at the user's own risk.
X
X	(Copyright notice courtesy of News 2.11 :-)
X
X	Additionally:  you break it, you bought it.  I've listed the problems
X	that I know of in comments in the code; if you come up with fixes, I'd
X	like to see them, but I'm not planning on supporting anything.  It's
X	"good enough"; that's all that I'm looking for.	*/
X
X#include	<stdio.h>
X#include	<sys/types.h>
X#include	<sys/phone.h>
X#include	<sys/window.h>
X#include	<sys/signal.h>
X#include	<sys/filsys.h>
X#include	<sys/proc.h>
X#include	<sys/user.h>
X#include	<sys/syslocal.h>
X#include	<sys/tune.h>
X#include	<sys/stat.h>
X#include	<fcntl.h>
X#include	<time.h>
X#include	<utmp.h>
X#include	<nlist.h>
X#include	<pwd.h>
X
X#ifndef WINDOW_PROGRAM
X#define WINDOW_PROGRAM	"/usr/local/bin/window"
X#endif
X
X#define	PHONE1		"ph0"
X#define	PHONE2		"ph1"
X#define	IDLE		0
X#define	ANSR		1
X#define	MACH		2
X#define	USER		3
X#define	CALL		4
X#define LOCKDIR		"/usr/spool/uucp/"
X#define LOCKPREFIX	"LCK.."
X
X#define	BARFORM		"%s1 %s2       %s, up %s       w%d %.20s"
X#define	DATETIMEFORM	"%.2d-%s-%.2d %.2d:%.2d"
X#define	UPTIMEFORM	"%d day%s, %.2d:%.2d"
X
X#define	MACHLEN		15
X#define	MAXPATHLEN	255
X#define	TRUE		(1)
X#define	FALSE		(0)
X#define	TICK		15
X#define	WHOSLEEP	15
X#define	NOWHERE		0
X#define	FORWARD		1
X#define	BACKWARD	2
X#define	HERE		3
X#define	MINWIN		1
X#define	MAXWIN		12
X
Xchar	*phstat[5] = { "Idle", "Ansr", "Mach", "User", "Call" };
X
Xmain()
X{
X    int		window_num, fd;
X    char	datetime[26], uptime[26], *fmtdatetime(), *fmtuptime();
X    char	line[100], window_name[20];
X    struct	utmp *ut, *getutent();
X    long	temp, boottime;
X
X    /* Ignore keyboard interrupt signals. */
X
X    signal(SIGINT, SIG_IGN);
X    signal(SIGQUIT, SIG_IGN);
X    signal(SIGUSR2, SIG_IGN);
X
X    fd = open("/dev/window", O_RDONLY);
X    window_num = ioctl(fd, WIOCGPREV, (char *)0);
X    close(fd);
X    if (window_num >= 0) {
X	sprintf(line, "/dev/w%d", window_num);
X	fd = open(line, O_RDONLY);
X	if (fd >= 0)
X	    ioctl(fd, WIOCSELECT, (char *)0);
X	close(fd);
X    }
X
X    /* Open up the utmp file and find the boot time; save for display. */
X
X    while ((ut = getutent()) != NULL)
X	if ((int)ut->ut_type == BOOT_TIME) {
X	    boottime = ut->ut_time;
X	    break;
X	}
X    endutent();
X
X    for (;;) {
X	/* Figure out the current time.  Temp is needed 'cause localtime wants
X	   an address. */
X
X	temp = time((long *)0);
X	fmtdatetime(localtime(&temp), datetime);
X	fmtuptime(temp-boottime, uptime);
X
X	/* Format and print.  Flush stdout to make it really get printed. */
X
X	window_num = curwininfo(window_name);
X	sprintf(line, BARFORM, phstat[phone_stat(PHONE1)],
X	    phstat[phone_stat(PHONE2)], datetime, uptime,
X	    window_num, window_name);
X	strncat(line, "                                                    ",
X	    sizeof(line)-strlen(line));
X	printf("%.79s\r", line);
X	fflush(stdout);
X
X	/* Take care of the window management stuff; also do some sleeping down
X	   there. */
X
X	wcheckz();
X    }
X}
X
Xstatic char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X			 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
X
Xchar *
Xfmtdatetime(tm, buf)
X    struct tm *tm;
X    char *buf;
X{
X    sprintf(buf, DATETIMEFORM, tm->tm_year, month[tm->tm_mon], tm->tm_mday,
X	tm->tm_hour, tm->tm_min);
X    return(buf);
X}
X
Xchar *
Xfmtuptime(time, buf)
X    long time;
X    char *buf;
X{
X    int days, hours, mins;
X
X    time = time / 60;	/* put to minutes */;
X    mins = time % 60;
X    time = time / 60;
X    hours = time % 24;
X    days = time / 24;
X
X    sprintf(buf, UPTIMEFORM, days, (days != 1) ? "s" : "", hours, mins);
X    return(buf);
X}
X
X/*	Figure out the phone line status.  There are five possible states:
X		Idle - completely free line
X		Ansr - getty
X		Mach - incoming machine
X		User - incoming user
X		Call - outbound
X
X	These five states are defined roughly as follows:
X
X		Ansr - utmp -> LOGIN_PROCESS.
X		Mach - utmp -> USER_PROCESS; E LCK.
X		User - utmp -> USER_PROCESS; ~E LCK.
X		Call - utmp -> nothing; E LCK.
X		Idle - utmp -> nothing; ~E LCK.
X
X	Basically, we decide what we have by a process of elimination.
X
X	Known bugs:  This may have problems with HDB UUCP, since I've got no
X	way to test against it.  One thing that I am sure of is that since I
X	believe that uugetty creates a LCK..device file, this routine will
X	consistently mistake users logged in through uugetty for machines
X	logged into uucico.	*/
X
Xphone_stat(phone)
X    char	*phone;
X{
X    struct utmp	ut_try, *ut, *getutline();
X    short	utmptype;
X    char	machine[MACHLEN];
X
X    /*	Look for the phone line in the utmp file.  getutline() should return
X	only LOGIN_PROCESS, USER_PROCESS, or NULL (for failure).  We need to
X	save some values before the endutent() call because it clears out the
X	static structure pointed to by the return value of getutline().  We
X	save the ut_user field because uucico creates a machine LCK file using
X	the first six characters of this name.	*/
X
X    strcpy(ut_try.ut_line, phone);
X    ut = getutline(&ut_try);
X    utmptype = ut->ut_type;
X    strcpy(machine, ut->ut_user);
X
X    /*	AT&T UUCP specifies a maximum length of six characters.  Remove this
X	line for less brain-damaged implementations.	*/
X
X    machine[6] = '\0';
X    endutent();
X
X    /*	First condition:  was there a utmp entry?	*/
X
X    if (ut != NULL)
X
X    /*	There was.  If the type field is LOGIN_PROCESS, we have a getty and can
X	return the appropriate value and quit.	*/
X
X	if (utmptype == LOGIN_PROCESS)
X	    return(ANSR);
X	else
X
X    /*	A USER_PROCESS is incoming and can be either a user or a uucico.  An
X	incoming uucico has a LCK..machine file, so we check for that.	*/
X
X	    return(locked(machine) ? MACH : USER);
X    else
X
X    /*	There is no utmp entry.  There are only two possibilities here; if there
X	is an outgoing call of some sort, there will be a LCK..device file; if
X	not, the line is really idle.	*/
X
X	    return(locked(phone) ? CALL : IDLE);
X}
X
X/* See if a lock file exists. */
X
Xlocked(device)
X    char	*device;
X{
X    char	temp[MAXPATHLEN];
X    int		fd;
X
X    /* Make up the full path name. */
X
X    strcpy(temp, LOCKDIR);
X    strcat(temp, LOCKPREFIX);
X    strcat(temp, device);
X
X    /* Attempt to open the lock file.  Assume that if the open fails the lock
X    file does not exist. */
X
X    fd = open(temp, O_RDONLY);
X    if (fd == -1)
X	return(FALSE);
X    close(fd);
X    return(TRUE);
X}
X
X/*	Figure out the current window; we need a starting point, after all.
X	Known bug:  this sometimes returns the wrong value if it gets into a
X	race condition with a terminating/restarting (eg, getty) job.  It also
X	doesn't check to see if the ioctl fails.	*/
X
Xcurwininfo(buf)
Xchar *buf;
X{
X    int		foowin, temp;
X    char name[20];
X    struct utdata text;
X
X    foowin = open("/dev/window", O_RDONLY);
X    temp = ioctl(foowin, WIOCGCURR, (char *)0);
X    close(foowin);
X    if (temp < 0) {
X	strncpy(buf, "No window selected", 20);
X	return 0;
X    }
X    if (buf == (char *)0)
X	return temp;
X    sprintf(name, "/dev/w%d", temp);
X    foowin = open(name, O_RDONLY);
X    text.ut_num = WTXTUSER;
X    if (foowin < 0 || ioctl(foowin, WIOCGETTEXT, &text) < 0) {
X	close(foowin);
X	strncpy(buf, "Unknown", 20);
X	return temp;
X    }
X    close(foowin);
X    strncpy(buf, text.ut_text, 20);
X    return temp;
X}
X
Xwcheckz()
X
X/*	This is where the code to check for change-window keys lives.  Also,
X	sleep for the main program here.  It should return about every TICK
X	seconds.	*/
X
X{
X    int		tock, newwin, dir;
X
X    tock = 0;
X
X    do {
X	/* Check for a command key. */
X
X	if ((dir = checkey()) != NOWHERE) {
X	    /* Determine which window is next and select it. */
X
X	    if (dir != HERE) {
X		newwin = nexwin(curwininfo((char *)0), dir);
X		ioctl(newwin, WIOCSELECT);
X		close(newwin);
X	    }
X	    tock = TICK;	/* Kludge to force return and redisplay. */
X	}
X    }
X
X    /* Return after TICK tocks. */
X
X    while ( tock++ < TICK )
X	;
X}
X
X/* Stub routine to "handle" the alarm signal.  Doesn't do a lot. */
X
Xjustgoon()
X{
X    return;
X}
X
X/* Check and see if one of our keys has been poked.  The only keys this window
X   is supposed to respond to all generate three character escape sequences.
X   Set and catch an alarm signal to keep from getting hung in the fread();
X   this probably could be done more neatly some other way, but... */
X
Xcheckey()
X{
X    int		direction, i;
X    FILE	*fw;
X    char	key[3];
X
X    /* Set the alarm signal. */
X
X    signal(SIGALRM, justgoon);
X    alarm(1);
X
X    if (fread(key, 1, 3, stdin) == 3) {
X
X	/* Reset the alarm signal to be ignored.*/
X
X	signal( SIGALRM, SIG_IGN );
X	alarm( 0 );
X
X	/* Pick a function. */
X
X	switch (key[2]) {
X	    case 'p': 
X	    case 'P':
X		direction = FORWARD;
X		break;
X	    case 'q':
X	    case 'Q':
X		direction = BACKWARD;
X		break;
X	    case 'Z':
X		make_login();
X		direction = HERE;
X		break;
X	    default:
X		direction = NOWHERE;break;
X	}
X    }
X    else
X	direction = NOWHERE;
X
X    /* Reset the alarm signal to be ignored. */
X
X    signal(SIGALRM, SIG_IGN);
X    alarm(0);
X    return(direction);
X}
X
X/*	Decide what should be the next window.  This relies on the fact that
X	when you open a window device directly the open call will fail if it has
X	not already been opened by someone else; this is how we find the open
X	windows.  Invisible windows should have their user-text set to "Invisible"
X	if they wish to be ignored.	*/
X
Xnexwin(winno, dir)
Xint winno, dir;
X{
X    int		wd;
X    char	windex[12];
X    struct	utdata	wintext;
X
X    /*	Trivial loop; at worst, we'll wind up back where we started.  I suppose
X	it's possible to have a system with no windows except those marked as
X	"Invisible"; it doesn't seem too useful, though. */
X
X    while (1) {
X
X	/* Forward/backward sort-of modulo arithmetic.  Real modulo arithmetic
X	starts at zero. */
X
X	winno += ( dir == FORWARD ? 1 : -1 );
X	if ( winno > MAXWIN )
X	    winno = MINWIN;
X	else if ( winno < MINWIN )
X	    winno = MAXWIN;
X
X	/* Generate a window name and test for existence. */
X	sprintf(windex, "/dev/w%d", winno);
X	if ((wd = open(windex, O_RDONLY)) != -1) {
X
X	    /* It exists, now look at its user text info.  This is where
X		"Invisible" gets skipped. */
X
X	    wintext.ut_num = WTXTUSER;
X	    ioctl(wd, WIOCGETTEXT, &wintext);
X	    if (strcmp(wintext.ut_text, "Invisible"))
X		return(wd);
X	    else
X		close(wd);
X	}
X    }
X}
X
X/*	Run the window program to spawn a login window.	*/
X
Xmake_login()
X{
X    int pid;
X
X    pid = fork();
X    if (pid < 0)
X	return;
X
X    if (pid == 0) { /* child */
X	execl(WINDOW_PROGRAM, "window", (char *)0);
X	exit(1);
X    }
X    else { /* parent */
X	while (pid != wait((int *)0))
X	    ;
X    }
X    return;
X}
END_OF_FILE
if test 11889 -ne `wc -c <'newmgr.c'`; then
    echo shar: \"'newmgr.c'\" unpacked with wrong size!
fi
# end of 'newmgr.c'
fi
if test -f 'startmgr.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'startmgr.c'\"
else
echo shar: Extracting \"'startmgr.c'\" \(3930 characters\)
sed "s/^X//" >'startmgr.c' <<'END_OF_FILE'
X/*	startmgr:  Create a one-line window on the top line of the Unix-PC
X	(7300/3B1) screen and start a program running in that window.
X	Declare this window to be the Window Manager, which gives it control
X	over the Suspend, Resume and Print keys.  This is meant to be used to
X	start newmgr, but can probably be adapted to be a fairly generic program
X	starter.  It was designed to mimic the observed operation of things like
X	cron, smgr, etc.
X
X	This software is Copyright (c) 1987 by Scott Hazen Mueller.
X 
X	Permission is hereby granted to copy, reproduce, redistribute or
X	otherwise use this software as long as: there is no monetary
X	profit gained specifically from the use or reproduction or this
X	software, it is not sold, rented, traded or otherwise marketed, and
X	this copyright notice is included prominently in any copy
X	made.
X
X	The author make no claims as to the fitness or correctness of
X	this software for any use whatsoever, and it is provided as is. 
X	Any use of this software is at the user's own risk.
X
X	(Copyright notice courtesy of News 2.11 :-)
X
X	Additionally:  you break it, you bought it.  I've listed the problems
X	that I know of in comments in the code; if you come up with fixes, I'd
X	like to see them, but I'm not planning on supporting anything.  It's
X	"good enough"; that's all that I'm looking for.	*/
X
X#include	<stdio.h>
X#include	<sys/types.h>
X#include	<wind.h>
X#include	<sys/window.h>
X#include	<fcntl.h>
X#include	<termio.h>
X
X/*	Parameters defining the window and the program for that window.  You'll
X	almost certainly have to change barprog to correspond with whatever
X	directory you use for your local stuff.
X
X	Note:  WFLAGS was determined empirically, by looking at the flags for
X	the AT&T-supplied smgr window.  No guarantees here.	*/
X
X#ifndef BARPROG
X#define		BARPROG		"/usr/local/bin/newmgr"
X#endif
X#define		WXSTART		0
X#define		WYSTART		0
X#define		WWIDTH		720
X#define		WHEIGHT		12
X#define		WFLAGS		257
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
Xint	wd, dummy, pid, status;
Xstruct uwdata winbar;
Xstruct utdata winname;
Xstruct termio bartty;
X
Xwinbar.uw_x = WXSTART;
Xwinbar.uw_y = WYSTART;
Xwinbar.uw_width = WWIDTH;
Xwinbar.uw_height = WHEIGHT;
Xwinbar.uw_uflags = WFLAGS;
Xwinname.ut_num = WTXTUSER;
X
Xstrcpy( winname.ut_text, "Invisible" );
Xif ( pid = fork() ) {
X    if (argc == 1)
X	exit( 0 );
X    while (pid != wait(&status))
X	;
X    if (status == 0)
X	fprintf(stderr, "newmgr exited normally\n");
X    else if (status & 0xff != 0)
X	fprintf(stderr, "newmgr exited on signal %d%s\n", status & 0xff,
X	    (status & 0x80) ? " (core dumped)" : "");
X    else
X	fprintf(stderr, "newmgr exit status %d\n", (status >> 8) & 0xff);
X    exit(0);
X}
Xelse {
X
X/*	Setpgrp() cannot be called from processes associated with windows.
X	From the manual.  Since it's a cleaner model for the parent to redirect
X	the child's stdin, stdout and stderr, we do that.  We have to setpgrp(),
X	or else the child would be killed when the process group leader exited.	*/
X
X	setpgrp();
X	for (wd=0; wd<20; ++wd)
X	    close(wd);
X
X/*	Get a window, and set it up according to our parameters.  Since stdin
X	was closed above, wd gets to be stdin (roughly).	*/
X
X	wd = open( "/dev/window", O_RDWR|O_EXCL, 0 );
X	ioctl( wd, WIOCSETD, &winbar );
X	ioctl( wd, WIOCSETTEXT, &winname );
X	ioctl( wd, WIOCSYS, SYSWMGR );
X	ioctl( wd, WIOCSYS, SYSPMGR );
X	ioctl( wd, WIOCPGRP, (char *)0);
X
X/*	Set up the child's stdout and stderr to point at the window.	*/
X
X	dup( wd );
X	dup( wd );
X
X/*	Set terminal parameters; after all, we'll want to read escape codes and
X	other neat stuff.	*/
X
X	ioctl( wd, TCGETA, &bartty );
X	bartty.c_iflag &= ~IGNBRK;
X	bartty.c_lflag &= ~( ICANON | ECHO );
X
X/*	Read three characters at a time; 1 second timeout interval.  Whether
X	this really does anything, I don't know.	*/
X
X	bartty.c_cc[4] = 3;
X	bartty.c_cc[5] = 10;
X	ioctl( wd, TCSETA, &bartty );
X	
X/*	Execute with no command line arguments.	*/
X
X	execl( BARPROG, "newmgr", 0 );
X	}
X}
X
END_OF_FILE
if test 3930 -ne `wc -c <'startmgr.c'`; then
    echo shar: \"'startmgr.c'\" unpacked with wrong size!
fi
# end of 'startmgr.c'
fi
if test -f 'window.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'window.c'\"
else
echo shar: Extracting \"'window.c'\" \(7021 characters\)
sed "s/^X//" >'window.c' <<'END_OF_FILE'
X/*
X *     window  pathname  program_name  [ args... ]
X *
X * Execute any program in a separate window.  Update utmp so that
X * it shows as a login.  Requires that the user specify argv[0].
X * If the pathname is not specified, run /bin/login.
X *
X * This software is Copyright (c) 1988 by Andy Fyfe.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * (Copyright notice courtesy of News 2.11 :-)
X */
X
X#include <stdio.h>
X#include <string.h>
X#include <sys/window.h>
X#include <sys/types.h>
X#include <utmp.h>
X#include <pwd.h>
X#include <signal.h>
X#include <fcntl.h>
X
X#define WINDOW_DEVICE		"/dev/window"
X#define TTY_NAME		"/dev/tty"
X#define LOGIN_PROG		"/bin/login"
X#define LOGIN_NAME		"login"
X#define LOGIN_USER		"LOGIN"
X#define LOGIN_WINDOW_NAME	"Login Window"
X
X#ifdef __STDC__
Xstruct utmp *getutline(struct utmp *);
Xstruct passwd *getpwuid(int);
Xchar *ttyname(int);
Xvoid *malloc(int);
X#else
Xstruct utmp *getutline();
Xstruct passwd *getpwuid();
Xchar *ttyname();
Xchar *malloc();
X#endif
X
Xvoid
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    char *cmdname;
X    struct uwdata d;
X    struct utdata t;
X    int fd, pid, status, is_login;
X    struct utmp	u, *v;
X    struct passwd *p;
X    char *ttyp, *ttyn, *win_name, *q;
X
X    cmdname = argv[0];
X    is_login = argc == 1;
X
X    if (argc < 3 && !is_login) {
X	fprintf(stderr, "%s: usage: %s [pathname program_name [ args... ]]\n",
X	    cmdname, cmdname);
X	exit(1);
X    }
X
X    /*
X     * Make sure we're running in a window
X     */
X    fd = open(TTY_NAME, O_RDWR);
X    if (fd < 0) {
X	fprintf(stderr, "%s: can't open %s: ", cmdname, TTY_NAME);
X	perror("");
X	exit(1);
X    }
X    if (ioctl(fd, WIOCGETD, &d) < 0) {
X	fprintf(stderr, "%s must be run from a console window\n", cmdname);
X	exit(1);
X    }
X    close(fd);
X
X    /*
X     * Find out the user name of the user executing the program, and
X     * add it to the utmp entry.
X     */
X    if (is_login)
X	strncpy(u.ut_user, LOGIN_USER, sizeof(u.ut_user));
X    else {
X	p = getpwuid(getuid());
X	if (p == NULL) {
X	    fprintf(stderr, "%s: can't find username for uid %d\n",
X		cmdname, getuid());
X	    exit(1);
X	}
X	strncpy(u.ut_user, p->pw_name, sizeof(u.ut_user));
X    }
X
X    /*
X     * Create a new window.  Find out which one it is, and add that
X     * information to the utmp entry.
X     */
X    fd = open(WINDOW_DEVICE, O_RDWR);
X    if (fd < 0) {
X	fprintf(stderr, "%s: can't open %s: ", cmdname, WINDOW_DEVICE);
X	perror("");
X	exit(1);
X    }
X    ttyp = ttyname(fd);
X    if (ttyp == NULL) {
X	fprintf(stderr, "%s: can't find tty name for %s\n",
X	    cmdname, WINDOW_DEVICE);
X	exit(1);
X    }
X    ttyn = strrchr(ttyp, '/');
X    if (ttyn == NULL)
X	ttyn = ttyp;
X    else
X	ttyn++;
X    strncpy(u.ut_id, ttyn, sizeof(u.ut_id));
X    strncpy(u.ut_line, ttyn, sizeof(u.ut_line));
X
X    /*
X     * Fork.  The idea here is to orphan this program.  The parent will
X     * just exit, and the child will go on.  This keeps the initiating
X     * shell from having to worry about the child.
X     */
X    pid = fork();
X    if (pid < 0) {
X	fprintf(stderr, "%s: can't fork: ", cmdname);
X	perror("");
X	exit(1);
X    }
X    else if (pid > 0)
X	exit(0);
X
X    /*
X     * Fork.  The child will go on to exec the program provided, and the
X     * parent will wait until the child completes
X     */
X    pid = fork();
X    if (pid < 0) {
X	fprintf(stderr, "%s: can't fork: ", cmdname);
X	perror("");
X	exit(1);
X    }
X    else if (pid > 0) {
X	/*
X	 * We need to make sure we're not holding on to anything, so
X	 * chdir to / and close all our files.  We want to be safe
X	 * from signals, etc., so we change our process group and ignore
X	 * lots of signals.
X	 */
X	chdir("/");
X	close(fd);
X	for (fd=0; fd<_NFILE; ++fd)
X	    close(fd);
X	setpgrp();
X	signal(SIGHUP, SIG_IGN);
X	signal(SIGINT, SIG_IGN);
X	signal(SIGQUIT, SIG_IGN);
X	signal(SIGTERM, SIG_IGN);
X	signal(SIGALRM, SIG_IGN);
X
X	/*
X	 * Wait for the child
X	 */
X	while (pid != wait(&status))
X	    ;
X	
X	/*
X	 * Write out a terminating utmp entry.
X	 * If it's a login, add entry to end of wtmp
X	 */
X	setutent();
X	v = getutline(&u);
X	if (v != NULL)
X	    memcpy(&u, v, sizeof(u));
X	else
X	    u.ut_pid = pid;
X	u.ut_type = DEAD_PROCESS;
X	u.ut_time = time((long *)0);
X	u.ut_exit.e_termination = status & 0xff;
X	u.ut_exit.e_exit = (status >> 8) & 0xff;
X	pututline(&u);
X	endutent();
X	if (is_login) {
X	    fd = open(WTMP_FILE, O_WRONLY | O_APPEND);
X	    if (fd >= 0) {
X		write(fd, &u, sizeof(u));
X		close(fd);
X	    }
X	}
X
X	exit(0);
X    }
X
X    /*
X     * Put the child in it's own process group
X     */
X    setpgrp();
X    ioctl(fd, WIOCPGRP, (char *)0);
X
X    /*
X     * Set the window to use the full screen
X     */
X    d.uw_x = 0;
X    d.uw_y = WLINE(2);
X    d.uw_width = WINWIDTH;
X    d.uw_height = WINHEIGHT - d.uw_y;
X    d.uw_uflags = NBORDER;
X    if (ioctl(fd, WIOCSETD, &d) < 0) {
X	fprintf(stderr, "%s: window ioctl failed: ", cmdname);
X	perror("");
X	exit(1);
X    }
X
X    /*
X     * Give the window a name
X     */
X    t.ut_num = WTXTUSER;
X    win_name = is_login ? LOGIN_WINDOW_NAME : argv[1];
X    strncpy(t.ut_text, win_name, WTXTLEN);
X    t.ut_text[WTXTLEN-1] = '\0';
X    ioctl(fd, WIOCSETTEXT, &t);
X
X    /*
X     * Reset stdin, stdout, and stderr to use new window
X     */
X    if (fd != 0) {
X	close(0);
X	dup(fd);
X	close(fd);
X    }
X    close(1);
X    dup(0);
X    close(2);
X    dup(0);
X
X    /*
X     * Set up some environment variable with the window size and name
X     */
X    putenv("LINES=24");
X    putenv("COLUMNS=80");
X    putenv("WNWIDTH=80");
X    putenv("WNHEIGHT=24");
X    q = malloc(sizeof("WNNAME=") + strlen(win_name));
X    if (q != NULL)
X	putenv(strcat(strcpy(q, "WNNAME="), win_name));
X
X    /*
X     * Fill in the rest of the utmp entry and write it to /etc/utmp
X     * If it's a login, add entry to end of wtmp
X     */
X    u.ut_pid = getpid();
X    if (is_login)
X	u.ut_type = LOGIN_PROCESS;
X    else
X	u.ut_type = USER_PROCESS;
X    u.ut_exit.e_termination = 0;
X    u.ut_exit.e_exit = 0;
X    u.ut_time = time((long *)0);
X    setutent();
X    pututline(&u);
X    endutent();
X    if (is_login) {
X	fd = open(WTMP_FILE, O_WRONLY | O_APPEND);
X	if (fd >= 0) {
X	    write(fd, &u, sizeof(u));
X	    close(fd);
X	}
X    }
X
X    /*
X     * Finally exec the program.  But first we have to reset the effective uid
X     * if we're not exec'ing /bin/login.
X     */
X    if (is_login) {
X	execl(LOGIN_PROG, LOGIN_NAME, (char *)0);
X	fprintf(stderr, "%s: could not EXECL '%s': ", cmdname, LOGIN_PROG);
X    }
X    else {
X	setuid(getuid());
X	execvp(argv[1], argv+2);
X	fprintf(stderr, "%s: could not EXECVP '%s' as '%s': ",
X	    cmdname, argv[1], argv[2]);
X    }
X    perror("");
X    exit(1);
X}
END_OF_FILE
if test 7021 -ne `wc -c <'window.c'`; then
    echo shar: \"'window.c'\" unpacked with wrong size!
fi
# end of 'window.c'
fi
echo shar: End of shell archive.
exit 0