bug-bash
[Top][All Lists]
Advanced

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

[PATCH] Add EXECIGNORE


From: Eric Blake
Subject: [PATCH] Add EXECIGNORE
Date: Fri, 17 Apr 2015 15:53:02 -0600

Based on an idea from Dan Colascione:
https://sourceware.org/ml/cygwin/2010-11/msg00022.html

Usage: add this to your shell startup:
  EXECIGNORE='*.so:*.so.*'
to cause TAB completion to ignore shared library files when crawling
the file system to look for executables.

Cygwin has been using a variation of this patch since December 2010,
in part because on that platform, shared libraries (*.dll) MUST have
the executable bit set, and MUST live in /usr/bin alongside normal
executables, so they can easily get in the way of TAB completion.
At least on Linux, *.so and *.so.* files tend to live in a
separate location (/usr/lib{,64}/) from normal executables.  But
even on Linux, attempts to directly execute a .so file (other than
the rare exception like /usr/lib/libc.so) usually results in
spectacular failure, so this is useful to more than just Cygwin.

Signed-off-by: Eric Blake <address@hidden>
---
 doc/bash.1       |  8 ++++++++
 doc/bashref.texi | 11 ++++++++++-
 findcmd.c        | 43 +++++++++++++++++++++++++++++++++++++------
 findcmd.h        |  1 +
 pathexp.h        |  4 ++--
 variables.c      |  9 +++++++++
 variables.h      |  1 +
 7 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/doc/bash.1 b/doc/bash.1
index f8deeb8..fbbac23 100644
--- a/doc/bash.1
+++ b/doc/bash.1
@@ -1990,6 +1990,14 @@ Similar to
 .BR BASH_ENV ;
 used when the shell is invoked in POSIX mode.
 .TP
+.B EXECIGNORE
+A colon-separated list of extended glob patterns (see \fBPattern
+Matching\fP). Files with full paths matching one of these patterns are
+not considered executable for the purposes of completion and PATH
+searching, but the \fB[\fP, \fB[[\fP, and \fBtest\fP builtins are not
+affected. Use this variable to ignore shared library files that must
+have the executable bit set, but which are not actually executable.
+.TP
 .B FCEDIT
 The default editor for the
 .B fc
diff --git a/doc/bashref.texi b/doc/bashref.texi
index 397e1d6..dedc41b 100644
--- a/doc/bashref.texi
+++ b/doc/bashref.texi
@@ -14,7 +14,7 @@ This is Edition @value{EDITION}, last updated @value{UPDATED},
 of @cite{The GNU Bash Reference Manual},
 for @code{Bash}, Version @value{VERSION}.

-Copyright @copyright{} 1988--2014 Free Software Foundation, Inc.
+Copyright @copyright{} 1988--2015 Free Software Foundation, Inc.

 @quotation
 Permission is granted to copy, distribute and/or modify this document
@@ -5653,2 +5653,2 @@ Similar to @code{BASH_ENV}; used when the shell is 
invoked in
 The numeric effective user id of the current user.  This variable
 is readonly.

address@hidden EXECIGNORE
+A colon-separated list of extended glob patterns (@pxref{Pattern
+Matching}). Files with full paths matching one of these patterns are
+not considered executable for the purposes of completion and PATH
+searching, but the @code{[}, @code{[[}, and @code{test} builtins are
+not affected. Use this variable to ignore shared library files that
+must have the executable bit set, but which are not actually
+executable.
+
 @item FCEDIT
 The editor used as a default by the @option{-e} option to the @code{fc}
 builtin command.
diff --git a/findcmd.c b/findcmd.c
index e667691..bfd8685 100644
--- a/findcmd.c
+++ b/findcmd.c
@@ -48,6 +48,8 @@
 extern int errno;
 #endif

+#include <glob/strmatch.h>
+
 extern int posixly_correct;
 extern int last_command_exit_value;

@@ -77,6 +79,35 @@ int check_hashed_filenames = CHECKHASH_DEFAULT;
    containing the file of interest. */
 int dot_found_in_search = 0;

+static struct ignorevar execignore =
+{
+  "EXECIGNORE",
+  NULL,
+  0,
+  NULL,
+  NULL
+};
+
+void
+setup_exec_ignore (char *varname)
+{
+  setup_ignore_patterns (&execignore);
+}
+
+/* Return non-zero if we should pretend the file is not executable,
+ * regardless of what the file system tells us. */
+static int
+is_on_exec_blacklist (const char *name)
+{
+  struct ign *p;
+  int flags = FNM_EXTMATCH | FNM_CASEFOLD;
+
+  for (p = execignore.ignores; p && p->val; p++)
+    if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
+      return 1;
+  return 0;
+}
+
 /* Return some flags based on information about this file.
    The EXISTS bit is non-zero if the file is found.
    The EXECABLE bit is non-zero the file is executble.
@@ -104,7 +135,7 @@ file_status (name)
      file access mechanisms into account.  eaccess uses the effective
      user and group IDs, not the real ones.  We could use sh_eaccess,
      but we don't want any special treatment for /dev/fd. */
-  if (eaccess (name, X_OK) == 0)
+  if (!is_on_exec_blacklist (name) && eaccess (name, X_OK) == 0)
     r |= FS_EXECABLE;
   if (eaccess (name, R_OK) == 0)
     r |= FS_READABLE;
@@ -114,7 +145,7 @@ file_status (name)
   /* We have to use access(2) to determine access because AFS does not
      support Unix file system semantics.  This may produce wrong
      answers for non-AFS files when ruid != euid.  I hate AFS. */
-  if (access (name, X_OK) == 0)
+  if (!is_on_exec_blacklist (name) && access (name, X_OK) == 0)
     r |= FS_EXECABLE;
   if (access (name, R_OK) == 0)
     r |= FS_READABLE;
@@ -131,7 +162,7 @@ file_status (name)
   if (current_user.euid == (uid_t)0)
     {
       r |= FS_READABLE;
-      if (finfo.st_mode & S_IXUGO)
+      if (!is_on_exec_blacklist (name) && (finfo.st_mode & S_IXUGO))
        r |= FS_EXECABLE;
       return r;
     }
@@ -139,7 +170,7 @@ file_status (name)
   /* If we are the owner of the file, the owner bits apply. */
   if (current_user.euid == finfo.st_uid)
     {
-      if (finfo.st_mode & S_IXUSR)
+      if (!is_on_exec_blacklist (name) && (finfo.st_mode & S_IXUSR))
        r |= FS_EXECABLE;
       if (finfo.st_mode & S_IRUSR)
        r |= FS_READABLE;
@@ -148,7 +179,7 @@ file_status (name)
   /* If we are in the owning group, the group permissions apply. */
   else if (group_member (finfo.st_gid))
     {
-      if (finfo.st_mode & S_IXGRP)
+      if (!is_on_exec_blacklist (name) && (finfo.st_mode & S_IXGRP))
        r |= FS_EXECABLE;
       if (finfo.st_mode & S_IRGRP)
        r |= FS_READABLE;
@@ -157,7 +188,7 @@ file_status (name)
   /* Else we check whether `others' have permission to execute the file */
   else
     {
-      if (finfo.st_mode & S_IXOTH)
+      if (!is_on_exec_blacklist (name) && (finfo.st_mode & S_IXOTH))
        r |= FS_EXECABLE;
       if (finfo.st_mode & S_IROTH)
        r |= FS_READABLE;
diff --git a/findcmd.h b/findcmd.h
index 52ad1d0..116d2a7 100644
--- a/findcmd.h
+++ b/findcmd.h
@@ -31,5 +31,6 @@ extern char *find_user_command __P((const char *));
 extern char *find_path_file __P((const char *));
 extern char *search_for_command __P((const char *, int));
 extern char *user_command_matches __P((const char *, int, int));
+extern void setup_exec_ignore __P((char *));

 #endif /* _FINDCMD_H_ */
diff --git a/pathexp.h b/pathexp.h
index 601914c..741d32a 100644
--- a/pathexp.h
+++ b/pathexp.h
@@ -71,7 +71,7 @@ extern char *quote_globbing_chars __P((const char *));
 extern char **shell_glob_filename __P((const char *));

 /* Filename completion ignore.  Used to implement the "fignore" facility of
-   tcsh and GLOBIGNORE (like ksh-93 FIGNORE).
+   tcsh, GLOBIGNORE (like ksh-93 FIGNORE), and EXECIGNORE.

    It is passed a NULL-terminated array of (char *)'s that must be
    free()'d if they are deleted.  The first element (names[0]) is the
@@ -87,7 +87,7 @@ struct ign {
 typedef int sh_iv_item_func_t __P((struct ign *));

 struct ignorevar {
-  char *varname;       /* FIGNORE or GLOBIGNORE */
+  char *varname;       /* FIGNORE, GLOBIGNORE, or EXECIGNORE */
   struct ign *ignores; /* Store the ignore strings here */
   int num_ignores;     /* How many are there? */
   char *last_ignoreval;        /* Last value of variable - cached for speed */
diff --git a/variables.c b/variables.c
index c992410..1981c6a 100644
--- a/variables.c
+++ b/variables.c
@@ -4732,6 +4732,8 @@ static struct name_and_function special_vars[] = {
   { "COMP_WORDBREAKS", sv_comp_wordbreaks },
 #endif

+  { "EXECIGNORE", sv_execignore },
+
   { "FUNCNEST", sv_funcnest },

   { "GLOBIGNORE", sv_globignore },
@@ -4930,6 +4932,13 @@ sv_globignore (name)
     setup_glob_ignore (name);
 }

+/* What to do when EXECIGNORE changes. */
+void
+sv_execignore (char *name)
+{
+  setup_exec_ignore (name);
+}
+
 #if defined (READLINE)
 void
 sv_comp_wordbreaks (name)
diff --git a/variables.h b/variables.h
index 4574ca8..f4f5042 100644
--- a/variables.h
+++ b/variables.h
@@ -375,6 +375,7 @@ extern void sv_ifs __P((char *));
 extern void sv_path __P((char *));
 extern void sv_mail __P((char *));
 extern void sv_funcnest __P((char *));
+extern void sv_execignore __P((char *));
 extern void sv_globignore __P((char *));
 extern void sv_ignoreeof __P((char *));
 extern void sv_strict_posix __P((char *));
-- 
2.1.0




reply via email to

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