bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

direxec patch


From: Kevin Pulo
Subject: direxec patch
Date: Mon, 1 Oct 2001 22:10:49 +1000

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. |
| kev@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 via email to

[Prev in Thread] Current Thread [Next in Thread]