]> granicus.if.org Git - neomutt/commitdiff
browser: <change-dir> mutt_realpath() <goto-parent> 1037/head
authorMichael J. Bazzinotti <mbazzinotti@gmail.com>
Wed, 31 Jan 2018 17:20:05 +0000 (12:20 -0500)
committerRichard Russon <rich@flatcap.org>
Thu, 1 Feb 2018 14:51:10 +0000 (14:51 +0000)
For "regular" filesystems (not IMAP or Notmuch folder hierarchies),
<change-dir> and the browser GUI now resolve paths via a new mutt library
function `mutt_realpath`. This means no more path buildup such as
"a/b/../../c". Additionally, Symlinks are resolved automatically. This is
a circumstance of realpath(). Note that a future PR is planned to add a
non-symlinking option.

The new <goto-parent> function has a default shortcut key of 'p' for parent.
Since this function uses the pre-existing `mutt_get_parent_path()`,
<goto-parent> may also work on IMAP and Notmuch folder hierarchies
(tested and working on IMAP)

Note: The paths passed to `mutt_get_parent_path()` formerly would not
parse correctly a path which included a final trailing slash (eg 'abc/')
So, I added it a check to that function to remove it before hitting
the parser.

Note2: The browser for IMAP was janky before this commit, but the browser
is not the primary mailbox navigation tool, afterall. At least 'p'
<goto-parent> adds to its functionality. I expect a similar evaluation for
Notmuch folder browsing, although I have never used Notmuch. Any
contributions to make a better IMAP/Notmuch folder browsing experience
are welcome. I have reported some of the fallbacks in Github PR #1037
comments (look for IMAP headlines).

browser.c
functions.h
muttlib.c
opcodes.h
protos.h

index 53723c273f1649dfafd242fbb8c55c085af40f06..4d2a3b5942a5d592b1466846854c27efd6412b9a 100644 (file)
--- a/browser.c
+++ b/browser.c
@@ -1509,16 +1509,22 @@ void mutt_select_file(char *f, size_t flen, int flags, char ***files, int *numfi
             }
             else
 #endif
-                if (examine_directory(menu, &state, LastDir, prefix) == -1)
             {
-              /* try to restore the old values */
-              mutt_str_strfcpy(LastDir, OldLastDir, sizeof(LastDir));
               if (examine_directory(menu, &state, LastDir, prefix) == -1)
               {
-                mutt_str_strfcpy(LastDir, NONULL(HomeDir), sizeof(LastDir));
-                goto bail;
+                /* try to restore the old values */
+                mutt_str_strfcpy(LastDir, OldLastDir, sizeof(LastDir));
+                if (examine_directory(menu, &state, LastDir, prefix) == -1)
+                {
+                  mutt_str_strfcpy(LastDir, NONULL(HomeDir), sizeof(LastDir));
+                  goto bail;
+                }
               }
+              /* resolve paths navigated from GUI */
+              if (mutt_realpath(LastDir) == 0)
+                break;
             }
+
             browser_highlight_default(&state, menu);
             init_menu(&state, menu, title, sizeof(title), buffy);
             if (GotoSwapper[0])
@@ -1686,6 +1692,7 @@ void mutt_select_file(char *f, size_t flen, int flags, char ***files, int *numfi
         break;
 #endif
 
+      case OP_GOTO_PARENT:
       case OP_CHANGE_DIRECTORY:
 
 #ifdef USE_NNTP
@@ -1707,7 +1714,16 @@ void mutt_select_file(char *f, size_t flen, int flags, char ***files, int *numfi
           }
         }
 
-        if (mutt_get_field(_("Chdir to: "), buf, sizeof(buf), MUTT_FILE) == 0 && buf[0])
+        if (i == OP_CHANGE_DIRECTORY)
+        {
+          int ret = mutt_get_field(_("Chdir to: "), buf, sizeof(buf), MUTT_FILE);
+          if (ret != 0)
+            break;
+        }
+        else if (i == OP_GOTO_PARENT)
+          mutt_get_parent_path(buf, buf, sizeof(buf));
+
+        if (buf[0])
         {
           buffy = 0;
           mutt_expand_path(buf, sizeof(buf));
@@ -1735,6 +1751,12 @@ void mutt_select_file(char *f, size_t flen, int flags, char ***files, int *numfi
               mutt_file_concat_path(tmp, LastDir, buf, sizeof(tmp));
               mutt_str_strfcpy(buf, tmp, sizeof(buf));
             }
+            /* Resolve path from <chdir>
+             * Avoids buildup such as /a/b/../../c
+             * Symlinks are always unraveled to keep code simple */
+            if (mutt_realpath(buf) == 0)
+              break;
+
             if (stat(buf, &st) == 0)
             {
               if (S_ISDIR(st.st_mode))
index 4430c41fa078aa929b17bcc3c23e894951f6f5c7..23645fd5b6ce3520f2903450c8a42d45d92a213e 100644 (file)
@@ -472,6 +472,7 @@ const struct Binding OpAlias[] = { /* map: alias */
 /* The file browser */
 const struct Binding OpBrowser[] = { /* map: browser */
   { "change-dir",            OP_CHANGE_DIRECTORY,            "c" },
+  { "goto-parent",           OP_GOTO_PARENT,                 "p" },
   { "display-filename",      OP_BROWSER_TELL,                "@" },
   { "enter-mask",            OP_ENTER_MASK,                  "m" },
   { "sort",                  OP_SORT,                        "o" },
index 322e9a0d528cb94b743d4dc99bd1373fb16b253b..a412b1eda355c0a8416fb6d8908235d5ae49b5d9 100644 (file)
--- a/muttlib.c
+++ b/muttlib.c
@@ -1466,6 +1466,10 @@ void mutt_get_parent_path(char *output, char *path, size_t olen)
     mutt_str_strfcpy(output, path, olen);
     int n = mutt_str_strlen(output);
 
+    /* remove any final trailing '/' */
+    if (output[n - 1] == '/')
+      output[n - 1] = '\0';
+
     /* Remove everything until the next slash */
     for (n--; ((n >= 0) && (output[n] != '/')); n--)
       ;
@@ -1480,6 +1484,26 @@ void mutt_get_parent_path(char *output, char *path, size_t olen)
   }
 }
 
+/**
+ * mutt_realpath - resolve path, unraveling symlinks
+ * @param buf Buffer containing path
+ * @retval len String length of resolved path
+ * @retval 0   Error, buf is not overwritten
+ *
+ * Resolve and overwrite the path in buf.
+ *
+ * @note Size of buf should be at least PATH_MAX bytes.
+ */
+size_t mutt_realpath(char *buf)
+{
+  char s[PATH_MAX];
+
+  if (realpath(buf, s) == NULL)
+    return 0;
+
+  return mutt_str_strfcpy(buf, s, PATH_MAX);
+}
+
 char debugfilename[_POSIX_PATH_MAX];
 FILE *debugfile = NULL;
 int debuglevel;
index 090c490c675a078ef9b694bd16b81af25eadd322..5dab7238b028977e86c11afa23ed23c0badedf56 100644 (file)
--- a/opcodes.h
+++ b/opcodes.h
@@ -43,6 +43,7 @@
   _fmt(OP_BUFFY_LIST,                     N_("list mailboxes with new mail")) \
   _fmt(OP_CATCHUP,                        N_("mark all articles in newsgroup as read")) \
   _fmt(OP_CHANGE_DIRECTORY,               N_("change directories")) \
+  _fmt(OP_GOTO_PARENT,                    N_("go to parent directory")) \
   _fmt(OP_CHECK_NEW,                      N_("check mailboxes for new mail")) \
   _fmt(OP_COMPOSE_ATTACH_FILE,            N_("attach file(s) to this message")) \
   _fmt(OP_COMPOSE_ATTACH_MESSAGE,         N_("attach message(s) to this message")) \
index e2595e95a40546419f4fc85faafaf18348fa0723..75b42288affad64f9a361f1aea8563e236b559e1 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -338,6 +338,7 @@ int mutt_save_confirm(const char *s, struct stat *st);
 
 void mutt_browser_select_dir(char *f);
 void mutt_get_parent_path(char *output, char *path, size_t olen);
+size_t mutt_realpath(char *buf);
 
 #define MUTT_RANDTAG_LEN (16)
 void mutt_rand_base32(void *out, size_t len);