[net.sources] Emacs mods: #4

chris@umcp-cs.UUCP (08/29/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 abspath.c'
sed 's/^X//' <<'//go.sysin dd *' >abspath.c
X/* convert a pathname to an absolute one, if it is absolute already,
   it is returned in the buffer unchanged, otherwise leading "./"s
   will be removed, the name of the current working directory will be
   prepended, and "../"s will be resolved.

   In a moment of weakness, I have implemented the cshell ~ filename
   convention.  ~/foobar will have the ~ replaced by the home directory of
   the current user.  ~user/foobar will have the ~user replaced by the
   home directory of the named user.  This should really be in the kernel
   (or be replaced by a better kernel mechanism).  Doing file name
   expansion like this in a user-level program leads to some very
   distasteful non-uniformities.

   Another fit of dementia has led me to implement the expansion of shell
   environment variables.  $HOME/mbox is the same as ~/mbox.  If the
   environment variable a = "foo" and b = "bar" then:
	$a	=>	foo
	$a$b	=>	foobar
	$a.c	=>	foo.c
	xxx$a	=>	xxxfoo
	${a}!	=>	foo!

				James Gosling @ CMU
 */

#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include "keyboard.h"
#include "mlisp.h"
#include "ctype.h"
#ifdef LIBNDIR
#include <dir.h>
#endif LIBNDIR

X/* Hack! */
#define CHDIR(dirname) syscall(12,dirname)

static char curwd[MaxPathNameLen];/* the current working directory is
				     remembered here.  chdir()'s are
				     trapped and this gets updated. */
char *getenv ();


abspath (nm, buf)		/* input name in nm, absolute pathname
				   output to buf.  returns -1 if the
				   pathname cannot be successfully
				   converted (only happens if the
				   current directory cannot be found) */
char	*nm,
* buf; {
    register char  *s,
                   *d;
    char    lnm[1000];
    s = nm;
    d = lnm;
    while (*d++ = *s)
	if (*s++ == '$') {
	    register char  *start = d;
	    register    braces = *s == '{';
	    register char  *value;
	    while (*d++ = *s)
		if (braces ? *s == '}' : !isalnum (*s))
		    break;
		else
		    s++;
	    *--d = 0;
	    value = getenv (braces ? start + 1 : start);
	    if (value) {
		for (d = start - 1; *d++ = *value++;);
		d--;
		if (braces && *s)
		    s++;
	    }
	}
    d = buf;
    s = curwd;
    nm = lnm;
    if (nm[0] == '~')		/* prefix ~ */
	if (nm[1] == '/' || nm[1] == 0)/* ~/filename */
	    if (s = getenv ("HOME")) {
		if (*++nm)
		    nm++;
	    }
	    else
		s = "";
	else {			/* ~user/filename */
	    register char  *nnm;
	    register struct passwd *pw;
	    for (s = nm; *s && *s != '/'; s++);
	    nnm = *s ? s + 1 : s;
	    *s = 0;
	    pw = (struct passwd *) getpwnam (nm + 1);
	    if (pw == 0) {
		error ("\"%s\" isn't a registered user.", nm+1);
		s = "";
	    }
	    else {
		nm = nnm;
		s = pw -> pw_dir;
	    }
	}
    while (*d++ = *s++);
    *(d - 1) = '/';
    s = nm;
    if (*s == '/')
	d = buf;
    while (*d++ = *s++);
    *(d - 1) = '/';
    *d = '\0';
    d = buf;
    s = buf;
    while (*s)
	if ((*d++ = *s++) == '/' && d > buf + 1) {
	    register char  *t = d - 2;
	    switch (*t) {
		case '/': 	/* found // in the name */
		    --d;
		    break;
		case '.': 
		    switch (*--t) {
			case '/': /* found /./ in the name */
			    d -= 2;
			    break;
			case '.': 
			    if (*--t == '/') {/* found /../ */
				while (t > buf && *--t != '/');
				d = t + 1;
			    }
			    break;
		    }
		    break;
	    }
	}
    if (*(d - 1) == '/' && d > buf+1)
	d--;
    *d = '\0';
    return 0;
}

X/*
 *  getwd - get current working directory  (algorithm from /bin/pwd)
 *
 *  Author:  Mike Accetta, 19-May-78
 *
 **********************************************************************
 * HISTORY
 * 20-Nov-79  Steven Shafer (sas) at Carnegie-Mellon University
 *	Modified (by Mike Accetta) for VAX.  I tried using a "popen" on "pwd"
 *	to achieve the same effect; there's less risk since errors in pwd
 *	don't trash the current directory of the calling program; however,
 *	it takes about two full seconds (this routine takes about zero time).
 *	This routine wins.
 *
 * 10-Jun-83 Chris Kent (cak) at DecWRL
 *	Upgraded to new directory access routines for 4.1cBSD
 *
 * 10-Jul-83 Chris Torek at Umcp-Cs
 *	#ifdefs for new directory vs. straight open/read
 *
 **********************************************************************
 *
 *  Remarks:
 *
 *     The name of the current working directory is copied into
 *  the supplied string `wdir'.  The current working directory
 *  is changed during the execution of the routine and restored
 *  at the end by a chdir(wdir).  If an error occurs the current
 *  working directory is undefined.
 */

getwd (wdir)
char   *wdir;
{
#ifdef LIBNDIR

#define isbad(xx)	(xx) == NULL	/* used on opendir() */
#define	readit()	(dirp = readdir (fd))
#define skipit()	0
    struct direct *dirp;
    register DIR *fd;

#else LIBNDIR

#define	isbad(xx)	(xx) < 0	/* used on opendir() (ie open()) */
#define closedir(d)	close (d)
#define opendir(d)	open (d, 0)
#define dirp		(&db)
#define readit()	read (fd, &db, 16) == 16
#define skipit()	db.d_inode == 0
    struct {
	ino_t	d_inode;
	char	d_name[15];		/* long enough to add \0 */
    } db;
    register int fd;

#endif LIBNDIR

    char temp[MaxPathNameLen];
    struct stat sb,
		sbc,
		root;
    register int found;

 /* Initially root */
    strcpy (wdir, "/");
    stat ("/", &root);
#ifndef LIBNDIR
    db.d_name[14] = 0;
#endif LIBNDIR

    for (;;) {
	if (isbad (fd = opendir ("..")))
	    return (-1);
	if (stat (".", &sbc) < 0 || stat ("..", &sb) < 0)
	    goto out;
	if (sbc.st_ino == root.st_ino && sbc.st_dev == root.st_dev) {
	    closedir (fd);
	    return CHDIR (wdir);
	}

	if (sbc.st_ino == sb.st_ino && sbc.st_dev == sb.st_dev) {
	    closedir (fd);
	    CHDIR ("/");
	    if (isbad (fd = opendir (".")))
		return (-1);
	    if (stat (".", &sb) < 0)
		goto out;
	/*  scan the root directory for directory with same device  */
	    if (sbc.st_dev != sb.st_dev) {
		while (readit ()) {
		    if (skipit ())
			continue;
		    if (stat (dirp->d_name, &sb) < 0)
			goto out;
		    if (sbc.st_dev == sb.st_dev) {
			sprintfl (temp, sizeof temp,
					"%s%s", dirp->d_name, wdir);
			strcpy (wdir + 1, temp);
			closedir (fd);
			return (CHDIR (wdir));
		    }
		}
	    }
	    else {
		closedir (fd);
		return (CHDIR (wdir));
	    }
	}

    /*  scan parent directory for file with inode of current directory  */
	found = 0;
	while (readit ()) {
	    if (skipit ())
		continue;
	    sprintfl (temp, sizeof temp, "../%s", dirp->d_name);
	    if (stat (temp, &sb) >= 0
		    && sb.st_ino == sbc.st_ino
		    && sb.st_dev == sbc.st_dev) {
		closedir (fd);
		found++;
		CHDIR ("..");
		sprintfl (temp, sizeof temp, "%s%s", dirp->d_name, wdir);
		strcpy (wdir + 1, temp);
		break;
	    }
	}
	if (!found)
	    goto out;
    }
out: 
    closedir (fd);
    return (-1);
}

X/* A chdir() that fiddles the global record */
chdir (dirname)
register char   *dirname; {
    register    ret;
    register char *p;
    char	path1[MaxPathNameLen], path2[MaxPathNameLen];
    for (p = path1; *p++ = *dirname++; ) ;
    *(p-1) = '/';		/* append a '/' so that "cd ~" works */
    *p = 0;
    ret = abspath (path1, path2);
    if (ret == 0 && (ret = CHDIR (path2)) == 0)
	strcpy (curwd, path2);
    return ret;
}

X/* return a pointer to a copy of a file name that has been
   converted to absolute form.  This routine cannot return failure. */
char *SaveAbs (fn)
char *fn; {
    static char buf[MaxPathNameLen];
    if (fn==0) return 0;
    abspath (fn, buf);
    return buf;
}

WorkingDirectory () {
    MLvalue -> exp_type = IsString;
    MLvalue -> exp_v.v_string = curwd;
    MLvalue -> exp_release = 0;
    MLvalue -> exp_int = strlen (MLvalue -> exp_v.v_string);
    return 0;
}

InitAbs () {
    int i;
    if (getwd (curwd) < 0) {
	char *i = getenv ("HOME");
	if (i == 0 || CHDIR (i))
	    CHDIR (i = "/");
	strcpy (curwd, i);
	fprintf (stderr, "[NOTE: Changed to directory %s]\r\n", curwd);
	fflush (stderr);
    }
    if ((i = strlen (curwd)) > 1 && curwd[i-1] == '/')
	curwd[i-1] = 0;
#ifdef DumpableEmacs
    if (!Once)
#endif
	defproc (WorkingDirectory, "working-directory");
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 abspath.c
	/bin/echo -n '	'; /bin/ls -ld abspath.c
fi
/bin/echo 'Extracting dsp.c'
sed 's/^X//' <<'//go.sysin dd *' >dsp.c
X/* Display routines */

X/*		Copyright (c) 1981,1980 James Gosling		*/

#include "config.h"
#include "keyboard.h"
#include "buffer.h"
#include "window.h"
#include "display.h"
#include <sys/ioctl.h>
#include <sgtty.h>
#include <stdio.h>

extern QuitDoRstDsp;		/* Set while in raw */
struct sgttyb old;		/* The initial tty mode bits */

InitDsp () {
    struct sgttyb   sg;
    extern char _sobuf[];
    gtty (0, &old);
    sg = old;
    QuitDoRstDsp++;
    sg.sg_flags = (sg.sg_flags & ~(ECHO | CRMOD | XTABS)) | RAW;
    stty (0, &sg);
    ScreenGarbaged = 1;
    setbuf (stdout, _sobuf);
    term_init ();
    if (tt.t_window) (*tt.t_window) (0);
}

RstDsp () {
    if (tt.t_window) (*tt.t_window) (0);
    (*tt.t_topos) (ScreenLength, 1);
    (*tt.t_wipeline) (0, ScreenWidth);
    (*tt.t_cleanup) ();
    fflush (stdout);
    stty (0, &old);
    QuitDoRstDsp = 0;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 dsp.c
	/bin/echo -n '	'; /bin/ls -ld dsp.c
fi
/bin/echo 'Extracting filecomp.c'
sed 's/^X//' <<'//go.sysin dd *' >filecomp.c
X/* File completion routines */

X/* Original code by Chris Torek <chris@umcp-cs>

 * Modifications for 4.1[ac]BSD by Marshall Rose <mrose@uci>
   If you want the 4.1[ac]BSD version, #define LIBNDIR.
   Note that this introduces the new global variable fast-file-searches.

 */

#include "config.h"
#include "buffer.h"
#include "window.h"
#include "keyboard.h"
#include "mlisp.h"
#include <sys/types.h>
#ifndef	LIBNDIR
#include <sys/dir.h>
#else
#include <ndir.h>
#endif
#include <sys/stat.h>

#define DONE		0
#define CONTIN		1
#define GARBAGE		2
#define CANTHELP	3
#define EMPTY		4
#define MANY		5

#define min(a,b) ((a)<(b)?(a):(b))
#ifdef	LIBNDIR
#define max(a,b) ((a)>(b)?(a):(b))
#define	WID	18
#define	NCOLS	78
#endif

#ifndef	LIBNDIR
static char path[MaxPathNameLen], file[DIRSIZ];
#else
static char path[MaxPathNameLen], file[MAXNAMLEN];
#endif
static struct stat st;
static DirUsed;			/* Number of entries in DirEnts */
static unsigned DirSize;	/* Number of bytes allocated to DirEnts */
static DirSorted;		/* True iff table has been sorted */
static DirMatches;		/* Number of matches */
static MatchSize;		/* Number of characters matched */
static time_t	DirMtime;	/* st_mtime of current in-core dir */
static dev_t	DirDevice;	/* st_dev of current dir */
static ino_t	DirInode;	/* st_ino of current dir */
static struct direct *DirEnts;	/* The first entry */
static struct direct *FirstMatch;/* The first entry matching desired name */

extern int AutoHelp;
extern	PopUpWindows;
extern	RemoveHelpWindow;
static	struct	window *killee;

#ifdef	LIBNDIR
extern	FastFileSearches;
#endif

char *malloc (), *realloc (), *index (), *rindex ();

X/* Get the name of some existing file */

char *
GetFileName (prompt)
char *prompt;
{
    static char result[MaxPathNameLen];
    register char *name = "";
    register f, len;
    int	oldpop = PopUpWindows;
    struct buffer *old = bf_cur;

    if (RemoveHelpWindow)
	PopUpWindows = 0;
    killee = 0;
    *result = 0;
    for (;;) {
	name = BrGetstr (1, result, &prompt);
	if (name == 0) {
	    if (killee)
		WindowOn (old);
	    PopUpWindows = oldpop;
	    return name;
	}
	f = name[strlen(name)-1] == '/';
	abspath (name, result);
	name = result;
	len = strlen (name);
	if (f) {
	    name[len++] = '/';
	    name[len] = 0;
	}
	if (LastKeyStruck == '?') {	/* Show possible completions */
	    name[len - 1] = 0;
	    SplitPath (name, path, file);
	    if (ReadDir (path) == 0) {
		MarkDir (file);
		showchoices ("Choose one of these:\n");
	    }
	    else
		Ding ();
	}
	else switch (PerformCompletion (name)) {
	    case DONE:		/* we got a file */
		if (killee)
	            WindowOn (old);
		PopUpWindows = oldpop;
		return name;
	    case GARBAGE:	/* foo on you */
		if (AutoHelp) {
		    ReadDir (path);
		    MarkDir (file);
		    showchoices ("Garbage!!  Use one of the following:\n");
		} else
		    Ding ();
		continue;
	    case CONTIN:	/* completed something */
		continue;
	    case CANTHELP:	/* directory unreadable */
		Ding ();
		continue;
	    case EMPTY:		/* empty directory */
		if (AutoHelp)
		    showchoices ("Empty directory!\n");
		Ding ();
		continue;
	    case MANY:		/* matches a bunch of names */
		if (AutoHelp) {
		    MarkDir (file);
		    showchoices ("Ambiguous, use one of the following:\n");
		}
		else
		    Ding ();
		continue;
	}
    }
}

static
PerformCompletion (name)
register char *name;
{
    register diving = 0;
    register pathlen;		/* Quick index into path or name */

top:
    if (stat (name, &st) == 0 && (st.st_mode & S_IFDIR) == 0)
	return DONE;		/* Got a filename */

    SplitPath (name, path, file);
    if (stat (path, &st) || (st.st_mode & S_IFDIR) == 0) {
	do {
	    register char *p = path;
	    while (*p++) ;
	    p[-2] = 0;		/* Remove trailing slash */
	    SplitPath (path, path, file);
	}
	while (stat (path, &st) || (st.st_mode & S_IFDIR) == 0);
	strcpy (name, path);
	return GARBAGE;		/* No such directory */
    }
    if (access (path, 4) < 0)	/* Cant read directory */
	return diving ? CONTIN : CANTHELP;
    if (ReadDir (path)) {	/* "Can't happen" */
	Ding ();
	return CONTIN;
    }
    if (DirUsed == 0)
	return EMPTY;		/* Empty directory */
    pathlen = strlen (path);
    MarkDir (file);
    if (DirMatches == 0) {	/* No such file */
	do file[--MatchSize] = 0;
	while (MarkDir (file) == 0);
	strcpy (name+pathlen, file);
	return GARBAGE;
    }
    if (DirMatches == 1) {	/* Exact match on one name */
	diving++;
#ifndef	LIBNDIR
	strncpy (name+pathlen, FirstMatch -> d_name, DIRSIZ);
	*(name+pathlen+DIRSIZ) = 0;
#else				/* already null terminated */
	strcpy (name+pathlen, FirstMatch -> d_name);
#endif
	if (stat (name, &st) == 0 && st.st_mode & S_IFDIR)
	    strcat (name, "/");
	goto top;
    }
X/*
 * Make the name as long as possible such that it still matches the
 * same entries.  If we cannot add anything then the name was ambiguous.
 */
    {
	register oDirMatches = DirMatches, extended = 0;

	while (file[MatchSize] = FirstMatch -> d_name[MatchSize]) {
	    MatchSize++;
#ifndef	LIBNDIR
	    if (MatchSize < DIRSIZ)
		file[MatchSize] = 0;
#else
	    if (MatchSize < MAXNAMLEN)
		file[MatchSize] = 0;
#endif
	    if (MarkDir (file) < oDirMatches) {
		file[--MatchSize] = 0;
		break;
	    }
	    extended++;
	}
#ifndef	LIBNDIR
	strncpy (name+pathlen, file, DIRSIZ);/* (current path is correct) */
#else
	strcpy (name+pathlen, file);
#endif
	return extended || diving ? CONTIN : MANY;
    }
}

X/* Make the table by reading the directory.  Return 0 if everything goes
   well.  Also, remember current table and only remake if new. */

#ifndef	LIBNDIR
static
ReadDir (dir)
char *dir;
{
    static lastrv;
    register struct direct *d, *p;
    register char *s;
    register f, l;

    f = open (dir, 0);
    if (f < 0)
	return lastrv = -1;
    fstat (f, &st);
    if (st.st_mtime == DirMtime && st.st_dev == DirDevice
			&& st.st_ino == DirInode) {
	close (f);
	return lastrv;
    }
    DirMtime = st.st_mtime;
    DirDevice = st.st_dev;
    DirInode = st.st_ino;
    if (st.st_size >= DirSize) {
	if (DirEnts)
	    free ((char *) DirEnts);
	DirSize = st.st_size + 30;
	DirEnts = (struct direct *) malloc (DirSize);
    }
    DirSorted = 0;
    lastrv = read (f, (char *) DirEnts, st.st_size + 1) != st.st_size;
    close (f);
    if (lastrv)
	return lastrv;
    p = DirEnts;
    d = DirEnts;
    for (f = st.st_size / sizeof *p; --f >= 0; p++) {
	if (p -> d_ino == 0)
	    continue;
	s = p -> d_name;
	if (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)))
	    continue;
	l = DIRSIZ;
	while (*s++ && --l >= 0);
	--s;
	switch (*--s) {
	    case 'o':
		if (*--s == '.')
		    continue;
	    case 'P':
		if (*--s == 'K' && *--s == 'C' && *--s == '.')
		    continue;
	    case 'k':
		if (*--s == 'a' && *--s == 'b' && *--s == '.')
		    continue;
	}
	*d++ = *p;
    }
    DirUsed = d - DirEnts;
    return 0;
}
#else
static
ReadDir (dir)
char *dir;
{
    static int  lastrv;
    register int    f,
                    g,
                    l;
    register char  *e,
                   *s;
    short   byte;		/* 16 bits (I hope) */
    static char filnam[MaxPathNameLen];
    register struct direct *d,
                           *p;
    register    DIR * dd;

    if ((dd = opendir (dir)) == NULL)
	return (lastrv = -1);
    fstat (dd -> dd_fd, &st);
    if (st.st_mtime == DirMtime
	    && st.st_dev == DirDevice
	    && st.st_ino == DirInode) {
	closedir (dd);
	return lastrv;
    }
    DirMtime = st.st_mtime;
    DirDevice = st.st_dev;
    DirInode = st.st_ino;

    for (f = 0; p = readdir (dd); f++)
	continue;
again: ;
    f += 10;			/* fudge factor... */
    l = f * sizeof (struct direct);
    if (l >= DirSize) {
	if (DirEnts)
	    free ((char *) DirEnts);
	DirSize = l * 2;	/* why not? */
	DirEnts = (struct direct   *) malloc (DirSize);
    }

    DirSorted = 0;
    rewinddir (dd);
    for (d = DirEnts, g = 0; p = readdir (dd);) {
	if ((p -> d_namlen == 1 && !strcmp (p -> d_name, "."))
		|| (p -> d_namlen == 2 && !strcmp (p -> d_name, "..")))
	    continue;
	s = p -> d_name + p -> d_namlen;
	if (p -> d_namlen > 2 && !strcmp (s - 2, ".o"))
	    continue;
#ifdef	PrependExtension
	e = CheckpointExtension;
	if (!strncmp (p -> d_name, e, strlen (e)))
	    continue;
	e = BackupExtension;
	if (!strncmp (p -> d_name, e, strlen (e)))
	    continue;
#else
	if ((p -> d_namlen > (l = strlen (e = CheckpointExtension))
		    && !strcmp (s - l, e))
		|| ((p -> d_namlen > (l = strlen (e = BackupExtension))
			&& !strcmp (s - l, e))))
	    continue;
#endif
	if (FastFileSearches)
	    goto no_tricks;
	sprintfl (filnam, sizeof filnam, "%s/%s", dir, p -> d_name);
	if (stat (filnam, &st))
	    continue;
	if ((st.st_mode & S_IFDIR) != 0)
	    l = -1;
	else
	    if ((l = open (filnam, 0)) < 0)
		continue;
	if (l >= 0) {
	    if (read (l, (char *) (&byte), sizeof byte) != sizeof byte)
		byte = 0;
	    close (l);
	    switch (byte) {	/* is it a text file? */
		case 0405: 
		case 0407: 	/* OMAGIC */
		case 0410: 	/* NMAGIC */
		case 0411: 
		case 0413: 	/* ZMAGIC */
		case 0177545: 
		    continue;

		default: 	/* perhaps it is... */
		    break;
	    }
	}

no_tricks: ;
	if (f <= g++) {		/* directory grew!!! */
	    for (f = g; p = readdir (dd); f++)
		continue;
	    goto again;
	}
	d -> d_ino = p -> d_ino;
	d -> d_reclen = sizeof (struct direct);
	d -> d_namlen = p -> d_namlen;
	strcpy (d -> d_name, p -> d_name);
	d++;
    }
    closedir (dd);
    DirUsed = d - DirEnts;
    return (lastrv = 0);
}
#endif

X/* Mark all the table entries that match 'string' */
static
MarkDir (string)
char *string;
{
    register struct direct *p;
#ifndef	LIBNDIR
    register len = DIRSIZ;
    register char *s = string;
#endif

#ifndef	LIBNDIR
    while (*s++ && --len >= 0) ;
    MatchSize = s - string - 1;
#else
    MatchSize = min (strlen (string), MAXNAMLEN);
#endif
    DirMatches = 0;
    for (p = &DirEnts[DirUsed - 1]; p>=DirEnts; p--)
	if (MatchSize == 0 || p->d_name[0]==string[0]
			&& strcmpn(p->d_name,string,MatchSize)==0) {
	    DirMatches++;
	    p -> d_ino = 1;
	    FirstMatch = p;
	} else p->d_ino = 0;
    return DirMatches;
}

X/* Compare two table entries (for qsort) */

static
DirCompare (p1, p2)
register struct direct *p1, *p2;
{
#ifndef	LIBNDIR
    return strcmpn (p1 -> d_name, p2 -> d_name, DIRSIZ);
#else
    return strncmp (p1 -> d_name, p2 -> d_name,
	max (p1 -> d_namlen, p2 -> d_namlen));
#endif
}

X/* Write all the matched entries into "Help" buffer.  Sort first
   if needed. */
static
showchoices (msg)
char *msg;
{
    register struct direct *p;
    register i;
#ifndef	LIBNDIR
    register side = 0;
    char buf[22];
#else
    register pos, j;
    char buf[MAXNAMLEN + WID];
#endif

    if (DirUsed > 1 && !DirSorted)
	qsort (DirEnts, DirUsed, sizeof (struct direct), DirCompare);
    DirSorted++;
    SetBfn ("Help");
    WindowOn (bf_cur);
    EraseBf (bf_cur);
    InsStr (msg);
    killee = wn_cur;
#ifndef	LIBNDIR
    for (p = DirEnts, i=DirUsed; --i>=0; p++) {
	if (p -> d_ino) {
	    sprintfl (buf, sizeof buf, (side==3 ? ((side=0), "%.*s\n")
		  : (side++, "%-18.*s")), DIRSIZ, p -> d_name);
	    InsStr (buf);
	}
    }
#else
    for (p = DirEnts, i = DirUsed, pos = 0; --i >= 0; p++)
	if (p -> d_ino) {
	    if (pos > 0) {
		if (pos + (j = WID - (pos % WID)) + p -> d_namlen > NCOLS)
		    pos = j = 0, InsStr ("\n");
	    }
	    else
		j = 0;
	    sprintfl (buf, sizeof buf, "%*s%.*s", j, "",
		    p -> d_namlen, p -> d_name);
	    InsStr (buf);
	    pos += p -> d_namlen +j;
	}
#endif
    BeginningOfFile ();
    bf_cur -> b_mode.md_NeedsCheckpointing = 0;
    bf_modified = 0;
}

X/* Split a pathname into directory and filename components */
static SplitPath (path, dir, file)
register char *path;
char *dir, *file; {
    register char *p, *d = 0;

    for (p = path; *p; ) if (*p++ == '/') d = p;
    if (d) {
	strncpy (dir, path, d - path);
	dir[d-path] = 0;
#ifndef	LIBNDIR
	strncpy (file, d, DIRSIZ);
#else
	strncpy (file, d, MAXNAMLEN);
#endif
    } else {
	dir[0] = 0;
#ifndef	LIBNDIR
	strncpy (file, path, DIRSIZ);
#else
	strncpy (file, path, MAXNAMLEN);
#endif
    }
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 filecomp.c
	/bin/echo -n '	'; /bin/ls -ld filecomp.c
fi
/bin/echo 'Extracting fileio.c'
sed 's/^X//' <<'//go.sysin dd *' >fileio.c
X/* File IO for Emacs */

X/*		Copyright (c) 1981,1980 James Gosling		*/

#include "keyboard.h"
#include "mlisp.h"
#include "buffer.h"
#include "window.h"
#include "config.h"
#include "macros.h"
#include <sys/types.h>
#include <stat.h>

char *GetFileName();

struct AutoMode {		/* information for automatic mode
				   recognition */
    char   *a_pattern;		/* the pattern that the name must match */
    int     a_len;		/* the length of the pattern string */
    struct BoundName *a_what;	/* what to do if we find it */
    struct AutoMode *a_next;	/* the next thing to try */
};

static struct AutoMode *AutoList;/* the list of filename-pattern pairs that
				    have been specified by auto-execute */
static FilesShouldEndWithNewline;/* If true, then if the user trys to write
				    out a buffer that doesn't end in a
				    newline then they'll get asked about it.
				    I almost called this variable
				    "kazar-mode" but good taste prevaled */
static BackupBeforeWriting;	/* if true, then file being written will be
				   backed up just before the first time that
				   it is written. */
static BackupByCopying;		/* if true, then when a backup is made, it
				   will be made by copying the file, rather
				   than by fancy footwork with links (this
				   is for folks who like to preserve links
				   to files) */
static BackupByCopyingWhenLinked;/* if true, then when a backup for a file
				    with multiple links is made, it will be
				    made by copying */
static UnlinkCheckpointFiles;	/* if true, then when a file is written out
				   the corresponding checkpoint file is
				   deleted -- some people don't like to have
				   .CKP files cluttering up their
				   directories, but some people like the
				   added security. */
static AskAboutBufferNames;	/* If true (the default) Emacs will ask
				   instead of synthesizing a unique name in
				   the case where visit-file encounters a
				   conflict in generated buffer names. */
#ifdef LIBNDIR
int FastFileSearches;		/* If true (the default) then Emacs will
				   not perform any fancy tests to determine
				   what files the user is interested in
				   during filename command completion */
#endif LIBNDIR
static Umask;			/* the current umask() */

DoAuto (filename)		/* Perform the auto-execute action (if any)
				   for the specified filename */
char   *filename; {
    register struct AutoMode   *p;
    register    len = strlen (filename);
    register saverr = err;
    err = 0;
    for (p = AutoList; p; p = p -> a_next)
	if ( len+1 >= p->a_len && (*p -> a_pattern == '*'
		    ? strcmpn (p -> a_pattern + 1,
			filename + len - p -> a_len + 1,
			p -> a_len - 1)
		    : strcmpn (p -> a_pattern, filename, p -> a_len - 1)
		) == 0) {
	    ExecuteBound (p->a_what);
	    break;
	}
    err |= saverr;
}

AutoExecute () {
    int     what = getword (MacNames, ": auto-execute ");
    char   *pattern;
    register struct AutoMode   *p;
    if (what < 0)
	return 0;
    pattern = getstr (": auto-execute %s when name matches ",
	    MacNames[what]);
    if (pattern == 0)
	return 0;
    if ((*pattern == '*') == (pattern[strlen (pattern) - 1] == '*'))
	error ("Improper pattern \"%s\"; should either be of the form \"*X\" or \"X*\"", pattern);
    else {
	p = (struct AutoMode   *) malloc (sizeof *p);
	p -> a_pattern = savestr (pattern);
	p -> a_len = strlen (p -> a_pattern);
	p -> a_what = MacBodies[what];
	p -> a_next = AutoList;
	AutoList = p;
    }
    return 0;
}

static PerformAutoMode () {
    if (CurExec && CurExec -> p_nargs) {
	StringArg (1);
	if (err) return 0;
	DoAuto (MLvalue -> exp_v.v_string);
	ReleaseExpr (MLvalue);
    }
    else
	DoAuto (bf_cur -> b_name);
    return 0;
}

static
WriteFileExit () {
    return ModWrite () ? -1 : 0;
}

X/* Call with pointer to buffer whose checkpoint file may be deleted. */

DeleteBuffersCheckpointFile (b)
register struct buffer *b;
{
    if (UnlinkCheckpointFiles) {
	if (b -> b_checkpointfn)
	    unlink (b -> b_checkpointfn);
	b -> b_checkpointed = 0;
    }
}

static  WriteThis () {
    register    rv = 0;
    if (!bf_cur -> b_fname)
	error ("No file name assocated with buffer");
    else
	if (WriteFile (bf_cur -> b_fname, 0))
	    rv = -1;
    if (UnlinkCheckpointFiles) {
	if (!err && bf_cur -> b_checkpointfn)
	    unlink (bf_cur -> b_checkpointfn);
	bf_cur -> b_checkpointed = 0;
    }
    return rv;
}

static InsertFile () {
    char *fn = GetFileName("Insert file: ");

    if (fn) {
	readfile (SaveAbs (fn), 0, 0);
	bf_modified++;
    }
    return 0;
}

static
WriteModifiedFiles () {
    ModWrite ();
    return 0;
}

static
ReadFile () {
    register char  *fn = getstr (": read-file ");
    if (fn == 0)
	return 0;
    readfile (SaveAbs(*fn ? fn : bf_cur->b_fname), 1, 0);
    DoAuto (fn);
    return 0;
}

static
UnlinkFile () {
    register char *fn = (char *) SaveAbs (getstr (": unlink-file "));
    MLvalue -> exp_int = fn ? unlink (fn) : -1;
    MLvalue -> exp_type = IsInteger;
    return 0;
}

static
FileExists () {
    register char *fn = (char *) SaveAbs (getstr (": file-exists "));
    if (fn==0) return 0;
    MLvalue -> exp_int = *fn == 0 ? 0
		: access (fn, 2)>=0 ? 1
		: access(fn, 4)>=0 ? -1
		: 0;
    MLvalue -> exp_type = IsInteger;
    return 0;
}

static
WriteCurrentFile () {
    if (!bf_cur -> b_fname)
	error ("No file name assocated with buffer");
    else
	WriteFile (bf_cur -> b_fname, 0);
    if (UnlinkCheckpointFiles) {
	if (!err && bf_cur -> b_checkpointfn)
	    unlink (bf_cur -> b_checkpointfn);
	bf_cur -> b_checkpointed = 0;
    }
    return 0;
}

static  VisitFileCommand () {
    VisitFile (getstr ("Visit file: "), 1, 1);
    return 0;
}

static	VisitExistingFileCommand () {
    VisitFile (GetFileName ("Visit existing file: "), 1, 1);
    return 0;
}

static	SetFileName () {
    register char *fn = getstr (": set-file-name to ");
    if (fn == 0 || *fn == 0)
	return 0;
    if (bf_cur -> b_fname)
	free (bf_cur -> b_fname);
    bf_cur -> b_fname = savestr ((char *) SaveAbs (fn));
    Cant1WinOpt++;
    bf_cur -> b_kind = FileBuffer;
    return 0;
}

static  WriteNamedFile () {
    register char  *fn = getstr ("Write file: ");
    if (fn == 0)
	return 0;
    if (*fn == '\0') {
	if (bf_cur -> b_fname == 0) {
	    error ("I don't like empty file names!");
	    return 0;
	}
    }
    else {
	if (bf_cur -> b_fname)
	    free (bf_cur -> b_fname);
	bf_cur -> b_fname = savestr ((char *) SaveAbs (fn));
    }
    if (bf_cur -> b_checkpointfn) {
	free (bf_cur -> b_checkpointfn);
	bf_cur -> b_checkpointfn = 0;
	bf_cur -> b_checkpointed = 0;
    }
    Cant1WinOpt++;
    bf_cur -> b_kind = FileBuffer;
    WriteCurrentFile ();
    return 0;
}

static  AppendToFile () {
    register char  *fn = getstr (": append-to-file ");
    char fnbuf[MaxPathNameLen];	/* if only C had real strings...  Then I
				   wouldn't have to resort to returning
				   strings in static & getting bitten by
				   later overwrites. */
    if (fn == 0)
	return 0;
    if (*fn=='\0'){
	error("I don't like empty file names!");
	return 0;
    }
    strcpy(fnbuf, SaveAbs (fn));
    WriteFile (fnbuf, 1);
    return 0;
}

VisitFile (fn, CreateNew, WindowFiddle)
char   *fn; {
    char    fullname[500];
    register struct buffer *b,
                           *oldb = bf_cur;
    if (fn == 0 || *fn == 0)
	return 0;
    strcpy (fullname, SaveAbs (fn));
    for (b = buffers;
	    b && (b -> b_fname == 0 || strcmp (fullname, b -> b_fname) != 0);
	    b = b -> b_next);
    if (b)
	SetBfp (b);
    else {
	char   *bufname;
	register char  *p = fullname;
	bufname = fullname;
	while (*p)
	    if (*p++ == '/' && *p)
		bufname = p;
	if (FindBf (bufname)) {
	    if (interactive && AskAboutBufferNames) {
		p = getstr (
"Buffer name %s is in use, type a new name or <CR> to clobber: ", bufname);
		if (p == 0)
		    return 0;
		if (*p)
		    bufname = p;
	    }
	    else {
		static char SynthName[100];
		register    seq = 1;
	    /* I'm making the (perhaps) brash assumption that the
	       following loop is guaranteed to terminate.  To those who
	       think that this is an inefficient technique: you have
	       been deluded. */
		do sprintf (SynthName, "%s<%d>", bufname, ++seq);
		while (FindBf (SynthName));
		bufname = SynthName;
	    }
	}
	SetBfn (bufname);
	if (!readfile (fullname, 1, CreateNew) && !CreateNew) {
	    SetBfp (oldb);
	    return 0;
	}
	else {
	    bf_cur -> b_kind = FileBuffer;
	    if (bf_cur -> b_fname)
		free (bf_cur -> b_fname);
	    if (bf_cur -> b_checkpointfn)
		free (bf_cur -> b_checkpointfn);
	    bf_cur -> b_checkpointfn = 0;
	    bf_cur -> b_checkpointed = 0;
	    bf_cur -> b_fname = savestr (fullname);
	}
    }
    if (WindowFiddle) WindowOn (bf_cur);
    if (b == 0)
	DoAuto (fn);
    return 1;
}

readfile (fn, erase, CreateNew)
char   *fn; {
    struct stat st;
    register int    fd;
    register int    n,
                    i;
    if (fn == 0)
	return 0;
    if (*fn == 0) {
	error ("Aw come on, if you want me to read something I need file name");
	return 0;
    }
    if (stat (fn, &st) < 0 || (fd = open (fn, 0)) < 0) {
	error (CreateNew ? "New file: %s" : "Can't find \"%s\"", fn);
	return 0;
    }
    Cant1LineOpt++;
    RedoModes++;
    WidenRegion ();
    if (erase)
	EraseBf (bf_cur);
    GapTo (dot);
    DoneIsDone ();
    if (GapRoom (st.st_size))
	return 0;
    n = 0;
    while ((i = read (fd, bf_p1 + bf_s1 + 1 + n, st.st_size - n)) > 0)
	n += i;
    close (fd);
    if (n > 0) {
	bf_s1 += n;
	bf_gap -= n;
	bf_p2 -= n;
    }
    if (n == 0)
	message ("Empty file.");
    if (i < 0)
	error ("Error reading file \"%s\"", fn);
    if (erase) {
	if (bf_cur -> b_fname)
	    free (bf_cur -> b_fname);
	if (bf_cur -> b_checkpointfn) {
	    free (bf_cur -> b_checkpointfn);
	    bf_cur -> b_checkpointfn = 0;
	    bf_cur -> b_checkpointed = 0;
	}
	bf_cur -> b_fname = savestr (fn);
	bf_cur -> b_kind = FileBuffer;
    }
    return i >= 0;
}

X/* Given a file name and an extension to be forced, concoct a new file name
   which is their concatenation, accounting for the restricton on the length
   of the last component of a file name begin 14 characters. */
char *ConcoctName (fn, extension)
char *fn, *extension;
{
    static  char name[200];
#ifdef PrependExtension
    register    extlen;
#endif
    register char  *p,
                   *s,
                   *tail;
#ifdef PrependExtension
    for (p = extension, extlen = 0; *p++;)
	extlen++;
#endif
    for (s = fn, p = tail = name; *p = *s++; p++)
	if (*p == '/')
	    tail = p + 1;
#ifdef PrependExtension
    /* put the extension at the beginning and let system truncate
     * name
     */
    for( ; p >= tail ; *(p+extlen) = *p, p-- );	/* shift right */
    for( p = extension ; *p ; *tail++ = *p++);  /* insert extension */
#else
    if (p - tail > 10)
	p = tail + 10;
    for(s=extension; *p++ = *s++; );
#endif
    return name;
}

X/* write the current buffer to the named file; returns true iff
   successful.  Appends to the file if AppendIt is >0, does a checkpoint
   style write if AppendIt is <0. */
WriteFile (fn, AppendIt)
register char  *fn; {
    register    fd;
    register    nc = bf_s1 + bf_s2;
    int     mode = 0666 & ~Umask;
    int     TempFile =	   fn[0] == '/' && fn[1] == 't' && fn[2] == 'm'
			&& fn[3] == 'p' && fn[4] == '/';
    if(AppendIt<0) mode = 0600 & ~Umask;
    if(AppendIt>=0 && !access(fn,0) && access(fn,2)) {
	error("File %s cannot be written",fn);
	return 0;
    }
    if (fn == 0 || *fn == 0)
	return 0;

    /* (ACT) Dont write if ReadOnly */

    if (AppendIt >= 0 && bf_mode.md_ReadOnly) {
	error ("File %s is read-only", fn);
	return 0;
    }

    if (AppendIt>0) {
	fd = open (fn, 1);
	if (fd < 0)
	    fd = creat (fn, mode);
	if (fd >= 0)
	    if (lseek (fd, 0, 2) < 0)
		close (fd), fd = -1;
    }
    else {
	if (AppendIt>=0 && BackupBeforeWriting && !TempFile
		&& !bf_cur -> b_BackedUp) {
	    struct stat st;
	    char    *name = ConcoctName (fn, BackupExtension);
	    bf_cur -> b_BackedUp++;
	    if (stat (fn, &st) == 0)
		mode = st.st_mode;
	    if (BackupByCopying
		    || st.st_nlink>1 && BackupByCopyingWhenLinked) {
		int     ifd,
		        ofd = -1,
		        n;
		char    buf[2048];
		if ((ifd = open (fn, 0)) >= 0
			&& (ofd = creat (name, 0600)) >= 0)
		    while ((n = read (ifd, buf, sizeof buf)) > 0)
			write (ofd, buf, n);
		if (ifd >= 0)
		    close (ifd);
		if (ofd >= 0)
		    close (ofd);
	    }
	    else {
		unlink (name);
		link (fn, name);
		unlink (fn);
	    }
	}
	fd = creat (fn, mode);
	if (fd >=0 && (mode & ~Umask) != mode)
	    chmod (fn,mode);
    }
    if (fd < 0) {
	error ("Can't write %s", fn);
	return 0;
    }
    if (FilesShouldEndWithNewline
	&& nc > 0 && CharAt (nc) != '\n' && interactive && AppendIt>=0 &&
	    *getnbstr (
		"\"%s\" doesn't end with a newline, should I add one? ",
		bf_cur->b_name) == 'y')
	InsertAt (nc + 1, '\n');
    if (write (fd, bf_p1 + 1, bf_s1) < 0
	    || write (fd, bf_p1 + 1 + bf_s1 + bf_gap, bf_s2) < 0) {
	error ("IO error writing %s", fn);
	close (fd);
	return 0;
    }
    if(!err) {
	bf_modified = 0;
	bf_cur -> b_checkpointed = 0;
	if (!TempFile && AppendIt >= 0)/* (ACT) Don't message about CKP's */
	    message ("Wrote %s", fn);
    }
    close (fd);
    Cant1LineOpt++;		/* Force update of the mode line */
    return 1;
}

X/* fopenp opens the file fn with the given IO mode using the given
   search path.  The actual file name is returned in fnb.  The search
   path is interpreted in the same way as the PATH environment variable
   is interpreted by exec?p().  This routine normally comes from the CMU
   C library, but since Emacs is being distributed I have to roll-my-own.
   */
FILE *
fopenp (path, fn, fnb, mode)
register char *path;
char *fn, *fnb, *mode;
{
    register FILE *fd;
    char AbsForm[MaxPathNameLen];
    register char  *dst,
                   *src;
    if (path == 0)
	path = "";
    if (*fn=='~') {
	abspath (fn, AbsForm);
	fn = AbsForm;
    }
    if (*fn=='/'){
	if(( fd = fopen(fn, mode)) != NULL) {
	    strcpy(fnb, fn);
	    return fd;
	}
	return NULL;
    }
    do {
	dst = fnb;
	while (*path && *path != ':')
	    *dst++ = *path++;
	if (dst != fnb)
	    *dst++ = '/';
	for (src = fn; *dst++ = *src++;);
	if ((fd = fopen (fnb, mode)) != NULL)
	    return fd;
    } while (*path++);
    return NULL;
}

X/* returns true if modified buffers exist */
ModExist () {
    register struct buffer *b;
    register modcount = 0;

    SetBfp (bf_cur);
    for (b = buffers; b; b = b -> b_next)
	if (b -> b_modified && b -> b_kind == FileBuffer)
	    modcount++;
    return modcount;
}

static
ModificationsExist () {
    MLvalue -> exp_int = ModExist();
    MLvalue -> exp_type = IsInteger;
    return 0;
}

X/* write all modified buffers; return true iff OK */
ModWrite () {
    register struct buffer *b;
    struct buffer  *old = bf_cur;
    register WriteErrors = 0;
    for (b = buffers; b; b = b -> b_next) {
	SetBfp (b);
	if (bf_cur->b_kind==FileBuffer && bf_modified
	    && WriteThis () == 0
	    && 'y' != *getnbstr ("Can't write buffer %s, can I ignore it? ",
				b -> b_name)){
	    WriteErrors++;
	}
    }
    SetBfp (old);
    return !err && !WriteErrors;
}

static DisplayCheckpointMessage;/* if off, prevents message */

CheckpointEverything () {
    register struct buffer *b;
    struct buffer  *old = bf_cur;
    register    WriteErrors = 0, modcnt;
    int Checkpointed = 0;
    for (b = buffers; b; b = b -> b_next)
	if (b -> b_mode.md_NeedsCheckpointing
		&& b -> b_checkpointed < (modcnt = b == bf_cur ? bf_modified
						: b -> b_modified)) {
	    SetBfp (b);
	    if (b -> b_checkpointfn == 0)
		b -> b_checkpointfn =
		    savestr (ConcoctName (b -> b_fname ? b -> b_fname
						    : b -> b_name,
					  CheckpointExtension));
	    WriteErrors |= WriteFile (b -> b_checkpointfn, -1) == 0;
	    Checkpointed++;
	    b ->b_checkpointed = bf_modified = modcnt;
	}
    SetBfp (old);
    if(!WriteErrors && Checkpointed && DisplayCheckpointMessage)
	message("Checkpointed...");
    if (WriteErrors) err = 0;	/* to avoid having errors during checkpoints
				   blow away functions in the middle of
				   execution. */
    return 0;
}

InitFIO () {
    umask(Umask = umask(077));
#ifdef DumpableEmacs
    if (!Once)
#endif
    {
	DefIntVar ("backup-before-writing", &BackupBeforeWriting);
	DefIntVar ("backup-by-copying", &BackupByCopying);
	DefIntVar ("backup-by-copying-when-linked", &BackupByCopyingWhenLinked);
	DefIntVar ("unlink-checkpoint-files", &UnlinkCheckpointFiles);
	DefIntVar ("files-should-end-with-newline", &FilesShouldEndWithNewline);
	FilesShouldEndWithNewline = 1;
	DefIntVar ("ask-about-buffer-names", &AskAboutBufferNames);
	AskAboutBufferNames = 1;
	DefIntVar ("display-checkpoint-message", &DisplayCheckpointMessage);
	DisplayCheckpointMessage = 1;
#ifdef LIBNDIR
	DefIntVar ("fast-file-searches", &FastFileSearches);
	FastFileSearches = 1;
#endif LIBNDIR
	setkey (CtlXmap, (Ctl ('F')), WriteFileExit, "write-file-exit");
	setkey (CtlXmap, (Ctl ('R')), ReadFile, "read-file");
	setkey (CtlXmap, (Ctl ('I')), InsertFile, "insert-file");
	setkey (CtlXmap, (Ctl ('V')), VisitFileCommand, "visit-file");
	setkey (CtlXmap, (Ctl ('Q')), VisitExistingFileCommand, "visit-existing-file");
	setkey (CtlXmap, (Ctl ('W')), WriteNamedFile, "write-named-file");
	setkey (CtlXmap, (Ctl ('M')), WriteModifiedFiles, "write-modified-files");
	setkey (CtlXmap, (Ctl ('S')), WriteCurrentFile, "write-current-file");
	defproc (AppendToFile, "append-to-file");
	defproc (UnlinkFile, "unlink-file");
	defproc (FileExists, "file-exists");
	defproc (ModificationsExist, "modifications-exist");
	defproc (PerformAutoMode, "perform-automode-action");
	defproc (CheckpointEverything, "checkpoint");
	defproc (AutoExecute, "auto-execute");
	defproc (SetFileName, "set-file-name");
    }
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 fileio.c
	/bin/echo -n '	'; /bin/ls -ld fileio.c
fi
/bin/echo 'Extracting window.c'
sed 's/^X//' <<'//go.sysin dd *' >window.c
X/* Window manipulation primitives */

X/*		Copyright (c) 1981,1980 James Gosling		*/

X/* DJH added substitute Ding() for putchar(07);			*/

#include "config.h"
#include "buffer.h"
#include "keyboard.h"
#include "window.h"
#include "display.h"
#include <stdio.h>
#include <ctype.h>
#include "mlisp.h"

static
struct marker *OneLStart;	/* Starting character position of the line
				   containing dot -- used when doing the
				   one line redisplay optimization. */
static OneLValid;		/* True iff OneLStart points at something
				   valid */
static OneLLine;		/* The display line which contains dot */
static MBLine;			/* The line on which the minibuf starts */
static LineWrapped;		/* True iff the line just dumped has
				   wrapped, this really slows down the the
				   redisplay if it's the current line. */
static QuickRD;			/* True iff quick redisplay alg. is to be
				   used */
static UseTime;			/* A counter used to set the time of last use
				   of a window: for selecting the LRU
				   window */
static GSaveMiniBuf;		/* True iff the cursor is in the minibuf */
char GlobalModeString[30];	/* The global-mode-string variable */
int PopUpWindows;		/* True iff new windows should be
				   automatically selected by commands that
				   play with other buffers (eg. ^X^V and
				   ^X^B) */
				/* changed from static to int 13-Jan-83
				   so that minibuf.c could use and reset
				   it. - BNI */
static WrapLines;		/* True iff long lines should wrap around */
static ScrollStep;		/* The number of lines to try scrolling a
				   window by when dot leaves the window; if
				   it is <=0 then dot is centered in the
				   window */
static SplitHeightThreshhold;	/* If a window is larger than this it will be
				   considered splitabble when a window is to
				   be popped up (rather than picking the LRU
				   window) */
static MouseX;			/* The X screen coordinate of the mouse */
static MouseY;			/* The Y screen coordinate of the mouse */
static struct window *MouseWin;	/* The window corresponding to
				   (MouseX,MouseY) */
static MouseDot;		/* The character position corresponding to
				   (MouseX,MouseY) */
int    QuitDoRstDsp;		/* When set, quit should do a RstDsp */
int    QuitDoQuitMpx;		/* When set, quit should do a QuitMpx */
struct window  *SplitWin ();

X/* Move dot to the buffer and character corresponding to some absolute X
   and Y coordinate. */
MoveDotToXY () {
    MouseX = getnum ("X coordinate: ");
    if (!err)
	MouseY = getnum ("Y coordinate: ");
    if (!err) {
	MouseWin = 0;
	Cant1LineOpt++;
	DoDsp (1);
	if (MouseWin == 0)
	    error ("The mouse isn't pointing at a part of a buffer");
	else {
	    SetWin (MouseWin);
	    SetDot (MouseDot);
	}
    }
    return 0;
}
X/* initialize the window system */
InitWin () {
    register struct window *w;
    
#ifdef DumpableEmacs
    if (Once) {			/* Restarting, clean up old junk */
	for (w = windows; w; w = w -> w_next) {
	    DestMark (w -> w_dot);
	    DestMark (w -> w_start);
	    free (w);
	}
    } else
#endif
    {
	PopUpWindows = 1;
	SplitHeightThreshhold = 20;
	OneLStart = NewMark ();
	OneLValid = 0;
	DefStrVar ("global-mode-string", GlobalModeString);
	DefIntVar ("scroll-step", &ScrollStep);
	DefIntVar ("quick-redisplay", &QuickRD);
	DefIntVar ("wrap-long-lines", &WrapLines);
	DefIntVar ("pop-up-windows", &PopUpWindows);
	DefIntVar ("split-height-threshhold", &SplitHeightThreshhold);
	defproc (MoveDotToXY, "move-dot-to-x-y");
    }
    w = (struct window *) malloc (sizeof (struct window));
    windows = w;
    SetDot (1);
    w -> w_height = ScreenLength;
    w -> w_prev = 0;
    w -> w_dot = NewMark ();
    SetMark (w -> w_dot, bf_cur, 1);
    w -> w_start = NewMark ();
    SetMark (w -> w_start, bf_cur, 1);
    w -> w_force = 0;
    w -> w_next = 0;
    w -> w_buf = bf_cur;
    wn_cur = w;
    SetWin (SplitWin (w));
    TieWin (wn_cur, minibuf);
    ChangeWindowSize (1 - wn_cur -> w_height);
    SetWin (w);
}

X/* set the current window */
SetWin (w)
struct window  *w; {
    if (w == 0)
	return;
    w -> w_lastuse = UseTime++;
    SetBfp (w -> w_buf);
    wn_cur = w;
    bf_cur = 0;
    Cant1WinOpt++;
    SetBfp (w -> w_buf);
}

struct window  *SplitWin (w)
register struct window *w; {
    register struct window *n;
    register struct buffer *old = bf_cur;
    if (w -> w_height<=4) {
	error ("You can't have windows smaller than two lines high.");
	return w;
    }
    n = (struct window *) malloc (sizeof (struct window));
    n -> w_prev = w;
    n -> w_force = 0;
    n -> w_next = w -> w_next;
    w -> w_next = n;
    if (n -> w_next)
	n -> w_next -> w_prev = n;
    n -> w_height = w -> w_height / 2;
    w -> w_height -= n -> w_height;
    n -> w_dot = NewMark ();
    n -> w_lastuse = 0;
    n -> w_buf = w -> w_buf;
    n -> w_start = NewMark ();
    SetMark (n -> w_dot, n->w_buf, ToMark (w -> w_dot));
    SetMark (n -> w_start, n->w_buf, ToMark (w -> w_start));
    SetBfp (old);
    Cant1WinOpt++;
    return n;
}

X/* split the largest window, and return a pointer to it */
struct window *SplitLargestWindow () {
    register struct window *w, *bestw;
    register besth = -1;
    for (w = windows; w -> w_next; w = w->w_next)
	if (w->w_height>besth) besth = w->w_height, bestw = w;
    return SplitWin (bestw);
}

X/* Delete the indicated window */
DelWin (w)
register struct window *w; {
    if (w -> w_next == 0)	/* Can't delete the last window -- it's the
				   minibuf */
	return 0;
    if (w -> w_prev) {
	w -> w_prev -> w_height += w -> w_height;
	w -> w_prev -> w_next = w -> w_next;
    }
    else {
	if (w -> w_next -> w_next == 0)
	    return 0;
	windows = w -> w_next;
	windows -> w_height += w -> w_height;
    }
    if (w -> w_next)
	w -> w_next -> w_prev = w -> w_prev;
    if (w == wn_cur)
	SetWin (w -> w_prev ? w -> w_prev : windows);
    DestMark (w->w_dot);
    DestMark (w->w_start);
    Cant1WinOpt++;

    /* I'm not sure if this'll work.  Delete it if it screws up. */
    /* ACT 25 Jun 1983 */
    free (w);

    return 0;
}

X/* tie a window to a buffer */
TieWin (w, b)
register struct window *w;
register struct buffer  *b; {
    register newdot;
    if (b == 0 || w == 0 || w -> w_buf == b || b -> b_kind == DeletedBuffer)
	return;
    w -> w_buf = b;
    w -> w_force = 0;
    w -> w_lastuse = UseTime++;
    newdot = b == bf_cur ? dot : b -> b_EphemeralDot;
    SetMark (w -> w_dot, b, newdot);
    SetMark (w -> w_start, b, 1);
}

X/* Change the height of the pointed to window by delta; returns true iff
   the change succeeds.  Chains forward if dir>0, backward if dir<0 in
   attempting to find a suitable window. */
ChgWHeight (w, delta, dir)
register struct window *w; {
    while (w)
	if (w -> w_height + delta >= (w -> w_next ? 2 : 1)
		&& (dir == 0 || w -> w_next)) {
	    Cant1WinOpt++;
	    w -> w_height += delta;
	    return 1;
	}
	else
	    w = dir == 0 ? 0 : dir < 0 ? w -> w_prev : w -> w_next;
    return 0;
}

X/* find the least recently used window; split if only one window */
struct window  *LRUwin () {
    register struct window *w,
                           *bestw = 0;
    register    youngest = 07777777777;
    register    LargestHeight = 0;
    for (w = windows; w -> w_next; w = w -> w_next) {
	if ((w -> w_buf == bf_cur ? bf_s1 + bf_s2
		    : w -> w_buf -> b_size1 + w -> w_buf -> b_size2) == 0)
	    return w;
	if (w -> w_lastuse < youngest && w != wn_cur) {
	    bestw = w;
	    youngest = w -> w_lastuse;
	}
	if (w -> w_height > LargestHeight)
	    LargestHeight = w -> w_height;
    }
    if (bestw == 0 || LargestHeight >= SplitHeightThreshhold)
	bestw = SplitLargestWindow ();
    return bestw;
}

X/* make sure that the current window is on the given buffer, either
   by picking the window that already contains it, the LRU window,
   or some brand new window */
WindowOn (bf)
struct buffer  *bf; {
    register struct window *w;
    if ((w = wn_cur) -> w_buf != bf)
	for (w = windows; w; w = w -> w_next)
	    if (w -> w_buf == bf)
		break;
    if (!w)
	w = (PopUpWindows || wn_cur->w_next == NULL) ? LRUwin () : wn_cur;
    TieWin (w, bf);
    SetWin (w);
}

X/* full screen update -- called when absolutely nothing is known or
   many things have been fiddled with */
FullUpd () {
    register struct buffer *keep_bf = bf_cur,
                           *hit_bf = wn_cur -> w_buf;
    register struct window *w = windows;
    register    sline = 1;
    register    hits = 0;
    register    slow = 0;
    while (w) {
	SetBfp (w -> w_buf);
	if (bf_cur == hit_bf)
	    hits++;
	slow |= w -> w_force;
	if ( /* w != wn_cur */ 0)
	    DumpWin (w, sline, 1);
	else {
	    register    ldot;
	    register    dumpstate = 0;
	    if (w != wn_cur)
		ldot = dot, SetDot (ToMark (w -> w_dot));
	    while (dumpstate >= 0 && DumpWin (w, sline, dumpstate == 0)) {
		slow++;
		if (w -> w_force) {
		    SetDot (dumpstate ? ToMark (w -> w_start)
			    : ScanBf ('\n', ToMark (w -> w_start),
				w -> w_height / 2));
		    if (w != wn_cur)
			SetMark (w -> w_dot, w -> w_buf, dot);
		    if (dumpstate++)
			w -> w_force = 0;
		}
		else {
		    register    old,
		                next;
		    switch (dumpstate) {
			case 0: 
			    dumpstate++;
			    if (ScrollStep > 0) {
				old = ToMark (w -> w_start);
				next = ScanBf ('\n', old,
				        old>dot ? -ScrollStep-1 : ScrollStep);
				if (dot >= next)
				    break;
			    }
			case 1: 
			    next = ScanBf ('\n', dot, -(w -> w_height / 2));
			    dumpstate++;
			    break;
			case 2: 
			    next = ScanBf ('\n', (old = ToMark (w -> w_start)), 1);
			    if (old < next && next <= dot)
				break;
			default: 
			    dumpstate++;
			    next = ToMark (w -> w_start) + 50;
			    if (dumpstate > 10)
				dumpstate = -1;
			case -1: 
			    break;
		    }
		    if (next <= dot)
			SetMark (w -> w_start, w -> w_buf, next);
		    else
			dumpstate = -1;
		}
	    }
	    if (w != wn_cur)
		SetDot (ldot);
	    w -> w_force = 0;
	}
	sline += w -> w_height;
	if (RedoModes && w -> w_next)
	    DumpMode (w, sline - 1);
	w = w -> w_next;
    }
    CantEverOpt = hits > 1 && !QuickRD;
    SetBfp (keep_bf);
    return slow;
}

X/* Dump the mode line for window w on line n -- assumes the current buffer
   is the one associated with window w */
DumpMode (w, l)
register struct window *w; {
    char    buf[300],
            tbuf[20];
    register char  *p = buf;
    register char  *s = bf_mode.md_ModeFormat;
    register char  *str;
    register char   c;
    int     width;
X/* ACT 17-Oct-1982 Added '-' format */
    int     negative = 0;
#define ModeC(c) if (p>buf+(sizeof buf)-2) goto out; else *p++ = c;
    while (c = *s++)
	if (c == '%') {
	    str = 0;
	    width = 0;
	    if (*s == '-') {
		++negative;
		++s;
	    }
	    while (isdigit (c = *s++))
		width = width * 10 + (c - '0');
	    switch (c) {
		case 0: 
		    goto out;
		default: 
		    ModeC (c);
		    break;
		case 'b': 
		    str = bf_cur -> b_name;
		    break;
		case 'f': 
		    if ((str = bf_cur -> b_fname) == 0)
			str = "[None]";
		    break;
		case 'F':	/* ACT 17-Oct-1982 */
		    if ((str = bf_cur -> b_fname) == 0)
			str = "[None]";
		    else {
			register char *str1 = str;
			while (*str1) if (*str1++ == '/' && *str1) str = str1;
		    }
		    break;
		case 'm': 
		    str = bf_mode.md_ModeString;
		    break;
		case 'a':	/* ACT 19-Aug-1983 */
		    str = bf_mode.md_AbbrevOn ? "abbrev" : "";
		    break;
		case 'M': 
		    str = GlobalModeString;
		    break;
		case '*': 
		    str = bf_modified ? "*" : "";
		    break;
		case 'p': {
			int     tl = bf_s1 + bf_s2,
			        d;
			d = w == wn_cur ? dot : ToMark (w -> w_dot);
			if (d <= 1)
			    str = "Top";
			else
			    if (d > tl)
				str = "Bottom";
			    else {
				sprintf (tbuf, "%2d%%", (d - 1) * 100 / tl);
				str = tbuf;
			    }
			break;
		    }
		case 'D':	/* ACT 25 Jun 1983 */
		    sprintf (tbuf, "%d", RecurseDepth - MinibufDepth);
		    str = tbuf;
		    break;
		case '[': 
			str = RecurseDepth-MinibufDepth > 10
					? "*["
					: ("[[[[[[[[[[" + 10)
						- (RecurseDepth-MinibufDepth);
		    break;
		case ']': 
			str = RecurseDepth-MinibufDepth > 10
					? "*]"
					: ("]]]]]]]]]]" + 10)
						- (RecurseDepth-MinibufDepth);
		    break;
	    }
	    if (str) {
		if (negative && width) {
		    if ((negative = strlen (str)) > width)
			str += negative - width;
		    else {
			while (width > negative) {
			    width--;
			    ModeC (' ');
			}
		    }
		}
		while (*str) {
		    ModeC (*str++);
		    if (--width == 0)
			break;
		}
		while (--width >= 0)
		    ModeC (' ');
	    }
	}
	else
	    ModeC (c);
out: 
    *p++ = 0;
    DumpStr (buf, 300, l, 1);
}

X/* dump the indicated string (with maximum length n) to line l */
DumpStr (s, n, l, highlight)
register char  *s; {
    register    col = 1;
    register    setcurs = s == MiniBuf && InMiniBuf;
    setpos (l, col);
    if (highlight)
	HighLine ();
    while (--n >= 0) {
	register char   c = *s++;
	if (c == 0)
	    break;
	if (c == 011 && bf_mode.md_TabSize >= 1) {
	    col = ((col - 1) / bf_mode.md_TabSize + 1)
				* bf_mode.md_TabSize + 1;
	    if (col < ScreenWidth)
		setpos (l, col);
	}
	else
	    if (c < 040 || c >= 0177)
		if (CtlArrow && (c & 0200) == 0) {
		    col += 2;
		    if (col <= ScreenWidth) {
			dsputc ('^');
			dsputc (c < 040 ? (c & 037) + 0100 : '?');
		    }
		}
		else {
		    col += 4;
		    if (col <= ScreenWidth) {
			dsputc ('\\');
			dsputc (((c >> 6) & 3) + '0');
			dsputc (((c >> 3) & 7) + '0');
			dsputc ((c & 7) + '0');
		    }
		}
	    else {
		col++;
		if (col <= ScreenWidth)
		    dsputc (c);
	    }
    }
    if (col > ScreenWidth) {
	setpos (l, ScreenWidth);
	dsputc ('$');
    }
    if (setcurs) {
	cursY = l;
	cursX = col > ScreenWidth ? ScreenWidth : col;
    }
}

X/* dump one line from the current buffer starting at character n onto
   line l; setting cursX and cursY if appropriate */
DumpBfl (n, l, w)
register struct window *w;
register    n; {
    register    col = ScreenWidth + 1 - left;
    register    lim = NumCharacters;
    int     misseddot = 1;
    register char   c;
    while (1) {
	if (n == dot) {
	    if (w == wn_cur && (!GSaveMiniBuf || !InMiniBuf)) {
		cursX = col;
		cursY = l;
		DotCol = col;
		ColValid++;
		if (cursX > ScreenWidth)
		    cursX = ScreenWidth;
	    }
	    misseddot = 0;
	}
	if (n > lim) {
	    n++;
	    c = '\n';
	    break;
	}
	if (MouseY == l && MouseX<col && MouseWin==0) {
	    MouseWin = w;
	    MouseDot = n-1;
	}
	c = CharAt (n);
	n++;
	if (c == '\n')
	    break;
	if (c == 011 && bf_mode.md_TabSize >= 1) {
	    col = ((col - 1) / bf_mode.md_TabSize + 1)
				* bf_mode.md_TabSize + 1;
	    if (col < ScreenWidth)
		setpos (l, col);
	    else
		if (WrapLines) {
		    n--;
		    break;
		}
	}
	else
	    if (c < 040 || c >= 0177)
		if (CtlArrow && (c & 0200) == 0) {
		    col += 2;
		    if (col <= ScreenWidth) {
			dsputc ('^');
			dsputc (c < 040 ? (c & 037) + 0100 : '?');
		    }
		    else
			if (WrapLines) {
			    n--;
			    break;
			}
		}
		else {
		    col += 4;
		    if (col <= ScreenWidth) {
			dsputc ('\\');
			dsputc (((c >> 6) & 3) + '0');
			dsputc (((c >> 3) & 7) + '0');
			dsputc ((c & 7) + '0');
		    }
		    else
			if (WrapLines) {
			    n--;
			    break;
			}
		}
	    else {
		col++;
		if (col <= ScreenWidth)
		    dsputc (c);
		else
		    if (WrapLines) {
			n--;
			break;
		    }
	    }
    }
    if (MouseY == l && MouseWin==0) {
	MouseWin = w;
	MouseDot = n-1;
    }
    LineWrapped = 0;
    if (col > ScreenWidth || c != '\n') {
	setpos (l, ScreenWidth);
	dsputc (WrapLines ? '\\' : '$');
	if (WrapLines)
	    LineWrapped++;
    }
    return misseddot ? n : -n;
}

X/* dump the text from the indicated window on the indicated line;
   the current buffer must be the one tied to this window */
DumpWin (Window, Line, CanMove)
register struct window *Window;
register    Line; {
    register    left = Window -> w_next ? Window -> w_height - 1
    :           Window -> w_height;
    register    n = ToMark (Window -> w_start);
    int     misseddot = 1;
    int     DoClear = 0;
    if (CanMove && ((n > FirstCharacter && CharAt (n - 1) != '\n')
		|| n < FirstCharacter)) {
	n = n < FirstCharacter ? FirstCharacter : ScanBf ('\n', n, -1);
	SetMark (Window -> w_start, Window -> w_buf, n);
    }
    if (Window -> w_next == 0) {
	MBLine = Line;
	if (GSaveMiniBuf && MiniBuf == 0)
	    return 0;
	clearline (Line);
	if (MiniBuf) {
	    if (n == 1)
		DumpStr (MiniBuf, 300, Line, 0);
	    if (*MiniBuf == 0) {
		while (--left > 0)
		    clearline (++Line);
		return 0;
	    }
	}
    }
    else
	clearline (Line);
    while (--left >= 0) {
	register    next;
	if (DoClear)
	    clearline (Line);
	DoClear++;
	next = DumpBfl (n, Line++, Window);
	if (next < 0) {
	    if (Window == wn_cur) {
		SetMark (OneLStart, bf_cur, LineWrapped ? 1 : n);
		OneLValid = !LineWrapped;
		OneLLine = Line - 1;
	    }
	    next = -next;
	    misseddot = 0;
	}
	n = next;
    }
    return misseddot;
}

X/* Leave emacs after (optionally) spitting some expletive on the tty */
X/* VARARGS 1 */
quit (code, fmt, args) char *fmt; {
#ifdef subprocesses
    kill_processes ();
#endif
    if (QuitDoQuitMpx)
	QuitMpx ();
    if (QuitDoRstDsp)
	RstDsp ();
    if (fmt)
	_doprnt (fmt, &args, stderr);
#ifdef OneEmacsPerTty
    UnlockTty ();
#endif
    exit (code);
}

X/* Scan the current buffer for the k'th occurrence of character c,
   starting at position n; k may be negative.  Returns the position
   of the character following the one found */
ScanBf (c, n, k)
char    c;
register    n; {
    while (k)
	if (k > 0) {
	    do {
		if (n > NumCharacters)
		    return n;
		if (CharAt (n) == c)
		    break;
		n++;
	    } while (1);
	    if(--k) n++;
	}
	else {
	    do {
		n--;
		if (n < FirstCharacter)
		    return FirstCharacter;
		if (CharAt (n) == c)
		    break;
	    } while (1);
	    k++;
	}
    return n + 1;
}

X/* do a screen update, taking possible shortcuts into account */
DoDsp (SaveMiniBuf) {
    register    SlowUpdate = 0;
    register    DoneMiniBuf = 0;
    GSaveMiniBuf = SaveMiniBuf;
    if (ScreenGarbaged || err || (LastRedisplayPaused && !InMiniBuf))
	Cant1WinOpt++, DumpMiniBuf++, LastRedisplayPaused = 0;
    if (Cant1WinOpt)
	Cant1LineOpt++, RedoModes++;
    if (!Cant1LineOpt && OneLValid && !OneLStart -> m_modified
	    && OneLStart -> m_buf == bf_cur) {
	register    n = ToMark (OneLStart);
	clearline (OneLLine);
	if (MiniBuf && wn_cur -> w_next == 0) {
	    if (n == 1)
		DumpStr (MiniBuf, 300, OneLLine, 0);
	    DoneMiniBuf++;
	}
	if (DumpBfl (n, OneLLine, wn_cur) < 0 && !LineWrapped)
	    goto update;	/* we made it ! */
	else
	    if (!WrapLines)
		SlowUpdate = -1;
    }
    DoneMiniBuf++;
    SlowUpdate++;
    OneLValid = 0;
    if (FullUpd ())
	SlowUpdate = 1;
update: 
    if (MiniBuf && (!GSaveMiniBuf || *MiniBuf)) {
	if (!DoneMiniBuf) {
	    clearline (MBLine);
	    DumpStr (MiniBuf, 300, MBLine, 0);
	}
	if (ResetMiniBuf) {
	    MiniBuf = ResetMiniBuf;
	    if (*ResetMiniBuf == 0) ResetMiniBuf = 0;
	}
	else
	    MiniBuf = *MiniBuf ? "" : 0;
    }
    UpdateScreen (SlowUpdate);
    if (err) {
	Ding ();
	err = 0;
    }
    Cant1LineOpt = 0;
    Cant1WinOpt = CantEverOpt;
    fflush (stdout);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 window.c
	/bin/echo -n '	'; /bin/ls -ld window.c
fi
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris.umcp-cs@UDel-Relay