Support setting locale per-mailbox (changes date/time languages for email, pager messages).

(closes issue #14333)
 Reported by: klaus3000
 Patches: 
       20090515__issue14333.diff.txt uploaded by tilghman (license 14)
       app_voicemail.c-svn-trunk-rev211675-patch.txt uploaded by klaus3000 (license 65)
 Tested by: klaus3000


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@266828 65c4cc65-6c06-0410-ace0-fbb531ad65f3
certified/1.8.6
Tilghman Lesher 15 years ago
parent ffbb85bb4d
commit b0357dcc3e

@ -149,6 +149,7 @@ Applications
* Added custom device states to ConfBridge bridges. Use 'confbridge:<name>' to
retrieve state for a particular bridge, where <name> is the conference name
* app_directory now allows exiting at any time using the operator or pound key.
* Voicemail now supports setting a locale per-mailbox.
Dialplan Functions
------------------

@ -636,6 +636,7 @@ struct ast_vm_user {
char mailcmd[160]; /*!< Configurable mail command */
char language[MAX_LANGUAGE]; /*!< Config: Language setting */
char zonetag[80]; /*!< Time zone */
char locale[20]; /*!< The locale (for presentation of date/time) */
char callback[80];
char dialout[80];
char uniqueid[80]; /*!< Unique integer identifier */
@ -773,6 +774,7 @@ static char *sayname_app = "VMSayName";
static AST_LIST_HEAD_STATIC(users, ast_vm_user);
static AST_LIST_HEAD_STATIC(zones, vm_zone);
static char zonetag[80];
static char locale[20];
static int maxsilence;
static int maxmsg;
static int maxdeletedmsg;
@ -994,6 +996,7 @@ static void populate_defaults(struct ast_vm_user *vmu)
ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
if (vmminsecs) {
vmu->minsecs = vmminsecs;
}
@ -1035,6 +1038,8 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
ast_copy_string(vmu->language, value, sizeof(vmu->language));
} else if (!strcasecmp(var, "tz")) {
ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
} else if (!strcasecmp(var, "locale")) {
ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
#ifdef IMAP_STORAGE
} else if (!strcasecmp(var, "imapuser")) {
ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
@ -4206,7 +4211,7 @@ static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu
struct timeval tv = { inttime, };
struct ast_tm tm;
ast_localtime(&tv, &tm, NULL);
ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
}
ast_config_destroy(msg_cfg);
@ -4377,18 +4382,18 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
} else {
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
greeting_attachment = strrchr(ast_strdupa(attach), '/');
if (greeting_attachment) {
*greeting_attachment++ = '\0';
}
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
fprintf(p, "Date: %s" ENDL, date);
/* Set date format for voicemail mail */
ast_strftime(date, sizeof(date), emaildateformat, &tm);
ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
if (!ast_strlen_zero(fromstring)) {
struct ast_channel *ast;
@ -4571,7 +4576,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
struct timeval tv = { inttime, };
struct ast_tm tm;
ast_localtime(&tv, &tm, NULL);
ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
}
fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
" a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
@ -4733,11 +4738,11 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
fprintf(p, "Date: %s\n", date);
/* Reformat for custom pager format */
ast_strftime(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm));
ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
if (!ast_strlen_zero(pagerfromstring)) {
struct ast_channel *ast;
@ -12005,6 +12010,9 @@ static int load_config(int reload)
if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
ast_copy_string(zonetag, val, sizeof(zonetag));
}
if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
ast_copy_string(locale, val, sizeof(locale));
}
if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
emailsubject = ast_strdup(val);
}

@ -93,7 +93,8 @@ maxlogins=3
; For the directory, you can override the intro file if you want
;directoryintro=dir-intro
; The character set for voicemail messages can be specified here
;charset=ISO-8859-1
; default: ISO-8859-1
;charset=UTF-8
; The ADSI feature descriptor number to download to
;adsifdn=0000000F
; The ADSI security lock code
@ -226,6 +227,13 @@ pagerdateformat=%A, %B %d, %Y at %r
; overridden in the per-mailbox settings, unless listed otherwise.
;
; tz=central ; Timezone from zonemessages below. Irrelevant if envelope=no.
; locale=de_DE.UTF-8 ; set the locale for generation of the date/time strings (make
; sure the locales are installed in your operating system; e.g
; on Debian Linux you can use "dpkg-reconfigure locales").
; If you use UTF-8 locales, make sure to set the "charset" option
; to UTF-8 too. If you mix different locales for different users
; you should avoid words in the emaildateformat specification, e.g.:
; emaildateformat=%A, %d %B %Y, %H:%M:%S
; attach=yes ; Attach the voicemail to the notification email *NOT* the pager email
; attachfmt=wav49 ; Which format to attach to the email. Normally this is the
; first format specified in the format parameter above, but this

52838
configure vendored

File diff suppressed because it is too large Load Diff

@ -442,7 +442,7 @@ AC_FUNC_STRNLEN
AC_FUNC_STRTOD
AC_FUNC_UTIME_NULL
AC_FUNC_VPRINTF
AC_CHECK_FUNCS([asprintf atexit closefrom dup2 eaccess endpwent euidaccess ffsll ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday glob htonll ioperm inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap ntohll putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtod strtol strtold strtoq unsetenv utime vasprintf getpeereid sysctl swapctl])
AC_CHECK_FUNCS([asprintf atexit closefrom dup2 eaccess endpwent euidaccess ffsll ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday glob htonll ioperm inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap ntohll newlocale putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtod strtol strtold strtoq unsetenv utime vasprintf getpeereid sysctl swapctl])
# NOTE: we use AC_CHECK_LIB to get -lm into the arguments for later checks,
# so that AC_CHECK_FUNCS can detect functions in that library.

@ -466,6 +466,9 @@
/* Define if your system has the NETSNMP libraries. */
#undef HAVE_NETSNMP
/* Define to 1 if you have the `newlocale' function. */
#undef HAVE_NEWLOCALE
/* Define to 1 if you have the newt library. */
#undef HAVE_NEWT
@ -770,7 +773,7 @@
/* Define to 1 if you have the `strtoq' function. */
#undef HAVE_STRTOQ
/* Define to 1 if `st_blksize' is a member of `struct stat'. */
/* Define to 1 if `st_blksize' is member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BLKSIZE
/* Define to 1 if you have the mISDN Supplemental Services library. */
@ -1038,12 +1041,12 @@
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if the C compiler supports function prototypes. */
#undef PROTOTYPES
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
@ -1063,6 +1066,11 @@
/* Define to the type of arg 5 for `select'. */
#undef SELECT_TYPE_ARG5
/* Define to 1 if the `setvbuf' function takes the buffering type as its
second argument and the buffer pointer as the third, as on System V before
release 3. */
#undef SETVBUF_REVERSED
/* The size of `char *', as computed by sizeof. */
#undef SIZEOF_CHAR_P
@ -1092,30 +1100,20 @@
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
/* Enable extensions on AIX 3, Interix. */
/* Define to 1 if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
#undef _LARGEFILE_SOURCE
@ -1133,6 +1131,20 @@
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE
/* Enable extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Define like PROTOTYPES; this can be used by system headers. */
#undef __PROTOTYPES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const

@ -1,10 +1,10 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
* Copyright (C) 1999 - 2010, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
* Tilghman Lesher <tlesher@vcch.com>
* Tilghman Lesher <tlesher AT digium DOT com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
@ -24,6 +24,12 @@
#ifndef _ASTERISK_LOCALTIME_H
#define _ASTERISK_LOCALTIME_H
#ifdef HAVE_NEWLOCALE
#include <locale.h>
#else
typedef void * locale_t;
#endif
struct ast_tm {
int tm_sec; /*!< Seconds. [0-60] (1 leap second) */
int tm_min; /*!< Minutes. [0-59] */
@ -57,6 +63,9 @@ void ast_get_dst_info(const time_t * const timep, int *dst_enabled, time_t *dst_
*/
struct timeval ast_mktime(struct ast_tm * const tmp, const char *zone);
/*!\brief Set the thread-local representation of the current locale. */
const char *ast_setlocale(const char *locale);
/*!\brief Special version of strftime(3) that handles fractions of a second.
* Takes the same arguments as strftime(3), with the addition of %q, which
* specifies microseconds.
@ -64,9 +73,11 @@ struct timeval ast_mktime(struct ast_tm * const tmp, const char *zone);
* \param len Size of the chunk of memory buf.
* \param format A string specifying the format of time to be placed into buf.
* \param tm Pointer to the broken out time to be used for the format.
* \param locale Text string specifying the locale to be used for language strings.
* \retval An integer value specifying the number of bytes placed into buf or -1 on error.
*/
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm);
int ast_strftime_locale(char *buf, size_t len, const char *format, const struct ast_tm *tm, const char *locale);
/*!\brief Special version of strptime(3) which places the answer in the common
* structure ast_tm. Also, unlike strptime(3), ast_strptime() initializes its
@ -74,9 +85,11 @@ int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm
* \param s A string specifying some portion of a date and time.
* \param format The format in which the string, s, is expected.
* \param tm The broken-out time structure into which the parsed data is expected.
* \param locale Text string specifying the locale to be used for language strings.
* \retval A pointer to the first character within s not used to parse the date and time.
*/
char *ast_strptime(const char *s, const char *format, struct ast_tm *tm);
char *ast_strptime_locale(const char *s, const char *format, struct ast_tm *tm, const char *locale);
/*!\brief Wakeup localtime monitor thread
* For use in testing. Normally, the failsafe monitor thread waits 60 seconds

@ -173,6 +173,12 @@ struct state {
AST_LIST_ENTRY(state) list;
};
struct locale_entry {
AST_LIST_ENTRY(locale_entry) list;
locale_t locale;
char name[0];
};
struct rule {
int r_type; /* type of rule--see below */
int r_day; /* day number of rule */
@ -235,6 +241,7 @@ static int tzparse P((const char * name, struct state * sp,
int lastditch));
static AST_LIST_HEAD_STATIC(zonelist, state);
static AST_LIST_HEAD_STATIC(localelist, locale_entry);
#ifndef TZ_STRLEN_MAX
#define TZ_STRLEN_MAX 255
@ -2128,15 +2135,104 @@ struct timeval ast_mktime(struct ast_tm *tmp, const char *zone)
return time1(tmp, localsub, 0L, sp);
}
int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm)
#ifdef HAVE_NEWLOCALE
static struct locale_entry *find_by_locale(locale_t locale)
{
struct locale_entry *cur;
AST_LIST_TRAVERSE(&localelist, cur, list) {
if (locale == cur->locale) {
return cur;
}
}
return NULL;
}
static struct locale_entry *find_by_name(const char *name)
{
struct locale_entry *cur;
AST_LIST_TRAVERSE(&localelist, cur, list) {
if (strcmp(name, cur->name) == 0) {
return cur;
}
}
return NULL;
}
static const char *store_by_locale(locale_t prevlocale)
{
struct locale_entry *cur;
if (prevlocale == LC_GLOBAL_LOCALE) {
return NULL;
} else {
/* Get a handle for this entry, if any */
if ((cur = find_by_locale(prevlocale))) {
return cur->name;
} else {
/* Create an entry, so it can be restored later */
int x;
cur = NULL;
AST_LIST_LOCK(&localelist);
for (x = 0; x < 10000; x++) {
char name[5];
snprintf(name, sizeof(name), "%04d", x);
if (!find_by_name(name)) {
if ((cur = ast_calloc(1, sizeof(*cur) + strlen(name) + 1))) {
cur->locale = prevlocale;
strcpy(cur->name, name); /* SAFE */
AST_LIST_INSERT_TAIL(&localelist, cur, list);
}
break;
}
}
AST_LIST_UNLOCK(&localelist);
return cur ? cur->name : NULL;
}
}
}
const char *ast_setlocale(const char *locale)
{
struct locale_entry *cur;
locale_t prevlocale = LC_GLOBAL_LOCALE;
if (locale == NULL) {
return store_by_locale(uselocale(LC_GLOBAL_LOCALE));
}
AST_LIST_LOCK(&localelist);
if ((cur = find_by_name(locale))) {
prevlocale = uselocale(cur->locale);
}
if (!cur) {
if ((cur = ast_calloc(1, sizeof(*cur) + strlen(locale) + 1))) {
cur->locale = newlocale(LC_ALL_MASK, locale, NULL);
strcpy(cur->name, locale); /* SAFE */
AST_LIST_INSERT_TAIL(&localelist, cur, list);
prevlocale = uselocale(cur->locale);
}
}
AST_LIST_UNLOCK(&localelist);
return store_by_locale(prevlocale);
}
#else
const char *ast_setlocale(const char *unused)
{
return NULL;
}
#endif
int ast_strftime_locale(char *buf, size_t len, const char *tmp, const struct ast_tm *tm, const char *locale)
{
size_t fmtlen = strlen(tmp) + 1;
char *format = ast_calloc(1, fmtlen), *fptr = format, *newfmt;
int decimals = -1, i, res;
long fraction;
const char *prevlocale;
if (!format)
if (!format) {
return -1;
}
for (; *tmp; tmp++) {
if (*tmp == '%') {
switch (tmp[1]) {
@ -2146,14 +2242,16 @@ int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm
case '4':
case '5':
case '6':
if (tmp[2] != 'q')
if (tmp[2] != 'q') {
goto defcase;
}
decimals = tmp[1] - '0';
tmp++;
/* Fall through */
case 'q': /* Milliseconds */
if (decimals == -1)
if (decimals == -1) {
decimals = 3;
}
/* Juggle some memory to fit the item */
newfmt = ast_realloc(format, fmtlen + decimals);
@ -2166,8 +2264,9 @@ int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm
fmtlen += decimals;
/* Reduce the fraction of time to the accuracy needed */
for (i = 6, fraction = tm->tm_usec; i > decimals; i--)
for (i = 6, fraction = tm->tm_usec; i > decimals; i--) {
fraction /= 10;
}
fptr += sprintf(fptr, "%0*ld", decimals, fraction);
/* Reset, in case more than one 'q' specifier exists */
@ -2177,20 +2276,37 @@ int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm
default:
goto defcase;
}
} else
} else {
defcase: *fptr++ = *tmp;
}
}
*fptr = '\0';
#undef strftime
if (locale) {
prevlocale = ast_setlocale(locale);
}
res = (int)strftime(buf, len, format, (struct tm *)tm);
if (locale) {
ast_setlocale(prevlocale);
}
ast_free(format);
return res;
}
char *ast_strptime(const char *s, const char *format, struct ast_tm *tm)
int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm)
{
return ast_strftime_locale(buf, len, tmp, tm, NULL);
}
char *ast_strptime_locale(const char *s, const char *format, struct ast_tm *tm, const char *locale)
{
struct tm tm2 = { 0, };
char *res = strptime(s, format, &tm2);
char *res;
const char *prevlocale;
prevlocale = ast_setlocale(locale);
res = strptime(s, format, &tm2);
ast_setlocale(prevlocale);
memcpy(tm, &tm2, sizeof(*tm));
tm->tm_usec = 0;
/* strptime(3) doesn't set .tm_isdst correctly, so to force ast_mktime(3)
@ -2199,3 +2315,8 @@ char *ast_strptime(const char *s, const char *format, struct ast_tm *tm)
return res;
}
char *ast_strptime(const char *s, const char *format, struct ast_tm *tm)
{
return ast_strptime_locale(s, format, tm, NULL);
}

@ -0,0 +1,183 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2009, Digium, Inc.
*
* Tilghman Lesher <tlesher AT digium DOT com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Locale Test
*
* \author\verbatim Tilghman Lesher <tlesher AT digium DOT com> \endverbatim
*
* \ingroup tests
*/
/*** MODULEINFO
<defaultenabled>no</defaultenabled>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/types.h>
#include <dirent.h>
#ifndef __USE_GNU
#define __USE_GNU 1
#endif
#include <locale.h>
#include "asterisk/cli.h"
#include "asterisk/linkedlists.h"
#include "asterisk/localtime.h"
#include "asterisk/utils.h"
#include "asterisk/module.h"
static char *handle_cli_test_locales(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
DIR *localedir;
struct dirent *dent;
struct ast_tm atm;
struct timeval tv;
const char *orig_locale;
char origlocalformat[200] = "", localformat[200] = "";
struct test_locales {
AST_LIST_ENTRY(test_locales) list;
char *localformat;
char name[0];
} *tl = NULL;
AST_LIST_HEAD_NOLOCK(locales, test_locales) locales;
int varies = 0, all_successful = 1, count = 0, count_fail = 0;
switch (cmd) {
case CLI_INIT:
e->command = "test locale";
e->usage = ""
"Usage: test locale\n"
" Test thread safety of locale functions.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != e->args) {
return CLI_SHOWUSAGE;
}
/* First we run a set of tests with the global locale, which isn't thread-safe. */
if (!(localedir = opendir(
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__)
"/usr/share/locale"
#else /* Linux */
"/usr/lib/locale"
#endif
))) {
ast_cli(a->fd, "No locales seem to exist on this platform.\n");
return CLI_SUCCESS;
}
tv = ast_tvnow();
ast_localtime(&tv, &atm, NULL);
orig_locale = setlocale(LC_ALL, NULL);
AST_LIST_HEAD_SET_NOLOCK(&locales, NULL);
/* Get something different, to compare against. */
ast_strftime(origlocalformat, sizeof(origlocalformat), "%c", &atm);
while ((dent = readdir(localedir))) {
size_t namelen;
if (dent->d_name[0] == '.') {
continue;
}
setlocale(LC_ALL, dent->d_name);
ast_strftime(localformat, sizeof(localformat), "%c", &atm);
/* Store values */
if (!(tl = ast_calloc(1, sizeof(*tl) + strlen(localformat) + (namelen = strlen(dent->d_name)) + 2))) {
continue;
}
strcpy(tl->name, dent->d_name); /* SAFE */
tl->localformat = tl->name + namelen + 1;
strcpy(tl->localformat, localformat); /* SAFE */
AST_LIST_INSERT_TAIL(&locales, tl, list);
/* Ensure that at least two entries differ, otherwise this test doesn't mean much. */
if (!varies && strcmp(AST_LIST_FIRST(&locales)->localformat, localformat)) {
varies = 1;
}
}
setlocale(LC_ALL, orig_locale);
closedir(localedir);
if (!varies) {
if (!strcmp(origlocalformat, localformat)) {
ast_cli(a->fd, "WARNING: the locales on your system don't differ. Install more locales if you want this test to mean something.\n");
}
}
orig_locale = ast_setlocale(AST_LIST_FIRST(&locales)->name);
while ((tl = AST_LIST_REMOVE_HEAD(&locales, list))) {
ast_setlocale(tl->name);
ast_strftime(localformat, sizeof(localformat), "%c", &atm);
if (strcmp(localformat, tl->localformat)) {
ast_cli(a->fd, "WARNING: locale test fails for locale %s\n", tl->name);
all_successful = 0;
count_fail++;
}
ast_free(tl);
count++;
}
ast_setlocale(orig_locale);
if (all_successful) {
ast_cli(a->fd, "All %d locale tests successful\n", count);
} else if (count_fail == count && count > 0) {
ast_cli(a->fd, "No locale tests successful out of %d tries\n", count);
} else if (count > 0) {
ast_cli(a->fd, "Partial failure (%d/%d) for a %.0f%% failure rate\n", count_fail, count, count_fail * 100.0 / count);
} else {
ast_cli(a->fd, "No locales tested. Install more locales.\n");
}
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_locales[] = {
AST_CLI_DEFINE(handle_cli_test_locales, "Test locales for thread-safety"),
};
static int unload_module(void)
{
ast_cli_unregister_multiple(cli_locales, ARRAY_LEN(cli_locales));
return 0;
}
static int load_module(void)
{
ast_cli_register_multiple(cli_locales, ARRAY_LEN(cli_locales));
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Locale tests");
Loading…
Cancel
Save