[PATCH 2/7] gdb: use get_standard_config_dir when looking for .gdbinit

Andrew Burgess
2020-10-07T19:05:36Z

This commit effectively changes the default location of the .gdbinit
file, while maintaining backward compatibility.

For non Apple hosts the .gdbinit file will now be looked for in the
following locations:

$XDG_CONFIG_HOME/gdb/gdbinit
$HOME/.config/gdb/gdbinit
$HOME/.gdbinit

On Apple hosts the search order is instead:

$HOME/Library/Preferences/gdb/gdbinit
$HOME/.gdbinit

I've performed an extensive rewrite of the documentation, moving all
information about initialization files and where to find them into a
new @node, text from other areas has been moved into this one
location, and other areas cross-reference to this new @node as much as
possible.

gdb/ChangeLog:

* NEWS: Mention changes to config file search path.
* main.c

gdb/doc/ChangeLog:

* gdb.texinfo (Mode Options): Descriptions of initialization files
has been moved to 'Initialization Files'.
(Startup): Likewise.
(Initialization Files): New node.
(gdb man): Update to mention alternative file paths.
(gdbinit man): Likewise.
---
gdb/ChangeLog | 5 +
gdb/NEWS | 6 ++
gdb/auto-load.c | 27 ++++--
gdb/doc/ChangeLog | 9 ++
gdb/doc/gdb.texinfo | 207 +++++++++++++++++++++++++---------------
gdb/main.c | 13 +--
gdbsupport/pathstuff.cc | 49 ++++++++++
gdbsupport/pathstuff.h | 23 +++++
8 files changed, 246 insertions(+), 93 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d230b1c3228..c64dda7bbcb 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -15,6 +15,12 @@

* GDB now supports core file debugging for x86_64 Cygwin programs.

+* GDB will now look for the .gdbinit file in a config directory before
+ looking for ~/.gdbinit. The file is searched for in the following
+ locations: $XDG_CONFIG_HOME/gdb/gdbinit, $HOME/.config/gdb/gdbinit,
+ $HOME/.gdbinit. On Apple hosts the search order is instead:
+ $HOME/Library/Preferences/gdb/gdbinit, $HOME/.gdbinit.
+
* New commands

set debug event-loop
diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 43d007ca5b0..837d2219bb5 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -498,11 +498,26 @@ file_is_auto_load_safe (const char *filename, const char *debug_fmt, ...)

if (!advice_printed)
{
- const char *homedir = getenv ("HOME");
-
- if (homedir == NULL)
- homedir = "$HOME";
- std::string homeinit = string_printf ("%s/%s", homedir, GDBINIT);
+ /* Find the existing home directory config file. */
+ struct stat buf;
+ std::string home_config = find_gdb_home_config_file (GDBINIT, &buf);
+ if (home_config.empty ())
+ {
+ /* The user doesn't have an existing home directory config file,
+ so we should suggest a suitable path for them to use. */
+ std::string config_dir_file
+ = get_standard_config_filename (GDBINIT);
+ if (!config_dir_file.empty ())
+ home_config = config_dir_file;
+ else
+ {
+ const char *homedir = getenv ("HOME");
+ if (homedir == nullptr)
+ homedir = "$HOME";
+ home_config = (std::string (homedir) + SLASH_STRING
+ + std::string (GDBINIT));
+ }
+ }

printf_filtered (_("\
To enable execution of this file add\n\
@@ -515,7 +530,7 @@ For more information about this security protection see the\n\
\"Auto-loading safe path\" section in the GDB manual. E.g., run from the shell:\n\
\tinfo \"(gdb)Auto-loading safe path\"\n"),
filename_real.get (),
- homeinit.c_str (), homeinit.c_str ());
+ home_config.c_str (), home_config.c_str ());
advice_printed = 1;
}

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2636b6f9903..af5f27f8a8a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -937,6 +937,7 @@
* File Options:: Choosing files
* Mode Options:: Choosing modes
* Startup:: What @value{GDBN} does during startup
+* Initialization Files:: Initialization Files
@end menu

@node File Options
@@ -1077,47 +1078,16 @@
@itemx -n
@cindex @code{--nx}
@cindex @code{-n}
-Do not execute commands found in any initialization file.
-There are three init files, loaded in the following order:
-
-@table @code
-@item @file{system.gdbinit}
-This is the system-wide init file.
-Its location is specified with the @code{--with-system-gdbinit}
-configure option (@pxref{System-wide configuration}).
-It is loaded first when @value{GDBN} starts, before command line options
-have been processed.
-@item @file{system.gdbinit.d}
-This is the system-wide init directory.
-Its location is specified with the @code{--with-system-gdbinit-dir}
-configure option (@pxref{System-wide configuration}).
-Files in this directory are loaded in alphabetical order immediately after
-system.gdbinit (if enabled) when @value{GDBN} starts, before command line
-options have been processed. Files need to have a recognized scripting
-language extension (@file{.py}/@file{.scm}) or be named with a @file{.gdb}
-extension to be interpreted as regular @value{GDBN} commands. @value{GDBN}
-will not recurse into any subdirectories of this directory.
-@item @file{~/.gdbinit}
-This is the init file in your home directory.
-It is loaded next, after @file{system.gdbinit}, and before
-command options have been processed.
-@item @file{./.gdbinit}
-This is the init file in the current directory.
-It is loaded last, after command line options other than @code{-x} and
-@code{-ex} have been processed. Command line options @code{-x} and
-@code{-ex} are processed last, after @file{./.gdbinit} has been loaded.
-@end table
-
-For further documentation on startup processing, @xref{Startup}.
-For documentation on how to write command files,
-@xref{Command Files,,Command Files}.
+Do not execute commands found in any initialization file
+(@pxref{Initialization Files}).

@anchor{-nh}
@item -nh
@cindex @code{--nh}
-Do not execute commands found in @file{~/.gdbinit}, the init file
-in your home directory.
-@xref{Startup}.
+Do not execute commands found in any home directory initialization
+file (@pxref{Initialization Files,,Home directory initialization
+file}). The system wide and current directory initialization files
+are still loaded.

@item -quiet
@itemx -silent
@@ -1327,20 +1297,13 @@

@item
@cindex init file
-Reads the system-wide @dfn{init file} (if @option{--with-system-gdbinit} was
-used when building @value{GDBN}; @pxref{System-wide configuration,
- ,System-wide configuration and settings}) and the files in the system-wide
-gdbinit directory (if @option{--with-system-gdbinit-dir} was used) and executes
-all the commands in those files. The files need to be named with a @file{.gdb}
-extension to be interpreted as @value{GDBN} commands, or they can be written
-in a supported scripting language with an appropriate file extension.
+Reads the system wide initialization file and the files from the
+system wide initialization directory, @pxref{System Wide Init Files}.

-@anchor{Home Directory Init File}
@item
-Reads the init file (if any) in your home directory@footnote{On
-DOS/Windows systems, the home directory is the one pointed to by the
-@code{HOME} environment variable.} and executes all the commands in
-that file.
+Reads the initialization file (if any) in your home directory and
+executes all the commands in that file, @pxref{Home Directory Init
+File}.

@anchor{Option -init-eval-command}
@item
@@ -1353,16 +1316,16 @@
@item
Processes command line options and operands.

-@anchor{Init File in the Current Directory during Startup}
@item
-Reads and executes the commands from init file (if any) in the current
-working directory as long as @samp{set auto-load local-gdbinit} is set to
-@samp{on} (@pxref{Init File in the Current Directory}).
-This is only done if the current directory is
-different from your home directory. Thus, you can have more than one
-init file, one generic in your home directory, and another, specific
-to the program you are debugging, in the directory where you invoke
-@value{GDBN}.
+Reads and executes the commands from the initialization file (if any)
+in the current working directory as long as @samp{set auto-load
+local-gdbinit} is set to @samp{on} (@pxref{Init File in the Current
+Directory}). This is only done if the current directory is different
+from your home directory. Thus, you can have more than one init file,
+one generic in your home directory, and another, specific to the
+program you are debugging, in the directory where you invoke
+@value{GDBN}. @xref{Init File in the Current Directory during
+Startup}.

@item
If the command line specified a program to debug, or a process to
@@ -1391,26 +1354,115 @@
files where @value{GDBN} records it.
@end enumerate

-Init files use the same syntax as @dfn{command files} (@pxref{Command
-Files}) and are processed by @value{GDBN} in the same way. The init
-file in your home directory can set options (such as @samp{set
-complaints}) that affect subsequent processing of command line options
-and operands. Init files are not executed if you use the @samp{-nx}
-option (@pxref{Mode Options, ,Choosing Modes}).
+@node Initialization Files
+@subsection Initialization Files
+@cindex init file name

-To display the list of init files loaded by gdb at startup, you
-can use @kbd{gdb --help}.
+During startup (@pxref{Startup}) @value{GDBN} will execute commands
+from several initialization files. These initialization files use the
+same syntax as @dfn{command files} (@pxref{Command Files}) and are
+processed by @value{GDBN} in the same way.

-@cindex init file name
+To display the list of initialization files loaded by @value{GDBN} at
+startup, in the order they will be loaded, you can use @kbd{gdb
+--help}.
+
+As the system wide and home directory initialization files are
+processed before most command line options, changes to settings
+(e.g. @samp{set complaints}) can affect subsequent processing of
+command line options and operands.
+
+The following sections describe where @value{GDBN} looks for the
+initialization and the order that the files are searched for.
+
+@anchor{System Wide Init Files}
+@subsubsection System wide initialization files
+
+There are two locations that are searched for system wide
+initialization files. Both of these locations are always checked:
+
+@table @code
+
+@item @file{system.gdbinit}
+This is a single system-wide initialization file. Its location is
+specified with the @code{--with-system-gdbinit} configure option
+(@pxref{System-wide configuration}). It is loaded first when
+@value{GDBN} starts, before command line options have been processed.
+
+@item @file{system.gdbinit.d}
+This is the system-wide initialization directory. Its location is
+specified with the @code{--with-system-gdbinit-dir} configure option
+(@pxref{System-wide configuration}). Files in this directory are
+loaded in alphabetical order immediately after @file{system.gdbinit}
+(if enabled) when @value{GDBN} starts, before command line options
+have been processed. Files need to have a recognized scripting
+language extension (@file{.py}/@file{.scm}) or be named with a
+@file{.gdb} extension to be interpreted as regular @value{GDBN}
+commands. @value{GDBN} will not recurse into any subdirectories of
+this directory.
+
+@end table
+
+It is possible to prevent the system wide initialization files from
+being loaded using the @samp{-nx} command line option, @pxref{Mode
+Options,,Choosing Modes}.
+
+@anchor{Home Directory Init File}
+@subsubsection Home directory initialization file
+@cindex @file{gdbinit}
@cindex @file{.gdbinit}
@cindex @file{gdb.ini}
-The @value{GDBN} init files are normally called @file{.gdbinit}.
-The DJGPP port of @value{GDBN} uses the name @file{gdb.ini}, due to
-the limitations of file names imposed by DOS filesystems. The Windows
-port of @value{GDBN} uses the standard name, but if it finds a
-@file{gdb.ini} file in your home directory, it warns you about that
-and suggests to rename the file to the standard name.

+After loading the system wide initialization files @value{GDBN} will
+look for an initialization file in the users home
+directory@footnote{On DOS/Windows systems, the home directory is the
+one pointed to by the @code{HOME} environment variable.}. There are a
+number of locations that @value{GDBN} will search in the home
+directory, these locations are searched in order and @value{GDBN} will
+load the first file that it finds, and subsequent locations will not
+be checked.
+
+On non-Apple hosts the locations searched are:
+@table @file
+@item $XDG_CONFIG_HOME/gdb/gdbinit
+@item $HOME/.config/gdb/gdbinit
+@item $HOME/.gdbinit
+@end table
+
+While on Apple hosts the locations searched are:
+@table @file
+@item $HOME/Library/Preferences/gdb/gdbinit
+@item $HOME/.gdbinit
+@end table
+
+It is possible to prevent the home directory initialization file from
+being loaded using the @samp{-nx} or @samp{-nh} command line options,
+@pxref{Mode Options,,Choosing Modes}.
+
+The DJGPP port of @value{GDBN} uses the name @file{gdb.ini} instead of
+@file{.gdbinit} or @file{gdbinit}, due to the limitations of file
+names imposed by DOS filesystems. The Windows port of @value{GDBN}
+uses the standard name, but if it finds a @file{gdb.ini} file in your
+home directory, it warns you about that and suggests to rename the
+file to the standard name.
+
+@anchor{Init File in the Current Directory during Startup}
+@subsubsection Local directory initialization file
+
+@value{GDBN} will check the current directory for a file called
+@file{./.gdbinit}. It is loaded last, after command line options
+other than @samp{-x} and @samp{-ex} have been processed. The command
+line options @samp{-x} and @samp{-ex} are processed last, after
+@file{./.gdbinit} has been loaded, @pxref{File Options,,Choosing
+Files}.
+
+If the file in the current directory was already loaded as the home
+directory initialization file then it will not be loaded a second
+time.
+
+It is possible to prevent the local directory initialization file from
+being loaded using the @samp{-nx} command line option, @pxref{Mode
+Options,,Choosing Modes}.

@node Quitting GDB
@section Quitting @value{GDBN}
@@ -46400,7 +46452,8 @@
Add @var{directory} to the path to search for source files.

@item -nh
-Do not execute commands from @file{~/.gdbinit}.
+Do not execute commands from @file{~/.config/gdb/gdbinit} or
+@file{~/.gdbinit}.

@item -nx
@itemx -n
@@ -46825,6 +46878,8 @@
@value{SYSTEM_GDBINIT_DIR}/*
@end ifset

+~/.config/gdb/gdbinit
+
~/.gdbinit

./.gdbinit
@@ -46884,11 +46939,11 @@
@ref{System-wide configuration}.
@end ifclear

-@item ~/.gdbinit
+@item @file{~/.config/gdb/gdbinit} or @file{~/.gdbinit}
User initialization file. It is executed unless user specified
@value{GDBN} options @code{-nx}, @code{-n} or @code{-nh}.

-@item ./.gdbinit
+@item @file{./.gdbinit}
Initialization file for current directory. It may need to be enabled with
@value{GDBN} security command @code{set auto-load local-gdbinit}.
See more in
diff --git a/gdb/main.c b/gdb/main.c
index 19bbb923889..f6377f2df28 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -301,8 +301,6 @@ get_init_files (std::vector<std::string> *system_gdbinit,
}
}

- const char *homedir = getenv ("HOME");
-
/* If the .gdbinit file in the current directory is the same as
the $HOME/.gdbinit file, it should not be sourced. homebuf
and cwdbuf are used in that purpose. Make sure that the stats
@@ -312,14 +310,7 @@ get_init_files (std::vector<std::string> *system_gdbinit,
memset (&homebuf, 0, sizeof (struct stat));
memset (&cwdbuf, 0, sizeof (struct stat));

- if (homedir)
- {
- homeinit = std::string (homedir) + SLASH_STRING + GDBINIT;
- if (stat (homeinit.c_str (), &homebuf) != 0)
- {
- homeinit = "";
- }
- }
+ homeinit = find_gdb_home_config_file (GDBINIT, &homebuf);

if (stat (GDBINIT, &cwdbuf) == 0)
{
@@ -328,7 +319,7 @@ get_init_files (std::vector<std::string> *system_gdbinit,
sizeof (struct stat)))
localinit = GDBINIT;
}
-
+
initialized = 1;
}

diff --git a/gdbsupport/pathstuff.cc b/gdbsupport/pathstuff.cc
index 9fb5e5cf614..a52e53b8671 100644
--- a/gdbsupport/pathstuff.cc
+++ b/gdbsupport/pathstuff.cc
@@ -23,6 +23,10 @@
#include "filenames.h"
#include "gdb_tilde_expand.h"

+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
#ifdef USE_WIN32API
#include <windows.h>
#endif
@@ -298,6 +302,51 @@ get_standard_config_dir ()
return {};
}

+/* See pathstuff.h. */
+
+std::string
+get_standard_config_filename (const char *filename)
+{
+ std::string config_dir = get_standard_config_dir ();
+ if (config_dir != "")
+ {
+ const char *tmp = (*filename == '.') ? (filename + 1) : filename;
+ std::string path = config_dir + SLASH_STRING + std::string (tmp);
+ return path;
+ }
+
+ return {};
+}
+
+/* See pathstuff.h. */
+
+std::string
+find_gdb_home_config_file (const char *name, struct stat *buf)
+{
+ gdb_assert (name != nullptr);
+ gdb_assert (*name != '\0');
+
+ std::string config_dir_file = get_standard_config_filename (name);
+ if (!config_dir_file.empty ())
+ {
+ if (stat (config_dir_file.c_str (), buf) == 0)
+ return config_dir_file;
+ }
+
+ const char *homedir = getenv ("HOME");
+ if (homedir != nullptr)
+ {
+ /* Make sure the path is absolute and tilde-expanded. */
+ gdb::unique_xmalloc_ptr<char> abs (gdb_abspath (homedir));
+ std::string path = (std::string (abs.get ()) + SLASH_STRING
+ + std::string (name));
+ if (stat (path.c_str (), buf) == 0)
+ return path;
+ }
+
+ return {};
+}
+
/* See gdbsupport/pathstuff.h. */

const char *
diff --git a/gdbsupport/pathstuff.h b/gdbsupport/pathstuff.h
index 85241bc8c7c..996c8f2bbf6 100644
--- a/gdbsupport/pathstuff.h
+++ b/gdbsupport/pathstuff.h
@@ -99,6 +99,29 @@ extern std::string get_standard_temp_dir ();

extern std::string get_standard_config_dir ();

+/* Look for FILENAME in the standard configuration directory as returned by
+ GET_STANDARD_CONFIG_DIR and return the path to the file. No check is
+ performed that the file actually exists or not.
+
+ If FILENAME begins with a '.' then the path returned will remove the
+ leading '.' character, for example passing '.gdbinit' could return the
+ path '/home/username/.config/gdb/gdbinit'. */
+
+extern std::string get_standard_config_filename (const char *filename);
+
+/* Look for a file called NAME in either the standard config directory or
+ in the users home directory. If a suitable file is found then *BUF will
+ be filled with the contents of a call to 'stat' on the found file,
+ otherwise *BUF is undefined after this call.
+
+ If NAME starts with a '.' character then, when looking in the standard
+ config directory the file searched for has the '.' removed. For
+ example, if NAME is '.gdbinit' then on a Linux target GDB might look for
+ '~/.config/gdb/gdbinit' and then '~/.gdbinit'. */
+
+extern std::string find_gdb_home_config_file (const char *name,
+ struct stat *buf);
+
/* Return the file name of the user's shell. Normally this comes from
the SHELL environment variable. */

--
2.25.4