<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16467">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">func_sayfiles: Retrieve say file names<br><br>Up until now, all of the logic used to translate<br>arguments to the Say applications has been<br>directly coupled to playback, preventing other<br>modules from using this logic.<br><br>This refactors code in say.c and adds a SAYFILES<br>function that can be used to retrieve the file<br>names that would be played. These can then be<br>used in other applications or for other purposes.<br><br>Additionally, a SayMoney application and a SayOrdinal<br>application are added. Both SayOrdinal and SayNumber<br>are also expanded to support integers greater than<br>one billion.<br><br>ASTERISK-29531<br><br>Change-Id: If9718c89353b8e153d84add3cc4637b79585db19<br>---<br>A doc/CHANGES-staging/say.txt<br>A funcs/func_sayfiles.c<br>M include/asterisk/say.h<br>M main/channel.c<br>M main/pbx_builtins.c<br>M main/say.c<br>6 files changed, 1,056 insertions(+), 95 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/67/16467/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/CHANGES-staging/say.txt b/doc/CHANGES-staging/say.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..115ceea</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/say.txt</span><br><span>@@ -0,0 +1,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: say.c</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Adds SAYFILES function to retrieve the file names that would</span><br><span style="color: hsl(120, 100%, 40%);">+be played by corresponding Say applications, such as</span><br><span style="color: hsl(120, 100%, 40%);">+SayDigits, SayAlpha, etc.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Additionally adds SayMoney and SayOrdinal applications.</span><br><span>diff --git a/funcs/func_sayfiles.c b/funcs/func_sayfiles.c</span><br><span>new file mode 100644</span><br><span>index 0000000..81f3259</span><br><span>--- /dev/null</span><br><span>+++ b/funcs/func_sayfiles.c</span><br><span>@@ -0,0 +1,396 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns files played by Say applications</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup functions</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/say.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/lock.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/localtime.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/test.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/conversions.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <function name="SAYFILES" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Returns the ampersand-delimited file names that would be played by the Say applications (e.g. SayAlpha, SayDigits).</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="value" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The value to be translated to filenames.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="type"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Say application type.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="alpha"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Files played by SayAlpha(). Default if none is specified.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </enum></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="digits"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Files played by SayDigits().</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </enum></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="money"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Files played by SayMoney(). Currently supported for English and US dollars only.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </enum></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="number"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Files played by SayNumber(). Currently supported for English only.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </enum></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="ordinal"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Files played by SayOrdinal(). Currently supported for English only.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </enum></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="phonetic"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Files played by SayPhonetic().</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </enum></span><br><span style="color: hsl(120, 100%, 40%);">+ </enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Returns the files that would be played by a Say application. These filenames could then be</span><br><span style="color: hsl(120, 100%, 40%);">+ passed directly into Playback, BackGround, Read, Queue, or any application which supports</span><br><span style="color: hsl(120, 100%, 40%);">+ playback of multiple ampersand-delimited files.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="Read using the number 123"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Read(response,${SAYFILES(123,number)})</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayAlpha</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayDigits</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayMoney</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayNumber</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayOrdinal</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayPhonetic</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ </function></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+static int sayfile_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *value, *type, *files;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *lang;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(value);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(type);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "SAYFILES requires an argument\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ value = args.value;</span><br><span style="color: hsl(120, 100%, 40%);">+ type = (ast_strlen_zero(args.type) ? "alpha" : args.type);</span><br><span style="color: hsl(120, 100%, 40%);">+ lang = (chan ? ast_channel_language(chan) : "en"); /* No chan for unit tests */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcmp(type, "alpha")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_get_character_str(value, lang, AST_SAY_CASE_NONE);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcmp(type, "phonetic")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_get_phonetic_str(value, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcmp(type, "digits")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_get_digit_str(value, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcmp(type, "number")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int num;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_str_to_int(value, &num)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid numeric argument: %s\n", value);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_get_number_str(num, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcmp(type, "ordinal")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int num;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_str_to_int(value, &num)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid numeric argument: %s\n", value);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_get_ordinal_str(num, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcmp(type, "money")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_get_money_str(value, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid say type specified: %s\n", type);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ files = ast_str_buffer(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(buf, len, "%s", files);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_custom_function sayfiles = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "SAYFILES",</span><br><span style="color: hsl(120, 100%, 40%);">+ .read = sayfile_exec,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(test_SAYFILES_function)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_test_result_state res = AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *expr, *result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = "test_SAYFILES_function";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = "/funcs/func_sayfiles/";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test SAYFILES function substitution";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Executes a series of variable substitutions using the SAYFILES function and ensures that the expected results are received.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing SAYFILES() substitution ...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(expr = ast_str_create(16))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(result = ast_str_create(16))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(expr);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(hi Th3re,alpha)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "letters/h&letters/i&letters/space&letters/t&letters/h&digits/3&letters/r&letters/e") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(hi Th3re,alpha) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(phreak,phonetic)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "phonetic/p_p&phonetic/h_p&phonetic/r_p&phonetic/e_p&phonetic/a_p&phonetic/k_p") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(phreak,phonetic) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(35,digits)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/3&digits/5") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(35,digits) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(35,number)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/30&digits/5") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(35,number) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(747,number)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/7&digits/hundred&digits/40&digits/7") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(747,number) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(1042,number)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/1&digits/thousand&digits/40&digits/2") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(1042,number) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(0,number)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/0") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(0,digits) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(2001000001,number)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/2&digits/billion&digits/1&digits/million&digits/1") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(2001000001,number) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(7,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/h-7") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(7,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(35,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/30&digits/h-5") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(35,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(1042,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/1&digits/thousand&digits/40&digits/h-2") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(1042,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(11042,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/11&digits/thousand&digits/40&digits/h-2") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(11042,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(40000,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/40&digits/h-thousand") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(40000,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(43638,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/40&digits/3&digits/thousand&digits/6&digits/hundred&digits/30&digits/h-8") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(43638,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(1000000,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/1&digits/h-million") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(1000000,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(1000001,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/1&digits/million&digits/h-1") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(1000001,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(2001000001,ordinal)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/2&digits/billion&digits/1&digits/million&digits/h-1") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(2001000001,ordinal) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(0,money)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/0¢s") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(0,money) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(0.01,money)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/1¢") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(0.01,money) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(0.42,money)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/40&digits/2¢s") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(0.42,money) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(1.00,money)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/1&letters/dollar") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(1.00,money) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(1.42,money)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/1&letters/dollar_&and&digits/40&digits/2¢s") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(1.42,money) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(2.00,money)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/2&dollars") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(2.00,money) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&expr, 0, "${SAYFILES(2.42,money)}");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_str_buffer(result), "digits/2&dollars&and&digits/40&digits/2¢s") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "SAYFILES(2.42,money) test failed ('%s')\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_buffer(result));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(expr);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(result);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(test_SAYFILES_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_custom_function_unregister(&sayfiles);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(test_SAYFILES_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_custom_function_register(&sayfiles);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say application files");</span><br><span>diff --git a/include/asterisk/say.h b/include/asterisk/say.h</span><br><span>index a4aa90c..5e55b0f 100644</span><br><span>--- a/include/asterisk/say.h</span><br><span>+++ b/include/asterisk/say.h</span><br><span>@@ -86,6 +86,25 @@</span><br><span> SAY_EXTERN int (* ast_say_number_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_number_full);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief says an ordinal number</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan channel to say them number on</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param num ordinal number to say on the channel</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param ints which dtmf to interrupt on</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param lang language to speak the number</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param options set to 'f' for female, 'm' for male, 'c' for commune, 'n' for neuter</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * Vocally says an ordinal number on a given channel</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval DTMF digit on interrupt</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_say_ordinal(struct ast_channel *chan, int num,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ints, const char *lang, const char *options);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Same as \ref ast_say_number() with audiofd for received audio and returns 1 on ctrlfd being readable */</span><br><span style="color: hsl(120, 100%, 40%);">+SAY_EXTERN int (* ast_say_ordinal_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_ordinal_full);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief says an enumeration</span><br><span> * \param chan channel to say them enumeration on</span><br><span> * \param num number to say on the channel</span><br><span>@@ -143,6 +162,14 @@</span><br><span> SAY_EXTERN int (* ast_say_digit_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_digit_str_full);</span><br><span> </span><br><span> /*! \brief</span><br><span style="color: hsl(120, 100%, 40%);">+ * function to pronounce monetary amounts</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_say_money_str(struct ast_channel *chan, const char *num,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ints, const char *lang);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+SAY_EXTERN int (* ast_say_money_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_money_str_full);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief</span><br><span> * the generic 'say' routine, with the first chars in the string</span><br><span> * defining the format to use</span><br><span> */</span><br><span>@@ -184,6 +211,79 @@</span><br><span> </span><br><span> int ast_say_counted_adjective(struct ast_channel *chan, int num, const char *adjective, const char *gender);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns an ast_str of files for SayAlpha playback.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param str Text to be translated to the corresponding audio files.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param lang Channel language</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param sensitivity Case sensitivity</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Computes the list of files to be played by SayAlpha.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval ampersand-separated string of Asterisk sound files that can be played back.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns an ast_str of files for SayPhonetic playback.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param str Text to be translated to the corresponding audio files.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param lang Channel language</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Computes the list of files to be played by SayPhonetic.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval ampersand-separated string of Asterisk sound files that can be played back.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_phonetic_str(const char *str, const char *lang);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns an ast_str of files for SayDigits playback.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param str Text to be translated to the corresponding audio files.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param lang Channel language</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Computes the list of files to be played by SayDigits.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval ampersand-separated string of Asterisk sound files that can be played back.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_digit_str(const char *str, const char *lang);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns an ast_str of files for SayMoney playback.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param str Text to be translated to the corresponding audio files.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param lang Channel language</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Computes the list of files to be played by SayMoney.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval ampersand-separated string of Asterisk sound files that can be played back.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_money_str(const char *str, const char *lang);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns an ast_str of files for SayNumber playback.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param num Integer to be translated to the corresponding audio files.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param lang Channel language</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Computes the list of files to be played by SayNumber.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval ampersand-separated string of Asterisk sound files that can be played back.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_number_str(int num, const char *lang);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns an ast_str of files for SayOrdinal playback.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param num Integer to be translated to the corresponding audio files.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param lang Channel language</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Computes the list of files to be played by SayOrdinal.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval ampersand-separated string of Asterisk sound files that can be played back.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_ordinal_str(int num, const char *lang);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #if defined(__cplusplus) || defined(c_plusplus)</span><br><span> }</span><br><span> #endif</span><br><span>diff --git a/main/channel.c b/main/channel.c</span><br><span>index 9e33fb2..9104ef8 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -8257,6 +8257,12 @@</span><br><span> return ast_say_number_full(chan, num, ints, language, options, -1, -1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_say_ordinal(struct ast_channel *chan, int num,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ints, const char *language, const char *options)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_say_ordinal_full(chan, num, ints, language, options, -1, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_say_enumeration(struct ast_channel *chan, int num,</span><br><span> const char *ints, const char *language, const char *options)</span><br><span> {</span><br><span>@@ -8275,6 +8281,12 @@</span><br><span> return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_say_money_str(struct ast_channel *chan, const char *str,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ints, const char *lang)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_say_money_str_full(chan, str, ints, lang, -1, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_say_character_str(struct ast_channel *chan, const char *str,</span><br><span> const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)</span><br><span> {</span><br><span>diff --git a/main/pbx_builtins.c b/main/pbx_builtins.c</span><br><span>index e370338..eae4c6b 100644</span><br><span>--- a/main/pbx_builtins.c</span><br><span>+++ b/main/pbx_builtins.c</span><br><span>@@ -37,6 +37,7 @@</span><br><span> #include "asterisk/say.h"</span><br><span> #include "asterisk/app.h"</span><br><span> #include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/conversions.h"</span><br><span> #include "pbx_private.h"</span><br><span> </span><br><span> /*** DOCUMENTATION</span><br><span>@@ -440,9 +441,12 @@</span><br><span> </description></span><br><span> <see-also></span><br><span> <ref type="application">SayDigits</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayMoney</ref></span><br><span> <ref type="application">SayNumber</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayOrdinal</ref></span><br><span> <ref type="application">SayPhonetic</ref></span><br><span> <ref type="function">CHANNEL</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">SAYFILES</ref></span><br><span> </see-also></span><br><span> </application></span><br><span> <application name="SayAlphaCase" language="en_US"></span><br><span>@@ -481,7 +485,9 @@</span><br><span> </description></span><br><span> <see-also></span><br><span> <ref type="application">SayDigits</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayMoney</ref></span><br><span> <ref type="application">SayNumber</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayOrdinal</ref></span><br><span> <ref type="application">SayPhonetic</ref></span><br><span> <ref type="application">SayAlpha</ref></span><br><span> <ref type="function">CHANNEL</ref></span><br><span>@@ -503,9 +509,35 @@</span><br><span> </description></span><br><span> <see-also></span><br><span> <ref type="application">SayAlpha</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayMoney</ref></span><br><span> <ref type="application">SayNumber</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayOrdinal</ref></span><br><span> <ref type="application">SayPhonetic</ref></span><br><span> <ref type="function">CHANNEL</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">SAYFILES</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="SayMoney" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Say Money.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="dollars" required="true" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application will play the currency sounds for the given floating point number</span><br><span style="color: hsl(120, 100%, 40%);">+ in the current language. Currently only English and US Dollars is supported.</span><br><span style="color: hsl(120, 100%, 40%);">+ If the channel variable <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true'</span><br><span style="color: hsl(120, 100%, 40%);">+ (case insensitive), then this application will react to DTMF in the same way as</span><br><span style="color: hsl(120, 100%, 40%);">+ <literal>Background</literal>.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayAlpha</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayNumber</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayOrdinal</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayPhonetic</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">CHANNEL</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">SAYFILES</ref></span><br><span> </see-also></span><br><span> </application></span><br><span> <application name="SayNumber" language="en_US"></span><br><span>@@ -527,8 +559,37 @@</span><br><span> <see-also></span><br><span> <ref type="application">SayAlpha</ref></span><br><span> <ref type="application">SayDigits</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayMoney</ref></span><br><span> <ref type="application">SayPhonetic</ref></span><br><span> <ref type="function">CHANNEL</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">SAYFILES</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="SayOrdinal" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Say Ordinal Number.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="digits" required="true" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="gender" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application will play the ordinal sounds that correspond to the given</span><br><span style="color: hsl(120, 100%, 40%);">+ <replaceable>digits</replaceable> (e.g. 1st, 42nd). Currently only English is supported.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Optionally, a <replaceable>gender</replaceable> may be</span><br><span style="color: hsl(120, 100%, 40%);">+ specified. This will use the language that is currently set for the channel. See the CHANNEL()</span><br><span style="color: hsl(120, 100%, 40%);">+ function for more information on setting the language for the channel. If the channel variable</span><br><span style="color: hsl(120, 100%, 40%);">+ <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this</span><br><span style="color: hsl(120, 100%, 40%);">+ application will react to DTMF in the same way as <literal>Background</literal>.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayAlpha</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayDigits</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayMoney</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayNumber</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayPhonetic</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">CHANNEL</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">SAYFILES</ref></span><br><span> </see-also></span><br><span> </application></span><br><span> <application name="SayPhonetic" language="en_US"></span><br><span>@@ -547,7 +608,10 @@</span><br><span> <see-also></span><br><span> <ref type="application">SayAlpha</ref></span><br><span> <ref type="application">SayDigits</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayMoney</ref></span><br><span> <ref type="application">SayNumber</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SayOrdinal</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="function">SAYFILES</ref></span><br><span> </see-also></span><br><span> </application></span><br><span> <application name="SetAMAFlags" language="en_US"></span><br><span>@@ -1283,7 +1347,7 @@</span><br><span> ast_copy_string(tmp, data, sizeof(tmp));</span><br><span> strsep(&number, ",");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (sscanf(tmp, "%d", &number_val) != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_str_to_int(tmp, &number_val)) {</span><br><span> ast_log(LOG_WARNING, "argument '%s' to SayNumber could not be parsed as a number.\n", tmp);</span><br><span> return 0;</span><br><span> }</span><br><span>@@ -1306,6 +1370,53 @@</span><br><span> return interrupt ? res : 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int pbx_builtin_sayordinal(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char tmp[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ char *number = tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+ int number_val;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *options;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ int interrupt = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *interrupt_string;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_true(interrupt_string)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ interrupt = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "SayOrdinal requires an argument (number)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(tmp, data, sizeof(tmp));</span><br><span style="color: hsl(120, 100%, 40%);">+ strsep(&number, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_str_to_int(tmp, &number_val)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "argument '%s' to SayOrdinal could not be parsed as a number.\n", tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ options = strsep(&number, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (options) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ( strcasecmp(options, "f") && strcasecmp(options, "m") &&</span><br><span style="color: hsl(120, 100%, 40%);">+ strcasecmp(options, "c") && strcasecmp(options, "n") ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "SayOrdinal gender option is either 'f', 'm', 'c' or 'n'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_say_ordinal(chan, number_val, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), options);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res < 0 && !ast_check_hangup_locked(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return interrupt ? res : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int pbx_builtin_saydigits(struct ast_channel *chan, const char *data)</span><br><span> {</span><br><span> int res = 0;</span><br><span>@@ -1326,6 +1437,26 @@</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int pbx_builtin_saymoney(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int interrupt = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *interrupt_string;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_true(interrupt_string)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ interrupt = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_say_money_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int pbx_builtin_saycharacters_case(struct ast_channel *chan, const char *data)</span><br><span> {</span><br><span> int res = 0;</span><br><span>@@ -1483,7 +1614,9 @@</span><br><span> { "SayAlpha", pbx_builtin_saycharacters },</span><br><span> { "SayAlphaCase", pbx_builtin_saycharacters_case },</span><br><span> { "SayDigits", pbx_builtin_saydigits },</span><br><span style="color: hsl(120, 100%, 40%);">+ { "SayMoney", pbx_builtin_saymoney },</span><br><span> { "SayNumber", pbx_builtin_saynumber },</span><br><span style="color: hsl(120, 100%, 40%);">+ { "SayOrdinal", pbx_builtin_sayordinal },</span><br><span> { "SayPhonetic", pbx_builtin_sayphonetic },</span><br><span> { "SetAMAFlags", pbx_builtin_setamaflags },</span><br><span> { "Wait", pbx_builtin_wait },</span><br><span>diff --git a/main/say.c b/main/say.c</span><br><span>index 0a37091..009ee8f 100644</span><br><span>--- a/main/say.c</span><br><span>+++ b/main/say.c</span><br><span>@@ -29,6 +29,8 @@</span><br><span> * Next Generation Networks (NGN).</span><br><span> * \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,</span><br><span> * IP Crossing Co., Ltd.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note 2021-07-26 : Refactoring to separate string buildup and playback</span><br><span style="color: hsl(120, 100%, 40%);">+ * by Naveen Albert <asterisk@phreaknet.org></span><br><span> */</span><br><span> </span><br><span> /*** MODULEINFO</span><br><span>@@ -58,9 +60,7 @@</span><br><span> /* Forward declaration */</span><br><span> static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {</span><br><span> const char *fn;</span><br><span> char fnbuf[10], asciibuf[20] = "letters/ascii";</span><br><span> char ltr;</span><br><span>@@ -69,6 +69,12 @@</span><br><span> int upper = 0;</span><br><span> int lower = 0;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_str_create(20);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_reset(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> while (str[num] && !res) {</span><br><span> fn = NULL;</span><br><span> switch (str[num]) {</span><br><span>@@ -154,14 +160,7 @@</span><br><span> }</span><br><span> if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||</span><br><span> (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_streamfile(chan, fn, lang);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!res) {</span><br><span style="color: hsl(0, 100%, 40%);">- if ((audiofd > -1) && (ctrlfd > -1))</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);</span><br><span style="color: hsl(0, 100%, 40%);">- else</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream(chan, ints);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stopstream(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);</span><br><span> }</span><br><span> if (upper || lower) {</span><br><span> continue;</span><br><span>@@ -169,18 +168,56 @@</span><br><span> num++;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ return filenames;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int say_filenames(struct ast_channel *chan, const char *ints, const char *lang, int audiofd, int ctrlfd, struct ast_str *filenames)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *files;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *fn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ files = ast_str_buffer(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((fn = strsep(&files, "&"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_streamfile(chan, fn, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((audiofd > -1) && (ctrlfd > -1))</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_waitstream(chan, ints);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stopstream(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)</span><br><span style="color: hsl(120, 100%, 40%);">+static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);</span><br><span style="color: hsl(120, 100%, 40%);">+ return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)</span><br><span> {</span><br><span> const char *fn;</span><br><span> char fnbuf[256];</span><br><span> char ltr;</span><br><span> int num = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- int res = 0;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- while (str[num] && !res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_str_create(20);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_reset(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (str[num]) {</span><br><span> fn = NULL;</span><br><span> switch (str[num]) {</span><br><span> case ('*'):</span><br><span>@@ -237,29 +274,33 @@</span><br><span> fn = fnbuf;</span><br><span> }</span><br><span> if (fn && ast_fileexists(fn, NULL, lang) > 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_streamfile(chan, fn, lang);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!res) {</span><br><span style="color: hsl(0, 100%, 40%);">- if ((audiofd > -1) && (ctrlfd > -1))</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);</span><br><span style="color: hsl(0, 100%, 40%);">- else</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream(chan, ints);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stopstream(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);</span><br><span> }</span><br><span> num++;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return res;</span><br><span style="color: hsl(120, 100%, 40%);">+ return filenames;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)</span><br><span style="color: hsl(120, 100%, 40%);">+static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_get_phonetic_str(str, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_digit_str(const char *str, const char *lang)</span><br><span> {</span><br><span> const char *fn;</span><br><span> char fnbuf[256];</span><br><span> int num = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- int res = 0;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- while (str[num] && !res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_str_create(20);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_reset(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (str[num]) {</span><br><span> fn = NULL;</span><br><span> switch (str[num]) {</span><br><span> case ('*'):</span><br><span>@@ -287,19 +328,340 @@</span><br><span> break;</span><br><span> }</span><br><span> if (fn && ast_fileexists(fn, NULL, lang) > 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_streamfile(chan, fn, lang);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!res) {</span><br><span style="color: hsl(0, 100%, 40%);">- if ((audiofd > -1) && (ctrlfd > -1))</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);</span><br><span style="color: hsl(0, 100%, 40%);">- else</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream(chan, ints);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stopstream(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);</span><br><span> }</span><br><span> num++;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return res;</span><br><span style="color: hsl(120, 100%, 40%);">+ return filenames;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_get_digit_str(str, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *fnr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ double dollars = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int amt, cents;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *fnrecurse = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_str_create(20);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_reset(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sscanf(str, "%30lf", &dollars) != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ amt = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else { /* convert everything to cents */</span><br><span style="color: hsl(120, 100%, 40%);">+ amt = dollars * 100;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Just the cents after the dollar decimal point */</span><br><span style="color: hsl(120, 100%, 40%);">+ cents = amt - (((int) dollars) * 100);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Cents is %d, amount is %d\n", cents, amt);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (amt >= 100) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = ast_get_number_str((amt / 100), lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Couldn't get string for dollars\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, "%s", fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If this is it, end on a down pitch, otherwise up pitch */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (amt < 200) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, "&%s", "dollars");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If dollars and cents, add "and" in the middle */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cents > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, "&%s", "and");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cents > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = ast_get_number_str(cents, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for cents\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (amt == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = ast_get_digit_str("0", lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for cents\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, "%s", fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, "&%s", "cents");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return filenames;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief ast_get_money_str: call language-specific functions */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_money_str(const char *str, const char *lang)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strncasecmp(lang, "en", 2)) { /* English syntax */</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_get_money_en_dollars_str(str, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Default to english */</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_get_money_en_dollars_str(str, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_get_money_str(str, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_str* get_number_str_en(int num, const char *lang)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *fnr;</span><br><span style="color: hsl(120, 100%, 40%);">+ int loops = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int playh = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char fn[256] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!num) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_get_digit_str("0", lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_str_create(20);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_reset(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (!res && (num || playh)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, "digits/minus", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ if ( num > INT_MIN ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ num = -num;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ num = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (playh) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, "digits/hundred", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ playh = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (num < 20) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "digits/%d", num);</span><br><span style="color: hsl(120, 100%, 40%);">+ num = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (num < 100) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 10;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 1000){</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));</span><br><span style="color: hsl(120, 100%, 40%);">+ playh++;</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 100;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *fnrecurse = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 1000000) { /* 1,000,000 */</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = get_number_str_en((num / 1000), lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for num\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "&digits/thousand");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 1000000000) { /* 1,000,000,000 */</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = get_number_str_en((num / 1000000), lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for num\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 1000000;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, "&digits/million", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < INT_MAX) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = get_number_str_en((num / 1000000000), lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for num\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 1000000000;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, "&digits/billion", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* we already decided whether or not to add an &, don't add another one immediately */</span><br><span style="color: hsl(120, 100%, 40%);">+ loops = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);</span><br><span style="color: hsl(120, 100%, 40%);">+ loops++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return filenames;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief ast_get_number_str: call language-specific functions */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_number_str(int num, const char *lang)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strncasecmp(lang, "en", 2)) { /* English syntax */</span><br><span style="color: hsl(120, 100%, 40%);">+ return get_number_str_en(num, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Default to english */</span><br><span style="color: hsl(120, 100%, 40%);">+ return get_number_str_en(num, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_str* get_ordinal_str_en(int num, const char *lang)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *fnr;</span><br><span style="color: hsl(120, 100%, 40%);">+ int loops = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int playh = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char fn[256] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!num) {</span><br><span style="color: hsl(120, 100%, 40%);">+ num = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ filenames = ast_str_create(20);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filenames) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_reset(filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (!res && (num || playh)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, "digits/minus", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ if ( num > INT_MIN ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ num = -num;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ num = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (playh) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, (num % 100 == 0) ? "digits/h-hundred" : "digits/hundred", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ playh = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (num < 20) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "digits/h-%d", num);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Unsupported ordinal number: %d\n", num);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (num < 100) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int base = (num / 10) * 10;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (base != num) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "digits/%d", base);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "digits/h-%d", base);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 10;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 1000){</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));</span><br><span style="color: hsl(120, 100%, 40%);">+ playh++;</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 100;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *fnrecurse = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 1000000) { /* 1,000,000 */</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = get_number_str_en((num / 1000), lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for num\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(fn, sizeof(fn), (num % 1000 == 0) ? "&digits/h-thousand" : "&digits/thousand");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < 1000000000) { /* 1,000,000,000 */</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = get_number_str_en((num / 1000000), lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for num\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 1000000;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, (num % 1000000 == 0) ? "&digits/h-million" : "&digits/million", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (num < INT_MAX) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnrecurse = get_number_str_en((num / 1000000000), lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Couldn't get string for num\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fnr = ast_str_buffer(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ num %= 1000000000;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(fn, (num % 1000000000 == 0) ? "&digits/h-billion" : "&digits/billion", sizeof(fn));</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fnrecurse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(fnrecurse);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* we already decided whether or not to add an &, don't add another one immediately */</span><br><span style="color: hsl(120, 100%, 40%);">+ loops = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);</span><br><span style="color: hsl(120, 100%, 40%);">+ loops++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return filenames;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief ast_get_ordinal_str: call language-specific functions */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_str* ast_get_ordinal_str(int num, const char *lang)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strncasecmp(lang, "en", 2)) { /* English syntax */</span><br><span style="color: hsl(120, 100%, 40%);">+ return get_ordinal_str_en(num, lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Default to english */</span><br><span style="color: hsl(120, 100%, 40%);">+ return get_ordinal_str_en(num, lang);</span><br><span> }</span><br><span> </span><br><span> /* Forward declarations */</span><br><span>@@ -540,66 +902,15 @@</span><br><span> \note This is the default syntax, if no other syntax defined in this file is used */</span><br><span> static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- int res = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- int playh = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- char fn[256] = "";</span><br><span style="color: hsl(0, 100%, 40%);">- if (!num)</span><br><span style="color: hsl(0, 100%, 40%);">- return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_get_number_str(num, language);</span><br><span style="color: hsl(120, 100%, 40%);">+ return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- while (!res && (num || playh)) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (num < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_copy_string(fn, "digits/minus", sizeof(fn));</span><br><span style="color: hsl(0, 100%, 40%);">- if ( num > INT_MIN ) {</span><br><span style="color: hsl(0, 100%, 40%);">- num = -num;</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- num = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (playh) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_copy_string(fn, "digits/hundred", sizeof(fn));</span><br><span style="color: hsl(0, 100%, 40%);">- playh = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (num < 20) {</span><br><span style="color: hsl(0, 100%, 40%);">- snprintf(fn, sizeof(fn), "digits/%d", num);</span><br><span style="color: hsl(0, 100%, 40%);">- num = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (num < 100) {</span><br><span style="color: hsl(0, 100%, 40%);">- snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);</span><br><span style="color: hsl(0, 100%, 40%);">- num %= 10;</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- if (num < 1000){</span><br><span style="color: hsl(0, 100%, 40%);">- snprintf(fn, sizeof(fn), "digits/%d", (num/100));</span><br><span style="color: hsl(0, 100%, 40%);">- playh++;</span><br><span style="color: hsl(0, 100%, 40%);">- num %= 100;</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- if (num < 1000000) { /* 1,000,000 */</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);</span><br><span style="color: hsl(0, 100%, 40%);">- if (res)</span><br><span style="color: hsl(0, 100%, 40%);">- return res;</span><br><span style="color: hsl(0, 100%, 40%);">- num %= 1000;</span><br><span style="color: hsl(0, 100%, 40%);">- snprintf(fn, sizeof(fn), "digits/thousand");</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- if (num < 1000000000) { /* 1,000,000,000 */</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);</span><br><span style="color: hsl(0, 100%, 40%);">- if (res)</span><br><span style="color: hsl(0, 100%, 40%);">- return res;</span><br><span style="color: hsl(0, 100%, 40%);">- num %= 1000000;</span><br><span style="color: hsl(0, 100%, 40%);">- ast_copy_string(fn, "digits/million", sizeof(fn));</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Number '%d' is too big for me\n", num);</span><br><span style="color: hsl(0, 100%, 40%);">- res = -1;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- if (!res) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_streamfile(chan, fn, language)) {</span><br><span style="color: hsl(0, 100%, 40%);">- if ((audiofd > -1) && (ctrlfd > -1))</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);</span><br><span style="color: hsl(0, 100%, 40%);">- else</span><br><span style="color: hsl(0, 100%, 40%);">- res = ast_waitstream(chan, ints);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stopstream(chan);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- return res;</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief say_ordinal_full */</span><br><span style="color: hsl(120, 100%, 40%);">+static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *filenames = ast_get_ordinal_str(num, language);</span><br><span style="color: hsl(120, 100%, 40%);">+ return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);</span><br><span> }</span><br><span> </span><br><span> static int exp10_int(int power)</span><br><span>@@ -9410,8 +9721,10 @@</span><br><span> static void __attribute__((constructor)) __say_init(void)</span><br><span> {</span><br><span> ast_say_number_full = say_number_full;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_say_ordinal_full = say_ordinal_full;</span><br><span> ast_say_enumeration_full = say_enumeration_full;</span><br><span> ast_say_digit_str_full = say_digit_str_full;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_say_money_str_full = say_money_str_full;</span><br><span> ast_say_character_str_full = say_character_str_full;</span><br><span> ast_say_phonetic_str_full = say_phonetic_str_full;</span><br><span> ast_say_datetime = say_datetime;</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/16467">change 16467</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/16467"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 18 </div>
<div style="display:none"> Gerrit-Change-Id: If9718c89353b8e153d84add3cc4637b79585db19 </div>
<div style="display:none"> Gerrit-Change-Number: 16467 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>