[gnome-terminal] profile: editor: Add setting whether to preserve the working directory
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-terminal] profile: editor: Add setting whether to preserve the working directory
- Date: Mon, 30 Sep 2019 20:04:09 +0000 (UTC)
commit 79b2291b12cfa106149437d63394deddddbc7dca
Author: Christian Persch <chpe src gnome org>
Date: Mon Sep 30 22:03:55 2019 +0200
profile: editor: Add setting whether to preserve the working directory
Add a setting to control whether newly opened terminals open in the
working directory of the parent terminal. By default, the working
directory is only preserved when the command is a shell (as per /etc/shells).
https://bugzilla.gnome.org/show_bug.cgi?id=706927
https://gitlab.gnome.org/GNOME/gnome-terminal/issues/126
src/org.gnome.Terminal.gschema.xml | 15 ++++-
src/preferences.ui | 60 ++++++++++++++++++-
src/profile-editor.c | 10 ++++
src/terminal-enums.h | 6 ++
src/terminal-schemas.h | 1 +
src/terminal-screen.c | 81 +++++++++++++++++--------
src/terminal-util.c | 119 ++++++++++++++++++++++++++++++++++++-
src/terminal-util.h | 3 +
8 files changed, 265 insertions(+), 30 deletions(-)
---
diff --git a/src/org.gnome.Terminal.gschema.xml b/src/org.gnome.Terminal.gschema.xml
index b9e7d741..72ae48bb 100644
--- a/src/org.gnome.Terminal.gschema.xml
+++ b/src/org.gnome.Terminal.gschema.xml
@@ -53,11 +53,17 @@
<value nick='hold' value='2'/>
</enum>
- <enum id='org.gnome.Terminal.CJKWidth'>
+ <enum id='org.gnome.Terminal.CJKWidth'>
<value nick='narrow' value='1'/>
<value nick='wide' value='2'/>
</enum>
+ <enum id="org.gnome.Terminal.PreserveWorkingDirectory">
+ <value nick="never" value='0'/>
+ <value nick="safe" value='1'/>
+ <value nick="always" value='2'/>
+ </enum>
+
<!-- From gtk+ -->
<enum id="org.gnome.Terminal.TabPosition">
<!-- <value nick="left" value="0" /> -->
@@ -257,6 +263,13 @@
<summary>Whether to launch the command in the terminal as a login shell</summary>
<description>If true, the command inside the terminal will be launched as a login shell (argv[0] will
have a hyphen in front of it).</description>
</key>
+ <key name="preserve-working-directory" enum="org.gnome.Terminal.PreserveWorkingDirectory">
+ <default>'safe'</default>
+ <summary>Whether to preserve the working directory when opening a new terminal</summary>
+ <description>
+ Controls when opening a new terminal from a previous one carries over the working directory of the
opening terminal to the new one.
+ </description>
+ </key>
<key name="use-custom-command" type="b">
<default>false</default>
<summary>Whether to run a custom command instead of the shell</summary>
diff --git a/src/preferences.ui b/src/preferences.ui
index 00b6f697..627f0430 100644
--- a/src/preferences.ui
+++ b/src/preferences.ui
@@ -235,6 +235,28 @@
</row>
</data>
</object>
+ <object class="GtkListStore" id="preserve-working-directory-model">
+ <columns>
+ <!-- column-name label -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes" comments="Preserve working directory">Never</col>
+ <col id="1">never</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes" comments="Preserve working directory">Shell only</col>
+ <col id="1">safe</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes" comments="Preserve working directory">Always</col>
+ <col id="1">always</col>
+ </row>
+ </data>
+ </object>
<object class="GtkApplicationWindow" id="preferences-dialog">
<property name="can_focus">False</property>
<property name="show_menubar">False</property>
@@ -1901,6 +1923,40 @@
<property name="left_attach">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkLabel" id="preserve-working-directory-checkbutton-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Preserve working
directory:</property>
+ <property name="use_underline">True</property>
+ <property name="justify">center</property>
+ <property
name="mnemonic_widget">preserve-working-directory-combobox</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="left_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="preserve-working-directory-combobox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">preserve-working-directory-model</property>
+ <property name="focus_on_click">False</property>
+ <property name="halign">start</property>
+ <child>
+ <object class="GtkCellRendererText"
id="preserve-working-directory-renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="left_attach">1</property>
+ </packing>
+ </child>
<child>
<object class="GtkLabel" id="exit-action-combobox-label">
<property name="visible">True</property>
@@ -1911,7 +1967,7 @@
<property name="xalign">0</property>
</object>
<packing>
- <property name="top_attach">3</property>
+ <property name="top_attach">4</property>
<property name="left_attach">0</property>
</packing>
</child>
@@ -1930,7 +1986,7 @@
</child>
</object>
<packing>
- <property name="top_attach">3</property>
+ <property name="top_attach">4</property>
<property name="left_attach">1</property>
</packing>
</child>
diff --git a/src/profile-editor.c b/src/profile-editor.c
index 8916c2ed..d7e81b08 100644
--- a/src/profile-editor.c
+++ b/src/profile-editor.c
@@ -1117,10 +1117,20 @@ profile_prefs_load (const char *uuid, GSettings *profile)
"active",
G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET |
G_SETTINGS_BIND_INVERT_BOOLEAN);
+
+ w = (GtkWidget *) gtk_builder_get_object (builder, "preserve-working-directory-combobox");
+ profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_PRESERVE_WORKING_DIRECTORY_KEY, w,
+ "active",
+ G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET,
+ (GSettingsBindGetMapping) string_to_enum,
+ (GSettingsBindSetMapping) enum_to_string,
+ terminal_preserve_working_directory_get_type, NULL);
+
profile_prefs_settings_bind (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY,
gtk_builder_get_object (builder,
"use-custom-command-checkbutton"),
"active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET);
+
profile_prefs_settings_bind (profile, TERMINAL_PROFILE_USE_THEME_COLORS_KEY,
gtk_builder_get_object (builder,
"use-theme-colors-checkbutton"),
diff --git a/src/terminal-enums.h b/src/terminal-enums.h
index bdd354e3..3bae788b 100644
--- a/src/terminal-enums.h
+++ b/src/terminal-enums.h
@@ -53,6 +53,12 @@ typedef enum {
TERMINAL_THEME_VARIANT_DARK = 2
} TerminalThemeVariant;
+typedef enum {
+ TERMINAL_PRESERVE_WORKING_DIRECTORY_NEVER = 0,
+ TERMINAL_PRESERVE_WORKING_DIRECTORY_SAFE = 1,
+ TERMINAL_PRESERVE_WORKING_DIRECTORY_ALWAYS = 2,
+} TerminalPreserveWorkingDirectory;
+
G_END_DECLS
#endif /* TERMINAL_ENUMS_H */
diff --git a/src/terminal-schemas.h b/src/terminal-schemas.h
index bd2fa8c5..8f3b214b 100644
--- a/src/terminal-schemas.h
+++ b/src/terminal-schemas.h
@@ -61,6 +61,7 @@ G_BEGIN_DECLS
#define TERMINAL_PROFILE_LOGIN_SHELL_KEY "login-shell"
#define TERMINAL_PROFILE_NAME_KEY "name"
#define TERMINAL_PROFILE_PALETTE_KEY "palette"
+#define TERMINAL_PROFILE_PRESERVE_WORKING_DIRECTORY_KEY "preserve-working-directory"
#define TERMINAL_PROFILE_REWRAP_ON_RESIZE_KEY "rewrap-on-resize"
#define TERMINAL_PROFILE_SCROLLBACK_LINES_KEY "scrollback-lines"
#define TERMINAL_PROFILE_SCROLLBACK_UNLIMITED_KEY "scrollback-unlimited"
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index 8a68caa4..af68fc34 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -1084,26 +1084,53 @@ terminal_screen_get_initial_environment (TerminalScreen *screen)
return screen->priv->initial_env;
}
+static gboolean
+should_preserve_cwd (TerminalPreserveWorkingDirectory preserve_cwd,
+ const char *path,
+ const char *arg0)
+{
+ switch (preserve_cwd) {
+ case TERMINAL_PRESERVE_WORKING_DIRECTORY_SAFE: {
+ gs_free char *resolved_arg0 = terminal_util_find_program_in_path (path, arg0);
+ return resolved_arg0 != NULL &&
+ terminal_util_get_is_shell (resolved_arg0);
+ }
+
+ case TERMINAL_PRESERVE_WORKING_DIRECTORY_ALWAYS:
+ return TRUE;
+
+ case TERMINAL_PRESERVE_WORKING_DIRECTORY_NEVER:
+ default:
+ return FALSE;
+ }
+}
+
static gboolean
get_child_command (TerminalScreen *screen,
+ const char *path_env,
const char *shell_env,
+ gboolean *preserve_cwd_p,
GSpawnFlags *spawn_flags_p,
char ***argv_p,
GError **err)
{
TerminalScreenPrivate *priv = screen->priv;
GSettings *profile = priv->profile;
+ TerminalPreserveWorkingDirectory preserve_cwd;
char **argv;
- g_assert (spawn_flags_p != NULL && argv_p != NULL);
+ g_assert (spawn_flags_p != NULL && argv_p != NULL && preserve_cwd_p != NULL);
*argv_p = argv = NULL;
+ preserve_cwd = g_settings_get_enum (profile, TERMINAL_PROFILE_PRESERVE_WORKING_DIRECTORY_KEY);
+
if (priv->override_command)
{
argv = g_strdupv (priv->override_command);
- *spawn_flags_p |= G_SPAWN_SEARCH_PATH;
+ *preserve_cwd_p = should_preserve_cwd (preserve_cwd, path_env, argv[0]);
+ *spawn_flags_p |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
}
else if (g_settings_get_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY))
{
@@ -1113,7 +1140,8 @@ get_child_command (TerminalScreen *screen,
if (!g_shell_parse_argv (argv_str, NULL, &argv, err))
return FALSE;
- *spawn_flags_p |= G_SPAWN_SEARCH_PATH;
+ *preserve_cwd_p = should_preserve_cwd (preserve_cwd, path_env, argv[0]);
+ *spawn_flags_p |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
}
else if (priv->shell)
{
@@ -1126,8 +1154,10 @@ get_child_command (TerminalScreen *screen,
only_name = strrchr (shell, '/');
if (only_name != NULL)
only_name++;
- else
+ else {
only_name = shell;
+ *spawn_flags_p |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
+ }
argv = g_new (char*, 3);
@@ -1140,6 +1170,7 @@ get_child_command (TerminalScreen *screen,
argv[argc++] = NULL;
+ *preserve_cwd_p = should_preserve_cwd (preserve_cwd, path_env, shell);
*spawn_flags_p |= G_SPAWN_FILE_AND_ARGV_ZERO;
}
@@ -1157,7 +1188,7 @@ get_child_command (TerminalScreen *screen,
static char**
get_child_environment (TerminalScreen *screen,
- const char *cwd,
+ char **path,
char **shell)
{
TerminalApp *app = terminal_app_get ();
@@ -1216,6 +1247,7 @@ get_child_environment (TerminalScreen *screen,
g_ptr_array_add (retval, g_strdup_printf ("%s=%s", e, v ? v : ""));
g_ptr_array_add (retval, NULL);
+ *path = g_strdup (g_hash_table_lookup (env_table, "PATH"));
*shell = g_strdup (g_hash_table_lookup (env_table, "SHELL"));
g_hash_table_destroy (env_table);
@@ -1388,14 +1420,16 @@ terminal_screen_do_exec (TerminalScreen *screen,
{
TerminalScreenPrivate *priv = screen->priv;
VteTerminal *terminal = VTE_TERMINAL (screen);
- GSettings *profile;
- char **env, **argv;
- char *shell = NULL;
- const char *working_dir;
VtePtyFlags pty_flags = VTE_PTY_DEFAULT;
GSpawnFlags spawn_flags = G_SPAWN_SEARCH_PATH_FROM_ENVP |
VTE_SPAWN_NO_PARENT_ENVV;
GCancellable *cancellable = NULL;
+ gs_strfreev char **argv = NULL;
+ gs_strfreev char **env = NULL;
+ gs_free char *path = NULL;
+ gs_free char *shell = NULL;
+ gboolean preserve_cwd = FALSE;
+ const char *cwd;
if (priv->child_pid != -1) {
g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
@@ -1409,23 +1443,24 @@ terminal_screen_do_exec (TerminalScreen *screen,
"[screen %p] now launching the child process\n",
screen);
- profile = priv->profile;
-
- if (priv->initial_working_directory &&
- !g_settings_get_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY))
- working_dir = priv->initial_working_directory;
- else
- working_dir = g_get_home_dir ();
+ env = get_child_environment (screen, &path, &shell);
- env = get_child_environment (screen, working_dir, &shell);
-
- argv = NULL;
- if (!get_child_command (screen, shell, &spawn_flags, &argv, error))
+ if (!get_child_command (screen,
+ shell, path,
+ &preserve_cwd, &spawn_flags, &argv,
+ error))
return FALSE;
+ if (preserve_cwd) {
+ cwd = priv->initial_working_directory;
+ } else {
+ cwd = g_get_home_dir ();
+ env = g_environ_unsetenv (env, "PWD");
+ }
+
vte_terminal_spawn_async (terminal,
pty_flags,
- working_dir,
+ cwd,
argv,
env,
spawn_flags,
@@ -1436,10 +1471,6 @@ terminal_screen_do_exec (TerminalScreen *screen,
cancellable,
spawn_result_cb, NULL);
- g_free (shell);
- g_strfreev (argv);
- g_strfreev (env);
-
return TRUE; /* can't report any more errors since they only occur async */
}
diff --git a/src/terminal-util.c b/src/terminal-util.c
index 39e90bf1..ab749d83 100644
--- a/src/terminal-util.c
+++ b/src/terminal-util.c
@@ -713,8 +713,15 @@ terminal_util_get_etc_shells (void)
char *str, *nl, *end;
GPtrArray *arr;
- if (!g_file_get_contents ("/etc/shells", &contents, &len, &err) || len == 0)
- return NULL;
+ if (!g_file_get_contents ("/etc/shells", &contents, &len, &err) || len == 0) {
+ /* Defaults as per man:getusershell(3) */
+ char *default_shells[3] = {
+ (char*) "/bin/sh",
+ (char*) "/bin/csh",
+ NULL
+ };
+ return g_strdupv (default_shells);
+ }
arr = g_ptr_array_new ();
str = contents;
@@ -1430,3 +1437,111 @@ terminal_util_save_print_settings (GtkPrintSettings *settings,
save_cache_keyfile (keyfile, TERMINAL_PRINT_SETTINGS_FILENAME);
}
+
+/* BEGIN code copied from glib
+ *
+ * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * Code originally under LGPL2+; used and modified here under GPL3+
+ * Changes:
+ * Remove win32 support.
+ * Make @program nullable.
+ * Use @path instead of getenv("PATH").
+ * Use strchrnul
+ */
+
+/**
+ * terminal_util_find_program_in_path:
+ * @path: (type filename) (nullable): the search path (delimited by G_SEARCHPATH_SEPARATOR)
+ * @program: (type filename) (nullable): the programme to find in @path
+ *
+ * Like g_find_program_in_path(), but uses @path instead of the
+ * PATH environment variable as the search path.
+ *
+ * Returns: (type filename) (transfer full) (nullable): a newly allocated
+ * string containing the full path to @program, or %NULL if @program
+ * could not be found in @path.
+ */
+char *
+terminal_util_find_program_in_path (const char *path,
+ const char *program)
+{
+ const gchar *p;
+ gchar *name, *freeme;
+ gsize len;
+ gsize pathlen;
+
+ if (program == NULL)
+ return NULL;
+
+ /* If it is an absolute path, or a relative path including subdirectories,
+ * don't look in PATH.
+ */
+ if (g_path_is_absolute (program)
+ || strchr (program, G_DIR_SEPARATOR) != NULL
+ )
+ {
+ if (g_file_test (program, G_FILE_TEST_IS_EXECUTABLE) &&
+ !g_file_test (program, G_FILE_TEST_IS_DIR))
+ return g_strdup (program);
+ else
+ return NULL;
+ }
+
+ if (path == NULL)
+ {
+ /* There is no 'PATH' in the environment. The default
+ * search path in GNU libc is the current directory followed by
+ * the path 'confstr' returns for '_CS_PATH'.
+ */
+
+ /* In GLib we put . last, for security, and don't use the
+ * unportable confstr(); UNIX98 does not actually specify
+ * what to search if PATH is unset. POSIX may, dunno.
+ */
+
+ path = "/bin:/usr/bin:.";
+ }
+
+ len = strlen (program) + 1;
+ pathlen = strlen (path);
+ freeme = name = g_malloc (pathlen + len + 1);
+
+ /* Copy the file name at the top, including '\0' */
+ memcpy (name + pathlen + 1, program, len);
+ name = name + pathlen;
+ /* And add the slash before the filename */
+ *name = G_DIR_SEPARATOR;
+
+ p = path;
+ do
+ {
+ char *startp;
+
+ path = p;
+ p = strchrnul (path, G_SEARCHPATH_SEPARATOR);
+
+ if (p == path)
+ /* Two adjacent colons, or a colon at the beginning or the end
+ * of 'PATH' means to search the current directory.
+ */
+ startp = name + 1;
+ else
+ startp = memcpy (name - (p - path), path, p - path);
+
+ if (g_file_test (startp, G_FILE_TEST_IS_EXECUTABLE) &&
+ !g_file_test (startp, G_FILE_TEST_IS_DIR))
+ {
+ gchar *ret;
+ ret = g_strdup (startp);
+ g_free (freeme);
+ return ret;
+ }
+ }
+ while (*p++ != '\0');
+
+ g_free (freeme);
+ return NULL;
+}
+
+/* END code copied from glib */
diff --git a/src/terminal-util.h b/src/terminal-util.h
index abd34fd7..37e04dba 100644
--- a/src/terminal-util.h
+++ b/src/terminal-util.h
@@ -108,6 +108,9 @@ void terminal_util_load_print_settings (GtkPrintSettings **settings,
void terminal_util_save_print_settings (GtkPrintSettings *settings,
GtkPageSetup *page_setup);
+char *terminal_util_find_program_in_path (const char *path,
+ const char *program);
+
G_END_DECLS
#endif /* TERMINAL_UTIL_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]