Index: include/xlocale/_locale.h =================================================================== --- include/xlocale/_locale.h +++ include/xlocale/_locale.h @@ -43,6 +43,7 @@ #define LC_MESSAGES_MASK (1<<5) #define LC_ALL_MASK (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MESSAGES_MASK | \ LC_MONETARY_MASK | LC_NUMERIC_MASK | LC_TIME_MASK) +#define LC_VERSION_MASK (1<<6) #define LC_GLOBAL_LOCALE ((locale_t)-1) #ifndef _LOCALE_T_DEFINED 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; + strlcpy(table->header.version, TMP, sizeof(table->header.version)); + TMP += XLOCALE_VERSION_LEN; info = (void *)TMP; TMP += sizeof (*info); Index: lib/libc/locale/querylocale.3 =================================================================== --- lib/libc/locale/querylocale.3 +++ lib/libc/locale/querylocale.3 @@ -27,12 +27,12 @@ .\" .\" $FreeBSD$ .\" -.Dd May 3, 2013 +.Dd September 15, 2018 .Dt QUERYLOCALE 3 .Os .Sh NAME .Nm querylocale -.Nd Look up the locale name for a specified category +.Nd Look up the locale name or version for a specified category .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -40,11 +40,22 @@ .Ft const char * .Fn querylocale "int mask" "locale_t locale" .Sh DESCRIPTION -Returns the name of the locale for the category specified by +Returns the name or version of the locale for the category specified by .Fa mask . This possible values for the mask are the same as those in -.Xr newlocale 3 . -If more than one bit in the mask is set, the returned value is undefined. +.Xr newlocale 3 , +when requesting the locale name. +Specify the bitwise OR of +.Fa LC_VERSION_MASK +and another mask value to request a version string. +Version strings can be compared to detect changes to the locale's +definition. +Currently, version information is only available for +.Fa LC_COLLATE_MASK , +and an empty string is returned for other categories. +If more than one bit in the mask is set, not counting +.Fa LC_VERSION_MASK , +the returned value is undefined. .Sh SEE ALSO .Xr duplocale 3 , .Xr freelocale 3 , @@ -52,3 +63,13 @@ .Xr newlocale 3 , .Xr uselocale 3 , .Xr xlocale 3 +.Sh HISTORY +The +.Fn querylocale +function first appeared in +.Fx 9.1 , +and is based on the function of the same name in Darwin. +The ability to ask for a version with +.Fa LC_VERSION_MASK +first appeared in +.Fx 13.0 . 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]); @@ -342,17 +344,24 @@ } /* - * Returns the name of the locale for a particular component of a locale_t. + * Returns the name or version of the locale for a particular component of a + * locale_t. */ const char *querylocale(int mask, locale_t loc) { - int type = ffs(mask) - 1; + int type = ffs(mask & ~LC_VERSION_MASK) - 1; FIX_LOCALE(loc); if (type >= XLC_LAST) return (NULL); - if (loc->components[type]) - return (loc->components[type]->locale); - return ("C"); + if (mask & LC_VERSION_MASK) { + if (loc->components[type]) + return (loc->components[type]->version); + return (""); + } else { + if (loc->components[type]) + return (loc->components[type]->locale); + return ("C"); + } } /* 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 @@ -33,7 +33,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 28, 2015 +.Dd September 15, 2018 .Dt LOCALEDEF 1 .Os .Sh NAME @@ -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 querylocale 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 querylocale 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);