home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
programs
/
text
/
vim
/
src
/
fileio.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-27
|
32KB
|
1,264 lines
/* vi:ts=4:sw=4
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Read the file "credits.txt" for a list of people who contributed.
* Read the file "uganda.txt" for copying and usage conditions.
*/
/*
* fileio.c: read from and write to a file
*/
/*
* special feature of this version: NUL characters in the file are
* replaced by newline characters in memory. This allows us to edit
* binary files!
*/
#ifdef MSDOS
# include <io.h>
#endif
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "param.h"
#include "fcntl.h"
#ifdef LATTICE
# include <proto/dos.h> /* for Lock() and UnLock() */
#endif
#define BUFSIZE 8192 /* size of normal write buffer */
#define SBUFSIZE 256 /* size of emergency write buffer */
static int write_buf __ARGS((int, char_u *, int));
static void do_mlines __ARGS((void));
void
filemess(name, s)
char_u *name;
char_u *s;
{
/* careful: home_replace calls vimgetenv(), which also uses IObuff! */
home_replace(name, IObuff + 1, IOSIZE - 1);
IObuff[0] = '"';
STRCAT(IObuff, "\" ");
STRCAT(IObuff, s);
/*
* don't use msg(), because it sometimes outputs a newline
*/
msg_start();
msg_outstr(IObuff);
msg_ceol();
flushbuf();
}
/*
* Read lines from file 'fname' into the buffer after line 'from'.
*
* 1. We allocate blocks with lalloc, as big as possible.
* 2. Each block is filled with characters from the file with a single read().
* 3. The lines are inserted in the buffer with ml_append().
*
* (caller must check that fname != NULL)
*
* skip_lnum is the number of lines that must be skipped
* nlines is the number of lines that are appended
* When not recovering skip_lnum is 0 and nlines MAXLNUM.
*
* return FAIL for failure, OK otherwise
*/
int
readfile(fname, sfname, from, newfile, skip_lnum, nlines)
char_u *fname;
char_u *sfname;
linenr_t from;
int newfile;
linenr_t skip_lnum;
linenr_t nlines;
{
#ifdef UNIX
int fd = -1;
#else
int fd;
#endif
register char_u c;
register linenr_t lnum = from;
register char_u *ptr = NULL; /* pointer into read buffer */
register char_u *buffer = NULL; /* read buffer */
char_u *new_buffer = NULL; /* init to shut up gcc */
char_u *line_start = NULL; /* init to shut up gcc */
colnr_t len;
register long size;
register char_u *p;
long filesize = 0;
int split = 0; /* number of split lines */
#define UNKNOWN 0x0fffffff /* file size is unknown */
linenr_t linecnt = curbuf->b_ml.ml_line_count;
int incomplete = FALSE; /* was the last line incomplete? */
int error = FALSE; /* errors encountered */
long linerest = 0; /* remaining characters in line */
int firstpart = TRUE; /* reading first part */
#ifdef UNIX
int perm;
#endif
int textmode = curbuf->b_p_tx; /* accept CR-LF for line break */
struct stat st;
int readonly;
/*
* If there is no file name yet, use the one for the read file.
* b_notedited is set to reflect this.
*/
if (curbuf->b_filename == NULL)
{
if (setfname(fname, sfname, FALSE) == OK)
curbuf->b_notedited = TRUE;
}
if (sfname == NULL)
sfname = fname;
/*
* Use the short filename whenever possible.
* Avoids problems with networks and when directory names are changed.
*/
if (!did_cd)
fname = sfname;
if (bufempty()) /* special case: buffer has no lines */
linecnt = 0;
#ifdef UNIX
/*
* On Unix it is possible to read a directory, so we have to
* check for it before the open().
*/
perm = getperm(fname);
# ifdef _POSIX_SOURCE
if (perm >= 0 && !S_ISREG(perm)) /* not a regular file */
# else
if (perm >= 0 && (perm & S_IFMT) != S_IFREG) /* not a regular file */
# endif
{
# ifdef _POSIX_SOURCE
if (S_ISDIR(perm))
# else
if ((perm & S_IFMT) == S_IFDIR)
# endif
filemess(fname, (char_u *)"is a directory");
else
filemess(fname, (char_u *)"is not a file");
return FAIL;
}
#endif
if (newfile && !readonlymode) /* default: set file not readonly */
curbuf->b_p_ro = FALSE;
if (newfile && stat((char *)fname, &st) != -1) /* remember time of file */
curbuf->b_mtime = st.st_mtime;
else
curbuf->b_mtime = 0;
/*
* for UNIX: check readonly with perm and access()
* for MSDOS and Amiga: check readonly by trying to open the file for writing
*/
readonly = FALSE;
#ifdef UNIX
if (!(perm & 0222) || access((char *)fname, 2))
readonly = TRUE;
fd = open((char *)fname, O_RDONLY);
#else
if (!newfile || readonlymode || (fd = open((char *)fname, O_RDWR)) < 0)
{
readonly = TRUE;
fd = open((char *)fname, O_RDONLY); /* try to open ro */
}
#endif
if (fd < 0) /* cannot open at all */
{
#ifdef MSDOS
/*
* The screen may be messed up by the "insert disk
* in drive b: and hit return" message
*/
screenclear();
#endif
#ifndef UNIX
/*
* On MSDOS and Amiga we can't open a directory, check here.
*/
if (isdir(fname) == TRUE)
filemess(fname, (char_u *)"is a directory");
else
#endif
if (newfile)
#ifdef UNIX
if (perm < 0)
#endif
filemess(fname, (char_u *)"[New File]");
#ifdef UNIX
else
filemess(fname, (char_u *)"[Permission Denied]");
#endif
return FAIL;
}
if (newfile && readonly) /* set file readonly */
curbuf->b_p_ro = TRUE;
if (newfile)
curbuf->b_p_eol = TRUE;
++no_wait_return; /* don't wait for return yet */
if (!recoverymode)
filemess(fname, (char_u *)""); /* show that we are busy */
while (!error && !got_int)
{
/*
* We allocate as much space for the file as we can get, plus
* space for the old line plus room for one terminating NUL.
* The amount is limited by the fact that read() only can read
* upto max_unsigned characters (and other things).
*/
#if defined(AMIGA) || defined(MSDOS)
if (sizeof(int) <= 2 && linerest >= 0x7ff0)
{
++split;
*ptr = NL; /* split line by inserting a NL */
size = 1;
}
else
#endif
{
#if !(defined(AMIGA) || defined(MSDOS))
if (sizeof(int) > 2)
size = 0x10000L; /* read 64K at a time */
else
#endif
size = 0x7ff0L - linerest; /* limit buffer to 32K */
for ( ; size >= 10; size >>= 1)
{
if ((new_buffer = lalloc((long_u)(size + linerest + 1), FALSE)) != NULL)
break;
}
if (new_buffer == NULL)
{
emsg(e_outofmem);
error = TRUE;
break;
}
if (linerest) /* copy characters from the previous buffer */
memmove((char *)new_buffer, (char *)ptr - linerest, linerest);
free(buffer);
buffer = new_buffer;
ptr = buffer + linerest;
line_start = buffer;
if ((size = read(fd, (char *)ptr, (size_t)size)) <= 0)
{
if (size < 0) /* read error */
error = TRUE;
break;
}
filesize += size; /* count the number of characters */
/*
* when reading the first part of a file: guess EOL type
*/
if (firstpart && p_ta)
{
for (p = ptr; p < ptr + size; ++p)
if (*p == NL)
{
if (p > ptr && p[-1] == CR) /* found CR-NL */
textmode = TRUE;
else /* found a single NL */
textmode = FALSE;
/* if editing a new file: may set p_tx */
if (newfile && curbuf->b_p_tx != textmode)
{
curbuf->b_p_tx = textmode;
paramchanged((char_u *)"tx");
}
break;
}
}
}
/*
* This loop is executed once for every character read.
* Keep it fast!
*/
--ptr;
while (++ptr, --size >= 0)
{
if ((c = *ptr) != NUL && c != NL) /* catch most common case */
continue;
if (c == NUL)
*ptr = NL; /* NULs are replaced by newlines! */
else
{
if (skip_lnum == 0)
{
*ptr = NUL; /* end of line */
len = ptr - line_start + 1;
if (textmode && ptr[-1] == CR) /* remove CR */
{
ptr[-1] = NUL;
--len;
}
if (ml_append(lnum, line_start, len, newfile) == FAIL)
{
error = TRUE;
break;
}
++lnum;
if (--nlines == 0)
{
error = TRUE; /* break loop */
line_start = ptr; /* nothing left to write */
break;
}
}
else
--skip_lnum;
line_start = ptr + 1;
}
}
linerest = ptr - line_start;
firstpart = FALSE;
breakcheck();
}
if (error && nlines == 0) /* not an error, max. number of lines reached */
error = FALSE;
if (!error && !got_int && linerest != 0
#ifdef MSDOS
/*
* in MSDOS textmode ignore a trailing CTRL-Z
*/
&& !(!curbuf->b_p_bin && *line_start == Ctrl('Z') && ptr == line_start + 1)
#endif
)
{
/*
* If we get EOF in the middle of a line, note the fact and
* complete the line ourselves.
*/
incomplete = TRUE;
if (newfile && curbuf->b_p_bin) /* remember for when writing */
curbuf->b_p_eol = FALSE;
*ptr = NUL;
if (ml_append(lnum, line_start, (colnr_t)(ptr - line_start + 1), newfile) == FAIL)
error = TRUE;
else
++lnum;
}
if (lnum != from && !newfile) /* added at least one line */
CHANGED;
close(fd);
free(buffer);
--no_wait_return; /* may wait for return now */
if (recoverymode) /* in recovery mode return here */
{
if (error)
return FAIL;
return OK;
}
#ifdef MSDOS /* the screen may be messed up by the "insert disk
in drive b: and hit return" message */
screenclear();
#endif
linecnt = curbuf->b_ml.ml_line_count - linecnt;
if (!newfile)
mark_adjust(from + 1, MAXLNUM, (long)linecnt);
if (got_int)
{
filemess(fname, e_interr);
return OK; /* an interrupt isn't really an error */
}
/* careful: home_replace calls vimgetenv(), which also uses IObuff! */
home_replace(fname, IObuff + 1, IOSIZE - 1);
IObuff[0] = '"';
sprintf((char *)IObuff + STRLEN(IObuff),
"\" %s%s%s%s%s%ld line%s, %ld character%s",
curbuf->b_p_ro ? "[readonly] " : "",
incomplete ? "[Incomplete last line] " : "",
split ? "[long lines split] " : "",
error ? "[READ ERRORS] " : "",
#ifdef MSDOS
textmode ? "" : "[notextmode] ",
#else
textmode ? "[textmode] " : "",
#endif
(long)linecnt, plural((long)linecnt),
filesize, plural(filesize));
msg(IObuff);
if (error && newfile) /* with errors we should not write the file */
{
curbuf->b_p_ro = TRUE;
paramchanged((char_u *)"ro");
}
u_clearline(); /* cannot use "U" command after adding lines */
if (newfile) /* edit a new file: read mode from lines */
do_mlines();
if (from < curbuf->b_ml.ml_line_count)
{
curwin->w_cursor.lnum = from + 1; /* put cursor at first new line */
curwin->w_cursor.col = 0;
}
return OK;
}
/*
* writeit - write to file 'fname' lines 'start' through 'end'
*
* We do our own buffering here because fwrite() is so slow.
*
* If forceit is true, we don't care for errors when attempting backups (jw).
* In case of an error everything possible is done to restore the original file.
* But when forceit is TRUE, we risk loosing it.
* When reset_changed is TRUE and start == 1 and end ==
* curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
*
* return FAIL for failure, OK otherwise
*/
int
buf_write(buf, fname, sfname, start, end, append, forceit, reset_changed)
BUF *buf;
char_u *fname;
char_u *sfname;
linenr_t start, end;
int append;
int forceit;
int reset_changed;
{
int fd;
char_u *backup = NULL;
char_u *ffname;
register char_u *s;
register char_u *ptr;
register char_u c;
register int len;
register linenr_t lnum;
long nchars;
char_u *errmsg = NULL;
char_u *buffer;
char_u smallbuf[SBUFSIZE];
int bufsize;
long perm = -1; /* file permissions */
int retval = OK;
int newfile = FALSE; /* TRUE if file does not exist yet */
#ifdef UNIX
struct stat old;
int made_writable = FALSE; /* 'w' bit has been set */
#endif
#ifdef AMIGA
BPTR flock;
#endif
/* writing everything */
int whole = (start == 1 && end == buf->b_ml.ml_line_count);
if (fname == NULL || *fname == NUL) /* safety check */
return FAIL;
/*
* If there is no file name yet, use the one for the written file.
* b_notedited is set to reflect this (in case the write fails).
*/
if (reset_changed && whole && buf == curbuf && curbuf->b_filename == NULL)
{
if (setfname(fname, sfname, FALSE) == OK)
curbuf->b_notedited = TRUE;
}
if (sfname == NULL)
sfname = fname;
/*
* Use the short filename whenever possible.
* Avoids problems with networks and when directory names are changed.
*/
ffname = fname; /* remember full fname */
if (!did_cd)
fname = sfname;
/*
* Disallow writing from .exrc and .vimrc in current directory for
* security reasons.
*/
if (secure)
{
secure = 2;
emsg(e_curdir);
return FAIL;
}
if (exiting)
settmode(0); /* when exiting allow typahead now */
++no_wait_return; /* don't wait for return yet */
filemess(fname, (char_u *)""); /* show that we are busy */
buffer = alloc(BUFSIZE);
if (buffer == NULL) /* can't allocate big buffer, use small one */
{
buffer = smallbuf;
bufsize = SBUFSIZE;
}
else
bufsize = BUFSIZE;
#if defined(UNIX) && !defined(ARCHIE)
/* get information about original file (if there is one) */
old.st_dev = old.st_ino = 0;
if (stat((char *)fname, &old))
newfile = TRUE;
else
{
#ifdef _POSIX_SOURCE
if (!S_ISREG(old.st_mode)) /* not a file */
#else
if ((old.st_mode & S_IFMT) != S_IFREG) /* not a file */
#endif
{
#ifdef _POSIX_SOURCE
if (S_ISDIR(old.st_mode))
#else
if ((old.st_mode & S_IFMT) == S_IFDIR)
#endif
errmsg = (char_u *)"is a directory";
else
errmsg = (char_u *)"is not a file";
goto fail;
}
perm = old.st_mode;
}
/*
* If we are not appending, the file exists, and the 'writebackup', 'backup'
* or 'patchmode' option is set, try to make a backup copy of the file.
*/
if (!append && perm >= 0 && (p_wb || p_bk || (p_pm != NULL && *p_pm != NUL)) &&
(fd = open((char *)fname, O_RDONLY)) >= 0)
{
int bfd, buflen;
char_u copybuf[BUFSIZE + 1], *wp;
int some_error = FALSE;
struct stat new;
new.st_dev = new.st_ino = 0;
/*
* Unix semantics has it, that we may have a writable file,
* that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
* - the directory is not writable,
* - the file may be a symbolic link,
* - the file may belong to another user/group, etc.
*
* For these reasons, the existing writable file must be truncated and
* reused. Creation of a backup COPY will be attempted.
*/
if (*p_bdir != '>') /* try to put .bak in current dir */
{
if ((backup = modname(fname, ".bak")) == NULL)
{
some_error = TRUE; /* out of memory */
goto nobackup;
}
if (!stat((char *)backup, &new) &&
new.st_dev == old.st_dev && new.st_ino == old.st_ino)
{
/*
* may happen when modname gave the same file back.
* E.g. silly link, or filename-length reached.
* If we don't check here, we either ruin the file when
* copying or erase it after writing. jw.
*/
free(backup);
backup = NULL; /* there is no backup file to delete */
if (*p_bdir == NUL)
{
errmsg = (char_u *)"Invalid backup file (use ! to override)";
goto nobackup;
}
}
else
remove((char *)backup); /* remove old backup, if present */
}
if (backup == NULL || (bfd = open((char *)backup, O_WRONLY | O_CREAT, 0666)) < 0)
{
/*
* 'backupdir' starts with '>' or no write/create permission
* in current dirr: try again in p_bdir directory.
*/
free(backup);
wp = gettail(fname);
sprintf((char *)copybuf, "%s/%s", *p_bdir == '>' ? p_bdir + 1 : p_bdir, wp);
if ((backup = buf_modname(buf, copybuf, (char_u *)".bak")) == NULL)
{
some_error = TRUE; /* out of memory */
goto nobackup;
}
if (!stat((char *)backup, &new) &&
new.st_dev == old.st_dev && new.st_ino == old.st_ino)
{
errmsg = (char_u *)"Invalid backup file (use ! to override)";
free(backup);
backup = NULL; /* there is no backup file to delete */
goto nobackup;
}
remove((char *)backup);
if ((bfd = open((char *)backup, O_WRONLY | O_CREAT, 0666)) < 0)
{
free(backup);
backup = NULL; /* there is no backup file to delete */
errmsg = (char_u *)"Can't make backup file (use ! to override)";
goto nobackup;
}
}
/* set file protection same as original file, but strip s-bit */
(void)setperm(backup, perm & 0777);
/* copy the file. */
while ((buflen = read(fd, (char *)copybuf, BUFSIZE)) > 0)
{
if (write_buf(bfd, copybuf, buflen) == FAIL)
{
errmsg = (char_u *)"Can't write to backup file (use ! to override)";
goto writeerr;
}
}
writeerr:
close(bfd);
if (buflen < 0)
errmsg = (char_u *)"Can't read file for backup (use ! to override)";
nobackup:
close(fd);
/* ignore errors when forceit is TRUE */
if ((some_error || errmsg) && !forceit)
{
retval = FAIL;
goto fail;
}
errmsg = NULL;
}
/* if forceit and the file was read-only: make it writable */
if (forceit && (old.st_uid == getuid()) && perm >= 0 && !(perm & 0200))
{
perm |= 0200;
(void)setperm(fname, perm);
made_writable = TRUE;
/* if we are writing to the current file, readonly makes no sense */
if (fname == buf->b_filename || fname == buf->b_sfilename)
buf->b_p_ro = FALSE;
}
#else /* end of UNIX, start of the rest */
/*
* If we are not appending, the file exists, and the 'writebackup' or
* 'backup' option is set, make a backup.
* Do not make any backup, if "writebackup" and "backup" are
* both switched off. This helps when editing large files on
* almost-full disks. (jw)
*/
perm = getperm(fname);
if (perm < 0)
newfile = TRUE;
else if (isdir(fname) == TRUE)
{
errmsg = (char_u *)"is a directory";
goto fail;
}
if (!append && perm >= 0 && (p_wb || p_bk || (p_pm != NULL && *p_pm != NUL)))
{
/*
* Form the backup file name - change path/fo.o.h to path/fo.o.h.bak
*/
backup = buf_modname(buf, fname, (char_u *)".bak");
if (backup == NULL)
{
if (!forceit)
goto fail;
}
else
{
/*
* Delete any existing backup and move the current version to the backup.
* For safety, we don't remove the backup until the write has finished
* successfully. And if the 'backup' option is set, leave it around.
*/
#ifdef AMIGA
/*
* With MSDOS-compatible filesystems (crossdos, messydos) it is
* possible that the name of the backup file is the same as the
* original file. To avoid the chance of accidently deleting the
* original file (horror!) we lock it during the remove.
* This should not happen with ":w", because startscript() should
* detect this problem and set buf->b_shortname, causing modname to
* return a correct ".bak" filename. This problem does exist with
* ":w filename", but then the original file will be somewhere else
* so the backup isn't really important. If autoscripting is off
* the rename may fail.
*/
flock = Lock((UBYTE *)fname, (long)ACCESS_READ);
#endif
remove((char *)backup);
#ifdef AMIGA
if (flock)
UnLock(flock);
#endif
len = rename((char *)fname, (char *)backup);
if (len != 0)
{
if (forceit)
{
free(backup); /* don't do the rename below */
backup = NULL;
}
else
{
errmsg = (char_u *)"Can't make backup file (use ! to override)";
goto fail;
}
}
}
}
#endif /* UNIX */
/*
* If the original file is being overwritten, there is a small chance that
* we crash in the middle of writing. Therefore the file is preserved now.
* This makes all block numbers positive so that recovery does not need
* the original file.
* Don't do this if there is a backup file and we are exiting.
*/
if (reset_changed && !newfile && !otherfile(ffname) && !(exiting && backup != NULL))
ml_preserve(buf, FALSE);
/*
* We may try to open the file twice: If we can't write to the
* file and forceit is TRUE we delete the existing file and try to create
* a new one. If this still fails we may have lost the original file!
* (this may happen when the user reached his quotum for number of files).
* Appending will fail if the file does not exist and forceit is FALSE.
*/
while ((fd = open((char *)fname, O_WRONLY | (append ?
(forceit ? (O_APPEND | O_CREAT) : O_APPEND) :
(O_CREAT | O_TRUNC)), 0666)) < 0)
{
/*
* A forced write will try to create a new file if the old one is
* still readonly. This may also happen when the directory is
* read-only. In that case the remove() will fail.
*/
if (!errmsg)
{
errmsg = (char_u *)"Can't open file for writing";
if (forceit)
{
#ifdef UNIX
/* we write to the file, thus it should be marked
writable after all */
perm |= 0200;
made_writable = TRUE;
if (old.st_uid != getuid() || old.st_gid != getgid())
perm &= 0777;
#endif /* UNIX */
if (!append) /* don't remove when appending */
remove((char *)fname);
continue;
}
}
/*
* If we failed to open the file, we don't need a backup. Throw it away.
* If we moved or removed the original file try to put the backup in its place.
*/
if (backup != NULL)
{
#ifdef UNIX
struct stat st;
/*
* There is a small chance that we removed the original, try
* to move the copy in its place.
* This won't work if the backup is in another file system!
* In that case we leave the copy around.
*/
if (stat((char *)fname, &st) < 0) /* file does not exist */
rename((char *)backup, (char *)fname); /* put the copy in its place */
if (stat((char *)fname, &st) >= 0) /* original file does exist */
remove((char *)backup); /* throw away the copy */
#else
rename((char *)backup, (char *)fname); /* try to put the original file back */
#endif
}
goto fail;
}
errmsg = NULL;
if (end > buf->b_ml.ml_line_count)
end = buf->b_ml.ml_line_count;
len = 0;
s = buffer;
nchars = 0;
for (lnum = start; lnum <= end; ++lnum)
{
/*
* The next while loop is done once for each character written.
* Keep it fast!
*/
ptr = ml_get_buf(buf, lnum, FALSE) - 1;
while ((c = *++ptr) != NUL)
{
if (c == NL)
*s = NUL; /* replace newlines with NULs */
else
*s = c;
++s;
if (++len != bufsize)
continue;
if (write_buf(fd, buffer, bufsize) == FAIL)
{
end = 0; /* write error: break loop */
break;
}
nchars += bufsize;
s = buffer;
len = 0;
}
/* write failed or last line has no EOL: stop here */
if (end == 0 || (buf->b_p_bin && lnum == buf->b_ml.ml_line_count && !buf->b_p_eol))
break;
if (buf->b_p_tx) /* write CR-NL */
{
*s = CR;
++s;
if (++len == bufsize)
{
if (write_buf(fd, buffer, bufsize) == FAIL)
{
end = 0; /* write error: break loop */
break;
}
nchars += bufsize;
s = buffer;
len = 0;
}
}
*s = NL;
++s;
if (++len == bufsize && end)
{
if (write_buf(fd, buffer, bufsize) == FAIL)
{
end = 0; /* write error: break loop */
break;
}
nchars += bufsize;
s = buffer;
len = 0;
}
}
if (len && end)
{
if (write_buf(fd, buffer, len) == FAIL)
end = 0; /* write error */
nchars += len;
}
if (close(fd) != 0)
{
errmsg = (char_u *)"Close failed";
goto fail;
}
#ifdef UNIX
if (made_writable)
perm &= ~0200; /* reset 'w' bit for security reasons */
#endif
if (perm >= 0)
(void)setperm(fname, perm); /* set permissions of new file same as old file */
if (end == 0)
{
errmsg = (char_u *)"write error (file system full?)";
goto fail;
}
#ifdef MSDOS /* the screen may be messed up by the "insert disk
in drive b: and hit return" message */
if (!exiting)
screenclear();
#endif
lnum -= start; /* compute number of written lines */
--no_wait_return; /* may wait for return now */
/* careful: home_replace calls vimgetenv(), which also uses IObuff! */
home_replace(fname, IObuff + 1, IOSIZE - 1);
IObuff[0] = '"';
sprintf((char *)IObuff + STRLEN(IObuff),
"\"%s%s %ld line%s, %ld character%s",
newfile ? " [New File]" : " ",
#ifdef MSDOS
buf->b_p_tx ? "" : "[notextmode]",
#else
buf->b_p_tx ? "[textmode]" : "",
#endif
(long)lnum, plural((long)lnum),
nchars, plural(nchars));
msg(IObuff);
if (reset_changed && whole) /* when written everything */
{
UNCHANGED(buf);
u_unchanged(buf);
/*
* If written to the current file, update the timestamp of the swap file
* and reset the 'notedited' flag.
*/
if (!exiting && buf->b_filename != NULL &&
fnamecmp(ffname, buf->b_filename) == 0)
{
ml_timestamp(buf);
buf->b_notedited = FALSE;
}
}
/*
* If we kept a backup until now, and we are in patch mode, then we make
* the backup file our 'original' file.
*/
if (p_pm && *p_pm)
{
char *org = (char *)modname(fname, p_pm);
if (backup != NULL)
{
struct stat st;
/*
* If the original file does not exist yet
* the current backup file becomes the original file
*/
if (org == NULL)
EMSG("patchmode: can't save original file");
else if (stat(org, &st) < 0)
{
rename((char *)backup, org);
free(backup); /* don't delete the file */
backup = NULL;
}
}
/*
* If there is no backup file, remember that a (new) file was
* created.
*/
else
{
int fd;
if (org == NULL || (fd = open(org, O_CREAT, 0666)) < 0)
EMSG("patchmode: can't touch empty original file");
else
close(fd);
}
if (org != NULL)
{
setperm((char_u *)org, getperm(fname) & 0777);
free(org);
}
}
/*
* Remove the backup unless 'backup' option is set
*/
if (!p_bk && backup != NULL && remove((char *)backup) != 0)
EMSG("Can't delete backup file");
goto nofail;
fail:
--no_wait_return; /* may wait for return now */
#ifdef MSDOS /* the screen may be messed up by the "insert disk
in drive b: and hit return" message */
screenclear();
#endif
nofail:
free(backup);
free(buffer);
if (errmsg != NULL)
{
filemess(fname, errmsg);
retval = FAIL;
}
return retval;
}
/*
* write_buf: call write() to write a buffer
*
* return FAIL for failure, OK otherwise
*/
static int
write_buf(fd, buf, len)
int fd;
char_u *buf;
int len;
{
int wlen;
while (len)
{
wlen = write(fd, (char *)buf, (size_t)len);
if (wlen <= 0) /* error! */
return FAIL;
len -= wlen;
buf += wlen;
}
return OK;
}
/*
* do_mlines() - process mode lines for the current file
*
* Returns immediately if the "ml" parameter isn't set.
*/
static void chk_mline __ARGS((linenr_t));
static void
do_mlines()
{
linenr_t lnum;
int nmlines;
if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
return;
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines; ++lnum)
chk_mline(lnum);
for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines &&
lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
chk_mline(lnum);
}
/*
* chk_mline() - check a single line for a mode string
*/
static void
chk_mline(lnum)
linenr_t lnum;
{
register char_u *s;
register char_u *e;
char_u *cs; /* local copy of any modeline found */
int prev;
int end;
prev = ' ';
for (s = ml_get(lnum); *s != NUL; ++s)
{
if (isspace(prev) && (STRNCMP(s, "vi:", (size_t)3) == 0 || STRNCMP(s, "ex:", (size_t)3) == 0 || STRNCMP(s, "vim:", (size_t)4) == 0))
{
do
++s;
while (s[-1] != ':');
s = cs = strsave(s);
if (cs == NULL)
break;
end = FALSE;
while (end == FALSE)
{
while (*s == ' ' || *s == TAB)
++s;
if (*s == NUL)
break;
for (e = s; (*e != ':' || *(e - 1) == '\\') && *e != NUL; ++e)
;
if (*e == NUL)
end = TRUE;
*e = NUL;
if (STRNCMP(s, "set ", (size_t)4) == 0) /* "vi:set opt opt opt: foo" */
{
(void)doset(s + 4);
break;
}
if (doset(s) == FAIL) /* stop if error found */
break;
s = e + 1;
}
free(cs);
break;
}
prev = *s;
}
}
/*
* add extention to filename - change path/fo.o.h to path/fo.o.h.ext or
* fo_o_h.ext for MSDOS or when dotfname option reset.
*
* Assumed that fname is a valid name found in the filesystem we assure that
* the return value is a different name and ends in ".ext".
* "ext" MUST start with a "." and MUST be at most 4 characters long.
* Space for the returned name is allocated, must be freed later.
*/
char_u *
modname(fname, ext)
char_u *fname, *ext;
{
return buf_modname(curbuf, fname, ext);
}
char_u *
buf_modname(buf, fname, ext)
BUF *buf;
char_u *fname, *ext;
{
char_u *retval;
register char_u *s;
register char_u *ptr;
register int fnamelen, extlen;
char_u currentdir[512];
extlen = STRLEN(ext);
/*
* if there is no filename we must get the name of the current directory
* (we need the full path in case :cd is used)
*/
if (fname == NULL || *fname == NUL)
{
if (vim_dirname(currentdir, 510) == FAIL || (fnamelen = STRLEN(currentdir)) == 0)
return NULL;
if (!ispathsep(currentdir[fnamelen - 1]))
{
currentdir[fnamelen++] = PATHSEP;
currentdir[fnamelen] = NUL;
}
}
else
fnamelen = STRLEN(fname);
retval = alloc((unsigned) (fnamelen + extlen + 1));
if (retval != NULL)
{
if (fname == NULL || *fname == NUL)
STRCPY(retval, currentdir);
else
STRCPY(retval, fname);
/*
* search backwards until we hit a '/', '\' or ':' replacing all '.' by '_'
* for MSDOS or when dotfname option reset.
* Then truncate what is after the '/', '\' or ':' to 8 characters for MSDOS
* and 26 characters for AMIGA and UNIX.
*/
for (ptr = retval + fnamelen; ptr >= retval; ptr--)
{
#ifndef MSDOS
if (buf->b_p_sn || buf->b_shortname)
#endif
if (*ptr == '.') /* replace '.' by '_' */
*ptr = '_';
if (ispathsep(*ptr))
break;
}
ptr++;
/* the filename has at most BASENAMELEN characters. */
if (STRLEN(ptr) > (unsigned)BASENAMELEN)
ptr[BASENAMELEN] = '\0';
#ifndef MSDOS
if ((buf->b_p_sn || buf->b_shortname) && STRLEN(ptr) > (unsigned)8)
ptr[8] = '\0';
#endif
s = ptr + STRLEN(ptr);
/*
* Append the extention.
* ext must start with '.' and cannot exceed 3 more characters.
*/
STRCPY(s, ext);
#ifdef MSDOS
if (fname == NULL || *fname == NUL) /* can't have just the extension */
*s = '_';
#endif
if (fname != NULL && STRCMP(fname, retval) == 0)
{
/* after modification still the same name? */
/* we search for a character that can be replaced by '_' */
while (--s >= ptr)
{
if (*s != '_')
{
*s = '_';
break;
}
}
if (s < ptr)
{
/* fname was "________.<ext>" how tricky! */
*ptr = 'v';
}
}
}
return retval;
}
#ifdef WEBB_COMPLETE
/* vim_fgets();
*
* Like fgets(), but if the file line is too long, it is truncated and the
* rest of the line is thrown away. Returns TRUE or FALSE for end-of-file or
* not. The integer pointed to by lnum is incremented. Note: do not pass
* IObuff as the buffer since this is used to read and discard the extra part
* of any long lines.
*/
int
vim_fgets(buf, size, fp, lnum)
char_u *buf;
int size;
FILE *fp;
int *lnum;
{
char *eof;
buf[size - 2] = NUL;
eof = fgets((char *)buf, size, fp);
if (buf[size - 2] != NUL && buf[size - 2] != '\n')
{
buf[size - 1] = NUL; /* Truncate the line */
/* Now throw away the rest of the line: */
do
{
IObuff[IOSIZE - 2] = NUL;
eof = fgets((char *)IObuff, IOSIZE, fp);
} while (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != '\n');
return FALSE;
}
++*lnum;
return (eof == NULL);
}
#endif /* WEBB_COMPLETE */