From a25e5f80a53d2d08c1bcc72033e0931a65737715 Mon Sep 17 00:00:00 2001
From: Thomas Jarosch <thomas.jarosch@intra2net.com>
Date: Sun, 14 Dec 2014 00:24:35 +0100
Subject: [PATCH] Add ability to spawn shell

Key 'b' in the browse window spawns a shell in the current directoy.

We first check the $SHELL environment variable of the user for the preferred
shell interpreter. If it's not set, we fall back to the compile time
configured default shell (usually /bin/bash).

Signed-off-by: Thomas Jarosch <thomas.jarosch@intra2net.com>
---
 Makefile.am   |  2 ++
 configure.ac  | 15 ++++++++--
 doc/ncdu.pod  |  8 ++++++
 src/browser.c |  3 ++
 src/global.h  |  1 +
 src/help.c    |  1 +
 src/main.c    |  1 +
 src/shell.c   | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shell.h   | 35 +++++++++++++++++++++++
 9 files changed, 142 insertions(+), 2 deletions(-)
 create mode 100644 src/shell.c
 create mode 100644 src/shell.h

diff --git a/Makefile.am b/Makefile.am
index 8275232..41cd6ec 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,6 +12,7 @@ ncdu_SOURCES=\
 	src/dir_scan.c\
 	src/exclude.c\
 	src/help.c\
+	src/shell.c\
 	src/main.c\
 	src/path.c\
 	src/util.c
@@ -26,6 +27,7 @@ noinst_HEADERS=\
 	src/exclude.h\
 	src/global.h\
 	src/help.h\
+	src/shell.h\
 	src/path.h\
 	src/util.h
 
diff --git a/configure.ac b/configure.ac
index 4e87cf2..b1e8c5c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,7 +25,7 @@ AC_STRUCT_ST_BLOCKS
 
 # Check for library functions.
 AC_CHECK_FUNCS(
-  [getcwd gettimeofday fnmatch chdir rmdir unlink lstat],[],
+  [getcwd gettimeofday fnmatch chdir rmdir unlink lstat system getenv],[],
   AC_MSG_ERROR([required function missing]))
 
 
@@ -55,7 +55,18 @@ if test "$ncurses" = "ncurses"; then
     ])
 fi
 
+# Configure default shell for spawning shell when $SHELL is not set
+AC_ARG_WITH([shell],
+  [AS_HELP_STRING([--with-shell],
+                  [used interpreter as default shell (default is /bin/bash)])],
+                  [DEFAULT_SHELL=$withval],
+                  [DEFAULT_SHELL=/bin/bash])
+AC_MSG_NOTICE([Using $DEFAULT_SHELL as the default shell if \$SHELL is not set])
+AC_CHECK_PROG(TEST_SHELL, $DEFAULT_SHELL, yes, no, [/])
+if test "x$TEST_SHELL" != "xyes"; then
+  AC_MSG_ERROR([Default shell interpreter $DEFAULT_SHELL not found])
+fi
+AC_DEFINE_UNQUOTED(DEFAULT_SHELL, "$DEFAULT_SHELL", [Used default shell interpreter])
 
 
 AC_OUTPUT([Makefile])
-
diff --git a/doc/ncdu.pod b/doc/ncdu.pod
index d4b5bd8..e0ba511 100644
--- a/doc/ncdu.pod
+++ b/doc/ncdu.pod
@@ -210,6 +210,14 @@ Show information about the current selected item.
 
 Refresh/recalculate the current directory.
 
+=item b
+
+Spawn shell in current directory.
+
+We first check the $SHELL environment variable of the user for the preferred
+shell interpreter. If it's not set, we fall back to the compile time
+configured default shell (usually /bin/bash).
+
 =item q
 
 Quit
diff --git a/src/browser.c b/src/browser.c
index ac39a32..7fe43a3 100644
--- a/src/browser.c
+++ b/src/browser.c
@@ -428,6 +428,9 @@ int browse_key(int ch) {
           t = NULL;
       delete_init(sel, t);
       break;
+     case 'b':
+      shell_init();
+      break;
     }
 
   /* make sure the info_* options are correct */
diff --git a/src/global.h b/src/global.h
index e298f1e..67e4141 100644
--- a/src/global.h
+++ b/src/global.h
@@ -55,6 +55,7 @@
 #define ST_BROWSE 1
 #define ST_DEL    2
 #define ST_HELP   3
+#define ST_SHELL  4
 
 
 /* structure representing a file or directory */
diff --git a/src/help.c b/src/help.c
index be16550..074d9ed 100644
--- a/src/help.c
+++ b/src/help.c
@@ -50,6 +50,7 @@ char *keys[KEYS*2] = {
             "e", "Show/hide hidden or excluded files",
             "i", "Show information about selected item",
             "r", "Recalculate the current directory",
+            "b", "Spawn shell in current directory",
             "q", "Quit ncdu"
 };
 
diff --git a/src/main.c b/src/main.c
index 3426548..206c3b7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -52,6 +52,7 @@ static void screen_draw() {
     case ST_CALC:   dir_draw();    break;
     case ST_BROWSE: browse_draw(); break;
     case ST_HELP:   help_draw();   break;
+    case ST_SHELL:  shell_draw();  break;
     case ST_DEL:    delete_draw(); break;
   }
 }
diff --git a/src/shell.c b/src/shell.c
new file mode 100644
index 0000000..ae3cc22
--- /dev/null
+++ b/src/shell.c
@@ -0,0 +1,78 @@
+/* ncdu - NCurses Disk Usage
+
+  Copyright (c) 2007-2014 Yoran Heling
+  Shell support: Copyright (c) 2014 Thomas Jarosch
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include "config.h"
+#include "global.h"
+#include "dirlist.h"
+#include "util.h"
+
+#include <ncurses.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void shell_draw() {
+  char *full_path, *shell;
+  int res;
+
+  /* suspend ncurses mode */
+  def_prog_mode();
+  endwin();
+
+  full_path = getpath(dirlist_par);
+  res = chdir(full_path);
+  if (res != 0) {
+    reset_prog_mode();
+    clear();
+    printw("ERROR: Can't change directory: %s (errcode: %d)\n"
+           "\n"
+           "Press any key to continue.",
+           full_path, res);
+  } else {
+    char *shell = getenv("SHELL");
+    if (shell == NULL)
+      shell = DEFAULT_SHELL;
+
+    res = system(shell);
+
+    /* resume ncurses mode */
+    reset_prog_mode();
+
+    if (res == -1 || WEXITSTATUS(res) != 0) {
+      clear();
+      printw("ERROR: Can't execute shell interpreter: %s\n"
+             "\n"
+             "Press any key to continue.",
+             shell);
+    }
+  }
+
+  refresh();
+  pstate = ST_BROWSE;
+}
+
+void shell_init() {
+  pstate = ST_SHELL;
+}
diff --git a/src/shell.h b/src/shell.h
new file mode 100644
index 0000000..712be06
--- /dev/null
+++ b/src/shell.h
@@ -0,0 +1,35 @@
+/* ncdu - NCurses Disk Usage
+
+  Copyright (c) 2007-2014 Yoran Heling
+  Shell support: Copyright (c) 2014 Thomas Jarosch
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#ifndef _shell_h
+#define _shell_h
+
+#include "global.h"
+
+void shell_draw(void);
+void shell_init();
+
+#endif
-- 
GitLab