git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] set LC_TIME even if locale dir is not present

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Mar 27, 2022, 10:13 UTC
Message-ID
<220327.867d8ffy37.gmgdl@evledraar.gmail.com>
In-Reply-To
<pull.1189.git.1648371489398.gitgitgadget@gmail.com>
On Sun, Mar 27 2022, Matthias Aßhauer via GitGitGadget wrote:
Show 13 quoted lines
> From: =?UTF-8?q?Matthias=20A=C3=9Fhauer?= <mha1993@live.de>
>
> Since Commit aa1462c (introduce "format" date-mode, 2015-06-25) git log can
> pass user specified format strings directly to strftime(). One special
> format string we explicitly mention in our documentation is %c, which
> depends on the system locale. To accommodate for %c we added a call to
> setlocale() in git_setup_gettext().
>
> In Commit cc5e1bf (gettext: avoid initialization if the locale dir is not
> present, 2018-04-21) we added an early exit to git_setup_gettext() in case
> no textdomain directory is present. This early exit is so early, that we
> don't even set the locale for %c in that case, despite strftime() not
> needing the textdomain directory at all.

Thanks for tracking this down. This commit & end-state looks good to me, I just have comments about the implementation below, i.e. you're doing more work than you need to, some of this we have test infrastructure already...

Show 25 quoted lines
> diff --git a/Makefile b/Makefile
> index e8aba291d7f..ddca29b550b 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -410,6 +410,10 @@ include shared.mak
>  # If it isn't set, fallback to $LC_ALL, $LANG or use the first utf-8
>  # locale returned by "locale -a".
>  #
> +# Define GIT_TEST_TIME_LOCALE to preferred non-us locale for testing.
> +# If it isn't set, fallback to $LC_ALL, $LANG or use the first non-us
> +# locale returned by "locale -a".
> +#
>  # Define HAVE_CLOCK_GETTIME if your platform has clock_gettime.
>  #
>  # Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC.
> @@ -2862,6 +2866,9 @@ ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
>  endif
>  ifdef GIT_TEST_UTF8_LOCALE
>  	@echo GIT_TEST_UTF8_LOCALE=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_UTF8_LOCALE)))'\' >>$@+
> +endif
> +ifdef GIT_TEST_TIME_LOCALE
> +	@echo GIT_TEST_TIME_LOCALE=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_TIME_LOCALE)))'\' >>$@+
>  endif
>  	@echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@+
>  ifdef GIT_PERF_REPEAT_COUNT
You won't need this, more later...
Show 52 quoted lines
> diff --git a/gettext.c b/gettext.c
> index bb5ba1fe7cc..2b614c2b8c6 100644
> --- a/gettext.c
> +++ b/gettext.c
> @@ -107,6 +107,8 @@ void git_setup_gettext(void)
>  	const char *podir = getenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT);
>  	char *p = NULL;
>  
> +	setlocale(LC_TIME, "");
> +
>  	if (!podir)
>  		podir = p = system_path(GIT_LOCALE_PATH);
>  
> @@ -117,7 +119,6 @@ void git_setup_gettext(void)
>  
>  	bindtextdomain("git", podir);
>  	setlocale(LC_MESSAGES, "");
> -	setlocale(LC_TIME, "");
>  	init_gettext_charset("git");
>  	textdomain("git");
>  
> diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
> index e448ef2928a..01a1e61ecea 100755
> --- a/t/t4205-log-pretty-formats.sh
> +++ b/t/t4205-log-pretty-formats.sh
> @@ -25,6 +25,29 @@ commit_msg () {
>  	fi
>  }
>  
> +prepare_time_locale () {
> +	if test -z "$GIT_TEST_TIME_LOCALE"
> +	then
> +		case "${LC_ALL:-$LANG}" in
> +		C | C.* | POSIX | POSIX.* | en_US | en_US.* )
> +			GIT_TEST_TIME_LOCALE=$(locale -a | sed -n '/^\(C\|POSIX\|en_US\)/I !{
> +				p
> +				q
> +			}')
> +			;;
> +		*)
> +			GIT_TEST_TIME_LOCALE="${LC_ALL:-$LANG}"
> +			;;
> +		esac
> +	fi
> +	if test -n "$GIT_TEST_TIME_LOCALE"
> +	then
> +		test_set_prereq TIME_LOCALE
> +	else
> +		say "# No non-us locale available, some tests are skipped"
> +	fi
> +}
> +
And this setup we do already elsewhere.

I think the below would be a good candidate to squash into this. The C change doesn't change the behavior from your version, but I think it's good to take the change here about being explicit what we do and don't seup with/without a podir.

Your prepare_time_locale() is then duplicating lib-gettext.sh, the below shows a replacement for your test piggy-backing on existing test infra.

I was then worried that we'd introduced a regression in cc5e1bf in assuming that we didn't need to setlocale(LC_MESSAGES) just because we didn't have a podir, because the C library might have some, but the below test passe for me on linux+glibc.

Maybe there's still a regression there, I'm not sure. Perhaps it's also good to drop that "optimization" on non-Windows platforms?

diff --git a/gettext.c b/gettext.c
index bb5ba1fe7cc..9b46c224230 100644
--- a/gettext.c
+++ b/gettext.c
@@ -102,25 +102,34 @@ static void init_gettext_charset(const char *domain)
 		setlocale(LC_CTYPE, "C");
 }
 
+static void git_setup_gettext_no_podir(void)
+{
+	setlocale(LC_TIME, "");
+}
+
+static void git_setup_gettext_podir(const char *podir)
+{
+	bindtextdomain("git", podir);
+	setlocale(LC_MESSAGES, "");
+	init_gettext_charset("git");
+	textdomain("git");
+}
+
 void git_setup_gettext(void)
 {
 	const char *podir = getenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT);
 	char *p = NULL;
 
+	git_setup_gettext_no_podir();
+
 	if (!podir)
 		podir = p = system_path(GIT_LOCALE_PATH);
 
-	if (!is_directory(podir)) {
-		free(p);
-		return;
-	}
-
-	bindtextdomain("git", podir);
-	setlocale(LC_MESSAGES, "");
-	setlocale(LC_TIME, "");
-	init_gettext_charset("git");
-	textdomain("git");
+	if (!is_directory(podir))
+		goto done;
 
+	git_setup_gettext_podir(podir);
+done:
 	free(p);
 }
 
diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh
index 0ce1f22eff6..69facd2f8ed 100755
--- a/t/t0203-gettext-setlocale-sanity.sh
+++ b/t/t0203-gettext-setlocale-sanity.sh
@@ -23,4 +23,49 @@ test_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 l
 	grep -q "iso-utf8-commit" out
 '
 
+test_expect_success GETTEXT_LOCALE 'the %c date format works even without a localedir (LC_TIME)' '
+	test_when_finished "rm -rf empty" &&
+	mkdir empty &&
+	LANGUAGE=is LC_ALL="$is_IS_locale" GIT_TEXTDOMAINDIR="$PWD/empty" \
+		git log --pretty=format:%ad --date=format:%c HEAD^1..HEAD >actual &&
+
+	# Avoid testing the raw format (it might differ?). But
+	# Thursday is Fimmtudagur in Icelandic, so grepping "fim" is
+	# pretty certain to test that the locale was used.
+	grep -iF fim actual
+'
+
+test_lazy_prereq GETTEXT_HAVE_STRERROR_TRANSLATED '
+	test_have_prereq GETTEXT_LOCALE &&
+	test_have_prereq POSIXPERM &&
+
+	test_when_finished "rm -f file" &&
+	>file &&
+
+	# German is more likely to have a strerror() translation
+	test_must_fail git init file 2>loc-C &&
+	grep "cannot mkdir" loc-C &&
+	test_must_fail env \
+		LANGUAGE=de LC_ALL="de_DE.utf8" \
+		git init file 2>loc-de &&
p+	! grep "cannot mkdir" loc-de
+
+'
+
+test_expect_success GETTEXT_LOCALE,GETTEXT_HAVE_STRERROR_TRANSLATED \
+	'LC_MESSAGES is set up without a localedir (for strerror())' '
+	test_when_finished "rm -f file" &&
+	>file &&
+	test_when_finished "rm -rf no-domain" &&
+	mkdir no-domain &&
+
+	test_must_fail git init file 2>loc-C &&
+	test_must_fail env \
+		LANGUAGE=de LC_ALL="de_DE.utf8" \
+		NO_SET_GIT_TEXTDOMAINDIR=StopDoingThatInBinWrappers \
+		GIT_TEXTDOMAINDIR="$PWD/no-domain" \
+		git init file 2>loc-de-no-textdomain &&
+	! test_cmp loc-C loc-de-no-textdomain
+'
+
 test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 515b1af7ed4..886d260082d 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1428,6 +1428,8 @@ else # normal case, use ../bin-wrappers only unless $with_dashes:
 			fi
 			with_dashes=t
 		fi
+		GIT_RUNING_TEST_LIB_SH=t
+		export GIT_RUNING_TEST_LIB_SH
 		PATH="$git_bin_dir:$PATH"
 	fi
 	GIT_EXEC_PATH=$GIT_BUILD_DIR
diff --git a/wrap-for-bin.sh b/wrap-for-bin.sh
index 95851b85b6b..3089bcad37c 100644
--- a/wrap-for-bin.sh
+++ b/wrap-for-bin.sh
@@ -15,10 +15,14 @@ else
 	export GIT_TEMPLATE_DIR
 fi
 GITPERLLIB='@@BUILD_DIR@@/perl/build/lib'"${GITPERLLIB:+:$GITPERLLIB}"
-GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
+if test -z "$NO_SET_GIT_TEXTDOMAINDIR"
+then
+	GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
+	export GIT_TEXTDOMAINDIR
+fi
 PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
 
-export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
+export GIT_EXEC_PATH GITPERLLIB PATH
 
 case "$GIT_DEBUGGER" in
 '')
Previous: Matthias Aßhauer via GitGitGadget
Message 2 of 2 in “set LC_TIME even if locale dir is not present”
  1. set LC_TIME even if locale dir is not presentMatthias Aßhauer via GitGitGadget, Mar 27, 2022
  2. Ævar Arnfjörð BjarmasonMar 27, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.