direxec patch

2 views
Skip to first unread message

Kevin Pulo

unread,
Oct 1, 2001, 8:10:49 AM10/1/01
to
Hi all,

Here's a patch for my 'direxec' feature for bash 2.05. This feature
lets you "execute" a directory by defining a function called 'direxec'
which takes the directory (and other args) as its arguments. When
this function isn't defined, the behaviour is the same as before (ie.
a "is a directory" or "command not found" error). When it is defined,
typing something like

$ /tmp foo bar

is equivalent to

$ direxec /tmp foo bar


If there's a directory with the same name as some legitimate command
(eg. 'ls'), then the command will be run rather than direxec - that
is, direxec only runs when when an error would otherwise occur (ie.
"is a directory" or "command not found").


I find it's very handy to have direxec defined as

function direxec { cd "$1"; shift; ls "$@"; }

This lets me navigate around the filesystem by just typing
directories, rather than repeated cd and ls commands. For example,

$ dl
$ incoming
$ gnu
$ bash

instead of

$ cd dl
$ ls
$ cd incoming
$ ls
$ cd gnu
$ ls
$ cd bash
$ ls


It also means I can do

$ /tmp -lat

instead of

$ cd /tmp
$ ls -lat


This is my first play with the bash source, so there could still be
bugs. It turned out to be harder and more complicated than I
originally thought, and changed structure several times. I think I
have the forking and piping working (eg. "/tmp -lat | head").
Feedback is of course appreciated.

Bye 4 now,
Kev.

--- execute_cmd.c.orig Fri Mar 23 02:17:23 2001
+++ execute_cmd.c Mon Oct 1 21:51:00 2001
@@ -145,7 +145,7 @@
static int execute_builtin_or_function ();
static int builtin_status ();
static void execute_subshell_builtin_or_function ();
-static void execute_disk_command ();
+static int execute_disk_command ();
static int execute_connection ();
static int execute_intern_function ();

@@ -154,6 +154,10 @@
/* The line number that the currently executing function starts on. */
static int function_line_number;

+/* The name of the function which, if set, will be run when a directory
+ is "executed". */
+static char direxec[] = "direxec";
+
/* Set to 1 if fd 0 was the subject of redirection to a subshell. Global
so that reader_loop can set it to zero before executing a command. */
int stdin_redir;
@@ -2601,6 +2605,8 @@
last_shell_builtin = this_shell_builtin;
this_shell_builtin = builtin;

+ runfunc:
+
if (builtin || func)
{
if (already_forked)
@@ -2659,9 +2665,16 @@
if (command_line == 0)
command_line = savestring (the_printed_command);

- execute_disk_command (words, simple_command->redirects, command_line,
- pipe_in, pipe_out, async, fds_to_close,
- simple_command->flags);
+ if (!execute_disk_command (words, simple_command->redirects,
+ command_line, pipe_in, pipe_out, async,
+ fds_to_close, simple_command->flags))
+ {
+ /* This means that we should run direxec, so prepend 'direxec' to
+ the words and set func, then jump back and run the function. */
+ words = make_word_list (make_word (direxec), words);
+ func = find_function (direxec);
+ goto runfunc;
+ }

return_result:
bind_lastarg (lastarg);
@@ -3103,7 +3116,7 @@
in the parent. This is probably why the Bourne style shells
don't handle it, since that would require them to go through
this gnarly hair, for no good reason. */
-static void
+static int
execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
async, fds_to_close, cmdflags)
WORD_LIST *words;
@@ -3116,7 +3129,11 @@
char *pathname, *command, **args;
int nofork;
pid_t pid;
+ int maybedir;
+ struct stat finfo;
+ int result;

+ result = 1;
nofork = (cmdflags & CMD_NO_FORK); /* Don't fork, just exec, if no pipes */
pathname = words->word->word;

@@ -3132,6 +3149,24 @@

command = search_for_command (pathname);

+ /* If search_for_command() found a valid command, use that rather than
+ pathname. This means that when you type 'ls' where there's a directory
+ called 'ls', command will be '/bin/ls', which isn't a directory, and so
+ maybedir will be false, and we'll fork to run /bin/ls. If you check for
+ 'ls' (ie. pathname) being a directory rather than '/bin/ls' (ie.
+ command), then maybedir will be true and so we won't fork to run
+ /bin/ls, which is obviously bad news. */
+ if (command)
+ {
+ maybedir = (find_function (direxec) &&
+ (stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)));
+ }
+ else
+ {
+ maybedir = (find_function (direxec) &&
+ (stat (pathname, &finfo) == 0) && (S_ISDIR (finfo.st_mode)));
+ }
+
if (command)
{
maybe_make_export_env ();
@@ -3142,7 +3177,10 @@
of COMMAND, since we want the error messages to be redirected. */
/* If we can get away without forking and there are no pipes to deal with,
don't bother to fork, just directly exec the command. */
- if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
+ /* If this command might be a directory (which requires running the
+ 'direxec' function in this process), then we purposely don't fork.
+ In this case, we avoid exit()ing after calling shell_execve(). */
+ if ((nofork || maybedir) && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
pid = 0;
else
pid = make_child (savestring (command_line), async);
@@ -3207,15 +3245,40 @@

if (command == 0)
{
- internal_error ("%s: command not found", pathname);
- exit (EX_NOTFOUND); /* Posix.2 says the exit status is 127 */
+ /* Having a null command might not be an error, since if maybedir
+ is true, then we should run direxec. */
+ if (!maybedir)
+ {
+ internal_error ("%s: command not found", pathname);
+ exit(EX_NOTFOUND); /* Posix.2 says the exit status is 127 */
+ }
+ }
+ else
+ {
+ /* Execve expects the command name to be in args[0]. So we
+ leave it there, in the same format that the user used to
+ type it in. */
+ args = word_list_to_argv (words, 0, 0, (int *)NULL);
+ result = shell_execve (command, args, export_env);
+ }
+
+ if (maybedir)
+ {
+ /* Didn't fork, so don't exit(). */
+ /* If shell_execve failed and this might be a dir for execution,
+ then run the direxec function. This way typing 'ls' where
+ the subdir 'ls' exists will run the command 'ls' (ie. /bin/ls)
+ rather than running the direxec function. Typing 'ls/' runs
+ direxec though. */
+ /* So we return 1 to indicate to execute_simple_command that it
+ should run direxec however it needs to. */
+ result = 0;
+ }
+ else
+ {
+ /* Forked, so exit(). */
+ exit (result);
}
-
- /* Execve expects the command name to be in args[0]. So we
- leave it there, in the same format that the user used to
- type it in. */
- args = word_list_to_argv (words, 0, 0, (int *)NULL);
- exit (shell_execve (command, args, export_env));
}
else
{
@@ -3225,7 +3288,9 @@
unlink_fifo_list ();
#endif
FREE (command);
+ result = 1;
}
+ return (result);
}

#if !defined (HAVE_HASH_BANG_EXEC)
@@ -3401,7 +3466,14 @@
if (i != ENOEXEC)
{
if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
- internal_error ("%s: is a directory", command);
+ {
+ /* Suppress error message if we're going to run the direxec function
+ on returning to execute_disk_command() */
+ if (!find_function (direxec))
+ {
+ internal_error ("%s: is a directory", command);
+ }
+ }
else
{
#if defined (HAVE_HASH_BANG_EXEC)


--
----------------------------------------------------------------------.
| Kevin Pulo Quidquid latine dictum sit, altum viditur. |
| k...@pulo.com.au _ll l_ng__g_e_ _r_ hi__ly p__d_ct__le. |
| http://www.kev.pulo.com.au/ God casts the die, not the dice. |
`--------------- Linux: The choice of a GNU generation. ---------------'


Reply all
Reply to author
Forward
0 new messages