Index: include/xlocale/_locale.h =================================================================== --- include/xlocale/_locale.h +++ include/xlocale/_locale.h @@ -54,6 +54,7 @@ void freelocale(locale_t loc); locale_t newlocale(int mask, const char *locale, locale_t base); const char *querylocale(int mask, locale_t loc); +const char *querylocaleversion(int mask, locale_t loc); locale_t uselocale(locale_t loc); #endif /* _XLOCALE_LOCALE_H */ Index: lib/libc/locale/Symbol.map =================================================================== --- lib/libc/locale/Symbol.map +++ lib/libc/locale/Symbol.map @@ -209,6 +209,11 @@ mbrtoc32_l; }; +/* TODO: Change to FBSD_1.6 once it has been defined, for FreeBSD 13. */ +FBSD_1.5 { + querylocaleversion; +}; + FBSDprivate_1.0 { _PathLocale; __detect_path_locale; Index: lib/libc/locale/collate.h =================================================================== --- lib/libc/locale/collate.h +++ lib/libc/locale/collate.h @@ -53,6 +53,8 @@ #endif #define COLLATE_STR_LEN 24 /* should be 64-bit multiple */ + +#define COLLATE_FORMAT_VERSION_LEN 12 #define COLLATE_VERSION "BSD 1.0\n" #define COLLATE_MAX_PRIORITY (0x7fffffff) /* max signed value */ @@ -69,7 +71,8 @@ /* * The collate file format is as follows: * - * char version[COLLATE_STR_LEN]; // must be COLLATE_VERSION + * char format_version[COLLATE_FORMAT_VERSION_LEN]; // must be COLLATE_VERSION + * char data_version[XLOCALE_VERSION_LEN]; // NUL-terminated, may be empty * collate_info_t info; // see below, includes padding * collate_char_pri_t char_data[256]; // 8 bit char values * collate_subst_t subst[*]; // 0 or more substitutions Index: lib/libc/locale/collate.c =================================================================== --- lib/libc/locale/collate.c +++ lib/libc/locale/collate.c @@ -150,12 +150,14 @@ return (_LDP_ERROR); } - if (strncmp(TMP, COLLATE_VERSION, COLLATE_STR_LEN) != 0) { + if (strncmp(TMP, COLLATE_VERSION, COLLATE_FORMAT_VERSION_LEN) != 0) { (void) munmap(map, sbuf.st_size); errno = EINVAL; return (_LDP_ERROR); } - TMP += COLLATE_STR_LEN; + TMP += COLLATE_FORMAT_VERSION_LEN; + strncpy(table->header.version, TMP, sizeof(table->header.version)); + TMP += XLOCALE_VERSION_LEN; info = (void *)TMP; TMP += sizeof (*info); Index: lib/libc/locale/querylocaleversion.3 =================================================================== --- /dev/null +++ lib/libc/locale/querylocaleversion.3 @@ -0,0 +1,51 @@ +.\" Copyright (c) 2018 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 3, 2018 +.Dt QUERYLOCALEVERSION 3 +.Os +.Sh NAME +.Nm querylocaleversion +.Nd Look up the locale version for a specified category +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In locale.h +.Ft const char * +.Fn querylocaleversion "int mask" "locale_t locale" +.Sh DESCRIPTION +Returns the version of the locale definition for the category specified by +.Fa mask . +The possible values for the mask are the same as those in +.Xr newlocale 3 . +Currently the only component that can provide version information is +.Dv LC_COLLATE_MASK . +If no version information is available, a +pointer to an empty string is returned. +If more than one bit in the mask is set, the returned value is undefined. +.Sh SEE ALSO +.Xr localedef 1 , +.Xr querylocale 3 Index: lib/libc/locale/xlocale.c =================================================================== --- lib/libc/locale/xlocale.c +++ lib/libc/locale/xlocale.c @@ -231,6 +231,8 @@ if (new->components[type]) { strncpy(new->components[type]->locale, src->locale, ENCODING_LEN); + strncpy(new->components[type]->version, src->version, + XLOCALE_VERSION_LEN); } } else if (base->components[type]) { new->components[type] = xlocale_retain(base->components[type]); @@ -355,6 +357,20 @@ return ("C"); } +/* + * Returns the version of the locale for a particular component of a locale_t. + */ +const char *querylocaleversion(int mask, locale_t loc) +{ + int type = ffs(mask) - 1; + FIX_LOCALE(loc); + if (type >= XLC_LAST) + return (NULL); + if (loc->components[type]) + return (loc->components[type]->version); + return (""); +} + /* * Installs the specified locale_t as this thread's locale. */ Index: lib/libc/locale/xlocale_private.h =================================================================== --- lib/libc/locale/xlocale_private.h +++ lib/libc/locale/xlocale_private.h @@ -91,6 +91,9 @@ /** Function used to destroy this component, if one is required*/ void(*destructor)(void*); }; + +#define XLOCALE_VERSION_LEN 12 + /** * Header for a locale component. All locale components must begin with this * header. @@ -99,6 +102,8 @@ struct xlocale_refcounted header; /** Name of the locale used for this component. */ char locale[ENCODING_LEN+1]; + /** Version of the data for this component. */ + char version[XLOCALE_VERSION_LEN]; }; /** Index: usr.bin/localedef/collate.c =================================================================== --- usr.bin/localedef/collate.c +++ usr.bin/localedef/collate.c @@ -1113,7 +1113,8 @@ collelem_t *ce; collchar_t *cc; subst_t *sb; - char vers[COLLATE_STR_LEN]; + char format_version[COLLATE_FORMAT_VERSION_LEN]; + char data_version[XLOCALE_VERSION_LEN]; collate_char_t chars[UCHAR_MAX + 1]; collate_large_t *large; collate_subst_t *subst[COLL_WEIGHTS_MAX]; @@ -1154,8 +1155,12 @@ } (void) memset(&chars, 0, sizeof (chars)); - (void) memset(vers, 0, COLLATE_STR_LEN); - (void) strlcpy(vers, COLLATE_VERSION, sizeof (vers)); + (void) memset(format_version, 0, COLLATE_FORMAT_VERSION_LEN); + (void) strlcpy(format_version, COLLATE_VERSION, + sizeof (format_version)); + (void) memset(data_version, 0, XLOCALE_VERSION_LEN); + if (version) + (void) strlcpy(data_version, version, sizeof (data_version)); /* * We need to make sure we arrange for the UNDEFINED field @@ -1288,7 +1293,8 @@ /* Time to write the entire data set out */ - if ((wr_category(vers, COLLATE_STR_LEN, f) < 0) || + if ((wr_category(format_version, COLLATE_FORMAT_VERSION_LEN, f) < 0) || + (wr_category(data_version, XLOCALE_VERSION_LEN, f) < 0) || (wr_category(&collinfo, sizeof (collinfo), f) < 0) || (wr_category(&chars, sizeof (chars), f) < 0)) { return; Index: usr.bin/localedef/localedef.h =================================================================== --- usr.bin/localedef/localedef.h +++ usr.bin/localedef/localedef.h @@ -53,6 +53,8 @@ extern int warnok; extern int warnings; +extern char *version; + int yylex(void); void yyerror(const char *); _Noreturn void errf(const char *, ...) __printflike(1, 2); Index: usr.bin/localedef/localedef.1 =================================================================== --- usr.bin/localedef/localedef.1 +++ usr.bin/localedef/localedef.1 @@ -131,6 +131,12 @@ generally not account for East Asian encodings requiring more than a single character cell to display, nor for combining or accent marks that occupy no additional screen width. +.It Fl V Ar version +Specifies a version string describing the source collation data. +This string can be retrieved using +.Xr querylocaleversion 3 , +and is intended to allow applications to detect when the definition of a +collation changes. .El .Pp The following operands are required: @@ -194,6 +200,7 @@ .Xr locale 1 , .Xr iconv_open 3 , .Xr nl_langinfo 3 , +.Xr querylocaleversion 3 , .Xr strftime 3 , .Xr environ 7 .Sh WARNINGS Index: usr.bin/localedef/localedef.c =================================================================== --- usr.bin/localedef/localedef.c +++ usr.bin/localedef/localedef.c @@ -46,6 +46,7 @@ #include #include #include +#include "collate.h" #include "localedef.h" #include "parser.h" @@ -59,6 +60,7 @@ int warnok = 0; static char *locname = NULL; static char locpath[PATH_MAX]; +char *version = NULL; const char * category_name(void) @@ -236,6 +238,7 @@ (void) fprintf(stderr, " -u encoding : assume encoding\n"); (void) fprintf(stderr, " -w widths : use screen widths file\n"); (void) fprintf(stderr, " -i locsrc : source file for locale\n"); + (void) fprintf(stderr, " -V version : version string for locale\n"); exit(4); } @@ -260,7 +263,7 @@ (void) setlocale(LC_ALL, ""); - while ((c = getopt(argc, argv, "w:i:cf:u:vUD")) != -1) { + while ((c = getopt(argc, argv, "w:i:cf:u:vUDV:")) != -1) { switch (c) { case 'D': bsd = 1; @@ -289,6 +292,9 @@ case '?': usage(); break; + case 'V': + version = optarg; + break; } } @@ -300,6 +306,11 @@ (void) printf("Processing locale %s.\n", locname); } + if (version && strlen(version) >= XLOCALE_VERSION_LEN) { + (void) fprintf(stderr, "Version string too long.\n"); + exit(1); + } + if (cfname) { if (verbose) (void) printf("Loading charmap %s.\n", cfname);