[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