Re: [PATCH] XDG base directory support (history and wxt)

Jun T <[email protected]>
Newsgroups gmane.comp.graphics.gnuplot.devel
Message-ID <[email protected]>
> 2021/09/14 4:06, Ethan A Merritt <[email protected]> wrote:
> 
>  I realize that the previous XDG patch already put a
> chunk of code in plot.c to hande the gnuplotrc file, but wouldn't it
> be better to have that code and the new patch code for the history file
> live in xdg.c instead?
> Then in plot.c or history.c the call site would
> be:
> 
> #ifdef USE_XDG_BASEDIR
>    expanded_history_filename = some_new_xdg_routine(GNUPLOT_HISTORY_FILE));
> #endif
>    if (!expanded_history_filename) {
> 	/* current code */
> 	expanded_history_filename = tilde_expand(GNUPLOT_HISTORY_FILE);
>    }
>    read_history(expanded_history_filename);

How about the revised patch (xdg-v2.patch)?
I moved "most" of the complication into a function in xdg.c, but plot.c is
not as simple as above.


>> [2] Migration
>> With the patch, gnuplot will continue to use traditional config or history
>> files in ~/ if they exist (as it does for the main config file ~/.gnuplot).
>> Users must move the traditional files to XDG dirs (or remove ~/.gnuplot-wxt
>> and ~/.gnuplot_history) manually if they want to migrate.
> 
> Isn't the obvious migration to set
> 	XDG_DATA_DIRS = $HOME:$HOME/.local/share
> 	XDG_CONFIG_HOME = $HOME:$HOME/.config

I couldn't understand this part...

> Your current home directory files will be used whether or not
> you have XDG enabled, so moving files is not required.

> If you choose to enable XDG and move the files, that works also.

Yes, this is the case with my match.

As I wrote, with my patch, if ~/.gnuplot_history or ~/.gnuplot-wxt exists
gnuplot will continue to use them. User need not do anything if they are
satisfied with this traditional behavior.

If a user wants to use XDG directory, she/he must manually move
~/.gnuplot_history to ~/.local/state/gnuplot_history, and
~/.gnuplot-wxt to ~/.config/gnuplot/gnuplot-wxt.conf
(or just remove them), but need not set XDG_CONFIG_HOME etc.

(if XDG_CONFIG_HOME is set then it is used instead of ~/.config/, of course)

Is this OK?

_______________________________________________
gnuplot-beta mailing list
[email protected]
Membership management via: https://lists.sourceforge.net/lists/listinfo/gnuplot-beta
xdg-v2.patch (application/octet-stream, 7.9 KB)
diff --git a/src/gp_hist.h b/src/gp_hist.h
index 1a938a609..dc6b3d616 100644
--- a/src/gp_hist.h
+++ b/src/gp_hist.h
@@ -75,7 +75,7 @@ extern int history_base;
 void using_history(void);
 void clear_history(void);
 void add_history(char *line);
-void read_history(char *);
+int read_history(char *);
 int write_history(char *);
 int where_history(void);
 int history_set_pos(int offset);
diff --git a/src/history.c b/src/history.c
index 109018116..0c9999e5c 100644
--- a/src/history.c
+++ b/src/history.c
@@ -88,10 +88,10 @@ write_history(char *filename)
 
 /* routine to read history entries from a file
  */
-void
+int
 read_history(char *filename)
 {
-    gp_read_history(filename);
+    return gp_read_history(filename);
 }
 
 
diff --git a/src/plot.c b/src/plot.c
index 66fdb6e45..9770ff9b1 100644
--- a/src/plot.c
+++ b/src/plot.c
@@ -495,7 +495,19 @@ main(int argc_orig, char **argv)
 	    expanded_history_filename = gp_strdup(GNUPLOT_HISTORY_FILE);
 	    gp_expand_tilde(&expanded_history_filename);
 #endif
-	    read_history(expanded_history_filename);
+	    if (read_history(expanded_history_filename)) {
+#ifdef USE_XDG_BASEDIR
+		/* use $XDG_STATE_DIR/gnuplot_history only if
+		 * $XDG_STATE_DIR exists or can be created */
+		char *xdgpath = xdg_get_path(kXDGStateHome, "gnuplot_history",
+				FALSE /* no subdir */, TRUE /* create */);
+		if (xdgpath) {
+		    free(expanded_history_filename);
+		    expanded_history_filename = xdgpath;
+		    read_history(expanded_history_filename);
+		}
+#endif /* USE_XDG_BASEDIR */
+	    }
 
 	    /*
 	     * It is safe to ignore the return values of 'atexit()' and
@@ -790,13 +802,10 @@ load_rcfile(int where)
 	plotrc = fopen(rcfile, "r");
     } else if (where == 3) {
 #ifdef USE_XDG_BASEDIR
-	char * XDGConfigHome = xdg_get_var(kXDGConfigHome);
-	size_t len = strlen(XDGConfigHome);
-	rcfile = gp_alloc(len + 1 + sizeof("gnuplot/gnuplotrc"), "rcfile");
-	strcpy(rcfile, XDGConfigHome);
-	PATH_CONCAT(rcfile, "gnuplot/gnuplotrc");
-	plotrc = fopen(rcfile, "r");
-	free(XDGConfigHome);
+	rcfile = xdg_get_path(kXDGConfigHome, "gnuplotrc",
+			TRUE /* use subdir */, FALSE /* not create */);
+	if (rcfile)
+	    plotrc = fopen(rcfile, "r");
 #endif
     }
 
diff --git a/src/wxterminal/wxt_gui.cpp b/src/wxterminal/wxt_gui.cpp
index 7e59ee655..d79fd818b 100644
--- a/src/wxterminal/wxt_gui.cpp
+++ b/src/wxterminal/wxt_gui.cpp
@@ -120,6 +120,7 @@
 #include <wx/printdlg.h>
 
 extern "C" {
+#include "xdg.h"
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
@@ -327,13 +328,28 @@ bool wxtApp::OnInit()
 	/* application and vendor name are used by wxConfig to construct the name
 	 * of the config file/registry key and must be set before the first call
 	 * to Get() */
-	SetVendorName(wxT("gnuplot"));
-	SetAppName(wxT("gnuplot-wxt"));
-	wxConfigBase *pConfig = wxConfigBase::Get();
+	const wxString vendorName(wxT("gnuplot"));
+	const wxString appName(wxT("gnuplot-wxt"));
+	SetVendorName(vendorName);
+	SetAppName(appName);
+
+#ifdef USE_XDG_BASEDIR
+	if (!wxFileConfig::GetLocalFile(appName).Exists()) {
+		// Traditional config file (~/.gnuplot-wxt) does not exist.
+		// We will use $XDG_CONFIG_HOME/gnuplot/gnuplot-wxt.conf only if
+		// $XDG_CONFIG_HOME/gnuplot/ exists or can be created.
+		char *configFile = xdg_get_path(kXDGConfigHome, "gnuplot-wxt.conf",
+									TRUE /* subdir */, TRUE /* create */);
+		if (configFile) {
+			wxConfig::Set(new wxFileConfig(appName, vendorName, configFile));
+			free(configFile);
+		}
+	}
+#endif
 	/* this will force writing back of the defaults for all values
 	 * if they're not present in the config - this can give the user an idea
 	 * of all possible settings */
-	pConfig->SetRecordDefaults();
+	wxConfig::Get()->SetRecordDefaults();
 
 	FPRINTF((stderr, "OnInit finished\n"));
 
diff --git a/src/xdg.c b/src/xdg.c
index e6652d329..513236a96 100644
--- a/src/xdg.c
+++ b/src/xdg.c
@@ -1,8 +1,9 @@
 #include "xdg.h"
+#ifdef USE_XDG_BASEDIR
+
 #include "plot.h"
 #include "util.h"
-
-#ifdef USE_XDG_BASEDIR
+#include "alloc.h"
 
 #include <assert.h>
 #include <stdio.h>
@@ -13,6 +14,7 @@
 static const char *xdg_env_vars[] = {
     [kXDGConfigHome] = "XDG_CONFIG_HOME",
     [kXDGDataHome] = "XDG_DATA_HOME",
+    [kXDGStateHome] = "XDG_STATE_HOME",
     [kXDGCacheHome] = "XDG_CACHE_HOME",
     [kXDGRuntimeDir] = "XDG_RUNTIME_DIR",
     [kXDGConfigDirs] = "XDG_CONFIG_DIRS",
@@ -25,36 +27,69 @@ static const char *xdg_env_vars[] = {
 static const char *const xdg_defaults[] = {
     [kXDGConfigHome] = "~/.config",
     [kXDGDataHome] = "~/.local/share",
+    [kXDGStateHome] = "~/.local/state",
     [kXDGCacheHome] = "~/.cache",
     [kXDGRuntimeDir] = "",
     [kXDGConfigDirs] = "/etc/xdg/",
     [kXDGDataDirs] = "/usr/local/share/:/usr/share/",
 };
 
+/* helper function: return TRUE if dirname exists or can be created */
+
+static TBOOLEAN check_dir(const char *dirname) {
+#ifdef HAVE_SYS_STAT_H
+     return existdir(dirname) || !mkdir(dirname, 00700);
+#else	/* I believe this does not happen */
+     return FALSE;
+#endif
+}
+
+/* name used for subdirectory */
+static const char *appname = "gnuplot";
 
-/* Return XDG variable value
+/* Return pathname of XDG base directory or a file in it
  *
- * First query the environement variable and if that does not exist fall back
- * to the defaults. Further reading:
+ * XDG base directory specification:
  * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
  *
- * @param[in]  idx  XDG variable to use.
+ * @param[in]  idx     XDG variable to use.
+ * @param[in]  fname   if not NULL, name of a file in the base directory
+ * @param[in]  subdir  if TRUE, append "/gnuplot" to the base directory
+ * @param[in]  create  if TRUE, try to create the directory
  *
- * @return [allocated] variable value.
+ * @return [allocated] pathname of the base directory if fname is NULL,
+ *                     else of the file in the directory. Returns NULL if
+ *                     create is TURE but the directory can't be created.
  */
-char *xdg_get_var(const XDGVarType idx) {
-    /* Check the environment variable. If it is there, we are done. */
-    char *XDGVar;
-    if ((XDGVar = getenv(xdg_env_vars[idx])) != NULL) {
-        return gp_strdup(XDGVar);
+char *xdg_get_path(XDGVarType idx, const char* fname,
+		    TBOOLEAN subdir, TBOOLEAN create) {
+    char *pathname;
+    if ((pathname = getenv(xdg_env_vars[idx]))) {
+        pathname = gp_strdup(pathname);	/* use the environment variable */
     }
-
-    /* Looks like the environment variable is not there.
-     * Load the default and run word expansion on it.
-     */
-    XDGVar = gp_strdup(xdg_defaults[idx]);
-    gp_expand_tilde(&XDGVar);
-    return XDGVar;
+    else {
+	pathname = gp_strdup(xdg_defaults[idx]);    /* use the default */
+	gp_expand_tilde(&pathname);
+    }
+    if (create && !check_dir(pathname)) {
+	free(pathname);
+	return NULL;
+    }
+    if (subdir) {
+	pathname = gp_realloc(pathname,
+			    strlen(pathname) + strlen(appname) + 2, "XDG");
+	PATH_CONCAT(pathname, appname);
+	if (create && !check_dir(pathname)) {
+	    free(pathname);
+	    return NULL;
+	}
+    }
+    if (fname) {
+	pathname = gp_realloc(pathname,
+			    strlen(pathname) + strlen(fname) + 2, "XDG");
+	PATH_CONCAT(pathname, fname);
+    }
+    return pathname;
 }
 
 #endif /* USE_XDG_BASEDIR */
diff --git a/src/xdg.h b/src/xdg.h
index cab5e1415..7510444ee 100644
--- a/src/xdg.h
+++ b/src/xdg.h
@@ -5,18 +5,22 @@
 
 #define USE_XDG_BASEDIR
 
+#include "syscfg.h" /* for TBOOLEAN */
+
 /* List of possible XDG variables */
 typedef enum {
   kXDGNone = -1,
   kXDGConfigHome,  /* XDG_CONFIG_HOME */
   kXDGDataHome,    /* XDG_DATA_HOME */
+  kXDGStateHome,   /* XDG_STATE_HOME */
   kXDGCacheHome,   /* XDG_CACHE_HOME */
   kXDGRuntimeDir,  /* XDG_RUNTIME_DIR */
   kXDGConfigDirs,  /* XDG_CONFIG_DIRS */
   kXDGDataDirs,    /* XDG_DATA_DIRS */
 } XDGVarType;
 
-char *xdg_get_var(const XDGVarType idx);
+char *xdg_get_path(XDGVarType idx, const char *fname,
+		    TBOOLEAN subdir, TBOOLEAN create);
 
 #endif /* USE_XDG_BASEDIR */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.