/*! \file lib/gis/env.c \brief GIS library - environment routines (C) 2001-2022 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 Original author CERL \author Updated for GRASS7 by Glynn Clements */ #include #include #include #include /* for sleep() */ #include #include #include struct bind { int loc; char *name; char *value; }; struct env { struct bind *binds; int count; int size; }; static struct state { struct env env; struct env env2; char *gisrc; int varmode; int init[2]; } state; static struct state *st = &state; static int read_env(int); static int set_env(const char *, const char *, int); static int unset_env(const char *, int); static const char *get_env(const char *, int); static void write_env(int); static void parse_env(FILE *, int); static void force_read_env(int); static FILE *open_env(const char *, int); /*! \brief Set where to find/store variables Modes: - G_GISRC_MODE_FILE - G_GISRC_MODE_MEMORY \param mode mode to find/store variables (G_GISRC_MODE_FILE by default) */ void G_set_gisrc_mode(int mode) { st->varmode = mode; } /*! \brief Get info where variables are stored \return mode */ int G_get_gisrc_mode(void) { return (st->varmode); } /*! \brief Initialize variables \return */ void G_init_env(void) { read_env(G_VAR_GISRC); read_env(G_VAR_MAPSET); } /*! * \brief Force to read the mapset environment file VAR * * The mapset specific VAR file of the mapset set with G_setenv() * will be read into memory, ignoring if it was readed before. * Existing values will be overwritten, new values appended. * * \return */ void G__read_mapset_env(void) { force_read_env(G_VAR_MAPSET); } /*! * \brief Force to read the GISRC environment file * * The GISRC file * will be read into memory, ignoring if it was readed before. * Existing values will be overwritten, new values appended. * * \return */ void G__read_gisrc_env(void) { force_read_env(G_VAR_GISRC); } static void parse_env(FILE *fd, int loc) { /* Account for long lines up to GPATH_MAX. E.g. "GISDBASE: GPATH_MAX\n\0" */ char buf[GPATH_MAX + 16]; char *name; char *value; while (G_getl2(buf, sizeof buf, fd)) { for (name = value = buf; *value; value++) if (*value == ':') break; if (*value == 0) continue; *value++ = 0; G_strip(name); G_strip(value); if (*name && *value) set_env(name, value, loc); } } static int read_env(int loc) { FILE *fd; if (loc == G_VAR_GISRC && st->varmode == G_GISRC_MODE_MEMORY) return 0; /* don't use file for GISRC */ if (G_is_initialized(&st->init[loc])) return 1; if ((fd = open_env("r", loc))) { parse_env(fd, loc); fclose(fd); } G_initialize_done(&st->init[loc]); return 0; } /*! * \brief Force the reading or the GISRC or MAPSET/VAR files * and overwrite/append the specified variables * */ static void force_read_env(int loc) { FILE *fd; if ((fd = open_env("r", loc))) { parse_env(fd, loc); fclose(fd); } } static int set_env(const char *name, const char *value, int loc) { int n; int empty; char *tv; /* if value is NULL or empty string, convert into an unsetenv() */ if (!value || !strlen(value)) { unset_env(name, loc); return 0; } tv = G_store(value); G_strip(tv); if (*tv == 0) { G_free(tv); unset_env(name, loc); return 1; } /* * search the array * keep track of first empty slot * and look for name in the environment */ empty = -1; for (n = 0; n < st->env.count; n++) { struct bind *b = &st->env.binds[n]; if (!b->name) /* mark empty slot found */ empty = n; else if (strcmp(b->name, name) == 0 && b->loc == loc) { b->value = tv; return 1; } } /* add name to env: to empty slot if any */ if (empty >= 0) { struct bind *b = &st->env.binds[empty]; b->loc = loc; b->name = G_store(name); b->value = tv; return 0; } /* must increase the env list and add in */ if (st->env.count >= st->env.size) { st->env.size += 20; st->env.binds = G_realloc(st->env.binds, st->env.size * sizeof(struct bind)); } { struct bind *b = &st->env.binds[st->env.count++]; b->loc = loc; b->name = G_store(name); b->value = tv; } return 0; } static int unset_env(const char *name, int loc) { int n; for (n = 0; n < st->env.count; n++) { struct bind *b = &st->env.binds[n]; if (b->name && strcmp(b->name, name) == 0 && b->loc == loc) { G_free(b->name); b->name = 0; return 1; } } return 0; } static const char *get_env(const char *name, int loc) { int n; for (n = 0; n < st->env.count; n++) { struct bind *b = &st->env.binds[n]; if (b->name && (strcmp(b->name, name) == 0) && b->loc == loc) return b->value; } return NULL; } static void write_env(int loc) { FILE *fd; int n; char dummy[2]; void (*sigint)(int); #ifdef SIGQUIT void (*sigquit)(int); #endif if (loc == G_VAR_GISRC && st->varmode == G_GISRC_MODE_MEMORY) return; /* don't use file for GISRC */ /* * THIS CODE NEEDS TO BE PROTECTED FROM INTERRUPTS * If interrupted, it can wipe out the GISRC file */ sigint = signal(SIGINT, SIG_IGN); #ifdef SIGQUIT sigquit = signal(SIGQUIT, SIG_IGN); #endif if ((fd = open_env("w", loc))) { for (n = 0; n < st->env.count; n++) { struct bind *b = &st->env.binds[n]; if (b->name && b->value && b->loc == loc && (sscanf(b->value, "%1s", dummy) == 1)) fprintf(fd, "%s: %s\n", b->name, b->value); } fclose(fd); } signal(SIGINT, sigint); #ifdef SIGQUIT signal(SIGQUIT, sigquit); #endif } static FILE *open_env(const char *mode, int loc) { char buf[GPATH_MAX]; if (loc == G_VAR_GISRC) { if (!st->gisrc) st->gisrc = getenv("GISRC"); if (!st->gisrc) { G_fatal_error(_("No active GRASS session: " "GISRC environment variable not set")); return NULL; } strcpy(buf, st->gisrc); } else if (loc == G_VAR_MAPSET) { /* Warning: G_VAR_GISRC must be previously read -> */ /* TODO: better place ? */ read_env(G_VAR_GISRC); sprintf(buf, "%s/%s/VAR", G_location_path(), G_mapset()); } return fopen(buf, mode); } /*! \brief Get environment variable G_fatal_error() is called when variable is not found. \param name variable name \return char pointer to value for name */ const char *G_getenv(const char *name) { const char *value = G_getenv_nofatal(name); if (value) return value; G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), name); return NULL; } /*! \brief Get variable from specific place Locations: - G_VAR_GISRC - G_VAR_MAPSET G_fatal_error() is called when variable is not found. \param name variable name \param loc location (G_VAR_GISRC, G_VAR_MAPSET) \return variable value \return NULL if not found */ const char *G_getenv2(const char *name, int loc) { const char *value = G_getenv_nofatal2(name, loc); if (value) return value; G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), name); return NULL; } /*! \brief Get environment variable \param name variable name \return char pointer to value for name \return NULL if name not set */ const char *G_getenv_nofatal(const char *name) { if (strcmp(name, "GISBASE") == 0) return getenv(name); read_env(G_VAR_GISRC); return get_env(name, G_VAR_GISRC); } /*! \brief Get environment variable from specific place \param name variable name \param loc location (G_VAR_GISRC, G_VAR_MAPSET) \return char pointer to value for name \return NULL if name not set */ const char *G_getenv_nofatal2(const char *name, int loc) { if (strcmp(name, "GISBASE") == 0) return getenv(name); read_env(loc); return get_env(name, loc); } /*! \brief Set environment variable (updates .gisrc) If value is NULL, becomes an G_unsetenv(). \param name variable name \param value variable value */ void G_setenv(const char *name, const char *value) { read_env(G_VAR_GISRC); set_env(name, value, G_VAR_GISRC); write_env(G_VAR_GISRC); } /*! \brief Set environment variable from specific place (updates .gisrc) If value is NULL, becomes an G_unsetenv(). \param name variable name \param value variable value \param loc location (G_VAR_GISRC, G_VAR_MAPSET) */ void G_setenv2(const char *name, const char *value, int loc) { read_env(loc); set_env(name, value, loc); write_env(loc); } /*! \brief Set environment name to value (doesn't update .gisrc) \param name variable name \param value variable value */ void G_setenv_nogisrc(const char *name, const char *value) { read_env(G_VAR_GISRC); set_env(name, value, G_VAR_GISRC); } /*! \brief Set environment name to value from specific place (doesn't update .gisrc) \param name variable name \param value variable value \param loc location (G_VAR_GISRC, G_VAR_MAPSET) */ void G_setenv_nogisrc2(const char *name, const char *value, int loc) { read_env(loc); set_env(name, value, loc); } /*! \brief Remove name from environment Updates .gisrc \param name variable name */ void G_unsetenv(const char *name) { read_env(G_VAR_GISRC); unset_env(name, G_VAR_GISRC); write_env(G_VAR_GISRC); } /*! \brief Remove name from environment from specific place Updates .gisrc \param name variable name \param loc location (G_VAR_GISRC, G_VAR_MAPSET) */ void G_unsetenv2(const char *name, int loc) { read_env(loc); unset_env(name, loc); write_env(loc); } /*! \brief Writes current environment to .gisrc */ void G__write_env(void) { if (st->init[G_VAR_GISRC]) write_env(G_VAR_GISRC); } /*! \brief Get variable name for index n. For example: \code for (n = 0; ; n++) if ((name = G_get_env_name(n)) == NULL) break; \endcode \param n index of variable \return pointer to variable name \return NULL not found */ const char *G_get_env_name(int n) { int i; read_env(G_VAR_GISRC); if (n >= 0) for (i = 0; i < st->env.count; i++) if (st->env.binds[i].name && *st->env.binds[i].name && (n-- == 0)) return st->env.binds[i].name; return NULL; } /*! \brief Initialize init array for G_VAR_GISRC. */ void G__read_env(void) { st->init[G_VAR_GISRC] = 0; } /*! \brief Set up alternative environment variables */ void G_create_alt_env(void) { int i; /* copy env to env2 */ st->env2 = st->env; st->env.count = 0; st->env.size = 0; st->env.binds = NULL; for (i = 0; i < st->env2.count; i++) { struct bind *b = &st->env2.binds[i]; if (b->name) set_env(b->name, b->value, G_VAR_GISRC); } } /*! \brief Switch environments */ void G_switch_env(void) { struct env tmp; tmp = st->env; st->env = st->env2; st->env2 = tmp; }