827 lines
20 KiB
C
827 lines
20 KiB
C
|
/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
|
|||
|
* This software is quasi-public; it may be used freely with
|
|||
|
* like software, but may NOT be sold or made part of licensed
|
|||
|
* products without permission of the author.
|
|||
|
*/
|
|||
|
/*
|
|||
|
* EEFILE File reading/writing functions
|
|||
|
*/
|
|||
|
|
|||
|
#include "elle.h"
|
|||
|
#include <stdio.h> /* Use "standard" I/O package for writing */
|
|||
|
#ifndef BUFSIZ
|
|||
|
#define BUFSIZ BUFSIZE /* Some places use wrong name in stdio.h */
|
|||
|
#endif /*-BUFSIZ*/
|
|||
|
#if V6
|
|||
|
struct stat {
|
|||
|
int st_dev;
|
|||
|
int st_ino;
|
|||
|
char *st_mode;
|
|||
|
char st_nlink;
|
|||
|
char st_uid;
|
|||
|
char st_gid;
|
|||
|
char st_size0;
|
|||
|
char st_size;
|
|||
|
int st_addr[8];
|
|||
|
long st_atime;
|
|||
|
long st_mtime;
|
|||
|
};
|
|||
|
#define ENOENT (2) /* Syscall error - no such file or dir */
|
|||
|
#else
|
|||
|
#include <errno.h>
|
|||
|
#include <sys/types.h>
|
|||
|
#include <sys/stat.h>
|
|||
|
#endif /*-V6*/
|
|||
|
|
|||
|
#if TOPS20
|
|||
|
#include <sys/file.h> /* Get open mode bits */
|
|||
|
#endif
|
|||
|
|
|||
|
extern char *strerror(); /* Return error string for errno */
|
|||
|
extern struct buffer *make_buf(), *find_buf();
|
|||
|
|
|||
|
char *fncons(), *last_fname();
|
|||
|
|
|||
|
int hoardfd = -1; /* Retain a FD here to ensure we can always write */
|
|||
|
|
|||
|
/* Flags for iwritfile() */
|
|||
|
#define WF_SMASK 07 /* Source Mask */
|
|||
|
#define WF_SBUFF 0 /* source: Buffer */
|
|||
|
#define WF_SREG 1 /* source: Region */
|
|||
|
#define WF_SKILL 2 /* source: Last Kill */
|
|||
|
#define WF_ASK 010 /* Ask for filename to write to */
|
|||
|
static int iwritfile();
|
|||
|
|
|||
|
/* EFUN: "Find File" */
|
|||
|
/* Ask user for a filename and do a find_file for it.
|
|||
|
* If buffer exists for that filename, select that buffer.
|
|||
|
* Else create a buffer for it, and read in the file if it exists.
|
|||
|
*/
|
|||
|
f_ffile()
|
|||
|
{ int find_file();
|
|||
|
#if IMAGEN
|
|||
|
hack_file("Visit file: ", find_file);
|
|||
|
#else
|
|||
|
hack_file("Find file: ", find_file);
|
|||
|
#endif /*-IMAGEN*/
|
|||
|
}
|
|||
|
|
|||
|
/* EFUN: "Read File" */
|
|||
|
/* User read_file function, asks user for a filename and reads it
|
|||
|
*/
|
|||
|
f_rfile() { u_r_file("Read file: "); }
|
|||
|
|
|||
|
/* EFUN: "Visit File" */
|
|||
|
/* Same as Read File, with different prompt.
|
|||
|
*/
|
|||
|
f_vfile() { u_r_file("Visit file: "); }
|
|||
|
|
|||
|
|
|||
|
u_r_file(prompt)
|
|||
|
char *prompt;
|
|||
|
{ register char *f_name;
|
|||
|
register struct buffer *b;
|
|||
|
|
|||
|
if((f_name = ask (prompt))==0) /* prompt user for filename */
|
|||
|
return; /* user punted... */
|
|||
|
b = cur_buf;
|
|||
|
if(*f_name == '\0')
|
|||
|
{ if (b -> b_fn == 0)
|
|||
|
ding("No default file name.");
|
|||
|
else read_file(b -> b_fn);
|
|||
|
}
|
|||
|
else read_file(f_name);
|
|||
|
chkfree(f_name);
|
|||
|
}
|
|||
|
|
|||
|
/* EFUN: "Insert File" */
|
|||
|
/* Asks for a filename and inserts the file at current location.
|
|||
|
* Point is left at beginning, and the mark at the end.
|
|||
|
*/
|
|||
|
f_ifile()
|
|||
|
{ int ins_file();
|
|||
|
hack_file("Insert file: ", ins_file);
|
|||
|
}
|
|||
|
|
|||
|
/* EFUN: "Save File" */
|
|||
|
/* Save current buffer to its default file name
|
|||
|
*/
|
|||
|
f_sfile()
|
|||
|
{ if(cur_buf->b_flags&B_MODIFIED)
|
|||
|
return(iwritfile(WF_SBUFF)); /* Write buffer, don't ask */
|
|||
|
else
|
|||
|
{ saynow("(No changes need to be written)");
|
|||
|
return(1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if FX_SAVEFILES || FX_WFEXIT
|
|||
|
/* EFUN: "Save All Files" */
|
|||
|
/* F_SAVEFILES - Offer to save all modified files.
|
|||
|
* With argument, doesn't ask.
|
|||
|
* Returns 0 if user aborts or if an error happened.
|
|||
|
*/
|
|||
|
f_savefiles()
|
|||
|
{ register struct buffer *b, *savb;
|
|||
|
register int res = 1;
|
|||
|
char *ans;
|
|||
|
|
|||
|
savb = cur_buf;
|
|||
|
for (b = buf_head; res && b; b = b->b_next)
|
|||
|
if ((b->b_flags & B_MODIFIED) && b->b_fn)
|
|||
|
{ if(exp_p) /* If arg, */
|
|||
|
{ chg_buf(b); /* just save, don't ask */
|
|||
|
res = f_sfile();
|
|||
|
continue; /* Check next buffer */
|
|||
|
}
|
|||
|
/* Ask user whether to save */
|
|||
|
ans = ask("Buffer %s contains changes - write out? ",
|
|||
|
b->b_name);
|
|||
|
if(ans == 0)
|
|||
|
{ res = 0; /* User aborted */
|
|||
|
break;
|
|||
|
}
|
|||
|
if (upcase(*ans) == 'Y')
|
|||
|
{ chg_buf(b);
|
|||
|
res = f_sfile(); /* Save File */
|
|||
|
}
|
|||
|
chkfree(ans);
|
|||
|
}
|
|||
|
chg_buf(savb);
|
|||
|
return(res);
|
|||
|
}
|
|||
|
#endif /*FX_SAVEFILES||FX_WFEXIT*/
|
|||
|
|
|||
|
/* EFUN: "Write File" */
|
|||
|
/* Write out the buffer to an output file.
|
|||
|
*/
|
|||
|
f_wfile()
|
|||
|
{ return iwritfile(WF_ASK|WF_SBUFF);
|
|||
|
}
|
|||
|
|
|||
|
/* EFUN: "Write Region" */
|
|||
|
/* Write region out to a file
|
|||
|
*/
|
|||
|
f_wreg()
|
|||
|
{ return iwritfile(WF_ASK|WF_SREG); /* Ask, write region */
|
|||
|
}
|
|||
|
|
|||
|
#if FX_WLASTKILL
|
|||
|
/* EFUN: "Write Last Kill" (not EMACS) */
|
|||
|
/* Write current kill buffer out to a file.
|
|||
|
** This is mainly for MINIX.
|
|||
|
*/
|
|||
|
extern int kill_ptr; /* From EEF3 */
|
|||
|
extern SBSTR *kill_ring[];
|
|||
|
|
|||
|
f_wlastkill()
|
|||
|
{ return iwritfile(WF_ASK|WF_SKILL);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
/* HACK_FILE - intermediate subroutine
|
|||
|
*/
|
|||
|
hack_file(prompt, rtn)
|
|||
|
char *prompt;
|
|||
|
int (*rtn)();
|
|||
|
{ register char *f_name;
|
|||
|
|
|||
|
if((f_name = ask(prompt)) == 0)
|
|||
|
return;
|
|||
|
if (*f_name != '\0') /* Check for null answer */
|
|||
|
(*rtn)(f_name);
|
|||
|
chkfree(f_name);
|
|||
|
}
|
|||
|
|
|||
|
/* FIND_FILE(f_name)
|
|||
|
* If there is a buffer whose fn == f_name, select that buffer.
|
|||
|
* Else create one with name of the last section of f_name and
|
|||
|
* read the file into that buffer.
|
|||
|
*/
|
|||
|
find_file(f_name)
|
|||
|
register char *f_name;
|
|||
|
{ register struct buffer *b;
|
|||
|
register char *ans;
|
|||
|
char *lastn;
|
|||
|
int fd;
|
|||
|
|
|||
|
#if IMAGEN
|
|||
|
char real_name[128]; /* File name w/ expanded ~ and $ */
|
|||
|
expand_file(real_name, f_name);
|
|||
|
f_name = real_name;
|
|||
|
#endif /*IMAGEN*/
|
|||
|
|
|||
|
for (b = buf_head; b; b = b -> b_next)
|
|||
|
if(b->b_fn && (strcmp (b -> b_fn, f_name) == 0))
|
|||
|
break;
|
|||
|
if (b) /* if we found one */
|
|||
|
{ sel_buf(b); /* go there */
|
|||
|
return; /* and we are done */
|
|||
|
}
|
|||
|
if((fd = open(f_name,0)) < 0) /* See if file exists */
|
|||
|
{ if(errno != ENOENT) /* No, check reason */
|
|||
|
{ ferr_ropn(); /* Strange error, complain */
|
|||
|
return; /* and do nothing else. */
|
|||
|
}
|
|||
|
}
|
|||
|
else close(fd); /* Found! Close FD, since the */
|
|||
|
/* read_file rtn will re-open. */
|
|||
|
|
|||
|
lastn = last_fname(f_name); /* Find buffer name */
|
|||
|
b = find_buf(lastn); /* Is there a buffer of that name? */
|
|||
|
if (b && (ex_blen(b) || b->b_fn))
|
|||
|
{ ans = ask("Buffer %s contains %s, which buffer shall I use? ",
|
|||
|
b -> b_name, b->b_fn ? b->b_fn : "something");
|
|||
|
if(ans == 0) return; /* Aborted */
|
|||
|
if (*ans != '\0') /* if null answer, use b */
|
|||
|
b = make_buf(ans); /* else use ans */
|
|||
|
chkfree(ans);
|
|||
|
}
|
|||
|
else
|
|||
|
b = make_buf(lastn);
|
|||
|
sel_buf(b);
|
|||
|
if(fd < 0) /* If file doesn't exist, */
|
|||
|
{ set_fn(f_name); /* just say "new" and set filename */
|
|||
|
return; /* and return right away. */
|
|||
|
}
|
|||
|
if (read_file(f_name)==0) /* File exists, read it in! */
|
|||
|
{ if(b->b_fn) /* Failed... if filename, */
|
|||
|
{ chkfree(b->b_fn); /* flush the filename. */
|
|||
|
b->b_fn = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* READ_FILE(f_name)
|
|||
|
* Reads file into current buffer, flushing any
|
|||
|
* previous contents (if buffer modified, will ask about saving)
|
|||
|
* Returns 0 if failed.
|
|||
|
*/
|
|||
|
read_file(f_name)
|
|||
|
char *f_name;
|
|||
|
{
|
|||
|
#if IMAGEN
|
|||
|
struct stat s;
|
|||
|
char real_name[128]; /* File name w/ expanded ~ and $ */
|
|||
|
#endif /*IMAGEN*/
|
|||
|
|
|||
|
if(!zap_buffer()) /* Flush the whole buffer */
|
|||
|
return; /* Unless user aborts */
|
|||
|
#if IMAGEN
|
|||
|
expand_file(real_name, f_name);
|
|||
|
f_name = real_name; /* Hack, hack! */
|
|||
|
#endif /*IMAGEN*/
|
|||
|
set_fn(f_name);
|
|||
|
if (ins_file(f_name)==0)
|
|||
|
return 0;
|
|||
|
f_bufnotmod(); /* Say not modified now */
|
|||
|
#if IMAGEN
|
|||
|
stat(f_name, &s); /* Get file stat */
|
|||
|
cur_buf->b_mtime = s.st_mtime; /* and pick out last-modified time */
|
|||
|
#endif /*IMAGEN*/
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/* INS_FILE(f_name)
|
|||
|
* Inserts file named f_name into current buffer at current point
|
|||
|
* Point is not moved; mark is set to end of inserted stuff.
|
|||
|
* Returns 0 if failed, 1 if won.
|
|||
|
*/
|
|||
|
ins_file (f_name)
|
|||
|
char *f_name;
|
|||
|
{ register int ifd;
|
|||
|
register SBSTR *sd;
|
|||
|
chroff insdot; /* To check for range of mods */
|
|||
|
|
|||
|
#if IMAGEN
|
|||
|
char real_name[128]; /* File name w/ expanded ~ and $ */
|
|||
|
expand_file(real_name, f_name);
|
|||
|
f_name = real_name;
|
|||
|
#endif /*IMAGEN*/
|
|||
|
#if !(TOPS20)
|
|||
|
if((ifd = open(f_name,0)) < 0)
|
|||
|
#else
|
|||
|
if((ifd = open(f_name,O_RDONLY|O_UNCONVERTED)) < 0)
|
|||
|
#endif /*TOPS20*/
|
|||
|
{ ferr_ropn(); /* Can't open, complain */
|
|||
|
return 0; /* no redisplay */
|
|||
|
}
|
|||
|
errno = 0;
|
|||
|
if((sd = sb_fduse(ifd)) == 0)
|
|||
|
{ if (ifd >= SB_NFILES)
|
|||
|
dingtoo(" Cannot read - too many internal files");
|
|||
|
else if (errno)
|
|||
|
ferr_ropn();
|
|||
|
else errbarf("SB rtn cannot read file?");
|
|||
|
close(ifd);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
sb_sins(cur_buf,sd);
|
|||
|
insdot = e_dot();
|
|||
|
f_setmark(); /* Set mark at current ptr */
|
|||
|
if(cur_dot != insdot) /* If pointer was advanced, */
|
|||
|
buf_tmat(insdot); /* then stuff was inserted */
|
|||
|
e_gocur();
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
ferr_ropn() { ferr(" Cannot read"); }
|
|||
|
ferr_wopn() { ferr(" Cannot write"); }
|
|||
|
ferr(str)
|
|||
|
char *str;
|
|||
|
{ saytoo(str);
|
|||
|
saytoo(" - ");
|
|||
|
dingtoo(strerror(errno));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* IWRITFILE - auxiliary for writing files.
|
|||
|
** Returns 1 if write successful, 0 if not.
|
|||
|
*/
|
|||
|
static int
|
|||
|
iwritfile(flags)
|
|||
|
int flags;
|
|||
|
{ register struct buffer *b;
|
|||
|
register char *o_name; /* output file name */
|
|||
|
int styp = flags & WF_SMASK; /* Source type, one of WF_Sxxx */
|
|||
|
char *prompt;
|
|||
|
#ifdef STDWRITE
|
|||
|
register FILE *o_file; /* output file pointer */
|
|||
|
char obuf[BUFSIZ];
|
|||
|
chroff dotcnt;
|
|||
|
#endif /*STDWRITE*/
|
|||
|
int ofd; /* output file FD */
|
|||
|
SBSTR *sd;
|
|||
|
char fname[FNAMSIZ]; /* To avoid chkfree hassle */
|
|||
|
char newname[FNAMSIZ]; /* for robustness */
|
|||
|
char oldname[FNAMSIZ]; /* ditto */
|
|||
|
int res;
|
|||
|
struct stat statb;
|
|||
|
int statres;
|
|||
|
#if IMAGEN
|
|||
|
struct stat s;
|
|||
|
char real_name[128]; /* File name w/ expanded ~ and $ */
|
|||
|
#endif /*IMAGEN*/
|
|||
|
res = 1; /* Let's keep track of success */
|
|||
|
|
|||
|
/* Check for existence of source, and set prompt string */
|
|||
|
switch(styp)
|
|||
|
{
|
|||
|
case WF_SBUFF:
|
|||
|
prompt = "Write File: ";
|
|||
|
break;
|
|||
|
case WF_SREG:
|
|||
|
if(!mark_p)
|
|||
|
{ dingtoo(" No Mark!");
|
|||
|
return(0);
|
|||
|
}
|
|||
|
prompt = "Write Region: ";
|
|||
|
break;
|
|||
|
#if FX_WLASTKILL
|
|||
|
case WF_SKILL:
|
|||
|
if(!kill_ring[kill_ptr])
|
|||
|
{ dingtoo("No killed stuff");
|
|||
|
return(0);
|
|||
|
}
|
|||
|
prompt = "Write Last Kill: ";
|
|||
|
break;
|
|||
|
#endif
|
|||
|
default: /* Internal error */
|
|||
|
errbarf("bad iwritfile arg");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (flags&WF_ASK)
|
|||
|
{ if((o_name = ask(prompt))==0)
|
|||
|
return(0); /* User punted. */
|
|||
|
strcpy(&fname[0], o_name); /* Copy filename onto stack */
|
|||
|
chkfree(o_name);
|
|||
|
}
|
|||
|
o_name = &fname[0];
|
|||
|
b = cur_buf;
|
|||
|
if (!(flags&WF_ASK) || (*o_name == '\0'))
|
|||
|
{ if (b->b_fn == 0)
|
|||
|
{ ding("No default file name.");
|
|||
|
return(0);
|
|||
|
}
|
|||
|
strcpy(o_name, b->b_fn);
|
|||
|
}
|
|||
|
|
|||
|
#if IMAGEN
|
|||
|
expand_file(real_name, o_name);
|
|||
|
o_name = real_name; /* Hack, hack */
|
|||
|
#endif /*IMAGEN*/
|
|||
|
|
|||
|
statres = stat(o_name,&statb); /* Get old file's info (if any) */
|
|||
|
|
|||
|
#if IMAGEN
|
|||
|
/* Now, make sure someone hasn't written the file behind our backs */
|
|||
|
if ((styp==WF_SBUFF) && !(flags&WF_ASK)
|
|||
|
&& b->b_fn && stat(b->b_fn, &s) >= 0)
|
|||
|
if (s.st_mtime != b->b_mtime)
|
|||
|
{ char *ans;
|
|||
|
ans = ask("Since you last read \"%s\", someone has changed it.\nDo you want to write it anyway (NOT RECOMMENDED!)? ",
|
|||
|
b->b_fn);
|
|||
|
if (ans == 0 || upcase(*ans) != 'Y')
|
|||
|
{
|
|||
|
ding("I suggest you either read it again, or\nwrite it to a temporary file, and merge the two versions manually.");
|
|||
|
if (ans) chkfree(ans);
|
|||
|
return(0);
|
|||
|
}
|
|||
|
if (ans) chkfree(ans);
|
|||
|
}
|
|||
|
#endif /*IMAGEN*/
|
|||
|
|
|||
|
/* Try to get around major UNIX screw of smashing files.
|
|||
|
* This still isn't perfect (screws up with long filenames) but...
|
|||
|
* 1. Write out to <newname>
|
|||
|
* 2. Rename <name> to <oldname> (may have to delete existing <oldname>)
|
|||
|
* 3. Rename <newname> to <name>.
|
|||
|
*/
|
|||
|
fncons(oldname,ev_fno1,o_name,ev_fno2); /* Set up "old" filename */
|
|||
|
fncons(newname,ev_fnn1,o_name,ev_fnn2); /* Set up "new" filename */
|
|||
|
unlink(newname); /* Ensure we don't clobber */
|
|||
|
unhoard(); /* Now give up saved FD */
|
|||
|
#if !(V6) /* Standard V6 doesn't have access call */
|
|||
|
if(statres >= 0) /* If file exists, */
|
|||
|
{ if(access(o_name, 2) != 0) /* check for write access */
|
|||
|
{ ferr_wopn();
|
|||
|
res = 0; /* Failure */
|
|||
|
goto wdone;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif /*-V6*/
|
|||
|
#ifdef STDWRITE
|
|||
|
if(flags&WF_ASK)
|
|||
|
{ if((o_file = fopen(newname, "w")) ==0) /* Create new output file */
|
|||
|
{ ferr_wopn();
|
|||
|
res = 0; /* Failure */
|
|||
|
goto wdone;
|
|||
|
}
|
|||
|
setbuf(o_file,obuf); /* Ensure always have buffer */
|
|||
|
}
|
|||
|
else /* New stuff */
|
|||
|
#endif /*STDWRITE*/
|
|||
|
{
|
|||
|
#if !(TOPS20)
|
|||
|
if((ofd = creat(newname,ev_filmod)) < 0)
|
|||
|
#else
|
|||
|
if((ofd = open(newname,O_WRONLY|O_UNCONVERTED)) < 0)
|
|||
|
#endif /*TOPS20*/
|
|||
|
{ ferr_wopn();
|
|||
|
res = 0; /* Failure */
|
|||
|
goto wdone;
|
|||
|
}
|
|||
|
}
|
|||
|
if (styp==WF_SBUFF)
|
|||
|
set_fn(o_name); /* Won, so set default fn for buff */
|
|||
|
#if IMAGEN
|
|||
|
saynow("Writing ");
|
|||
|
switch(styp)
|
|||
|
{ case WF_SBUFF: saytoo(b->b_fn); break;
|
|||
|
case WF_SREG: saytoo("region"); break;
|
|||
|
#if FX_WLASTKILL
|
|||
|
case WF_SKILL: saytoo("last kill"); break;
|
|||
|
#endif
|
|||
|
}
|
|||
|
sayntoo("...");
|
|||
|
#else
|
|||
|
saynow("Writing...");
|
|||
|
#endif /*-IMAGEN*/
|
|||
|
|
|||
|
#if !(TOPS20) /* T20 does all this already */
|
|||
|
if(statres >= 0) /* Get old file's modes */
|
|||
|
{ /* Try to duplicate them */
|
|||
|
/* Do chmod first since after changing owner we may not
|
|||
|
** have permission to change mode, at least on V6.
|
|||
|
*/
|
|||
|
chmod(newname,statb.st_mode & 07777);
|
|||
|
#if V6
|
|||
|
chown(newname, (statb.st_gid<<8)|(statb.st_uid&0377));
|
|||
|
#else
|
|||
|
chown(newname,statb.st_uid,statb.st_gid);
|
|||
|
#endif /*-V6*/
|
|||
|
}
|
|||
|
#if V6
|
|||
|
/* If no old file existed, and we are a V6 system, try to set
|
|||
|
* the modes explicitly. On V7 we're OK because the user can
|
|||
|
* diddle "umask" to get whatever is desired.
|
|||
|
* On TOPS-20 of course everything is all peachy.
|
|||
|
*/
|
|||
|
else chmod(newname, ev_filmod);
|
|||
|
#endif /*V6*/
|
|||
|
#endif /*TOPS20*/
|
|||
|
|
|||
|
|
|||
|
#ifdef STDWRITE
|
|||
|
if(flags&WF_ASK)
|
|||
|
{ switch(styp)
|
|||
|
{
|
|||
|
case WF_SBUFF:
|
|||
|
dotcnt = e_blen();
|
|||
|
e_gobob();
|
|||
|
break;
|
|||
|
case WF_SREG:
|
|||
|
if((dotcnt = mark_dot - cur_dot) < 0)
|
|||
|
{ e_goff(dotcnt);
|
|||
|
dotcnt = -dotcnt;
|
|||
|
}
|
|||
|
else e_gocur();
|
|||
|
break;
|
|||
|
/* WF_SKILL not implemented here */
|
|||
|
}
|
|||
|
while(--dotcnt >= 0)
|
|||
|
putc(sb_getc(((SBBUF *)b)), o_file);
|
|||
|
e_gocur();
|
|||
|
fflush(o_file); /* Force everything out */
|
|||
|
res = ferror(o_file); /* Save result of stuff */
|
|||
|
fclose(o_file); /* Now flush FD */
|
|||
|
}
|
|||
|
else /* New stuff */
|
|||
|
#endif /*STDWRITE*/
|
|||
|
{
|
|||
|
switch(styp)
|
|||
|
{
|
|||
|
case WF_SBUFF:
|
|||
|
res = sb_fsave((SBBUF *)b, ofd);
|
|||
|
break;
|
|||
|
case WF_SREG:
|
|||
|
e_gocur();
|
|||
|
sd = e_copyn((chroff)(mark_dot - cur_dot));
|
|||
|
res = sbx_aout(sd, 2, ofd);
|
|||
|
sbs_del(sd);
|
|||
|
break;
|
|||
|
#if FX_WLASTKILL
|
|||
|
case WF_SKILL:
|
|||
|
res = sbx_aout(kill_ring[kill_ptr], 2, ofd);
|
|||
|
break;
|
|||
|
#endif
|
|||
|
}
|
|||
|
close(ofd);
|
|||
|
}
|
|||
|
if(errno = res)
|
|||
|
{ ferr(" Output error");
|
|||
|
res = 0; /* Failure */
|
|||
|
goto wdone;
|
|||
|
}
|
|||
|
else
|
|||
|
res = 1; /* Success so far */
|
|||
|
if(styp == WF_SBUFF)
|
|||
|
f_bufnotmod(); /* Reset "buffer modified" flag */
|
|||
|
|
|||
|
/* Here we effect the screw-prevention steps explained earlier. */
|
|||
|
/* TOPS-20, with generation numbers, need not worry about this. */
|
|||
|
#if TOPS20
|
|||
|
saynow("Written");
|
|||
|
|
|||
|
#else /*-TOPS20*/
|
|||
|
#if IMAGEN /* KLH -- This conditional bracketting is prone to lossage */
|
|||
|
/* Only create the .BAK file once per editing session!! */
|
|||
|
if ((styp==WF_SBUFF) || !(b->b_flags & B_BACKEDUP))
|
|||
|
{ if (styp==WF_SBUFF)
|
|||
|
b->b_flags |= B_BACKEDUP;
|
|||
|
#endif /*IMAGEN*/
|
|||
|
unlink(oldname); /* remove any existing "old" file */
|
|||
|
if(link(o_name,oldname) == 0) /* Rename current to "old" */
|
|||
|
unlink(o_name);
|
|||
|
/* Here is the critical point... if we stop here, there is no
|
|||
|
* longer any file with the appropriate filename!!!
|
|||
|
*/
|
|||
|
#if IMAGEN
|
|||
|
}
|
|||
|
else
|
|||
|
unlink(o_name);
|
|||
|
#endif /*IMAGEN*/
|
|||
|
if(link(newname,o_name) == 0) /* Rename "new" to current */
|
|||
|
{ unlink(newname);
|
|||
|
#if IMAGEN
|
|||
|
sayntoo("OK");
|
|||
|
#else
|
|||
|
saynow("Written");
|
|||
|
#endif /*-IMAGEN*/
|
|||
|
}
|
|||
|
else
|
|||
|
{ dingtoo("rename error!");
|
|||
|
res = 0;
|
|||
|
}
|
|||
|
#endif /*-TOPS20*/
|
|||
|
|
|||
|
#if IMAGEN
|
|||
|
/* Update the last-modified time for the file in this buffer */
|
|||
|
if ((styp == WF_SBUFF) && b->b_fn)
|
|||
|
{ stat(b->b_fn, &s);
|
|||
|
b->b_mtime = s.st_mtime;
|
|||
|
}
|
|||
|
#endif /*IMAGEN*/
|
|||
|
|
|||
|
wdone:
|
|||
|
hoard(); /* Get back a retained FD */
|
|||
|
return(res);
|
|||
|
}
|
|||
|
|
|||
|
/* FNCONS(dest,pre,f_name,post)
|
|||
|
* Specialized routine to cons up a filename string into "dest",
|
|||
|
* given prefix and postfix strings to be added onto last component of
|
|||
|
* filename.
|
|||
|
*/
|
|||
|
char *
|
|||
|
fncons(dest, pre, f_name, post)
|
|||
|
char *dest,*pre,*f_name,*post;
|
|||
|
{ register char *cp, *cp2;
|
|||
|
char *last_fname();
|
|||
|
|
|||
|
cp = dest;
|
|||
|
*cp = 0; /* Make dest string null initially */
|
|||
|
cp2 = last_fname(f_name); /* Get pointer to beg of last name */
|
|||
|
strncat(cp,f_name,cp2-f_name); /* Copy first part of filename */
|
|||
|
if(pre) strcat(cp, pre); /* If prefix exists, add it on */
|
|||
|
cp = last_fname(cp); /* Recheck in case levels added */
|
|||
|
strcat(cp, cp2); /* Now add last name */
|
|||
|
if(cp2 = post) /* If there's a postfix, must check */
|
|||
|
{ cp[FNAMELEN-strlen(cp2)] = 0; /* and cut dest so postfix */
|
|||
|
strcat(cp, cp2); /* will fit on end. */
|
|||
|
}
|
|||
|
return(dest);
|
|||
|
}
|
|||
|
|
|||
|
/* LAST_FNAME(string)
|
|||
|
* Get the last component of a file name. Returns pointer to
|
|||
|
* start of component; does NOT copy string!
|
|||
|
*/
|
|||
|
char *
|
|||
|
last_fname(f_name)
|
|||
|
char *f_name;
|
|||
|
{ register char *cp, *p;
|
|||
|
register int c;
|
|||
|
|
|||
|
p = f_name; /* pointer to last slash */
|
|||
|
cp = p;
|
|||
|
while(c = *cp++)
|
|||
|
if(c == '/')
|
|||
|
p = cp; /* point to after the slash */
|
|||
|
return(p);
|
|||
|
}
|
|||
|
|
|||
|
/* SET_FN(string)
|
|||
|
* Set the default filename for current buffer to "string".
|
|||
|
*/
|
|||
|
set_fn (string)
|
|||
|
char *string;
|
|||
|
{ register struct buffer *b;
|
|||
|
register char *str;
|
|||
|
#if IMAGEN
|
|||
|
register char *cp;
|
|||
|
register int len;
|
|||
|
#endif /*IMAGEN*/
|
|||
|
char *strdup();
|
|||
|
|
|||
|
b = cur_buf;
|
|||
|
str = strdup(string); /* Copy now in case copying self */
|
|||
|
if(b->b_fn)
|
|||
|
chkfree(b->b_fn);
|
|||
|
b -> b_fn = str;
|
|||
|
#if IMAGEN
|
|||
|
/* Do mode determination based on file name (HACK HACK) */
|
|||
|
len = strlen(str);
|
|||
|
b->b_flags &= ~(B_CMODE|B_TEXTMODE);
|
|||
|
if (len > 4)
|
|||
|
{ if (strcmp(&str[len - 5], "draft") == 0)
|
|||
|
b->b_flags |= B_TEXTMODE;
|
|||
|
else
|
|||
|
{ cp = &str[len - 4];
|
|||
|
if (strcmp(cp, ".txt") == 0 ||
|
|||
|
strcmp(cp, ".mss") == 0)
|
|||
|
b->b_flags |= B_TEXTMODE;
|
|||
|
}
|
|||
|
}
|
|||
|
if (len > 2)
|
|||
|
{ cp = &str[len - 2];
|
|||
|
if (strcmp(cp, ".h") == 0 || strcmp(cp, ".c") == 0)
|
|||
|
b->b_flags |= B_CMODE;
|
|||
|
}
|
|||
|
#endif /*IMAGEN*/
|
|||
|
redp(RD_MODE);
|
|||
|
}
|
|||
|
|
|||
|
/* SAVEWORLD - Attempt to save all changes user has made.
|
|||
|
* Currently this amounts to writing out all modified buffers
|
|||
|
* to the files $HOME/+buffername. If a buffer is given as argument,
|
|||
|
* only that buffer is saved.
|
|||
|
* This is only called from the error handling routines with
|
|||
|
* the TTY either gone or in normal (non-edit) mode. The "grunt"
|
|||
|
* flag says whether to output feedback during the saving process.
|
|||
|
*/
|
|||
|
saveworld(bp, grunt)
|
|||
|
struct buffer *bp;
|
|||
|
int grunt;
|
|||
|
{ register struct buffer *b;
|
|||
|
register int wfd;
|
|||
|
char sfname[FNAMSIZ];
|
|||
|
struct buffer *sel_mbuf();
|
|||
|
|
|||
|
unhoard(); /* Ensure a FD is free for writing */
|
|||
|
if(b = bp) goto once;
|
|||
|
while(!bp && (b = sel_mbuf(b)))
|
|||
|
{
|
|||
|
once: strcat(strcat(strcpy(sfname,homedir),"/+"),b->b_name);
|
|||
|
if(grunt) printf("Saving %s...",sfname);
|
|||
|
#if !(TOPS20)
|
|||
|
if((wfd = creat(sfname, ev_filmod)) < 0)
|
|||
|
#else
|
|||
|
if((wfd = open(sfname,O_WRONLY|O_UNCONVERTED)) < 0)
|
|||
|
#endif /*TOPS20*/
|
|||
|
{ if(grunt)
|
|||
|
printf(" error - %s\n", strerror(errno));
|
|||
|
}
|
|||
|
else
|
|||
|
{ sb_fsave((SBBUF *)b, wfd);
|
|||
|
close(wfd);
|
|||
|
if(grunt) printf("\n");
|
|||
|
}
|
|||
|
b->b_flags &= ~B_MODIFIED;
|
|||
|
}
|
|||
|
hoard();
|
|||
|
}
|
|||
|
|
|||
|
/* HOARD, UNHOARD - Routines to save a FD for writing, to make sure
|
|||
|
* that we can always write out a buffer no matter how many
|
|||
|
* file descriptors we are currently using.
|
|||
|
*/
|
|||
|
hoard() /* Stash away a FD */
|
|||
|
{ if(hoardfd <= 0)
|
|||
|
#if !(TOPS20)
|
|||
|
hoardfd = open("nul:", 1);
|
|||
|
#else
|
|||
|
hoardfd = open("/dev/null", 1);
|
|||
|
#endif
|
|||
|
}
|
|||
|
unhoard() /* Give up our stashed FD so it can be re-used */
|
|||
|
{ close(hoardfd);
|
|||
|
hoardfd = -1;
|
|||
|
}
|
|||
|
|
|||
|
#if IMAGEN
|
|||
|
#include <pwd.h>
|
|||
|
#include <ctype.h>
|
|||
|
|
|||
|
/*
|
|||
|
* expand_file: expand any ~user-name/ or $env-var/ prefixes in sfn,
|
|||
|
* producing the full name in dfn
|
|||
|
*/
|
|||
|
expand_file(dfn, sfn)
|
|||
|
register char *dfn, *sfn;
|
|||
|
{
|
|||
|
register char *sp, *tp;
|
|||
|
register int c;
|
|||
|
register struct passwd *pw;
|
|||
|
char ts[128];
|
|||
|
|
|||
|
/* HORRIBLE, GROSS, DISGUSTING HACK: if the destination and
|
|||
|
* source strings are identical (same pointer), then do not
|
|||
|
* do any expansion--this happens to work with the current
|
|||
|
* structure very well, since multiple expansions may happen.
|
|||
|
*/
|
|||
|
if (dfn == sfn)
|
|||
|
return;
|
|||
|
|
|||
|
ts[0] = 0;
|
|||
|
|
|||
|
/* If have a leading $, then expand environment variable */
|
|||
|
if (*sfn == '$')
|
|||
|
{ ++sfn;
|
|||
|
tp = ts;
|
|||
|
while (*tp++ = *sfn)
|
|||
|
if (!isalnum(*sfn))
|
|||
|
break;
|
|||
|
else
|
|||
|
++sfn;
|
|||
|
*--tp = 0; /* Just in case */
|
|||
|
strcpy(ts, getenv(ts)); /* MARGINAL!! */
|
|||
|
}
|
|||
|
/* If have leading ~, then expand login name (null means $HOME) */
|
|||
|
else if (*sfn == '~')
|
|||
|
{ ++sfn;
|
|||
|
if (*sfn == '/' || *sfn == 0)
|
|||
|
strcpy(ts, getenv("HOME"));
|
|||
|
else
|
|||
|
{ tp = ts;
|
|||
|
while (*sfn && *sfn != '/')
|
|||
|
*tp++ = *sfn++;
|
|||
|
*tp = 0;
|
|||
|
pw = (struct passwd *)getpwnam(ts);
|
|||
|
if (! pw)
|
|||
|
strcpy(ts, "???");
|
|||
|
else
|
|||
|
strcpy(ts, pw->pw_dir);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Now, ts is either empty or contains the expansion;
|
|||
|
* sfn has been updated correctly.
|
|||
|
*/
|
|||
|
strcpy(dfn, ts);
|
|||
|
strcat(dfn, sfn);
|
|||
|
}
|
|||
|
#endif /*IMAGEN*/
|