Let's also include aclocal.m4
[asterisk-bristuff.git] / main / say.c
blob5903b45977ae592b2ca75141715bc61b34702325
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
7 * George Konstantoulakis <gkon@inaccessnetworks.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
20 /*! \file
22 * \brief Say numbers and dates (maybe words one day too)
24 * \author Mark Spencer <markster@digium.com>
26 * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
28 * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
29 * Next Generation Networks (NGN).
32 #include "asterisk.h"
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include <sys/types.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <netinet/in.h>
40 #include <time.h>
41 #include <ctype.h>
42 #include <math.h>
43 #include <stdio.h>
45 #ifdef SOLARIS
46 #include <iso/limits_iso.h>
47 #endif
49 #include "asterisk/file.h"
50 #include "asterisk/channel.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/options.h"
53 #include "asterisk/say.h"
54 #include "asterisk/lock.h"
55 #include "asterisk/localtime.h"
56 #include "asterisk/utils.h"
58 /* Forward declaration */
59 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
62 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
64 const char *fn;
65 char fnbuf[256];
66 char ltr;
67 int num = 0;
68 int res = 0;
70 while (str[num] && !res) {
71 fn = NULL;
72 switch (str[num]) {
73 case ('*'):
74 fn = "digits/star";
75 break;
76 case ('#'):
77 fn = "digits/pound";
78 break;
79 case ('!'):
80 fn = "letters/exclaimation-point";
81 break;
82 case ('@'):
83 fn = "letters/at";
84 break;
85 case ('$'):
86 fn = "letters/dollar";
87 break;
88 case ('-'):
89 fn = "letters/dash";
90 break;
91 case ('.'):
92 fn = "letters/dot";
93 break;
94 case ('='):
95 fn = "letters/equals";
96 break;
97 case ('+'):
98 fn = "letters/plus";
99 break;
100 case ('/'):
101 fn = "letters/slash";
102 break;
103 case (' '):
104 fn = "letters/space";
105 break;
106 case ('0'):
107 case ('1'):
108 case ('2'):
109 case ('3'):
110 case ('4'):
111 case ('5'):
112 case ('6'):
113 case ('7'):
114 case ('8'):
115 case ('9'):
116 strcpy(fnbuf, "digits/X");
117 fnbuf[7] = str[num];
118 fn = fnbuf;
119 break;
120 default:
121 ltr = str[num];
122 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
123 strcpy(fnbuf, "letters/X");
124 fnbuf[8] = ltr;
125 fn = fnbuf;
127 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
128 res = ast_streamfile(chan, fn, lang);
129 if (!res) {
130 if ((audiofd > -1) && (ctrlfd > -1))
131 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
132 else
133 res = ast_waitstream(chan, ints);
135 ast_stopstream(chan);
137 num++;
140 return res;
143 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
145 const char *fn;
146 char fnbuf[256];
147 char ltr;
148 int num = 0;
149 int res = 0;
151 while (str[num] && !res) {
152 fn = NULL;
153 switch (str[num]) {
154 case ('*'):
155 fn = "digits/star";
156 break;
157 case ('#'):
158 fn = "digits/pound";
159 break;
160 case ('!'):
161 fn = "letters/exclaimation-point";
162 break;
163 case ('@'):
164 fn = "letters/at";
165 break;
166 case ('$'):
167 fn = "letters/dollar";
168 break;
169 case ('-'):
170 fn = "letters/dash";
171 break;
172 case ('.'):
173 fn = "letters/dot";
174 break;
175 case ('='):
176 fn = "letters/equals";
177 break;
178 case ('+'):
179 fn = "letters/plus";
180 break;
181 case ('/'):
182 fn = "letters/slash";
183 break;
184 case (' '):
185 fn = "letters/space";
186 break;
187 case ('0'):
188 case ('1'):
189 case ('2'):
190 case ('3'):
191 case ('4'):
192 case ('5'):
193 case ('6'):
194 case ('7'):
195 case ('8'):
196 strcpy(fnbuf, "digits/X");
197 fnbuf[7] = str[num];
198 fn = fnbuf;
199 break;
200 default: /* '9' falls here... */
201 ltr = str[num];
202 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
203 strcpy(fnbuf, "phonetic/X_p");
204 fnbuf[9] = ltr;
205 fn = fnbuf;
207 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
208 res = ast_streamfile(chan, fn, lang);
209 if (!res) {
210 if ((audiofd > -1) && (ctrlfd > -1))
211 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
212 else
213 res = ast_waitstream(chan, ints);
215 ast_stopstream(chan);
217 num++;
220 return res;
223 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
225 const char *fn;
226 char fnbuf[256];
227 int num = 0;
228 int res = 0;
230 while (str[num] && !res) {
231 fn = NULL;
232 switch (str[num]) {
233 case ('*'):
234 fn = "digits/star";
235 break;
236 case ('#'):
237 fn = "digits/pound";
238 break;
239 case ('-'):
240 fn = "digits/minus";
241 break;
242 case '0':
243 case '1':
244 case '2':
245 case '3':
246 case '4':
247 case '5':
248 case '6':
249 case '7':
250 case '8':
251 case '9':
252 strcpy(fnbuf, "digits/X");
253 fnbuf[7] = str[num];
254 fn = fnbuf;
255 break;
257 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
258 res = ast_streamfile(chan, fn, lang);
259 if (!res) {
260 if ((audiofd > -1) && (ctrlfd > -1))
261 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
262 else
263 res = ast_waitstream(chan, ints);
265 ast_stopstream(chan);
267 num++;
270 return res;
273 /* Forward declarations */
274 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
275 \note Not really language codes.
276 For these language codes, Asterisk will change the syntax when
277 saying numbers (and in some cases dates and voicemail messages
278 as well)
279 \arg \b da - Danish
280 \arg \b de - German
281 \arg \b en - English (US)
282 \arg \b en_GB - English (British)
283 \arg \b es - Spanish, Mexican
284 \arg \b fr - French
285 \arg \b he - Hebrew
286 \arg \b it - Italian
287 \arg \b nl - Dutch
288 \arg \b no - Norwegian
289 \arg \b pl - Polish
290 \arg \b pt - Portuguese
291 \arg \b pt_BR - Portuguese (Brazil)
292 \arg \b se - Swedish
293 \arg \b tw - Taiwanese / Chinese
294 \arg \b ru - Russian
295 \arg \b ge - Georgian
297 \par Gender:
298 For Some languages the numbers differ for gender and plural.
299 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
300 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
301 use the option argument 'p' for plural enumerations like in German
303 Date/Time functions currently have less languages supported than saynumber().
305 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
307 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
309 \par Portuguese
310 Portuguese sound files needed for Time/Date functions:
311 pt-ah
312 pt-ao
313 pt-de
314 pt-e
315 pt-ora
316 pt-meianoite
317 pt-meiodia
318 pt-sss
320 \par Spanish
321 Spanish sound files needed for Time/Date functions:
322 es-de
323 es-el
325 \par Italian
326 Italian sound files needed for Time/Date functions:
327 ore-una
328 ore-mezzanotte
332 /* Forward declarations of language specific variants of ast_say_number_full */
333 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
334 static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
335 static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
336 static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
337 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
338 static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
339 static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
340 static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
341 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
342 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
343 static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
344 static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
345 static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
346 static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
347 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
348 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
349 static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
350 static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
352 /* Forward declarations of language specific variants of ast_say_enumeration_full */
353 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
354 static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
355 static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
356 static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
358 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
359 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
360 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
361 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
362 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
363 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
364 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
370 static int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
371 static int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
372 static int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
373 static int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
374 static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
375 static int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
376 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
377 static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
378 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
379 static int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
380 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
382 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
383 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
384 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
385 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
386 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
387 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
388 static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
389 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
390 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
391 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
399 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
400 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
402 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
404 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
405 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
406 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
407 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
408 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
410 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
412 int res;
413 if ((res = ast_streamfile(chan, file, lang)))
414 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
415 if (!res)
416 res = ast_waitstream(chan, ints);
417 return res;
420 /*! \brief ast_say_number_full: call language-specific functions */
421 /* Called from AGI */
422 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
424 if (!strcasecmp(language,"en") ) { /* English syntax */
425 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
426 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
427 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
428 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
429 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
430 } else if (!strcasecmp(language, "de") ) { /* German syntax */
431 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
432 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
433 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
434 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
435 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
436 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
437 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
438 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
439 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
440 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
441 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
442 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
443 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
444 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
445 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
446 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
447 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
448 } else if (!strcasecmp(language, "pt") || !strcasecmp(language, "pt_BR")) { /* Portuguese syntax */
449 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
450 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
451 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
452 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
453 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
454 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
455 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
456 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
457 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
458 } else if (!strcasecmp(language, "ge") ) { /* Georgian syntax */
459 return(ast_say_number_full_ge(chan, num, ints, language, options, audiofd, ctrlfd));
462 /* Default to english */
463 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
466 /*! \brief ast_say_number_full_en: English syntax */
467 /* This is the default syntax, if no other syntax defined in this file is used */
468 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
470 int res = 0;
471 int playh = 0;
472 char fn[256] = "";
473 if (!num)
474 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
476 while (!res && (num || playh)) {
477 if (num < 0) {
478 snprintf(fn, sizeof(fn), "digits/minus");
479 if ( num > INT_MIN ) {
480 num = -num;
481 } else {
482 num = 0;
484 } else if (playh) {
485 snprintf(fn, sizeof(fn), "digits/hundred");
486 playh = 0;
487 } else if (num < 20) {
488 snprintf(fn, sizeof(fn), "digits/%d", num);
489 num = 0;
490 } else if (num < 100) {
491 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
492 num -= ((num / 10) * 10);
493 } else {
494 if (num < 1000){
495 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
496 playh++;
497 num -= ((num / 100) * 100);
498 } else {
499 if (num < 1000000) { /* 1,000,000 */
500 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
501 if (res)
502 return res;
503 num = num % 1000;
504 snprintf(fn, sizeof(fn), "digits/thousand");
505 } else {
506 if (num < 1000000000) { /* 1,000,000,000 */
507 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
508 if (res)
509 return res;
510 num = num % 1000000;
511 snprintf(fn, sizeof(fn), "digits/million");
512 } else {
513 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
514 res = -1;
519 if (!res) {
520 if (!ast_streamfile(chan, fn, language)) {
521 if ((audiofd > -1) && (ctrlfd > -1))
522 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
523 else
524 res = ast_waitstream(chan, ints);
526 ast_stopstream(chan);
529 return res;
532 static int exp10_int(int power)
534 int x, res= 1;
535 for (x=0;x<power;x++)
536 res *= 10;
537 return res;
540 /*! \brief ast_say_number_full_cz: Czech syntax */
541 /* files needed:
542 * 1m,2m - gender male
543 * 1w,2w - gender female
544 * 3,4,...,20
545 * 30,40,...,90
547 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
549 * for each number 10^(3n + 3) exist 3 files represented as:
550 * 1 tousand = jeden tisic = 1_E3
551 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
552 * 5,6,... tousands = pet,sest,... tisic = 5_E3
554 * million = _E6
555 * miliard = _E9
556 * etc...
558 * tousand, milion are gender male, so 1 and 2 is 1m 2m
559 * miliard is gender female, so 1 and 2 is 1w 2w
561 static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
563 int res = 0;
564 int playh = 0;
565 char fn[256] = "";
567 int hundered = 0;
568 int left = 0;
569 int length = 0;
571 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
572 if (!options)
573 options = "w";
575 if (!num)
576 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
578 while (!res && (num || playh)) {
579 if (num < 0) {
580 snprintf(fn, sizeof(fn), "digits/minus");
581 if ( num > INT_MIN ) {
582 num = -num;
583 } else {
584 num = 0;
586 } else if (num < 3 ) {
587 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
588 playh = 0;
589 num = 0;
590 } else if (num < 20) {
591 snprintf(fn, sizeof(fn), "digits/%d",num);
592 playh = 0;
593 num = 0;
594 } else if (num < 100) {
595 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
596 num -= ((num / 10) * 10);
597 } else if (num < 1000) {
598 hundered = num / 100;
599 if ( hundered == 1 ) {
600 snprintf(fn, sizeof(fn), "digits/1sto");
601 } else if ( hundered == 2 ) {
602 snprintf(fn, sizeof(fn), "digits/2ste");
603 } else {
604 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
605 if (res)
606 return res;
607 if (hundered == 3 || hundered == 4) {
608 snprintf(fn, sizeof(fn), "digits/sta");
609 } else if ( hundered > 4 ) {
610 snprintf(fn, sizeof(fn), "digits/set");
613 num -= (hundered * 100);
614 } else { /* num > 1000 */
615 length = (int)log10(num)+1;
616 while ( (length % 3 ) != 1 ) {
617 length--;
619 left = num / (exp10_int(length-1));
620 if ( left == 2 ) {
621 switch (length-1) {
622 case 9: options = "w"; /* 1,000,000,000 gender female */
623 break;
624 default : options = "m"; /* others are male */
627 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
628 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
629 if (res)
630 return res;
632 if ( left >= 5 ) { /* >= 5 have the same declesion */
633 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
634 } else if ( left >= 2 && left <= 4 ) {
635 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
636 } else { /* left == 1 */
637 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
639 num -= left * (exp10_int(length-1));
641 if (!res) {
642 if (!ast_streamfile(chan, fn, language)) {
643 if ((audiofd > -1) && (ctrlfd > -1)) {
644 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
645 } else {
646 res = ast_waitstream(chan, ints);
649 ast_stopstream(chan);
652 return res;
655 /*! \brief ast_say_number_full_da: Danish syntax */
656 /* New files:
657 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
659 static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
661 int res = 0;
662 int playh = 0;
663 int playa = 0;
664 int cn = 1; /* +1 = commune; -1 = neuter */
665 char fn[256] = "";
666 if (!num)
667 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
669 if (options && !strncasecmp(options, "n",1)) cn = -1;
671 while (!res && (num || playh || playa )) {
672 /* The grammar for Danish numbers is the same as for English except
673 * for the following:
674 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
675 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
676 * "one-and twenty" and 68 is "eight-and sixty".
677 * - "million" is different in singular and plural form
678 * - numbers > 1000 with zero as the third digit from last have an
679 * "and" before the last two digits, i.e. 2034 is "two thousand and
680 * four-and thirty" and 1000012 is "one million and twelve".
682 if (num < 0) {
683 snprintf(fn, sizeof(fn), "digits/minus");
684 if ( num > INT_MIN ) {
685 num = -num;
686 } else {
687 num = 0;
689 } else if (playh) {
690 snprintf(fn, sizeof(fn), "digits/hundred");
691 playh = 0;
692 } else if (playa) {
693 snprintf(fn, sizeof(fn), "digits/and");
694 playa = 0;
695 } else if (num == 1 && cn == -1) {
696 snprintf(fn, sizeof(fn), "digits/1N");
697 num = 0;
698 } else if (num < 20) {
699 snprintf(fn, sizeof(fn), "digits/%d", num);
700 num = 0;
701 } else if (num < 100) {
702 int ones = num % 10;
703 if (ones) {
704 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
705 num -= ones;
706 } else {
707 snprintf(fn, sizeof(fn), "digits/%d", num);
708 num = 0;
710 } else {
711 if (num < 1000) {
712 int hundreds = num / 100;
713 if (hundreds == 1)
714 snprintf(fn, sizeof(fn), "digits/1N");
715 else
716 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
718 playh++;
719 num -= 100 * hundreds;
720 if (num)
721 playa++;
723 } else {
724 if (num < 1000000) {
725 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
726 if (res)
727 return res;
728 num = num % 1000;
729 snprintf(fn, sizeof(fn), "digits/thousand");
730 } else {
731 if (num < 1000000000) {
732 int millions = num / 1000000;
733 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
734 if (res)
735 return res;
736 if (millions == 1)
737 snprintf(fn, sizeof(fn), "digits/million");
738 else
739 snprintf(fn, sizeof(fn), "digits/millions");
740 num = num % 1000000;
741 } else {
742 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
743 res = -1;
746 if (num && num < 100)
747 playa++;
750 if (!res) {
751 if (!ast_streamfile(chan, fn, language)) {
752 if ((audiofd > -1) && (ctrlfd > -1))
753 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
754 else
755 res = ast_waitstream(chan, ints);
757 ast_stopstream(chan);
760 return res;
763 /*! \brief ast_say_number_full_de: German syntax */
764 /* New files:
765 In addition to English, the following sounds are required:
766 "millions"
767 "1-and" through "9-and"
768 "1F" (eine)
769 "1N" (ein)
770 NB "1" is recorded as 'eins'
772 static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
774 int res = 0, t = 0;
775 int mf = 1; /* +1 = male and neuter; -1 = female */
776 char fn[256] = "";
777 char fna[256] = "";
778 if (!num)
779 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
781 if (options && (!strncasecmp(options, "f",1)))
782 mf = -1;
784 while (!res && num) {
785 /* The grammar for German numbers is the same as for English except
786 * for the following:
787 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
788 * "one-and twenty" and 68 is "eight-and sixty".
789 * - "one" varies according to gender
790 * - 100 is 'hundert', however all other instances are 'ein hundert'
791 * - 1000 is 'tausend', however all other instances are 'ein tausend'
792 * - 1000000 is always 'eine million'
793 * - "million" is different in singular and plural form
795 if (num < 0) {
796 snprintf(fn, sizeof(fn), "digits/minus");
797 if ( num > INT_MIN ) {
798 num = -num;
799 } else {
800 num = 0;
802 } else if (num < 100 && t) {
803 snprintf(fn, sizeof(fn), "digits/and");
804 t = 0;
805 } else if (num == 1 && mf == -1) {
806 snprintf(fn, sizeof(fn), "digits/%dF", num);
807 num = 0;
808 } else if (num < 20) {
809 snprintf(fn, sizeof(fn), "digits/%d", num);
810 num = 0;
811 } else if (num < 100) {
812 int ones = num % 10;
813 if (ones) {
814 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
815 num -= ones;
816 } else {
817 snprintf(fn, sizeof(fn), "digits/%d", num);
818 num = 0;
820 } else if (num == 100 && t == 0) {
821 snprintf(fn, sizeof(fn), "digits/hundred");
822 num = 0;
823 } else if (num < 1000) {
824 int hundreds = num / 100;
825 num = num % 100;
826 if (hundreds == 1) {
827 snprintf(fn, sizeof(fn), "digits/1N");
828 } else {
829 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
831 snprintf(fna, sizeof(fna), "digits/hundred");
832 t = 1;
833 } else if (num == 1000 && t == 0) {
834 snprintf(fn, sizeof(fn), "digits/thousand");
835 num = 0;
836 } else if (num < 1000000) {
837 int thousands = num / 1000;
838 num = num % 1000;
839 t = 1;
840 if (thousands == 1) {
841 snprintf(fn, sizeof(fn), "digits/1N");
842 snprintf(fna, sizeof(fna), "digits/thousand");
843 } else {
844 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
845 if (res)
846 return res;
847 snprintf(fn, sizeof(fn), "digits/thousand");
849 } else if (num < 1000000000) {
850 int millions = num / 1000000;
851 num = num % 1000000;
852 t = 1;
853 if (millions == 1) {
854 snprintf(fn, sizeof(fn), "digits/1F");
855 snprintf(fna, sizeof(fna), "digits/million");
856 } else {
857 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
858 if (res)
859 return res;
860 snprintf(fn, sizeof(fn), "digits/millions");
862 } else if (num <= INT_MAX) {
863 int billions = num / 1000000000;
864 num = num % 1000000000;
865 t = 1;
866 if (billions == 1) {
867 snprintf(fn, sizeof(fn), "digits/1F");
868 snprintf(fna, sizeof(fna), "digits/milliard");
869 } else {
870 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
871 if (res) {
872 return res;
874 snprintf(fn, sizeof(fn), "digits/milliards");
876 } else {
877 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
878 res = -1;
880 if (!res) {
881 if (!ast_streamfile(chan, fn, language)) {
882 if ((audiofd > -1) && (ctrlfd > -1))
883 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
884 else
885 res = ast_waitstream(chan, ints);
887 ast_stopstream(chan);
888 if (!res) {
889 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
890 if ((audiofd > -1) && (ctrlfd > -1))
891 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
892 else
893 res = ast_waitstream(chan, ints);
895 ast_stopstream(chan);
896 strcpy(fna, "");
900 return res;
903 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
904 /* New files:
905 In addition to American English, the following sounds are required: "and"
907 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
909 int res = 0;
910 int playh = 0;
911 int playa = 0;
912 char fn[256] = "";
913 if (!num)
914 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
916 while (!res && (num || playh || playa )) {
917 if (num < 0) {
918 snprintf(fn, sizeof(fn), "digits/minus");
919 if ( num > INT_MIN ) {
920 num = -num;
921 } else {
922 num = 0;
924 } else if (playh) {
925 snprintf(fn, sizeof(fn), "digits/hundred");
926 playh = 0;
927 } else if (playa) {
928 snprintf(fn, sizeof(fn), "digits/and");
929 playa = 0;
930 } else if (num < 20) {
931 snprintf(fn, sizeof(fn), "digits/%d", num);
932 num = 0;
933 } else if (num < 100) {
934 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
935 num -= ((num / 10) * 10);
936 } else if (num < 1000) {
937 int hundreds = num / 100;
938 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
940 playh++;
941 num -= 100 * hundreds;
942 if (num)
943 playa++;
944 } else if (num < 1000000) {
945 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
946 if (res)
947 return res;
948 snprintf(fn, sizeof(fn), "digits/thousand");
949 num = num % 1000;
950 if (num && num < 100)
951 playa++;
952 } else if (num < 1000000000) {
953 int millions = num / 1000000;
954 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
955 if (res)
956 return res;
957 snprintf(fn, sizeof(fn), "digits/million");
958 num = num % 1000000;
959 if (num && num < 100)
960 playa++;
961 } else {
962 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
963 res = -1;
966 if (!res) {
967 if (!ast_streamfile(chan, fn, language)) {
968 if ((audiofd > -1) && (ctrlfd > -1))
969 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
970 else
971 res = ast_waitstream(chan, ints);
973 ast_stopstream(chan);
976 return res;
979 /*! \brief ast_say_number_full_es: Spanish syntax */
980 /* New files:
981 Requires a few new audios:
982 1F.gsm: feminine 'una'
983 21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm
985 static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
987 int res = 0;
988 int playa = 0;
989 int mf = 0; /* +1 = male; -1 = female */
990 char fn[256] = "";
991 if (!num)
992 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
994 if (options) {
995 if (!strncasecmp(options, "f",1))
996 mf = -1;
997 else if (!strncasecmp(options, "m", 1))
998 mf = 1;
1001 while (!res && num) {
1002 if (num < 0) {
1003 snprintf(fn, sizeof(fn), "digits/minus");
1004 if ( num > INT_MIN ) {
1005 num = -num;
1006 } else {
1007 num = 0;
1009 } else if (playa) {
1010 snprintf(fn, sizeof(fn), "digits/and");
1011 playa = 0;
1012 } else if (num == 1) {
1013 if (mf < 0)
1014 snprintf(fn, sizeof(fn), "digits/%dF", num);
1015 else if (mf > 0)
1016 snprintf(fn, sizeof(fn), "digits/%dM", num);
1017 else
1018 snprintf(fn, sizeof(fn), "digits/%d", num);
1019 num = 0;
1020 } else if (num < 31) {
1021 snprintf(fn, sizeof(fn), "digits/%d", num);
1022 num = 0;
1023 } else if (num < 100) {
1024 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1025 num -= ((num/10)*10);
1026 if (num)
1027 playa++;
1028 } else if (num == 100) {
1029 snprintf(fn, sizeof(fn), "digits/100");
1030 num = 0;
1031 } else if (num < 200) {
1032 snprintf(fn, sizeof(fn), "digits/100-and");
1033 num -= 100;
1034 } else {
1035 if (num < 1000) {
1036 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1037 num -= ((num/100)*100);
1038 } else if (num < 2000) {
1039 num = num % 1000;
1040 snprintf(fn, sizeof(fn), "digits/thousand");
1041 } else {
1042 if (num < 1000000) {
1043 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1044 if (res)
1045 return res;
1046 num = num % 1000;
1047 snprintf(fn, sizeof(fn), "digits/thousand");
1048 } else {
1049 if (num < 2147483640) {
1050 if ((num/1000000) == 1) {
1051 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1052 if (res)
1053 return res;
1054 snprintf(fn, sizeof(fn), "digits/million");
1055 } else {
1056 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1057 if (res)
1058 return res;
1059 snprintf(fn, sizeof(fn), "digits/millions");
1061 num = num % 1000000;
1062 } else {
1063 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1064 res = -1;
1070 if (!res) {
1071 if (!ast_streamfile(chan, fn, language)) {
1072 if ((audiofd > -1) && (ctrlfd > -1))
1073 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1074 else
1075 res = ast_waitstream(chan, ints);
1077 ast_stopstream(chan);
1082 return res;
1085 /*! \brief ast_say_number_full_fr: French syntax */
1086 /* Extra sounds needed:
1087 1F: feminin 'une'
1088 et: 'and' */
1089 static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1091 int res = 0;
1092 int playh = 0;
1093 int playa = 0;
1094 int mf = 1; /* +1 = male; -1 = female */
1095 char fn[256] = "";
1096 if (!num)
1097 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1099 if (options && !strncasecmp(options, "f",1))
1100 mf = -1;
1102 while (!res && (num || playh || playa)) {
1103 if (num < 0) {
1104 snprintf(fn, sizeof(fn), "digits/minus");
1105 if ( num > INT_MIN ) {
1106 num = -num;
1107 } else {
1108 num = 0;
1110 } else if (playh) {
1111 snprintf(fn, sizeof(fn), "digits/hundred");
1112 playh = 0;
1113 } else if (playa) {
1114 snprintf(fn, sizeof(fn), "digits/et");
1115 playa = 0;
1116 } else if (num == 1) {
1117 if (mf < 0)
1118 snprintf(fn, sizeof(fn), "digits/%dF", num);
1119 else
1120 snprintf(fn, sizeof(fn), "digits/%d", num);
1121 num = 0;
1122 } else if (num < 21) {
1123 snprintf(fn, sizeof(fn), "digits/%d", num);
1124 num = 0;
1125 } else if (num < 70) {
1126 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1127 if ((num % 10) == 1) playa++;
1128 num = num % 10;
1129 } else if (num < 80) {
1130 snprintf(fn, sizeof(fn), "digits/60");
1131 if ((num % 10) == 1) playa++;
1132 num = num - 60;
1133 } else if (num < 100) {
1134 snprintf(fn, sizeof(fn), "digits/80");
1135 num = num - 80;
1136 } else if (num < 200) {
1137 snprintf(fn, sizeof(fn), "digits/hundred");
1138 num = num - 100;
1139 } else if (num < 1000) {
1140 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1141 playh++;
1142 num = num % 100;
1143 } else if (num < 2000) {
1144 snprintf(fn, sizeof(fn), "digits/thousand");
1145 num = num - 1000;
1146 } else if (num < 1000000) {
1147 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1148 if (res)
1149 return res;
1150 snprintf(fn, sizeof(fn), "digits/thousand");
1151 num = num % 1000;
1152 } else if (num < 1000000000) {
1153 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1154 if (res)
1155 return res;
1156 snprintf(fn, sizeof(fn), "digits/million");
1157 num = num % 1000000;
1158 } else {
1159 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1160 res = -1;
1162 if (!res) {
1163 if (!ast_streamfile(chan, fn, language)) {
1164 if ((audiofd > -1) && (ctrlfd > -1))
1165 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1166 else
1167 res = ast_waitstream(chan, ints);
1169 ast_stopstream(chan);
1172 return res;
1177 /* Hebrew syntax */
1178 /* Check doc/lang/hebrew-digits.txt for information about the various
1179 * recordings required to make this translation work properly */
1180 #define SAY_NUM_BUF_SIZE 256
1181 static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1183 int res = 0;
1184 int state = 0; /* no need to save anything */
1185 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1186 int tmpnum = 0;
1188 char fn[SAY_NUM_BUF_SIZE] = "";
1190 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1192 if (!num) {
1193 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1195 if (options && !strncasecmp(options, "m", 1)) {
1196 mf = 1;
1198 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1200 /* Do we have work to do? */
1201 while (!res && (num || (state > 0))) {
1202 /* first type of work: play a second sound. In this loop
1203 * we can only play one sound file at a time. Thus playing
1204 * a second one requires repeating the loop just for the
1205 * second file. The variable 'state' remembers where we were.
1206 * state==0 is the normal mode and it means that we continue
1207 * to check if the number num has yet anything left.
1209 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1211 if (state == 1) {
1212 state = 0;
1213 } else if (state == 2) {
1214 if ((num >= 11) && (num < 21)) {
1215 if (mf < 0) {
1216 snprintf(fn, sizeof(fn), "digits/ve");
1217 } else {
1218 snprintf(fn, sizeof(fn), "digits/uu");
1220 } else {
1221 switch (num) {
1222 case 1:
1223 snprintf(fn, sizeof(fn), "digits/ve");
1224 break;
1225 case 2:
1226 snprintf(fn, sizeof(fn), "digits/uu");
1227 break;
1228 case 3:
1229 if (mf < 0) {
1230 snprintf(fn, sizeof(fn), "digits/ve");
1231 } else {
1232 snprintf(fn, sizeof(fn), "digits/uu");
1234 break;
1235 case 4:
1236 snprintf(fn, sizeof(fn), "digits/ve");
1237 break;
1238 case 5:
1239 snprintf(fn, sizeof(fn), "digits/ve");
1240 break;
1241 case 6:
1242 snprintf(fn, sizeof(fn), "digits/ve");
1243 break;
1244 case 7:
1245 snprintf(fn, sizeof(fn), "digits/ve");
1246 break;
1247 case 8:
1248 snprintf(fn, sizeof(fn), "digits/uu");
1249 break;
1250 case 9:
1251 snprintf(fn, sizeof(fn), "digits/ve");
1252 break;
1253 case 10:
1254 snprintf(fn, sizeof(fn), "digits/ve");
1255 break;
1258 state = 0;
1259 } else if (state == 3) {
1260 snprintf(fn, sizeof(fn), "digits/1k");
1261 state = 0;
1262 } else if (num < 0) {
1263 snprintf(fn, sizeof(fn), "digits/minus");
1264 num = (-1) * num;
1265 } else if (num < 20) {
1266 if (mf < 0) {
1267 snprintf(fn, sizeof(fn), "digits/%d", num);
1268 } else {
1269 snprintf(fn, sizeof(fn), "digits/%dm", num);
1271 num = 0;
1272 } else if ((num < 100) && (num >= 20)) {
1273 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1274 num = num % 10;
1275 if (num > 0) {
1276 state = 2;
1278 } else if ((num >= 100) && (num < 1000)) {
1279 tmpnum = num / 100;
1280 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1281 num = num - (tmpnum * 100);
1282 if ((num > 0) && (num < 11)) {
1283 state = 2;
1285 } else if ((num >= 1000) && (num < 10000)) {
1286 tmpnum = num / 1000;
1287 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1288 num = num - (tmpnum * 1000);
1289 if ((num > 0) && (num < 11)) {
1290 state = 2;
1292 } else if (num < 20000) {
1293 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1294 num = num % 1000;
1295 state = 3;
1296 } else if (num < 1000000) {
1297 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1298 if (res) {
1299 return res;
1301 snprintf(fn, sizeof(fn), "digits/1k");
1302 num = num % 1000;
1303 if ((num > 0) && (num < 11)) {
1304 state = 2;
1306 } else if (num < 2000000) {
1307 snprintf(fn, sizeof(fn), "digits/million");
1308 num = num % 1000000;
1309 if ((num > 0) && (num < 11)) {
1310 state = 2;
1312 } else if (num < 3000000) {
1313 snprintf(fn, sizeof(fn), "digits/twomillion");
1314 num = num - 2000000;
1315 if ((num > 0) && (num < 11)) {
1316 state = 2;
1318 } else if (num < 1000000000) {
1319 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1320 if (res) {
1321 return res;
1323 snprintf(fn, sizeof(fn), "digits/million");
1324 num = num % 1000000;
1325 if ((num > 0) && (num < 11)) {
1326 state = 2;
1328 } else {
1329 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1330 res = -1;
1332 tmpnum = 0;
1333 if (!res) {
1334 if (!ast_streamfile(chan, fn, language)) {
1335 if ((audiofd > -1) && (ctrlfd > -1)) {
1336 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1337 } else {
1338 res = ast_waitstream(chan, ints);
1341 ast_stopstream(chan);
1344 return res;
1347 /*! \brief ast_say_number_full_it: Italian */
1348 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1350 int res = 0;
1351 int playh = 0;
1352 int tempnum = 0;
1353 char fn[256] = "";
1355 if (!num)
1356 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1359 Italian support
1361 Like english, numbers up to 20 are a single 'word', and others
1362 compound, but with exceptions.
1363 For example 21 is not twenty-one, but there is a single word in 'it'.
1364 Idem for 28 (ie when a the 2nd part of a compund number
1365 starts with a vowel)
1367 There are exceptions also for hundred, thousand and million.
1368 In english 100 = one hundred, 200 is two hundred.
1369 In italian 100 = cento , like to say hundred (without one),
1370 200 and more are like english.
1372 Same applies for thousand:
1373 1000 is one thousand in en, 2000 is two thousand.
1374 In it we have 1000 = mille , 2000 = 2 mila
1376 For million(s) we use the plural, if more than one
1377 Also, one million is abbreviated in it, like on-million,
1378 or 'un milione', not 'uno milione'.
1379 So the right file is provided.
1382 while (!res && (num || playh)) {
1383 if (num < 0) {
1384 snprintf(fn, sizeof(fn), "digits/minus");
1385 if ( num > INT_MIN ) {
1386 num = -num;
1387 } else {
1388 num = 0;
1390 } else if (playh) {
1391 snprintf(fn, sizeof(fn), "digits/hundred");
1392 playh = 0;
1393 } else if (num < 20) {
1394 snprintf(fn, sizeof(fn), "digits/%d", num);
1395 num = 0;
1396 } else if (num == 21) {
1397 snprintf(fn, sizeof(fn), "digits/%d", num);
1398 num = 0;
1399 } else if (num == 28) {
1400 snprintf(fn, sizeof(fn), "digits/%d", num);
1401 num = 0;
1402 } else if (num == 31) {
1403 snprintf(fn, sizeof(fn), "digits/%d", num);
1404 num = 0;
1405 } else if (num == 38) {
1406 snprintf(fn, sizeof(fn), "digits/%d", num);
1407 num = 0;
1408 } else if (num == 41) {
1409 snprintf(fn, sizeof(fn), "digits/%d", num);
1410 num = 0;
1411 } else if (num == 48) {
1412 snprintf(fn, sizeof(fn), "digits/%d", num);
1413 num = 0;
1414 } else if (num == 51) {
1415 snprintf(fn, sizeof(fn), "digits/%d", num);
1416 num = 0;
1417 } else if (num == 58) {
1418 snprintf(fn, sizeof(fn), "digits/%d", num);
1419 num = 0;
1420 } else if (num == 61) {
1421 snprintf(fn, sizeof(fn), "digits/%d", num);
1422 num = 0;
1423 } else if (num == 68) {
1424 snprintf(fn, sizeof(fn), "digits/%d", num);
1425 num = 0;
1426 } else if (num == 71) {
1427 snprintf(fn, sizeof(fn), "digits/%d", num);
1428 num = 0;
1429 } else if (num == 78) {
1430 snprintf(fn, sizeof(fn), "digits/%d", num);
1431 num = 0;
1432 } else if (num == 81) {
1433 snprintf(fn, sizeof(fn), "digits/%d", num);
1434 num = 0;
1435 } else if (num == 88) {
1436 snprintf(fn, sizeof(fn), "digits/%d", num);
1437 num = 0;
1438 } else if (num == 91) {
1439 snprintf(fn, sizeof(fn), "digits/%d", num);
1440 num = 0;
1441 } else if (num == 98) {
1442 snprintf(fn, sizeof(fn), "digits/%d", num);
1443 num = 0;
1444 } else if (num < 100) {
1445 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1446 num -= ((num / 10) * 10);
1447 } else {
1448 if (num < 1000) {
1449 if ((num / 100) > 1) {
1450 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1451 playh++;
1452 } else {
1453 snprintf(fn, sizeof(fn), "digits/hundred");
1455 num -= ((num / 100) * 100);
1456 } else {
1457 if (num < 1000000) { /* 1,000,000 */
1458 if ((num/1000) > 1)
1459 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1460 if (res)
1461 return res;
1462 tempnum = num;
1463 num = num % 1000;
1464 if ((tempnum / 1000) < 2)
1465 snprintf(fn, sizeof(fn), "digits/thousand");
1466 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1467 snprintf(fn, sizeof(fn), "digits/thousands");
1468 } else {
1469 if (num < 1000000000) { /* 1,000,000,000 */
1470 if ((num / 1000000) > 1)
1471 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1472 if (res)
1473 return res;
1474 tempnum = num;
1475 num = num % 1000000;
1476 if ((tempnum / 1000000) < 2)
1477 snprintf(fn, sizeof(fn), "digits/million");
1478 else
1479 snprintf(fn, sizeof(fn), "digits/millions");
1480 } else {
1481 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1482 res = -1;
1487 if (!res) {
1488 if (!ast_streamfile(chan, fn, language)) {
1489 if ((audiofd > -1) && (ctrlfd > -1))
1490 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1491 else
1492 res = ast_waitstream(chan, ints);
1494 ast_stopstream(chan);
1497 return res;
1500 /*! \brief ast_say_number_full_nl: dutch syntax */
1501 /* New files: digits/nl-en
1503 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1505 int res = 0;
1506 int playh = 0;
1507 int units = 0;
1508 char fn[256] = "";
1509 if (!num)
1510 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1511 while (!res && (num || playh )) {
1512 if (num < 0) {
1513 snprintf(fn, sizeof(fn), "digits/minus");
1514 if ( num > INT_MIN ) {
1515 num = -num;
1516 } else {
1517 num = 0;
1519 } else if (playh) {
1520 snprintf(fn, sizeof(fn), "digits/hundred");
1521 playh = 0;
1522 } else if (num < 20) {
1523 snprintf(fn, sizeof(fn), "digits/%d", num);
1524 num = 0;
1525 } else if (num < 100) {
1526 units = num % 10;
1527 if (units > 0) {
1528 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1529 if (res)
1530 return res;
1531 num = num - units;
1532 snprintf(fn, sizeof(fn), "digits/nl-en");
1533 } else {
1534 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1535 num = 0;
1537 } else if (num < 200) {
1538 /* hundred, not one-hundred */
1539 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1540 num -= ((num / 100) * 100);
1541 } else if (num < 1000) {
1542 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
1543 playh++;
1544 num -= ((num / 100) * 100);
1545 } else {
1546 if (num < 1100) {
1547 /* thousand, not one-thousand */
1548 num = num % 1000;
1549 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1550 } else if (num < 10000) { /* 1,100 to 9,9999 */
1551 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
1552 if (res)
1553 return res;
1554 num = num % 100;
1555 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1556 } else {
1557 if (num < 1000000) { /* 1,000,000 */
1558 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
1559 if (res)
1560 return res;
1561 num = num % 1000;
1562 snprintf(fn, sizeof(fn), "digits/thousand");
1563 } else {
1564 if (num < 1000000000) { /* 1,000,000,000 */
1565 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1566 if (res)
1567 return res;
1568 num = num % 1000000;
1569 snprintf(fn, sizeof(fn), "digits/million");
1570 } else {
1571 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1572 res = -1;
1578 if (!res) {
1579 if (!ast_streamfile(chan, fn, language)) {
1580 if ((audiofd > -1) && (ctrlfd > -1))
1581 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1582 else
1583 res = ast_waitstream(chan, ints);
1585 ast_stopstream(chan);
1588 return res;
1591 /*! \brief ast_say_number_full_no: Norwegian syntax */
1592 /* New files:
1593 In addition to American English, the following sounds are required: "and", "1N"
1595 static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1597 int res = 0;
1598 int playh = 0;
1599 int playa = 0;
1600 int cn = 1; /* +1 = commune; -1 = neuter */
1601 char fn[256] = "";
1603 if (!num)
1604 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1606 if (options && !strncasecmp(options, "n",1)) cn = -1;
1608 while (!res && (num || playh || playa )) {
1609 /* The grammar for Norwegian numbers is the same as for English except
1610 * for the following:
1611 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1612 * "and" before the last two digits, i.e. 2034 is "two thousand and
1613 * thirty-four" and 1000012 is "one million and twelve".
1615 if (num < 0) {
1616 snprintf(fn, sizeof(fn), "digits/minus");
1617 if ( num > INT_MIN ) {
1618 num = -num;
1619 } else {
1620 num = 0;
1622 } else if (playh) {
1623 snprintf(fn, sizeof(fn), "digits/hundred");
1624 playh = 0;
1625 } else if (playa) {
1626 snprintf(fn, sizeof(fn), "digits/and");
1627 playa = 0;
1628 } else if (num == 1 && cn == -1) {
1629 snprintf(fn, sizeof(fn), "digits/1N");
1630 num = 0;
1631 } else if (num < 20) {
1632 snprintf(fn, sizeof(fn), "digits/%d", num);
1633 num = 0;
1634 } else if (num < 100) {
1635 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1636 num -= ((num / 10) * 10);
1637 } else if (num < 1000) {
1638 int hundreds = num / 100;
1639 if (hundreds == 1)
1640 snprintf(fn, sizeof(fn), "digits/1N");
1641 else
1642 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1644 playh++;
1645 num -= 100 * hundreds;
1646 if (num)
1647 playa++;
1648 } else if (num < 1000000) {
1649 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1650 if (res)
1651 return res;
1652 snprintf(fn, sizeof(fn), "digits/thousand");
1653 num = num % 1000;
1654 if (num && num < 100)
1655 playa++;
1656 } else if (num < 1000000000) {
1657 int millions = num / 1000000;
1658 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1659 if (res)
1660 return res;
1661 snprintf(fn, sizeof(fn), "digits/million");
1662 num = num % 1000000;
1663 if (num && num < 100)
1664 playa++;
1665 } else {
1666 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1667 res = -1;
1670 if (!res) {
1671 if (!ast_streamfile(chan, fn, language)) {
1672 if ((audiofd > -1) && (ctrlfd > -1))
1673 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1674 else
1675 res = ast_waitstream(chan, ints);
1677 ast_stopstream(chan);
1680 return res;
1683 typedef struct {
1684 char *separator_dziesiatek;
1685 char *cyfry[10];
1686 char *cyfry2[10];
1687 char *setki[10];
1688 char *dziesiatki[10];
1689 char *nastki[10];
1690 char *rzedy[3][3];
1691 } odmiana;
1693 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1695 if (rzad==0)
1696 return "";
1698 if (i==1)
1699 return odm->rzedy[rzad - 1][0];
1700 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1701 return odm->rzedy[rzad - 1][1];
1702 else
1703 return odm->rzedy[rzad - 1][2];
1706 static char* pl_append(char* buffer, char* str)
1708 strcpy(buffer, str);
1709 buffer += strlen(str);
1710 return buffer;
1713 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1715 char file_name[255] = "digits/";
1716 strcat(file_name, fn);
1717 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1718 if (!ast_streamfile(chan, file_name, language)) {
1719 if ((audiofd > -1) && (ctrlfd > -1))
1720 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1721 else
1722 ast_waitstream(chan, ints);
1724 ast_stopstream(chan);
1727 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1729 /* Initialise variables to allow compilation on Debian-stable, etc */
1730 int m1000E6 = 0;
1731 int i1000E6 = 0;
1732 int m1000E3 = 0;
1733 int i1000E3 = 0;
1734 int m1000 = 0;
1735 int i1000 = 0;
1736 int m100 = 0;
1737 int i100 = 0;
1739 if (i == 0 && rzad > 0) {
1740 return;
1742 if (i == 0) {
1743 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1744 return;
1747 m1000E6 = i % 1000000000;
1748 i1000E6 = i / 1000000000;
1750 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1752 m1000E3 = m1000E6 % 1000000;
1753 i1000E3 = m1000E6 / 1000000;
1755 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1757 m1000 = m1000E3 % 1000;
1758 i1000 = m1000E3 / 1000;
1760 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1762 m100 = m1000 % 100;
1763 i100 = m1000 / 100;
1765 if (i100>0)
1766 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1768 if ( m100 > 0 && m100 <=9 ) {
1769 if (m1000>0)
1770 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1771 else
1772 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1773 } else if (m100 % 10 == 0) {
1774 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1775 } else if (m100 <= 19 ) {
1776 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1777 } else if (m100 != 0) {
1778 if (odm->separator_dziesiatek[0]==' ') {
1779 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1780 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1781 } else {
1782 char buf[10];
1783 char *b = buf;
1784 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1785 b = pl_append(b, odm->separator_dziesiatek);
1786 b = pl_append(b, odm->cyfry2[m100 % 10]);
1787 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1791 if (rzad > 0) {
1792 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1796 /* ast_say_number_full_pl: Polish syntax */
1797 static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1799 Sounds needed:
1800 0 zero
1801 1 jeden
1802 10 dziesiec
1803 100 sto
1804 1000 tysiac
1805 1000000 milion
1806 1000000000 miliard
1807 1000000000.2 miliardy
1808 1000000000.5 miliardow
1809 1000000.2 miliony
1810 1000000.5 milionow
1811 1000.2 tysiace
1812 1000.5 tysiecy
1813 100m stu
1814 10m dziesieciu
1815 11 jedenascie
1816 11m jedenastu
1817 12 dwanascie
1818 12m dwunastu
1819 13 trzynascie
1820 13m trzynastu
1821 14 czternascie
1822 14m czternastu
1823 15 pietnascie
1824 15m pietnastu
1825 16 szesnascie
1826 16m szesnastu
1827 17 siedemnascie
1828 17m siedemnastu
1829 18 osiemnascie
1830 18m osiemnastu
1831 19 dziewietnascie
1832 19m dziewietnastu
1833 1z jedna
1834 2 dwa
1835 20 dwadziescia
1836 200 dwiescie
1837 200m dwustu
1838 20m dwudziestu
1839 2-1m dwaj
1840 2-2m dwoch
1841 2z dwie
1842 3 trzy
1843 30 trzydziesci
1844 300 trzysta
1845 300m trzystu
1846 30m trzydziestu
1847 3-1m trzej
1848 3-2m trzech
1849 4 cztery
1850 40 czterdziesci
1851 400 czterysta
1852 400m czterystu
1853 40m czterdziestu
1854 4-1m czterej
1855 4-2m czterech
1856 5 piec
1857 50 piecdziesiat
1858 500 piecset
1859 500m pieciuset
1860 50m piedziesieciu
1861 5m pieciu
1862 6 szesc
1863 60 szescdziesiat
1864 600 szescset
1865 600m szesciuset
1866 60m szescdziesieciu
1867 6m szesciu
1868 7 siedem
1869 70 siedemdziesiat
1870 700 siedemset
1871 700m siedmiuset
1872 70m siedemdziesieciu
1873 7m siedmiu
1874 8 osiem
1875 80 osiemdziesiat
1876 800 osiemset
1877 800m osmiuset
1878 80m osiemdziesieciu
1879 8m osmiu
1880 9 dziewiec
1881 90 dziewiecdziesiat
1882 900 dziewiecset
1883 900m dziewieciuset
1884 90m dziewiedziesieciu
1885 9m dziewieciu
1886 and combinations of eg.: 20_1, 30m_3m, etc...
1890 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1892 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1894 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1896 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1898 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1900 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1902 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1904 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1906 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1908 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1910 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1912 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1914 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1916 /* Initialise variables to allow compilation on Debian-stable, etc */
1917 odmiana *o;
1919 static odmiana *odmiana_nieosobowa = NULL;
1920 static odmiana *odmiana_meska = NULL;
1921 static odmiana *odmiana_zenska = NULL;
1923 if (odmiana_nieosobowa == NULL) {
1924 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1926 odmiana_nieosobowa->separator_dziesiatek = " ";
1928 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1929 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1930 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1931 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1932 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1933 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1936 if (odmiana_zenska == NULL) {
1937 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1939 odmiana_zenska->separator_dziesiatek = " ";
1941 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1942 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1943 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1944 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1945 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1946 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1949 if (odmiana_meska == NULL) {
1950 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1952 odmiana_meska->separator_dziesiatek = " ";
1954 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1955 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1956 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1957 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1958 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1959 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1962 if (options) {
1963 if (strncasecmp(options, "f", 1) == 0)
1964 o = odmiana_zenska;
1965 else if (strncasecmp(options, "m", 1) == 0)
1966 o = odmiana_meska;
1967 else
1968 o = odmiana_nieosobowa;
1969 } else
1970 o = odmiana_nieosobowa;
1972 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1973 return 0;
1976 /* ast_say_number_full_pt: Portuguese syntax */
1977 /* Extra sounds needed: */
1978 /* For feminin all sound files end with F */
1979 /* 100E for 100+ something */
1980 /* 1000000S for plural */
1981 /* pt-e for 'and' */
1982 static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1984 int res = 0;
1985 int playh = 0;
1986 int mf = 1; /* +1 = male; -1 = female */
1987 char fn[256] = "";
1989 if (!num)
1990 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1992 if (options && !strncasecmp(options, "f",1))
1993 mf = -1;
1995 while (!res && num ) {
1996 if (num < 0) {
1997 snprintf(fn, sizeof(fn), "digits/minus");
1998 if ( num > INT_MIN ) {
1999 num = -num;
2000 } else {
2001 num = 0;
2003 } else if (num < 20) {
2004 if ((num == 1 || num == 2) && (mf < 0))
2005 snprintf(fn, sizeof(fn), "digits/%dF", num);
2006 else
2007 snprintf(fn, sizeof(fn), "digits/%d", num);
2008 num = 0;
2009 } else if (num < 100) {
2010 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2011 if (num % 10)
2012 playh = 1;
2013 num = num % 10;
2014 } else if (num < 1000) {
2015 if (num == 100)
2016 snprintf(fn, sizeof(fn), "digits/100");
2017 else if (num < 200)
2018 snprintf(fn, sizeof(fn), "digits/100E");
2019 else {
2020 if (mf < 0 && num > 199)
2021 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2022 else
2023 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2024 if (num % 100)
2025 playh = 1;
2027 num = num % 100;
2028 } else if (num < 1000000) {
2029 if (num > 1999) {
2030 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2031 if (res)
2032 return res;
2034 snprintf(fn, sizeof(fn), "digits/1000");
2035 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2036 playh = 1;
2037 num = num % 1000;
2038 } else if (num < 1000000000) {
2039 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2040 if (res)
2041 return res;
2042 if (num < 2000000)
2043 snprintf(fn, sizeof(fn), "digits/1000000");
2044 else
2045 snprintf(fn, sizeof(fn), "digits/1000000S");
2047 if ((num % 1000000) &&
2048 /* no thousands */
2049 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2050 /* no hundreds and below */
2051 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2052 playh = 1;
2053 num = num % 1000000;
2054 } else {
2055 /* number is too big */
2056 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2057 res = -1;
2059 if (!res) {
2060 if (!ast_streamfile(chan, fn, language)) {
2061 if ((audiofd > -1) && (ctrlfd > -1))
2062 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2063 else
2064 res = ast_waitstream(chan, ints);
2066 ast_stopstream(chan);
2068 if (!res && playh) {
2069 res = wait_file(chan, ints, "digits/pt-e", language);
2070 ast_stopstream(chan);
2071 playh = 0;
2074 return res;
2077 /*! \brief ast_say_number_full_se: Swedish syntax */
2078 static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2080 int res = 0;
2081 int playh = 0;
2082 char fn[256] = "";
2083 int cn = 1; /* +1 = commune; -1 = neuter */
2084 if (!num)
2085 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2086 if (options && !strncasecmp(options, "n",1)) cn = -1;
2088 while (!res && (num || playh)) {
2089 if (num < 0) {
2090 snprintf(fn, sizeof(fn), "digits/minus");
2091 if ( num > INT_MIN ) {
2092 num = -num;
2093 } else {
2094 num = 0;
2096 } else if (playh) {
2097 snprintf(fn, sizeof(fn), "digits/hundred");
2098 playh = 0;
2099 } else if (num < 20) {
2100 snprintf(fn, sizeof(fn), "digits/%d", num);
2101 num = 0;
2102 } else if (num < 100) {
2103 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2104 num -= ((num / 10) * 10);
2105 } else if (num == 1 && cn == -1) { /* En eller ett? */
2106 snprintf(fn, sizeof(fn), "digits/1N");
2107 num = 0;
2108 } else {
2109 if (num < 1000){
2110 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2111 playh++;
2112 num -= ((num / 100) * 100);
2113 } else {
2114 if (num < 1000000) { /* 1,000,000 */
2115 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2116 if (res) {
2117 return res;
2119 num = num % 1000;
2120 snprintf(fn, sizeof(fn), "digits/thousand");
2121 } else {
2122 if (num < 1000000000) { /* 1,000,000,000 */
2123 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2124 if (res) {
2125 return res;
2127 num = num % 1000000;
2128 snprintf(fn, sizeof(fn), "digits/million");
2129 } else {
2130 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2131 res = -1;
2136 if (!res) {
2137 if (!ast_streamfile(chan, fn, language)) {
2138 if ((audiofd > -1) && (ctrlfd > -1))
2139 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2140 else
2141 res = ast_waitstream(chan, ints);
2142 ast_stopstream(chan);
2146 return res;
2149 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2150 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2152 int res = 0;
2153 int playh = 0;
2154 int playt = 0;
2155 int playz = 0;
2156 int last_length = 0;
2157 char buf[20] = "";
2158 char fn[256] = "";
2159 if (!num)
2160 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2162 while (!res && (num || playh || playt || playz)) {
2163 if (num < 0) {
2164 snprintf(fn, sizeof(fn), "digits/minus");
2165 if ( num > INT_MIN ) {
2166 num = -num;
2167 } else {
2168 num = 0;
2170 } else if (playz) {
2171 snprintf(fn, sizeof(fn), "digits/0");
2172 last_length = 0;
2173 playz = 0;
2174 } else if (playh) {
2175 snprintf(fn, sizeof(fn), "digits/hundred");
2176 playh = 0;
2177 } else if (playt) {
2178 snprintf(fn, sizeof(fn), "digits/thousand");
2179 playt = 0;
2180 } else if (num < 10) {
2181 snprintf(buf, 10, "%d", num);
2182 if (last_length - strlen(buf) > 1 && last_length != 0) {
2183 last_length = strlen(buf);
2184 playz++;
2185 continue;
2187 if (strcasecmp(language,"twz") == 0)
2188 snprintf(fn, sizeof(fn), "digits/%d", num);
2189 else
2190 snprintf(fn, sizeof(fn), "digits/%d", num);
2191 num = 0;
2192 } else if (num < 100) {
2193 snprintf(buf, 10, "%d", num);
2194 if (last_length - strlen(buf) > 1 && last_length != 0) {
2195 last_length = strlen(buf);
2196 playz++;
2197 continue;
2199 last_length = strlen(buf);
2200 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2201 num -= ((num / 10) * 10);
2202 } else {
2203 if (num < 1000){
2204 snprintf(buf, 10, "%d", num);
2205 if (last_length - strlen(buf) > 1 && last_length != 0) {
2206 last_length = strlen(buf);
2207 playz++;
2208 continue;
2210 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2211 playh++;
2212 snprintf(buf, 10, "%d", num);
2213 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2214 last_length = strlen(buf);
2215 num -= ((num / 100) * 100);
2216 } else if (num < 10000){
2217 snprintf(buf, 10, "%d", num);
2218 if (last_length - strlen(buf) > 1 && last_length != 0 && last_length % strlen(buf) > 0) {
2219 last_length = strlen(buf);
2220 playz++;
2221 continue;
2223 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2224 playt++;
2225 snprintf(buf, 10, "%d", num);
2226 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2227 last_length = strlen(buf);
2228 num -= ((num / 1000) * 1000);
2229 } else if (num < 100000000) { /* 100,000,000 */
2230 res = ast_say_number_full_tw(chan, num / 10000, ints, language, audiofd, ctrlfd);
2231 if (res)
2232 return res;
2233 if (((num / 10000) % (num/100000)) == 0)
2234 playz++;
2236 snprintf(buf, 10, "%d", num);
2237 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2238 num -= ((num / 10000) * 10000);
2239 last_length = strlen(buf);
2240 snprintf(fn, sizeof(fn), "digits/wan");
2241 } else {
2242 if (num < 1000000000) { /* 1000,000,000 */
2243 res = ast_say_number_full_tw(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2244 if (res)
2245 return res;
2246 snprintf(buf, 10, "%d", num);
2247 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2248 last_length = strlen(buf);
2249 num -= ((num / 100000000) * 100000000);
2250 snprintf(fn, sizeof(fn), "digits/yi");
2251 } else {
2252 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2253 res = -1;
2257 if (!res) {
2258 if (!ast_streamfile(chan, fn, language)) {
2259 if ((audiofd > -1) && (ctrlfd > -1))
2260 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2261 else
2262 res = ast_waitstream(chan, ints);
2264 ast_stopstream(chan);
2267 return res;
2271 /*! \brief determine last digits for thousands/millions (ru) */
2272 static int get_lastdigits_ru(int num) {
2273 if (num < 20) {
2274 return num;
2275 } else if (num < 100) {
2276 return get_lastdigits_ru(num % 10);
2277 } else if (num < 1000) {
2278 return get_lastdigits_ru(num % 100);
2280 return 0; /* number too big */
2284 /*! \brief ast_say_number_full_ru: Russian syntax */
2285 /*! \brief additional files:
2286 n00.gsm (one hundred, two hundred, ...)
2287 thousand.gsm
2288 million.gsm
2289 thousands-i.gsm (tisyachi)
2290 million-a.gsm (milliona)
2291 thousands.gsm
2292 millions.gsm
2293 1f.gsm (odna)
2294 2f.gsm (dve)
2296 where 'n' from 1 to 9
2298 static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2300 int res = 0;
2301 int lastdigits = 0;
2302 char fn[256] = "";
2303 if (!num)
2304 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2306 while (!res && (num)) {
2307 if (num < 0) {
2308 snprintf(fn, sizeof(fn), "digits/minus");
2309 if ( num > INT_MIN ) {
2310 num = -num;
2311 } else {
2312 num = 0;
2314 } else if (num < 20) {
2315 if (options && strlen(options) == 1 && num < 3) {
2316 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2317 } else {
2318 snprintf(fn, sizeof(fn), "digits/%d", num);
2320 num = 0;
2321 } else if (num < 100) {
2322 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2323 num %= 10;
2324 } else if (num < 1000){
2325 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2326 num %= 100;
2327 } else if (num < 1000000) { /* 1,000,000 */
2328 lastdigits = get_lastdigits_ru(num / 1000);
2329 /* say thousands */
2330 if (lastdigits < 3) {
2331 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2332 } else {
2333 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2335 if (res)
2336 return res;
2337 if (lastdigits == 1) {
2338 snprintf(fn, sizeof(fn), "digits/thousand");
2339 } else if (lastdigits > 1 && lastdigits < 5) {
2340 snprintf(fn, sizeof(fn), "digits/thousands-i");
2341 } else {
2342 snprintf(fn, sizeof(fn), "digits/thousands");
2344 num %= 1000;
2345 } else if (num < 1000000000) { /* 1,000,000,000 */
2346 lastdigits = get_lastdigits_ru(num / 1000000);
2347 /* say millions */
2348 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2349 if (res)
2350 return res;
2351 if (lastdigits == 1) {
2352 snprintf(fn, sizeof(fn), "digits/million");
2353 } else if (lastdigits > 1 && lastdigits < 5) {
2354 snprintf(fn, sizeof(fn), "digits/million-a");
2355 } else {
2356 snprintf(fn, sizeof(fn), "digits/millions");
2358 num %= 1000000;
2359 } else {
2360 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2361 res = -1;
2363 if (!res) {
2364 if (!ast_streamfile(chan, fn, language)) {
2365 if ((audiofd > -1) && (ctrlfd > -1))
2366 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2367 else
2368 res = ast_waitstream(chan, ints);
2370 ast_stopstream(chan);
2373 return res;
2377 /*! \brief ast_say_enumeration_full: call language-specific functions */
2378 /* Called from AGI */
2379 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2381 if (!strcasecmp(language,"en") ) { /* English syntax */
2382 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2383 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2384 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2385 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2386 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2387 } else if (!strcasecmp(language, "he")) { /* Hebrew syntax */
2388 return (ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
2391 /* Default to english */
2392 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2395 /*! \brief ast_say_enumeration_full_en: English syntax */
2396 /* This is the default syntax, if no other syntax defined in this file is used */
2397 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2399 int res = 0, t = 0;
2400 char fn[256] = "";
2402 while (!res && num) {
2403 if (num < 0) {
2404 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2405 if ( num > INT_MIN ) {
2406 num = -num;
2407 } else {
2408 num = 0;
2410 } else if (num < 20) {
2411 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2412 num = 0;
2413 } else if (num < 100) {
2414 int tens = num / 10;
2415 num = num % 10;
2416 if (num == 0) {
2417 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2418 } else {
2419 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2421 } else if (num < 1000) {
2422 int hundreds = num / 100;
2423 num = num % 100;
2424 if (hundreds > 1 || t == 1) {
2425 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2427 if (res)
2428 return res;
2429 if (num) {
2430 snprintf(fn, sizeof(fn), "digits/hundred");
2431 } else {
2432 snprintf(fn, sizeof(fn), "digits/h-hundred");
2434 } else if (num < 1000000) {
2435 int thousands = num / 1000;
2436 num = num % 1000;
2437 if (thousands > 1 || t == 1) {
2438 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2440 if (res)
2441 return res;
2442 if (num) {
2443 snprintf(fn, sizeof(fn), "digits/thousand");
2444 } else {
2445 snprintf(fn, sizeof(fn), "digits/h-thousand");
2447 t = 1;
2448 } else if (num < 1000000000) {
2449 int millions = num / 1000000;
2450 num = num % 1000000;
2451 t = 1;
2452 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2453 if (res)
2454 return res;
2455 if (num) {
2456 snprintf(fn, sizeof(fn), "digits/million");
2457 } else {
2458 snprintf(fn, sizeof(fn), "digits/h-million");
2460 } else if (num < INT_MAX) {
2461 int billions = num / 1000000000;
2462 num = num % 1000000000;
2463 t = 1;
2464 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2465 if (res)
2466 return res;
2467 if (num) {
2468 snprintf(fn, sizeof(fn), "digits/billion");
2469 } else {
2470 snprintf(fn, sizeof(fn), "digits/h-billion");
2472 } else if (num == INT_MAX) {
2473 snprintf(fn, sizeof(fn), "digits/h-last");
2474 num = 0;
2475 } else {
2476 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2477 res = -1;
2480 if (!res) {
2481 if (!ast_streamfile(chan, fn, language)) {
2482 if ((audiofd > -1) && (ctrlfd > -1)) {
2483 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2484 } else {
2485 res = ast_waitstream(chan, ints);
2488 ast_stopstream(chan);
2491 return res;
2494 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2495 static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2497 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2498 int res = 0, t = 0;
2499 char fn[256] = "", fna[256] = "";
2500 char *gender;
2502 if (options && !strncasecmp(options, "f",1)) {
2503 gender = "F";
2504 } else if (options && !strncasecmp(options, "n",1)) {
2505 gender = "N";
2506 } else {
2507 gender = "";
2510 if (!num)
2511 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2513 while (!res && num) {
2514 if (num < 0) {
2515 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2516 if ( num > INT_MIN ) {
2517 num = -num;
2518 } else {
2519 num = 0;
2521 } else if (num < 100 && t) {
2522 snprintf(fn, sizeof(fn), "digits/and");
2523 t = 0;
2524 } else if (num < 20) {
2525 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2526 num = 0;
2527 } else if (num < 100) {
2528 int ones = num % 10;
2529 if (ones) {
2530 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2531 num -= ones;
2532 } else {
2533 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2534 num = 0;
2536 } else if (num == 100 && t == 0) {
2537 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2538 num = 0;
2539 } else if (num < 1000) {
2540 int hundreds = num / 100;
2541 num = num % 100;
2542 if (hundreds == 1) {
2543 snprintf(fn, sizeof(fn), "digits/1N");
2544 } else {
2545 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2547 if (num) {
2548 snprintf(fna, sizeof(fna), "digits/hundred");
2549 } else {
2550 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2552 t = 1;
2553 } else if (num < 1000000) {
2554 int thousands = num / 1000;
2555 num = num % 1000;
2556 if (thousands == 1) {
2557 if (num) {
2558 snprintf(fn, sizeof(fn), "digits/1N");
2559 snprintf(fna, sizeof(fna), "digits/thousand");
2560 } else {
2561 if (t) {
2562 snprintf(fn, sizeof(fn), "digits/1N");
2563 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2564 } else {
2565 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2568 } else {
2569 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2570 if (res) {
2571 return res;
2573 if (num) {
2574 snprintf(fn, sizeof(fn), "digits/thousand");
2575 } else {
2576 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2579 t = 1;
2580 } else if (num < 1000000000) {
2581 int millions = num / 1000000;
2582 num = num % 1000000;
2583 if (millions == 1) {
2584 if (num) {
2585 snprintf(fn, sizeof(fn), "digits/1F");
2586 snprintf(fna, sizeof(fna), "digits/million");
2587 } else {
2588 snprintf(fn, sizeof(fn), "digits/1N");
2589 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2591 } else {
2592 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2593 if (res) {
2594 return res;
2596 if (num) {
2597 snprintf(fn, sizeof(fn), "digits/millions");
2598 } else {
2599 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2602 t = 1;
2603 } else if (num < INT_MAX) {
2604 int billions = num / 1000000000;
2605 num = num % 1000000000;
2606 if (billions == 1) {
2607 if (num) {
2608 snprintf(fn, sizeof(fn), "digits/1F");
2609 snprintf(fna, sizeof(fna), "digits/milliard");
2610 } else {
2611 snprintf(fn, sizeof(fn), "digits/1N");
2612 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2614 } else {
2615 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2616 if (res)
2617 return res;
2618 if (num) {
2619 snprintf(fn, sizeof(fna), "digits/milliards");
2620 } else {
2621 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2624 t = 1;
2625 } else if (num == INT_MAX) {
2626 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2627 num = 0;
2628 } else {
2629 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2630 res = -1;
2633 if (!res) {
2634 if (!ast_streamfile(chan, fn, language)) {
2635 if ((audiofd > -1) && (ctrlfd > -1))
2636 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2637 else
2638 res = ast_waitstream(chan, ints);
2640 ast_stopstream(chan);
2641 if (!res) {
2642 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2643 if ((audiofd > -1) && (ctrlfd > -1)) {
2644 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2645 } else {
2646 res = ast_waitstream(chan, ints);
2649 ast_stopstream(chan);
2650 strcpy(fna, "");
2654 return res;
2657 /*! \brief ast_say_enumeration_full_de: German syntax */
2658 static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2660 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2661 int res = 0, t = 0;
2662 char fn[256] = "", fna[256] = "";
2663 char *gender;
2665 if (options && !strncasecmp(options, "f",1)) {
2666 gender = "F";
2667 } else if (options && !strncasecmp(options, "n",1)) {
2668 gender = "N";
2669 } else {
2670 gender = "";
2673 if (!num)
2674 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2676 while (!res && num) {
2677 if (num < 0) {
2678 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2679 if ( num > INT_MIN ) {
2680 num = -num;
2681 } else {
2682 num = 0;
2684 } else if (num < 100 && t) {
2685 snprintf(fn, sizeof(fn), "digits/and");
2686 t = 0;
2687 } else if (num < 20) {
2688 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2689 num = 0;
2690 } else if (num < 100) {
2691 int ones = num % 10;
2692 if (ones) {
2693 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2694 num -= ones;
2695 } else {
2696 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2697 num = 0;
2699 } else if (num == 100 && t == 0) {
2700 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2701 num = 0;
2702 } else if (num < 1000) {
2703 int hundreds = num / 100;
2704 num = num % 100;
2705 if (hundreds == 1) {
2706 snprintf(fn, sizeof(fn), "digits/1N");
2707 } else {
2708 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2710 if (num) {
2711 snprintf(fna, sizeof(fna), "digits/hundred");
2712 } else {
2713 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2715 t = 1;
2716 } else if (num < 1000000) {
2717 int thousands = num / 1000;
2718 num = num % 1000;
2719 if (thousands == 1) {
2720 if (num) {
2721 snprintf(fn, sizeof(fn), "digits/1N");
2722 snprintf(fna, sizeof(fna), "digits/thousand");
2723 } else {
2724 if (t) {
2725 snprintf(fn, sizeof(fn), "digits/1N");
2726 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2727 } else {
2728 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2731 } else {
2732 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2733 if (res) {
2734 return res;
2736 if (num) {
2737 snprintf(fn, sizeof(fn), "digits/thousand");
2738 } else {
2739 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2742 t = 1;
2743 } else if (num < 1000000000) {
2744 int millions = num / 1000000;
2745 num = num % 1000000;
2746 if (millions == 1) {
2747 if (num) {
2748 snprintf(fn, sizeof(fn), "digits/1F");
2749 snprintf(fna, sizeof(fna), "digits/million");
2750 } else {
2751 snprintf(fn, sizeof(fn), "digits/1N");
2752 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2754 } else {
2755 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2756 if (res) {
2757 return res;
2759 if (num) {
2760 snprintf(fn, sizeof(fn), "digits/millions");
2761 } else {
2762 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2765 t = 1;
2766 } else if (num < INT_MAX) {
2767 int billions = num / 1000000000;
2768 num = num % 1000000000;
2769 if (billions == 1) {
2770 if (num) {
2771 snprintf(fn, sizeof(fn), "digits/1F");
2772 snprintf(fna, sizeof(fna), "digits/milliard");
2773 } else {
2774 snprintf(fn, sizeof(fn), "digits/1N");
2775 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2777 } else {
2778 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2779 if (res)
2780 return res;
2781 if (num) {
2782 snprintf(fn, sizeof(fna), "digits/milliards");
2783 } else {
2784 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2787 t = 1;
2788 } else if (num == INT_MAX) {
2789 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2790 num = 0;
2791 } else {
2792 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2793 res = -1;
2796 if (!res) {
2797 if (!ast_streamfile(chan, fn, language)) {
2798 if ((audiofd > -1) && (ctrlfd > -1))
2799 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2800 else
2801 res = ast_waitstream(chan, ints);
2803 ast_stopstream(chan);
2804 if (!res) {
2805 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2806 if ((audiofd > -1) && (ctrlfd > -1)) {
2807 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2808 } else {
2809 res = ast_waitstream(chan, ints);
2812 ast_stopstream(chan);
2813 strcpy(fna, "");
2817 return res;
2820 static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2822 int res = 0;
2823 char fn[256] = "";
2824 int mf = -1; /* +1 = Masculin; -1 = Feminin */
2825 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
2827 if (options && !strncasecmp(options, "m", 1)) {
2828 mf = -1;
2831 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
2833 while (!res && num) {
2834 if (num < 0) {
2835 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2836 if (num > INT_MIN) {
2837 num = -num;
2838 } else {
2839 num = 0;
2841 } else if (num < 21) {
2842 if (mf < 0) {
2843 if (num < 10) {
2844 snprintf(fn, sizeof(fn), "digits/f-0%d", num);
2845 } else {
2846 snprintf(fn, sizeof(fn), "digits/f-%d", num);
2848 } else {
2849 if (num < 10) {
2850 snprintf(fn, sizeof(fn), "digits/m-0%d", num);
2851 } else {
2852 snprintf(fn, sizeof(fn), "digits/m-%d", num);
2855 num = 0;
2856 } else if ((num < 100) && num >= 20) {
2857 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2858 num = num % 10;
2859 } else if ((num >= 100) && (num < 1000)) {
2860 int tmpnum = num / 100;
2861 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
2862 num = num - (tmpnum * 100);
2863 } else if ((num >= 1000) && (num < 10000)) {
2864 int tmpnum = num / 1000;
2865 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
2866 num = num - (tmpnum * 1000);
2867 } else if (num < 20000) {
2868 snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
2869 num = num % 1000;
2870 } else if (num < 1000000) {
2871 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
2872 if (res) {
2873 return res;
2875 snprintf(fn, sizeof(fn), "digits/1k");
2876 num = num % 1000;
2877 } else if (num < 2000000) {
2878 snprintf(fn, sizeof(fn), "digits/1m");
2879 num = num % 1000000;
2880 } else if (num < 3000000) {
2881 snprintf(fn, sizeof(fn), "digits/2m");
2882 num = num - 2000000;
2883 } else if (num < 1000000000) {
2884 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
2885 if (res) {
2886 return res;
2888 snprintf(fn, sizeof(fn), "digits/1m");
2889 num = num % 1000000;
2890 } else {
2891 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2892 res = -1;
2894 if (!res) {
2895 if (!ast_streamfile(chan, fn, language)) {
2896 if ((audiofd > -1) && (ctrlfd > -1)) {
2897 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2898 } else {
2899 res = ast_waitstream(chan, ints);
2902 ast_stopstream(chan);
2905 return res;
2908 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2910 if (!strcasecmp(lang, "en") ) { /* English syntax */
2911 return(ast_say_date_en(chan, t, ints, lang));
2912 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2913 return(ast_say_date_da(chan, t, ints, lang));
2914 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2915 return(ast_say_date_de(chan, t, ints, lang));
2916 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2917 return(ast_say_date_fr(chan, t, ints, lang));
2918 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2919 return(ast_say_date_nl(chan, t, ints, lang));
2920 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2921 return(ast_say_date_pt(chan, t, ints, lang));
2922 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2923 return(ast_say_date_gr(chan, t, ints, lang));
2924 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
2925 return(ast_say_date_ge(chan, t, ints, lang));
2926 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2927 return (ast_say_date_he(chan, t, ints, lang));
2930 /* Default to English */
2931 return(ast_say_date_en(chan, t, ints, lang));
2934 /* English syntax */
2935 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2937 struct tm tm;
2938 char fn[256];
2939 int res = 0;
2940 ast_localtime(&t,&tm,NULL);
2941 if (!res) {
2942 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2943 res = ast_streamfile(chan, fn, lang);
2944 if (!res)
2945 res = ast_waitstream(chan, ints);
2947 if (!res) {
2948 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2949 res = ast_streamfile(chan, fn, lang);
2950 if (!res)
2951 res = ast_waitstream(chan, ints);
2953 if (!res)
2954 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2955 if (!res)
2956 res = ast_waitstream(chan, ints);
2957 if (!res)
2958 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2959 return res;
2962 /* Danish syntax */
2963 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2965 struct tm tm;
2966 char fn[256];
2967 int res = 0;
2968 ast_localtime(&t,&tm,NULL);
2969 if (!res) {
2970 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2971 res = ast_streamfile(chan, fn, lang);
2972 if (!res)
2973 res = ast_waitstream(chan, ints);
2975 if (!res)
2976 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2977 if (!res)
2978 res = ast_waitstream(chan, ints);
2979 if (!res) {
2980 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2981 res = ast_streamfile(chan, fn, lang);
2982 if (!res)
2983 res = ast_waitstream(chan, ints);
2985 if (!res) {
2986 /* Year */
2987 int year = tm.tm_year + 1900;
2988 if (year > 1999) { /* year 2000 and later */
2989 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2990 } else {
2991 if (year < 1100) {
2992 /* I'm not going to handle 1100 and prior */
2993 /* We'll just be silent on the year, instead of bombing out. */
2994 } else {
2995 /* year 1100 to 1999. will anybody need this?!? */
2996 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2997 res = wait_file(chan, ints, fn, lang);
2998 if (!res) {
2999 res = wait_file(chan,ints, "digits/hundred", lang);
3000 if (!res && year % 100 != 0) {
3001 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3007 return res;
3010 /* German syntax */
3011 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3013 struct tm tm;
3014 char fn[256];
3015 int res = 0;
3016 ast_localtime(&t,&tm,NULL);
3017 if (!res) {
3018 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3019 res = ast_streamfile(chan, fn, lang);
3020 if (!res)
3021 res = ast_waitstream(chan, ints);
3023 if (!res)
3024 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3025 if (!res)
3026 res = ast_waitstream(chan, ints);
3027 if (!res) {
3028 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3029 res = ast_streamfile(chan, fn, lang);
3030 if (!res)
3031 res = ast_waitstream(chan, ints);
3033 if (!res) {
3034 /* Year */
3035 int year = tm.tm_year + 1900;
3036 if (year > 1999) { /* year 2000 and later */
3037 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3038 } else {
3039 if (year < 1100) {
3040 /* I'm not going to handle 1100 and prior */
3041 /* We'll just be silent on the year, instead of bombing out. */
3042 } else {
3043 /* year 1100 to 1999. will anybody need this?!? */
3044 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3045 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
3046 res = wait_file(chan, ints, fn, lang);
3047 if (!res) {
3048 res = wait_file(chan,ints, "digits/hundred", lang);
3049 if (!res && year % 100 != 0) {
3050 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3056 return res;
3059 /* French syntax */
3060 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3062 struct tm tm;
3063 char fn[256];
3064 int res = 0;
3065 ast_localtime(&t,&tm,NULL);
3066 if (!res) {
3067 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3068 res = ast_streamfile(chan, fn, lang);
3069 if (!res)
3070 res = ast_waitstream(chan, ints);
3072 if (!res)
3073 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3074 if (!res)
3075 res = ast_waitstream(chan, ints);
3076 if (!res) {
3077 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3078 res = ast_streamfile(chan, fn, lang);
3079 if (!res)
3080 res = ast_waitstream(chan, ints);
3082 if (!res)
3083 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3084 return res;
3087 /* Dutch syntax */
3088 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3090 struct tm tm;
3091 char fn[256];
3092 int res = 0;
3093 ast_localtime(&t,&tm,NULL);
3094 if (!res) {
3095 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3096 res = ast_streamfile(chan, fn, lang);
3097 if (!res)
3098 res = ast_waitstream(chan, ints);
3100 if (!res)
3101 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3102 if (!res) {
3103 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3104 res = ast_streamfile(chan, fn, lang);
3105 if (!res)
3106 res = ast_waitstream(chan, ints);
3108 if (!res)
3109 res = ast_waitstream(chan, ints);
3110 if (!res)
3111 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3112 return res;
3115 /* Portuguese syntax */
3116 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3118 struct tm tm;
3119 char fn[256];
3120 int res = 0;
3122 ast_localtime(&t, &tm, NULL);
3123 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3124 if (!res)
3125 res = wait_file(chan, ints, fn, lang);
3126 if (!res)
3127 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
3128 if (!res)
3129 res = wait_file(chan, ints, "digits/pt-de", lang);
3130 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3131 if (!res)
3132 res = wait_file(chan, ints, fn, lang);
3133 if (!res)
3134 res = wait_file(chan, ints, "digits/pt-de", lang);
3135 if (!res)
3136 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3138 return res;
3141 /* Hebrew syntax */
3142 int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3144 struct tm tm;
3145 char fn[256];
3146 int res = 0;
3147 ast_localtime(&t, &tm, NULL);
3148 if (!res) {
3149 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3150 res = ast_streamfile(chan, fn, lang);
3151 if (!res) {
3152 res = ast_waitstream(chan, ints);
3155 if (!res) {
3156 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3157 res = ast_streamfile(chan, fn, lang);
3158 if (!res) {
3159 res = ast_waitstream(chan, ints);
3162 if (!res) {
3163 res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
3165 if (!res) {
3166 res = ast_waitstream(chan, ints);
3168 if (!res) {
3169 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
3171 return res;
3174 static int say_date_with_format(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3176 if (!strcasecmp(lang, "en") ) { /* English syntax */
3177 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
3178 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
3179 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
3180 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
3181 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
3182 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
3183 return (ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
3184 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
3185 return (ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
3186 } else if (!strcasecmp(lang, "fr")) { /* French syntax */
3187 return (ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
3188 } else if (!strcasecmp(lang, "it")) { /* Italian syntax */
3189 return (ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
3190 } else if (!strcasecmp(lang, "nl")) { /* Dutch syntax */
3191 return (ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
3192 } else if (!strcasecmp(lang, "pl")) { /* Polish syntax */
3193 return (ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
3194 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
3195 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
3196 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
3197 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
3198 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
3199 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
3202 /* Default to English */
3203 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
3206 /* English syntax */
3207 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3209 struct tm tm;
3210 int res=0, offset, sndoffset;
3211 char sndfile[256], nextmsg[256];
3213 if (format == NULL)
3214 format = "ABdY 'digits/at' IMp";
3216 ast_localtime(&time,&tm,timezone);
3218 for (offset=0 ; format[offset] != '\0' ; offset++) {
3219 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3220 switch (format[offset]) {
3221 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3222 case '\'':
3223 /* Literal name of a sound file */
3224 sndoffset=0;
3225 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3226 sndfile[sndoffset] = format[offset];
3227 sndfile[sndoffset] = '\0';
3228 res = wait_file(chan,ints,sndfile,lang);
3229 break;
3230 case 'A':
3231 case 'a':
3232 /* Sunday - Saturday */
3233 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3234 res = wait_file(chan,ints,nextmsg,lang);
3235 break;
3236 case 'B':
3237 case 'b':
3238 case 'h':
3239 /* January - December */
3240 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3241 res = wait_file(chan,ints,nextmsg,lang);
3242 break;
3243 case 'm':
3244 /* Month enumerated */
3245 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
3246 break;
3247 case 'd':
3248 case 'e':
3249 /* First - Thirtyfirst */
3250 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
3251 break;
3252 case 'Y':
3253 /* Year */
3254 if (tm.tm_year > 99) {
3255 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3256 } else if (tm.tm_year < 1) {
3257 /* I'm not going to handle 1900 and prior */
3258 /* We'll just be silent on the year, instead of bombing out. */
3259 } else {
3260 res = wait_file(chan, ints, "digits/19", lang);
3261 if (!res) {
3262 if (tm.tm_year <= 9) {
3263 /* 1901 - 1909 */
3264 res = wait_file(chan,ints, "digits/oh", lang);
3267 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3270 break;
3271 case 'I':
3272 case 'l':
3273 /* 12-Hour */
3274 if (tm.tm_hour == 0)
3275 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3276 else if (tm.tm_hour > 12)
3277 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3278 else
3279 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3280 res = wait_file(chan,ints,nextmsg,lang);
3281 break;
3282 case 'H':
3283 case 'k':
3284 /* 24-Hour */
3285 if (format[offset] == 'H') {
3286 /* e.g. oh-eight */
3287 if (tm.tm_hour < 10) {
3288 res = wait_file(chan,ints, "digits/oh",lang);
3290 } else {
3291 /* e.g. eight */
3292 if (tm.tm_hour == 0) {
3293 res = wait_file(chan,ints, "digits/oh",lang);
3296 if (!res) {
3297 if (tm.tm_hour != 0) {
3298 int remainder = tm.tm_hour;
3299 if (tm.tm_hour > 20) {
3300 res = wait_file(chan,ints, "digits/20",lang);
3301 remainder -= 20;
3303 if (!res) {
3304 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3305 res = wait_file(chan,ints,nextmsg,lang);
3309 break;
3310 case 'M':
3311 case 'N':
3312 /* Minute */
3313 if (tm.tm_min == 0) {
3314 if (format[offset] == 'M') {
3315 res = wait_file(chan, ints, "digits/oclock", lang);
3316 } else {
3317 res = wait_file(chan, ints, "digits/hundred", lang);
3319 } else if (tm.tm_min < 10) {
3320 res = wait_file(chan,ints, "digits/oh",lang);
3321 if (!res) {
3322 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3323 res = wait_file(chan,ints,nextmsg,lang);
3325 } else {
3326 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3328 break;
3329 case 'P':
3330 case 'p':
3331 /* AM/PM */
3332 if (tm.tm_hour > 11)
3333 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3334 else
3335 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3336 res = wait_file(chan,ints,nextmsg,lang);
3337 break;
3338 case 'Q':
3339 /* Shorthand for "Today", "Yesterday", or ABdY */
3340 /* XXX As emphasized elsewhere, this should the native way in your
3341 * language to say the date, with changes in what you say, depending
3342 * upon how recent the date is. XXX */
3344 struct timeval now;
3345 struct tm tmnow;
3346 time_t beg_today, tt;
3348 gettimeofday(&now,NULL);
3349 tt = now.tv_sec;
3350 ast_localtime(&tt,&tmnow,timezone);
3351 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3352 /* In any case, it saves not having to do ast_mktime() */
3353 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3354 if (beg_today < time) {
3355 /* Today */
3356 res = wait_file(chan,ints, "digits/today",lang);
3357 } else if (beg_today - 86400 < time) {
3358 /* Yesterday */
3359 res = wait_file(chan,ints, "digits/yesterday",lang);
3360 } else if (beg_today - 86400 * 6 < time) {
3361 /* Within the last week */
3362 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3363 } else if (beg_today - 2628000 < time) {
3364 /* Less than a month ago - "Sunday, October third" */
3365 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3366 } else if (beg_today - 15768000 < time) {
3367 /* Less than 6 months ago - "August seventh" */
3368 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3369 } else {
3370 /* More than 6 months ago - "April nineteenth two thousand three" */
3371 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3374 break;
3375 case 'q':
3376 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3377 /* XXX As emphasized elsewhere, this should the native way in your
3378 * language to say the date, with changes in what you say, depending
3379 * upon how recent the date is. XXX */
3381 struct timeval now;
3382 struct tm tmnow;
3383 time_t beg_today, tt;
3385 gettimeofday(&now,NULL);
3386 tt = now.tv_sec;
3387 ast_localtime(&tt,&tmnow,timezone);
3388 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3389 /* In any case, it saves not having to do ast_mktime() */
3390 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3391 if (beg_today < time) {
3392 /* Today */
3393 } else if ((beg_today - 86400) < time) {
3394 /* Yesterday */
3395 res = wait_file(chan,ints, "digits/yesterday",lang);
3396 } else if (beg_today - 86400 * 6 < time) {
3397 /* Within the last week */
3398 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3399 } else if (beg_today - 2628000 < time) {
3400 /* Less than a month ago - "Sunday, October third" */
3401 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3402 } else if (beg_today - 15768000 < time) {
3403 /* Less than 6 months ago - "August seventh" */
3404 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3405 } else {
3406 /* More than 6 months ago - "April nineteenth two thousand three" */
3407 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3410 break;
3411 case 'R':
3412 res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
3413 break;
3414 case 'S':
3415 /* Seconds */
3416 if (tm.tm_sec == 0) {
3417 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3418 res = wait_file(chan,ints,nextmsg,lang);
3419 } else if (tm.tm_sec < 10) {
3420 res = wait_file(chan,ints, "digits/oh",lang);
3421 if (!res) {
3422 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3423 res = wait_file(chan,ints,nextmsg,lang);
3425 } else {
3426 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
3428 break;
3429 case 'T':
3430 res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
3431 break;
3432 case ' ':
3433 case ' ':
3434 /* Just ignore spaces and tabs */
3435 break;
3436 default:
3437 /* Unknown character */
3438 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3440 /* Jump out on DTMF */
3441 if (res) {
3442 break;
3445 return res;
3448 /* Danish syntax */
3449 int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3451 struct tm tm;
3452 int res=0, offset, sndoffset;
3453 char sndfile[256], nextmsg[256];
3455 if (!format)
3456 format = "A dBY HMS";
3458 ast_localtime(&time,&tm,timezone);
3460 for (offset=0 ; format[offset] != '\0' ; offset++) {
3461 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3462 switch (format[offset]) {
3463 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3464 case '\'':
3465 /* Literal name of a sound file */
3466 sndoffset=0;
3467 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3468 sndfile[sndoffset] = format[offset];
3469 sndfile[sndoffset] = '\0';
3470 res = wait_file(chan,ints,sndfile,lang);
3471 break;
3472 case 'A':
3473 case 'a':
3474 /* Sunday - Saturday */
3475 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3476 res = wait_file(chan,ints,nextmsg,lang);
3477 break;
3478 case 'B':
3479 case 'b':
3480 case 'h':
3481 /* January - December */
3482 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3483 res = wait_file(chan,ints,nextmsg,lang);
3484 break;
3485 case 'm':
3486 /* Month enumerated */
3487 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3488 break;
3489 case 'd':
3490 case 'e':
3491 /* First - Thirtyfirst */
3492 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3493 break;
3494 case 'Y':
3495 /* Year */
3497 int year = tm.tm_year + 1900;
3498 if (year > 1999) { /* year 2000 and later */
3499 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3500 } else {
3501 if (year < 1100) {
3502 /* I'm not going to handle 1100 and prior */
3503 /* We'll just be silent on the year, instead of bombing out. */
3504 } else {
3505 /* year 1100 to 1999. will anybody need this?!? */
3506 /* say 1967 as 'nineteen hundred seven and sixty' */
3507 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3508 res = wait_file(chan,ints,nextmsg,lang);
3509 if (!res) {
3510 res = wait_file(chan,ints, "digits/hundred",lang);
3511 if (!res && year % 100 != 0) {
3512 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3518 break;
3519 case 'I':
3520 case 'l':
3521 /* 12-Hour */
3522 res = wait_file(chan,ints,"digits/oclock",lang);
3523 if (tm.tm_hour == 0)
3524 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3525 else if (tm.tm_hour > 12)
3526 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3527 else
3528 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3529 if (!res) {
3530 res = wait_file(chan,ints,nextmsg,lang);
3532 break;
3533 case 'H':
3534 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3535 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
3536 res = wait_file(chan,ints, "digits/0",lang);
3538 /* FALLTRHU */
3539 case 'k':
3540 /* 24-Hour */
3541 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3542 break;
3543 case 'M':
3544 /* Minute */
3545 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3546 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3548 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3549 if (tm.tm_min == 1) {
3550 res = wait_file(chan,ints,"digits/minute",lang);
3551 } else {
3552 res = wait_file(chan,ints,"digits/minutes",lang);
3555 break;
3556 case 'P':
3557 case 'p':
3558 /* AM/PM */
3559 if (tm.tm_hour > 11)
3560 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3561 else
3562 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3563 res = wait_file(chan,ints,nextmsg,lang);
3564 break;
3565 case 'Q':
3566 /* Shorthand for "Today", "Yesterday", or AdBY */
3567 /* XXX As emphasized elsewhere, this should the native way in your
3568 * language to say the date, with changes in what you say, depending
3569 * upon how recent the date is. XXX */
3571 struct timeval now;
3572 struct tm tmnow;
3573 time_t beg_today, tt;
3575 gettimeofday(&now,NULL);
3576 tt = now.tv_sec;
3577 ast_localtime(&tt,&tmnow,timezone);
3578 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3579 /* In any case, it saves not having to do ast_mktime() */
3580 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3581 if (beg_today < time) {
3582 /* Today */
3583 res = wait_file(chan,ints, "digits/today",lang);
3584 } else if (beg_today - 86400 < time) {
3585 /* Yesterday */
3586 res = wait_file(chan,ints, "digits/yesterday",lang);
3587 } else {
3588 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3591 break;
3592 case 'q':
3593 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3594 /* XXX As emphasized elsewhere, this should the native way in your
3595 * language to say the date, with changes in what you say, depending
3596 * upon how recent the date is. XXX */
3598 struct timeval now;
3599 struct tm tmnow;
3600 time_t beg_today, tt;
3602 gettimeofday(&now,NULL);
3603 tt = now.tv_sec;
3604 ast_localtime(&tt,&tmnow,timezone);
3605 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3606 /* In any case, it saves not having to do ast_mktime() */
3607 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3608 if (beg_today < time) {
3609 /* Today */
3610 } else if ((beg_today - 86400) < time) {
3611 /* Yesterday */
3612 res = wait_file(chan,ints, "digits/yesterday",lang);
3613 } else if (beg_today - 86400 * 6 < time) {
3614 /* Within the last week */
3615 res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
3616 } else {
3617 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3620 break;
3621 case 'R':
3622 res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
3623 break;
3624 case 'S':
3625 /* Seconds */
3626 res = wait_file(chan,ints, "digits/and",lang);
3627 if (!res) {
3628 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3629 if (!res) {
3630 res = wait_file(chan,ints, "digits/seconds",lang);
3633 break;
3634 case 'T':
3635 res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
3636 break;
3637 case ' ':
3638 case ' ':
3639 /* Just ignore spaces and tabs */
3640 break;
3641 default:
3642 /* Unknown character */
3643 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3645 /* Jump out on DTMF */
3646 if (res) {
3647 break;
3650 return res;
3653 /* German syntax */
3654 int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3656 struct tm tm;
3657 int res=0, offset, sndoffset;
3658 char sndfile[256], nextmsg[256];
3660 if (!format)
3661 format = "A dBY HMS";
3663 ast_localtime(&time,&tm,timezone);
3665 for (offset=0 ; format[offset] != '\0' ; offset++) {
3666 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3667 switch (format[offset]) {
3668 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3669 case '\'':
3670 /* Literal name of a sound file */
3671 sndoffset=0;
3672 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3673 sndfile[sndoffset] = format[offset];
3674 sndfile[sndoffset] = '\0';
3675 res = wait_file(chan,ints,sndfile,lang);
3676 break;
3677 case 'A':
3678 case 'a':
3679 /* Sunday - Saturday */
3680 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3681 res = wait_file(chan,ints,nextmsg,lang);
3682 break;
3683 case 'B':
3684 case 'b':
3685 case 'h':
3686 /* January - December */
3687 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3688 res = wait_file(chan,ints,nextmsg,lang);
3689 break;
3690 case 'm':
3691 /* Month enumerated */
3692 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3693 break;
3694 case 'd':
3695 case 'e':
3696 /* First - Thirtyfirst */
3697 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3698 break;
3699 case 'Y':
3700 /* Year */
3702 int year = tm.tm_year + 1900;
3703 if (year > 1999) { /* year 2000 and later */
3704 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3705 } else {
3706 if (year < 1100) {
3707 /* I'm not going to handle 1100 and prior */
3708 /* We'll just be silent on the year, instead of bombing out. */
3709 } else {
3710 /* year 1100 to 1999. will anybody need this?!? */
3711 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3712 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3713 res = wait_file(chan,ints,nextmsg,lang);
3714 if (!res) {
3715 res = wait_file(chan,ints, "digits/hundred",lang);
3716 if (!res && year % 100 != 0) {
3717 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3723 break;
3724 case 'I':
3725 case 'l':
3726 /* 12-Hour */
3727 if (tm.tm_hour == 0)
3728 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3729 else if (tm.tm_hour > 12)
3730 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3731 else
3732 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3733 res = wait_file(chan,ints,nextmsg,lang);
3734 if (!res) {
3735 res = wait_file(chan,ints,"digits/oclock",lang);
3737 break;
3738 case 'H':
3739 case 'k':
3740 /* 24-Hour */
3741 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3742 if (!res) {
3743 res = wait_file(chan,ints,"digits/oclock",lang);
3745 break;
3746 case 'M':
3747 /* Minute */
3748 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3749 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3751 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3752 if (tm.tm_min == 1) {
3753 res = wait_file(chan,ints,"digits/minute",lang);
3754 } else {
3755 res = wait_file(chan,ints,"digits/minutes",lang);
3758 break;
3759 case 'P':
3760 case 'p':
3761 /* AM/PM */
3762 if (tm.tm_hour > 11)
3763 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3764 else
3765 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3766 res = wait_file(chan,ints,nextmsg,lang);
3767 break;
3768 case 'Q':
3769 /* Shorthand for "Today", "Yesterday", or AdBY */
3770 /* XXX As emphasized elsewhere, this should the native way in your
3771 * language to say the date, with changes in what you say, depending
3772 * upon how recent the date is. XXX */
3774 struct timeval now;
3775 struct tm tmnow;
3776 time_t beg_today, tt;
3778 gettimeofday(&now,NULL);
3779 tt = now.tv_sec;
3780 ast_localtime(&tt,&tmnow,timezone);
3781 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3782 /* In any case, it saves not having to do ast_mktime() */
3783 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3784 if (beg_today < time) {
3785 /* Today */
3786 res = wait_file(chan,ints, "digits/today",lang);
3787 } else if (beg_today - 86400 < time) {
3788 /* Yesterday */
3789 res = wait_file(chan,ints, "digits/yesterday",lang);
3790 } else {
3791 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3794 break;
3795 case 'q':
3796 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3797 /* XXX As emphasized elsewhere, this should the native way in your
3798 * language to say the date, with changes in what you say, depending
3799 * upon how recent the date is. XXX */
3801 struct timeval now;
3802 struct tm tmnow;
3803 time_t beg_today, tt;
3805 gettimeofday(&now,NULL);
3806 tt = now.tv_sec;
3807 ast_localtime(&tt,&tmnow,timezone);
3808 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3809 /* In any case, it saves not having to do ast_mktime() */
3810 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3811 if (beg_today < time) {
3812 /* Today */
3813 } else if ((beg_today - 86400) < time) {
3814 /* Yesterday */
3815 res = wait_file(chan,ints, "digits/yesterday",lang);
3816 } else if (beg_today - 86400 * 6 < time) {
3817 /* Within the last week */
3818 res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
3819 } else {
3820 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3823 break;
3824 case 'R':
3825 res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
3826 break;
3827 case 'S':
3828 /* Seconds */
3829 res = wait_file(chan,ints, "digits/and",lang);
3830 if (!res) {
3831 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3832 if (!res) {
3833 res = wait_file(chan,ints, "digits/seconds",lang);
3836 break;
3837 case 'T':
3838 res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
3839 break;
3840 case ' ':
3841 case ' ':
3842 /* Just ignore spaces and tabs */
3843 break;
3844 default:
3845 /* Unknown character */
3846 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3848 /* Jump out on DTMF */
3849 if (res) {
3850 break;
3853 return res;
3856 /* TODO: this probably is not the correct format for doxygen remarks */
3858 /** ast_say_date_with_format_he Say formmated date in Hebrew
3860 * \ref ast_say_date_with_format_en for the details of the options
3862 * Changes from the English version:
3864 * * don't replicate in here the logic of ast_say_number_full_he
3866 * * year is always 4-digit (because it's simpler)
3868 * * added c, x, and X. Mainly for my tests
3870 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3872 * TODO:
3873 * * A "ha" is missing in the standard date format, before the 'd'.
3874 * * The numbers of 3000--19000 are not handled well
3876 #define IL_DATE_STR "AdBY"
3877 #define IL_TIME_STR "HM" /* NOTE: In Hebrew we do not support 12 hours, only 24. No AM or PM exists in the Hebrew language */
3878 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3879 int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3881 /* TODO: This whole function is cut&paste from
3882 * ast_say_date_with_format_en . Is that considered acceptable?
3884 struct tm tm;
3885 int res = 0, offset, sndoffset;
3886 char sndfile[256], nextmsg[256];
3888 if (!format) {
3889 format = IL_DATE_STR_FULL;
3892 ast_localtime(&time, &tm, timezone);
3894 for (offset = 0; format[offset] != '\0'; offset++) {
3895 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3896 switch (format[offset]) {
3897 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3898 case '\'':
3899 /* Literal name of a sound file */
3900 sndoffset=0;
3901 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3902 sndfile[sndoffset] = format[offset];
3903 sndfile[sndoffset] = '\0';
3904 res = wait_file(chan,ints,sndfile,lang);
3905 break;
3906 case 'A':
3907 case 'a':
3908 /* Sunday - Saturday */
3909 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3910 res = wait_file(chan,ints,nextmsg,lang);
3911 break;
3912 case 'B':
3913 case 'b':
3914 case 'h':
3915 /* January - December */
3916 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3917 res = wait_file(chan,ints,nextmsg,lang);
3918 break;
3919 case 'd':
3920 case 'e': /* Day of the month */
3921 /* I'm not sure exactly what the parameters
3922 * audiofd and ctrlfd to
3923 * ast_say_number_full_he mean, but it seems
3924 * safe to pass -1 there.
3926 * At least in one of the pathes :-(
3928 res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
3929 break;
3930 case 'Y': /* Year */
3931 res = ast_say_number_full_he(chan, tm.tm_year+1900,
3932 ints, lang, "f", -1, -1
3934 break;
3935 case 'I':
3936 case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
3937 case 'H':
3938 case 'k': /* 24-Hour */
3939 res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
3940 break;
3941 case 'M': /* Minute */
3942 if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
3943 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
3944 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
3945 break;
3946 case 'P':
3947 case 'p':
3948 /* AM/PM - There is no AM/PM in Hebrew... */
3949 break;
3950 case 'Q':
3951 /* Shorthand for "Today", "Yesterday", or "date" */
3952 case 'q':
3953 /* Shorthand for "" (today), "Yesterday", A
3954 * (weekday), or "date" */
3955 /* XXX As emphasized elsewhere, this should the native way in your
3956 * language to say the date, with changes in what you say, depending
3957 * upon how recent the date is. XXX */
3959 struct timeval now;
3960 struct tm tmnow;
3961 time_t beg_today, tt;
3962 char todo = format[offset]; /* The letter to format*/
3964 gettimeofday(&now,NULL);
3965 tt = now.tv_sec;
3966 ast_localtime(&tt,&tmnow,timezone);
3967 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3968 /* In any case, it saves not having to do ast_mktime() */
3969 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3970 if (beg_today < time) {
3971 /* Today */
3972 if (todo == 'Q') {
3973 res = wait_file(chan,
3974 ints,
3975 "digits/today",
3976 lang);
3978 } else if (beg_today - 86400 < time) {
3979 /* Yesterday */
3980 res = wait_file(chan,ints, "digits/yesterday",lang);
3981 } else if ((todo != 'Q') &&
3982 (beg_today - 86400 * 6 < time))
3984 /* Within the last week */
3985 res = ast_say_date_with_format_he(chan,
3986 time, ints, lang,
3987 "A", timezone);
3988 } else {
3989 res = ast_say_date_with_format_he(chan,
3990 time, ints, lang,
3991 IL_DATE_STR, timezone);
3994 break;
3995 case 'R':
3996 res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
3997 break;
3998 case 'S': /* Seconds */
3999 res = ast_say_number_full_he(chan, tm.tm_sec,
4000 ints, lang, "f", -1, -1
4002 break;
4003 case 'T':
4004 res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
4005 break;
4006 /* c, x, and X seem useful for testing. Not sure
4007 * if thiey're good for the general public */
4008 case 'c':
4009 res = ast_say_date_with_format_he(chan, time,
4010 ints, lang, IL_DATE_STR_FULL, timezone);
4011 break;
4012 case 'x':
4013 res = ast_say_date_with_format_he(chan, time,
4014 ints, lang, IL_DATE_STR, timezone);
4015 break;
4016 case 'X': /* Currently not locale-dependent...*/
4017 res = ast_say_date_with_format_he(chan, time,
4018 ints, lang, IL_TIME_STR, timezone);
4019 break;
4020 case ' ':
4021 case ' ':
4022 /* Just ignore spaces and tabs */
4023 break;
4024 default:
4025 /* Unknown character */
4026 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4028 /* Jump out on DTMF */
4029 if (res) {
4030 break;
4033 return res;
4037 /* Spanish syntax */
4038 int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4040 struct tm tm;
4041 int res=0, offset, sndoffset;
4042 char sndfile[256], nextmsg[256];
4044 if (format == NULL)
4045 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
4047 ast_localtime(&time,&tm,timezone);
4049 for (offset=0 ; format[offset] != '\0' ; offset++) {
4050 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4051 switch (format[offset]) {
4052 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4053 case '\'':
4054 /* Literal name of a sound file */
4055 sndoffset=0;
4056 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4057 sndfile[sndoffset] = format[offset];
4058 sndfile[sndoffset] = '\0';
4059 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
4060 res = wait_file(chan,ints,nextmsg,lang);
4061 break;
4062 case 'A':
4063 case 'a':
4064 /* Sunday - Saturday */
4065 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4066 res = wait_file(chan,ints,nextmsg,lang);
4067 break;
4068 case 'B':
4069 case 'b':
4070 case 'h':
4071 /* January - December */
4072 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4073 res = wait_file(chan,ints,nextmsg,lang);
4074 break;
4075 case 'm':
4076 /* First - Twelfth */
4077 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4078 res = wait_file(chan,ints,nextmsg,lang);
4079 break;
4080 case 'd':
4081 case 'e':
4082 /* First - Thirtyfirst */
4083 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4084 break;
4085 case 'Y':
4086 /* Year */
4087 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4088 break;
4089 case 'I':
4090 case 'l':
4091 /* 12-Hour */
4092 if (tm.tm_hour == 0)
4093 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4094 else if (tm.tm_hour > 12)
4095 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4096 else
4097 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4098 res = wait_file(chan,ints,nextmsg,lang);
4099 break;
4100 case 'H':
4101 case 'k':
4102 /* 24-Hour */
4103 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
4104 break;
4105 case 'M':
4106 /* Minute */
4107 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4108 break;
4109 case 'P':
4110 case 'p':
4111 /* AM/PM */
4112 if (tm.tm_hour > 18)
4113 res = wait_file(chan, ints, "digits/p-m", lang);
4114 else if (tm.tm_hour > 12)
4115 res = wait_file(chan, ints, "digits/afternoon", lang);
4116 else if (tm.tm_hour)
4117 res = wait_file(chan, ints, "digits/a-m", lang);
4118 break;
4119 case 'Q':
4120 /* Shorthand for "Today", "Yesterday", or ABdY */
4121 /* XXX As emphasized elsewhere, this should the native way in your
4122 * language to say the date, with changes in what you say, depending
4123 * upon how recent the date is. XXX */
4125 struct timeval now;
4126 struct tm tmnow;
4127 time_t beg_today, tt;
4129 gettimeofday(&now,NULL);
4130 tt = now.tv_sec;
4131 ast_localtime(&tt,&tmnow,timezone);
4132 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4133 /* In any case, it saves not having to do ast_mktime() */
4134 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4135 if (beg_today < time) {
4136 /* Today */
4137 res = wait_file(chan,ints, "digits/today",lang);
4138 } else if (beg_today - 86400 < time) {
4139 /* Yesterday */
4140 res = wait_file(chan,ints, "digits/yesterday",lang);
4141 } else {
4142 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
4145 break;
4146 case 'q':
4147 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4148 /* XXX As emphasized elsewhere, this should the native way in your
4149 * language to say the date, with changes in what you say, depending
4150 * upon how recent the date is. XXX */
4152 struct timeval now;
4153 struct tm tmnow;
4154 time_t beg_today, tt;
4156 gettimeofday(&now,NULL);
4157 tt = now.tv_sec;
4158 ast_localtime(&tt,&tmnow,timezone);
4159 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4160 /* In any case, it saves not having to do ast_mktime() */
4161 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4162 if (beg_today < time) {
4163 /* Today */
4164 res = wait_file(chan,ints, "digits/today",lang);
4165 } else if ((beg_today - 86400) < time) {
4166 /* Yesterday */
4167 res = wait_file(chan,ints, "digits/yesterday",lang);
4168 } else if (beg_today - 86400 * 6 < time) {
4169 /* Within the last week */
4170 res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
4171 } else {
4172 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
4175 break;
4176 case 'R':
4177 res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
4178 break;
4179 case 'S':
4180 /* Seconds */
4181 if (tm.tm_sec == 0) {
4182 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4183 res = wait_file(chan,ints,nextmsg,lang);
4184 } else if (tm.tm_sec < 10) {
4185 res = wait_file(chan,ints, "digits/oh",lang);
4186 if (!res) {
4187 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4188 res = wait_file(chan,ints,nextmsg,lang);
4190 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4191 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4192 res = wait_file(chan,ints,nextmsg,lang);
4193 } else {
4194 int ten, one;
4195 ten = (tm.tm_sec / 10) * 10;
4196 one = (tm.tm_sec % 10);
4197 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4198 res = wait_file(chan,ints,nextmsg,lang);
4199 if (!res) {
4200 /* Fifty, not fifty-zero */
4201 if (one != 0) {
4202 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4203 res = wait_file(chan,ints,nextmsg,lang);
4207 break;
4208 case 'T':
4209 res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
4210 break;
4211 case ' ':
4212 case ' ':
4213 /* Just ignore spaces and tabs */
4214 break;
4215 default:
4216 /* Unknown character */
4217 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4219 /* Jump out on DTMF */
4220 if (res) {
4221 break;
4224 return res;
4227 /* French syntax
4228 oclock = heure
4230 int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4232 struct tm tm;
4233 int res=0, offset, sndoffset;
4234 char sndfile[256], nextmsg[256];
4236 if (format == NULL)
4237 format = "AdBY 'digits/at' IMp";
4239 ast_localtime(&time,&tm,timezone);
4241 for (offset=0 ; format[offset] != '\0' ; offset++) {
4242 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4243 switch (format[offset]) {
4244 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4245 case '\'':
4246 /* Literal name of a sound file */
4247 sndoffset=0;
4248 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4249 sndfile[sndoffset] = format[offset];
4250 sndfile[sndoffset] = '\0';
4251 res = wait_file(chan,ints,sndfile,lang);
4252 break;
4253 case 'A':
4254 case 'a':
4255 /* Sunday - Saturday */
4256 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4257 res = wait_file(chan,ints,nextmsg,lang);
4258 break;
4259 case 'B':
4260 case 'b':
4261 case 'h':
4262 /* January - December */
4263 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4264 res = wait_file(chan,ints,nextmsg,lang);
4265 break;
4266 case 'm':
4267 /* First - Twelfth */
4268 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4269 res = wait_file(chan,ints,nextmsg,lang);
4270 break;
4271 case 'd':
4272 case 'e':
4273 /* First */
4274 if (tm.tm_mday == 1) {
4275 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4276 res = wait_file(chan,ints,nextmsg,lang);
4277 } else {
4278 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4280 break;
4281 case 'Y':
4282 /* Year */
4283 if (tm.tm_year > 99) {
4284 res = wait_file(chan,ints, "digits/2",lang);
4285 if (!res) {
4286 res = wait_file(chan,ints, "digits/thousand",lang);
4288 if (tm.tm_year > 100) {
4289 if (!res) {
4290 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
4293 } else {
4294 if (tm.tm_year < 1) {
4295 /* I'm not going to handle 1900 and prior */
4296 /* We'll just be silent on the year, instead of bombing out. */
4297 } else {
4298 res = wait_file(chan,ints, "digits/thousand",lang);
4299 if (!res) {
4300 wait_file(chan,ints, "digits/9",lang);
4301 wait_file(chan,ints, "digits/hundred",lang);
4302 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
4306 break;
4307 case 'I':
4308 case 'l':
4309 /* 12-Hour */
4310 if (tm.tm_hour == 0)
4311 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4312 else if (tm.tm_hour > 12)
4313 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4314 else
4315 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4316 res = wait_file(chan,ints,nextmsg,lang);
4317 if (!res)
4318 res = wait_file(chan,ints, "digits/oclock",lang);
4319 break;
4320 case 'H':
4321 case 'k':
4322 /* 24-Hour */
4323 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4324 if (!res)
4325 res = wait_file(chan,ints, "digits/oclock",lang);
4326 break;
4327 case 'M':
4328 /* Minute */
4329 if (tm.tm_min == 0) {
4330 break;
4332 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
4333 break;
4334 case 'P':
4335 case 'p':
4336 /* AM/PM */
4337 if (tm.tm_hour > 11)
4338 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4339 else
4340 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4341 res = wait_file(chan,ints,nextmsg,lang);
4342 break;
4343 case 'Q':
4344 /* Shorthand for "Today", "Yesterday", or AdBY */
4345 /* XXX As emphasized elsewhere, this should the native way in your
4346 * language to say the date, with changes in what you say, depending
4347 * upon how recent the date is. XXX */
4349 struct timeval now;
4350 struct tm tmnow;
4351 time_t beg_today, tt;
4353 gettimeofday(&now,NULL);
4354 tt = now.tv_sec;
4355 ast_localtime(&tt,&tmnow,timezone);
4356 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4357 /* In any case, it saves not having to do ast_mktime() */
4358 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4359 if (beg_today < time) {
4360 /* Today */
4361 res = wait_file(chan,ints, "digits/today",lang);
4362 } else if (beg_today - 86400 < time) {
4363 /* Yesterday */
4364 res = wait_file(chan,ints, "digits/yesterday",lang);
4365 } else {
4366 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4369 break;
4370 case 'q':
4371 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4372 /* XXX As emphasized elsewhere, this should the native way in your
4373 * language to say the date, with changes in what you say, depending
4374 * upon how recent the date is. XXX */
4376 struct timeval now;
4377 struct tm tmnow;
4378 time_t beg_today, tt;
4380 gettimeofday(&now,NULL);
4381 tt = now.tv_sec;
4382 ast_localtime(&tt,&tmnow,timezone);
4383 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4384 /* In any case, it saves not having to do ast_mktime() */
4385 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4386 if (beg_today < time) {
4387 /* Today */
4388 } else if ((beg_today - 86400) < time) {
4389 /* Yesterday */
4390 res = wait_file(chan,ints, "digits/yesterday",lang);
4391 } else if (beg_today - 86400 * 6 < time) {
4392 /* Within the last week */
4393 res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
4394 } else {
4395 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4398 break;
4399 case 'R':
4400 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
4401 break;
4402 case 'S':
4403 /* Seconds */
4404 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
4405 if (!res) {
4406 res = wait_file(chan,ints, "digits/second",lang);
4408 break;
4409 case 'T':
4410 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
4411 break;
4412 case ' ':
4413 case ' ':
4414 /* Just ignore spaces and tabs */
4415 break;
4416 default:
4417 /* Unknown character */
4418 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4420 /* Jump out on DTMF */
4421 if (res) {
4422 break;
4425 return res;
4428 int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4430 struct tm tm;
4431 int res=0, offset, sndoffset;
4432 char sndfile[256], nextmsg[256];
4434 if (format == NULL)
4435 format = "AdB 'digits/at' IMp";
4437 ast_localtime(&time,&tm,timezone);
4439 for (offset=0 ; format[offset] != '\0' ; offset++) {
4440 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4441 switch (format[offset]) {
4442 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4443 case '\'':
4444 /* Literal name of a sound file */
4445 sndoffset=0;
4446 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4447 sndfile[sndoffset] = format[offset];
4448 sndfile[sndoffset] = '\0';
4449 res = wait_file(chan,ints,sndfile,lang);
4450 break;
4451 case 'A':
4452 case 'a':
4453 /* Sunday - Saturday */
4454 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4455 res = wait_file(chan,ints,nextmsg,lang);
4456 break;
4457 case 'B':
4458 case 'b':
4459 case 'h':
4460 /* January - December */
4461 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4462 res = wait_file(chan,ints,nextmsg,lang);
4463 break;
4464 case 'm':
4465 /* First - Twelfth */
4466 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4467 res = wait_file(chan,ints,nextmsg,lang);
4468 break;
4469 case 'd':
4470 case 'e':
4471 /* First day of the month is spelled as ordinal */
4472 if (tm.tm_mday == 1) {
4473 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4474 res = wait_file(chan,ints,nextmsg,lang);
4475 } else {
4476 if (!res) {
4477 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4480 break;
4481 case 'Y':
4482 /* Year */
4483 if (tm.tm_year > 99) {
4484 res = wait_file(chan,ints, "digits/ore-2000",lang);
4485 if (tm.tm_year > 100) {
4486 if (!res) {
4487 /* This works until the end of 2021 */
4488 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4489 res = wait_file(chan,ints,nextmsg,lang);
4492 } else {
4493 if (tm.tm_year < 1) {
4494 /* I'm not going to handle 1900 and prior */
4495 /* We'll just be silent on the year, instead of bombing out. */
4496 } else {
4497 res = wait_file(chan,ints, "digits/ore-1900",lang);
4498 if ((!res) && (tm.tm_year != 0)) {
4499 if (tm.tm_year <= 21) {
4500 /* 1910 - 1921 */
4501 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4502 res = wait_file(chan,ints,nextmsg,lang);
4503 } else {
4504 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4505 int ten, one;
4506 ten = tm.tm_year / 10;
4507 one = tm.tm_year % 10;
4508 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4509 res = wait_file(chan,ints,nextmsg,lang);
4510 if (!res) {
4511 if (one != 0) {
4512 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4513 res = wait_file(chan,ints,nextmsg,lang);
4520 break;
4521 case 'I':
4522 case 'l':
4523 /* 12-Hour */
4524 if (tm.tm_hour == 0)
4525 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4526 else if (tm.tm_hour > 12)
4527 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4528 else
4529 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4530 res = wait_file(chan,ints,nextmsg,lang);
4531 break;
4532 case 'H':
4533 case 'k':
4534 /* 24-Hour */
4535 if (tm.tm_hour == 0) {
4536 res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
4537 } else if (tm.tm_hour == 1) {
4538 res = wait_file(chan,ints, "digits/ore-una",lang);
4539 } else {
4540 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4542 break;
4543 case 'M':
4544 /* Minute */
4545 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4546 break;
4547 case 'P':
4548 case 'p':
4549 /* AM/PM */
4550 if (tm.tm_hour > 11)
4551 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4552 else
4553 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4554 res = wait_file(chan,ints,nextmsg,lang);
4555 break;
4556 case 'Q':
4557 /* Shorthand for "Today", "Yesterday", or ABdY */
4558 /* XXX As emphasized elsewhere, this should the native way in your
4559 * language to say the date, with changes in what you say, depending
4560 * upon how recent the date is. XXX */
4562 struct timeval now;
4563 struct tm tmnow;
4564 time_t beg_today, tt;
4566 gettimeofday(&now,NULL);
4567 tt = now.tv_sec;
4568 ast_localtime(&tt,&tmnow,timezone);
4569 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4570 /* In any case, it saves not having to do ast_mktime() */
4571 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4572 if (beg_today < time) {
4573 /* Today */
4574 res = wait_file(chan,ints, "digits/today",lang);
4575 } else if (beg_today - 86400 < time) {
4576 /* Yesterday */
4577 res = wait_file(chan,ints, "digits/yesterday",lang);
4578 } else {
4579 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4582 break;
4583 case 'q':
4584 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4586 struct timeval now;
4587 struct tm tmnow;
4588 time_t beg_today, tt;
4590 gettimeofday(&now,NULL);
4591 tt = now.tv_sec;
4592 ast_localtime(&tt,&tmnow,timezone);
4593 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4594 /* In any case, it saves not having to do ast_mktime() */
4595 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4596 if (beg_today < time) {
4597 /* Today */
4598 } else if ((beg_today - 86400) < time) {
4599 /* Yesterday */
4600 res = wait_file(chan,ints, "digits/yesterday",lang);
4601 } else if (beg_today - 86400 * 6 < time) {
4602 /* Within the last week */
4603 res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
4604 } else {
4605 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4608 break;
4609 case 'R':
4610 res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
4611 break;
4612 case 'S':
4613 /* Seconds */
4614 if (tm.tm_sec == 0) {
4615 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4616 res = wait_file(chan,ints,nextmsg,lang);
4617 } else if (tm.tm_sec < 10) {
4618 res = wait_file(chan,ints, "digits/oh",lang);
4619 if (!res) {
4620 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4621 res = wait_file(chan,ints,nextmsg,lang);
4623 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4624 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4625 res = wait_file(chan,ints,nextmsg,lang);
4626 } else {
4627 int ten, one;
4628 ten = (tm.tm_sec / 10) * 10;
4629 one = (tm.tm_sec % 10);
4630 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4631 res = wait_file(chan,ints,nextmsg,lang);
4632 if (!res) {
4633 /* Fifty, not fifty-zero */
4634 if (one != 0) {
4635 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4636 res = wait_file(chan,ints,nextmsg,lang);
4640 break;
4641 case 'T':
4642 res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
4643 break;
4644 case ' ':
4645 case ' ':
4646 /* Just ignore spaces and tabs */
4647 break;
4648 default:
4649 /* Unknown character */
4650 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4652 /* Jump out on DTMF */
4653 if (res) {
4654 break;
4657 return res;
4660 /* Dutch syntax */
4661 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4663 struct tm tm;
4664 int res=0, offset, sndoffset;
4665 char sndfile[256], nextmsg[256];
4667 if (format == NULL)
4668 format = "ABdY 'digits/at' IMp";
4670 ast_localtime(&time,&tm,timezone);
4672 for (offset=0 ; format[offset] != '\0' ; offset++) {
4673 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4674 switch (format[offset]) {
4675 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4676 case '\'':
4677 /* Literal name of a sound file */
4678 sndoffset=0;
4679 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4680 sndfile[sndoffset] = format[offset];
4681 sndfile[sndoffset] = '\0';
4682 res = wait_file(chan,ints,sndfile,lang);
4683 break;
4684 case 'A':
4685 case 'a':
4686 /* Sunday - Saturday */
4687 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4688 res = wait_file(chan,ints,nextmsg,lang);
4689 break;
4690 case 'B':
4691 case 'b':
4692 case 'h':
4693 /* January - December */
4694 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4695 res = wait_file(chan,ints,nextmsg,lang);
4696 break;
4697 case 'm':
4698 /* First - Twelfth */
4699 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4700 res = wait_file(chan,ints,nextmsg,lang);
4701 break;
4702 case 'd':
4703 case 'e':
4704 /* First - Thirtyfirst */
4705 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
4706 break;
4707 case 'Y':
4708 /* Year */
4709 if (tm.tm_year > 99) {
4710 res = wait_file(chan,ints, "digits/2",lang);
4711 if (!res) {
4712 res = wait_file(chan,ints, "digits/thousand",lang);
4714 if (tm.tm_year > 100) {
4715 if (!res) {
4716 /* This works until the end of 2020 */
4717 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4718 res = wait_file(chan,ints,nextmsg,lang);
4721 } else {
4722 if (tm.tm_year < 1) {
4723 /* I'm not going to handle 1900 and prior */
4724 /* We'll just be silent on the year, instead of bombing out. */
4725 } else {
4726 res = wait_file(chan,ints, "digits/19",lang);
4727 if (!res) {
4728 if (tm.tm_year <= 9) {
4729 /* 1901 - 1909 */
4730 res = wait_file(chan,ints, "digits/oh",lang);
4731 if (!res) {
4732 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4733 res = wait_file(chan,ints,nextmsg,lang);
4735 } else if (tm.tm_year <= 20) {
4736 /* 1910 - 1920 */
4737 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4738 res = wait_file(chan,ints,nextmsg,lang);
4739 } else {
4740 /* 1921 - 1999 */
4741 int ten, one;
4742 ten = tm.tm_year / 10;
4743 one = tm.tm_year % 10;
4744 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4745 res = wait_file(chan,ints,nextmsg,lang);
4746 if (!res) {
4747 if (one != 0) {
4748 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4749 res = wait_file(chan,ints,nextmsg,lang);
4756 break;
4757 case 'I':
4758 case 'l':
4759 /* 12-Hour */
4760 if (tm.tm_hour == 0)
4761 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4762 else if (tm.tm_hour > 12)
4763 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4764 else
4765 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4766 res = wait_file(chan,ints,nextmsg,lang);
4767 break;
4768 case 'H':
4769 case 'k':
4770 /* 24-Hour */
4771 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4772 if (!res) {
4773 res = wait_file(chan,ints, "digits/nl-uur",lang);
4775 break;
4776 case 'M':
4777 /* Minute */
4778 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4779 break;
4780 case 'P':
4781 case 'p':
4782 /* AM/PM */
4783 if (tm.tm_hour > 11)
4784 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4785 else
4786 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4787 res = wait_file(chan,ints,nextmsg,lang);
4788 break;
4789 case 'Q':
4790 /* Shorthand for "Today", "Yesterday", or ABdY */
4791 /* XXX As emphasized elsewhere, this should the native way in your
4792 * language to say the date, with changes in what you say, depending
4793 * upon how recent the date is. XXX */
4795 struct timeval now;
4796 struct tm tmnow;
4797 time_t beg_today, tt;
4799 gettimeofday(&now,NULL);
4800 tt = now.tv_sec;
4801 ast_localtime(&tt,&tmnow,timezone);
4802 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4803 /* In any case, it saves not having to do ast_mktime() */
4804 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4805 if (beg_today < time) {
4806 /* Today */
4807 res = wait_file(chan,ints, "digits/today",lang);
4808 } else if (beg_today - 86400 < time) {
4809 /* Yesterday */
4810 res = wait_file(chan,ints, "digits/yesterday",lang);
4811 } else {
4812 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4815 break;
4816 case 'q':
4817 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4819 struct timeval now;
4820 struct tm tmnow;
4821 time_t beg_today, tt;
4823 gettimeofday(&now,NULL);
4824 tt = now.tv_sec;
4825 ast_localtime(&tt,&tmnow,timezone);
4826 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4827 /* In any case, it saves not having to do ast_mktime() */
4828 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4829 if (beg_today < time) {
4830 /* Today */
4831 } else if ((beg_today - 86400) < time) {
4832 /* Yesterday */
4833 res = wait_file(chan,ints, "digits/yesterday",lang);
4834 } else if (beg_today - 86400 * 6 < time) {
4835 /* Within the last week */
4836 res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
4837 } else {
4838 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4841 break;
4842 case 'R':
4843 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
4844 break;
4845 case 'S':
4846 /* Seconds */
4847 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4848 break;
4849 case 'T':
4850 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
4851 break;
4852 case ' ':
4853 case ' ':
4854 /* Just ignore spaces and tabs */
4855 break;
4856 default:
4857 /* Unknown character */
4858 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4860 /* Jump out on DTMF */
4861 if (res) {
4862 break;
4865 return res;
4868 /* Polish syntax */
4869 int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const char *ints, const char *lang, const char *format, const char *timezone)
4871 struct tm tm;
4872 int res=0, offset, sndoffset;
4873 char sndfile[256], nextmsg[256];
4875 ast_localtime(&thetime, &tm, timezone);
4877 for (offset = 0 ; format[offset] != '\0' ; offset++) {
4878 int remainder;
4879 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4880 switch (format[offset]) {
4881 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4882 case '\'':
4883 /* Literal name of a sound file */
4884 sndoffset = 0;
4885 for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4886 sndfile[sndoffset] = format[offset];
4887 sndfile[sndoffset] = '\0';
4888 res = wait_file(chan, ints, sndfile, lang);
4889 break;
4890 case 'A':
4891 case 'a':
4892 /* Sunday - Saturday */
4893 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4894 res = wait_file(chan, ints, nextmsg, lang);
4895 break;
4896 case 'B':
4897 case 'b':
4898 case 'h':
4899 /* January - December */
4900 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4901 res = wait_file(chan, ints, nextmsg, lang);
4902 break;
4903 case 'm':
4904 /* Month enumerated */
4905 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
4906 break;
4907 case 'd':
4908 case 'e':
4909 /* First - Thirtyfirst */
4910 remainder = tm.tm_mday;
4911 if (tm.tm_mday > 30) {
4912 res = wait_file(chan, ints, "digits/h-30", lang);
4913 remainder -= 30;
4915 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
4916 res = wait_file(chan, ints, "digits/h-20", lang);
4917 remainder -= 20;
4919 if (!res) {
4920 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
4921 res = wait_file(chan, ints, nextmsg, lang);
4923 break;
4924 case 'Y':
4925 /* Year */
4926 if (tm.tm_year > 100) {
4927 res = wait_file(chan, ints, "digits/2", lang);
4928 if (!res)
4929 res = wait_file(chan, ints, "digits/1000.2",lang);
4930 if (tm.tm_year > 100) {
4931 if (!res)
4932 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
4934 } else if (tm.tm_year == 100) {
4935 res = wait_file(chan, ints, "digits/h-2000", lang);
4936 } else {
4937 if (tm.tm_year < 1) {
4938 /* I'm not going to handle 1900 and prior */
4939 /* We'll just be silent on the year, instead of bombing out. */
4940 break;
4941 } else {
4942 res = wait_file(chan, ints, "digits/1000", lang);
4943 if (!res) {
4944 wait_file(chan, ints, "digits/900", lang);
4945 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
4949 if (!res)
4950 wait_file(chan, ints, "digits/year", lang);
4951 break;
4952 case 'I':
4953 case 'l':
4954 /* 12-Hour */
4955 if (tm.tm_hour == 0)
4956 snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
4957 else if (tm.tm_hour > 12)
4958 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
4959 else
4960 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4962 res = wait_file(chan, ints, nextmsg, lang);
4963 break;
4964 case 'H':
4965 case 'k':
4966 /* 24-Hour */
4967 if (tm.tm_hour != 0) {
4968 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4969 res = wait_file(chan, ints, nextmsg, lang);
4970 } else
4971 res = wait_file(chan, ints, "digits/t-24", lang);
4972 break;
4973 case 'M':
4974 case 'N':
4975 /* Minute */
4976 if (tm.tm_min == 0) {
4977 if (format[offset] == 'M') {
4978 res = wait_file(chan, ints, "digits/oclock", lang);
4979 } else {
4980 res = wait_file(chan, ints, "digits/100", lang);
4982 } else
4983 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4984 break;
4985 case 'P':
4986 case 'p':
4987 /* AM/PM */
4988 if (tm.tm_hour > 11)
4989 snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
4990 else
4991 snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
4992 res = wait_file(chan, ints, nextmsg, lang);
4993 break;
4994 case 'Q':
4995 /* Shorthand for "Today", "Yesterday", or AdBY */
4997 time_t tv_sec = time(NULL);
4998 struct tm tmnow;
4999 time_t beg_today;
5001 ast_localtime(&tv_sec,&tmnow, timezone);
5002 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5003 /* In any case, it saves not having to do ast_mktime() */
5004 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5005 if (beg_today < thetime) {
5006 /* Today */
5007 res = wait_file(chan, ints, "digits/today", lang);
5008 } else if (beg_today - 86400 < thetime) {
5009 /* Yesterday */
5010 res = wait_file(chan, ints, "digits/yesterday", lang);
5011 } else {
5012 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
5015 break;
5016 case 'q':
5017 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5019 time_t tv_sec = time(NULL);
5020 struct tm tmnow;
5021 time_t beg_today;
5023 ast_localtime(&tv_sec, &tmnow, timezone);
5024 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5025 /* In any case, it saves not having to do ast_mktime() */
5026 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5027 if (beg_today < thetime) {
5028 /* Today */
5029 } else if ((beg_today - 86400) < thetime) {
5030 /* Yesterday */
5031 res = wait_file(chan, ints, "digits/yesterday", lang);
5032 } else if (beg_today - 86400 * 6 < thetime) {
5033 /* Within the last week */
5034 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
5035 } else {
5036 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
5039 break;
5040 case 'R':
5041 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
5042 break;
5043 case 'S':
5044 /* Seconds */
5045 res = wait_file(chan, ints, "digits/and", lang);
5046 if (!res) {
5047 if (tm.tm_sec == 1) {
5048 res = wait_file(chan, ints, "digits/1z", lang);
5049 if (!res)
5050 res = wait_file(chan, ints, "digits/second-a", lang);
5051 } else {
5052 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5053 if (!res) {
5054 int ten, one;
5055 ten = tm.tm_sec / 10;
5056 one = tm.tm_sec % 10;
5058 if (one > 1 && one < 5 && ten != 1)
5059 res = wait_file(chan,ints, "digits/seconds",lang);
5060 else
5061 res = wait_file(chan,ints, "digits/second",lang);
5065 break;
5066 case 'T':
5067 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
5068 break;
5069 case ' ':
5070 case ' ':
5071 /* Just ignore spaces and tabs */
5072 break;
5073 default:
5074 /* Unknown character */
5075 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5077 /* Jump out on DTMF */
5078 if (res)
5079 break;
5081 return res;
5084 /* Portuguese syntax */
5085 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
5087 struct tm tm;
5088 int res=0, offset, sndoffset;
5089 char sndfile[256], nextmsg[256];
5091 if (format == NULL)
5092 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
5094 ast_localtime(&time,&tm,timezone);
5096 for (offset=0 ; format[offset] != '\0' ; offset++) {
5097 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5098 switch (format[offset]) {
5099 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5100 case '\'':
5101 /* Literal name of a sound file */
5102 sndoffset=0;
5103 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5104 sndfile[sndoffset] = format[offset];
5105 sndfile[sndoffset] = '\0';
5106 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
5107 res = wait_file(chan,ints,nextmsg,lang);
5108 break;
5109 case 'A':
5110 case 'a':
5111 /* Sunday - Saturday */
5112 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5113 res = wait_file(chan,ints,nextmsg,lang);
5114 break;
5115 case 'B':
5116 case 'b':
5117 case 'h':
5118 /* January - December */
5119 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5120 res = wait_file(chan,ints,nextmsg,lang);
5121 break;
5122 case 'm':
5123 /* First - Twelfth */
5124 if (!strcasecmp(lang, "pt_BR")) {
5125 res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
5126 } else {
5127 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5128 res = wait_file(chan,ints,nextmsg,lang);
5130 break;
5131 case 'd':
5132 case 'e':
5133 /* First - Thirtyfirst */
5134 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5135 break;
5136 case 'Y':
5137 /* Year */
5138 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5139 break;
5140 case 'I':
5141 case 'l':
5142 /* 12-Hour */
5143 if (!strcasecmp(lang, "pt_BR")) {
5144 if (tm.tm_hour == 0) {
5145 if (format[offset] == 'I')
5146 res = wait_file(chan, ints, "digits/pt-a", lang);
5147 if (!res)
5148 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
5149 } else if (tm.tm_hour == 12) {
5150 if (format[offset] == 'I')
5151 res = wait_file(chan, ints, "digits/pt-ao", lang);
5152 if (!res)
5153 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
5154 } else {
5155 if (format[offset] == 'I') {
5156 if ((tm.tm_hour % 12) != 1)
5157 res = wait_file(chan, ints, "digits/pt-as", lang);
5158 else
5159 res = wait_file(chan, ints, "digits/pt-a", lang);
5161 if (!res)
5162 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
5164 } else {
5165 if (tm.tm_hour == 0) {
5166 if (format[offset] == 'I')
5167 res = wait_file(chan, ints, "digits/pt-ah", lang);
5168 if (!res)
5169 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
5171 else if (tm.tm_hour == 12) {
5172 if (format[offset] == 'I')
5173 res = wait_file(chan, ints, "digits/pt-ao", lang);
5174 if (!res)
5175 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
5177 else {
5178 if (format[offset] == 'I') {
5179 res = wait_file(chan, ints, "digits/pt-ah", lang);
5180 if ((tm.tm_hour % 12) != 1)
5181 if (!res)
5182 res = wait_file(chan, ints, "digits/pt-sss", lang);
5184 if (!res)
5185 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
5188 break;
5189 case 'H':
5190 case 'k':
5191 /* 24-Hour */
5192 if (!strcasecmp(lang, "pt_BR")) {
5193 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5194 if ((!res) && (format[offset] == 'H')) {
5195 if (tm.tm_hour > 1) {
5196 res = wait_file(chan,ints,"digits/hours",lang);
5197 } else {
5198 res = wait_file(chan,ints,"digits/hour",lang);
5201 } else {
5202 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
5203 if (!res) {
5204 if (tm.tm_hour != 0) {
5205 int remainder = tm.tm_hour;
5206 if (tm.tm_hour > 20) {
5207 res = wait_file(chan,ints, "digits/20",lang);
5208 remainder -= 20;
5210 if (!res) {
5211 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
5212 res = wait_file(chan,ints,nextmsg,lang);
5217 break;
5218 case 'M':
5219 /* Minute */
5220 if (!strcasecmp(lang, "pt_BR")) {
5221 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5222 if (!res) {
5223 if (tm.tm_min > 1) {
5224 res = wait_file(chan,ints,"digits/minutes",lang);
5225 } else {
5226 res = wait_file(chan,ints,"digits/minute",lang);
5229 } else {
5230 if (tm.tm_min == 0) {
5231 res = wait_file(chan, ints, "digits/pt-hora", lang);
5232 if (tm.tm_hour != 1)
5233 if (!res)
5234 res = wait_file(chan, ints, "digits/pt-sss", lang);
5235 } else {
5236 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5239 break;
5240 case 'P':
5241 case 'p':
5242 /* AM/PM */
5243 if (!strcasecmp(lang, "pt_BR")) {
5244 if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
5245 res = wait_file(chan, ints, "digits/pt-da", lang);
5246 if (!res) {
5247 if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
5248 res = wait_file(chan, ints, "digits/morning", lang);
5249 else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
5250 res = wait_file(chan, ints, "digits/afternoon", lang);
5251 else res = wait_file(chan, ints, "digits/night", lang);
5254 } else {
5255 if (tm.tm_hour > 12)
5256 res = wait_file(chan, ints, "digits/p-m", lang);
5257 else if (tm.tm_hour && tm.tm_hour < 12)
5258 res = wait_file(chan, ints, "digits/a-m", lang);
5260 break;
5261 case 'Q':
5262 /* Shorthand for "Today", "Yesterday", or ABdY */
5263 /* XXX As emphasized elsewhere, this should the native way in your
5264 * language to say the date, with changes in what you say, depending
5265 * upon how recent the date is. XXX */
5267 struct timeval now;
5268 struct tm tmnow;
5269 time_t beg_today, tt;
5271 gettimeofday(&now,NULL);
5272 tt = now.tv_sec;
5273 ast_localtime(&tt,&tmnow,timezone);
5274 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5275 /* In any case, it saves not having to do ast_mktime() */
5276 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5277 if (beg_today < time) {
5278 /* Today */
5279 res = wait_file(chan,ints, "digits/today",lang);
5280 } else if (beg_today - 86400 < time) {
5281 /* Yesterday */
5282 res = wait_file(chan,ints, "digits/yesterday",lang);
5283 } else {
5284 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5287 break;
5288 case 'q':
5289 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5290 /* XXX As emphasized elsewhere, this should the native way in your
5291 * language to say the date, with changes in what you say, depending
5292 * upon how recent the date is. XXX */
5294 struct timeval now;
5295 struct tm tmnow;
5296 time_t beg_today, tt;
5298 gettimeofday(&now,NULL);
5299 tt = now.tv_sec;
5300 ast_localtime(&tt,&tmnow,timezone);
5301 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5302 /* In any case, it saves not having to do ast_mktime() */
5303 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5304 if (beg_today < time) {
5305 /* Today */
5306 } else if ((beg_today - 86400) < time) {
5307 /* Yesterday */
5308 res = wait_file(chan,ints, "digits/yesterday",lang);
5309 } else if (beg_today - 86400 * 6 < time) {
5310 /* Within the last week */
5311 res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
5312 } else {
5313 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5316 break;
5317 case 'R':
5318 res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
5319 break;
5320 case 'S':
5321 /* Seconds */
5322 if (!strcasecmp(lang, "pt_BR")) {
5323 res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
5324 if (!res) {
5325 if (tm.tm_sec > 1) {
5326 res = wait_file(chan,ints,"digits/seconds",lang);
5327 } else {
5328 res = wait_file(chan,ints,"digits/second",lang);
5330 } else if (tm.tm_sec < 10) {
5331 res = wait_file(chan,ints, "digits/oh",lang);
5332 if (!res) {
5333 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5334 res = wait_file(chan,ints,nextmsg,lang);
5336 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5337 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5338 res = wait_file(chan,ints,nextmsg,lang);
5339 } else {
5340 int ten, one;
5341 ten = (tm.tm_sec / 10) * 10;
5342 one = (tm.tm_sec % 10);
5343 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
5344 res = wait_file(chan,ints,nextmsg,lang);
5345 if (!res) {
5346 /* Fifty, not fifty-zero */
5347 if (one != 0) {
5348 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
5349 res = wait_file(chan,ints,nextmsg,lang);
5354 break;
5355 case 'T':
5356 res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
5357 break;
5358 case ' ':
5359 case ' ':
5360 /* Just ignore spaces and tabs */
5361 break;
5362 default:
5363 /* Unknown character */
5364 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5366 /* Jump out on DTMF */
5367 if (res) {
5368 break;
5371 return res;
5374 /* Taiwanese / Chinese syntax */
5375 int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
5377 struct tm tm;
5378 int res=0, offset, sndoffset;
5379 char sndfile[256], nextmsg[256];
5381 if (format == NULL)
5382 format = "YBdAkM";
5384 ast_localtime(&time,&tm,timezone);
5386 for (offset=0 ; format[offset] != '\0' ; offset++) {
5387 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5388 switch (format[offset]) {
5389 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5390 case '\'':
5391 /* Literal name of a sound file */
5392 sndoffset=0;
5393 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5394 sndfile[sndoffset] = format[offset];
5395 sndfile[sndoffset] = '\0';
5396 res = wait_file(chan,ints,sndfile,lang);
5397 break;
5398 case 'A':
5399 case 'a':
5400 /* Sunday - Saturday */
5401 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5402 res = wait_file(chan,ints,nextmsg,lang);
5403 break;
5404 case 'B':
5405 case 'b':
5406 case 'h':
5407 case 'm':
5408 /* January - December */
5409 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5410 res = wait_file(chan,ints,nextmsg,lang);
5411 break;
5412 case 'd':
5413 case 'e':
5414 /* First - Thirtyfirst */
5415 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
5416 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
5417 res = wait_file(chan,ints,nextmsg,lang);
5418 } else {
5419 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
5420 res = wait_file(chan,ints,nextmsg,lang);
5421 if (!res) {
5422 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
5423 res = wait_file(chan,ints,nextmsg,lang);
5426 if (!res) res = wait_file(chan,ints,"digits/day",lang);
5427 break;
5428 case 'Y':
5429 /* Year */
5430 if (tm.tm_year > 99) {
5431 res = wait_file(chan,ints, "digits/2",lang);
5432 if (!res) {
5433 res = wait_file(chan,ints, "digits/thousand",lang);
5435 if (tm.tm_year > 100) {
5436 if (!res) {
5437 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
5438 res = wait_file(chan,ints,nextmsg,lang);
5439 if (!res) {
5440 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
5441 res = wait_file(chan,ints,nextmsg,lang);
5445 if (!res) {
5446 res = wait_file(chan,ints, "digits/year",lang);
5448 } else {
5449 if (tm.tm_year < 1) {
5450 /* I'm not going to handle 1900 and prior */
5451 /* We'll just be silent on the year, instead of bombing out. */
5452 } else {
5453 res = wait_file(chan,ints, "digits/1",lang);
5454 if (!res) {
5455 res = wait_file(chan,ints, "digits/9",lang);
5457 if (!res) {
5458 if (tm.tm_year <= 9) {
5459 /* 1901 - 1909 */
5460 res = wait_file(chan,ints, "digits/0",lang);
5461 if (!res) {
5462 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
5463 res = wait_file(chan,ints,nextmsg,lang);
5465 } else {
5466 /* 1910 - 1999 */
5467 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
5468 res = wait_file(chan,ints,nextmsg,lang);
5469 if (!res) {
5470 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
5471 res = wait_file(chan,ints,nextmsg,lang);
5476 if (!res) {
5477 res = wait_file(chan,ints, "digits/year",lang);
5480 break;
5481 case 'I':
5482 case 'l':
5483 /* 12-Hour */
5484 if (tm.tm_hour == 0)
5485 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
5486 else if (tm.tm_hour > 12)
5487 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5488 else
5489 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5490 res = wait_file(chan,ints,nextmsg,lang);
5491 if (!res) {
5492 res = wait_file(chan,ints, "digits/oclock",lang);
5494 break;
5495 case 'H':
5496 if (tm.tm_hour < 10) {
5497 res = wait_file(chan, ints, "digits/0", lang);
5499 case 'k':
5500 /* 24-Hour */
5501 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
5502 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5503 res = wait_file(chan,ints,nextmsg,lang);
5504 } else {
5505 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
5506 res = wait_file(chan,ints,nextmsg,lang);
5507 if (!res) {
5508 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
5509 res = wait_file(chan,ints,nextmsg,lang);
5512 if (!res) {
5513 res = wait_file(chan,ints, "digits/oclock",lang);
5515 break;
5516 case 'M':
5517 /* Minute */
5518 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
5519 if (tm.tm_min < 10) {
5520 res = wait_file(chan, ints, "digits/0", lang);
5522 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
5523 res = wait_file(chan,ints,nextmsg,lang);
5524 } else {
5525 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
5526 res = wait_file(chan,ints,nextmsg,lang);
5527 if (!res) {
5528 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
5529 res = wait_file(chan,ints,nextmsg,lang);
5532 if (!res) {
5533 res = wait_file(chan,ints, "digits/minute",lang);
5535 break;
5536 case 'P':
5537 case 'p':
5538 /* AM/PM */
5539 if (tm.tm_hour > 11)
5540 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
5541 else
5542 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
5543 res = wait_file(chan,ints,nextmsg,lang);
5544 break;
5545 case 'Q':
5546 /* Shorthand for "Today", "Yesterday", or ABdY */
5547 /* XXX As emphasized elsewhere, this should the native way in your
5548 * language to say the date, with changes in what you say, depending
5549 * upon how recent the date is. XXX */
5551 struct timeval now;
5552 struct tm tmnow;
5553 time_t beg_today, tt;
5555 gettimeofday(&now,NULL);
5556 tt = now.tv_sec;
5557 ast_localtime(&tt,&tmnow,timezone);
5558 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5559 /* In any case, it saves not having to do ast_mktime() */
5560 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5561 if (beg_today < time) {
5562 /* Today */
5563 res = wait_file(chan,ints, "digits/today",lang);
5564 } else if (beg_today - 86400 < time) {
5565 /* Yesterday */
5566 res = wait_file(chan,ints, "digits/yesterday",lang);
5567 } else {
5568 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5571 break;
5572 case 'q':
5573 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5574 /* XXX As emphasized elsewhere, this should the native way in your
5575 * language to say the date, with changes in what you say, depending
5576 * upon how recent the date is. XXX */
5578 struct timeval now;
5579 struct tm tmnow;
5580 time_t beg_today, tt;
5582 gettimeofday(&now,NULL);
5583 tt = now.tv_sec;
5584 ast_localtime(&tt,&tmnow,timezone);
5585 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5586 /* In any case, it saves not having to do ast_mktime() */
5587 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5588 if (beg_today < time) {
5589 /* Today */
5590 } else if ((beg_today - 86400) < time) {
5591 /* Yesterday */
5592 res = wait_file(chan,ints, "digits/yesterday",lang);
5593 } else if (beg_today - 86400 * 6 < time) {
5594 /* Within the last week */
5595 res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
5596 } else {
5597 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5600 break;
5601 case 'R':
5602 res = ast_say_date_with_format_tw(chan, time, ints, lang, "kM", timezone);
5603 break;
5604 case 'S':
5605 /* Seconds */
5606 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
5607 if (tm.tm_sec < 10) {
5608 res = wait_file(chan, ints, "digits/0", lang);
5610 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5611 res = wait_file(chan,ints,nextmsg,lang);
5612 } else {
5613 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
5614 res = wait_file(chan,ints,nextmsg,lang);
5615 if (!res) {
5616 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
5617 res = wait_file(chan,ints,nextmsg,lang);
5620 if (!res) {
5621 res = wait_file(chan,ints, "digits/second",lang);
5623 break;
5624 case 'T':
5625 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
5626 break;
5627 case ' ':
5628 case ' ':
5629 /* Just ignore spaces and tabs */
5630 break;
5631 default:
5632 /* Unknown character */
5633 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5635 /* Jump out on DTMF */
5636 if (res) {
5637 break;
5640 return res;
5643 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5645 if (!strcasecmp(lang, "en") ) { /* English syntax */
5646 return(ast_say_time_en(chan, t, ints, lang));
5647 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5648 return(ast_say_time_de(chan, t, ints, lang));
5649 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5650 return(ast_say_time_fr(chan, t, ints, lang));
5651 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5652 return(ast_say_time_nl(chan, t, ints, lang));
5653 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5654 return(ast_say_time_pt(chan, t, ints, lang));
5655 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5656 return(ast_say_time_pt_BR(chan, t, ints, lang));
5657 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5658 return(ast_say_time_tw(chan, t, ints, lang));
5659 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5660 return(ast_say_time_gr(chan, t, ints, lang));
5661 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5662 return(ast_say_time_ge(chan, t, ints, lang));
5663 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
5664 return (ast_say_time_he(chan, t, ints, lang));
5667 /* Default to English */
5668 return(ast_say_time_en(chan, t, ints, lang));
5671 /* English syntax */
5672 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5674 struct tm tm;
5675 int res = 0;
5676 int hour, pm=0;
5678 ast_localtime(&t, &tm, NULL);
5679 hour = tm.tm_hour;
5680 if (!hour)
5681 hour = 12;
5682 else if (hour == 12)
5683 pm = 1;
5684 else if (hour > 12) {
5685 hour -= 12;
5686 pm = 1;
5688 if (!res)
5689 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5691 if (tm.tm_min > 9) {
5692 if (!res)
5693 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5694 } else if (tm.tm_min) {
5695 if (!res)
5696 res = ast_streamfile(chan, "digits/oh", lang);
5697 if (!res)
5698 res = ast_waitstream(chan, ints);
5699 if (!res)
5700 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5701 } else {
5702 if (!res)
5703 res = ast_streamfile(chan, "digits/oclock", lang);
5704 if (!res)
5705 res = ast_waitstream(chan, ints);
5707 if (pm) {
5708 if (!res)
5709 res = ast_streamfile(chan, "digits/p-m", lang);
5710 } else {
5711 if (!res)
5712 res = ast_streamfile(chan, "digits/a-m", lang);
5714 if (!res)
5715 res = ast_waitstream(chan, ints);
5716 return res;
5719 /* German syntax */
5720 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5722 struct tm tm;
5723 int res = 0;
5725 ast_localtime(&t, &tm, NULL);
5726 if (!res)
5727 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5728 if (!res)
5729 res = ast_streamfile(chan, "digits/oclock", lang);
5730 if (!res)
5731 res = ast_waitstream(chan, ints);
5732 if (!res)
5733 if (tm.tm_min > 0)
5734 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5735 return res;
5738 /* French syntax */
5739 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5741 struct tm tm;
5742 int res = 0;
5744 ast_localtime(&t, &tm, NULL);
5746 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5747 if (!res)
5748 res = ast_streamfile(chan, "digits/oclock", lang);
5749 if (tm.tm_min) {
5750 if (!res)
5751 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5753 return res;
5756 /* Dutch syntax */
5757 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5759 struct tm tm;
5760 int res = 0;
5762 ast_localtime(&t, &tm, NULL);
5763 if (!res)
5764 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5765 if (!res)
5766 res = ast_streamfile(chan, "digits/nl-uur", lang);
5767 if (!res)
5768 res = ast_waitstream(chan, ints);
5769 if (!res)
5770 if (tm.tm_min > 0)
5771 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5772 return res;
5775 /* Portuguese syntax */
5776 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5778 struct tm tm;
5779 int res = 0;
5780 int hour;
5782 ast_localtime(&t, &tm, NULL);
5783 hour = tm.tm_hour;
5784 if (!res)
5785 res = ast_say_number(chan, hour, ints, lang, "f");
5786 if (tm.tm_min) {
5787 if (!res)
5788 res = wait_file(chan, ints, "digits/pt-e", lang);
5789 if (!res)
5790 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5791 } else {
5792 if (!res)
5793 res = wait_file(chan, ints, "digits/pt-hora", lang);
5794 if (tm.tm_hour != 1)
5795 if (!res)
5796 res = wait_file(chan, ints, "digits/pt-sss", lang);
5798 if (!res)
5799 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5800 return res;
5803 /* Brazilian Portuguese syntax */
5804 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5806 struct tm tm;
5807 int res = 0;
5809 ast_localtime(&t, &tm, NULL);
5811 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5812 if (!res) {
5813 if (tm.tm_hour > 1)
5814 res = wait_file(chan, ints, "digits/hours", lang);
5815 else
5816 res = wait_file(chan, ints, "digits/hour", lang);
5818 if ((!res) && (tm.tm_min)) {
5819 res = wait_file(chan, ints, "digits/pt-e", lang);
5820 if (!res)
5821 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5822 if (!res) {
5823 if (tm.tm_min > 1)
5824 res = wait_file(chan, ints, "digits/minutes", lang);
5825 else
5826 res = wait_file(chan, ints, "digits/minute", lang);
5829 return res;
5832 /* Taiwanese / Chinese syntax */
5833 int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5835 struct tm tm;
5836 int res = 0;
5837 int hour, pm=0;
5839 ast_localtime(&t, &tm, NULL);
5840 hour = tm.tm_hour;
5841 if (!hour)
5842 hour = 12;
5843 else if (hour == 12)
5844 pm = 1;
5845 else if (hour > 12) {
5846 hour -= 12;
5847 pm = 1;
5849 if (pm) {
5850 if (!res)
5851 res = ast_streamfile(chan, "digits/p-m", lang);
5852 } else {
5853 if (!res)
5854 res = ast_streamfile(chan, "digits/a-m", lang);
5856 if (!res)
5857 res = ast_waitstream(chan, ints);
5858 if (!res)
5859 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5860 if (!res)
5861 res = ast_streamfile(chan, "digits/oclock", lang);
5862 if (!res)
5863 res = ast_waitstream(chan, ints);
5864 if (!res)
5865 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5866 if (!res)
5867 res = ast_streamfile(chan, "digits/minute", lang);
5868 if (!res)
5869 res = ast_waitstream(chan, ints);
5870 return res;
5873 /* Hebrew syntax */
5874 int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5876 struct tm tm;
5877 int res = 0;
5878 int hour;
5880 ast_localtime(&t, &tm, NULL);
5881 hour = tm.tm_hour;
5882 if (!hour)
5883 hour = 12;
5885 if (!res)
5886 res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
5888 if (tm.tm_min > 9) {
5889 if (!res)
5890 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5891 } else if (tm.tm_min) {
5892 if (!res) { /* say a leading zero if needed */
5893 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
5895 if (!res)
5896 res = ast_waitstream(chan, ints);
5897 if (!res)
5898 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5899 } else {
5900 if (!res)
5901 res = ast_waitstream(chan, ints);
5903 if (!res)
5904 res = ast_waitstream(chan, ints);
5905 return res;
5907 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5909 if (!strcasecmp(lang, "en") ) { /* English syntax */
5910 return(ast_say_datetime_en(chan, t, ints, lang));
5911 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5912 return(ast_say_datetime_de(chan, t, ints, lang));
5913 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5914 return(ast_say_datetime_fr(chan, t, ints, lang));
5915 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5916 return(ast_say_datetime_nl(chan, t, ints, lang));
5917 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5918 return(ast_say_datetime_pt(chan, t, ints, lang));
5919 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5920 return(ast_say_datetime_pt_BR(chan, t, ints, lang));
5921 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5922 return(ast_say_datetime_tw(chan, t, ints, lang));
5923 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5924 return(ast_say_datetime_gr(chan, t, ints, lang));
5925 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5926 return(ast_say_datetime_ge(chan, t, ints, lang));
5927 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
5928 return (ast_say_datetime_he(chan, t, ints, lang));
5931 /* Default to English */
5932 return(ast_say_datetime_en(chan, t, ints, lang));
5935 /* English syntax */
5936 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5938 struct tm tm;
5939 char fn[256];
5940 int res = 0;
5941 int hour, pm=0;
5943 ast_localtime(&t, &tm, NULL);
5944 if (!res) {
5945 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5946 res = ast_streamfile(chan, fn, lang);
5947 if (!res)
5948 res = ast_waitstream(chan, ints);
5950 if (!res) {
5951 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5952 res = ast_streamfile(chan, fn, lang);
5953 if (!res)
5954 res = ast_waitstream(chan, ints);
5956 if (!res)
5957 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5959 hour = tm.tm_hour;
5960 if (!hour)
5961 hour = 12;
5962 else if (hour == 12)
5963 pm = 1;
5964 else if (hour > 12) {
5965 hour -= 12;
5966 pm = 1;
5968 if (!res)
5969 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5971 if (tm.tm_min > 9) {
5972 if (!res)
5973 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5974 } else if (tm.tm_min) {
5975 if (!res)
5976 res = ast_streamfile(chan, "digits/oh", lang);
5977 if (!res)
5978 res = ast_waitstream(chan, ints);
5979 if (!res)
5980 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5981 } else {
5982 if (!res)
5983 res = ast_streamfile(chan, "digits/oclock", lang);
5984 if (!res)
5985 res = ast_waitstream(chan, ints);
5987 if (pm) {
5988 if (!res)
5989 res = ast_streamfile(chan, "digits/p-m", lang);
5990 } else {
5991 if (!res)
5992 res = ast_streamfile(chan, "digits/a-m", lang);
5994 if (!res)
5995 res = ast_waitstream(chan, ints);
5996 if (!res)
5997 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5998 return res;
6001 /* German syntax */
6002 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6004 struct tm tm;
6005 int res = 0;
6007 ast_localtime(&t, &tm, NULL);
6008 res = ast_say_date(chan, t, ints, lang);
6009 if (!res)
6010 ast_say_time(chan, t, ints, lang);
6011 return res;
6015 /* French syntax */
6016 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6018 struct tm tm;
6019 char fn[256];
6020 int res = 0;
6022 ast_localtime(&t, &tm, NULL);
6024 if (!res)
6025 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6027 if (!res) {
6028 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6029 res = ast_streamfile(chan, fn, lang);
6030 if (!res)
6031 res = ast_waitstream(chan, ints);
6033 if (!res) {
6034 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6035 res = ast_streamfile(chan, fn, lang);
6036 if (!res)
6037 res = ast_waitstream(chan, ints);
6040 if (!res)
6041 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
6042 if (!res)
6043 res = ast_streamfile(chan, "digits/oclock", lang);
6044 if (tm.tm_min > 0) {
6045 if (!res)
6046 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6048 if (!res)
6049 res = ast_waitstream(chan, ints);
6050 if (!res)
6051 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6052 return res;
6055 /* Dutch syntax */
6056 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6058 struct tm tm;
6059 int res = 0;
6061 ast_localtime(&t, &tm, NULL);
6062 res = ast_say_date(chan, t, ints, lang);
6063 if (!res) {
6064 res = ast_streamfile(chan, "digits/nl-om", lang);
6065 if (!res)
6066 res = ast_waitstream(chan, ints);
6068 if (!res)
6069 ast_say_time(chan, t, ints, lang);
6070 return res;
6073 /* Portuguese syntax */
6074 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6076 struct tm tm;
6077 char fn[256];
6078 int res = 0;
6079 int hour, pm=0;
6081 ast_localtime(&t, &tm, NULL);
6082 if (!res) {
6083 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6084 res = ast_streamfile(chan, fn, lang);
6085 if (!res)
6086 res = ast_waitstream(chan, ints);
6088 if (!res) {
6089 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6090 res = ast_streamfile(chan, fn, lang);
6091 if (!res)
6092 res = ast_waitstream(chan, ints);
6094 if (!res)
6095 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6097 hour = tm.tm_hour;
6098 if (!hour)
6099 hour = 12;
6100 else if (hour == 12)
6101 pm = 1;
6102 else if (hour > 12) {
6103 hour -= 12;
6104 pm = 1;
6106 if (!res)
6107 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
6109 if (tm.tm_min > 9) {
6110 if (!res)
6111 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6112 } else if (tm.tm_min) {
6113 if (!res)
6114 res = ast_streamfile(chan, "digits/oh", lang);
6115 if (!res)
6116 res = ast_waitstream(chan, ints);
6117 if (!res)
6118 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6119 } else {
6120 if (!res)
6121 res = ast_streamfile(chan, "digits/oclock", lang);
6122 if (!res)
6123 res = ast_waitstream(chan, ints);
6125 if (pm) {
6126 if (!res)
6127 res = ast_streamfile(chan, "digits/p-m", lang);
6128 } else {
6129 if (!res)
6130 res = ast_streamfile(chan, "digits/a-m", lang);
6132 if (!res)
6133 res = ast_waitstream(chan, ints);
6134 if (!res)
6135 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6136 return res;
6139 /* Brazilian Portuguese syntax */
6140 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6142 struct tm tm;
6143 int res = 0;
6145 ast_localtime(&t, &tm, NULL);
6146 res = ast_say_date(chan, t, ints, lang);
6147 if (!res)
6148 res = ast_say_time(chan, t, ints, lang);
6149 return res;
6152 /* Taiwanese / Chinese syntax */
6153 int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6155 struct tm tm;
6156 char fn[256];
6157 int res = 0;
6158 int hour, pm=0;
6160 ast_localtime(&t, &tm, NULL);
6161 if (!res)
6162 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6163 if (!res) {
6164 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6165 res = ast_streamfile(chan, fn, lang);
6166 if (!res)
6167 res = ast_waitstream(chan, ints);
6169 if (!res)
6170 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6171 if (!res) {
6172 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6173 res = ast_streamfile(chan, fn, lang);
6174 if (!res)
6175 res = ast_waitstream(chan, ints);
6178 hour = tm.tm_hour;
6179 if (!hour)
6180 hour = 12;
6181 else if (hour == 12)
6182 pm = 1;
6183 else if (hour > 12) {
6184 hour -= 12;
6185 pm = 1;
6187 if (pm) {
6188 if (!res)
6189 res = ast_streamfile(chan, "digits/p-m", lang);
6190 } else {
6191 if (!res)
6192 res = ast_streamfile(chan, "digits/a-m", lang);
6194 if (!res)
6195 res = ast_waitstream(chan, ints);
6196 if (!res)
6197 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
6198 if (!res)
6199 res = ast_streamfile(chan, "digits/oclock", lang);
6200 if (!res)
6201 res = ast_waitstream(chan, ints);
6202 if (!res)
6203 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6204 if (!res)
6205 res = ast_streamfile(chan, "digits/minute", lang);
6206 if (!res)
6207 res = ast_waitstream(chan, ints);
6208 return res;
6211 /* Hebrew syntax */
6212 int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6214 struct tm tm;
6215 char fn[256];
6216 int res = 0;
6217 int hour;
6219 ast_localtime(&t, &tm, NULL);
6220 if (!res) {
6221 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6222 res = ast_streamfile(chan, fn, lang);
6223 if (!res) {
6224 res = ast_waitstream(chan, ints);
6227 if (!res) {
6228 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6229 res = ast_streamfile(chan, fn, lang);
6230 if (!res) {
6231 res = ast_waitstream(chan, ints);
6234 if (!res) {
6235 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
6238 hour = tm.tm_hour;
6239 if (!hour) {
6240 hour = 12;
6243 if (!res) {
6244 res = ast_say_number(chan, hour, ints, lang, "f");
6247 if (tm.tm_min > 9) {
6248 if (!res) {
6249 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
6251 } else if (tm.tm_min) {
6252 if (!res) {
6253 /* say a leading zero if needed */
6254 res = ast_say_number(chan, 0, ints, lang, "f");
6256 if (!res) {
6257 res = ast_waitstream(chan, ints);
6259 if (!res) {
6260 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
6262 } else {
6263 if (!res) {
6264 res = ast_waitstream(chan, ints);
6267 if (!res) {
6268 res = ast_waitstream(chan, ints);
6270 if (!res) {
6271 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
6273 return res;
6275 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6277 if (!strcasecmp(lang, "en") ) { /* English syntax */
6278 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
6279 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
6280 return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
6281 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
6282 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
6283 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
6284 return(ast_say_datetime_from_now_ge(chan, t, ints, lang));
6285 } else if (!strcasecmp(lang, "he")) { /* Georgian syntax */
6286 return (ast_say_datetime_from_now_he(chan, t, ints, lang));
6289 /* Default to English */
6290 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
6293 /* English syntax */
6294 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6296 int res=0;
6297 time_t nowt;
6298 int daydiff;
6299 struct tm tm;
6300 struct tm now;
6301 char fn[256];
6303 time(&nowt);
6305 ast_localtime(&t, &tm, NULL);
6306 ast_localtime(&nowt,&now, NULL);
6307 daydiff = now.tm_yday - tm.tm_yday;
6308 if ((daydiff < 0) || (daydiff > 6)) {
6309 /* Day of month and month */
6310 if (!res) {
6311 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6312 res = ast_streamfile(chan, fn, lang);
6313 if (!res)
6314 res = ast_waitstream(chan, ints);
6316 if (!res)
6317 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6319 } else if (daydiff) {
6320 /* Just what day of the week */
6321 if (!res) {
6322 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6323 res = ast_streamfile(chan, fn, lang);
6324 if (!res)
6325 res = ast_waitstream(chan, ints);
6327 } /* Otherwise, it was today */
6328 if (!res)
6329 res = ast_say_time(chan, t, ints, lang);
6330 return res;
6333 /* French syntax */
6334 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6336 int res=0;
6337 time_t nowt;
6338 int daydiff;
6339 struct tm tm;
6340 struct tm now;
6341 char fn[256];
6343 time(&nowt);
6345 ast_localtime(&t, &tm, NULL);
6346 ast_localtime(&nowt, &now, NULL);
6347 daydiff = now.tm_yday - tm.tm_yday;
6348 if ((daydiff < 0) || (daydiff > 6)) {
6349 /* Day of month and month */
6350 if (!res) {
6351 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6352 res = ast_streamfile(chan, fn, lang);
6353 if (!res)
6354 res = ast_waitstream(chan, ints);
6356 if (!res)
6357 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6359 } else if (daydiff) {
6360 /* Just what day of the week */
6361 if (!res) {
6362 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6363 res = ast_streamfile(chan, fn, lang);
6364 if (!res)
6365 res = ast_waitstream(chan, ints);
6367 } /* Otherwise, it was today */
6368 if (!res)
6369 res = ast_say_time(chan, t, ints, lang);
6370 return res;
6373 /* Portuguese syntax */
6374 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6376 int res=0;
6377 time_t nowt;
6378 int daydiff;
6379 struct tm tm;
6380 struct tm now;
6381 char fn[256];
6383 time(&nowt);
6385 ast_localtime(&t, &tm, NULL);
6386 ast_localtime(&nowt, &now, NULL);
6387 daydiff = now.tm_yday - tm.tm_yday;
6388 if ((daydiff < 0) || (daydiff > 6)) {
6389 /* Day of month and month */
6390 if (!res)
6391 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6392 if (!res)
6393 res = wait_file(chan, ints, "digits/pt-de", lang);
6394 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6395 if (!res)
6396 res = wait_file(chan, ints, fn, lang);
6398 } else if (daydiff) {
6399 /* Just what day of the week */
6400 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6401 if (!res)
6402 res = wait_file(chan, ints, fn, lang);
6403 } /* Otherwise, it was today */
6404 if (!strcasecmp(lang, "pt_BR")) {
6405 if (tm.tm_hour > 1) {
6406 snprintf(fn, sizeof(fn), "digits/pt-as");
6407 } else {
6408 snprintf(fn, sizeof(fn), "digits/pt-a");
6410 if (!res)
6411 res = wait_file(chan, ints, fn, lang);
6412 } else {
6413 snprintf(fn, sizeof(fn), "digits/pt-ah");
6414 if (!res)
6415 res = wait_file(chan, ints, fn, lang);
6416 if (tm.tm_hour != 1)
6417 if (!res)
6418 res = wait_file(chan, ints, "digits/pt-sss", lang);
6419 if (!res)
6420 res = ast_say_time(chan, t, ints, lang);
6422 return res;
6425 /* Hebrew syntax */
6426 int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6428 int res = 0;
6429 time_t nowt;
6430 int daydiff;
6431 struct tm tm;
6432 struct tm now;
6433 char fn[256];
6435 time(&nowt);
6437 ast_localtime(&t, &tm, NULL);
6438 ast_localtime(&nowt, &now, NULL);
6439 daydiff = now.tm_yday - tm.tm_yday;
6440 if ((daydiff < 0) || (daydiff > 6)) {
6441 /* Day of month and month */
6442 if (!res) {
6443 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6444 res = ast_streamfile(chan, fn, lang);
6445 if (!res)
6446 res = ast_waitstream(chan, ints);
6448 if (!res) {
6449 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
6451 } else if (daydiff) {
6452 /* Just what day of the week */
6453 if (!res) {
6454 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6455 res = ast_streamfile(chan, fn, lang);
6456 if (!res) {
6457 res = ast_waitstream(chan, ints);
6460 } /* Otherwise, it was today */
6461 if (!res) {
6462 res = ast_say_time(chan, t, ints, lang);
6464 return res;
6467 /*********************************** GREEK SUPPORT ***************************************/
6471 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
6473 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
6474 int tmp;
6475 int left;
6476 int res;
6477 char fn[256] = "";
6479 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
6480 if (num < 5) {
6481 snprintf(fn, sizeof(fn), "digits/female-%d", num);
6482 res = wait_file(chan, ints, fn, lang);
6483 } else if (num < 13) {
6484 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
6485 } else if (num <100 ) {
6486 tmp = (num/10) * 10;
6487 left = num - tmp;
6488 snprintf(fn, sizeof(fn), "digits/%d", tmp);
6489 res = ast_streamfile(chan, fn, lang);
6490 if (!res)
6491 res = ast_waitstream(chan, ints);
6492 if (left)
6493 gr_say_number_female(left, chan, ints, lang);
6495 } else {
6496 return -1;
6498 return res;
6504 * A list of the files that you need to create
6505 -> digits/xilia = "xilia"
6506 -> digits/myrio = "ekatomyrio"
6507 -> digits/thousands = "xiliades"
6508 -> digits/millions = "ektatomyria"
6509 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6510 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6511 e,g 80 = "ogdonta"
6512 Here we must note that we use digits/tens/100 to utter "ekato"
6513 and digits/hundred-100 to utter "ekaton"
6514 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6515 "terakosia". Here again we use hundreds/1000 for "xilia"
6516 and digits/thousnds for "xiliades"
6519 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
6521 int res = 0;
6522 char fn[256] = "";
6523 int i=0;
6526 if (!num) {
6527 snprintf(fn, sizeof(fn), "digits/0");
6528 res = ast_streamfile(chan, fn, chan->language);
6529 if (!res)
6530 return ast_waitstream(chan, ints);
6533 while (!res && num ) {
6534 i++;
6535 if (num < 13) {
6536 snprintf(fn, sizeof(fn), "digits/%d", num);
6537 num = 0;
6538 } else if (num <= 100) {
6539 /* 13 < num <= 100 */
6540 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
6541 num -= ((num / 10) * 10);
6542 } else if (num < 200) {
6543 /* 100 < num < 200 */
6544 snprintf(fn, sizeof(fn), "digits/hundred-100");
6545 num -= ((num / 100) * 100);
6546 } else if (num < 1000) {
6547 /* 200 < num < 1000 */
6548 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
6549 num -= ((num / 100) * 100);
6550 } else if (num < 2000){
6551 snprintf(fn, sizeof(fn), "digits/xilia");
6552 num -= ((num / 1000) * 1000);
6553 } else {
6554 /* num > 1000 */
6555 if (num < 1000000) {
6556 res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
6557 if (res)
6558 return res;
6559 num = num % 1000;
6560 snprintf(fn, sizeof(fn), "digits/thousands");
6561 } else {
6562 if (num < 1000000000) { /* 1,000,000,000 */
6563 res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
6564 if (res)
6565 return res;
6566 num = num % 1000000;
6567 snprintf(fn, sizeof(fn), "digits/millions");
6568 } else {
6569 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
6570 res = -1;
6574 if (!res) {
6575 if (!ast_streamfile(chan, fn, language)) {
6576 if ((audiofd > -1) && (ctrlfd > -1))
6577 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6578 else
6579 res = ast_waitstream(chan, ints);
6581 ast_stopstream(chan);
6584 return res;
6589 * The format is weekday - day - month -year
6591 * A list of the files that you need to create
6592 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6593 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6594 Attention the months are in
6595 "gekinh klhsh"
6599 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6601 struct tm tm;
6603 char fn[256];
6604 int res = 0;
6607 ast_localtime(&t,&tm,NULL);
6608 /* W E E K - D A Y */
6609 if (!res) {
6610 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6611 res = ast_streamfile(chan, fn, lang);
6612 if (!res)
6613 res = ast_waitstream(chan, ints);
6615 /* D A Y */
6616 if (!res) {
6617 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6619 /* M O N T H */
6620 if (!res) {
6621 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6622 res = ast_streamfile(chan, fn, lang);
6623 if (!res)
6624 res = ast_waitstream(chan, ints);
6626 /* Y E A R */
6627 if (!res)
6628 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6629 return res;
6634 /* A list of the files that you need to create
6635 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6636 * digits/kai : "KAI"
6637 * didgits : "h wra"
6638 * digits/p-m : "meta meshmbrias"
6639 * digits/a-m : "pro meshmbrias"
6642 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6645 struct tm tm;
6646 int res = 0;
6647 int hour, pm=0;
6649 ast_localtime(&t, &tm, NULL);
6650 hour = tm.tm_hour;
6652 if (!hour)
6653 hour = 12;
6654 else if (hour == 12)
6655 pm = 1;
6656 else if (hour > 12) {
6657 hour -= 12;
6658 pm = 1;
6661 res = gr_say_number_female(hour, chan, ints, lang);
6662 if (tm.tm_min) {
6663 if (!res)
6664 res = ast_streamfile(chan, "digits/kai", lang);
6665 if (!res)
6666 res = ast_waitstream(chan, ints);
6667 if (!res)
6668 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6669 } else {
6670 if (!res)
6671 res = ast_streamfile(chan, "digits/hwra", lang);
6672 if (!res)
6673 res = ast_waitstream(chan, ints);
6675 if (pm) {
6676 if (!res)
6677 res = ast_streamfile(chan, "digits/p-m", lang);
6678 } else {
6679 if (!res)
6680 res = ast_streamfile(chan, "digits/a-m", lang);
6682 if (!res)
6683 res = ast_waitstream(chan, ints);
6684 return res;
6689 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6691 struct tm tm;
6692 char fn[256];
6693 int res = 0;
6695 ast_localtime(&t, &tm, NULL);
6698 /* W E E K - D A Y */
6699 if (!res) {
6700 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6701 res = ast_streamfile(chan, fn, lang);
6702 if (!res)
6703 res = ast_waitstream(chan, ints);
6705 /* D A Y */
6706 if (!res) {
6707 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6709 /* M O N T H */
6710 if (!res) {
6711 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6712 res = ast_streamfile(chan, fn, lang);
6713 if (!res)
6714 res = ast_waitstream(chan, ints);
6717 res = ast_say_time_gr(chan, t, ints, lang);
6718 return res;
6721 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
6724 struct tm tm;
6725 int res=0, offset, sndoffset;
6726 char sndfile[256], nextmsg[256];
6728 if (!format)
6729 format = "AdBY 'digits/at' IMp";
6731 ast_localtime(&time,&tm,timezone);
6733 for (offset=0 ; format[offset] != '\0' ; offset++) {
6734 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6735 switch (format[offset]) {
6736 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6737 case '\'':
6738 /* Literal name of a sound file */
6739 sndoffset=0;
6740 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
6741 sndfile[sndoffset] = format[offset];
6742 sndfile[sndoffset] = '\0';
6743 res = wait_file(chan,ints,sndfile,lang);
6744 break;
6745 case 'A':
6746 case 'a':
6747 /* Sunday - Saturday */
6748 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6749 res = wait_file(chan,ints,nextmsg,lang);
6750 break;
6751 case 'B':
6752 case 'b':
6753 case 'h':
6754 /* January - December */
6755 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6756 res = wait_file(chan,ints,nextmsg,lang);
6757 break;
6758 case 'd':
6759 case 'e':
6760 /* first - thirtyfirst */
6761 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6762 break;
6763 case 'Y':
6764 /* Year */
6766 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
6767 break;
6768 case 'I':
6769 case 'l':
6770 /* 12-Hour */
6771 if (tm.tm_hour == 0)
6772 gr_say_number_female(12, chan, ints, lang);
6773 else if (tm.tm_hour > 12)
6774 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
6775 else
6776 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6777 break;
6778 case 'H':
6779 case 'k':
6780 /* 24-Hour */
6781 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6782 break;
6783 case 'M':
6784 /* Minute */
6785 if (tm.tm_min) {
6786 if (!res)
6787 res = ast_streamfile(chan, "digits/kai", lang);
6788 if (!res)
6789 res = ast_waitstream(chan, ints);
6790 if (!res)
6791 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
6792 } else {
6793 if (!res)
6794 res = ast_streamfile(chan, "digits/oclock", lang);
6795 if (!res)
6796 res = ast_waitstream(chan, ints);
6798 break;
6799 case 'P':
6800 case 'p':
6801 /* AM/PM */
6802 if (tm.tm_hour > 11)
6803 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
6804 else
6805 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
6806 res = wait_file(chan,ints,nextmsg,lang);
6807 break;
6808 case 'Q':
6809 /* Shorthand for "Today", "Yesterday", or ABdY */
6810 /* XXX As emphasized elsewhere, this should the native way in your
6811 * language to say the date, with changes in what you say, depending
6812 * upon how recent the date is. XXX */
6814 struct timeval now;
6815 struct tm tmnow;
6816 time_t beg_today, tt;
6818 gettimeofday(&now,NULL);
6819 tt = now.tv_sec;
6820 ast_localtime(&tt,&tmnow,timezone);
6821 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6822 /* In any case, it saves not having to do ast_mktime() */
6823 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6824 if (beg_today < time) {
6825 /* Today */
6826 res = wait_file(chan,ints, "digits/today",lang);
6827 } else if (beg_today - 86400 < time) {
6828 /* Yesterday */
6829 res = wait_file(chan,ints, "digits/yesterday",lang);
6830 } else {
6831 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6834 break;
6835 case 'q':
6836 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6837 /* XXX As emphasized elsewhere, this should the native way in your
6838 * language to say the date, with changes in what you say, depending
6839 * upon how recent the date is. XXX */
6841 struct timeval now;
6842 struct tm tmnow;
6843 time_t beg_today, tt;
6845 gettimeofday(&now,NULL);
6846 tt = now.tv_sec;
6847 ast_localtime(&tt,&tmnow,timezone);
6848 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6849 /* In any case, it saves not having to do ast_mktime() */
6850 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6851 if (beg_today < time) {
6852 /* Today */
6853 } else if ((beg_today - 86400) < time) {
6854 /* Yesterday */
6855 res = wait_file(chan,ints, "digits/yesterday",lang);
6856 } else if (beg_today - 86400 * 6 < time) {
6857 /* Within the last week */
6858 res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
6859 } else {
6860 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6863 break;
6864 case 'R':
6865 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
6866 break;
6867 case 'S':
6868 /* Seconds */
6869 snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
6870 res = wait_file(chan,ints,nextmsg,lang);
6871 if (!res)
6872 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
6873 if (!res)
6874 snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
6875 res = wait_file(chan,ints,nextmsg,lang);
6876 break;
6877 case 'T':
6878 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
6879 break;
6880 case ' ':
6881 case ' ':
6882 /* Just ignore spaces and tabs */
6883 break;
6884 default:
6885 /* Unknown character */
6886 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6888 /* Jump out on DTMF */
6889 if (res) {
6890 break;
6893 return res;
6899 /*********************************** Georgian Support ***************************************/
6903 Convert a number into a semi-localized string. Only for Georgian.
6904 res must be of at least 256 bytes, preallocated.
6905 The output corresponds to Georgian spoken numbers, so
6906 it may be either converted to real words by applying a direct conversion
6907 table, or played just by substituting the entities with played files.
6909 Output may consist of the following tokens (separated by spaces):
6910 0, minus.
6911 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
6912 10-19.
6913 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
6914 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
6915 1000, 1000_. (atasi, atas).
6916 1000000, 1000000_. (milioni, milion).
6917 1000000000, 1000000000_. (miliardi, miliard).
6919 To be able to play the sounds, each of the above tokens needs
6920 a corresponding sound file. (e.g. 200_.gsm).
6922 static char* ast_translate_number_ge(int num, char* res, int res_len)
6924 char buf[256];
6925 int digit = 0;
6926 int remainder = 0;
6929 if (num < 0) {
6930 strncat(res, "minus ", res_len - strlen(res) - 1);
6931 if ( num > INT_MIN ) {
6932 num = -num;
6933 } else {
6934 num = 0;
6939 /* directly read the numbers */
6940 if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
6941 snprintf(buf, sizeof(buf), "%d", num);
6942 strncat(res, buf, res_len - strlen(res) - 1);
6943 return res;
6947 if (num < 40) { /* ocda... */
6948 strncat(res, "20_ ", res_len - strlen(res) - 1);
6949 return ast_translate_number_ge(num - 20, res, res_len);
6952 if (num < 60) { /* ormocda... */
6953 strncat(res, "40_ ", res_len - strlen(res) - 1);
6954 return ast_translate_number_ge(num - 40, res, res_len);
6957 if (num < 80) { /* samocda... */
6958 strncat(res, "60_ ", res_len - strlen(res) - 1);
6959 return ast_translate_number_ge(num - 60, res, res_len);
6962 if (num < 100) { /* otxmocda... */
6963 strncat(res, "80_ ", res_len - strlen(res) - 1);
6964 return ast_translate_number_ge(num - 80, res, res_len);
6968 if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
6969 remainder = num % 100;
6970 digit = (num - remainder) / 100;
6972 if (remainder == 0) {
6973 snprintf(buf, sizeof(buf), "%d", num);
6974 strncat(res, buf, res_len - strlen(res) - 1);
6975 return res;
6976 } else {
6977 snprintf(buf, sizeof(buf), "%d_ ", digit*100);
6978 strncat(res, buf, res_len - strlen(res) - 1);
6979 return ast_translate_number_ge(remainder, res, res_len);
6984 if (num == 1000) {
6985 strncat(res, "1000", res_len - strlen(res) - 1);
6986 return res;
6990 if (num < 1000000) {
6991 remainder = num % 1000;
6992 digit = (num - remainder) / 1000;
6994 if (remainder == 0) {
6995 ast_translate_number_ge(digit, res, res_len);
6996 strncat(res, " 1000", res_len - strlen(res) - 1);
6997 return res;
7000 if (digit == 1) {
7001 strncat(res, "1000_ ", res_len - strlen(res) - 1);
7002 return ast_translate_number_ge(remainder, res, res_len);
7005 ast_translate_number_ge(digit, res, res_len);
7006 strncat(res, " 1000_ ", res_len - strlen(res) - 1);
7007 return ast_translate_number_ge(remainder, res, res_len);
7012 if (num == 1000000) {
7013 strncat(res, "1 1000000", res_len - strlen(res) - 1);
7014 return res;
7018 if (num < 1000000000) {
7019 remainder = num % 1000000;
7020 digit = (num - remainder) / 1000000;
7022 if (remainder == 0) {
7023 ast_translate_number_ge(digit, res, res_len);
7024 strncat(res, " 1000000", res_len - strlen(res) - 1);
7025 return res;
7028 ast_translate_number_ge(digit, res, res_len);
7029 strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
7030 return ast_translate_number_ge(remainder, res, res_len);
7035 if (num == 1000000000) {
7036 strncat(res, "1 1000000000", res_len - strlen(res) - 1);
7037 return res;
7041 if (num > 1000000000) {
7042 remainder = num % 1000000000;
7043 digit = (num - remainder) / 1000000000;
7045 if (remainder == 0) {
7046 ast_translate_number_ge(digit, res, res_len);
7047 strncat(res, " 1000000000", res_len - strlen(res) - 1);
7048 return res;
7051 ast_translate_number_ge(digit, res, res_len);
7052 strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
7053 return ast_translate_number_ge(remainder, res, res_len);
7057 return res;
7063 /*! \brief ast_say_number_full_ge: Georgian syntax */
7064 static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
7066 int res = 0;
7067 char fn[512] = "";
7068 char* s = 0;
7069 const char* remainder = fn;
7071 if (!num)
7072 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
7075 ast_translate_number_ge(num, fn, 512);
7079 while (res == 0 && (s = strstr(remainder, " "))) {
7080 size_t len = s - remainder;
7081 char* new_string = malloc(len + 1 + strlen("digits/"));
7083 sprintf(new_string, "digits/");
7084 strncat(new_string, remainder, len); /* we can't sprintf() it, it's not null-terminated. */
7085 /* new_string[len + strlen("digits/")] = '\0'; */
7087 if (!ast_streamfile(chan, new_string, language)) {
7088 if ((audiofd > -1) && (ctrlfd > -1))
7089 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
7090 else
7091 res = ast_waitstream(chan, ints);
7093 ast_stopstream(chan);
7095 free(new_string);
7097 remainder = s + 1; /* position just after the found space char. */
7098 while (*remainder == ' ') /* skip multiple spaces */
7099 remainder++;
7103 /* the last chunk. */
7104 if (res == 0 && *remainder) {
7106 char* new_string = malloc(strlen(remainder) + 1 + strlen("digits/"));
7107 sprintf(new_string, "digits/%s", remainder);
7109 if (!ast_streamfile(chan, new_string, language)) {
7110 if ((audiofd > -1) && (ctrlfd > -1))
7111 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
7112 else
7113 res = ast_waitstream(chan, ints);
7115 ast_stopstream(chan);
7117 free(new_string);
7122 return res;
7129 Georgian support for date/time requires the following files (*.gsm):
7131 mon-1, mon-2, ... (ianvari, tebervali, ...)
7132 day-1, day-2, ... (orshabati, samshabati, ...)
7133 saati_da
7134 tsuti
7135 tslis
7140 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
7141 static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7143 struct tm tm;
7144 char fn[256];
7145 int res = 0;
7146 ast_localtime(&t,&tm,NULL);
7148 if (!res)
7149 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7151 if (!res) {
7152 snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
7153 res = ast_streamfile(chan, fn, lang);
7154 if (!res)
7155 res = ast_waitstream(chan, ints);
7158 if (!res) {
7159 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
7160 /* if (!res)
7161 res = ast_waitstream(chan, ints);
7165 if (!res) {
7166 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7167 res = ast_streamfile(chan, fn, lang);
7168 if (!res)
7169 res = ast_waitstream(chan, ints);
7171 return res;
7179 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
7180 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7182 struct tm tm;
7183 int res = 0;
7185 ast_localtime(&t, &tm, NULL);
7187 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
7188 if (!res) {
7189 res = ast_streamfile(chan, "digits/saati_da", lang);
7190 if (!res)
7191 res = ast_waitstream(chan, ints);
7194 if (tm.tm_min) {
7195 if (!res) {
7196 res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
7198 if (!res) {
7199 res = ast_streamfile(chan, "digits/tsuti", lang);
7200 if (!res)
7201 res = ast_waitstream(chan, ints);
7205 return res;
7210 /* Georgian syntax. Say date, then say time. */
7211 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7213 struct tm tm;
7214 int res = 0;
7216 ast_localtime(&t, &tm, NULL);
7217 res = ast_say_date(chan, t, ints, lang);
7218 if (!res)
7219 ast_say_time(chan, t, ints, lang);
7220 return res;
7227 /* Georgian syntax */
7228 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7230 int res=0;
7231 time_t nowt;
7232 int daydiff;
7233 struct tm tm;
7234 struct tm now;
7235 char fn[256];
7237 time(&nowt);
7239 ast_localtime(&t, &tm, NULL);
7240 ast_localtime(&nowt, &now, NULL);
7241 daydiff = now.tm_yday - tm.tm_yday;
7242 if ((daydiff < 0) || (daydiff > 6)) {
7243 /* Day of month and month */
7244 if (!res)
7245 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7246 if (!res) {
7247 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7248 res = ast_streamfile(chan, fn, lang);
7249 if (!res)
7250 res = ast_waitstream(chan, ints);
7253 } else if (daydiff) {
7254 /* Just what day of the week */
7255 if (!res) {
7256 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7257 res = ast_streamfile(chan, fn, lang);
7258 if (!res)
7259 res = ast_waitstream(chan, ints);
7261 } /* Otherwise, it was today */
7262 if (!res)
7263 res = ast_say_time(chan, t, ints, lang);
7265 return res;
7271 * remap the 'say' functions to use those in this file
7273 static void __attribute__((constructor)) __say_init(void)
7275 ast_say_number_full = say_number_full;
7276 ast_say_enumeration_full = say_enumeration_full;
7277 ast_say_digit_str_full = say_digit_str_full;
7278 ast_say_character_str_full = say_character_str_full;
7279 ast_say_phonetic_str_full = say_phonetic_str_full;
7280 ast_say_datetime = say_datetime;
7281 ast_say_time = say_time;
7282 ast_say_date = say_date;
7283 ast_say_datetime_from_now = say_datetime_from_now;
7284 ast_say_date_with_format = say_date_with_format;