Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
func_sayfiles.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2021, Naveen Albert <asterisk@phreaknet.org>
5 *
6 * See http://www.asterisk.org for more information about
7 * the Asterisk project. Please do not directly contact
8 * any of the maintainers of this project for assistance;
9 * the project provides a web site, mailing lists and IRC
10 * channels for your use.
11 *
12 * This program is free software, distributed under the terms of
13 * the GNU General Public License Version 2. See the LICENSE file
14 * at the top of the source tree.
15 */
16
17/*! \file
18 *
19 * \brief Returns files played by Say applications
20 *
21 * \author Naveen Albert <asterisk@phreaknet.org>
22 * \ingroup functions
23 */
24
25/*** MODULEINFO
26 <support_level>extended</support_level>
27 ***/
28
29#include "asterisk.h"
30
31#include "asterisk/pbx.h"
32#include "asterisk/file.h"
33#include "asterisk/channel.h"
34#include "asterisk/say.h"
35#include "asterisk/lock.h"
36#include "asterisk/localtime.h"
37#include "asterisk/utils.h"
38#include "asterisk/app.h"
39#include "asterisk/test.h"
40#include "asterisk/module.h"
42
43/*** DOCUMENTATION
44 <function name="SAYFILES" language="en_US">
45 <since>
46 <version>16.21.0</version>
47 <version>18.7.0</version>
48 </since>
49 <synopsis>
50 Returns the ampersand-delimited file names that would be played by the Say applications (e.g. SayAlpha, SayDigits).
51 </synopsis>
52 <syntax>
53 <parameter name="value" required="true">
54 <para>The value to be translated to filenames.</para>
55 </parameter>
56 <parameter name="type">
57 <para>Say application type.</para>
58 <enumlist>
59 <enum name="alpha">
60 <para>Files played by SayAlpha(). Default if none is specified.</para>
61 </enum>
62 <enum name="digits">
63 <para>Files played by SayDigits().</para>
64 </enum>
65 <enum name="money">
66 <para>Files played by SayMoney(). Currently supported for English and US dollars only.</para>
67 </enum>
68 <enum name="number">
69 <para>Files played by SayNumber(). Currently supported for English only.</para>
70 </enum>
71 <enum name="ordinal">
72 <para>Files played by SayOrdinal(). Currently supported for English only.</para>
73 </enum>
74 <enum name="phonetic">
75 <para>Files played by SayPhonetic().</para>
76 </enum>
77 </enumlist>
78 </parameter>
79 </syntax>
80 <description>
81 <para>Returns the files that would be played by a Say application. These filenames could then be
82 passed directly into Playback, BackGround, Read, Queue, or any application which supports
83 playback of multiple ampersand-delimited files.</para>
84 <example title="Read using the number 123">
85 same => n,Read(response,${SAYFILES(123,number)})
86 </example>
87 </description>
88 <see-also>
89 <ref type="application">SayAlpha</ref>
90 <ref type="application">SayDigits</ref>
91 <ref type="application">SayMoney</ref>
92 <ref type="application">SayNumber</ref>
93 <ref type="application">SayOrdinal</ref>
94 <ref type="application">SayPhonetic</ref>
95 </see-also>
96 </function>
97 ***/
98static int sayfile_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
99{
100 char *value, *type, *files;
101 const char *lang;
102 struct ast_str *filenames = NULL;
106 );
107
108 if (ast_strlen_zero(data)) {
109 ast_log(LOG_WARNING, "SAYFILES requires an argument\n");
110 return 0;
111 }
112
114
115 value = args.value;
116 type = (ast_strlen_zero(args.type) ? "alpha" : args.type);
117 lang = (chan ? ast_channel_language(chan) : "en"); /* No chan for unit tests */
118
119 if (!strcmp(type, "alpha")) {
121 } else if (!strcmp(type, "phonetic")) {
122 filenames = ast_get_phonetic_str(value, lang);
123 } else if (!strcmp(type, "digits")) {
124 filenames = ast_get_digit_str(value, lang);
125 } else if (!strcmp(type, "number")) {
126 int num;
127 if (ast_str_to_int(value, &num)) {
128 ast_log(LOG_WARNING, "Invalid numeric argument: %s\n", value);
129 } else {
130 filenames = ast_get_number_str(num, lang);
131 }
132 } else if (!strcmp(type, "ordinal")) {
133 int num;
134 if (ast_str_to_int(value, &num)) {
135 ast_log(LOG_WARNING, "Invalid numeric argument: %s\n", value);
136 } else {
137 filenames = ast_get_ordinal_str(num, lang);
138 }
139 } else if (!strcmp(type, "money")) {
140 filenames = ast_get_money_str(value, lang);
141 } else {
142 ast_log(LOG_WARNING, "Invalid say type specified: %s\n", type);
143 }
144
145 if (!filenames) {
146 return -1;
147 }
148
149 files = ast_str_buffer(filenames);
150 snprintf(buf, len, "%s", files);
151 ast_free(filenames);
152
153 return 0;
154}
155
157 .name = "SAYFILES",
158 .read = sayfile_exec,
159};
160
161#ifdef TEST_FRAMEWORK
162AST_TEST_DEFINE(test_SAYFILES_function)
163{
165 struct ast_str *expr, *result;
166
167 switch (cmd) {
168 case TEST_INIT:
169 info->name = "test_SAYFILES_function";
170 info->category = "/funcs/func_sayfiles/";
171 info->summary = "Test SAYFILES function substitution";
172 info->description =
173 "Executes a series of variable substitutions using the SAYFILES function and ensures that the expected results are received.";
174 return AST_TEST_NOT_RUN;
175 case TEST_EXECUTE:
176 break;
177 }
178
179 ast_test_status_update(test, "Testing SAYFILES() substitution ...\n");
180
181 if (!(expr = ast_str_create(16))) {
182 return AST_TEST_FAIL;
183 }
184 if (!(result = ast_str_create(16))) {
185 ast_free(expr);
186 return AST_TEST_FAIL;
187 }
188
189 ast_str_set(&expr, 0, "${SAYFILES(hi Th3re,alpha)}");
191 if (strcmp(ast_str_buffer(result), "letters/h&letters/i&letters/space&letters/t&letters/h&digits/3&letters/r&letters/e") != 0) {
192 ast_test_status_update(test, "SAYFILES(hi Th3re,alpha) test failed ('%s')\n",
194 res = AST_TEST_FAIL;
195 }
196
197 ast_str_set(&expr, 0, "${SAYFILES(phreak,phonetic)}");
199 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) {
200 ast_test_status_update(test, "SAYFILES(phreak,phonetic) test failed ('%s')\n",
202 res = AST_TEST_FAIL;
203 }
204
205 ast_str_set(&expr, 0, "${SAYFILES(35,digits)}");
207 if (strcmp(ast_str_buffer(result), "digits/3&digits/5") != 0) {
208 ast_test_status_update(test, "SAYFILES(35,digits) test failed ('%s')\n",
210 res = AST_TEST_FAIL;
211 }
212
213 /* + should be ignored and there should not be a leading & */
214 ast_str_set(&expr, 0, "${SAYFILES(+18005551212,digits)}");
216 if (strcmp(ast_str_buffer(result), "digits/1&digits/8&digits/0&digits/0&digits/5&digits/5&digits/5&digits/1&digits/2&digits/1&digits/2") != 0) {
217 ast_test_status_update(test, "SAYFILES(+18005551212,digits) test failed ('%s')\n",
219 res = AST_TEST_FAIL;
220 }
221
222 ast_str_set(&expr, 0, "${SAYFILES(35,number)}");
224 if (strcmp(ast_str_buffer(result), "digits/30&digits/5") != 0) {
225 ast_test_status_update(test, "SAYFILES(35,number) test failed ('%s')\n",
227 res = AST_TEST_FAIL;
228 }
229
230 ast_str_set(&expr, 0, "${SAYFILES(747,number)}");
232 if (strcmp(ast_str_buffer(result), "digits/7&digits/hundred&digits/40&digits/7") != 0) {
233 ast_test_status_update(test, "SAYFILES(747,number) test failed ('%s')\n",
235 res = AST_TEST_FAIL;
236 }
237
238 ast_str_set(&expr, 0, "${SAYFILES(1042,number)}");
240 if (strcmp(ast_str_buffer(result), "digits/1&digits/thousand&digits/40&digits/2") != 0) {
241 ast_test_status_update(test, "SAYFILES(1042,number) test failed ('%s')\n",
243 res = AST_TEST_FAIL;
244 }
245
246 ast_str_set(&expr, 0, "${SAYFILES(0,number)}");
248 if (strcmp(ast_str_buffer(result), "digits/0") != 0) {
249 ast_test_status_update(test, "SAYFILES(0,digits) test failed ('%s')\n",
251 res = AST_TEST_FAIL;
252 }
253
254 ast_str_set(&expr, 0, "${SAYFILES(2001000001,number)}");
256 if (strcmp(ast_str_buffer(result), "digits/2&digits/billion&digits/1&digits/million&digits/1") != 0) {
257 ast_test_status_update(test, "SAYFILES(2001000001,number) test failed ('%s')\n",
259 res = AST_TEST_FAIL;
260 }
261
262 ast_str_set(&expr, 0, "${SAYFILES(7,ordinal)}");
264 if (strcmp(ast_str_buffer(result), "digits/h-7") != 0) {
265 ast_test_status_update(test, "SAYFILES(7,ordinal) test failed ('%s')\n",
267 res = AST_TEST_FAIL;
268 }
269
270 ast_str_set(&expr, 0, "${SAYFILES(35,ordinal)}");
272 if (strcmp(ast_str_buffer(result), "digits/30&digits/h-5") != 0) {
273 ast_test_status_update(test, "SAYFILES(35,ordinal) test failed ('%s')\n",
275 res = AST_TEST_FAIL;
276 }
277
278 ast_str_set(&expr, 0, "${SAYFILES(1042,ordinal)}");
280 if (strcmp(ast_str_buffer(result), "digits/1&digits/thousand&digits/40&digits/h-2") != 0) {
281 ast_test_status_update(test, "SAYFILES(1042,ordinal) test failed ('%s')\n",
283 res = AST_TEST_FAIL;
284 }
285
286 ast_str_set(&expr, 0, "${SAYFILES(11042,ordinal)}");
288 if (strcmp(ast_str_buffer(result), "digits/11&digits/thousand&digits/40&digits/h-2") != 0) {
289 ast_test_status_update(test, "SAYFILES(11042,ordinal) test failed ('%s')\n",
291 res = AST_TEST_FAIL;
292 }
293
294 ast_str_set(&expr, 0, "${SAYFILES(40000,ordinal)}");
296 if (strcmp(ast_str_buffer(result), "digits/40&digits/h-thousand") != 0) {
297 ast_test_status_update(test, "SAYFILES(40000,ordinal) test failed ('%s')\n",
299 res = AST_TEST_FAIL;
300 }
301
302 ast_str_set(&expr, 0, "${SAYFILES(43638,ordinal)}");
304 if (strcmp(ast_str_buffer(result), "digits/40&digits/3&digits/thousand&digits/6&digits/hundred&digits/30&digits/h-8") != 0) {
305 ast_test_status_update(test, "SAYFILES(43638,ordinal) test failed ('%s')\n",
307 res = AST_TEST_FAIL;
308 }
309
310 ast_str_set(&expr, 0, "${SAYFILES(1000000,ordinal)}");
312 if (strcmp(ast_str_buffer(result), "digits/1&digits/h-million") != 0) {
313 ast_test_status_update(test, "SAYFILES(1000000,ordinal) test failed ('%s')\n",
315 res = AST_TEST_FAIL;
316 }
317
318 ast_str_set(&expr, 0, "${SAYFILES(1000001,ordinal)}");
320 if (strcmp(ast_str_buffer(result), "digits/1&digits/million&digits/h-1") != 0) {
321 ast_test_status_update(test, "SAYFILES(1000001,ordinal) test failed ('%s')\n",
323 res = AST_TEST_FAIL;
324 }
325
326 ast_str_set(&expr, 0, "${SAYFILES(2001000001,ordinal)}");
328 if (strcmp(ast_str_buffer(result), "digits/2&digits/billion&digits/1&digits/million&digits/h-1") != 0) {
329 ast_test_status_update(test, "SAYFILES(2001000001,ordinal) test failed ('%s')\n",
331 res = AST_TEST_FAIL;
332 }
333
334 ast_str_set(&expr, 0, "${SAYFILES(0,money)}");
336 if (strcmp(ast_str_buffer(result), "digits/0&cents") != 0) {
337 ast_test_status_update(test, "SAYFILES(0,money) test failed ('%s')\n",
339 res = AST_TEST_FAIL;
340 }
341
342 ast_str_set(&expr, 0, "${SAYFILES(0.01,money)}");
344 if (strcmp(ast_str_buffer(result), "digits/1&cent") != 0) {
345 ast_test_status_update(test, "SAYFILES(0.01,money) test failed ('%s')\n",
347 res = AST_TEST_FAIL;
348 }
349
350 ast_str_set(&expr, 0, "${SAYFILES(0.42,money)}");
352 if (strcmp(ast_str_buffer(result), "digits/40&digits/2&cents") != 0) {
353 ast_test_status_update(test, "SAYFILES(0.42,money) test failed ('%s')\n",
355 res = AST_TEST_FAIL;
356 }
357
358 ast_str_set(&expr, 0, "${SAYFILES(.42,money)}");
360 if (strcmp(ast_str_buffer(result), "digits/40&digits/2&cents") != 0) {
361 ast_test_status_update(test, "SAYFILES(.42,money) test failed ('%s')\n",
363 res = AST_TEST_FAIL;
364 }
365
366 ast_str_set(&expr, 0, "${SAYFILES(1.00,money)}");
368 if (strcmp(ast_str_buffer(result), "digits/1&letters/dollar") != 0) {
369 ast_test_status_update(test, "SAYFILES(1.00,money) test failed ('%s')\n",
371 res = AST_TEST_FAIL;
372 }
373
374 ast_str_set(&expr, 0, "${SAYFILES(1.42,money)}");
376 if (strcmp(ast_str_buffer(result), "digits/1&letters/dollar_&and&digits/40&digits/2&cents") != 0) {
377 ast_test_status_update(test, "SAYFILES(1.42,money) test failed ('%s')\n",
379 res = AST_TEST_FAIL;
380 }
381
382 ast_str_set(&expr, 0, "${SAYFILES(2.00,money)}");
384 if (strcmp(ast_str_buffer(result), "digits/2&dollars") != 0) {
385 ast_test_status_update(test, "SAYFILES(2.00,money) test failed ('%s')\n",
387 res = AST_TEST_FAIL;
388 }
389
390 ast_str_set(&expr, 0, "${SAYFILES(2,money)}");
392 if (strcmp(ast_str_buffer(result), "digits/2&dollars") != 0) {
393 ast_test_status_update(test, "SAYFILES(2,money) test failed ('%s')\n",
395 res = AST_TEST_FAIL;
396 }
397
398 ast_str_set(&expr, 0, "${SAYFILES(2.42,money)}");
400 if (strcmp(ast_str_buffer(result), "digits/2&dollars&and&digits/40&digits/2&cents") != 0) {
401 ast_test_status_update(test, "SAYFILES(2.42,money) test failed ('%s')\n",
403 res = AST_TEST_FAIL;
404 }
405
406 ast_str_set(&expr, 0, "${SAYFILES(2.05,money)}");
408 if (strcmp(ast_str_buffer(result), "digits/2&dollars&and&digits/5&cents") != 0) {
409 ast_test_status_update(test, "SAYFILES(2.05,money) test failed ('%s')\n",
411 res = AST_TEST_FAIL;
412 }
413
414 ast_str_set(&expr, 0, "${SAYFILES(2.051,money)}");
416 if (strcmp(ast_str_buffer(result), "digits/2&dollars&and&digits/5&cents") != 0) {
417 ast_test_status_update(test, "SAYFILES(2.051,money) test failed ('%s')\n",
419 res = AST_TEST_FAIL;
420 }
421
422 /* Invalid amounts */
423 ast_str_set(&expr, 0, "${SAYFILES(blah,money)}");
425 if (strcmp(ast_str_buffer(result), "digits/0&cents") != 0) {
426 ast_test_status_update(test, "SAYFILES(blah,money) test failed ('%s')\n",
428 res = AST_TEST_FAIL;
429 }
430
431 ast_str_set(&expr, 0, "${SAYFILES(2blah.05,money)}");
433 if (strcmp(ast_str_buffer(result), "digits/2&dollars") != 0) {
434 ast_test_status_update(test, "SAYFILES(2blah.05,money) test failed ('%s')\n",
436 res = AST_TEST_FAIL;
437 }
438
439 ast_str_set(&expr, 0, "${SAYFILES(2.-05,money)}");
441 if (strcmp(ast_str_buffer(result), "digits/2&dollars") != 0) {
442 ast_test_status_update(test, "SAYFILES(2.-05,money) test failed ('%s')\n",
444 res = AST_TEST_FAIL;
445 }
446
447 ast_str_set(&expr, 0, "${SAYFILES(2. 05,money)}");
449 if (strcmp(ast_str_buffer(result), "digits/2&dollars") != 0) {
450 ast_test_status_update(test, "SAYFILES(2. 05,money) test failed ('%s')\n",
452 res = AST_TEST_FAIL;
453 }
454
455 ast_str_set(&expr, 0, "${SAYFILES(. 05,money)}");
457 if (strcmp(ast_str_buffer(result), "digits/0&cents") != 0) {
458 ast_test_status_update(test, "SAYFILES(. 05,money) test failed ('%s')\n",
460 res = AST_TEST_FAIL;
461 }
462
463 ast_free(expr);
465
466 return res;
467}
468#endif
469
470static int unload_module(void)
471{
472 AST_TEST_UNREGISTER(test_SAYFILES_function);
474}
475
476static int load_module(void)
477{
478 AST_TEST_REGISTER(test_SAYFILES_function);
480}
481
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_log
Definition: astobj2.c:42
static PGresult * result
Definition: cel_pgsql.c:84
static const char type[]
Definition: chan_ooh323.c:109
General Asterisk PBX channel definitions.
const char * ast_channel_language(const struct ast_channel *chan)
Conversion utility functions.
int ast_str_to_int(const char *str, int *res)
Convert the given string to a signed integer.
Definition: conversions.c:44
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
static struct ast_custom_function sayfiles
static int sayfile_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_sayfiles.c:98
static int load_module(void)
static int unload_module(void)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define LOG_WARNING
Custom localtime functions for multiple timezones.
Asterisk locking-related definitions:
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
def info(msg)
Core PBX routines and definitions.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define NULL
Definition: resample.c:96
Say numbers and dates (maybe words one day too)
struct ast_str * ast_get_ordinal_str(int num, const char *lang)
Returns an ast_str of files for SayOrdinal playback.
Definition: say.c:686
struct ast_str * ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity)
Returns an ast_str of files for SayAlpha playback.
Definition: say.c:64
struct ast_str * ast_get_number_str(int num, const char *lang)
Returns an ast_str of files for SayNumber playback.
Definition: say.c:566
struct ast_str * ast_get_phonetic_str(const char *str, const char *lang)
Returns an ast_str of files for SayPhonetic playback.
Definition: say.c:216
struct ast_str * ast_get_money_str(const char *str, const char *lang)
Returns an ast_str of files for SayMoney playback.
Definition: say.c:449
@ AST_SAY_CASE_NONE
Definition: say.h:182
struct ast_str * ast_get_digit_str(const char *str, const char *lang)
Returns an ast_str of files for SayDigits playback.
Definition: say.c:300
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Support for dynamic strings.
Definition: strings.h:623
int value
Definition: syslog.c:37
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
const char * args
Utility functions.