GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-299a28a7d0
user_config.c
Go to the documentation of this file.
1 /*!
2  * \file lib/gis/user_config.c
3  *
4  * \brief GIS Library - Routines related to user's GRASS configuration, tmp, and
5  * miscellaneous files.
6  *
7  * Functions related to the user's GRASS configuration, tmp, and
8  * miscellaneous files. Provides a set of routines for creating and
9  * accessing elements within the user's "rc" directory. The
10  * directory is in $HOME/.grass.<br>
11  *
12  * <b>NOTE:</b> As of 2001-03-25 this file is not hooked up. It is
13  * provided as a candidate for handling $HOME/.grass files and
14  * subdirectories. There may be more functionality desired (such as
15  * deletion routines, directory globs).<br>
16  *
17  * (C) 2001-2014 by the GRASS Development Team
18  *
19  * This program is free software under the GNU General Public License
20  * (>=v2). Read the file COPYING that comes with GRASS for details.
21  *
22  * \author Eric G Miller - egm2 at jps net
23  *
24  * \date 2007-04-14
25  */
26 
27 #include <grass/config.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <stdint.h>
34 #include <stddef.h>
35 #ifndef _WIN32
36 #include <pwd.h>
37 #endif
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <errno.h>
41 #include <grass/gis.h>
42 
43 /**************************************************************************
44  * _make_toplevel(): make user's toplevel config directory if it doesn't
45  * already exist. Adjust perms to 1700. Returns the toplevel directory
46  * path [caller must G_free ()] on success, or NULL on failure
47  *************************************************************************/
48 
49 #ifndef _WIN32 /* TODO */
50 static char *_make_toplevel(void)
51 {
52  size_t len;
53  int status;
54 
55 #ifdef __MINGW32__
56  char *defaulthomedir = "c:";
57  char *homedir = getenv("HOME");
58 #else
59  uid_t me;
60  struct passwd *my_passwd;
61 #endif
62  struct stat buf;
63  char *path;
64 
65  errno = 0;
66 
67  /* Query whatever database to get user's home dir */
68 #ifdef __MINGW32__
69  if (NULL == homedir) {
70  homedir = defaulthomedir;
71  }
72 
73  len = strlen(homedir) + 8; /* + "/.grass\0" */
74  if (NULL == (path = G_calloc(1, len))) {
75  return NULL;
76  }
77  snprintf(path, len, "%s%s", homedir, "/.grass");
78 #else
79  me = getuid();
80  my_passwd = getpwuid(me);
81  if (my_passwd == NULL)
82  return NULL;
83 
84  len = strlen(my_passwd->pw_dir) + 8; /* + "/.grass\0" */
85  if (NULL == (path = G_calloc(1, len)))
86  return NULL;
87 
88  snprintf(path, len, "%s%s", my_passwd->pw_dir, "/.grass");
89 #endif
90 
91  status = G_lstat(path, &buf);
92 
93  /* If errno == ENOENT, the directory doesn't exist */
94  if (status != 0) {
95  if (errno == ENOENT) {
96  status = G_mkdir(path);
97 
98  if (status != 0) { /* mkdir failed */
99  G_free(path);
100  return NULL;
101  }
102 
103  /* override umask settings, if possible */
104  chmod(path, S_IRWXU);
105 
106  /* otherwise mkdir succeeded, we're done here */
107  return path;
108  }
109 
110  /* other errors should not be defined ??? give up */
111  G_free(path);
112  return NULL;
113  }
114  /* implicit else */
115 
116  /* Examine the stat "buf" */
117  /* It better be a directory */
118  if (!S_ISDIR(buf.st_mode)) { /* File, link, something else */
119  errno = ENOTDIR; /* element is not a directory, but should be */
120  G_free(path);
121  return NULL;
122  }
123 
124  /* No read/write/execute ??? */
125  if (!((S_IRUSR & buf.st_mode) && (S_IWUSR & buf.st_mode) &&
126  (S_IXUSR & buf.st_mode))) {
127  errno = EACCES; /* Permissions error */
128  G_free(path);
129  return NULL;
130  }
131 
132  /* We'll assume that if the user grants greater permissions
133  * than we would, that they know what they're doing
134  * -- so we're done here...
135  */
136 
137  return path;
138 }
139 
140 /**************************************************************************
141  * _elem_count_split: Does a couple things:
142  * 1) Counts the number of elements in "elems"
143  * 2) Replaces occurrences of '/' with '\0'
144  * 3) Checks that no element begins with a '.'
145  * 4) Checks there are no '//'
146  *
147  * Therefore, THE STRING THAT IS PASSED IN IS MODIFIED
148  * Returns 0 if there are no elements, or an element
149  * beginning with a '.' or containing a '//' is found.
150  *************************************************************************/
151 static int _elem_count_split(char *elems)
152 {
153  int i;
154  size_t len;
155  char *begin, *end;
156 
157  /* Some basic assertions */
158  assert(elems != NULL);
159 
160  len = strlen(elems);
161  assert(len > 0);
162  assert(len < PTRDIFF_MAX);
163  assert(*elems != '/');
164 
165  begin = elems;
166  for (i = 0; begin != NULL && (ptrdiff_t)len > begin - elems; i++) {
167  /* check '.' condition */
168  if (*begin == '.')
169  return 0;
170  end = strchr(begin, '/');
171  /* check '//' condition */
172  if (end != NULL && end == begin)
173  return 0;
174  /* okay, change '/' into '\0' */
175  begin = end;
176  if (begin != NULL) {
177  *begin = '\0'; /* begin points at '/', change it */
178  begin++; /* increment begin to next char */
179  }
180  }
181 
182  /* That's it */
183  return i;
184 }
185 
186 /**************************************************************************
187  * _make_sublevels(): creates subelements as necessary from the passed
188  * "elems" string. It returns the full path if successful or NULL
189  * if it fails. "elems" must not be NULL, zero length, or have any
190  * elements that begin with a '.' or any occurrences of '//'.
191  *************************************************************************/
192 static char *_make_sublevels(const char *elems)
193 {
194  int i, status;
195  char *cp, *path, *top, *ptr;
196  struct stat buf;
197 
198  /* Get top level path */
199  if (NULL == (top = _make_toplevel()))
200  return NULL;
201 
202  /* Make a copy of elems */
203  if (NULL == (cp = G_store(elems))) {
204  G_free(top);
205  return NULL;
206  }
207 
208  /* Do element count, sanity checking and "splitting" */
209  if ((i = _elem_count_split(cp)) < 1) {
210  G_free(cp);
211  G_free(top);
212  return NULL;
213  }
214 
215  /* Allocate our path to be large enough */
216  size_t bufsize = strlen(top) + strlen(elems) + 2;
217  if ((path = G_calloc(1, bufsize)) == NULL) {
218  G_free(top);
219  G_free(cp);
220  return NULL;
221  }
222 
223  /* Now loop along adding directories if they don't exist
224  * make sure the thing is a directory as well.
225  * If there was a trailing '/' in the original "elem", it doesn't
226  * make it into the returned path.
227  */
228  for (; i > 0; i--) {
229  snprintf(path, bufsize, "%s/%s", top, cp);
230  errno = 0;
231  status = G_lstat(path, &buf);
232  if (status != 0) {
233  /* the element doesn't exist */
234  status = G_mkdir(path);
235  if (status != 0) {
236  /* Some kind of problem... */
237  G_free(top);
238  G_free(cp);
239  return NULL;
240  }
241  /* override umask settings, if possible */
242  chmod(path, S_IRWXU);
243  }
244  else {
245  /* Examine the stat "buf" */
246  /* It better be a directory */
247  if (!S_ISDIR(buf.st_mode)) { /* File, link, something else */
248  errno = ENOTDIR; /* element is not a directory, but should be */
249  G_free(path);
250  return NULL;
251  }
252 
253  /* No read/write/execute ??? */
254  if (!((S_IRUSR & buf.st_mode) && (S_IWUSR & buf.st_mode) &&
255  (S_IXUSR & buf.st_mode))) {
256  errno = EACCES; /* Permissions error */
257  G_free(path);
258  return NULL;
259  }
260 
261  /* okay continue ... */
262  }
263 
264  ptr = strchr(cp, '\0');
265  *ptr = '/';
266  }
267 
268  /* All done, free memory */
269  G_free(top);
270  G_free(cp);
271 
272  return path;
273 }
274 
275 /**
276  * \brief Returns path to <b>element</b> and <b>item</b>.
277  *
278  * Either <b>element</b> or <b>item</b> can be NULL, but not both. If
279  * <b>element</b> is NULL, then the file is assumed to live at the top
280  * level. If file is NULL, then it is assumed the caller is not
281  * interested in the file. If the element or rc dir do not exist, they
282  * are created. However, the file is never checked for.
283  *
284  * \param[in] element
285  * \param[in] item
286  * \return Pointer to string path
287  */
288 
289 char *G_rc_path(const char *element, const char *item)
290 {
291  size_t len;
292  char *path, *ptr;
293 
294  assert(!(element == NULL && item == NULL));
295 
296  /* Simple item in top-level */
297  if (element == NULL) {
298  path = _make_toplevel();
299  }
300  else if (item == NULL) {
301  return _make_sublevels(element);
302  }
303  else {
304  path = _make_sublevels(element);
305  }
306 
307  assert(*item != '.');
308  assert(path != NULL);
309  ptr = strchr(item, '/'); /* should not have slashes */
310  assert(ptr == NULL);
311  len = strlen(path) + strlen(item) + 2;
312  if ((ptr = G_realloc(path, len)) == NULL) {
313  G_free(path);
314  return NULL;
315  }
316  path = ptr;
317  ptr = strchr(path, '\0');
318  snprintf(ptr, len, "/%s", item);
319 
320  return path;
321 } /* G_rc_path */
322 
323 /* vim: set softtabstop=4 shiftwidth=4 expandtab: */
324 #endif
#define NULL
Definition: ccmath.h:32
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:150
#define G_realloc(p, n)
Definition: defs/gis.h:96
#define G_calloc(m, n)
Definition: defs/gis.h:95
int G_lstat(const char *, struct stat *)
Get file status.
Definition: paths.c:145
int G_mkdir(const char *)
Creates a new directory.
Definition: paths.c:27
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
#define assert(condition)
Definition: lz4.c:291
#define S_ISDIR(mode)
Definition: stat.h:6
Definition: lidar.h:85
Definition: path.h:15
char * G_rc_path(const char *element, const char *item)
Returns path to element and item.
Definition: user_config.c:289