Fix a typo in the non-DEBUG_THREADS version of the recently added DEADLOCK_AVOIDANCE()
[asterisk-bristuff.git] / main / say.c
blob84002f1bf97a6acdc90c508aab9c27539ed3bc13
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 char fn[256] = "";
2155 if (!num)
2156 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2158 while (!res && (num || playh)) {
2159 if (num < 0) {
2160 snprintf(fn, sizeof(fn), "digits/minus");
2161 if ( num > INT_MIN ) {
2162 num = -num;
2163 } else {
2164 num = 0;
2166 } else if (playh) {
2167 snprintf(fn, sizeof(fn), "digits/hundred");
2168 playh = 0;
2169 } else if (num < 10) {
2170 snprintf(fn, sizeof(fn), "digits/%d", num);
2171 num = 0;
2172 } else if (num < 100) {
2173 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2174 num -= ((num / 10) * 10);
2175 } else {
2176 if (num < 1000){
2177 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2178 playh++;
2179 num -= ((num / 100) * 100);
2180 } else {
2181 if (num < 1000000) { /* 1,000,000 */
2182 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2183 if (res)
2184 return res;
2185 num = num % 1000;
2186 snprintf(fn, sizeof(fn), "digits/thousand");
2187 } else {
2188 if (num < 1000000000) { /* 1,000,000,000 */
2189 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2190 if (res)
2191 return res;
2192 num = num % 1000000;
2193 snprintf(fn, sizeof(fn), "digits/million");
2194 } else {
2195 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2196 res = -1;
2201 if (!res) {
2202 if (!ast_streamfile(chan, fn, language)) {
2203 if ((audiofd > -1) && (ctrlfd > -1))
2204 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2205 else
2206 res = ast_waitstream(chan, ints);
2208 ast_stopstream(chan);
2211 return res;
2215 /*! \brief determine last digits for thousands/millions (ru) */
2216 static int get_lastdigits_ru(int num) {
2217 if (num < 20) {
2218 return num;
2219 } else if (num < 100) {
2220 return get_lastdigits_ru(num % 10);
2221 } else if (num < 1000) {
2222 return get_lastdigits_ru(num % 100);
2224 return 0; /* number too big */
2228 /*! \brief ast_say_number_full_ru: Russian syntax */
2229 /*! \brief additional files:
2230 n00.gsm (one hundred, two hundred, ...)
2231 thousand.gsm
2232 million.gsm
2233 thousands-i.gsm (tisyachi)
2234 million-a.gsm (milliona)
2235 thousands.gsm
2236 millions.gsm
2237 1f.gsm (odna)
2238 2f.gsm (dve)
2240 where 'n' from 1 to 9
2242 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)
2244 int res = 0;
2245 int lastdigits = 0;
2246 char fn[256] = "";
2247 if (!num)
2248 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2250 while (!res && (num)) {
2251 if (num < 0) {
2252 snprintf(fn, sizeof(fn), "digits/minus");
2253 if ( num > INT_MIN ) {
2254 num = -num;
2255 } else {
2256 num = 0;
2258 } else if (num < 20) {
2259 if (options && strlen(options) == 1 && num < 3) {
2260 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2261 } else {
2262 snprintf(fn, sizeof(fn), "digits/%d", num);
2264 num = 0;
2265 } else if (num < 100) {
2266 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2267 num %= 10;
2268 } else if (num < 1000){
2269 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2270 num %= 100;
2271 } else if (num < 1000000) { /* 1,000,000 */
2272 lastdigits = get_lastdigits_ru(num / 1000);
2273 /* say thousands */
2274 if (lastdigits < 3) {
2275 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2276 } else {
2277 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2279 if (res)
2280 return res;
2281 if (lastdigits == 1) {
2282 snprintf(fn, sizeof(fn), "digits/thousand");
2283 } else if (lastdigits > 1 && lastdigits < 5) {
2284 snprintf(fn, sizeof(fn), "digits/thousands-i");
2285 } else {
2286 snprintf(fn, sizeof(fn), "digits/thousands");
2288 num %= 1000;
2289 } else if (num < 1000000000) { /* 1,000,000,000 */
2290 lastdigits = get_lastdigits_ru(num / 1000000);
2291 /* say millions */
2292 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2293 if (res)
2294 return res;
2295 if (lastdigits == 1) {
2296 snprintf(fn, sizeof(fn), "digits/million");
2297 } else if (lastdigits > 1 && lastdigits < 5) {
2298 snprintf(fn, sizeof(fn), "digits/million-a");
2299 } else {
2300 snprintf(fn, sizeof(fn), "digits/millions");
2302 num %= 1000000;
2303 } else {
2304 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2305 res = -1;
2307 if (!res) {
2308 if (!ast_streamfile(chan, fn, language)) {
2309 if ((audiofd > -1) && (ctrlfd > -1))
2310 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2311 else
2312 res = ast_waitstream(chan, ints);
2314 ast_stopstream(chan);
2317 return res;
2321 /*! \brief ast_say_enumeration_full: call language-specific functions */
2322 /* Called from AGI */
2323 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2325 if (!strcasecmp(language,"en") ) { /* English syntax */
2326 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2327 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2328 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2329 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2330 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2331 } else if (!strcasecmp(language, "he")) { /* Hebrew syntax */
2332 return (ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
2335 /* Default to english */
2336 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2339 /*! \brief ast_say_enumeration_full_en: English syntax */
2340 /* This is the default syntax, if no other syntax defined in this file is used */
2341 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2343 int res = 0, t = 0;
2344 char fn[256] = "";
2346 while (!res && num) {
2347 if (num < 0) {
2348 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2349 if ( num > INT_MIN ) {
2350 num = -num;
2351 } else {
2352 num = 0;
2354 } else if (num < 20) {
2355 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2356 num = 0;
2357 } else if (num < 100) {
2358 int tens = num / 10;
2359 num = num % 10;
2360 if (num == 0) {
2361 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2362 } else {
2363 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2365 } else if (num < 1000) {
2366 int hundreds = num / 100;
2367 num = num % 100;
2368 if (hundreds > 1 || t == 1) {
2369 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2371 if (res)
2372 return res;
2373 if (num) {
2374 snprintf(fn, sizeof(fn), "digits/hundred");
2375 } else {
2376 snprintf(fn, sizeof(fn), "digits/h-hundred");
2378 } else if (num < 1000000) {
2379 int thousands = num / 1000;
2380 num = num % 1000;
2381 if (thousands > 1 || t == 1) {
2382 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2384 if (res)
2385 return res;
2386 if (num) {
2387 snprintf(fn, sizeof(fn), "digits/thousand");
2388 } else {
2389 snprintf(fn, sizeof(fn), "digits/h-thousand");
2391 t = 1;
2392 } else if (num < 1000000000) {
2393 int millions = num / 1000000;
2394 num = num % 1000000;
2395 t = 1;
2396 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2397 if (res)
2398 return res;
2399 if (num) {
2400 snprintf(fn, sizeof(fn), "digits/million");
2401 } else {
2402 snprintf(fn, sizeof(fn), "digits/h-million");
2404 } else if (num < INT_MAX) {
2405 int billions = num / 1000000000;
2406 num = num % 1000000000;
2407 t = 1;
2408 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2409 if (res)
2410 return res;
2411 if (num) {
2412 snprintf(fn, sizeof(fn), "digits/billion");
2413 } else {
2414 snprintf(fn, sizeof(fn), "digits/h-billion");
2416 } else if (num == INT_MAX) {
2417 snprintf(fn, sizeof(fn), "digits/h-last");
2418 num = 0;
2419 } else {
2420 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2421 res = -1;
2424 if (!res) {
2425 if (!ast_streamfile(chan, fn, language)) {
2426 if ((audiofd > -1) && (ctrlfd > -1)) {
2427 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2428 } else {
2429 res = ast_waitstream(chan, ints);
2432 ast_stopstream(chan);
2435 return res;
2438 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2439 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)
2441 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2442 int res = 0, t = 0;
2443 char fn[256] = "", fna[256] = "";
2444 char *gender;
2446 if (options && !strncasecmp(options, "f",1)) {
2447 gender = "F";
2448 } else if (options && !strncasecmp(options, "n",1)) {
2449 gender = "N";
2450 } else {
2451 gender = "";
2454 if (!num)
2455 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2457 while (!res && num) {
2458 if (num < 0) {
2459 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2460 if ( num > INT_MIN ) {
2461 num = -num;
2462 } else {
2463 num = 0;
2465 } else if (num < 100 && t) {
2466 snprintf(fn, sizeof(fn), "digits/and");
2467 t = 0;
2468 } else if (num < 20) {
2469 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2470 num = 0;
2471 } else if (num < 100) {
2472 int ones = num % 10;
2473 if (ones) {
2474 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2475 num -= ones;
2476 } else {
2477 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2478 num = 0;
2480 } else if (num == 100 && t == 0) {
2481 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2482 num = 0;
2483 } else if (num < 1000) {
2484 int hundreds = num / 100;
2485 num = num % 100;
2486 if (hundreds == 1) {
2487 snprintf(fn, sizeof(fn), "digits/1N");
2488 } else {
2489 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2491 if (num) {
2492 snprintf(fna, sizeof(fna), "digits/hundred");
2493 } else {
2494 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2496 t = 1;
2497 } else if (num < 1000000) {
2498 int thousands = num / 1000;
2499 num = num % 1000;
2500 if (thousands == 1) {
2501 if (num) {
2502 snprintf(fn, sizeof(fn), "digits/1N");
2503 snprintf(fna, sizeof(fna), "digits/thousand");
2504 } else {
2505 if (t) {
2506 snprintf(fn, sizeof(fn), "digits/1N");
2507 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2508 } else {
2509 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2512 } else {
2513 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2514 if (res) {
2515 return res;
2517 if (num) {
2518 snprintf(fn, sizeof(fn), "digits/thousand");
2519 } else {
2520 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2523 t = 1;
2524 } else if (num < 1000000000) {
2525 int millions = num / 1000000;
2526 num = num % 1000000;
2527 if (millions == 1) {
2528 if (num) {
2529 snprintf(fn, sizeof(fn), "digits/1F");
2530 snprintf(fna, sizeof(fna), "digits/million");
2531 } else {
2532 snprintf(fn, sizeof(fn), "digits/1N");
2533 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2535 } else {
2536 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2537 if (res) {
2538 return res;
2540 if (num) {
2541 snprintf(fn, sizeof(fn), "digits/millions");
2542 } else {
2543 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2546 t = 1;
2547 } else if (num < INT_MAX) {
2548 int billions = num / 1000000000;
2549 num = num % 1000000000;
2550 if (billions == 1) {
2551 if (num) {
2552 snprintf(fn, sizeof(fn), "digits/1F");
2553 snprintf(fna, sizeof(fna), "digits/milliard");
2554 } else {
2555 snprintf(fn, sizeof(fn), "digits/1N");
2556 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2558 } else {
2559 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2560 if (res)
2561 return res;
2562 if (num) {
2563 snprintf(fn, sizeof(fna), "digits/milliards");
2564 } else {
2565 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2568 t = 1;
2569 } else if (num == INT_MAX) {
2570 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2571 num = 0;
2572 } else {
2573 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2574 res = -1;
2577 if (!res) {
2578 if (!ast_streamfile(chan, fn, language)) {
2579 if ((audiofd > -1) && (ctrlfd > -1))
2580 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2581 else
2582 res = ast_waitstream(chan, ints);
2584 ast_stopstream(chan);
2585 if (!res) {
2586 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2587 if ((audiofd > -1) && (ctrlfd > -1)) {
2588 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2589 } else {
2590 res = ast_waitstream(chan, ints);
2593 ast_stopstream(chan);
2594 strcpy(fna, "");
2598 return res;
2601 /*! \brief ast_say_enumeration_full_de: German syntax */
2602 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)
2604 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2605 int res = 0, t = 0;
2606 char fn[256] = "", fna[256] = "";
2607 char *gender;
2609 if (options && !strncasecmp(options, "f",1)) {
2610 gender = "F";
2611 } else if (options && !strncasecmp(options, "n",1)) {
2612 gender = "N";
2613 } else {
2614 gender = "";
2617 if (!num)
2618 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2620 while (!res && num) {
2621 if (num < 0) {
2622 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2623 if ( num > INT_MIN ) {
2624 num = -num;
2625 } else {
2626 num = 0;
2628 } else if (num < 100 && t) {
2629 snprintf(fn, sizeof(fn), "digits/and");
2630 t = 0;
2631 } else if (num < 20) {
2632 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2633 num = 0;
2634 } else if (num < 100) {
2635 int ones = num % 10;
2636 if (ones) {
2637 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2638 num -= ones;
2639 } else {
2640 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2641 num = 0;
2643 } else if (num == 100 && t == 0) {
2644 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2645 num = 0;
2646 } else if (num < 1000) {
2647 int hundreds = num / 100;
2648 num = num % 100;
2649 if (hundreds == 1) {
2650 snprintf(fn, sizeof(fn), "digits/1N");
2651 } else {
2652 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2654 if (num) {
2655 snprintf(fna, sizeof(fna), "digits/hundred");
2656 } else {
2657 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2659 t = 1;
2660 } else if (num < 1000000) {
2661 int thousands = num / 1000;
2662 num = num % 1000;
2663 if (thousands == 1) {
2664 if (num) {
2665 snprintf(fn, sizeof(fn), "digits/1N");
2666 snprintf(fna, sizeof(fna), "digits/thousand");
2667 } else {
2668 if (t) {
2669 snprintf(fn, sizeof(fn), "digits/1N");
2670 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2671 } else {
2672 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2675 } else {
2676 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2677 if (res) {
2678 return res;
2680 if (num) {
2681 snprintf(fn, sizeof(fn), "digits/thousand");
2682 } else {
2683 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2686 t = 1;
2687 } else if (num < 1000000000) {
2688 int millions = num / 1000000;
2689 num = num % 1000000;
2690 if (millions == 1) {
2691 if (num) {
2692 snprintf(fn, sizeof(fn), "digits/1F");
2693 snprintf(fna, sizeof(fna), "digits/million");
2694 } else {
2695 snprintf(fn, sizeof(fn), "digits/1N");
2696 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2698 } else {
2699 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2700 if (res) {
2701 return res;
2703 if (num) {
2704 snprintf(fn, sizeof(fn), "digits/millions");
2705 } else {
2706 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2709 t = 1;
2710 } else if (num < INT_MAX) {
2711 int billions = num / 1000000000;
2712 num = num % 1000000000;
2713 if (billions == 1) {
2714 if (num) {
2715 snprintf(fn, sizeof(fn), "digits/1F");
2716 snprintf(fna, sizeof(fna), "digits/milliard");
2717 } else {
2718 snprintf(fn, sizeof(fn), "digits/1N");
2719 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2721 } else {
2722 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2723 if (res)
2724 return res;
2725 if (num) {
2726 snprintf(fn, sizeof(fna), "digits/milliards");
2727 } else {
2728 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2731 t = 1;
2732 } else if (num == INT_MAX) {
2733 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2734 num = 0;
2735 } else {
2736 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2737 res = -1;
2740 if (!res) {
2741 if (!ast_streamfile(chan, fn, language)) {
2742 if ((audiofd > -1) && (ctrlfd > -1))
2743 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2744 else
2745 res = ast_waitstream(chan, ints);
2747 ast_stopstream(chan);
2748 if (!res) {
2749 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2750 if ((audiofd > -1) && (ctrlfd > -1)) {
2751 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2752 } else {
2753 res = ast_waitstream(chan, ints);
2756 ast_stopstream(chan);
2757 strcpy(fna, "");
2761 return res;
2764 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)
2766 int res = 0;
2767 char fn[256] = "";
2768 int mf = -1; /* +1 = Masculin; -1 = Feminin */
2769 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
2771 if (options && !strncasecmp(options, "m", 1)) {
2772 mf = -1;
2775 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
2777 while (!res && num) {
2778 if (num < 0) {
2779 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2780 if (num > INT_MIN) {
2781 num = -num;
2782 } else {
2783 num = 0;
2785 } else if (num < 21) {
2786 if (mf < 0) {
2787 if (num < 10) {
2788 snprintf(fn, sizeof(fn), "digits/f-0%d", num);
2789 } else {
2790 snprintf(fn, sizeof(fn), "digits/f-%d", num);
2792 } else {
2793 if (num < 10) {
2794 snprintf(fn, sizeof(fn), "digits/m-0%d", num);
2795 } else {
2796 snprintf(fn, sizeof(fn), "digits/m-%d", num);
2799 num = 0;
2800 } else if ((num < 100) && num >= 20) {
2801 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2802 num = num % 10;
2803 } else if ((num >= 100) && (num < 1000)) {
2804 int tmpnum = num / 100;
2805 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
2806 num = num - (tmpnum * 100);
2807 } else if ((num >= 1000) && (num < 10000)) {
2808 int tmpnum = num / 1000;
2809 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
2810 num = num - (tmpnum * 1000);
2811 } else if (num < 20000) {
2812 snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
2813 num = num % 1000;
2814 } else if (num < 1000000) {
2815 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
2816 if (res) {
2817 return res;
2819 snprintf(fn, sizeof(fn), "digits/1k");
2820 num = num % 1000;
2821 } else if (num < 2000000) {
2822 snprintf(fn, sizeof(fn), "digits/1m");
2823 num = num % 1000000;
2824 } else if (num < 3000000) {
2825 snprintf(fn, sizeof(fn), "digits/2m");
2826 num = num - 2000000;
2827 } else if (num < 1000000000) {
2828 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
2829 if (res) {
2830 return res;
2832 snprintf(fn, sizeof(fn), "digits/1m");
2833 num = num % 1000000;
2834 } else {
2835 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2836 res = -1;
2838 if (!res) {
2839 if (!ast_streamfile(chan, fn, language)) {
2840 if ((audiofd > -1) && (ctrlfd > -1)) {
2841 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2842 } else {
2843 res = ast_waitstream(chan, ints);
2846 ast_stopstream(chan);
2849 return res;
2852 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2854 if (!strcasecmp(lang, "en") ) { /* English syntax */
2855 return(ast_say_date_en(chan, t, ints, lang));
2856 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2857 return(ast_say_date_da(chan, t, ints, lang));
2858 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2859 return(ast_say_date_de(chan, t, ints, lang));
2860 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2861 return(ast_say_date_fr(chan, t, ints, lang));
2862 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2863 return(ast_say_date_nl(chan, t, ints, lang));
2864 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2865 return(ast_say_date_pt(chan, t, ints, lang));
2866 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2867 return(ast_say_date_gr(chan, t, ints, lang));
2868 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
2869 return(ast_say_date_ge(chan, t, ints, lang));
2870 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2871 return (ast_say_date_he(chan, t, ints, lang));
2874 /* Default to English */
2875 return(ast_say_date_en(chan, t, ints, lang));
2878 /* English syntax */
2879 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2881 struct tm tm;
2882 char fn[256];
2883 int res = 0;
2884 ast_localtime(&t,&tm,NULL);
2885 if (!res) {
2886 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2887 res = ast_streamfile(chan, fn, lang);
2888 if (!res)
2889 res = ast_waitstream(chan, ints);
2891 if (!res) {
2892 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2893 res = ast_streamfile(chan, fn, lang);
2894 if (!res)
2895 res = ast_waitstream(chan, ints);
2897 if (!res)
2898 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2899 if (!res)
2900 res = ast_waitstream(chan, ints);
2901 if (!res)
2902 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2903 return res;
2906 /* Danish syntax */
2907 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2909 struct tm tm;
2910 char fn[256];
2911 int res = 0;
2912 ast_localtime(&t,&tm,NULL);
2913 if (!res) {
2914 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2915 res = ast_streamfile(chan, fn, lang);
2916 if (!res)
2917 res = ast_waitstream(chan, ints);
2919 if (!res)
2920 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2921 if (!res)
2922 res = ast_waitstream(chan, ints);
2923 if (!res) {
2924 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2925 res = ast_streamfile(chan, fn, lang);
2926 if (!res)
2927 res = ast_waitstream(chan, ints);
2929 if (!res) {
2930 /* Year */
2931 int year = tm.tm_year + 1900;
2932 if (year > 1999) { /* year 2000 and later */
2933 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2934 } else {
2935 if (year < 1100) {
2936 /* I'm not going to handle 1100 and prior */
2937 /* We'll just be silent on the year, instead of bombing out. */
2938 } else {
2939 /* year 1100 to 1999. will anybody need this?!? */
2940 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2941 res = wait_file(chan, ints, fn, lang);
2942 if (!res) {
2943 res = wait_file(chan,ints, "digits/hundred", lang);
2944 if (!res && year % 100 != 0) {
2945 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2951 return res;
2954 /* German syntax */
2955 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2957 struct tm tm;
2958 char fn[256];
2959 int res = 0;
2960 ast_localtime(&t,&tm,NULL);
2961 if (!res) {
2962 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2963 res = ast_streamfile(chan, fn, lang);
2964 if (!res)
2965 res = ast_waitstream(chan, ints);
2967 if (!res)
2968 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2969 if (!res)
2970 res = ast_waitstream(chan, ints);
2971 if (!res) {
2972 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2973 res = ast_streamfile(chan, fn, lang);
2974 if (!res)
2975 res = ast_waitstream(chan, ints);
2977 if (!res) {
2978 /* Year */
2979 int year = tm.tm_year + 1900;
2980 if (year > 1999) { /* year 2000 and later */
2981 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2982 } else {
2983 if (year < 1100) {
2984 /* I'm not going to handle 1100 and prior */
2985 /* We'll just be silent on the year, instead of bombing out. */
2986 } else {
2987 /* year 1100 to 1999. will anybody need this?!? */
2988 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2989 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2990 res = wait_file(chan, ints, fn, lang);
2991 if (!res) {
2992 res = wait_file(chan,ints, "digits/hundred", lang);
2993 if (!res && year % 100 != 0) {
2994 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3000 return res;
3003 /* French syntax */
3004 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3006 struct tm tm;
3007 char fn[256];
3008 int res = 0;
3009 ast_localtime(&t,&tm,NULL);
3010 if (!res) {
3011 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3012 res = ast_streamfile(chan, fn, lang);
3013 if (!res)
3014 res = ast_waitstream(chan, ints);
3016 if (!res)
3017 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3018 if (!res)
3019 res = ast_waitstream(chan, ints);
3020 if (!res) {
3021 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3022 res = ast_streamfile(chan, fn, lang);
3023 if (!res)
3024 res = ast_waitstream(chan, ints);
3026 if (!res)
3027 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3028 return res;
3031 /* Dutch syntax */
3032 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3034 struct tm tm;
3035 char fn[256];
3036 int res = 0;
3037 ast_localtime(&t,&tm,NULL);
3038 if (!res) {
3039 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3040 res = ast_streamfile(chan, fn, lang);
3041 if (!res)
3042 res = ast_waitstream(chan, ints);
3044 if (!res)
3045 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3046 if (!res) {
3047 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3048 res = ast_streamfile(chan, fn, lang);
3049 if (!res)
3050 res = ast_waitstream(chan, ints);
3052 if (!res)
3053 res = ast_waitstream(chan, ints);
3054 if (!res)
3055 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3056 return res;
3059 /* Portuguese syntax */
3060 int ast_say_date_pt(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;
3066 ast_localtime(&t, &tm, NULL);
3067 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3068 if (!res)
3069 res = wait_file(chan, ints, fn, lang);
3070 if (!res)
3071 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
3072 if (!res)
3073 res = wait_file(chan, ints, "digits/pt-de", lang);
3074 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3075 if (!res)
3076 res = wait_file(chan, ints, fn, lang);
3077 if (!res)
3078 res = wait_file(chan, ints, "digits/pt-de", lang);
3079 if (!res)
3080 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3082 return res;
3085 /* Hebrew syntax */
3086 int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3088 struct tm tm;
3089 char fn[256];
3090 int res = 0;
3091 ast_localtime(&t, &tm, NULL);
3092 if (!res) {
3093 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3094 res = ast_streamfile(chan, fn, lang);
3095 if (!res) {
3096 res = ast_waitstream(chan, ints);
3099 if (!res) {
3100 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3101 res = ast_streamfile(chan, fn, lang);
3102 if (!res) {
3103 res = ast_waitstream(chan, ints);
3106 if (!res) {
3107 res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
3109 if (!res) {
3110 res = ast_waitstream(chan, ints);
3112 if (!res) {
3113 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
3115 return res;
3118 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)
3120 if (!strcasecmp(lang, "en") ) { /* English syntax */
3121 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
3122 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
3123 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
3124 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
3125 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
3126 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
3127 return (ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
3128 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
3129 return (ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
3130 } else if (!strcasecmp(lang, "fr")) { /* French syntax */
3131 return (ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
3132 } else if (!strcasecmp(lang, "it")) { /* Italian syntax */
3133 return (ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
3134 } else if (!strcasecmp(lang, "nl")) { /* Dutch syntax */
3135 return (ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
3136 } else if (!strcasecmp(lang, "pl")) { /* Polish syntax */
3137 return (ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
3138 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
3139 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
3140 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
3141 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
3142 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
3143 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
3146 /* Default to English */
3147 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
3150 /* English syntax */
3151 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)
3153 struct tm tm;
3154 int res=0, offset, sndoffset;
3155 char sndfile[256], nextmsg[256];
3157 if (format == NULL)
3158 format = "ABdY 'digits/at' IMp";
3160 ast_localtime(&time,&tm,timezone);
3162 for (offset=0 ; format[offset] != '\0' ; offset++) {
3163 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3164 switch (format[offset]) {
3165 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3166 case '\'':
3167 /* Literal name of a sound file */
3168 sndoffset=0;
3169 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3170 sndfile[sndoffset] = format[offset];
3171 sndfile[sndoffset] = '\0';
3172 res = wait_file(chan,ints,sndfile,lang);
3173 break;
3174 case 'A':
3175 case 'a':
3176 /* Sunday - Saturday */
3177 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3178 res = wait_file(chan,ints,nextmsg,lang);
3179 break;
3180 case 'B':
3181 case 'b':
3182 case 'h':
3183 /* January - December */
3184 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3185 res = wait_file(chan,ints,nextmsg,lang);
3186 break;
3187 case 'm':
3188 /* Month enumerated */
3189 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
3190 break;
3191 case 'd':
3192 case 'e':
3193 /* First - Thirtyfirst */
3194 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
3195 break;
3196 case 'Y':
3197 /* Year */
3198 if (tm.tm_year > 99) {
3199 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3200 } else if (tm.tm_year < 1) {
3201 /* I'm not going to handle 1900 and prior */
3202 /* We'll just be silent on the year, instead of bombing out. */
3203 } else {
3204 res = wait_file(chan, ints, "digits/19", lang);
3205 if (!res) {
3206 if (tm.tm_year <= 9) {
3207 /* 1901 - 1909 */
3208 res = wait_file(chan,ints, "digits/oh", lang);
3211 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3214 break;
3215 case 'I':
3216 case 'l':
3217 /* 12-Hour */
3218 if (tm.tm_hour == 0)
3219 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3220 else if (tm.tm_hour > 12)
3221 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3222 else
3223 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3224 res = wait_file(chan,ints,nextmsg,lang);
3225 break;
3226 case 'H':
3227 case 'k':
3228 /* 24-Hour */
3229 if (format[offset] == 'H') {
3230 /* e.g. oh-eight */
3231 if (tm.tm_hour < 10) {
3232 res = wait_file(chan,ints, "digits/oh",lang);
3234 } else {
3235 /* e.g. eight */
3236 if (tm.tm_hour == 0) {
3237 res = wait_file(chan,ints, "digits/oh",lang);
3240 if (!res) {
3241 if (tm.tm_hour != 0) {
3242 int remainder = tm.tm_hour;
3243 if (tm.tm_hour > 20) {
3244 res = wait_file(chan,ints, "digits/20",lang);
3245 remainder -= 20;
3247 if (!res) {
3248 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3249 res = wait_file(chan,ints,nextmsg,lang);
3253 break;
3254 case 'M':
3255 case 'N':
3256 /* Minute */
3257 if (tm.tm_min == 0) {
3258 if (format[offset] == 'M') {
3259 res = wait_file(chan, ints, "digits/oclock", lang);
3260 } else {
3261 res = wait_file(chan, ints, "digits/hundred", lang);
3263 } else if (tm.tm_min < 10) {
3264 res = wait_file(chan,ints, "digits/oh",lang);
3265 if (!res) {
3266 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3267 res = wait_file(chan,ints,nextmsg,lang);
3269 } else {
3270 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3272 break;
3273 case 'P':
3274 case 'p':
3275 /* AM/PM */
3276 if (tm.tm_hour > 11)
3277 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3278 else
3279 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3280 res = wait_file(chan,ints,nextmsg,lang);
3281 break;
3282 case 'Q':
3283 /* Shorthand for "Today", "Yesterday", or ABdY */
3284 /* XXX As emphasized elsewhere, this should the native way in your
3285 * language to say the date, with changes in what you say, depending
3286 * upon how recent the date is. XXX */
3288 struct timeval now;
3289 struct tm tmnow;
3290 time_t beg_today, tt;
3292 gettimeofday(&now,NULL);
3293 tt = now.tv_sec;
3294 ast_localtime(&tt,&tmnow,timezone);
3295 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3296 /* In any case, it saves not having to do ast_mktime() */
3297 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3298 if (beg_today < time) {
3299 /* Today */
3300 res = wait_file(chan,ints, "digits/today",lang);
3301 } else if (beg_today - 86400 < time) {
3302 /* Yesterday */
3303 res = wait_file(chan,ints, "digits/yesterday",lang);
3304 } else if (beg_today - 86400 * 6 < time) {
3305 /* Within the last week */
3306 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3307 } else if (beg_today - 2628000 < time) {
3308 /* Less than a month ago - "Sunday, October third" */
3309 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3310 } else if (beg_today - 15768000 < time) {
3311 /* Less than 6 months ago - "August seventh" */
3312 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3313 } else {
3314 /* More than 6 months ago - "April nineteenth two thousand three" */
3315 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3318 break;
3319 case 'q':
3320 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3321 /* XXX As emphasized elsewhere, this should the native way in your
3322 * language to say the date, with changes in what you say, depending
3323 * upon how recent the date is. XXX */
3325 struct timeval now;
3326 struct tm tmnow;
3327 time_t beg_today, tt;
3329 gettimeofday(&now,NULL);
3330 tt = now.tv_sec;
3331 ast_localtime(&tt,&tmnow,timezone);
3332 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3333 /* In any case, it saves not having to do ast_mktime() */
3334 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3335 if (beg_today < time) {
3336 /* Today */
3337 } else if ((beg_today - 86400) < time) {
3338 /* Yesterday */
3339 res = wait_file(chan,ints, "digits/yesterday",lang);
3340 } else if (beg_today - 86400 * 6 < time) {
3341 /* Within the last week */
3342 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3343 } else if (beg_today - 2628000 < time) {
3344 /* Less than a month ago - "Sunday, October third" */
3345 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3346 } else if (beg_today - 15768000 < time) {
3347 /* Less than 6 months ago - "August seventh" */
3348 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3349 } else {
3350 /* More than 6 months ago - "April nineteenth two thousand three" */
3351 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3354 break;
3355 case 'R':
3356 res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
3357 break;
3358 case 'S':
3359 /* Seconds */
3360 if (tm.tm_sec == 0) {
3361 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3362 res = wait_file(chan,ints,nextmsg,lang);
3363 } else if (tm.tm_sec < 10) {
3364 res = wait_file(chan,ints, "digits/oh",lang);
3365 if (!res) {
3366 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3367 res = wait_file(chan,ints,nextmsg,lang);
3369 } else {
3370 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
3372 break;
3373 case 'T':
3374 res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
3375 break;
3376 case ' ':
3377 case ' ':
3378 /* Just ignore spaces and tabs */
3379 break;
3380 default:
3381 /* Unknown character */
3382 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3384 /* Jump out on DTMF */
3385 if (res) {
3386 break;
3389 return res;
3392 /* Danish syntax */
3393 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)
3395 struct tm tm;
3396 int res=0, offset, sndoffset;
3397 char sndfile[256], nextmsg[256];
3399 if (!format)
3400 format = "A dBY HMS";
3402 ast_localtime(&time,&tm,timezone);
3404 for (offset=0 ; format[offset] != '\0' ; offset++) {
3405 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3406 switch (format[offset]) {
3407 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3408 case '\'':
3409 /* Literal name of a sound file */
3410 sndoffset=0;
3411 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3412 sndfile[sndoffset] = format[offset];
3413 sndfile[sndoffset] = '\0';
3414 res = wait_file(chan,ints,sndfile,lang);
3415 break;
3416 case 'A':
3417 case 'a':
3418 /* Sunday - Saturday */
3419 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3420 res = wait_file(chan,ints,nextmsg,lang);
3421 break;
3422 case 'B':
3423 case 'b':
3424 case 'h':
3425 /* January - December */
3426 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3427 res = wait_file(chan,ints,nextmsg,lang);
3428 break;
3429 case 'm':
3430 /* Month enumerated */
3431 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3432 break;
3433 case 'd':
3434 case 'e':
3435 /* First - Thirtyfirst */
3436 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3437 break;
3438 case 'Y':
3439 /* Year */
3441 int year = tm.tm_year + 1900;
3442 if (year > 1999) { /* year 2000 and later */
3443 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3444 } else {
3445 if (year < 1100) {
3446 /* I'm not going to handle 1100 and prior */
3447 /* We'll just be silent on the year, instead of bombing out. */
3448 } else {
3449 /* year 1100 to 1999. will anybody need this?!? */
3450 /* say 1967 as 'nineteen hundred seven and sixty' */
3451 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3452 res = wait_file(chan,ints,nextmsg,lang);
3453 if (!res) {
3454 res = wait_file(chan,ints, "digits/hundred",lang);
3455 if (!res && year % 100 != 0) {
3456 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3462 break;
3463 case 'I':
3464 case 'l':
3465 /* 12-Hour */
3466 res = wait_file(chan,ints,"digits/oclock",lang);
3467 if (tm.tm_hour == 0)
3468 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3469 else if (tm.tm_hour > 12)
3470 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3471 else
3472 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3473 if (!res) {
3474 res = wait_file(chan,ints,nextmsg,lang);
3476 break;
3477 case 'H':
3478 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3479 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
3480 res = wait_file(chan,ints, "digits/0",lang);
3482 /* FALLTRHU */
3483 case 'k':
3484 /* 24-Hour */
3485 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3486 break;
3487 case 'M':
3488 /* Minute */
3489 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3490 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3492 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3493 if (tm.tm_min == 1) {
3494 res = wait_file(chan,ints,"digits/minute",lang);
3495 } else {
3496 res = wait_file(chan,ints,"digits/minutes",lang);
3499 break;
3500 case 'P':
3501 case 'p':
3502 /* AM/PM */
3503 if (tm.tm_hour > 11)
3504 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3505 else
3506 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3507 res = wait_file(chan,ints,nextmsg,lang);
3508 break;
3509 case 'Q':
3510 /* Shorthand for "Today", "Yesterday", or AdBY */
3511 /* XXX As emphasized elsewhere, this should the native way in your
3512 * language to say the date, with changes in what you say, depending
3513 * upon how recent the date is. XXX */
3515 struct timeval now;
3516 struct tm tmnow;
3517 time_t beg_today, tt;
3519 gettimeofday(&now,NULL);
3520 tt = now.tv_sec;
3521 ast_localtime(&tt,&tmnow,timezone);
3522 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3523 /* In any case, it saves not having to do ast_mktime() */
3524 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3525 if (beg_today < time) {
3526 /* Today */
3527 res = wait_file(chan,ints, "digits/today",lang);
3528 } else if (beg_today - 86400 < time) {
3529 /* Yesterday */
3530 res = wait_file(chan,ints, "digits/yesterday",lang);
3531 } else {
3532 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3535 break;
3536 case 'q':
3537 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3538 /* XXX As emphasized elsewhere, this should the native way in your
3539 * language to say the date, with changes in what you say, depending
3540 * upon how recent the date is. XXX */
3542 struct timeval now;
3543 struct tm tmnow;
3544 time_t beg_today, tt;
3546 gettimeofday(&now,NULL);
3547 tt = now.tv_sec;
3548 ast_localtime(&tt,&tmnow,timezone);
3549 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3550 /* In any case, it saves not having to do ast_mktime() */
3551 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3552 if (beg_today < time) {
3553 /* Today */
3554 } else if ((beg_today - 86400) < time) {
3555 /* Yesterday */
3556 res = wait_file(chan,ints, "digits/yesterday",lang);
3557 } else if (beg_today - 86400 * 6 < time) {
3558 /* Within the last week */
3559 res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
3560 } else {
3561 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3564 break;
3565 case 'R':
3566 res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
3567 break;
3568 case 'S':
3569 /* Seconds */
3570 res = wait_file(chan,ints, "digits/and",lang);
3571 if (!res) {
3572 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3573 if (!res) {
3574 res = wait_file(chan,ints, "digits/seconds",lang);
3577 break;
3578 case 'T':
3579 res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
3580 break;
3581 case ' ':
3582 case ' ':
3583 /* Just ignore spaces and tabs */
3584 break;
3585 default:
3586 /* Unknown character */
3587 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3589 /* Jump out on DTMF */
3590 if (res) {
3591 break;
3594 return res;
3597 /* German syntax */
3598 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)
3600 struct tm tm;
3601 int res=0, offset, sndoffset;
3602 char sndfile[256], nextmsg[256];
3604 if (!format)
3605 format = "A dBY HMS";
3607 ast_localtime(&time,&tm,timezone);
3609 for (offset=0 ; format[offset] != '\0' ; offset++) {
3610 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3611 switch (format[offset]) {
3612 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3613 case '\'':
3614 /* Literal name of a sound file */
3615 sndoffset=0;
3616 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3617 sndfile[sndoffset] = format[offset];
3618 sndfile[sndoffset] = '\0';
3619 res = wait_file(chan,ints,sndfile,lang);
3620 break;
3621 case 'A':
3622 case 'a':
3623 /* Sunday - Saturday */
3624 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3625 res = wait_file(chan,ints,nextmsg,lang);
3626 break;
3627 case 'B':
3628 case 'b':
3629 case 'h':
3630 /* January - December */
3631 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3632 res = wait_file(chan,ints,nextmsg,lang);
3633 break;
3634 case 'm':
3635 /* Month enumerated */
3636 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3637 break;
3638 case 'd':
3639 case 'e':
3640 /* First - Thirtyfirst */
3641 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3642 break;
3643 case 'Y':
3644 /* Year */
3646 int year = tm.tm_year + 1900;
3647 if (year > 1999) { /* year 2000 and later */
3648 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3649 } else {
3650 if (year < 1100) {
3651 /* I'm not going to handle 1100 and prior */
3652 /* We'll just be silent on the year, instead of bombing out. */
3653 } else {
3654 /* year 1100 to 1999. will anybody need this?!? */
3655 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3656 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3657 res = wait_file(chan,ints,nextmsg,lang);
3658 if (!res) {
3659 res = wait_file(chan,ints, "digits/hundred",lang);
3660 if (!res && year % 100 != 0) {
3661 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3667 break;
3668 case 'I':
3669 case 'l':
3670 /* 12-Hour */
3671 if (tm.tm_hour == 0)
3672 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3673 else if (tm.tm_hour > 12)
3674 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3675 else
3676 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3677 res = wait_file(chan,ints,nextmsg,lang);
3678 if (!res) {
3679 res = wait_file(chan,ints,"digits/oclock",lang);
3681 break;
3682 case 'H':
3683 case 'k':
3684 /* 24-Hour */
3685 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3686 if (!res) {
3687 res = wait_file(chan,ints,"digits/oclock",lang);
3689 break;
3690 case 'M':
3691 /* Minute */
3692 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3693 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3695 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3696 if (tm.tm_min == 1) {
3697 res = wait_file(chan,ints,"digits/minute",lang);
3698 } else {
3699 res = wait_file(chan,ints,"digits/minutes",lang);
3702 break;
3703 case 'P':
3704 case 'p':
3705 /* AM/PM */
3706 if (tm.tm_hour > 11)
3707 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3708 else
3709 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3710 res = wait_file(chan,ints,nextmsg,lang);
3711 break;
3712 case 'Q':
3713 /* Shorthand for "Today", "Yesterday", or AdBY */
3714 /* XXX As emphasized elsewhere, this should the native way in your
3715 * language to say the date, with changes in what you say, depending
3716 * upon how recent the date is. XXX */
3718 struct timeval now;
3719 struct tm tmnow;
3720 time_t beg_today, tt;
3722 gettimeofday(&now,NULL);
3723 tt = now.tv_sec;
3724 ast_localtime(&tt,&tmnow,timezone);
3725 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3726 /* In any case, it saves not having to do ast_mktime() */
3727 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3728 if (beg_today < time) {
3729 /* Today */
3730 res = wait_file(chan,ints, "digits/today",lang);
3731 } else if (beg_today - 86400 < time) {
3732 /* Yesterday */
3733 res = wait_file(chan,ints, "digits/yesterday",lang);
3734 } else {
3735 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3738 break;
3739 case 'q':
3740 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3741 /* XXX As emphasized elsewhere, this should the native way in your
3742 * language to say the date, with changes in what you say, depending
3743 * upon how recent the date is. XXX */
3745 struct timeval now;
3746 struct tm tmnow;
3747 time_t beg_today, tt;
3749 gettimeofday(&now,NULL);
3750 tt = now.tv_sec;
3751 ast_localtime(&tt,&tmnow,timezone);
3752 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3753 /* In any case, it saves not having to do ast_mktime() */
3754 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3755 if (beg_today < time) {
3756 /* Today */
3757 } else if ((beg_today - 86400) < time) {
3758 /* Yesterday */
3759 res = wait_file(chan,ints, "digits/yesterday",lang);
3760 } else if (beg_today - 86400 * 6 < time) {
3761 /* Within the last week */
3762 res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
3763 } else {
3764 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3767 break;
3768 case 'R':
3769 res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
3770 break;
3771 case 'S':
3772 /* Seconds */
3773 res = wait_file(chan,ints, "digits/and",lang);
3774 if (!res) {
3775 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3776 if (!res) {
3777 res = wait_file(chan,ints, "digits/seconds",lang);
3780 break;
3781 case 'T':
3782 res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
3783 break;
3784 case ' ':
3785 case ' ':
3786 /* Just ignore spaces and tabs */
3787 break;
3788 default:
3789 /* Unknown character */
3790 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3792 /* Jump out on DTMF */
3793 if (res) {
3794 break;
3797 return res;
3800 /* TODO: this probably is not the correct format for doxygen remarks */
3802 /** ast_say_date_with_format_he Say formmated date in Hebrew
3804 * \ref ast_say_date_with_format_en for the details of the options
3806 * Changes from the English version:
3808 * * don't replicate in here the logic of ast_say_number_full_he
3810 * * year is always 4-digit (because it's simpler)
3812 * * added c, x, and X. Mainly for my tests
3814 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3816 * TODO:
3817 * * A "ha" is missing in the standard date format, before the 'd'.
3818 * * The numbers of 3000--19000 are not handled well
3820 #define IL_DATE_STR "AdBY"
3821 #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 */
3822 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3823 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)
3825 /* TODO: This whole function is cut&paste from
3826 * ast_say_date_with_format_en . Is that considered acceptable?
3828 struct tm tm;
3829 int res = 0, offset, sndoffset;
3830 char sndfile[256], nextmsg[256];
3832 if (!format) {
3833 format = IL_DATE_STR_FULL;
3836 ast_localtime(&time, &tm, timezone);
3838 for (offset = 0; format[offset] != '\0'; offset++) {
3839 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3840 switch (format[offset]) {
3841 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3842 case '\'':
3843 /* Literal name of a sound file */
3844 sndoffset=0;
3845 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3846 sndfile[sndoffset] = format[offset];
3847 sndfile[sndoffset] = '\0';
3848 res = wait_file(chan,ints,sndfile,lang);
3849 break;
3850 case 'A':
3851 case 'a':
3852 /* Sunday - Saturday */
3853 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3854 res = wait_file(chan,ints,nextmsg,lang);
3855 break;
3856 case 'B':
3857 case 'b':
3858 case 'h':
3859 /* January - December */
3860 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3861 res = wait_file(chan,ints,nextmsg,lang);
3862 break;
3863 case 'd':
3864 case 'e': /* Day of the month */
3865 /* I'm not sure exactly what the parameters
3866 * audiofd and ctrlfd to
3867 * ast_say_number_full_he mean, but it seems
3868 * safe to pass -1 there.
3870 * At least in one of the pathes :-(
3872 res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
3873 break;
3874 case 'Y': /* Year */
3875 res = ast_say_number_full_he(chan, tm.tm_year+1900,
3876 ints, lang, "f", -1, -1
3878 break;
3879 case 'I':
3880 case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
3881 case 'H':
3882 case 'k': /* 24-Hour */
3883 res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
3884 break;
3885 case 'M': /* Minute */
3886 if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
3887 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
3888 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
3889 break;
3890 case 'P':
3891 case 'p':
3892 /* AM/PM - There is no AM/PM in Hebrew... */
3893 break;
3894 case 'Q':
3895 /* Shorthand for "Today", "Yesterday", or "date" */
3896 case 'q':
3897 /* Shorthand for "" (today), "Yesterday", A
3898 * (weekday), or "date" */
3899 /* XXX As emphasized elsewhere, this should the native way in your
3900 * language to say the date, with changes in what you say, depending
3901 * upon how recent the date is. XXX */
3903 struct timeval now;
3904 struct tm tmnow;
3905 time_t beg_today, tt;
3906 char todo = format[offset]; /* The letter to format*/
3908 gettimeofday(&now,NULL);
3909 tt = now.tv_sec;
3910 ast_localtime(&tt,&tmnow,timezone);
3911 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3912 /* In any case, it saves not having to do ast_mktime() */
3913 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3914 if (beg_today < time) {
3915 /* Today */
3916 if (todo == 'Q') {
3917 res = wait_file(chan,
3918 ints,
3919 "digits/today",
3920 lang);
3922 } else if (beg_today - 86400 < time) {
3923 /* Yesterday */
3924 res = wait_file(chan,ints, "digits/yesterday",lang);
3925 } else if ((todo != 'Q') &&
3926 (beg_today - 86400 * 6 < time))
3928 /* Within the last week */
3929 res = ast_say_date_with_format_he(chan,
3930 time, ints, lang,
3931 "A", timezone);
3932 } else {
3933 res = ast_say_date_with_format_he(chan,
3934 time, ints, lang,
3935 IL_DATE_STR, timezone);
3938 break;
3939 case 'R':
3940 res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
3941 break;
3942 case 'S': /* Seconds */
3943 res = ast_say_number_full_he(chan, tm.tm_sec,
3944 ints, lang, "f", -1, -1
3946 break;
3947 case 'T':
3948 res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
3949 break;
3950 /* c, x, and X seem useful for testing. Not sure
3951 * if thiey're good for the general public */
3952 case 'c':
3953 res = ast_say_date_with_format_he(chan, time,
3954 ints, lang, IL_DATE_STR_FULL, timezone);
3955 break;
3956 case 'x':
3957 res = ast_say_date_with_format_he(chan, time,
3958 ints, lang, IL_DATE_STR, timezone);
3959 break;
3960 case 'X': /* Currently not locale-dependent...*/
3961 res = ast_say_date_with_format_he(chan, time,
3962 ints, lang, IL_TIME_STR, timezone);
3963 break;
3964 case ' ':
3965 case ' ':
3966 /* Just ignore spaces and tabs */
3967 break;
3968 default:
3969 /* Unknown character */
3970 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3972 /* Jump out on DTMF */
3973 if (res) {
3974 break;
3977 return res;
3981 /* Spanish syntax */
3982 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)
3984 struct tm tm;
3985 int res=0, offset, sndoffset;
3986 char sndfile[256], nextmsg[256];
3988 if (format == NULL)
3989 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3991 ast_localtime(&time,&tm,timezone);
3993 for (offset=0 ; format[offset] != '\0' ; offset++) {
3994 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3995 switch (format[offset]) {
3996 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3997 case '\'':
3998 /* Literal name of a sound file */
3999 sndoffset=0;
4000 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4001 sndfile[sndoffset] = format[offset];
4002 sndfile[sndoffset] = '\0';
4003 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
4004 res = wait_file(chan,ints,nextmsg,lang);
4005 break;
4006 case 'A':
4007 case 'a':
4008 /* Sunday - Saturday */
4009 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4010 res = wait_file(chan,ints,nextmsg,lang);
4011 break;
4012 case 'B':
4013 case 'b':
4014 case 'h':
4015 /* January - December */
4016 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4017 res = wait_file(chan,ints,nextmsg,lang);
4018 break;
4019 case 'm':
4020 /* First - Twelfth */
4021 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4022 res = wait_file(chan,ints,nextmsg,lang);
4023 break;
4024 case 'd':
4025 case 'e':
4026 /* First - Thirtyfirst */
4027 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4028 break;
4029 case 'Y':
4030 /* Year */
4031 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4032 break;
4033 case 'I':
4034 case 'l':
4035 /* 12-Hour */
4036 if (tm.tm_hour == 0)
4037 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4038 else if (tm.tm_hour > 12)
4039 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4040 else
4041 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4042 res = wait_file(chan,ints,nextmsg,lang);
4043 break;
4044 case 'H':
4045 case 'k':
4046 /* 24-Hour */
4047 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
4048 break;
4049 case 'M':
4050 /* Minute */
4051 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4052 break;
4053 case 'P':
4054 case 'p':
4055 /* AM/PM */
4056 if (tm.tm_hour > 18)
4057 res = wait_file(chan, ints, "digits/p-m", lang);
4058 else if (tm.tm_hour > 12)
4059 res = wait_file(chan, ints, "digits/afternoon", lang);
4060 else if (tm.tm_hour)
4061 res = wait_file(chan, ints, "digits/a-m", lang);
4062 break;
4063 case 'Q':
4064 /* Shorthand for "Today", "Yesterday", or ABdY */
4065 /* XXX As emphasized elsewhere, this should the native way in your
4066 * language to say the date, with changes in what you say, depending
4067 * upon how recent the date is. XXX */
4069 struct timeval now;
4070 struct tm tmnow;
4071 time_t beg_today, tt;
4073 gettimeofday(&now,NULL);
4074 tt = now.tv_sec;
4075 ast_localtime(&tt,&tmnow,timezone);
4076 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4077 /* In any case, it saves not having to do ast_mktime() */
4078 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4079 if (beg_today < time) {
4080 /* Today */
4081 res = wait_file(chan,ints, "digits/today",lang);
4082 } else if (beg_today - 86400 < time) {
4083 /* Yesterday */
4084 res = wait_file(chan,ints, "digits/yesterday",lang);
4085 } else {
4086 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
4089 break;
4090 case 'q':
4091 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4092 /* XXX As emphasized elsewhere, this should the native way in your
4093 * language to say the date, with changes in what you say, depending
4094 * upon how recent the date is. XXX */
4096 struct timeval now;
4097 struct tm tmnow;
4098 time_t beg_today, tt;
4100 gettimeofday(&now,NULL);
4101 tt = now.tv_sec;
4102 ast_localtime(&tt,&tmnow,timezone);
4103 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4104 /* In any case, it saves not having to do ast_mktime() */
4105 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4106 if (beg_today < time) {
4107 /* Today */
4108 res = wait_file(chan,ints, "digits/today",lang);
4109 } else if ((beg_today - 86400) < time) {
4110 /* Yesterday */
4111 res = wait_file(chan,ints, "digits/yesterday",lang);
4112 } else if (beg_today - 86400 * 6 < time) {
4113 /* Within the last week */
4114 res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
4115 } else {
4116 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
4119 break;
4120 case 'R':
4121 res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
4122 break;
4123 case 'S':
4124 /* Seconds */
4125 if (tm.tm_sec == 0) {
4126 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4127 res = wait_file(chan,ints,nextmsg,lang);
4128 } else if (tm.tm_sec < 10) {
4129 res = wait_file(chan,ints, "digits/oh",lang);
4130 if (!res) {
4131 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4132 res = wait_file(chan,ints,nextmsg,lang);
4134 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4135 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4136 res = wait_file(chan,ints,nextmsg,lang);
4137 } else {
4138 int ten, one;
4139 ten = (tm.tm_sec / 10) * 10;
4140 one = (tm.tm_sec % 10);
4141 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4142 res = wait_file(chan,ints,nextmsg,lang);
4143 if (!res) {
4144 /* Fifty, not fifty-zero */
4145 if (one != 0) {
4146 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4147 res = wait_file(chan,ints,nextmsg,lang);
4151 break;
4152 case 'T':
4153 res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
4154 break;
4155 case ' ':
4156 case ' ':
4157 /* Just ignore spaces and tabs */
4158 break;
4159 default:
4160 /* Unknown character */
4161 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4163 /* Jump out on DTMF */
4164 if (res) {
4165 break;
4168 return res;
4171 /* French syntax
4172 oclock = heure
4174 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)
4176 struct tm tm;
4177 int res=0, offset, sndoffset;
4178 char sndfile[256], nextmsg[256];
4180 if (format == NULL)
4181 format = "AdBY 'digits/at' IMp";
4183 ast_localtime(&time,&tm,timezone);
4185 for (offset=0 ; format[offset] != '\0' ; offset++) {
4186 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4187 switch (format[offset]) {
4188 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4189 case '\'':
4190 /* Literal name of a sound file */
4191 sndoffset=0;
4192 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4193 sndfile[sndoffset] = format[offset];
4194 sndfile[sndoffset] = '\0';
4195 res = wait_file(chan,ints,sndfile,lang);
4196 break;
4197 case 'A':
4198 case 'a':
4199 /* Sunday - Saturday */
4200 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4201 res = wait_file(chan,ints,nextmsg,lang);
4202 break;
4203 case 'B':
4204 case 'b':
4205 case 'h':
4206 /* January - December */
4207 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4208 res = wait_file(chan,ints,nextmsg,lang);
4209 break;
4210 case 'm':
4211 /* First - Twelfth */
4212 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4213 res = wait_file(chan,ints,nextmsg,lang);
4214 break;
4215 case 'd':
4216 case 'e':
4217 /* First */
4218 if (tm.tm_mday == 1) {
4219 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4220 res = wait_file(chan,ints,nextmsg,lang);
4221 } else {
4222 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4224 break;
4225 case 'Y':
4226 /* Year */
4227 if (tm.tm_year > 99) {
4228 res = wait_file(chan,ints, "digits/2",lang);
4229 if (!res) {
4230 res = wait_file(chan,ints, "digits/thousand",lang);
4232 if (tm.tm_year > 100) {
4233 if (!res) {
4234 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
4237 } else {
4238 if (tm.tm_year < 1) {
4239 /* I'm not going to handle 1900 and prior */
4240 /* We'll just be silent on the year, instead of bombing out. */
4241 } else {
4242 res = wait_file(chan,ints, "digits/thousand",lang);
4243 if (!res) {
4244 wait_file(chan,ints, "digits/9",lang);
4245 wait_file(chan,ints, "digits/hundred",lang);
4246 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
4250 break;
4251 case 'I':
4252 case 'l':
4253 /* 12-Hour */
4254 if (tm.tm_hour == 0)
4255 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4256 else if (tm.tm_hour > 12)
4257 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4258 else
4259 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4260 res = wait_file(chan,ints,nextmsg,lang);
4261 if (!res)
4262 res = wait_file(chan,ints, "digits/oclock",lang);
4263 break;
4264 case 'H':
4265 case 'k':
4266 /* 24-Hour */
4267 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4268 if (!res)
4269 res = wait_file(chan,ints, "digits/oclock",lang);
4270 break;
4271 case 'M':
4272 /* Minute */
4273 if (tm.tm_min == 0) {
4274 break;
4276 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
4277 break;
4278 case 'P':
4279 case 'p':
4280 /* AM/PM */
4281 if (tm.tm_hour > 11)
4282 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4283 else
4284 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4285 res = wait_file(chan,ints,nextmsg,lang);
4286 break;
4287 case 'Q':
4288 /* Shorthand for "Today", "Yesterday", or AdBY */
4289 /* XXX As emphasized elsewhere, this should the native way in your
4290 * language to say the date, with changes in what you say, depending
4291 * upon how recent the date is. XXX */
4293 struct timeval now;
4294 struct tm tmnow;
4295 time_t beg_today, tt;
4297 gettimeofday(&now,NULL);
4298 tt = now.tv_sec;
4299 ast_localtime(&tt,&tmnow,timezone);
4300 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4301 /* In any case, it saves not having to do ast_mktime() */
4302 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4303 if (beg_today < time) {
4304 /* Today */
4305 res = wait_file(chan,ints, "digits/today",lang);
4306 } else if (beg_today - 86400 < time) {
4307 /* Yesterday */
4308 res = wait_file(chan,ints, "digits/yesterday",lang);
4309 } else {
4310 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4313 break;
4314 case 'q':
4315 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4316 /* XXX As emphasized elsewhere, this should the native way in your
4317 * language to say the date, with changes in what you say, depending
4318 * upon how recent the date is. XXX */
4320 struct timeval now;
4321 struct tm tmnow;
4322 time_t beg_today, tt;
4324 gettimeofday(&now,NULL);
4325 tt = now.tv_sec;
4326 ast_localtime(&tt,&tmnow,timezone);
4327 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4328 /* In any case, it saves not having to do ast_mktime() */
4329 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4330 if (beg_today < time) {
4331 /* Today */
4332 } else if ((beg_today - 86400) < time) {
4333 /* Yesterday */
4334 res = wait_file(chan,ints, "digits/yesterday",lang);
4335 } else if (beg_today - 86400 * 6 < time) {
4336 /* Within the last week */
4337 res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
4338 } else {
4339 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4342 break;
4343 case 'R':
4344 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
4345 break;
4346 case 'S':
4347 /* Seconds */
4348 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
4349 if (!res) {
4350 res = wait_file(chan,ints, "digits/second",lang);
4352 break;
4353 case 'T':
4354 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
4355 break;
4356 case ' ':
4357 case ' ':
4358 /* Just ignore spaces and tabs */
4359 break;
4360 default:
4361 /* Unknown character */
4362 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4364 /* Jump out on DTMF */
4365 if (res) {
4366 break;
4369 return res;
4372 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)
4374 struct tm tm;
4375 int res=0, offset, sndoffset;
4376 char sndfile[256], nextmsg[256];
4378 if (format == NULL)
4379 format = "AdB 'digits/at' IMp";
4381 ast_localtime(&time,&tm,timezone);
4383 for (offset=0 ; format[offset] != '\0' ; offset++) {
4384 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4385 switch (format[offset]) {
4386 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4387 case '\'':
4388 /* Literal name of a sound file */
4389 sndoffset=0;
4390 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4391 sndfile[sndoffset] = format[offset];
4392 sndfile[sndoffset] = '\0';
4393 res = wait_file(chan,ints,sndfile,lang);
4394 break;
4395 case 'A':
4396 case 'a':
4397 /* Sunday - Saturday */
4398 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4399 res = wait_file(chan,ints,nextmsg,lang);
4400 break;
4401 case 'B':
4402 case 'b':
4403 case 'h':
4404 /* January - December */
4405 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4406 res = wait_file(chan,ints,nextmsg,lang);
4407 break;
4408 case 'm':
4409 /* First - Twelfth */
4410 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4411 res = wait_file(chan,ints,nextmsg,lang);
4412 break;
4413 case 'd':
4414 case 'e':
4415 /* First day of the month is spelled as ordinal */
4416 if (tm.tm_mday == 1) {
4417 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4418 res = wait_file(chan,ints,nextmsg,lang);
4419 } else {
4420 if (!res) {
4421 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4424 break;
4425 case 'Y':
4426 /* Year */
4427 if (tm.tm_year > 99) {
4428 res = wait_file(chan,ints, "digits/ore-2000",lang);
4429 if (tm.tm_year > 100) {
4430 if (!res) {
4431 /* This works until the end of 2021 */
4432 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4433 res = wait_file(chan,ints,nextmsg,lang);
4436 } else {
4437 if (tm.tm_year < 1) {
4438 /* I'm not going to handle 1900 and prior */
4439 /* We'll just be silent on the year, instead of bombing out. */
4440 } else {
4441 res = wait_file(chan,ints, "digits/ore-1900",lang);
4442 if ((!res) && (tm.tm_year != 0)) {
4443 if (tm.tm_year <= 21) {
4444 /* 1910 - 1921 */
4445 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4446 res = wait_file(chan,ints,nextmsg,lang);
4447 } else {
4448 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4449 int ten, one;
4450 ten = tm.tm_year / 10;
4451 one = tm.tm_year % 10;
4452 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4453 res = wait_file(chan,ints,nextmsg,lang);
4454 if (!res) {
4455 if (one != 0) {
4456 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4457 res = wait_file(chan,ints,nextmsg,lang);
4464 break;
4465 case 'I':
4466 case 'l':
4467 /* 12-Hour */
4468 if (tm.tm_hour == 0)
4469 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4470 else if (tm.tm_hour > 12)
4471 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4472 else
4473 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4474 res = wait_file(chan,ints,nextmsg,lang);
4475 break;
4476 case 'H':
4477 case 'k':
4478 /* 24-Hour */
4479 if (tm.tm_hour == 0) {
4480 res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
4481 } else if (tm.tm_hour == 1) {
4482 res = wait_file(chan,ints, "digits/ore-una",lang);
4483 } else {
4484 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4486 break;
4487 case 'M':
4488 /* Minute */
4489 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4490 break;
4491 case 'P':
4492 case 'p':
4493 /* AM/PM */
4494 if (tm.tm_hour > 11)
4495 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4496 else
4497 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4498 res = wait_file(chan,ints,nextmsg,lang);
4499 break;
4500 case 'Q':
4501 /* Shorthand for "Today", "Yesterday", or ABdY */
4502 /* XXX As emphasized elsewhere, this should the native way in your
4503 * language to say the date, with changes in what you say, depending
4504 * upon how recent the date is. XXX */
4506 struct timeval now;
4507 struct tm tmnow;
4508 time_t beg_today, tt;
4510 gettimeofday(&now,NULL);
4511 tt = now.tv_sec;
4512 ast_localtime(&tt,&tmnow,timezone);
4513 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4514 /* In any case, it saves not having to do ast_mktime() */
4515 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4516 if (beg_today < time) {
4517 /* Today */
4518 res = wait_file(chan,ints, "digits/today",lang);
4519 } else if (beg_today - 86400 < time) {
4520 /* Yesterday */
4521 res = wait_file(chan,ints, "digits/yesterday",lang);
4522 } else {
4523 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4526 break;
4527 case 'q':
4528 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4530 struct timeval now;
4531 struct tm tmnow;
4532 time_t beg_today, tt;
4534 gettimeofday(&now,NULL);
4535 tt = now.tv_sec;
4536 ast_localtime(&tt,&tmnow,timezone);
4537 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4538 /* In any case, it saves not having to do ast_mktime() */
4539 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4540 if (beg_today < time) {
4541 /* Today */
4542 } else if ((beg_today - 86400) < time) {
4543 /* Yesterday */
4544 res = wait_file(chan,ints, "digits/yesterday",lang);
4545 } else if (beg_today - 86400 * 6 < time) {
4546 /* Within the last week */
4547 res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
4548 } else {
4549 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4552 break;
4553 case 'R':
4554 res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
4555 break;
4556 case 'S':
4557 /* Seconds */
4558 if (tm.tm_sec == 0) {
4559 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4560 res = wait_file(chan,ints,nextmsg,lang);
4561 } else if (tm.tm_sec < 10) {
4562 res = wait_file(chan,ints, "digits/oh",lang);
4563 if (!res) {
4564 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4565 res = wait_file(chan,ints,nextmsg,lang);
4567 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4568 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4569 res = wait_file(chan,ints,nextmsg,lang);
4570 } else {
4571 int ten, one;
4572 ten = (tm.tm_sec / 10) * 10;
4573 one = (tm.tm_sec % 10);
4574 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4575 res = wait_file(chan,ints,nextmsg,lang);
4576 if (!res) {
4577 /* Fifty, not fifty-zero */
4578 if (one != 0) {
4579 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4580 res = wait_file(chan,ints,nextmsg,lang);
4584 break;
4585 case 'T':
4586 res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
4587 break;
4588 case ' ':
4589 case ' ':
4590 /* Just ignore spaces and tabs */
4591 break;
4592 default:
4593 /* Unknown character */
4594 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4596 /* Jump out on DTMF */
4597 if (res) {
4598 break;
4601 return res;
4604 /* Dutch syntax */
4605 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)
4607 struct tm tm;
4608 int res=0, offset, sndoffset;
4609 char sndfile[256], nextmsg[256];
4611 if (format == NULL)
4612 format = "ABdY 'digits/at' IMp";
4614 ast_localtime(&time,&tm,timezone);
4616 for (offset=0 ; format[offset] != '\0' ; offset++) {
4617 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4618 switch (format[offset]) {
4619 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4620 case '\'':
4621 /* Literal name of a sound file */
4622 sndoffset=0;
4623 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4624 sndfile[sndoffset] = format[offset];
4625 sndfile[sndoffset] = '\0';
4626 res = wait_file(chan,ints,sndfile,lang);
4627 break;
4628 case 'A':
4629 case 'a':
4630 /* Sunday - Saturday */
4631 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4632 res = wait_file(chan,ints,nextmsg,lang);
4633 break;
4634 case 'B':
4635 case 'b':
4636 case 'h':
4637 /* January - December */
4638 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4639 res = wait_file(chan,ints,nextmsg,lang);
4640 break;
4641 case 'm':
4642 /* First - Twelfth */
4643 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4644 res = wait_file(chan,ints,nextmsg,lang);
4645 break;
4646 case 'd':
4647 case 'e':
4648 /* First - Thirtyfirst */
4649 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
4650 break;
4651 case 'Y':
4652 /* Year */
4653 if (tm.tm_year > 99) {
4654 res = wait_file(chan,ints, "digits/2",lang);
4655 if (!res) {
4656 res = wait_file(chan,ints, "digits/thousand",lang);
4658 if (tm.tm_year > 100) {
4659 if (!res) {
4660 /* This works until the end of 2020 */
4661 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4662 res = wait_file(chan,ints,nextmsg,lang);
4665 } else {
4666 if (tm.tm_year < 1) {
4667 /* I'm not going to handle 1900 and prior */
4668 /* We'll just be silent on the year, instead of bombing out. */
4669 } else {
4670 res = wait_file(chan,ints, "digits/19",lang);
4671 if (!res) {
4672 if (tm.tm_year <= 9) {
4673 /* 1901 - 1909 */
4674 res = wait_file(chan,ints, "digits/oh",lang);
4675 if (!res) {
4676 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4677 res = wait_file(chan,ints,nextmsg,lang);
4679 } else if (tm.tm_year <= 20) {
4680 /* 1910 - 1920 */
4681 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4682 res = wait_file(chan,ints,nextmsg,lang);
4683 } else {
4684 /* 1921 - 1999 */
4685 int ten, one;
4686 ten = tm.tm_year / 10;
4687 one = tm.tm_year % 10;
4688 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4689 res = wait_file(chan,ints,nextmsg,lang);
4690 if (!res) {
4691 if (one != 0) {
4692 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4693 res = wait_file(chan,ints,nextmsg,lang);
4700 break;
4701 case 'I':
4702 case 'l':
4703 /* 12-Hour */
4704 if (tm.tm_hour == 0)
4705 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4706 else if (tm.tm_hour > 12)
4707 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4708 else
4709 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4710 res = wait_file(chan,ints,nextmsg,lang);
4711 break;
4712 case 'H':
4713 case 'k':
4714 /* 24-Hour */
4715 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4716 if (!res) {
4717 res = wait_file(chan,ints, "digits/nl-uur",lang);
4719 break;
4720 case 'M':
4721 /* Minute */
4722 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4723 break;
4724 case 'P':
4725 case 'p':
4726 /* AM/PM */
4727 if (tm.tm_hour > 11)
4728 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4729 else
4730 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4731 res = wait_file(chan,ints,nextmsg,lang);
4732 break;
4733 case 'Q':
4734 /* Shorthand for "Today", "Yesterday", or ABdY */
4735 /* XXX As emphasized elsewhere, this should the native way in your
4736 * language to say the date, with changes in what you say, depending
4737 * upon how recent the date is. XXX */
4739 struct timeval now;
4740 struct tm tmnow;
4741 time_t beg_today, tt;
4743 gettimeofday(&now,NULL);
4744 tt = now.tv_sec;
4745 ast_localtime(&tt,&tmnow,timezone);
4746 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4747 /* In any case, it saves not having to do ast_mktime() */
4748 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4749 if (beg_today < time) {
4750 /* Today */
4751 res = wait_file(chan,ints, "digits/today",lang);
4752 } else if (beg_today - 86400 < time) {
4753 /* Yesterday */
4754 res = wait_file(chan,ints, "digits/yesterday",lang);
4755 } else {
4756 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4759 break;
4760 case 'q':
4761 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4763 struct timeval now;
4764 struct tm tmnow;
4765 time_t beg_today, tt;
4767 gettimeofday(&now,NULL);
4768 tt = now.tv_sec;
4769 ast_localtime(&tt,&tmnow,timezone);
4770 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4771 /* In any case, it saves not having to do ast_mktime() */
4772 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4773 if (beg_today < time) {
4774 /* Today */
4775 } else if ((beg_today - 86400) < time) {
4776 /* Yesterday */
4777 res = wait_file(chan,ints, "digits/yesterday",lang);
4778 } else if (beg_today - 86400 * 6 < time) {
4779 /* Within the last week */
4780 res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
4781 } else {
4782 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4785 break;
4786 case 'R':
4787 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
4788 break;
4789 case 'S':
4790 /* Seconds */
4791 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4792 break;
4793 case 'T':
4794 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
4795 break;
4796 case ' ':
4797 case ' ':
4798 /* Just ignore spaces and tabs */
4799 break;
4800 default:
4801 /* Unknown character */
4802 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4804 /* Jump out on DTMF */
4805 if (res) {
4806 break;
4809 return res;
4812 /* Polish syntax */
4813 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)
4815 struct tm tm;
4816 int res=0, offset, sndoffset;
4817 char sndfile[256], nextmsg[256];
4819 ast_localtime(&thetime, &tm, timezone);
4821 for (offset = 0 ; format[offset] != '\0' ; offset++) {
4822 int remainder;
4823 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4824 switch (format[offset]) {
4825 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4826 case '\'':
4827 /* Literal name of a sound file */
4828 sndoffset = 0;
4829 for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4830 sndfile[sndoffset] = format[offset];
4831 sndfile[sndoffset] = '\0';
4832 res = wait_file(chan, ints, sndfile, lang);
4833 break;
4834 case 'A':
4835 case 'a':
4836 /* Sunday - Saturday */
4837 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4838 res = wait_file(chan, ints, nextmsg, lang);
4839 break;
4840 case 'B':
4841 case 'b':
4842 case 'h':
4843 /* January - December */
4844 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4845 res = wait_file(chan, ints, nextmsg, lang);
4846 break;
4847 case 'm':
4848 /* Month enumerated */
4849 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
4850 break;
4851 case 'd':
4852 case 'e':
4853 /* First - Thirtyfirst */
4854 remainder = tm.tm_mday;
4855 if (tm.tm_mday > 30) {
4856 res = wait_file(chan, ints, "digits/h-30", lang);
4857 remainder -= 30;
4859 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
4860 res = wait_file(chan, ints, "digits/h-20", lang);
4861 remainder -= 20;
4863 if (!res) {
4864 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
4865 res = wait_file(chan, ints, nextmsg, lang);
4867 break;
4868 case 'Y':
4869 /* Year */
4870 if (tm.tm_year > 100) {
4871 res = wait_file(chan, ints, "digits/2", lang);
4872 if (!res)
4873 res = wait_file(chan, ints, "digits/1000.2",lang);
4874 if (tm.tm_year > 100) {
4875 if (!res)
4876 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
4878 } else if (tm.tm_year == 100) {
4879 res = wait_file(chan, ints, "digits/h-2000", lang);
4880 } else {
4881 if (tm.tm_year < 1) {
4882 /* I'm not going to handle 1900 and prior */
4883 /* We'll just be silent on the year, instead of bombing out. */
4884 break;
4885 } else {
4886 res = wait_file(chan, ints, "digits/1000", lang);
4887 if (!res) {
4888 wait_file(chan, ints, "digits/900", lang);
4889 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
4893 if (!res)
4894 wait_file(chan, ints, "digits/year", lang);
4895 break;
4896 case 'I':
4897 case 'l':
4898 /* 12-Hour */
4899 if (tm.tm_hour == 0)
4900 snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
4901 else if (tm.tm_hour > 12)
4902 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
4903 else
4904 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4906 res = wait_file(chan, ints, nextmsg, lang);
4907 break;
4908 case 'H':
4909 case 'k':
4910 /* 24-Hour */
4911 if (tm.tm_hour != 0) {
4912 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4913 res = wait_file(chan, ints, nextmsg, lang);
4914 } else
4915 res = wait_file(chan, ints, "digits/t-24", lang);
4916 break;
4917 case 'M':
4918 case 'N':
4919 /* Minute */
4920 if (tm.tm_min == 0) {
4921 if (format[offset] == 'M') {
4922 res = wait_file(chan, ints, "digits/oclock", lang);
4923 } else {
4924 res = wait_file(chan, ints, "digits/100", lang);
4926 } else
4927 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4928 break;
4929 case 'P':
4930 case 'p':
4931 /* AM/PM */
4932 if (tm.tm_hour > 11)
4933 snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
4934 else
4935 snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
4936 res = wait_file(chan, ints, nextmsg, lang);
4937 break;
4938 case 'Q':
4939 /* Shorthand for "Today", "Yesterday", or AdBY */
4941 time_t tv_sec = time(NULL);
4942 struct tm tmnow;
4943 time_t beg_today;
4945 ast_localtime(&tv_sec,&tmnow, timezone);
4946 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4947 /* In any case, it saves not having to do ast_mktime() */
4948 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4949 if (beg_today < thetime) {
4950 /* Today */
4951 res = wait_file(chan, ints, "digits/today", lang);
4952 } else if (beg_today - 86400 < thetime) {
4953 /* Yesterday */
4954 res = wait_file(chan, ints, "digits/yesterday", lang);
4955 } else {
4956 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4959 break;
4960 case 'q':
4961 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4963 time_t tv_sec = time(NULL);
4964 struct tm tmnow;
4965 time_t beg_today;
4967 ast_localtime(&tv_sec, &tmnow, timezone);
4968 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4969 /* In any case, it saves not having to do ast_mktime() */
4970 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4971 if (beg_today < thetime) {
4972 /* Today */
4973 } else if ((beg_today - 86400) < thetime) {
4974 /* Yesterday */
4975 res = wait_file(chan, ints, "digits/yesterday", lang);
4976 } else if (beg_today - 86400 * 6 < thetime) {
4977 /* Within the last week */
4978 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
4979 } else {
4980 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4983 break;
4984 case 'R':
4985 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
4986 break;
4987 case 'S':
4988 /* Seconds */
4989 res = wait_file(chan, ints, "digits/and", lang);
4990 if (!res) {
4991 if (tm.tm_sec == 1) {
4992 res = wait_file(chan, ints, "digits/1z", lang);
4993 if (!res)
4994 res = wait_file(chan, ints, "digits/second-a", lang);
4995 } else {
4996 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4997 if (!res) {
4998 int ten, one;
4999 ten = tm.tm_sec / 10;
5000 one = tm.tm_sec % 10;
5002 if (one > 1 && one < 5 && ten != 1)
5003 res = wait_file(chan,ints, "digits/seconds",lang);
5004 else
5005 res = wait_file(chan,ints, "digits/second",lang);
5009 break;
5010 case 'T':
5011 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
5012 break;
5013 case ' ':
5014 case ' ':
5015 /* Just ignore spaces and tabs */
5016 break;
5017 default:
5018 /* Unknown character */
5019 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5021 /* Jump out on DTMF */
5022 if (res)
5023 break;
5025 return res;
5028 /* Portuguese syntax */
5029 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)
5031 struct tm tm;
5032 int res=0, offset, sndoffset;
5033 char sndfile[256], nextmsg[256];
5035 if (format == NULL)
5036 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
5038 ast_localtime(&time,&tm,timezone);
5040 for (offset=0 ; format[offset] != '\0' ; offset++) {
5041 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5042 switch (format[offset]) {
5043 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5044 case '\'':
5045 /* Literal name of a sound file */
5046 sndoffset=0;
5047 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5048 sndfile[sndoffset] = format[offset];
5049 sndfile[sndoffset] = '\0';
5050 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
5051 res = wait_file(chan,ints,nextmsg,lang);
5052 break;
5053 case 'A':
5054 case 'a':
5055 /* Sunday - Saturday */
5056 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5057 res = wait_file(chan,ints,nextmsg,lang);
5058 break;
5059 case 'B':
5060 case 'b':
5061 case 'h':
5062 /* January - December */
5063 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5064 res = wait_file(chan,ints,nextmsg,lang);
5065 break;
5066 case 'm':
5067 /* First - Twelfth */
5068 if (!strcasecmp(lang, "pt_BR")) {
5069 res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
5070 } else {
5071 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5072 res = wait_file(chan,ints,nextmsg,lang);
5074 break;
5075 case 'd':
5076 case 'e':
5077 /* First - Thirtyfirst */
5078 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5079 break;
5080 case 'Y':
5081 /* Year */
5082 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5083 break;
5084 case 'I':
5085 case 'l':
5086 /* 12-Hour */
5087 if (!strcasecmp(lang, "pt_BR")) {
5088 if (tm.tm_hour == 0) {
5089 if (format[offset] == 'I')
5090 res = wait_file(chan, ints, "digits/pt-a", lang);
5091 if (!res)
5092 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
5093 } else if (tm.tm_hour == 12) {
5094 if (format[offset] == 'I')
5095 res = wait_file(chan, ints, "digits/pt-ao", lang);
5096 if (!res)
5097 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
5098 } else {
5099 if (format[offset] == 'I') {
5100 if ((tm.tm_hour % 12) != 1)
5101 res = wait_file(chan, ints, "digits/pt-as", lang);
5102 else
5103 res = wait_file(chan, ints, "digits/pt-a", lang);
5105 if (!res)
5106 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
5108 } else {
5109 if (tm.tm_hour == 0) {
5110 if (format[offset] == 'I')
5111 res = wait_file(chan, ints, "digits/pt-ah", lang);
5112 if (!res)
5113 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
5115 else if (tm.tm_hour == 12) {
5116 if (format[offset] == 'I')
5117 res = wait_file(chan, ints, "digits/pt-ao", lang);
5118 if (!res)
5119 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
5121 else {
5122 if (format[offset] == 'I') {
5123 res = wait_file(chan, ints, "digits/pt-ah", lang);
5124 if ((tm.tm_hour % 12) != 1)
5125 if (!res)
5126 res = wait_file(chan, ints, "digits/pt-sss", lang);
5128 if (!res)
5129 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
5132 break;
5133 case 'H':
5134 case 'k':
5135 /* 24-Hour */
5136 if (!strcasecmp(lang, "pt_BR")) {
5137 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5138 if ((!res) && (format[offset] == 'H')) {
5139 if (tm.tm_hour > 1) {
5140 res = wait_file(chan,ints,"digits/hours",lang);
5141 } else {
5142 res = wait_file(chan,ints,"digits/hour",lang);
5145 } else {
5146 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
5147 if (!res) {
5148 if (tm.tm_hour != 0) {
5149 int remainder = tm.tm_hour;
5150 if (tm.tm_hour > 20) {
5151 res = wait_file(chan,ints, "digits/20",lang);
5152 remainder -= 20;
5154 if (!res) {
5155 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
5156 res = wait_file(chan,ints,nextmsg,lang);
5161 break;
5162 case 'M':
5163 /* Minute */
5164 if (!strcasecmp(lang, "pt_BR")) {
5165 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5166 if (!res) {
5167 if (tm.tm_min > 1) {
5168 res = wait_file(chan,ints,"digits/minutes",lang);
5169 } else {
5170 res = wait_file(chan,ints,"digits/minute",lang);
5173 } else {
5174 if (tm.tm_min == 0) {
5175 res = wait_file(chan, ints, "digits/pt-hora", lang);
5176 if (tm.tm_hour != 1)
5177 if (!res)
5178 res = wait_file(chan, ints, "digits/pt-sss", lang);
5179 } else {
5180 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5183 break;
5184 case 'P':
5185 case 'p':
5186 /* AM/PM */
5187 if (!strcasecmp(lang, "pt_BR")) {
5188 if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
5189 res = wait_file(chan, ints, "digits/pt-da", lang);
5190 if (!res) {
5191 if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
5192 res = wait_file(chan, ints, "digits/morning", lang);
5193 else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
5194 res = wait_file(chan, ints, "digits/afternoon", lang);
5195 else res = wait_file(chan, ints, "digits/night", lang);
5198 } else {
5199 if (tm.tm_hour > 12)
5200 res = wait_file(chan, ints, "digits/p-m", lang);
5201 else if (tm.tm_hour && tm.tm_hour < 12)
5202 res = wait_file(chan, ints, "digits/a-m", lang);
5204 break;
5205 case 'Q':
5206 /* Shorthand for "Today", "Yesterday", or ABdY */
5207 /* XXX As emphasized elsewhere, this should the native way in your
5208 * language to say the date, with changes in what you say, depending
5209 * upon how recent the date is. XXX */
5211 struct timeval now;
5212 struct tm tmnow;
5213 time_t beg_today, tt;
5215 gettimeofday(&now,NULL);
5216 tt = now.tv_sec;
5217 ast_localtime(&tt,&tmnow,timezone);
5218 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5219 /* In any case, it saves not having to do ast_mktime() */
5220 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5221 if (beg_today < time) {
5222 /* Today */
5223 res = wait_file(chan,ints, "digits/today",lang);
5224 } else if (beg_today - 86400 < time) {
5225 /* Yesterday */
5226 res = wait_file(chan,ints, "digits/yesterday",lang);
5227 } else {
5228 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5231 break;
5232 case 'q':
5233 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5234 /* XXX As emphasized elsewhere, this should the native way in your
5235 * language to say the date, with changes in what you say, depending
5236 * upon how recent the date is. XXX */
5238 struct timeval now;
5239 struct tm tmnow;
5240 time_t beg_today, tt;
5242 gettimeofday(&now,NULL);
5243 tt = now.tv_sec;
5244 ast_localtime(&tt,&tmnow,timezone);
5245 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5246 /* In any case, it saves not having to do ast_mktime() */
5247 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5248 if (beg_today < time) {
5249 /* Today */
5250 } else if ((beg_today - 86400) < time) {
5251 /* Yesterday */
5252 res = wait_file(chan,ints, "digits/yesterday",lang);
5253 } else if (beg_today - 86400 * 6 < time) {
5254 /* Within the last week */
5255 res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
5256 } else {
5257 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5260 break;
5261 case 'R':
5262 res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
5263 break;
5264 case 'S':
5265 /* Seconds */
5266 if (!strcasecmp(lang, "pt_BR")) {
5267 res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
5268 if (!res) {
5269 if (tm.tm_sec > 1) {
5270 res = wait_file(chan,ints,"digits/seconds",lang);
5271 } else {
5272 res = wait_file(chan,ints,"digits/second",lang);
5274 } else if (tm.tm_sec < 10) {
5275 res = wait_file(chan,ints, "digits/oh",lang);
5276 if (!res) {
5277 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5278 res = wait_file(chan,ints,nextmsg,lang);
5280 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5281 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5282 res = wait_file(chan,ints,nextmsg,lang);
5283 } else {
5284 int ten, one;
5285 ten = (tm.tm_sec / 10) * 10;
5286 one = (tm.tm_sec % 10);
5287 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
5288 res = wait_file(chan,ints,nextmsg,lang);
5289 if (!res) {
5290 /* Fifty, not fifty-zero */
5291 if (one != 0) {
5292 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
5293 res = wait_file(chan,ints,nextmsg,lang);
5298 break;
5299 case 'T':
5300 res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
5301 break;
5302 case ' ':
5303 case ' ':
5304 /* Just ignore spaces and tabs */
5305 break;
5306 default:
5307 /* Unknown character */
5308 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5310 /* Jump out on DTMF */
5311 if (res) {
5312 break;
5315 return res;
5318 /* Taiwanese / Chinese syntax */
5319 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)
5321 struct tm tm;
5322 int res=0, offset, sndoffset;
5323 char sndfile[256], nextmsg[256];
5325 if (format == NULL)
5326 format = "YBdAkM";
5328 ast_localtime(&time,&tm,timezone);
5330 for (offset=0 ; format[offset] != '\0' ; offset++) {
5331 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5332 switch (format[offset]) {
5333 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5334 case '\'':
5335 /* Literal name of a sound file */
5336 sndoffset=0;
5337 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5338 sndfile[sndoffset] = format[offset];
5339 sndfile[sndoffset] = '\0';
5340 res = wait_file(chan,ints,sndfile,lang);
5341 break;
5342 case 'A':
5343 case 'a':
5344 /* Sunday - Saturday */
5345 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5346 res = wait_file(chan,ints,nextmsg,lang);
5347 break;
5348 case 'B':
5349 case 'b':
5350 case 'h':
5351 case 'm':
5352 /* January - December */
5353 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5354 res = wait_file(chan,ints,nextmsg,lang);
5355 break;
5356 case 'd':
5357 case 'e':
5358 /* First - Thirtyfirst */
5359 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
5360 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
5361 res = wait_file(chan,ints,nextmsg,lang);
5362 } else {
5363 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
5364 res = wait_file(chan,ints,nextmsg,lang);
5365 if (!res) {
5366 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
5367 res = wait_file(chan,ints,nextmsg,lang);
5370 if (!res) res = wait_file(chan,ints,"digits/day",lang);
5371 break;
5372 case 'Y':
5373 /* Year */
5374 if (tm.tm_year > 99) {
5375 res = wait_file(chan,ints, "digits/2",lang);
5376 if (!res) {
5377 res = wait_file(chan,ints, "digits/thousand",lang);
5379 if (tm.tm_year > 100) {
5380 if (!res) {
5381 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
5382 res = wait_file(chan,ints,nextmsg,lang);
5383 if (!res) {
5384 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
5385 res = wait_file(chan,ints,nextmsg,lang);
5389 if (!res) {
5390 res = wait_file(chan,ints, "digits/year",lang);
5392 } else {
5393 if (tm.tm_year < 1) {
5394 /* I'm not going to handle 1900 and prior */
5395 /* We'll just be silent on the year, instead of bombing out. */
5396 } else {
5397 res = wait_file(chan,ints, "digits/1",lang);
5398 if (!res) {
5399 res = wait_file(chan,ints, "digits/9",lang);
5401 if (!res) {
5402 if (tm.tm_year <= 9) {
5403 /* 1901 - 1909 */
5404 res = wait_file(chan,ints, "digits/0",lang);
5405 if (!res) {
5406 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
5407 res = wait_file(chan,ints,nextmsg,lang);
5409 } else {
5410 /* 1910 - 1999 */
5411 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
5412 res = wait_file(chan,ints,nextmsg,lang);
5413 if (!res) {
5414 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
5415 res = wait_file(chan,ints,nextmsg,lang);
5420 if (!res) {
5421 res = wait_file(chan,ints, "digits/year",lang);
5424 break;
5425 case 'I':
5426 case 'l':
5427 /* 12-Hour */
5428 if (tm.tm_hour == 0)
5429 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
5430 else if (tm.tm_hour > 12)
5431 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5432 else
5433 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5434 res = wait_file(chan,ints,nextmsg,lang);
5435 if (!res) {
5436 res = wait_file(chan,ints, "digits/oclock",lang);
5438 break;
5439 case 'H':
5440 if (tm.tm_hour < 10) {
5441 res = wait_file(chan, ints, "digits/0", lang);
5443 case 'k':
5444 /* 24-Hour */
5445 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
5446 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5447 res = wait_file(chan,ints,nextmsg,lang);
5448 } else {
5449 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
5450 res = wait_file(chan,ints,nextmsg,lang);
5451 if (!res) {
5452 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
5453 res = wait_file(chan,ints,nextmsg,lang);
5456 if (!res) {
5457 res = wait_file(chan,ints, "digits/oclock",lang);
5459 break;
5460 case 'M':
5461 /* Minute */
5462 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
5463 if (tm.tm_min < 10) {
5464 res = wait_file(chan, ints, "digits/0", lang);
5466 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
5467 res = wait_file(chan,ints,nextmsg,lang);
5468 } else {
5469 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
5470 res = wait_file(chan,ints,nextmsg,lang);
5471 if (!res) {
5472 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
5473 res = wait_file(chan,ints,nextmsg,lang);
5476 if (!res) {
5477 res = wait_file(chan,ints, "digits/minute",lang);
5479 break;
5480 case 'P':
5481 case 'p':
5482 /* AM/PM */
5483 if (tm.tm_hour > 11)
5484 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
5485 else
5486 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
5487 res = wait_file(chan,ints,nextmsg,lang);
5488 break;
5489 case 'Q':
5490 /* Shorthand for "Today", "Yesterday", or ABdY */
5491 /* XXX As emphasized elsewhere, this should the native way in your
5492 * language to say the date, with changes in what you say, depending
5493 * upon how recent the date is. XXX */
5495 struct timeval now;
5496 struct tm tmnow;
5497 time_t beg_today, tt;
5499 gettimeofday(&now,NULL);
5500 tt = now.tv_sec;
5501 ast_localtime(&tt,&tmnow,timezone);
5502 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5503 /* In any case, it saves not having to do ast_mktime() */
5504 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5505 if (beg_today < time) {
5506 /* Today */
5507 res = wait_file(chan,ints, "digits/today",lang);
5508 } else if (beg_today - 86400 < time) {
5509 /* Yesterday */
5510 res = wait_file(chan,ints, "digits/yesterday",lang);
5511 } else {
5512 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5515 break;
5516 case 'q':
5517 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5518 /* XXX As emphasized elsewhere, this should the native way in your
5519 * language to say the date, with changes in what you say, depending
5520 * upon how recent the date is. XXX */
5522 struct timeval now;
5523 struct tm tmnow;
5524 time_t beg_today, tt;
5526 gettimeofday(&now,NULL);
5527 tt = now.tv_sec;
5528 ast_localtime(&tt,&tmnow,timezone);
5529 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5530 /* In any case, it saves not having to do ast_mktime() */
5531 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5532 if (beg_today < time) {
5533 /* Today */
5534 } else if ((beg_today - 86400) < time) {
5535 /* Yesterday */
5536 res = wait_file(chan,ints, "digits/yesterday",lang);
5537 } else if (beg_today - 86400 * 6 < time) {
5538 /* Within the last week */
5539 res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
5540 } else {
5541 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5544 break;
5545 case 'R':
5546 res = ast_say_date_with_format_tw(chan, time, ints, lang, "kM", timezone);
5547 break;
5548 case 'S':
5549 /* Seconds */
5550 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
5551 if (tm.tm_sec < 10) {
5552 res = wait_file(chan, ints, "digits/0", lang);
5554 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5555 res = wait_file(chan,ints,nextmsg,lang);
5556 } else {
5557 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
5558 res = wait_file(chan,ints,nextmsg,lang);
5559 if (!res) {
5560 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
5561 res = wait_file(chan,ints,nextmsg,lang);
5564 if (!res) {
5565 res = wait_file(chan,ints, "digits/second",lang);
5567 break;
5568 case 'T':
5569 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
5570 break;
5571 case ' ':
5572 case ' ':
5573 /* Just ignore spaces and tabs */
5574 break;
5575 default:
5576 /* Unknown character */
5577 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5579 /* Jump out on DTMF */
5580 if (res) {
5581 break;
5584 return res;
5587 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5589 if (!strcasecmp(lang, "en") ) { /* English syntax */
5590 return(ast_say_time_en(chan, t, ints, lang));
5591 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5592 return(ast_say_time_de(chan, t, ints, lang));
5593 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5594 return(ast_say_time_fr(chan, t, ints, lang));
5595 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5596 return(ast_say_time_nl(chan, t, ints, lang));
5597 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5598 return(ast_say_time_pt(chan, t, ints, lang));
5599 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5600 return(ast_say_time_pt_BR(chan, t, ints, lang));
5601 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5602 return(ast_say_time_tw(chan, t, ints, lang));
5603 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5604 return(ast_say_time_gr(chan, t, ints, lang));
5605 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5606 return(ast_say_time_ge(chan, t, ints, lang));
5607 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
5608 return (ast_say_time_he(chan, t, ints, lang));
5611 /* Default to English */
5612 return(ast_say_time_en(chan, t, ints, lang));
5615 /* English syntax */
5616 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5618 struct tm tm;
5619 int res = 0;
5620 int hour, pm=0;
5622 ast_localtime(&t, &tm, NULL);
5623 hour = tm.tm_hour;
5624 if (!hour)
5625 hour = 12;
5626 else if (hour == 12)
5627 pm = 1;
5628 else if (hour > 12) {
5629 hour -= 12;
5630 pm = 1;
5632 if (!res)
5633 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5635 if (tm.tm_min > 9) {
5636 if (!res)
5637 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5638 } else if (tm.tm_min) {
5639 if (!res)
5640 res = ast_streamfile(chan, "digits/oh", lang);
5641 if (!res)
5642 res = ast_waitstream(chan, ints);
5643 if (!res)
5644 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5645 } else {
5646 if (!res)
5647 res = ast_streamfile(chan, "digits/oclock", lang);
5648 if (!res)
5649 res = ast_waitstream(chan, ints);
5651 if (pm) {
5652 if (!res)
5653 res = ast_streamfile(chan, "digits/p-m", lang);
5654 } else {
5655 if (!res)
5656 res = ast_streamfile(chan, "digits/a-m", lang);
5658 if (!res)
5659 res = ast_waitstream(chan, ints);
5660 return res;
5663 /* German syntax */
5664 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5666 struct tm tm;
5667 int res = 0;
5669 ast_localtime(&t, &tm, NULL);
5670 if (!res)
5671 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5672 if (!res)
5673 res = ast_streamfile(chan, "digits/oclock", lang);
5674 if (!res)
5675 res = ast_waitstream(chan, ints);
5676 if (!res)
5677 if (tm.tm_min > 0)
5678 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5679 return res;
5682 /* French syntax */
5683 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5685 struct tm tm;
5686 int res = 0;
5688 ast_localtime(&t, &tm, NULL);
5690 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5691 if (!res)
5692 res = ast_streamfile(chan, "digits/oclock", lang);
5693 if (tm.tm_min) {
5694 if (!res)
5695 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5697 return res;
5700 /* Dutch syntax */
5701 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5703 struct tm tm;
5704 int res = 0;
5706 ast_localtime(&t, &tm, NULL);
5707 if (!res)
5708 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5709 if (!res)
5710 res = ast_streamfile(chan, "digits/nl-uur", lang);
5711 if (!res)
5712 res = ast_waitstream(chan, ints);
5713 if (!res)
5714 if (tm.tm_min > 0)
5715 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5716 return res;
5719 /* Portuguese syntax */
5720 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5722 struct tm tm;
5723 int res = 0;
5724 int hour;
5726 ast_localtime(&t, &tm, NULL);
5727 hour = tm.tm_hour;
5728 if (!res)
5729 res = ast_say_number(chan, hour, ints, lang, "f");
5730 if (tm.tm_min) {
5731 if (!res)
5732 res = wait_file(chan, ints, "digits/pt-e", lang);
5733 if (!res)
5734 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5735 } else {
5736 if (!res)
5737 res = wait_file(chan, ints, "digits/pt-hora", lang);
5738 if (tm.tm_hour != 1)
5739 if (!res)
5740 res = wait_file(chan, ints, "digits/pt-sss", lang);
5742 if (!res)
5743 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5744 return res;
5747 /* Brazilian Portuguese syntax */
5748 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5750 struct tm tm;
5751 int res = 0;
5753 ast_localtime(&t, &tm, NULL);
5755 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5756 if (!res) {
5757 if (tm.tm_hour > 1)
5758 res = wait_file(chan, ints, "digits/hours", lang);
5759 else
5760 res = wait_file(chan, ints, "digits/hour", lang);
5762 if ((!res) && (tm.tm_min)) {
5763 res = wait_file(chan, ints, "digits/pt-e", lang);
5764 if (!res)
5765 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5766 if (!res) {
5767 if (tm.tm_min > 1)
5768 res = wait_file(chan, ints, "digits/minutes", lang);
5769 else
5770 res = wait_file(chan, ints, "digits/minute", lang);
5773 return res;
5776 /* Taiwanese / Chinese syntax */
5777 int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5779 struct tm tm;
5780 int res = 0;
5781 int hour, pm=0;
5783 ast_localtime(&t, &tm, NULL);
5784 hour = tm.tm_hour;
5785 if (!hour)
5786 hour = 12;
5787 else if (hour == 12)
5788 pm = 1;
5789 else if (hour > 12) {
5790 hour -= 12;
5791 pm = 1;
5793 if (pm) {
5794 if (!res)
5795 res = ast_streamfile(chan, "digits/p-m", lang);
5796 } else {
5797 if (!res)
5798 res = ast_streamfile(chan, "digits/a-m", lang);
5800 if (!res)
5801 res = ast_waitstream(chan, ints);
5802 if (!res)
5803 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5804 if (!res)
5805 res = ast_streamfile(chan, "digits/oclock", lang);
5806 if (!res)
5807 res = ast_waitstream(chan, ints);
5808 if (!res)
5809 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5810 if (!res)
5811 res = ast_streamfile(chan, "digits/minute", lang);
5812 if (!res)
5813 res = ast_waitstream(chan, ints);
5814 return res;
5817 /* Hebrew syntax */
5818 int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5820 struct tm tm;
5821 int res = 0;
5822 int hour;
5824 ast_localtime(&t, &tm, NULL);
5825 hour = tm.tm_hour;
5826 if (!hour)
5827 hour = 12;
5829 if (!res)
5830 res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
5832 if (tm.tm_min > 9) {
5833 if (!res)
5834 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5835 } else if (tm.tm_min) {
5836 if (!res) { /* say a leading zero if needed */
5837 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
5839 if (!res)
5840 res = ast_waitstream(chan, ints);
5841 if (!res)
5842 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5843 } else {
5844 if (!res)
5845 res = ast_waitstream(chan, ints);
5847 if (!res)
5848 res = ast_waitstream(chan, ints);
5849 return res;
5851 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5853 if (!strcasecmp(lang, "en") ) { /* English syntax */
5854 return(ast_say_datetime_en(chan, t, ints, lang));
5855 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5856 return(ast_say_datetime_de(chan, t, ints, lang));
5857 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5858 return(ast_say_datetime_fr(chan, t, ints, lang));
5859 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5860 return(ast_say_datetime_nl(chan, t, ints, lang));
5861 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5862 return(ast_say_datetime_pt(chan, t, ints, lang));
5863 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5864 return(ast_say_datetime_pt_BR(chan, t, ints, lang));
5865 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5866 return(ast_say_datetime_tw(chan, t, ints, lang));
5867 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5868 return(ast_say_datetime_gr(chan, t, ints, lang));
5869 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5870 return(ast_say_datetime_ge(chan, t, ints, lang));
5871 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
5872 return (ast_say_datetime_he(chan, t, ints, lang));
5875 /* Default to English */
5876 return(ast_say_datetime_en(chan, t, ints, lang));
5879 /* English syntax */
5880 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5882 struct tm tm;
5883 char fn[256];
5884 int res = 0;
5885 int hour, pm=0;
5887 ast_localtime(&t, &tm, NULL);
5888 if (!res) {
5889 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5890 res = ast_streamfile(chan, fn, lang);
5891 if (!res)
5892 res = ast_waitstream(chan, ints);
5894 if (!res) {
5895 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5896 res = ast_streamfile(chan, fn, lang);
5897 if (!res)
5898 res = ast_waitstream(chan, ints);
5900 if (!res)
5901 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5903 hour = tm.tm_hour;
5904 if (!hour)
5905 hour = 12;
5906 else if (hour == 12)
5907 pm = 1;
5908 else if (hour > 12) {
5909 hour -= 12;
5910 pm = 1;
5912 if (!res)
5913 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5915 if (tm.tm_min > 9) {
5916 if (!res)
5917 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5918 } else if (tm.tm_min) {
5919 if (!res)
5920 res = ast_streamfile(chan, "digits/oh", lang);
5921 if (!res)
5922 res = ast_waitstream(chan, ints);
5923 if (!res)
5924 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5925 } else {
5926 if (!res)
5927 res = ast_streamfile(chan, "digits/oclock", lang);
5928 if (!res)
5929 res = ast_waitstream(chan, ints);
5931 if (pm) {
5932 if (!res)
5933 res = ast_streamfile(chan, "digits/p-m", lang);
5934 } else {
5935 if (!res)
5936 res = ast_streamfile(chan, "digits/a-m", lang);
5938 if (!res)
5939 res = ast_waitstream(chan, ints);
5940 if (!res)
5941 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5942 return res;
5945 /* German syntax */
5946 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5948 struct tm tm;
5949 int res = 0;
5951 ast_localtime(&t, &tm, NULL);
5952 res = ast_say_date(chan, t, ints, lang);
5953 if (!res)
5954 ast_say_time(chan, t, ints, lang);
5955 return res;
5959 /* French syntax */
5960 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5962 struct tm tm;
5963 char fn[256];
5964 int res = 0;
5966 ast_localtime(&t, &tm, NULL);
5968 if (!res)
5969 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5971 if (!res) {
5972 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5973 res = ast_streamfile(chan, fn, lang);
5974 if (!res)
5975 res = ast_waitstream(chan, ints);
5977 if (!res) {
5978 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5979 res = ast_streamfile(chan, fn, lang);
5980 if (!res)
5981 res = ast_waitstream(chan, ints);
5984 if (!res)
5985 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5986 if (!res)
5987 res = ast_streamfile(chan, "digits/oclock", lang);
5988 if (tm.tm_min > 0) {
5989 if (!res)
5990 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5992 if (!res)
5993 res = ast_waitstream(chan, ints);
5994 if (!res)
5995 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5996 return res;
5999 /* Dutch syntax */
6000 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6002 struct tm tm;
6003 int res = 0;
6005 ast_localtime(&t, &tm, NULL);
6006 res = ast_say_date(chan, t, ints, lang);
6007 if (!res) {
6008 res = ast_streamfile(chan, "digits/nl-om", lang);
6009 if (!res)
6010 res = ast_waitstream(chan, ints);
6012 if (!res)
6013 ast_say_time(chan, t, ints, lang);
6014 return res;
6017 /* Portuguese syntax */
6018 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6020 struct tm tm;
6021 char fn[256];
6022 int res = 0;
6023 int hour, pm=0;
6025 ast_localtime(&t, &tm, NULL);
6026 if (!res) {
6027 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6028 res = ast_streamfile(chan, fn, lang);
6029 if (!res)
6030 res = ast_waitstream(chan, ints);
6032 if (!res) {
6033 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6034 res = ast_streamfile(chan, fn, lang);
6035 if (!res)
6036 res = ast_waitstream(chan, ints);
6038 if (!res)
6039 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6041 hour = tm.tm_hour;
6042 if (!hour)
6043 hour = 12;
6044 else if (hour == 12)
6045 pm = 1;
6046 else if (hour > 12) {
6047 hour -= 12;
6048 pm = 1;
6050 if (!res)
6051 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
6053 if (tm.tm_min > 9) {
6054 if (!res)
6055 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6056 } else if (tm.tm_min) {
6057 if (!res)
6058 res = ast_streamfile(chan, "digits/oh", lang);
6059 if (!res)
6060 res = ast_waitstream(chan, ints);
6061 if (!res)
6062 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6063 } else {
6064 if (!res)
6065 res = ast_streamfile(chan, "digits/oclock", lang);
6066 if (!res)
6067 res = ast_waitstream(chan, ints);
6069 if (pm) {
6070 if (!res)
6071 res = ast_streamfile(chan, "digits/p-m", lang);
6072 } else {
6073 if (!res)
6074 res = ast_streamfile(chan, "digits/a-m", lang);
6076 if (!res)
6077 res = ast_waitstream(chan, ints);
6078 if (!res)
6079 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6080 return res;
6083 /* Brazilian Portuguese syntax */
6084 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6086 struct tm tm;
6087 int res = 0;
6089 ast_localtime(&t, &tm, NULL);
6090 res = ast_say_date(chan, t, ints, lang);
6091 if (!res)
6092 res = ast_say_time(chan, t, ints, lang);
6093 return res;
6096 /* Taiwanese / Chinese syntax */
6097 int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6099 struct tm tm;
6100 char fn[256];
6101 int res = 0;
6102 int hour, pm=0;
6104 ast_localtime(&t, &tm, NULL);
6105 if (!res)
6106 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6107 if (!res) {
6108 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6109 res = ast_streamfile(chan, fn, lang);
6110 if (!res)
6111 res = ast_waitstream(chan, ints);
6113 if (!res)
6114 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6115 if (!res) {
6116 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6117 res = ast_streamfile(chan, fn, lang);
6118 if (!res)
6119 res = ast_waitstream(chan, ints);
6122 hour = tm.tm_hour;
6123 if (!hour)
6124 hour = 12;
6125 else if (hour == 12)
6126 pm = 1;
6127 else if (hour > 12) {
6128 hour -= 12;
6129 pm = 1;
6131 if (pm) {
6132 if (!res)
6133 res = ast_streamfile(chan, "digits/p-m", lang);
6134 } else {
6135 if (!res)
6136 res = ast_streamfile(chan, "digits/a-m", lang);
6138 if (!res)
6139 res = ast_waitstream(chan, ints);
6140 if (!res)
6141 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
6142 if (!res)
6143 res = ast_streamfile(chan, "digits/oclock", lang);
6144 if (!res)
6145 res = ast_waitstream(chan, ints);
6146 if (!res)
6147 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6148 if (!res)
6149 res = ast_streamfile(chan, "digits/minute", lang);
6150 if (!res)
6151 res = ast_waitstream(chan, ints);
6152 return res;
6155 /* Hebrew syntax */
6156 int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6158 struct tm tm;
6159 char fn[256];
6160 int res = 0;
6161 int hour;
6163 ast_localtime(&t, &tm, NULL);
6164 if (!res) {
6165 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6166 res = ast_streamfile(chan, fn, lang);
6167 if (!res) {
6168 res = ast_waitstream(chan, ints);
6171 if (!res) {
6172 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6173 res = ast_streamfile(chan, fn, lang);
6174 if (!res) {
6175 res = ast_waitstream(chan, ints);
6178 if (!res) {
6179 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
6182 hour = tm.tm_hour;
6183 if (!hour) {
6184 hour = 12;
6187 if (!res) {
6188 res = ast_say_number(chan, hour, ints, lang, "f");
6191 if (tm.tm_min > 9) {
6192 if (!res) {
6193 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
6195 } else if (tm.tm_min) {
6196 if (!res) {
6197 /* say a leading zero if needed */
6198 res = ast_say_number(chan, 0, ints, lang, "f");
6200 if (!res) {
6201 res = ast_waitstream(chan, ints);
6203 if (!res) {
6204 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
6206 } else {
6207 if (!res) {
6208 res = ast_waitstream(chan, ints);
6211 if (!res) {
6212 res = ast_waitstream(chan, ints);
6214 if (!res) {
6215 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
6217 return res;
6219 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6221 if (!strcasecmp(lang, "en") ) { /* English syntax */
6222 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
6223 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
6224 return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
6225 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
6226 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
6227 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
6228 return(ast_say_datetime_from_now_ge(chan, t, ints, lang));
6229 } else if (!strcasecmp(lang, "he")) { /* Georgian syntax */
6230 return (ast_say_datetime_from_now_he(chan, t, ints, lang));
6233 /* Default to English */
6234 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
6237 /* English syntax */
6238 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6240 int res=0;
6241 time_t nowt;
6242 int daydiff;
6243 struct tm tm;
6244 struct tm now;
6245 char fn[256];
6247 time(&nowt);
6249 ast_localtime(&t, &tm, NULL);
6250 ast_localtime(&nowt,&now, NULL);
6251 daydiff = now.tm_yday - tm.tm_yday;
6252 if ((daydiff < 0) || (daydiff > 6)) {
6253 /* Day of month and month */
6254 if (!res) {
6255 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6256 res = ast_streamfile(chan, fn, lang);
6257 if (!res)
6258 res = ast_waitstream(chan, ints);
6260 if (!res)
6261 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6263 } else if (daydiff) {
6264 /* Just what day of the week */
6265 if (!res) {
6266 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6267 res = ast_streamfile(chan, fn, lang);
6268 if (!res)
6269 res = ast_waitstream(chan, ints);
6271 } /* Otherwise, it was today */
6272 if (!res)
6273 res = ast_say_time(chan, t, ints, lang);
6274 return res;
6277 /* French syntax */
6278 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6280 int res=0;
6281 time_t nowt;
6282 int daydiff;
6283 struct tm tm;
6284 struct tm now;
6285 char fn[256];
6287 time(&nowt);
6289 ast_localtime(&t, &tm, NULL);
6290 ast_localtime(&nowt, &now, NULL);
6291 daydiff = now.tm_yday - tm.tm_yday;
6292 if ((daydiff < 0) || (daydiff > 6)) {
6293 /* Day of month and month */
6294 if (!res) {
6295 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6296 res = ast_streamfile(chan, fn, lang);
6297 if (!res)
6298 res = ast_waitstream(chan, ints);
6300 if (!res)
6301 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6303 } else if (daydiff) {
6304 /* Just what day of the week */
6305 if (!res) {
6306 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6307 res = ast_streamfile(chan, fn, lang);
6308 if (!res)
6309 res = ast_waitstream(chan, ints);
6311 } /* Otherwise, it was today */
6312 if (!res)
6313 res = ast_say_time(chan, t, ints, lang);
6314 return res;
6317 /* Portuguese syntax */
6318 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6320 int res=0;
6321 time_t nowt;
6322 int daydiff;
6323 struct tm tm;
6324 struct tm now;
6325 char fn[256];
6327 time(&nowt);
6329 ast_localtime(&t, &tm, NULL);
6330 ast_localtime(&nowt, &now, NULL);
6331 daydiff = now.tm_yday - tm.tm_yday;
6332 if ((daydiff < 0) || (daydiff > 6)) {
6333 /* Day of month and month */
6334 if (!res)
6335 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6336 if (!res)
6337 res = wait_file(chan, ints, "digits/pt-de", lang);
6338 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6339 if (!res)
6340 res = wait_file(chan, ints, fn, lang);
6342 } else if (daydiff) {
6343 /* Just what day of the week */
6344 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6345 if (!res)
6346 res = wait_file(chan, ints, fn, lang);
6347 } /* Otherwise, it was today */
6348 if (!strcasecmp(lang, "pt_BR")) {
6349 if (tm.tm_hour > 1) {
6350 snprintf(fn, sizeof(fn), "digits/pt-as");
6351 } else {
6352 snprintf(fn, sizeof(fn), "digits/pt-a");
6354 if (!res)
6355 res = wait_file(chan, ints, fn, lang);
6356 } else {
6357 snprintf(fn, sizeof(fn), "digits/pt-ah");
6358 if (!res)
6359 res = wait_file(chan, ints, fn, lang);
6360 if (tm.tm_hour != 1)
6361 if (!res)
6362 res = wait_file(chan, ints, "digits/pt-sss", lang);
6363 if (!res)
6364 res = ast_say_time(chan, t, ints, lang);
6366 return res;
6369 /* Hebrew syntax */
6370 int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6372 int res = 0;
6373 time_t nowt;
6374 int daydiff;
6375 struct tm tm;
6376 struct tm now;
6377 char fn[256];
6379 time(&nowt);
6381 ast_localtime(&t, &tm, NULL);
6382 ast_localtime(&nowt, &now, NULL);
6383 daydiff = now.tm_yday - tm.tm_yday;
6384 if ((daydiff < 0) || (daydiff > 6)) {
6385 /* Day of month and month */
6386 if (!res) {
6387 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6388 res = ast_streamfile(chan, fn, lang);
6389 if (!res)
6390 res = ast_waitstream(chan, ints);
6392 if (!res) {
6393 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
6395 } else if (daydiff) {
6396 /* Just what day of the week */
6397 if (!res) {
6398 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6399 res = ast_streamfile(chan, fn, lang);
6400 if (!res) {
6401 res = ast_waitstream(chan, ints);
6404 } /* Otherwise, it was today */
6405 if (!res) {
6406 res = ast_say_time(chan, t, ints, lang);
6408 return res;
6411 /*********************************** GREEK SUPPORT ***************************************/
6415 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
6417 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
6418 int tmp;
6419 int left;
6420 int res;
6421 char fn[256] = "";
6423 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
6424 if (num < 5) {
6425 snprintf(fn, sizeof(fn), "digits/female-%d", num);
6426 res = wait_file(chan, ints, fn, lang);
6427 } else if (num < 13) {
6428 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
6429 } else if (num <100 ) {
6430 tmp = (num/10) * 10;
6431 left = num - tmp;
6432 snprintf(fn, sizeof(fn), "digits/%d", tmp);
6433 res = ast_streamfile(chan, fn, lang);
6434 if (!res)
6435 res = ast_waitstream(chan, ints);
6436 if (left)
6437 gr_say_number_female(left, chan, ints, lang);
6439 } else {
6440 return -1;
6442 return res;
6448 * A list of the files that you need to create
6449 -> digits/xilia = "xilia"
6450 -> digits/myrio = "ekatomyrio"
6451 -> digits/thousands = "xiliades"
6452 -> digits/millions = "ektatomyria"
6453 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6454 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6455 e,g 80 = "ogdonta"
6456 Here we must note that we use digits/tens/100 to utter "ekato"
6457 and digits/hundred-100 to utter "ekaton"
6458 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6459 "terakosia". Here again we use hundreds/1000 for "xilia"
6460 and digits/thousnds for "xiliades"
6463 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
6465 int res = 0;
6466 char fn[256] = "";
6467 int i=0;
6470 if (!num) {
6471 snprintf(fn, sizeof(fn), "digits/0");
6472 res = ast_streamfile(chan, fn, chan->language);
6473 if (!res)
6474 return ast_waitstream(chan, ints);
6477 while (!res && num ) {
6478 i++;
6479 if (num < 13) {
6480 snprintf(fn, sizeof(fn), "digits/%d", num);
6481 num = 0;
6482 } else if (num <= 100) {
6483 /* 13 < num <= 100 */
6484 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
6485 num -= ((num / 10) * 10);
6486 } else if (num < 200) {
6487 /* 100 < num < 200 */
6488 snprintf(fn, sizeof(fn), "digits/hundred-100");
6489 num -= ((num / 100) * 100);
6490 } else if (num < 1000) {
6491 /* 200 < num < 1000 */
6492 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
6493 num -= ((num / 100) * 100);
6494 } else if (num < 2000){
6495 snprintf(fn, sizeof(fn), "digits/xilia");
6496 num -= ((num / 1000) * 1000);
6497 } else {
6498 /* num > 1000 */
6499 if (num < 1000000) {
6500 res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
6501 if (res)
6502 return res;
6503 num = num % 1000;
6504 snprintf(fn, sizeof(fn), "digits/thousands");
6505 } else {
6506 if (num < 1000000000) { /* 1,000,000,000 */
6507 res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
6508 if (res)
6509 return res;
6510 num = num % 1000000;
6511 snprintf(fn, sizeof(fn), "digits/millions");
6512 } else {
6513 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
6514 res = -1;
6518 if (!res) {
6519 if (!ast_streamfile(chan, fn, language)) {
6520 if ((audiofd > -1) && (ctrlfd > -1))
6521 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6522 else
6523 res = ast_waitstream(chan, ints);
6525 ast_stopstream(chan);
6528 return res;
6533 * The format is weekday - day - month -year
6535 * A list of the files that you need to create
6536 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6537 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6538 Attention the months are in
6539 "gekinh klhsh"
6543 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6545 struct tm tm;
6547 char fn[256];
6548 int res = 0;
6551 ast_localtime(&t,&tm,NULL);
6552 /* W E E K - D A Y */
6553 if (!res) {
6554 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6555 res = ast_streamfile(chan, fn, lang);
6556 if (!res)
6557 res = ast_waitstream(chan, ints);
6559 /* D A Y */
6560 if (!res) {
6561 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6563 /* M O N T H */
6564 if (!res) {
6565 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6566 res = ast_streamfile(chan, fn, lang);
6567 if (!res)
6568 res = ast_waitstream(chan, ints);
6570 /* Y E A R */
6571 if (!res)
6572 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6573 return res;
6578 /* A list of the files that you need to create
6579 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6580 * digits/kai : "KAI"
6581 * didgits : "h wra"
6582 * digits/p-m : "meta meshmbrias"
6583 * digits/a-m : "pro meshmbrias"
6586 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6589 struct tm tm;
6590 int res = 0;
6591 int hour, pm=0;
6593 ast_localtime(&t, &tm, NULL);
6594 hour = tm.tm_hour;
6596 if (!hour)
6597 hour = 12;
6598 else if (hour == 12)
6599 pm = 1;
6600 else if (hour > 12) {
6601 hour -= 12;
6602 pm = 1;
6605 res = gr_say_number_female(hour, chan, ints, lang);
6606 if (tm.tm_min) {
6607 if (!res)
6608 res = ast_streamfile(chan, "digits/kai", lang);
6609 if (!res)
6610 res = ast_waitstream(chan, ints);
6611 if (!res)
6612 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6613 } else {
6614 if (!res)
6615 res = ast_streamfile(chan, "digits/hwra", lang);
6616 if (!res)
6617 res = ast_waitstream(chan, ints);
6619 if (pm) {
6620 if (!res)
6621 res = ast_streamfile(chan, "digits/p-m", lang);
6622 } else {
6623 if (!res)
6624 res = ast_streamfile(chan, "digits/a-m", lang);
6626 if (!res)
6627 res = ast_waitstream(chan, ints);
6628 return res;
6633 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6635 struct tm tm;
6636 char fn[256];
6637 int res = 0;
6639 ast_localtime(&t, &tm, NULL);
6642 /* W E E K - D A Y */
6643 if (!res) {
6644 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6645 res = ast_streamfile(chan, fn, lang);
6646 if (!res)
6647 res = ast_waitstream(chan, ints);
6649 /* D A Y */
6650 if (!res) {
6651 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6653 /* M O N T H */
6654 if (!res) {
6655 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6656 res = ast_streamfile(chan, fn, lang);
6657 if (!res)
6658 res = ast_waitstream(chan, ints);
6661 res = ast_say_time_gr(chan, t, ints, lang);
6662 return res;
6665 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)
6668 struct tm tm;
6669 int res=0, offset, sndoffset;
6670 char sndfile[256], nextmsg[256];
6672 if (!format)
6673 format = "AdBY 'digits/at' IMp";
6675 ast_localtime(&time,&tm,timezone);
6677 for (offset=0 ; format[offset] != '\0' ; offset++) {
6678 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6679 switch (format[offset]) {
6680 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6681 case '\'':
6682 /* Literal name of a sound file */
6683 sndoffset=0;
6684 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
6685 sndfile[sndoffset] = format[offset];
6686 sndfile[sndoffset] = '\0';
6687 res = wait_file(chan,ints,sndfile,lang);
6688 break;
6689 case 'A':
6690 case 'a':
6691 /* Sunday - Saturday */
6692 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6693 res = wait_file(chan,ints,nextmsg,lang);
6694 break;
6695 case 'B':
6696 case 'b':
6697 case 'h':
6698 /* January - December */
6699 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6700 res = wait_file(chan,ints,nextmsg,lang);
6701 break;
6702 case 'd':
6703 case 'e':
6704 /* first - thirtyfirst */
6705 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6706 break;
6707 case 'Y':
6708 /* Year */
6710 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
6711 break;
6712 case 'I':
6713 case 'l':
6714 /* 12-Hour */
6715 if (tm.tm_hour == 0)
6716 gr_say_number_female(12, chan, ints, lang);
6717 else if (tm.tm_hour > 12)
6718 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
6719 else
6720 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6721 break;
6722 case 'H':
6723 case 'k':
6724 /* 24-Hour */
6725 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6726 break;
6727 case 'M':
6728 /* Minute */
6729 if (tm.tm_min) {
6730 if (!res)
6731 res = ast_streamfile(chan, "digits/kai", lang);
6732 if (!res)
6733 res = ast_waitstream(chan, ints);
6734 if (!res)
6735 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
6736 } else {
6737 if (!res)
6738 res = ast_streamfile(chan, "digits/oclock", lang);
6739 if (!res)
6740 res = ast_waitstream(chan, ints);
6742 break;
6743 case 'P':
6744 case 'p':
6745 /* AM/PM */
6746 if (tm.tm_hour > 11)
6747 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
6748 else
6749 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
6750 res = wait_file(chan,ints,nextmsg,lang);
6751 break;
6752 case 'Q':
6753 /* Shorthand for "Today", "Yesterday", or ABdY */
6754 /* XXX As emphasized elsewhere, this should the native way in your
6755 * language to say the date, with changes in what you say, depending
6756 * upon how recent the date is. XXX */
6758 struct timeval now;
6759 struct tm tmnow;
6760 time_t beg_today, tt;
6762 gettimeofday(&now,NULL);
6763 tt = now.tv_sec;
6764 ast_localtime(&tt,&tmnow,timezone);
6765 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6766 /* In any case, it saves not having to do ast_mktime() */
6767 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6768 if (beg_today < time) {
6769 /* Today */
6770 res = wait_file(chan,ints, "digits/today",lang);
6771 } else if (beg_today - 86400 < time) {
6772 /* Yesterday */
6773 res = wait_file(chan,ints, "digits/yesterday",lang);
6774 } else {
6775 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6778 break;
6779 case 'q':
6780 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6781 /* XXX As emphasized elsewhere, this should the native way in your
6782 * language to say the date, with changes in what you say, depending
6783 * upon how recent the date is. XXX */
6785 struct timeval now;
6786 struct tm tmnow;
6787 time_t beg_today, tt;
6789 gettimeofday(&now,NULL);
6790 tt = now.tv_sec;
6791 ast_localtime(&tt,&tmnow,timezone);
6792 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6793 /* In any case, it saves not having to do ast_mktime() */
6794 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6795 if (beg_today < time) {
6796 /* Today */
6797 } else if ((beg_today - 86400) < time) {
6798 /* Yesterday */
6799 res = wait_file(chan,ints, "digits/yesterday",lang);
6800 } else if (beg_today - 86400 * 6 < time) {
6801 /* Within the last week */
6802 res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
6803 } else {
6804 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6807 break;
6808 case 'R':
6809 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
6810 break;
6811 case 'S':
6812 /* Seconds */
6813 snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
6814 res = wait_file(chan,ints,nextmsg,lang);
6815 if (!res)
6816 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
6817 if (!res)
6818 snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
6819 res = wait_file(chan,ints,nextmsg,lang);
6820 break;
6821 case 'T':
6822 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
6823 break;
6824 case ' ':
6825 case ' ':
6826 /* Just ignore spaces and tabs */
6827 break;
6828 default:
6829 /* Unknown character */
6830 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6832 /* Jump out on DTMF */
6833 if (res) {
6834 break;
6837 return res;
6843 /*********************************** Georgian Support ***************************************/
6847 Convert a number into a semi-localized string. Only for Georgian.
6848 res must be of at least 256 bytes, preallocated.
6849 The output corresponds to Georgian spoken numbers, so
6850 it may be either converted to real words by applying a direct conversion
6851 table, or played just by substituting the entities with played files.
6853 Output may consist of the following tokens (separated by spaces):
6854 0, minus.
6855 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
6856 10-19.
6857 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
6858 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
6859 1000, 1000_. (atasi, atas).
6860 1000000, 1000000_. (milioni, milion).
6861 1000000000, 1000000000_. (miliardi, miliard).
6863 To be able to play the sounds, each of the above tokens needs
6864 a corresponding sound file. (e.g. 200_.gsm).
6866 static char* ast_translate_number_ge(int num, char* res, int res_len)
6868 char buf[256];
6869 int digit = 0;
6870 int remainder = 0;
6873 if (num < 0) {
6874 strncat(res, "minus ", res_len - strlen(res) - 1);
6875 if ( num > INT_MIN ) {
6876 num = -num;
6877 } else {
6878 num = 0;
6883 /* directly read the numbers */
6884 if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
6885 snprintf(buf, sizeof(buf), "%d", num);
6886 strncat(res, buf, res_len - strlen(res) - 1);
6887 return res;
6891 if (num < 40) { /* ocda... */
6892 strncat(res, "20_ ", res_len - strlen(res) - 1);
6893 return ast_translate_number_ge(num - 20, res, res_len);
6896 if (num < 60) { /* ormocda... */
6897 strncat(res, "40_ ", res_len - strlen(res) - 1);
6898 return ast_translate_number_ge(num - 40, res, res_len);
6901 if (num < 80) { /* samocda... */
6902 strncat(res, "60_ ", res_len - strlen(res) - 1);
6903 return ast_translate_number_ge(num - 60, res, res_len);
6906 if (num < 100) { /* otxmocda... */
6907 strncat(res, "80_ ", res_len - strlen(res) - 1);
6908 return ast_translate_number_ge(num - 80, res, res_len);
6912 if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
6913 remainder = num % 100;
6914 digit = (num - remainder) / 100;
6916 if (remainder == 0) {
6917 snprintf(buf, sizeof(buf), "%d", num);
6918 strncat(res, buf, res_len - strlen(res) - 1);
6919 return res;
6920 } else {
6921 snprintf(buf, sizeof(buf), "%d_ ", digit*100);
6922 strncat(res, buf, res_len - strlen(res) - 1);
6923 return ast_translate_number_ge(remainder, res, res_len);
6928 if (num == 1000) {
6929 strncat(res, "1000", res_len - strlen(res) - 1);
6930 return res;
6934 if (num < 1000000) {
6935 remainder = num % 1000;
6936 digit = (num - remainder) / 1000;
6938 if (remainder == 0) {
6939 ast_translate_number_ge(digit, res, res_len);
6940 strncat(res, " 1000", res_len - strlen(res) - 1);
6941 return res;
6944 if (digit == 1) {
6945 strncat(res, "1000_ ", res_len - strlen(res) - 1);
6946 return ast_translate_number_ge(remainder, res, res_len);
6949 ast_translate_number_ge(digit, res, res_len);
6950 strncat(res, " 1000_ ", res_len - strlen(res) - 1);
6951 return ast_translate_number_ge(remainder, res, res_len);
6956 if (num == 1000000) {
6957 strncat(res, "1 1000000", res_len - strlen(res) - 1);
6958 return res;
6962 if (num < 1000000000) {
6963 remainder = num % 1000000;
6964 digit = (num - remainder) / 1000000;
6966 if (remainder == 0) {
6967 ast_translate_number_ge(digit, res, res_len);
6968 strncat(res, " 1000000", res_len - strlen(res) - 1);
6969 return res;
6972 ast_translate_number_ge(digit, res, res_len);
6973 strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
6974 return ast_translate_number_ge(remainder, res, res_len);
6979 if (num == 1000000000) {
6980 strncat(res, "1 1000000000", res_len - strlen(res) - 1);
6981 return res;
6985 if (num > 1000000000) {
6986 remainder = num % 1000000000;
6987 digit = (num - remainder) / 1000000000;
6989 if (remainder == 0) {
6990 ast_translate_number_ge(digit, res, res_len);
6991 strncat(res, " 1000000000", res_len - strlen(res) - 1);
6992 return res;
6995 ast_translate_number_ge(digit, res, res_len);
6996 strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
6997 return ast_translate_number_ge(remainder, res, res_len);
7001 return res;
7007 /*! \brief ast_say_number_full_ge: Georgian syntax */
7008 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)
7010 int res = 0;
7011 char fn[512] = "";
7012 char* s = 0;
7013 const char* remainder = fn;
7015 if (!num)
7016 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
7019 ast_translate_number_ge(num, fn, 512);
7023 while (res == 0 && (s = strstr(remainder, " "))) {
7024 size_t len = s - remainder;
7025 char* new_string = malloc(len + 1 + strlen("digits/"));
7027 sprintf(new_string, "digits/");
7028 strncat(new_string, remainder, len); /* we can't sprintf() it, it's not null-terminated. */
7029 /* new_string[len + strlen("digits/")] = '\0'; */
7031 if (!ast_streamfile(chan, new_string, language)) {
7032 if ((audiofd > -1) && (ctrlfd > -1))
7033 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
7034 else
7035 res = ast_waitstream(chan, ints);
7037 ast_stopstream(chan);
7039 free(new_string);
7041 remainder = s + 1; /* position just after the found space char. */
7042 while (*remainder == ' ') /* skip multiple spaces */
7043 remainder++;
7047 /* the last chunk. */
7048 if (res == 0 && *remainder) {
7050 char* new_string = malloc(strlen(remainder) + 1 + strlen("digits/"));
7051 sprintf(new_string, "digits/%s", remainder);
7053 if (!ast_streamfile(chan, new_string, language)) {
7054 if ((audiofd > -1) && (ctrlfd > -1))
7055 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
7056 else
7057 res = ast_waitstream(chan, ints);
7059 ast_stopstream(chan);
7061 free(new_string);
7066 return res;
7073 Georgian support for date/time requires the following files (*.gsm):
7075 mon-1, mon-2, ... (ianvari, tebervali, ...)
7076 day-1, day-2, ... (orshabati, samshabati, ...)
7077 saati_da
7078 tsuti
7079 tslis
7084 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
7085 static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7087 struct tm tm;
7088 char fn[256];
7089 int res = 0;
7090 ast_localtime(&t,&tm,NULL);
7092 if (!res)
7093 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7095 if (!res) {
7096 snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
7097 res = ast_streamfile(chan, fn, lang);
7098 if (!res)
7099 res = ast_waitstream(chan, ints);
7102 if (!res) {
7103 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
7104 /* if (!res)
7105 res = ast_waitstream(chan, ints);
7109 if (!res) {
7110 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7111 res = ast_streamfile(chan, fn, lang);
7112 if (!res)
7113 res = ast_waitstream(chan, ints);
7115 return res;
7123 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
7124 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7126 struct tm tm;
7127 int res = 0;
7129 ast_localtime(&t, &tm, NULL);
7131 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
7132 if (!res) {
7133 res = ast_streamfile(chan, "digits/saati_da", lang);
7134 if (!res)
7135 res = ast_waitstream(chan, ints);
7138 if (tm.tm_min) {
7139 if (!res) {
7140 res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
7142 if (!res) {
7143 res = ast_streamfile(chan, "digits/tsuti", lang);
7144 if (!res)
7145 res = ast_waitstream(chan, ints);
7149 return res;
7154 /* Georgian syntax. Say date, then say time. */
7155 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7157 struct tm tm;
7158 int res = 0;
7160 ast_localtime(&t, &tm, NULL);
7161 res = ast_say_date(chan, t, ints, lang);
7162 if (!res)
7163 ast_say_time(chan, t, ints, lang);
7164 return res;
7171 /* Georgian syntax */
7172 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7174 int res=0;
7175 time_t nowt;
7176 int daydiff;
7177 struct tm tm;
7178 struct tm now;
7179 char fn[256];
7181 time(&nowt);
7183 ast_localtime(&t, &tm, NULL);
7184 ast_localtime(&nowt, &now, NULL);
7185 daydiff = now.tm_yday - tm.tm_yday;
7186 if ((daydiff < 0) || (daydiff > 6)) {
7187 /* Day of month and month */
7188 if (!res)
7189 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7190 if (!res) {
7191 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7192 res = ast_streamfile(chan, fn, lang);
7193 if (!res)
7194 res = ast_waitstream(chan, ints);
7197 } else if (daydiff) {
7198 /* Just what day of the week */
7199 if (!res) {
7200 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7201 res = ast_streamfile(chan, fn, lang);
7202 if (!res)
7203 res = ast_waitstream(chan, ints);
7205 } /* Otherwise, it was today */
7206 if (!res)
7207 res = ast_say_time(chan, t, ints, lang);
7209 return res;
7215 * remap the 'say' functions to use those in this file
7217 static void __attribute__((constructor)) __say_init(void)
7219 ast_say_number_full = say_number_full;
7220 ast_say_enumeration_full = say_enumeration_full;
7221 ast_say_digit_str_full = say_digit_str_full;
7222 ast_say_character_str_full = say_character_str_full;
7223 ast_say_phonetic_str_full = say_phonetic_str_full;
7224 ast_say_datetime = say_datetime;
7225 ast_say_time = say_time;
7226 ast_say_date = say_date;
7227 ast_say_datetime_from_now = say_datetime_from_now;
7228 ast_say_date_with_format = say_date_with_format;