sh: Simplify cd-hash interaction.

Instead of rechecking relative paths for all hashed utilities after a cd,
track if any utility in cmdtable depends on a relative path in PATH.
If there is such a utility, cd clears the entire table.

As a result, the '*' in hash no longer happens.
This commit is contained in:
Jilles Tjoelker 2013-01-14 16:40:50 +00:00
parent eb84fc957c
commit 523646ee5c
2 changed files with 15 additions and 40 deletions

View File

@ -86,12 +86,12 @@ struct tblentry {
union param param; /* definition of builtin function */
int special; /* flag for special builtin commands */
signed char cmdtype; /* index identifying command */
char rehash; /* if set, cd done since entry created */
char cmdname[]; /* name of command */
};
static struct tblentry *cmdtable[CMDTABLESIZE];
static int cmdtable_cd = 0; /* cmdtable contains cd-dependent entries */
int exerrno = 0; /* Last exec error */
@ -305,8 +305,6 @@ printentry(struct tblentry *cmdp, int verbose)
error("internal error: cmdtype %d", cmdp->cmdtype);
#endif
}
if (cmdp->rehash)
out1c('*');
out1c('\n');
}
@ -323,12 +321,12 @@ find_command(const char *name, struct cmdentry *entry, int act,
{
struct tblentry *cmdp, loc_cmd;
int idx;
int prev;
char *fullname;
struct stat statb;
int e;
int i;
int spec;
int cd;
/* If name contains a slash, don't use the hash table */
if (strchr(name, '/') != NULL) {
@ -337,8 +335,10 @@ find_command(const char *name, struct cmdentry *entry, int act,
return;
}
cd = 0;
/* If name is in the table, and not invalidated by cd, we're done */
if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
if ((cmdp = cmdlookup(name, 0)) != NULL) {
if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
cmdp = NULL;
else
@ -359,13 +359,6 @@ find_command(const char *name, struct cmdentry *entry, int act,
}
/* We have to search path. */
prev = -1; /* where to start */
if (cmdp) { /* doing a rehash */
if (cmdp->cmdtype == CMDBUILTIN)
prev = -1;
else
prev = cmdp->param.index;
}
e = ENOENT;
idx = -1;
@ -380,13 +373,8 @@ find_command(const char *name, struct cmdentry *entry, int act,
goto loop; /* ignore unimplemented options */
}
}
/* if rehash, don't redo absolute path names */
if (fullname[0] == '/' && idx <= prev) {
if (idx < prev)
goto loop;
TRACE(("searchexec \"%s\": no change\n", name));
goto success;
}
if (fullname[0] != '/')
cd = 1;
if (stat(fullname, &statb) < 0) {
if (errno != ENOENT && errno != ENOTDIR)
e = errno;
@ -426,9 +414,6 @@ find_command(const char *name, struct cmdentry *entry, int act,
goto success;
}
/* We failed. If there was an entry for this command, delete it */
if (cmdp && cmdp->cmdtype != CMDFUNCTION)
delete_cmd_entry();
if (act & DO_ERR) {
if (e == ENOENT || e == ENOTDIR)
outfmt(out2, "%s: not found\n", name);
@ -440,7 +425,8 @@ find_command(const char *name, struct cmdentry *entry, int act,
return;
success:
cmdp->rehash = 0;
if (cd)
cmdtable_cd = 1;
entry->cmdtype = cmdp->cmdtype;
entry->u = cmdp->param;
entry->special = cmdp->special;
@ -469,22 +455,15 @@ find_builtin(const char *name, int *special)
/*
* Called when a cd is done. Marks all commands so the next time they
* are executed they will be rehashed.
* Called when a cd is done. If any entry in cmdtable depends on the current
* directory, simply clear cmdtable completely.
*/
void
hashcd(void)
{
struct tblentry **pp;
struct tblentry *cmdp;
for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
if (cmdp->cmdtype == CMDNORMAL)
cmdp->rehash = 1;
}
}
if (cmdtable_cd)
clearcmdentry();
}
@ -526,6 +505,7 @@ clearcmdentry(void)
}
}
}
cmdtable_cd = 0;
INTON;
}
@ -566,7 +546,6 @@ cmdlookup(const char *name, int add)
+ strlen(name) + 1);
cmdp->next = NULL;
cmdp->cmdtype = CMDUNKNOWN;
cmdp->rehash = 0;
strcpy(cmdp->cmdname, name);
INTON;
}

View File

@ -32,7 +32,7 @@
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
.\" $FreeBSD$
.\"
.Dd July 15, 2012
.Dd January 14, 2013
.Dt SH 1
.Os
.Sh NAME
@ -2219,10 +2219,6 @@ The shell maintains a hash table which remembers the locations of commands.
With no arguments whatsoever, the
.Ic hash
command prints out the contents of this table.
Entries which have not been looked at since the last
.Ic cd
command are marked with an asterisk;
it is possible for these entries to be invalid.
.Pp
With arguments, the
.Ic hash