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