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