changeset 27832:8fd7d1d2a4ca

Read startup files from XDG_CONFIG_HOME or LOCALAPPDATA (bug #36477). * oct-env.[cc,h] (get_user_config_directory, do_get_user_config_directory): New functions. * interpreter.cc (execute_startup_files): Try to execute the octaverc file at a platform dependent location. * doc/interpreter/basics.txi: Document new location for startup file. * NEWS: Announce change.
author Mike Miller <mtmiller@octave.org>
date Wed, 06 Mar 2019 14:54:38 -0800
parents b42e4b3dee5d
children c0ff4cb335a0
files NEWS doc/interpreter/basics.txi libinterp/corefcn/interpreter.cc liboctave/system/oct-env.cc liboctave/system/oct-env.h
diffstat 5 files changed, 93 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Sat Dec 14 19:07:44 2019 +0100
+++ b/NEWS	Wed Mar 06 14:54:38 2019 -0800
@@ -54,6 +54,11 @@
   `native2unicode` to convert from your preferred locale.  For example,
   the copyright symbol in UTF-8 is `native2unicode (169, "latin1")`.
 
+- The startup file `octaverc` now can be located in the platform
+  dependent location for user local configuration files (e.g.
+  ${XDG_CONFIG_HOME}/octave/octaverc on Unix-like operating systems or
+  %LOCALAPPDATA%\octave\octaverc on Windows).
+
 #### Graphics backend
 
 - The `legend` function has been entirely rewritten.  This fixes a
--- a/doc/interpreter/basics.txi	Sat Dec 14 19:07:44 2019 +0100
+++ b/doc/interpreter/basics.txi	Wed Mar 06 14:54:38 2019 -0800
@@ -340,7 +340,7 @@
 @table @code
 @item @var{octave-home}/share/octave/site/m/startup/octaverc
 @cindex site startup file
-where @var{octave-home} is the directory in which Octave is installed
+where @code{@var{octave-home}} is the directory in which Octave is installed
 (the default is @file{/usr/local}).
 This file is provided so that changes to the default Octave environment
 can be made globally for all users at your site for all versions of Octave
@@ -350,38 +350,42 @@
 
 @item @var{octave-home}/share/octave/@var{version}/m/startup/octaverc
 @cindex version startup file
-where @var{octave-home} is the directory in which Octave is
-installed (the default is @file{/usr/local}), and @var{version}
-is the version number of Octave.  This file is provided so that changes
-to the default Octave environment can be made globally for all users of
-a particular version of Octave.  Care should be taken when making
-changes to this file since all users of Octave at your site will be
-affected.  The default file may be overridden by the environment variable
-@w{@env{OCTAVE_VERSION_INITFILE}}.
+where @code{@var{octave-home}} is the directory in which Octave is installed
+(the default is @file{/usr/local}), and @code{@var{version}} is the version
+number of Octave.  This file is provided so that changes to the default
+Octave environment can be made globally for all users of a particular version
+of Octave.  Care should be taken when making changes to this file since all
+users of Octave at your site will be affected.  The default file may be
+overridden by the environment variable @w{@env{OCTAVE_VERSION_INITFILE}}.
+
+@item @var{config-dir}/octave/octaverc
+@cindex personal startup file
+where @code{@var{config-dir}} is the platform-dependent location for user
+local configuration files (e.g. @w{@env{$XDG_CONFIG_HOME}} on many Unix-like
+operating systems or @w{@env{%LOCALAPPDATA%}} on Windows).
 
 @item ~/.octaverc
 @cindex personal startup file
 @cindex @sortas{octaverc ~/.octaverc} @code{~/.octaverc}
-This file is used to make personal changes to the default
-Octave environment.
+This file is used to make personal changes to the default Octave environment.
 
 @item .octaverc
 @cindex project startup file
 @cindex @sortas{octaverc .octaverc} @code{.octaverc}
-This file can be used to make changes to the default Octave environment
-for a particular project.  Octave searches for this file in the current
-directory after it reads @file{~/.octaverc}.  Any use of the @code{cd}
-command in the @file{~/.octaverc} file will affect the directory where
-Octave searches for @file{.octaverc}.
+This file can be used to make changes to the default Octave environment for a
+particular project.  Octave searches for this file in the current directory
+after it reads @file{~/.octaverc}.  Any use of the @code{cd} command in the
+@file{~/.octaverc} file will affect the directory where Octave searches for
+@file{.octaverc}.
 
 If you start Octave in your home directory, commands from the file
 @file{~/.octaverc} will only be executed once.
 
 @item startup.m
 @cindex @code{startup.m}
-This file is used to make personal changes to the default
-Octave environment.  It is executed for @sc{matlab} compatibility, but
-@file{~/.octaverc} is the preferred location for configuration changes.
+This file is used to make personal changes to the default Octave environment.
+It is executed for @sc{matlab} compatibility, but @file{~/.octaverc} is the
+preferred location for configuration changes.
 @end table
 
 A message will be displayed as each of the startup files is read if you
--- a/libinterp/corefcn/interpreter.cc	Sat Dec 14 19:07:44 2019 +0100
+++ b/libinterp/corefcn/interpreter.cc	Wed Mar 06 14:54:38 2019 -0800
@@ -34,6 +34,7 @@
 #include "cmd-hist.h"
 #include "file-ops.h"
 #include "file-stat.h"
+#include "file-ops.h"
 #include "fpucw-wrappers.h"
 #include "lo-blas-proto.h"
 #include "lo-error.h"
@@ -891,6 +892,26 @@
               }
           }
 
+        // Try to execute commands from $CONFIG/octave/octaverc, where
+        // $CONFIG is the platform-dependent location for user local
+        // configuration files.
+
+        std::string user_config_dir = sys::env::get_user_config_directory ();
+
+        std::string cfg_dir = user_config_dir + sys::file_ops::dir_sep_str ()
+                            + "octave";
+
+        std::string cfg_rc = sys::env::make_absolute ("octaverc", cfg_dir);
+
+        if (! cfg_rc.empty ())
+          {
+            int status = safe_source_file (cfg_rc, context, verbose,
+                                           require_file);
+
+            if (status)
+              exit_status = status;
+          }
+
         // Try to execute commands from $HOME/$OCTAVE_INITFILE and
         // $OCTAVE_INITFILE.  If $OCTAVE_INITFILE is not set,
         // .octaverc is assumed.
--- a/liboctave/system/oct-env.cc	Sat Dec 14 19:07:44 2019 +0100
+++ b/liboctave/system/oct-env.cc	Wed Mar 06 14:54:38 2019 -0800
@@ -57,6 +57,13 @@
 #include "singleton-cleanup.h"
 #include "unistd-wrappers.h"
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include "uniconv-wrappers.h"
+
+#  include <windows.h>
+#  include <shlobj.h>
+#endif
+
 namespace octave
 {
   namespace sys
@@ -148,6 +155,13 @@
     }
 
     std::string
+    env::get_user_config_directory ()
+    {
+      return (instance_ok ())
+        ? instance->do_get_user_config_directory () : "";
+    }
+
+    std::string
     env::get_program_name (void)
     {
       return (instance_ok ())
@@ -221,6 +235,32 @@
       return tempd;
     }
 
+    std::string
+    env::do_get_user_config_directory (void) const
+    {
+      std::string cfg_dir;
+
+#if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && defined (OCTAVE_USE_WINDOWS_API)
+      wchar_t path[MAX_PATH+1];
+      if (SHGetFolderPathW (nullptr,
+                            CSIDL_LOCAL_APPDATA | CSIDL_FLAG_DONT_VERIFY,
+                            nullptr, SHGFP_TYPE_CURRENT, path) == S_OK)
+        {
+          char *local_app_data = u8_from_wchar (path);
+          cfg_dir = local_app_data;
+          free (local_app_data);
+        }
+#else
+      cfg_dir = do_getenv ("XDG_CONFIG_HOME");
+
+      if (cfg_dir.empty ())
+        cfg_dir = do_get_home_directory () + sys::file_ops::dir_sep_str ()
+             + ".config";
+#endif
+
+      return cfg_dir;
+    }
+
     // FIXME: this leaves no way to distinguish between a
     // variable that is not set and one that is set to the empty string.
     // Is this a problem?
--- a/liboctave/system/oct-env.h	Sat Dec 14 19:07:44 2019 +0100
+++ b/liboctave/system/oct-env.h	Wed Mar 06 14:54:38 2019 -0800
@@ -65,6 +65,8 @@
 
       static std::string get_temp_directory (void);
 
+      static std::string get_user_config_directory (void);
+
       static std::string get_program_name (void);
 
       static std::string get_program_invocation_name (void);
@@ -104,6 +106,8 @@
 
       std::string do_get_temp_directory (void) const;
 
+      std::string do_get_user_config_directory (void) const;
+
       std::string do_get_user_name (void) const;
 
       std::string do_get_host_name (void) const;