<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16467">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  George Joseph: Looks good to me, approved; Approved for Submit

</div><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;"><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&cents") != 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&cent") != 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&cents") != 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&cents") != 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&cents") != 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><div style="white-space:pre-wrap"></div><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: 2 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>