/*!
* \file lib/gis/user_config.c
*
* \brief GIS Library - Routines related to user's GRASS configuration, tmp, and
* miscellaneous files.
*
* Functions related to the user's GRASS configuration, tmp, and
* miscellaneous files. Provides a set of routines for creating and
* accessing elements within the user's "rc" directory. The
* directory is in $HOME/.grass.
*
* NOTE: As of 2001-03-25 this file is not hooked up. It is
* provided as a candidate for handling $HOME/.grass files and
* subdirectories. There may be more functionality desired (such as
* deletion routines, directory globs).
*
* (C) 2001-2014 by the GRASS Development Team
*
* This program is free software under the GNU General Public License
* (>=v2). Read the file COPYING that comes with GRASS for details.
*
* \author Eric G Miller - egm2 at jps net
*
* \date 2007-04-14
*/
#include
#include
#include
#include
#include
#include
#ifndef __MINGW32__
#include
#endif
#include
#include
#include
#include
/**************************************************************************
* _make_toplevel(): make user's toplevel config directory if it doesn't
* already exist. Adjust perms to 1700. Returns the toplevel directory
* path [caller must G_free ()] on success, or NULL on failure
*************************************************************************/
#ifndef __MINGW32__ /* TODO */
static char *_make_toplevel(void)
{
size_t len;
int status;
#ifdef __MINGW32__
char *defaulthomedir = "c:";
char *homedir = getenv("HOME");
#else
uid_t me;
struct passwd *my_passwd;
#endif
struct stat buf;
char *path;
errno = 0;
/* Query whatever database to get user's home dir */
#ifdef __MINGW32__
if (NULL == homedir) {
homedir = defaulthomedir;
}
len = strlen(homedir) + 8; /* + "/.grass\0" */
if (NULL == (path = G_calloc(1, len))) {
return NULL;
}
sprintf(path, "%s%s", homedir, "/.grass");
#else
me = getuid();
my_passwd = getpwuid(me);
if (my_passwd == NULL)
return NULL;
len = strlen(my_passwd->pw_dir) + 8; /* + "/.grass\0" */
if (NULL == (path = G_calloc(1, len)))
return NULL;
sprintf(path, "%s%s", my_passwd->pw_dir, "/.grass");
#endif
status = G_lstat(path, &buf);
/* If errno == ENOENT, the directory doesn't exist */
if (status != 0) {
if (errno == ENOENT) {
status = G_mkdir(path);
if (status != 0) { /* mkdir failed */
G_free(path);
return NULL;
}
/* override umask settings, if possible */
chmod(path, S_IRWXU);
/* otherwise mkdir succeeded, we're done here */
return path;
}
/* other errors should not be defined ??? give up */
G_free(path);
return NULL;
}
/* implicit else */
/* Examine the stat "buf" */
/* It better be a directory */
if (!S_ISDIR(buf.st_mode)) { /* File, link, something else */
errno = ENOTDIR; /* element is not a directory, but should be */
G_free(path);
return NULL;
}
/* No read/write/execute ??? */
if (!((S_IRUSR & buf.st_mode) &&
(S_IWUSR & buf.st_mode) && (S_IXUSR & buf.st_mode)
)
) {
errno = EACCES; /* Permissions error */
G_free(path);
return NULL;
}
/* We'll assume that if the user grants greater permissions
* than we would, that they know what they're doing
* -- so we're done here...
*/
return path;
}
/**************************************************************************
* _elem_count_split: Does a couple things:
* 1) Counts the number of elements in "elems"
* 2) Replaces occurrences of '/' with '\0'
* 3) Checks that no element begins with a '.'
* 4) Checks there are no '//'
*
* Therefore, THE STRING THAT IS PASSED IN IS MODIFIED
* Returns 0 if there are no elements, or an element
* beginning with a '.' or containing a '//' is found.
*************************************************************************/
static int _elem_count_split(char *elems)
{
int i;
size_t len;
char *begin, *end;
/* Some basic assertions */
assert(elems != NULL);
assert((len = strlen(elems)) > 0);
assert(*elems != '/');
begin = elems;
for (i = 0; begin != NULL && len > begin - elems; i++) {
/* check '.' condition */
if (*begin == '.')
return 0;
end = strchr(begin, '/');
/* check '//' condition */
if (end != NULL && end == begin)
return 0;
/* okay, change '/' into '\0' */
begin = end;
if (begin != NULL) {
*begin = '\0'; /* begin points at '/', change it */
begin++; /* increment begin to next char */
}
}
/* That's it */
return i;
}
/**************************************************************************
* _make_sublevels(): creates subelements as necessary from the passed
* "elems" string. It returns the full path if successful or NULL
* if it fails. "elems" must not be NULL, zero length, or have any
* elements that begin with a '.' or any occurrences of '//'.
*************************************************************************/
static char *_make_sublevels(const char *elems)
{
int i, status;
char *cp, *path, *top, *ptr;
struct stat buf;
/* Get top level path */
if (NULL == (top = _make_toplevel()))
return NULL;
/* Make a copy of elems */
if (NULL == (cp = G_store(elems))) {
G_free(top);
return NULL;
}
/* Do element count, sanity checking and "splitting" */
if ((i = _elem_count_split(cp)) < 1) {
G_free(cp);
G_free(top);
return NULL;
}
/* Allocate our path to be large enough */
if ((path = G_calloc(1, strlen(top) + strlen(elems) + 2)) == NULL) {
G_free(top);
G_free(cp);
return NULL;
}
/* Now loop along adding directories if they don't exist
* make sure the thing is a directory as well.
* If there was a trailing '/' in the original "elem", it doesn't
* make it into the returned path.
*/
for (; i > 0; i--) {
sprintf(path, "%s/%s", top, cp);
errno = 0;
status = G_lstat(path, &buf);
if (status != 0) {
/* the element doesn't exist */
status = G_mkdir(path);
if (status != 0) {
/* Some kind of problem... */
G_free(top);
G_free(cp);
return NULL;
}
/* override umask settings, if possible */
chmod(path, S_IRWXU);
}
else {
/* Examine the stat "buf" */
/* It better be a directory */
if (!S_ISDIR(buf.st_mode)) { /* File, link, something else */
errno = ENOTDIR; /* element is not a directory, but should be */
G_free(path);
return NULL;
}
/* No read/write/execute ??? */
if (!((S_IRUSR & buf.st_mode) &&
(S_IWUSR & buf.st_mode) && (S_IXUSR & buf.st_mode)
)
) {
errno = EACCES; /* Permissions error */
G_free(path);
return NULL;
}
/* okay continue ... */
}
ptr = strchr(cp, '\0');
*ptr = '/';
}
/* All done, free memory */
G_free(top);
G_free(cp);
return path;
}
/**
* \brief Returns path to element and item.
*
* Either element or item can be NULL, but not both. If
* element is NULL, then the file is assumed to live at the top
* level. If file is NULL, then it is assumed the caller is not
* interested in the file. If the element or rc dir do not exist, they
* are created. However, the file is never checked for.
*
* \param[in] element
* \param[in] item
* \return Pointer to string path
*/
char *G_rc_path(const char *element, const char *item)
{
size_t len;
char *path, *ptr;
assert(!(element == NULL && item == NULL));
/* Simple item in top-level */
if (element == NULL) {
path = _make_toplevel();
}
else if (item == NULL) {
return _make_sublevels(element);
}
else {
path = _make_sublevels(element);
}
assert(*item != '.');
assert(path != NULL);
ptr = strchr(item, '/'); /* should not have slashes */
assert(ptr == NULL);
len = strlen(path) + strlen(item) + 2;
if ((ptr = G_realloc(path, len)) == NULL) {
G_free(path);
return NULL;
}
path = ptr;
ptr = strchr(path, '\0');
sprintf(ptr, "/%s", item);
return path;
} /* G_rc_path */
/* vim: set softtabstop=4 shiftwidth=4 expandtab: */
#endif