Merged revisions 111125 via svnmerge from
[asterisk-bristuff.git] / main / say.c
blobede9e98da96862db56142b11b0e1104a73d659bf
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);
357 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
358 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
359 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
360 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
361 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
362 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
363 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
364 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 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_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
368 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);
369 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);
370 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);
371 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);
372 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);
373 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);
374 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);
375 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);
376 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);
377 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);
378 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);
380 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
381 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
382 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
383 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
384 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
385 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
386 static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
387 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
388 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
390 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
391 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
392 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
400 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
402 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
403 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
405 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
407 int res;
408 if ((res = ast_streamfile(chan, file, lang)))
409 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
410 if (!res)
411 res = ast_waitstream(chan, ints);
412 return res;
415 /*! \brief ast_say_number_full: call language-specific functions */
416 /* Called from AGI */
417 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
419 if (!strcasecmp(language,"en") ) { /* English syntax */
420 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
421 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
422 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
423 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
424 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
425 } else if (!strcasecmp(language, "de") ) { /* German syntax */
426 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
427 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
428 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
429 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
430 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
431 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
432 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
433 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
434 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
435 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
436 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
437 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
438 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
439 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
440 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
441 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
442 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
443 } else if (!strcasecmp(language, "pt") || !strcasecmp(language, "pt_BR")) { /* Portuguese syntax */
444 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
445 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
446 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
447 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
448 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
449 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
450 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
451 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
452 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
453 } else if (!strcasecmp(language, "ge") ) { /* Georgian syntax */
454 return(ast_say_number_full_ge(chan, num, ints, language, options, audiofd, ctrlfd));
457 /* Default to english */
458 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
461 /*! \brief ast_say_number_full_en: English syntax */
462 /* This is the default syntax, if no other syntax defined in this file is used */
463 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
465 int res = 0;
466 int playh = 0;
467 char fn[256] = "";
468 if (!num)
469 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
471 while (!res && (num || playh)) {
472 if (num < 0) {
473 snprintf(fn, sizeof(fn), "digits/minus");
474 if ( num > INT_MIN ) {
475 num = -num;
476 } else {
477 num = 0;
479 } else if (playh) {
480 snprintf(fn, sizeof(fn), "digits/hundred");
481 playh = 0;
482 } else if (num < 20) {
483 snprintf(fn, sizeof(fn), "digits/%d", num);
484 num = 0;
485 } else if (num < 100) {
486 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
487 num -= ((num / 10) * 10);
488 } else {
489 if (num < 1000){
490 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
491 playh++;
492 num -= ((num / 100) * 100);
493 } else {
494 if (num < 1000000) { /* 1,000,000 */
495 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
496 if (res)
497 return res;
498 num = num % 1000;
499 snprintf(fn, sizeof(fn), "digits/thousand");
500 } else {
501 if (num < 1000000000) { /* 1,000,000,000 */
502 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
503 if (res)
504 return res;
505 num = num % 1000000;
506 snprintf(fn, sizeof(fn), "digits/million");
507 } else {
508 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
509 res = -1;
514 if (!res) {
515 if (!ast_streamfile(chan, fn, language)) {
516 if ((audiofd > -1) && (ctrlfd > -1))
517 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
518 else
519 res = ast_waitstream(chan, ints);
521 ast_stopstream(chan);
524 return res;
527 static int exp10_int(int power)
529 int x, res= 1;
530 for (x=0;x<power;x++)
531 res *= 10;
532 return res;
535 /*! \brief ast_say_number_full_cz: Czech syntax */
536 /* files needed:
537 * 1m,2m - gender male
538 * 1w,2w - gender female
539 * 3,4,...,20
540 * 30,40,...,90
542 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
544 * for each number 10^(3n + 3) exist 3 files represented as:
545 * 1 tousand = jeden tisic = 1_E3
546 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
547 * 5,6,... tousands = pet,sest,... tisic = 5_E3
549 * million = _E6
550 * miliard = _E9
551 * etc...
553 * tousand, milion are gender male, so 1 and 2 is 1m 2m
554 * miliard is gender female, so 1 and 2 is 1w 2w
556 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)
558 int res = 0;
559 int playh = 0;
560 char fn[256] = "";
562 int hundered = 0;
563 int left = 0;
564 int length = 0;
566 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
567 if (!options)
568 options = "w";
570 if (!num)
571 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
573 while (!res && (num || playh)) {
574 if (num < 0) {
575 snprintf(fn, sizeof(fn), "digits/minus");
576 if ( num > INT_MIN ) {
577 num = -num;
578 } else {
579 num = 0;
581 } else if (num < 3 ) {
582 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
583 playh = 0;
584 num = 0;
585 } else if (num < 20) {
586 snprintf(fn, sizeof(fn), "digits/%d",num);
587 playh = 0;
588 num = 0;
589 } else if (num < 100) {
590 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
591 num -= ((num / 10) * 10);
592 } else if (num < 1000) {
593 hundered = num / 100;
594 if ( hundered == 1 ) {
595 snprintf(fn, sizeof(fn), "digits/1sto");
596 } else if ( hundered == 2 ) {
597 snprintf(fn, sizeof(fn), "digits/2ste");
598 } else {
599 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
600 if (res)
601 return res;
602 if (hundered == 3 || hundered == 4) {
603 snprintf(fn, sizeof(fn), "digits/sta");
604 } else if ( hundered > 4 ) {
605 snprintf(fn, sizeof(fn), "digits/set");
608 num -= (hundered * 100);
609 } else { /* num > 1000 */
610 length = (int)log10(num)+1;
611 while ( (length % 3 ) != 1 ) {
612 length--;
614 left = num / (exp10_int(length-1));
615 if ( left == 2 ) {
616 switch (length-1) {
617 case 9: options = "w"; /* 1,000,000,000 gender female */
618 break;
619 default : options = "m"; /* others are male */
622 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
623 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
624 if (res)
625 return res;
627 if ( left >= 5 ) { /* >= 5 have the same declesion */
628 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
629 } else if ( left >= 2 && left <= 4 ) {
630 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
631 } else { /* left == 1 */
632 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
634 num -= left * (exp10_int(length-1));
636 if (!res) {
637 if (!ast_streamfile(chan, fn, language)) {
638 if ((audiofd > -1) && (ctrlfd > -1)) {
639 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
640 } else {
641 res = ast_waitstream(chan, ints);
644 ast_stopstream(chan);
647 return res;
650 /*! \brief ast_say_number_full_da: Danish syntax */
651 /* New files:
652 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
654 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)
656 int res = 0;
657 int playh = 0;
658 int playa = 0;
659 int cn = 1; /* +1 = commune; -1 = neuter */
660 char fn[256] = "";
661 if (!num)
662 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
664 if (options && !strncasecmp(options, "n",1)) cn = -1;
666 while (!res && (num || playh || playa )) {
667 /* The grammar for Danish numbers is the same as for English except
668 * for the following:
669 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
670 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
671 * "one-and twenty" and 68 is "eight-and sixty".
672 * - "million" is different in singular and plural form
673 * - numbers > 1000 with zero as the third digit from last have an
674 * "and" before the last two digits, i.e. 2034 is "two thousand and
675 * four-and thirty" and 1000012 is "one million and twelve".
677 if (num < 0) {
678 snprintf(fn, sizeof(fn), "digits/minus");
679 if ( num > INT_MIN ) {
680 num = -num;
681 } else {
682 num = 0;
684 } else if (playh) {
685 snprintf(fn, sizeof(fn), "digits/hundred");
686 playh = 0;
687 } else if (playa) {
688 snprintf(fn, sizeof(fn), "digits/and");
689 playa = 0;
690 } else if (num == 1 && cn == -1) {
691 snprintf(fn, sizeof(fn), "digits/1N");
692 num = 0;
693 } else if (num < 20) {
694 snprintf(fn, sizeof(fn), "digits/%d", num);
695 num = 0;
696 } else if (num < 100) {
697 int ones = num % 10;
698 if (ones) {
699 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
700 num -= ones;
701 } else {
702 snprintf(fn, sizeof(fn), "digits/%d", num);
703 num = 0;
705 } else {
706 if (num < 1000) {
707 int hundreds = num / 100;
708 if (hundreds == 1)
709 snprintf(fn, sizeof(fn), "digits/1N");
710 else
711 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
713 playh++;
714 num -= 100 * hundreds;
715 if (num)
716 playa++;
718 } else {
719 if (num < 1000000) {
720 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
721 if (res)
722 return res;
723 num = num % 1000;
724 snprintf(fn, sizeof(fn), "digits/thousand");
725 } else {
726 if (num < 1000000000) {
727 int millions = num / 1000000;
728 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
729 if (res)
730 return res;
731 if (millions == 1)
732 snprintf(fn, sizeof(fn), "digits/million");
733 else
734 snprintf(fn, sizeof(fn), "digits/millions");
735 num = num % 1000000;
736 } else {
737 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
738 res = -1;
741 if (num && num < 100)
742 playa++;
745 if (!res) {
746 if (!ast_streamfile(chan, fn, language)) {
747 if ((audiofd > -1) && (ctrlfd > -1))
748 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
749 else
750 res = ast_waitstream(chan, ints);
752 ast_stopstream(chan);
755 return res;
758 /*! \brief ast_say_number_full_de: German syntax */
759 /* New files:
760 In addition to English, the following sounds are required:
761 "millions"
762 "1-and" through "9-and"
763 "1F" (eine)
764 "1N" (ein)
765 NB "1" is recorded as 'eins'
767 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)
769 int res = 0, t = 0;
770 int mf = 1; /* +1 = male and neuter; -1 = female */
771 char fn[256] = "";
772 char fna[256] = "";
773 if (!num)
774 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
776 if (options && (!strncasecmp(options, "f",1)))
777 mf = -1;
779 while (!res && num) {
780 /* The grammar for German numbers is the same as for English except
781 * for the following:
782 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
783 * "one-and twenty" and 68 is "eight-and sixty".
784 * - "one" varies according to gender
785 * - 100 is 'hundert', however all other instances are 'ein hundert'
786 * - 1000 is 'tausend', however all other instances are 'ein tausend'
787 * - 1000000 is always 'eine million'
788 * - "million" is different in singular and plural form
790 if (num < 0) {
791 snprintf(fn, sizeof(fn), "digits/minus");
792 if ( num > INT_MIN ) {
793 num = -num;
794 } else {
795 num = 0;
797 } else if (num < 100 && t) {
798 snprintf(fn, sizeof(fn), "digits/and");
799 t = 0;
800 } else if (num == 1 && mf == -1) {
801 snprintf(fn, sizeof(fn), "digits/%dF", num);
802 num = 0;
803 } else if (num < 20) {
804 snprintf(fn, sizeof(fn), "digits/%d", num);
805 num = 0;
806 } else if (num < 100) {
807 int ones = num % 10;
808 if (ones) {
809 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
810 num -= ones;
811 } else {
812 snprintf(fn, sizeof(fn), "digits/%d", num);
813 num = 0;
815 } else if (num == 100 && t == 0) {
816 snprintf(fn, sizeof(fn), "digits/hundred");
817 num = 0;
818 } else if (num < 1000) {
819 int hundreds = num / 100;
820 num = num % 100;
821 if (hundreds == 1) {
822 snprintf(fn, sizeof(fn), "digits/1N");
823 } else {
824 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
826 snprintf(fna, sizeof(fna), "digits/hundred");
827 t = 1;
828 } else if (num == 1000 && t == 0) {
829 snprintf(fn, sizeof(fn), "digits/thousand");
830 num = 0;
831 } else if (num < 1000000) {
832 int thousands = num / 1000;
833 num = num % 1000;
834 t = 1;
835 if (thousands == 1) {
836 snprintf(fn, sizeof(fn), "digits/1N");
837 snprintf(fna, sizeof(fna), "digits/thousand");
838 } else {
839 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
840 if (res)
841 return res;
842 snprintf(fn, sizeof(fn), "digits/thousand");
844 } else if (num < 1000000000) {
845 int millions = num / 1000000;
846 num = num % 1000000;
847 t = 1;
848 if (millions == 1) {
849 snprintf(fn, sizeof(fn), "digits/1F");
850 snprintf(fna, sizeof(fna), "digits/million");
851 } else {
852 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
853 if (res)
854 return res;
855 snprintf(fn, sizeof(fn), "digits/millions");
857 } else if (num <= INT_MAX) {
858 int billions = num / 1000000000;
859 num = num % 1000000000;
860 t = 1;
861 if (billions == 1) {
862 snprintf(fn, sizeof(fn), "digits/1F");
863 snprintf(fna, sizeof(fna), "digits/milliard");
864 } else {
865 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
866 if (res) {
867 return res;
869 snprintf(fn, sizeof(fn), "digits/milliards");
871 } else {
872 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
873 res = -1;
875 if (!res) {
876 if (!ast_streamfile(chan, fn, language)) {
877 if ((audiofd > -1) && (ctrlfd > -1))
878 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
879 else
880 res = ast_waitstream(chan, ints);
882 ast_stopstream(chan);
883 if (!res) {
884 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
885 if ((audiofd > -1) && (ctrlfd > -1))
886 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
887 else
888 res = ast_waitstream(chan, ints);
890 ast_stopstream(chan);
891 strcpy(fna, "");
895 return res;
898 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
899 /* New files:
900 In addition to American English, the following sounds are required: "and"
902 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
904 int res = 0;
905 int playh = 0;
906 int playa = 0;
907 char fn[256] = "";
908 if (!num)
909 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
911 while (!res && (num || playh || playa )) {
912 if (num < 0) {
913 snprintf(fn, sizeof(fn), "digits/minus");
914 if ( num > INT_MIN ) {
915 num = -num;
916 } else {
917 num = 0;
919 } else if (playh) {
920 snprintf(fn, sizeof(fn), "digits/hundred");
921 playh = 0;
922 } else if (playa) {
923 snprintf(fn, sizeof(fn), "digits/and");
924 playa = 0;
925 } else if (num < 20) {
926 snprintf(fn, sizeof(fn), "digits/%d", num);
927 num = 0;
928 } else if (num < 100) {
929 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
930 num -= ((num / 10) * 10);
931 } else if (num < 1000) {
932 int hundreds = num / 100;
933 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
935 playh++;
936 num -= 100 * hundreds;
937 if (num)
938 playa++;
939 } else if (num < 1000000) {
940 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
941 if (res)
942 return res;
943 snprintf(fn, sizeof(fn), "digits/thousand");
944 num = num % 1000;
945 if (num && num < 100)
946 playa++;
947 } else if (num < 1000000000) {
948 int millions = num / 1000000;
949 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
950 if (res)
951 return res;
952 snprintf(fn, sizeof(fn), "digits/million");
953 num = num % 1000000;
954 if (num && num < 100)
955 playa++;
956 } else {
957 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
958 res = -1;
961 if (!res) {
962 if (!ast_streamfile(chan, fn, language)) {
963 if ((audiofd > -1) && (ctrlfd > -1))
964 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
965 else
966 res = ast_waitstream(chan, ints);
968 ast_stopstream(chan);
971 return res;
974 /*! \brief ast_say_number_full_es: Spanish syntax */
975 /* New files:
976 Requires a few new audios:
977 1F.gsm: feminine 'una'
978 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
980 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)
982 int res = 0;
983 int playa = 0;
984 int mf = 0; /* +1 = male; -1 = female */
985 char fn[256] = "";
986 if (!num)
987 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
989 if (options) {
990 if (!strncasecmp(options, "f",1))
991 mf = -1;
992 else if (!strncasecmp(options, "m", 1))
993 mf = 1;
996 while (!res && num) {
997 if (num < 0) {
998 snprintf(fn, sizeof(fn), "digits/minus");
999 if ( num > INT_MIN ) {
1000 num = -num;
1001 } else {
1002 num = 0;
1004 } else if (playa) {
1005 snprintf(fn, sizeof(fn), "digits/and");
1006 playa = 0;
1007 } else if (num == 1) {
1008 if (mf < 0)
1009 snprintf(fn, sizeof(fn), "digits/%dF", num);
1010 else if (mf > 0)
1011 snprintf(fn, sizeof(fn), "digits/%dM", num);
1012 else
1013 snprintf(fn, sizeof(fn), "digits/%d", num);
1014 num = 0;
1015 } else if (num < 31) {
1016 snprintf(fn, sizeof(fn), "digits/%d", num);
1017 num = 0;
1018 } else if (num < 100) {
1019 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1020 num -= ((num/10)*10);
1021 if (num)
1022 playa++;
1023 } else if (num == 100) {
1024 snprintf(fn, sizeof(fn), "digits/100");
1025 num = 0;
1026 } else if (num < 200) {
1027 snprintf(fn, sizeof(fn), "digits/100-and");
1028 num -= 100;
1029 } else {
1030 if (num < 1000) {
1031 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1032 num -= ((num/100)*100);
1033 } else if (num < 2000) {
1034 num = num % 1000;
1035 snprintf(fn, sizeof(fn), "digits/thousand");
1036 } else {
1037 if (num < 1000000) {
1038 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1039 if (res)
1040 return res;
1041 num = num % 1000;
1042 snprintf(fn, sizeof(fn), "digits/thousand");
1043 } else {
1044 if (num < 2147483640) {
1045 if ((num/1000000) == 1) {
1046 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1047 if (res)
1048 return res;
1049 snprintf(fn, sizeof(fn), "digits/million");
1050 } else {
1051 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1052 if (res)
1053 return res;
1054 snprintf(fn, sizeof(fn), "digits/millions");
1056 num = num % 1000000;
1057 } else {
1058 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1059 res = -1;
1065 if (!res) {
1066 if (!ast_streamfile(chan, fn, language)) {
1067 if ((audiofd > -1) && (ctrlfd > -1))
1068 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1069 else
1070 res = ast_waitstream(chan, ints);
1072 ast_stopstream(chan);
1077 return res;
1080 /*! \brief ast_say_number_full_fr: French syntax */
1081 /* Extra sounds needed:
1082 1F: feminin 'une'
1083 et: 'and' */
1084 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)
1086 int res = 0;
1087 int playh = 0;
1088 int playa = 0;
1089 int mf = 1; /* +1 = male; -1 = female */
1090 char fn[256] = "";
1091 if (!num)
1092 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1094 if (options && !strncasecmp(options, "f",1))
1095 mf = -1;
1097 while (!res && (num || playh || playa)) {
1098 if (num < 0) {
1099 snprintf(fn, sizeof(fn), "digits/minus");
1100 if ( num > INT_MIN ) {
1101 num = -num;
1102 } else {
1103 num = 0;
1105 } else if (playh) {
1106 snprintf(fn, sizeof(fn), "digits/hundred");
1107 playh = 0;
1108 } else if (playa) {
1109 snprintf(fn, sizeof(fn), "digits/et");
1110 playa = 0;
1111 } else if (num == 1) {
1112 if (mf < 0)
1113 snprintf(fn, sizeof(fn), "digits/%dF", num);
1114 else
1115 snprintf(fn, sizeof(fn), "digits/%d", num);
1116 num = 0;
1117 } else if (num < 21) {
1118 snprintf(fn, sizeof(fn), "digits/%d", num);
1119 num = 0;
1120 } else if (num < 70) {
1121 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1122 if ((num % 10) == 1) playa++;
1123 num = num % 10;
1124 } else if (num < 80) {
1125 snprintf(fn, sizeof(fn), "digits/60");
1126 if ((num % 10) == 1) playa++;
1127 num = num - 60;
1128 } else if (num < 100) {
1129 snprintf(fn, sizeof(fn), "digits/80");
1130 num = num - 80;
1131 } else if (num < 200) {
1132 snprintf(fn, sizeof(fn), "digits/hundred");
1133 num = num - 100;
1134 } else if (num < 1000) {
1135 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1136 playh++;
1137 num = num % 100;
1138 } else if (num < 2000) {
1139 snprintf(fn, sizeof(fn), "digits/thousand");
1140 num = num - 1000;
1141 } else if (num < 1000000) {
1142 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1143 if (res)
1144 return res;
1145 snprintf(fn, sizeof(fn), "digits/thousand");
1146 num = num % 1000;
1147 } else if (num < 1000000000) {
1148 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1149 if (res)
1150 return res;
1151 snprintf(fn, sizeof(fn), "digits/million");
1152 num = num % 1000000;
1153 } else {
1154 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1155 res = -1;
1157 if (!res) {
1158 if (!ast_streamfile(chan, fn, language)) {
1159 if ((audiofd > -1) && (ctrlfd > -1))
1160 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1161 else
1162 res = ast_waitstream(chan, ints);
1164 ast_stopstream(chan);
1167 return res;
1172 /*! \brief ast_say_number_full_he: Hebrew syntax */
1173 /* Extra sounds needed:
1174 1F: feminin 'one'
1175 ve: 'and'
1176 1hundred: 1 hundred
1177 2hundred: 2 hundreds
1178 2thousands: 2 thousand
1179 thousands: plural of 'thousand'
1180 3sF 'Smichut forms (female)
1187 3s 'Smichut' forms (male)
1204 TODO: 've' should sometimed be 'hu':
1205 * before 'shtaym' (2, F)
1206 * before 'shnaym' (2, M)
1207 * before 'shlosha' (3, M)
1208 * before 'shmone' (8, M)
1209 * before 'shlosim' (30)
1210 * before 'shmonim' (80)
1212 What about:
1213 'sheva' (7, F)?
1214 'tesha' (9, F)?
1216 #define SAY_NUM_BUF_SIZE 256
1217 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1218 const char *ints, const char *language, const char *options,
1219 int audiofd, int ctrlfd)
1221 int res = 0;
1222 int state = 0; /* no need to save anything */
1223 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1224 char fn[SAY_NUM_BUF_SIZE] = "";
1225 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
1226 "num: %d, options=\"%s\"\n",
1227 num, options
1229 if (!num)
1230 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1232 if (options && !strncasecmp(options, "f",1))
1233 mf = -1;
1235 /* Do we have work to do? */
1236 while (!res && (num || (state>0) )) {
1237 /* first type of work: play a second sound. In this loop
1238 * we can only play one sound file at a time. Thus playing
1239 * a second one requires repeating the loop just for the
1240 * second file. The variable 'state' remembers where we were.
1241 * state==0 is the normal mode and it means that we continue
1242 * to check if the number num has yet anything left.
1244 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
1245 "state=%d, options=\"%s\", mf=%d\n",
1246 num, state, options, mf
1248 if (state==1) {
1249 snprintf(fn, sizeof(fn), "digits/hundred");
1250 state = 0;
1251 } else if (state==2) {
1252 snprintf(fn, sizeof(fn), "digits/ve");
1253 state = 0;
1254 } else if (state==3) {
1255 snprintf(fn, sizeof(fn), "digits/thousands");
1256 state=0;
1257 } else if (num <21) {
1258 if (mf < 0)
1259 snprintf(fn, sizeof(fn), "digits/%dF", num);
1260 else
1261 snprintf(fn, sizeof(fn), "digits/%d", num);
1262 num = 0;
1263 } else if (num < 100) {
1264 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1265 num = num % 10;
1266 if (num>0) state=2;
1267 } else if (num < 200) {
1268 snprintf(fn, sizeof(fn), "digits/1hundred");
1269 num = num - 100;
1270 state=2;
1271 } else if (num < 300) {
1272 snprintf(fn, sizeof(fn), "digits/2hundred");
1273 num = num - 200;
1274 state=2;
1275 } else if (num < 1000) {
1276 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1277 state=1;
1278 num = num % 100;
1279 } else if (num < 2000) {
1280 snprintf(fn, sizeof(fn), "digits/thousand");
1281 num = num - 1000;
1282 } else if (num < 3000) {
1283 snprintf(fn, sizeof(fn), "digits/2thousand");
1284 num = num - 2000;
1285 if (num>0) state=2;
1286 } else if (num < 20000) {
1287 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1288 num = num % 1000;
1289 state=3;
1290 } else if (num < 1000000) {
1291 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1292 if (res)
1293 return res;
1294 snprintf(fn, sizeof(fn), "digits/thousand");
1295 num = num % 1000;
1296 } else if (num < 1000000000) {
1297 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1298 if (res)
1299 return res;
1300 snprintf(fn, sizeof(fn), "digits/million");
1301 num = num % 1000000;
1302 } else {
1303 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1304 res = -1;
1306 if (!res) {
1307 if (!ast_streamfile(chan, fn, language)) {
1308 if ((audiofd > -1) && (ctrlfd > -1))
1309 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1310 else
1311 res = ast_waitstream(chan, ints);
1313 ast_stopstream(chan);
1316 return res;
1319 /*! \brief ast_say_number_full_it: Italian */
1320 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1322 int res = 0;
1323 int playh = 0;
1324 int tempnum = 0;
1325 char fn[256] = "";
1327 if (!num)
1328 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1331 Italian support
1333 Like english, numbers up to 20 are a single 'word', and others
1334 compound, but with exceptions.
1335 For example 21 is not twenty-one, but there is a single word in 'it'.
1336 Idem for 28 (ie when a the 2nd part of a compund number
1337 starts with a vowel)
1339 There are exceptions also for hundred, thousand and million.
1340 In english 100 = one hundred, 200 is two hundred.
1341 In italian 100 = cento , like to say hundred (without one),
1342 200 and more are like english.
1344 Same applies for thousand:
1345 1000 is one thousand in en, 2000 is two thousand.
1346 In it we have 1000 = mille , 2000 = 2 mila
1348 For million(s) we use the plural, if more than one
1349 Also, one million is abbreviated in it, like on-million,
1350 or 'un milione', not 'uno milione'.
1351 So the right file is provided.
1354 while (!res && (num || playh)) {
1355 if (num < 0) {
1356 snprintf(fn, sizeof(fn), "digits/minus");
1357 if ( num > INT_MIN ) {
1358 num = -num;
1359 } else {
1360 num = 0;
1362 } else if (playh) {
1363 snprintf(fn, sizeof(fn), "digits/hundred");
1364 playh = 0;
1365 } else if (num < 20) {
1366 snprintf(fn, sizeof(fn), "digits/%d", num);
1367 num = 0;
1368 } else if (num == 21) {
1369 snprintf(fn, sizeof(fn), "digits/%d", num);
1370 num = 0;
1371 } else if (num == 28) {
1372 snprintf(fn, sizeof(fn), "digits/%d", num);
1373 num = 0;
1374 } else if (num == 31) {
1375 snprintf(fn, sizeof(fn), "digits/%d", num);
1376 num = 0;
1377 } else if (num == 38) {
1378 snprintf(fn, sizeof(fn), "digits/%d", num);
1379 num = 0;
1380 } else if (num == 41) {
1381 snprintf(fn, sizeof(fn), "digits/%d", num);
1382 num = 0;
1383 } else if (num == 48) {
1384 snprintf(fn, sizeof(fn), "digits/%d", num);
1385 num = 0;
1386 } else if (num == 51) {
1387 snprintf(fn, sizeof(fn), "digits/%d", num);
1388 num = 0;
1389 } else if (num == 58) {
1390 snprintf(fn, sizeof(fn), "digits/%d", num);
1391 num = 0;
1392 } else if (num == 61) {
1393 snprintf(fn, sizeof(fn), "digits/%d", num);
1394 num = 0;
1395 } else if (num == 68) {
1396 snprintf(fn, sizeof(fn), "digits/%d", num);
1397 num = 0;
1398 } else if (num == 71) {
1399 snprintf(fn, sizeof(fn), "digits/%d", num);
1400 num = 0;
1401 } else if (num == 78) {
1402 snprintf(fn, sizeof(fn), "digits/%d", num);
1403 num = 0;
1404 } else if (num == 81) {
1405 snprintf(fn, sizeof(fn), "digits/%d", num);
1406 num = 0;
1407 } else if (num == 88) {
1408 snprintf(fn, sizeof(fn), "digits/%d", num);
1409 num = 0;
1410 } else if (num == 91) {
1411 snprintf(fn, sizeof(fn), "digits/%d", num);
1412 num = 0;
1413 } else if (num == 98) {
1414 snprintf(fn, sizeof(fn), "digits/%d", num);
1415 num = 0;
1416 } else if (num < 100) {
1417 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1418 num -= ((num / 10) * 10);
1419 } else {
1420 if (num < 1000) {
1421 if ((num / 100) > 1) {
1422 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1423 playh++;
1424 } else {
1425 snprintf(fn, sizeof(fn), "digits/hundred");
1427 num -= ((num / 100) * 100);
1428 } else {
1429 if (num < 1000000) { /* 1,000,000 */
1430 if ((num/1000) > 1)
1431 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1432 if (res)
1433 return res;
1434 tempnum = num;
1435 num = num % 1000;
1436 if ((tempnum / 1000) < 2)
1437 snprintf(fn, sizeof(fn), "digits/thousand");
1438 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1439 snprintf(fn, sizeof(fn), "digits/thousands");
1440 } else {
1441 if (num < 1000000000) { /* 1,000,000,000 */
1442 if ((num / 1000000) > 1)
1443 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1444 if (res)
1445 return res;
1446 tempnum = num;
1447 num = num % 1000000;
1448 if ((tempnum / 1000000) < 2)
1449 snprintf(fn, sizeof(fn), "digits/million");
1450 else
1451 snprintf(fn, sizeof(fn), "digits/millions");
1452 } else {
1453 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1454 res = -1;
1459 if (!res) {
1460 if (!ast_streamfile(chan, fn, language)) {
1461 if ((audiofd > -1) && (ctrlfd > -1))
1462 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1463 else
1464 res = ast_waitstream(chan, ints);
1466 ast_stopstream(chan);
1469 return res;
1472 /*! \brief ast_say_number_full_nl: dutch syntax */
1473 /* New files: digits/nl-en
1475 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1477 int res = 0;
1478 int playh = 0;
1479 int units = 0;
1480 char fn[256] = "";
1481 if (!num)
1482 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1483 while (!res && (num || playh )) {
1484 if (num < 0) {
1485 snprintf(fn, sizeof(fn), "digits/minus");
1486 if ( num > INT_MIN ) {
1487 num = -num;
1488 } else {
1489 num = 0;
1491 } else if (playh) {
1492 snprintf(fn, sizeof(fn), "digits/hundred");
1493 playh = 0;
1494 } else if (num < 20) {
1495 snprintf(fn, sizeof(fn), "digits/%d", num);
1496 num = 0;
1497 } else if (num < 100) {
1498 units = num % 10;
1499 if (units > 0) {
1500 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1501 if (res)
1502 return res;
1503 num = num - units;
1504 snprintf(fn, sizeof(fn), "digits/nl-en");
1505 } else {
1506 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1507 num = 0;
1509 } else if (num < 200) {
1510 /* hundred, not one-hundred */
1511 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1512 num -= ((num / 100) * 100);
1513 } else if (num < 1000) {
1514 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
1515 playh++;
1516 num -= ((num / 100) * 100);
1517 } else {
1518 if (num < 1100) {
1519 /* thousand, not one-thousand */
1520 num = num % 1000;
1521 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1522 } else if (num < 10000) { /* 1,100 to 9,9999 */
1523 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
1524 if (res)
1525 return res;
1526 num = num % 100;
1527 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1528 } else {
1529 if (num < 1000000) { /* 1,000,000 */
1530 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
1531 if (res)
1532 return res;
1533 num = num % 1000;
1534 snprintf(fn, sizeof(fn), "digits/thousand");
1535 } else {
1536 if (num < 1000000000) { /* 1,000,000,000 */
1537 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1538 if (res)
1539 return res;
1540 num = num % 1000000;
1541 snprintf(fn, sizeof(fn), "digits/million");
1542 } else {
1543 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1544 res = -1;
1550 if (!res) {
1551 if (!ast_streamfile(chan, fn, language)) {
1552 if ((audiofd > -1) && (ctrlfd > -1))
1553 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1554 else
1555 res = ast_waitstream(chan, ints);
1557 ast_stopstream(chan);
1560 return res;
1563 /*! \brief ast_say_number_full_no: Norwegian syntax */
1564 /* New files:
1565 In addition to American English, the following sounds are required: "and", "1N"
1567 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)
1569 int res = 0;
1570 int playh = 0;
1571 int playa = 0;
1572 int cn = 1; /* +1 = commune; -1 = neuter */
1573 char fn[256] = "";
1575 if (!num)
1576 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1578 if (options && !strncasecmp(options, "n",1)) cn = -1;
1580 while (!res && (num || playh || playa )) {
1581 /* The grammar for Norwegian numbers is the same as for English except
1582 * for the following:
1583 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1584 * "and" before the last two digits, i.e. 2034 is "two thousand and
1585 * thirty-four" and 1000012 is "one million and twelve".
1587 if (num < 0) {
1588 snprintf(fn, sizeof(fn), "digits/minus");
1589 if ( num > INT_MIN ) {
1590 num = -num;
1591 } else {
1592 num = 0;
1594 } else if (playh) {
1595 snprintf(fn, sizeof(fn), "digits/hundred");
1596 playh = 0;
1597 } else if (playa) {
1598 snprintf(fn, sizeof(fn), "digits/and");
1599 playa = 0;
1600 } else if (num == 1 && cn == -1) {
1601 snprintf(fn, sizeof(fn), "digits/1N");
1602 num = 0;
1603 } else if (num < 20) {
1604 snprintf(fn, sizeof(fn), "digits/%d", num);
1605 num = 0;
1606 } else if (num < 100) {
1607 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1608 num -= ((num / 10) * 10);
1609 } else if (num < 1000) {
1610 int hundreds = num / 100;
1611 if (hundreds == 1)
1612 snprintf(fn, sizeof(fn), "digits/1N");
1613 else
1614 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1616 playh++;
1617 num -= 100 * hundreds;
1618 if (num)
1619 playa++;
1620 } else if (num < 1000000) {
1621 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1622 if (res)
1623 return res;
1624 snprintf(fn, sizeof(fn), "digits/thousand");
1625 num = num % 1000;
1626 if (num && num < 100)
1627 playa++;
1628 } else if (num < 1000000000) {
1629 int millions = num / 1000000;
1630 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1631 if (res)
1632 return res;
1633 snprintf(fn, sizeof(fn), "digits/million");
1634 num = num % 1000000;
1635 if (num && num < 100)
1636 playa++;
1637 } else {
1638 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1639 res = -1;
1642 if (!res) {
1643 if (!ast_streamfile(chan, fn, language)) {
1644 if ((audiofd > -1) && (ctrlfd > -1))
1645 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1646 else
1647 res = ast_waitstream(chan, ints);
1649 ast_stopstream(chan);
1652 return res;
1655 typedef struct {
1656 char *separator_dziesiatek;
1657 char *cyfry[10];
1658 char *cyfry2[10];
1659 char *setki[10];
1660 char *dziesiatki[10];
1661 char *nastki[10];
1662 char *rzedy[3][3];
1663 } odmiana;
1665 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1667 if (rzad==0)
1668 return "";
1670 if (i==1)
1671 return odm->rzedy[rzad - 1][0];
1672 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1673 return odm->rzedy[rzad - 1][1];
1674 else
1675 return odm->rzedy[rzad - 1][2];
1678 static char* pl_append(char* buffer, char* str)
1680 strcpy(buffer, str);
1681 buffer += strlen(str);
1682 return buffer;
1685 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1687 char file_name[255] = "digits/";
1688 strcat(file_name, fn);
1689 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1690 if (!ast_streamfile(chan, file_name, language)) {
1691 if ((audiofd > -1) && (ctrlfd > -1))
1692 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1693 else
1694 ast_waitstream(chan, ints);
1696 ast_stopstream(chan);
1699 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1701 /* Initialise variables to allow compilation on Debian-stable, etc */
1702 int m1000E6 = 0;
1703 int i1000E6 = 0;
1704 int m1000E3 = 0;
1705 int i1000E3 = 0;
1706 int m1000 = 0;
1707 int i1000 = 0;
1708 int m100 = 0;
1709 int i100 = 0;
1711 if (i == 0 && rzad > 0) {
1712 return;
1714 if (i == 0) {
1715 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1716 return;
1719 m1000E6 = i % 1000000000;
1720 i1000E6 = i / 1000000000;
1722 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1724 m1000E3 = m1000E6 % 1000000;
1725 i1000E3 = m1000E6 / 1000000;
1727 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1729 m1000 = m1000E3 % 1000;
1730 i1000 = m1000E3 / 1000;
1732 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1734 m100 = m1000 % 100;
1735 i100 = m1000 / 100;
1737 if (i100>0)
1738 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1740 if ( m100 > 0 && m100 <=9 ) {
1741 if (m1000>0)
1742 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1743 else
1744 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1745 } else if (m100 % 10 == 0) {
1746 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1747 } else if (m100 <= 19 ) {
1748 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1749 } else if (m100 != 0) {
1750 if (odm->separator_dziesiatek[0]==' ') {
1751 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1752 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1753 } else {
1754 char buf[10];
1755 char *b = buf;
1756 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1757 b = pl_append(b, odm->separator_dziesiatek);
1758 b = pl_append(b, odm->cyfry2[m100 % 10]);
1759 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1763 if (rzad > 0) {
1764 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1768 /* ast_say_number_full_pl: Polish syntax */
1769 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)
1771 Sounds needed:
1772 0 zero
1773 1 jeden
1774 10 dziesiec
1775 100 sto
1776 1000 tysiac
1777 1000000 milion
1778 1000000000 miliard
1779 1000000000.2 miliardy
1780 1000000000.5 miliardow
1781 1000000.2 miliony
1782 1000000.5 milionow
1783 1000.2 tysiace
1784 1000.5 tysiecy
1785 100m stu
1786 10m dziesieciu
1787 11 jedenascie
1788 11m jedenastu
1789 12 dwanascie
1790 12m dwunastu
1791 13 trzynascie
1792 13m trzynastu
1793 14 czternascie
1794 14m czternastu
1795 15 pietnascie
1796 15m pietnastu
1797 16 szesnascie
1798 16m szesnastu
1799 17 siedemnascie
1800 17m siedemnastu
1801 18 osiemnascie
1802 18m osiemnastu
1803 19 dziewietnascie
1804 19m dziewietnastu
1805 1z jedna
1806 2 dwa
1807 20 dwadziescia
1808 200 dwiescie
1809 200m dwustu
1810 20m dwudziestu
1811 2-1m dwaj
1812 2-2m dwoch
1813 2z dwie
1814 3 trzy
1815 30 trzydziesci
1816 300 trzysta
1817 300m trzystu
1818 30m trzydziestu
1819 3-1m trzej
1820 3-2m trzech
1821 4 cztery
1822 40 czterdziesci
1823 400 czterysta
1824 400m czterystu
1825 40m czterdziestu
1826 4-1m czterej
1827 4-2m czterech
1828 5 piec
1829 50 piecdziesiat
1830 500 piecset
1831 500m pieciuset
1832 50m piedziesieciu
1833 5m pieciu
1834 6 szesc
1835 60 szescdziesiat
1836 600 szescset
1837 600m szesciuset
1838 60m szescdziesieciu
1839 6m szesciu
1840 7 siedem
1841 70 siedemdziesiat
1842 700 siedemset
1843 700m siedmiuset
1844 70m siedemdziesieciu
1845 7m siedmiu
1846 8 osiem
1847 80 osiemdziesiat
1848 800 osiemset
1849 800m osmiuset
1850 80m osiemdziesieciu
1851 8m osmiu
1852 9 dziewiec
1853 90 dziewiecdziesiat
1854 900 dziewiecset
1855 900m dziewieciuset
1856 90m dziewiedziesieciu
1857 9m dziewieciu
1858 and combinations of eg.: 20_1, 30m_3m, etc...
1862 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1864 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1866 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1868 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1870 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1872 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1874 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1876 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1878 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1880 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1882 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1884 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1886 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1888 /* Initialise variables to allow compilation on Debian-stable, etc */
1889 odmiana *o;
1891 static odmiana *odmiana_nieosobowa = NULL;
1892 static odmiana *odmiana_meska = NULL;
1893 static odmiana *odmiana_zenska = NULL;
1895 if (odmiana_nieosobowa == NULL) {
1896 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1898 odmiana_nieosobowa->separator_dziesiatek = " ";
1900 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1901 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1902 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1903 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1904 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1905 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1908 if (odmiana_zenska == NULL) {
1909 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1911 odmiana_zenska->separator_dziesiatek = " ";
1913 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1914 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1915 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1916 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1917 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1918 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1921 if (odmiana_meska == NULL) {
1922 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1924 odmiana_meska->separator_dziesiatek = " ";
1926 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1927 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1928 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1929 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1930 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1931 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1934 if (options) {
1935 if (strncasecmp(options, "f", 1) == 0)
1936 o = odmiana_zenska;
1937 else if (strncasecmp(options, "m", 1) == 0)
1938 o = odmiana_meska;
1939 else
1940 o = odmiana_nieosobowa;
1941 } else
1942 o = odmiana_nieosobowa;
1944 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1945 return 0;
1948 /* ast_say_number_full_pt: Portuguese syntax */
1949 /* Extra sounds needed: */
1950 /* For feminin all sound files end with F */
1951 /* 100E for 100+ something */
1952 /* 1000000S for plural */
1953 /* pt-e for 'and' */
1954 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)
1956 int res = 0;
1957 int playh = 0;
1958 int mf = 1; /* +1 = male; -1 = female */
1959 char fn[256] = "";
1961 if (!num)
1962 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1964 if (options && !strncasecmp(options, "f",1))
1965 mf = -1;
1967 while (!res && num ) {
1968 if (num < 0) {
1969 snprintf(fn, sizeof(fn), "digits/minus");
1970 if ( num > INT_MIN ) {
1971 num = -num;
1972 } else {
1973 num = 0;
1975 } else if (num < 20) {
1976 if ((num == 1 || num == 2) && (mf < 0))
1977 snprintf(fn, sizeof(fn), "digits/%dF", num);
1978 else
1979 snprintf(fn, sizeof(fn), "digits/%d", num);
1980 num = 0;
1981 } else if (num < 100) {
1982 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1983 if (num % 10)
1984 playh = 1;
1985 num = num % 10;
1986 } else if (num < 1000) {
1987 if (num == 100)
1988 snprintf(fn, sizeof(fn), "digits/100");
1989 else if (num < 200)
1990 snprintf(fn, sizeof(fn), "digits/100E");
1991 else {
1992 if (mf < 0 && num > 199)
1993 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1994 else
1995 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1996 if (num % 100)
1997 playh = 1;
1999 num = num % 100;
2000 } else if (num < 1000000) {
2001 if (num > 1999) {
2002 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2003 if (res)
2004 return res;
2006 snprintf(fn, sizeof(fn), "digits/1000");
2007 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2008 playh = 1;
2009 num = num % 1000;
2010 } else if (num < 1000000000) {
2011 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2012 if (res)
2013 return res;
2014 if (num < 2000000)
2015 snprintf(fn, sizeof(fn), "digits/1000000");
2016 else
2017 snprintf(fn, sizeof(fn), "digits/1000000S");
2019 if ((num % 1000000) &&
2020 /* no thousands */
2021 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2022 /* no hundreds and below */
2023 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2024 playh = 1;
2025 num = num % 1000000;
2026 } else {
2027 /* number is too big */
2028 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2029 res = -1;
2031 if (!res) {
2032 if (!ast_streamfile(chan, fn, language)) {
2033 if ((audiofd > -1) && (ctrlfd > -1))
2034 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2035 else
2036 res = ast_waitstream(chan, ints);
2038 ast_stopstream(chan);
2040 if (!res && playh) {
2041 res = wait_file(chan, ints, "digits/pt-e", language);
2042 ast_stopstream(chan);
2043 playh = 0;
2046 return res;
2049 /*! \brief ast_say_number_full_se: Swedish syntax */
2050 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)
2052 int res = 0;
2053 int playh = 0;
2054 char fn[256] = "";
2055 int cn = 1; /* +1 = commune; -1 = neuter */
2056 if (!num)
2057 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2058 if (options && !strncasecmp(options, "n",1)) cn = -1;
2060 while (!res && (num || playh)) {
2061 if (num < 0) {
2062 snprintf(fn, sizeof(fn), "digits/minus");
2063 if ( num > INT_MIN ) {
2064 num = -num;
2065 } else {
2066 num = 0;
2068 } else if (playh) {
2069 snprintf(fn, sizeof(fn), "digits/hundred");
2070 playh = 0;
2071 } else if (num < 20) {
2072 snprintf(fn, sizeof(fn), "digits/%d", num);
2073 num = 0;
2074 } else if (num < 100) {
2075 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2076 num -= ((num / 10) * 10);
2077 } else if (num == 1 && cn == -1) { /* En eller ett? */
2078 snprintf(fn, sizeof(fn), "digits/1N");
2079 num = 0;
2080 } else {
2081 if (num < 1000){
2082 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2083 playh++;
2084 num -= ((num / 100) * 100);
2085 } else {
2086 if (num < 1000000) { /* 1,000,000 */
2087 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2088 if (res) {
2089 return res;
2091 num = num % 1000;
2092 snprintf(fn, sizeof(fn), "digits/thousand");
2093 } else {
2094 if (num < 1000000000) { /* 1,000,000,000 */
2095 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2096 if (res) {
2097 return res;
2099 num = num % 1000000;
2100 snprintf(fn, sizeof(fn), "digits/million");
2101 } else {
2102 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2103 res = -1;
2108 if (!res) {
2109 if (!ast_streamfile(chan, fn, language)) {
2110 if ((audiofd > -1) && (ctrlfd > -1))
2111 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2112 else
2113 res = ast_waitstream(chan, ints);
2114 ast_stopstream(chan);
2118 return res;
2121 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2122 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2124 int res = 0;
2125 int playh = 0;
2126 char fn[256] = "";
2127 if (!num)
2128 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2130 while (!res && (num || playh)) {
2131 if (num < 0) {
2132 snprintf(fn, sizeof(fn), "digits/minus");
2133 if ( num > INT_MIN ) {
2134 num = -num;
2135 } else {
2136 num = 0;
2138 } else if (playh) {
2139 snprintf(fn, sizeof(fn), "digits/hundred");
2140 playh = 0;
2141 } else if (num < 10) {
2142 snprintf(fn, sizeof(fn), "digits/%d", num);
2143 num = 0;
2144 } else if (num < 100) {
2145 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2146 num -= ((num / 10) * 10);
2147 } else {
2148 if (num < 1000){
2149 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2150 playh++;
2151 num -= ((num / 100) * 100);
2152 } else {
2153 if (num < 1000000) { /* 1,000,000 */
2154 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2155 if (res)
2156 return res;
2157 num = num % 1000;
2158 snprintf(fn, sizeof(fn), "digits/thousand");
2159 } else {
2160 if (num < 1000000000) { /* 1,000,000,000 */
2161 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2162 if (res)
2163 return res;
2164 num = num % 1000000;
2165 snprintf(fn, sizeof(fn), "digits/million");
2166 } else {
2167 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2168 res = -1;
2173 if (!res) {
2174 if (!ast_streamfile(chan, fn, language)) {
2175 if ((audiofd > -1) && (ctrlfd > -1))
2176 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2177 else
2178 res = ast_waitstream(chan, ints);
2180 ast_stopstream(chan);
2183 return res;
2187 /*! \brief determine last digits for thousands/millions (ru) */
2188 static int get_lastdigits_ru(int num) {
2189 if (num < 20) {
2190 return num;
2191 } else if (num < 100) {
2192 return get_lastdigits_ru(num % 10);
2193 } else if (num < 1000) {
2194 return get_lastdigits_ru(num % 100);
2196 return 0; /* number too big */
2200 /*! \brief ast_say_number_full_ru: Russian syntax */
2201 /*! \brief additional files:
2202 n00.gsm (one hundred, two hundred, ...)
2203 thousand.gsm
2204 million.gsm
2205 thousands-i.gsm (tisyachi)
2206 million-a.gsm (milliona)
2207 thousands.gsm
2208 millions.gsm
2209 1f.gsm (odna)
2210 2f.gsm (dve)
2212 where 'n' from 1 to 9
2214 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)
2216 int res = 0;
2217 int lastdigits = 0;
2218 char fn[256] = "";
2219 if (!num)
2220 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2222 while (!res && (num)) {
2223 if (num < 0) {
2224 snprintf(fn, sizeof(fn), "digits/minus");
2225 if ( num > INT_MIN ) {
2226 num = -num;
2227 } else {
2228 num = 0;
2230 } else if (num < 20) {
2231 if (options && strlen(options) == 1 && num < 3) {
2232 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2233 } else {
2234 snprintf(fn, sizeof(fn), "digits/%d", num);
2236 num = 0;
2237 } else if (num < 100) {
2238 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2239 num %= 10;
2240 } else if (num < 1000){
2241 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2242 num %= 100;
2243 } else if (num < 1000000) { /* 1,000,000 */
2244 lastdigits = get_lastdigits_ru(num / 1000);
2245 /* say thousands */
2246 if (lastdigits < 3) {
2247 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2248 } else {
2249 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2251 if (res)
2252 return res;
2253 if (lastdigits == 1) {
2254 snprintf(fn, sizeof(fn), "digits/thousand");
2255 } else if (lastdigits > 1 && lastdigits < 5) {
2256 snprintf(fn, sizeof(fn), "digits/thousands-i");
2257 } else {
2258 snprintf(fn, sizeof(fn), "digits/thousands");
2260 num %= 1000;
2261 } else if (num < 1000000000) { /* 1,000,000,000 */
2262 lastdigits = get_lastdigits_ru(num / 1000000);
2263 /* say millions */
2264 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2265 if (res)
2266 return res;
2267 if (lastdigits == 1) {
2268 snprintf(fn, sizeof(fn), "digits/million");
2269 } else if (lastdigits > 1 && lastdigits < 5) {
2270 snprintf(fn, sizeof(fn), "digits/million-a");
2271 } else {
2272 snprintf(fn, sizeof(fn), "digits/millions");
2274 num %= 1000000;
2275 } else {
2276 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2277 res = -1;
2279 if (!res) {
2280 if (!ast_streamfile(chan, fn, language)) {
2281 if ((audiofd > -1) && (ctrlfd > -1))
2282 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2283 else
2284 res = ast_waitstream(chan, ints);
2286 ast_stopstream(chan);
2289 return res;
2293 /*! \brief ast_say_enumeration_full: call language-specific functions */
2294 /* Called from AGI */
2295 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2297 if (!strcasecmp(language,"en") ) { /* English syntax */
2298 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2299 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2300 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2301 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2302 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2305 /* Default to english */
2306 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2309 /*! \brief ast_say_enumeration_full_en: English syntax */
2310 /* This is the default syntax, if no other syntax defined in this file is used */
2311 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2313 int res = 0, t = 0;
2314 char fn[256] = "";
2316 while (!res && num) {
2317 if (num < 0) {
2318 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2319 if ( num > INT_MIN ) {
2320 num = -num;
2321 } else {
2322 num = 0;
2324 } else if (num < 20) {
2325 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2326 num = 0;
2327 } else if (num < 100) {
2328 int tens = num / 10;
2329 num = num % 10;
2330 if (num == 0) {
2331 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2332 } else {
2333 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2335 } else if (num < 1000) {
2336 int hundreds = num / 100;
2337 num = num % 100;
2338 if (hundreds > 1 || t == 1) {
2339 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2341 if (res)
2342 return res;
2343 if (num) {
2344 snprintf(fn, sizeof(fn), "digits/hundred");
2345 } else {
2346 snprintf(fn, sizeof(fn), "digits/h-hundred");
2348 } else if (num < 1000000) {
2349 int thousands = num / 1000;
2350 num = num % 1000;
2351 if (thousands > 1 || t == 1) {
2352 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2354 if (res)
2355 return res;
2356 if (num) {
2357 snprintf(fn, sizeof(fn), "digits/thousand");
2358 } else {
2359 snprintf(fn, sizeof(fn), "digits/h-thousand");
2361 t = 1;
2362 } else if (num < 1000000000) {
2363 int millions = num / 1000000;
2364 num = num % 1000000;
2365 t = 1;
2366 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2367 if (res)
2368 return res;
2369 if (num) {
2370 snprintf(fn, sizeof(fn), "digits/million");
2371 } else {
2372 snprintf(fn, sizeof(fn), "digits/h-million");
2374 } else if (num < INT_MAX) {
2375 int billions = num / 1000000000;
2376 num = num % 1000000000;
2377 t = 1;
2378 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2379 if (res)
2380 return res;
2381 if (num) {
2382 snprintf(fn, sizeof(fn), "digits/billion");
2383 } else {
2384 snprintf(fn, sizeof(fn), "digits/h-billion");
2386 } else if (num == INT_MAX) {
2387 snprintf(fn, sizeof(fn), "digits/h-last");
2388 num = 0;
2389 } else {
2390 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2391 res = -1;
2394 if (!res) {
2395 if (!ast_streamfile(chan, fn, language)) {
2396 if ((audiofd > -1) && (ctrlfd > -1)) {
2397 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2398 } else {
2399 res = ast_waitstream(chan, ints);
2402 ast_stopstream(chan);
2405 return res;
2408 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2409 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)
2411 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2412 int res = 0, t = 0;
2413 char fn[256] = "", fna[256] = "";
2414 char *gender;
2416 if (options && !strncasecmp(options, "f",1)) {
2417 gender = "F";
2418 } else if (options && !strncasecmp(options, "n",1)) {
2419 gender = "N";
2420 } else {
2421 gender = "";
2424 if (!num)
2425 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2427 while (!res && num) {
2428 if (num < 0) {
2429 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2430 if ( num > INT_MIN ) {
2431 num = -num;
2432 } else {
2433 num = 0;
2435 } else if (num < 100 && t) {
2436 snprintf(fn, sizeof(fn), "digits/and");
2437 t = 0;
2438 } else if (num < 20) {
2439 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2440 num = 0;
2441 } else if (num < 100) {
2442 int ones = num % 10;
2443 if (ones) {
2444 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2445 num -= ones;
2446 } else {
2447 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2448 num = 0;
2450 } else if (num == 100 && t == 0) {
2451 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2452 num = 0;
2453 } else if (num < 1000) {
2454 int hundreds = num / 100;
2455 num = num % 100;
2456 if (hundreds == 1) {
2457 snprintf(fn, sizeof(fn), "digits/1N");
2458 } else {
2459 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2461 if (num) {
2462 snprintf(fna, sizeof(fna), "digits/hundred");
2463 } else {
2464 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2466 t = 1;
2467 } else if (num < 1000000) {
2468 int thousands = num / 1000;
2469 num = num % 1000;
2470 if (thousands == 1) {
2471 if (num) {
2472 snprintf(fn, sizeof(fn), "digits/1N");
2473 snprintf(fna, sizeof(fna), "digits/thousand");
2474 } else {
2475 if (t) {
2476 snprintf(fn, sizeof(fn), "digits/1N");
2477 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2478 } else {
2479 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2482 } else {
2483 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2484 if (res) {
2485 return res;
2487 if (num) {
2488 snprintf(fn, sizeof(fn), "digits/thousand");
2489 } else {
2490 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2493 t = 1;
2494 } else if (num < 1000000000) {
2495 int millions = num / 1000000;
2496 num = num % 1000000;
2497 if (millions == 1) {
2498 if (num) {
2499 snprintf(fn, sizeof(fn), "digits/1F");
2500 snprintf(fna, sizeof(fna), "digits/million");
2501 } else {
2502 snprintf(fn, sizeof(fn), "digits/1N");
2503 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2505 } else {
2506 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2507 if (res) {
2508 return res;
2510 if (num) {
2511 snprintf(fn, sizeof(fn), "digits/millions");
2512 } else {
2513 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2516 t = 1;
2517 } else if (num < INT_MAX) {
2518 int billions = num / 1000000000;
2519 num = num % 1000000000;
2520 if (billions == 1) {
2521 if (num) {
2522 snprintf(fn, sizeof(fn), "digits/1F");
2523 snprintf(fna, sizeof(fna), "digits/milliard");
2524 } else {
2525 snprintf(fn, sizeof(fn), "digits/1N");
2526 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2528 } else {
2529 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2530 if (res)
2531 return res;
2532 if (num) {
2533 snprintf(fn, sizeof(fna), "digits/milliards");
2534 } else {
2535 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2538 t = 1;
2539 } else if (num == INT_MAX) {
2540 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2541 num = 0;
2542 } else {
2543 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2544 res = -1;
2547 if (!res) {
2548 if (!ast_streamfile(chan, fn, language)) {
2549 if ((audiofd > -1) && (ctrlfd > -1))
2550 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2551 else
2552 res = ast_waitstream(chan, ints);
2554 ast_stopstream(chan);
2555 if (!res) {
2556 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2557 if ((audiofd > -1) && (ctrlfd > -1)) {
2558 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2559 } else {
2560 res = ast_waitstream(chan, ints);
2563 ast_stopstream(chan);
2564 strcpy(fna, "");
2568 return res;
2571 /*! \brief ast_say_enumeration_full_de: German syntax */
2572 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)
2574 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2575 int res = 0, t = 0;
2576 char fn[256] = "", fna[256] = "";
2577 char *gender;
2579 if (options && !strncasecmp(options, "f",1)) {
2580 gender = "F";
2581 } else if (options && !strncasecmp(options, "n",1)) {
2582 gender = "N";
2583 } else {
2584 gender = "";
2587 if (!num)
2588 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2590 while (!res && num) {
2591 if (num < 0) {
2592 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2593 if ( num > INT_MIN ) {
2594 num = -num;
2595 } else {
2596 num = 0;
2598 } else if (num < 100 && t) {
2599 snprintf(fn, sizeof(fn), "digits/and");
2600 t = 0;
2601 } else if (num < 20) {
2602 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2603 num = 0;
2604 } else if (num < 100) {
2605 int ones = num % 10;
2606 if (ones) {
2607 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2608 num -= ones;
2609 } else {
2610 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2611 num = 0;
2613 } else if (num == 100 && t == 0) {
2614 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2615 num = 0;
2616 } else if (num < 1000) {
2617 int hundreds = num / 100;
2618 num = num % 100;
2619 if (hundreds == 1) {
2620 snprintf(fn, sizeof(fn), "digits/1N");
2621 } else {
2622 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2624 if (num) {
2625 snprintf(fna, sizeof(fna), "digits/hundred");
2626 } else {
2627 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2629 t = 1;
2630 } else if (num < 1000000) {
2631 int thousands = num / 1000;
2632 num = num % 1000;
2633 if (thousands == 1) {
2634 if (num) {
2635 snprintf(fn, sizeof(fn), "digits/1N");
2636 snprintf(fna, sizeof(fna), "digits/thousand");
2637 } else {
2638 if (t) {
2639 snprintf(fn, sizeof(fn), "digits/1N");
2640 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2641 } else {
2642 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2645 } else {
2646 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2647 if (res) {
2648 return res;
2650 if (num) {
2651 snprintf(fn, sizeof(fn), "digits/thousand");
2652 } else {
2653 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2656 t = 1;
2657 } else if (num < 1000000000) {
2658 int millions = num / 1000000;
2659 num = num % 1000000;
2660 if (millions == 1) {
2661 if (num) {
2662 snprintf(fn, sizeof(fn), "digits/1F");
2663 snprintf(fna, sizeof(fna), "digits/million");
2664 } else {
2665 snprintf(fn, sizeof(fn), "digits/1N");
2666 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2668 } else {
2669 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2670 if (res) {
2671 return res;
2673 if (num) {
2674 snprintf(fn, sizeof(fn), "digits/millions");
2675 } else {
2676 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2679 t = 1;
2680 } else if (num < INT_MAX) {
2681 int billions = num / 1000000000;
2682 num = num % 1000000000;
2683 if (billions == 1) {
2684 if (num) {
2685 snprintf(fn, sizeof(fn), "digits/1F");
2686 snprintf(fna, sizeof(fna), "digits/milliard");
2687 } else {
2688 snprintf(fn, sizeof(fn), "digits/1N");
2689 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2691 } else {
2692 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2693 if (res)
2694 return res;
2695 if (num) {
2696 snprintf(fn, sizeof(fna), "digits/milliards");
2697 } else {
2698 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2701 t = 1;
2702 } else if (num == INT_MAX) {
2703 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2704 num = 0;
2705 } else {
2706 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2707 res = -1;
2710 if (!res) {
2711 if (!ast_streamfile(chan, fn, language)) {
2712 if ((audiofd > -1) && (ctrlfd > -1))
2713 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2714 else
2715 res = ast_waitstream(chan, ints);
2717 ast_stopstream(chan);
2718 if (!res) {
2719 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2720 if ((audiofd > -1) && (ctrlfd > -1)) {
2721 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2722 } else {
2723 res = ast_waitstream(chan, ints);
2726 ast_stopstream(chan);
2727 strcpy(fna, "");
2731 return res;
2734 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2736 if (!strcasecmp(lang, "en") ) { /* English syntax */
2737 return(ast_say_date_en(chan, t, ints, lang));
2738 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2739 return(ast_say_date_da(chan, t, ints, lang));
2740 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2741 return(ast_say_date_de(chan, t, ints, lang));
2742 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2743 return(ast_say_date_fr(chan, t, ints, lang));
2744 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2745 return(ast_say_date_nl(chan, t, ints, lang));
2746 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2747 return(ast_say_date_pt(chan, t, ints, lang));
2748 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2749 return(ast_say_date_gr(chan, t, ints, lang));
2750 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
2751 return(ast_say_date_ge(chan, t, ints, lang));
2754 /* Default to English */
2755 return(ast_say_date_en(chan, t, ints, lang));
2758 /* English syntax */
2759 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2761 struct tm tm;
2762 char fn[256];
2763 int res = 0;
2764 ast_localtime(&t,&tm,NULL);
2765 if (!res) {
2766 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2767 res = ast_streamfile(chan, fn, lang);
2768 if (!res)
2769 res = ast_waitstream(chan, ints);
2771 if (!res) {
2772 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2773 res = ast_streamfile(chan, fn, lang);
2774 if (!res)
2775 res = ast_waitstream(chan, ints);
2777 if (!res)
2778 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2779 if (!res)
2780 res = ast_waitstream(chan, ints);
2781 if (!res)
2782 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2783 return res;
2786 /* Danish syntax */
2787 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2789 struct tm tm;
2790 char fn[256];
2791 int res = 0;
2792 ast_localtime(&t,&tm,NULL);
2793 if (!res) {
2794 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2795 res = ast_streamfile(chan, fn, lang);
2796 if (!res)
2797 res = ast_waitstream(chan, ints);
2799 if (!res)
2800 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2801 if (!res)
2802 res = ast_waitstream(chan, ints);
2803 if (!res) {
2804 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2805 res = ast_streamfile(chan, fn, lang);
2806 if (!res)
2807 res = ast_waitstream(chan, ints);
2809 if (!res) {
2810 /* Year */
2811 int year = tm.tm_year + 1900;
2812 if (year > 1999) { /* year 2000 and later */
2813 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2814 } else {
2815 if (year < 1100) {
2816 /* I'm not going to handle 1100 and prior */
2817 /* We'll just be silent on the year, instead of bombing out. */
2818 } else {
2819 /* year 1100 to 1999. will anybody need this?!? */
2820 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2821 res = wait_file(chan, ints, fn, lang);
2822 if (!res) {
2823 res = wait_file(chan,ints, "digits/hundred", lang);
2824 if (!res && year % 100 != 0) {
2825 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2831 return res;
2834 /* German syntax */
2835 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2837 struct tm tm;
2838 char fn[256];
2839 int res = 0;
2840 ast_localtime(&t,&tm,NULL);
2841 if (!res) {
2842 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2843 res = ast_streamfile(chan, fn, lang);
2844 if (!res)
2845 res = ast_waitstream(chan, ints);
2847 if (!res)
2848 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2849 if (!res)
2850 res = ast_waitstream(chan, ints);
2851 if (!res) {
2852 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2853 res = ast_streamfile(chan, fn, lang);
2854 if (!res)
2855 res = ast_waitstream(chan, ints);
2857 if (!res) {
2858 /* Year */
2859 int year = tm.tm_year + 1900;
2860 if (year > 1999) { /* year 2000 and later */
2861 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2862 } else {
2863 if (year < 1100) {
2864 /* I'm not going to handle 1100 and prior */
2865 /* We'll just be silent on the year, instead of bombing out. */
2866 } else {
2867 /* year 1100 to 1999. will anybody need this?!? */
2868 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2869 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2870 res = wait_file(chan, ints, fn, lang);
2871 if (!res) {
2872 res = wait_file(chan,ints, "digits/hundred", lang);
2873 if (!res && year % 100 != 0) {
2874 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2880 return res;
2883 /* French syntax */
2884 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2886 struct tm tm;
2887 char fn[256];
2888 int res = 0;
2889 ast_localtime(&t,&tm,NULL);
2890 if (!res) {
2891 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2892 res = ast_streamfile(chan, fn, lang);
2893 if (!res)
2894 res = ast_waitstream(chan, ints);
2896 if (!res)
2897 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2898 if (!res)
2899 res = ast_waitstream(chan, ints);
2900 if (!res) {
2901 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2902 res = ast_streamfile(chan, fn, lang);
2903 if (!res)
2904 res = ast_waitstream(chan, ints);
2906 if (!res)
2907 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2908 return res;
2911 /* Dutch syntax */
2912 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2914 struct tm tm;
2915 char fn[256];
2916 int res = 0;
2917 ast_localtime(&t,&tm,NULL);
2918 if (!res) {
2919 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2920 res = ast_streamfile(chan, fn, lang);
2921 if (!res)
2922 res = ast_waitstream(chan, ints);
2924 if (!res)
2925 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2926 if (!res) {
2927 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2928 res = ast_streamfile(chan, fn, lang);
2929 if (!res)
2930 res = ast_waitstream(chan, ints);
2932 if (!res)
2933 res = ast_waitstream(chan, ints);
2934 if (!res)
2935 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2936 return res;
2939 /* Portuguese syntax */
2940 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2942 struct tm tm;
2943 char fn[256];
2944 int res = 0;
2946 ast_localtime(&t, &tm, NULL);
2947 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2948 if (!res)
2949 res = wait_file(chan, ints, fn, lang);
2950 if (!res)
2951 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2952 if (!res)
2953 res = wait_file(chan, ints, "digits/pt-de", lang);
2954 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2955 if (!res)
2956 res = wait_file(chan, ints, fn, lang);
2957 if (!res)
2958 res = wait_file(chan, ints, "digits/pt-de", lang);
2959 if (!res)
2960 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2962 return res;
2965 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)
2967 if (!strcasecmp(lang, "en") ) { /* English syntax */
2968 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2969 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2970 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
2971 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2972 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
2973 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
2974 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
2975 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2976 return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
2977 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2978 return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
2979 } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
2980 return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
2981 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2982 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
2983 } else if (!strcasecmp(lang, "pl") ) { /* Polish syntax */
2984 return(ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
2985 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2986 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
2987 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
2988 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
2989 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2990 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
2993 /* Default to English */
2994 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2997 /* English syntax */
2998 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)
3000 struct tm tm;
3001 int res=0, offset, sndoffset;
3002 char sndfile[256], nextmsg[256];
3004 if (format == NULL)
3005 format = "ABdY 'digits/at' IMp";
3007 ast_localtime(&time,&tm,timezone);
3009 for (offset=0 ; format[offset] != '\0' ; offset++) {
3010 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3011 switch (format[offset]) {
3012 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3013 case '\'':
3014 /* Literal name of a sound file */
3015 sndoffset=0;
3016 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3017 sndfile[sndoffset] = format[offset];
3018 sndfile[sndoffset] = '\0';
3019 res = wait_file(chan,ints,sndfile,lang);
3020 break;
3021 case 'A':
3022 case 'a':
3023 /* Sunday - Saturday */
3024 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3025 res = wait_file(chan,ints,nextmsg,lang);
3026 break;
3027 case 'B':
3028 case 'b':
3029 case 'h':
3030 /* January - December */
3031 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3032 res = wait_file(chan,ints,nextmsg,lang);
3033 break;
3034 case 'm':
3035 /* Month enumerated */
3036 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
3037 break;
3038 case 'd':
3039 case 'e':
3040 /* First - Thirtyfirst */
3041 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
3042 break;
3043 case 'Y':
3044 /* Year */
3045 if (tm.tm_year > 99) {
3046 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3047 } else if (tm.tm_year < 1) {
3048 /* I'm not going to handle 1900 and prior */
3049 /* We'll just be silent on the year, instead of bombing out. */
3050 } else {
3051 res = wait_file(chan, ints, "digits/19", lang);
3052 if (!res) {
3053 if (tm.tm_year <= 9) {
3054 /* 1901 - 1909 */
3055 res = wait_file(chan,ints, "digits/oh", lang);
3058 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3061 break;
3062 case 'I':
3063 case 'l':
3064 /* 12-Hour */
3065 if (tm.tm_hour == 0)
3066 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3067 else if (tm.tm_hour > 12)
3068 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3069 else
3070 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3071 res = wait_file(chan,ints,nextmsg,lang);
3072 break;
3073 case 'H':
3074 case 'k':
3075 /* 24-Hour */
3076 if (format[offset] == 'H') {
3077 /* e.g. oh-eight */
3078 if (tm.tm_hour < 10) {
3079 res = wait_file(chan,ints, "digits/oh",lang);
3081 } else {
3082 /* e.g. eight */
3083 if (tm.tm_hour == 0) {
3084 res = wait_file(chan,ints, "digits/oh",lang);
3087 if (!res) {
3088 if (tm.tm_hour != 0) {
3089 int remainder = tm.tm_hour;
3090 if (tm.tm_hour > 20) {
3091 res = wait_file(chan,ints, "digits/20",lang);
3092 remainder -= 20;
3094 if (!res) {
3095 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3096 res = wait_file(chan,ints,nextmsg,lang);
3100 break;
3101 case 'M':
3102 case 'N':
3103 /* Minute */
3104 if (tm.tm_min == 0) {
3105 if (format[offset] == 'M') {
3106 res = wait_file(chan, ints, "digits/oclock", lang);
3107 } else {
3108 res = wait_file(chan, ints, "digits/hundred", lang);
3110 } else if (tm.tm_min < 10) {
3111 res = wait_file(chan,ints, "digits/oh",lang);
3112 if (!res) {
3113 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3114 res = wait_file(chan,ints,nextmsg,lang);
3116 } else {
3117 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3119 break;
3120 case 'P':
3121 case 'p':
3122 /* AM/PM */
3123 if (tm.tm_hour > 11)
3124 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3125 else
3126 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3127 res = wait_file(chan,ints,nextmsg,lang);
3128 break;
3129 case 'Q':
3130 /* Shorthand for "Today", "Yesterday", or ABdY */
3131 /* XXX As emphasized elsewhere, this should the native way in your
3132 * language to say the date, with changes in what you say, depending
3133 * upon how recent the date is. XXX */
3135 struct timeval now;
3136 struct tm tmnow;
3137 time_t beg_today, tt;
3139 gettimeofday(&now,NULL);
3140 tt = now.tv_sec;
3141 ast_localtime(&tt,&tmnow,timezone);
3142 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3143 /* In any case, it saves not having to do ast_mktime() */
3144 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3145 if (beg_today < time) {
3146 /* Today */
3147 res = wait_file(chan,ints, "digits/today",lang);
3148 } else if (beg_today - 86400 < time) {
3149 /* Yesterday */
3150 res = wait_file(chan,ints, "digits/yesterday",lang);
3151 } else if (beg_today - 86400 * 6 < time) {
3152 /* Within the last week */
3153 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3154 } else if (beg_today - 2628000 < time) {
3155 /* Less than a month ago - "Sunday, October third" */
3156 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3157 } else if (beg_today - 15768000 < time) {
3158 /* Less than 6 months ago - "August seventh" */
3159 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3160 } else {
3161 /* More than 6 months ago - "April nineteenth two thousand three" */
3162 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3165 break;
3166 case 'q':
3167 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3168 /* XXX As emphasized elsewhere, this should the native way in your
3169 * language to say the date, with changes in what you say, depending
3170 * upon how recent the date is. XXX */
3172 struct timeval now;
3173 struct tm tmnow;
3174 time_t beg_today, tt;
3176 gettimeofday(&now,NULL);
3177 tt = now.tv_sec;
3178 ast_localtime(&tt,&tmnow,timezone);
3179 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3180 /* In any case, it saves not having to do ast_mktime() */
3181 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3182 if (beg_today < time) {
3183 /* Today */
3184 } else if ((beg_today - 86400) < time) {
3185 /* Yesterday */
3186 res = wait_file(chan,ints, "digits/yesterday",lang);
3187 } else if (beg_today - 86400 * 6 < time) {
3188 /* Within the last week */
3189 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3190 } else if (beg_today - 2628000 < time) {
3191 /* Less than a month ago - "Sunday, October third" */
3192 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3193 } else if (beg_today - 15768000 < time) {
3194 /* Less than 6 months ago - "August seventh" */
3195 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3196 } else {
3197 /* More than 6 months ago - "April nineteenth two thousand three" */
3198 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3201 break;
3202 case 'R':
3203 res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
3204 break;
3205 case 'S':
3206 /* Seconds */
3207 if (tm.tm_sec == 0) {
3208 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3209 res = wait_file(chan,ints,nextmsg,lang);
3210 } else if (tm.tm_sec < 10) {
3211 res = wait_file(chan,ints, "digits/oh",lang);
3212 if (!res) {
3213 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3214 res = wait_file(chan,ints,nextmsg,lang);
3216 } else {
3217 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
3219 break;
3220 case 'T':
3221 res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
3222 break;
3223 case ' ':
3224 case ' ':
3225 /* Just ignore spaces and tabs */
3226 break;
3227 default:
3228 /* Unknown character */
3229 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3231 /* Jump out on DTMF */
3232 if (res) {
3233 break;
3236 return res;
3239 /* Danish syntax */
3240 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)
3242 struct tm tm;
3243 int res=0, offset, sndoffset;
3244 char sndfile[256], nextmsg[256];
3246 if (!format)
3247 format = "A dBY HMS";
3249 ast_localtime(&time,&tm,timezone);
3251 for (offset=0 ; format[offset] != '\0' ; offset++) {
3252 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3253 switch (format[offset]) {
3254 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3255 case '\'':
3256 /* Literal name of a sound file */
3257 sndoffset=0;
3258 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3259 sndfile[sndoffset] = format[offset];
3260 sndfile[sndoffset] = '\0';
3261 res = wait_file(chan,ints,sndfile,lang);
3262 break;
3263 case 'A':
3264 case 'a':
3265 /* Sunday - Saturday */
3266 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3267 res = wait_file(chan,ints,nextmsg,lang);
3268 break;
3269 case 'B':
3270 case 'b':
3271 case 'h':
3272 /* January - December */
3273 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3274 res = wait_file(chan,ints,nextmsg,lang);
3275 break;
3276 case 'm':
3277 /* Month enumerated */
3278 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3279 break;
3280 case 'd':
3281 case 'e':
3282 /* First - Thirtyfirst */
3283 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3284 break;
3285 case 'Y':
3286 /* Year */
3288 int year = tm.tm_year + 1900;
3289 if (year > 1999) { /* year 2000 and later */
3290 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3291 } else {
3292 if (year < 1100) {
3293 /* I'm not going to handle 1100 and prior */
3294 /* We'll just be silent on the year, instead of bombing out. */
3295 } else {
3296 /* year 1100 to 1999. will anybody need this?!? */
3297 /* say 1967 as 'nineteen hundred seven and sixty' */
3298 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3299 res = wait_file(chan,ints,nextmsg,lang);
3300 if (!res) {
3301 res = wait_file(chan,ints, "digits/hundred",lang);
3302 if (!res && year % 100 != 0) {
3303 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3309 break;
3310 case 'I':
3311 case 'l':
3312 /* 12-Hour */
3313 res = wait_file(chan,ints,"digits/oclock",lang);
3314 if (tm.tm_hour == 0)
3315 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3316 else if (tm.tm_hour > 12)
3317 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3318 else
3319 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3320 if (!res) {
3321 res = wait_file(chan,ints,nextmsg,lang);
3323 break;
3324 case 'H':
3325 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3326 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
3327 res = wait_file(chan,ints, "digits/0",lang);
3329 /* FALLTRHU */
3330 case 'k':
3331 /* 24-Hour */
3332 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3333 break;
3334 case 'M':
3335 /* Minute */
3336 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3337 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3339 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3340 if (tm.tm_min == 1) {
3341 res = wait_file(chan,ints,"digits/minute",lang);
3342 } else {
3343 res = wait_file(chan,ints,"digits/minutes",lang);
3346 break;
3347 case 'P':
3348 case 'p':
3349 /* AM/PM */
3350 if (tm.tm_hour > 11)
3351 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3352 else
3353 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3354 res = wait_file(chan,ints,nextmsg,lang);
3355 break;
3356 case 'Q':
3357 /* Shorthand for "Today", "Yesterday", or AdBY */
3358 /* XXX As emphasized elsewhere, this should the native way in your
3359 * language to say the date, with changes in what you say, depending
3360 * upon how recent the date is. XXX */
3362 struct timeval now;
3363 struct tm tmnow;
3364 time_t beg_today, tt;
3366 gettimeofday(&now,NULL);
3367 tt = now.tv_sec;
3368 ast_localtime(&tt,&tmnow,timezone);
3369 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3370 /* In any case, it saves not having to do ast_mktime() */
3371 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3372 if (beg_today < time) {
3373 /* Today */
3374 res = wait_file(chan,ints, "digits/today",lang);
3375 } else if (beg_today - 86400 < time) {
3376 /* Yesterday */
3377 res = wait_file(chan,ints, "digits/yesterday",lang);
3378 } else {
3379 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3382 break;
3383 case 'q':
3384 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3385 /* XXX As emphasized elsewhere, this should the native way in your
3386 * language to say the date, with changes in what you say, depending
3387 * upon how recent the date is. XXX */
3389 struct timeval now;
3390 struct tm tmnow;
3391 time_t beg_today, tt;
3393 gettimeofday(&now,NULL);
3394 tt = now.tv_sec;
3395 ast_localtime(&tt,&tmnow,timezone);
3396 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3397 /* In any case, it saves not having to do ast_mktime() */
3398 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3399 if (beg_today < time) {
3400 /* Today */
3401 } else if ((beg_today - 86400) < time) {
3402 /* Yesterday */
3403 res = wait_file(chan,ints, "digits/yesterday",lang);
3404 } else if (beg_today - 86400 * 6 < time) {
3405 /* Within the last week */
3406 res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
3407 } else {
3408 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3411 break;
3412 case 'R':
3413 res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
3414 break;
3415 case 'S':
3416 /* Seconds */
3417 res = wait_file(chan,ints, "digits/and",lang);
3418 if (!res) {
3419 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3420 if (!res) {
3421 res = wait_file(chan,ints, "digits/seconds",lang);
3424 break;
3425 case 'T':
3426 res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
3427 break;
3428 case ' ':
3429 case ' ':
3430 /* Just ignore spaces and tabs */
3431 break;
3432 default:
3433 /* Unknown character */
3434 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3436 /* Jump out on DTMF */
3437 if (res) {
3438 break;
3441 return res;
3444 /* German syntax */
3445 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)
3447 struct tm tm;
3448 int res=0, offset, sndoffset;
3449 char sndfile[256], nextmsg[256];
3451 if (!format)
3452 format = "A dBY HMS";
3454 ast_localtime(&time,&tm,timezone);
3456 for (offset=0 ; format[offset] != '\0' ; offset++) {
3457 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3458 switch (format[offset]) {
3459 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3460 case '\'':
3461 /* Literal name of a sound file */
3462 sndoffset=0;
3463 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3464 sndfile[sndoffset] = format[offset];
3465 sndfile[sndoffset] = '\0';
3466 res = wait_file(chan,ints,sndfile,lang);
3467 break;
3468 case 'A':
3469 case 'a':
3470 /* Sunday - Saturday */
3471 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3472 res = wait_file(chan,ints,nextmsg,lang);
3473 break;
3474 case 'B':
3475 case 'b':
3476 case 'h':
3477 /* January - December */
3478 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3479 res = wait_file(chan,ints,nextmsg,lang);
3480 break;
3481 case 'm':
3482 /* Month enumerated */
3483 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3484 break;
3485 case 'd':
3486 case 'e':
3487 /* First - Thirtyfirst */
3488 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3489 break;
3490 case 'Y':
3491 /* Year */
3493 int year = tm.tm_year + 1900;
3494 if (year > 1999) { /* year 2000 and later */
3495 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3496 } else {
3497 if (year < 1100) {
3498 /* I'm not going to handle 1100 and prior */
3499 /* We'll just be silent on the year, instead of bombing out. */
3500 } else {
3501 /* year 1100 to 1999. will anybody need this?!? */
3502 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3503 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3504 res = wait_file(chan,ints,nextmsg,lang);
3505 if (!res) {
3506 res = wait_file(chan,ints, "digits/hundred",lang);
3507 if (!res && year % 100 != 0) {
3508 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3514 break;
3515 case 'I':
3516 case 'l':
3517 /* 12-Hour */
3518 if (tm.tm_hour == 0)
3519 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3520 else if (tm.tm_hour > 12)
3521 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3522 else
3523 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3524 res = wait_file(chan,ints,nextmsg,lang);
3525 if (!res) {
3526 res = wait_file(chan,ints,"digits/oclock",lang);
3528 break;
3529 case 'H':
3530 case 'k':
3531 /* 24-Hour */
3532 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3533 if (!res) {
3534 res = wait_file(chan,ints,"digits/oclock",lang);
3536 break;
3537 case 'M':
3538 /* Minute */
3539 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3540 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3542 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3543 if (tm.tm_min == 1) {
3544 res = wait_file(chan,ints,"digits/minute",lang);
3545 } else {
3546 res = wait_file(chan,ints,"digits/minutes",lang);
3549 break;
3550 case 'P':
3551 case 'p':
3552 /* AM/PM */
3553 if (tm.tm_hour > 11)
3554 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3555 else
3556 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3557 res = wait_file(chan,ints,nextmsg,lang);
3558 break;
3559 case 'Q':
3560 /* Shorthand for "Today", "Yesterday", or AdBY */
3561 /* XXX As emphasized elsewhere, this should the native way in your
3562 * language to say the date, with changes in what you say, depending
3563 * upon how recent the date is. XXX */
3565 struct timeval now;
3566 struct tm tmnow;
3567 time_t beg_today, tt;
3569 gettimeofday(&now,NULL);
3570 tt = now.tv_sec;
3571 ast_localtime(&tt,&tmnow,timezone);
3572 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3573 /* In any case, it saves not having to do ast_mktime() */
3574 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3575 if (beg_today < time) {
3576 /* Today */
3577 res = wait_file(chan,ints, "digits/today",lang);
3578 } else if (beg_today - 86400 < time) {
3579 /* Yesterday */
3580 res = wait_file(chan,ints, "digits/yesterday",lang);
3581 } else {
3582 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3585 break;
3586 case 'q':
3587 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3588 /* XXX As emphasized elsewhere, this should the native way in your
3589 * language to say the date, with changes in what you say, depending
3590 * upon how recent the date is. XXX */
3592 struct timeval now;
3593 struct tm tmnow;
3594 time_t beg_today, tt;
3596 gettimeofday(&now,NULL);
3597 tt = now.tv_sec;
3598 ast_localtime(&tt,&tmnow,timezone);
3599 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3600 /* In any case, it saves not having to do ast_mktime() */
3601 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3602 if (beg_today < time) {
3603 /* Today */
3604 } else if ((beg_today - 86400) < time) {
3605 /* Yesterday */
3606 res = wait_file(chan,ints, "digits/yesterday",lang);
3607 } else if (beg_today - 86400 * 6 < time) {
3608 /* Within the last week */
3609 res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
3610 } else {
3611 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3614 break;
3615 case 'R':
3616 res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
3617 break;
3618 case 'S':
3619 /* Seconds */
3620 res = wait_file(chan,ints, "digits/and",lang);
3621 if (!res) {
3622 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3623 if (!res) {
3624 res = wait_file(chan,ints, "digits/seconds",lang);
3627 break;
3628 case 'T':
3629 res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
3630 break;
3631 case ' ':
3632 case ' ':
3633 /* Just ignore spaces and tabs */
3634 break;
3635 default:
3636 /* Unknown character */
3637 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3639 /* Jump out on DTMF */
3640 if (res) {
3641 break;
3644 return res;
3647 /* TODO: this probably is not the correct format for doxygen remarks */
3649 /** ast_say_date_with_format_he Say formmated date in Hebrew
3651 * \ref ast_say_date_with_format_en for the details of the options
3653 * Changes from the English version:
3655 * * don't replicate in here the logic of ast_say_number_full_he
3657 * * year is always 4-digit (because it's simpler)
3659 * * added c, x, and X. Mainly for my tests
3661 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3663 * TODO:
3664 * * A "ha" is missing in the standard date format, before the 'd'.
3665 * * The numbers of 3000--19000 are not handled well
3667 #define IL_DATE_STR "AdBY"
3668 #define IL_TIME_STR "IMp"
3669 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3670 int ast_say_date_with_format_he(struct ast_channel *chan, time_t time,
3671 const char *ints, const char *lang, const char *format,
3672 const char *timezone)
3674 /* TODO: This whole function is cut&paste from
3675 * ast_say_date_with_format_en . Is that considered acceptable?
3677 struct tm tm;
3678 int res=0, offset, sndoffset;
3679 char sndfile[256], nextmsg[256];
3681 if (!format)
3682 format = IL_DATE_STR_FULL;
3684 ast_localtime(&time,&tm,timezone);
3686 for (offset=0 ; format[offset] != '\0' ; offset++) {
3687 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3688 switch (format[offset]) {
3689 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3690 case '\'':
3691 /* Literal name of a sound file */
3692 sndoffset=0;
3693 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3694 sndfile[sndoffset] = format[offset];
3695 sndfile[sndoffset] = '\0';
3696 res = wait_file(chan,ints,sndfile,lang);
3697 break;
3698 case 'A':
3699 case 'a':
3700 /* Sunday - Saturday */
3701 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3702 res = wait_file(chan,ints,nextmsg,lang);
3703 break;
3704 case 'B':
3705 case 'b':
3706 case 'h':
3707 /* January - December */
3708 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3709 res = wait_file(chan,ints,nextmsg,lang);
3710 break;
3711 case 'd':
3712 case 'e': /* Day of the month */
3713 /* I'm not sure exactly what the parameters
3714 * audiofd and ctrlfd to
3715 * ast_say_number_full_he mean, but it seems
3716 * safe to pass -1 there.
3718 * At least in one of the pathes :-(
3720 res = ast_say_number_full_he(chan, tm.tm_mday,
3721 ints, lang, "m", -1, -1
3723 break;
3724 case 'Y': /* Year */
3725 res = ast_say_number_full_he(chan, tm.tm_year+1900,
3726 ints, lang, "f", -1, -1
3728 break;
3729 case 'I':
3730 case 'l': /* 12-Hour */
3732 int hour = tm.tm_hour;
3733 hour = hour%12;
3734 if (hour == 0) hour=12;
3736 res = ast_say_number_full_he(chan, hour,
3737 ints, lang, "f", -1, -1
3740 break;
3741 case 'H':
3742 case 'k': /* 24-Hour */
3743 /* With 'H' there is an 'oh' after a single-
3744 * digit hour */
3745 if ((format[offset] == 'H') &&
3746 (tm.tm_hour <10)&&(tm.tm_hour>0)
3747 ) { /* e.g. oh-eight */
3748 res = wait_file(chan,ints, "digits/oh",lang);
3751 res = ast_say_number_full_he(chan, tm.tm_hour,
3752 ints, lang, "f", -1, -1
3754 break;
3755 case 'M': /* Minute */
3756 res = ast_say_number_full_he(chan, tm.tm_min,
3757 ints, lang,"f", -1, -1
3759 break;
3760 case 'P':
3761 case 'p':
3762 /* AM/PM */
3763 if (tm.tm_hour > 11)
3764 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3765 else
3766 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3767 res = wait_file(chan,ints,nextmsg,lang);
3768 break;
3769 case 'Q':
3770 /* Shorthand for "Today", "Yesterday", or "date" */
3771 case 'q':
3772 /* Shorthand for "" (today), "Yesterday", A
3773 * (weekday), or "date" */
3774 /* XXX As emphasized elsewhere, this should the native way in your
3775 * language to say the date, with changes in what you say, depending
3776 * upon how recent the date is. XXX */
3778 struct timeval now;
3779 struct tm tmnow;
3780 time_t beg_today, tt;
3781 char todo = format[offset]; /* The letter to format*/
3783 gettimeofday(&now,NULL);
3784 tt = now.tv_sec;
3785 ast_localtime(&tt,&tmnow,timezone);
3786 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3787 /* In any case, it saves not having to do ast_mktime() */
3788 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3789 if (beg_today < time) {
3790 /* Today */
3791 if (todo == 'Q') {
3792 res = wait_file(chan,
3793 ints,
3794 "digits/today",
3795 lang);
3797 } else if (beg_today - 86400 < time) {
3798 /* Yesterday */
3799 res = wait_file(chan,ints, "digits/yesterday",lang);
3800 } else if ((todo != 'Q') &&
3801 (beg_today - 86400 * 6 < time))
3803 /* Within the last week */
3804 res = ast_say_date_with_format_he(chan,
3805 time, ints, lang,
3806 "A", timezone);
3807 } else {
3808 res = ast_say_date_with_format_he(chan,
3809 time, ints, lang,
3810 IL_DATE_STR, timezone);
3813 break;
3814 case 'R':
3815 res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
3816 break;
3817 case 'S': /* Seconds */
3818 res = ast_say_number_full_he(chan, tm.tm_sec,
3819 ints, lang, "f", -1, -1
3821 break;
3822 case 'T':
3823 res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
3824 break;
3825 /* c, x, and X seem useful for testing. Not sure
3826 * if thiey're good for the general public */
3827 case 'c':
3828 res = ast_say_date_with_format_he(chan, time,
3829 ints, lang, IL_DATE_STR_FULL, timezone);
3830 break;
3831 case 'x':
3832 res = ast_say_date_with_format_he(chan, time,
3833 ints, lang, IL_DATE_STR, timezone);
3834 break;
3835 case 'X': /* Currently not locale-dependent...*/
3836 res = ast_say_date_with_format_he(chan, time,
3837 ints, lang, IL_TIME_STR, timezone);
3838 break;
3839 case ' ':
3840 case ' ':
3841 /* Just ignore spaces and tabs */
3842 break;
3843 default:
3844 /* Unknown character */
3845 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3847 /* Jump out on DTMF */
3848 if (res) {
3849 break;
3852 return res;
3856 /* Spanish syntax */
3857 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)
3859 struct tm tm;
3860 int res=0, offset, sndoffset;
3861 char sndfile[256], nextmsg[256];
3863 if (format == NULL)
3864 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3866 ast_localtime(&time,&tm,timezone);
3868 for (offset=0 ; format[offset] != '\0' ; offset++) {
3869 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3870 switch (format[offset]) {
3871 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3872 case '\'':
3873 /* Literal name of a sound file */
3874 sndoffset=0;
3875 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3876 sndfile[sndoffset] = format[offset];
3877 sndfile[sndoffset] = '\0';
3878 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
3879 res = wait_file(chan,ints,nextmsg,lang);
3880 break;
3881 case 'A':
3882 case 'a':
3883 /* Sunday - Saturday */
3884 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3885 res = wait_file(chan,ints,nextmsg,lang);
3886 break;
3887 case 'B':
3888 case 'b':
3889 case 'h':
3890 /* January - December */
3891 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3892 res = wait_file(chan,ints,nextmsg,lang);
3893 break;
3894 case 'm':
3895 /* First - Twelfth */
3896 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
3897 res = wait_file(chan,ints,nextmsg,lang);
3898 break;
3899 case 'd':
3900 case 'e':
3901 /* First - Thirtyfirst */
3902 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
3903 break;
3904 case 'Y':
3905 /* Year */
3906 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3907 break;
3908 case 'I':
3909 case 'l':
3910 /* 12-Hour */
3911 if (tm.tm_hour == 0)
3912 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3913 else if (tm.tm_hour > 12)
3914 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3915 else
3916 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3917 res = wait_file(chan,ints,nextmsg,lang);
3918 break;
3919 case 'H':
3920 case 'k':
3921 /* 24-Hour */
3922 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
3923 break;
3924 case 'M':
3925 /* Minute */
3926 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3927 break;
3928 case 'P':
3929 case 'p':
3930 /* AM/PM */
3931 if (tm.tm_hour > 18)
3932 res = wait_file(chan, ints, "digits/p-m", lang);
3933 else if (tm.tm_hour > 12)
3934 res = wait_file(chan, ints, "digits/afternoon", lang);
3935 else if (tm.tm_hour)
3936 res = wait_file(chan, ints, "digits/a-m", lang);
3937 break;
3938 case 'Q':
3939 /* Shorthand for "Today", "Yesterday", or ABdY */
3940 /* XXX As emphasized elsewhere, this should the native way in your
3941 * language to say the date, with changes in what you say, depending
3942 * upon how recent the date is. XXX */
3944 struct timeval now;
3945 struct tm tmnow;
3946 time_t beg_today, tt;
3948 gettimeofday(&now,NULL);
3949 tt = now.tv_sec;
3950 ast_localtime(&tt,&tmnow,timezone);
3951 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3952 /* In any case, it saves not having to do ast_mktime() */
3953 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3954 if (beg_today < time) {
3955 /* Today */
3956 res = wait_file(chan,ints, "digits/today",lang);
3957 } else if (beg_today - 86400 < time) {
3958 /* Yesterday */
3959 res = wait_file(chan,ints, "digits/yesterday",lang);
3960 } else {
3961 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3964 break;
3965 case 'q':
3966 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3967 /* XXX As emphasized elsewhere, this should the native way in your
3968 * language to say the date, with changes in what you say, depending
3969 * upon how recent the date is. XXX */
3971 struct timeval now;
3972 struct tm tmnow;
3973 time_t beg_today, tt;
3975 gettimeofday(&now,NULL);
3976 tt = now.tv_sec;
3977 ast_localtime(&tt,&tmnow,timezone);
3978 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3979 /* In any case, it saves not having to do ast_mktime() */
3980 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3981 if (beg_today < time) {
3982 /* Today */
3983 res = wait_file(chan,ints, "digits/today",lang);
3984 } else if ((beg_today - 86400) < time) {
3985 /* Yesterday */
3986 res = wait_file(chan,ints, "digits/yesterday",lang);
3987 } else if (beg_today - 86400 * 6 < time) {
3988 /* Within the last week */
3989 res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
3990 } else {
3991 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3994 break;
3995 case 'R':
3996 res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
3997 break;
3998 case 'S':
3999 /* Seconds */
4000 if (tm.tm_sec == 0) {
4001 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4002 res = wait_file(chan,ints,nextmsg,lang);
4003 } else if (tm.tm_sec < 10) {
4004 res = wait_file(chan,ints, "digits/oh",lang);
4005 if (!res) {
4006 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4007 res = wait_file(chan,ints,nextmsg,lang);
4009 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4010 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4011 res = wait_file(chan,ints,nextmsg,lang);
4012 } else {
4013 int ten, one;
4014 ten = (tm.tm_sec / 10) * 10;
4015 one = (tm.tm_sec % 10);
4016 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4017 res = wait_file(chan,ints,nextmsg,lang);
4018 if (!res) {
4019 /* Fifty, not fifty-zero */
4020 if (one != 0) {
4021 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4022 res = wait_file(chan,ints,nextmsg,lang);
4026 break;
4027 case 'T':
4028 res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
4029 break;
4030 case ' ':
4031 case ' ':
4032 /* Just ignore spaces and tabs */
4033 break;
4034 default:
4035 /* Unknown character */
4036 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4038 /* Jump out on DTMF */
4039 if (res) {
4040 break;
4043 return res;
4046 /* French syntax
4047 oclock = heure
4049 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)
4051 struct tm tm;
4052 int res=0, offset, sndoffset;
4053 char sndfile[256], nextmsg[256];
4055 if (format == NULL)
4056 format = "AdBY 'digits/at' IMp";
4058 ast_localtime(&time,&tm,timezone);
4060 for (offset=0 ; format[offset] != '\0' ; offset++) {
4061 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4062 switch (format[offset]) {
4063 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4064 case '\'':
4065 /* Literal name of a sound file */
4066 sndoffset=0;
4067 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4068 sndfile[sndoffset] = format[offset];
4069 sndfile[sndoffset] = '\0';
4070 res = wait_file(chan,ints,sndfile,lang);
4071 break;
4072 case 'A':
4073 case 'a':
4074 /* Sunday - Saturday */
4075 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4076 res = wait_file(chan,ints,nextmsg,lang);
4077 break;
4078 case 'B':
4079 case 'b':
4080 case 'h':
4081 /* January - December */
4082 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4083 res = wait_file(chan,ints,nextmsg,lang);
4084 break;
4085 case 'm':
4086 /* First - Twelfth */
4087 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4088 res = wait_file(chan,ints,nextmsg,lang);
4089 break;
4090 case 'd':
4091 case 'e':
4092 /* First */
4093 if (tm.tm_mday == 1) {
4094 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4095 res = wait_file(chan,ints,nextmsg,lang);
4096 } else {
4097 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4099 break;
4100 case 'Y':
4101 /* Year */
4102 if (tm.tm_year > 99) {
4103 res = wait_file(chan,ints, "digits/2",lang);
4104 if (!res) {
4105 res = wait_file(chan,ints, "digits/thousand",lang);
4107 if (tm.tm_year > 100) {
4108 if (!res) {
4109 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
4112 } else {
4113 if (tm.tm_year < 1) {
4114 /* I'm not going to handle 1900 and prior */
4115 /* We'll just be silent on the year, instead of bombing out. */
4116 } else {
4117 res = wait_file(chan,ints, "digits/thousand",lang);
4118 if (!res) {
4119 wait_file(chan,ints, "digits/9",lang);
4120 wait_file(chan,ints, "digits/hundred",lang);
4121 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
4125 break;
4126 case 'I':
4127 case 'l':
4128 /* 12-Hour */
4129 if (tm.tm_hour == 0)
4130 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4131 else if (tm.tm_hour > 12)
4132 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4133 else
4134 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4135 res = wait_file(chan,ints,nextmsg,lang);
4136 if (!res)
4137 res = wait_file(chan,ints, "digits/oclock",lang);
4138 break;
4139 case 'H':
4140 case 'k':
4141 /* 24-Hour */
4142 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4143 if (!res)
4144 res = wait_file(chan,ints, "digits/oclock",lang);
4145 break;
4146 case 'M':
4147 /* Minute */
4148 if (tm.tm_min == 0) {
4149 break;
4151 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
4152 break;
4153 case 'P':
4154 case 'p':
4155 /* AM/PM */
4156 if (tm.tm_hour > 11)
4157 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4158 else
4159 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4160 res = wait_file(chan,ints,nextmsg,lang);
4161 break;
4162 case 'Q':
4163 /* Shorthand for "Today", "Yesterday", or AdBY */
4164 /* XXX As emphasized elsewhere, this should the native way in your
4165 * language to say the date, with changes in what you say, depending
4166 * upon how recent the date is. XXX */
4168 struct timeval now;
4169 struct tm tmnow;
4170 time_t beg_today, tt;
4172 gettimeofday(&now,NULL);
4173 tt = now.tv_sec;
4174 ast_localtime(&tt,&tmnow,timezone);
4175 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4176 /* In any case, it saves not having to do ast_mktime() */
4177 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4178 if (beg_today < time) {
4179 /* Today */
4180 res = wait_file(chan,ints, "digits/today",lang);
4181 } else if (beg_today - 86400 < time) {
4182 /* Yesterday */
4183 res = wait_file(chan,ints, "digits/yesterday",lang);
4184 } else {
4185 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4188 break;
4189 case 'q':
4190 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4191 /* XXX As emphasized elsewhere, this should the native way in your
4192 * language to say the date, with changes in what you say, depending
4193 * upon how recent the date is. XXX */
4195 struct timeval now;
4196 struct tm tmnow;
4197 time_t beg_today, tt;
4199 gettimeofday(&now,NULL);
4200 tt = now.tv_sec;
4201 ast_localtime(&tt,&tmnow,timezone);
4202 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4203 /* In any case, it saves not having to do ast_mktime() */
4204 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4205 if (beg_today < time) {
4206 /* Today */
4207 } else if ((beg_today - 86400) < time) {
4208 /* Yesterday */
4209 res = wait_file(chan,ints, "digits/yesterday",lang);
4210 } else if (beg_today - 86400 * 6 < time) {
4211 /* Within the last week */
4212 res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
4213 } else {
4214 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4217 break;
4218 case 'R':
4219 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
4220 break;
4221 case 'S':
4222 /* Seconds */
4223 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
4224 if (!res) {
4225 res = wait_file(chan,ints, "digits/second",lang);
4227 break;
4228 case 'T':
4229 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
4230 break;
4231 case ' ':
4232 case ' ':
4233 /* Just ignore spaces and tabs */
4234 break;
4235 default:
4236 /* Unknown character */
4237 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4239 /* Jump out on DTMF */
4240 if (res) {
4241 break;
4244 return res;
4247 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)
4249 struct tm tm;
4250 int res=0, offset, sndoffset;
4251 char sndfile[256], nextmsg[256];
4253 if (format == NULL)
4254 format = "AdB 'digits/at' IMp";
4256 ast_localtime(&time,&tm,timezone);
4258 for (offset=0 ; format[offset] != '\0' ; offset++) {
4259 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4260 switch (format[offset]) {
4261 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4262 case '\'':
4263 /* Literal name of a sound file */
4264 sndoffset=0;
4265 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4266 sndfile[sndoffset] = format[offset];
4267 sndfile[sndoffset] = '\0';
4268 res = wait_file(chan,ints,sndfile,lang);
4269 break;
4270 case 'A':
4271 case 'a':
4272 /* Sunday - Saturday */
4273 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4274 res = wait_file(chan,ints,nextmsg,lang);
4275 break;
4276 case 'B':
4277 case 'b':
4278 case 'h':
4279 /* January - December */
4280 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4281 res = wait_file(chan,ints,nextmsg,lang);
4282 break;
4283 case 'm':
4284 /* First - Twelfth */
4285 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4286 res = wait_file(chan,ints,nextmsg,lang);
4287 break;
4288 case 'd':
4289 case 'e':
4290 /* First day of the month is spelled as ordinal */
4291 if (tm.tm_mday == 1) {
4292 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4293 res = wait_file(chan,ints,nextmsg,lang);
4294 } else {
4295 if (!res) {
4296 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4299 break;
4300 case 'Y':
4301 /* Year */
4302 if (tm.tm_year > 99) {
4303 res = wait_file(chan,ints, "digits/ore-2000",lang);
4304 if (tm.tm_year > 100) {
4305 if (!res) {
4306 /* This works until the end of 2021 */
4307 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4308 res = wait_file(chan,ints,nextmsg,lang);
4311 } else {
4312 if (tm.tm_year < 1) {
4313 /* I'm not going to handle 1900 and prior */
4314 /* We'll just be silent on the year, instead of bombing out. */
4315 } else {
4316 res = wait_file(chan,ints, "digits/ore-1900",lang);
4317 if ((!res) && (tm.tm_year != 0)) {
4318 if (tm.tm_year <= 21) {
4319 /* 1910 - 1921 */
4320 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4321 res = wait_file(chan,ints,nextmsg,lang);
4322 } else {
4323 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4324 int ten, one;
4325 ten = tm.tm_year / 10;
4326 one = tm.tm_year % 10;
4327 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4328 res = wait_file(chan,ints,nextmsg,lang);
4329 if (!res) {
4330 if (one != 0) {
4331 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4332 res = wait_file(chan,ints,nextmsg,lang);
4339 break;
4340 case 'I':
4341 case 'l':
4342 /* 12-Hour */
4343 if (tm.tm_hour == 0)
4344 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4345 else if (tm.tm_hour > 12)
4346 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4347 else
4348 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4349 res = wait_file(chan,ints,nextmsg,lang);
4350 break;
4351 case 'H':
4352 case 'k':
4353 /* 24-Hour */
4354 if (tm.tm_hour == 0) {
4355 res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
4356 } else if (tm.tm_hour == 1) {
4357 res = wait_file(chan,ints, "digits/ore-una",lang);
4358 } else {
4359 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4361 break;
4362 case 'M':
4363 /* Minute */
4364 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4365 break;
4366 case 'P':
4367 case 'p':
4368 /* AM/PM */
4369 if (tm.tm_hour > 11)
4370 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4371 else
4372 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4373 res = wait_file(chan,ints,nextmsg,lang);
4374 break;
4375 case 'Q':
4376 /* Shorthand for "Today", "Yesterday", or ABdY */
4377 /* XXX As emphasized elsewhere, this should the native way in your
4378 * language to say the date, with changes in what you say, depending
4379 * upon how recent the date is. XXX */
4381 struct timeval now;
4382 struct tm tmnow;
4383 time_t beg_today, tt;
4385 gettimeofday(&now,NULL);
4386 tt = now.tv_sec;
4387 ast_localtime(&tt,&tmnow,timezone);
4388 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4389 /* In any case, it saves not having to do ast_mktime() */
4390 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4391 if (beg_today < time) {
4392 /* Today */
4393 res = wait_file(chan,ints, "digits/today",lang);
4394 } else if (beg_today - 86400 < time) {
4395 /* Yesterday */
4396 res = wait_file(chan,ints, "digits/yesterday",lang);
4397 } else {
4398 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4401 break;
4402 case 'q':
4403 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4405 struct timeval now;
4406 struct tm tmnow;
4407 time_t beg_today, tt;
4409 gettimeofday(&now,NULL);
4410 tt = now.tv_sec;
4411 ast_localtime(&tt,&tmnow,timezone);
4412 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4413 /* In any case, it saves not having to do ast_mktime() */
4414 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4415 if (beg_today < time) {
4416 /* Today */
4417 } else if ((beg_today - 86400) < time) {
4418 /* Yesterday */
4419 res = wait_file(chan,ints, "digits/yesterday",lang);
4420 } else if (beg_today - 86400 * 6 < time) {
4421 /* Within the last week */
4422 res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
4423 } else {
4424 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4427 break;
4428 case 'R':
4429 res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
4430 break;
4431 case 'S':
4432 /* Seconds */
4433 if (tm.tm_sec == 0) {
4434 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4435 res = wait_file(chan,ints,nextmsg,lang);
4436 } else if (tm.tm_sec < 10) {
4437 res = wait_file(chan,ints, "digits/oh",lang);
4438 if (!res) {
4439 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4440 res = wait_file(chan,ints,nextmsg,lang);
4442 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4443 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4444 res = wait_file(chan,ints,nextmsg,lang);
4445 } else {
4446 int ten, one;
4447 ten = (tm.tm_sec / 10) * 10;
4448 one = (tm.tm_sec % 10);
4449 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4450 res = wait_file(chan,ints,nextmsg,lang);
4451 if (!res) {
4452 /* Fifty, not fifty-zero */
4453 if (one != 0) {
4454 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4455 res = wait_file(chan,ints,nextmsg,lang);
4459 break;
4460 case 'T':
4461 res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
4462 break;
4463 case ' ':
4464 case ' ':
4465 /* Just ignore spaces and tabs */
4466 break;
4467 default:
4468 /* Unknown character */
4469 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4471 /* Jump out on DTMF */
4472 if (res) {
4473 break;
4476 return res;
4479 /* Dutch syntax */
4480 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)
4482 struct tm tm;
4483 int res=0, offset, sndoffset;
4484 char sndfile[256], nextmsg[256];
4486 if (format == NULL)
4487 format = "ABdY 'digits/at' IMp";
4489 ast_localtime(&time,&tm,timezone);
4491 for (offset=0 ; format[offset] != '\0' ; offset++) {
4492 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4493 switch (format[offset]) {
4494 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4495 case '\'':
4496 /* Literal name of a sound file */
4497 sndoffset=0;
4498 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4499 sndfile[sndoffset] = format[offset];
4500 sndfile[sndoffset] = '\0';
4501 res = wait_file(chan,ints,sndfile,lang);
4502 break;
4503 case 'A':
4504 case 'a':
4505 /* Sunday - Saturday */
4506 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4507 res = wait_file(chan,ints,nextmsg,lang);
4508 break;
4509 case 'B':
4510 case 'b':
4511 case 'h':
4512 /* January - December */
4513 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4514 res = wait_file(chan,ints,nextmsg,lang);
4515 break;
4516 case 'm':
4517 /* First - Twelfth */
4518 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4519 res = wait_file(chan,ints,nextmsg,lang);
4520 break;
4521 case 'd':
4522 case 'e':
4523 /* First - Thirtyfirst */
4524 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
4525 break;
4526 case 'Y':
4527 /* Year */
4528 if (tm.tm_year > 99) {
4529 res = wait_file(chan,ints, "digits/2",lang);
4530 if (!res) {
4531 res = wait_file(chan,ints, "digits/thousand",lang);
4533 if (tm.tm_year > 100) {
4534 if (!res) {
4535 /* This works until the end of 2020 */
4536 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4537 res = wait_file(chan,ints,nextmsg,lang);
4540 } else {
4541 if (tm.tm_year < 1) {
4542 /* I'm not going to handle 1900 and prior */
4543 /* We'll just be silent on the year, instead of bombing out. */
4544 } else {
4545 res = wait_file(chan,ints, "digits/19",lang);
4546 if (!res) {
4547 if (tm.tm_year <= 9) {
4548 /* 1901 - 1909 */
4549 res = wait_file(chan,ints, "digits/oh",lang);
4550 if (!res) {
4551 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4552 res = wait_file(chan,ints,nextmsg,lang);
4554 } else if (tm.tm_year <= 20) {
4555 /* 1910 - 1920 */
4556 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4557 res = wait_file(chan,ints,nextmsg,lang);
4558 } else {
4559 /* 1921 - 1999 */
4560 int ten, one;
4561 ten = tm.tm_year / 10;
4562 one = tm.tm_year % 10;
4563 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4564 res = wait_file(chan,ints,nextmsg,lang);
4565 if (!res) {
4566 if (one != 0) {
4567 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4568 res = wait_file(chan,ints,nextmsg,lang);
4575 break;
4576 case 'I':
4577 case 'l':
4578 /* 12-Hour */
4579 if (tm.tm_hour == 0)
4580 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4581 else if (tm.tm_hour > 12)
4582 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4583 else
4584 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4585 res = wait_file(chan,ints,nextmsg,lang);
4586 break;
4587 case 'H':
4588 case 'k':
4589 /* 24-Hour */
4590 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4591 if (!res) {
4592 res = wait_file(chan,ints, "digits/nl-uur",lang);
4594 break;
4595 case 'M':
4596 /* Minute */
4597 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4598 break;
4599 case 'P':
4600 case 'p':
4601 /* AM/PM */
4602 if (tm.tm_hour > 11)
4603 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4604 else
4605 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4606 res = wait_file(chan,ints,nextmsg,lang);
4607 break;
4608 case 'Q':
4609 /* Shorthand for "Today", "Yesterday", or ABdY */
4610 /* XXX As emphasized elsewhere, this should the native way in your
4611 * language to say the date, with changes in what you say, depending
4612 * upon how recent the date is. XXX */
4614 struct timeval now;
4615 struct tm tmnow;
4616 time_t beg_today, tt;
4618 gettimeofday(&now,NULL);
4619 tt = now.tv_sec;
4620 ast_localtime(&tt,&tmnow,timezone);
4621 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4622 /* In any case, it saves not having to do ast_mktime() */
4623 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4624 if (beg_today < time) {
4625 /* Today */
4626 res = wait_file(chan,ints, "digits/today",lang);
4627 } else if (beg_today - 86400 < time) {
4628 /* Yesterday */
4629 res = wait_file(chan,ints, "digits/yesterday",lang);
4630 } else {
4631 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4634 break;
4635 case 'q':
4636 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4638 struct timeval now;
4639 struct tm tmnow;
4640 time_t beg_today, tt;
4642 gettimeofday(&now,NULL);
4643 tt = now.tv_sec;
4644 ast_localtime(&tt,&tmnow,timezone);
4645 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4646 /* In any case, it saves not having to do ast_mktime() */
4647 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4648 if (beg_today < time) {
4649 /* Today */
4650 } else if ((beg_today - 86400) < time) {
4651 /* Yesterday */
4652 res = wait_file(chan,ints, "digits/yesterday",lang);
4653 } else if (beg_today - 86400 * 6 < time) {
4654 /* Within the last week */
4655 res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
4656 } else {
4657 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4660 break;
4661 case 'R':
4662 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
4663 break;
4664 case 'S':
4665 /* Seconds */
4666 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4667 break;
4668 case 'T':
4669 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
4670 break;
4671 case ' ':
4672 case ' ':
4673 /* Just ignore spaces and tabs */
4674 break;
4675 default:
4676 /* Unknown character */
4677 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4679 /* Jump out on DTMF */
4680 if (res) {
4681 break;
4684 return res;
4687 /* Polish syntax */
4688 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)
4690 struct tm tm;
4691 int res=0, offset, sndoffset;
4692 char sndfile[256], nextmsg[256];
4694 ast_localtime(&thetime, &tm, timezone);
4696 for (offset = 0 ; format[offset] != '\0' ; offset++) {
4697 int remainder;
4698 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4699 switch (format[offset]) {
4700 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4701 case '\'':
4702 /* Literal name of a sound file */
4703 sndoffset = 0;
4704 for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4705 sndfile[sndoffset] = format[offset];
4706 sndfile[sndoffset] = '\0';
4707 res = wait_file(chan, ints, sndfile, lang);
4708 break;
4709 case 'A':
4710 case 'a':
4711 /* Sunday - Saturday */
4712 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4713 res = wait_file(chan, ints, nextmsg, lang);
4714 break;
4715 case 'B':
4716 case 'b':
4717 case 'h':
4718 /* January - December */
4719 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4720 res = wait_file(chan, ints, nextmsg, lang);
4721 break;
4722 case 'm':
4723 /* Month enumerated */
4724 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
4725 break;
4726 case 'd':
4727 case 'e':
4728 /* First - Thirtyfirst */
4729 remainder = tm.tm_mday;
4730 if (tm.tm_mday > 30) {
4731 res = wait_file(chan, ints, "digits/h-30", lang);
4732 remainder -= 30;
4734 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
4735 res = wait_file(chan, ints, "digits/h-20", lang);
4736 remainder -= 20;
4738 if (!res) {
4739 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
4740 res = wait_file(chan, ints, nextmsg, lang);
4742 break;
4743 case 'Y':
4744 /* Year */
4745 if (tm.tm_year > 100) {
4746 res = wait_file(chan, ints, "digits/2", lang);
4747 if (!res)
4748 res = wait_file(chan, ints, "digits/1000.2",lang);
4749 if (tm.tm_year > 100) {
4750 if (!res)
4751 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
4753 } else if (tm.tm_year == 100) {
4754 res = wait_file(chan, ints, "digits/h-2000", lang);
4755 } else {
4756 if (tm.tm_year < 1) {
4757 /* I'm not going to handle 1900 and prior */
4758 /* We'll just be silent on the year, instead of bombing out. */
4759 break;
4760 } else {
4761 res = wait_file(chan, ints, "digits/1000", lang);
4762 if (!res) {
4763 wait_file(chan, ints, "digits/900", lang);
4764 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
4768 if (!res)
4769 wait_file(chan, ints, "digits/year", lang);
4770 break;
4771 case 'I':
4772 case 'l':
4773 /* 12-Hour */
4774 if (tm.tm_hour == 0)
4775 snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
4776 else if (tm.tm_hour > 12)
4777 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
4778 else
4779 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4781 res = wait_file(chan, ints, nextmsg, lang);
4782 break;
4783 case 'H':
4784 case 'k':
4785 /* 24-Hour */
4786 if (tm.tm_hour != 0) {
4787 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4788 res = wait_file(chan, ints, nextmsg, lang);
4789 } else
4790 res = wait_file(chan, ints, "digits/t-24", lang);
4791 break;
4792 case 'M':
4793 case 'N':
4794 /* Minute */
4795 if (tm.tm_min == 0) {
4796 if (format[offset] == 'M') {
4797 res = wait_file(chan, ints, "digits/oclock", lang);
4798 } else {
4799 res = wait_file(chan, ints, "digits/100", lang);
4801 } else
4802 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4803 break;
4804 case 'P':
4805 case 'p':
4806 /* AM/PM */
4807 if (tm.tm_hour > 11)
4808 snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
4809 else
4810 snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
4811 res = wait_file(chan, ints, nextmsg, lang);
4812 break;
4813 case 'Q':
4814 /* Shorthand for "Today", "Yesterday", or AdBY */
4816 time_t tv_sec = time(NULL);
4817 struct tm tmnow;
4818 time_t beg_today;
4820 ast_localtime(&tv_sec,&tmnow, timezone);
4821 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4822 /* In any case, it saves not having to do ast_mktime() */
4823 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4824 if (beg_today < thetime) {
4825 /* Today */
4826 res = wait_file(chan, ints, "digits/today", lang);
4827 } else if (beg_today - 86400 < thetime) {
4828 /* Yesterday */
4829 res = wait_file(chan, ints, "digits/yesterday", lang);
4830 } else {
4831 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4834 break;
4835 case 'q':
4836 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4838 time_t tv_sec = time(NULL);
4839 struct tm tmnow;
4840 time_t beg_today;
4842 ast_localtime(&tv_sec, &tmnow, timezone);
4843 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4844 /* In any case, it saves not having to do ast_mktime() */
4845 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4846 if (beg_today < thetime) {
4847 /* Today */
4848 } else if ((beg_today - 86400) < thetime) {
4849 /* Yesterday */
4850 res = wait_file(chan, ints, "digits/yesterday", lang);
4851 } else if (beg_today - 86400 * 6 < thetime) {
4852 /* Within the last week */
4853 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
4854 } else {
4855 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4858 break;
4859 case 'R':
4860 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
4861 break;
4862 case 'S':
4863 /* Seconds */
4864 res = wait_file(chan, ints, "digits/and", lang);
4865 if (!res) {
4866 if (tm.tm_sec == 1) {
4867 res = wait_file(chan, ints, "digits/1z", lang);
4868 if (!res)
4869 res = wait_file(chan, ints, "digits/second-a", lang);
4870 } else {
4871 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4872 if (!res) {
4873 int ten, one;
4874 ten = tm.tm_sec / 10;
4875 one = tm.tm_sec % 10;
4877 if (one > 1 && one < 5 && ten != 1)
4878 res = wait_file(chan,ints, "digits/seconds",lang);
4879 else
4880 res = wait_file(chan,ints, "digits/second",lang);
4884 break;
4885 case 'T':
4886 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
4887 break;
4888 case ' ':
4889 case ' ':
4890 /* Just ignore spaces and tabs */
4891 break;
4892 default:
4893 /* Unknown character */
4894 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4896 /* Jump out on DTMF */
4897 if (res)
4898 break;
4900 return res;
4903 /* Portuguese syntax */
4904 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)
4906 struct tm tm;
4907 int res=0, offset, sndoffset;
4908 char sndfile[256], nextmsg[256];
4910 if (format == NULL)
4911 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
4913 ast_localtime(&time,&tm,timezone);
4915 for (offset=0 ; format[offset] != '\0' ; offset++) {
4916 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4917 switch (format[offset]) {
4918 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4919 case '\'':
4920 /* Literal name of a sound file */
4921 sndoffset=0;
4922 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4923 sndfile[sndoffset] = format[offset];
4924 sndfile[sndoffset] = '\0';
4925 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
4926 res = wait_file(chan,ints,nextmsg,lang);
4927 break;
4928 case 'A':
4929 case 'a':
4930 /* Sunday - Saturday */
4931 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4932 res = wait_file(chan,ints,nextmsg,lang);
4933 break;
4934 case 'B':
4935 case 'b':
4936 case 'h':
4937 /* January - December */
4938 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4939 res = wait_file(chan,ints,nextmsg,lang);
4940 break;
4941 case 'm':
4942 /* First - Twelfth */
4943 if (!strcasecmp(lang, "pt_BR")) {
4944 res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
4945 } else {
4946 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4947 res = wait_file(chan,ints,nextmsg,lang);
4949 break;
4950 case 'd':
4951 case 'e':
4952 /* First - Thirtyfirst */
4953 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4954 break;
4955 case 'Y':
4956 /* Year */
4957 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4958 break;
4959 case 'I':
4960 case 'l':
4961 /* 12-Hour */
4962 if (!strcasecmp(lang, "pt_BR")) {
4963 if (tm.tm_hour == 0) {
4964 if (format[offset] == 'I')
4965 res = wait_file(chan, ints, "digits/pt-a", lang);
4966 if (!res)
4967 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
4968 } else if (tm.tm_hour == 12) {
4969 if (format[offset] == 'I')
4970 res = wait_file(chan, ints, "digits/pt-ao", lang);
4971 if (!res)
4972 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
4973 } else {
4974 if (format[offset] == 'I') {
4975 if ((tm.tm_hour % 12) != 1)
4976 res = wait_file(chan, ints, "digits/pt-as", lang);
4977 else
4978 res = wait_file(chan, ints, "digits/pt-a", lang);
4980 if (!res)
4981 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
4983 } else {
4984 if (tm.tm_hour == 0) {
4985 if (format[offset] == 'I')
4986 res = wait_file(chan, ints, "digits/pt-ah", lang);
4987 if (!res)
4988 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
4990 else if (tm.tm_hour == 12) {
4991 if (format[offset] == 'I')
4992 res = wait_file(chan, ints, "digits/pt-ao", lang);
4993 if (!res)
4994 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
4996 else {
4997 if (format[offset] == 'I') {
4998 res = wait_file(chan, ints, "digits/pt-ah", lang);
4999 if ((tm.tm_hour % 12) != 1)
5000 if (!res)
5001 res = wait_file(chan, ints, "digits/pt-sss", lang);
5003 if (!res)
5004 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
5007 break;
5008 case 'H':
5009 case 'k':
5010 /* 24-Hour */
5011 if (!strcasecmp(lang, "pt_BR")) {
5012 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5013 if ((!res) && (format[offset] == 'H')) {
5014 if (tm.tm_hour > 1) {
5015 res = wait_file(chan,ints,"digits/hours",lang);
5016 } else {
5017 res = wait_file(chan,ints,"digits/hour",lang);
5020 } else {
5021 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
5022 if (!res) {
5023 if (tm.tm_hour != 0) {
5024 int remainder = tm.tm_hour;
5025 if (tm.tm_hour > 20) {
5026 res = wait_file(chan,ints, "digits/20",lang);
5027 remainder -= 20;
5029 if (!res) {
5030 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
5031 res = wait_file(chan,ints,nextmsg,lang);
5036 break;
5037 case 'M':
5038 /* Minute */
5039 if (!strcasecmp(lang, "pt_BR")) {
5040 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5041 if (!res) {
5042 if (tm.tm_min > 1) {
5043 res = wait_file(chan,ints,"digits/minutes",lang);
5044 } else {
5045 res = wait_file(chan,ints,"digits/minute",lang);
5048 } else {
5049 if (tm.tm_min == 0) {
5050 res = wait_file(chan, ints, "digits/pt-hora", lang);
5051 if (tm.tm_hour != 1)
5052 if (!res)
5053 res = wait_file(chan, ints, "digits/pt-sss", lang);
5054 } else {
5055 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5058 break;
5059 case 'P':
5060 case 'p':
5061 /* AM/PM */
5062 if (!strcasecmp(lang, "pt_BR")) {
5063 if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
5064 res = wait_file(chan, ints, "digits/pt-da", lang);
5065 if (!res) {
5066 if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
5067 res = wait_file(chan, ints, "digits/morning", lang);
5068 else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
5069 res = wait_file(chan, ints, "digits/afternoon", lang);
5070 else res = wait_file(chan, ints, "digits/night", lang);
5073 } else {
5074 if (tm.tm_hour > 12)
5075 res = wait_file(chan, ints, "digits/p-m", lang);
5076 else if (tm.tm_hour && tm.tm_hour < 12)
5077 res = wait_file(chan, ints, "digits/a-m", lang);
5079 break;
5080 case 'Q':
5081 /* Shorthand for "Today", "Yesterday", or ABdY */
5082 /* XXX As emphasized elsewhere, this should the native way in your
5083 * language to say the date, with changes in what you say, depending
5084 * upon how recent the date is. XXX */
5086 struct timeval now;
5087 struct tm tmnow;
5088 time_t beg_today, tt;
5090 gettimeofday(&now,NULL);
5091 tt = now.tv_sec;
5092 ast_localtime(&tt,&tmnow,timezone);
5093 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5094 /* In any case, it saves not having to do ast_mktime() */
5095 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5096 if (beg_today < time) {
5097 /* Today */
5098 res = wait_file(chan,ints, "digits/today",lang);
5099 } else if (beg_today - 86400 < time) {
5100 /* Yesterday */
5101 res = wait_file(chan,ints, "digits/yesterday",lang);
5102 } else {
5103 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5106 break;
5107 case 'q':
5108 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5109 /* XXX As emphasized elsewhere, this should the native way in your
5110 * language to say the date, with changes in what you say, depending
5111 * upon how recent the date is. XXX */
5113 struct timeval now;
5114 struct tm tmnow;
5115 time_t beg_today, tt;
5117 gettimeofday(&now,NULL);
5118 tt = now.tv_sec;
5119 ast_localtime(&tt,&tmnow,timezone);
5120 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5121 /* In any case, it saves not having to do ast_mktime() */
5122 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5123 if (beg_today < time) {
5124 /* Today */
5125 } else if ((beg_today - 86400) < time) {
5126 /* Yesterday */
5127 res = wait_file(chan,ints, "digits/yesterday",lang);
5128 } else if (beg_today - 86400 * 6 < time) {
5129 /* Within the last week */
5130 res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
5131 } else {
5132 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5135 break;
5136 case 'R':
5137 res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
5138 break;
5139 case 'S':
5140 /* Seconds */
5141 if (!strcasecmp(lang, "pt_BR")) {
5142 res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
5143 if (!res) {
5144 if (tm.tm_sec > 1) {
5145 res = wait_file(chan,ints,"digits/seconds",lang);
5146 } else {
5147 res = wait_file(chan,ints,"digits/second",lang);
5149 } else if (tm.tm_sec < 10) {
5150 res = wait_file(chan,ints, "digits/oh",lang);
5151 if (!res) {
5152 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5153 res = wait_file(chan,ints,nextmsg,lang);
5155 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5156 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5157 res = wait_file(chan,ints,nextmsg,lang);
5158 } else {
5159 int ten, one;
5160 ten = (tm.tm_sec / 10) * 10;
5161 one = (tm.tm_sec % 10);
5162 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
5163 res = wait_file(chan,ints,nextmsg,lang);
5164 if (!res) {
5165 /* Fifty, not fifty-zero */
5166 if (one != 0) {
5167 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
5168 res = wait_file(chan,ints,nextmsg,lang);
5173 break;
5174 case 'T':
5175 res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
5176 break;
5177 case ' ':
5178 case ' ':
5179 /* Just ignore spaces and tabs */
5180 break;
5181 default:
5182 /* Unknown character */
5183 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5185 /* Jump out on DTMF */
5186 if (res) {
5187 break;
5190 return res;
5193 /* Taiwanese / Chinese syntax */
5194 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)
5196 struct tm tm;
5197 int res=0, offset, sndoffset;
5198 char sndfile[256], nextmsg[256];
5200 if (format == NULL)
5201 format = "YBdAkM";
5203 ast_localtime(&time,&tm,timezone);
5205 for (offset=0 ; format[offset] != '\0' ; offset++) {
5206 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5207 switch (format[offset]) {
5208 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5209 case '\'':
5210 /* Literal name of a sound file */
5211 sndoffset=0;
5212 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5213 sndfile[sndoffset] = format[offset];
5214 sndfile[sndoffset] = '\0';
5215 res = wait_file(chan,ints,sndfile,lang);
5216 break;
5217 case 'A':
5218 case 'a':
5219 /* Sunday - Saturday */
5220 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5221 res = wait_file(chan,ints,nextmsg,lang);
5222 break;
5223 case 'B':
5224 case 'b':
5225 case 'h':
5226 case 'm':
5227 /* January - December */
5228 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5229 res = wait_file(chan,ints,nextmsg,lang);
5230 break;
5231 case 'd':
5232 case 'e':
5233 /* First - Thirtyfirst */
5234 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
5235 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
5236 res = wait_file(chan,ints,nextmsg,lang);
5237 } else {
5238 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
5239 res = wait_file(chan,ints,nextmsg,lang);
5240 if (!res) {
5241 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
5242 res = wait_file(chan,ints,nextmsg,lang);
5245 if (!res) res = wait_file(chan,ints,"digits/day",lang);
5246 break;
5247 case 'Y':
5248 /* Year */
5249 if (tm.tm_year > 99) {
5250 res = wait_file(chan,ints, "digits/2",lang);
5251 if (!res) {
5252 res = wait_file(chan,ints, "digits/thousand",lang);
5254 if (tm.tm_year > 100) {
5255 if (!res) {
5256 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
5257 res = wait_file(chan,ints,nextmsg,lang);
5258 if (!res) {
5259 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
5260 res = wait_file(chan,ints,nextmsg,lang);
5264 if (!res) {
5265 res = wait_file(chan,ints, "digits/year",lang);
5267 } else {
5268 if (tm.tm_year < 1) {
5269 /* I'm not going to handle 1900 and prior */
5270 /* We'll just be silent on the year, instead of bombing out. */
5271 } else {
5272 res = wait_file(chan,ints, "digits/1",lang);
5273 if (!res) {
5274 res = wait_file(chan,ints, "digits/9",lang);
5276 if (!res) {
5277 if (tm.tm_year <= 9) {
5278 /* 1901 - 1909 */
5279 res = wait_file(chan,ints, "digits/0",lang);
5280 if (!res) {
5281 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
5282 res = wait_file(chan,ints,nextmsg,lang);
5284 } else {
5285 /* 1910 - 1999 */
5286 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
5287 res = wait_file(chan,ints,nextmsg,lang);
5288 if (!res) {
5289 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
5290 res = wait_file(chan,ints,nextmsg,lang);
5295 if (!res) {
5296 res = wait_file(chan,ints, "digits/year",lang);
5299 break;
5300 case 'I':
5301 case 'l':
5302 /* 12-Hour */
5303 if (tm.tm_hour == 0)
5304 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
5305 else if (tm.tm_hour > 12)
5306 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5307 else
5308 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5309 res = wait_file(chan,ints,nextmsg,lang);
5310 if (!res) {
5311 res = wait_file(chan,ints, "digits/oclock",lang);
5313 break;
5314 case 'H':
5315 if (tm.tm_hour < 10) {
5316 res = wait_file(chan, ints, "digits/0", lang);
5318 case 'k':
5319 /* 24-Hour */
5320 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
5321 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5322 res = wait_file(chan,ints,nextmsg,lang);
5323 } else {
5324 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
5325 res = wait_file(chan,ints,nextmsg,lang);
5326 if (!res) {
5327 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
5328 res = wait_file(chan,ints,nextmsg,lang);
5331 if (!res) {
5332 res = wait_file(chan,ints, "digits/oclock",lang);
5334 break;
5335 case 'M':
5336 /* Minute */
5337 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
5338 if (tm.tm_min < 10) {
5339 res = wait_file(chan, ints, "digits/0", lang);
5341 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
5342 res = wait_file(chan,ints,nextmsg,lang);
5343 } else {
5344 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
5345 res = wait_file(chan,ints,nextmsg,lang);
5346 if (!res) {
5347 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
5348 res = wait_file(chan,ints,nextmsg,lang);
5351 if (!res) {
5352 res = wait_file(chan,ints, "digits/minute",lang);
5354 break;
5355 case 'P':
5356 case 'p':
5357 /* AM/PM */
5358 if (tm.tm_hour > 11)
5359 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
5360 else
5361 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
5362 res = wait_file(chan,ints,nextmsg,lang);
5363 break;
5364 case 'Q':
5365 /* Shorthand for "Today", "Yesterday", or ABdY */
5366 /* XXX As emphasized elsewhere, this should the native way in your
5367 * language to say the date, with changes in what you say, depending
5368 * upon how recent the date is. XXX */
5370 struct timeval now;
5371 struct tm tmnow;
5372 time_t beg_today, tt;
5374 gettimeofday(&now,NULL);
5375 tt = now.tv_sec;
5376 ast_localtime(&tt,&tmnow,timezone);
5377 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5378 /* In any case, it saves not having to do ast_mktime() */
5379 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5380 if (beg_today < time) {
5381 /* Today */
5382 res = wait_file(chan,ints, "digits/today",lang);
5383 } else if (beg_today - 86400 < time) {
5384 /* Yesterday */
5385 res = wait_file(chan,ints, "digits/yesterday",lang);
5386 } else {
5387 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5390 break;
5391 case 'q':
5392 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5393 /* XXX As emphasized elsewhere, this should the native way in your
5394 * language to say the date, with changes in what you say, depending
5395 * upon how recent the date is. XXX */
5397 struct timeval now;
5398 struct tm tmnow;
5399 time_t beg_today, tt;
5401 gettimeofday(&now,NULL);
5402 tt = now.tv_sec;
5403 ast_localtime(&tt,&tmnow,timezone);
5404 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5405 /* In any case, it saves not having to do ast_mktime() */
5406 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5407 if (beg_today < time) {
5408 /* Today */
5409 } else if ((beg_today - 86400) < time) {
5410 /* Yesterday */
5411 res = wait_file(chan,ints, "digits/yesterday",lang);
5412 } else if (beg_today - 86400 * 6 < time) {
5413 /* Within the last week */
5414 res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
5415 } else {
5416 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5419 break;
5420 case 'R':
5421 res = ast_say_date_with_format_tw(chan, time, ints, lang, "kM", timezone);
5422 break;
5423 case 'S':
5424 /* Seconds */
5425 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
5426 if (tm.tm_sec < 10) {
5427 res = wait_file(chan, ints, "digits/0", lang);
5429 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5430 res = wait_file(chan,ints,nextmsg,lang);
5431 } else {
5432 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
5433 res = wait_file(chan,ints,nextmsg,lang);
5434 if (!res) {
5435 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
5436 res = wait_file(chan,ints,nextmsg,lang);
5439 if (!res) {
5440 res = wait_file(chan,ints, "digits/second",lang);
5442 break;
5443 case 'T':
5444 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
5445 break;
5446 case ' ':
5447 case ' ':
5448 /* Just ignore spaces and tabs */
5449 break;
5450 default:
5451 /* Unknown character */
5452 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5454 /* Jump out on DTMF */
5455 if (res) {
5456 break;
5459 return res;
5462 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5464 if (!strcasecmp(lang, "en") ) { /* English syntax */
5465 return(ast_say_time_en(chan, t, ints, lang));
5466 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5467 return(ast_say_time_de(chan, t, ints, lang));
5468 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5469 return(ast_say_time_fr(chan, t, ints, lang));
5470 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5471 return(ast_say_time_nl(chan, t, ints, lang));
5472 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5473 return(ast_say_time_pt(chan, t, ints, lang));
5474 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5475 return(ast_say_time_pt_BR(chan, t, ints, lang));
5476 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5477 return(ast_say_time_tw(chan, t, ints, lang));
5478 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5479 return(ast_say_time_gr(chan, t, ints, lang));
5480 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5481 return(ast_say_time_ge(chan, t, ints, lang));
5484 /* Default to English */
5485 return(ast_say_time_en(chan, t, ints, lang));
5488 /* English syntax */
5489 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5491 struct tm tm;
5492 int res = 0;
5493 int hour, pm=0;
5495 ast_localtime(&t, &tm, NULL);
5496 hour = tm.tm_hour;
5497 if (!hour)
5498 hour = 12;
5499 else if (hour == 12)
5500 pm = 1;
5501 else if (hour > 12) {
5502 hour -= 12;
5503 pm = 1;
5505 if (!res)
5506 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5508 if (tm.tm_min > 9) {
5509 if (!res)
5510 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5511 } else if (tm.tm_min) {
5512 if (!res)
5513 res = ast_streamfile(chan, "digits/oh", lang);
5514 if (!res)
5515 res = ast_waitstream(chan, ints);
5516 if (!res)
5517 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5518 } else {
5519 if (!res)
5520 res = ast_streamfile(chan, "digits/oclock", lang);
5521 if (!res)
5522 res = ast_waitstream(chan, ints);
5524 if (pm) {
5525 if (!res)
5526 res = ast_streamfile(chan, "digits/p-m", lang);
5527 } else {
5528 if (!res)
5529 res = ast_streamfile(chan, "digits/a-m", lang);
5531 if (!res)
5532 res = ast_waitstream(chan, ints);
5533 return res;
5536 /* German syntax */
5537 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5539 struct tm tm;
5540 int res = 0;
5542 ast_localtime(&t, &tm, NULL);
5543 if (!res)
5544 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5545 if (!res)
5546 res = ast_streamfile(chan, "digits/oclock", lang);
5547 if (!res)
5548 res = ast_waitstream(chan, ints);
5549 if (!res)
5550 if (tm.tm_min > 0)
5551 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5552 return res;
5555 /* French syntax */
5556 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5558 struct tm tm;
5559 int res = 0;
5561 ast_localtime(&t, &tm, NULL);
5563 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5564 if (!res)
5565 res = ast_streamfile(chan, "digits/oclock", lang);
5566 if (tm.tm_min) {
5567 if (!res)
5568 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5570 return res;
5573 /* Dutch syntax */
5574 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5576 struct tm tm;
5577 int res = 0;
5579 ast_localtime(&t, &tm, NULL);
5580 if (!res)
5581 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5582 if (!res)
5583 res = ast_streamfile(chan, "digits/nl-uur", lang);
5584 if (!res)
5585 res = ast_waitstream(chan, ints);
5586 if (!res)
5587 if (tm.tm_min > 0)
5588 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5589 return res;
5592 /* Portuguese syntax */
5593 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5595 struct tm tm;
5596 int res = 0;
5597 int hour;
5599 ast_localtime(&t, &tm, NULL);
5600 hour = tm.tm_hour;
5601 if (!res)
5602 res = ast_say_number(chan, hour, ints, lang, "f");
5603 if (tm.tm_min) {
5604 if (!res)
5605 res = wait_file(chan, ints, "digits/pt-e", lang);
5606 if (!res)
5607 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5608 } else {
5609 if (!res)
5610 res = wait_file(chan, ints, "digits/pt-hora", lang);
5611 if (tm.tm_hour != 1)
5612 if (!res)
5613 res = wait_file(chan, ints, "digits/pt-sss", lang);
5615 if (!res)
5616 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5617 return res;
5620 /* Brazilian Portuguese syntax */
5621 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5623 struct tm tm;
5624 int res = 0;
5626 ast_localtime(&t, &tm, NULL);
5628 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5629 if (!res) {
5630 if (tm.tm_hour > 1)
5631 res = wait_file(chan, ints, "digits/hours", lang);
5632 else
5633 res = wait_file(chan, ints, "digits/hour", lang);
5635 if ((!res) && (tm.tm_min)) {
5636 res = wait_file(chan, ints, "digits/pt-e", lang);
5637 if (!res)
5638 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5639 if (!res) {
5640 if (tm.tm_min > 1)
5641 res = wait_file(chan, ints, "digits/minutes", lang);
5642 else
5643 res = wait_file(chan, ints, "digits/minute", lang);
5646 return res;
5649 /* Taiwanese / Chinese syntax */
5650 int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5652 struct tm tm;
5653 int res = 0;
5654 int hour, pm=0;
5656 ast_localtime(&t, &tm, NULL);
5657 hour = tm.tm_hour;
5658 if (!hour)
5659 hour = 12;
5660 else if (hour == 12)
5661 pm = 1;
5662 else if (hour > 12) {
5663 hour -= 12;
5664 pm = 1;
5666 if (pm) {
5667 if (!res)
5668 res = ast_streamfile(chan, "digits/p-m", lang);
5669 } else {
5670 if (!res)
5671 res = ast_streamfile(chan, "digits/a-m", lang);
5673 if (!res)
5674 res = ast_waitstream(chan, ints);
5675 if (!res)
5676 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5677 if (!res)
5678 res = ast_streamfile(chan, "digits/oclock", lang);
5679 if (!res)
5680 res = ast_waitstream(chan, ints);
5681 if (!res)
5682 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5683 if (!res)
5684 res = ast_streamfile(chan, "digits/minute", lang);
5685 if (!res)
5686 res = ast_waitstream(chan, ints);
5687 return res;
5690 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5692 if (!strcasecmp(lang, "en") ) { /* English syntax */
5693 return(ast_say_datetime_en(chan, t, ints, lang));
5694 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5695 return(ast_say_datetime_de(chan, t, ints, lang));
5696 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5697 return(ast_say_datetime_fr(chan, t, ints, lang));
5698 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5699 return(ast_say_datetime_nl(chan, t, ints, lang));
5700 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5701 return(ast_say_datetime_pt(chan, t, ints, lang));
5702 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5703 return(ast_say_datetime_pt_BR(chan, t, ints, lang));
5704 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5705 return(ast_say_datetime_tw(chan, t, ints, lang));
5706 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5707 return(ast_say_datetime_gr(chan, t, ints, lang));
5708 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5709 return(ast_say_datetime_ge(chan, t, ints, lang));
5712 /* Default to English */
5713 return(ast_say_datetime_en(chan, t, ints, lang));
5716 /* English syntax */
5717 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5719 struct tm tm;
5720 char fn[256];
5721 int res = 0;
5722 int hour, pm=0;
5724 ast_localtime(&t, &tm, NULL);
5725 if (!res) {
5726 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5727 res = ast_streamfile(chan, fn, lang);
5728 if (!res)
5729 res = ast_waitstream(chan, ints);
5731 if (!res) {
5732 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5733 res = ast_streamfile(chan, fn, lang);
5734 if (!res)
5735 res = ast_waitstream(chan, ints);
5737 if (!res)
5738 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5740 hour = tm.tm_hour;
5741 if (!hour)
5742 hour = 12;
5743 else if (hour == 12)
5744 pm = 1;
5745 else if (hour > 12) {
5746 hour -= 12;
5747 pm = 1;
5749 if (!res)
5750 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5752 if (tm.tm_min > 9) {
5753 if (!res)
5754 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5755 } else if (tm.tm_min) {
5756 if (!res)
5757 res = ast_streamfile(chan, "digits/oh", lang);
5758 if (!res)
5759 res = ast_waitstream(chan, ints);
5760 if (!res)
5761 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5762 } else {
5763 if (!res)
5764 res = ast_streamfile(chan, "digits/oclock", lang);
5765 if (!res)
5766 res = ast_waitstream(chan, ints);
5768 if (pm) {
5769 if (!res)
5770 res = ast_streamfile(chan, "digits/p-m", lang);
5771 } else {
5772 if (!res)
5773 res = ast_streamfile(chan, "digits/a-m", lang);
5775 if (!res)
5776 res = ast_waitstream(chan, ints);
5777 if (!res)
5778 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5779 return res;
5782 /* German syntax */
5783 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5785 struct tm tm;
5786 int res = 0;
5788 ast_localtime(&t, &tm, NULL);
5789 res = ast_say_date(chan, t, ints, lang);
5790 if (!res)
5791 ast_say_time(chan, t, ints, lang);
5792 return res;
5796 /* French syntax */
5797 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5799 struct tm tm;
5800 char fn[256];
5801 int res = 0;
5803 ast_localtime(&t, &tm, NULL);
5805 if (!res)
5806 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5808 if (!res) {
5809 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5810 res = ast_streamfile(chan, fn, lang);
5811 if (!res)
5812 res = ast_waitstream(chan, ints);
5814 if (!res) {
5815 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5816 res = ast_streamfile(chan, fn, lang);
5817 if (!res)
5818 res = ast_waitstream(chan, ints);
5821 if (!res)
5822 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5823 if (!res)
5824 res = ast_streamfile(chan, "digits/oclock", lang);
5825 if (tm.tm_min > 0) {
5826 if (!res)
5827 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5829 if (!res)
5830 res = ast_waitstream(chan, ints);
5831 if (!res)
5832 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5833 return res;
5836 /* Dutch syntax */
5837 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5839 struct tm tm;
5840 int res = 0;
5842 ast_localtime(&t, &tm, NULL);
5843 res = ast_say_date(chan, t, ints, lang);
5844 if (!res) {
5845 res = ast_streamfile(chan, "digits/nl-om", lang);
5846 if (!res)
5847 res = ast_waitstream(chan, ints);
5849 if (!res)
5850 ast_say_time(chan, t, ints, lang);
5851 return res;
5854 /* Portuguese syntax */
5855 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5857 struct tm tm;
5858 char fn[256];
5859 int res = 0;
5860 int hour, pm=0;
5862 ast_localtime(&t, &tm, NULL);
5863 if (!res) {
5864 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5865 res = ast_streamfile(chan, fn, lang);
5866 if (!res)
5867 res = ast_waitstream(chan, ints);
5869 if (!res) {
5870 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5871 res = ast_streamfile(chan, fn, lang);
5872 if (!res)
5873 res = ast_waitstream(chan, ints);
5875 if (!res)
5876 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5878 hour = tm.tm_hour;
5879 if (!hour)
5880 hour = 12;
5881 else if (hour == 12)
5882 pm = 1;
5883 else if (hour > 12) {
5884 hour -= 12;
5885 pm = 1;
5887 if (!res)
5888 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5890 if (tm.tm_min > 9) {
5891 if (!res)
5892 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5893 } else if (tm.tm_min) {
5894 if (!res)
5895 res = ast_streamfile(chan, "digits/oh", lang);
5896 if (!res)
5897 res = ast_waitstream(chan, ints);
5898 if (!res)
5899 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5900 } else {
5901 if (!res)
5902 res = ast_streamfile(chan, "digits/oclock", lang);
5903 if (!res)
5904 res = ast_waitstream(chan, ints);
5906 if (pm) {
5907 if (!res)
5908 res = ast_streamfile(chan, "digits/p-m", lang);
5909 } else {
5910 if (!res)
5911 res = ast_streamfile(chan, "digits/a-m", lang);
5913 if (!res)
5914 res = ast_waitstream(chan, ints);
5915 if (!res)
5916 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5917 return res;
5920 /* Brazilian Portuguese syntax */
5921 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5923 struct tm tm;
5924 int res = 0;
5926 ast_localtime(&t, &tm, NULL);
5927 res = ast_say_date(chan, t, ints, lang);
5928 if (!res)
5929 res = ast_say_time(chan, t, ints, lang);
5930 return res;
5933 /* Taiwanese / Chinese syntax */
5934 int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5936 struct tm tm;
5937 char fn[256];
5938 int res = 0;
5939 int hour, pm=0;
5941 ast_localtime(&t, &tm, NULL);
5942 if (!res)
5943 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5944 if (!res) {
5945 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5946 res = ast_streamfile(chan, fn, lang);
5947 if (!res)
5948 res = ast_waitstream(chan, ints);
5950 if (!res)
5951 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5952 if (!res) {
5953 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5954 res = ast_streamfile(chan, fn, lang);
5955 if (!res)
5956 res = ast_waitstream(chan, ints);
5959 hour = tm.tm_hour;
5960 if (!hour)
5961 hour = 12;
5962 else if (hour == 12)
5963 pm = 1;
5964 else if (hour > 12) {
5965 hour -= 12;
5966 pm = 1;
5968 if (pm) {
5969 if (!res)
5970 res = ast_streamfile(chan, "digits/p-m", lang);
5971 } else {
5972 if (!res)
5973 res = ast_streamfile(chan, "digits/a-m", lang);
5975 if (!res)
5976 res = ast_waitstream(chan, ints);
5977 if (!res)
5978 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5979 if (!res)
5980 res = ast_streamfile(chan, "digits/oclock", lang);
5981 if (!res)
5982 res = ast_waitstream(chan, ints);
5983 if (!res)
5984 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5985 if (!res)
5986 res = ast_streamfile(chan, "digits/minute", lang);
5987 if (!res)
5988 res = ast_waitstream(chan, ints);
5989 return res;
5992 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5994 if (!strcasecmp(lang, "en") ) { /* English syntax */
5995 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5996 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5997 return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
5998 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
5999 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
6000 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
6001 return(ast_say_datetime_from_now_ge(chan, t, ints, lang));
6004 /* Default to English */
6005 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
6008 /* English syntax */
6009 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6011 int res=0;
6012 time_t nowt;
6013 int daydiff;
6014 struct tm tm;
6015 struct tm now;
6016 char fn[256];
6018 time(&nowt);
6020 ast_localtime(&t, &tm, NULL);
6021 ast_localtime(&nowt,&now, NULL);
6022 daydiff = now.tm_yday - tm.tm_yday;
6023 if ((daydiff < 0) || (daydiff > 6)) {
6024 /* Day of month and month */
6025 if (!res) {
6026 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6027 res = ast_streamfile(chan, fn, lang);
6028 if (!res)
6029 res = ast_waitstream(chan, ints);
6031 if (!res)
6032 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6034 } else if (daydiff) {
6035 /* Just what day of the week */
6036 if (!res) {
6037 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6038 res = ast_streamfile(chan, fn, lang);
6039 if (!res)
6040 res = ast_waitstream(chan, ints);
6042 } /* Otherwise, it was today */
6043 if (!res)
6044 res = ast_say_time(chan, t, ints, lang);
6045 return res;
6048 /* French syntax */
6049 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6051 int res=0;
6052 time_t nowt;
6053 int daydiff;
6054 struct tm tm;
6055 struct tm now;
6056 char fn[256];
6058 time(&nowt);
6060 ast_localtime(&t, &tm, NULL);
6061 ast_localtime(&nowt, &now, NULL);
6062 daydiff = now.tm_yday - tm.tm_yday;
6063 if ((daydiff < 0) || (daydiff > 6)) {
6064 /* Day of month and month */
6065 if (!res) {
6066 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6067 res = ast_streamfile(chan, fn, lang);
6068 if (!res)
6069 res = ast_waitstream(chan, ints);
6071 if (!res)
6072 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6074 } else if (daydiff) {
6075 /* Just what day of the week */
6076 if (!res) {
6077 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6078 res = ast_streamfile(chan, fn, lang);
6079 if (!res)
6080 res = ast_waitstream(chan, ints);
6082 } /* Otherwise, it was today */
6083 if (!res)
6084 res = ast_say_time(chan, t, ints, lang);
6085 return res;
6088 /* Portuguese syntax */
6089 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6091 int res=0;
6092 time_t nowt;
6093 int daydiff;
6094 struct tm tm;
6095 struct tm now;
6096 char fn[256];
6098 time(&nowt);
6100 ast_localtime(&t, &tm, NULL);
6101 ast_localtime(&nowt, &now, NULL);
6102 daydiff = now.tm_yday - tm.tm_yday;
6103 if ((daydiff < 0) || (daydiff > 6)) {
6104 /* Day of month and month */
6105 if (!res)
6106 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6107 if (!res)
6108 res = wait_file(chan, ints, "digits/pt-de", lang);
6109 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6110 if (!res)
6111 res = wait_file(chan, ints, fn, lang);
6113 } else if (daydiff) {
6114 /* Just what day of the week */
6115 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6116 if (!res)
6117 res = wait_file(chan, ints, fn, lang);
6118 } /* Otherwise, it was today */
6119 if (!strcasecmp(lang, "pt_BR")) {
6120 if (tm.tm_hour > 1) {
6121 snprintf(fn, sizeof(fn), "digits/pt-as");
6122 } else {
6123 snprintf(fn, sizeof(fn), "digits/pt-a");
6125 if (!res)
6126 res = wait_file(chan, ints, fn, lang);
6127 } else {
6128 snprintf(fn, sizeof(fn), "digits/pt-ah");
6129 if (!res)
6130 res = wait_file(chan, ints, fn, lang);
6131 if (tm.tm_hour != 1)
6132 if (!res)
6133 res = wait_file(chan, ints, "digits/pt-sss", lang);
6134 if (!res)
6135 res = ast_say_time(chan, t, ints, lang);
6137 return res;
6141 /*********************************** GREEK SUPPORT ***************************************/
6145 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
6147 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
6148 int tmp;
6149 int left;
6150 int res;
6151 char fn[256] = "";
6153 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
6154 if (num < 5) {
6155 snprintf(fn, sizeof(fn), "digits/female-%d", num);
6156 res = wait_file(chan, ints, fn, lang);
6157 } else if (num < 13) {
6158 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
6159 } else if (num <100 ) {
6160 tmp = (num/10) * 10;
6161 left = num - tmp;
6162 snprintf(fn, sizeof(fn), "digits/%d", tmp);
6163 res = ast_streamfile(chan, fn, lang);
6164 if (!res)
6165 res = ast_waitstream(chan, ints);
6166 if (left)
6167 gr_say_number_female(left, chan, ints, lang);
6169 } else {
6170 return -1;
6172 return res;
6178 * A list of the files that you need to create
6179 -> digits/xilia = "xilia"
6180 -> digits/myrio = "ekatomyrio"
6181 -> digits/thousands = "xiliades"
6182 -> digits/millions = "ektatomyria"
6183 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6184 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6185 e,g 80 = "ogdonta"
6186 Here we must note that we use digits/tens/100 to utter "ekato"
6187 and digits/hundred-100 to utter "ekaton"
6188 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6189 "terakosia". Here again we use hundreds/1000 for "xilia"
6190 and digits/thousnds for "xiliades"
6193 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
6195 int res = 0;
6196 char fn[256] = "";
6197 int i=0;
6200 if (!num) {
6201 snprintf(fn, sizeof(fn), "digits/0");
6202 res = ast_streamfile(chan, fn, chan->language);
6203 if (!res)
6204 return ast_waitstream(chan, ints);
6207 while (!res && num ) {
6208 i++;
6209 if (num < 13) {
6210 snprintf(fn, sizeof(fn), "digits/%d", num);
6211 num = 0;
6212 } else if (num <= 100) {
6213 /* 13 < num <= 100 */
6214 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
6215 num -= ((num / 10) * 10);
6216 } else if (num < 200) {
6217 /* 100 < num < 200 */
6218 snprintf(fn, sizeof(fn), "digits/hundred-100");
6219 num -= ((num / 100) * 100);
6220 } else if (num < 1000) {
6221 /* 200 < num < 1000 */
6222 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
6223 num -= ((num / 100) * 100);
6224 } else if (num < 2000){
6225 snprintf(fn, sizeof(fn), "digits/xilia");
6226 num -= ((num / 1000) * 1000);
6227 } else {
6228 /* num > 1000 */
6229 if (num < 1000000) {
6230 res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
6231 if (res)
6232 return res;
6233 num = num % 1000;
6234 snprintf(fn, sizeof(fn), "digits/thousands");
6235 } else {
6236 if (num < 1000000000) { /* 1,000,000,000 */
6237 res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
6238 if (res)
6239 return res;
6240 num = num % 1000000;
6241 snprintf(fn, sizeof(fn), "digits/millions");
6242 } else {
6243 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
6244 res = -1;
6248 if (!res) {
6249 if (!ast_streamfile(chan, fn, language)) {
6250 if ((audiofd > -1) && (ctrlfd > -1))
6251 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6252 else
6253 res = ast_waitstream(chan, ints);
6255 ast_stopstream(chan);
6258 return res;
6263 * The format is weekday - day - month -year
6265 * A list of the files that you need to create
6266 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6267 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6268 Attention the months are in
6269 "gekinh klhsh"
6273 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6275 struct tm tm;
6277 char fn[256];
6278 int res = 0;
6281 ast_localtime(&t,&tm,NULL);
6282 /* W E E K - D A Y */
6283 if (!res) {
6284 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6285 res = ast_streamfile(chan, fn, lang);
6286 if (!res)
6287 res = ast_waitstream(chan, ints);
6289 /* D A Y */
6290 if (!res) {
6291 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6293 /* M O N T H */
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 /* Y E A R */
6301 if (!res)
6302 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6303 return res;
6308 /* A list of the files that you need to create
6309 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6310 * digits/kai : "KAI"
6311 * didgits : "h wra"
6312 * digits/p-m : "meta meshmbrias"
6313 * digits/a-m : "pro meshmbrias"
6316 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6319 struct tm tm;
6320 int res = 0;
6321 int hour, pm=0;
6323 ast_localtime(&t, &tm, NULL);
6324 hour = tm.tm_hour;
6326 if (!hour)
6327 hour = 12;
6328 else if (hour == 12)
6329 pm = 1;
6330 else if (hour > 12) {
6331 hour -= 12;
6332 pm = 1;
6335 res = gr_say_number_female(hour, chan, ints, lang);
6336 if (tm.tm_min) {
6337 if (!res)
6338 res = ast_streamfile(chan, "digits/kai", lang);
6339 if (!res)
6340 res = ast_waitstream(chan, ints);
6341 if (!res)
6342 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6343 } else {
6344 if (!res)
6345 res = ast_streamfile(chan, "digits/hwra", lang);
6346 if (!res)
6347 res = ast_waitstream(chan, ints);
6349 if (pm) {
6350 if (!res)
6351 res = ast_streamfile(chan, "digits/p-m", lang);
6352 } else {
6353 if (!res)
6354 res = ast_streamfile(chan, "digits/a-m", lang);
6356 if (!res)
6357 res = ast_waitstream(chan, ints);
6358 return res;
6363 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6365 struct tm tm;
6366 char fn[256];
6367 int res = 0;
6369 ast_localtime(&t, &tm, NULL);
6372 /* W E E K - D A Y */
6373 if (!res) {
6374 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6375 res = ast_streamfile(chan, fn, lang);
6376 if (!res)
6377 res = ast_waitstream(chan, ints);
6379 /* D A Y */
6380 if (!res) {
6381 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6383 /* M O N T H */
6384 if (!res) {
6385 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6386 res = ast_streamfile(chan, fn, lang);
6387 if (!res)
6388 res = ast_waitstream(chan, ints);
6391 res = ast_say_time_gr(chan, t, ints, lang);
6392 return res;
6395 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)
6398 struct tm tm;
6399 int res=0, offset, sndoffset;
6400 char sndfile[256], nextmsg[256];
6402 if (!format)
6403 format = "AdBY 'digits/at' IMp";
6405 ast_localtime(&time,&tm,timezone);
6407 for (offset=0 ; format[offset] != '\0' ; offset++) {
6408 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6409 switch (format[offset]) {
6410 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6411 case '\'':
6412 /* Literal name of a sound file */
6413 sndoffset=0;
6414 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
6415 sndfile[sndoffset] = format[offset];
6416 sndfile[sndoffset] = '\0';
6417 res = wait_file(chan,ints,sndfile,lang);
6418 break;
6419 case 'A':
6420 case 'a':
6421 /* Sunday - Saturday */
6422 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6423 res = wait_file(chan,ints,nextmsg,lang);
6424 break;
6425 case 'B':
6426 case 'b':
6427 case 'h':
6428 /* January - December */
6429 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6430 res = wait_file(chan,ints,nextmsg,lang);
6431 break;
6432 case 'd':
6433 case 'e':
6434 /* first - thirtyfirst */
6435 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6436 break;
6437 case 'Y':
6438 /* Year */
6440 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
6441 break;
6442 case 'I':
6443 case 'l':
6444 /* 12-Hour */
6445 if (tm.tm_hour == 0)
6446 gr_say_number_female(12, chan, ints, lang);
6447 else if (tm.tm_hour > 12)
6448 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
6449 else
6450 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6451 break;
6452 case 'H':
6453 case 'k':
6454 /* 24-Hour */
6455 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6456 break;
6457 case 'M':
6458 /* Minute */
6459 if (tm.tm_min) {
6460 if (!res)
6461 res = ast_streamfile(chan, "digits/kai", lang);
6462 if (!res)
6463 res = ast_waitstream(chan, ints);
6464 if (!res)
6465 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
6466 } else {
6467 if (!res)
6468 res = ast_streamfile(chan, "digits/oclock", lang);
6469 if (!res)
6470 res = ast_waitstream(chan, ints);
6472 break;
6473 case 'P':
6474 case 'p':
6475 /* AM/PM */
6476 if (tm.tm_hour > 11)
6477 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
6478 else
6479 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
6480 res = wait_file(chan,ints,nextmsg,lang);
6481 break;
6482 case 'Q':
6483 /* Shorthand for "Today", "Yesterday", or ABdY */
6484 /* XXX As emphasized elsewhere, this should the native way in your
6485 * language to say the date, with changes in what you say, depending
6486 * upon how recent the date is. XXX */
6488 struct timeval now;
6489 struct tm tmnow;
6490 time_t beg_today, tt;
6492 gettimeofday(&now,NULL);
6493 tt = now.tv_sec;
6494 ast_localtime(&tt,&tmnow,timezone);
6495 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6496 /* In any case, it saves not having to do ast_mktime() */
6497 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6498 if (beg_today < time) {
6499 /* Today */
6500 res = wait_file(chan,ints, "digits/today",lang);
6501 } else if (beg_today - 86400 < time) {
6502 /* Yesterday */
6503 res = wait_file(chan,ints, "digits/yesterday",lang);
6504 } else {
6505 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6508 break;
6509 case 'q':
6510 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6511 /* XXX As emphasized elsewhere, this should the native way in your
6512 * language to say the date, with changes in what you say, depending
6513 * upon how recent the date is. XXX */
6515 struct timeval now;
6516 struct tm tmnow;
6517 time_t beg_today, tt;
6519 gettimeofday(&now,NULL);
6520 tt = now.tv_sec;
6521 ast_localtime(&tt,&tmnow,timezone);
6522 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6523 /* In any case, it saves not having to do ast_mktime() */
6524 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6525 if (beg_today < time) {
6526 /* Today */
6527 } else if ((beg_today - 86400) < time) {
6528 /* Yesterday */
6529 res = wait_file(chan,ints, "digits/yesterday",lang);
6530 } else if (beg_today - 86400 * 6 < time) {
6531 /* Within the last week */
6532 res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
6533 } else {
6534 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6537 break;
6538 case 'R':
6539 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
6540 break;
6541 case 'S':
6542 /* Seconds */
6543 snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
6544 res = wait_file(chan,ints,nextmsg,lang);
6545 if (!res)
6546 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
6547 if (!res)
6548 snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
6549 res = wait_file(chan,ints,nextmsg,lang);
6550 break;
6551 case 'T':
6552 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
6553 break;
6554 case ' ':
6555 case ' ':
6556 /* Just ignore spaces and tabs */
6557 break;
6558 default:
6559 /* Unknown character */
6560 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6562 /* Jump out on DTMF */
6563 if (res) {
6564 break;
6567 return res;
6573 /*********************************** Georgian Support ***************************************/
6577 Convert a number into a semi-localized string. Only for Georgian.
6578 res must be of at least 256 bytes, preallocated.
6579 The output corresponds to Georgian spoken numbers, so
6580 it may be either converted to real words by applying a direct conversion
6581 table, or played just by substituting the entities with played files.
6583 Output may consist of the following tokens (separated by spaces):
6584 0, minus.
6585 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
6586 10-19.
6587 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
6588 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
6589 1000, 1000_. (atasi, atas).
6590 1000000, 1000000_. (milioni, milion).
6591 1000000000, 1000000000_. (miliardi, miliard).
6593 To be able to play the sounds, each of the above tokens needs
6594 a corresponding sound file. (e.g. 200_.gsm).
6596 static char* ast_translate_number_ge(int num, char* res, int res_len)
6598 char buf[256];
6599 int digit = 0;
6600 int remainder = 0;
6603 if (num < 0) {
6604 strncat(res, "minus ", res_len - strlen(res) - 1);
6605 if ( num > INT_MIN ) {
6606 num = -num;
6607 } else {
6608 num = 0;
6613 /* directly read the numbers */
6614 if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
6615 snprintf(buf, sizeof(buf), "%d", num);
6616 strncat(res, buf, res_len - strlen(res) - 1);
6617 return res;
6621 if (num < 40) { /* ocda... */
6622 strncat(res, "20_ ", res_len - strlen(res) - 1);
6623 return ast_translate_number_ge(num - 20, res, res_len);
6626 if (num < 60) { /* ormocda... */
6627 strncat(res, "40_ ", res_len - strlen(res) - 1);
6628 return ast_translate_number_ge(num - 40, res, res_len);
6631 if (num < 80) { /* samocda... */
6632 strncat(res, "60_ ", res_len - strlen(res) - 1);
6633 return ast_translate_number_ge(num - 60, res, res_len);
6636 if (num < 100) { /* otxmocda... */
6637 strncat(res, "80_ ", res_len - strlen(res) - 1);
6638 return ast_translate_number_ge(num - 80, res, res_len);
6642 if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
6643 remainder = num % 100;
6644 digit = (num - remainder) / 100;
6646 if (remainder == 0) {
6647 snprintf(buf, sizeof(buf), "%d", num);
6648 strncat(res, buf, res_len - strlen(res) - 1);
6649 return res;
6650 } else {
6651 snprintf(buf, sizeof(buf), "%d_ ", digit*100);
6652 strncat(res, buf, res_len - strlen(res) - 1);
6653 return ast_translate_number_ge(remainder, res, res_len);
6658 if (num == 1000) {
6659 strncat(res, "1000", res_len - strlen(res) - 1);
6660 return res;
6664 if (num < 1000000) {
6665 remainder = num % 1000;
6666 digit = (num - remainder) / 1000;
6668 if (remainder == 0) {
6669 ast_translate_number_ge(digit, res, res_len);
6670 strncat(res, " 1000", res_len - strlen(res) - 1);
6671 return res;
6674 if (digit == 1) {
6675 strncat(res, "1000_ ", res_len - strlen(res) - 1);
6676 return ast_translate_number_ge(remainder, res, res_len);
6679 ast_translate_number_ge(digit, res, res_len);
6680 strncat(res, " 1000_ ", res_len - strlen(res) - 1);
6681 return ast_translate_number_ge(remainder, res, res_len);
6686 if (num == 1000000) {
6687 strncat(res, "1 1000000", res_len - strlen(res) - 1);
6688 return res;
6692 if (num < 1000000000) {
6693 remainder = num % 1000000;
6694 digit = (num - remainder) / 1000000;
6696 if (remainder == 0) {
6697 ast_translate_number_ge(digit, res, res_len);
6698 strncat(res, " 1000000", res_len - strlen(res) - 1);
6699 return res;
6702 ast_translate_number_ge(digit, res, res_len);
6703 strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
6704 return ast_translate_number_ge(remainder, res, res_len);
6709 if (num == 1000000000) {
6710 strncat(res, "1 1000000000", res_len - strlen(res) - 1);
6711 return res;
6715 if (num > 1000000000) {
6716 remainder = num % 1000000000;
6717 digit = (num - remainder) / 1000000000;
6719 if (remainder == 0) {
6720 ast_translate_number_ge(digit, res, res_len);
6721 strncat(res, " 1000000000", res_len - strlen(res) - 1);
6722 return res;
6725 ast_translate_number_ge(digit, res, res_len);
6726 strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
6727 return ast_translate_number_ge(remainder, res, res_len);
6731 return res;
6737 /*! \brief ast_say_number_full_ge: Georgian syntax */
6738 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)
6740 int res = 0;
6741 char fn[512] = "";
6742 char* s = 0;
6743 const char* remainder = fn;
6745 if (!num)
6746 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
6749 ast_translate_number_ge(num, fn, 512);
6753 while (res == 0 && (s = strstr(remainder, " "))) {
6754 size_t len = s - remainder;
6755 char* new_string = malloc(len + 1 + strlen("digits/"));
6757 sprintf(new_string, "digits/");
6758 strncat(new_string, remainder, len); /* we can't sprintf() it, it's not null-terminated. */
6759 /* new_string[len + strlen("digits/")] = '\0'; */
6761 if (!ast_streamfile(chan, new_string, language)) {
6762 if ((audiofd > -1) && (ctrlfd > -1))
6763 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6764 else
6765 res = ast_waitstream(chan, ints);
6767 ast_stopstream(chan);
6769 free(new_string);
6771 remainder = s + 1; /* position just after the found space char. */
6772 while (*remainder == ' ') /* skip multiple spaces */
6773 remainder++;
6777 /* the last chunk. */
6778 if (res == 0 && *remainder) {
6780 char* new_string = malloc(strlen(remainder) + 1 + strlen("digits/"));
6781 sprintf(new_string, "digits/%s", remainder);
6783 if (!ast_streamfile(chan, new_string, language)) {
6784 if ((audiofd > -1) && (ctrlfd > -1))
6785 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6786 else
6787 res = ast_waitstream(chan, ints);
6789 ast_stopstream(chan);
6791 free(new_string);
6796 return res;
6803 Georgian support for date/time requires the following files (*.gsm):
6805 mon-1, mon-2, ... (ianvari, tebervali, ...)
6806 day-1, day-2, ... (orshabati, samshabati, ...)
6807 saati_da
6808 tsuti
6809 tslis
6814 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
6815 static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6817 struct tm tm;
6818 char fn[256];
6819 int res = 0;
6820 ast_localtime(&t,&tm,NULL);
6822 if (!res)
6823 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6825 if (!res) {
6826 snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
6827 res = ast_streamfile(chan, fn, lang);
6828 if (!res)
6829 res = ast_waitstream(chan, ints);
6832 if (!res) {
6833 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
6834 /* if (!res)
6835 res = ast_waitstream(chan, ints);
6839 if (!res) {
6840 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6841 res = ast_streamfile(chan, fn, lang);
6842 if (!res)
6843 res = ast_waitstream(chan, ints);
6845 return res;
6853 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
6854 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6856 struct tm tm;
6857 int res = 0;
6859 ast_localtime(&t, &tm, NULL);
6861 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
6862 if (!res) {
6863 res = ast_streamfile(chan, "digits/saati_da", lang);
6864 if (!res)
6865 res = ast_waitstream(chan, ints);
6868 if (tm.tm_min) {
6869 if (!res) {
6870 res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
6872 if (!res) {
6873 res = ast_streamfile(chan, "digits/tsuti", lang);
6874 if (!res)
6875 res = ast_waitstream(chan, ints);
6879 return res;
6884 /* Georgian syntax. Say date, then say time. */
6885 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6887 struct tm tm;
6888 int res = 0;
6890 ast_localtime(&t, &tm, NULL);
6891 res = ast_say_date(chan, t, ints, lang);
6892 if (!res)
6893 ast_say_time(chan, t, ints, lang);
6894 return res;
6901 /* Georgian syntax */
6902 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6904 int res=0;
6905 time_t nowt;
6906 int daydiff;
6907 struct tm tm;
6908 struct tm now;
6909 char fn[256];
6911 time(&nowt);
6913 ast_localtime(&t, &tm, NULL);
6914 ast_localtime(&nowt, &now, NULL);
6915 daydiff = now.tm_yday - tm.tm_yday;
6916 if ((daydiff < 0) || (daydiff > 6)) {
6917 /* Day of month and month */
6918 if (!res)
6919 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6920 if (!res) {
6921 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6922 res = ast_streamfile(chan, fn, lang);
6923 if (!res)
6924 res = ast_waitstream(chan, ints);
6927 } else if (daydiff) {
6928 /* Just what day of the week */
6929 if (!res) {
6930 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6931 res = ast_streamfile(chan, fn, lang);
6932 if (!res)
6933 res = ast_waitstream(chan, ints);
6935 } /* Otherwise, it was today */
6936 if (!res)
6937 res = ast_say_time(chan, t, ints, lang);
6939 return res;
6945 * remap the 'say' functions to use those in this file
6947 static void __attribute__((constructor)) __say_init(void)
6949 ast_say_number_full = say_number_full;
6950 ast_say_enumeration_full = say_enumeration_full;
6951 ast_say_digit_str_full = say_digit_str_full;
6952 ast_say_character_str_full = say_character_str_full;
6953 ast_say_phonetic_str_full = say_phonetic_str_full;
6954 ast_say_datetime = say_datetime;
6955 ast_say_time = say_time;
6956 ast_say_date = say_date;
6957 ast_say_datetime_from_now = say_datetime_from_now;
6958 ast_say_date_with_format = say_date_with_format;