[asterisk-commits] tilghman: trunk r266828 - in /trunk: ./ apps/ configs/ include/asterisk/ main...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Jun 1 16:28:24 CDT 2010
Author: tilghman
Date: Tue Jun 1 16:28:19 2010
New Revision: 266828
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=266828
Log:
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
Added:
trunk/tests/test_locale.c (with props)
Modified:
trunk/CHANGES
trunk/apps/app_voicemail.c
trunk/configs/voicemail.conf.sample
trunk/configure
trunk/configure.ac
trunk/include/asterisk/autoconfig.h.in
trunk/include/asterisk/localtime.h
trunk/main/stdtime/localtime.c
Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=266828&r1=266827&r2=266828
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Tue Jun 1 16:28:19 2010
@@ -149,6 +149,7 @@
* 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
------------------
Modified: trunk/apps/app_voicemail.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_voicemail.c?view=diff&rev=266828&r1=266827&r2=266828
==============================================================================
--- trunk/apps/app_voicemail.c (original)
+++ trunk/apps/app_voicemail.c Tue Jun 1 16:28:19 2010
@@ -636,6 +636,7 @@
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 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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
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);
}
Modified: trunk/configs/voicemail.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/voicemail.conf.sample?view=diff&rev=266828&r1=266827&r2=266828
==============================================================================
--- trunk/configs/voicemail.conf.sample (original)
+++ trunk/configs/voicemail.conf.sample Tue Jun 1 16:28:19 2010
@@ -93,7 +93,8 @@
; 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 @@
; 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
Modified: trunk/configure.ac
URL: http://svnview.digium.com/svn/asterisk/trunk/configure.ac?view=diff&rev=266828&r1=266827&r2=266828
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Tue Jun 1 16:28:19 2010
@@ -442,7 +442,7 @@
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.
Modified: trunk/include/asterisk/autoconfig.h.in
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/autoconfig.h.in?view=diff&rev=266828&r1=266827&r2=266828
==============================================================================
--- trunk/include/asterisk/autoconfig.h.in (original)
+++ trunk/include/asterisk/autoconfig.h.in Tue Jun 1 16:28:19 2010
@@ -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,11 +1041,11 @@
/* 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. */
@@ -1062,6 +1065,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,46 +1100,50 @@
/* 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. */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#undef _LARGEFILE_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* 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
-/* 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
-
-/* Define for large files, on AIX-style hosts. */
-#undef _LARGE_FILES
-
-/* Define to 1 if on MINIX. */
-#undef _MINIX
-
-/* Define to 2 if the system does not provide POSIX.1 features except with
- this defined. */
-#undef _POSIX_1_SOURCE
-
-/* Define to 1 if you need to in order for `stat' and other things to work. */
-#undef _POSIX_SOURCE
+
+/* 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
Modified: trunk/include/asterisk/localtime.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/localtime.h?view=diff&rev=266828&r1=266827&r2=266828
==============================================================================
--- trunk/include/asterisk/localtime.h (original)
+++ trunk/include/asterisk/localtime.h Tue Jun 1 16:28:19 2010
@@ -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 at digium.com>
- * Tilghman Lesher <tlesher at 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
@@ -23,6 +23,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) */
@@ -57,6 +63,9 @@
*/
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 @@
* \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 @@
* \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
Modified: trunk/main/stdtime/localtime.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/stdtime/localtime.c?view=diff&rev=266828&r1=266827&r2=266828
==============================================================================
--- trunk/main/stdtime/localtime.c (original)
+++ trunk/main/stdtime/localtime.c Tue Jun 1 16:28:19 2010
@@ -171,6 +171,12 @@
time_t mtime[2];
#endif
AST_LIST_ENTRY(state) list;
+};
+
+struct locale_entry {
+ AST_LIST_ENTRY(locale_entry) list;
+ locale_t locale;
+ char name[0];
};
struct rule {
@@ -235,6 +241,7 @@
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 @@
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;
-
- if (!format)
+ const char *prevlocale;
+
+ if (!format) {
return -1;
+ }
for (; *tmp; tmp++) {
if (*tmp == '%') {
switch (tmp[1]) {
@@ -2146,14 +2242,16 @@
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 @@
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 @@
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 @@
return res;
}
+char *ast_strptime(const char *s, const char *format, struct ast_tm *tm)
+{
+ return ast_strptime_locale(s, format, tm, NULL);
+}
+
Added: trunk/tests/test_locale.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_locale.c?view=auto&rev=266828
==============================================================================
--- trunk/tests/test_locale.c (added)
+++ trunk/tests/test_locale.c Tue Jun 1 16:28:19 2010
@@ -1,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");
Propchange: trunk/tests/test_locale.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: trunk/tests/test_locale.c
------------------------------------------------------------------------------
svn:keywords = 'Date Author Id Revision Yoyo'
Propchange: trunk/tests/test_locale.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
More information about the asterisk-commits
mailing list