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