From 110d87a1cc6a47e31ad9d70cc6366dd81d213860 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 27 Aug 2014 11:56:47 -0700 Subject: [PATCH] Improve robustness of new string-collation code. * configure.ac (newlocale): Check for this, not for uselocale. * src/sysdep.c (LC_COLLATE, LC_COLLATE_MASK, freelocale, locale_t) (newlocale, wcscoll_l): Define substitutes for platforms that lack them, so as to simplify the mainline code. (str_collate): Simplify the code by assuming the above definitions. Use wcscoll_l, not uselocale, as uselocale is too fragile. For example, the old version left the Emacs in the wrong locale if wcscoll reported an error. Use 'int', not ptrdiff_t, for the int result. Report an error if newlocale fails. Fixes: debbugs:18051 --- ChangeLog | 5 ++ configure.ac | 2 +- src/ChangeLog | 12 +++++ src/sysdep.c | 129 ++++++++++++++++++++++++++++++++++---------------- 4 files changed, 107 insertions(+), 41 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9eb4637694f..a4cef305560 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2014-08-27 Paul Eggert + + Improve robustness of new string-collation code (Bug#18051). + * configure.ac (newlocale): Check for this, not for uselocale. + 2014-08-26 Dmitry Antipov Detect features needed to handle C stack overflows. diff --git a/configure.ac b/configure.ac index 4f22be4745d..4617942409a 100644 --- a/configure.ac +++ b/configure.ac @@ -3558,7 +3558,7 @@ LIBS="$LIB_PTHREAD $LIB_MATH $LIBS" AC_CHECK_FUNCS(accept4 fchdir gethostname \ getrusage get_current_dir_name \ lrand48 random rint \ -select getpagesize setlocale uselocale \ +select getpagesize setlocale newlocale \ getrlimit setrlimit shutdown getaddrinfo \ pthread_sigmask strsignal setitimer \ sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \ diff --git a/src/ChangeLog b/src/ChangeLog index d7066b823b9..8a32bc27b0b 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,15 @@ +2014-08-27 Paul Eggert + + Improve robustness of new string-collation code (Bug#18051). + * sysdep.c (LC_COLLATE, LC_COLLATE_MASK, freelocale, locale_t) + (newlocale, wcscoll_l): Define substitutes for platforms that + lack them, so as to simplify the mainline code. + (str_collate): Simplify the code by assuming the above definitions. + Use wcscoll_l, not uselocale, as uselocale is too fragile. For + example, the old version left the Emacs in the wrong locale if + wcscoll reported an error. Use 'int', not ptrdiff_t, for the int + result. Report an error if newlocale fails. + 2014-08-27 Michael Albinus * lisp.h (str_collate): diff --git a/src/sysdep.c b/src/sysdep.c index 4b0f54ebe6e..d50e2398a53 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -3599,24 +3599,89 @@ system_process_attributes (Lisp_Object pid) #ifdef __STDC_ISO_10646__ # include -# if defined HAVE_USELOCALE || defined HAVE_SETLOCALE +# if defined HAVE_NEWLOCALE || defined HAVE_SETLOCALE # include +# else +# define LC_COLLATE 0 +# define LC_COLLATE_MASK 0 # endif -# ifndef HAVE_SETLOCALE -# define setlocale(category, locale) ((char *) 0) +# ifndef HAVE_NEWLOCALE +# undef freelocale +# undef locale_t +# undef newlocale +# undef wcscoll_l +# define freelocale emacs_freelocale +# define locale_t emacs_locale_t +# define newlocale emacs_newlocale +# define wcscoll_l emacs_wcscoll_l + +typedef char const *locale_t; + +static locale_t +newlocale (int category_mask, char const *locale, locale_t loc) +{ + return locale; +} + +static void +freelocale (locale_t loc) +{ +} + +static char * +emacs_setlocale (int category, char const *locale) +{ +# ifdef HAVE_SETLOCALE + errno = 0; + char *loc = setlocale (category, locale); + if (loc || errno) + return loc; + errno = EINVAL; +# else + errno = ENOTSUP; +# endif + return 0; +} + +static int +wcscoll_l (wchar_t const *a, wchar_t const *b, locale_t loc) +{ + int result = 0; + char *oldloc = emacs_setlocale (LC_COLLATE, NULL); + int err; + + if (! oldloc) + err = errno; + else + { + USE_SAFE_ALLOCA; + char *oldcopy = SAFE_ALLOCA (strlen (oldloc) + 1); + strcpy (oldcopy, oldloc); + if (! emacs_setlocale (LC_COLLATE, loc)) + err = errno; + else + { + errno = 0; + result = wcscoll (a, b); + err = errno; + if (! emacs_setlocale (LC_COLLATE, oldcopy)) + err = errno; + } + SAFE_FREE (); + } + + errno = err; + return result; +} # endif int str_collate (Lisp_Object s1, Lisp_Object s2) { - ptrdiff_t res, len, i, i_byte; + int res, err; + ptrdiff_t len, i, i_byte; wchar_t *p1, *p2; Lisp_Object lc_collate; -# ifdef HAVE_USELOCALE - locale_t loc = 0, oldloc = 0; -# else - char *oldloc = NULL; -# endif USE_SAFE_ALLOCA; @@ -3633,44 +3698,28 @@ str_collate (Lisp_Object s1, Lisp_Object s2) FETCH_STRING_CHAR_ADVANCE (*(p2+i-1), s2, i, i_byte); *(p2+len) = 0; - /* Create a new locale object, and set it. */ lc_collate = Fgetenv_internal (build_string ("LC_COLLATE"), Vprocess_environment); if (STRINGP (lc_collate)) { -#ifdef HAVE_USELOCALE - loc = newlocale (LC_COLLATE_MASK, SSDATA (lc_collate), 0); - if (loc) - oldloc = uselocale (loc); -#else - oldloc = setlocale (LC_COLLATE, NULL); - if (oldloc) - { - oldloc = xstrdup (oldloc); - setlocale (LC_COLLATE, SSDATA (lc_collate)); - } -#endif + locale_t loc = newlocale (LC_COLLATE_MASK, SSDATA (lc_collate), 0); + if (!loc) + error ("Wrong locale: %s", strerror (errno)); + errno = 0; + res = wcscoll_l (p1, p2, loc); + err = errno; + freelocale (loc); } + else + { + errno = 0; + res = wcscoll (p1, p2); + err = errno; + } + if (err) + error ("Wrong argument: %s", strerror (err)); - errno = 0; - res = wcscoll (p1, p2); - if (errno) - error ("Wrong argument: %s", strerror (errno)); - -#ifdef HAVE_USELOCALE - /* Free the locale object, and reset. */ - if (loc) - freelocale (loc); - if (oldloc) - uselocale (oldloc); -#else - /* Restore the original locale. */ - setlocale (LC_COLLATE, oldloc); - xfree (oldloc); -#endif - - /* Return result. */ SAFE_FREE (); return res; } -- 2.39.5