xdg.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /*! \file
3  * \brief Implementation of XDG Base Directory Specification
4  *
5  * Copyright (c) 2020-2024 by the developers. See the LICENSE file for details.
6  *
7  * This is used by different modules that should not depend on CORE module.
8  */
9 
10 
11 /* ========================================================================== */
12 /* Include headers */
13 
14 #include "posix.h" /* Include this first because of feature test macros */
15 
16 #include <string.h>
17 
18 #include "fileutils.h"
19 #include "main.h"
20 #include "xdg.h"
21 
22 
23 /* ========================================================================== */
24 /*! \defgroup XDG XDG: Freedesktop.org Base Directory Specification
25  *
26  * Implementation of XDG Base Directory Specification (version 0.7).
27  */
28 /*! @{ */
29 
30 
31 /* ========================================================================== */
32 /* Constants */
33 
34 /*! \brief Message prefix for XDG module */
35 #define MAIN_ERR_PREFIX "XDG: "
36 
37 
38 /* ========================================================================== */
39 /*! \brief Append path component to buffer
40  *
41  * \param[in] buf Pointer to buffer pointer
42  * \param[in] newcomp New path component to append
43  *
44  * First a slash "/" is appended to the content of buf.
45  * Then, if \e newcomp is not \c NULL , it is appended after the slash.
46  * Additional memory is automatically allocated.
47  *
48  * \attention Dereferenced \e buf must be usable for \c realloc() .
49  *
50  * \return
51  * - Zero on success
52  * - Negative value on error
53  */
54 
55 int xdg_append_to_path(const char** buf, const char* newcomp)
56 {
57  int res = 0;
58  char* tmp;
59  size_t len_buf = 0;
60  size_t len_new = 0;
61 
62  if(NULL != *buf)
63  {
64  len_buf = strlen(*buf);
65  }
66  if(NULL != newcomp)
67  {
68  len_new = strlen(newcomp);
69  }
70 
71  /* Allocate 2 additional bytes for "/" separator and string termination */
72  tmp = (char*) api_posix_realloc((void*) *buf,
73  len_buf + len_new + (size_t) 2);
74  if(NULL == tmp)
75  {
76  PRINT_ERROR("Cannot allocate memory for path or pathname");
77  res = -1;
78  }
79  else
80  {
81  tmp[len_buf++] = '/'; /* Increment length for slash */
82  if(NULL != newcomp)
83  {
84  memcpy((void*) &tmp[len_buf], (void*) newcomp, len_new);
85  }
86  /* Terminate new string */
87  tmp[len_buf + len_new] = 0;
88  *buf = tmp;
89  }
90 
91  return(res);
92 }
93 
94 
95 /* ========================================================================== */
96 /*! \brief Get configuration directory
97  *
98  * \param[in] progname Program name
99  *
100  * If \e progname is \c NULL then \c $XDG_CONFIG_HOME is returned. Otherwise
101  * \e progname is appended as subdirectory and $XDG_CONFIG_HOME/progname is
102  * returned.
103  *
104  * If \c XDG_CONFIG_HOME is not defined, the default \c $HOME/.config is used
105  * (it is treated as error if \c HOME is not defined in this case).
106  *
107  * \note
108  * On success, the caller is responsible to free the memory allocated for the
109  * result.
110  *
111  * \return
112  * - Pointer to result on success
113  * - \c NULL on error
114  */
115 
116 const char* xdg_get_confdir(const char* progname)
117 {
118  static int confpath_msg_printed = 0;
119  size_t len;
120  char* tmp;
121  const char* buf = NULL;
122 
123  /* User specified path has higher precedence than XDG_CONFIG_HOME */
124  if(NULL != main_confprefix)
125  {
126  if('/' != main_confprefix[0])
127  {
128  PRINT_ERROR("No absolute path specified for configuration directory");
129  goto error;
130  }
131 
132  /* Use path specified by user */
133  len = strlen(main_confprefix);
134  tmp = (char*) api_posix_malloc(++len);
135  if(NULL == tmp)
136  {
137  goto error;
138  }
139  memcpy(tmp, main_confprefix, len);
140  buf = tmp;
141  }
142  else
143  {
144  /* Use path specified by XDG */
145  if(0 == ts_getenv("XDG_CONFIG_HOME", &buf))
146  {
147  /* XDG_CONFIG_HOME must contain an absolute path */
148  if('/' != buf[0])
149  {
150  PRINT_ERROR("XDG_CONFIG_HOME present, but not an absolute path");
151  goto error;
152  }
153  }
154  else
155  {
156  /* "XDG_CONFIG_HOME" not defined => Use default "$HOME/.config" */
157  if(0 != ts_getenv("HOME", &buf))
158  {
159  /* Treat missing "HOME" as error */
160  PRINT_ERROR("Environment variable HOME is not defined");
161  goto error;
162  }
163  else
164  {
165  if(0 != xdg_append_to_path(&buf, ".config"))
166  {
167  goto error;
168  }
169  }
170  }
171 
172  /* Append program name, if specified */
173  if(NULL != progname)
174  {
175  if(0 != xdg_append_to_path(&buf, progname))
176  {
177  goto error;
178  }
179  }
180  }
181 
182  /* Check path for unsupported characters */
183  if(0 != fu_check_path(buf))
184  {
185  goto error;
186  }
187 
188  /* Create configuration path, if it doesn't exist */
189  if(0 != fu_create_path(buf, (api_posix_mode_t) API_POSIX_S_IRWXU))
190  {
191  goto error;
192  }
193 
194  if(main_debug)
195  {
196  /* Print debug message only once */
197  if(!confpath_msg_printed)
198  {
199  printf("%s: %sConfiguration path: %s\n",
200  CFG_NAME, MAIN_ERR_PREFIX, buf);
201  confpath_msg_printed = 1;
202  }
203  }
204 
205 exit:
206  return(buf);
207 
208 error:
209  api_posix_free((void*) buf);
210  buf = NULL;
211  goto exit;
212 }
213 
214 
215 /*! @} */
216 
217 /* EOF */
MAIN_ERR_PREFIX
#define MAIN_ERR_PREFIX
Message prefix for XDG module.
Definition: xdg.c:35
main_debug
int main_debug
Enable additional debug output if nonzero.
Definition: main.cxx:64
fu_create_path
int fu_create_path(const char *path, api_posix_mode_t perm)
Create path.
Definition: fileutils.c:122
PRINT_ERROR
#define PRINT_ERROR(s)
Prepend module prefix and print error message.
Definition: main.h:19
ts_getenv
int ts_getenv(const char *, const char **)
Thread safe replacement for getenv()
Definition: ts_functions.c:136
main_confprefix
const char * main_confprefix
Configuration directory path from command line option or NULL otherwise.
Definition: main.cxx:67
xdg_get_confdir
const char * xdg_get_confdir(const char *progname)
Get configuration directory.
Definition: xdg.c:116
xdg_append_to_path
int xdg_append_to_path(const char **buf, const char *newcomp)
Append path component to buffer.
Definition: xdg.c:55
fu_check_path
int fu_check_path(const char *path)
Check path.
Definition: fileutils.c:76

Generated at 2026-01-27 using  doxygen