From 776f1de18c0ac8fb08630caa32d2b0be026b73bc Mon Sep 17 00:00:00 2001
From: 90 <hi@90.gripe>
Date: Sat, 9 Mar 2024 17:28:13 +0000
Subject: [PATCH] Add XDGBDS fallbacks for Bash-related files and BASHRC
 environment variable

---
 builtins/exit.def | 11 ++++++++-
 config-top.h      |  3 ---
 shell.c           | 57 ++++++++++++++++++++++++++++++++++++++++++-----
 shell.h           |  2 ++
 variables.c       |  7 +++++-
 5 files changed, 69 insertions(+), 11 deletions(-)

diff --git a/builtins/exit.def b/builtins/exit.def
index 9c85bc5..1cb1192 100644
--- a/builtins/exit.def
+++ b/builtins/exit.def
@@ -161,7 +161,16 @@ bash_logout ()
   /* Run our `~/.bash_logout' file if it exists, and this is a login shell. */
   if (login_shell && sourced_logout++ == 0 && subshell_environment == 0)
     {
-      maybe_execute_file ("~/.bash_logout", 1);
+      if (maybe_execute_file ("~/.bash_logout", 1) == 0) {
+	char *cfg_env = get_string_value ("XDG_CONFIG_HOME");
+	char *xdg_path = malloc (strlen (cfg_env ? cfg_env : "~/.config")
+	    + strlen ("/bash/bash_logout"));
+
+	strcpy (xdg_path, cfg_env ? cfg_env : "~/.config");
+	strcat (xdg_path, "/bash/bash_logout");
+
+	maybe_execute_file (xdg_path, 1);
+      }
 #ifdef SYS_BASH_LOGOUT
       maybe_execute_file (SYS_BASH_LOGOUT, 1);
 #endif
diff --git a/config-top.h b/config-top.h
index db4ab6e..dbf95da 100644
--- a/config-top.h
+++ b/config-top.h
@@ -93,9 +93,6 @@
    reply to the select query is an empty line. */
 #define KSH_COMPATIBLE_SELECT
 
-/* Default interactive shell startup file. */
-#define DEFAULT_BASHRC "~/.bashrc"
-
 /* System-wide .bashrc file for interactive shells. */
 /* #define SYS_BASHRC "/etc/bash.bashrc" */
 
diff --git a/shell.c b/shell.c
index ebd8965..72478a3 100644
--- a/shell.c
+++ b/shell.c
@@ -105,6 +105,10 @@ extern int errno;
 extern char **environ;	/* used if no third argument to main() */
 #endif
 
+#if !HAVE_ASPRINTF
+extern int asprintf PARAMS((char **, const char *, ...)) __attribute__((__format__ (printf, 2, 3)));
+#endif
+
 extern int gnu_error_format;
 
 /* Non-zero means that this shell has already been run; i.e. you should
@@ -194,7 +198,7 @@ int have_devfd = 0;
 #endif
 
 /* The name of the .(shell)rc file. */
-static char *bashrc_file = DEFAULT_BASHRC;
+static char *bashrc_file;
 
 /* Non-zero means to act more like the Bourne shell on startup. */
 static int act_like_sh;
@@ -320,6 +324,7 @@ static void add_shopt_to_alist PARAMS((char *, int));
 static void run_shopt_alist PARAMS((void));
 
 static void execute_env_file PARAMS((char *));
+static void init_bashrc_file PARAMS((void));
 static void run_startup_files PARAMS((void));
 static int open_shell_script PARAMS((char *));
 static void set_bash_input PARAMS((void));
@@ -1120,6 +1125,43 @@ execute_env_file (env_file)
     }
 }
 
+char *
+find_bash_user_file (fname, xdgvar, xdgdef)
+     const char *fname, *xdgvar, *xdgdef;
+{
+  struct stat sb;
+  char *legacy_path, *xdg_path, *cfg_env;
+  int len;
+
+  len = asprintf(&legacy_path, "~/.%s", fname);
+  legacy_path = bash_tilde_expand (legacy_path, 0);
+
+  if (stat (legacy_path, &sb)) {
+    cfg_env = get_string_value (xdgvar);
+    xdgdef = bash_tilde_expand (xdgdef, 0);
+    len = asprintf(&xdg_path, "%s/bash/%s", cfg_env ? cfg_env : xdgdef, fname);
+    return stat (xdg_path, &sb) ? legacy_path : xdg_path;
+  } else
+    return legacy_path;
+}
+
+static int
+find_bash_config_file (fname)
+     const char *fname;
+{
+  return maybe_execute_file (find_bash_user_file
+      (fname, "XDG_CONFIG_HOME", "~/.config"), 1);
+}
+
+static void
+init_bashrc_file ()
+{
+  if (!bashrc_file)
+    bashrc_file = get_string_value ("BASHRC") ?
+        get_string_value ("BASHRC") : find_bash_user_file ("bashrc",
+        "XDG_CONFIG_HOME", "~/.config");
+}
+
 static void
 run_startup_files ()
 {
@@ -1128,6 +1170,9 @@ run_startup_files ()
 #endif
   int sourced_login, run_by_ssh;
 
+  /* Initialise bashrc_file to be run when in interactive mode */
+  init_bashrc_file ();
+
 #if 1	/* TAG:bash-5.3 andrew.gregory.8@gmail.com 2/21/2022 */
   /* get the rshd/sshd case out of the way first. */
   if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 &&
@@ -1189,8 +1234,8 @@ run_startup_files ()
 
 	  if (act_like_sh)	/* sh */
 	    maybe_execute_file ("~/.profile", 1);
-	  else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) &&
-		   (maybe_execute_file ("~/.bash_login", 1) == 0))	/* bash */
+	  else if ((find_bash_config_file ("bash_profile") == 0) &&
+		   (find_bash_config_file ("bash_login") == 0))	/* bash */
 	    maybe_execute_file ("~/.profile", 1);
 	}
 
@@ -1225,8 +1270,8 @@ run_startup_files ()
 
 	      if (act_like_sh)	/* sh */
 		maybe_execute_file ("~/.profile", 1);
-	      else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) &&
-		       (maybe_execute_file ("~/.bash_login", 1) == 0))	/* bash */
+	      else if ((find_bash_config_file ("bash_profile") == 0) &&
+		       (find_bash_config_file ("bash_login") == 0))	/* bash */
 		maybe_execute_file ("~/.profile", 1);
 	    }
 	}
@@ -2040,7 +2085,7 @@ shell_reinitialize ()
 
   /* Ensure that the default startup file is used.  (Except that we don't
      execute this file for reinitialized shells). */
-  bashrc_file = DEFAULT_BASHRC;
+  init_bashrc_file ();
 
   /* Delete all variables and functions.  They will be reinitialized when
      the environment is parsed. */
diff --git a/shell.h b/shell.h
index 6e44bca..981074e 100644
--- a/shell.h
+++ b/shell.h
@@ -227,6 +227,8 @@ typedef struct _sh_input_line_state_t
 #endif
 } sh_input_line_state_t;
 
+char *find_bash_user_file PARAMS((const char *, const char *, const char *));
+
 /* Let's try declaring these here. */
 extern void shell_ungets PARAMS((char *));
 extern void rewind_input_string PARAMS((void));
diff --git a/variables.c b/variables.c
index 1a0c2c4..0b6c7a2 100644
--- a/variables.c
+++ b/variables.c
@@ -640,6 +640,9 @@ initialize_shell_variables (env, privmode)
   if (command_execution_string)
     bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
 
+  if (interactive_shell)
+    set_if_not ("BASHRC", find_bash_user_file ("bashrc", "XDG_CONFIG_HOME", "~/.config"));
+
   /* Find out if we're supposed to be in Posix.2 mode via an
      environment variable. */
   temp_var = find_variable ("POSIXLY_CORRECT");
@@ -654,7 +657,9 @@ initialize_shell_variables (env, privmode)
      that we are remembering commands on the history list. */
   if (remember_on_history)
     {
-      name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
+      name = find_bash_user_file (
+	  posixly_correct ? "sh_history" : "bash_history",
+	  "XDG_STATE_HOME", "~/.local/state");
 
       set_if_not ("HISTFILE", name);
       free (name);
-- 
2.44.0

