[comp.emacs] Automatic backup for MicroEmacs 3.9e

zu@ethz.UUCP (Urs Zurbuchen) (12/15/87)

[May the schwartz be with the line eater]

Hello, you fans of MicroEmacs and other small things,

Something I missed badly in MicroEmacs (ever since I used it), was its
inability to automatically save my original file as a backup file. More
than once I lost some hours work hitting the wrong key, just beacuse there
was no backup file.
(BTW, this would also solve the problem (recently mentioned) when working
under MS-DOS and hitting ^C during a save. Ok, the ^C wouldn't be trapped
here, too, but at least you had a backup and could start again :-) )

Now, instead of going on complaining, I put my nose deep into the sources
and added that feature. 

I had to change three source and two header files. Following are the dif-
files as shar. Also included is a file 'readme' with some remarks concerning
the implementation.
Note that I posted the complete version of file.c. The diffs were as large
as the original.

----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE --
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	readme
#	edef.dif
#	estruct.dif
#	fileio.dif
#	main.dif
#	file.c
# This archive created: Tue Dec 15 11:08:50 1987
export PATH; PATH=/bin:$PATH
if test -f 'readme'
then
	echo shar: will not over-write existing file "'readme'"
else
cat << \SHAR_EOF > 'readme'
To add the backup-feature to MicroEmacs 3.9e use the four dif-files in this
posting. You should be able to use patch to apply the changes to your sources.
But be careful. I already implemented some of the patches posted on usenet
during the last few weeks, BUT NOT ALL OF THEM. So it's really possible
that you don't have the same 'original' sources as I had when extracting the
diffs.
This is true for MAIN.C for sure. I have changed some other things which
aren't included in this posting here. I added a short notice at the end of the
revision history. And you will see only my last entry in the dif-file. But,
hey, now you know what to look for.
(There is of course another entry in the revision history for this new
feature.)
(BTW the way, I don't have a version of Patch myself. Could some kind soul
please send the sources to me?)
Ok, now to the important stuff (assuming that you made the patch successfully):
In estruct.h:
	There are two new preprocessor constants called BACKUP and BUPEVS
	respectively. The meaning for the first should be clear. If you set
	it to one, the code for the backup feature is compiled, too.
	With the second constant you can define wether a backup file should
	be generated with every save or just with the first one of an editing
	session. If you set it to one you will always have the youngest two
	version of your file saved to disk: the original and a backup. If you
	set the constant to zero you will have the original file and, as a
	backup file, the file as it was when you started editing.
	Made everything unclear ?

How does it work:
	The file is first saved under a name which should be guaranteed to
	be unique on a given system ( using mktemp("meXXXXXX") ). If that is
	successfull, the original is renamed to the name of the backup file
	and the saved file is then renamed to the original file. If anything
	in this process goes wrong, the original situation is reinstalled
	and the user gets a message "SAVE FAILED".
	The name of the backup file is 'computed' as follows:

	MSDOS:	a tilde '~' is prepended to the extension. This way we get
		different backup files for (e.g.) foo.c & foo.h
	UNIX:	at the front of the filename is a '#' inserted. The filename
		is guaranteed to be not longer than 14 characters (255 on BSD).
	
Some special remarks:
	I also changed the function 'fexist' in fileio.c to use the system
	call 'access(filename, mode)' when using a Unix C compiler or
	Microsoft C. I think it should be faster and you're not using a
	filehandle to test for a file's presence.
	Please tell me of any other compiler which knows this function.
	I also added a new function 'frename' which just does what you think.
	This is ifdef'ed for MSC and UNIX as well. The same applies as above.

Restrictions:
	This patch works only for MS-DOS and (any) UNIX, by now. I don't
	have an AMIGA, an ATARI STx nor a VMS-VAX, so I couldn't add the code
	necessary for them.
	I have some colleagues who have Amigas and Ataris. I should be able
	to figure out how it should be done. But DO NOT rely on that. If
	someone of you implemets it, please send a copy to Daniel Lawrence
	and to me (unless Daniel states that he isn't interested in that
	feature at all, of course).
	On UNIX, it will work and make a backup file, but I'm not sure if
	the new, saved file has the same status according to locks as the
	original. (Bye the way, does it if I just save my file without
	exiting ?)
	For you BSD-types: I'm sure you already got used to it, but I used
	some of the string functions as they are defined for SYS V. I decided
	not to use a defined constant for that function because all the
	other functions (like strcat, strchr, ...) aren't defined as well.
	You better look for strrchr, strcpy and strcat in file.c

SHAR_EOF
fi # end of overwriting check
if test -f 'edef.dif'
then
	echo shar: will not over-write existing file "'edef.dif'"
else
cat << \SHAR_EOF > 'edef.dif'
*** edef.org	Sun Dec 13 22:16:06 1987
--- edef.h	Mon Dec 14 20:19:40 1987
***************
*** 127,132
  char	tap[NPAT];			/* Reversed pattern array.	*/
  char	rpat[NPAT];			/* replacement pattern		*/
  
  /* The variable matchlen holds the length of the matched
   * string - used by the replace functions.
   * The variable patmatch holds the string that satisfies

--- 127,136 -----
  char	tap[NPAT];			/* Reversed pattern array.	*/
  char	rpat[NPAT];			/* replacement pattern		*/
  
+ #if	BACKUP
+ char	*bup_file;		/* temporary filename for saved files	*/
+ #endif
+ 
  /* The variable matchlen holds the length of the matched
   * string - used by the replace functions.
   * The variable patmatch holds the string that satisfies
***************
*** 180,185
  extern  short   kbdm[];                 /* Holds kayboard macro data    */
  extern  char    pat[];                  /* Search pattern               */
  extern	char	rpat[];			/* Replacement pattern		*/
  extern	char	*execstr;		/* pointer to string to execute	*/
  extern	char	golabel[];		/* current line to go to	*/
  extern	int	execlevel;		/* execution IF level		*/

--- 184,192 -----
  extern  short   kbdm[];                 /* Holds kayboard macro data    */
  extern  char    pat[];                  /* Search pattern               */
  extern	char	rpat[];			/* Replacement pattern		*/
+ #if	BACKUP
+ extern	char	*bup_file;		/* temporary save filename	*/
+ #endif
  extern	char	*execstr;		/* pointer to string to execute	*/
  extern	char	golabel[];		/* current line to go to	*/
  extern	int	execlevel;		/* execution IF level		*/
SHAR_EOF
fi # end of overwriting check
if test -f 'estruct.dif'
then
	echo shar: will not over-write existing file "'estruct.dif'"
else
cat << \SHAR_EOF > 'estruct.dif'
*** estruct.org	Sun Dec 13 17:14:06 1987
--- estruct.h	Mon Dec 14 20:19:00 1987
***************
*** 97,102
  #define	COLOR	1	/* color commands and windows			*/
  
  #define	FILOCK	0	/* file locking under unix BSD 4.2		*/
  #define	ISRCH	1	/* Incremental searches like ITS EMACS		*/
  #define	WORDPRO	1	/* Advanced word processing features		*/
  #define	FLABEL	0	/* function key label code [HP150]		*/

--- 97,104 -----
  #define	COLOR	1	/* color commands and windows			*/
  
  #define	FILOCK	0	/* file locking under unix BSD 4.2		*/
+ #define	BACKUP	1	/* automatically make a backup file		*/
+ #define BUPEVS	0	/* backup file every save (or with first save only) */
  #define	ISRCH	1	/* Incremental searches like ITS EMACS		*/
  #define	WORDPRO	1	/* Advanced word processing features		*/
  #define	FLABEL	0	/* function key label code [HP150]		*/
***************
*** 420,425
          char    b_flag;                 /* Flags                        */
  	int	b_mode;			/* editor mode of this buffer	*/
          char    b_fname[NFILEN];        /* File name                    */
          char    b_bname[NBUFN];         /* Buffer name                  */
  #if	CRYPT
  	char	b_key[NPAT];		/* current encrypted key	*/

--- 422,430 -----
          char    b_flag;                 /* Flags                        */
  	int	b_mode;			/* editor mode of this buffer	*/
          char    b_fname[NFILEN];        /* File name                    */
+ #if	BACKUP && !BUPEVS
+         int	b_bupflg;		/* Backup file at next save?	*/
+ #endif
          char    b_bname[NBUFN];         /* Buffer name                  */
  #if	CRYPT
  	char	b_key[NPAT];		/* current encrypted key	*/
***************
*** 426,432
  #endif
  }       BUFFER;
  
! #define BFINVS  0x01                    /* Internal invisable buffer    */
  #define BFCHG   0x02                    /* Changed since last write     */
  #define	BFTRUNC	0x04			/* buffer was truncated when read */
  

--- 431,437 -----
  #endif
  }       BUFFER;
  
! #define BFINVS  0x01                    /* Internal invisible buffer    */
  #define BFCHG   0x02                    /* Changed since last write     */
  #define	BFTRUNC	0x04			/* buffer was truncated when read */
  
SHAR_EOF
fi # end of overwriting check
if test -f 'fileio.dif'
then
	echo shar: will not over-write existing file "'fileio.dif'"
else
cat << \SHAR_EOF > 'fileio.dif'
*** fileio.org	Fri Nov 20 14:26:22 1987
--- fileio.c	Mon Dec 14 21:08:28 1987
***************
*** 186,191
  {
  	FILE *fp;
  
  	/* try to open the file for reading */
  	fp = fopen(fname, "r");
  

--- 186,194 -----
  {
  	FILE *fp;
  
+ #if	MSC | UNIX
+ 	return( ( access( fname, 0 )) ? FALSE : TRUE );
+ #else
  	/* try to open the file for reading */
  	fp = fopen(fname, "r");
  
***************
*** 196,201
  	/* otherwise, close it and report true */
  	fclose(fp);
  	return(TRUE);
  }
  
  #if	AZTEC & MSDOS

--- 199,221 -----
  	/* otherwise, close it and report true */
  	fclose(fp);
  	return(TRUE);
+ #endif	/* MSC | UNIX */
+ }
+ 
+ int frename( from, to )	/* rename a file */
+ char	*from;
+ char	*to;
+ {
+ #if	MSC
+ 	return( rename( from, to ));
+ #endif
+ #if	UNIX
+ 	if( link( from, to ) == 0 )
+ 		if( unlink( from ) == 0 )
+ 			return( 0 );
+ 		unlink( to );
+ 		return( -1 );
+ #endif	/* UNIX */
  }
  
  #if	AZTEC & MSDOS
SHAR_EOF
fi # end of overwriting check
if test -f 'main.dif'
then
	echo shar: will not over-write existing file "'main.dif'"
else
cat << \SHAR_EOF > 'main.dif'
*** main.org	Sun Dec 13 17:12:32 1987
--- main.c	Mon Dec 14 20:19:46 1987
***************
*** 859,864
   *	30-nov-87
   *	- made flook() look first in the list in epath.h, then in the HOME
   *	  directory, then in the current directory, and then down the $PATH
   */
    
  #include        <stdio.h>

--- 859,868 -----
   *	30-nov-87
   *	- made flook() look first in the list in epath.h, then in the HOME
   *	  directory, then in the current directory, and then down the $PATH
+  *	14-dec-87
+  *	- added support for backup files. when saved for the first time during
+  *	  and edit-session each file is renamed to a backup file.
+  *	  by now, only MS-DOS and Unix supported.
   */
    
  #include        <stdio.h>
***************
*** 933,938
  #endif
  	char *strncpy();
  	extern *pathname[];		/* startup file path/name array */
  
  	/* initialize the editor */
          vtinit();		/* Display */

--- 937,945 -----
  #endif
  	char *strncpy();
  	extern *pathname[];		/* startup file path/name array */
+ #if	BACKUP
+ 	char *mktemp();
+ #endif
  
  	/* initialize the editor */
          vtinit();		/* Display */
***************
*** 952,957
  	eexitflag = FALSE;	/* not time to exit yet */
  #endif
  
  	/* Parse the command line */
  	for (carg = 1; carg < argc; ++carg) {
  

--- 959,969 -----
  	eexitflag = FALSE;	/* not time to exit yet */
  #endif
  
+ #if	BACKUP
+ 				/* initialise global variables for backups */
+ 	bup_file  = mktemp( "meXXXXXX" );
+ #endif
+ 	
  	/* Parse the command line */
  	for (carg = 1; carg < argc; ++carg) {
SHAR_EOF
fi # end of overwriting check
if test -f 'file.c'
then
	echo shar: will not over-write existing file "'file.c'"
else
cat << \SHAR_EOF > 'file.c'
/*	FILE.C:   for MicroEMACS

	The routines in this file handle the reading, writing
	and lookup of disk files.  All of details about the
	reading and writing of the disk are in "fileio.c".

*/

#include        <stdio.h>
#include	"estruct.h"
#include        "edef.h"

#if	BACKUP
#include	<errno.h>
extern	int	errno;
#endif

/*
 * Read a file into the current
 * buffer. This is really easy; all you do it
 * find the name of the file, and call the standard
 * "read a file into the current buffer" code.
 * Bound to "C-X C-R".
 */
fileread(f, n)
{
        register int    s;
        char fname[NFILEN];

	if (restflag)		/* don't allow this command if restricted */
		return(resterr());
        if ((s=mlreply("Read file: ", fname, NFILEN)) != TRUE)
                return(s);
        return(readin(fname, TRUE));
}

/*
 * Insert a file into the current
 * buffer. This is really easy; all you do it
 * find the name of the file, and call the standard
 * "insert a file into the current buffer" code.
 * Bound to "C-X C-I".
 */
insfile(f, n)
{
        register int    s;
        char fname[NFILEN];

	if (restflag)		/* don't allow this command if restricted */
		return(resterr());
	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
		return(rdonly());	/* we are in read only mode	*/
        if ((s=mlreply("Insert file: ", fname, NFILEN)) != TRUE)
                return(s);
        return(ifile(fname));
}

/*
 * Select a file for editing.
 * Look around to see if you can find the
 * fine in another buffer; if you can find it
 * just switch to the buffer. If you cannot find
 * the file, create a new buffer, read in the
 * text, and switch to the new buffer.
 * Bound to C-X C-F.
 */
filefind(f, n)
{
        char fname[NFILEN];	/* file user wishes to find */
        register int s;		/* status return */

	if (restflag)		/* don't allow this command if restricted */
		return(resterr());
        if ((s=mlreply("Find file: ", fname, NFILEN)) != TRUE)
                return(s);
	return(getfile(fname, TRUE));
}

viewfile(f, n)	/* visit a file in VIEW mode */
{
        char fname[NFILEN];	/* file user wishes to find */
        register int s;		/* status return */
	register WINDOW *wp;	/* scan for windows that need updating */

	if (restflag)		/* don't allow this command if restricted */
		return(resterr());
        if ((s=mlreply("View file: ", fname, NFILEN)) != TRUE)
                return (s);
	s = getfile(fname, FALSE);
	if (s) {	/* if we succeed, put it in view mode */
		curwp->w_bufp->b_mode |= MDVIEW;

		/* scan through and update mode lines of all windows */
		wp = wheadp;
		while (wp != NULL) {
			wp->w_flag |= WFMODE;
			wp = wp->w_wndp;
		}
	}
	return(s);
}

#if	CRYPT
resetkey()	/* reset the encryption key if needed */

{
	register int s;	/* return status */

	/* turn off the encryption flag */
	cryptflag = FALSE;

	/* if we are in crypt mode */
	if (curbp->b_mode & MDCRYPT) {
		if (curbp->b_key[0] == 0) {
			s = setkey(FALSE, 0);
			if (s != TRUE)
				return(s);
		}

		/* let others know... */
		cryptflag = TRUE;

		/* and set up the key to be used! */
		/* de-encrypt it */
		crypt((char *)NULL, 0);
		crypt(curbp->b_key, strlen(curbp->b_key));

		/* re-encrypt it...seeding it to start */
		crypt((char *)NULL, 0);
		crypt(curbp->b_key, strlen(curbp->b_key));
	}

	return(TRUE);
}
#endif

getfile(fname, lockfl)

char fname[];		/* file name to find */
int lockfl;		/* check the file for locks? */

{
        register BUFFER *bp;
        register LINE   *lp;
        register int    i;
        register int    s;
        char bname[NBUFN];	/* buffer name to put file */

#if	MSDOS
	mklower(fname);		/* msdos isn't case sensitive */
#endif
        for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
                if ((bp->b_flag&BFINVS)==0 && strcmp(bp->b_fname, fname)==0) {
			swbuffer(bp);
                        lp = curwp->w_dotp;
                        i = curwp->w_ntrows/2;
                        while (i-- && lback(lp)!=curbp->b_linep)
                                lp = lback(lp);
                        curwp->w_linep = lp;
                        curwp->w_flag |= WFMODE|WFHARD;
                        mlwrite("[Old buffer]");
                        return (TRUE);
                }
        }
        makename(bname, fname);                 /* New buffer name.     */
        while ((bp=bfind(bname, FALSE, 0)) != NULL) {
		/* old buffer name conflict code */
                s = mlreply("Buffer name: ", bname, NBUFN);
                if (s == ABORT)                 /* ^G to just quit      */
                        return (s);
                if (s == FALSE) {               /* CR to clobber it     */
                        makename(bname, fname);
                        break;
                }
        }
        if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
                mlwrite("Cannot create buffer");
                return (FALSE);
        }
        if (--curbp->b_nwnd == 0) {             /* Undisplay.           */
                curbp->b_dotp = curwp->w_dotp;
                curbp->b_doto = curwp->w_doto;
                curbp->b_markp = curwp->w_markp;
                curbp->b_marko = curwp->w_marko;
        }
        curbp = bp;                             /* Switch to it.        */
        curwp->w_bufp = bp;
        curbp->b_nwnd++;
        return(readin(fname, lockfl));          /* Read it in.          */
}

/*
	Read file "fname" into the current buffer, blowing away any text
	found there.  Called by both the read and find commands.  Return
	the final status of the read.  Also called by the mainline, to
	read in a file specified on the command line as an argument. 
	The command bound to M-FNR is called after the buffer is set up
	and before it is read. 
*/

readin(fname, lockfl)

char    fname[];	/* name of file to read */
int	lockfl;		/* check for file locks? */

{
        register LINE   *lp1;
        register LINE   *lp2;
        register int    i;
        register WINDOW *wp;
        register BUFFER *bp;
        register int    s;
        register int    nbytes;
        register int    nline;
	int		lflag;		/* any lines longer than allowed? */
	char mesg[NSTRING];

#if	FILOCK
	if (lockfl && lockchk(fname) == ABORT)
		return(ABORT);
#endif
#if	CRYPT
	s = resetkey();
	if (s != TRUE)
		return(s);
#endif
        bp = curbp;                             /* Cheap.               */
        if ((s=bclear(bp)) != TRUE)             /* Might be old.        */
                return (s);
        bp->b_flag &= ~(BFINVS|BFCHG);
#if	BACKUP && !BUPEVS
	curbp->b_bupflg = FALSE;		/* no backup file yet	*/
#endif
        strcpy(bp->b_fname, fname);

	/* let a user macro get hold of things...if he wants */
	execute(META|SPEC|'R', FALSE, 1);

	/* turn off ALL keyboard translation in case we get a dos error */
	TTkclose();

        if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
                goto out;

        if (s == FIOFNF) {                      /* File not found.      */
                mlwrite("[New file]");
                goto out;
        }

	/* read the file in */
        mlwrite("[Reading file]");
        nline = 0;
	lflag = FALSE;
        while ((s=ffgetline()) == FIOSUC) {
                nbytes = strlen(fline);
                if ((lp1=lalloc(nbytes)) == NULL) {
                        s = FIOMEM;             /* Keep message on the  */
                        break;                  /* display.             */
                }
                lp2 = lback(curbp->b_linep);
                lp2->l_fp = lp1;
                lp1->l_fp = curbp->b_linep;
                lp1->l_bp = lp2;
                curbp->b_linep->l_bp = lp1;
                for (i=0; i<nbytes; ++i)
                        lputc(lp1, i, fline[i]);
                ++nline;
        }
        ffclose();                              /* Ignore errors.       */
	strcpy(mesg, "[");
	if (s==FIOERR) {
		strcat(mesg, "I/O ERROR, ");
		curbp->b_flag |= BFTRUNC;
	}
	if (s == FIOMEM) {
		strcat(mesg, "OUT OF MEMORY, ");
		curbp->b_flag |= BFTRUNC;
	}
	sprintf(&mesg[strlen(mesg)], "Read %d line", nline);
        if (nline > 1)
		strcat(mesg, "s");
	strcat(mesg, "]");
	mlwrite(mesg);

out:
	TTkopen();	/* open the keyboard again */
        for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
                if (wp->w_bufp == curbp) {
                        wp->w_linep = lforw(curbp->b_linep);
                        wp->w_dotp  = lforw(curbp->b_linep);
                        wp->w_doto  = 0;
                        wp->w_markp = NULL;
                        wp->w_marko = 0;
                        wp->w_flag |= WFMODE|WFHARD;
                }
        }
        if (s == FIOERR || s == FIOFNF)		/* False if error.      */
                return(FALSE);
        return (TRUE);
}

/*
 * Take a file name, and from it
 * fabricate a buffer name. This routine knows
 * about the syntax of file names on the target system.
 * I suppose that this information could be put in
 * a better place than a line of code.
 */
makename(bname, fname)
char    bname[];
char    fname[];
{
        register char *cp1;
        register char *cp2;

        cp1 = &fname[0];
        while (*cp1 != 0)
                ++cp1;

#if     AMIGA
        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='/')
                --cp1;
#endif
#if     VMS
        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!=']')
                --cp1;
#endif
#if     CPM
        while (cp1!=&fname[0] && cp1[-1]!=':')
                --cp1;
#endif
#if     MSDOS
        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\'&&cp1[-1]!='/')
                --cp1;
#endif
#if     ST520
        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\')
                --cp1;
#endif
#if     FINDER
        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\'&&cp1[-1]!='/')
                --cp1;
#endif
#if     V7 | USG | BSD
        while (cp1!=&fname[0] && cp1[-1]!='/')
                --cp1;
#endif
        cp2 = &bname[0];
        while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
                *cp2++ = *cp1++;
        *cp2 = 0;
}

unqname(name)	/* make sure a buffer name is unique */

char *name;	/* name to check on */

{
	register char *sp;

	/* check to see if it is in the buffer list */
	while (bfind(name, 0, FALSE) != NULL) {

		/* go to the end of the name */
		sp = name;
		while (*sp)
			++sp;
		if (sp == name || (*(sp-1) <'0' || *(sp-1) > '8')) {
			*sp++ = '0';
			*sp = 0;
		} else
			*(--sp) += 1;
	}
}

/*
 * Ask for a file name, and write the
 * contents of the current buffer to that file.
 * Update the remembered file name and clear the
 * buffer changed flag. This handling of file names
 * is different from the earlier versions, and
 * is more compatable with Gosling EMACS than
 * with ITS EMACS. Bound to "C-X C-W".
 */
filewrite(f, n)
{
        register WINDOW *wp;
        register int    s;
        char            fname[NFILEN];

	if (restflag)		/* don't allow this command if restricted */
		return(resterr());
        if ((s=mlreply("Write file: ", fname, NFILEN)) != TRUE)
                return (s);
        if ((s=writeout(fname)) == TRUE) {
                strcpy(curbp->b_fname, fname);
                curbp->b_flag &= ~BFCHG;
#if	BACKUP && !BUPEVS
		curbp->b_bupflg = TRUE;
#endif
                wp = wheadp;                    /* Update mode lines.   */
                while (wp != NULL) {
                        if (wp->w_bufp == curbp)
                                wp->w_flag |= WFMODE;
                        wp = wp->w_wndp;
                }
        }
        return (s);
}

/*
 * Save the contents of the current
 * buffer in its associated file. Do nothing
 * if nothing has changed (this may be a bug, not a
 * feature). Error if there is no remembered file
 * name for the buffer. Bound to "C-X C-S". May
 * get called by "C-Z".
 */
filesave(f, n)
{
        register WINDOW *wp;
        register int    s;

	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
		return(rdonly());	/* we are in read only mode	*/
        if ((curbp->b_flag&BFCHG) == 0)         /* Return, no changes.  */
                return (TRUE);
        if (curbp->b_fname[0] == 0) {           /* Must have a name.    */
                mlwrite("No file name");
                return (FALSE);
        }

	/* complain about truncated files */
	if ((curbp->b_flag&BFTRUNC) != 0) {
		if (mlyesno("Truncated file..write it out") == FALSE) {
			mlwrite("[Aborted]");
			return(FALSE);
		}
	}

        if ((s=writeout(curbp->b_fname)) == TRUE) {
                curbp->b_flag &= ~BFCHG;
#if	BACKUP && !BUPEVS
		curbp->b_bupflg = TRUE;
#endif
                wp = wheadp;                    /* Update mode lines.   */
                while (wp != NULL) {
                        if (wp->w_bufp == curbp)
                                wp->w_flag |= WFMODE;
                        wp = wp->w_wndp;
                }
        }
        return (s);
}

/*
 * This function performs the details of file
 * writing. Uses the file management routines in the
 * "fileio.c" package. The number of lines written is
 * displayed. Sadly, it looks inside a LINE; provide
 * a macro for this. Most of the grief is error
 * checking of some sort.
 * If backuping is enabled the original file gets renamed
 * to a backup file. It tests a flag of the current buffer.
 * Maybe it would be better to pass it as a parameter to
 * this function. But you don't need it if you don't enable
 * backuping.
 */
writeout(fn)
char    *fn;
{
        register int    s;
        register LINE   *lp;
        register int    nline;
#if	BACKUP
	char		save_file[NFILEN];
#endif

#if	CRYPT
	s = resetkey();
	if (s != TRUE)
		return(s);
#endif
	/* turn off ALL keyboard translation in case we get a dos error */
	TTkclose();

#if	!BACKUP
        if ((s=ffwopen(fn)) != FIOSUC) {        /* Open writes message. */
#else
	getsavefilename( save_file,  fn );
	if ((s=ffwopen( save_file )) != FIOSUC) {
        	/* Open a file using a name which is guaranteed to be
        	 * unique on this system (using PID). If the write is
        	 * successfull it will be renamed to its real name. This
        	 * way we don't have a problem with the backup file.
        	 * If we get an error here, Open writes a message itself.
        	 */
#endif
		TTkopen();
                return (FALSE);
        }
	mlwrite("[Writing...]");		/* tell us were writing */
        lp = lforw(curbp->b_linep);             /* First line.          */
        nline = 0;                              /* Number of lines.     */
        while (lp != curbp->b_linep) {
                if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
                        break;
                ++nline;
                lp = lforw(lp);
        }

	/* turn keyboard translation back on.
	 * we do it here so we don't have to call
	 * this function with every error return.
	 */
	TTkopen();

        if (s == FIOSUC) {                      /* No write error.      */
                s = ffclose();
                if (s == FIOSUC) {              /* No close error.      */
                        if (nline == 1)
                                mlwrite("[Wrote 1 line]");
                        else
                                mlwrite("[Wrote %d lines]", nline);
		}
#if	BACKUP
#if	!BUPEVS
		if( curbp->b_bupflg ) {
			if( unlink( fn ) != 0 && errno != ENOENT ) {
				mlwrite( "SAVE FAILED" );
				unlink( save_file );
				return( FALSE );
			}
		} else
#endif
		if( makebackup( fn ) == FALSE )
			/* We couldn't remove an already existing,
			 * old backup file.
			 * Ask the user if he wishes to save the
			 * file anyway.
			 */
			if( ( s = mlyesno( "Cannot create backup file -- Continue" )) != TRUE ) {
				mlwrite( "SAVE FAILED" );
				unlink( save_file );
				return( s );
			}
			else if( unlink( fn ) != 0 && errno != ENOENT ) {
				/* cannot delete original file */
				mlwrite( "SAVE FAILED" );
				unlink( save_file );
				return( FALSE );
			}
		frename( save_file, fn );
#endif
        } else {                                /* Ignore close error   */
                ffclose();                      /* if a write error.    */
#if	BACKUP
		unlink( save_file );		/* remove temp file	*/
#endif
	}
        if (s != FIOSUC)                        /* Some sort of error.  */
                return (FALSE);
        return (TRUE);
}

/*
 * The command allows the user
 * to modify the file name associated with
 * the current buffer. It is like the "f" command
 * in UNIX "ed". The operation is simple; just zap
 * the name in the BUFFER structure, and mark the windows
 * as needing an update. You can type a blank line at the
 * prompt if you wish.
 * We set the backup flag here, too. When this buffer is
 * saved to disk the next time the 'new' file is backed up.
 */
filename(f, n)
{
        register WINDOW *wp;
        register int    s;
        char            fname[NFILEN];

	if (restflag)		/* don't allow this command if restricted */
		return(resterr());
        if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
                return (s);
#if	BACKUP && !BUPEVS
	if( strcmp( curbp->b_fname, fname ) != 0 )
		curbp->b_bupflg = FALSE;       /* next save makes backup file */
#endif
        if (s == FALSE)
                strcpy(curbp->b_fname, "");
        else
                strcpy(curbp->b_fname, fname);
        wp = wheadp;                            /* Update mode lines.   */
        while (wp != NULL) {
                if (wp->w_bufp == curbp)
                        wp->w_flag |= WFMODE;
                wp = wp->w_wndp;
        }
	curbp->b_mode &= ~MDVIEW;	/* no longer read only mode */
        return (TRUE);
}

/*
 * Insert file "fname" into the current
 * buffer, Called by insert file command. Return the final
 * status of the read.
 */
ifile(fname)
char    fname[];
{
        register LINE   *lp0;
        register LINE   *lp1;
        register LINE   *lp2;
        register int    i;
        register BUFFER *bp;
        register int    s;
        register int    nbytes;
        register int    nline;
	int		lflag;		/* any lines longer than allowed? */
	char mesg[NSTRING];

        bp = curbp;                             /* Cheap.               */
        bp->b_flag |= BFCHG;			/* we have changed	*/
	bp->b_flag &= ~BFINVS;			/* and are not temporary*/
        if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
                goto out;
        if (s == FIOFNF) {                      /* File not found.      */
                mlwrite("[No such file]");
		return(FALSE);
        }
        mlwrite("[Inserting file]");

#if	CRYPT
	s = resetkey();
	if (s != TRUE)
		return(s);
#endif
	/* back up a line and save the mark here */
	curwp->w_dotp = lback(curwp->w_dotp);
	curwp->w_doto = 0;
	curwp->w_markp = curwp->w_dotp;
	curwp->w_marko = 0;

        nline = 0;
	lflag = FALSE;
        while ((s=ffgetline()) == FIOSUC) {
                nbytes = strlen(fline);
                if ((lp1=lalloc(nbytes)) == NULL) {
                        s = FIOMEM;             /* Keep message on the  */
                        break;                  /* display.             */
                }
		lp0 = curwp->w_dotp;	/* line previous to insert */
		lp2 = lp0->l_fp;	/* line after insert */

		/* re-link new line between lp0 and lp2 */
		lp2->l_bp = lp1;
		lp0->l_fp = lp1;
		lp1->l_bp = lp0;
		lp1->l_fp = lp2;

		/* and advance and write out the current line */
		curwp->w_dotp = lp1;
                for (i=0; i<nbytes; ++i)
                        lputc(lp1, i, fline[i]);
                ++nline;
        }
        ffclose();                              /* Ignore errors.       */
	curwp->w_markp = lforw(curwp->w_markp);
	strcpy(mesg, "[");
	if (s==FIOERR) {
		strcat(mesg, "I/O ERROR, ");
		curbp->b_flag |= BFTRUNC;
	}
	if (s == FIOMEM) {
		strcat(mesg, "OUT OF MEMORY, ");
		curbp->b_flag |= BFTRUNC;
	}
	sprintf(&mesg[strlen(mesg)], "Inserted %d line", nline);
        if (nline > 1)
		strcat(mesg, "s");
	strcat(mesg, "]");
	mlwrite(mesg);

out:
	/* advance to the next line and mark the window for changes */
	curwp->w_dotp = lforw(curwp->w_dotp);
	curwp->w_flag |= WFHARD | WFMODE;

	/* copy window parameters back to the buffer structure */
	curbp->b_dotp = curwp->w_dotp;
	curbp->b_doto = curwp->w_doto;
	curbp->b_markp = curwp->w_markp;
	curbp->b_marko = curwp->w_marko;

        if (s == FIOERR)                        /* False if error.      */
                return (FALSE);
        return (TRUE);
}

/* Make a backup file.
 * This is done by renaming the original file to a new name.
 * The name of the backup file is defined as follows:
 *
 * MSDOS: The base file name remains untouched, the extension has a tilde (~)
 *	  as its first character and the other characters moved one place
 *	  to the right.
 * UNIX:  The first character is a '#'. The rest of the name is moved one
 *	  place to the right. The length of the filename may not exceed 14
 *	  characters (for BSD the limit is 255).
 *
 * No other operating system supported by now.
 */
#if	BACKUP

makebackup( filename )
char	*filename;
{
	char	backupname[NFILEN];

	getbackupname( backupname, filename );
#if	MSDOS | V7 | USG | BSD
	if( !fexist( filename, 0 ))		/* no original file - */
		return( TRUE );			/* nothing to backup  */
	if( fexist( backupname, 0 ))
		if( unlink( backupname ))	/* file is readonly */
			return( FALSE );
	if( frename( filename, backupname ) != 0 )
		return( FALSE );
	return( TRUE );
#else			/* for all other operating systems return fail */
	return( FALSE );
#endif	/* MSDOS | V7 | USG | BSD */
}

/* Make the name of the backup file according to the original file name
 * This routines knows about the filename possibilities of the different
 * operating systems.
 */
getbackupname( backup, file )
char	*backup;
char	*file;
{
	char	*temp[NFILEN];
	char	*dotpos;

	strcpy( backup, file );
#if	MSDOS
	if( (dotpos = strrchr( backup, '.' )) == NULL )
		strcat( backup, ".~" );
	else {
		strcpy( temp, dotpos +1 );
		dotpos[1] = '~';	/* first char of new extension	    */
		temp[2] = '\0';		/* extension is at most 3 chars long*/
		strcat( backup, temp );
	}
#endif	/* MSDOS */

#if	V7 | USG | BSD
	if( (dotpos = strrchr( backup, '/' )) != NULL )
	    	strcpy( temp, dotpos +1 );
	else
		strcpy( temp, backup );
#if	BSD	/* BSD allows filenames	up to 255 characters	*/
	if( strlen( temp ) > 255 )
		temp[255] = '\0';
#else		/* other Unixes allow only 14 characters	*/
	if( strlen( temp ) > 13 )
		temp[ 13] = '\0';
#endif
	strcpy( backup, "#" );
	strcat( backup, temp );
#endif	/* V7 | USG | BSD */
}

/* Return the name of the savefile in save_file.
 * This routines knows about the filename possibilities of the different
 * operating systems.
 */
getsavefilename( newname,  oldname )
char	*newname;
char	*oldname;
{
	char	*dotpos;

	strcpy( newname, oldname );
#if	MSDOS
	if( (dotpos = strrchr( newname, '/' )) != NULL ||
	    (dotpos = strrchr( newname, '\\' )) != NULL )
		strcpy( dotpos +1,  bup_file );
	else if( (dotpos = strrchr( newname, ':' )) != NULL )
		strcpy( dotpos +1,  bup_file );
	else
		strcpy( newname,  bup_file );
#endif	/* MSDOS */

#if	V7 | USG | BSD
	if( (dotpos = strrchr( newname, '/' )) != NULL )
		strcpy( dotpos +1,  bup_file );
	else
		strcpy( newname,  bup_file );
#endif	/* V7 | USG | BSD */
}

#endif	/* BACKUP */

SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0