[comp.sources.misc] v03i034: mg 2a part 10 of 15

BLARSON@ECLA.USC.EDU (Bob Larson) (05/26/88)

comp.sources.misc: Volume 3, Issue 34
Submitted-By: "Bob Larson" <BLARSON@ECLA.USC.EDU>
Archive-Name: mg2a/Part10

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	sys/osk/fileio.c
#	sys/osk/makefile
#	sys/osk/readme.osk
#	sys/osk/spawn.c
#	sys/osk/sysdef.h
#	sys/osk/ttyio.c
#	sys/osk/varargs.h
#	sys/vms/aaareadme.1st
#	sys/vms/ccom.com
#	sys/vms/fileio.c
#	sys/vms/fparse.c
#	sys/vms/make.com
#	sys/vms/mg.com
#	sys/vms/mg.opt
#	sys/vms/spawn.c
#	sys/vms/sysdef.h
#	sys/vms/trnlnm.c
#	sys/vms/unixfns.mar
# This archive created: Mon May 23 18:09:33 1988
# By:	blarson
if test -d sys
then true
else mkdir sys
fi
if test -d sys/osk
then true
else mkdir sys/osk
fi
if test -d sys/vms
then true
else mkdir sys/vsm
fi
cat << \SHAR_EOF > sys/osk/fileio.c
/*
 * Os9/68k fileio.c for MicroGnuEmacs by Robert A. Larson
 *	 system dependent file io routines
 */
#include "def.h"
#include "kbd.h"
#include <modes.h>
#include <dir.h>
#include <direct.h>

char	*getenv(), *index();

static	FILE	*ffp;

/*
 * Open a file for reading.
 */
ffropen(fn)
char	*fn;
{
	if ((ffp=fopen(fn, "r")) == NULL) return FIOFNF;
	return FIOSUC;
}

/*
 * Open a file for writing.
 */
ffwopen(fn)
char	*fn;
{
	if ((ffp=fopen(fn, "w")) == NULL) {
		ewprintf("Cannot open file %s for writing", fn);
		return FIOERR;
	}
	return FIOSUC;
}

/*
 * Close a file.
 * Should look at the status.
 */
ffclose()
{
	fclose(ffp);
	return FIOSUC;
}

/*
 * Write a buffer to the already
 * opened file. bp points to the
 * buffer. Return the status.
 * Check only at the newline and
 * end of buffer.
 */
ffputbuf(bp)
BUFFER *bp;
{
    register char *cp;
    register char *cpend;
    register LINE *lp;
    register LINE *lpend;

    lpend = bp->b_linep;
    lp = lforw(lpend);
    do {
	cp = &ltext(lp)[0];		/* begining of line	*/
	cpend = &cp[llength(lp)];	/* end of line		*/
	while(cp != cpend) {
	    putc(*cp, ffp);
	    cp++;	/* putc may evalualte arguments more than once */
	}
	lp = lforw(lp);
	if(lp == lpend) break;		/* no implied newline on last line */
	putc('\n', ffp);
    } while(!ferror(ffp));
    if(ferror(ffp)) {
	ewprintf("Write I/O error");
	return FIOERR;
    }
    return FIOSUC;
}

/*
 * Read a line from a file, and store the bytes
 * in the supplied buffer. Stop on end of file or end of
 * line.  When FIOEOF is returned, there is a valid line
 * of data without the normally implied \n.
 */
ffgetline(buf, nbuf, nbytes)
register char	*buf;
register int	nbuf;
register int	*nbytes;
{
	register int	c;
	register int	i;

	i = 0;
	while((c = getc(ffp))!=EOF && c!='\n') {
		buf[i++] = c;
		if (i >= nbuf) return FIOLONG;
	}
	if (c == EOF  && ferror(ffp) != FALSE) {
		ewprintf("File read error");
		return FIOERR;
	}
	*nbytes = i;
	return c==EOF ? FIOEOF : FIOSUC;
}

#ifndef NO_BACKUP
/*
 * Rename the file "fname" into a backup copy.
 * The backup copy is the same name with ".BAK" appended unless the file
 * name is to long.  The error handling is all in "file.c".
 */
fbackupfile(fname)
char	*fname;
{
	char *rindex();
	register char *backname, *cp;
	int stat;

	if((backname = malloc(strlen(fname) + 5)) == NULL) return FALSE;
/* delete the old backup */
	strcpy(backname, fname);
	strcat(backname, ".BAK");
	unlink(backname);			/* ignore errors */
	if(cp = rindex(fname, '/')) {
	    strcpy(backname, cp+1);
	    strcat(backname, ".BAK");
	}
	stat = rename(fname, backname) >= 0;
	free(backname);
	return stat;
}
#endif

#ifdef NO_BACKUP
#ifdef NO_DIRED
#define NO_RENAME
#endif
#endif

#ifndef NO_RENAME
rename(frname, toname)
char *frname, *toname;
{
	register char *params;
	register int frnamel, tonamel;
	int status;

	frnamel = strlen(frname);
	tonamel = strlen(toname);
	if((params = malloc(frnamel + tonamel + 2)) == NULL) return FALSE;
	strcpy(params, frname);
	params[frnamel] = ' ';
	strcpy(params + frnamel + 1, toname);
	if(os9fork("rename", frnamel + tonamel + 2, params, 0, 0, 0, 0)==-1) {
		free(params);
		return -1;
	}
	wait(&status);
	free(params);
	return (status & 0xffff)==0 ? 0 : -1;
}
#endif

/*
 * The string "fn" is a file name.
 * Perform any required appending of directory name or case adjustments.
 * If NO_DIR is not defined, the same file should be refered to even if the
 * working directory changes.  For Os9/68k, leave the file name case alone
 * so we use what the user specified.  (Should, but doesn't, use the case
 * stored on the disk for existing files.)
 */
#ifndef NO_DIR
extern char *wdir;
#endif

char *adjustname(fn)
register char	*fn;
{
    static char fnb[NFILEN];
    register char *cp;
    register char *rootp;

    switch(*fn) {
    	case '/':
	    cp = fnb;
	    *cp++ = *fn++;
	    while(*fn && (*cp++ = *fn++) != '/') {}
	    rootp = cp - 1;
	    break;
	case '~':
	    if(fn[1] != '/') return fn;		/* invalid, punt */
	    strcpy(fnb, getenv("HOME"));
	    rootp = index(fnb+1, '/');
	    cp = fnb + strlen(fnb);
	    if(cp==fnb) return fn;		/* invalid */
	    if(rootp == NULL) rootp = cp;
	    *cp++ = '/';
	    fn += 2;
	    break;
	default:
#ifndef	NODIR
	    strcpy(fnb, wdir);
	    rootp = index(fnb+1, '/');
	    cp = fnb + strlen(fnb);
	    if(cp==fnb) return fn;		/* invalid */
	    if(rootp == NULL) rootp = cp;
	    *cp++ = '/';
	    break;
#else
	    return fn;				/* punt */
#endif
    }
    while(*fn) {
    	if(*fn == '.') {
	    switch(fn[1]) {
	        case '\0':
		    *--cp = '\0';
		    return fnb;
	    	case '/':
	    	    fn += 2;
		    continue;
		case '.':
		    if(fn[2]=='/' || fn[2] == '\0') {
			--cp;
			while(cp > rootp && *--cp != '/') {}
			++cp;
			if(fn[2]=='\0') {
			    *--cp = '\0';
			    return fnb;
			}
		        fn += 3;
		        continue;
		    }
		    break;
		default: break;
	    }
	}
	while(*fn && (*cp++ = *fn++) != '/') {}
    }
    *cp = '\0';
    return fnb;
}

/*
 * fncmp: compare file or buffer names.  Return 0 on equality.  
 * (for compatibility with strcmp)  Both arguments have been 
 * through adjustname.
 */

fncmp(fna, fnb)
register char *fna, *fnb;
{
    register char ca, cb;

    while(ca = *fna++)
	if(ca != (cb = *fnb++) &&
		(!ISUPPER(ca) || TOLOWER(ca) != cb) &&
		(!ISUPPER(cb) || ca != TOLOWER(cb)))
	    return -1;
    return *fnb;
}

#ifndef NO_STARTUP
char *startupfile(suffix)
char *suffix;
{
	register char	*file;
	static char	home[NFILEN];
	char		*getenv();

	if ((file = getenv("HOME")) == NULL) goto notfound;
	if (strlen(file)+4 >= NFILEN - 1) goto notfound;
	(VOID) strcpy(home, file);
	(VOID) strcat(home, "/.mg");
	if (suffix != NULL) {
		(VOID) strcat(home, "-");
		(VOID) strcat(home, suffix);
	}
	if (access(home, 0) == 0) return home;

notfound:
#ifdef	STARTUPFILE
	file = STARTUPFILE;
	if (suffix != NULL) {
		(VOID) strcpy(home, file);
		(VOID) strcat(home, "-");
		(VOID) strcat(home, suffix);
		file = home;
	}
	if (access(file, 0) == 0) return file;
#endif

	return NULL;
}
#endif

#ifndef NO_DIR
char *getwd(cwd)
char *cwd;
{
    char backpath[MAXPATH];
    char *bpp = backpath, *path = cwd;
    DIR *dirp;
    struct direct *dp;
    long inode, inode2;
    char dots[MAXPATH];

    if((dirp = opendir(".")) == NULL || readdir(dirp) == NULL ||
		(dp = readdir(dirp)) == NULL) {
	closedir(dirp);
	return (char *)NULL;
    }
    inode = dp->d_addr;
    *path++ = '/';
    _gs_devn(dirp->dd_fd, path);
    path += strlen(path);
    closedir(dirp);
    strcpy(dots, "..");
    for(;;) {
	if((dirp = opendir(dots)) == NULL || readdir(dirp) == NULL ||
		    (dp = readdir(dirp)) == NULL) {
	    closedir(dirp);
	    return (char *)NULL;
	}
	inode2 = dp->d_addr;
	if(inode == inode2) break;
	do {
	    if((dp = readdir(dirp)) == NULL) {
		closedir(dirp);
		return (char *)NULL;
	    }
	} while(dp->d_addr != inode);
	*bpp++ = '/';
	strcpy(bpp, dp->d_name);
	bpp += strlen(bpp);
	closedir(dirp);
	inode = inode2;
	strcat(dots, "/..");
    }
    while(bpp > backpath) {
	*bpp = '\0';
	while(*--bpp != '/') {}
	strcpy(path, bpp);
	path += strlen(path);
    }
    return cwd;
}
#endif

#ifndef NO_DIRED
copy(frname, toname)
char *frname, *toname;
{
	register char *params;
	register int frnamel, tonamel;
	int status;

	frnamel = strlen(frname);
	tonamel = strlen(toname);
	if((params = malloc(frnamel + tonamel + 2)) == NULL) return FALSE;
	strcpy(params, frname);
	params[frnamel] = ' ';
	strcpy(params + frnamel + 1, toname);
	if(os9fork("copy", frnamel + tonamel + 2, params, 0, 0, 0, 0)==-1) {
		free(params);
		return -1;
	}
	wait(&status);
	free(params);
	return (status & 0xffff)==0 ? 0 : -1;
}

unlinkdir(fname)
char *fname;
{
    /* does NOT delete non-empty directories */
    if(_ss_attr(fname, S_IWRITE | S_IREAD) < 0) return -1;
    return unlink(fname);
}

BUFFER *dired_(dirname)
char *dirname;
{
    extern char *wdir;			/* defined in dir.c */
    register BUFFER *bp;
    char line[128];
    register FILE *dirpipe;
    int i;
    BUFFER *findbuffer();

    if(dirname == NULL || *dirname == '\0') dirname = wdir;
    else if((dirname = adjustname(dirname)) == NULL) {
	ewprintf("Could not adjust dir name");
	return NULL;
    }
    if((bp = findbuffer(dirname)) == NULL) {
	ewprintf("Could not create buffer");
	return NULL;
    }
    if(bclear(bp) != TRUE) return NULL;
    i = -1;
    strcpy(line, "-ae ");
    strcpy(&line[4], dirname);
	/* fopen doesn't work here for some reason, so use open & fdopen */
    if((i = open("/pipe", S_IREAD | S_IWRITE)) < 0 ||
    	    ((dirpipe = fdopen(i, "rw")) == NULL) || ((i = dup(1)) < 0) ||
	    (close(1) < 0) || (dup(fileno(dirpipe)) != 1) ||
	    (os9fork("dir", strlen(line) + 1, line, 0, 0, 0, 0) < 0) ||
	    (close(1) < 0) || (dup(i) != 1) || (close(i) < 0)) {
	ewprintf("Could not open pipe to dir");
	fclose(dirpipe);
	if(i >= 0) {
	    close(1);
	    dup(i);
	    close(i);
	}
	return NULL;
    }
    line[0] = line[1] = ' ';
    while(fgets(&line[2], 126, dirpipe) != NULL) {
    	line[strlen(line) - 1] = '\0';
	addline(bp, line);
    }
    if(fclose(dirpipe) < 0 || wait(&i) < 0 || i != 0) {
    	ewprintf("Trouble closing directory pipe");
	return NULL;
    }
    bp->b_dotp = lforw(bp->b_linep);		/* go to first line */
    strncpy(bp->b_fname, dirname, NFILEN);
    if((bp->b_modes[0] = name_mode("dired")) == NULL) {
	bp->b_modes[0] = &map_table[0];
	ewprintf("Could not find mode dired");
	return NULL;
    }
    bp->b_nmodes = 0;
    return bp;
}

d_makename(lp, fn)
register LINE *lp;
register char *fn;
{
    register char *cp;

    if(llength(lp) <= 54) return ABORT;
    strcpy(fn, curbp->b_fname);
    cp = fn + strlen(fn);
    *cp++ = '/';
    bcopy(&lp->l_text[54], cp, llength(lp) - 54);
    cp[llength(lp) - 54] = '\0';
    return lgetc(lp, 27) == 'd';
}
#endif
SHAR_EOF
cat << \SHAR_EOF > sys/osk/makefile


# Makefile for OSK MicroGnuEMACS by Robert A. Larson
#	07/12/86	MicroGnuEMACS version
# Copy ./sys/osk/makefile to . before making.  Check
# OPTS and LIBS.
#
# mg is short for micrognuemacs.  Call it what you want.
NAME = mg
# terminal type or termcap
TERM = TERMCAP
# opts is for user definable options
OPTS = -dMAXMEM=320 -dXKEYS
# termlib is needed for termcap.
LFLAGS = -l=/dd/lib/termlib.l -m=4
CFLAGS = -r=. -t=/r0
OBJ = basic.r \
 dir.r \
 dired.r \
 file.r \
 line.r \
 match.r \
 paragraph.r \
 random.r \
 region.r \
 search.r \
 version.r \
 window.r \
 word.r
# unique requirements
IND = buffer.r \
 display.r \
 echo.r \
 extend.r \
 help.r \
 kbd.r \
 keymap.r \
 macro.r \
 main.r \
 modes.r \
 regex.r \
 re_search.r
# these files are listed individually below
SYSOBJ = cinfo.r \
 fileio.r \
 spawn.r \
 ttyio.r \
 tty.r \
 ttykbd.r
#
$(NAME): $(OBJ) $(IND) $(SYSOBJ) makefile
 cc -i -f=$(NAME) $(LFLAGS) $(OBJ) $(IND) $(SYSOBJ)
$(OBJ): def.h sysdef.h ttydef.h chrdef.h makefile
 cc $(CFLAGS) $(OPTS) $*.c
kbd.r: def.h sysdef.h ttydef.h chrdef.h makefile macro.h kbd.h key.h
 cc $(CFLAGS) $(OPTS) $*.c
macro.r main.r: def.h sysdef.h ttydef.h chrdef.h makefile macro.h
 cc $(CFLAGS) $(OPTS) $*.c
buffer.r display.r keymap.r help.r modes.r dired.r: \
  def.h sysdef.h ttydef.h chrdef.h makefile kbd.h
 cc $(CFLAGS) $(OPTS) $*.c
extend.r: def.h sysdef.h ttydef.h chrdef.h makefile kbd.h macro.h key.h
 cc $(CFLAGS) $(OPTS) $*.c
help.r: def.h sysdef.h ttydef.h chrdef.h makefile kbd.h key.h macro.h
 cc $(CFLAGS) $(OPTS) $*.c
echo.r: def.h sysdef.h ttydef.h chrdef.h makefile key.h macro.h
 cc $(CFLAGS) $(OPTS) $*.c
regex.r re_search.r: def.h sysdef.h ttydef.h chrdef.h makefile regex.h
 cc $(CFLAGS) $(OPTS) $*.c
fileio.r: sys/osk/fileio.c def.h sysdef.h ttydef.h chrdef.h makefile kbd.h
 cc $(CFLAGS) $(OPTS) sys/osk/fileio.c
spawn.r: sys/osk/spawn.c def.h sysdef.h ttydef.h chrdef.h makefile
 cc $(CFLAGS) $(OPTS) sys/osk/spawn.c
ttyio.r: sys/osk/ttyio.c def.h sysdef.h ttydef.h chrdef.h makefile
 cc $(CFLAGS) $(OPTS) sys/osk/ttyio.c
tty.r: sys/default/tty.c def.h sysdef.h ttydef.h chrdef.h makefile
 cc $(CFLAGS) $(OPTS) sys/default/tty.c
ttykbd.r: sys/default/ttykbd.c def.h sysdef.h ttydef.h chrdef.h makefile
 cc $(CFLAGS) $(OPTS) sys/default/ttykbd.c
cinfo.r: sys/default/cinfo.c def.h sysdef.h ttydef.h chrdef.h makefile
 cc $(CFLAGS) $(OPTS) sys/default/cinfo.c
# The touch commands are needed to update the modified time.
sysdef.h: sys/osk/sysdef.h
 copy -b=16 -w=. -r sys/osk/sysdef.h
 touch sysdef.h
ttydef.h: sys/default/ttydef.h
 copy -b=16 -w=. -r sys/default/ttydef.h
 touch ttydef.h
chrdef.h: sys/default/chrdef.h
 copy -b=16 -w=. -r sys/default/chrdef.h
 touch chrdef.h
SHAR_EOF
cat << \SHAR_EOF > sys/osk/readme.osk

readme.osk for MicroGnuEmacs 2a				Oct 27, 1987

Copy sys/osk/makefile to .

copy sys/osk/varargs.h to /dd/defs/varargs.h if you don't have one.

Look at makefile for personal preference options and terminal selection.

Use make to compile.

Baud should be set correctly in tmode/xmode even if it is set in
hardware as it is on a QT+.  This is used for display optimization
and padding.

All files are indented assuming tabs every eight columns, and mg
itself uses tabs there.  I have no idea why Microware decided not to
follow this defacto industry standard, but at least they allow you to
change your system to whatever you want.

The enviornment variable TERM is used to determine your terminal type,
and HOME is used to find the .mg startup file and for expanding a
leading ~ in a file name.  
SHAR_EOF
cat << \SHAR_EOF > sys/osk/spawn.c
/*
 * Name:	MicroGnuEmacs
 *		OS9/68k Spawn Shell
 */
#include	"def.h"

#include	<sgstat.h>

extern	struct	sgbuf	oldtty;		/* There really should be a	*/
extern	struct	sgbuf	newtty;		/* nicer way of doing this	*/

spawncli(f, n)
{
	register int	pid;
	register int	wpid;
	int		status;
	register char	*cp;
	int		os9fork();
	static char	*argv[2] = {"shell", NULL};
	extern char	**environ;
	char		*getenv();

	ttcolor(CTEXT);
	ttnowindow();
	ttmove(nrow-1, 0);
	if (epresf != FALSE) {
		tteeol();
		epresf = FALSE;
	}
	ttflush();
	if((cp=getenv("SHELL")) != NULL) argv[0] = cp;
	if(_ss_opt(0, &oldtty) == -1) {
		ewprintf("_ss_opt #1 to terminal failed");
		return FALSE;
	}
	if((pid=os9exec(os9fork, argv[0], argv, environ, 0, 0)) == -1) {
		ewprintf("Failed to create process");
		return FALSE;
	}
	while ((wpid=wait(&status))>=0 && wpid!=pid) {}
	sgarbf = TRUE;				/* Force repaint.	*/
	if(_ss_opt(0, &newtty) == -1) {
		ewprintf("_ss_opt #2 to terminal failed");
		return FALSE;
	}
	return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > sys/osk/sysdef.h
/*
 * Os9/68K specific definitions for micrognuemacs
 */
#include <stdio.h>

#define KBLOCK	1024		/* Kill grow.				*/
#define GOOD	0		/* Good exit status.			*/
#define NO_VOID_TYPE		/* void not supported yet		*/
#define PC PC_			/* compiler can't handle variable	*/
#define SR SR_			/* called PC or SR			*/
#define NO_RESIZE		/* terminal doesn't change size		*/
typedef int	RSIZE;		/* Type for file/region sizes		*/
typedef short	KCHAR;		/* Type for keyboard character		*/
#define MAXPATH 128		/* reasonable maximum path length	*/

			/* malloc works best with a multiple of 8 bytes */
#define MALLOCROUND(v)	((v) += 7, (v) &= ~7)

/*
 * Macros used by the buffer name making code.
 * Start at the end of the file name, scan to the left
 * until BDC1 (or BDC2, if defined) is reached. The buffer
 * name starts just to the right of that location, and
 * stops at end of string (or at the next BDC3 character,
 * if defined). BDC2 and BDC3 are mainly for VMS.
 */
#define BDC1	'/'			/* Buffer names.		*/

/*
 * Needed for lots of small mallocs on os9/68k.	 _memmins should be
 * (maximum malloced memory)/16.  (defalt _memmins is 4096)
 * Note that malloc may now fail if there isn't _memmins bytes
 * contiguous free memory.  _memins could be reduced and the malloc
 * tried again.	 (Not currently implemented.)
 */
#ifdef MAXMEM
#  define SYSINIT	{extern int _memmins;	_memmins=MAXMEM*64; }
#else
#  define SYSINIT	{extern int _memmins;	_memmins=32768; }
#endif

/* see "caveates" in the osk C manual on _strass */
#define bcopy(from,to,len)	_strass(to,from,len)

/* see comments on these in display.c.	OSK can't stand the wasted memory
 * without making the score array "remote", which generates lousy code.
 * Besides, I don't have an extra 100kb of memory for the score array.
 */
#define XCHAR	char
#define XSHORT	short

char	*getenv();
#define gettermtype()	getenv("TERM")	/* get terminal type		*/
SHAR_EOF
cat << \SHAR_EOF > sys/osk/ttyio.c
/*
 *	sys/osk/ttyio.c by Robert A. Larson
 *
 * The functions in this file
 * negotiate with the operating system for
 * keyboard characters, and write characters to
 * the display in a barely buffered fashion.
 */
#include	"def.h"

#include	<sgstat.h>
#ifndef NO_DPROMPT
#  include	<varargs.h>
#  define S_RDY 2437			/* arbitrary user signal */
#endif

#define NOBUF	512			/* Output buffer size.		*/

char	obuf[NOBUF];			/* Output buffer.		*/
int	nobuf;
struct	sgbuf	oldtty, newtty;
int	nrow;				/* Terminal size, rows.		*/
int	ncol;				/* Terminal size, columns.	*/
short	ospeed;				/* Terminal speed, for termlib.l */

#ifndef NO_DPROMPT
wakeup(signum)
int signum;
{
	/* ignore the signal */
}
#endif

/*
 * This function gets called once, to set up
 * the terminal channel.
 */

ttopen()
{
	if(_gs_opt(0, &oldtty) == -1) panic("can't get options");
	ospeed = oldtty.sg_baud;
	_strass(&newtty, &oldtty, sizeof(newtty));	/* newtty=oldtty; */
	if(oldtty.sg_class == 0) {			/* scf */
		newtty.sg_backsp=
		newtty.sg_delete=
		newtty.sg_echo	=
		newtty.sg_alf	=
		newtty.sg_pause =
		newtty.sg_bspch =
		newtty.sg_dlnch =
		newtty.sg_eorch =
		newtty.sg_eofch =
		newtty.sg_rlnch =
		newtty.sg_dulnch=
		newtty.sg_psch	=
		newtty.sg_kbich =
		newtty.sg_kbach = 0;
#ifndef xon_xoff
		newtty.sg_xon	=
		newtty.sg_xoff	= 0;
#endif
		if(_ss_opt(0, &newtty) == -1) panic("can't set options");
		nrow = oldtty.sg_page == 0 ? NROW : oldtty.sg_page;
	} else {				/* not scf, fake it */
		nrow = NROW;
	}
	ncol = NCOL;
#ifndef NO_DPROMPT
	intercept(wakeup);		/* ignore signals */
#endif
}

/*
 * This function gets called just
 * before we go back home to the shell. Put all of
 * the terminal parameters back.
 */
ttclose()
{
	ttflush();
	if(_ss_opt(0, &oldtty) == -1) panic("can't reset options");
}

/*
 * Write character to the display.
 * Characters are buffered up, to make things
 * a little bit more efficient.
 */
ttputc(c)
{
	if (nobuf >= NOBUF)
		ttflush();
	obuf[nobuf++] = c;
}

/*
 * Flush output.
 */
ttflush()
{
	if (nobuf != 0) {
		write(1, obuf, nobuf);
		nobuf = 0;
	}
}

/*
 * Read character from terminal.
 * All 8 bits are returned, so that you can use
 * a multi-national terminal.
 */
ttgetc()
{
	char	buf[1];

	while (read(0, &buf[0], 1) != 1)
		;
	return (buf[0] & 0xFF);
}

int typeahead()
{
  return _gs_rdy(0) > 0;
}

panic(s) char *s; {
  _ss_opt(0, &oldtty);			/*  ignore errors */
  fputs("Panic: ", stdout);		/* avoid printf, don't load all that */
  puts(s);
  exit(1);
}

#ifndef NO_DPROMPT
ttwait() {
  if(_gs_rdy(0) > 0) return FALSE;	/* already something waiting */
  _ss_ssig(0, S_RDY);			/* wake me when you have something */
  if(sleep(2)!=0) return FALSE;		/* sleep interupted */
  _ss_rel(0);
  return TRUE;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > sys/osk/varargs.h
/* varargs.h for os9/68k by Robert A. Larson	*/
/* version 0 for os9/68k C 2.0 04/20/86		*/
/* varargs is a "portable" way to write a routine that takes a variable */
/* number of arguements.  This implemination agrees with both the 4.2bsd*/
/* and Sys V documentation of varargs.  Note that just because varargs.h*/
/* is used does not mean that it is used properly.			*/
/* Ignore the "expression with little effect" warnings.  (Seems to be a */
/* compiler bug.)							*/

#define va_alist	_va_arg1, _va_arg2, _va_arg3

#define va_dcl		unsigned _va_arg1, _va_arg2, _va_arg3;

typedef	struct {
	  unsigned _va_at;  /* number of arguments used, 0, 1, or more  */
                            /* (double as first arg counts as 2)        */
	  union {
	    struct {
	      unsigned _va_uns1, _va_uns2;
	    } _va_uuns;
	    double _va_udouble;
	  } _va_union;
	  char *_va_pointer;
        } va_list;

#define	va_start(pvar)	( (pvar)._va_at = 0,				\
			  (pvar)._va_union._va_uuns._va_uns1 = _va_arg1,\
			  (pvar)._va_union._va_uuns._va_uns2 = _va_arg2,\
			  (pvar)._va_pointer = (char *) &_va_arg3	\
			)

#define va_arg(pvar, type)	(				\
	((pvar)._va_at++) ? (					\
		((pvar)._va_at == 2) ? (			\
			(sizeof(type) == 8) ? (			\
				*(((type *)((pvar)._va_pointer))++)	\
			) : (type)(				\
				(pvar)._va_union._va_uuns._va_uns2	\
			)					\
		) : (						\

			*(((type *)((pvar)._va_pointer))++)	\
		)						\
	) : (							\
		(sizeof(type) == 8) ? (type)(			\
			(pvar)._va_at++,			\
			(pvar)._va_union._va_udouble		\
		) : (type)(					\
			(pvar)._va_union._va_uuns._va_uns1	\
		)						\
	)							\
)

#define va_end(pvar)				/* va_end is simple */
SHAR_EOF
cat << \SHAR_EOF > sys/vms/aaareadme.1st
This directory ([.SYS.VMS]) contains the VMS-specific files for MG.

			+-----------------------+
			|      Construction	|
			+-----------------------+

By this point you should have put the ``system-independent'' files
into a directory of your choice, then put the VMS files into the
subdirectory [.SYS.VMS].  You should also put the termcap terminal
driver into the subdirectory [.SYS.DEFAULT], and the termcap library
files into [.SYS.TERMLIB].  These come with the standard MG distribution, 
so you shouldn't have much trouble.

The command file MAKE.COM is designed to compile and link the entire
program, using the VMS system functions and the termcap terminal
driver.

To invoke MAKE.COM, enter

	SET DEF dev:[emacs-dir]	! location of system-independent files
	@[.SYS.VMS]MAKE		! go have some coffee...

This will create the termcap library, compile each of the necessary
modules, and link the entire program into dev:[emacs-dir]MG.EXE.

NOTE: To keep the size of the executable program down, MAKE.COM
attempts to link in the VAX C shareable run-time library. Since VMS
4.4 and up have the VAX C run-time library bundled into it, there
shouldn't be any problem finding it.  If there is, modify MG.OPT to
link with SYS$LIBRARY:VAXCRTL.OBJ/LIB instead. 

	+-------------------------------------------------------+
	|	Specifying Your Terminal With Termcap		|
	+-------------------------------------------------------+

(CAVEAT AND CREDITS: The termcap subroutine library in
[.SYS.TERMLIB] was written by Fred Fish (of Amiga Public Domain
Library fame) and placed in the public domain. It is not guaranteed to
be a complete implementation of the Unix termcap(5) library; the usual
disclaimers (like "it works for me":-) apply here.  I have modified it
to support the tc= capability, which lets you define terminals in
terms (pardon the pun) of other terminals, so it should work
reasonably well with the termcap provided in [.SYS.TERMLIB]TERMCAP.)

To use the termcap library, you need to tell it where to find a
terminal definition (termcap) file.  If you have GNU Emacs installed,
MG will use its termcap file from EMACS_LIBRARY:[ETC]TERMCAP.DAT.
Otherwise, it will try to find the file which on Unix systems is
called /etc/termcap.  To emulate the same behavior on VMS, DEFINE/JOB
the logical name ETC to point to a directory that contains a termcap
file, with the name TERMCAP. (no extension). (The /JOB qualifier is
needed when you run MG as a spawned supprocess.)

	DEFINE/JOB ETC dev:[dir]

If your system has Eunice, there is a large termcap file already
available via this exact mechanism, so you shouldn't need to define
ETC at all.  If you don't have Eunice, never fear; a termcap resides
in the file [.SYS.TERMLIB]TERMCAP., so all you have to do is

	DEFINE ETC [emacs-directory.SYS.TERMLIB]

to get started.  You get the idea.  Lastly, if your site uses the
logical name ETC for another purpose, you can define the logical name
TERMCAP to point to the MG termcap file.  You must specify the path in
Unix format, with the root being the disk drive the file resides on.
For example, if the termcap file is in DUA0:[USER]TERMCAP., the
command would be

	DEFINE TERMCAP "/dua0/user/termcap"

The VAX C run-time library can translate this into the appropriate VMS
file specification for you (a rather nice feature...).

Once you've indicated where the termcap file is, you can tell MG what
kind of terminal you're using by defining the logical name EMACS_TERM.

	DEFINE/JOB EMACS_TERM "termtype"

where "termtype" is in lower case and matches an entry in the termcap
file.  (Alternatively, you can define the logical name TERM and MG will
look there if EMACS_TERM is not defined.  Again, GNU Emacs uses EMACS_TERM
and Eunice uses TERM.)

NOTE: One performance aspect of termcap files is that they are
searched sequentially, so you may want to move the most frequently
used terminals at your site to the beginning of the file to minimize
startup overhead.

		+---------------------------------------+
		+	TERMINAL INITIALIZATION FILE	|
		+---------------------------------------+

The termcap terminal driver allows you to initialize MG differently,
depending on what kind of terminal you're using.  See
[.SYS.DEFAULT]README for more information.

		+---------------------------------------+
		|		INVOKING MG		|
		+---------------------------------------+

First of all, remember to set up the logical names for the terminal
type and termcap file.  This can be done just once, in your LOGIN.COM file.

Then, to just run MG in your current process,
	$ RUN [emacs-directory]MG

Or you can define a symbol to run it with command line arguments:
	$ MG :== $dev:[emacs-directory]MG
	$ MG [file]

		+---------------------------------------+
		|     	     MG As a Kept Fork		|
		+---------------------------------------+

You can use [.SYS.VMS]MG.COM to spawn a MicroEmacs subjob, which you
can then attach to and pop out of as you please. Edit the line at the
top of MG.COM that defines the path to the image MG.EXE, then define a
global symbol called MG:

	$ MG :== @dev:[emacs-directory.SYS.VMS]MG

You can then use MG to edit files:

	$ MG [file]

When inside MG, use the command M-x suspend-emacs (bound to C-z by
default) to suspend the MG process and attach your terminal to the
process that spawned it. To re-attach to MG, just issue the MG command
again:

	$ MG [file]

The command file will reattach you to your MG process, where you can
continue editing where you left off. If you specify a new file to
edit, the command file sets a logical name which MG then looks at when
you reattach.


		+-------------------------------+
		|     MG As a Mail Editor	|
		+-------------------------------+

As an added bonus, the file MGMAILEDIT.COM makes MG your mail editor
when you use the SEND/EDIT command.  Issue the command

	DEFINE/JOB MAIL$EDIT dev:[dir]MGMAILEDIT.COM

to inform the mail system you want to use MGMAILEDIT.COM, then whenever
you issue SEND/EDIT inside mail, MG will be used as your mail editor.
SHAR_EOF
cat << \SHAR_EOF > sys/vms/ccom.com
$	Verify = F$Verify(0)
$!
$! CCOM.COM
$!
$!	Run the C compiler on P1, but only if the .c file
$!	is newer than the corresponding .obj file.
$!
$! Usage:
$!	@CCOM [file [qualifiers]]
$!
$	If P1 .Eqs. "" Then -
		Inquire P1 "C Source File"
$	Name = P1 - ".C"
$	Source = Name + ".C"
$	Object = f$parse(P1,,,"NAME") + ".OBJ"
$!
$! See if both files exist.  If both exist, only compile the
$! source if the revision date is greater than or equal to
$! that of the object file.
$!
$	If F$Search(Source) .Eqs. "" Then -
		Goto NoSource
$	If F$Search(Object) .Eqs. "" Then -
$		Goto Compile
$	SDate = F$File_Attributes(Source, "RDT")
$	ODate = F$File_Attributes(Object, "RDT") 
$	If SDate .Lts. ODate Then -
		Goto Bye
$!
$! Compile the program
$!
$Compile:
$	On Error Then Goto Fail
$	Write Sys$Output "Compiling " + Source
$	CC 'P2' 'Source
$	If F$Search(Object) .Eqs. "" Then -
		Goto Fail
$!
$! Done.
$!
$Bye:
$	If Verify Then	Set Verify
$	Exit
$!
$NoSource:
$	Write Sys$Output "%CCOM-F-NOTFOUND, file not found"
$	Goto Bye
$!
$Fail:
$	Write Sys$Output "%CCOM-F-FAIL, compile failed"
$	Goto Bye

SHAR_EOF
cat << \SHAR_EOF > sys/vms/fileio.c
/*
 * Name:	MicroGNUEmacs
 * Version:	2a
 *		VAX/VMS file I/O.
 * Created:	05-Feb-86 decvax!decwrl!dec-rhea!dec-rex!conroy
 * Last edit:	1-Mar-88  sandra@cs.utah.edu
 *
 * Read and write ASCII files on VAX/VMS. All of the low level file
 * I/O knowledge is here.  This uses RMS system calls directly because
 * the VAX C i/o functions are so slow.
 */

#include	"def.h"
#include	<rms.h>
#define DEFAULT_READ_BUFFER_SIZE 1024

struct FAB fab;
struct RAB rab;
struct XABFHC xab;
char *inbuf;
int instart, inend;


/*
 * Open a file for reading.  Also malloc's an input buffer that is big enough
 *	to hold the longest line in the file.  
 */
ffropen(fn)
char	*fn;
{
	int maxlen;

	fab = cc$rms_fab;
	rab = cc$rms_rab;
	xab = cc$rms_xabfhc;
        fab.fab$l_fna = fn;
	fab.fab$b_fns = strlen(fn);
	fab.fab$l_xab = &xab;
	rab.rab$l_fab = &fab;
	rab.rab$w_isi = 0;
	if (SYS$OPEN (&fab, 0, 0) == RMS$_NORMAL)  {
		SYS$CONNECT (&rab, 0, 0);
		if (xab.xab$w_lrl == 0)
		    maxlen = DEFAULT_READ_BUFFER_SIZE;
 		else 
		    maxlen = xab.xab$w_lrl;
		inbuf = malloc(maxlen);
		instart = 0;
		inend = 0;
		rab.rab$l_ubf = inbuf;
		rab.rab$w_usz = maxlen;
		return (FIOSUC);
		}
	else
		return (FIOFNF);
}

/*
 * Open a file for writing.
 * Return TRUE if all is well, and
 * FALSE on error (cannot create).
 */
ffwopen(fn)
char	*fn;
{
	int status;

	fab = cc$rms_fab;
	rab = cc$rms_rab;
        fab.fab$l_fna = fn;
	fab.fab$b_fns = strlen(fn);
	fab.fab$b_rat = FAB$M_CR;
	fab.fab$b_rfm = FAB$C_VAR;
	rab.rab$l_fab = &fab;
	rab.rab$w_isi = 0;
        status = SYS$CREATE (&fab, 0, 0);
        switch (status) {

          case RMS$_NORMAL:
          case RMS$_FILEPURGED:
                SYS$CONNECT (&rab, 0, 0);
		inbuf = NULL;
		return (FIOSUC);

          default:
                ewprintf("Cannot open file for writing STS=%d, STV=%d",
                         fab.fab$l_sts, fab.fab$l_stv);
                return (FIOERR);
        }
}

/*
 * Close a file.
 */
ffclose()
{
        if (inbuf)
	    free(inbuf);
        if (SYS$CLOSE(&fab, 0, 0) == RMS$_NORMAL)
		return (FIOSUC);
	else
		return (FIOERR);
}

/*
 * Write a buffer to the already opened file. bp points to the
 * buffer. Return the status.
 * This doesn't write out the last line of the buffer unless it is
 * non-empty, to prevent VMS from putting an extra newline at the
 * end of the file.
 */

ffputbuf(bp)
BUFFER *bp;
{
    register LINE *lp;
    register LINE *lpend;
    int status;

    lpend = bp->b_linep;
    lp = lforw(lpend);
    while (! ((lp == lpend) || ((lforw(lp) == lpend) && (llength(lp) == 0))))  {
        rab.rab$l_rbf = &ltext(lp)[0];
	rab.rab$w_rsz = llength(lp);
	if ((status = SYS$PUT (&rab, 0, 0)) != RMS$_NORMAL)  {
	    ewprintf("Write I/O error, VMS status code %d", status);
	    return(FIOERR);
	    }
	lp = lforw(lp);
	}
    return FIOSUC;
}

/*
 * Read a line from a file, and store the bytes in the
 * supplied buffer. Stop on end of file or end of line.
 * Returns an extra newline on FIOEOF.
 *
 */

ffgetline(buf, nbuf, nbytes)
register char	*buf;
register int	nbuf;
register int	*nbytes;

{	register int status;
	register int i;


		/* Load the input buffer if it's empty.  */

        if (instart >= inend)  {
	    status = SYS$GET (&rab, 0, 0);
	    if (status == RMS$_EOF)  {
	        *nbytes = 0;
		return FIOEOF;
		}
	    else if (status != RMS$_NORMAL)  {
  	        ewprintf("File read error, VMS status code %d", status);
	        return FIOERR;
	        }
	    else {
	        instart = 0;
	        inend = rab.rab$w_rsz;
		}
	    }

		/* Copy contents of the input buffer */

	*nbytes = inend - instart;
        if (*nbytes > nbuf) *nbytes = nbuf;
	for (i=0; i<*nbytes; i++)
	    buf[i] = inbuf[instart++];
	if (instart < inend)
	    return FIOLONG;
	else
	    return FIOSUC;

}

#ifndef	NO_BACKUP
/*
 * VMS has version numbers, so there is no need for
 * MicroEMACS to bother making its own flavour of
 * backup copy. Return TRUE so the caller doesn't quit.
 */
fbackupfile(fname)
char	*fname;
{
	return (TRUE);
}
#endif

/*
 * The string "fn" is a file name. Canonicalize it by making RMS
 * do the dirty work, then lowercase it.
 */
char *adjustname(fn)
char	*fn;
{
	register char *cp;
#ifndef	NO_DIR
	char *fullname, *fparse(), *strrchr();
	static char name[NFILEN];

	if (fullname = fparse(fn, NULL, NULL, NULL)) {
		strncpy(name, fullname, NFILEN);
		fn = name;
		free(fullname);	/* fparse malloc()'s the name */
	}
#endif
	for (cp = fn; *cp ; cp++)
		if (ISUPPER(*cp))
			*cp = *cp - 'A' + 'a';
	return fn;
}

/*
 * Fn1 and Fn2 are two file names. Return 0 if they represent the
 * same file.
 * Both arguments have already gone through adjustname(), so just do
 * strcmp() here.
 */
fncmp(fn1,fn2)
char *fn1,*fn2;
{
	return strcmp(fn1,fn2);
}

#ifndef	NO_STARTUP
#include <file.h>
/*
 * Find the user's startup file, and return its name.
 */
char *
startupfile(suffix)
char *suffix;
{
	static char	file[NFILEN];

        (VOID) strcpy(file, "SYS$LOGIN:.MG");
        if (suffix)  {
	    strcat(file,"-");
	    strcat(file,suffix);
	}
	if (access(file, O_RDONLY ) == 0) return file;
	return NULL;
}
#endif

#ifndef NO_DIR
#include <descrip.h>
#include <ssdef.h>

/*
 * Get current working directory.  Path had best be rather big
 * (at least 400 characters long...)
 */

$DESCRIPTOR(sys_disk,"SYS$DISK");

char *getwd(path)
char path[];
{
	struct	dsc$descriptor	devdsc, dirdsc;
	char 	tempdev[512], tempdir[512];
	int	status, templen;
	short	devlen, dirlen;

	/* translate the logical name SYS$DISK */

	devdsc.dsc$a_pointer = tempdev;
	devdsc.dsc$w_length  = sizeof(tempdev) - 1;
	devdsc.dsc$b_dtype   = DSC$K_DTYPE_T;
	devdsc.dsc$b_class   = DSC$K_CLASS_S;
	
	status = LIB$SYS_TRNLOG(&sys_disk, &devlen, &devdsc);
	if (status!=SS$_NORMAL && status!=SS$_NOTRAN)
		panic("getwd: can't translate SYS$DISK");

	/* Append the current default directory using SYS$SETDDIR() */

	dirdsc.dsc$b_dtype   = DSC$K_DTYPE_T;
	dirdsc.dsc$b_class   = DSC$K_CLASS_S;
	dirdsc.dsc$a_pointer = tempdir;
	dirdsc.dsc$w_length  = sizeof(tempdir) - 1;

	status = SYS$SETDDIR(0L, &dirlen, &dirdsc);
	if (status != RMS$_NORMAL)
		panic("getwd: can't get current directory!");

	bcopy(tempdev,path,devlen);
	bcopy(tempdir,path+devlen,dirlen);
	path[devlen + dirlen] = '\0';
	return path;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > sys/vms/fparse.c
/*
 *From ut-ngp!ut-sally!im4u!rutgers!nike!ucbcad!ucbvax!YALE.ARPA!LEICHTER-JERRY
 * Thu Nov  6 08:29:57 CST 1986
 *
 *
 *         I am writing a user interface program for radio telescopes and
 *    am trying to locate a piece of code to parse VMS file names.  I
 *    would prefer C but we also have Fortran and Pascal compilers.  I'm
 *    trying to avoid having figuring out the call to SYS$PARSE and would
 *    rather not have to open the file at this point in the program.
 * Try the following (I didn't write it but I do use it).
 *							-- Jerry
 */

/*)LIBRARY
*/

/*

fparse.c - fparse routine to emulate f$parse in DCL command files

Synposis:

	char *fparse( file_name, default_name, related_name, field)
	    char *file_name, default_name, related_name, field;

Description:

	All strings are null-terminated.  Only the first one or two characters
	are checked in field names. Parameters can be omitted by passing NULL.

	Fields are:

	    "node"		node name
	    "device"		device name
	    "directory"		directory name
	    "name"		file name
	    "type"		file type
	    "version"		file version number

	If the field parameter is null, all fields are expanded, except that
	the node name is included only if it appears in the file_name,
	default_name or related_name.  Note that field parameters
	must be given in lower-case.

	Within each field, the expanded name is taken from the file_name,
	default_name and related_name, in that order.

	The value returned is the address of the null-terminated file name;
	fparse calls malloc to reserve space for the string.

	The string "" is returned on either an RMS parse error, or
	an erroroneous field parameter name.  The RMS status error
	is not available.  Only one field may be given.

Example:


	To parse a command line LINK/EXE=exefile objfile, the default extension
	for "objfile" is .OBJ.  The default file name for "exefile" is the file
	name of "objfile", and the default extension is .EXE.

	Say that the char * variable "objfile" points to the object file
	name from the command line, and the char * "exefile" points to the
	exe file from the command line.  Then to expand these into file
	names for calls to open() or fopen():

		objfile = fparse( objfile, ".OBJ", NULL, NULL);
		exefile = fparse( exefile, ".EXE", objfile, NULL);

	To find only the directory of the object file:

		dir = fparse( objfile, ".OBJ", NULL, "directory");

	The field name could also be abbreviated:

		dir = fparse( objfile, ".OBJ", NULL, "di");

*/

#include <stdio.h>
#include <rms.h>
#include <ssdef.h>

char *fparse( file_name, default_name, related_name, field)
    char *file_name, *default_name, *related_name, *field;
{
    struct FAB ff_file_fab;
    struct NAM fn_file_nam, rn_related_nam, *nam;
    char *expanded_name, *eptr;
    int expanded_length, rms_status;
    char expand_buffer[ NAM$C_MAXRSS];

/* initialize all the blocks for RMS */

    ff_file_fab = cc$rms_fab;
    ff_file_fab.fab$l_nam = &fn_file_nam;
    ff_file_fab.fab$l_fna = file_name;
    ff_file_fab.fab$b_fns = (file_name == NULL) ? 0 : strlen( file_name);
    ff_file_fab.fab$l_dna = default_name;
    ff_file_fab.fab$b_dns = (default_name == NULL) ? 0 : strlen( default_name);

    fn_file_nam = cc$rms_nam;
    nam = &fn_file_nam;
    nam->nam$l_esa = expand_buffer;
    nam->nam$b_ess = NAM$C_MAXRSS;
    nam->nam$l_rlf = &rn_related_nam;

    rn_related_nam = cc$rms_nam;
    rn_related_nam.nam$l_rsa = related_name;
    rn_related_nam.nam$b_rsl =
      (related_name == NULL) ? 0 : strlen( related_name);

/* call SYS$PARSE to parse the file name */

    rms_status = sys$parse( &ff_file_fab);
    if (rms_status != RMS$_NORMAL)
	{  /* error in parse, so return empty string */
	expanded_name = malloc(1);
	expanded_name[0] = '\0';
	return( expanded_name);
	}

/* construct the expanded file name */

    if (field == NULL || field[0] == '\0')
	{  /* caller wants all fields */
	expanded_length = nam->nam$b_esl;
	eptr = expand_buffer;
	}
    else
	{  /* caller wants just one field */
	switch (field[0])
	    {
	    case 'n':		/* node or name */
		if (field[1] == 'o')
		    {		/* node */
		    expanded_length = nam->nam$b_node;
		    eptr = nam->nam$l_node;
		    }
		else
		    {		/* name */
		    expanded_length = nam->nam$b_name;
		    eptr = nam->nam$l_name;
		    }
		break;
	    case 'd':		/* device or directory */
		if (field[1] == 'e')
		    {		/* device */
		    expanded_length = nam->nam$b_dev;
		    eptr = nam->nam$l_dev;
		    }
		else
		    {		/* directory */
		    expanded_length = nam->nam$b_dir;
		    eptr = nam->nam$l_dir;
		    }
		break;
	    case 't':		/* type */
		expanded_length = nam->nam$b_type;
		eptr = nam->nam$l_type;
		break;
	    case 'v':		/* version */
		expanded_length = nam->nam$b_ver;
		eptr = nam->nam$l_ver;
		break;
	    default:
		expanded_length = 0;
		break;
	    }
	}
    expanded_name = malloc( expanded_length + 1);
    strncpy( expanded_name, eptr, expanded_length);
    expanded_name[ expanded_length] = '\0';
    return( expanded_name);
}


SHAR_EOF
cat << \SHAR_EOF > sys/vms/make.com
$	on error then goto trouble
$	on severe_error then goto trouble
$!
$! Command procedure to build MG on VMS systems.  To use it, issue the
$! command
$!	@[SYS.VMS]MAKE
$!
$! while in the top-level MG directory.
$!
$!* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
$!
$! Define a search path for include files.
$!
$	define c$include [],[.sys.vms],[.sys.default]
$!
$! Define alias for the compilation command.  Use a command file that
$! checks revision dates and only compiles when it has to.
$!
$	ccom := @[.sys.vms]ccom
$!
$! Select compilation options:
$!
$!	PREFIXREGION	-- use a silly hack that I (mpk) like
$!	XKEYS		-- look for a terminal-specific startup file
$!	NO_BACKUP	-- do not include code for making backup files
$!	NO_DIRED	-- DIRED hasn't been implemented yet
$!	REGEX		-- Include the regular expression code
$!
$!	See MGPROG.DOC in the main directory for more info.
$!
$	ccomflags =  "/DEFINE=(""PREFIXREGION"",""DO_METAKEY"",""XKEYS""" + -
	",""NO_BACKUP"",""NO_DIRED"",""REGEX"")"
$!
$! If REGEX is #defined, link in these two object modules.
$!
$	if f$locate("REGEX",ccomflags) .ne. f$length(ccomflags) then -
		regexobj = "[]re_search.obj,[]regex.obj,"
$!
$! Compile the system-independent files
$!
$ ccom basic.c		'ccomflags
$ ccom dir.c		'ccomflags
$ ccom dired.c		'ccomflags
$ ccom file.c		'ccomflags
$ ccom line.c		'ccomflags
$ ccom match.c		'ccomflags
$ ccom paragraph.c	'ccomflags
$ ccom random.c		'ccomflags
$ ccom region.c		'ccomflags
$ ccom search.c		'ccomflags
$ ccom version.c	'ccomflags
$ ccom window.c		'ccomflags
$ ccom word.c		'ccomflags
$ ccom buffer.c		'ccomflags
$ ccom display.c	'ccomflags
$ ccom echo.c		'ccomflags
$ ccom extend.c		'ccomflags
$ ccom help.c		'ccomflags
$ ccom kbd.c		'ccomflags
$ ccom keymap.c		'ccomflags
$ ccom macro.c		'ccomflags
$ ccom main.c		'ccomflags
$ ccom modes.c		'ccomflags
$ ccom regex.c		'ccomflags
$ ccom re_search.c	'ccomflags
$!
$! Use the termcap terminal driver and default ASCII character set info
$!
$ ccom [.sys.default]cinfo.c		'ccomflags
$ ccom [.sys.default]tty.c		'ccomflags
$ ccom [.sys.default]ttykbd.c		'ccomflags
$!
$! Make the termcap library
$!
$ ccom [.termlib]fgetlr
$ ccom [.termlib]tgetent
$ ccom [.termlib]tgetflag
$ ccom [.termlib]tgetnum
$ ccom [.termlib]tgetstr
$ ccom [.termlib]tgoto
$ ccom [.termlib]tputs
$!
$ library/create/object termcap.olb fgetlr.obj,tgetent.obj,-
tgetflag.obj,tgetnum.obj,tgetstr.obj,tgoto.obj,tputs.obj
$ purge/keep=2 termcap.olb
$!
$! VMS-specific files
$!
$ ccom [.sys.vms]fileio.c		'ccomflags
$ ccom [.sys.vms]spawn.c		'ccomflags
$ ccom [.sys.vms]ttyio.c		'ccomflags
$ ccom [.sys.vms]trnlnm.c		'ccomflags
$ ccom [.sys.vms]fparse.c		'ccomflags
$!
$! VAX Macro sources
$!
$	macro [.sys.vms]unixfns.mar
$!
$! Link it all together
$!
$	link /exe=mg  [.sys.vms]mg.opt/opt, 'regexobj' []termcap.olb/lib
$	exit
$!
$! Trouble!
$!
$trouble:
$	write sys$output "%MAKEIT-F-TROUBLE, trouble building MG"
$	exit
SHAR_EOF
cat << \SHAR_EOF > sys/vms/mg.com
$	Verify = 'F$Verify(0)
$!
$! MG.COM
$!
$! Usage:
$! @MG [file1 [file2 [...]]]		! To start up MG
$! @MG [file]				! To reattach to MG after ^Z
$!
$! MG.COM implements a "kept-fork" capability for MG, allowing you to pop
$! in and out of the editor without reloading it all the time.  If a
$! process called user_MG (where user is your username) exists, this
$! command file attempts to attach to it.  If not, it silently spawns a
$! subjob to run Emacs for you. 
$!
$! To `keep' MG around once you get into it, use "suspend-emacs" (bound
$! to C-z by default) to suspend MG and attach back to the process
$! pointed to by MG$AttachTo. 
$!
$! To get back into MG from DCL enter @MG again.  You may optionally
$! specify *one* new file name, in which case MG will attempt to
$! visit that file when you re-attach. 
$!
$!----------------------------------------------------------------
$!
$! Set things up.  Change the definition of MG_Name to whatever you like.
$! You'll *have* to redefine MG_PROG, of course...
$!
$	MG_Name = F$Edit(F$Getjpi("","USERNAME"),"TRIM") + "_MG"
$	MG_Prog = "Disk$Staff:[Ccep001.Proj.Mg3]MG.Exe"
$	MG_Base = MG_Name			! Used for additions
$	If F$Length(MG_Base) .GT. 13 Then -	! Truncate base for _1,_2...
$		MG_Base = F$Extract(0,13,MG_Base)
$	Proc = F$GetJpi("","PRCNAM")
$	Master_Pid = F$Getjpi("","MASTER_PID")
$!
$! Define logical names used for communicating with MG
$!
$	Define/Nolog/Job MG$AttachTo	"''Proc'"
$	Define/Nolog/Job MG$File	" "	! No file by default
$	If P1 .Nes. "" Then -
		Define/Nolog/Job MG$File "''P1'"
$!
$! Attempt to find MG subprocess in current tree.  If found, attach
$! to it, else spawn a new MG process
$!
$	Save_Priv = F$SetPrv("NOWORLD,NOGROUP")	! Only look in job tree
$	Try_Count = 1
$Search:
$	Context = ""			! Set up process search context
$ProcLoop:
$	Pid = F$Pid(Context)		! Get next PID
$	If Pid .Eqs. "" Then -
		 Goto Spawn		! No MG_Name found; spawn a process
$	If F$GetJpi(Pid,"PRCNAM") .Nes. MG_Name Then -
		Goto Procloop		! Try next process
$! Process name matches; see if it's in our job
$	If F$GetJpi(Pid,"MASTER_PID") .Eqs. Master_Pid Then -
		Goto Attach		! Found process in our job!
$! Process name matches, but isn't in our job.  Re-start search
$	MG_Name = MG_Base + "_" + F$String(Try_Count)
$	Try_Count = Try_Count + 1
$	Goto Search
$!
$! Here to attach to a process in our tree. Set message to
$! turn off the "Attaching to..." message
$!
$Attach:
$	Message = F$Environment("MESSAGE")
$	Set Proc/Priv=('Save_Priv')		! Restore privileges
$	Set Message/NoFacility/NoIdentification/NoSeverity/NoText
$	Attach "''MG_Name'"
$	Set Message/Facility/Identification/Severity/Text
$	Goto Done
$!
$! Here if can't attach.  Spawn a new MG process
$!
$Spawn:
$	Set Process/Priv=('Save_Priv')		! Restore privileges
$	MG$MG :== $'MG_Prog'			! Avoid recursion
$	Spawn/NoLog/Proc="''MG_Name'" MG$MG 'P1' 'P2' 'P3' 'P4' 'P5' 'P6' 'P7' 'P8'
$	Delete/Symbol/Global MG$MG		! Get rid of it 
$Done:
$!
$! Here once we reconnect from MG, whether we detached or exited.
$!
$	Deassign/Job MG$File
$	Deassign/Job MG$AttachTo
$	If Verify Then -
		Set Verify
SHAR_EOF
cat << \SHAR_EOF > sys/vms/mg.opt
!
! Options file for MG 2a
!
basic.obj, dir.obj, dired.obj, file.obj, line.obj, match.obj, -
paragraph.obj, random.obj, region.obj, search.obj, version.obj,  -
window.obj, word.obj, buffer.obj, display.obj, echo.obj, extend.obj, -
help.obj, kbd.obj, keymap.obj, macro.obj, main.obj,  -
modes.obj, cinfo.obj, fileio.obj, spawn.obj, ttyio.obj, tty.obj, -
ttykbd.obj, unixfns.obj, fparse.obj, trnlnm.obj,  -
SYS$SHARE:VAXCRTL.EXE/SHARE
SHAR_EOF
cat << \SHAR_EOF > sys/vms/spawn.c
/*
 * Name:	MicroEMACS
 *		VAX/VMS spawn and attach to a DCL subprocess.
 * Created:	rex::conroy
 *		decvax!decwrl!dec-rhea!dec-rex!conroy
 * Modified:
 * 		19-May-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Add att-to-parent command to attach to the parent
 *			process.  If we can't attach to parent somehow,
 *			spawn a DCL subjob.  This gives us the same
 *			suspend capability as Unix Emacses.
 *
 *			As an added hook, you can DEFINE/JOB
 *			MG$ATTACHTO to a process name, and
 *			the code will try to attach to that name.
 *
 *			Also, if the logical name MG$FILE is
 *			defined, attachtoparent() will visit that file
 *			when you re-attach to Emacs.  This is useful
 *			for a lot of applications, especially MAIL/EDIT...
 *		26-Jun-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Specify process we're attaching to when we attempt
 *			to attach to it.
 *		03-Sep-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Call savebuffers() before leaving the editor.
 *			Unlike csh, DCL has no problem with people
 *			logging out without completing subjobs...
 *			#define NOSAVEONZ if you don't want this behavior.
 *		13-Oct-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Change MICROEMACS$... to MG$... for consistency.
 *		20-Feb-87 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Get rid of call to eputs(), so it can be private
 *			to echo.c.
 */
#include	"def.h"

#include	<ssdef.h>
#include	<stsdef.h>
#include	<descrip.h>
#include	<iodef.h>
#include	<jpidef.h>

#define	EFN	0				/* Event flag.		*/

extern	int	oldmode[3];			/* In "ttyio.c".	*/
extern	int	newmode[3];
extern	short	iochan;
extern	int	ckttysize();			/* Checks for new term size */
extern	int	savebuffers();			/* Save all buffers before  */

/*
 * Create a subjob with a copy
 * of the command intrepreter in it. When the
 * command interpreter exits, mark the screen as
 * garbage so that you do a full repaint. Bound
 * to "C-C" and called from "C-Z". The message at
 * the start in VMS puts out a newline. Under
 * some (unknown) condition, you don't get one
 * free when DCL starts up.
 */

spawncli(f, n)
{
	register int	s;
	register char	*msg;

	if (savebuffers() == ABORT)		/* TRUE means all saved,*/
		return (ABORT);			/* FALSE means not.	*/
	eerase();				/* Get rid of echo line	*/
	ttcolor(CTEXT);				/* Normal color.	*/
	ttnowindow();				/* Full screen scroll.	*/
	ttmove(nrow-1, 0);			/* Last line.		*/
	msg = "Starting DCL";
	while (*msg)				/* Avoid using eputs()	*/
		ttputc(*(msg++));
	ttputc('\r');
	ttputc('\n');
	ttflush();
	sgarbf = TRUE;
	s = sys(NULL);				/* NULL => DCL.		*/
	return (s);
}

/*
 * Run a command. The "cmd" is a pointer
 * to a command string, or NULL if you want to run
 * a copy of DCL in the subjob (this is how the standard
 * routine LIB$SPAWN works. You have to do wierd stuff
 * with the terminal on the way in and the way out,
 * because DCL does not want the channel to be
 * in raw mode.
 */
sys(cmd)
register char	*cmd;
{
	struct	dsc$descriptor	cdsc;
	struct	dsc$descriptor	*cdscp;
	long	status;
	long	substatus;
	long	iosb[2];

	status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
			  oldmode, sizeof(oldmode), 0, 0, 0, 0);
	if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
		return (FALSE);
	cdscp = NULL;				/* Assume DCL.		*/
	if (cmd != NULL) {			/* Build descriptor.	*/
		cdsc.dsc$a_pointer = cmd;
		cdsc.dsc$w_length  = strlen(cmd);
		cdsc.dsc$b_dtype   = DSC$K_DTYPE_T;
		cdsc.dsc$b_class   = DSC$K_CLASS_S;
		cdscp = &cdsc;
	}
	status = LIB$SPAWN(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0);
	if (status != SS$_NORMAL)
		substatus = status;
	ckttysize();			/* check for new terminal size */
	status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
			  newmode, sizeof(newmode), 0, 0, 0, 0);
	if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
		return (FALSE);
	if ((substatus&STS$M_SUCCESS) == 0)	/* Command failed.	*/
		return (FALSE);
	return (TRUE);
}

/*
 * Front end for combined attach-to-parent and spawn-cli action
 */

attachtoparent(f, n)
{
	register int	s;
	s = attparent();
	if (s == ABORT)
		return (ABORT);
	else if (s == FALSE)
		return spawncli(f, n);	/* better than nothing */
	else
		return (TRUE);
}

/*
 * Attach to parent.  If the logical name MG$ATTACHTO
 * is present, attempt to attach to it.  If not, attempt to
 * attach to parent process.
 *
 * On return, see if the logical name MG$FILE contains
 * anything, and try to visit that file.
 */

static $DESCRIPTOR(nmdsc,"MG$ATTACHTO");

attparent()
{
	long		pid, jpi_code;	
	char		equiv[18], msgbuf[60];
	struct	dsc$descriptor_s eqdsc;
	short		eqlen;
	int		status, pos;
	register BUFFER	*bp;
	BUFFER		*findbuffer();
	int		s;


	/* Set up string descriptor */
	eqdsc.dsc$a_pointer = equiv;
	eqdsc.dsc$w_length  = sizeof(equiv);
	eqdsc.dsc$b_dtype   = DSC$K_DTYPE_T;
	eqdsc.dsc$b_class   = DSC$K_CLASS_S;

	/* Try to translate MG$ATTACH */
	status = lib$sys_trnlog(&nmdsc, &eqdsc.dsc$w_length, &eqdsc);
	if (status!=SS$_NORMAL && status!=SS$_NOTRAN) {
		ewprintf("Error translating %s",nmdsc.dsc$a_pointer);/* DEBUG */
		return (FALSE);
	}

	if (status == SS$_NORMAL) {
		/* Found a translation -- attempt to attach to it */
		jpi_code = JPI$_PID;

		status = lib$getjpi(&jpi_code,0,&eqdsc,&pid,0);
		equiv[eqdsc.dsc$w_length] = '\0';
		if (status != SS$_NORMAL) {
			ewprintf("Error getting JPI for \"%s\"",equiv);
			return (FALSE);
		}

		/* Attempt to attach to named process.  Save all buffers,  */
		/* set sgarbf because attach() always trashes the display  */
		if (savebuffers() == ABORT)
			return (ABORT);
		/* indicate process we're attaching to */
		strcpy(msgbuf,"Attaching to process \"");
		for (pos = strlen(equiv) - 1; pos >= 0; --pos)
			if (equiv[pos] != ' ') {
				equiv[pos+1] = '\0';
				break;
			}
		strcat(msgbuf,equiv);
		strcat(msgbuf,"\"");

		sgarbf = TRUE;
		if (attach(pid,msgbuf) == FALSE)	/* whups -- try spawn */
			return (FALSE);
	}
	else {	/* No translation -- attempt to find parent process */
		jpi_code = JPI$_OWNER;
		status = lib$getjpi(&jpi_code,0,0,&pid,0,0);

		if ((status != SS$_NORMAL) || (pid == 0))	/* not found! */
			return (FALSE);

		if (savebuffers() == ABORT)
			return (ABORT);
		sgarbf = TRUE;
		if (attach(pid,"Attaching to parent process") == FALSE)
			return (FALSE);
	}

	newfile();	/* attempt to find a new file, but don't care	*/
			/* if we don't find one...			*/
	refresh(FFRAND, 0);
	return (TRUE);
}

/*
 * If we find after re-attaching that there is
 * a new file to be edited, attempt to read it in,
 * using essentially the same code as findfile().
 */

static newfile()
{
	register BUFFER	*bp;
	register int	s;
	char		filename[NFILEN];
	BUFFER		*findbuffer();

	if ((s = cknewfile(filename, sizeof filename)) != TRUE)
		return (s);
	if ((bp = findbuffer(filename, &s)) == NULL)
		return (s);
	curbp = bp;
	if (showbuffer(bp, curwp, WFHARD) != TRUE)
		return (FALSE);
	if (bp->b_fname[0] == 0)
		return (readin(filename));	/* Read it in. */
	return (TRUE);
}

/*
 * Attach to a process by process number.  Restore the
 * terminal channel to the way it was when we started.
 * Also put out an optional message to the user.
 */ 

static attach(pid, msg)
long pid;
char *msg;
{
	long	status, attstatus;
	long	iosb[2];

	ttcolor(CTEXT);				/* Normal color.	*/
	ttnowindow();				/* Full screen scroll.	*/
	ttmove(nrow-1, 0);			/* Last line.		*/
	if (msg) {				/* Display a message	*/
		while (*msg)
			ttputc(*(msg++));
		ttputc('\r');
		ttputc('\n');
	}
	ttflush();

	/* Set terminal to old modes */
	status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
			  oldmode, sizeof(oldmode), 0, 0, 0, 0);
	if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
		return (FALSE);

	/* Attach to the process */
	attstatus = LIB$ATTACH(&pid);

	/* Return terminal to the modes MG needs */
	ckttysize();			/* check for new terminal size first */
	status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
			  newmode, sizeof(newmode), 0, 0, 0, 0);
	if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
		return (FALSE);

	return (attstatus == SS$_NORMAL ? TRUE : FALSE);
}


/*
 * Attempt to translate MG$FILE into fname.
 * If it's there and non-empty, return TRUE.
 */

static $DESCRIPTOR(filedsc,"MG$FILE");

static cknewfile(fname,fnsiz)
char *fname;
int fnsiz;
{
	char 	equiv[NFILEN];
	struct dsc$descriptor_s eqdsc;
	short	len;
	register int	status;

	eqdsc.dsc$a_pointer = equiv;
	eqdsc.dsc$w_length  = sizeof(equiv);
	eqdsc.dsc$b_dtype   = DSC$K_DTYPE_T;
	eqdsc.dsc$b_class   = DSC$K_CLASS_S;

	status = lib$sys_trnlog(&filedsc, &len, &eqdsc);
	if (status!=SS$_NORMAL && status!=SS$_NOTRAN) {
		ewprintf("Error translating MG$FILE");
		return (FALSE);
	}

	if (status == SS$_NOTRAN)		/* No new file found	*/
		return (FALSE);

	if (equiv[0] == ' ')
		return (FALSE);

	equiv[len] = '\0';
	strcpy(fname, equiv);
	return (TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > sys/vms/sysdef.h
/*
 * Name:	MicroEMACS
 *		VAX/VMS system header file.
 * Version:	29
 * Last edit:	05-Feb-86
 * By:		rex::conroy
 *		decvax!decwrl!dec-rhea!dec-rex!conroy
 */
#include	<stdio.h>
#include	<ssdef.h>

#define	overwrite	ovwrite		/* avoid conflict with curses	*/

#define	PCC	0			/* "[]" works.			*/
#define	KBLOCK	8192			/* Kill grow.			*/
#define	GOOD	(SS$_NORMAL)		/* Good exit status.		*/
#define	MAXPATH	1024			/* Goodly-sized path name	*/

typedef int	RSIZE;			/* Type for file/region sizes	*/
typedef unsigned char KCHAR;		/* Type for keyboard character  */
					/* (all must be positive)       */

/*
 * Macros used by the buffer name making code.
 * Start at the end of the file name, scan to the left
 * until BDC1 (or BDC2, if defined) is reached. The buffer
 * name starts just to the right of that location, and
 * stops at end of string (or at the next BDC3 character,
 * if defined). BDC2 and BDC3 are mainly for VMS.
 */
#define	BDC1	':'			/* Buffer names.		*/
#define	BDC2	']'
#define	BDC3	';'

#define	DPROMPT				/* use delayed prompts		*/
#define GOSMACS				/* for Gosling's compatibility fns */
SHAR_EOF
cat << \SHAR_EOF > sys/vms/trnlnm.c
/*
 * Name:	MicroEmacs
 *		VAX/VMS translate logical name routine
 * Version:	Gnu30
 * Last Edit:	10-Jul-86
 * By:		...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *
 */

/*
 *
 * Trnlnm()
 *
 * Description:
 *	Attempt to translate the logical name logname into an equivalence
 *	string, using the standard VMS routine LIB$SYS_TRNLOG().
 *	If a translation exists, return a pointer to the static area
 *	that contains the null-terminated translation string.  If not,
 *	return 0.
 *
 *  Bugs:
 *	Returns a pointer to static data that is modified each time
 *	the routine successfully translates a logical name.
 */

#include <ssdef.h>
#include <descrip.h>
#include <stdio.h>

static char _equiv_buf[256];
static struct dsc$descriptor_s
_equiv = {
	sizeof(_equiv_buf), DSC$K_DTYPE_T, DSC$K_CLASS_S, _equiv_buf
},
_name = {
	0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL
};

char *trnlnm(logname)
char *logname;
{
	short eqlen;
	int status;

	if (logname == NULL)
		return (NULL);

	_name.dsc$a_pointer = logname;
	_name.dsc$w_length = strlen(logname);

	status = lib$sys_trnlog(&_name, &eqlen, &_equiv);
	if (status != SS$_NORMAL)
		return (NULL);

	_equiv_buf[eqlen] = '\0';
	return (_equiv_buf);
}

/* gettermtype -- get the terminal type used for MG.
 *
 * If there is a logical name translation for EMACS_TERM, return that.
 * Otherwise translate the logical name TERM.
 * (Both GNU Emacs and Unipress Emacs use EMACS_TERM, but Eunice likes TERM.)
 *
 */


char *gettermtype()
{   register char *result;

    if (result = trnlnm("EMACS_TERM"))
        return(result);
    else
        return(trnlnm("TERM"));
    }
SHAR_EOF
cat << \SHAR_EOF > sys/vms/unixfns.mar
	.title	unixfns	MG access to Unix library functions
;
;	bcopy by Mic Kaczmarczik, July 11, 1986
;	everything else by Peter Newton April 24, 1987
;
; UNIX stack-based memory allocation
;
	.entry	alloca,^m<>
	movl	4(ap),r0		; number of bytes
	movl	16(fp),r1		; return address
	moval	alloca_reenter,16(fp)	; return to reenter code
	ret
alloca_reenter:
	addl	#3,r0			; round up byte count to
	bicl	#3,r0			;   longword boundary
	subl	r0,sp			; allocate stack space
	moval	(sp),r0			; address of allocated block
	jmp	(r1)			; return to caller
;
; UNIX bcmp function
;
	.entry	bcmp,^m<r2,r3>
	cmpc3	12(ap),@4(ap),@8(ap)
	ret
;
;  UNIX bcopy function
;
	.entry	bcopy,^m<r2,r3,r4,r5>	; MOVC3 side-effects r0-r5
	subl2	#4,sp			; Step over call frame
	movc3	12(ap),@4(ap),@8(ap)	; Copy them bytes
	ret				; Bye!
;
; UNIX bzero function
;
	.entry	bzero,^m<r2,r3,r4,r5>
	movc5	#0,(r0),#0,8(ap),@4(ap)
	ret
	.end
SHAR_EOF
#	End of shell archive
exit 0
-------