From 1d30e3d975038572a922b67a2ce509b8e64d9d7f Mon Sep 17 00:00:00 2001
From: Andreas Grapentin <andreas@grapentin.org>
Date: Sun, 29 Nov 2020 10:01:22 +0100
Subject: [PATCH] abook.c, abook.h: enable support for XDG_CONFIG_HOME and
 XDG_DATA_HOME

---
 abook.1   |   4 +-
 abook.c   | 125 ++++++++++++++++++++++++++++++++++++++++++++++++------
 abook.h   |   1 +
 abookrc.5 |   5 ++-
 4 files changed, 118 insertions(+), 17 deletions(-)

diff --git a/abook.1 b/abook.1
index 96d665d..2641f28 100644
--- a/abook.1
+++ b/abook.1
@@ -16,10 +16,10 @@ equally useful on its own.
 Show usage.
 .TP
 \fB\-C \-\-config\fP \fI<filename>\fR
-Use an alternative configuration file (default is \fI$HOME/.abook/abookrc\fR).
+Use an alternative configuration file (default is \fI$XDG_CONFIG_HOME/abook/abookrc\fR or \fI$HOME/.abook/abookrc\fR).
 .TP
 \fB\-f \-\-datafile\fP \fI<filename>\fR
-Use an alternative addressbook file (default is \fI$HOME/.abook/addressbook\fR).
+Use an alternative addressbook file (default is \fI$XDG_DATA_HOME/abook/addressbook\fR or \fI$HOME/.abook/addressbook\fR).
 .TP
 \fB\-\-mutt\-query\fP \fI<string>\fR [ \fB\-\-outformat\fP \fI<outputformat>\fR ]
 Make a query for mutt (search the addressbook for \fI<string>\fR).
diff --git a/abook.c b/abook.c
index eeef5d7..125c693 100644
--- a/abook.c
+++ b/abook.c
@@ -78,31 +78,63 @@ check_abook_directory()
 {
 	struct stat s;
 	char *dir;
+	char *xdg_data_home;
+	int xdg_data_home_needs_free = 0;
 
 	assert(!is_ui_initialized());
 
 	if(alternative_datafile)
 		return;
 
-	dir = strconcat(getenv("HOME"), "/" DIR_IN_HOME, NULL);
+	xdg_data_home = getenv("XDG_DATA_HOME");
+	if(!xdg_data_home) {
+		xdg_data_home = strconcat(getenv("HOME"), "/.local/share/", NULL);
+		assert(xdg_data_home != NULL);
+		xdg_data_home_needs_free = 1;
+	}
+
+	dir = strconcat(xdg_data_home, "/" DIR_IN_XDG, NULL);
 	assert(dir != NULL);
 
+	if (xdg_data_home_needs_free)
+		free(xdg_data_home);
+
 	if(stat(dir, &s) == -1) {
 		if(errno != ENOENT) {
-			perror(dir);
-                        free(dir);
-                        exit(EXIT_FAILURE);
-		}
-		if(mkdir(dir, 0700) == -1) {
-			printf(_("Cannot create directory %s\n"), dir);
 			perror(dir);
 			free(dir);
 			exit(EXIT_FAILURE);
+		} else {
+			free(dir);
+			dir = NULL;
+			errno = 0;
 		}
 	} else if(!S_ISDIR(s.st_mode)) {
-		printf(_("%s is not a directory\n"), dir);
 		free(dir);
-		exit(EXIT_FAILURE);
+		dir = NULL;
+	}
+
+	if(dir == NULL) {
+		dir = strconcat(getenv("HOME"), "/" DIR_IN_HOME, NULL);
+		assert(dir != NULL);
+
+		if(stat(dir, &s) == -1) {
+			if(errno != ENOENT) {
+				perror(dir);
+				free(dir);
+				exit(EXIT_FAILURE);
+			}
+			if(mkdir(dir, 0700) == -1) {
+				printf(_("Cannot create directory %s\n"), dir);
+				perror(dir);
+				free(dir);
+				exit(EXIT_FAILURE);
+			}
+		} else if(!S_ISDIR(s.st_mode)) {
+			printf(_("%s is not a directory\n"), dir);
+			free(dir);
+			exit(EXIT_FAILURE);
+		}
 	}
 
 	free(dir);
@@ -226,19 +258,86 @@ static void
 set_filenames()
 {
 	struct stat s;
+	char *xdg_config_home;
+	char *xdg_data_home;
+
+	char *datadir;
+	char *configdir;
 
 	if( (stat(getenv("HOME"), &s)) == -1 || ! S_ISDIR(s.st_mode) ) {
 		fprintf(stderr,_("%s is not a valid HOME directory\n"), getenv("HOME") );
 		exit(EXIT_FAILURE);
 	}
 
-	if(!datafile)
-		datafile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
+	if(!datafile) {
+		xdg_data_home = getenv("XDG_DATA_HOME");
+		if (!xdg_data_home) {
+			datadir = strconcat(getenv("HOME"), "/.local/share/"
+				DIR_IN_XDG, NULL);
+		} else {
+			datadir = strconcat(xdg_data_home, "/" DIR_IN_XDG, NULL);
+		}
+		assert(datadir != NULL);
+
+		if(stat(datadir, &s) == -1) {
+			if(errno != ENOENT) {
+				perror(datadir);
+				free(datadir);
+				exit(EXIT_FAILURE);
+			} else {
+				free(datadir);
+				datadir = NULL;
+				errno = 0;
+			}
+		} else if(!S_ISDIR(s.st_mode)) {
+			free(datadir);
+			datadir = NULL;
+		}
+
+		if(datadir == NULL) {
+			datafile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
 				DATAFILE, NULL);
+		} else {
+			datafile = strconcat(datadir, "/" DATAFILE, NULL);
+			free(datadir);
+		}
+		assert(datafile != NULL);
+	}
 
-	if(!rcfile)
-		rcfile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
+	if(!rcfile) {
+		xdg_config_home = getenv("XDG_CONFIG_HOME");
+		if (!xdg_config_home) {
+			configdir = strconcat(getenv("HOME"), "/.config/"
+				DIR_IN_XDG, NULL);
+		} else {
+			configdir = strconcat(xdg_config_home, "/" DIR_IN_XDG, NULL);
+		}
+		assert(configdir != NULL);
+
+		if(stat(configdir, &s) == -1) {
+			if(errno != ENOENT) {
+				perror(configdir);
+				free(configdir);
+				exit(EXIT_FAILURE);
+			} else {
+				free(configdir);
+				configdir = NULL;
+				errno = 0;
+			}
+		} else if(!S_ISDIR(s.st_mode)) {
+			free(configdir);
+			configdir = NULL;
+		}
+
+		if(configdir == NULL) {
+			rcfile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
 				RCFILE, NULL);
+		} else {
+			rcfile = strconcat(configdir, "/" RCFILE, NULL);
+			free(configdir);
+		}
+		assert(rcfile != NULL);
+	}
 
 	atexit(free_filenames);
 }
diff --git a/abook.h b/abook.h
index 0631b8f..555bcda 100644
--- a/abook.h
+++ b/abook.h
@@ -22,6 +22,7 @@ int		strncasecmp (const char *, const char *, size_t);
 
 #define DEFAULT_UMASK	066
 #define DIR_IN_HOME	".abook"
+#define DIR_IN_XDG	"abook"
 #define DATAFILE	"addressbook"
 
 #define RCFILE		"abookrc"
diff --git a/abookrc.5 b/abookrc.5
index 8fbb10b..7812f84 100644
--- a/abookrc.5
+++ b/abookrc.5
@@ -1,7 +1,7 @@
 .TH ABOOKRC 5 "Oct 25, 2005"
 .nh
 .SH NAME
-\fB$HOME/.abook/abookrc\fP \- configuration file for abook address book program
+\fBabookrc\fP \- configuration file for abook address book program
 .SH DESCRIPTION
 .B abookrc
 is the (optional) configuration file for use with the
@@ -9,7 +9,8 @@ is the (optional) configuration file for use with the
 address book program.
 
 .B abookrc
-is stored in a user's $HOME/.abook directory. It follows a format of
+is stored in a user's $XDG_CONFIG_HOME/abook/ directory, or alternatively a user's
+$HOME/.abook directory. It follows a format of
 \(lq\fBset option=value\fP\(rq.
 The following information lists each of these options and the values they
 may take.
-- 
2.29.2

