Asterisk - The Open Source Telephony Project  18.5.0
say.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <[email protected]>
7  * George Konstantoulakis <[email protected]>
8  *
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.
14  *
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.
18  */
19 
20 /*! \file
21  *
22  * \brief Say numbers and dates (maybe words one day too)
23  *
24  * \author Mark Spencer <[email protected]>
25  *
26  * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <[email protected]>
27  *
28  * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <[email protected]>,
29  * Next Generation Networks (NGN).
30  * \note 2007-03-20 : Support for Thai added by Dome C. <[email protected]>,
31  * IP Crossing Co., Ltd.
32  * \note 2021-07-26 : Refactoring to separate string buildup and playback
33  * by Naveen Albert <[email protected]>
34  */
35 
36 /*** MODULEINFO
37  <support_level>core</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 #include <netinet/in.h>
43 #include <time.h>
44 #include <ctype.h>
45 #include <math.h>
46 
47 #ifdef SOLARIS
48 #include <iso/limits_iso.h>
49 #endif
50 
51 #include "asterisk/file.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/say.h"
54 #include "asterisk/lock.h"
55 #include "asterisk/localtime.h"
56 #include "asterisk/utils.h"
57 #include "asterisk/app.h"
58 #include "asterisk/test.h"
59 
60 /* Forward declaration */
61 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
62 
63 struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {
64  const char *fn;
65  char fnbuf[10], asciibuf[20] = "letters/ascii";
66  char ltr;
67  int num = 0;
68  int res = 0;
69  int upper = 0;
70  int lower = 0;
71 
72  struct ast_str *filenames = ast_str_create(20);
73  ast_str_reset(filenames);
74 
75  while (str[num] && !res) {
76  fn = NULL;
77  switch (str[num]) {
78  case ('*'):
79  fn = "digits/star";
80  break;
81  case ('#'):
82  fn = "digits/pound";
83  break;
84  case ('!'):
85  fn = "letters/exclaimation-point";
86  break;
87  case ('@'):
88  fn = "letters/at";
89  break;
90  case ('$'):
91  fn = "letters/dollar";
92  break;
93  case ('-'):
94  fn = "letters/dash";
95  break;
96  case ('.'):
97  fn = "letters/dot";
98  break;
99  case ('='):
100  fn = "letters/equals";
101  break;
102  case ('+'):
103  fn = "letters/plus";
104  break;
105  case ('/'):
106  fn = "letters/slash";
107  break;
108  case (' '):
109  fn = "letters/space";
110  break;
111  case ('0'):
112  case ('1'):
113  case ('2'):
114  case ('3'):
115  case ('4'):
116  case ('5'):
117  case ('6'):
118  case ('7'):
119  case ('8'):
120  case ('9'):
121  strcpy(fnbuf, "digits/X");
122  fnbuf[7] = str[num];
123  fn = fnbuf;
124  break;
125  default:
126  ltr = str[num];
127  if ('A' <= ltr && ltr <= 'Z') {
128  ltr += 'a' - 'A'; /* file names are all lower-case */
129  switch (sensitivity) {
130  case AST_SAY_CASE_UPPER:
131  case AST_SAY_CASE_ALL:
132  upper = !upper;
133  case AST_SAY_CASE_LOWER:
134  case AST_SAY_CASE_NONE:
135  break;
136  }
137  } else if ('a' <= ltr && ltr <= 'z') {
138  switch (sensitivity) {
139  case AST_SAY_CASE_LOWER:
140  case AST_SAY_CASE_ALL:
141  lower = !lower;
142  case AST_SAY_CASE_UPPER:
143  case AST_SAY_CASE_NONE:
144  break;
145  }
146  }
147 
148  if (upper) {
149  strcpy(fnbuf, "uppercase");
150  } else if (lower) {
151  strcpy(fnbuf, "lowercase");
152  } else {
153  strcpy(fnbuf, "letters/X");
154  fnbuf[8] = ltr;
155  }
156  fn = fnbuf;
157  }
158  if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
159  (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
160  ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
161  }
162  if (upper || lower) {
163  continue;
164  }
165  num++;
166  }
167 
168  return filenames;
169 }
170 
171 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)
172 {
173  const char *fn;
174  int res = 0;
175 
176  struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);
177  char *files = ast_str_buffer(filenames);
178 
179  while ((fn = strsep(&files, "&"))) {
180  res = ast_streamfile(chan, fn, lang);
181  if (!res) {
182  if ((audiofd > -1) && (ctrlfd > -1))
183  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
184  else
185  res = ast_waitstream(chan, ints);
186  }
187  ast_stopstream(chan);
188  }
189 
190  ast_free(filenames);
191 
192  return res;
193 }
194 
195 struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)
196 {
197  const char *fn;
198  char fnbuf[256];
199  char ltr;
200  int num = 0;
201 
202  struct ast_str *filenames = ast_str_create(20);
203  ast_str_reset(filenames);
204 
205  while (str[num]) {
206  fn = NULL;
207  switch (str[num]) {
208  case ('*'):
209  fn = "digits/star";
210  break;
211  case ('#'):
212  fn = "digits/pound";
213  break;
214  case ('!'):
215  fn = "letters/exclaimation-point";
216  break;
217  case ('@'):
218  fn = "letters/at";
219  break;
220  case ('$'):
221  fn = "letters/dollar";
222  break;
223  case ('-'):
224  fn = "letters/dash";
225  break;
226  case ('.'):
227  fn = "letters/dot";
228  break;
229  case ('='):
230  fn = "letters/equals";
231  break;
232  case ('+'):
233  fn = "letters/plus";
234  break;
235  case ('/'):
236  fn = "letters/slash";
237  break;
238  case (' '):
239  fn = "letters/space";
240  break;
241  case ('0'):
242  case ('1'):
243  case ('2'):
244  case ('3'):
245  case ('4'):
246  case ('5'):
247  case ('6'):
248  case ('7'):
249  case ('8'):
250  strcpy(fnbuf, "digits/X");
251  fnbuf[7] = str[num];
252  fn = fnbuf;
253  break;
254  default: /* '9' falls here... */
255  ltr = str[num];
256  if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
257  strcpy(fnbuf, "phonetic/X_p");
258  fnbuf[9] = ltr;
259  fn = fnbuf;
260  }
261  if (fn && ast_fileexists(fn, NULL, lang) > 0) {
262  ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
263  }
264  num++;
265  }
266 
267  return filenames;
268 }
269 
270 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
271 {
272  const char *fn;
273  int res = 0;
274 
275  struct ast_str *filenames = ast_get_phonetic_str(str, lang);
276  char *files = ast_str_buffer(filenames);
277 
278  while ((fn = strsep(&files, "&"))) {
279  res = ast_streamfile(chan, fn, lang);
280  if (!res) {
281  if ((audiofd > -1) && (ctrlfd > -1))
282  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
283  else
284  res = ast_waitstream(chan, ints);
285  }
286  ast_stopstream(chan);
287  }
288 
289  ast_free(filenames);
290 
291  return res;
292 }
293 
294 struct ast_str* ast_get_digit_str(const char *str, const char *lang)
295 {
296  const char *fn;
297  char fnbuf[256];
298  int num = 0;
299 
300  struct ast_str *filenames = ast_str_create(20);
301  ast_str_reset(filenames);
302 
303  while (str[num]) {
304  fn = NULL;
305  switch (str[num]) {
306  case ('*'):
307  fn = "digits/star";
308  break;
309  case ('#'):
310  fn = "digits/pound";
311  break;
312  case ('-'):
313  fn = "digits/minus";
314  break;
315  case '0':
316  case '1':
317  case '2':
318  case '3':
319  case '4':
320  case '5':
321  case '6':
322  case '7':
323  case '8':
324  case '9':
325  strcpy(fnbuf, "digits/X");
326  fnbuf[7] = str[num];
327  fn = fnbuf;
328  break;
329  }
330  if (fn && ast_fileexists(fn, NULL, lang) > 0) {
331  ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
332  }
333  num++;
334  }
335 
336  return filenames;
337 }
338 
339 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
340 {
341  const char *fn;
342  int res = 0;
343 
344  struct ast_str *filenames = ast_get_digit_str(str, lang);
345  char *files = ast_str_buffer(filenames);
346 
347  while ((fn = strsep(&files, "&"))) {
348  res = ast_streamfile(chan, fn, lang);
349  if (!res) {
350  if ((audiofd > -1) && (ctrlfd > -1))
351  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
352  else
353  res = ast_waitstream(chan, ints);
354  }
355  ast_stopstream(chan);
356  }
357 
358  ast_free(filenames);
359 
360  return res;
361 }
362 
363 static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)
364 {
365  const char *fnr;
366 
367  double dollars = 0;
368  int amt, cents;
369  struct ast_str *fnrecurse;
370 
371  struct ast_str *filenames = ast_str_create(20);
372  ast_str_reset(filenames);
373 
374  if (sscanf(str, "%30lf", &dollars) != 1) {
375  amt = 0;
376  } else { /* convert everything to cents */
377  amt = dollars * 100;
378  }
379 
380  /* Just the cents after the dollar decimal point */
381  cents = amt - (((int) dollars) * 100);
382  ast_debug(1, "Cents is %d, amount is %d\n", cents, amt);
383 
384  if (amt >= 100) {
385  fnrecurse = ast_get_number_str((amt / 100), lang);
386  fnr = ast_str_buffer(fnrecurse);
387  ast_str_append(&filenames, 0, "%s", fnr);
388 
389  /* If this is it, end on a down pitch, otherwise up pitch */
390  if (amt < 200) {
391  ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");
392  } else {
393  ast_str_append(&filenames, 0, "&%s", "dollars");
394  }
395 
396  /* If dollars and cents, add "and" in the middle */
397  if (cents > 0) {
398  ast_str_append(&filenames, 0, "&%s", "and");
399  }
400  }
401 
402  if (cents > 0) {
403  ast_debug(1, "Entered cents block\n");
404  fnrecurse = ast_get_number_str(cents, lang);
405  fnr = ast_str_buffer(fnrecurse);
406  ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);
407  ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");
408  } else if (amt == 0) {
409  fnrecurse = ast_get_digit_str("0", lang);
410  fnr = ast_str_buffer(fnrecurse);
411  ast_str_append(&filenames, 0, "%s", fnr);
412  ast_str_append(&filenames, 0, "&%s", "cents");
413  }
414 
415  return filenames;
416 }
417 
418 /*! \brief ast_get_money_str: call language-specific functions */
419 struct ast_str* ast_get_money_str(const char *str, const char *lang)
420 {
421  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
422  return ast_get_money_en_dollars_str(str, lang);
423  }
424 
425  ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);
426  /* Default to english */
427  return ast_get_money_en_dollars_str(str, lang);
428 }
429 
430 static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
431 {
432  const char *fn;
433  int res = 0;
434 
435  struct ast_str *filenames = ast_get_money_str(str, lang);
436  char *files = ast_str_buffer(filenames);
437 
438  while ((fn = strsep(&files, "&"))) {
439  res = ast_streamfile(chan, fn, lang);
440  if (!res) {
441  if ((audiofd > -1) && (ctrlfd > -1))
442  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
443  else
444  res = ast_waitstream(chan, ints);
445  }
446  ast_stopstream(chan);
447  }
448 
449  ast_free(filenames);
450 
451  return res;
452 }
453 
454 static struct ast_str* get_number_str_en(int num, const char *lang)
455 {
456  const char *fnr;
457  int loops = 0;
458 
459  int res = 0;
460  int playh = 0;
461  char fn[256] = "";
462 
463  struct ast_str *filenames;
464 
465  if (!num)
466  return ast_get_digit_str("0", lang);
467 
468  filenames = ast_str_create(20);
469  ast_str_reset(filenames);
470 
471  while (!res && (num || playh)) {
472  if (num < 0) {
473  ast_copy_string(fn, "digits/minus", sizeof(fn));
474  if ( num > INT_MIN ) {
475  num = -num;
476  } else {
477  num = 0;
478  }
479  } else if (playh) {
480  ast_copy_string(fn, "digits/hundred", sizeof(fn));
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 %= 10;
488  } else {
489  if (num < 1000){
490  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
491  playh++;
492  num %= 100;
493  } else {
494  struct ast_str *fnrecurse;
495  if (num < 1000000) { /* 1,000,000 */
496  fnrecurse = ast_get_number_str((num / 1000), lang);
497  fnr = ast_str_buffer(fnrecurse);
498  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
499  num %= 1000;
500  snprintf(fn, sizeof(fn), "&digits/thousand");
501  } else {
502  if (num < 1000000000) { /* 1,000,000,000 */
503  fnrecurse = ast_get_number_str((num / 1000000), lang);
504  fnr = ast_str_buffer(fnrecurse);
505  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
506  num %= 1000000;
507  ast_copy_string(fn, "&digits/million", sizeof(fn));
508  } else {
509  ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
510  res = -1;
511  }
512  }
513  }
514  }
515  if (!res) {
516  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
517  loops++;
518  }
519  }
520 
521  return filenames;
522 }
523 
524 /*! \brief ast_get_number_str: call language-specific functions */
525 struct ast_str* ast_get_number_str(int num, const char *lang)
526 {
527  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
528  return get_number_str_en(num, lang);
529  }
530 
531  ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
532  /* Default to english */
533  return get_number_str_en(num, lang);
534 }
535 
536 /* Forward declarations */
537 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
538  \note Not really language codes.
539  For these language codes, Asterisk will change the syntax when
540  saying numbers (and in some cases dates and voicemail messages
541  as well)
542  \arg \b da - Danish
543  \arg \b de - German
544  \arg \b en - English (US)
545  \arg \b en_GB - English (British)
546  \arg \b es - Spanish, Mexican
547  \arg \b fr - French
548  \arg \b he - Hebrew
549  \arg \b is - Icelandic
550  \arg \b it - Italian
551  \arg \b nl - Dutch
552  \arg \b no - Norwegian
553  \arg \b pl - Polish
554  \arg \b pt - Portuguese
555  \arg \b pt_BR - Portuguese (Brazil)
556  \arg \b se - Swedish
557  \arg \b zh - Taiwanese / Chinese
558  \arg \b ru - Russian
559  \arg \b ka - Georgian
560  \arg \b hu - Hungarian
561 
562  \par Gender:
563  For some languages the numbers differ for gender of the countable object.
564  Commonly for "one", like "un"/"une" in French. Note that the interface
565  is somewhat peculiar, as differing languages can have conflicting
566  genders.
567  \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
568  \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
569 
570  Date/Time functions currently have less languages supported than saynumber().
571 
572  \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
573 
574  See contrib/i18n.testsuite.conf for some examples of the different syntaxes
575 
576  \par Portuguese
577  Portuguese sound files needed for Time/Date functions:
578  pt-ah
579  pt-ao
580  pt-de
581  pt-e
582  pt-ora
583  pt-meianoite
584  pt-meiodia
585  pt-sss
586 
587  \par Spanish
588  Spanish sound files needed for Time/Date functions:
589  es-de
590  es-el
591 
592  \par Italian
593  Italian sound files needed for Time/Date functions:
594  ore-una
595  ore-mezzanotte
596 
597 */
598 
599 /* Forward declarations of language specific variants of ast_say_number_full */
600 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
601 static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
602 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);
603 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);
604 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
605 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);
606 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);
607 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);
608 static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
609 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
610 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
611 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);
612 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);
613 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);
614 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);
615 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
616 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
617 static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
618 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);
619 static int ast_say_number_full_ka(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
620 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
621 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
622 static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
623 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
624 
625 /* Forward declarations of language specific variants of ast_say_enumeration_full */
626 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
627 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);
628 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);
629 static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
630 static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
631 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
632 
633 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
634 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
635 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
636 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
637 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
638 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
639 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
640 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
641 static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
642 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
643 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
644 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
645 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
646 static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
647 
648 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
649 static int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
650 static int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
651 static int ast_say_date_with_format_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
652 static int ast_say_date_with_format_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
653 static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
654 static int ast_say_date_with_format_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
655 static int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
656 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
657 static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
658 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
659 static int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
660 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
661 static int ast_say_date_with_format_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
662 static int ast_say_date_with_format_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
663 static int ast_say_date_with_format_vi(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
664 
665 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
666 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
667 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
668 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
669 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
670 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
671 static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
672 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
673 static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
674 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
675 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
676 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
677 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
678 
679 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
680 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
681 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
682 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
683 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
684 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
685 static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
686 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
687 static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
688 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
689 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
690 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
691 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
692 
693 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
694 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
695 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
696 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
697 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
698 
699 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
700 {
701  int res;
702  if ((res = ast_streamfile(chan, file, lang))) {
703  ast_log(LOG_WARNING, "Unable to play message %s\n", file);
704  }
705  if (!res) {
706  res = ast_waitstream(chan, ints);
707  }
708  return res;
709 }
710 
711 /*! \brief ast_say_number_full: call language-specific functions
712  \note Called from AGI */
713 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
714 {
715  ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
716  if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
717  return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
718  } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
719  return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
720  } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
721  return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
722  } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
723  return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
724  } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
725  return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
726  } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
727  return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
728  } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
729  return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
730  } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
731  return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
732  } else if (!strncasecmp(language, "ja", 2)) { /* Japanese syntax */
733  return ast_say_number_full_ja(chan, num, ints, language, audiofd, ctrlfd);
734  } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
735  return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
736  } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
737  return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
738  } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
739  return ast_say_number_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
740  } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
741  return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
742  } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
743  return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
744  } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
745  return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
746  } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
747  return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
748  } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
749  return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
750  } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
751  return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
752  } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
753  return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
754  } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
755  return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
756  } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
757  return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
758  } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
759  return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
760  } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
761  return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
762  } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
763  return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
764  }
765 
766  /* Default to english */
767  return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
768 }
769 
770 /*! \brief ast_say_number_full_en: English syntax
771  \note This is the default syntax, if no other syntax defined in this file is used */
772 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
773 {
774  const char *fn;
775  int res = 0;
776 
777  struct ast_str *filenames = ast_get_number_str(num, language);
778  char *files = ast_str_buffer(filenames);
779 
780  while ((fn = strsep(&files, "&"))) {
781  res = ast_streamfile(chan, fn, language);
782  if (!res) {
783  if ((audiofd > -1) && (ctrlfd > -1))
784  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
785  else
786  res = ast_waitstream(chan, ints);
787  }
788  ast_stopstream(chan);
789  }
790 
791  ast_free(filenames);
792 
793  return res;
794 }
795 
796 static int exp10_int(int power)
797 {
798  int x, res= 1;
799  for (x=0;x<power;x++)
800  res *= 10;
801  return res;
802 }
803 
804 /*! \brief ast_say_number_full_cs: Czech syntax
805  *
806  * files needed:
807  * - 1m,2m - gender male
808  * - 1w,2w - gender female
809  * - 3,4,...,20
810  * - 30,40,...,90
811  *
812  * - hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
813  *
814  * for each number 10^(3n + 3) exist 3 files represented as:
815  * 1 tousand = jeden tisic = 1_E3
816  * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
817  * 5,6,... tousands = pet,sest,... tisic = 5_E3
818  *
819  * million = _E6
820  * miliard = _E9
821  * etc...
822  *
823  * tousand, milion are gender male, so 1 and 2 is 1m 2m
824  * miliard is gender female, so 1 and 2 is 1w 2w
825  */
826 static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
827 {
828  int res = 0;
829  int playh = 0;
830  char fn[256] = "";
831 
832  int hundered = 0;
833  int left = 0;
834  int length = 0;
835 
836  /* options - w = woman, m = man, n = neutral. Defaultl is woman */
837  if (!options)
838  options = "w";
839 
840  if (!num)
841  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
842 
843  while (!res && (num || playh)) {
844  if (num < 0) {
845  ast_copy_string(fn, "digits/minus", sizeof(fn));
846  if ( num > INT_MIN ) {
847  num = -num;
848  } else {
849  num = 0;
850  }
851  } else if (num < 3 ) {
852  snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
853  playh = 0;
854  num = 0;
855  } else if (num < 20) {
856  snprintf(fn, sizeof(fn), "digits/%d", num);
857  playh = 0;
858  num = 0;
859  } else if (num < 100) {
860  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
861  num %= 10;
862  } else if (num < 1000) {
863  hundered = num / 100;
864  if ( hundered == 1 ) {
865  ast_copy_string(fn, "digits/1sto", sizeof(fn));
866  } else if ( hundered == 2 ) {
867  ast_copy_string(fn, "digits/2ste", sizeof(fn));
868  } else {
869  res = ast_say_number_full_cs(chan, hundered, ints, language, options, audiofd, ctrlfd);
870  if (res)
871  return res;
872  if (hundered == 3 || hundered == 4) {
873  ast_copy_string(fn, "digits/sta", sizeof(fn));
874  } else if ( hundered > 4 ) {
875  ast_copy_string(fn, "digits/set", sizeof(fn));
876  }
877  }
878  num -= (hundered * 100);
879  } else { /* num > 1000 */
880  length = (int)log10(num)+1;
881  while ( (length % 3 ) != 1 ) {
882  length--;
883  }
884  left = num / (exp10_int(length-1));
885  if ( left == 2 ) {
886  switch (length-1) {
887  case 9: options = "w"; /* 1,000,000,000 gender female */
888  break;
889  default : options = "m"; /* others are male */
890  }
891  }
892  if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
893  res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
894  if (res)
895  return res;
896  }
897  if ( left >= 5 ) { /* >= 5 have the same declesion */
898  snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
899  } else if ( left >= 2 && left <= 4 ) {
900  snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
901  } else { /* left == 1 */
902  snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
903  }
904  num -= left * (exp10_int(length-1));
905  }
906  if (!res) {
907  if (!ast_streamfile(chan, fn, language)) {
908  if ((audiofd > -1) && (ctrlfd > -1)) {
909  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
910  } else {
911  res = ast_waitstream(chan, ints);
912  }
913  }
914  ast_stopstream(chan);
915  }
916  }
917  return res;
918 }
919 
920 /*! \brief ast_say_number_full_da: Danish syntax
921  New files:
922  - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
923  */
924 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)
925 {
926  int res = 0;
927  int playh = 0;
928  int playa = 0;
929  int cn = 1; /* +1 = commune; -1 = neuter */
930  char fn[256] = "";
931  if (!num)
932  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
933 
934  if (options && !strncasecmp(options, "n", 1)) cn = -1;
935 
936  while (!res && (num || playh || playa )) {
937  /* The grammar for Danish numbers is the same as for English except
938  * for the following:
939  * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
940  * - numbers 20 through 99 are said in reverse order, i.e. 21 is
941  * "one-and twenty" and 68 is "eight-and sixty".
942  * - "million" is different in singular and plural form
943  * - numbers > 1000 with zero as the third digit from last have an
944  * "and" before the last two digits, i.e. 2034 is "two thousand and
945  * four-and thirty" and 1000012 is "one million and twelve".
946  */
947  if (num < 0) {
948  ast_copy_string(fn, "digits/minus", sizeof(fn));
949  if ( num > INT_MIN ) {
950  num = -num;
951  } else {
952  num = 0;
953  }
954  } else if (playh) {
955  ast_copy_string(fn, "digits/hundred", sizeof(fn));
956  playh = 0;
957  } else if (playa) {
958  ast_copy_string(fn, "digits/and", sizeof(fn));
959  playa = 0;
960  } else if (num == 1 && cn == -1) {
961  ast_copy_string(fn, "digits/1N", sizeof(fn));
962  num = 0;
963  } else if (num < 20) {
964  snprintf(fn, sizeof(fn), "digits/%d", num);
965  num = 0;
966  } else if (num < 100) {
967  int ones = num % 10;
968  if (ones) {
969  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
970  num -= ones;
971  } else {
972  snprintf(fn, sizeof(fn), "digits/%d", num);
973  num = 0;
974  }
975  } else {
976  if (num < 1000) {
977  int hundreds = num / 100;
978  if (hundreds == 1)
979  ast_copy_string(fn, "digits/1N", sizeof(fn));
980  else
981  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
982 
983  playh++;
984  num -= 100 * hundreds;
985  if (num)
986  playa++;
987 
988  } else {
989  if (num < 1000000) {
990  res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
991  if (res)
992  return res;
993  num = num % 1000;
994  ast_copy_string(fn, "digits/thousand", sizeof(fn));
995  } else {
996  if (num < 1000000000) {
997  int millions = num / 1000000;
998  res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
999  if (res)
1000  return res;
1001  if (millions == 1)
1002  ast_copy_string(fn, "digits/million", sizeof(fn));
1003  else
1004  ast_copy_string(fn, "digits/millions", sizeof(fn));
1005  num = num % 1000000;
1006  } else {
1007  ast_debug(1, "Number '%d' is too big for me\n", num);
1008  res = -1;
1009  }
1010  }
1011  if (num && num < 100)
1012  playa++;
1013  }
1014  }
1015  if (!res) {
1016  if (!ast_streamfile(chan, fn, language)) {
1017  if ((audiofd > -1) && (ctrlfd > -1))
1018  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1019  else
1020  res = ast_waitstream(chan, ints);
1021  }
1022  ast_stopstream(chan);
1023  }
1024  }
1025  return res;
1026 }
1027 
1028 /*! \brief ast_say_number_full_de: German syntax
1029 
1030  New files:
1031  In addition to English, the following sounds are required:
1032  - "millions"
1033  - "1-and" through "9-and"
1034  - "1F" (eine)
1035  - "1N" (ein)
1036  - NB "1" is recorded as 'eins'
1037  */
1038 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)
1039 {
1040  int res = 0, t = 0;
1041  int mf = 1; /* +1 = male and neuter; -1 = female */
1042  char fn[256] = "";
1043  char fna[256] = "";
1044  if (!num)
1045  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1046 
1047  if (options && (!strncasecmp(options, "f", 1)))
1048  mf = -1;
1049 
1050  while (!res && num) {
1051  /* The grammar for German numbers is the same as for English except
1052  * for the following:
1053  * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1054  * "one-and twenty" and 68 is "eight-and sixty".
1055  * - "one" varies according to gender
1056  * - 100 is 'hundert', however all other instances are 'ein hundert'
1057  * - 1000 is 'tausend', however all other instances are 'ein tausend'
1058  * - 1000000 is always 'eine million'
1059  * - "million" is different in singular and plural form
1060  * - 'and' should not go between a hundreds place value and any
1061  * tens/ones place values that follows it. i.e 136 is ein hundert
1062  * sechs und dreizig, not ein hundert und sechs und dreizig.
1063  */
1064  if (num < 0) {
1065  ast_copy_string(fn, "digits/minus", sizeof(fn));
1066  if ( num > INT_MIN ) {
1067  num = -num;
1068  } else {
1069  num = 0;
1070  }
1071  } else if (num == 1 && mf == -1) {
1072  snprintf(fn, sizeof(fn), "digits/%dF", num);
1073  num = 0;
1074  } else if (num < 20) {
1075  snprintf(fn, sizeof(fn), "digits/%d", num);
1076  num = 0;
1077  } else if (num < 100) {
1078  int ones = num % 10;
1079  if (ones) {
1080  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1081  num -= ones;
1082  } else {
1083  snprintf(fn, sizeof(fn), "digits/%d", num);
1084  num = 0;
1085  }
1086  } else if (num == 100 && t == 0) {
1087  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1088  num = 0;
1089  } else if (num < 1000) {
1090  int hundreds = num / 100;
1091  num = num % 100;
1092  if (hundreds == 1) {
1093  ast_copy_string(fn, "digits/1N", sizeof(fn));
1094  } else {
1095  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1096  }
1097  ast_copy_string(fna, "digits/hundred", sizeof(fna));
1098  } else if (num == 1000 && t == 0) {
1099  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1100  num = 0;
1101  } else if (num < 1000000) {
1102  int thousands = num / 1000;
1103  num = num % 1000;
1104  t = 1;
1105  if (thousands == 1) {
1106  ast_copy_string(fn, "digits/1N", sizeof(fn));
1107  ast_copy_string(fna, "digits/thousand", sizeof(fna));
1108  } else {
1109  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
1110  if (res)
1111  return res;
1112  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1113  }
1114  } else if (num < 1000000000) {
1115  int millions = num / 1000000;
1116  num = num % 1000000;
1117  t = 1;
1118  if (millions == 1) {
1119  ast_copy_string(fn, "digits/1F", sizeof(fn));
1120  ast_copy_string(fna, "digits/million", sizeof(fna));
1121  } else {
1122  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
1123  if (res)
1124  return res;
1125  ast_copy_string(fn, "digits/millions", sizeof(fn));
1126  }
1127  } else if (num <= INT_MAX) {
1128  int billions = num / 1000000000;
1129  num = num % 1000000000;
1130  t = 1;
1131  if (billions == 1) {
1132  ast_copy_string(fn, "digits/1F", sizeof(fn));
1133  ast_copy_string(fna, "digits/milliard", sizeof(fna));
1134  } else {
1135  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
1136  if (res) {
1137  return res;
1138  }
1139  ast_copy_string(fn, "digits/milliards", sizeof(fn));
1140  }
1141  } else {
1142  ast_debug(1, "Number '%d' is too big for me\n", num);
1143  res = -1;
1144  }
1145  if (!res) {
1146  if (!ast_streamfile(chan, fn, language)) {
1147  if ((audiofd > -1) && (ctrlfd > -1))
1148  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1149  else
1150  res = ast_waitstream(chan, ints);
1151  }
1152  ast_stopstream(chan);
1153  if (!res) {
1154  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
1155  if ((audiofd > -1) && (ctrlfd > -1))
1156  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1157  else
1158  res = ast_waitstream(chan, ints);
1159  }
1160  ast_stopstream(chan);
1161  strcpy(fna, "");
1162  }
1163  }
1164  }
1165  return res;
1166 }
1167 
1168 /*! \brief ast_say_number_full_en_GB: British syntax
1169  New files:
1170  - In addition to American English, the following sounds are required: "vm-and"
1171  */
1172 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1173 {
1174  int res = 0;
1175  int playh = 0;
1176  int playa = 0;
1177  char fn[256] = "";
1178  if (!num)
1179  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1180 
1181  while (!res && (num || playh || playa )) {
1182  if (num < 0) {
1183  ast_copy_string(fn, "digits/minus", sizeof(fn));
1184  if ( num > INT_MIN ) {
1185  num = -num;
1186  } else {
1187  num = 0;
1188  }
1189  } else if (playh) {
1190  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1191  playh = 0;
1192  } else if (playa) {
1193  ast_copy_string(fn, "vm-and", sizeof(fn));
1194  playa = 0;
1195  } else if (num < 20) {
1196  snprintf(fn, sizeof(fn), "digits/%d", num);
1197  num = 0;
1198  } else if (num < 100) {
1199  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1200  num %= 10;
1201  } else if (num < 1000) {
1202  int hundreds = num / 100;
1203  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1204 
1205  playh++;
1206  num -= 100 * hundreds;
1207  if (num)
1208  playa++;
1209  } else if (num < 1000000) {
1210  res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
1211  if (res)
1212  return res;
1213  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1214  num %= 1000;
1215  if (num && num < 100)
1216  playa++;
1217  } else if (num < 1000000000) {
1218  int millions = num / 1000000;
1219  res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1220  if (res)
1221  return res;
1222  ast_copy_string(fn, "digits/million", sizeof(fn));
1223  num %= 1000000;
1224  if (num && num < 100)
1225  playa++;
1226  } else {
1227  ast_debug(1, "Number '%d' is too big for me\n", num);
1228  res = -1;
1229  }
1230 
1231  if (!res) {
1232  if (!ast_streamfile(chan, fn, language)) {
1233  if ((audiofd > -1) && (ctrlfd > -1))
1234  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1235  else
1236  res = ast_waitstream(chan, ints);
1237  }
1238  ast_stopstream(chan);
1239  }
1240  }
1241  return res;
1242 }
1243 
1244 /*! \brief ast_say_number_full_es: Spanish syntax
1245 
1246  New files:
1247  Requires a few new audios:
1248  1F.gsm: feminine 'una'
1249  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
1250  */
1251 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)
1252 {
1253  int res = 0;
1254  int playa = 0;
1255  int mf = 0; /* +1 = male; -1 = female */
1256  char fn[256] = "";
1257  if (!num)
1258  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1259 
1260  if (options) {
1261  if (!strncasecmp(options, "f", 1))
1262  mf = -1;
1263  else if (!strncasecmp(options, "m", 1))
1264  mf = 1;
1265  }
1266 
1267  while (!res && num) {
1268  if (num < 0) {
1269  ast_copy_string(fn, "digits/minus", sizeof(fn));
1270  if ( num > INT_MIN ) {
1271  num = -num;
1272  } else {
1273  num = 0;
1274  }
1275  } else if (playa) {
1276  ast_copy_string(fn, "digits/and", sizeof(fn));
1277  playa = 0;
1278  } else if (num == 1) {
1279  if (mf < 0)
1280  snprintf(fn, sizeof(fn), "digits/%dF", num);
1281  else if (mf > 0)
1282  snprintf(fn, sizeof(fn), "digits/%dM", num);
1283  else
1284  snprintf(fn, sizeof(fn), "digits/%d", num);
1285  num = 0;
1286  } else if (num < 31) {
1287  snprintf(fn, sizeof(fn), "digits/%d", num);
1288  num = 0;
1289  } else if (num < 100) {
1290  snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1291  num %= 10;
1292  if (num)
1293  playa++;
1294  } else if (num == 100) {
1295  ast_copy_string(fn, "digits/100", sizeof(fn));
1296  num = 0;
1297  } else if (num < 200) {
1298  ast_copy_string(fn, "digits/100-and", sizeof(fn));
1299  num -= 100;
1300  } else {
1301  if (num < 1000) {
1302  snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1303  num %= 100;
1304  } else if (num < 2000) {
1305  num %= 1000;
1306  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1307  } else {
1308  if (num < 1000000) {
1309  res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1310  if (res)
1311  return res;
1312  num %= 1000;
1313  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1314  } else {
1315  if (num < 2147483640) {
1316  if ((num/1000000) == 1) {
1317  res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1318  if (res)
1319  return res;
1320  ast_copy_string(fn, "digits/million", sizeof(fn));
1321  } else {
1322  res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1323  if (res)
1324  return res;
1325  ast_copy_string(fn, "digits/millions", sizeof(fn));
1326  }
1327  num %= 1000000;
1328  } else {
1329  ast_debug(1, "Number '%d' is too big for me\n", num);
1330  res = -1;
1331  }
1332  }
1333  }
1334  }
1335 
1336  if (!res) {
1337  if (!ast_streamfile(chan, fn, language)) {
1338  if ((audiofd > -1) && (ctrlfd > -1))
1339  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1340  else
1341  res = ast_waitstream(chan, ints);
1342  }
1343  ast_stopstream(chan);
1344 
1345  }
1346 
1347  }
1348  return res;
1349 }
1350 
1351 /*! \brief ast_say_number_full_fr: French syntax
1352  Extra sounds needed:
1353  1F: feminin 'une'
1354  et: 'and' */
1355 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)
1356 {
1357  int res = 0;
1358  int playh = 0;
1359  int playa = 0;
1360  int mf = 1; /* +1 = male; -1 = female */
1361  char fn[256] = "";
1362  if (!num)
1363  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1364 
1365  if (options && !strncasecmp(options, "f", 1))
1366  mf = -1;
1367 
1368  while (!res && (num || playh || playa)) {
1369  if (num < 0) {
1370  ast_copy_string(fn, "digits/minus", sizeof(fn));
1371  if ( num > INT_MIN ) {
1372  num = -num;
1373  } else {
1374  num = 0;
1375  }
1376  } else if (playh) {
1377  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1378  playh = 0;
1379  } else if (playa) {
1380  ast_copy_string(fn, "digits/et", sizeof(fn));
1381  playa = 0;
1382  } else if (num == 1) {
1383  if (mf < 0)
1384  snprintf(fn, sizeof(fn), "digits/%dF", num);
1385  else
1386  snprintf(fn, sizeof(fn), "digits/%d", num);
1387  num = 0;
1388  } else if (num < 21) {
1389  snprintf(fn, sizeof(fn), "digits/%d", num);
1390  num = 0;
1391  } else if (num < 70) {
1392  snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1393  if ((num % 10) == 1) playa++;
1394  num = num % 10;
1395  } else if (num < 80) {
1396  ast_copy_string(fn, "digits/60", sizeof(fn));
1397  if ((num % 10) == 1) playa++;
1398  num -= 60;
1399  } else if (num < 100) {
1400  ast_copy_string(fn, "digits/80", sizeof(fn));
1401  num = num - 80;
1402  } else if (num < 200) {
1403  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1404  num = num - 100;
1405  } else if (num < 1000) {
1406  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1407  playh++;
1408  num = num % 100;
1409  } else if (num < 2000) {
1410  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1411  num = num - 1000;
1412  } else if (num < 1000000) {
1413  res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1414  if (res)
1415  return res;
1416  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1417  num = num % 1000;
1418  } else if (num < 1000000000) {
1419  res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1420  if (res)
1421  return res;
1422  ast_copy_string(fn, "digits/million", sizeof(fn));
1423  num = num % 1000000;
1424  } else {
1425  ast_debug(1, "Number '%d' is too big for me\n", num);
1426  res = -1;
1427  }
1428  if (!res) {
1429  if (!ast_streamfile(chan, fn, language)) {
1430  if ((audiofd > -1) && (ctrlfd > -1))
1431  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1432  else
1433  res = ast_waitstream(chan, ints);
1434  }
1435  ast_stopstream(chan);
1436  }
1437  }
1438  return res;
1439 }
1440 
1441 
1442 
1443 /* Hebrew syntax
1444  * Check doc/lang/hebrew-digits.txt for information about the various
1445  * recordings required to make this translation work properly */
1446 #define SAY_NUM_BUF_SIZE 256
1447 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)
1448 {
1449  int res = 0;
1450  int state = 0; /* no need to save anything */
1451  int mf = -1; /* +1 = Masculin; -1 = Feminin */
1452  int tmpnum = 0;
1453 
1454  char fn[SAY_NUM_BUF_SIZE] = "";
1455 
1456  ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1457 
1458  if (!num) {
1459  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1460  }
1461  if (options && !strncasecmp(options, "m", 1)) {
1462  mf = 1;
1463  }
1464  ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1465 
1466  /* Do we have work to do? */
1467  while (!res && (num || (state > 0))) {
1468  /* first type of work: play a second sound. In this loop
1469  * we can only play one sound file at a time. Thus playing
1470  * a second one requires repeating the loop just for the
1471  * second file. The variable 'state' remembers where we were.
1472  * state==0 is the normal mode and it means that we continue
1473  * to check if the number num has yet anything left.
1474  */
1475  ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1476 
1477  if (state == 1) {
1478  state = 0;
1479  } else if (state == 2) {
1480  if ((num >= 11) && (num < 21)) {
1481  if (mf < 0) {
1482  snprintf(fn, sizeof(fn), "digits/ve");
1483  } else {
1484  snprintf(fn, sizeof(fn), "digits/uu");
1485  }
1486  } else {
1487  switch (num) {
1488  case 1:
1489  snprintf(fn, sizeof(fn), "digits/ve");
1490  break;
1491  case 2:
1492  snprintf(fn, sizeof(fn), "digits/uu");
1493  break;
1494  case 3:
1495  if (mf < 0) {
1496  snprintf(fn, sizeof(fn), "digits/ve");
1497  } else {
1498  snprintf(fn, sizeof(fn), "digits/uu");
1499  }
1500  break;
1501  case 4:
1502  snprintf(fn, sizeof(fn), "digits/ve");
1503  break;
1504  case 5:
1505  snprintf(fn, sizeof(fn), "digits/ve");
1506  break;
1507  case 6:
1508  snprintf(fn, sizeof(fn), "digits/ve");
1509  break;
1510  case 7:
1511  snprintf(fn, sizeof(fn), "digits/ve");
1512  break;
1513  case 8:
1514  snprintf(fn, sizeof(fn), "digits/uu");
1515  break;
1516  case 9:
1517  snprintf(fn, sizeof(fn), "digits/ve");
1518  break;
1519  case 10:
1520  snprintf(fn, sizeof(fn), "digits/ve");
1521  break;
1522  }
1523  }
1524  state = 0;
1525  } else if (state == 3) {
1526  snprintf(fn, sizeof(fn), "digits/1k");
1527  state = 0;
1528  } else if (num < 0) {
1529  snprintf(fn, sizeof(fn), "digits/minus");
1530  num = (-1) * num;
1531  } else if (num < 20) {
1532  if (mf < 0) {
1533  snprintf(fn, sizeof(fn), "digits/%d", num);
1534  } else {
1535  snprintf(fn, sizeof(fn), "digits/%dm", num);
1536  }
1537  num = 0;
1538  } else if ((num < 100) && (num >= 20)) {
1539  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1540  num = num % 10;
1541  if (num > 0) {
1542  state = 2;
1543  }
1544  } else if ((num >= 100) && (num < 1000)) {
1545  tmpnum = num / 100;
1546  snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1547  num = num - (tmpnum * 100);
1548  if ((num > 0) && (num < 11)) {
1549  state = 2;
1550  }
1551  } else if ((num >= 1000) && (num < 10000)) {
1552  tmpnum = num / 1000;
1553  snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1554  num = num - (tmpnum * 1000);
1555  if ((num > 0) && (num < 11)) {
1556  state = 2;
1557  }
1558  } else if (num < 20000) {
1559  snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1560  num = num % 1000;
1561  state = 3;
1562  } else if (num < 1000000) {
1563  res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1564  if (res) {
1565  return res;
1566  }
1567  snprintf(fn, sizeof(fn), "digits/1k");
1568  num = num % 1000;
1569  if ((num > 0) && (num < 11)) {
1570  state = 2;
1571  }
1572  } else if (num < 2000000) {
1573  snprintf(fn, sizeof(fn), "digits/million");
1574  num = num % 1000000;
1575  if ((num > 0) && (num < 11)) {
1576  state = 2;
1577  }
1578  } else if (num < 3000000) {
1579  snprintf(fn, sizeof(fn), "digits/twomillion");
1580  num = num - 2000000;
1581  if ((num > 0) && (num < 11)) {
1582  state = 2;
1583  }
1584  } else if (num < 1000000000) {
1585  res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1586  if (res) {
1587  return res;
1588  }
1589  snprintf(fn, sizeof(fn), "digits/million");
1590  num = num % 1000000;
1591  if ((num > 0) && (num < 11)) {
1592  state = 2;
1593  }
1594  } else {
1595  ast_debug(1, "Number '%d' is too big for me\n", num);
1596  res = -1;
1597  }
1598  tmpnum = 0;
1599  if (!res) {
1600  if (!ast_streamfile(chan, fn, language)) {
1601  if ((audiofd > -1) && (ctrlfd > -1)) {
1602  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1603  } else {
1604  res = ast_waitstream(chan, ints);
1605  }
1606  }
1607  ast_stopstream(chan);
1608  }
1609  }
1610  return res;
1611 }
1612 
1613 /*! \brief ast_say_number_full_hu: Hungarian syntax
1614 
1615  Extra sounds needed:
1616  10en: "tizen"
1617  20on: "huszon"
1618 */
1619 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1620 {
1621  int res = 0;
1622  int playh = 0;
1623  char fn[256] = "";
1624  if (!num)
1625  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1626 
1627  /*
1628  Hungarian support
1629  like english, except numbers up to 29 are from 2 words.
1630  10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1631  */
1632 
1633  while(!res && (num || playh)) {
1634  if (num < 0) {
1635  ast_copy_string(fn, "digits/minus", sizeof(fn));
1636  if ( num > INT_MIN ) {
1637  num = -num;
1638  } else {
1639  num = 0;
1640  }
1641  } else if (playh) {
1642  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1643  playh = 0;
1644  } else if (num < 11 || num == 20) {
1645  snprintf(fn, sizeof(fn), "digits/%d", num);
1646  num = 0;
1647  } else if (num < 20) {
1648  ast_copy_string(fn, "digits/10en", sizeof(fn));
1649  num -= 10;
1650  } else if (num < 30) {
1651  ast_copy_string(fn, "digits/20on", sizeof(fn));
1652  num -= 20;
1653  } else if (num < 100) {
1654  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1655  num %= 10;
1656  } else {
1657  if (num < 1000){
1658  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1659  playh++;
1660  num %= 100;
1661  } else {
1662  if (num < 1000000) { /* 1,000,000 */
1663  res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1664  if (res)
1665  return res;
1666  num %= 1000;
1667  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1668  } else {
1669  if (num < 1000000000) { /* 1,000,000,000 */
1670  res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1671  if (res)
1672  return res;
1673  num %= 1000000;
1674  ast_copy_string(fn, "digits/million", sizeof(fn));
1675  } else {
1676  ast_debug(1, "Number '%d' is too big for me\n", num);
1677  res = -1;
1678  }
1679  }
1680  }
1681  }
1682  if (!res) {
1683  if(!ast_streamfile(chan, fn, language)) {
1684  if ((audiofd > -1) && (ctrlfd > -1))
1685  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1686  else
1687  res = ast_waitstream(chan, ints);
1688  }
1689  ast_stopstream(chan);
1690  }
1691  }
1692  return res;
1693 }
1694 
1695 /*! \brief ast_say_number_full_is: Icelandic syntax */
1696 /* New files:
1697  In addition to American English, the following sounds are required: "hundreds", "millions", "1kvk", "1hk", "2kvk", "2hk", "3kvk", "3hk", "4kvk", "4hk"
1698  */
1699 static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1700 {
1701  int res = 0;
1702  int playh = 0;
1703  int playa = 0;
1704  int cn = 1; /* 1 = masc; 2 = fem; 3 = neut */
1705  char fn[256] = "";
1706 
1707  if (!num)
1708  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1709 
1710  if (options && !strncasecmp(options, "f", 1)) cn = 2;
1711  if (options && !strncasecmp(options, "c", 1)) cn = 3;
1712  /* It seems that sometimes people are using c and sometimes n. */
1713  if (options && !strncasecmp(options, "n", 1)) cn = 3;
1714 
1715  while (!res && (num || playh || playa )) {
1716  if (num < 0) {
1717  ast_copy_string(fn, "digits/minus", sizeof(fn));
1718  if ( num > INT_MIN ) {
1719  num = -num;
1720  } else {
1721  num = 0;
1722  }
1723  } else if (playh) {
1724  if (playh > 1)
1725  ast_copy_string(fn, "digits/hundreds", sizeof(fn));
1726  else
1727  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1728  playh = 0;
1729  } else if (playa) {
1730  ast_copy_string(fn, "digits/and", sizeof(fn));
1731  playa = 0;
1732  } else if (num < 5 && cn == 2) {
1733  snprintf(fn, sizeof(fn), "digits/%dkvk", num);
1734  num = 0;
1735  } else if (num < 5 && cn == 3) {
1736  snprintf(fn, sizeof(fn), "digits/%dhk", num);
1737  num = 0;
1738  } else if (num < 20) {
1739  snprintf(fn, sizeof(fn), "digits/%d", num);
1740  num = 0;
1741  } else if (num < 100) {
1742  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1743  num %= 10;
1744  if (num)
1745  playa++;
1746  } else if (num < 1000) {
1747  int hundreds = num / 100;
1748  /* The number prepending hundreds are in neutral */
1749  if (hundreds < 5)
1750  snprintf(fn, sizeof(fn), "digits/%dhk", hundreds);
1751  else
1752  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1753 
1754  playh = hundreds;
1755  num -= 100 * hundreds;
1756  if (num && num < 20)
1757  playa++;
1758  /* The 'and' moves forward on even tens. */
1759  if (num && (num % 10) == 0)
1760  playa++;
1761  } else if (num < 1000000) {
1762  res = ast_say_number_full_is(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1763  /* Play 'and' if it's an even hundred. */
1764  if ((num % 100) == 0 && (num % 1000 != 0)) {
1765  playa++;
1766  }
1767  if (res)
1768  return res;
1769  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1770  num %= 1000;
1771  if (num && (num < 20 || (num % 10 == 0)))
1772  playa++;
1773  } else if (num < 1000000000) {
1774  int millions = num / 1000000;
1775  /* The number of millions is feminine */
1776  res = ast_say_number_full_is(chan, millions, ints, language, "f", audiofd, ctrlfd);
1777  if (res)
1778  return res;
1779  if (millions > 1)
1780  ast_copy_string(fn, "digits/millions", sizeof(fn));
1781  else
1782  ast_copy_string(fn, "digits/million", sizeof(fn));
1783  num %= 1000000;
1784  if (num && num < 100)
1785  playa++;
1786  } else if (num < INT_MAX) {
1787  int milliards = num / 1000000000;
1788  /* The number of milliards is masculine */
1789  res = ast_say_number_full_is(chan, milliards, ints, language, "m", audiofd, ctrlfd);
1790  if (res)
1791  return res;
1792  if (milliards > 1)
1793  ast_copy_string(fn, "digits/milliards", sizeof(fn));
1794  else
1795  ast_copy_string(fn, "digits/milliard", sizeof(fn));
1796  num %= 1000000000;
1797  if (num && num < 100)
1798  playa++;
1799  } else {
1800  ast_debug(1, "Number '%d' is too big for me\n", num);
1801  res = -1;
1802  }
1803 
1804  if (!res) {
1805  if (!ast_streamfile(chan, fn, language)) {
1806  if ((audiofd > -1) && (ctrlfd > -1))
1807  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1808  else
1809  res = ast_waitstream(chan, ints);
1810  }
1811  ast_stopstream(chan);
1812  }
1813  }
1814  return res;
1815 }
1816 
1817 
1818 /*! \brief ast_say_number_full_it: Italian */
1819 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1820 {
1821  int res = 0;
1822  int playh = 0;
1823  int tempnum = 0;
1824  char fn[256] = "";
1825 
1826  if (!num)
1827  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1828 
1829  /*
1830  Italian support
1831 
1832  Like english, numbers up to 20 are a single 'word', and others
1833  compound, but with exceptions.
1834  For example 21 is not twenty-one, but there is a single word in 'it'.
1835  Idem for 28 (ie when a the 2nd part of a compund number
1836  starts with a vowel)
1837 
1838  There are exceptions also for hundred, thousand and million.
1839  In english 100 = one hundred, 200 is two hundred.
1840  In italian 100 = cento , like to say hundred (without one),
1841  200 and more are like english.
1842 
1843  Same applies for thousand:
1844  1000 is one thousand in en, 2000 is two thousand.
1845  In it we have 1000 = mille , 2000 = 2 mila
1846 
1847  For million(s) we use the plural, if more than one
1848  Also, one million is abbreviated in it, like on-million,
1849  or 'un milione', not 'uno milione'.
1850  So the right file is provided.
1851  */
1852 
1853  while (!res && (num || playh)) {
1854  if (num < 0) {
1855  ast_copy_string(fn, "digits/minus", sizeof(fn));
1856  if ( num > INT_MIN ) {
1857  num = -num;
1858  } else {
1859  num = 0;
1860  }
1861  } else if (playh) {
1862  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1863  playh = 0;
1864  } else if (num < 20) {
1865  snprintf(fn, sizeof(fn), "digits/%d", num);
1866  num = 0;
1867  } else if (num == 21) {
1868  snprintf(fn, sizeof(fn), "digits/%d", num);
1869  num = 0;
1870  } else if (num == 28) {
1871  snprintf(fn, sizeof(fn), "digits/%d", num);
1872  num = 0;
1873  } else if (num == 31) {
1874  snprintf(fn, sizeof(fn), "digits/%d", num);
1875  num = 0;
1876  } else if (num == 38) {
1877  snprintf(fn, sizeof(fn), "digits/%d", num);
1878  num = 0;
1879  } else if (num == 41) {
1880  snprintf(fn, sizeof(fn), "digits/%d", num);
1881  num = 0;
1882  } else if (num == 48) {
1883  snprintf(fn, sizeof(fn), "digits/%d", num);
1884  num = 0;
1885  } else if (num == 51) {
1886  snprintf(fn, sizeof(fn), "digits/%d", num);
1887  num = 0;
1888  } else if (num == 58) {
1889  snprintf(fn, sizeof(fn), "digits/%d", num);
1890  num = 0;
1891  } else if (num == 61) {
1892  snprintf(fn, sizeof(fn), "digits/%d", num);
1893  num = 0;
1894  } else if (num == 68) {
1895  snprintf(fn, sizeof(fn), "digits/%d", num);
1896  num = 0;
1897  } else if (num == 71) {
1898  snprintf(fn, sizeof(fn), "digits/%d", num);
1899  num = 0;
1900  } else if (num == 78) {
1901  snprintf(fn, sizeof(fn), "digits/%d", num);
1902  num = 0;
1903  } else if (num == 81) {
1904  snprintf(fn, sizeof(fn), "digits/%d", num);
1905  num = 0;
1906  } else if (num == 88) {
1907  snprintf(fn, sizeof(fn), "digits/%d", num);
1908  num = 0;
1909  } else if (num == 91) {
1910  snprintf(fn, sizeof(fn), "digits/%d", num);
1911  num = 0;
1912  } else if (num == 98) {
1913  snprintf(fn, sizeof(fn), "digits/%d", num);
1914  num = 0;
1915  } else if (num < 100) {
1916  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1917  num %= 10;
1918  } else {
1919  if (num < 1000) {
1920  if ((num / 100) > 1) {
1921  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1922  playh++;
1923  } else {
1924  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1925  }
1926  num %= 100;
1927  } else {
1928  if (num < 1000000) { /* 1,000,000 */
1929  if ((num/1000) > 1)
1930  res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1931  if (res)
1932  return res;
1933  tempnum = num;
1934  num %= 1000;
1935  if ((tempnum / 1000) < 2)
1936  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1937  else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1938  ast_copy_string(fn, "digits/thousands", sizeof(fn));
1939  } else {
1940  if (num < 1000000000) { /* 1,000,000,000 */
1941  if ((num / 1000000) > 1)
1942  res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1943  if (res)
1944  return res;
1945  tempnum = num;
1946  num %= 1000000;
1947  if ((tempnum / 1000000) < 2)
1948  ast_copy_string(fn, "digits/million", sizeof(fn));
1949  else
1950  ast_copy_string(fn, "digits/millions", sizeof(fn));
1951  } else {
1952  ast_debug(1, "Number '%d' is too big for me\n", num);
1953  res = -1;
1954  }
1955  }
1956  }
1957  }
1958  if (!res) {
1959  if (!ast_streamfile(chan, fn, language)) {
1960  if ((audiofd > -1) && (ctrlfd > -1))
1961  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1962  else
1963  res = ast_waitstream(chan, ints);
1964  }
1965  ast_stopstream(chan);
1966  }
1967  }
1968  return res;
1969 }
1970 
1971 /*! \brief ast_say_number_full_nl: dutch syntax
1972  * New files: digits/nl-en
1973  */
1974 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1975 {
1976  int res = 0;
1977  int playh = 0;
1978  int units = 0;
1979  char fn[256] = "";
1980  if (!num)
1981  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1982  while (!res && (num || playh )) {
1983  if (num < 0) {
1984  ast_copy_string(fn, "digits/minus", sizeof(fn));
1985  if ( num > INT_MIN ) {
1986  num = -num;
1987  } else {
1988  num = 0;
1989  }
1990  } else if (playh) {
1991  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1992  playh = 0;
1993  } else if (num < 20) {
1994  snprintf(fn, sizeof(fn), "digits/%d", num);
1995  num = 0;
1996  } else if (num < 100) {
1997  units = num % 10;
1998  if (units > 0) {
1999  res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
2000  if (res)
2001  return res;
2002  num = num - units;
2003  ast_copy_string(fn, "digits/nl-en", sizeof(fn));
2004  } else {
2005  snprintf(fn, sizeof(fn), "digits/%d", num - units);
2006  num = 0;
2007  }
2008  } else if (num < 200) {
2009  /* hundred, not one-hundred */
2010  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2011  num %= 100;
2012  } else if (num < 1000) {
2013  snprintf(fn, sizeof(fn), "digits/%d", num / 100);
2014  playh++;
2015  num %= 100;
2016  } else {
2017  if (num < 1100) {
2018  /* thousand, not one-thousand */
2019  num %= 1000;
2020  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2021  } else if (num < 10000) { /* 1,100 to 9,9999 */
2022  res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
2023  if (res)
2024  return res;
2025  num %= 100;
2026  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2027  } else {
2028  if (num < 1000000) { /* 1,000,000 */
2029  res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
2030  if (res)
2031  return res;
2032  num %= 1000;
2033  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2034  } else {
2035  if (num < 1000000000) { /* 1,000,000,000 */
2036  res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2037  if (res)
2038  return res;
2039  num %= 1000000;
2040  ast_copy_string(fn, "digits/million", sizeof(fn));
2041  } else {
2042  ast_debug(1, "Number '%d' is too big for me\n", num);
2043  res = -1;
2044  }
2045  }
2046  }
2047  }
2048 
2049  if (!res) {
2050  if (!ast_streamfile(chan, fn, language)) {
2051  if ((audiofd > -1) && (ctrlfd > -1))
2052  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2053  else
2054  res = ast_waitstream(chan, ints);
2055  }
2056  ast_stopstream(chan);
2057  }
2058  }
2059  return res;
2060 }
2061 
2062 /*! \brief ast_say_number_full_no: Norwegian syntax
2063  * New files:
2064  * In addition to American English, the following sounds are required: "and", "1N"
2065  *
2066  * The grammar for Norwegian numbers is the same as for English except
2067  * for the following:
2068  * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
2069  * "and" before the last two digits, i.e. 2034 is "two thousand and
2070  * thirty-four" and 1000012 is "one million and twelve".
2071  */
2072 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)
2073 {
2074  int res = 0;
2075  int playh = 0;
2076  int playa = 0;
2077  int cn = 1; /* +1 = commune; -1 = neuter */
2078  char fn[256] = "";
2079 
2080  if (!num)
2081  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2082 
2083  if (options && !strncasecmp(options, "n", 1)) cn = -1;
2084 
2085  while (!res && (num || playh || playa )) {
2086  if (num < 0) {
2087  ast_copy_string(fn, "digits/minus", sizeof(fn));
2088  if ( num > INT_MIN ) {
2089  num = -num;
2090  } else {
2091  num = 0;
2092  }
2093  } else if (playh) {
2094  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2095  playh = 0;
2096  } else if (playa) {
2097  ast_copy_string(fn, "digits/and", sizeof(fn));
2098  playa = 0;
2099  } else if (num == 1 && cn == -1) {
2100  ast_copy_string(fn, "digits/1N", sizeof(fn));
2101  num = 0;
2102  } else if (num < 20) {
2103  snprintf(fn, sizeof(fn), "digits/%d", num);
2104  num = 0;
2105  } else if (num < 100) {
2106  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2107  num %= 10;
2108  } else if (num < 1000) {
2109  int hundreds = num / 100;
2110  if (hundreds == 1)
2111  ast_copy_string(fn, "digits/1N", sizeof(fn));
2112  else
2113  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2114 
2115  playh++;
2116  num -= 100 * hundreds;
2117  if (num)
2118  playa++;
2119  } else if (num < 1000000) {
2120  res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
2121  if (res)
2122  return res;
2123  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2124  num %= 1000;
2125  if (num && num < 100)
2126  playa++;
2127  } else if (num < 1000000000) {
2128  int millions = num / 1000000;
2129  res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
2130  if (res)
2131  return res;
2132  ast_copy_string(fn, "digits/million", sizeof(fn));
2133  num %= 1000000;
2134  if (num && num < 100)
2135  playa++;
2136  } else {
2137  ast_debug(1, "Number '%d' is too big for me\n", num);
2138  res = -1;
2139  }
2140 
2141  if (!res) {
2142  if (!ast_streamfile(chan, fn, language)) {
2143  if ((audiofd > -1) && (ctrlfd > -1))
2144  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2145  else
2146  res = ast_waitstream(chan, ints);
2147  }
2148  ast_stopstream(chan);
2149  }
2150  }
2151  return res;
2152 }
2153 
2154 typedef struct {
2156  char *cyfry[10];
2157  char *cyfry2[10];
2158  char *setki[10];
2159  char *dziesiatki[10];
2160  char *nastki[10];
2161  char *rzedy[3][3];
2162 } odmiana;
2163 
2164 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
2165 {
2166  if (rzad==0)
2167  return "";
2168 
2169  if (i==1)
2170  return odm->rzedy[rzad - 1][0];
2171  if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
2172  return odm->rzedy[rzad - 1][1];
2173  else
2174  return odm->rzedy[rzad - 1][2];
2175 }
2176 
2177 static char* pl_append(char* buffer, char* str)
2178 {
2179  strcpy(buffer, str);
2180  buffer += strlen(str);
2181  return buffer;
2182 }
2183 
2184 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
2185 {
2186  char file_name[255] = "digits/";
2187  strcat(file_name, fn);
2188  ast_debug(1, "Trying to play: %s\n", file_name);
2189  if (!ast_streamfile(chan, file_name, language)) {
2190  if ((audiofd > -1) && (ctrlfd > -1))
2191  ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2192  else
2193  ast_waitstream(chan, ints);
2194  }
2195  ast_stopstream(chan);
2196 }
2197 
2198 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
2199 {
2200  /* Initialise variables to allow compilation on Debian-stable, etc */
2201  int m1000E6 = 0;
2202  int i1000E6 = 0;
2203  int m1000E3 = 0;
2204  int i1000E3 = 0;
2205  int m1000 = 0;
2206  int i1000 = 0;
2207  int m100 = 0;
2208  int i100 = 0;
2209 
2210  if (i == 0 && rzad > 0) {
2211  return;
2212  }
2213  if (i == 0) {
2214  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
2215  return;
2216  }
2217 
2218  m1000E6 = i % 1000000000;
2219  i1000E6 = i / 1000000000;
2220 
2221  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
2222 
2223  m1000E3 = m1000E6 % 1000000;
2224  i1000E3 = m1000E6 / 1000000;
2225 
2226  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
2227 
2228  m1000 = m1000E3 % 1000;
2229  i1000 = m1000E3 / 1000;
2230 
2231  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
2232 
2233  m100 = m1000 % 100;
2234  i100 = m1000 / 100;
2235 
2236  if (i100>0)
2237  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
2238 
2239  if (m100 > 0 && m100 <= 9) {
2240  if (m1000 > 0)
2241  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
2242  else
2243  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
2244  } else if (m100 % 10 == 0 && m100 != 0) {
2245  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2246  } else if (m100 > 10 && m100 <= 19) {
2247  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
2248  } else if (m100 > 20) {
2249  if (odm->separator_dziesiatek[0] == ' ') {
2250  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2251  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
2252  } else {
2253  char buf[10];
2254  char *b = buf;
2255  b = pl_append(b, odm->dziesiatki[m100 / 10]);
2256  b = pl_append(b, odm->separator_dziesiatek);
2257  pl_append(b, odm->cyfry2[m100 % 10]);
2258  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
2259  }
2260  }
2261 
2262  if (rzad > 0) {
2263  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
2264  }
2265 }
2266 
2267 /* ast_say_number_full_pl: Polish syntax
2268 
2269 Sounds needed:
2270 0 zero
2271 1 jeden
2272 10 dziesiec
2273 100 sto
2274 1000 tysiac
2275 1000000 milion
2276 1000000000 miliard
2277 1000000000.2 miliardy
2278 1000000000.5 miliardow
2279 1000000.2 miliony
2280 1000000.5 milionow
2281 1000.2 tysiace
2282 1000.5 tysiecy
2283 100m stu
2284 10m dziesieciu
2285 11 jedenascie
2286 11m jedenastu
2287 12 dwanascie
2288 12m dwunastu
2289 13 trzynascie
2290 13m trzynastu
2291 14 czternascie
2292 14m czternastu
2293 15 pietnascie
2294 15m pietnastu
2295 16 szesnascie
2296 16m szesnastu
2297 17 siedemnascie
2298 17m siedemnastu
2299 18 osiemnascie
2300 18m osiemnastu
2301 19 dziewietnascie
2302 19m dziewietnastu
2303 1z jedna
2304 2 dwa
2305 20 dwadziescia
2306 200 dwiescie
2307 200m dwustu
2308 20m dwudziestu
2309 2-1m dwaj
2310 2-2m dwoch
2311 2z dwie
2312 3 trzy
2313 30 trzydziesci
2314 300 trzysta
2315 300m trzystu
2316 30m trzydziestu
2317 3-1m trzej
2318 3-2m trzech
2319 4 cztery
2320 40 czterdziesci
2321 400 czterysta
2322 400m czterystu
2323 40m czterdziestu
2324 4-1m czterej
2325 4-2m czterech
2326 5 piec
2327 50 piecdziesiat
2328 500 piecset
2329 500m pieciuset
2330 50m piedziesieciu
2331 5m pieciu
2332 6 szesc
2333 60 szescdziesiat
2334 600 szescset
2335 600m szesciuset
2336 60m szescdziesieciu
2337 6m szesciu
2338 7 siedem
2339 70 siedemdziesiat
2340 700 siedemset
2341 700m siedmiuset
2342 70m siedemdziesieciu
2343 7m siedmiu
2344 8 osiem
2345 80 osiemdziesiat
2346 800 osiemset
2347 800m osmiuset
2348 80m osiemdziesieciu
2349 8m osmiu
2350 9 dziewiec
2351 90 dziewiecdziesiat
2352 900 dziewiecset
2353 900m dziewieciuset
2354 90m dziewiedziesieciu
2355 9m dziewieciu
2356 and combinations of eg.: 20_1, 30m_3m, etc...
2357 
2358 */
2359 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)
2360 {
2361  char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2362 
2363  char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2364 
2365  char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2366 
2367  char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2368 
2369  char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2370 
2371  char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2372 
2373  char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2374 
2375  char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2376 
2377  char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2378 
2379  char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2380 
2381  char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2382 
2383  char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2384 
2385  char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2386 
2387  /* Initialise variables to allow compilation on Debian-stable, etc */
2388  odmiana *o;
2389 
2390  static odmiana *odmiana_nieosobowa = NULL;
2391  static odmiana *odmiana_meska = NULL;
2392  static odmiana *odmiana_zenska = NULL;
2393 
2394  if (odmiana_nieosobowa == NULL) {
2395  odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2396 
2397  odmiana_nieosobowa->separator_dziesiatek = " ";
2398 
2399  memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2400  memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2401  memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2402  memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2403  memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2404  memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2405  }
2406 
2407  if (odmiana_zenska == NULL) {
2408  odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2409 
2410  odmiana_zenska->separator_dziesiatek = " ";
2411 
2412  memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2413  memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2414  memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2415  memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2416  memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2417  memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2418  }
2419 
2420  if (odmiana_meska == NULL) {
2421  odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2422 
2423  odmiana_meska->separator_dziesiatek = " ";
2424 
2425  memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2426  memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2427  memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2428  memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2429  memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2430  memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2431  }
2432 
2433  if (options) {
2434  if (strncasecmp(options, "f", 1) == 0)
2435  o = odmiana_zenska;
2436  else if (strncasecmp(options, "m", 1) == 0)
2437  o = odmiana_meska;
2438  else
2439  o = odmiana_nieosobowa;
2440  } else
2441  o = odmiana_nieosobowa;
2442 
2443  powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2444  return 0;
2445 }
2446 
2447 /* ast_say_number_full_pt: Portuguese syntax
2448 
2449  * Extra sounds needed:
2450  * For feminin all sound files ends with F
2451  * 100E for 100+ something
2452  * 1000000S for plural
2453  * pt-e for 'and'
2454  */
2455 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)
2456 {
2457  int res = 0;
2458  int playh = 0;
2459  int mf = 1; /* +1 = male; -1 = female */
2460  char fn[256] = "";
2461 
2462  if (!num)
2463  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2464 
2465  if (options && !strncasecmp(options, "f", 1))
2466  mf = -1;
2467 
2468  while (!res && num ) {
2469  if (num < 0) {
2470  ast_copy_string(fn, "digits/minus", sizeof(fn));
2471  if ( num > INT_MIN ) {
2472  num = -num;
2473  } else {
2474  num = 0;
2475  }
2476  } else if (num < 20) {
2477  if ((num == 1 || num == 2) && (mf < 0))
2478  snprintf(fn, sizeof(fn), "digits/%dF", num);
2479  else
2480  snprintf(fn, sizeof(fn), "digits/%d", num);
2481  num = 0;
2482  } else if (num < 100) {
2483  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2484  if (num % 10)
2485  playh = 1;
2486  num = num % 10;
2487  } else if (num < 1000) {
2488  if (num == 100)
2489  ast_copy_string(fn, "digits/100", sizeof(fn));
2490  else if (num < 200)
2491  ast_copy_string(fn, "digits/100E", sizeof(fn));
2492  else {
2493  if (mf < 0 && num > 199)
2494  snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2495  else
2496  snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2497  if (num % 100)
2498  playh = 1;
2499  }
2500  num = num % 100;
2501  } else if (num < 1000000) {
2502  if (num > 1999) {
2503  res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2504  if (res)
2505  return res;
2506  }
2507  ast_copy_string(fn, "digits/1000", sizeof(fn));
2508  if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2509  playh = 1;
2510  num = num % 1000;
2511  } else if (num < 1000000000) {
2512  res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2513  if (res)
2514  return res;
2515  if (num < 2000000)
2516  ast_copy_string(fn, "digits/1000000", sizeof(fn));
2517  else
2518  ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2519 
2520  if ((num % 1000000) &&
2521  /* no thousands */
2522  ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2523  /* no hundreds and below */
2524  (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2525  playh = 1;
2526  num = num % 1000000;
2527  } else {
2528  /* number is too big */
2529  ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2530  res = -1;
2531  }
2532  if (!res) {
2533  if (!ast_streamfile(chan, fn, language)) {
2534  if ((audiofd > -1) && (ctrlfd > -1))
2535  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2536  else
2537  res = ast_waitstream(chan, ints);
2538  }
2539  ast_stopstream(chan);
2540  }
2541  if (!res && playh) {
2542  res = wait_file(chan, ints, "digits/pt-e", language);
2543  ast_stopstream(chan);
2544  playh = 0;
2545  }
2546  }
2547  return res;
2548 }
2549 
2550 /*! \brief ast_say_number_full_se: Swedish syntax
2551 
2552  Sound files needed
2553  - 1N
2554 */
2555 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)
2556 {
2557  int playh = 0;
2558  int start = 1;
2559  char fn[256] = "";
2560  int cn = 1; /* +1 = commune; -1 = neuter */
2561  int res = 0;
2562 
2563  if (!num) {
2564  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2565  }
2566  if (options && !strncasecmp(options, "n", 1)) cn = -1;
2567 
2568  while (num || playh) {
2569  if (num < 0) {
2570  ast_copy_string(fn, "digits/minus", sizeof(fn));
2571  if ( num > INT_MIN ) {
2572  num = -num;
2573  } else {
2574  num = 0;
2575  }
2576  } else if (playh) {
2577  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2578  playh = 0;
2579  } else if (start && num < 200 && num > 99 && cn == -1) {
2580  /* Don't say "en hundra" just say "hundra". */
2581  snprintf(fn, sizeof(fn), "digits/hundred");
2582  num -= 100;
2583  } else if (num == 1 && cn == -1) { /* En eller ett? */
2584  ast_copy_string(fn, "digits/1N", sizeof(fn));
2585  num = 0;
2586  } else if (num < 20) {
2587  snprintf(fn, sizeof(fn), "digits/%d", num);
2588  num = 0;
2589  } else if (num < 100) { /* Below hundreds - teens and tens */
2590  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2591  num %= 10;
2592  } else if (num < 1000) {
2593  /* Hundreds */
2594  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2595  playh++;
2596  num %= 100;
2597  } else if (num < 1000000) { /* 1,000,000 */
2598  /* Always say "ett hundra tusen", not "en hundra tusen" */
2599  res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2600  if (res) {
2601  return res;
2602  }
2603  num %= 1000;
2604  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2605  } else if (num < 1000000000) { /* 1,000,000,000 */
2606  /* Always say "en miljon", not "ett miljon" */
2607  res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2608  if (res) {
2609  return res;
2610  }
2611  num %= 1000000;
2612  ast_copy_string(fn, "digits/million", sizeof(fn));
2613  } else { /* Miljarder - Billions */
2614  ast_debug(1, "Number '%d' is too big for me\n", num);
2615  return -1;
2616  }
2617 
2618  if (!ast_streamfile(chan, fn, language)) {
2619  if ((audiofd > -1) && (ctrlfd > -1)) {
2620  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2621  } else {
2622  res = ast_waitstream(chan, ints);
2623  }
2624  ast_stopstream(chan);
2625  if (res) {
2626  return res;
2627  }
2628  }
2629  start = 0;
2630  }
2631  return 0;
2632 }
2633 
2634 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2635 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2636 {
2637  int res = 0;
2638  int playh = 0;
2639  int playt = 0;
2640  int playz = 0;
2641  int last_length = 0;
2642  char buf[20] = "";
2643  char fn[256] = "";
2644  if (!num)
2645  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2646 
2647  while (!res && (num || playh || playt || playz)) {
2648  if (num < 0) {
2649  ast_copy_string(fn, "digits/minus", sizeof(fn));
2650  if ( num > INT_MIN ) {
2651  num = -num;
2652  } else {
2653  num = 0;
2654  }
2655  } else if (playz) {
2656  snprintf(fn, sizeof(fn), "digits/0");
2657  last_length = 0;
2658  playz = 0;
2659  } else if (playh) {
2660  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2661  playh = 0;
2662  } else if (playt) {
2663  snprintf(fn, sizeof(fn), "digits/thousand");
2664  playt = 0;
2665  } else if (num < 10) {
2666  snprintf(buf, 12, "%d", num);
2667  if (last_length - strlen(buf) > 1 && last_length != 0) {
2668  last_length = strlen(buf);
2669  playz++;
2670  continue;
2671  }
2672  snprintf(fn, sizeof(fn), "digits/%d", num);
2673  num = 0;
2674  } else if (num < 100) {
2675  snprintf(buf, 10, "%d", num);
2676  if (last_length - strlen(buf) > 1 && last_length != 0) {
2677  last_length = strlen(buf);
2678  playz++;
2679  continue;
2680  }
2681  last_length = strlen(buf);
2682  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2683  num %= 10;
2684  } else {
2685  if (num < 1000){
2686  snprintf(buf, 10, "%d", num);
2687  if (last_length - strlen(buf) > 1 && last_length != 0) {
2688  last_length = strlen(buf);
2689  playz++;
2690  continue;
2691  }
2692  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2693  playh++;
2694  snprintf(buf, 10, "%d", num);
2695  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2696  last_length = strlen(buf);
2697  num -= ((num / 100) * 100);
2698  } else if (num < 10000){
2699  snprintf(buf, 10, "%d", num);
2700  snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2701  playt++;
2702  snprintf(buf, 10, "%d", num);
2703  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2704  last_length = strlen(buf);
2705  num -= ((num / 1000) * 1000);
2706  } else if (num < 100000000) { /* 100,000,000 */
2707  res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2708  if (res)
2709  return res;
2710  snprintf(buf, 10, "%d", num);
2711  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2712  num -= ((num / 10000) * 10000);
2713  last_length = strlen(buf);
2714  snprintf(fn, sizeof(fn), "digits/wan");
2715  } else {
2716  if (num < 1000000000) { /* 1,000,000,000 */
2717  res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2718  if (res)
2719  return res;
2720  snprintf(buf, 10, "%d", num);
2721  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2722  last_length = strlen(buf);
2723  num -= ((num / 100000000) * 100000000);
2724  snprintf(fn, sizeof(fn), "digits/yi");
2725  } else {
2726  ast_debug(1, "Number '%d' is too big for me\n", num);
2727  res = -1;
2728  }
2729  }
2730  }
2731  if (!res) {
2732  if (!ast_streamfile(chan, fn, language)) {
2733  if ((audiofd > -1) && (ctrlfd > -1))
2734  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2735  else
2736  res = ast_waitstream(chan, ints);
2737  }
2738  ast_stopstream(chan);
2739  }
2740  }
2741  return res;
2742 }
2743 
2744 /*!\internal
2745  * \brief Counting in Urdu, the national language of Pakistan
2746  * \since 1.8
2747  */
2748 static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2749 {
2750  int res = 0;
2751  int playh = 0;
2752  char fn[256] = "";
2753 
2754  if (!num) {
2755  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2756  }
2757 
2758  while (!res && (num || playh)) {
2759  if (playh) {
2760  snprintf(fn, sizeof(fn), "digits/hundred");
2761  playh = 0;
2762  } else if (num < 100) {
2763  snprintf(fn, sizeof(fn), "digits/%d", num);
2764  num = 0;
2765  } else if (num < 1000) {
2766  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2767  playh++;
2768  num -= ((num / 100) * 100);
2769  } else if (num < 100000) { /* 1,00,000 */
2770  if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2771  return res;
2772  }
2773  num = num % 1000;
2774  snprintf(fn, sizeof(fn), "digits/thousand");
2775  } else if (num < 10000000) { /* 1,00,00,000 */
2776  if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2777  return res;
2778  }
2779  num = num % 100000;
2780  snprintf(fn, sizeof(fn), "digits/lac");
2781  } else if (num < 1000000000) { /* 1,00,00,00,000 */
2782  if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2783  return res;
2784  }
2785  num = num % 10000000;
2786  snprintf(fn, sizeof(fn), "digits/crore");
2787  } else {
2788  ast_debug(1, "Number '%d' is too big for me\n", num);
2789  res = -1;
2790  }
2791 
2792  if (!res) {
2793  if (!ast_streamfile(chan, fn, language)) {
2794  if ((audiofd > -1) && (ctrlfd > -1)) {
2795  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2796  } else {
2797  res = ast_waitstream(chan, ints);
2798  }
2799  }
2800  ast_stopstream(chan);
2801  }
2802  }
2803  return res;
2804 }
2805 
2806 /*! \brief determine last digits for thousands/millions (ru) */
2807 static int get_lastdigits_ru(int num) {
2808  if (num < 20) {
2809  return num;
2810  } else if (num < 100) {
2811  return get_lastdigits_ru(num % 10);
2812  } else if (num < 1000) {
2813  return get_lastdigits_ru(num % 100);
2814  }
2815  return 0; /* number too big */
2816 }
2817 
2818 
2819 /*! \brief ast_say_number_full_ru: Russian syntax
2820 
2821  additional files:
2822  n00.gsm (one hundred, two hundred, ...)
2823  thousand.gsm
2824  million.gsm
2825  thousands-i.gsm (tisyachi)
2826  million-a.gsm (milliona)
2827  thousands.gsm
2828  millions.gsm
2829  1f.gsm (odna)
2830  2f.gsm (dve)
2831 
2832  where 'n' from 1 to 9
2833 */
2834 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)
2835 {
2836  int res = 0;
2837  int lastdigits = 0;
2838  char fn[256] = "";
2839  if (!num)
2840  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2841 
2842  while (!res && (num)) {
2843  if (num < 0) {
2844  ast_copy_string(fn, "digits/minus", sizeof(fn));
2845  if ( num > INT_MIN ) {
2846  num = -num;
2847  } else {
2848  num = 0;
2849  }
2850  } else if (num < 20) {
2851  if (options && strlen(options) == 1 && num < 3) {
2852  snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2853  } else {
2854  snprintf(fn, sizeof(fn), "digits/%d", num);
2855  }
2856  num = 0;
2857  } else if (num < 100) {
2858  snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2859  num %= 10;
2860  } else if (num < 1000){
2861  snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2862  num %= 100;
2863  } else if (num < 1000000) { /* 1,000,000 */
2864  lastdigits = get_lastdigits_ru(num / 1000);
2865  /* say thousands */
2866  if (lastdigits < 3) {
2867  res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2868  } else {
2869  res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2870  }
2871  if (res)
2872  return res;
2873  if (lastdigits == 1) {
2874  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2875  } else if (lastdigits > 1 && lastdigits < 5) {
2876  ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2877  } else {
2878  ast_copy_string(fn, "digits/thousands", sizeof(fn));
2879  }
2880  num %= 1000;
2881  } else if (num < 1000000000) { /* 1,000,000,000 */
2882  lastdigits = get_lastdigits_ru(num / 1000000);
2883  /* say millions */
2884  res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2885  if (res)
2886  return res;
2887  if (lastdigits == 1) {
2888  ast_copy_string(fn, "digits/million", sizeof(fn));
2889  } else if (lastdigits > 1 && lastdigits < 5) {
2890  ast_copy_string(fn, "digits/million-a", sizeof(fn));
2891  } else {
2892  ast_copy_string(fn, "digits/millions", sizeof(fn));
2893  }
2894  num %= 1000000;
2895  } else {
2896  ast_debug(1, "Number '%d' is too big for me\n", num);
2897  res = -1;
2898  }
2899  if (!res) {
2900  if (!ast_streamfile(chan, fn, language)) {
2901  if ((audiofd > -1) && (ctrlfd > -1))
2902  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2903  else
2904  res = ast_waitstream(chan, ints);
2905  }
2906  ast_stopstream(chan);
2907  }
2908  }
2909  return res;
2910 }
2911 
2912 /*! \brief Thai syntax */
2913 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2914 {
2915  int res = 0;
2916  int playh = 0;
2917  char fn[256] = "";
2918  if (!num)
2919  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2920 
2921  while(!res && (num || playh)) {
2922  if (num < 0) {
2923  ast_copy_string(fn, "digits/lop", sizeof(fn));
2924  if ( num > INT_MIN ) {
2925  num = -num;
2926  } else {
2927  num = 0;
2928  }
2929  } else if (playh) {
2930  ast_copy_string(fn, "digits/roi", sizeof(fn));
2931  playh = 0;
2932  } else if (num < 100) {
2933  if ((num <= 20) || ((num % 10) == 1)) {
2934  snprintf(fn, sizeof(fn), "digits/%d", num);
2935  num = 0;
2936  } else {
2937  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2938  num %= 10;
2939  }
2940  } else if (num < 1000) {
2941  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2942  playh++;
2943  num %= 100;
2944  } else if (num < 10000) { /* 10,000 */
2945  res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2946  if (res)
2947  return res;
2948  num %= 1000;
2949  ast_copy_string(fn, "digits/pan", sizeof(fn));
2950  } else if (num < 100000) { /* 100,000 */
2951  res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2952  if (res)
2953  return res;
2954  num %= 10000;
2955  ast_copy_string(fn, "digits/muan", sizeof(fn));
2956  } else if (num < 1000000) { /* 1,000,000 */
2957  res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2958  if (res)
2959  return res;
2960  num %= 100000;
2961  ast_copy_string(fn, "digits/san", sizeof(fn));
2962  } else {
2963  res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2964  if (res)
2965  return res;
2966  num %= 1000000;
2967  ast_copy_string(fn, "digits/larn", sizeof(fn));
2968  }
2969  if (!res) {
2970  if(!ast_streamfile(chan, fn, language)) {
2971  if ((audiofd > -1) && (ctrlfd > -1))
2972  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2973  else
2974  res = ast_waitstream(chan, ints);
2975  }
2976  ast_stopstream(chan);
2977  }
2978  }
2979  return res;
2980 }
2981 
2982 /*! \brief ast_say_number_full_vi: Vietnamese syntax */
2983 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2984 {
2985  int res = 0;
2986  int playh = 0;
2987  int playoh = 0;
2988  int playohz = 0;
2989  int playz = 0;
2990  int playl = 0;
2991  char fn[256] = "";
2992  if (!num)
2993  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2994  while (!res && (num || playh)) {
2995  if (num < 0) {
2996  ast_copy_string(fn, "digits/minus", sizeof(fn));
2997  if ( num > INT_MIN ) {
2998  num = -num;
2999  } else {
3000  num = 0;
3001  }
3002  } else if (playl) {
3003  snprintf(fn, sizeof(fn), "digits/%da", num);
3004  playl = 0;
3005  num = 0;
3006  } else if (playh) {
3007  ast_copy_string(fn, "digits/hundred", sizeof(fn));
3008  playh = 0;
3009  } else if (playz) {
3010  ast_copy_string(fn, "digits/odd", sizeof(fn));
3011  playz = 0;
3012  } else if (playoh) {
3013  ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
3014  playoh = 0;
3015  } else if (playohz) {
3016  ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
3017  playohz = 0;
3018  } else if (num < 20) {
3019  snprintf(fn, sizeof(fn), "digits/%d", num);
3020  num = 0;
3021  } else if (num < 100) {
3022  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
3023  num %= 10;
3024  if ((num == 5) || (num == 4) || (num == 1)) playl++;
3025  } else {
3026  if (num < 1000) {
3027  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3028  num %= 100;
3029  if (num && (num < 10)) {
3030  playz++;
3031  playh++;
3032  } else {
3033  playh++;
3034  }
3035  } else {
3036  if (num < 1000000) { /* 1,000,000 */
3037  res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
3038  if (res)
3039  return res;
3040  num %= 1000;
3041  snprintf(fn, sizeof(fn), "digits/thousand");
3042  if (num && (num < 10)) {
3043  playohz++;
3044  } else if (num && (num < 100)){
3045  playoh++;
3046  } else {
3047  playh = 0;
3048  playohz = 0;
3049  playoh = 0;
3050  }
3051  } else {
3052  if (num < 1000000000) { /* 1,000,000,000 */
3053  res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3054  if (res)
3055  return res;
3056  num %= 1000000;
3057  ast_copy_string(fn, "digits/million", sizeof(fn));
3058  } else {
3059  res = -1;
3060  }
3061  }
3062  }
3063  }
3064  if (!res) {
3065  if (!ast_streamfile(chan, fn, language)) {
3066  if ((audiofd > -1) && (ctrlfd > -1))
3067  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3068  else
3069  res = ast_waitstream(chan, ints);
3070  }
3071  ast_stopstream(chan);
3072  }
3073  }
3074  return res;
3075 }
3076 
3077 /*! \brief ast_say_enumeration_full: call language-specific functions
3078  * \note Called from AGI */
3079 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3080 {
3081  if (!strncasecmp(language, "en", 2)) { /* English syntax */
3082  return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3083  } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
3084  return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
3085  } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
3086  return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
3087  } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
3088  return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
3089  } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
3090  return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
3091  } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
3092  return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3093  }
3094 
3095  /* Default to english */
3096  return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3097 }
3098 
3099 /*! \brief ast_say_enumeration_full_en: English syntax
3100  \note This is the default syntax, if no other syntax defined in this file is used */
3101 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3102 {
3103  int res = 0, t = 0;
3104  char fn[256] = "";
3105 
3106  while (!res && num) {
3107  if (num < 0) {
3108  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3109  if ( num > INT_MIN ) {
3110  num = -num;
3111  } else {
3112  num = 0;
3113  }
3114  } else if (num < 20) {
3115  snprintf(fn, sizeof(fn), "digits/h-%d", num);
3116  num = 0;
3117  } else if (num < 100) {
3118  int tens = num / 10;
3119  num = num % 10;
3120  if (num == 0) {
3121  snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3122  } else {
3123  snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3124  }
3125  } else if (num < 1000) {
3126  int hundreds = num / 100;
3127  num = num % 100;
3128  if (hundreds > 1 || t == 1) {
3129  res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3130  }
3131  if (res)
3132  return res;
3133  if (num) {
3134  ast_copy_string(fn, "digits/hundred", sizeof(fn));
3135  } else {
3136  ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3137  }
3138  } else if (num < 1000000) {
3139  int thousands = num / 1000;
3140  num = num % 1000;
3141  if (thousands > 1 || t == 1) {
3142  res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3143  }
3144  if (res)
3145  return res;
3146  if (num) {
3147  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3148  } else {
3149  ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3150  }
3151  t = 1;
3152  } else if (num < 1000000000) {
3153  int millions = num / 1000000;
3154  num = num % 1000000;
3155  t = 1;
3156  res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3157  if (res)
3158  return res;
3159  if (num) {
3160  ast_copy_string(fn, "digits/million", sizeof(fn));
3161  } else {
3162  ast_copy_string(fn, "digits/h-million", sizeof(fn));
3163  }
3164  } else if (num < INT_MAX) {
3165  int billions = num / 1000000000;
3166  num = num % 1000000000;
3167  t = 1;
3168  res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3169  if (res)
3170  return res;
3171  if (num) {
3172  ast_copy_string(fn, "digits/billion", sizeof(fn));
3173  } else {
3174  ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3175  }
3176  } else if (num == INT_MAX) {
3177  ast_copy_string(fn, "digits/h-last", sizeof(fn));
3178  num = 0;
3179  } else {
3180  ast_debug(1, "Number '%d' is too big for me\n", num);
3181  res = -1;
3182  }
3183 
3184  if (!res) {
3185  if (!ast_streamfile(chan, fn, language)) {
3186  if ((audiofd > -1) && (ctrlfd > -1)) {
3187  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3188  } else {
3189  res = ast_waitstream(chan, ints);
3190  }
3191  }
3192  ast_stopstream(chan);
3193  }
3194  }
3195  return res;
3196 }
3197 
3198 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3199 {
3200  int res = 0;
3201  char fn[256] = "";
3202  ast_copy_string(fn, "digits/h", sizeof(fn));
3203  if (!res) {
3204  if (!ast_streamfile(chan, fn, language)) {
3205  if ((audiofd > -1) && (ctrlfd > -1)) {
3206  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3207  } else {
3208  res = ast_waitstream(chan, ints);
3209  }
3210  }
3211  ast_stopstream(chan);
3212  }
3213 
3214  return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3215 }
3216 
3217 /*! \brief ast_say_enumeration_full_da: Danish syntax */
3218 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)
3219 {
3220  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3221  int res = 0, t = 0;
3222  char fn[256] = "", fna[256] = "";
3223  char *gender;
3224 
3225  if (options && !strncasecmp(options, "f", 1)) {
3226  gender = "F";
3227  } else if (options && !strncasecmp(options, "n", 1)) {
3228  gender = "N";
3229  } else {
3230  gender = "";
3231  }
3232 
3233  if (!num)
3234  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3235 
3236  while (!res && num) {
3237  if (num < 0) {
3238  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3239  if ( num > INT_MIN ) {
3240  num = -num;
3241  } else {
3242  num = 0;
3243  }
3244  } else if (num < 100 && t) {
3245  ast_copy_string(fn, "digits/and", sizeof(fn));
3246  t = 0;
3247  } else if (num < 20) {
3248  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3249  num = 0;
3250  } else if (num < 100) {
3251  int ones = num % 10;
3252  if (ones) {
3253  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3254  num -= ones;
3255  } else {
3256  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3257  num = 0;
3258  }
3259  } else if (num == 100 && t == 0) {
3260  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3261  num = 0;
3262  } else if (num < 1000) {
3263  int hundreds = num / 100;
3264  num = num % 100;
3265  if (hundreds == 1) {
3266  ast_copy_string(fn, "digits/1N", sizeof(fn));
3267  } else {
3268  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3269  }
3270  if (num) {
3271  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3272  } else {
3273  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3274  }
3275  t = 1;
3276  } else if (num < 1000000) {
3277  int thousands = num / 1000;
3278  num = num % 1000;
3279  if (thousands == 1) {
3280  if (num) {
3281  ast_copy_string(fn, "digits/1N", sizeof(fn));
3282  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3283  } else {
3284  if (t) {
3285  ast_copy_string(fn, "digits/1N", sizeof(fn));
3286  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3287  } else {
3288  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3289  }
3290  }
3291  } else {
3292  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3293  if (res) {
3294  return res;
3295  }
3296  if (num) {
3297  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3298  } else {
3299  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3300  }
3301  }
3302  t = 1;
3303  } else if (num < 1000000000) {
3304  int millions = num / 1000000;
3305  num = num % 1000000;
3306  if (millions == 1) {
3307  if (num) {
3308  ast_copy_string(fn, "digits/1F", sizeof(fn));
3309  ast_copy_string(fna, "digits/million", sizeof(fna));
3310  } else {
3311  ast_copy_string(fn, "digits/1N", sizeof(fn));
3312  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3313  }
3314  } else {
3315  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3316  if (res) {
3317  return res;
3318  }
3319  if (num) {
3320  ast_copy_string(fn, "digits/millions", sizeof(fn));
3321  } else {
3322  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3323  }
3324  }
3325  t = 1;
3326  } else if (num < INT_MAX) {
3327  int billions = num / 1000000000;
3328  num = num % 1000000000;
3329  if (billions == 1) {
3330  if (num) {
3331  ast_copy_string(fn, "digits/1F", sizeof(fn));
3332  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3333  } else {
3334  ast_copy_string(fn, "digits/1N", sizeof(fn));
3335  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3336  }
3337  } else {
3338  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3339  if (res)
3340  return res;
3341  if (num) {
3342  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3343  } else {
3344  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3345  }
3346  }
3347  t = 1;
3348  } else if (num == INT_MAX) {
3349  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3350  num = 0;
3351  } else {
3352  ast_debug(1, "Number '%d' is too big for me\n", num);
3353  res = -1;
3354  }
3355 
3356  if (!res) {
3357  if (!ast_streamfile(chan, fn, language)) {
3358  if ((audiofd > -1) && (ctrlfd > -1))
3359  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3360  else
3361  res = ast_waitstream(chan, ints);
3362  }
3363  ast_stopstream(chan);
3364  if (!res) {
3365  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3366  if ((audiofd > -1) && (ctrlfd > -1)) {
3367  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3368  } else {
3369  res = ast_waitstream(chan, ints);
3370  }
3371  }
3372  ast_stopstream(chan);
3373  strcpy(fna, "");
3374  }
3375  }
3376  }
3377  return res;
3378 }
3379 
3380 /*! \brief ast_say_enumeration_full_de: German syntax */
3381 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)
3382 {
3383  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3384  int res = 0, t = 0;
3385  char fn[256] = "", fna[256] = "";
3386  char *gender;
3387 
3388  if (options && !strncasecmp(options, "f", 1)) {
3389  gender = "F";
3390  } else if (options && !strncasecmp(options, "n", 1)) {
3391  gender = "N";
3392  } else {
3393  gender = "";
3394  }
3395 
3396  if (!num)
3397  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3398 
3399  while (!res && num) {
3400  if (num < 0) {
3401  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3402  if ( num > INT_MIN ) {
3403  num = -num;
3404  } else {
3405  num = 0;
3406  }
3407  } else if (num < 100 && t) {
3408  ast_copy_string(fn, "digits/and", sizeof(fn));
3409  t = 0;
3410  } else if (num < 20) {
3411  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3412  num = 0;
3413  } else if (num < 100) {
3414  int ones = num % 10;
3415  if (ones) {
3416  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3417  num -= ones;
3418  } else {
3419  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3420  num = 0;
3421  }
3422  } else if (num == 100 && t == 0) {
3423  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3424  num = 0;
3425  } else if (num < 1000) {
3426  int hundreds = num / 100;
3427  num = num % 100;
3428  if (hundreds == 1) {
3429  ast_copy_string(fn, "digits/1N", sizeof(fn));
3430  } else {
3431  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3432  }
3433  if (num) {
3434  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3435  } else {
3436  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3437  }
3438  t = 1;
3439  } else if (num < 1000000) {
3440  int thousands = num / 1000;
3441  num = num % 1000;
3442  if (thousands == 1) {
3443  if (num) {
3444  ast_copy_string(fn, "digits/1N", sizeof(fn));
3445  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3446  } else {
3447  if (t) {
3448  ast_copy_string(fn, "digits/1N", sizeof(fn));
3449  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3450  } else {
3451  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3452  }
3453  }
3454  } else {
3455  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3456  if (res) {
3457  return res;
3458  }
3459  if (num) {
3460  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3461  } else {
3462  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3463  }
3464  }
3465  t = 1;
3466  } else if (num < 1000000000) {
3467  int millions = num / 1000000;
3468  num = num % 1000000;
3469  if (millions == 1) {
3470  if (num) {
3471  ast_copy_string(fn, "digits/1F", sizeof(fn));
3472  ast_copy_string(fna, "digits/million", sizeof(fna));
3473  } else {
3474  ast_copy_string(fn, "digits/1N", sizeof(fn));
3475  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3476  }
3477  } else {
3478  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3479  if (res) {
3480  return res;
3481  }
3482  if (num) {
3483  ast_copy_string(fn, "digits/millions", sizeof(fn));
3484  } else {
3485  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3486  }
3487  }
3488  t = 1;
3489  } else if (num < INT_MAX) {
3490  int billions = num / 1000000000;
3491  num = num % 1000000000;
3492  if (billions == 1) {
3493  if (num) {
3494  ast_copy_string(fn, "digits/1F", sizeof(fn));
3495  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3496  } else {
3497  ast_copy_string(fn, "digits/1N", sizeof(fn));
3498  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3499  }
3500  } else {
3501  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3502  if (res)
3503  return res;
3504  if (num) {
3505  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3506  } else {
3507  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3508  }
3509  }
3510  t = 1;
3511  } else if (num == INT_MAX) {
3512  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3513  num = 0;
3514  } else {
3515  ast_debug(1, "Number '%d' is too big for me\n", num);
3516  res = -1;
3517  }
3518 
3519  if (!res) {
3520  if (!ast_streamfile(chan, fn, language)) {
3521  if ((audiofd > -1) && (ctrlfd > -1))
3522  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3523  else
3524  res = ast_waitstream(chan, ints);
3525  }
3526  ast_stopstream(chan);
3527  if (!res) {
3528  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3529  if ((audiofd > -1) && (ctrlfd > -1)) {
3530  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3531  } else {
3532  res = ast_waitstream(chan, ints);
3533  }
3534  }
3535  ast_stopstream(chan);
3536  strcpy(fna, "");
3537  }
3538  }
3539  }
3540  return res;
3541 }
3542 
3543 static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3544 {
3545  int res = 0;
3546  char fn[256] = "";
3547  int mf = -1; /* +1 = Masculin; -1 = Feminin */
3548  ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
3549 
3550  if (options && !strncasecmp(options, "m", 1)) {
3551  mf = -1;
3552  }
3553 
3554  ast_verb(3, "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
3555 
3556  while (!res && num) {
3557  if (num < 0) {
3558  snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
3559  if (num > INT_MIN) {
3560  num = -num;
3561  } else {
3562  num = 0;
3563  }
3564  } else if (num < 21) {
3565  if (mf < 0) {
3566  if (num < 10) {
3567  snprintf(fn, sizeof(fn), "digits/f-0%d", num);
3568  } else {
3569  snprintf(fn, sizeof(fn), "digits/f-%d", num);
3570  }
3571  } else {
3572  if (num < 10) {
3573  snprintf(fn, sizeof(fn), "digits/m-0%d", num);
3574  } else {
3575  snprintf(fn, sizeof(fn), "digits/m-%d", num);
3576  }
3577  }
3578  num = 0;
3579  } else if ((num < 100) && num >= 20) {
3580  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3581  num = num % 10;
3582  } else if ((num >= 100) && (num < 1000)) {
3583  int tmpnum = num / 100;
3584  snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
3585  num = num - (tmpnum * 100);
3586  } else if ((num >= 1000) && (num < 10000)) {
3587  int tmpnum = num / 1000;
3588  snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
3589  num = num - (tmpnum * 1000);
3590  } else if (num < 20000) {
3591  snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
3592  num = num % 1000;
3593  } else if (num < 1000000) {
3594  res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
3595  if (res) {
3596  return res;
3597  }
3598  snprintf(fn, sizeof(fn), "digits/1k");
3599  num = num % 1000;
3600  } else if (num < 2000000) {
3601  snprintf(fn, sizeof(fn), "digits/1m");
3602  num = num % 1000000;
3603  } else if (num < 3000000) {
3604  snprintf(fn, sizeof(fn), "digits/2m");
3605  num = num - 2000000;
3606  } else if (num < 1000000000) {
3607  res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
3608  if (res) {
3609  return res;
3610  }
3611  snprintf(fn, sizeof(fn), "digits/1m");
3612  num = num % 1000000;
3613  } else {
3614  ast_debug(1, "Number '%d' is too big for me\n", num);
3615  res = -1;
3616  }
3617  if (!res) {
3618  if (!ast_streamfile(chan, fn, language)) {
3619  if ((audiofd > -1) && (ctrlfd > -1)) {
3620  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3621  } else {
3622  res = ast_waitstream(chan, ints);
3623  }
3624  }
3625  ast_stopstream(chan);
3626  }
3627  }
3628  return res;
3629 }
3630 
3631 /*! \brief ast_say_enumeration_full_is: Icelandic syntax */
3632 static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3633 {
3634  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3635  int res = 0, t = 0;
3636  char fn[256] = "", fna[256] = "";
3637  char *gender;
3638 
3639  if (options && !strncasecmp(options, "f", 1)) {
3640  gender = "F";
3641  } else if (options && !strncasecmp(options, "n", 1)) {
3642  gender = "N";
3643  } else {
3644  gender = "";
3645  }
3646 
3647  if (!num)
3648  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3649 
3650  while (!res && num) {
3651  if (num < 0) {
3652  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3653  if ( num > INT_MIN ) {
3654  num = -num;
3655  } else {
3656  num = 0;
3657  }
3658  } else if (num < 100 && t) {
3659  ast_copy_string(fn, "digits/and", sizeof(fn));
3660  t = 0;
3661  } else if (num < 20) {
3662  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3663  num = 0;
3664  } else if (num < 100) {
3665  int ones = num % 10;
3666  if (ones) {
3667  int tens = num - ones;
3668  snprintf(fn, sizeof(fn), "digits/h-%d%s", tens, gender);
3669  num = ones;
3670  t++;
3671  }
3672  else if (t) {
3673  snprintf(fn, sizeof(fn), "digits/and");
3674  t = 0;
3675  }
3676  else {
3677  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3678  num = 0;
3679  }
3680 
3681  } else if (num == 100 && t == 0) {
3682  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3683  num = 0;
3684  } else if (num < 1000) {
3685  int hundreds = num / 100;
3686  num = num % 100;
3687  if (hundreds == 1) {
3688  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3689  } else {
3690  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3691  }
3692  if (num) {
3693  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3694  } else {
3695  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3696  }
3697  t = 1;
3698  } else if (num < 1000000) {
3699  int thousands = num / 1000;
3700  num = num % 1000;
3701  if (thousands == 1) {
3702  if (num) {
3703  /* Thousand is a neutral word, so use the neutral recording */
3704  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3705  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3706  } else {
3707  if (t) {
3708  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3709  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3710  } else {
3711  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3712  }
3713  }
3714  } else {
3715  res = ast_say_number_full_is(chan, thousands, ints, language, options, audiofd, ctrlfd);
3716  if (res) {
3717  return res;
3718  }
3719  if (num) {
3720  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3721  } else {
3722  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3723  }
3724  }
3725  if (num)
3726  t = 1;
3727  } else if (num < 1000000000) {
3728  int millions = num / 1000000;
3729  num = num % 1000000;
3730  if (millions == 1) {
3731  if (num) {
3732  /* Million is a feminine word, so use the female form */
3733  ast_copy_string(fn, "digits/1kvk", sizeof(fn));
3734  ast_copy_string(fna, "digits/million", sizeof(fna));
3735  } else {
3736  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3737  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3738  }
3739  } else {
3740  res = ast_say_number_full_is(chan, millions, ints, language, options, audiofd, ctrlfd);
3741  if (res) {
3742  return res;
3743  }
3744  if (num) {
3745  ast_copy_string(fn, "digits/millions", sizeof(fn));
3746  } else {
3747  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3748  }
3749  }
3750  if (num)
3751  t = 1;
3752  } else if (num < INT_MAX) {
3753  int billions = num / 1000000000;
3754  num = num % 1000000000;
3755  if (billions == 1) {
3756  if (num) {
3757  ast_copy_string(fn, "digits/1", sizeof(fn));
3758  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3759  } else {
3760  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3761  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3762  }
3763  } else {
3764  res = ast_say_number_full_is(chan, billions, ints, language, options, audiofd, ctrlfd);
3765  if (res)
3766  return res;
3767  if (num) {
3768  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3769  } else {
3770  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3771  }
3772  }
3773  if (num)
3774  t = 1;
3775  } else if (num == INT_MAX) {
3776  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3777  num = 0;
3778  } else {
3779  ast_debug(1, "Number '%d' is too big for me\n", num);
3780  res = -1;
3781  }
3782 
3783  if (!res) {
3784  if (!ast_streamfile(chan, fn, language)) {
3785  if ((audiofd > -1) && (ctrlfd > -1))
3786  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3787  else
3788  res = ast_waitstream(chan, ints);
3789  }
3790  ast_stopstream(chan);
3791  if (!res) {
3792  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3793  if ((audiofd > -1) && (ctrlfd > -1)) {
3794  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3795  } else {
3796  res = ast_waitstream(chan, ints);
3797  }
3798  }
3799  ast_stopstream(chan);
3800  strcpy(fna, "");
3801  }
3802  }
3803  }
3804  return res;
3805 }
3806 
3807 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3808 {
3809  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
3810  return ast_say_date_en(chan, t, ints, lang);
3811  } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
3812  return ast_say_date_da(chan, t, ints, lang);
3813  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
3814  return ast_say_date_de(chan, t, ints, lang);
3815  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
3816  return ast_say_date_fr(chan, t, ints, lang);
3817  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
3818  return ast_say_date_gr(chan, t, ints, lang);
3819  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
3820  return ast_say_date_ja(chan, t, ints, lang);
3821  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
3822  return ast_say_date_he(chan, t, ints, lang);
3823  } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
3824  return ast_say_date_hu(chan, t, ints, lang);
3825  } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
3826  return ast_say_date_is(chan, t, ints, lang);
3827  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
3828  return ast_say_date_ka(chan, t, ints, lang);
3829  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
3830  return ast_say_date_nl(chan, t, ints, lang);
3831  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
3832  return ast_say_date_pt(chan, t, ints, lang);
3833  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
3834  return ast_say_date_th(chan, t, ints, lang);
3835  }
3836 
3837  /* Default to English */
3838  return ast_say_date_en(chan, t, ints, lang);
3839 }
3840 
3841 /*! \brief English syntax */
3842 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3843 {
3844  struct ast_tm tm;
3845  struct timeval when = { t, 0 };
3846  char fn[256];
3847  int res = 0;
3848  ast_localtime(&when, &tm, NULL);
3849  if (!res) {
3850  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3851  res = ast_streamfile(chan, fn, lang);
3852  if (!res)
3853  res = ast_waitstream(chan, ints);
3854  }
3855  if (!res) {
3856  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3857  res = ast_streamfile(chan, fn, lang);
3858  if (!res)
3859  res = ast_waitstream(chan, ints);
3860  }
3861  if (!res)
3862  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3863  if (!res)
3864  res = ast_waitstream(chan, ints);
3865  if (!res)
3866  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3867  return res;
3868 }
3869 
3870 /*! \brief Danish syntax */
3871 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3872 {
3873  struct timeval when = { t, 0 };
3874  struct ast_tm tm;
3875  char fn[256];
3876  int res = 0;
3877  ast_localtime(&when, &tm, NULL);
3878  if (!res) {
3879  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3880  res = ast_streamfile(chan, fn, lang);
3881  if (!res)
3882  res = ast_waitstream(chan, ints);
3883  }
3884  if (!res)
3885  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3886  if (!res)
3887  res = ast_waitstream(chan, ints);
3888  if (!res) {
3889  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3890  res = ast_streamfile(chan, fn, lang);
3891  if (!res)
3892  res = ast_waitstream(chan, ints);
3893  }
3894  if (!res) {
3895  /* Year */
3896  int year = tm.tm_year + 1900;
3897  if (year > 1999) { /* year 2000 and later */
3898  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3899  } else {
3900  if (year < 1100) {
3901  /* I'm not going to handle 1100 and prior */
3902  /* We'll just be silent on the year, instead of bombing out. */
3903  } else {
3904  /* year 1100 to 1999. will anybody need this?!? */
3905  snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
3906  res = wait_file(chan, ints, fn, lang);
3907  if (!res) {
3908  res = wait_file(chan, ints, "digits/hundred", lang);
3909  if (!res && year % 100 != 0) {
3910  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3911  }
3912  }
3913  }
3914  }
3915  }
3916  return res;
3917 }
3918 
3919 /*! \brief German syntax */
3920 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3921 {
3922  struct timeval when = { t, 0 };
3923  struct ast_tm tm;
3924  char fn[256];
3925  int res = 0;
3926  ast_localtime(&when, &tm, NULL);
3927  if (!res) {
3928  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3929  res = ast_streamfile(chan, fn, lang);
3930  if (!res)
3931  res = ast_waitstream(chan, ints);
3932  }
3933  if (!res)
3934  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3935  if (!res)
3936  res = ast_waitstream(chan, ints);
3937  if (!res) {
3938  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3939  res = ast_streamfile(chan, fn, lang);
3940  if (!res)
3941  res = ast_waitstream(chan, ints);
3942  }
3943  if (!res) {
3944  /* Year */
3945  int year = tm.tm_year + 1900;
3946  if (year > 1999) { /* year 2000 and later */
3947  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3948  } else {
3949  if (year < 1100) {
3950  /* I'm not going to handle 1100 and prior */
3951  /* We'll just be silent on the year, instead of bombing out. */
3952  } else {
3953  /* year 1100 to 1999. will anybody need this?!? */
3954  /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3955  snprintf(fn, sizeof(fn), "digits/%d", (year / 100) );
3956  res = wait_file(chan, ints, fn, lang);
3957  if (!res) {
3958  res = wait_file(chan, ints, "digits/hundred", lang);
3959  if (!res && year % 100 != 0) {
3960  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3961  }
3962  }
3963  }
3964  }
3965  }
3966  return res;
3967 }
3968 
3969 /*! \brief Hungarian syntax */
3970 int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3971 {
3972  struct timeval when = { t, 0 };
3973  struct ast_tm tm;
3974  char fn[256];
3975  int res = 0;
3976  ast_localtime(&when, &tm, NULL);
3977 
3978  if (!res)
3979  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3980  if (!res)
3981  res = ast_waitstream(chan, ints);
3982  if (!res) {
3983  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3984  res = ast_streamfile(chan, fn, lang);
3985  if (!res)
3986  res = ast_waitstream(chan, ints);
3987  }
3988  if (!res)
3989  ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
3990  if (!res)
3991  res = ast_waitstream(chan, ints);
3992  if (!res) {
3993  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3994  res = ast_streamfile(chan, fn, lang);
3995  if (!res)
3996  res = ast_waitstream(chan, ints);
3997  }
3998  return res;
3999 }
4000 
4001 /*! \brief French syntax */
4002 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4003 {
4004  struct timeval when = { t, 0 };
4005  struct ast_tm tm;
4006  char fn[256];
4007  int res = 0;
4008  ast_localtime(&when, &tm, NULL);
4009  if (!res) {
4010  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4011  res = ast_streamfile(chan, fn, lang);
4012  if (!res)
4013  res = ast_waitstream(chan, ints);
4014  }
4015  if (!res)
4016  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4017  if (!res)
4018  res = ast_waitstream(chan, ints);
4019  if (!res) {
4020  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4021  res = ast_streamfile(chan, fn, lang);
4022  if (!res)
4023  res = ast_waitstream(chan, ints);
4024  }
4025  if (!res)
4026  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4027  return res;
4028 }
4029 
4030 /*! \brief Dutch syntax */
4031 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4032 {
4033  struct timeval when = { t, 0 };
4034  struct ast_tm tm;
4035  char fn[256];
4036  int res = 0;
4037  ast_localtime(&when, &tm, NULL);
4038  if (!res) {
4039  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4040  res = ast_streamfile(chan, fn, lang);
4041  if (!res)
4042  res = ast_waitstream(chan, ints);
4043  }
4044  if (!res)
4045  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4046  if (!res) {
4047  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4048  res = ast_streamfile(chan, fn, lang);
4049  if (!res)
4050  res = ast_waitstream(chan, ints);
4051  }
4052  if (!res)
4053  res = ast_waitstream(chan, ints);
4054  if (!res)
4055  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4056  return res;
4057 }
4058 
4059 /*! \brief Thai syntax */
4060 int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4061 {
4062  struct timeval when = { t, 0 };
4063  struct ast_tm tm;
4064  char fn[256];
4065  int res = 0;
4066  ast_localtime(&when, &tm, NULL);
4067  if (!res) {
4068  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4069  res = ast_streamfile(chan, fn, lang);
4070  ast_copy_string(fn, "digits/tee", sizeof(fn));
4071  res = ast_streamfile(chan, fn, lang);
4072  if (!res)
4073  res = ast_waitstream(chan, ints);
4074  }
4075  if (!res)
4076  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4077  if (!res)
4078  res = ast_waitstream(chan, ints);
4079  if (!res) {
4080  ast_copy_string(fn, "digits/duan", sizeof(fn));
4081  res = ast_streamfile(chan, fn, lang);
4082  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4083  res = ast_streamfile(chan, fn, lang);
4084  if (!res)
4085  res = ast_waitstream(chan, ints);
4086  }
4087  if (!res){
4088  ast_copy_string(fn, "digits/posor", sizeof(fn));
4089  res = ast_streamfile(chan, fn, lang);
4090  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4091  }
4092  return res;
4093 }
4094 
4095 /*! \brief Portuguese syntax */
4096 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4097 {
4098  struct timeval when = { t, 0 };
4099  struct ast_tm tm;
4100  char fn[256];
4101  int res = 0;
4102 
4103  ast_localtime(&when, &tm, NULL);
4104  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4105  if (!res)
4106  res = wait_file(chan, ints, fn, lang);
4107  if (!res)
4108  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4109  if (!res)
4110  res = wait_file(chan, ints, "digits/pt-de", lang);
4111  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4112  if (!res)
4113  res = wait_file(chan, ints, fn, lang);
4114  if (!res)
4115  res = wait_file(chan, ints, "digits/pt-de", lang);
4116  if (!res)
4117  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4118 
4119  return res;
4120 }
4121 
4122 /*! \brief Hebrew syntax */
4123 int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4124 {
4125  struct timeval when = { t, 0 };
4126  struct ast_tm tm;
4127  char fn[256];
4128  int res = 0;
4129  ast_localtime(&when, &tm, NULL);
4130  if (!res) {
4131  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4132  res = ast_streamfile(chan, fn, lang);
4133  if (!res) {
4134  res = ast_waitstream(chan, ints);
4135  }
4136  }
4137  if (!res) {
4138  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4139  res = ast_streamfile(chan, fn, lang);
4140  if (!res) {
4141  res = ast_waitstream(chan, ints);
4142  }
4143  }
4144  if (!res) {
4145  res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
4146  }
4147  if (!res) {
4148  res = ast_waitstream(chan, ints);
4149  }
4150  if (!res) {
4151  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
4152  }
4153  return res;
4154 }
4155 
4156 /* Icelandic syntax */
4157 int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4158 {
4159  struct timeval when = { t, 0 };
4160  struct ast_tm tm;
4161  char fn[256];
4162  int res = 0;
4163  ast_localtime(&when, &tm, NULL);
4164  if (!res) {
4165  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4166  res = ast_streamfile(chan, fn, lang);
4167  if (!res)
4168  res = ast_waitstream(chan, ints);
4169  }
4170  if (!res)
4171  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4172  if (!res)
4173  res = ast_waitstream(chan, ints);
4174  if (!res) {
4175  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4176  res = ast_streamfile(chan, fn, lang);
4177  if (!res)
4178  res = ast_waitstream(chan, ints);
4179  }
4180  if (!res) {
4181  /* Year */
4182  int year = tm.tm_year + 1900;
4183  if (year > 1999) { /* year 2000 and later */
4184  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4185  } else {
4186  if (year < 1100) {
4187  /* I'm not going to handle 1100 and prior */
4188  /* We'll just be silent on the year, instead of bombing out. */
4189  } else {
4190  /* year 1100 to 1999. will anybody need this?!? */
4191  snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4192  res = wait_file(chan, ints, fn, lang);
4193  if (!res) {
4194  res = wait_file(chan, ints, "digits/hundred", lang);
4195  if (!res && year % 100 != 0) {
4196  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4197  }
4198  }
4199  }
4200  }
4201  }
4202  return res;
4203 }
4204 
4205 static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4206 {
4207  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
4208  return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4209  } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
4210  return ast_say_date_with_format_da(chan, t, ints, lang, format, tzone);
4211  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
4212  return ast_say_date_with_format_de(chan, t, ints, lang, format, tzone);
4213  } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
4214  return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
4215  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
4216  return ast_say_date_with_format_he(chan, t, ints, lang, format, tzone);
4217  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
4218  return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone);
4219  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
4220  return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone);
4221  } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
4222  return ast_say_date_with_format_is(chan, t, ints, lang, format, tzone);
4223  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
4224  return ast_say_date_with_format_ja(chan, t, ints, lang, format, tzone);
4225  } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
4226  return ast_say_date_with_format_it(chan, t, ints, lang, format, tzone);
4227  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
4228  return ast_say_date_with_format_nl(chan, t, ints, lang, format, tzone);
4229  } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
4230  return ast_say_date_with_format_pl(chan, t, ints, lang, format, tzone);
4231  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
4232  return ast_say_date_with_format_pt(chan, t, ints, lang, format, tzone);
4233  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
4234  return ast_say_date_with_format_th(chan, t, ints, lang, format, tzone);
4235  } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
4236  return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
4237  } else if (!strncasecmp(lang, "vi", 2)) { /* Vietnamese syntax */
4238  return ast_say_date_with_format_vi(chan, t, ints, lang, format, tzone);
4239  }
4240 
4241  /* Default to English */
4242  return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4243 }
4244 
4245 /*! \brief English syntax */
4246 int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4247 {
4248  struct timeval when = { t, 0 };
4249  struct ast_tm tm;
4250  int res=0, offset, sndoffset;
4251  char sndfile[256], nextmsg[256];
4252 
4253  if (format == NULL)
4254  format = "ABdY 'digits/at' IMp";
4255 
4256  ast_localtime(&when, &tm, tzone);
4257 
4258  for (offset=0 ; format[offset] != '\0' ; offset++) {
4259  ast_debug(1, "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  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4265  sndfile[sndoffset] = format[offset];
4266  }
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  /* Month enumerated */
4285  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
4286  break;
4287  case 'd':
4288  case 'e':
4289  /* First - Thirtyfirst */
4290  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
4291  break;
4292  case 'Y':
4293  /* Year */
4294  if (tm.tm_year > 99) {
4295  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4296  } else if (tm.tm_year < 1) {
4297  /* I'm not going to handle 1900 and prior */
4298  /* We'll just be silent on the year, instead of bombing out. */
4299  } else {
4300  res = wait_file(chan, ints, "digits/19", lang);
4301  if (!res) {
4302  if (tm.tm_year <= 9) {
4303  /* 1901 - 1909 */
4304  res = wait_file(chan, ints, "digits/oh", lang);
4305  }
4306 
4307  res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
4308  }
4309  }
4310  break;
4311  case 'I':
4312  case 'l':
4313  /* 12-Hour */
4314  if (tm.tm_hour == 0)
4315  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4316  else if (tm.tm_hour > 12)
4317  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4318  else
4319  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4320  res = wait_file(chan, ints, nextmsg, lang);
4321  break;
4322  case 'H':
4323  case 'k':
4324  /* 24-Hour */
4325  if (format[offset] == 'H') {
4326  /* e.g. oh-eight */
4327  if (tm.tm_hour < 10) {
4328  res = wait_file(chan, ints, "digits/oh", lang);
4329  }
4330  } else {
4331  /* e.g. eight */
4332  if (tm.tm_hour == 0) {
4333  res = wait_file(chan, ints, "digits/oh", lang);
4334  }
4335  }
4336  if (!res) {
4337  if (tm.tm_hour != 0) {
4338  int remaining = tm.tm_hour;
4339  if (tm.tm_hour > 20) {
4340  res = wait_file(chan, ints, "digits/20", lang);
4341  remaining -= 20;
4342  }
4343  if (!res) {
4344  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
4345  res = wait_file(chan, ints, nextmsg, lang);
4346  }
4347  }
4348  }
4349  break;
4350  case 'M':
4351  case 'N':
4352  /* Minute */
4353  if (tm.tm_min == 0) {
4354  if (format[offset] == 'M') {
4355  res = wait_file(chan, ints, "digits/oclock", lang);
4356  } else {
4357  res = wait_file(chan, ints, "digits/hundred", lang);
4358  }
4359  } else if (tm.tm_min < 10) {
4360  res = wait_file(chan, ints, "digits/oh", lang);
4361  if (!res) {
4362  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
4363  res = wait_file(chan, ints, nextmsg, lang);
4364  }
4365  } else {
4366  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4367  }
4368  break;
4369  case 'P':
4370  case 'p':
4371  /* AM/PM */
4372  if (tm.tm_hour > 11)
4373  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4374  else
4375  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4376  res = wait_file(chan, ints, nextmsg, lang);
4377  break;
4378  case 'Q':
4379  /* Shorthand for "Today", "Yesterday", or ABdY */
4380  /* XXX As emphasized elsewhere, this should the native way in your
4381  * language to say the date, with changes in what you say, depending
4382  * upon how recent the date is. XXX */
4383  {
4384  struct timeval now = ast_tvnow();
4385  struct ast_tm tmnow;
4386  time_t beg_today;
4387 
4388  gettimeofday(&now, NULL);
4389  ast_localtime(&now, &tmnow, tzone);
4390  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4391  /* In any case, it saves not having to do ast_mktime() */
4392  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4393  if (beg_today < t) {
4394  /* Today */
4395  res = wait_file(chan, ints, "digits/today", lang);
4396  } else if (beg_today - 86400 < t) {
4397  /* Yesterday */
4398  res = wait_file(chan, ints, "digits/yesterday", lang);
4399  } else if (beg_today - 86400 * 6 < t) {
4400  /* Within the last week */
4401  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4402  } else if (beg_today - 2628000 < t) {
4403  /* Less than a month ago - "Sunday, October third" */
4404  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4405  } else if (beg_today - 15768000 < t) {
4406  /* Less than 6 months ago - "August seventh" */
4407  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4408  } else {
4409  /* More than 6 months ago - "April nineteenth two thousand three" */
4410  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4411  }
4412  }
4413  break;
4414  case 'q':
4415  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4416  /* XXX As emphasized elsewhere, this should the native way in your
4417  * language to say the date, with changes in what you say, depending
4418  * upon how recent the date is. XXX */
4419  {
4420  struct timeval now;
4421  struct ast_tm tmnow;
4422  time_t beg_today;
4423 
4424  now = ast_tvnow();
4425  ast_localtime(&now, &tmnow, tzone);
4426  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4427  /* In any case, it saves not having to do ast_mktime() */
4428  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4429  if (beg_today < t) {
4430  /* Today */
4431  } else if ((beg_today - 86400) < t) {
4432  /* Yesterday */
4433  res = wait_file(chan, ints, "digits/yesterday", lang);
4434  } else if (beg_today - 86400 * 6 < t) {
4435  /* Within the last week */
4436  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4437  } else if (beg_today - 2628000 < t) {
4438  /* Less than a month ago - "Sunday, October third" */
4439  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4440  } else if (beg_today - 15768000 < t) {
4441  /* Less than 6 months ago - "August seventh" */
4442  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4443  } else {
4444  /* More than 6 months ago - "April nineteenth two thousand three" */
4445  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4446  }
4447  }
4448  break;
4449  case 'R':
4450  res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
4451  break;
4452  case 'S':
4453  /* Seconds */
4454  if (tm.tm_sec == 0) {
4455  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4456  res = wait_file(chan, ints, nextmsg, lang);
4457  } else if (tm.tm_sec < 10) {
4458  res = wait_file(chan, ints, "digits/oh", lang);
4459  if (!res) {
4460  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4461  res = wait_file(chan, ints, nextmsg, lang);
4462  }
4463  } else {
4464  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4465  }
4466  break;
4467  case 'T':
4468  res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
4469  break;
4470  case ' ':
4471  case ' ':
4472  /* Just ignore spaces and tabs */
4473  break;
4474  default:
4475  /* Unknown character */
4476  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4477  }
4478  /* Jump out on DTMF */
4479  if (res) {
4480  break;
4481  }
4482  }
4483  return res;
4484 }
4485 
4486 static char next_item(const char *format)
4487 {
4488  const char *next = ast_skip_blanks(format);
4489  return *next;
4490 }
4491 
4492 /*! \brief Danish syntax */
4493 int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4494 {
4495  struct timeval when = { t, 0 };
4496  struct ast_tm tm;
4497  int res=0, offset, sndoffset;
4498  char sndfile[256], nextmsg[256];
4499 
4500  if (!format)
4501  format = "A dBY HMS";
4502 
4503  ast_localtime(&when, &tm, tzone);
4504 
4505  for (offset=0 ; format[offset] != '\0' ; offset++) {
4506  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4507  switch (format[offset]) {
4508  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4509  case '\'':
4510  /* Literal name of a sound file */
4511  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4512  sndfile[sndoffset] = format[offset];
4513  }
4514  sndfile[sndoffset] = '\0';
4515  res = wait_file(chan, ints, sndfile, lang);
4516  break;
4517  case 'A':
4518  case 'a':
4519  /* Sunday - Saturday */
4520  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4521  res = wait_file(chan, ints, nextmsg, lang);
4522  break;
4523  case 'B':
4524  case 'b':
4525  case 'h':
4526  /* January - December */
4527  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4528  res = wait_file(chan, ints, nextmsg, lang);
4529  break;
4530  case 'm':
4531  /* Month enumerated */
4532  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4533  break;
4534  case 'd':
4535  case 'e':
4536  /* First - Thirtyfirst */
4537  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4538  break;
4539  case 'Y':
4540  /* Year */
4541  {
4542  int year = tm.tm_year + 1900;
4543  if (year > 1999) { /* year 2000 and later */
4544  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4545  } else {
4546  if (year < 1100) {
4547  /* I'm not going to handle 1100 and prior */
4548  /* We'll just be silent on the year, instead of bombing out. */
4549  } else {
4550  /* year 1100 to 1999. will anybody need this?!? */
4551  /* say 1967 as 'nineteen hundred seven and sixty' */
4552  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4553  res = wait_file(chan, ints, nextmsg, lang);
4554  if (!res) {
4555  res = wait_file(chan, ints, "digits/hundred", lang);
4556  if (!res && year % 100 != 0) {
4557  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4558  }
4559  }
4560  }
4561  }
4562  }
4563  break;
4564  case 'I':
4565  case 'l':
4566  /* 12-Hour */
4567  res = wait_file(chan, ints, "digits/oclock", lang);
4568  if (tm.tm_hour == 0)
4569  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4570  else if (tm.tm_hour > 12)
4571  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4572  else
4573  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4574  if (!res) {
4575  res = wait_file(chan, ints, nextmsg, lang);
4576  }
4577  break;
4578  case 'H':
4579  /* 24-Hour, single digit hours preceded by "oh" (0) */
4580  if (tm.tm_hour < 10 && tm.tm_hour > 0) {
4581  res = wait_file(chan, ints, "digits/0", lang);
4582  }
4583  /* FALLTRHU */
4584  case 'k':
4585  /* 24-Hour */
4586  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4587  break;
4588  case 'M':
4589  /* Minute */
4590  if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4591  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4592  }
4593  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4594  if (tm.tm_min == 1) {
4595  res = wait_file(chan, ints, "minute", lang);
4596  } else {
4597  res = wait_file(chan, ints, "minutes", lang);
4598  }
4599  }
4600  break;
4601  case 'P':
4602  case 'p':
4603  /* AM/PM */
4604  if (tm.tm_hour > 11)
4605  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4606  else
4607  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4608  res = wait_file(chan, ints, nextmsg, lang);
4609  break;
4610  case 'Q':
4611  /* Shorthand for "Today", "Yesterday", or AdBY */
4612  /* XXX As emphasized elsewhere, this should the native way in your
4613  * language to say the date, with changes in what you say, depending
4614  * upon how recent the date is. XXX */
4615  {
4616  struct timeval now = ast_tvnow();
4617  struct ast_tm tmnow;
4618  time_t beg_today;
4619 
4620  ast_localtime(&now, &tmnow, tzone);
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 = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4624  if (beg_today < t) {
4625  /* Today */
4626  res = wait_file(chan, ints, "digits/today", lang);
4627  } else if (beg_today - 86400 < t) {
4628  /* Yesterday */
4629  res = wait_file(chan, ints, "digits/yesterday", lang);
4630  } else {
4631  res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4632  }
4633  }
4634  break;
4635  case 'q':
4636  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4637  /* XXX As emphasized elsewhere, this should the native way in your
4638  * language to say the date, with changes in what you say, depending
4639  * upon how recent the date is. XXX */
4640  {
4641  struct timeval now = ast_tvnow();
4642  struct ast_tm tmnow;
4643  time_t beg_today;
4644 
4645  ast_localtime(&now, &tmnow, tzone);
4646  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4647  /* In any case, it saves not having to do ast_mktime() */
4648  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4649  if (beg_today < t) {
4650  /* Today */
4651  } else if ((beg_today - 86400) < t) {
4652  /* Yesterday */
4653  res = wait_file(chan, ints, "digits/yesterday", lang);
4654  } else if (beg_today - 86400 * 6 < t) {
4655  /* Within the last week */
4656  res = ast_say_date_with_format_da(chan, t, ints, lang, "A", tzone);
4657  } else {
4658  res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4659  }
4660  }
4661  break;
4662  case 'R':
4663  res = ast_say_date_with_format_da(chan, t, ints, lang, "HM", tzone);
4664  break;
4665  case 'S':
4666  /* Seconds */
4667  res = wait_file(chan, ints, "digits/and", lang);
4668  if (!res) {
4669  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
4670  if (!res) {
4671  res = wait_file(chan, ints, "seconds", lang);
4672  }
4673  }
4674  break;
4675  case 'T':
4676  res = ast_say_date_with_format_da(chan, t, ints, lang, "HMS", tzone);
4677  break;
4678  case ' ':
4679  case ' ':
4680  /* Just ignore spaces and tabs */
4681  break;
4682  default:
4683  /* Unknown character */
4684  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4685  }
4686  /* Jump out on DTMF */
4687  if (res) {
4688  break;
4689  }
4690  }
4691  return res;
4692 }
4693 
4694 /*! \brief German syntax */
4695 int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4696 {
4697  struct timeval when = { t, 0 };
4698  struct ast_tm tm;
4699  int res=0, offset, sndoffset;
4700  char sndfile[256], nextmsg[256];
4701 
4702  if (!format)
4703  format = "A dBY HMS";
4704 
4705  ast_localtime(&when, &tm, tzone);
4706 
4707  for (offset=0 ; format[offset] != '\0' ; offset++) {
4708  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4709  switch (format[offset]) {
4710  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4711  case '\'':
4712  /* Literal name of a sound file */
4713  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4714  sndfile[sndoffset] = format[offset];
4715  }
4716  sndfile[sndoffset] = '\0';
4717  res = wait_file(chan, ints, sndfile, lang);
4718  break;
4719  case 'A':
4720  case 'a':
4721  /* Sunday - Saturday */
4722  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4723  res = wait_file(chan, ints, nextmsg, lang);
4724  break;
4725  case 'B':
4726  case 'b':
4727  case 'h':
4728  /* January - December */
4729  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4730  res = wait_file(chan, ints, nextmsg, lang);
4731  break;
4732  case 'm':
4733  /* Month enumerated */
4734  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4735  break;
4736  case 'd':
4737  case 'e':
4738  /* First - Thirtyfirst */
4739  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4740  break;
4741  case 'Y':
4742  /* Year */
4743  {
4744  int year = tm.tm_year + 1900;
4745  if (year > 1999) { /* year 2000 and later */
4746  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4747  } else {
4748  if (year < 1100) {
4749  /* I'm not going to handle 1100 and prior */
4750  /* We'll just be silent on the year, instead of bombing out. */
4751  } else {
4752  /* year 1100 to 1999. will anybody need this?!? */
4753  /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4754  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4755  res = wait_file(chan, ints, nextmsg, lang);
4756  if (!res) {
4757  res = wait_file(chan, ints, "digits/hundred", lang);
4758  if (!res && year % 100 != 0) {
4759  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4760  }
4761  }
4762  }
4763  }
4764  }
4765  break;
4766  case 'I':
4767  case 'l':
4768  /* 12-Hour */
4769  if (tm.tm_hour == 0)
4770  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4771  else if (tm.tm_hour > 12)
4772  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4773  else
4774  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4775  res = wait_file(chan, ints, nextmsg, lang);
4776  if (!res) {
4777  res = wait_file(chan, ints, "digits/oclock", lang);
4778  }
4779  break;
4780  case 'H':
4781  case 'k':
4782  /* 24-Hour */
4783  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4784  if (!res) {
4785  res = wait_file(chan, ints, "digits/oclock", lang);
4786  }
4787  break;
4788  case 'M':
4789  /* Minute */
4790  if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4791  res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say minutes */
4792  } else if (tm.tm_min > 0) {
4793  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4794  }
4795 
4796  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4797  if (tm.tm_min == 1) {
4798  res = wait_file(chan, ints, "minute", lang);
4799  } else {
4800  res = wait_file(chan, ints, "minutes", lang);
4801  }
4802  }
4803  break;
4804  case 'P':
4805  case 'p':
4806  /* AM/PM */
4807  if (tm.tm_hour > 11)
4808  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4809  else
4810  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4811  res = wait_file(chan, ints, nextmsg, lang);
4812  break;
4813  case 'Q':
4814  /* Shorthand for "Today", "Yesterday", or AdBY */
4815  /* XXX As emphasized elsewhere, this should the native way in your
4816  * language to say the date, with changes in what you say, depending
4817  * upon how recent the date is. XXX */
4818  {
4819  struct timeval now = ast_tvnow();
4820  struct ast_tm tmnow;
4821  time_t beg_today;
4822 
4823  ast_localtime(&now, &tmnow, tzone);
4824  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4825  /* In any case, it saves not having to do ast_mktime() */
4826  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4827  if (beg_today < t) {
4828  /* Today */
4829  res = wait_file(chan, ints, "digits/today", lang);
4830  } else if (beg_today - 86400 < t) {
4831  /* Yesterday */
4832  res = wait_file(chan, ints, "digits/yesterday", lang);
4833  } else {
4834  res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
4835  }
4836  }
4837  break;
4838  case 'q':
4839  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4840  /* XXX As emphasized elsewhere, this should the native way in your
4841  * language to say the date, with changes in what you say, depending
4842  * upon how recent the date is. XXX */
4843  {
4844  struct timeval now = ast_tvnow();
4845  struct ast_tm tmnow;
4846  time_t beg_today;
4847 
4848  ast_localtime(&now, &tmnow, tzone);
4849  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4850  /* In any case, it saves not having to do ast_mktime() */
4851  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4852  if (beg_today < t) {
4853  /* Today */
4854  } else if ((beg_today - 86400) < t) {
4855  /* Yesterday */
4856  res = wait_file(chan, ints, "digits/yesterday", lang);
4857  } else if (beg_today - 86400 * 6 < t) {
4858  /* Within the last week */
4859  res = ast_say_date_with_format_de(chan, t, ints, lang, "A", tzone);
4860  } else {
4861  res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
4862  }
4863  }
4864  break;
4865  case 'R':
4866  res = ast_say_date_with_format_de(chan, t, ints, lang, "HM", tzone);
4867  break;
4868  case 'S':
4869  /* Seconds */
4870  res = wait_file(chan, ints, "digits/and", lang);
4871  if (!res) {
4872  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
4873  if (!res) {
4874  res = wait_file(chan, ints, tm.tm_sec == 1 ? "second" : "seconds", lang);
4875  }
4876  }
4877  break;
4878  case 'T':
4879  res = ast_say_date_with_format_de(chan, t, ints, lang, "HMS", tzone);
4880  break;
4881  case ' ':
4882  case ' ':
4883  /* Just ignore spaces and tabs */
4884  break;
4885  default:
4886  /* Unknown character */
4887  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4888  }
4889  /* Jump out on DTMF */
4890  if (res) {
4891  break;
4892  }
4893  }
4894  return res;
4895 }
4896 
4897 /* Icelandic syntax */
4898 int ast_say_date_with_format_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4899 {
4900  struct timeval when = { t, 0 };
4901  struct ast_tm tm;
4902  int res=0, offset, sndoffset;
4903  char sndfile[256], nextmsg[256];
4904 
4905  if (!format)
4906  format = "A dBY HMS";
4907 
4908  ast_localtime(&when, &tm, tzone);
4909 
4910  for (offset=0 ; format[offset] != '\0' ; offset++) {
4911  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4912  switch (format[offset]) {
4913  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4914  case '\'':
4915  /* Literal name of a sound file */
4916  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4917  sndfile[sndoffset] = format[offset];
4918  }
4919  sndfile[sndoffset] = '\0';
4920  res = wait_file(chan, ints, sndfile, lang);
4921  break;
4922  case 'A':
4923  case 'a':
4924  /* Sunday - Saturday */
4925  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4926  res = wait_file(chan, ints, nextmsg, lang);
4927  break;
4928  case 'B':
4929  case 'b':
4930  case 'h':
4931  /* January - December */
4932  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4933  res = wait_file(chan, ints, nextmsg, lang);
4934  break;
4935  case 'm':
4936  /* Month enumerated */
4937  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4938  break;
4939  case 'd':
4940  case 'e':
4941  /* First - Thirtyfirst */
4942  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4943  break;
4944  case 'Y':
4945  /* Year */
4946  {
4947  int year = tm.tm_year + 1900;
4948  if (year > 1999) { /* year 2000 and later */
4949  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4950  } else {
4951  if (year < 1100) {
4952  /* I'm not going to handle 1100 and prior */
4953  /* We'll just be silent on the year, instead of bombing out. */
4954  } else {
4955  /* year 1100 to 1999. will anybody need this?!? */
4956  /* say 1967 as 'nineteen hundred seven and sixty' */
4957  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4958  res = wait_file(chan, ints, nextmsg, lang);
4959  if (!res) {
4960  res = wait_file(chan, ints, "digits/hundred", lang);
4961  if (!res && year % 100 != 0) {
4962  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4963  }
4964  }
4965  }
4966  }
4967  }
4968  break;
4969  case 'I':
4970  case 'l':
4971  /* 12-Hour */
4972  res = wait_file(chan, ints, "digits/oclock", lang);
4973  if (tm.tm_hour == 0)
4974  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4975  else if (tm.tm_hour > 12)
4976  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4977  else
4978  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4979  if (!res) {
4980  res = wait_file(chan, ints, nextmsg, lang);
4981  }
4982  break;
4983  case 'H':
4984  /* 24-Hour, single digit hours preceeded by "oh" (0) */
4985  if (tm.tm_hour < 10 && tm.tm_hour > 0) {
4986  res = wait_file(chan, ints, "digits/0", lang);
4987  }
4988  /* FALLTRHU */
4989  case 'k':
4990  /* 24-Hour */
4991  res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
4992  break;
4993  case 'M':
4994  /* Minute */
4995  if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4996  if (tm.tm_min < 10)
4997  res = wait_file(chan, ints, "digits/0", lang);
4998  /* Gender depends on whether or not seconds follow */
4999  if (next_item(&format[offset + 1]) == 'S')
5000  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5001  else
5002  res = ast_say_number(chan, tm.tm_min, ints, lang, "n");
5003  }
5004  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
5005  /* Say minute/minutes depending on whether minutes end in 1 */
5006  if ((tm.tm_min % 10 == 1) && (tm.tm_min != 11)) {
5007  res = wait_file(chan, ints, "minute", lang);
5008  } else {
5009  res = wait_file(chan, ints, "minutes", lang);
5010  }
5011  }
5012  break;
5013  case 'P':
5014  case 'p':
5015  /* AM/PM */
5016  if (tm.tm_hour > 11)
5017  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5018  else
5019  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5020  res = wait_file(chan, ints, nextmsg, lang);
5021  break;
5022  case 'Q':
5023  /* Shorthand for "Today", "Yesterday", or AdBY */
5024  /* XXX As emphasized elsewhere, this should the native way in your
5025  * language to say the date, with changes in what you say, depending
5026  * upon how recent the date is. XXX */
5027  {
5028  struct timeval now = ast_tvnow();
5029  struct ast_tm tmnow;
5030  time_t beg_today;
5031 
5032  ast_localtime(&now, &tmnow, tzone);
5033  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5034  /* In any case, it saves not having to do ast_mktime() */
5035  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5036  if (beg_today < t) {
5037  /* Today */
5038  res = wait_file(chan, ints, "digits/today", lang);
5039  } else if (beg_today - 86400 < t) {
5040  /* Yesterday */
5041  res = wait_file(chan, ints, "digits/yesterday", lang);
5042  } else {
5043  res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5044  }
5045  }
5046  break;
5047  case 'q':
5048  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5049  /* XXX As emphasized elsewhere, this should the native way in your
5050  * language to say the date, with changes in what you say, depending
5051  * upon how recent the date is. XXX */
5052  {
5053  struct timeval now = ast_tvnow();
5054  struct ast_tm tmnow;
5055  time_t beg_today;
5056 
5057  ast_localtime(&now, &tmnow, tzone);
5058  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5059  /* In any case, it saves not having to do ast_mktime() */
5060  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5061  if (beg_today < t) {
5062  /* Today */
5063  } else if ((beg_today - 86400) < t) {
5064  /* Yesterday */
5065  res = wait_file(chan, ints, "digits/yesterday", lang);
5066  } else if (beg_today - 86400 * 6 < t) {
5067  /* Within the last week */
5068  res = ast_say_date_with_format_is(chan, t, ints, lang, "A", tzone);
5069  } else {
5070  res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5071  }
5072  }
5073  break;
5074  case 'R':
5075  res = ast_say_date_with_format_is(chan, t, ints, lang, "HM", tzone);
5076  break;
5077  case 'S':
5078  /* Seconds */
5079  res = wait_file(chan, ints, "digits/and", lang);
5080  if (!res) {
5081  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5082  /* Say minute/minutes depending on whether seconds end in 1 */
5083  if (!res && (tm.tm_sec % 10 == 1) && (tm.tm_sec != 11)) {
5084  res = wait_file(chan, ints, "second", lang);
5085  } else {
5086  res = wait_file(chan, ints, "seconds", lang);
5087  }
5088  }
5089  break;
5090  case 'T':
5091  res = ast_say_date_with_format_is(chan, t, ints, lang, "HMS", tzone);
5092  break;
5093  case ' ':
5094  case ' ':
5095  /* Just ignore spaces and tabs */
5096  break;
5097  default:
5098  /* Unknown character */
5099  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5100  }
5101  /* Jump out on DTMF */
5102  if (res) {
5103  break;
5104  }
5105  }
5106  return res;
5107 }
5108 
5109 /*! \brief Thai syntax */
5110 int ast_say_date_with_format_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5111 {
5112  struct timeval when = { t, 0 };
5113  struct ast_tm tm;
5114  int res=0, offset, sndoffset;
5115  char sndfile[256], nextmsg[256];
5116 
5117  if (format == NULL)
5118  format = "a 'digits/tee' e 'digits/duan' hY I 'digits/naliga' M 'digits/natee'";
5119 
5120  ast_localtime(&when, &tm, tzone);
5121 
5122  for (offset=0 ; format[offset] != '\0' ; offset++) {
5123  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5124  switch (format[offset]) {
5125  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5126  case '\'':
5127  /* Literal name of a sound file */
5128  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5129  sndfile[sndoffset] = format[offset];
5130  }
5131  sndfile[sndoffset] = '\0';
5132  res = wait_file(chan, ints, sndfile, lang);
5133  break;
5134  case 'A':
5135  case 'a':
5136  /* Sunday - Saturday */
5137  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5138  res = wait_file(chan, ints, nextmsg, lang);
5139  break;
5140  case 'B':
5141  case 'b':
5142  case 'h':
5143  /* January - December */
5144  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5145  res = wait_file(chan, ints, nextmsg, lang);
5146  break;
5147  case 'm':
5148  /* Month enumerated */
5149  res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
5150  break;
5151  case 'd':
5152  case 'e':
5153  /* First - Thirtyfirst */
5154  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5155  break;
5156  case 'Y':
5157  /* Year */
5158  res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
5159  break;
5160  case 'I':
5161  case 'l':
5162  /* 12-Hour */
5163  if (tm.tm_hour == 0)
5164  ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5165  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5166  res = wait_file(chan, ints, nextmsg, lang);
5167  break;
5168  case 'H':
5169  case 'k':
5170  /* 24-Hour */
5171  if (tm.tm_hour == 0)
5172  ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5173  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5174  res = wait_file(chan, ints, nextmsg, lang);
5175  break;
5176  case 'M':
5177  case 'N':
5178  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5179  break;
5180  case 'P':
5181  case 'p':
5182  break;
5183  case 'Q':
5184  /* Shorthand for "Today", "Yesterday", or ABdY */
5185  /* XXX As emphasized elsewhere, this should the native way in your
5186  * language to say the date, with changes in what you say, depending
5187  * upon how recent the date is. XXX */
5188  {
5189  struct timeval now = ast_tvnow();
5190  struct ast_tm tmnow;
5191  time_t beg_today;
5192 
5193  ast_localtime(&now, &tmnow, tzone);
5194  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5195  /* In any case, it saves not having to do ast_mktime() */
5196  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5197  if (beg_today < t) {
5198  /* Today */
5199  res = wait_file(chan, ints, "digits/today", lang);
5200  } else if (beg_today - 86400 < t) {
5201  /* Yesterday */
5202  res = wait_file(chan, ints, "digits/yesterday", lang);
5203  } else if (beg_today - 86400 * 6 < t) {
5204  /* Within the last week */
5205  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5206  } else if (beg_today - 2628000 < t) {
5207  /* Less than a month ago - "Sunday, October third" */
5208  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5209  } else if (beg_today - 15768000 < t) {
5210  /* Less than 6 months ago - "August seventh" */
5211  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5212  } else {
5213  /* More than 6 months ago - "April nineteenth two thousand three" */
5214  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5215  }
5216  }
5217  break;
5218  case 'q':
5219  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5220  /* XXX As emphasized elsewhere, this should the native way in your
5221  * language to say the date, with changes in what you say, depending
5222  * upon how recent the date is. XXX */
5223  {
5224  struct timeval now = ast_tvnow();
5225  struct ast_tm tmnow;
5226  time_t beg_today;
5227 
5228  ast_localtime(&now, &tmnow, tzone);
5229  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5230  /* In any case, it saves not having to do ast_mktime() */
5231  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5232  if (beg_today < t) {
5233  /* Today */
5234  } else if ((beg_today - 86400) < t) {
5235  /* Yesterday */
5236  res = wait_file(chan, ints, "digits/yesterday", lang);
5237  } else if (beg_today - 86400 * 6 < t) {
5238  /* Within the last week */
5239  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5240  } else if (beg_today - 2628000 < t) {
5241  /* Less than a month ago - "Sunday, October third" */
5242  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5243  } else if (beg_today - 15768000 < t) {
5244  /* Less than 6 months ago - "August seventh" */
5245  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5246  } else {
5247  /* More than 6 months ago - "April nineteenth two thousand three" */
5248  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5249  }
5250  }
5251  break;
5252  case 'R':
5253  res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
5254  break;
5255  case 'S':
5256  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
5257  break;
5258  case 'T':
5259  res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
5260  break;
5261  case ' ':
5262  case ' ':
5263  /* Just ignore spaces and tabs */
5264  break;
5265  default:
5266  /* Unknown character */
5267  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5268  }
5269  /* Jump out on DTMF */
5270  if (res) {
5271  break;
5272  }
5273  }
5274  return res;
5275 }
5276 
5277 /*! \brief ast_say_date_with_format_he Say formatted date in Hebrew
5278  *
5279  * \ref ast_say_date_with_format_en for the details of the options
5280  *
5281  * Changes from the English version:
5282  *
5283  * - don't replicate in here the logic of ast_say_number_full_he
5284  *
5285  * - year is always 4-digit (because it's simpler)
5286  *
5287  * - added c, x, and X. Mainly for my tests
5288  *
5289  * - The standard "long" format used in Hebrew is AdBY, rather than ABdY
5290  *
5291  * \todo
5292  * - A "ha" is missing in the standard date format, before the 'd'.
5293  * - The numbers of 3000--19000 are not handled well
5294  */
5295 int ast_say_date_with_format_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5296 {
5297 #define IL_DATE_STR "AdBY"
5298 #define IL_TIME_STR "HM" /* NOTE: In Hebrew we do not support 12 hours, only 24. No AM or PM exists in the Hebrew language */
5299 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
5300  /* TODO: This whole function is cut&paste from
5301  * ast_say_date_with_format_en . Is that considered acceptable?
5302  **/
5303  struct timeval when = { t, 0 };
5304  struct ast_tm tm;
5305  int res = 0, offset, sndoffset;
5306  char sndfile[256], nextmsg[256];
5307 
5308  if (!format) {
5309  format = IL_DATE_STR_FULL;
5310  }
5311 
5312  ast_localtime(&when, &tm, tzone);
5313 
5314  for (offset = 0; format[offset] != '\0'; offset++) {
5315  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5316  switch (format[offset]) {
5317  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5318  case '\'':
5319  /* Literal name of a sound file */
5320  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5321  sndfile[sndoffset] = format[offset];
5322  }
5323  sndfile[sndoffset] = '\0';
5324  res = wait_file(chan, ints, sndfile, lang);
5325  break;
5326  case 'A':
5327  case 'a':
5328  /* Sunday - Saturday */
5329  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5330  res = wait_file(chan, ints, nextmsg, lang);
5331  break;
5332  case 'B':
5333  case 'b':
5334  case 'h':
5335  /* January - December */
5336  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5337  res = wait_file(chan, ints, nextmsg, lang);
5338  break;
5339  case 'd':
5340  case 'e': /* Day of the month */
5341  /* I'm not sure exactly what the parameters
5342  * audiofd and ctrlfd to
5343  * ast_say_number_full_he mean, but it seems
5344  * safe to pass -1 there.
5345  *
5346  * At least in one of the pathes :-(
5347  */
5348  res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
5349  break;
5350  case 'Y': /* Year */
5351  res = ast_say_number_full_he(chan, tm.tm_year + 1900, ints, lang, "f", -1, -1);
5352  break;
5353  case 'I':
5354  case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
5355  case 'H':
5356  case 'k': /* 24-Hour */
5357  res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
5358  break;
5359  case 'M': /* Minute */
5360  if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
5361  res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
5362  res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5363  break;
5364  case 'P':
5365  case 'p':
5366  /* AM/PM - There is no AM/PM in Hebrew... */
5367  break;
5368  case 'Q':
5369  /* Shorthand for "Today", "Yesterday", or "date" */
5370  case 'q':
5371  /* Shorthand for "" (today), "Yesterday", A
5372  * (weekday), or "date" */
5373  /* XXX As emphasized elsewhere, this should the native way in your
5374  * language to say the date, with changes in what you say, depending
5375  * upon how recent the date is. XXX */
5376  {
5377  struct timeval now = ast_tvnow();
5378  struct ast_tm tmnow;
5379  time_t beg_today;
5380  char todo = format[offset]; /* The letter to format*/
5381 
5382  ast_localtime(&now, &tmnow, tzone);
5383  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5384  /* In any case, it saves not having to do ast_mktime() */
5385  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5386  if (beg_today < t) {
5387  /* Today */
5388  if (todo == 'Q') {
5389  res = wait_file(chan, ints, "digits/today", lang);
5390  }
5391  } else if (beg_today - 86400 < t) {
5392  /* Yesterday */
5393  res = wait_file(chan, ints, "digits/yesterday", lang);
5394  } else if ((todo != 'Q') && (beg_today - 86400 * 6 < t)) {
5395  /* Within the last week */
5396  res = ast_say_date_with_format_he(chan, t, ints, lang, "A", tzone);
5397  } else {
5398  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5399  }
5400  }
5401  break;
5402  case 'R':
5403  res = ast_say_date_with_format_he(chan, t, ints, lang, "HM", tzone);
5404  break;
5405  case 'S': /* Seconds */
5406  res = ast_say_number_full_he(chan, tm.tm_sec,
5407  ints, lang, "f", -1, -1
5408  );
5409  break;
5410  case 'T':
5411  res = ast_say_date_with_format_he(chan, t, ints, lang, "HMS", tzone);
5412  break;
5413  /* c, x, and X seem useful for testing. Not sure
5414  * if they're good for the general public */
5415  case 'c':
5416  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR_FULL, tzone);
5417  break;
5418  case 'x':
5419  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5420  break;
5421  case 'X': /* Currently not locale-dependent...*/
5422  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_TIME_STR, tzone);
5423  break;
5424  case ' ':
5425  case ' ':
5426  /* Just ignore spaces and tabs */
5427  break;
5428  default:
5429  /* Unknown character */
5430  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5431  }
5432  /* Jump out on DTMF */
5433  if (res) {
5434  break;
5435  }
5436  }
5437  return res;
5438 }
5439 
5440 
5441 /*! \brief Spanish syntax */
5442 int ast_say_date_with_format_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5443 {
5444  struct timeval when = { t, 0 };
5445  struct ast_tm tm;
5446  int res=0, offset, sndoffset;
5447  char sndfile[256], nextmsg[256];
5448 
5449  if (format == NULL)
5450  format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
5451 
5452  ast_localtime(&when, &tm, tzone);
5453 
5454  for (offset=0 ; format[offset] != '\0' ; offset++) {
5455  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5456  switch (format[offset]) {
5457  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5458  case '\'':
5459  /* Literal name of a sound file */
5460  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5461  sndfile[sndoffset] = format[offset];
5462  }
5463  sndfile[sndoffset] = '\0';
5464  snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
5465  res = wait_file(chan, ints, nextmsg, lang);
5466  break;
5467  case 'A':
5468  case 'a':
5469  /* Sunday - Saturday */
5470  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5471  res = wait_file(chan, ints, nextmsg, lang);
5472  break;
5473  case 'B':
5474  case 'b':
5475  case 'h':
5476  /* January - December */
5477  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5478  res = wait_file(chan, ints, nextmsg, lang);
5479  break;
5480  case 'm':
5481  /* First - Twelfth */
5482  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5483  res = wait_file(chan, ints, nextmsg, lang);
5484  break;
5485  case 'd':
5486  case 'e':
5487  /* First - Thirtyfirst */
5488  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5489  break;
5490  case 'Y':
5491  /* Year */
5492  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5493  break;
5494  case 'I':
5495  case 'l':
5496  /* 12-Hour */
5497  if (tm.tm_hour == 0)
5498  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5499  else if (tm.tm_hour == 1 || tm.tm_hour == 13)
5500  snprintf(nextmsg,sizeof(nextmsg), "digits/1F");
5501  else if (tm.tm_hour > 12)
5502  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5503  else
5504  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5505  res = wait_file(chan, ints, nextmsg, lang);
5506  break;
5507  case 'H':
5508  case 'k':
5509  /* 24-Hour */
5510  res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
5511  break;
5512  case 'M':
5513  /* Minute */
5514  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5515  break;
5516  case 'P':
5517  case 'p':
5518  /* AM/PM */
5519  if (tm.tm_hour > 18)
5520  res = wait_file(chan, ints, "digits/p-m", lang);
5521  else if (tm.tm_hour > 12)
5522  res = wait_file(chan, ints, "digits/afternoon", lang);
5523  else if (tm.tm_hour)
5524  res = wait_file(chan, ints, "digits/a-m", lang);
5525  break;
5526  case 'Q':
5527  /* Shorthand for "Today", "Yesterday", or ABdY */
5528  /* XXX As emphasized elsewhere, this should the native way in your
5529  * language to say the date, with changes in what you say, depending
5530  * upon how recent the date is. XXX */
5531  {
5532  struct timeval now = ast_tvnow();
5533  struct ast_tm tmnow;
5534  time_t beg_today;
5535 
5536  ast_localtime(&now, &tmnow, tzone);
5537  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5538  /* In any case, it saves not having to do ast_mktime() */
5539  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5540  if (beg_today < t) {
5541  /* Today */
5542  res = wait_file(chan, ints, "digits/today", lang);
5543  } else if (beg_today - 86400 < t) {
5544  /* Yesterday */
5545  res = wait_file(chan, ints, "digits/yesterday", lang);
5546  } else {
5547  res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5548  }
5549  }
5550  break;
5551  case 'q':
5552  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5553  /* XXX As emphasized elsewhere, this should the native way in your
5554  * language to say the date, with changes in what you say, depending
5555  * upon how recent the date is. XXX */
5556  {
5557  struct timeval now = ast_tvnow();
5558  struct ast_tm tmnow;
5559  time_t beg_today;
5560 
5561  ast_localtime(&now, &tmnow, tzone);
5562  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5563  /* In any case, it saves not having to do ast_mktime() */
5564  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5565  if (beg_today < t) {
5566  /* Today */
5567  res = wait_file(chan, ints, "digits/today", lang);
5568  } else if ((beg_today - 86400) < t) {
5569  /* Yesterday */
5570  res = wait_file(chan, ints, "digits/yesterday", lang);
5571  } else if (beg_today - 86400 * 6 < t) {
5572  /* Within the last week */
5573  res = ast_say_date_with_format_es(chan, t, ints, lang, "A", tzone);
5574  } else {
5575  res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5576  }
5577  }
5578  break;
5579  case 'R':
5580  res = ast_say_date_with_format_es(chan, t, ints, lang, "H 'digits/y' M", tzone);
5581  break;
5582  case 'S':
5583  /* Seconds */
5584  if (tm.tm_sec == 0) {
5585  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5586  res = wait_file(chan, ints, nextmsg, lang);
5587  } else if (tm.tm_sec < 10) {
5588  res = wait_file(chan, ints, "digits/oh", lang);
5589  if (!res) {
5590  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5591  res = wait_file(chan, ints, nextmsg, lang);
5592  }
5593  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5594  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5595  res = wait_file(chan, ints, nextmsg, lang);
5596  } else {
5597  int ten, one;
5598  ten = (tm.tm_sec / 10) * 10;
5599  one = (tm.tm_sec % 10);
5600  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
5601  res = wait_file(chan, ints, nextmsg, lang);
5602  if (!res) {
5603  /* Fifty, not fifty-zero */
5604  if (one != 0) {
5605  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
5606  res = wait_file(chan, ints, nextmsg, lang);
5607  }
5608  }
5609  }
5610  break;
5611  case 'T':
5612  res = ast_say_date_with_format_es(chan, t, ints, lang, "HMS", tzone);
5613  break;
5614  case ' ':
5615  case ' ':
5616  /* Just ignore spaces and tabs */
5617  break;
5618  default:
5619  /* Unknown character */
5620  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5621  }
5622  /* Jump out on DTMF */
5623  if (res) {
5624  break;
5625  }
5626  }
5627  return res;
5628 }
5629 
5630 /*! \brief French syntax
5631 oclock = heure
5632 */
5633 int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5634 {
5635  struct timeval when = { t, 0 };
5636  struct ast_tm tm;
5637  int res=0, offset, sndoffset;
5638  char sndfile[256], nextmsg[256];
5639 
5640  if (format == NULL)
5641  format = "AdBY 'digits/at' IMp";
5642 
5643  ast_localtime(&when, &tm, tzone);
5644 
5645  for (offset=0 ; format[offset] != '\0' ; offset++) {
5646  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5647  switch (format[offset]) {
5648  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5649  case '\'':
5650  /* Literal name of a sound file */
5651  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5652  sndfile[sndoffset] = format[offset];
5653  }
5654  sndfile[sndoffset] = '\0';
5655  res = wait_file(chan, ints, sndfile, lang);
5656  break;
5657  case 'A':
5658  case 'a':
5659  /* Sunday - Saturday */
5660  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5661  res = wait_file(chan, ints, nextmsg, lang);
5662  break;
5663  case 'B':
5664  case 'b':
5665  case 'h':
5666  /* January - December */
5667  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5668  res = wait_file(chan, ints, nextmsg, lang);
5669  break;
5670  case 'm':
5671  /* First - Twelfth */
5672  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5673  res = wait_file(chan, ints, nextmsg, lang);
5674  break;
5675  case 'd':
5676  case 'e':
5677  /* First */
5678  if (tm.tm_mday == 1) {
5679  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5680  res = wait_file(chan, ints, nextmsg, lang);
5681  } else {
5682  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
5683  }
5684  break;
5685  case 'Y':
5686  /* Year */
5687  if (tm.tm_year > 99) {
5688  res = wait_file(chan, ints, "digits/2", lang);
5689  if (!res) {
5690  res = wait_file(chan, ints, "digits/thousand", lang);
5691  }
5692  if (tm.tm_year > 100) {
5693  if (!res) {
5694  res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
5695  }
5696  }
5697  } else {
5698  if (tm.tm_year < 1) {
5699  /* I'm not going to handle 1900 and prior */
5700  /* We'll just be silent on the year, instead of bombing out. */
5701  } else {
5702  res = wait_file(chan, ints, "digits/thousand", lang);
5703  if (!res) {
5704  wait_file(chan, ints, "digits/9", lang);
5705  wait_file(chan, ints, "digits/hundred", lang);
5706  res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
5707  }
5708  }
5709  }
5710  break;
5711  case 'I':
5712  case 'l':
5713  /* 12-Hour */
5714  if (tm.tm_hour == 0)
5715  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5716  else if (tm.