[comp.sources.unix] v17i097: E, friendlier front-end to vi, Part03/03

rsalz@uunet.uu.net (Rich Salz) (02/10/89)

Submitted-by: speedboat jones <tcjones@watdragon.waterloo.edu>
Posting-number: Volume 17, Issue 97
Archive-name: e2/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 3)."
# Contents:  do_vi.c e.1 e.c read_hist.c
# Wrapped by tcjones@watdragon on Thu Feb  9 10:01:03 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'do_vi.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'do_vi.c'\"
else
echo shar: Extracting \"'do_vi.c'\" \(4970 characters\)
sed "s/^X//" >'do_vi.c' <<'END_OF_FILE'
X#include "e.h"
X
X/* 
X * do_vi()
X *
X * Split the arguments (if any) in 'thing' up to be passed to execvp,
X * and exec vi on them. The arguments in 'thing' are space separated.
X * Just walk down the 'thing' array, zero the spaces and set pointers 
X * to the arguments.
X *
X */
X
void
do_vi(thing)
char *thing;
X{
X    char *args[MAX_ARGS];
X    register int i = 1;
X	int go_home = 0;
X
X    args[0] = "vi";
X	skip_white(thing);
X
X	while (*thing){
X		register char *tmp = thing;
X		skip_to_white(tmp);
X		if (*tmp) *tmp++ = '\0';
X		skip_white(tmp);
X
X		if (i > 1 || (*thing != '-' && *thing != '+')){
X			if (safety_first(thing)){
X				/*
X				 * Have to go home for this one.
X				 * Grab some space to put the new filename into.
X				 * The filename is to be cwd/thing.
X				 *
X				 */
X				char *space = sbrk(MAXPATHLEN + 1);
X				go_home = 1;
X				if (space == (char *)-1){
X					e_error("Could not sbrk.");
X				}
X				sprintf(space, "%s/%s", cwd, thing);
X				thing = space;
X			}
X		}
X		args[i++] = thing;
X		thing = tmp;
X	}
X
X    args[i] = NULL;
X
X	if (go_home && chdir(home) == -1){
X		e_error("Could not chdir to '%s'.", home);
X	}
X        
X    if (execvp(VI, args) == -1){
X        e_error("Could not execvp '%s'", VI);
X    }
X}
X
X
int
safety_first(s)
char *s;
X{
X	register char *slash;
X	struct stat sbuf;
X	char away_dir[MAXPATHLEN];
X	int away;
X	int own;
X
X	/*
X	 * If they are not concerned with inheritance, just return.
X	 *
X	 */
X	if (!safe_inherit && !inherit){
X		return 0;
X	}
X
X	/*
X	 * See if there is a .exrc file in the directory in which the file we
X	 * are going to be editing lives.
X	 */
X	slash = rindex(s, '/');
X
X	if (slash){
X		/*
X		 * The file we are editing lies (probably) in a directory that is 
X		 * not the one we are in. Check it by inode numbers though.
X		 *
X		 */
X		char tmp[MAXPATHLEN];
X		ino_t here_ino;
X
X		*slash = '\0';
X		ok_sprintf(away_dir, "%s", s);
X		*slash = '/';
X		ok_sprintf(tmp, "%s/.exrc", away_dir);
X
X		if (stat(tmp, &sbuf) == -1){
X			/*
X			 * There is no .exrc file.
X			 *
X			 */
X			return 0;
X		}
X		
X		/*
X		 * There is a .exrc file.
X		 *
X		 */
X		if (uid == sbuf.st_uid){
X			/*
X			 * And we own it.
X			 *
X			 */
X			away = 1;
X			own = 1;
X		}
X		else{
X			/*
X			 * We don't own the .exrc file. 
X			 *
X			 */
X			own = 0;
X		}
X
X		/*
X		 * Decide whether or not the file we are going to edit is in
X		 * this directory. We may have been deceived by the presence of the
X		 * '/' since the filename could be ../this_dir etc etc.
X		 *
X		 */
X
X		if (stat(cwd, &sbuf) == -1){
X			e_error("Cannot stat '%s'.", cwd);
X		}
X		here_ino = sbuf.st_ino;
X
X		if (stat(away_dir, &sbuf) == -1){
X			e_error("Cannot stat '%s'.", away_dir);
X		}
X
X		if (sbuf.st_ino == here_ino){
X			 away = 0;
X		}
X		else{
X			away = 1;
X		}
X	}
X	else{
X		/*
X		 * The file is in this directory.
X		 *
X		 */
X
X		/*
X		 * We know we are away, there was no '/' in the filename.
X		 * The possibility of the filename being a link is excluded I
X		 * think, because earlier on we checked for regular files.
X		 * I'll check this again. In any case no harm could come as
X		 * at worst we would attempt to vi a directory.
X		 *
X		 */
X		away = 0;
X
X		if (stat(".exrc", &sbuf) == -1){
X			/*
X			 * No .exrc file.
X			 *
X			 */
X			return 0;
X		}
X
X		if (uid == sbuf.st_uid){
X			own = 1;
X		}
X		else{
X			own = 0;
X		}
X	}
X
X	/*
X	 * We have now set up 'away' and 'own'.
X	 *
X	 * away = 1  =>  The file we are editing is not in our current directory.
X	 * away = 0  =>  The file we are editing is in our current directory.
X	 *
X	 * own = 1   =>  We own the .exrc file.
X	 * own = 0   =>  We do not own the .exrc file.
X	 *
X	 *
X	 */
X
X	/*
X	 * It's in this directory and we own it. So just return and pretend
X	 * to be a normal vi :-)
X	 *
X	 */
X	if (!away && own){
X		return 0;
X	}
X
X	/*
X	 * It's in this directory and we don't own it and we don't want to inherit
X	 * it. 
X	 *
X	 * Return 1 to indicate that the filename that needs to be edited 
X	 * should now be preceeded by 'cwd/'. And that we need to chdir to home
X	 * before we exec vi.
X	 *
X	 */
X	if (!away && !own && safe_inherit){
X		return 1;
X	}
X
X	/*
X	 * The other option is that it's in this directory and we don't own
X	 * it and either 1) inherit is set or 2) inherit is not set AND neither
X	 * is safe_inherit. In either case we are going to inherit the thing.
X	 *
X	 */
X
X	 /*
X	  * Now to the cases where the file we are going to edit is not in
X	  * this directory.
X	  *
X	  */
X
X	/*
X	 * It's not in this directory and we own it and we want to inherit. 
X	 * So chdir over there before returning.
X	 *
X	 */
X	if (away && own && inherit){
X		if (chdir(away_dir) == -1){
X			e_error("Could not chdir to '%s'.", away_dir);
X		}
X		return 0;
X	}
X
X	/*
X	 * It's not in this directory and we don't own it and we want to inherit
X	 * even if it's not safe. So chdir over there before returning.
X	 *
X	 */
X	if (away && !own && inherit && !safe_inherit){
X		if (chdir(away_dir) == -1){
X			e_error("Could not chdir to '%s'.", away_dir);
X		}
X		return 0;
X	}
X
X	/*
X	 * And I think that's about it. Time will tell.
X	 *
X	 */
X	
X	return 0;
X}
END_OF_FILE
if test 4970 -ne `wc -c <'do_vi.c'`; then
    echo shar: \"'do_vi.c'\" unpacked with wrong size!
fi
# end of 'do_vi.c'
fi
if test -f 'e.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'e.1'\"
else
echo shar: Extracting \"'e.1'\" \(9576 characters\)
sed "s/^X//" >'e.1' <<'END_OF_FILE'
X.TH E P "November 16, 1988"
X.SH NAME
e \- command line preprocessor for the vi(1) editor. 
X.SH SYNOPSIS
X.B e
X[
X.B option
X] [
X.B file
X] ...
X.SH DESCRIPTION
X.I e 
is an interface to 
X.IR vi (1)
that maintains a history of the most recently 
X.IR e 'ed
files for each directory. 
Using the history, it is both fast and simple to re-edit
previously 
X.IR e 'ed
files.
X.PP
In addition, 
X.I e
does spelling corrections on its arguments,
fast lookup and editing of files in other directories, and allows for
the inheritance of .exrc
files in other directories. It also closes the modeline security hole,
making it possible to ensure that no .exrc file 
that you don't own is ever seen by 
X.IR vi .
X.PP
X.SH INVOCATION
The invocation syntax is (almost) a superset of that of 
X.IR vi .
A list of the interesting command line variations is given below.
X[x] indicates that "x" is optional. "cmd" means an
X.IR ex (1)
command.
X.TP 18
X.B e           
Invokes 
X.I vi 
on the last file that was 
X.IR e 'ed
in this directory.
X.TP 18
X.B e \-
Prints the history for this directory and allows fast selection
of a previous file - or a new one.
X.TP 18
X.B e .
Prints the history for this directory without asking for input.
X.TP 18
X.B e \-pat
X.IR vi 's 
the last file that was 
X.IR e 'ed
with the string "pat" on
the command line.
X.TP 18
X.B e +[cmd]
X.IR vi 's 
the last file that was 
X.IR e 'ed
in this directory, but executes
command "cmd" on entering
X.IR vi .
X.TP 18
X.B e [+[cmd]] file  
X.IR vi 's 
the file and adds it to the history list. Minor spelling
corrections are suggested if "file" does not exist but is close 
X(in spelling) to some file that does.
X.TP 18
X.B e [+[cmd]] files 
X.IR vi 's 
the files and adds them as a single entry to the history.
X.PP
The 
X.IR vi (1)
options \-r, \-v, \-x, \-w and \-R are all passed as is to 
X.I vi
and so behave as would be expected.
X.SH EXAMPLE
Assume here that you are in the directory /tmp/mydir and that it
contains (only) the files "death", "main.c", "pete.c", "bigmac"
and "fries"; and that your 
X.I e 
history file contains
X.PP
X.in 0.5i
X.B /tmp/mydir
X.in 1.0i
X.B death
X.br
X.B +/main.c pete.c
X.br
X.B bigmac
X.br
X.B fries
X.br
X.PP
X"\fBe .\fR" will show you the history...
X.PP
X.in 1.0i
X.B [3]: death
X.br
X.B [2]: +/main pete.c
X.br
X.B [1]: bigmac
X.br
X.B [0]: fries
X.br
X.PP
X"\fBe\fR" by itself will get you immediately back into "fries".
X.PP
X"\fBe \-\fR" will present the same as shown for "\fBe .\fR",
but will ask for a number or another filename.
Carriage return is equivalent to zero (i.e. the last filename),
backspace or interrupt will quit.
X.PP
X"\fBe -2\fR" will get you immediately into "pete.c", searching for
X"main" on entering 
X.IR vi .
X.PP
X"\fBe \-ath\fR" searches for the pattern "ath", finds it in "death",
and that's what you get.
X.PP
X"\fBe +10\fR"
will get you into "fries" on line 10 (assuming it has 10 lines).
X.SH "THE HISTORY"
A single history file is kept. Its default location is $HOME/.e but this
can be changed with the VIHIST environment variable.
The history is rearranged with each use to place the last
X.IR e 'ed
file at the end of the list. Duplicate entries are removed.
The number of history items kept for each directory is 8. Seeing as the
list is ordered by use, this number is ample.
X.PP
The history file consists of entries composed of a directory name followed by
lines of file names (oldest to newest). Each filename is preceded by a 
single TAB character. The directory names are all absolute pathnames.
X.SH "FILENAME CORRECTION"
If you make a simple spelling mistake when typing a file name,
X.I e
will detect it, ask whether it was really what you meant, and offer
suggestions.
XErrors that are detected are: wrong character, omitted character, 
interchanged characters and extra character.
X.PP
In the above example,
X"\fBe bigamc\fR"
will prompt you with 
X.br
X.in 1.0i
X"correct to bigmac [y]?"
X.PP
Answering "n" will not do the correction, "Q" or "q" will quit.
If there is more than one possible correction, 
you will be prompted for each in
turn. A response of "N" means that you really want what you typed, and no
further corrections will be offered.
Any other response (e.g. a RETURN) will do the correction for you.
X.SH "CROSS-DIRECTORY EDITING"
The environment variable VIPATH can be used to enable fast lookup and 
editing of files
in different directories. It should contain a list of directories that you want
searched when 
X.I e
cannot find the file you request in the current
directory. Here is an example:
X.PP
Suppose you are in the directory /bin with VIPATH set to /usr/include/sys
X.PP
X"\fBe inode.h\fR"
will prompt you with 
X.br
X.in 1.0i
X"/usr/include/sys/inode.h [y]?"
X.PP
since /bin/inode.h does not exist, but /usr/include/sys/inode.h does.
You can say "n" or "q" to reject or quit. 
X"N" will reject the current suggestion and
searching for further ones will be stopped.
Any other response (e.g. a RETURN) is taken as a yes.
X.PP
It is handy to have $HOME in your VIPATH, this gives you easy access
from anywhere to commonly used files.
X.PP
If you accept a suggestion, then the file is put into the history. 
Spelling corrections are not suggested across directories.
There is (of course) no need to put "." in your VIPATH. Doing so will just slow
things down and cannot possibly be of help.
This should be clear, "." is always searched first for the given filename.
Putting it into your VIPATH will have it searched twice. The directory names
in VIPATH may be separated by white space (including newlines) and colons.
X.SH ".exrc INHERITANCE"
If you set the environment variable "VIINHERIT", 
X.I e
will look for 
a .exrc file in the directory where the (first) file you are about to edit 
resides. If it finds
one, it arranges to read it as though it were in the directory from which you
issued the 
X.I e
command. This is very useful as many people 
have specialised .exrc files in their directory trees. 
X.PP
XFor instance, in a directory of C source
you might like to set autoindent and showmatch, whereas in a directory that
contained correspondence, you might want neither of these, but wrapmargin and
ignorecase instead. Inherited .exrc files allow you to 
X.I e
files in other
directories and get the results intended by the .exrc files in them.
X.PP
There is a drawback however. 
X.I vi
has an option called "modeline"
which makes it possible for a malicious user to leave a trojan-horse type file
in a directory with a specially prepared .exrc file that turns on "modeline".
As a result, if you cd to that directory and 
X.I vi
the wrong file, then the modeline feature allows the other user to execute
commands as you. Not nice.
X.PP
If you set "VISAFEINHERIT", 
X.I e
will make sure that you never get caught by
this one. In short, it notices when this could happen and changes directory
to avoid the .exrc. There is no need to have "VIINHERIT" set if all you want to
do is avoid the security problem. But setting them both gives you the best of
both worlds. (See the NOTES for the drawback...)
X.SH NOTES
When using "e -", the terminal is put into cbreak mode. If the first
character typed is a digit (in the acceptable range of history items)
then you will get that history item without further ado. Thus if you
have a file called 4play and you try to type "4play" from within an "e -", 
then you'll probably end up in the wrong place.
This is to say you'll get the file that was the 4th last in the history.
X.PP
The history length must be less than or equal to 9 (the code sets
it to 8 at present). 
The problem with having more is that with "e -" you go into cbreak and the 
first digit entered (say 
X.I n
X) is taken to mean 
X"I want the 
X.IR n th 
last file". This saves the need for hitting return, but also means that
two digit numbers can't be done.
X.PP
When "VISAFEINHERIT" is set and your command would have resulted in 
X.I vi
going through a foreign .exrc, 
X.I e
will change the name
of the file you want to its full path name, and change directory underneath
you to your home directory. For example, if user joe says "e file" in /tmp and
user mary owns /tmp/.exrc, then the result will be as though joe had typed "e
X/tmp/file" from his home directory. Of course when he exits 
X.I vi 
he will still 
be in /tmp. Perhaps this could be considered a bug. If you don't like it, 
you can live with the modeline problem.
X.PP
The same problem with the directory changing underneath you happens when
X.I e
inherits a .exrc for you (via "VIINHERIT") \- you get changed to that 
directory while you are in vi.
X.SH FILES
X$HOME/.e \- the default e history file.
X.SH "ENVIRONMENT VARIABLES"
VIHIST \- The location used by e for the history.
X.br
VIPATH \- Search directories.
X.br
VIINHERIT \- If set,
X.I e
tries to inherit .exrc files.
X.br
VISAFEINHERIT \- Ensures you don't inadvertently go through someone
else's .exrc file.
X.SH BUGS
The first character on a select line cannot be backspaced over.
X.br
The
X.I vi
option "\-" is not available. I should have chosen another letter.
X.SH "SEE ALSO"
ex(1), edit(1).
X.SH AUTHOR
Terry Jones
X.br
Department of Computer Science
X.br
University of Waterloo
X
X.\"
X.\" Man page for e. Version 1.3
X.\"
X.\"    ------------------------------------------------------------------------
X.\"    Terry Jones, Department Of Computer Science, University Of Waterloo
X.\"         Waterloo Ontario Canada N2L 3G1
X.\"
X.\"    {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones
X.\"    tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet
X.\"    tcjones%watdragon@waterloo.csnet 
X.\"    -------------------------------------------------------------------------
X.\"
END_OF_FILE
if test 9576 -ne `wc -c <'e.1'`; then
    echo shar: \"'e.1'\" unpacked with wrong size!
fi
chmod +x 'e.1'
# end of 'e.1'
fi
if test -f 'e.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'e.c'\"
else
echo shar: Extracting \"'e.c'\" \(5750 characters\)
sed "s/^X//" >'e.c' <<'END_OF_FILE'
X#include "e.h"
X
void
e(c, v)
int c;
char **v;
X{
X    /*
X     * Process the command line. This gets a little messy as there are so
X     * many ways e can be invoked. They are listed below and there is an
X     * example provided in each of the switch cases to illustrate the 
X     * particular one we are trying to handle.
X     *
X     * The idea in most cases is to get the arguments that will be passed
X     * to vi into a character array (arg), and pass it to do_vi(). do_vi()
X     * splits up the arguments and execs vi. Occasionally it is simpler and
X     * do_vi() can be called more directly.
X     *
X     *
X     * Command Line Options.
X     * =====================
X     *
X     * No arguments.
X     *
X     *     (1) "e"
X     *
X     * One argument.
X     *
X     *     (2) "e -"
X     *     (3) "e -#"                # is the number of some history item.
X     *     (4) "e -r"
X     *     (5) "e -pat"              pat is a search pattern.
X     *     (6) "e +100"
X     *     (7) "e ."
X     *     (8) "e <filename > "
X     *
X     * Multiple arguments.
X     *
X     *     (9) "e fred harry joe"   Also handles "e -t tag", "e -r file" etc.
X     *
X     */
X
X
X    switch (c){
X        case 1: {
X
X            /* 
X             * Command line option (1).
X             * Example: "e"
X             *
X             * Just go and vi the last file that was e'ed.
X             *
X             */
X
X            check_hist();
X            abandon();
X            do_vi(hist[hist_count - 1]);
X            break;
X        }
X        
X        case 2:{
X            switch ((*++v)[0]){
X
X                case '-':{
X
X                    if ((c = (*v)[1]) == '\0'){
X
X                        /* 
X                         * Command line option (2).
X                         * Example: "e -"
X                         *
X                         * This is a select from history, ask what they want.
X                         *
X                         */
X
X                        check_hist();
X                        ask_hist();
X                        do_vi(arg);
X                    }
X                    else if (isdigit(c)){
X
X                        /* 
X                         * Command line option (3).
X                         * Example: "e -3"
X                         *
X                         * Get the nth last file from the history and vi it.
X                         *
X                         */
X
X                        check_hist();
X                        nth_hist(c-'0');
X                        do_vi(arg);
X                    }
X                    else if (c == 'r' && (*v)[2] == '\0'){
X
X                        /* 
X                         * Command line option (4).
X                         * Example: "e -r"
X                         *
X                         * A recover, just pass it to vi and don't interfere.
X                         *
X                         */
X
X                        do_vi(*v);
X                    }
X                    else{
X
X                        /* 
X                         * Command line option (5).
X                         * Example: "e -pat"
X                         *
X                         * This is a pattern - try to match it.
X                         *
X                         */
X
X                        check_hist();
X                        find_match(++*v);
X                        do_vi(arg);
X                    }
X                    break;
X                }
X
X                case '+':{
X
X                    /* 
X                     * Command line option (6).
X                     * Example: "e +100"
X                     *
X                     * A command, put it before the last file name.
X                     *
X                     */
X
X                    check_hist();
X                    insert_cmd(*v);
X                    do_vi(arg);
X                    break;
X                }
X
X                case '.':{
X
X                    /* 
X                     * Command line option (7).
X                     * Example: "e ."
X                     * Example: "e .login"  (falls through to option (8)).
X                     *
X                     * Just give a history list if there is only a dot.
X                     * Otherwise fall through as it must be a filename.
X                     *
X                     */
X
X                    if ((*v)[1] == '\0'){
X                        register i;
X
X                        check_hist();
X                        for (i = 0; i < hist_count; i++){
X                            ok_fprintf(stderr, "\t[%d]: %s\n",
X                                hist_count - i - 1, hist[i]);
X                        }
X                        abandon();
X                    }
X                    /* 
X                     * The switch falls through in the case where there is a
X                     * filename that starts with a period.
X                     *
X                     */
X
X                }
X                /* FALLTHROUGH */
X
X                default :{
X
X                    /* 
X                     * Command line option (8).
X                     * Example: "e fred"
X                     * Example: "e .login"  (fell through from option (8)).
X                     *
X                     * Looks like it's just a plain old file name. vi it!
X                     *
X                     */
X
X                    normal(*v);
X                    do_vi(arg);
X                    break;
X                }
X            }
X            break;
X        }
X
X        default:{
X
X            /* 
X             * Command line option (9).
X             * Example: "e fred harry joe"
X             *
X             * A bunch of arguments, fix the history & vi them all as normal.
X             *
X             */
X
X            multiple(c, v, ARG_CHARS);
X            do_vi(arg);
X            break;
X        }
X    }
X}
END_OF_FILE
if test 5750 -ne `wc -c <'e.c'`; then
    echo shar: \"'e.c'\" unpacked with wrong size!
fi
# end of 'e.c'
fi
if test -f 'read_hist.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'read_hist.c'\"
else
echo shar: Extracting \"'read_hist.c'\" \(6117 characters\)
sed "s/^X//" >'read_hist.c' <<'END_OF_FILE'
X#include "e.h"
X
X/*
X * read_hist()
X *
X * Do some initialization type things such as finding out if the user has a
X * uid, password entry, home directory etc. Then find out where the history
X * is kept and check to see if it's there. If it's not just indicate this by
X * returning -1 and let other functions deal with this problem as they see
X * fit.
X *
X * If the history exists then read it. The format of the history file is
X *
X
X/absolute/path/name/for/some/directory
X\tfilename_1
X\tfilename_2
X\tfilename_3
X\tfilename_4
X/absolute/path/of/some/other/directory
X\tsome_filename_or_other
X\tanother_filename
X
X *
X * Where \t is of course a single TAB.
X * The files for a directory are kept in least recently used order. So in
X * the above example, 'filename_1' was used before 'filename_2' and so on.
X * There may be a maximum of HIST_LINES file names associated with each 
X * directory.
X *
X * If the history is used and needs to be updated, then we will have to create
X * a new file that looks pretty much like the current one. Unfortunately we
X * will have to read and write the whole of the old file.
X *
X * Seeing as we are going to have to write the new history file, it doesn't
X * make much sense to try and find the relevant directory in a clever manner
X * (e.g. by keeping the history sorted by directory). The point is that
X * we HAVE to read and write the whole file once so it doesn't really matter
X * where we encounter the directory we want (if at all).
X *
X *
X * We return the number of history items found after setting the pointers in
X * hist[] to point to them. hist[0] points to the oldest filename, hist[1]
X * to the next oldest etc etc... hist[hist_count - 1] to the most recently
X * used.
X *
X */
X
int
read_hist()
X{
X    static char line[MAXPATHLEN]; /* Static, as saved_line may point at it. */
X    int h_index = 0;              /* # of history items found so far.       */
X    register int cwd_len;         /* # of chars in the current dir name.    */
X    struct stat sbuf;
X
X    cwd_len = strlen(cwd);
X
X    if (stat(ehist, &sbuf) == -1){
X        return -1;
X    }
X
X    emode = (int)sbuf.st_mode;
X
X    hist_fp = fopen(ehist, "r");
X    if (!hist_fp){
X        e_error("Could not open history '%s'.", ehist);
X    }
X
X    /*
X     * Open a new history file that will renamed to the old one once
X     * we update. (If we update.)
X     *
X     */
X
X    get_temp();
X
X	/*
X	 * Make sure that the first line of the history starts with a '/'
X	 * This way we know we are not dealing with an old-style (pre version 1.3)
X	 * history file. The error message here is unsatisfactory - it should make
X	 * a better attempt to find out what's wrong and point them to the 
X	 * script for converting to the new format etc etc. Fortunately I'm the
X	 * only person in the world who uses e and so it's not a problem.
X	 *
X	 */
X
X    if (!fgets(line, MAXPATHLEN, hist_fp)){
X		e_error("Could not read first line of '%s'.\n", ehist);
X	}
X
X	if (line[0] != '/'){
X		e_error("The history (%s) is munged. It is not in version %s format.",
X			ehist, VERSION);
X	}
X
X    ok_fprintf(tmp_fp, "%s", line);
X
X    /*
X     * Get lines and write them to the new history until you see one that 
X     * matches the directory we are in.
X     *
X     */
X    while (fgets(line, MAXPATHLEN, hist_fp)){
X
X        register char *tmp = line;
X        int room_left = HIST_LINES * MAXPATHLEN;
X        char *room;
X
X        /* 
X         * If it's not a path starting with a '/' then continue. 
X         * We have a filename. Keep going until the next directory entry.
X         *
X         */
X        if (line[0] != '/'){
X            ok_fprintf(tmp_fp, "%s", line);
X            continue;
X        }
X
X        zap_nl(tmp);
X
X        /*
X         * Test to see if this is the directory we want. tmp - line is the
X         * length of the name of the found directory. Do a length test first
X		 * up to avoid a strcmp if possible.
X         *
X         */
X        if (tmp - line != cwd_len || strcmp(line, cwd)){
X            ok_fprintf(tmp_fp, "%s\n", line);
X            continue;
X        }
X
X        /* 
X         * So we have found the directory we were looking for. Allocate a
X         * hunk of space to read all the filenames. 
X         */
X        room = sbrk(room_left);
X        if (room == (char *)-1){
X            e_error("Could not sbrk().");
X        }
X
X        /*
X         * Read the filenames. If we reach a line that starts with a '/'
X         * then we are onto the next directory in the history and we save
X         * a pointer to this line to emit it later.
X         *
X         */
X        while (fgets(room, room_left, hist_fp)){
X            char *cp = hist[h_index] = room + 1;
X            zap_nl(cp);
X            room_left -= (cp - room);
X
X            /*
X             * In theory we should never run out of space, but in case we do
X             * we might as well try to grab some more rather than just dying.
X             *
X             */
X            if (room_left <= MAXPATHLEN){
X                room_left = HIST_LINES * MAXPATHLEN;
X                room = sbrk(room_left);
X                if (room == (char *)-1){
X                    e_error("Could not sbrk().");
X                }
X            }
X            
X            if (*room == '/'){
X                saved_line = room; /* Read one too many! */
X                return h_index;
X            }
X
X            room = cp + 1;
X            h_index++;
X
X            /*
X             * Check to see if we have read HIST_LINES filenames. If we have,
X             * make sure that the next line is in fact a directory (by
X             * verifying that it starts with a '/'.
X             *
X             */
X            if (h_index == HIST_LINES){
X                if (fgets(line, MAXPATHLEN, hist_fp)){
X                    if (line[0] != '/'){
X                        e_error("History contains too many entries!");
X                    }
X                    else{
X                        tmp = line;
X                        zap_nl(tmp);
X                        saved_line = line;
X                    }
X                }
X                return HIST_LINES;
X            }
X        }
X        return h_index;
X    }
X    return h_index;
X}
END_OF_FILE
if test 6117 -ne `wc -c <'read_hist.c'`; then
    echo shar: \"'read_hist.c'\" unpacked with wrong size!
fi
# end of 'read_hist.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.