From 2ee5faae4a2285302f5a033375e09045ab90accd Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Sat, 4 Jul 2026 06:17:26 -0400 Subject: [PATCH 1/2] libgis: Avoid infinite recursion in G_init_locale on missing variable G_locale_dir() calls G_fatal_error() when GRASS_LOCALEDIR is not set, and G_fatal_error() translates its message, which re-enters G_init_locale() and recurses until the stack overflows. Read the variable directly and skip bindtextdomain() when it is not set, matching the pre-existing behavior for GISBASE. Likely the cause of the g.proj stack overflow (0xC00000FD) seen on Windows CI in the FHS preparation PR. Found and fixed with Claude Code. --- lib/gis/locale.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gis/locale.c b/lib/gis/locale.c index f1aabf31f71..16b9acd289c 100644 --- a/lib/gis/locale.c +++ b/lib/gis/locale.c @@ -39,7 +39,9 @@ void G_init_locale(void) #ifdef LC_MESSAGES setlocale(LC_MESSAGES, ""); #endif - const char *localedir = G_locale_dir(); + /* Not using G_locale_dir(): its G_fatal_error() would translate the + message and re-enter G_init_locale(), recursing without bound. */ + const char *localedir = getenv("GRASS_LOCALEDIR"); if (localedir && *localedir) { bindtextdomain("grasslibs", localedir); From e650ea64eaa4e3203d0a50084d46fd65ce11597c Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Sat, 4 Jul 2026 08:00:39 -0400 Subject: [PATCH 2/2] libgis: Fall back to GISBASE-relative paths for resource directories When a GRASS_* resource variable is not set, fall back to the legacy layout location relative to GISBASE so that environments created from GISBASE alone keep working with legacy installations. Fail fatally only when the variable is not set and the fallback directory does not exist, which is the case for an FHS installation without a properly created session. Verified with a bare-GISBASE environment: legacy build lists color rules through the fallback; FHS build reports the incomplete session error cleanly. Written with Claude Code. --- lib/gis/resource_dirs.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/gis/resource_dirs.c b/lib/gis/resource_dirs.c index 8602ceefe49..8560cb4b671 100644 --- a/lib/gis/resource_dirs.c +++ b/lib/gis/resource_dirs.c @@ -10,44 +10,61 @@ SPDX-License-Identifier: GPL-2.0-or-later */ +#include #include +#include #include #include -static const char *get_g_env(const char *); +static const char *get_g_env(const char *, const char *); const char *G_colors_dir(void) { - return get_g_env("GRASS_COLORSDIR"); + return get_g_env("GRASS_COLORSDIR", "etc/colors"); } const char *G_etcbin_dir(void) { - return get_g_env("GRASS_ETCBINDIR"); + return get_g_env("GRASS_ETCBINDIR", "etc"); } const char *G_etc_dir(void) { - return get_g_env("GRASS_ETCDIR"); + return get_g_env("GRASS_ETCDIR", "etc"); } const char *G_fonts_dir(void) { - return get_g_env("GRASS_FONTSDIR"); + return get_g_env("GRASS_FONTSDIR", "fonts"); } const char *G_locale_dir(void) { - return get_g_env("GRASS_LOCALEDIR"); + return get_g_env("GRASS_LOCALEDIR", "locale"); } -static const char *get_g_env(const char *env_var) +/*! + \brief Get a resource directory from the environment. + + Falls back to the legacy (non-FHS) location relative to GISBASE when + the variable is not set. Fatal when neither yields an existing path. + */ +static const char *get_g_env(const char *env_var, const char *gisbase_rel_dir) { const char *value = getenv(env_var); if (value) return value; + const char *gisbase = getenv("GISBASE"); + if (gisbase && *gisbase) { + char path[GPATH_MAX]; + + snprintf(path, sizeof(path), "%s/%s", gisbase, gisbase_rel_dir); + if (access(path, F_OK) == 0) + return G_store(path); + } + G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), env_var); }