mirror of https://github.com/tildeclub/ex-vi.git
903 lines
21 KiB
C
903 lines
21 KiB
C
/*
|
|
* This code contains changes by
|
|
* Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
|
|
*
|
|
* Conditions 1, 2, and 4 and the no-warranty notice below apply
|
|
* to these changes.
|
|
*
|
|
*
|
|
* Copyright (c) 1980, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*
|
|
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* Redistributions of source code and documentation must retain the
|
|
* above copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed or owned by Caldera
|
|
* International, Inc.
|
|
* Neither the name of Caldera International, Inc. nor the names of
|
|
* other contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
|
|
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef __GNUC__
|
|
#define UNUSED __attribute__ ((unused))
|
|
#else
|
|
#define UNUSED
|
|
#endif
|
|
|
|
#ifndef lint
|
|
#ifdef DOSCCS
|
|
char *copyright =
|
|
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
|
|
All rights reserved.\n";
|
|
#endif
|
|
static char sccsid[] UNUSED = "@(#)exrecover.c 1.21 (gritter) 11/27/04";
|
|
#endif
|
|
|
|
/* from exrecover.c 7.9.2 (2.11BSD) 1996/10/26 */
|
|
|
|
#include <stdarg.h>
|
|
#ifdef notdef /* GR */
|
|
#include <stdio.h> /* mjm: BUFSIZ: stdio = 512, VMUNIX = 1024 */
|
|
#undef BUFSIZ /* mjm: BUFSIZ different */
|
|
#undef EOF /* mjm: EOF and NULL effectively the same */
|
|
#undef NULL
|
|
#else
|
|
#define xstderr (int*)0
|
|
typedef int xFILE;
|
|
extern void perror(const char *);
|
|
extern int vsprintf(char *, const char *, va_list);
|
|
#endif
|
|
|
|
#define var
|
|
|
|
#include "ex.h"
|
|
#include "ex_temp.h"
|
|
#include "ex_tty.h"
|
|
#include <dirent.h>
|
|
#include <time.h>
|
|
|
|
#ifndef MAXNAMLEN
|
|
#ifdef FNSIZE
|
|
#define MAXNAMLEN FNSIZE
|
|
#else
|
|
#ifdef NAME_MAX
|
|
#define MAXNAMLEN NAME_MAX
|
|
#else
|
|
#define MAXNAMLEN 255
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#define TMP "/var/tmp"
|
|
|
|
#ifdef LANGMSG
|
|
nl_catd catd;
|
|
#endif
|
|
|
|
char xstr[1]; /* make loader happy */
|
|
int tfile = -1; /* ditto */
|
|
|
|
/*
|
|
*
|
|
* This program searches through the specified directory and then
|
|
* the directory /usr/preserve looking for an instance of the specified
|
|
* file from a crashed editor or a crashed system.
|
|
* If this file is found, it is unscrambled and written to
|
|
* the standard output.
|
|
*
|
|
* If this program terminates without a "broken pipe" diagnostic
|
|
* (i.e. the editor doesn't die right away) then the buffer we are
|
|
* writing from is removed when we finish. This is potentially a mistake
|
|
* as there is not enough handshaking to guarantee that the file has actually
|
|
* been recovered, but should suffice for most cases.
|
|
*/
|
|
|
|
/*
|
|
* Here we save the information about files, when
|
|
* you ask us what files we have saved for you.
|
|
* We buffer file name, number of lines, and the time
|
|
* at which the file was saved.
|
|
*/
|
|
struct svfile {
|
|
char sf_name[FNSIZE + 1];
|
|
int sf_lines;
|
|
char sf_entry[MAXNAMLEN + 1];
|
|
time_t sf_time;
|
|
};
|
|
|
|
#define ignorl(a) a
|
|
|
|
/*
|
|
* This directory definition also appears (obviously) in expreserve.c.
|
|
* Change both if you change either.
|
|
*/
|
|
#ifdef notdef
|
|
char mydir[] = "/usr/preserve";
|
|
#else
|
|
char mydir[] = "/var/preserve";
|
|
#endif
|
|
|
|
/*
|
|
* Limit on the number of printed entries
|
|
* when an, e.g. ``ex -r'' command is given.
|
|
*/
|
|
#define NENTRY 50
|
|
|
|
char nb[BUFSIZ];
|
|
int vercnt; /* Count number of versions of file found */
|
|
|
|
extern void error(char *, ...);
|
|
extern void listfiles(char *);
|
|
extern void enter(struct svfile *, char *, int);
|
|
extern int qucmp(struct svfile *, struct svfile *);
|
|
extern void findtmp(char *);
|
|
extern void searchdir(char *);
|
|
extern int yeah(char *);
|
|
extern int preserve(void);
|
|
extern void scrapbad(void);
|
|
extern void putfile(int);
|
|
extern void wrerror(void);
|
|
extern void clrstats(void);
|
|
extern void getline(line);
|
|
extern char *getblock(line, int);
|
|
extern void blkio(bloc, char *, ssize_t (*)(int, void *, size_t));
|
|
extern void syserror(void);
|
|
extern void xvfprintf(xFILE *, char *, va_list);
|
|
extern void xfprintf(xFILE *, char *, ...);
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
register char *cp;
|
|
register int b, i;
|
|
|
|
/*
|
|
* Initialize the built-in memory allocator.
|
|
*/
|
|
#ifdef VMUNIX
|
|
poolsbrk(0);
|
|
#endif
|
|
#ifdef LANGMSG
|
|
setlocale(LC_MESSAGES, "");
|
|
catd = catopen(CATNAME, NL_CAT_LOCALE);
|
|
#endif
|
|
|
|
/*
|
|
* Initialize as though the editor had just started.
|
|
*/
|
|
fendcore = (line *) sbrk(0);
|
|
dot = zero = dol = fendcore;
|
|
one = zero + 1;
|
|
endcore = fendcore - 2;
|
|
iblock = oblock = -1;
|
|
|
|
/*
|
|
* If given only a -r argument, then list the saved files.
|
|
*/
|
|
if (argc == 2 && strcmp(argv[1], "-r") == 0) {
|
|
listfiles(mydir);
|
|
listfiles(TMP);
|
|
exit(0);
|
|
}
|
|
if (argc != 3)
|
|
error(catgets(catd, 2, 1,
|
|
" Wrong number of arguments to exrecover"), 0);
|
|
|
|
strcpy(file, argv[2]);
|
|
|
|
/*
|
|
* Search for this file.
|
|
*/
|
|
findtmp(argv[1]);
|
|
|
|
/*
|
|
* Got (one of the versions of) it, write it back to the editor.
|
|
*/
|
|
cp = ctime(&H.Time);
|
|
cp[19] = 0;
|
|
xfprintf(xstderr, catgets(catd, 2, 2, " [Dated: %s"), cp);
|
|
xfprintf(xstderr, vercnt > 1 ? catgets(catd, 2, 3,
|
|
", newest of %d saved]")
|
|
: catgets(catd, 2, 4, "]"), vercnt);
|
|
H.Flines++;
|
|
|
|
/*
|
|
* Allocate space for the line pointers from the temp file.
|
|
*/
|
|
if ((char *) sbrk(H.Flines * sizeof (line)) == (char *) -1)
|
|
/*
|
|
* Good grief.
|
|
*/
|
|
error(catgets(catd, 1, 5, " Not enough core for lines"), 0);
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "%d lines\n", H.Flines);
|
|
#endif
|
|
|
|
/*
|
|
* Now go get the blocks of seek pointers which are scattered
|
|
* throughout the temp file, reconstructing the incore
|
|
* line pointers at point of crash.
|
|
*/
|
|
b = 0;
|
|
while (H.Flines > 0) {
|
|
ignorl(lseek(tfile, (off_t) ((blocks[b] & BLKMSK) * BUFSIZ),
|
|
SEEK_SET));
|
|
i = H.Flines < BUFSIZ / sizeof (line) ?
|
|
H.Flines * sizeof (line) : BUFSIZ;
|
|
if (read(tfile, (char *) dot, i) != i) {
|
|
perror(nb);
|
|
exit(1);
|
|
}
|
|
dot += i / sizeof (line);
|
|
H.Flines -= i / sizeof (line);
|
|
b++;
|
|
}
|
|
dot--; dol = dot;
|
|
|
|
/*
|
|
* Sigh... due to sandbagging some lines may really not be there.
|
|
* Find and discard such. This shouldn't happen much.
|
|
*/
|
|
scrapbad();
|
|
|
|
/*
|
|
* Now if there were any lines in the recovered file
|
|
* write them to the standard output.
|
|
*/
|
|
if (dol > zero) {
|
|
addr1 = one; addr2 = dol; io = 1;
|
|
putfile(0);
|
|
}
|
|
|
|
/*
|
|
* Trash the saved buffer.
|
|
* Hopefully the system won't crash before the editor
|
|
* syncs the new recovered buffer; i.e. for an instant here
|
|
* you may lose if the system crashes because this file
|
|
* is gone, but the editor hasn't completed reading the recovered
|
|
* file from the pipe from us to it.
|
|
*
|
|
* This doesn't work if we are coming from an non-absolute path
|
|
* name since we may have chdir'ed but what the hay, noone really
|
|
* ever edits with temporaries in "." anyways.
|
|
*/
|
|
if (nb[0] == '/')
|
|
ignore(unlink(nb));
|
|
|
|
/*
|
|
* Adieu.
|
|
*/
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
* Print an error message (notably not in error
|
|
* message file). If terminal is in RAW mode, then
|
|
* we should be writing output for "vi", so don't print
|
|
* a newline which would screw up the screen.
|
|
*/
|
|
/*VARARGS2*/
|
|
void
|
|
error(char *str, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, str);
|
|
xvfprintf(xstderr, str, ap);
|
|
va_end(ap);
|
|
tcgetattr(2, &tty);
|
|
if (tty.c_lflag & ICANON)
|
|
xfprintf(xstderr, "\n");
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
listfiles(char *dirname)
|
|
{
|
|
register DIR *dir;
|
|
struct dirent *dirent;
|
|
int ecount;
|
|
register int f;
|
|
char *cp;
|
|
struct svfile *fp, svbuf[NENTRY];
|
|
|
|
/*
|
|
* Open /usr/preserve, and go there to make things quick.
|
|
*/
|
|
dir = opendir(dirname);
|
|
if (dir == NULL) {
|
|
perror(dirname);
|
|
return;
|
|
}
|
|
if (chdir(dirname) < 0) {
|
|
perror(dirname);
|
|
return;
|
|
}
|
|
xfprintf(xstderr, "%s:\n", dirname);
|
|
|
|
/*
|
|
* Look at the candidate files in /usr/preserve.
|
|
*/
|
|
fp = &svbuf[0];
|
|
ecount = 0;
|
|
while ((dirent = readdir(dir)) != NULL) {
|
|
if (dirent->d_name[0] != 'E')
|
|
continue;
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "considering %s\n", dirent->d_name);
|
|
#endif
|
|
/*
|
|
* Name begins with E; open it and
|
|
* make sure the uid in the header is our uid.
|
|
* If not, then don't bother with this file, it can't
|
|
* be ours.
|
|
*/
|
|
f = open(dirent->d_name, O_RDONLY);
|
|
if (f < 0) {
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "open failed\n");
|
|
#endif
|
|
continue;
|
|
}
|
|
if (read(f, (char *) &H, sizeof H) != sizeof H) {
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "culdnt read hedr\n");
|
|
#endif
|
|
ignore(close(f));
|
|
continue;
|
|
}
|
|
ignore(close(f));
|
|
if (getuid() != H.Uid) {
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "uid wrong\n");
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Saved the day!
|
|
*/
|
|
enter(fp++, dirent->d_name, ecount);
|
|
ecount++;
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "entered file %s\n", dirent->d_name);
|
|
#endif
|
|
}
|
|
ignore(closedir(dir));
|
|
|
|
/*
|
|
* If any files were saved, then sort them and print
|
|
* them out.
|
|
*/
|
|
if (ecount == 0) {
|
|
xfprintf(xstderr, catgets(catd, 2, 6, "No files saved.\n"));
|
|
return;
|
|
}
|
|
qsort(&svbuf[0], ecount, sizeof svbuf[0], (int(*)()) qucmp);
|
|
for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) {
|
|
cp = ctime(&fp->sf_time);
|
|
cp[10] = 0;
|
|
xfprintf(xstderr, catgets(catd, 2, 7, "On %s at "), cp);
|
|
cp[16] = 0;
|
|
xfprintf(xstderr, &cp[11]);
|
|
xfprintf(xstderr, catgets(catd, 2, 8,
|
|
" saved %d lines of file \"%s\"\n"),
|
|
fp->sf_lines, fp->sf_name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Enter a new file into the saved file information.
|
|
*/
|
|
void
|
|
enter(struct svfile *fp, char *fname, int count)
|
|
{
|
|
register char *cp, *cp2;
|
|
register struct svfile *f, *fl;
|
|
time_t curtime, itol();
|
|
|
|
f = 0;
|
|
if (count >= NENTRY) {
|
|
/*
|
|
* My god, a huge number of saved files.
|
|
* Would you work on a system that crashed this
|
|
* often? Hope not. So lets trash the oldest
|
|
* as the most useless.
|
|
*
|
|
* (I wonder if this code has ever run?)
|
|
*/
|
|
fl = fp - count + NENTRY - 1;
|
|
curtime = fl->sf_time;
|
|
for (f = fl; --f > fp-count; )
|
|
if (f->sf_time < curtime)
|
|
curtime = f->sf_time;
|
|
for (f = fl; --f > fp-count; )
|
|
if (f->sf_time == curtime)
|
|
break;
|
|
fp = f;
|
|
}
|
|
|
|
/*
|
|
* Gotcha.
|
|
*/
|
|
fp->sf_time = H.Time;
|
|
fp->sf_lines = H.Flines;
|
|
cp2 = fp->sf_name, cp = savedfile;
|
|
while (*cp2++ = *cp++);
|
|
for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;)
|
|
*cp2++ = *cp++;
|
|
*cp2++ = 0;
|
|
}
|
|
|
|
/*
|
|
* Do the qsort compare to sort the entries first by file name,
|
|
* then by modify time.
|
|
*/
|
|
int
|
|
qucmp(struct svfile *p1, struct svfile *p2)
|
|
{
|
|
register int t;
|
|
|
|
if (t = strcmp(p1->sf_name, p2->sf_name))
|
|
return(t);
|
|
if (p1->sf_time > p2->sf_time)
|
|
return(-1);
|
|
return(p1->sf_time < p2->sf_time);
|
|
}
|
|
|
|
/*
|
|
* Scratch for search.
|
|
*/
|
|
char bestnb[BUFSIZ]; /* Name of the best one */
|
|
long besttime; /* Time at which the best file was saved */
|
|
int bestfd; /* Keep best file open so it dont vanish */
|
|
|
|
/*
|
|
* Look for a file, both in the users directory option value
|
|
* (i.e. usually /tmp) and in /usr/preserve.
|
|
* Want to find the newest so we search on and on.
|
|
*/
|
|
void
|
|
findtmp(char *dir)
|
|
{
|
|
|
|
/*
|
|
* No name or file so far.
|
|
*/
|
|
bestnb[0] = 0;
|
|
bestfd = -1;
|
|
|
|
/*
|
|
* Search /usr/preserve and, if we can get there, /tmp
|
|
* (actually the users "directory" option).
|
|
*/
|
|
searchdir(dir);
|
|
if (chdir(mydir) == 0)
|
|
searchdir(mydir);
|
|
if (bestfd != -1) {
|
|
/*
|
|
* Gotcha.
|
|
* Put the file (which is already open) in the file
|
|
* used by the temp file routines, and save its
|
|
* name for later unlinking.
|
|
*/
|
|
tfile = bestfd;
|
|
strcpy(nb, bestnb);
|
|
ignorl(lseek(tfile, (off_t) 0, SEEK_SET));
|
|
|
|
/*
|
|
* Gotta be able to read the header or fall through
|
|
* to lossage.
|
|
*/
|
|
if (read(tfile, (char *) &H, sizeof H) == sizeof H)
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Extreme lossage...
|
|
*/
|
|
error(catgets(catd, 2, 9, " File not found"), 0);
|
|
}
|
|
|
|
/*
|
|
* Search for the file in directory dirname.
|
|
*
|
|
* Don't chdir here, because the users directory
|
|
* may be ".", and we would move away before we searched it.
|
|
* Note that we actually chdir elsewhere (because it is too slow
|
|
* to look around in /usr/preserve without chdir'ing there) so we
|
|
* can't win, because we don't know the name of '.' and if the path
|
|
* name of the file we want to unlink is relative, rather than absolute
|
|
* we won't be able to find it again.
|
|
*/
|
|
void
|
|
searchdir(char *dirname)
|
|
{
|
|
struct dirent *dirent;
|
|
register DIR *dir;
|
|
/* char dbuf[BUFSIZ]; */
|
|
|
|
dir = opendir(dirname);
|
|
if (dir == NULL)
|
|
return;
|
|
while ((dirent = readdir(dir)) != NULL) {
|
|
if (dirent->d_name[0] != 'E')
|
|
continue;
|
|
/*
|
|
* Got a file in the directory starting with E...
|
|
* Save a consed up name for the file to unlink
|
|
* later, and check that this is really a file
|
|
* we are looking for.
|
|
*/
|
|
ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent->d_name));
|
|
if (yeah(nb)) {
|
|
/*
|
|
* Well, it is the file we are looking for.
|
|
* Is it more recent than any version we found before?
|
|
*/
|
|
if (H.Time > besttime) {
|
|
/*
|
|
* A winner.
|
|
*/
|
|
ignore(close(bestfd));
|
|
bestfd = dup(tfile);
|
|
besttime = H.Time;
|
|
strcpy(bestnb, nb);
|
|
}
|
|
/*
|
|
* Count versions so user can be told there are
|
|
* ``yet more pages to be turned''.
|
|
*/
|
|
vercnt++;
|
|
}
|
|
ignore(close(tfile));
|
|
}
|
|
ignore(closedir(dir));
|
|
}
|
|
|
|
/*
|
|
* Given a candidate file to be recovered, see
|
|
* if its really an editor temporary and of this
|
|
* user and the file specified.
|
|
*/
|
|
int
|
|
yeah(char *name)
|
|
{
|
|
|
|
tfile = open(name, O_RDWR);
|
|
if (tfile < 0)
|
|
return (0);
|
|
if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
|
|
nope:
|
|
ignore(close(tfile));
|
|
return (0);
|
|
}
|
|
if (strcmp(savedfile, file))
|
|
goto nope;
|
|
if (getuid() != H.Uid)
|
|
goto nope;
|
|
/*
|
|
* This is old and stupid code, which
|
|
* puts a word LOST in the header block, so that lost lines
|
|
* can be made to point at it.
|
|
*/
|
|
ignorl(lseek(tfile, (off_t) (BUFSIZ*HBLKS-8), SEEK_SET));
|
|
ignore(write(tfile, "LOST", 5));
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
preserve(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find the true end of the scratch file, and ``LOSE''
|
|
* lines which point into thin air. This lossage occurs
|
|
* due to the sandbagging of i/o which can cause blocks to
|
|
* be written in a non-obvious order, different from the order
|
|
* in which the editor tried to write them.
|
|
*
|
|
* Lines which are lost are replaced with the text LOST so
|
|
* they are easy to find. We work hard at pretty formatting here
|
|
* as lines tend to be lost in blocks.
|
|
*
|
|
* This only seems to happen on very heavily loaded systems, and
|
|
* not very often.
|
|
*/
|
|
void
|
|
scrapbad(void)
|
|
{
|
|
register line *ip;
|
|
struct stat stbuf;
|
|
off_t size, maxt;
|
|
bbloc bno, cnt = 0, bad, was;
|
|
char bk[BUFSIZ];
|
|
|
|
ignore(fstat(tfile, &stbuf));
|
|
size = stbuf.st_size;
|
|
maxt = (size >> SHFT) | (BNDRY-1);
|
|
bno = (maxt >> OFFBTS) & BLKMSK;
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
|
|
#endif
|
|
|
|
/*
|
|
* Look for a null separating two lines in the temp file;
|
|
* if last line was split across blocks, then it is lost
|
|
* if the last block is.
|
|
*/
|
|
while (bno > 0) {
|
|
ignorl(lseek(tfile, (off_t) (BUFSIZ * (bno & BLKMSK)),
|
|
SEEK_SET));
|
|
cnt = read(tfile, (char *) bk, BUFSIZ);
|
|
while (cnt > 0)
|
|
if (bk[--cnt] == 0)
|
|
goto null;
|
|
bno--;
|
|
}
|
|
null:
|
|
|
|
/*
|
|
* Magically calculate the largest valid pointer in the temp file,
|
|
* consing it up from the block number and the count.
|
|
*/
|
|
maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
|
|
#endif
|
|
|
|
/*
|
|
* Now cycle through the line pointers,
|
|
* trashing the Lusers.
|
|
*/
|
|
was = bad = 0;
|
|
for (ip = one; ip <= dol; ip++)
|
|
if (*ip > maxt) {
|
|
#ifdef DEBUG
|
|
xfprintf(xstderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
|
|
#endif
|
|
if (was == 0)
|
|
was = ip - zero;
|
|
*ip = ((HBLKS*BUFSIZ)-8) >> SHFT;
|
|
} else if (was) {
|
|
if (bad == 0)
|
|
xfprintf(xstderr, catgets(catd, 2, 10,
|
|
" [Lost line(s):"));
|
|
xfprintf(xstderr, catgets(catd, 2, 11,
|
|
" %d"), was);
|
|
if ((ip - 1) - zero > was)
|
|
xfprintf(xstderr, catgets(catd, 2, 12, "-%d"),
|
|
(int) ((ip - 1) - zero));
|
|
bad++;
|
|
was = 0;
|
|
}
|
|
if (was != 0) {
|
|
if (bad == 0)
|
|
xfprintf(xstderr, catgets(catd, 2, 13,
|
|
" [Lost line(s):"));
|
|
xfprintf(xstderr, catgets(catd, 2, 14, " %d"), was);
|
|
if (dol - zero != was)
|
|
xfprintf(xstderr, catgets(catd, 2, 15,
|
|
"-%d"), (int) (dol - zero));
|
|
bad++;
|
|
}
|
|
if (bad)
|
|
xfprintf(xstderr, catgets(catd, 2, 16, "]"));
|
|
}
|
|
|
|
int cntch, cntln, cntodd, cntnull;
|
|
|
|
/*
|
|
* Following routines stolen mercilessly from ex.
|
|
*/
|
|
void
|
|
putfile(int unused)
|
|
{
|
|
line *a1;
|
|
register char *fp, *lp;
|
|
register int nib;
|
|
|
|
a1 = addr1;
|
|
clrstats();
|
|
cntln = addr2 - a1 + 1;
|
|
if (cntln == 0)
|
|
return;
|
|
nib = BUFSIZ;
|
|
fp = genbuf;
|
|
do {
|
|
getline(*a1++);
|
|
lp = linebuf;
|
|
for (;;) {
|
|
if (--nib < 0) {
|
|
nib = fp - genbuf;
|
|
if (write(io, genbuf, nib) != nib)
|
|
wrerror();
|
|
cntch += nib;
|
|
nib = MAXBSIZE - 1 /* 511 */;
|
|
fp = genbuf;
|
|
}
|
|
if ((*fp++ = *lp++) == 0) {
|
|
fp[-1] = '\n';
|
|
break;
|
|
}
|
|
}
|
|
} while (a1 <= addr2);
|
|
nib = fp - genbuf;
|
|
if (write(io, genbuf, nib) != nib)
|
|
wrerror();
|
|
cntch += nib;
|
|
}
|
|
|
|
void
|
|
wrerror(void)
|
|
{
|
|
|
|
syserror();
|
|
}
|
|
|
|
void
|
|
clrstats(void)
|
|
{
|
|
|
|
ninbuf = 0;
|
|
cntch = 0;
|
|
cntln = 0;
|
|
cntnull = 0;
|
|
cntodd = 0;
|
|
}
|
|
|
|
#define READ 0
|
|
#define WRITE 1
|
|
|
|
void
|
|
getline(line tl)
|
|
{
|
|
register char *bp, *lp;
|
|
register int nl;
|
|
|
|
lp = linebuf;
|
|
bp = getblock(tl, READ);
|
|
nl = nleft;
|
|
tl &= ~OFFMSK;
|
|
while (*lp++ = *bp++)
|
|
if (--nl == 0) {
|
|
bp = getblock(tl += INCRMT, READ);
|
|
nl = nleft;
|
|
}
|
|
}
|
|
|
|
char *
|
|
getblock(line atl, int iof)
|
|
{
|
|
register bbloc bno, off;
|
|
|
|
bno = (atl >> OFFBTS) & BLKMSK;
|
|
off = (atl << SHFT) & LBTMSK;
|
|
if (bno >= NMBLKS)
|
|
error(catgets(catd, 2, 17, " Tmp file too large"));
|
|
nleft = BUFSIZ - off;
|
|
if (bno == iblock) {
|
|
ichanged |= iof;
|
|
return (ibuff + off);
|
|
}
|
|
if (bno == oblock)
|
|
return (obuff + off);
|
|
if (iof == READ) {
|
|
if (ichanged)
|
|
blkio(iblock, ibuff, (ssize_t(*)())write);
|
|
ichanged = 0;
|
|
iblock = bno;
|
|
blkio(bno, ibuff, (ssize_t(*)())read);
|
|
return (ibuff + off);
|
|
}
|
|
if (oblock >= 0)
|
|
blkio(oblock, obuff, (ssize_t(*)())write);
|
|
oblock = bno;
|
|
return (obuff + off);
|
|
}
|
|
|
|
void
|
|
blkio(bloc b, char *buf, ssize_t (*iofcn)(int, void *, size_t))
|
|
{
|
|
|
|
lseek(tfile, (off_t) ((b & BLKMSK) * BUFSIZ), SEEK_SET);
|
|
if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
|
|
syserror();
|
|
}
|
|
|
|
void
|
|
syserror(void)
|
|
{
|
|
|
|
dirtcnt = 0;
|
|
write(2, " ", 1);
|
|
error("%s", strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Must avoid stdio because expreserve uses sbrk to do memory
|
|
* allocation and stdio uses malloc.
|
|
*/
|
|
/*
|
|
* I do not know whether vsprintf() uses malloc() or not.
|
|
* So this may be fail, too.
|
|
*/
|
|
void
|
|
xvfprintf(xFILE *fp, char *fmt, va_list ap)
|
|
{
|
|
char buf[BUFSIZ];
|
|
|
|
if (fp != xstderr)
|
|
return;
|
|
vsprintf(buf, fmt, ap);
|
|
write(2, buf, strlen(buf));
|
|
}
|
|
|
|
void
|
|
xfprintf(xFILE *fp, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (fp != xstderr)
|
|
return;
|
|
va_start(ap, fmt);
|
|
xvfprintf(fp, fmt, ap);
|
|
va_end(ap);
|
|
}
|